diff options
author | Valentine Barshak <vbarshak@ru.mvista.com> | 2007-12-30 18:28:50 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 17:35:01 -0500 |
commit | da0e8fb00b862aa10265f0c64930b432cd44420b (patch) | |
tree | 7e8ee6ba65a8826a24268e8e353798b97b30de2a | |
parent | 040fa1b9620cd019349414505828b2ffbeded7f8 (diff) |
USB: add ehci-ppc-of bus glue (device-tree aware)
This adds device-tree-aware ehci-ppc-of driver.
The code is based on the ehci-ppc-soc driver by
Stefan Roese <sr@denx.de>.
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Stefan Roese <sr@denx.de>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 67 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ppc-of.c | 238 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 2 |
4 files changed, 286 insertions, 29 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 9b202d5f88c0..1f45aa25608e 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig | |||
@@ -85,6 +85,14 @@ config USB_EHCI_FSL | |||
85 | ---help--- | 85 | ---help--- |
86 | Variation of ARC USB block used in some Freescale chips. | 86 | Variation of ARC USB block used in some Freescale chips. |
87 | 87 | ||
88 | config USB_EHCI_HCD_PPC_OF | ||
89 | bool "EHCI support for PPC USB controller on OF platform bus" | ||
90 | depends on USB_EHCI_HCD && PPC_OF | ||
91 | default y | ||
92 | ---help--- | ||
93 | Enables support for the USB controller present on the PowerPC | ||
94 | OpenFirmware platform bus. | ||
95 | |||
88 | config USB_ISP116X_HCD | 96 | config USB_ISP116X_HCD |
89 | tristate "ISP116X HCD support" | 97 | tristate "ISP116X HCD support" |
90 | depends on USB | 98 | depends on USB |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7c5c8796d175..53fc97fced20 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -993,11 +993,16 @@ MODULE_LICENSE ("GPL"); | |||
993 | #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver | 993 | #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver |
994 | #endif | 994 | #endif |
995 | 995 | ||
996 | #ifdef CONFIG_440EPX | 996 | #if defined(CONFIG_440EPX) && !defined(CONFIG_PPC_MERGE) |
997 | #include "ehci-ppc-soc.c" | 997 | #include "ehci-ppc-soc.c" |
998 | #define PLATFORM_DRIVER ehci_ppc_soc_driver | 998 | #define PLATFORM_DRIVER ehci_ppc_soc_driver |
999 | #endif | 999 | #endif |
1000 | 1000 | ||
1001 | #ifdef CONFIG_USB_EHCI_HCD_PPC_OF | ||
1002 | #include "ehci-ppc-of.c" | ||
1003 | #define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver | ||
1004 | #endif | ||
1005 | |||
1001 | #ifdef CONFIG_ARCH_ORION | 1006 | #ifdef CONFIG_ARCH_ORION |
1002 | #include "ehci-orion.c" | 1007 | #include "ehci-orion.c" |
1003 | #define PLATFORM_DRIVER ehci_orion_driver | 1008 | #define PLATFORM_DRIVER ehci_orion_driver |
@@ -1025,52 +1030,58 @@ static int __init ehci_hcd_init(void) | |||
1025 | 1030 | ||
1026 | #ifdef PLATFORM_DRIVER | 1031 | #ifdef PLATFORM_DRIVER |
1027 | retval = platform_driver_register(&PLATFORM_DRIVER); | 1032 | retval = platform_driver_register(&PLATFORM_DRIVER); |
1028 | if (retval < 0) { | 1033 | if (retval < 0) |
1029 | #ifdef DEBUG | 1034 | goto clean0; |
1030 | debugfs_remove(ehci_debug_root); | ||
1031 | ehci_debug_root = NULL; | ||
1032 | #endif | ||
1033 | return retval; | ||
1034 | } | ||
1035 | #endif | 1035 | #endif |
1036 | 1036 | ||
1037 | #ifdef PCI_DRIVER | 1037 | #ifdef PCI_DRIVER |
1038 | retval = pci_register_driver(&PCI_DRIVER); | 1038 | retval = pci_register_driver(&PCI_DRIVER); |
1039 | if (retval < 0) { | 1039 | if (retval < 0) |
1040 | #ifdef DEBUG | 1040 | goto clean1; |
1041 | debugfs_remove(ehci_debug_root); | ||
1042 | ehci_debug_root = NULL; | ||
1043 | #endif | ||
1044 | #ifdef PLATFORM_DRIVER | ||
1045 | platform_driver_unregister(&PLATFORM_DRIVER); | ||
1046 | #endif | ||
1047 | return retval; | ||
1048 | } | ||
1049 | #endif | 1041 | #endif |
1050 | 1042 | ||
1051 | #ifdef PS3_SYSTEM_BUS_DRIVER | 1043 | #ifdef PS3_SYSTEM_BUS_DRIVER |
1052 | retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); | 1044 | retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); |
1053 | if (retval < 0) { | 1045 | if (retval < 0) |
1054 | #ifdef DEBUG | 1046 | goto clean2; |
1055 | debugfs_remove(ehci_debug_root); | ||
1056 | ehci_debug_root = NULL; | ||
1057 | #endif | 1047 | #endif |
1058 | #ifdef PLATFORM_DRIVER | 1048 | |
1059 | platform_driver_unregister(&PLATFORM_DRIVER); | 1049 | #ifdef OF_PLATFORM_DRIVER |
1050 | retval = of_register_platform_driver(&OF_PLATFORM_DRIVER); | ||
1051 | if (retval < 0) | ||
1052 | goto clean3; | ||
1053 | #endif | ||
1054 | return retval; | ||
1055 | |||
1056 | #ifdef OF_PLATFORM_DRIVER | ||
1057 | /* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */ | ||
1058 | clean3: | ||
1059 | #endif | ||
1060 | #ifdef PS3_SYSTEM_BUS_DRIVER | ||
1061 | ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); | ||
1062 | clean2: | ||
1060 | #endif | 1063 | #endif |
1061 | #ifdef PCI_DRIVER | 1064 | #ifdef PCI_DRIVER |
1062 | pci_unregister_driver(&PCI_DRIVER); | 1065 | pci_unregister_driver(&PCI_DRIVER); |
1066 | clean1: | ||
1063 | #endif | 1067 | #endif |
1064 | return retval; | 1068 | #ifdef PLATFORM_DRIVER |
1065 | } | 1069 | platform_driver_unregister(&PLATFORM_DRIVER); |
1070 | clean0: | ||
1071 | #endif | ||
1072 | #ifdef DEBUG | ||
1073 | debugfs_remove(ehci_debug_root); | ||
1074 | ehci_debug_root = NULL; | ||
1066 | #endif | 1075 | #endif |
1067 | |||
1068 | return retval; | 1076 | return retval; |
1069 | } | 1077 | } |
1070 | module_init(ehci_hcd_init); | 1078 | module_init(ehci_hcd_init); |
1071 | 1079 | ||
1072 | static void __exit ehci_hcd_cleanup(void) | 1080 | static void __exit ehci_hcd_cleanup(void) |
1073 | { | 1081 | { |
1082 | #ifdef OF_PLATFORM_DRIVER | ||
1083 | of_unregister_platform_driver(&OF_PLATFORM_DRIVER); | ||
1084 | #endif | ||
1074 | #ifdef PLATFORM_DRIVER | 1085 | #ifdef PLATFORM_DRIVER |
1075 | platform_driver_unregister(&PLATFORM_DRIVER); | 1086 | platform_driver_unregister(&PLATFORM_DRIVER); |
1076 | #endif | 1087 | #endif |
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c new file mode 100644 index 000000000000..ee305b1f99ff --- /dev/null +++ b/drivers/usb/host/ehci-ppc-of.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * EHCI HCD (Host Controller Driver) for USB. | ||
3 | * | ||
4 | * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus | ||
5 | * Tested on AMCC PPC 440EPx | ||
6 | * | ||
7 | * Valentine Barshak <vbarshak@ru.mvista.com> | ||
8 | * | ||
9 | * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> | ||
10 | * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> | ||
11 | * | ||
12 | * This file is licenced under the GPL. | ||
13 | */ | ||
14 | |||
15 | #include <linux/signal.h> | ||
16 | |||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | |||
20 | /* called during probe() after chip reset completes */ | ||
21 | static int ehci_ppc_of_setup(struct usb_hcd *hcd) | ||
22 | { | ||
23 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
24 | int retval; | ||
25 | |||
26 | retval = ehci_halt(ehci); | ||
27 | if (retval) | ||
28 | return retval; | ||
29 | |||
30 | retval = ehci_init(hcd); | ||
31 | if (retval) | ||
32 | return retval; | ||
33 | |||
34 | ehci->sbrn = 0x20; | ||
35 | return ehci_reset(ehci); | ||
36 | } | ||
37 | |||
38 | |||
39 | static const struct hc_driver ehci_ppc_of_hc_driver = { | ||
40 | .description = hcd_name, | ||
41 | .product_desc = "OF EHCI", | ||
42 | .hcd_priv_size = sizeof(struct ehci_hcd), | ||
43 | |||
44 | /* | ||
45 | * generic hardware linkage | ||
46 | */ | ||
47 | .irq = ehci_irq, | ||
48 | .flags = HCD_MEMORY | HCD_USB2, | ||
49 | |||
50 | /* | ||
51 | * basic lifecycle operations | ||
52 | */ | ||
53 | .reset = ehci_ppc_of_setup, | ||
54 | .start = ehci_run, | ||
55 | .stop = ehci_stop, | ||
56 | .shutdown = ehci_shutdown, | ||
57 | |||
58 | /* | ||
59 | * managing i/o requests and associated device resources | ||
60 | */ | ||
61 | .urb_enqueue = ehci_urb_enqueue, | ||
62 | .urb_dequeue = ehci_urb_dequeue, | ||
63 | .endpoint_disable = ehci_endpoint_disable, | ||
64 | |||
65 | /* | ||
66 | * scheduling support | ||
67 | */ | ||
68 | .get_frame_number = ehci_get_frame, | ||
69 | |||
70 | /* | ||
71 | * root hub support | ||
72 | */ | ||
73 | .hub_status_data = ehci_hub_status_data, | ||
74 | .hub_control = ehci_hub_control, | ||
75 | #ifdef CONFIG_PM | ||
76 | .bus_suspend = ehci_bus_suspend, | ||
77 | .bus_resume = ehci_bus_resume, | ||
78 | #endif | ||
79 | }; | ||
80 | |||
81 | |||
82 | /* | ||
83 | * 440EPx Errata USBH_3 | ||
84 | * Fix: Enable Break Memory Transfer (BMT) in INSNREG3 | ||
85 | */ | ||
86 | #define PPC440EPX_EHCI0_INSREG_BMT (0x1 << 0) | ||
87 | static int __devinit | ||
88 | ppc44x_enable_bmt(struct device_node *dn) | ||
89 | { | ||
90 | __iomem u32 *insreg_virt; | ||
91 | |||
92 | insreg_virt = of_iomap(dn, 1); | ||
93 | if (!insreg_virt) | ||
94 | return -EINVAL; | ||
95 | |||
96 | out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT); | ||
97 | |||
98 | iounmap(insreg_virt); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | |||
103 | static int __devinit | ||
104 | ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) | ||
105 | { | ||
106 | struct device_node *dn = op->node; | ||
107 | struct usb_hcd *hcd; | ||
108 | struct ehci_hcd *ehci; | ||
109 | struct resource res; | ||
110 | int irq; | ||
111 | int rv; | ||
112 | |||
113 | if (usb_disabled()) | ||
114 | return -ENODEV; | ||
115 | |||
116 | dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n"); | ||
117 | |||
118 | rv = of_address_to_resource(dn, 0, &res); | ||
119 | if (rv) | ||
120 | return rv; | ||
121 | |||
122 | hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB"); | ||
123 | if (!hcd) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | hcd->rsrc_start = res.start; | ||
127 | hcd->rsrc_len = res.end - res.start + 1; | ||
128 | |||
129 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { | ||
130 | printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); | ||
131 | rv = -EBUSY; | ||
132 | goto err_rmr; | ||
133 | } | ||
134 | |||
135 | irq = irq_of_parse_and_map(dn, 0); | ||
136 | if (irq == NO_IRQ) { | ||
137 | printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); | ||
138 | rv = -EBUSY; | ||
139 | goto err_irq; | ||
140 | } | ||
141 | |||
142 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | ||
143 | if (!hcd->regs) { | ||
144 | printk(KERN_ERR __FILE__ ": ioremap failed\n"); | ||
145 | rv = -ENOMEM; | ||
146 | goto err_ioremap; | ||
147 | } | ||
148 | |||
149 | ehci = hcd_to_ehci(hcd); | ||
150 | |||
151 | if (of_get_property(dn, "big-endian", NULL)) { | ||
152 | ehci->big_endian_mmio = 1; | ||
153 | ehci->big_endian_desc = 1; | ||
154 | } | ||
155 | if (of_get_property(dn, "big-endian-regs", NULL)) | ||
156 | ehci->big_endian_mmio = 1; | ||
157 | if (of_get_property(dn, "big-endian-desc", NULL)) | ||
158 | ehci->big_endian_desc = 1; | ||
159 | |||
160 | ehci->caps = hcd->regs; | ||
161 | ehci->regs = hcd->regs + | ||
162 | HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
163 | |||
164 | /* cache this readonly data; minimize chip reads */ | ||
165 | ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); | ||
166 | |||
167 | if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) { | ||
168 | rv = ppc44x_enable_bmt(dn); | ||
169 | ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n", | ||
170 | rv ? "NOT ": ""); | ||
171 | } | ||
172 | |||
173 | rv = usb_add_hcd(hcd, irq, 0); | ||
174 | if (rv == 0) | ||
175 | return 0; | ||
176 | |||
177 | iounmap(hcd->regs); | ||
178 | err_ioremap: | ||
179 | irq_dispose_mapping(irq); | ||
180 | err_irq: | ||
181 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
182 | err_rmr: | ||
183 | usb_put_hcd(hcd); | ||
184 | |||
185 | return rv; | ||
186 | } | ||
187 | |||
188 | |||
189 | static int ehci_hcd_ppc_of_remove(struct of_device *op) | ||
190 | { | ||
191 | struct usb_hcd *hcd = dev_get_drvdata(&op->dev); | ||
192 | dev_set_drvdata(&op->dev, NULL); | ||
193 | |||
194 | dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); | ||
195 | |||
196 | usb_remove_hcd(hcd); | ||
197 | |||
198 | iounmap(hcd->regs); | ||
199 | irq_dispose_mapping(hcd->irq); | ||
200 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
201 | |||
202 | usb_put_hcd(hcd); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | |||
208 | static int ehci_hcd_ppc_of_shutdown(struct of_device *op) | ||
209 | { | ||
210 | struct usb_hcd *hcd = dev_get_drvdata(&op->dev); | ||
211 | |||
212 | if (hcd->driver->shutdown) | ||
213 | hcd->driver->shutdown(hcd); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | |||
219 | static struct of_device_id ehci_hcd_ppc_of_match[] = { | ||
220 | { | ||
221 | .compatible = "usb-ehci", | ||
222 | }, | ||
223 | {}, | ||
224 | }; | ||
225 | MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); | ||
226 | |||
227 | |||
228 | static struct of_platform_driver ehci_hcd_ppc_of_driver = { | ||
229 | .name = "ppc-of-ehci", | ||
230 | .match_table = ehci_hcd_ppc_of_match, | ||
231 | .probe = ehci_hcd_ppc_of_probe, | ||
232 | .remove = ehci_hcd_ppc_of_remove, | ||
233 | .shutdown = ehci_hcd_ppc_of_shutdown, | ||
234 | .driver = { | ||
235 | .name = "ppc-of-ehci", | ||
236 | .owner = THIS_MODULE, | ||
237 | }, | ||
238 | }; | ||
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index eeda4c88ebae..c78c52771e64 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -741,7 +741,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) | |||
741 | * definition below can die once the 4xx support is | 741 | * definition below can die once the 4xx support is |
742 | * finally ported over. | 742 | * finally ported over. |
743 | */ | 743 | */ |
744 | #if defined(CONFIG_PPC) | 744 | #if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE) |
745 | #define readl_be(addr) in_be32((__force unsigned *)addr) | 745 | #define readl_be(addr) in_be32((__force unsigned *)addr) |
746 | #define writel_be(val, addr) out_be32((__force unsigned *)addr, val) | 746 | #define writel_be(val, addr) out_be32((__force unsigned *)addr, val) |
747 | #endif | 747 | #endif |