]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/usb/host/ohci-jz4740.c
Merge branch 'topic/workq-update' into topic/misc
[karo-tx-linux.git] / drivers / usb / host / ohci-jz4740.c
1 /*
2  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3  *
4  *  This program is free software; you can redistribute it and/or modify it
5  *  under  the terms of the GNU General  Public License as published by the
6  *  Free Software Foundation;  either version 2 of the License, or (at your
7  *  option) any later version.
8  *
9  *  You should have received a copy of the  GNU General Public License along
10  *  with this program; if not, write  to the Free Software Foundation, Inc.,
11  *  675 Mass Ave, Cambridge, MA 02139, USA.
12  *
13  */
14
15 #include <linux/platform_device.h>
16 #include <linux/clk.h>
17 #include <linux/regulator/consumer.h>
18
19 struct jz4740_ohci_hcd {
20         struct ohci_hcd ohci_hcd;
21
22         struct regulator *vbus;
23         bool vbus_enabled;
24         struct clk *clk;
25 };
26
27 static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
28 {
29         return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
30 }
31
32 static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
33 {
34         return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
35 }
36
37 static int ohci_jz4740_start(struct usb_hcd *hcd)
38 {
39         struct ohci_hcd *ohci = hcd_to_ohci(hcd);
40         int     ret;
41
42         ret = ohci_init(ohci);
43         if (ret < 0)
44                 return ret;
45
46         ohci->num_ports = 1;
47
48         ret = ohci_run(ohci);
49         if (ret < 0) {
50                 dev_err(hcd->self.controller, "Can not start %s",
51                         hcd->self.bus_name);
52                 ohci_stop(hcd);
53                 return ret;
54         }
55         return 0;
56 }
57
58 static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
59         bool enabled)
60 {
61         int ret = 0;
62
63         if (!jz4740_ohci->vbus)
64                 return 0;
65
66         if (enabled && !jz4740_ohci->vbus_enabled) {
67                 ret = regulator_enable(jz4740_ohci->vbus);
68                 if (ret)
69                         dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
70                                 "Could not power vbus\n");
71         } else if (!enabled && jz4740_ohci->vbus_enabled) {
72                 ret = regulator_disable(jz4740_ohci->vbus);
73         }
74
75         if (ret == 0)
76                 jz4740_ohci->vbus_enabled = enabled;
77
78         return ret;
79 }
80
81 static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
82         u16 wIndex, char *buf, u16 wLength)
83 {
84         struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
85         int ret;
86
87         switch (typeReq) {
88         case SetHubFeature:
89                 if (wValue == USB_PORT_FEAT_POWER)
90                         ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
91                 break;
92         case ClearHubFeature:
93                 if (wValue == USB_PORT_FEAT_POWER)
94                         ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
95                 break;
96         }
97
98         if (ret)
99                 return ret;
100
101         return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
102 }
103
104
105 static const struct hc_driver ohci_jz4740_hc_driver = {
106         .description =          hcd_name,
107         .product_desc =         "JZ4740 OHCI",
108         .hcd_priv_size =        sizeof(struct jz4740_ohci_hcd),
109
110         /*
111          * generic hardware linkage
112          */
113         .irq =                  ohci_irq,
114         .flags =                HCD_USB11 | HCD_MEMORY,
115
116         /*
117          * basic lifecycle operations
118          */
119         .start =                ohci_jz4740_start,
120         .stop =                 ohci_stop,
121         .shutdown =             ohci_shutdown,
122
123         /*
124          * managing i/o requests and associated device resources
125          */
126         .urb_enqueue =          ohci_urb_enqueue,
127         .urb_dequeue =          ohci_urb_dequeue,
128         .endpoint_disable =     ohci_endpoint_disable,
129
130         /*
131          * scheduling support
132          */
133         .get_frame_number =     ohci_get_frame,
134
135         /*
136          * root hub support
137          */
138         .hub_status_data =      ohci_hub_status_data,
139         .hub_control =          ohci_jz4740_hub_control,
140 #ifdef  CONFIG_PM
141         .bus_suspend =          ohci_bus_suspend,
142         .bus_resume =           ohci_bus_resume,
143 #endif
144         .start_port_reset =     ohci_start_port_reset,
145 };
146
147
148 static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
149 {
150         int ret;
151         struct usb_hcd *hcd;
152         struct jz4740_ohci_hcd *jz4740_ohci;
153         struct resource *res;
154         int irq;
155
156         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
157
158         if (!res) {
159                 dev_err(&pdev->dev, "Failed to get platform resource\n");
160                 return -ENOENT;
161         }
162
163         irq = platform_get_irq(pdev, 0);
164         if (irq < 0) {
165                 dev_err(&pdev->dev, "Failed to get platform irq\n");
166                 return irq;
167         }
168
169         hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
170         if (!hcd) {
171                 dev_err(&pdev->dev, "Failed to create hcd.\n");
172                 return -ENOMEM;
173         }
174
175         jz4740_ohci = hcd_to_jz4740_hcd(hcd);
176
177         res = request_mem_region(res->start, resource_size(res), hcd_name);
178         if (!res) {
179                 dev_err(&pdev->dev, "Failed to request mem region.\n");
180                 ret = -EBUSY;
181                 goto err_free;
182         }
183
184         hcd->rsrc_start = res->start;
185         hcd->rsrc_len = resource_size(res);
186         hcd->regs = ioremap(res->start, resource_size(res));
187
188         if (!hcd->regs) {
189                 dev_err(&pdev->dev, "Failed to ioremap registers.\n");
190                 ret = -EBUSY;
191                 goto err_release_mem;
192         }
193
194         jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
195         if (IS_ERR(jz4740_ohci->clk)) {
196                 ret = PTR_ERR(jz4740_ohci->clk);
197                 dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
198                 goto err_iounmap;
199         }
200
201         jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
202         if (IS_ERR(jz4740_ohci->vbus))
203                 jz4740_ohci->vbus = NULL;
204
205
206         clk_set_rate(jz4740_ohci->clk, 48000000);
207         clk_enable(jz4740_ohci->clk);
208         if (jz4740_ohci->vbus)
209                 ohci_jz4740_set_vbus_power(jz4740_ohci, true);
210
211         platform_set_drvdata(pdev, hcd);
212
213         ohci_hcd_init(hcd_to_ohci(hcd));
214
215         ret = usb_add_hcd(hcd, irq, 0);
216         if (ret) {
217                 dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
218                 goto err_disable;
219         }
220
221         return 0;
222
223 err_disable:
224         platform_set_drvdata(pdev, NULL);
225         if (jz4740_ohci->vbus) {
226                 regulator_disable(jz4740_ohci->vbus);
227                 regulator_put(jz4740_ohci->vbus);
228         }
229         clk_disable(jz4740_ohci->clk);
230
231         clk_put(jz4740_ohci->clk);
232 err_iounmap:
233         iounmap(hcd->regs);
234 err_release_mem:
235         release_mem_region(res->start, resource_size(res));
236 err_free:
237         usb_put_hcd(hcd);
238
239         return ret;
240 }
241
242 static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
243 {
244         struct usb_hcd *hcd = platform_get_drvdata(pdev);
245         struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
246
247         usb_remove_hcd(hcd);
248
249         platform_set_drvdata(pdev, NULL);
250
251         if (jz4740_ohci->vbus) {
252                 regulator_disable(jz4740_ohci->vbus);
253                 regulator_put(jz4740_ohci->vbus);
254         }
255
256         clk_disable(jz4740_ohci->clk);
257         clk_put(jz4740_ohci->clk);
258
259         iounmap(hcd->regs);
260         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
261
262         usb_put_hcd(hcd);
263
264         return 0;
265 }
266
267 static struct platform_driver ohci_hcd_jz4740_driver = {
268         .probe = jz4740_ohci_probe,
269         .remove = __devexit_p(jz4740_ohci_remove),
270         .driver = {
271                 .name = "jz4740-ohci",
272                 .owner = THIS_MODULE,
273         },
274 };
275
276 MODULE_ALIAS("platform:jz4740-ohci");