[xburst] Add 2.6.35 patches
[openwrt/svn-archive/archive.git] / target / linux / xburst / patches-2.6.35 / 055-ohci.patch
1 From 4365ef4ae6c7c08950ac34c47f7beaece2dc48ea Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Sun, 1 Aug 2010 21:13:26 +0200
4 Subject: [PATCH] USB: Add JZ4740 OHCI support
5
6 Add OHCI glue code for JZ4740 SoCs OHCI module.
7
8 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
9 Cc: Greg Kroah-Hartman <gregkh@suse.de>
10 Cc: David Brownell <dbrownell@users.sourceforge.net>
11 Cc: linux-usb@vger.kernel.org
12 Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
13 Cc: linux-mips@linux-mips.org
14 Cc: linux-kernel@vger.kernel.org
15 Patchwork: https://patchwork.linux-mips.org/patch/1411/
16 Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
17 ---
18 drivers/usb/Kconfig | 1 +
19 drivers/usb/host/ohci-hcd.c | 5 +
20 drivers/usb/host/ohci-jz4740.c | 276 ++++++++++++++++++++++++++++++++++++++++
21 3 files changed, 282 insertions(+), 0 deletions(-)
22 create mode 100644 drivers/usb/host/ohci-jz4740.c
23
24 --- a/drivers/usb/Kconfig
25 +++ b/drivers/usb/Kconfig
26 @@ -46,6 +46,7 @@ config USB_ARCH_HAS_OHCI
27 default y if PPC_MPC52xx
28 # MIPS:
29 default y if SOC_AU1X00
30 + default y if MACH_JZ4740
31 # SH:
32 default y if CPU_SUBTYPE_SH7720
33 default y if CPU_SUBTYPE_SH7721
34 --- a/drivers/usb/host/ohci-hcd.c
35 +++ b/drivers/usb/host/ohci-hcd.c
36 @@ -1095,6 +1095,11 @@ MODULE_LICENSE ("GPL");
37 #define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
38 #endif
39
40 +#ifdef CONFIG_MACH_JZ4740
41 +#include "ohci-jz4740.c"
42 +#define PLATFORM_DRIVER ohci_hcd_jz4740_driver
43 +#endif
44 +
45 #if !defined(PCI_DRIVER) && \
46 !defined(PLATFORM_DRIVER) && \
47 !defined(OMAP1_PLATFORM_DRIVER) && \
48 --- /dev/null
49 +++ b/drivers/usb/host/ohci-jz4740.c
50 @@ -0,0 +1,276 @@
51 +/*
52 + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
53 + *
54 + * This program is free software; you can redistribute it and/or modify it
55 + * under the terms of the GNU General Public License as published by the
56 + * Free Software Foundation; either version 2 of the License, or (at your
57 + * option) any later version.
58 + *
59 + * You should have received a copy of the GNU General Public License along
60 + * with this program; if not, write to the Free Software Foundation, Inc.,
61 + * 675 Mass Ave, Cambridge, MA 02139, USA.
62 + *
63 + */
64 +
65 +#include <linux/platform_device.h>
66 +#include <linux/clk.h>
67 +#include <linux/regulator/consumer.h>
68 +
69 +struct jz4740_ohci_hcd {
70 + struct ohci_hcd ohci_hcd;
71 +
72 + struct regulator *vbus;
73 + bool vbus_enabled;
74 + struct clk *clk;
75 +};
76 +
77 +static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
78 +{
79 + return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
80 +}
81 +
82 +static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
83 +{
84 + return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
85 +}
86 +
87 +static int ohci_jz4740_start(struct usb_hcd *hcd)
88 +{
89 + struct ohci_hcd *ohci = hcd_to_ohci(hcd);
90 + int ret;
91 +
92 + ret = ohci_init(ohci);
93 + if (ret < 0)
94 + return ret;
95 +
96 + ohci->num_ports = 1;
97 +
98 + ret = ohci_run(ohci);
99 + if (ret < 0) {
100 + dev_err(hcd->self.controller, "Can not start %s",
101 + hcd->self.bus_name);
102 + ohci_stop(hcd);
103 + return ret;
104 + }
105 + return 0;
106 +}
107 +
108 +static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
109 + bool enabled)
110 +{
111 + int ret = 0;
112 +
113 + if (!jz4740_ohci->vbus)
114 + return 0;
115 +
116 + if (enabled && !jz4740_ohci->vbus_enabled) {
117 + ret = regulator_enable(jz4740_ohci->vbus);
118 + if (ret)
119 + dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
120 + "Could not power vbus\n");
121 + } else if (!enabled && jz4740_ohci->vbus_enabled) {
122 + ret = regulator_disable(jz4740_ohci->vbus);
123 + }
124 +
125 + if (ret == 0)
126 + jz4740_ohci->vbus_enabled = enabled;
127 +
128 + return ret;
129 +}
130 +
131 +static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
132 + u16 wIndex, char *buf, u16 wLength)
133 +{
134 + struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
135 + int ret;
136 +
137 + switch (typeReq) {
138 + case SetHubFeature:
139 + if (wValue == USB_PORT_FEAT_POWER)
140 + ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
141 + break;
142 + case ClearHubFeature:
143 + if (wValue == USB_PORT_FEAT_POWER)
144 + ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
145 + break;
146 + }
147 +
148 + if (ret)
149 + return ret;
150 +
151 + return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
152 +}
153 +
154 +
155 +static const struct hc_driver ohci_jz4740_hc_driver = {
156 + .description = hcd_name,
157 + .product_desc = "JZ4740 OHCI",
158 + .hcd_priv_size = sizeof(struct jz4740_ohci_hcd),
159 +
160 + /*
161 + * generic hardware linkage
162 + */
163 + .irq = ohci_irq,
164 + .flags = HCD_USB11 | HCD_MEMORY,
165 +
166 + /*
167 + * basic lifecycle operations
168 + */
169 + .start = ohci_jz4740_start,
170 + .stop = ohci_stop,
171 + .shutdown = ohci_shutdown,
172 +
173 + /*
174 + * managing i/o requests and associated device resources
175 + */
176 + .urb_enqueue = ohci_urb_enqueue,
177 + .urb_dequeue = ohci_urb_dequeue,
178 + .endpoint_disable = ohci_endpoint_disable,
179 +
180 + /*
181 + * scheduling support
182 + */
183 + .get_frame_number = ohci_get_frame,
184 +
185 + /*
186 + * root hub support
187 + */
188 + .hub_status_data = ohci_hub_status_data,
189 + .hub_control = ohci_jz4740_hub_control,
190 +#ifdef CONFIG_PM
191 + .bus_suspend = ohci_bus_suspend,
192 + .bus_resume = ohci_bus_resume,
193 +#endif
194 + .start_port_reset = ohci_start_port_reset,
195 +};
196 +
197 +
198 +static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
199 +{
200 + int ret;
201 + struct usb_hcd *hcd;
202 + struct jz4740_ohci_hcd *jz4740_ohci;
203 + struct resource *res;
204 + int irq;
205 +
206 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
207 +
208 + if (!res) {
209 + dev_err(&pdev->dev, "Failed to get platform resource\n");
210 + return -ENOENT;
211 + }
212 +
213 + irq = platform_get_irq(pdev, 0);
214 + if (irq < 0) {
215 + dev_err(&pdev->dev, "Failed to get platform irq\n");
216 + return irq;
217 + }
218 +
219 + hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
220 + if (!hcd) {
221 + dev_err(&pdev->dev, "Failed to create hcd.\n");
222 + return -ENOMEM;
223 + }
224 +
225 + jz4740_ohci = hcd_to_jz4740_hcd(hcd);
226 +
227 + res = request_mem_region(res->start, resource_size(res), hcd_name);
228 + if (!res) {
229 + dev_err(&pdev->dev, "Failed to request mem region.\n");
230 + ret = -EBUSY;
231 + goto err_free;
232 + }
233 +
234 + hcd->rsrc_start = res->start;
235 + hcd->rsrc_len = resource_size(res);
236 + hcd->regs = ioremap(res->start, resource_size(res));
237 +
238 + if (!hcd->regs) {
239 + dev_err(&pdev->dev, "Failed to ioremap registers.\n");
240 + ret = -EBUSY;
241 + goto err_release_mem;
242 + }
243 +
244 + jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
245 + if (IS_ERR(jz4740_ohci->clk)) {
246 + ret = PTR_ERR(jz4740_ohci->clk);
247 + dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
248 + goto err_iounmap;
249 + }
250 +
251 + jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
252 + if (IS_ERR(jz4740_ohci->vbus))
253 + jz4740_ohci->vbus = NULL;
254 +
255 +
256 + clk_set_rate(jz4740_ohci->clk, 48000000);
257 + clk_enable(jz4740_ohci->clk);
258 + if (jz4740_ohci->vbus)
259 + ohci_jz4740_set_vbus_power(jz4740_ohci, true);
260 +
261 + platform_set_drvdata(pdev, hcd);
262 +
263 + ohci_hcd_init(hcd_to_ohci(hcd));
264 +
265 + ret = usb_add_hcd(hcd, irq, 0);
266 + if (ret) {
267 + dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
268 + goto err_disable;
269 + }
270 +
271 + return 0;
272 +
273 +err_disable:
274 + platform_set_drvdata(pdev, NULL);
275 + if (jz4740_ohci->vbus) {
276 + regulator_disable(jz4740_ohci->vbus);
277 + regulator_put(jz4740_ohci->vbus);
278 + }
279 + clk_disable(jz4740_ohci->clk);
280 +
281 + clk_put(jz4740_ohci->clk);
282 +err_iounmap:
283 + iounmap(hcd->regs);
284 +err_release_mem:
285 + release_mem_region(res->start, resource_size(res));
286 +err_free:
287 + usb_put_hcd(hcd);
288 +
289 + return ret;
290 +}
291 +
292 +static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
293 +{
294 + struct usb_hcd *hcd = platform_get_drvdata(pdev);
295 + struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
296 +
297 + usb_remove_hcd(hcd);
298 +
299 + platform_set_drvdata(pdev, NULL);
300 +
301 + if (jz4740_ohci->vbus) {
302 + regulator_disable(jz4740_ohci->vbus);
303 + regulator_put(jz4740_ohci->vbus);
304 + }
305 +
306 + clk_disable(jz4740_ohci->clk);
307 + clk_put(jz4740_ohci->clk);
308 +
309 + iounmap(hcd->regs);
310 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
311 +
312 + usb_put_hcd(hcd);
313 +
314 + return 0;
315 +}
316 +
317 +static struct platform_driver ohci_hcd_jz4740_driver = {
318 + .probe = jz4740_ohci_probe,
319 + .remove = __devexit_p(jz4740_ohci_remove),
320 + .driver = {
321 + .name = "jz4740-ohci",
322 + .owner = THIS_MODULE,
323 + },
324 +};
325 +
326 +MODULE_ALIAS("platfrom:jz4740-ohci");