Commit | Line | Data |
---|---|---|
62194244 JH |
1 | /* |
2 | * SAMSUNG EXYNOS USB HOST OHCI Controller | |
3 | * | |
4 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | |
5 | * Author: Jingoo Han <jg1.han@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/clk.h> | |
d5138930 | 15 | #include <linux/of.h> |
62194244 | 16 | #include <linux/platform_device.h> |
b8271856 | 17 | #include <linux/platform_data/usb-ohci-exynos.h> |
ed993bf1 | 18 | #include <linux/usb/phy.h> |
b506eebc | 19 | #include <linux/usb/samsung_usb_phy.h> |
62194244 JH |
20 | |
21 | struct exynos_ohci_hcd { | |
22 | struct device *dev; | |
23 | struct usb_hcd *hcd; | |
24 | struct clk *clk; | |
ed993bf1 VG |
25 | struct usb_phy *phy; |
26 | struct usb_otg *otg; | |
27 | struct exynos4_ohci_platdata *pdata; | |
62194244 JH |
28 | }; |
29 | ||
ed993bf1 VG |
30 | static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) |
31 | { | |
32 | struct platform_device *pdev = to_platform_device(exynos_ohci->dev); | |
33 | ||
34 | if (exynos_ohci->phy) | |
35 | usb_phy_init(exynos_ohci->phy); | |
2871782a | 36 | else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_init) |
ed993bf1 VG |
37 | exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); |
38 | } | |
39 | ||
40 | static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) | |
41 | { | |
42 | struct platform_device *pdev = to_platform_device(exynos_ohci->dev); | |
43 | ||
44 | if (exynos_ohci->phy) | |
45 | usb_phy_shutdown(exynos_ohci->phy); | |
2871782a | 46 | else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_exit) |
ed993bf1 VG |
47 | exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); |
48 | } | |
49 | ||
57465109 VP |
50 | static int ohci_exynos_reset(struct usb_hcd *hcd) |
51 | { | |
52 | return ohci_init(hcd_to_ohci(hcd)); | |
53 | } | |
54 | ||
62194244 JH |
55 | static int ohci_exynos_start(struct usb_hcd *hcd) |
56 | { | |
57 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | |
58 | int ret; | |
59 | ||
60 | ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci); | |
61 | ||
62194244 JH |
62 | ret = ohci_run(ohci); |
63 | if (ret < 0) { | |
5e415245 GKH |
64 | dev_err(hcd->self.controller, "can't start %s\n", |
65 | hcd->self.bus_name); | |
62194244 JH |
66 | ohci_stop(hcd); |
67 | return ret; | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | static const struct hc_driver exynos_ohci_hc_driver = { | |
74 | .description = hcd_name, | |
75 | .product_desc = "EXYNOS OHCI Host Controller", | |
76 | .hcd_priv_size = sizeof(struct ohci_hcd), | |
77 | ||
78 | .irq = ohci_irq, | |
79 | .flags = HCD_MEMORY|HCD_USB11, | |
80 | ||
57465109 | 81 | .reset = ohci_exynos_reset, |
62194244 JH |
82 | .start = ohci_exynos_start, |
83 | .stop = ohci_stop, | |
84 | .shutdown = ohci_shutdown, | |
85 | ||
86 | .get_frame_number = ohci_get_frame, | |
87 | ||
88 | .urb_enqueue = ohci_urb_enqueue, | |
89 | .urb_dequeue = ohci_urb_dequeue, | |
90 | .endpoint_disable = ohci_endpoint_disable, | |
91 | ||
92 | .hub_status_data = ohci_hub_status_data, | |
93 | .hub_control = ohci_hub_control, | |
94 | #ifdef CONFIG_PM | |
95 | .bus_suspend = ohci_bus_suspend, | |
96 | .bus_resume = ohci_bus_resume, | |
97 | #endif | |
98 | .start_port_reset = ohci_start_port_reset, | |
99 | }; | |
100 | ||
41ac7b3a | 101 | static int exynos_ohci_probe(struct platform_device *pdev) |
62194244 | 102 | { |
ed993bf1 | 103 | struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; |
62194244 JH |
104 | struct exynos_ohci_hcd *exynos_ohci; |
105 | struct usb_hcd *hcd; | |
106 | struct ohci_hcd *ohci; | |
107 | struct resource *res; | |
ed993bf1 | 108 | struct usb_phy *phy; |
62194244 JH |
109 | int irq; |
110 | int err; | |
111 | ||
d5138930 VG |
112 | /* |
113 | * Right now device-tree probed devices don't get dma_mask set. | |
114 | * Since shared usb code relies on it, set it here for now. | |
115 | * Once we move to full device tree support this will vanish off. | |
116 | */ | |
117 | if (!pdev->dev.dma_mask) | |
3b9561e9 | 118 | pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; |
d5138930 VG |
119 | if (!pdev->dev.coherent_dma_mask) |
120 | pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | |
121 | ||
390a0a78 JH |
122 | exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), |
123 | GFP_KERNEL); | |
62194244 JH |
124 | if (!exynos_ohci) |
125 | return -ENOMEM; | |
126 | ||
2871782a TA |
127 | if (of_device_is_compatible(pdev->dev.of_node, |
128 | "samsung,exynos5440-ohci")) | |
129 | goto skip_phy; | |
130 | ||
ed993bf1 | 131 | phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); |
9ee1c7fb | 132 | if (IS_ERR(phy)) { |
ed993bf1 VG |
133 | /* Fallback to pdata */ |
134 | if (!pdata) { | |
135 | dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); | |
136 | return -EPROBE_DEFER; | |
137 | } else { | |
138 | exynos_ohci->pdata = pdata; | |
139 | } | |
140 | } else { | |
141 | exynos_ohci->phy = phy; | |
142 | exynos_ohci->otg = phy->otg; | |
143 | } | |
144 | ||
2871782a TA |
145 | skip_phy: |
146 | ||
62194244 JH |
147 | exynos_ohci->dev = &pdev->dev; |
148 | ||
149 | hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, | |
150 | dev_name(&pdev->dev)); | |
151 | if (!hcd) { | |
152 | dev_err(&pdev->dev, "Unable to create HCD\n"); | |
390a0a78 | 153 | return -ENOMEM; |
62194244 JH |
154 | } |
155 | ||
156 | exynos_ohci->hcd = hcd; | |
60d80adb | 157 | exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost"); |
62194244 JH |
158 | |
159 | if (IS_ERR(exynos_ohci->clk)) { | |
160 | dev_err(&pdev->dev, "Failed to get usbhost clock\n"); | |
161 | err = PTR_ERR(exynos_ohci->clk); | |
162 | goto fail_clk; | |
163 | } | |
164 | ||
c05c946c | 165 | err = clk_prepare_enable(exynos_ohci->clk); |
62194244 | 166 | if (err) |
60d80adb | 167 | goto fail_clk; |
62194244 JH |
168 | |
169 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
170 | if (!res) { | |
171 | dev_err(&pdev->dev, "Failed to get I/O memory\n"); | |
172 | err = -ENXIO; | |
173 | goto fail_io; | |
174 | } | |
175 | ||
176 | hcd->rsrc_start = res->start; | |
177 | hcd->rsrc_len = resource_size(res); | |
390a0a78 | 178 | hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); |
62194244 JH |
179 | if (!hcd->regs) { |
180 | dev_err(&pdev->dev, "Failed to remap I/O memory\n"); | |
181 | err = -ENOMEM; | |
182 | goto fail_io; | |
183 | } | |
184 | ||
185 | irq = platform_get_irq(pdev, 0); | |
186 | if (!irq) { | |
187 | dev_err(&pdev->dev, "Failed to get IRQ\n"); | |
188 | err = -ENODEV; | |
390a0a78 | 189 | goto fail_io; |
62194244 JH |
190 | } |
191 | ||
ed993bf1 VG |
192 | if (exynos_ohci->otg) |
193 | exynos_ohci->otg->set_host(exynos_ohci->otg, | |
194 | &exynos_ohci->hcd->self); | |
195 | ||
196 | exynos_ohci_phy_enable(exynos_ohci); | |
62194244 JH |
197 | |
198 | ohci = hcd_to_ohci(hcd); | |
199 | ohci_hcd_init(ohci); | |
200 | ||
201 | err = usb_add_hcd(hcd, irq, IRQF_SHARED); | |
202 | if (err) { | |
203 | dev_err(&pdev->dev, "Failed to add USB HCD\n"); | |
ed993bf1 | 204 | goto fail_add_hcd; |
62194244 JH |
205 | } |
206 | ||
207 | platform_set_drvdata(pdev, exynos_ohci); | |
208 | ||
209 | return 0; | |
210 | ||
ed993bf1 VG |
211 | fail_add_hcd: |
212 | exynos_ohci_phy_disable(exynos_ohci); | |
62194244 | 213 | fail_io: |
c05c946c | 214 | clk_disable_unprepare(exynos_ohci->clk); |
62194244 JH |
215 | fail_clk: |
216 | usb_put_hcd(hcd); | |
62194244 JH |
217 | return err; |
218 | } | |
219 | ||
fb4e98ab | 220 | static int exynos_ohci_remove(struct platform_device *pdev) |
62194244 | 221 | { |
62194244 JH |
222 | struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); |
223 | struct usb_hcd *hcd = exynos_ohci->hcd; | |
224 | ||
225 | usb_remove_hcd(hcd); | |
226 | ||
ed993bf1 VG |
227 | if (exynos_ohci->otg) |
228 | exynos_ohci->otg->set_host(exynos_ohci->otg, | |
229 | &exynos_ohci->hcd->self); | |
230 | ||
231 | exynos_ohci_phy_disable(exynos_ohci); | |
62194244 | 232 | |
c05c946c | 233 | clk_disable_unprepare(exynos_ohci->clk); |
62194244 JH |
234 | |
235 | usb_put_hcd(hcd); | |
62194244 JH |
236 | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static void exynos_ohci_shutdown(struct platform_device *pdev) | |
241 | { | |
242 | struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); | |
243 | struct usb_hcd *hcd = exynos_ohci->hcd; | |
244 | ||
245 | if (hcd->driver->shutdown) | |
246 | hcd->driver->shutdown(hcd); | |
247 | } | |
248 | ||
249 | #ifdef CONFIG_PM | |
250 | static int exynos_ohci_suspend(struct device *dev) | |
251 | { | |
252 | struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); | |
253 | struct usb_hcd *hcd = exynos_ohci->hcd; | |
254 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | |
62194244 JH |
255 | unsigned long flags; |
256 | int rc = 0; | |
257 | ||
258 | /* | |
259 | * Root hub was already suspended. Disable irq emission and | |
260 | * mark HW unaccessible, bail out if RH has been resumed. Use | |
261 | * the spinlock to properly synchronize with possible pending | |
262 | * RH suspend or resume activity. | |
62194244 JH |
263 | */ |
264 | spin_lock_irqsave(&ohci->lock, flags); | |
2b4ffe31 JH |
265 | if (ohci->rh_state != OHCI_RH_SUSPENDED && |
266 | ohci->rh_state != OHCI_RH_HALTED) { | |
62194244 JH |
267 | rc = -EINVAL; |
268 | goto fail; | |
269 | } | |
270 | ||
271 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | |
272 | ||
ed993bf1 VG |
273 | if (exynos_ohci->otg) |
274 | exynos_ohci->otg->set_host(exynos_ohci->otg, | |
275 | &exynos_ohci->hcd->self); | |
276 | ||
277 | exynos_ohci_phy_disable(exynos_ohci); | |
e864abed | 278 | |
c05c946c | 279 | clk_disable_unprepare(exynos_ohci->clk); |
e864abed | 280 | |
62194244 JH |
281 | fail: |
282 | spin_unlock_irqrestore(&ohci->lock, flags); | |
283 | ||
284 | return rc; | |
285 | } | |
286 | ||
287 | static int exynos_ohci_resume(struct device *dev) | |
288 | { | |
289 | struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); | |
290 | struct usb_hcd *hcd = exynos_ohci->hcd; | |
62194244 | 291 | |
c05c946c | 292 | clk_prepare_enable(exynos_ohci->clk); |
e864abed | 293 | |
ed993bf1 VG |
294 | if (exynos_ohci->otg) |
295 | exynos_ohci->otg->set_host(exynos_ohci->otg, | |
296 | &exynos_ohci->hcd->self); | |
297 | ||
298 | exynos_ohci_phy_enable(exynos_ohci); | |
62194244 | 299 | |
cfa49b4b | 300 | ohci_resume(hcd, false); |
62194244 JH |
301 | |
302 | return 0; | |
303 | } | |
304 | #else | |
305 | #define exynos_ohci_suspend NULL | |
306 | #define exynos_ohci_resume NULL | |
307 | #endif | |
308 | ||
309 | static const struct dev_pm_ops exynos_ohci_pm_ops = { | |
310 | .suspend = exynos_ohci_suspend, | |
311 | .resume = exynos_ohci_resume, | |
312 | }; | |
313 | ||
d5138930 VG |
314 | #ifdef CONFIG_OF |
315 | static const struct of_device_id exynos_ohci_match[] = { | |
6e247777 | 316 | { .compatible = "samsung,exynos4210-ohci" }, |
2871782a | 317 | { .compatible = "samsung,exynos5440-ohci" }, |
d5138930 VG |
318 | {}, |
319 | }; | |
320 | MODULE_DEVICE_TABLE(of, exynos_ohci_match); | |
321 | #endif | |
322 | ||
62194244 JH |
323 | static struct platform_driver exynos_ohci_driver = { |
324 | .probe = exynos_ohci_probe, | |
7690417d | 325 | .remove = exynos_ohci_remove, |
62194244 JH |
326 | .shutdown = exynos_ohci_shutdown, |
327 | .driver = { | |
328 | .name = "exynos-ohci", | |
329 | .owner = THIS_MODULE, | |
330 | .pm = &exynos_ohci_pm_ops, | |
d5138930 | 331 | .of_match_table = of_match_ptr(exynos_ohci_match), |
62194244 JH |
332 | } |
333 | }; | |
334 | ||
335 | MODULE_ALIAS("platform:exynos-ohci"); | |
336 | MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); |