Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / drivers / usb / host / ohci-exynos.c
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>
15 #include <linux/platform_device.h>
16 #include <mach/ohci.h>
17 #include <plat/usb-phy.h>
18
19 struct exynos_ohci_hcd {
20         struct device *dev;
21         struct usb_hcd *hcd;
22         struct clk *clk;
23 };
24
25 static int ohci_exynos_start(struct usb_hcd *hcd)
26 {
27         struct ohci_hcd *ohci = hcd_to_ohci(hcd);
28         int ret;
29
30         ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci);
31
32         ret = ohci_init(ohci);
33         if (ret < 0)
34                 return ret;
35
36         ret = ohci_run(ohci);
37         if (ret < 0) {
38                 err("can't start %s", hcd->self.bus_name);
39                 ohci_stop(hcd);
40                 return ret;
41         }
42
43         return 0;
44 }
45
46 static const struct hc_driver exynos_ohci_hc_driver = {
47         .description            = hcd_name,
48         .product_desc           = "EXYNOS OHCI Host Controller",
49         .hcd_priv_size          = sizeof(struct ohci_hcd),
50
51         .irq                    = ohci_irq,
52         .flags                  = HCD_MEMORY|HCD_USB11,
53
54         .start                  = ohci_exynos_start,
55         .stop                   = ohci_stop,
56         .shutdown               = ohci_shutdown,
57
58         .get_frame_number       = ohci_get_frame,
59
60         .urb_enqueue            = ohci_urb_enqueue,
61         .urb_dequeue            = ohci_urb_dequeue,
62         .endpoint_disable       = ohci_endpoint_disable,
63
64         .hub_status_data        = ohci_hub_status_data,
65         .hub_control            = ohci_hub_control,
66 #ifdef  CONFIG_PM
67         .bus_suspend            = ohci_bus_suspend,
68         .bus_resume             = ohci_bus_resume,
69 #endif
70         .start_port_reset       = ohci_start_port_reset,
71 };
72
73 static int __devinit exynos_ohci_probe(struct platform_device *pdev)
74 {
75         struct exynos4_ohci_platdata *pdata;
76         struct exynos_ohci_hcd *exynos_ohci;
77         struct usb_hcd *hcd;
78         struct ohci_hcd *ohci;
79         struct resource *res;
80         int irq;
81         int err;
82
83         pdata = pdev->dev.platform_data;
84         if (!pdata) {
85                 dev_err(&pdev->dev, "No platform data defined\n");
86                 return -EINVAL;
87         }
88
89         exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL);
90         if (!exynos_ohci)
91                 return -ENOMEM;
92
93         exynos_ohci->dev = &pdev->dev;
94
95         hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
96                                         dev_name(&pdev->dev));
97         if (!hcd) {
98                 dev_err(&pdev->dev, "Unable to create HCD\n");
99                 err = -ENOMEM;
100                 goto fail_hcd;
101         }
102
103         exynos_ohci->hcd = hcd;
104         exynos_ohci->clk = clk_get(&pdev->dev, "usbhost");
105
106         if (IS_ERR(exynos_ohci->clk)) {
107                 dev_err(&pdev->dev, "Failed to get usbhost clock\n");
108                 err = PTR_ERR(exynos_ohci->clk);
109                 goto fail_clk;
110         }
111
112         err = clk_enable(exynos_ohci->clk);
113         if (err)
114                 goto fail_clken;
115
116         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
117         if (!res) {
118                 dev_err(&pdev->dev, "Failed to get I/O memory\n");
119                 err = -ENXIO;
120                 goto fail_io;
121         }
122
123         hcd->rsrc_start = res->start;
124         hcd->rsrc_len = resource_size(res);
125         hcd->regs = ioremap(res->start, resource_size(res));
126         if (!hcd->regs) {
127                 dev_err(&pdev->dev, "Failed to remap I/O memory\n");
128                 err = -ENOMEM;
129                 goto fail_io;
130         }
131
132         irq = platform_get_irq(pdev, 0);
133         if (!irq) {
134                 dev_err(&pdev->dev, "Failed to get IRQ\n");
135                 err = -ENODEV;
136                 goto fail;
137         }
138
139         if (pdata->phy_init)
140                 pdata->phy_init(pdev, S5P_USB_PHY_HOST);
141
142         ohci = hcd_to_ohci(hcd);
143         ohci_hcd_init(ohci);
144
145         err = usb_add_hcd(hcd, irq, IRQF_SHARED);
146         if (err) {
147                 dev_err(&pdev->dev, "Failed to add USB HCD\n");
148                 goto fail;
149         }
150
151         platform_set_drvdata(pdev, exynos_ohci);
152
153         return 0;
154
155 fail:
156         iounmap(hcd->regs);
157 fail_io:
158         clk_disable(exynos_ohci->clk);
159 fail_clken:
160         clk_put(exynos_ohci->clk);
161 fail_clk:
162         usb_put_hcd(hcd);
163 fail_hcd:
164         kfree(exynos_ohci);
165         return err;
166 }
167
168 static int __devexit exynos_ohci_remove(struct platform_device *pdev)
169 {
170         struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
171         struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
172         struct usb_hcd *hcd = exynos_ohci->hcd;
173
174         usb_remove_hcd(hcd);
175
176         if (pdata && pdata->phy_exit)
177                 pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
178
179         iounmap(hcd->regs);
180
181         clk_disable(exynos_ohci->clk);
182         clk_put(exynos_ohci->clk);
183
184         usb_put_hcd(hcd);
185         kfree(exynos_ohci);
186
187         return 0;
188 }
189
190 static void exynos_ohci_shutdown(struct platform_device *pdev)
191 {
192         struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
193         struct usb_hcd *hcd = exynos_ohci->hcd;
194
195         if (hcd->driver->shutdown)
196                 hcd->driver->shutdown(hcd);
197 }
198
199 #ifdef CONFIG_PM
200 static int exynos_ohci_suspend(struct device *dev)
201 {
202         struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
203         struct usb_hcd *hcd = exynos_ohci->hcd;
204         struct ohci_hcd *ohci = hcd_to_ohci(hcd);
205         struct platform_device *pdev = to_platform_device(dev);
206         struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
207         unsigned long flags;
208         int rc = 0;
209
210         /*
211          * Root hub was already suspended. Disable irq emission and
212          * mark HW unaccessible, bail out if RH has been resumed. Use
213          * the spinlock to properly synchronize with possible pending
214          * RH suspend or resume activity.
215          *
216          * This is still racy as hcd->state is manipulated outside of
217          * any locks =P But that will be a different fix.
218          */
219         spin_lock_irqsave(&ohci->lock, flags);
220         if (hcd->state != HC_STATE_SUSPENDED && hcd->state != HC_STATE_HALT) {
221                 rc = -EINVAL;
222                 goto fail;
223         }
224
225         clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
226
227         if (pdata && pdata->phy_exit)
228                 pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
229 fail:
230         spin_unlock_irqrestore(&ohci->lock, flags);
231
232         return rc;
233 }
234
235 static int exynos_ohci_resume(struct device *dev)
236 {
237         struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
238         struct usb_hcd *hcd = exynos_ohci->hcd;
239         struct platform_device *pdev = to_platform_device(dev);
240         struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
241
242         if (pdata && pdata->phy_init)
243                 pdata->phy_init(pdev, S5P_USB_PHY_HOST);
244
245         /* Mark hardware accessible again as we are out of D3 state by now */
246         set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
247
248         ohci_finish_controller_resume(hcd);
249
250         return 0;
251 }
252 #else
253 #define exynos_ohci_suspend     NULL
254 #define exynos_ohci_resume      NULL
255 #endif
256
257 static const struct dev_pm_ops exynos_ohci_pm_ops = {
258         .suspend        = exynos_ohci_suspend,
259         .resume         = exynos_ohci_resume,
260 };
261
262 static struct platform_driver exynos_ohci_driver = {
263         .probe          = exynos_ohci_probe,
264         .remove         = __devexit_p(exynos_ohci_remove),
265         .shutdown       = exynos_ohci_shutdown,
266         .driver = {
267                 .name   = "exynos-ohci",
268                 .owner  = THIS_MODULE,
269                 .pm     = &exynos_ohci_pm_ops,
270         }
271 };
272
273 MODULE_ALIAS("platform:exynos-ohci");
274 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");