Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / usb / host / ehci-h20ahb.c
1 /*
2  * Copyright (C) 2007-2013 Texas Instruments, Inc.
3  *      Author: Vikram Pandita <vikram.pandita@ti.com>
4  *      Author: Anand Gadiyar <gadiyar@ti.com>
5  *      Author: Keshava Munegowda <keshava_mgowda@ti.com>
6  *      Author: Roger Quadros <rogerq@ti.com>
7  *
8  * Copyright (C) 2009 Nokia Corporation
9  *      Contact: Felipe Balbi <felipe.balbi@nokia.com>
10  *
11  * Based on ehci-omap.c - driver for USBHOST on OMAP3/4 processors
12  *
13  * This file is subject to the terms and conditions of the GNU General Public
14  * License.  See the file COPYING in the main directory of this archive
15  * for more details.
16  *
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/io.h>
22 #include <linux/platform_device.h>
23 #include <linux/slab.h>
24 #include <linux/usb/ulpi.h>
25 #include <linux/pm_runtime.h>
26 #include <linux/gpio.h>
27 #include <linux/clk.h>
28 #include <linux/usb.h>
29 #include <linux/usb/hcd.h>
30 #include <linux/of.h>
31 #include <linux/dma-mapping.h>
32
33 #include "ehci.h"
34
35 #define H20AHB_HS_USB_PORTS     1
36
37 /* EHCI Synopsys-specific Register Set */
38 #define EHCI_INSNREG04                                  (0xA0)
39 #define EHCI_INSNREG04_DISABLE_UNSUSPEND                (1 << 5)
40 #define EHCI_INSNREG05_ULPI                             (0xA4)
41 #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT               31
42 #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT               24
43 #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT                 22
44 #define EHCI_INSNREG05_ULPI_REGADD_SHIFT                16
45 #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT             8
46 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT                0
47
48 #define DRIVER_DESC "H20AHB-EHCI Host Controller driver"
49
50 static const char hcd_name[] = "ehci-h20ahb";
51
52 /*-------------------------------------------------------------------------*/
53
54 struct h20ahb_hcd {
55         struct usb_phy *phy[H20AHB_HS_USB_PORTS]; /* one PHY for each port */
56         int nports;
57 };
58
59 static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
60 {
61         __raw_writel(val, base + reg);
62 }
63
64 static inline u32 ehci_read(void __iomem *base, u32 reg)
65 {
66         return __raw_readl(base + reg);
67 }
68
69 /* configure so an HC device and id are always provided */
70 /* always called with process context; sleeping is OK */
71
72 static struct hc_driver __read_mostly ehci_h20ahb_hc_driver;
73
74 static const struct ehci_driver_overrides ehci_h20ahb_overrides __initdata = {
75         .extra_priv_size = sizeof(struct h20ahb_hcd),
76 };
77
78 static int ehci_h20ahb_phy_read(struct usb_phy *x, u32 reg)
79 {
80         u32 val = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |
81                 (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |
82                 (3 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |
83                 (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT);
84         ehci_write(x->io_priv, 0, val);
85         while ((val = ehci_read(x->io_priv, 0)) &
86                 (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT));
87         return val & 0xff;
88 }
89
90 static int ehci_h20ahb_phy_write(struct usb_phy *x, u32 val, u32 reg)
91 {
92         u32 v = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |
93                 (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |
94                 (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |
95                 (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |
96                 (val & 0xff);
97         ehci_write(x->io_priv, 0, v);
98         while ((v = ehci_read(x->io_priv, 0)) &
99                 (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT));
100         return 0;
101 }
102
103 static struct usb_phy_io_ops ehci_h20ahb_phy_io_ops = {
104         .read = ehci_h20ahb_phy_read,
105         .write = ehci_h20ahb_phy_write,
106 };
107
108
109 /**
110  * ehci_hcd_h20ahb_probe - initialize Synopsis-based HCDs
111  *
112  * Allocates basic resources for this USB host controller, and
113  * then invokes the start() method for the HCD associated with it
114  * through the hotplug entry's driver_data.
115  */
116 static int ehci_hcd_h20ahb_probe(struct platform_device *pdev)
117 {
118         struct device *dev = &pdev->dev;
119         struct resource *res;
120         struct usb_hcd  *hcd;
121         void __iomem *regs;
122         int ret;
123         int irq;
124         int i;
125         struct h20ahb_hcd       *h20ahb;
126
127         if (usb_disabled())
128                 return -ENODEV;
129
130         /* if (!dev->parent) {
131                 dev_err(dev, "Missing parent device\n");
132                 return -ENODEV;
133                 }*/
134
135         /* For DT boot, get platform data from parent. i.e. usbhshost */
136         /*if (dev->of_node) {
137                 pdata = dev_get_platdata(dev->parent);
138                 dev->platform_data = pdata;
139         }
140
141         if (!pdata) {
142                 dev_err(dev, "Missing platform data\n");
143                 return -ENODEV;
144                 }*/
145
146         irq = platform_get_irq(pdev, 0);
147         if (irq < 0) {
148                 dev_err(dev, "EHCI irq failed\n");
149                 return -ENODEV;
150         }
151
152         res =  platform_get_resource(pdev, IORESOURCE_MEM, 0);
153         regs = devm_ioremap_resource(dev, res);
154         if (IS_ERR(regs))
155                 return PTR_ERR(regs);
156
157         /*
158          * Right now device-tree probed devices don't get dma_mask set.
159          * Since shared usb code relies on it, set it here for now.
160          * Once we have dma capability bindings this can go away.
161          */
162         ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
163         if (ret)
164                 return ret;
165
166         ret = -ENODEV;
167         hcd = usb_create_hcd(&ehci_h20ahb_hc_driver, dev,
168                         dev_name(dev));
169         if (!hcd) {
170                 dev_err(dev, "Failed to create HCD\n");
171                 return -ENOMEM;
172         }
173
174         hcd->rsrc_start = res->start;
175         hcd->rsrc_len = resource_size(res);
176         hcd->regs = regs;
177         hcd_to_ehci(hcd)->caps = regs;
178
179         h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv;
180         h20ahb->nports = 1;
181
182         platform_set_drvdata(pdev, hcd);
183
184         /* get the PHY devices if needed */
185         for (i = 0 ; i < h20ahb->nports ; i++) {
186                 struct usb_phy *phy;
187
188                 /* get the PHY device */
189 #if 0
190                 if (dev->of_node)
191                         phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
192                 else
193                         phy = devm_usb_get_phy_dev(dev, i);
194 #endif
195                 phy = otg_ulpi_create(&ehci_h20ahb_phy_io_ops, 0);
196                 if (IS_ERR(phy)) {
197                         ret = PTR_ERR(phy);
198                         dev_err(dev, "Can't get PHY device for port %d: %d\n",
199                                         i, ret);
200                         goto err_phy;
201                 }
202                 phy->dev = dev;
203                 usb_add_phy_dev(phy);
204
205                 h20ahb->phy[i] = phy;
206                 phy->io_priv = hcd->regs + EHCI_INSNREG05_ULPI;
207
208 #if 0
209                 usb_phy_init(h20ahb->phy[i]);
210                 /* bring PHY out of suspend */
211                 usb_phy_set_suspend(h20ahb->phy[i], 0);
212 #endif
213         }
214
215         /* make the first port's phy the one used by hcd as well */
216         hcd->phy = h20ahb->phy[0];
217
218         pm_runtime_enable(dev);
219         pm_runtime_get_sync(dev);
220
221         /*
222          * An undocumented "feature" in the H20AHB EHCI controller,
223          * causes suspended ports to be taken out of suspend when
224          * the USBCMD.Run/Stop bit is cleared (for example when
225          * we do ehci_bus_suspend).
226          * This breaks suspend-resume if the root-hub is allowed
227          * to suspend. Writing 1 to this undocumented register bit
228          * disables this feature and restores normal behavior.
229          */
230         ehci_write(regs, EHCI_INSNREG04,
231                                 EHCI_INSNREG04_DISABLE_UNSUSPEND);
232
233         ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
234         if (ret) {
235                 dev_err(dev, "failed to add hcd with err %d\n", ret);
236                 goto err_pm_runtime;
237         }
238         device_wakeup_enable(hcd->self.controller);
239
240         /*
241          * Bring PHYs out of reset for non PHY modes.
242          * Even though HSIC mode is a PHY-less mode, the reset
243          * line exists between the chips and can be modelled
244          * as a PHY device for reset control.
245          */
246         for (i = 0; i < h20ahb->nports; i++) {
247                 usb_phy_init(h20ahb->phy[i]);
248                 /* bring PHY out of suspend */
249                 usb_phy_set_suspend(h20ahb->phy[i], 0);
250         }
251
252         return 0;
253
254 err_pm_runtime:
255         pm_runtime_put_sync(dev);
256
257 err_phy:
258         for (i = 0; i < h20ahb->nports; i++) {
259                 if (h20ahb->phy[i])
260                         usb_phy_shutdown(h20ahb->phy[i]);
261         }
262
263         usb_put_hcd(hcd);
264
265         return ret;
266 }
267
268
269 /**
270  * ehci_hcd_h20ahb_remove - shutdown processing for EHCI HCDs
271  * @pdev: USB Host Controller being removed
272  *
273  * Reverses the effect of usb_ehci_hcd_h20ahb_probe(), first invoking
274  * the HCD's stop() method.  It is always called from a thread
275  * context, normally "rmmod", "apmd", or something similar.
276  */
277 static int ehci_hcd_h20ahb_remove(struct platform_device *pdev)
278 {
279         struct device *dev = &pdev->dev;
280         struct usb_hcd *hcd = dev_get_drvdata(dev);
281         struct h20ahb_hcd *h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv;
282         int i;
283
284         usb_remove_hcd(hcd);
285
286         for (i = 0; i < h20ahb->nports; i++) {
287                 if (h20ahb->phy[i])
288                         usb_phy_shutdown(h20ahb->phy[i]);
289         }
290
291         usb_put_hcd(hcd);
292         pm_runtime_put_sync(dev);
293         pm_runtime_disable(dev);
294
295         return 0;
296 }
297
298 static const struct of_device_id h20ahb_ehci_dt_ids[] = {
299         { .compatible = "snps,ehci-h20ahb" },
300         { }
301 };
302
303 MODULE_DEVICE_TABLE(of, h20ahb_ehci_dt_ids);
304
305 static struct platform_driver ehci_hcd_h20ahb_driver = {
306         .probe                  = ehci_hcd_h20ahb_probe,
307         .remove                 = ehci_hcd_h20ahb_remove,
308         .shutdown               = usb_hcd_platform_shutdown,
309         /*.suspend              = ehci_hcd_h20ahb_suspend, */
310         /*.resume               = ehci_hcd_h20ahb_resume, */
311         .driver = {
312                 .name           = hcd_name,
313                 .of_match_table = h20ahb_ehci_dt_ids,
314         }
315 };
316
317 /*-------------------------------------------------------------------------*/
318
319 static int __init ehci_h20ahb_init(void)
320 {
321         if (usb_disabled())
322                 return -ENODEV;
323
324         pr_info("%s: " DRIVER_DESC "\n", hcd_name);
325
326         ehci_init_driver(&ehci_h20ahb_hc_driver, &ehci_h20ahb_overrides);
327         return platform_driver_register(&ehci_hcd_h20ahb_driver);
328 }
329 module_init(ehci_h20ahb_init);
330
331 static void __exit ehci_h20ahb_cleanup(void)
332 {
333         platform_driver_unregister(&ehci_hcd_h20ahb_driver);
334 }
335 module_exit(ehci_h20ahb_cleanup);
336
337 MODULE_ALIAS("platform:ehci-h20ahb");
338 MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
339
340 MODULE_DESCRIPTION(DRIVER_DESC);
341 MODULE_LICENSE("GPL");