blob: 3656d7cea58c109637c34d423283be29cf449b1d [file] [log] [blame]
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +04001/*
2 * OHCI HCD (Host Controller Driver) for USB.
3 *
4 * TI DA8xx (OMAP-L1x) Bus Glue
5 *
6 * Derived from: ohci-omap.c and ohci-s3c2410.c
7 * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
8 *
9 * This file is licensed under the terms of the GNU General Public License
10 * version 2. This program is licensed "as is" without any warranty of any
11 * kind, whether express or implied.
12 */
13
14#include <linux/interrupt.h>
15#include <linux/jiffies.h>
16#include <linux/platform_device.h>
17#include <linux/clk.h>
David Lechner6110c422016-10-12 20:44:46 -050018#include <linux/phy/phy.h>
Arnd Bergmannec2a0832012-08-24 15:11:34 +020019#include <linux/platform_data/usb-davinci.h>
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040020
21#ifndef CONFIG_ARCH_DAVINCI_DA8XX
22#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX."
23#endif
24
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040025static struct clk *usb11_clk;
David Lechner6110c422016-10-12 20:44:46 -050026static struct phy *usb11_phy;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040027
28/* Over-current indicator change bitmask */
29static volatile u16 ocic_mask;
30
David Lechner6110c422016-10-12 20:44:46 -050031static int ohci_da8xx_enable(void)
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040032{
David Lechner6110c422016-10-12 20:44:46 -050033 int ret;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040034
David Lechner6110c422016-10-12 20:44:46 -050035 ret = clk_prepare_enable(usb11_clk);
36 if (ret)
37 return ret;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040038
David Lechner6110c422016-10-12 20:44:46 -050039 ret = phy_init(usb11_phy);
40 if (ret)
41 goto err_phy_init;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040042
David Lechner6110c422016-10-12 20:44:46 -050043 ret = phy_power_on(usb11_phy);
44 if (ret)
45 goto err_phy_power_on;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040046
David Lechner6110c422016-10-12 20:44:46 -050047 return 0;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040048
David Lechner6110c422016-10-12 20:44:46 -050049err_phy_power_on:
50 phy_exit(usb11_phy);
51err_phy_init:
52 clk_disable_unprepare(usb11_clk);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040053
David Lechner6110c422016-10-12 20:44:46 -050054 return ret;
55}
56
57static void ohci_da8xx_disable(void)
58{
59 phy_power_off(usb11_phy);
60 phy_exit(usb11_phy);
61 clk_disable_unprepare(usb11_clk);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040062}
63
64/*
65 * Handle the port over-current indicator change.
66 */
67static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
68 unsigned port)
69{
70 ocic_mask |= 1 << port;
71
72 /* Once over-current is detected, the port needs to be powered down */
73 if (hub->get_oci(port) > 0)
74 hub->set_power(port, 0);
75}
76
77static int ohci_da8xx_init(struct usb_hcd *hcd)
78{
79 struct device *dev = hcd->self.controller;
Jingoo Hand4f09e22013-07-30 19:59:40 +090080 struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040081 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
82 int result;
83 u32 rh_a;
84
85 dev_dbg(dev, "starting USB controller\n");
86
David Lechner6110c422016-10-12 20:44:46 -050087 result = ohci_da8xx_enable();
88 if (result < 0)
89 return result;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +040090
91 /*
92 * DA8xx only have 1 port connected to the pins but the HC root hub
93 * register A reports 2 ports, thus we'll have to override it...
94 */
95 ohci->num_ports = 1;
96
97 result = ohci_init(ohci);
David Lechner6110c422016-10-12 20:44:46 -050098 if (result < 0) {
99 ohci_da8xx_disable();
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400100 return result;
David Lechner6110c422016-10-12 20:44:46 -0500101 }
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400102
103 /*
104 * Since we're providing a board-specific root hub port power control
105 * and over-current reporting, we have to override the HC root hub A
106 * register's default value, so that ohci_hub_control() could return
107 * the correct hub descriptor...
108 */
109 rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
110 if (hub->set_power) {
111 rh_a &= ~RH_A_NPS;
112 rh_a |= RH_A_PSM;
113 }
114 if (hub->get_oci) {
115 rh_a &= ~RH_A_NOCP;
116 rh_a |= RH_A_OCPM;
117 }
118 rh_a &= ~RH_A_POTPGT;
119 rh_a |= hub->potpgt << 24;
120 ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
121
122 return result;
123}
124
125static void ohci_da8xx_stop(struct usb_hcd *hcd)
126{
127 ohci_stop(hcd);
David Lechner6110c422016-10-12 20:44:46 -0500128 ohci_da8xx_disable();
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400129}
130
131static int ohci_da8xx_start(struct usb_hcd *hcd)
132{
133 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
134 int result;
135
136 result = ohci_run(ohci);
137 if (result < 0)
138 ohci_da8xx_stop(hcd);
139
140 return result;
141}
142
143/*
144 * Update the status data from the hub with the over-current indicator change.
145 */
146static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
147{
148 int length = ohci_hub_status_data(hcd, buf);
149
150 /* See if we have OCIC bit set on port 1 */
151 if (ocic_mask & (1 << 1)) {
152 dev_dbg(hcd->self.controller, "over-current indicator change "
153 "on port 1\n");
154
155 if (!length)
156 length = 1;
157
158 buf[0] |= 1 << 1;
159 }
160 return length;
161}
162
163/*
164 * Look at the control requests to the root hub and see if we need to override.
165 */
166static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
167 u16 wIndex, char *buf, u16 wLength)
168{
169 struct device *dev = hcd->self.controller;
Jingoo Hand4f09e22013-07-30 19:59:40 +0900170 struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400171 int temp;
172
173 switch (typeReq) {
174 case GetPortStatus:
175 /* Check the port number */
176 if (wIndex != 1)
177 break;
178
179 dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
180
181 temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
182
183 /* The port power status (PPS) bit defaults to 1 */
184 if (hub->get_power && hub->get_power(wIndex) == 0)
185 temp &= ~RH_PS_PPS;
186
187 /* The port over-current indicator (POCI) bit is always 0 */
188 if (hub->get_oci && hub->get_oci(wIndex) > 0)
189 temp |= RH_PS_POCI;
190
191 /* The over-current indicator change (OCIC) bit is 0 too */
192 if (ocic_mask & (1 << wIndex))
193 temp |= RH_PS_OCIC;
194
195 put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
196 return 0;
197 case SetPortFeature:
198 temp = 1;
199 goto check_port;
200 case ClearPortFeature:
201 temp = 0;
202
203check_port:
204 /* Check the port number */
205 if (wIndex != 1)
206 break;
207
208 switch (wValue) {
209 case USB_PORT_FEAT_POWER:
210 dev_dbg(dev, "%sPortFeature(%u): %s\n",
211 temp ? "Set" : "Clear", wIndex, "POWER");
212
213 if (!hub->set_power)
214 return -EPIPE;
215
216 return hub->set_power(wIndex, temp) ? -EPIPE : 0;
217 case USB_PORT_FEAT_C_OVER_CURRENT:
218 dev_dbg(dev, "%sPortFeature(%u): %s\n",
219 temp ? "Set" : "Clear", wIndex,
220 "C_OVER_CURRENT");
221
222 if (temp)
223 ocic_mask |= 1 << wIndex;
224 else
225 ocic_mask &= ~(1 << wIndex);
226 return 0;
227 }
228 }
229
230 return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
231}
232
233static const struct hc_driver ohci_da8xx_hc_driver = {
234 .description = hcd_name,
235 .product_desc = "DA8xx OHCI",
236 .hcd_priv_size = sizeof(struct ohci_hcd),
237
238 /*
239 * generic hardware linkage
240 */
241 .irq = ohci_irq,
242 .flags = HCD_USB11 | HCD_MEMORY,
243
244 /*
245 * basic lifecycle operations
246 */
247 .reset = ohci_da8xx_init,
248 .start = ohci_da8xx_start,
249 .stop = ohci_da8xx_stop,
250 .shutdown = ohci_shutdown,
251
252 /*
253 * managing i/o requests and associated device resources
254 */
255 .urb_enqueue = ohci_urb_enqueue,
256 .urb_dequeue = ohci_urb_dequeue,
257 .endpoint_disable = ohci_endpoint_disable,
258
259 /*
260 * scheduling support
261 */
262 .get_frame_number = ohci_get_frame,
263
264 /*
265 * root hub support
266 */
267 .hub_status_data = ohci_da8xx_hub_status_data,
268 .hub_control = ohci_da8xx_hub_control,
269
270#ifdef CONFIG_PM
271 .bus_suspend = ohci_bus_suspend,
272 .bus_resume = ohci_bus_resume,
273#endif
274 .start_port_reset = ohci_start_port_reset,
275};
276
277/*-------------------------------------------------------------------------*/
278
279
280/**
281 * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs
282 * Context: !in_interrupt()
283 *
284 * Allocates basic resources for this USB host controller, and
285 * then invokes the start() method for the HCD associated with it
286 * through the hotplug entry's driver_data.
287 */
288static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
289 struct platform_device *pdev)
290{
Jingoo Hand4f09e22013-07-30 19:59:40 +0900291 struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400292 struct usb_hcd *hcd;
293 struct resource *mem;
294 int error, irq;
295
296 if (hub == NULL)
297 return -ENODEV;
298
Jingoo Han644db162013-12-11 16:23:39 +0900299 usb11_clk = devm_clk_get(&pdev->dev, "usb11");
David Lechner6110c422016-10-12 20:44:46 -0500300 if (IS_ERR(usb11_clk)) {
301 if (PTR_ERR(usb11_clk) != -EPROBE_DEFER)
302 dev_err(&pdev->dev, "Failed to get clock.\n");
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400303 return PTR_ERR(usb11_clk);
David Lechner6110c422016-10-12 20:44:46 -0500304 }
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400305
David Lechner6110c422016-10-12 20:44:46 -0500306 usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
307 if (IS_ERR(usb11_phy)) {
308 if (PTR_ERR(usb11_phy) != -EPROBE_DEFER)
309 dev_err(&pdev->dev, "Failed to get phy.\n");
310 return PTR_ERR(usb11_phy);
311 }
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400312
313 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
Jingoo Han644db162013-12-11 16:23:39 +0900314 if (!hcd)
315 return -ENOMEM;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400316
317 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Jingoo Han644db162013-12-11 16:23:39 +0900318 hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
319 if (IS_ERR(hcd->regs)) {
320 error = PTR_ERR(hcd->regs);
David Lechner6110c422016-10-12 20:44:46 -0500321 dev_err(&pdev->dev, "failed to map ohci.\n");
Jingoo Han644db162013-12-11 16:23:39 +0900322 goto err;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400323 }
Varka Bhadram54891d72014-11-04 07:51:09 +0530324 hcd->rsrc_start = mem->start;
325 hcd->rsrc_len = resource_size(mem);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400326
327 ohci_hcd_init(hcd_to_ohci(hcd));
328
329 irq = platform_get_irq(pdev, 0);
330 if (irq < 0) {
331 error = -ENODEV;
Jingoo Han644db162013-12-11 16:23:39 +0900332 goto err;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400333 }
Yong Zhangb5dd18d2011-09-07 16:10:52 +0800334 error = usb_add_hcd(hcd, irq, 0);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400335 if (error)
Jingoo Han644db162013-12-11 16:23:39 +0900336 goto err;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400337
Peter Chen3c9740a2013-11-05 10:46:02 +0800338 device_wakeup_enable(hcd->self.controller);
339
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400340 if (hub->ocic_notify) {
341 error = hub->ocic_notify(ohci_da8xx_ocic_handler);
342 if (!error)
343 return 0;
344 }
345
346 usb_remove_hcd(hcd);
Jingoo Han644db162013-12-11 16:23:39 +0900347err:
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400348 usb_put_hcd(hcd);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400349 return error;
350}
351
352/**
353 * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs
354 * @dev: USB Host Controller being removed
355 * Context: !in_interrupt()
356 *
357 * Reverses the effect of usb_hcd_da8xx_probe(), first invoking
358 * the HCD's stop() method. It is always called from a thread
359 * context, normally "rmmod", "apmd", or something similar.
360 */
361static inline void
362usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev)
363{
Jingoo Hand4f09e22013-07-30 19:59:40 +0900364 struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400365
366 hub->ocic_notify(NULL);
367 usb_remove_hcd(hcd);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400368 usb_put_hcd(hcd);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400369}
370
371static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev)
372{
373 return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev);
374}
375
376static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev)
377{
378 struct usb_hcd *hcd = platform_get_drvdata(dev);
379
380 usb_hcd_da8xx_remove(hcd, dev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400381
382 return 0;
383}
384
385#ifdef CONFIG_PM
Majunath Goudar933bb1f2013-11-13 17:40:19 +0530386static int ohci_da8xx_suspend(struct platform_device *pdev,
387 pm_message_t message)
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400388{
Majunath Goudar933bb1f2013-11-13 17:40:19 +0530389 struct usb_hcd *hcd = platform_get_drvdata(pdev);
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400390 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
Majunath Goudar933bb1f2013-11-13 17:40:19 +0530391 bool do_wakeup = device_may_wakeup(&pdev->dev);
392 int ret;
393
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400394
395 if (time_before(jiffies, ohci->next_statechange))
396 msleep(5);
397 ohci->next_statechange = jiffies;
398
Majunath Goudar933bb1f2013-11-13 17:40:19 +0530399 ret = ohci_suspend(hcd, do_wakeup);
400 if (ret)
401 return ret;
402
David Lechner6110c422016-10-12 20:44:46 -0500403 ohci_da8xx_disable();
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400404 hcd->state = HC_STATE_SUSPENDED;
Majunath Goudar933bb1f2013-11-13 17:40:19 +0530405
406 return ret;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400407}
408
409static int ohci_da8xx_resume(struct platform_device *dev)
410{
411 struct usb_hcd *hcd = platform_get_drvdata(dev);
412 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
David Lechner6110c422016-10-12 20:44:46 -0500413 int ret;
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400414
415 if (time_before(jiffies, ohci->next_statechange))
416 msleep(5);
417 ohci->next_statechange = jiffies;
418
David Lechner6110c422016-10-12 20:44:46 -0500419 ret = ohci_da8xx_enable();
420 if (ret)
421 return ret;
422
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400423 dev->dev.power.power_state = PMSG_ON;
424 usb_hcd_resume_root_hub(hcd);
David Lechner6110c422016-10-12 20:44:46 -0500425
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400426 return 0;
427}
428#endif
429
430/*
431 * Driver definition to register with platform structure.
432 */
433static struct platform_driver ohci_hcd_da8xx_driver = {
434 .probe = ohci_hcd_da8xx_drv_probe,
435 .remove = ohci_hcd_da8xx_drv_remove,
436 .shutdown = usb_hcd_platform_shutdown,
437#ifdef CONFIG_PM
438 .suspend = ohci_da8xx_suspend,
439 .resume = ohci_da8xx_resume,
440#endif
441 .driver = {
Sergei Shtylyovefe7daf2010-02-12 23:52:34 +0400442 .name = "ohci",
443 },
444};
Jan Luebbeab59ac012012-05-07 10:25:16 +0200445
446MODULE_ALIAS("platform:ohci");