]>
Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-2.0 |
efe7daf2 SS |
2 | /* |
3 | * OHCI HCD (Host Controller Driver) for USB. | |
4 | * | |
5 | * TI DA8xx (OMAP-L1x) Bus Glue | |
6 | * | |
7 | * Derived from: ohci-omap.c and ohci-s3c2410.c | |
8 | * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> | |
efe7daf2 SS |
9 | */ |
10 | ||
6c21caa3 | 11 | #include <linux/clk.h> |
d193abf1 | 12 | #include <linux/gpio/consumer.h> |
6c21caa3 | 13 | #include <linux/io.h> |
efe7daf2 SS |
14 | #include <linux/interrupt.h> |
15 | #include <linux/jiffies.h> | |
6c21caa3 MG |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | |
efe7daf2 | 18 | #include <linux/platform_device.h> |
6110c425 | 19 | #include <linux/phy/phy.h> |
ec2a0833 | 20 | #include <linux/platform_data/usb-davinci.h> |
c844ff74 | 21 | #include <linux/regulator/consumer.h> |
6c21caa3 MG |
22 | #include <linux/usb.h> |
23 | #include <linux/usb/hcd.h> | |
24 | #include <asm/unaligned.h> | |
efe7daf2 | 25 | |
6c21caa3 MG |
26 | #include "ohci.h" |
27 | ||
28 | #define DRIVER_DESC "DA8XX" | |
eacae5d2 | 29 | #define DRV_NAME "ohci-da8xx" |
6c21caa3 MG |
30 | |
31 | static struct hc_driver __read_mostly ohci_da8xx_hc_driver; | |
32 | ||
33 | static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, | |
34 | u16 wValue, u16 wIndex, char *buf, u16 wLength); | |
35 | static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); | |
efe7daf2 | 36 | |
c7a4f9f3 | 37 | struct da8xx_ohci_hcd { |
c844ff74 | 38 | struct usb_hcd *hcd; |
c7a4f9f3 AH |
39 | struct clk *usb11_clk; |
40 | struct phy *usb11_phy; | |
c844ff74 AH |
41 | struct regulator *vbus_reg; |
42 | struct notifier_block nb; | |
d193abf1 | 43 | struct gpio_desc *oc_gpio; |
c7a4f9f3 AH |
44 | }; |
45 | ||
46 | #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) | |
efe7daf2 SS |
47 | |
48 | /* Over-current indicator change bitmask */ | |
49 | static volatile u16 ocic_mask; | |
50 | ||
c7a4f9f3 | 51 | static int ohci_da8xx_enable(struct usb_hcd *hcd) |
efe7daf2 | 52 | { |
c7a4f9f3 | 53 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
6110c425 | 54 | int ret; |
efe7daf2 | 55 | |
c7a4f9f3 | 56 | ret = clk_prepare_enable(da8xx_ohci->usb11_clk); |
6110c425 DL |
57 | if (ret) |
58 | return ret; | |
efe7daf2 | 59 | |
c7a4f9f3 | 60 | ret = phy_init(da8xx_ohci->usb11_phy); |
6110c425 DL |
61 | if (ret) |
62 | goto err_phy_init; | |
63 | ||
c7a4f9f3 | 64 | ret = phy_power_on(da8xx_ohci->usb11_phy); |
6110c425 DL |
65 | if (ret) |
66 | goto err_phy_power_on; | |
67 | ||
68 | return 0; | |
69 | ||
70 | err_phy_power_on: | |
c7a4f9f3 | 71 | phy_exit(da8xx_ohci->usb11_phy); |
6110c425 | 72 | err_phy_init: |
c7a4f9f3 | 73 | clk_disable_unprepare(da8xx_ohci->usb11_clk); |
6110c425 DL |
74 | |
75 | return ret; | |
76 | } | |
77 | ||
c7a4f9f3 | 78 | static void ohci_da8xx_disable(struct usb_hcd *hcd) |
6110c425 | 79 | { |
c7a4f9f3 AH |
80 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
81 | ||
82 | phy_power_off(da8xx_ohci->usb11_phy); | |
83 | phy_exit(da8xx_ohci->usb11_phy); | |
84 | clk_disable_unprepare(da8xx_ohci->usb11_clk); | |
efe7daf2 SS |
85 | } |
86 | ||
f3c56fb3 AH |
87 | static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) |
88 | { | |
c844ff74 | 89 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
d193abf1 | 90 | struct device *dev = hcd->self.controller; |
c844ff74 | 91 | int ret; |
f3c56fb3 | 92 | |
c844ff74 AH |
93 | if (!da8xx_ohci->vbus_reg) |
94 | return 0; | |
95 | ||
8e2f5eae | 96 | if (on) { |
c844ff74 AH |
97 | ret = regulator_enable(da8xx_ohci->vbus_reg); |
98 | if (ret) { | |
99 | dev_err(dev, "Failed to enable regulator: %d\n", ret); | |
100 | return ret; | |
101 | } | |
8e2f5eae | 102 | } else { |
c844ff74 AH |
103 | ret = regulator_disable(da8xx_ohci->vbus_reg); |
104 | if (ret) { | |
105 | dev_err(dev, "Failed to disable regulator: %d\n", ret); | |
106 | return ret; | |
107 | } | |
c844ff74 AH |
108 | } |
109 | ||
f3c56fb3 AH |
110 | return 0; |
111 | } | |
112 | ||
113 | static int ohci_da8xx_get_power(struct usb_hcd *hcd) | |
114 | { | |
c844ff74 | 115 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
f3c56fb3 | 116 | |
c844ff74 AH |
117 | if (da8xx_ohci->vbus_reg) |
118 | return regulator_is_enabled(da8xx_ohci->vbus_reg); | |
119 | ||
f3c56fb3 AH |
120 | return 1; |
121 | } | |
122 | ||
123 | static int ohci_da8xx_get_oci(struct usb_hcd *hcd) | |
124 | { | |
c844ff74 | 125 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
c844ff74 AH |
126 | unsigned int flags; |
127 | int ret; | |
f3c56fb3 | 128 | |
d193abf1 BG |
129 | if (da8xx_ohci->oc_gpio) |
130 | return gpiod_get_value_cansleep(da8xx_ohci->oc_gpio); | |
f3c56fb3 | 131 | |
c844ff74 AH |
132 | if (!da8xx_ohci->vbus_reg) |
133 | return 0; | |
134 | ||
135 | ret = regulator_get_error_flags(da8xx_ohci->vbus_reg, &flags); | |
136 | if (ret) | |
137 | return ret; | |
138 | ||
139 | if (flags & REGULATOR_ERROR_OVER_CURRENT) | |
140 | return 1; | |
141 | ||
f3c56fb3 AH |
142 | return 0; |
143 | } | |
144 | ||
145 | static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) | |
146 | { | |
c844ff74 | 147 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
f3c56fb3 | 148 | |
c844ff74 AH |
149 | if (da8xx_ohci->vbus_reg) |
150 | return 1; | |
151 | ||
f3c56fb3 AH |
152 | return 0; |
153 | } | |
154 | ||
155 | static int ohci_da8xx_has_oci(struct usb_hcd *hcd) | |
156 | { | |
c844ff74 | 157 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
f3c56fb3 | 158 | |
d193abf1 | 159 | if (da8xx_ohci->oc_gpio) |
f3c56fb3 AH |
160 | return 1; |
161 | ||
c844ff74 AH |
162 | if (da8xx_ohci->vbus_reg) |
163 | return 1; | |
164 | ||
f3c56fb3 AH |
165 | return 0; |
166 | } | |
167 | ||
168 | static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) | |
169 | { | |
170 | struct device *dev = hcd->self.controller; | |
171 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | |
172 | ||
173 | if (hub && hub->potpgt) | |
174 | return 1; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
c844ff74 AH |
179 | static int ohci_da8xx_regulator_event(struct notifier_block *nb, |
180 | unsigned long event, void *data) | |
181 | { | |
182 | struct da8xx_ohci_hcd *da8xx_ohci = | |
183 | container_of(nb, struct da8xx_ohci_hcd, nb); | |
184 | ||
185 | if (event & REGULATOR_EVENT_OVER_CURRENT) { | |
186 | ocic_mask |= 1 << 1; | |
187 | ohci_da8xx_set_power(da8xx_ohci->hcd, 0); | |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
d3273301 | 193 | static irqreturn_t ohci_da8xx_oc_thread(int irq, void *data) |
d193abf1 BG |
194 | { |
195 | struct da8xx_ohci_hcd *da8xx_ohci = data; | |
d3273301 BG |
196 | struct device *dev = da8xx_ohci->hcd->self.controller; |
197 | int ret; | |
d193abf1 | 198 | |
512de1ce BG |
199 | if (gpiod_get_value_cansleep(da8xx_ohci->oc_gpio) && |
200 | da8xx_ohci->vbus_reg) { | |
201 | ret = regulator_disable(da8xx_ohci->vbus_reg); | |
202 | if (ret) | |
203 | dev_err(dev, "Failed to disable regulator: %d\n", ret); | |
d3273301 | 204 | } |
d193abf1 BG |
205 | |
206 | return IRQ_HANDLED; | |
207 | } | |
208 | ||
f3c56fb3 AH |
209 | static int ohci_da8xx_register_notify(struct usb_hcd *hcd) |
210 | { | |
c844ff74 | 211 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
f3c56fb3 | 212 | struct device *dev = hcd->self.controller; |
c844ff74 AH |
213 | int ret = 0; |
214 | ||
d193abf1 | 215 | if (!da8xx_ohci->oc_gpio && da8xx_ohci->vbus_reg) { |
c844ff74 AH |
216 | da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event; |
217 | ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg, | |
218 | &da8xx_ohci->nb); | |
219 | } | |
f3c56fb3 | 220 | |
c844ff74 AH |
221 | if (ret) |
222 | dev_err(dev, "Failed to register notifier: %d\n", ret); | |
f3c56fb3 | 223 | |
c844ff74 | 224 | return ret; |
f3c56fb3 AH |
225 | } |
226 | ||
6c21caa3 | 227 | static int ohci_da8xx_reset(struct usb_hcd *hcd) |
efe7daf2 SS |
228 | { |
229 | struct device *dev = hcd->self.controller; | |
d4f09e28 | 230 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); |
efe7daf2 SS |
231 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
232 | int result; | |
233 | u32 rh_a; | |
234 | ||
235 | dev_dbg(dev, "starting USB controller\n"); | |
236 | ||
c7a4f9f3 | 237 | result = ohci_da8xx_enable(hcd); |
6110c425 DL |
238 | if (result < 0) |
239 | return result; | |
efe7daf2 SS |
240 | |
241 | /* | |
242 | * DA8xx only have 1 port connected to the pins but the HC root hub | |
243 | * register A reports 2 ports, thus we'll have to override it... | |
244 | */ | |
245 | ohci->num_ports = 1; | |
246 | ||
6c21caa3 | 247 | result = ohci_setup(hcd); |
6110c425 | 248 | if (result < 0) { |
c7a4f9f3 | 249 | ohci_da8xx_disable(hcd); |
efe7daf2 | 250 | return result; |
6110c425 | 251 | } |
efe7daf2 SS |
252 | |
253 | /* | |
254 | * Since we're providing a board-specific root hub port power control | |
255 | * and over-current reporting, we have to override the HC root hub A | |
256 | * register's default value, so that ohci_hub_control() could return | |
257 | * the correct hub descriptor... | |
258 | */ | |
259 | rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); | |
f3c56fb3 | 260 | if (ohci_da8xx_has_set_power(hcd)) { |
efe7daf2 SS |
261 | rh_a &= ~RH_A_NPS; |
262 | rh_a |= RH_A_PSM; | |
263 | } | |
f3c56fb3 | 264 | if (ohci_da8xx_has_oci(hcd)) { |
efe7daf2 SS |
265 | rh_a &= ~RH_A_NOCP; |
266 | rh_a |= RH_A_OCPM; | |
267 | } | |
f3c56fb3 AH |
268 | if (ohci_da8xx_has_potpgt(hcd)) { |
269 | rh_a &= ~RH_A_POTPGT; | |
270 | rh_a |= hub->potpgt << 24; | |
271 | } | |
efe7daf2 SS |
272 | ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); |
273 | ||
274 | return result; | |
275 | } | |
276 | ||
efe7daf2 SS |
277 | /* |
278 | * Update the status data from the hub with the over-current indicator change. | |
279 | */ | |
280 | static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) | |
281 | { | |
6c21caa3 | 282 | int length = orig_ohci_hub_status_data(hcd, buf); |
efe7daf2 SS |
283 | |
284 | /* See if we have OCIC bit set on port 1 */ | |
285 | if (ocic_mask & (1 << 1)) { | |
286 | dev_dbg(hcd->self.controller, "over-current indicator change " | |
287 | "on port 1\n"); | |
288 | ||
289 | if (!length) | |
290 | length = 1; | |
291 | ||
292 | buf[0] |= 1 << 1; | |
293 | } | |
294 | return length; | |
295 | } | |
296 | ||
297 | /* | |
298 | * Look at the control requests to the root hub and see if we need to override. | |
299 | */ | |
300 | static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |
301 | u16 wIndex, char *buf, u16 wLength) | |
302 | { | |
303 | struct device *dev = hcd->self.controller; | |
efe7daf2 SS |
304 | int temp; |
305 | ||
306 | switch (typeReq) { | |
307 | case GetPortStatus: | |
308 | /* Check the port number */ | |
309 | if (wIndex != 1) | |
310 | break; | |
311 | ||
312 | dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); | |
313 | ||
314 | temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); | |
315 | ||
316 | /* The port power status (PPS) bit defaults to 1 */ | |
f3c56fb3 | 317 | if (!ohci_da8xx_get_power(hcd)) |
efe7daf2 SS |
318 | temp &= ~RH_PS_PPS; |
319 | ||
320 | /* The port over-current indicator (POCI) bit is always 0 */ | |
f3c56fb3 | 321 | if (ohci_da8xx_get_oci(hcd) > 0) |
efe7daf2 SS |
322 | temp |= RH_PS_POCI; |
323 | ||
324 | /* The over-current indicator change (OCIC) bit is 0 too */ | |
325 | if (ocic_mask & (1 << wIndex)) | |
326 | temp |= RH_PS_OCIC; | |
327 | ||
328 | put_unaligned(cpu_to_le32(temp), (__le32 *)buf); | |
329 | return 0; | |
330 | case SetPortFeature: | |
331 | temp = 1; | |
332 | goto check_port; | |
333 | case ClearPortFeature: | |
334 | temp = 0; | |
335 | ||
336 | check_port: | |
337 | /* Check the port number */ | |
338 | if (wIndex != 1) | |
339 | break; | |
340 | ||
341 | switch (wValue) { | |
342 | case USB_PORT_FEAT_POWER: | |
343 | dev_dbg(dev, "%sPortFeature(%u): %s\n", | |
344 | temp ? "Set" : "Clear", wIndex, "POWER"); | |
345 | ||
f3c56fb3 | 346 | return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0; |
efe7daf2 SS |
347 | case USB_PORT_FEAT_C_OVER_CURRENT: |
348 | dev_dbg(dev, "%sPortFeature(%u): %s\n", | |
349 | temp ? "Set" : "Clear", wIndex, | |
350 | "C_OVER_CURRENT"); | |
351 | ||
352 | if (temp) | |
353 | ocic_mask |= 1 << wIndex; | |
354 | else | |
355 | ocic_mask &= ~(1 << wIndex); | |
356 | return 0; | |
357 | } | |
358 | } | |
359 | ||
6c21caa3 MG |
360 | return orig_ohci_hub_control(hcd, typeReq, wValue, |
361 | wIndex, buf, wLength); | |
efe7daf2 SS |
362 | } |
363 | ||
efe7daf2 | 364 | /*-------------------------------------------------------------------------*/ |
190534f6 AH |
365 | #ifdef CONFIG_OF |
366 | static const struct of_device_id da8xx_ohci_ids[] = { | |
367 | { .compatible = "ti,da830-ohci" }, | |
368 | { } | |
369 | }; | |
370 | MODULE_DEVICE_TABLE(of, da8xx_ohci_ids); | |
371 | #endif | |
efe7daf2 | 372 | |
6c21caa3 | 373 | static int ohci_da8xx_probe(struct platform_device *pdev) |
efe7daf2 | 374 | { |
c7a4f9f3 | 375 | struct da8xx_ohci_hcd *da8xx_ohci; |
3d2ab9f3 | 376 | struct device *dev = &pdev->dev; |
d193abf1 | 377 | int error, hcd_irq, oc_irq; |
efe7daf2 SS |
378 | struct usb_hcd *hcd; |
379 | struct resource *mem; | |
08e46f18 | 380 | |
3d2ab9f3 | 381 | hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev)); |
c7a4f9f3 AH |
382 | if (!hcd) |
383 | return -ENOMEM; | |
384 | ||
385 | da8xx_ohci = to_da8xx_ohci(hcd); | |
c844ff74 | 386 | da8xx_ohci->hcd = hcd; |
c7a4f9f3 | 387 | |
3d2ab9f3 | 388 | da8xx_ohci->usb11_clk = devm_clk_get(dev, NULL); |
c7a4f9f3 AH |
389 | if (IS_ERR(da8xx_ohci->usb11_clk)) { |
390 | error = PTR_ERR(da8xx_ohci->usb11_clk); | |
391 | if (error != -EPROBE_DEFER) | |
3d2ab9f3 | 392 | dev_err(dev, "Failed to get clock.\n"); |
c7a4f9f3 | 393 | goto err; |
6110c425 | 394 | } |
efe7daf2 | 395 | |
3d2ab9f3 | 396 | da8xx_ohci->usb11_phy = devm_phy_get(dev, "usb-phy"); |
c7a4f9f3 AH |
397 | if (IS_ERR(da8xx_ohci->usb11_phy)) { |
398 | error = PTR_ERR(da8xx_ohci->usb11_phy); | |
399 | if (error != -EPROBE_DEFER) | |
3d2ab9f3 | 400 | dev_err(dev, "Failed to get phy.\n"); |
c7a4f9f3 | 401 | goto err; |
6110c425 | 402 | } |
efe7daf2 | 403 | |
3d2ab9f3 | 404 | da8xx_ohci->vbus_reg = devm_regulator_get_optional(dev, "vbus"); |
c844ff74 AH |
405 | if (IS_ERR(da8xx_ohci->vbus_reg)) { |
406 | error = PTR_ERR(da8xx_ohci->vbus_reg); | |
407 | if (error == -ENODEV) { | |
408 | da8xx_ohci->vbus_reg = NULL; | |
409 | } else if (error == -EPROBE_DEFER) { | |
410 | goto err; | |
411 | } else { | |
3d2ab9f3 | 412 | dev_err(dev, "Failed to get regulator\n"); |
c844ff74 AH |
413 | goto err; |
414 | } | |
415 | } | |
416 | ||
d193abf1 BG |
417 | da8xx_ohci->oc_gpio = devm_gpiod_get_optional(dev, "oc", GPIOD_IN); |
418 | if (IS_ERR(da8xx_ohci->oc_gpio)) | |
419 | goto err; | |
420 | ||
421 | if (da8xx_ohci->oc_gpio) { | |
422 | oc_irq = gpiod_to_irq(da8xx_ohci->oc_gpio); | |
423 | if (oc_irq < 0) | |
424 | goto err; | |
425 | ||
d3273301 BG |
426 | error = devm_request_threaded_irq(dev, oc_irq, NULL, |
427 | ohci_da8xx_oc_thread, IRQF_TRIGGER_RISING | | |
428 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
d193abf1 BG |
429 | "OHCI over-current indicator", da8xx_ohci); |
430 | if (error) | |
431 | goto err; | |
432 | } | |
433 | ||
efe7daf2 | 434 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
3d2ab9f3 | 435 | hcd->regs = devm_ioremap_resource(dev, mem); |
644db166 JH |
436 | if (IS_ERR(hcd->regs)) { |
437 | error = PTR_ERR(hcd->regs); | |
438 | goto err; | |
efe7daf2 | 439 | } |
54891d74 VB |
440 | hcd->rsrc_start = mem->start; |
441 | hcd->rsrc_len = resource_size(mem); | |
efe7daf2 | 442 | |
d193abf1 BG |
443 | hcd_irq = platform_get_irq(pdev, 0); |
444 | if (hcd_irq < 0) { | |
efe7daf2 | 445 | error = -ENODEV; |
644db166 | 446 | goto err; |
efe7daf2 | 447 | } |
6c21caa3 | 448 | |
d193abf1 | 449 | error = usb_add_hcd(hcd, hcd_irq, 0); |
efe7daf2 | 450 | if (error) |
644db166 | 451 | goto err; |
efe7daf2 | 452 | |
3c9740a1 PC |
453 | device_wakeup_enable(hcd->self.controller); |
454 | ||
f3c56fb3 AH |
455 | error = ohci_da8xx_register_notify(hcd); |
456 | if (error) | |
457 | goto err_remove_hcd; | |
458 | ||
459 | return 0; | |
efe7daf2 | 460 | |
f3c56fb3 | 461 | err_remove_hcd: |
efe7daf2 | 462 | usb_remove_hcd(hcd); |
644db166 | 463 | err: |
efe7daf2 | 464 | usb_put_hcd(hcd); |
efe7daf2 SS |
465 | return error; |
466 | } | |
467 | ||
6c21caa3 | 468 | static int ohci_da8xx_remove(struct platform_device *pdev) |
efe7daf2 | 469 | { |
6c21caa3 | 470 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
efe7daf2 | 471 | |
efe7daf2 | 472 | usb_remove_hcd(hcd); |
efe7daf2 | 473 | usb_put_hcd(hcd); |
efe7daf2 SS |
474 | |
475 | return 0; | |
476 | } | |
477 | ||
478 | #ifdef CONFIG_PM | |
933bb1f0 MG |
479 | static int ohci_da8xx_suspend(struct platform_device *pdev, |
480 | pm_message_t message) | |
efe7daf2 | 481 | { |
933bb1f0 | 482 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
efe7daf2 | 483 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
933bb1f0 MG |
484 | bool do_wakeup = device_may_wakeup(&pdev->dev); |
485 | int ret; | |
486 | ||
efe7daf2 SS |
487 | |
488 | if (time_before(jiffies, ohci->next_statechange)) | |
489 | msleep(5); | |
490 | ohci->next_statechange = jiffies; | |
491 | ||
933bb1f0 MG |
492 | ret = ohci_suspend(hcd, do_wakeup); |
493 | if (ret) | |
494 | return ret; | |
495 | ||
c7a4f9f3 | 496 | ohci_da8xx_disable(hcd); |
efe7daf2 | 497 | hcd->state = HC_STATE_SUSPENDED; |
933bb1f0 MG |
498 | |
499 | return ret; | |
efe7daf2 SS |
500 | } |
501 | ||
502 | static int ohci_da8xx_resume(struct platform_device *dev) | |
503 | { | |
504 | struct usb_hcd *hcd = platform_get_drvdata(dev); | |
505 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | |
6110c425 | 506 | int ret; |
efe7daf2 SS |
507 | |
508 | if (time_before(jiffies, ohci->next_statechange)) | |
509 | msleep(5); | |
510 | ohci->next_statechange = jiffies; | |
511 | ||
c7a4f9f3 | 512 | ret = ohci_da8xx_enable(hcd); |
6110c425 DL |
513 | if (ret) |
514 | return ret; | |
515 | ||
640308b7 | 516 | ohci_resume(hcd, false); |
6110c425 | 517 | |
efe7daf2 SS |
518 | return 0; |
519 | } | |
520 | #endif | |
521 | ||
6c21caa3 | 522 | static const struct ohci_driver_overrides da8xx_overrides __initconst = { |
c7a4f9f3 AH |
523 | .reset = ohci_da8xx_reset, |
524 | .extra_priv_size = sizeof(struct da8xx_ohci_hcd), | |
6c21caa3 MG |
525 | }; |
526 | ||
efe7daf2 SS |
527 | /* |
528 | * Driver definition to register with platform structure. | |
529 | */ | |
530 | static struct platform_driver ohci_hcd_da8xx_driver = { | |
6c21caa3 MG |
531 | .probe = ohci_da8xx_probe, |
532 | .remove = ohci_da8xx_remove, | |
efe7daf2 SS |
533 | .shutdown = usb_hcd_platform_shutdown, |
534 | #ifdef CONFIG_PM | |
535 | .suspend = ohci_da8xx_suspend, | |
536 | .resume = ohci_da8xx_resume, | |
537 | #endif | |
538 | .driver = { | |
6c21caa3 | 539 | .name = DRV_NAME, |
190534f6 | 540 | .of_match_table = of_match_ptr(da8xx_ohci_ids), |
efe7daf2 SS |
541 | }, |
542 | }; | |
ab59ac01 | 543 | |
6c21caa3 MG |
544 | static int __init ohci_da8xx_init(void) |
545 | { | |
546 | ||
547 | if (usb_disabled()) | |
548 | return -ENODEV; | |
549 | ||
550 | pr_info("%s: " DRIVER_DESC "\n", DRV_NAME); | |
551 | ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides); | |
552 | ||
553 | /* | |
554 | * The Davinci da8xx HW has some unusual quirks, which require | |
555 | * da8xx-specific workarounds. We override certain hc_driver | |
556 | * functions here to achieve that. We explicitly do not enhance | |
557 | * ohci_driver_overrides to allow this more easily, since this | |
558 | * is an unusual case, and we don't want to encourage others to | |
559 | * override these functions by making it too easy. | |
560 | */ | |
561 | ||
562 | orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control; | |
563 | orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data; | |
564 | ||
565 | ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data; | |
566 | ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control; | |
567 | ||
568 | return platform_driver_register(&ohci_hcd_da8xx_driver); | |
569 | } | |
570 | module_init(ohci_da8xx_init); | |
571 | ||
572 | static void __exit ohci_da8xx_exit(void) | |
573 | { | |
574 | platform_driver_unregister(&ohci_hcd_da8xx_driver); | |
575 | } | |
576 | module_exit(ohci_da8xx_exit); | |
577 | MODULE_DESCRIPTION(DRIVER_DESC); | |
578 | MODULE_LICENSE("GPL"); | |
579 | MODULE_ALIAS("platform:" DRV_NAME); |