blob: 46b5ce4214a2606718611ae0523303cccaff62ae [file] [log] [blame]
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +02001/*
2 * xhci-plat.c - xHCI host controller driver platform Bus Glue.
3 *
4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
5 * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
6 *
7 * A lot of code borrowed from the Linux xHCI driver.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
12 */
13
14#include <linux/platform_device.h>
Manu Gautamb5067272012-07-02 09:53:41 +053015#include <linux/pm_runtime.h>
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020016#include <linux/module.h>
17#include <linux/slab.h>
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +020018#include <linux/usb/otg.h>
Manu Gautambb825d72013-03-12 16:25:42 +053019#include <linux/usb/msm_hsusb.h>
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020020
21#include "xhci.h"
22
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053023#define SYNOPSIS_DWC3_VENDOR 0x5533
24
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +020025static struct usb_phy *phy;
26
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020027static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
28{
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053029 struct xhci_plat_data *pdata = dev->platform_data;
30
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020031 /*
32 * As of now platform drivers don't provide MSI support so we ensure
33 * here that the generic code does not try to make a pci_dev from our
34 * dev struct in order to setup MSI
35 */
36 xhci->quirks |= XHCI_BROKEN_MSI;
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053037
38 if (!pdata)
39 return;
40 else if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&
41 pdata->revision < 0x230A)
42 xhci->quirks |= XHCI_PORTSC_DELAY;
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020043}
44
45/* called during probe() after chip reset completes */
46static int xhci_plat_setup(struct usb_hcd *hcd)
47{
48 return xhci_gen_setup(hcd, xhci_plat_quirks);
49}
50
51static const struct hc_driver xhci_plat_xhci_driver = {
52 .description = "xhci-hcd",
53 .product_desc = "xHCI Host Controller",
54 .hcd_priv_size = sizeof(struct xhci_hcd *),
55
56 /*
57 * generic hardware linkage
58 */
59 .irq = xhci_irq,
60 .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
61
62 /*
63 * basic lifecycle operations
64 */
65 .reset = xhci_plat_setup,
66 .start = xhci_run,
67 .stop = xhci_stop,
68 .shutdown = xhci_shutdown,
69
70 /*
71 * managing i/o requests and associated device resources
72 */
73 .urb_enqueue = xhci_urb_enqueue,
74 .urb_dequeue = xhci_urb_dequeue,
75 .alloc_dev = xhci_alloc_dev,
76 .free_dev = xhci_free_dev,
77 .alloc_streams = xhci_alloc_streams,
78 .free_streams = xhci_free_streams,
79 .add_endpoint = xhci_add_endpoint,
80 .drop_endpoint = xhci_drop_endpoint,
81 .endpoint_reset = xhci_endpoint_reset,
82 .check_bandwidth = xhci_check_bandwidth,
83 .reset_bandwidth = xhci_reset_bandwidth,
84 .address_device = xhci_address_device,
85 .update_hub_device = xhci_update_hub_device,
86 .reset_device = xhci_discover_or_reset_device,
87
88 /*
89 * scheduling support
90 */
91 .get_frame_number = xhci_get_frame,
92
93 /* Root hub support */
94 .hub_control = xhci_hub_control,
95 .hub_status_data = xhci_hub_status_data,
96 .bus_suspend = xhci_bus_suspend,
97 .bus_resume = xhci_bus_resume,
98};
99
100static int xhci_plat_probe(struct platform_device *pdev)
101{
102 const struct hc_driver *driver;
103 struct xhci_hcd *xhci;
104 struct resource *res;
105 struct usb_hcd *hcd;
106 int ret;
107 int irq;
108
109 if (usb_disabled())
110 return -ENODEV;
111
112 driver = &xhci_plat_xhci_driver;
113
114 irq = platform_get_irq(pdev, 0);
115 if (irq < 0)
116 return -ENODEV;
117
118 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
119 if (!res)
120 return -ENODEV;
121
122 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
123 if (!hcd)
124 return -ENOMEM;
125
Vijayavardhan Vennapusa45145882013-01-03 14:11:58 +0530126 hcd_to_bus(hcd)->skip_resume = true;
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200127 hcd->rsrc_start = res->start;
128 hcd->rsrc_len = resource_size(res);
129
130 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
131 driver->description)) {
132 dev_dbg(&pdev->dev, "controller already in use\n");
133 ret = -EBUSY;
134 goto put_hcd;
135 }
136
137 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
138 if (!hcd->regs) {
139 dev_dbg(&pdev->dev, "error mapping memory\n");
140 ret = -EFAULT;
141 goto release_mem_region;
142 }
143
Manu Gautambb825d72013-03-12 16:25:42 +0530144 pm_runtime_set_active(&pdev->dev);
145 pm_runtime_enable(&pdev->dev);
146 pm_runtime_get_sync(&pdev->dev);
147
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200148 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
149 if (ret)
150 goto unmap_registers;
151
152 /* USB 2.0 roothub is stored in the platform_device now. */
153 hcd = dev_get_drvdata(&pdev->dev);
154 xhci = hcd_to_xhci(hcd);
155 xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
156 dev_name(&pdev->dev), hcd);
157 if (!xhci->shared_hcd) {
158 ret = -ENOMEM;
159 goto dealloc_usb2_hcd;
160 }
161
Vijayavardhan Vennapusa45145882013-01-03 14:11:58 +0530162 hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200163 /*
164 * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
165 * is called by usb_add_hcd().
166 */
167 *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
168
169 ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
170 if (ret)
171 goto put_usb3_hcd;
172
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200173 phy = usb_get_transceiver();
Manu Gautambb825d72013-03-12 16:25:42 +0530174 /* Register with OTG if present, ignore USB2 OTG using other PHY */
175 if (phy && phy->otg && !(phy->flags & ENABLE_SECONDARY_PHY)) {
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200176 dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200177 ret = otg_set_host(phy->otg, &hcd->self);
178 if (ret) {
179 dev_err(&pdev->dev, "%s otg_set_host failed\n",
180 __func__);
181 usb_put_transceiver(phy);
182 goto put_usb3_hcd;
183 }
Manu Gautamb5067272012-07-02 09:53:41 +0530184 } else {
185 pm_runtime_no_callbacks(&pdev->dev);
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200186 }
187
Manu Gautambb825d72013-03-12 16:25:42 +0530188 pm_runtime_put(&pdev->dev);
189
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200190 return 0;
191
192put_usb3_hcd:
193 usb_put_hcd(xhci->shared_hcd);
194
195dealloc_usb2_hcd:
196 usb_remove_hcd(hcd);
197
198unmap_registers:
199 iounmap(hcd->regs);
200
201release_mem_region:
202 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
203
204put_hcd:
205 usb_put_hcd(hcd);
206
207 return ret;
208}
209
210static int xhci_plat_remove(struct platform_device *dev)
211{
212 struct usb_hcd *hcd = platform_get_drvdata(dev);
213 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
214
Vijayavardhan Vennapusa45145882013-01-03 14:11:58 +0530215
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200216 usb_remove_hcd(xhci->shared_hcd);
217 usb_put_hcd(xhci->shared_hcd);
218
219 usb_remove_hcd(hcd);
220 iounmap(hcd->regs);
Manu Gautamf1fceddf2012-10-12 14:02:50 +0530221 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200222 usb_put_hcd(hcd);
223 kfree(xhci);
224
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200225 if (phy && phy->otg) {
226 otg_set_host(phy->otg, NULL);
227 usb_put_transceiver(phy);
228 }
229
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200230 return 0;
231}
232
Vijayavardhan Vennapusa45145882013-01-03 14:11:58 +0530233#ifdef CONFIG_PM_RUNTIME
234static int xhci_msm_runtime_idle(struct device *dev)
235{
236 dev_dbg(dev, "xhci msm runtime idle\n");
237 return 0;
238}
239
240static int xhci_msm_runtime_suspend(struct device *dev)
241{
242 dev_dbg(dev, "xhci msm runtime suspend\n");
243 /*
244 * Notify OTG about suspend. It takes care of
245 * putting the hardware in LPM.
246 */
247 if (phy)
248 return usb_phy_set_suspend(phy, 1);
249
250 return 0;
251}
252
253static int xhci_msm_runtime_resume(struct device *dev)
254{
255 dev_dbg(dev, "xhci msm runtime resume\n");
256
257 if (phy)
258 return usb_phy_set_suspend(phy, 0);
259
260 return 0;
261}
262#endif
263
264#ifdef CONFIG_PM_SLEEP
265static int xhci_msm_pm_suspend(struct device *dev)
266{
267 dev_dbg(dev, "xhci-msm PM suspend\n");
268
269 if (phy)
270 return usb_phy_set_suspend(phy, 1);
271
272 return 0;
273}
274
275static int xhci_msm_pm_resume(struct device *dev)
276{
277 dev_dbg(dev, "xhci-msm PM resume\n");
278
279 if (pm_runtime_suspended(dev))
280 return 0;
281
282 if (phy)
283 return usb_phy_set_suspend(phy, 0);
284
285 return 0;
286}
287#endif
288
289static const struct dev_pm_ops xhci_msm_dev_pm_ops = {
290 SET_SYSTEM_SLEEP_PM_OPS(xhci_msm_pm_suspend, xhci_msm_pm_resume)
291 SET_RUNTIME_PM_OPS(xhci_msm_runtime_suspend, xhci_msm_runtime_resume,
292 xhci_msm_runtime_idle)
293};
294
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200295static struct platform_driver usb_xhci_driver = {
296 .probe = xhci_plat_probe,
297 .remove = xhci_plat_remove,
298 .driver = {
299 .name = "xhci-hcd",
Vijayavardhan Vennapusa45145882013-01-03 14:11:58 +0530300 .pm = &xhci_msm_dev_pm_ops,
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200301 },
302};
303MODULE_ALIAS("platform:xhci-hcd");
304
305int xhci_register_plat(void)
306{
307 return platform_driver_register(&usb_xhci_driver);
308}
309
310void xhci_unregister_plat(void)
311{
312 platform_driver_unregister(&usb_xhci_driver);
313}