USB: support DWC_OTG Driver Version3.10 and used by default
[firefly-linux-kernel-4.4.55.git] / drivers / usb / host / ehci-rk.c
1 /* ehci-msm.c - HSUSB Host Controller Driver Implementation\r
2  *\r
3  * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.\r
4  *\r
5  * Partly derived from ehci-fsl.c and ehci-hcd.c\r
6  * Copyright (c) 2000-2004 by David Brownell\r
7  * Copyright (c) 2005 MontaVista Software\r
8  *\r
9  * All source code in this file is licensed under the following license except\r
10  * where indicated.\r
11  *\r
12  * This program is free software; you can redistribute it and/or modify it\r
13  * under the terms of the GNU General Public License version 2 as published\r
14  * by the Free Software Foundation.\r
15  *\r
16  * This program is distributed in the hope that it will be useful,\r
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
19  *\r
20  * See the GNU General Public License for more details.\r
21  * You should have received a copy of the GNU General Public License\r
22  * along with this program; if not, you can find it at http://www.fsf.org\r
23  */\r
24 \r
25 # include <linux/platform_device.h>\r
26 # include <linux/clk.h>\r
27 # include <linux/err.h>\r
28 # include <linux/device.h>\r
29 # include <linux/of.h>\r
30 # include <linux/of_platform.h>\r
31 # include "ehci.h"\r
32 #ifdef CONFIG_DWC_OTG_274\r
33 # include "../dwc_otg/usbdev_rk.h"\r
34 #endif\r
35 #ifdef CONFIG_DWC_OTG_310\r
36 # include "../dwc_otg_310/usbdev_rk.h"\r
37 #endif\r
38 \r
39 static int rkehci_status = 1;\r
40 static struct ehci_hcd *g_ehci;\r
41 #define EHCI_DEVICE_FILE        "/sys/devices/platform/rk_hsusb_host/ehci_power"\r
42 #define EHCI_PRINT(x...)        printk( KERN_INFO "EHCI: " x )\r
43 \r
44 extern struct rkehci_platform_data rkhsic_pdata;\r
45 \r
46 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)\r
47 {\r
48         unsigned port;\r
49 \r
50         if (!HCS_PPC (ehci->hcs_params))\r
51                 return;\r
52 \r
53         ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down");\r
54         for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )\r
55                 (void) ehci_hub_control(ehci_to_hcd(ehci),\r
56                                 is_on ? SetPortFeature : ClearPortFeature,\r
57                                 USB_PORT_FEAT_POWER,\r
58                                 port--, NULL, 0);\r
59         /* Flush those writes */\r
60         ehci_readl(ehci, &ehci->regs->command);\r
61         msleep(20);\r
62 }\r
63 \r
64 static struct hc_driver rk_hc_driver = {\r
65         .description            = hcd_name,\r
66         .product_desc           = "Rockchip On-Chip EHCI Host Controller",\r
67         .hcd_priv_size          = sizeof(struct ehci_hcd),\r
68 \r
69         /*\r
70          * generic hardware linkage\r
71          */\r
72         .irq                    = ehci_irq,\r
73         .flags                  = HCD_USB2 | HCD_MEMORY,\r
74 \r
75         .reset                  = ehci_init,\r
76         .start                  = ehci_run,\r
77 \r
78         .stop                   = ehci_stop,\r
79         .shutdown               = ehci_shutdown,\r
80 \r
81         /*\r
82          * managing i/o requests and associated device resources\r
83          */\r
84         .urb_enqueue            = ehci_urb_enqueue,\r
85         .urb_dequeue            = ehci_urb_dequeue,\r
86         .endpoint_disable       = ehci_endpoint_disable,\r
87         .endpoint_reset         = ehci_endpoint_reset,\r
88         .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,\r
89 \r
90         /*\r
91          * scheduling support\r
92          */\r
93         .get_frame_number       = ehci_get_frame,\r
94 \r
95         /*\r
96          * root hub support\r
97          */\r
98         .hub_status_data        = ehci_hub_status_data,\r
99         .hub_control            = ehci_hub_control,\r
100         .relinquish_port        = ehci_relinquish_port,\r
101         .port_handed_over       = ehci_port_handed_over,\r
102 \r
103         /*\r
104          * PM support\r
105          */\r
106 #ifdef CONFIG_PM\r
107         .bus_suspend            = ehci_bus_suspend,\r
108         .bus_resume             = ehci_bus_resume,\r
109 #endif\r
110 };\r
111 \r
112 static ssize_t ehci_power_show( struct device *_dev, \r
113                                 struct device_attribute *attr, char *buf) \r
114 {\r
115         return sprintf(buf, "%d\n", rkehci_status);\r
116 }\r
117 static ssize_t ehci_power_store( struct device *_dev,\r
118                                         struct device_attribute *attr, \r
119                                         const char *buf, size_t count ) \r
120 {\r
121         uint32_t val = simple_strtoul(buf, NULL, 16);\r
122         struct usb_hcd *hcd = dev_get_drvdata(_dev);\r
123         struct ehci_hcd *ehci = hcd_to_ehci(hcd);\r
124         struct rkehci_platform_data *pldata = _dev->platform_data;\r
125 \r
126         printk("%s: %d setting to: %d\n", __func__, rkehci_status, val);\r
127         if(val == rkehci_status)\r
128                 goto out;\r
129         \r
130         rkehci_status = val;\r
131         switch(val){\r
132                 case 0: //power down\r
133                         ehci_port_power(ehci, 0);\r
134                         writel_relaxed(0 ,hcd->regs +0xb0);\r
135                         dsb();\r
136                         msleep(5);\r
137                         usb_remove_hcd(hcd);\r
138                         break;\r
139                 case 1: // power on\r
140                         pldata->soft_reset();\r
141                         usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED);\r
142         \r
143                         ehci_port_power(ehci, 1);\r
144                         writel_relaxed(1 ,hcd->regs +0xb0);\r
145                         writel_relaxed(0x1d4d ,hcd->regs +0x90);\r
146                         writel_relaxed(0x4 ,hcd->regs +0xa0);\r
147                         dsb();\r
148                         break;\r
149                 default:\r
150                         break;\r
151         }\r
152 out:\r
153         return count;\r
154 }\r
155 static DEVICE_ATTR(ehci_power, S_IRUGO|S_IWUSR, ehci_power_show, ehci_power_store);\r
156 \r
157 static ssize_t debug_show( struct device *_dev,\r
158                                 struct device_attribute *attr, char *buf)\r
159 {\r
160         volatile uint32_t *addr;\r
161 \r
162         EHCI_PRINT("******** EHCI Capability Registers **********\n");\r
163         addr = &g_ehci->caps->hc_capbase;\r
164         EHCI_PRINT("HCIVERSION / CAPLENGTH  @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
165         addr = &g_ehci->caps->hcs_params;\r
166         EHCI_PRINT("HCSPARAMS               @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
167         addr = &g_ehci->caps->hcc_params;\r
168         EHCI_PRINT("HCCPARAMS               @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
169         EHCI_PRINT("********* EHCI Operational Registers *********\n");\r
170         addr = &g_ehci->regs->command;\r
171         EHCI_PRINT("USBCMD                  @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
172         addr = &g_ehci->regs->status;\r
173         EHCI_PRINT("USBSTS                  @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
174         addr = &g_ehci->regs->intr_enable;\r
175         EHCI_PRINT("USBINTR                 @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
176         addr = &g_ehci->regs->frame_index;\r
177         EHCI_PRINT("FRINDEX                 @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
178         addr = &g_ehci->regs->segment;\r
179         EHCI_PRINT("CTRLDSSEGMENT           @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
180         addr = &g_ehci->regs->frame_list;\r
181         EHCI_PRINT("PERIODICLISTBASE        @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); \r
182         addr = &g_ehci->regs->async_next;\r
183         EHCI_PRINT("ASYNCLISTADDR           @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
184         addr = &g_ehci->regs->configured_flag;\r
185         EHCI_PRINT("CONFIGFLAG              @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
186         addr = g_ehci->regs->port_status;\r
187         EHCI_PRINT("PORTSC                  @0x%08x:  0x%08x\n", (uint32_t)addr, readl_relaxed(addr));\r
188         return sprintf(buf, "EHCI Registers Dump\n");\r
189 }\r
190 static DEVICE_ATTR(debug_ehci, S_IRUGO, debug_show, NULL);\r
191 \r
192 static int ehci_rk_probe(struct platform_device *pdev)\r
193 {\r
194         struct usb_hcd *hcd;\r
195         struct ehci_hcd *ehci;\r
196         struct resource *res;\r
197         struct device *dev = &pdev->dev;\r
198         struct rkehci_platform_data *pldata;\r
199         int ret;\r
200         int retval = 0;\r
201         static u64 usb_dmamask = 0xffffffffUL;\r
202         struct device_node *node = pdev->dev.of_node;\r
203 \r
204         dev_dbg(&pdev->dev, "ehci_rk proble\n");\r
205 \r
206         dev->platform_data = &rkhsic_pdata;\r
207         pldata = dev->platform_data;\r
208         pldata->dev = dev;\r
209 \r
210         if (!node) {\r
211                 dev_err(dev, "device node not found\n");\r
212                 return -EINVAL;\r
213         }\r
214 \r
215         dev->dma_mask = &usb_dmamask;\r
216 \r
217         retval = device_create_file(dev, &dev_attr_ehci_power);\r
218         retval = device_create_file(dev, &dev_attr_debug_ehci);\r
219         hcd = usb_create_hcd(&rk_hc_driver, &pdev->dev, dev_name(&pdev->dev));\r
220         if (!hcd) {\r
221                 dev_err(&pdev->dev, "Unable to create HCD\n");\r
222                 return  -ENOMEM;\r
223         }\r
224 \r
225         pldata->hw_init();\r
226         pldata->clock_init(pldata);\r
227         pldata->clock_enable(pldata, 1);\r
228 \r
229         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
230         if (!res) {\r
231                 dev_err(&pdev->dev, "Unable to get memory resource\n");\r
232                 ret = -ENODEV;\r
233                 goto put_hcd;\r
234         }\r
235 \r
236         hcd->rsrc_start = res->start;\r
237         hcd->rsrc_len = resource_size(res);\r
238         hcd->regs = devm_ioremap_resource(dev, res);\r
239 \r
240         if (!hcd->regs) {\r
241                 dev_err(&pdev->dev, "ioremap failed\n");\r
242                 ret = -ENOMEM;\r
243                 goto put_hcd;\r
244         }\r
245         \r
246         hcd->irq = platform_get_irq(pdev, 0);\r
247         if (hcd->irq < 0) {\r
248                 dev_err(&pdev->dev, "Unable to get IRQ resource\n");\r
249                 ret = hcd->irq;\r
250                 goto put_hcd;\r
251         }\r
252         \r
253         ehci = hcd_to_ehci(hcd);\r
254         ehci->caps = hcd->regs;\r
255         ehci->regs = hcd->regs + 0x10;\r
256         printk("%s %p %p\n", __func__, ehci->caps, ehci->regs);\r
257     \r
258         dbg_hcs_params(ehci, "reset");\r
259         dbg_hcc_params(ehci, "reset");\r
260 \r
261         ehci->hcs_params = readl(&ehci->caps->hcs_params);\r
262 \r
263         ret = usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED);\r
264         if (ret) {\r
265                 dev_err(&pdev->dev, "Failed to add USB HCD\n");\r
266                 goto unmap;\r
267         }\r
268         \r
269         g_ehci = ehci;\r
270         ehci_port_power(ehci, 1);\r
271         writel_relaxed(1 ,hcd->regs +0xb0);\r
272         writel_relaxed(0x1d4d ,hcd->regs +0x90);\r
273         writel_relaxed(0x4 ,hcd->regs +0xa0);\r
274         dsb();\r
275 \r
276 \r
277         printk("%s ok\n", __func__);\r
278 \r
279         return 0;\r
280 \r
281 unmap:\r
282         iounmap(hcd->regs);\r
283 put_hcd:\r
284         usb_put_hcd(hcd);\r
285 \r
286         return ret;\r
287 }\r
288 \r
289 static int ehci_rk_remove(struct platform_device *pdev)\r
290 {\r
291         struct usb_hcd *hcd = platform_get_drvdata(pdev);\r
292 \r
293         usb_put_hcd(hcd);\r
294 \r
295         return 0;\r
296 }\r
297 \r
298 #ifdef CONFIG_PM\r
299 static int ehci_rk_pm_suspend(struct device *dev)\r
300 {\r
301         struct usb_hcd *hcd = dev_get_drvdata(dev);\r
302         bool wakeup = device_may_wakeup(dev);\r
303 \r
304         dev_dbg(dev, "ehci-rk PM suspend\n");\r
305 \r
306         /*\r
307          * EHCI helper function has also the same check before manipulating\r
308          * port wakeup flags.  We do check here the same condition before\r
309          * calling the same helper function to avoid bringing hardware\r
310          * from Low power mode when there is no need for adjusting port\r
311          * wakeup flags.\r
312          */\r
313         if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {\r
314                 pm_runtime_resume(dev);\r
315                 ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),\r
316                                 wakeup);\r
317         }\r
318 \r
319         return 0;\r
320 }\r
321 \r
322 static int ehci_rk_pm_resume(struct device *dev)\r
323 {\r
324         struct usb_hcd *hcd = dev_get_drvdata(dev);\r
325 \r
326         dev_dbg(dev, "ehci-rk PM resume\n");\r
327         ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));\r
328 \r
329         return 0;\r
330 }\r
331 #else\r
332 #define ehci_rk_pm_suspend      NULL\r
333 #define ehci_rk_pm_resume       NULL\r
334 #endif\r
335 \r
336 static const struct dev_pm_ops ehci_rk_dev_pm_ops = {\r
337         .suspend         = ehci_rk_pm_suspend,\r
338         .resume          = ehci_rk_pm_resume,\r
339 };\r
340 \r
341 static struct of_device_id rk_hsic_of_match[] = {\r
342         { .compatible = "rockchip,rk_hsic_host", },\r
343         { },\r
344 };\r
345 \r
346 MODULE_DEVICE_TABLE(of, rk_hsic_of_match);\r
347 \r
348 static struct platform_driver ehci_rk_driver = {\r
349         .probe  = ehci_rk_probe,\r
350         .remove = ehci_rk_remove,\r
351         .driver = {\r
352                    .name = "rk_hsic_host",\r
353                    .of_match_table = of_match_ptr(rk_hsic_of_match),\r
354 #ifdef CONFIG_PM\r
355                    .pm = &ehci_rk_dev_pm_ops,\r
356 #endif\r
357         },\r
358 };\r
359 \r