Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ehci-s5p.c
Go to the documentation of this file.
1 /*
2  * SAMSUNG S5P USB HOST EHCI Controller
3  *
4  * Copyright (C) 2011 Samsung Electronics Co.Ltd
5  * Author: Jingoo Han <[email protected]>
6  * Author: Joonyoung Shim <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  *
13  */
14 
15 #include <linux/clk.h>
16 #include <linux/of.h>
17 #include <linux/platform_device.h>
18 #include <linux/of_gpio.h>
20 #include <plat/usb-phy.h>
21 
22 #define EHCI_INSNREG00(base) (base + 0x90)
23 #define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25)
24 #define EHCI_INSNREG00_ENA_INCR8 (0x1 << 24)
25 #define EHCI_INSNREG00_ENA_INCR4 (0x1 << 23)
26 #define EHCI_INSNREG00_ENA_INCRX_ALIGN (0x1 << 22)
27 #define EHCI_INSNREG00_ENABLE_DMA_BURST \
28  (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
29  EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
30 
31 struct s5p_ehci_hcd {
32  struct device *dev;
33  struct usb_hcd *hcd;
34  struct clk *clk;
35 };
36 
37 static const struct hc_driver s5p_ehci_hc_driver = {
38  .description = hcd_name,
39  .product_desc = "S5P EHCI Host Controller",
40  .hcd_priv_size = sizeof(struct ehci_hcd),
41 
42  .irq = ehci_irq,
43  .flags = HCD_MEMORY | HCD_USB2,
44 
45  .reset = ehci_setup,
46  .start = ehci_run,
47  .stop = ehci_stop,
48  .shutdown = ehci_shutdown,
49 
50  .get_frame_number = ehci_get_frame,
51 
52  .urb_enqueue = ehci_urb_enqueue,
53  .urb_dequeue = ehci_urb_dequeue,
54  .endpoint_disable = ehci_endpoint_disable,
55  .endpoint_reset = ehci_endpoint_reset,
56 
57  .hub_status_data = ehci_hub_status_data,
58  .hub_control = ehci_hub_control,
59  .bus_suspend = ehci_bus_suspend,
60  .bus_resume = ehci_bus_resume,
61 
62  .relinquish_port = ehci_relinquish_port,
63  .port_handed_over = ehci_port_handed_over,
64 
65  .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
66 };
67 
68 static void s5p_setup_vbus_gpio(struct platform_device *pdev)
69 {
70  int err;
71  int gpio;
72 
73  if (!pdev->dev.of_node)
74  return;
75 
76  gpio = of_get_named_gpio(pdev->dev.of_node,
77  "samsung,vbus-gpio", 0);
78  if (!gpio_is_valid(gpio))
79  return;
80 
81  err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio");
82  if (err)
83  dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio);
84 }
85 
86 static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32);
87 
88 static int __devinit s5p_ehci_probe(struct platform_device *pdev)
89 {
90  struct s5p_ehci_platdata *pdata;
91  struct s5p_ehci_hcd *s5p_ehci;
92  struct usb_hcd *hcd;
93  struct ehci_hcd *ehci;
94  struct resource *res;
95  int irq;
96  int err;
97 
98  pdata = pdev->dev.platform_data;
99  if (!pdata) {
100  dev_err(&pdev->dev, "No platform data defined\n");
101  return -EINVAL;
102  }
103 
104  /*
105  * Right now device-tree probed devices don't get dma_mask set.
106  * Since shared usb code relies on it, set it here for now.
107  * Once we move to full device tree support this will vanish off.
108  */
109  if (!pdev->dev.dma_mask)
110  pdev->dev.dma_mask = &ehci_s5p_dma_mask;
111  if (!pdev->dev.coherent_dma_mask)
112  pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
113 
114  s5p_setup_vbus_gpio(pdev);
115 
116  s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd),
117  GFP_KERNEL);
118  if (!s5p_ehci)
119  return -ENOMEM;
120 
121  s5p_ehci->dev = &pdev->dev;
122 
123  hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
124  dev_name(&pdev->dev));
125  if (!hcd) {
126  dev_err(&pdev->dev, "Unable to create HCD\n");
127  return -ENOMEM;
128  }
129 
130  s5p_ehci->hcd = hcd;
131  s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
132 
133  if (IS_ERR(s5p_ehci->clk)) {
134  dev_err(&pdev->dev, "Failed to get usbhost clock\n");
135  err = PTR_ERR(s5p_ehci->clk);
136  goto fail_clk;
137  }
138 
139  err = clk_enable(s5p_ehci->clk);
140  if (err)
141  goto fail_clk;
142 
143  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
144  if (!res) {
145  dev_err(&pdev->dev, "Failed to get I/O memory\n");
146  err = -ENXIO;
147  goto fail_io;
148  }
149 
150  hcd->rsrc_start = res->start;
151  hcd->rsrc_len = resource_size(res);
152  hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
153  if (!hcd->regs) {
154  dev_err(&pdev->dev, "Failed to remap I/O memory\n");
155  err = -ENOMEM;
156  goto fail_io;
157  }
158 
159  irq = platform_get_irq(pdev, 0);
160  if (!irq) {
161  dev_err(&pdev->dev, "Failed to get IRQ\n");
162  err = -ENODEV;
163  goto fail_io;
164  }
165 
166  if (pdata->phy_init)
167  pdata->phy_init(pdev, S5P_USB_PHY_HOST);
168 
169  ehci = hcd_to_ehci(hcd);
170  ehci->caps = hcd->regs;
171 
172  /* DMA burst Enable */
174 
175  err = usb_add_hcd(hcd, irq, IRQF_SHARED);
176  if (err) {
177  dev_err(&pdev->dev, "Failed to add USB HCD\n");
178  goto fail_io;
179  }
180 
181  platform_set_drvdata(pdev, s5p_ehci);
182 
183  return 0;
184 
185 fail_io:
186  clk_disable(s5p_ehci->clk);
187 fail_clk:
188  usb_put_hcd(hcd);
189  return err;
190 }
191 
192 static int __devexit s5p_ehci_remove(struct platform_device *pdev)
193 {
194  struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
195  struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
196  struct usb_hcd *hcd = s5p_ehci->hcd;
197 
198  usb_remove_hcd(hcd);
199 
200  if (pdata && pdata->phy_exit)
201  pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
202 
203  clk_disable(s5p_ehci->clk);
204 
205  usb_put_hcd(hcd);
206 
207  return 0;
208 }
209 
210 static void s5p_ehci_shutdown(struct platform_device *pdev)
211 {
212  struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
213  struct usb_hcd *hcd = s5p_ehci->hcd;
214 
215  if (hcd->driver->shutdown)
216  hcd->driver->shutdown(hcd);
217 }
218 
219 #ifdef CONFIG_PM
220 static int s5p_ehci_suspend(struct device *dev)
221 {
222  struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
223  struct usb_hcd *hcd = s5p_ehci->hcd;
224  bool do_wakeup = device_may_wakeup(dev);
225  struct platform_device *pdev = to_platform_device(dev);
226  struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
227  int rc;
228 
229  rc = ehci_suspend(hcd, do_wakeup);
230 
231  if (pdata && pdata->phy_exit)
232  pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
233 
234  clk_disable(s5p_ehci->clk);
235 
236  return rc;
237 }
238 
239 static int s5p_ehci_resume(struct device *dev)
240 {
241  struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
242  struct usb_hcd *hcd = s5p_ehci->hcd;
243  struct platform_device *pdev = to_platform_device(dev);
244  struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
245 
246  clk_enable(s5p_ehci->clk);
247 
248  if (pdata && pdata->phy_init)
249  pdata->phy_init(pdev, S5P_USB_PHY_HOST);
250 
251  /* DMA burst Enable */
253 
254  ehci_resume(hcd, false);
255  return 0;
256 }
257 #else
258 #define s5p_ehci_suspend NULL
259 #define s5p_ehci_resume NULL
260 #endif
261 
262 static const struct dev_pm_ops s5p_ehci_pm_ops = {
263  .suspend = s5p_ehci_suspend,
264  .resume = s5p_ehci_resume,
265 };
266 
267 #ifdef CONFIG_OF
268 static const struct of_device_id exynos_ehci_match[] = {
269  { .compatible = "samsung,exynos-ehci" },
270  {},
271 };
272 MODULE_DEVICE_TABLE(of, exynos_ehci_match);
273 #endif
274 
275 static struct platform_driver s5p_ehci_driver = {
276  .probe = s5p_ehci_probe,
277  .remove = __devexit_p(s5p_ehci_remove),
278  .shutdown = s5p_ehci_shutdown,
279  .driver = {
280  .name = "s5p-ehci",
281  .owner = THIS_MODULE,
282  .pm = &s5p_ehci_pm_ops,
283  .of_match_table = of_match_ptr(exynos_ehci_match),
284  }
285 };
286 
287 MODULE_ALIAS("platform:s5p-ehci");