[kirkwood] Add Seagate Dockstar support
[openwrt.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");