diff options
author | Sylvain Munaut <tnt@246tNt.com> | 2006-12-13 15:09:55 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:44:33 -0500 |
commit | 495a678fc62e850d15f860d39faee07ba0a8910c (patch) | |
tree | 4f8f12bafc2039fadda6e88efdc1b57729ca31d8 /drivers/usb/host/ohci-ppc-of.c | |
parent | 5e16fabe5dbcff15de6cdcba406195fe6e4380df (diff) |
ohci: Add support for OHCI controller on the of_platform bus
PPC embedded systems can have a ohci controller builtin. In the
new model, it will end up as a driver on the of_platform bus,
this patches takes care of them.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ohci-ppc-of.c')
-rw-r--r-- | drivers/usb/host/ohci-ppc-of.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c new file mode 100644 index 000000000000..08e237c7bc43 --- /dev/null +++ b/drivers/usb/host/ohci-ppc-of.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * OHCI HCD (Host Controller Driver) for USB. | ||
3 | * | ||
4 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> | ||
5 | * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> | ||
6 | * (C) Copyright 2002 Hewlett-Packard Company | ||
7 | * (C) Copyright 2006 Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Bus glue for OHCI HC on the of_platform bus | ||
10 | * | ||
11 | * Modified for of_platform bus from ohci-sa1111.c | ||
12 | * | ||
13 | * This file is licenced under the GPL. | ||
14 | */ | ||
15 | |||
16 | #include <linux/signal.h> | ||
17 | |||
18 | #include <asm/of_platform.h> | ||
19 | #include <asm/prom.h> | ||
20 | |||
21 | |||
22 | static int __devinit | ||
23 | ohci_ppc_of_start(struct usb_hcd *hcd) | ||
24 | { | ||
25 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
26 | int ret; | ||
27 | |||
28 | if ((ret = ohci_init(ohci)) < 0) | ||
29 | return ret; | ||
30 | |||
31 | if ((ret = ohci_run(ohci)) < 0) { | ||
32 | err("can't start %s", ohci_to_hcd(ohci)->self.bus_name); | ||
33 | ohci_stop(hcd); | ||
34 | return ret; | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static const struct hc_driver ohci_ppc_of_hc_driver = { | ||
41 | .description = hcd_name, | ||
42 | .product_desc = "OF OHCI", | ||
43 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
44 | |||
45 | /* | ||
46 | * generic hardware linkage | ||
47 | */ | ||
48 | .irq = ohci_irq, | ||
49 | .flags = HCD_USB11 | HCD_MEMORY, | ||
50 | |||
51 | /* | ||
52 | * basic lifecycle operations | ||
53 | */ | ||
54 | .start = ohci_ppc_of_start, | ||
55 | .stop = ohci_stop, | ||
56 | .shutdown = ohci_shutdown, | ||
57 | |||
58 | /* | ||
59 | * managing i/o requests and associated device resources | ||
60 | */ | ||
61 | .urb_enqueue = ohci_urb_enqueue, | ||
62 | .urb_dequeue = ohci_urb_dequeue, | ||
63 | .endpoint_disable = ohci_endpoint_disable, | ||
64 | |||
65 | /* | ||
66 | * scheduling support | ||
67 | */ | ||
68 | .get_frame_number = ohci_get_frame, | ||
69 | |||
70 | /* | ||
71 | * root hub support | ||
72 | */ | ||
73 | .hub_status_data = ohci_hub_status_data, | ||
74 | .hub_control = ohci_hub_control, | ||
75 | .hub_irq_enable = ohci_rhsc_enable, | ||
76 | #ifdef CONFIG_PM | ||
77 | .bus_suspend = ohci_bus_suspend, | ||
78 | .bus_resume = ohci_bus_resume, | ||
79 | #endif | ||
80 | .start_port_reset = ohci_start_port_reset, | ||
81 | }; | ||
82 | |||
83 | |||
84 | static int __devinit | ||
85 | ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) | ||
86 | { | ||
87 | struct device_node *dn = op->node; | ||
88 | struct usb_hcd *hcd; | ||
89 | struct ohci_hcd *ohci; | ||
90 | struct resource res; | ||
91 | int irq; | ||
92 | |||
93 | int rv; | ||
94 | int is_bigendian; | ||
95 | |||
96 | if (usb_disabled()) | ||
97 | return -ENODEV; | ||
98 | |||
99 | is_bigendian = | ||
100 | device_is_compatible(dn, "ohci-bigendian") || | ||
101 | device_is_compatible(dn, "ohci-be"); | ||
102 | |||
103 | dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n"); | ||
104 | |||
105 | rv = of_address_to_resource(dn, 0, &res); | ||
106 | if (rv) | ||
107 | return rv; | ||
108 | |||
109 | hcd = usb_create_hcd(&ohci_ppc_of_hc_driver, &op->dev, "PPC-OF USB"); | ||
110 | if (!hcd) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | hcd->rsrc_start = res.start; | ||
114 | hcd->rsrc_len = res.end - res.start + 1; | ||
115 | |||
116 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { | ||
117 | printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); | ||
118 | rv = -EBUSY; | ||
119 | goto err_rmr; | ||
120 | } | ||
121 | |||
122 | irq = irq_of_parse_and_map(dn, 0); | ||
123 | if (irq == NO_IRQ) { | ||
124 | printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); | ||
125 | rv = -EBUSY; | ||
126 | goto err_irq; | ||
127 | } | ||
128 | |||
129 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | ||
130 | if (!hcd->regs) { | ||
131 | printk(KERN_ERR __FILE__ ": ioremap failed\n"); | ||
132 | rv = -ENOMEM; | ||
133 | goto err_ioremap; | ||
134 | } | ||
135 | |||
136 | ohci = hcd_to_ohci(hcd); | ||
137 | if (is_bigendian) | ||
138 | ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; | ||
139 | |||
140 | ohci_hcd_init(ohci); | ||
141 | |||
142 | rv = usb_add_hcd(hcd, irq, 0); | ||
143 | if (rv == 0) | ||
144 | return 0; | ||
145 | |||
146 | iounmap(hcd->regs); | ||
147 | err_ioremap: | ||
148 | irq_dispose_mapping(irq); | ||
149 | err_irq: | ||
150 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
151 | err_rmr: | ||
152 | usb_put_hcd(hcd); | ||
153 | |||
154 | return rv; | ||
155 | } | ||
156 | |||
157 | static int ohci_hcd_ppc_of_remove(struct of_device *op) | ||
158 | { | ||
159 | struct usb_hcd *hcd = dev_get_drvdata(&op->dev); | ||
160 | dev_set_drvdata(&op->dev, NULL); | ||
161 | |||
162 | dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); | ||
163 | |||
164 | usb_remove_hcd(hcd); | ||
165 | |||
166 | iounmap(hcd->regs); | ||
167 | irq_dispose_mapping(hcd->irq); | ||
168 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
169 | |||
170 | usb_put_hcd(hcd); | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int ohci_hcd_ppc_of_shutdown(struct of_device *op) | ||
176 | { | ||
177 | struct usb_hcd *hcd = dev_get_drvdata(&op->dev); | ||
178 | |||
179 | if (hcd->driver->shutdown) | ||
180 | hcd->driver->shutdown(hcd); | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | |||
186 | static struct of_device_id ohci_hcd_ppc_of_match[] = { | ||
187 | #ifdef CONFIG_USB_OHCI_HCD_PPC_OF_BE | ||
188 | { | ||
189 | .name = "usb", | ||
190 | .compatible = "ohci-bigendian", | ||
191 | }, | ||
192 | { | ||
193 | .name = "usb", | ||
194 | .compatible = "ohci-be", | ||
195 | }, | ||
196 | #endif | ||
197 | #ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE | ||
198 | { | ||
199 | .name = "usb", | ||
200 | .compatible = "ohci-littledian", | ||
201 | }, | ||
202 | { | ||
203 | .name = "usb", | ||
204 | .compatible = "ohci-le", | ||
205 | }, | ||
206 | #endif | ||
207 | {}, | ||
208 | }; | ||
209 | MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); | ||
210 | |||
211 | #if !defined(CONFIG_USB_OHCI_HCD_PPC_OF_BE) && \ | ||
212 | !defined(CONFIG_USB_OHCI_HCD_PPC_OF_LE) | ||
213 | #error "No endianess selected for ppc-of-ohci" | ||
214 | #endif | ||
215 | |||
216 | |||
217 | static struct of_platform_driver ohci_hcd_ppc_of_driver = { | ||
218 | .name = "ppc-of-ohci", | ||
219 | .match_table = ohci_hcd_ppc_of_match, | ||
220 | .probe = ohci_hcd_ppc_of_probe, | ||
221 | .remove = ohci_hcd_ppc_of_remove, | ||
222 | .shutdown = ohci_hcd_ppc_of_shutdown, | ||
223 | #ifdef CONFIG_PM | ||
224 | /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/ | ||
225 | /*.resume = ohci_hcd_ppc_soc_drv_resume,*/ | ||
226 | #endif | ||
227 | .driver = { | ||
228 | .name = "ppc-of-ohci", | ||
229 | .owner = THIS_MODULE, | ||
230 | }, | ||
231 | }; | ||
232 | |||