]> Pileus Git - ~andy/linux/blob - drivers/usb/host/ehci-omap.c
Merge branch 'for-linus' of git://git.linaro.org/people/rmk/linux-arm
[~andy/linux] / drivers / usb / host / ehci-omap.c
1 /*
2  * ehci-omap.c - driver for USBHOST on OMAP3/4 processors
3  *
4  * Bus Glue for the EHCI controllers in OMAP3/4
5  * Tested on several OMAP3 boards, and OMAP4 Pandaboard
6  *
7  * Copyright (C) 2007-2011 Texas Instruments, Inc.
8  *      Author: Vikram Pandita <vikram.pandita@ti.com>
9  *      Author: Anand Gadiyar <gadiyar@ti.com>
10  *      Author: Keshava Munegowda <keshava_mgowda@ti.com>
11  *
12  * Copyright (C) 2009 Nokia Corporation
13  *      Contact: Felipe Balbi <felipe.balbi@nokia.com>
14  *
15  * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 2 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
30  *
31  * TODO (last updated Feb 27, 2010):
32  *      - add kernel-doc
33  *      - enable AUTOIDLE
34  *      - add suspend/resume
35  *      - add HSIC and TLL support
36  *      - convert to use hwmod and runtime PM
37  */
38
39 #include <linux/platform_device.h>
40 #include <linux/slab.h>
41 #include <linux/usb/ulpi.h>
42 #include <plat/usb.h>
43 #include <linux/regulator/consumer.h>
44 #include <linux/pm_runtime.h>
45 #include <linux/gpio.h>
46 #include <linux/clk.h>
47
48 /* EHCI Register Set */
49 #define EHCI_INSNREG04                                  (0xA0)
50 #define EHCI_INSNREG04_DISABLE_UNSUSPEND                (1 << 5)
51 #define EHCI_INSNREG05_ULPI                             (0xA4)
52 #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT               31
53 #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT               24
54 #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT                 22
55 #define EHCI_INSNREG05_ULPI_REGADD_SHIFT                16
56 #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT             8
57 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT                0
58
59 /*-------------------------------------------------------------------------*/
60
61 static const struct hc_driver ehci_omap_hc_driver;
62
63
64 static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
65 {
66         __raw_writel(val, base + reg);
67 }
68
69 static inline u32 ehci_read(void __iomem *base, u32 reg)
70 {
71         return __raw_readl(base + reg);
72 }
73
74
75 static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
76 {
77         unsigned long timeout = jiffies + msecs_to_jiffies(1000);
78         unsigned reg = 0;
79
80         reg = ULPI_FUNC_CTRL_RESET
81                 /* FUNCTION_CTRL_SET register */
82                 | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
83                 /* Write */
84                 | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
85                 /* PORTn */
86                 | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
87                 /* start ULPI access*/
88                 | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
89
90         ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg);
91
92         /* Wait for ULPI access completion */
93         while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI)
94                         & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
95                 cpu_relax();
96
97                 if (time_after(jiffies, timeout)) {
98                         dev_dbg(hcd->self.controller,
99                                         "phy reset operation timed out\n");
100                         break;
101                 }
102         }
103 }
104
105 static int omap_ehci_init(struct usb_hcd *hcd)
106 {
107         struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
108         int                     rc;
109         struct ehci_hcd_omap_platform_data      *pdata;
110
111         pdata = hcd->self.controller->platform_data;
112
113         /* Hold PHYs in reset while initializing EHCI controller */
114         if (pdata->phy_reset) {
115                 if (gpio_is_valid(pdata->reset_gpio_port[0]))
116                         gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
117
118                 if (gpio_is_valid(pdata->reset_gpio_port[1]))
119                         gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
120
121                 /* Hold the PHY in RESET for enough time till DIR is high */
122                 udelay(10);
123         }
124
125         /* Soft reset the PHY using PHY reset command over ULPI */
126         if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
127                 omap_ehci_soft_phy_reset(hcd, 0);
128         if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
129                 omap_ehci_soft_phy_reset(hcd, 1);
130
131         /* we know this is the memory we want, no need to ioremap again */
132         ehci->caps = hcd->regs;
133
134         rc = ehci_setup(hcd);
135
136         if (pdata->phy_reset) {
137                 /* Hold the PHY in RESET for enough time till
138                  * PHY is settled and ready
139                  */
140                 udelay(10);
141
142                 if (gpio_is_valid(pdata->reset_gpio_port[0]))
143                         gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
144
145                 if (gpio_is_valid(pdata->reset_gpio_port[1]))
146                         gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
147         }
148
149         /* root ports should always stay powered */
150         ehci_port_power(ehci, 1);
151
152         return rc;
153 }
154
155 static void disable_put_regulator(
156                 struct ehci_hcd_omap_platform_data *pdata)
157 {
158         int i;
159
160         for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
161                 if (pdata->regulator[i]) {
162                         regulator_disable(pdata->regulator[i]);
163                         regulator_put(pdata->regulator[i]);
164                 }
165         }
166 }
167
168 /* configure so an HC device and id are always provided */
169 /* always called with process context; sleeping is OK */
170
171 /**
172  * ehci_hcd_omap_probe - initialize TI-based HCDs
173  *
174  * Allocates basic resources for this USB host controller, and
175  * then invokes the start() method for the HCD associated with it
176  * through the hotplug entry's driver_data.
177  */
178 static int ehci_hcd_omap_probe(struct platform_device *pdev)
179 {
180         struct device                           *dev = &pdev->dev;
181         struct ehci_hcd_omap_platform_data      *pdata = dev->platform_data;
182         struct resource                         *res;
183         struct usb_hcd                          *hcd;
184         void __iomem                            *regs;
185         int                                     ret = -ENODEV;
186         int                                     irq;
187         int                                     i;
188         char                                    supply[7];
189
190         if (usb_disabled())
191                 return -ENODEV;
192
193         if (!dev->parent) {
194                 dev_err(dev, "Missing parent device\n");
195                 return -ENODEV;
196         }
197
198         irq = platform_get_irq_byname(pdev, "ehci-irq");
199         if (irq < 0) {
200                 dev_err(dev, "EHCI irq failed\n");
201                 return -ENODEV;
202         }
203
204         res =  platform_get_resource_byname(pdev,
205                                 IORESOURCE_MEM, "ehci");
206         if (!res) {
207                 dev_err(dev, "UHH EHCI get resource failed\n");
208                 return -ENODEV;
209         }
210
211         regs = ioremap(res->start, resource_size(res));
212         if (!regs) {
213                 dev_err(dev, "UHH EHCI ioremap failed\n");
214                 return -ENOMEM;
215         }
216
217         hcd = usb_create_hcd(&ehci_omap_hc_driver, dev,
218                         dev_name(dev));
219         if (!hcd) {
220                 dev_err(dev, "failed to create hcd with err %d\n", ret);
221                 ret = -ENOMEM;
222                 goto err_io;
223         }
224
225         hcd->rsrc_start = res->start;
226         hcd->rsrc_len = resource_size(res);
227         hcd->regs = regs;
228
229         /* get ehci regulator and enable */
230         for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
231                 if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) {
232                         pdata->regulator[i] = NULL;
233                         continue;
234                 }
235                 snprintf(supply, sizeof(supply), "hsusb%d", i);
236                 pdata->regulator[i] = regulator_get(dev, supply);
237                 if (IS_ERR(pdata->regulator[i])) {
238                         pdata->regulator[i] = NULL;
239                         dev_dbg(dev,
240                         "failed to get ehci port%d regulator\n", i);
241                 } else {
242                         regulator_enable(pdata->regulator[i]);
243                 }
244         }
245
246         pm_runtime_enable(dev);
247         pm_runtime_get_sync(dev);
248
249         /*
250          * An undocumented "feature" in the OMAP3 EHCI controller,
251          * causes suspended ports to be taken out of suspend when
252          * the USBCMD.Run/Stop bit is cleared (for example when
253          * we do ehci_bus_suspend).
254          * This breaks suspend-resume if the root-hub is allowed
255          * to suspend. Writing 1 to this undocumented register bit
256          * disables this feature and restores normal behavior.
257          */
258         ehci_write(regs, EHCI_INSNREG04,
259                                 EHCI_INSNREG04_DISABLE_UNSUSPEND);
260
261         ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
262         if (ret) {
263                 dev_err(dev, "failed to add hcd with err %d\n", ret);
264                 goto err_pm_runtime;
265         }
266
267
268         return 0;
269
270 err_pm_runtime:
271         disable_put_regulator(pdata);
272         pm_runtime_put_sync(dev);
273         usb_put_hcd(hcd);
274
275 err_io:
276         iounmap(regs);
277         return ret;
278 }
279
280
281 /**
282  * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs
283  * @pdev: USB Host Controller being removed
284  *
285  * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking
286  * the HCD's stop() method.  It is always called from a thread
287  * context, normally "rmmod", "apmd", or something similar.
288  */
289 static int ehci_hcd_omap_remove(struct platform_device *pdev)
290 {
291         struct device *dev                              = &pdev->dev;
292         struct usb_hcd *hcd                             = dev_get_drvdata(dev);
293         struct ehci_hcd_omap_platform_data *pdata       = dev->platform_data;
294
295         usb_remove_hcd(hcd);
296         disable_put_regulator(dev->platform_data);
297         iounmap(hcd->regs);
298         usb_put_hcd(hcd);
299
300         pm_runtime_put_sync(dev);
301         pm_runtime_disable(dev);
302
303         if (pdata->phy_reset) {
304                 if (gpio_is_valid(pdata->reset_gpio_port[0]))
305                         gpio_free(pdata->reset_gpio_port[0]);
306
307                 if (gpio_is_valid(pdata->reset_gpio_port[1]))
308                         gpio_free(pdata->reset_gpio_port[1]);
309         }
310         return 0;
311 }
312
313 static void ehci_hcd_omap_shutdown(struct platform_device *pdev)
314 {
315         struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
316
317         if (hcd->driver->shutdown)
318                 hcd->driver->shutdown(hcd);
319 }
320
321 static struct platform_driver ehci_hcd_omap_driver = {
322         .probe                  = ehci_hcd_omap_probe,
323         .remove                 = ehci_hcd_omap_remove,
324         .shutdown               = ehci_hcd_omap_shutdown,
325         /*.suspend              = ehci_hcd_omap_suspend, */
326         /*.resume               = ehci_hcd_omap_resume, */
327         .driver = {
328                 .name           = "ehci-omap",
329         }
330 };
331
332 /*-------------------------------------------------------------------------*/
333
334 static const struct hc_driver ehci_omap_hc_driver = {
335         .description            = hcd_name,
336         .product_desc           = "OMAP-EHCI Host Controller",
337         .hcd_priv_size          = sizeof(struct ehci_hcd),
338
339         /*
340          * generic hardware linkage
341          */
342         .irq                    = ehci_irq,
343         .flags                  = HCD_MEMORY | HCD_USB2,
344
345         /*
346          * basic lifecycle operations
347          */
348         .reset                  = omap_ehci_init,
349         .start                  = ehci_run,
350         .stop                   = ehci_stop,
351         .shutdown               = ehci_shutdown,
352
353         /*
354          * managing i/o requests and associated device resources
355          */
356         .urb_enqueue            = ehci_urb_enqueue,
357         .urb_dequeue            = ehci_urb_dequeue,
358         .endpoint_disable       = ehci_endpoint_disable,
359         .endpoint_reset         = ehci_endpoint_reset,
360
361         /*
362          * scheduling support
363          */
364         .get_frame_number       = ehci_get_frame,
365
366         /*
367          * root hub support
368          */
369         .hub_status_data        = ehci_hub_status_data,
370         .hub_control            = ehci_hub_control,
371         .bus_suspend            = ehci_bus_suspend,
372         .bus_resume             = ehci_bus_resume,
373
374         .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
375 };
376
377 MODULE_ALIAS("platform:omap-ehci");
378 MODULE_AUTHOR("Texas Instruments, Inc.");
379 MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
380