Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ohci-jz4740.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
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>
18 
21 
22  struct regulator *vbus;
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");