blob: c9f91e6c72b6a00880077861afde11f5acef089a [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0+
Daniel Mack7e8d5cd2009-10-28 01:14:59 +01002/*
3 * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
4 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
Daniel Mack7e8d5cd2009-10-28 01:14:59 +01005 */
6
Alan Sterndba63b2f2013-01-23 13:26:15 -05007#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/io.h>
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010010#include <linux/platform_device.h>
11#include <linux/clk.h>
12#include <linux/delay.h>
13#include <linux/usb/otg.h>
Arnaud Patard (Rtp)a464dc42011-01-18 00:04:12 +010014#include <linux/usb/ulpi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Alan Sterndba63b2f2013-01-23 13:26:15 -050016#include <linux/usb.h>
17#include <linux/usb/hcd.h>
Arnd Bergmann82906b12012-08-24 15:14:29 +020018#include <linux/platform_data/usb-ehci-mxc.h>
Alan Sterndba63b2f2013-01-23 13:26:15 -050019#include "ehci.h"
20
21#define DRIVER_DESC "Freescale On-Chip EHCI Host driver"
22
23static const char hcd_name[] = "ehci-mxc";
24
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010025#define ULPI_VIEWPORT_OFFSET 0x170
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010026
27struct ehci_mxc_priv {
Sascha Hauerc9437402012-04-25 16:39:06 +020028 struct clk *usbclk, *ahbclk, *phyclk;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010029};
30
Alan Sterndba63b2f2013-01-23 13:26:15 -050031static struct hc_driver __read_mostly ehci_mxc_hc_driver;
Matthieu CASTET65fd4272010-09-06 18:26:56 +020032
Andi Kleen62d08a12013-04-22 09:44:56 -070033static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = {
Alan Sterndba63b2f2013-01-23 13:26:15 -050034 .extra_priv_size = sizeof(struct ehci_mxc_priv),
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010035};
36
37static int ehci_mxc_drv_probe(struct platform_device *pdev)
38{
Jingoo Hand4f09e22013-07-30 19:59:40 +090039 struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010040 struct usb_hcd *hcd;
41 struct resource *res;
Uwe Kleine-König724c8522010-11-02 10:30:57 +010042 int irq, ret;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010043 struct ehci_mxc_priv *priv;
44 struct device *dev = &pdev->dev;
Fabio Estevam0247a7b2010-12-15 22:31:28 -020045 struct ehci_hcd *ehci;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010046
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010047 if (!pdata) {
48 dev_err(dev, "No platform data given, bailing out.\n");
49 return -EINVAL;
50 }
51
52 irq = platform_get_irq(pdev, 0);
53
54 hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
55 if (!hcd)
56 return -ENOMEM;
57
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010058 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Thierry Reding148e1132013-01-21 11:09:22 +010059 hcd->regs = devm_ioremap_resource(&pdev->dev, res);
60 if (IS_ERR(hcd->regs)) {
61 ret = PTR_ERR(hcd->regs);
Julia Lawallaf09c062012-07-29 21:46:13 +020062 goto err_alloc;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010063 }
Varka Bhadramd6e269a2014-11-04 07:51:22 +053064 hcd->rsrc_start = res->start;
65 hcd->rsrc_len = resource_size(res);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010066
Alan Sterndba63b2f2013-01-23 13:26:15 -050067 hcd->has_tt = 1;
68 ehci = hcd_to_ehci(hcd);
69 priv = (struct ehci_mxc_priv *) ehci->priv;
70
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010071 /* enable clocks */
Julia Lawallaf09c062012-07-29 21:46:13 +020072 priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010073 if (IS_ERR(priv->usbclk)) {
74 ret = PTR_ERR(priv->usbclk);
Julia Lawallaf09c062012-07-29 21:46:13 +020075 goto err_alloc;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010076 }
Sascha Hauer198ad2c2012-03-07 20:58:21 +010077 clk_prepare_enable(priv->usbclk);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010078
Julia Lawallaf09c062012-07-29 21:46:13 +020079 priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
Sascha Hauerc9437402012-04-25 16:39:06 +020080 if (IS_ERR(priv->ahbclk)) {
81 ret = PTR_ERR(priv->ahbclk);
82 goto err_clk_ahb;
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010083 }
Sascha Hauerc9437402012-04-25 16:39:06 +020084 clk_prepare_enable(priv->ahbclk);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +010085
Eric Bénard3bb80292011-01-13 14:53:17 +010086 /* "dr" device has its own clock on i.MX51 */
Julia Lawallaf09c062012-07-29 21:46:13 +020087 priv->phyclk = devm_clk_get(&pdev->dev, "phy");
Sascha Hauerc9437402012-04-25 16:39:06 +020088 if (IS_ERR(priv->phyclk))
89 priv->phyclk = NULL;
90 if (priv->phyclk)
91 clk_prepare_enable(priv->phyclk);
Arnaud Patard (Rtp)711669e2010-12-20 16:48:58 +010092
93
94 /* call platform specific init function */
95 if (pdata->init) {
96 ret = pdata->init(pdev);
97 if (ret) {
98 dev_err(dev, "platform init failed\n");
99 goto err_init;
100 }
101 /* platforms need some time to settle changed IO settings */
102 mdelay(10);
103 }
104
Fabio Estevam0247a7b2010-12-15 22:31:28 -0200105 /* EHCI registers start at offset 0x100 */
106 ehci->caps = hcd->regs + 0x100;
107 ehci->regs = hcd->regs + 0x100 +
Jan Anderssonc4301312011-05-03 20:11:57 +0200108 HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
Fabio Estevam0247a7b2010-12-15 22:31:28 -0200109
110 /* set up the PORTSCx register */
111 ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
112
113 /* is this really needed? */
114 msleep(10);
115
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100116 /* Initialize the transceiver */
117 if (pdata->otg) {
118 pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
Heikki Krogerusb96d3b02012-02-13 13:24:18 +0200119 ret = usb_phy_init(pdata->otg);
Wolfram Sang4c9715d2010-06-15 12:34:23 +0200120 if (ret) {
121 dev_err(dev, "unable to init transceiver, probably missing\n");
122 ret = -ENODEV;
123 goto err_add;
124 }
Heikki Krogerus6e13c652012-02-13 13:24:20 +0200125 ret = otg_set_vbus(pdata->otg->otg, 1);
Wolfram Sang4c9715d2010-06-15 12:34:23 +0200126 if (ret) {
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100127 dev_err(dev, "unable to enable vbus on transceiver\n");
Wolfram Sang4c9715d2010-06-15 12:34:23 +0200128 goto err_add;
129 }
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100130 }
131
Alan Sterndba63b2f2013-01-23 13:26:15 -0500132 platform_set_drvdata(pdev, hcd);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100133
Yong Zhangb5dd18d2011-09-07 16:10:52 +0800134 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100135 if (ret)
136 goto err_add;
137
Peter Chen3c9740a2013-11-05 10:46:02 +0800138 device_wakeup_enable(hcd->self.controller);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100139 return 0;
140
141err_add:
142 if (pdata && pdata->exit)
143 pdata->exit(pdev);
144err_init:
Julia Lawallaf09c062012-07-29 21:46:13 +0200145 if (priv->phyclk)
Sascha Hauerc9437402012-04-25 16:39:06 +0200146 clk_disable_unprepare(priv->phyclk);
Sascha Hauerc9437402012-04-25 16:39:06 +0200147
148 clk_disable_unprepare(priv->ahbclk);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100149err_clk_ahb:
Sascha Hauer198ad2c2012-03-07 20:58:21 +0100150 clk_disable_unprepare(priv->usbclk);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100151err_alloc:
152 usb_put_hcd(hcd);
153 return ret;
154}
155
Dmitry Torokhov39d35682013-02-24 00:55:07 -0800156static int ehci_mxc_drv_remove(struct platform_device *pdev)
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100157{
Jingoo Hand4f09e22013-07-30 19:59:40 +0900158 struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
Alan Sterndba63b2f2013-01-23 13:26:15 -0500159 struct usb_hcd *hcd = platform_get_drvdata(pdev);
160 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
161 struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv;
162
163 usb_remove_hcd(hcd);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100164
165 if (pdata && pdata->exit)
166 pdata->exit(pdev);
167
Daniel Mackf375fc52013-08-21 11:17:21 +0200168 if (pdata && pdata->otg)
Heikki Krogerusb96d3b02012-02-13 13:24:18 +0200169 usb_phy_shutdown(pdata->otg);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100170
Sascha Hauer198ad2c2012-03-07 20:58:21 +0100171 clk_disable_unprepare(priv->usbclk);
Sascha Hauerc9437402012-04-25 16:39:06 +0200172 clk_disable_unprepare(priv->ahbclk);
Sascha Hauerc9437402012-04-25 16:39:06 +0200173
Julia Lawallaf09c062012-07-29 21:46:13 +0200174 if (priv->phyclk)
Sascha Hauerc9437402012-04-25 16:39:06 +0200175 clk_disable_unprepare(priv->phyclk);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100176
Alan Sterndba63b2f2013-01-23 13:26:15 -0500177 usb_put_hcd(hcd);
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100178 return 0;
179}
180
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100181MODULE_ALIAS("platform:mxc-ehci");
182
183static struct platform_driver ehci_mxc_driver = {
184 .probe = ehci_mxc_drv_probe,
Alan Sterndba63b2f2013-01-23 13:26:15 -0500185 .remove = ehci_mxc_drv_remove,
Roger Quadrosaaf6b522013-07-22 15:04:50 +0300186 .shutdown = usb_hcd_platform_shutdown,
Daniel Mack7e8d5cd2009-10-28 01:14:59 +0100187 .driver = {
188 .name = "mxc-ehci",
189 },
190};
Alan Sterndba63b2f2013-01-23 13:26:15 -0500191
192static int __init ehci_mxc_init(void)
193{
194 if (usb_disabled())
195 return -ENODEV;
196
197 pr_info("%s: " DRIVER_DESC "\n", hcd_name);
198
199 ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides);
200 return platform_driver_register(&ehci_mxc_driver);
201}
202module_init(ehci_mxc_init);
203
204static void __exit ehci_mxc_cleanup(void)
205{
206 platform_driver_unregister(&ehci_mxc_driver);
207}
208module_exit(ehci_mxc_cleanup);
209
210MODULE_DESCRIPTION(DRIVER_DESC);
211MODULE_AUTHOR("Sascha Hauer");
212MODULE_LICENSE("GPL");