diff options
author | Manjunath Goudar <manjunath.goudar@linaro.org> | 2013-09-21 07:08:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-09-26 14:35:02 -0400 |
commit | e3825b48e2cc8014b3088f8bff1c5f35652f298d (patch) | |
tree | c0519457e480d2e27f06ad9d1d3b7f084a4a4a3a /drivers/usb/host/ohci-at91.c | |
parent | 1cc6ac59ffaa164c12003c5c3ce9590b0cba3b50 (diff) |
USB: OHCI: make ohci-at91 a separate driver
Separate the TI OHCI Atmel host controller driver from ohci-hcd
host code so that it can be built as a separate driver module.
This work is part of enabling multi-platform kernels on ARM.
Signed-off-by: Manjunath Goudar <manjunath.goudar@linaro.org>
Signed-off-by: Deepak Saxena <dsaxena@linaro.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ohci-at91.c')
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 156 |
1 files changed, 70 insertions, 86 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index caa3764a3407..476b5a5baf25 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c | |||
@@ -13,19 +13,24 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/dma-mapping.h> |
17 | #include <linux/of_platform.h> | 17 | #include <linux/of_platform.h> |
18 | #include <linux/of_gpio.h> | 18 | #include <linux/of_gpio.h> |
19 | #include <linux/platform_device.h> | ||
19 | #include <linux/platform_data/atmel.h> | 20 | #include <linux/platform_data/atmel.h> |
21 | #include <linux/io.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/usb.h> | ||
25 | #include <linux/usb/hcd.h> | ||
20 | 26 | ||
21 | #include <mach/hardware.h> | 27 | #include <mach/hardware.h> |
22 | #include <asm/gpio.h> | 28 | #include <asm/gpio.h> |
23 | 29 | ||
24 | #include <mach/cpu.h> | 30 | #include <mach/cpu.h> |
25 | 31 | ||
26 | #ifndef CONFIG_ARCH_AT91 | 32 | |
27 | #error "CONFIG_ARCH_AT91 must be defined." | 33 | #include "ohci.h" |
28 | #endif | ||
29 | 34 | ||
30 | #define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS) | 35 | #define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS) |
31 | #define at91_for_each_port(index) \ | 36 | #define at91_for_each_port(index) \ |
@@ -33,7 +38,17 @@ | |||
33 | 38 | ||
34 | /* interface, function and usb clocks; sometimes also an AHB clock */ | 39 | /* interface, function and usb clocks; sometimes also an AHB clock */ |
35 | static struct clk *iclk, *fclk, *uclk, *hclk; | 40 | static struct clk *iclk, *fclk, *uclk, *hclk; |
41 | /* interface and function clocks; sometimes also an AHB clock */ | ||
42 | |||
43 | #define DRIVER_DESC "OHCI Atmel driver" | ||
44 | |||
45 | static const char hcd_name[] = "ohci-atmel"; | ||
46 | |||
47 | static struct hc_driver __read_mostly ohci_at91_hc_driver; | ||
36 | static int clocked; | 48 | static int clocked; |
49 | static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, | ||
50 | u16 wValue, u16 wIndex, char *buf, u16 wLength); | ||
51 | static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); | ||
37 | 52 | ||
38 | extern int usb_disabled(void); | 53 | extern int usb_disabled(void); |
39 | 54 | ||
@@ -117,6 +132,8 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); | |||
117 | static int usb_hcd_at91_probe(const struct hc_driver *driver, | 132 | static int usb_hcd_at91_probe(const struct hc_driver *driver, |
118 | struct platform_device *pdev) | 133 | struct platform_device *pdev) |
119 | { | 134 | { |
135 | struct at91_usbh_data *board; | ||
136 | struct ohci_hcd *ohci; | ||
120 | int retval; | 137 | int retval; |
121 | struct usb_hcd *hcd = NULL; | 138 | struct usb_hcd *hcd = NULL; |
122 | 139 | ||
@@ -177,8 +194,10 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, | |||
177 | } | 194 | } |
178 | } | 195 | } |
179 | 196 | ||
197 | board = hcd->self.controller->platform_data; | ||
198 | ohci = hcd_to_ohci(hcd); | ||
199 | ohci->num_ports = board->ports; | ||
180 | at91_start_hc(pdev); | 200 | at91_start_hc(pdev); |
181 | ohci_hcd_init(hcd_to_ohci(hcd)); | ||
182 | 201 | ||
183 | retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); | 202 | retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); |
184 | if (retval == 0) | 203 | if (retval == 0) |
@@ -238,36 +257,6 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, | |||
238 | } | 257 | } |
239 | 258 | ||
240 | /*-------------------------------------------------------------------------*/ | 259 | /*-------------------------------------------------------------------------*/ |
241 | |||
242 | static int | ||
243 | ohci_at91_reset (struct usb_hcd *hcd) | ||
244 | { | ||
245 | struct at91_usbh_data *board = dev_get_platdata(hcd->self.controller); | ||
246 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
247 | int ret; | ||
248 | |||
249 | if ((ret = ohci_init(ohci)) < 0) | ||
250 | return ret; | ||
251 | |||
252 | ohci->num_ports = board->ports; | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int | ||
257 | ohci_at91_start (struct usb_hcd *hcd) | ||
258 | { | ||
259 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
260 | int ret; | ||
261 | |||
262 | if ((ret = ohci_run(ohci)) < 0) { | ||
263 | dev_err(hcd->self.controller, "can't start %s\n", | ||
264 | hcd->self.bus_name); | ||
265 | ohci_stop(hcd); | ||
266 | return ret; | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) | 260 | static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) |
272 | { | 261 | { |
273 | if (!valid_port(port)) | 262 | if (!valid_port(port)) |
@@ -297,8 +286,8 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) | |||
297 | */ | 286 | */ |
298 | static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) | 287 | static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) |
299 | { | 288 | { |
300 | struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); | 289 | struct at91_usbh_data *pdata = hcd->self.controller->platform_data; |
301 | int length = ohci_hub_status_data(hcd, buf); | 290 | int length = orig_ohci_hub_status_data(hcd, buf); |
302 | int port; | 291 | int port; |
303 | 292 | ||
304 | at91_for_each_port(port) { | 293 | at91_for_each_port(port) { |
@@ -376,7 +365,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
376 | break; | 365 | break; |
377 | } | 366 | } |
378 | 367 | ||
379 | ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength); | 368 | ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, |
369 | buf, wLength); | ||
380 | if (ret) | 370 | if (ret) |
381 | goto out; | 371 | goto out; |
382 | 372 | ||
@@ -430,51 +420,6 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
430 | 420 | ||
431 | /*-------------------------------------------------------------------------*/ | 421 | /*-------------------------------------------------------------------------*/ |
432 | 422 | ||
433 | static const struct hc_driver ohci_at91_hc_driver = { | ||
434 | .description = hcd_name, | ||
435 | .product_desc = "AT91 OHCI", | ||
436 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
437 | |||
438 | /* | ||
439 | * generic hardware linkage | ||
440 | */ | ||
441 | .irq = ohci_irq, | ||
442 | .flags = HCD_USB11 | HCD_MEMORY, | ||
443 | |||
444 | /* | ||
445 | * basic lifecycle operations | ||
446 | */ | ||
447 | .reset = ohci_at91_reset, | ||
448 | .start = ohci_at91_start, | ||
449 | .stop = ohci_stop, | ||
450 | .shutdown = ohci_shutdown, | ||
451 | |||
452 | /* | ||
453 | * managing i/o requests and associated device resources | ||
454 | */ | ||
455 | .urb_enqueue = ohci_urb_enqueue, | ||
456 | .urb_dequeue = ohci_urb_dequeue, | ||
457 | .endpoint_disable = ohci_endpoint_disable, | ||
458 | |||
459 | /* | ||
460 | * scheduling support | ||
461 | */ | ||
462 | .get_frame_number = ohci_get_frame, | ||
463 | |||
464 | /* | ||
465 | * root hub support | ||
466 | */ | ||
467 | .hub_status_data = ohci_at91_hub_status_data, | ||
468 | .hub_control = ohci_at91_hub_control, | ||
469 | #ifdef CONFIG_PM | ||
470 | .bus_suspend = ohci_bus_suspend, | ||
471 | .bus_resume = ohci_bus_resume, | ||
472 | #endif | ||
473 | .start_port_reset = ohci_start_port_reset, | ||
474 | }; | ||
475 | |||
476 | /*-------------------------------------------------------------------------*/ | ||
477 | |||
478 | static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) | 423 | static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) |
479 | { | 424 | { |
480 | struct platform_device *pdev = data; | 425 | struct platform_device *pdev = data; |
@@ -703,7 +648,11 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) | |||
703 | * REVISIT: some boards will be able to turn VBUS off... | 648 | * REVISIT: some boards will be able to turn VBUS off... |
704 | */ | 649 | */ |
705 | if (at91_suspend_entering_slow_clock()) { | 650 | if (at91_suspend_entering_slow_clock()) { |
706 | ohci_usb_reset (ohci); | 651 | ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); |
652 | ohci->hc_control &= OHCI_CTRL_RWC; | ||
653 | ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); | ||
654 | ohci->rh_state = OHCI_RH_HALTED; | ||
655 | |||
707 | /* flush the writes */ | 656 | /* flush the writes */ |
708 | (void) ohci_readl (ohci, &ohci->regs->control); | 657 | (void) ohci_readl (ohci, &ohci->regs->control); |
709 | at91_stop_clock(); | 658 | at91_stop_clock(); |
@@ -730,8 +679,6 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) | |||
730 | #define ohci_hcd_at91_drv_resume NULL | 679 | #define ohci_hcd_at91_drv_resume NULL |
731 | #endif | 680 | #endif |
732 | 681 | ||
733 | MODULE_ALIAS("platform:at91_ohci"); | ||
734 | |||
735 | static struct platform_driver ohci_hcd_at91_driver = { | 682 | static struct platform_driver ohci_hcd_at91_driver = { |
736 | .probe = ohci_hcd_at91_drv_probe, | 683 | .probe = ohci_hcd_at91_drv_probe, |
737 | .remove = ohci_hcd_at91_drv_remove, | 684 | .remove = ohci_hcd_at91_drv_remove, |
@@ -744,3 +691,40 @@ static struct platform_driver ohci_hcd_at91_driver = { | |||
744 | .of_match_table = of_match_ptr(at91_ohci_dt_ids), | 691 | .of_match_table = of_match_ptr(at91_ohci_dt_ids), |
745 | }, | 692 | }, |
746 | }; | 693 | }; |
694 | |||
695 | static int __init ohci_at91_init(void) | ||
696 | { | ||
697 | if (usb_disabled()) | ||
698 | return -ENODEV; | ||
699 | |||
700 | pr_info("%s: " DRIVER_DESC "\n", hcd_name); | ||
701 | ohci_init_driver(&ohci_at91_hc_driver, NULL); | ||
702 | |||
703 | /* | ||
704 | * The Atmel HW has some unusual quirks, which require Atmel-specific | ||
705 | * workarounds. We override certain hc_driver functions here to | ||
706 | * achieve that. We explicitly do not enhance ohci_driver_overrides to | ||
707 | * allow this more easily, since this is an unusual case, and we don't | ||
708 | * want to encourage others to override these functions by making it | ||
709 | * too easy. | ||
710 | */ | ||
711 | |||
712 | orig_ohci_hub_control = ohci_at91_hc_driver.hub_control; | ||
713 | orig_ohci_hub_status_data = ohci_at91_hc_driver.hub_status_data; | ||
714 | |||
715 | ohci_at91_hc_driver.hub_status_data = ohci_at91_hub_status_data; | ||
716 | ohci_at91_hc_driver.hub_control = ohci_at91_hub_control; | ||
717 | |||
718 | return platform_driver_register(&ohci_hcd_at91_driver); | ||
719 | } | ||
720 | module_init(ohci_at91_init); | ||
721 | |||
722 | static void __exit ohci_at91_cleanup(void) | ||
723 | { | ||
724 | platform_driver_unregister(&ohci_hcd_at91_driver); | ||
725 | } | ||
726 | module_exit(ohci_at91_cleanup); | ||
727 | |||
728 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
729 | MODULE_LICENSE("GPL"); | ||
730 | MODULE_ALIAS("platform:at91_ohci"); | ||