blob: d895f27de63cad65907ec1d0b5b8970cfdb6aa7c [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>
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020019
20#include "xhci.h"
21
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053022#define SYNOPSIS_DWC3_VENDOR 0x5533
23
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +020024static struct usb_phy *phy;
25
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020026static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
27{
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053028 struct xhci_plat_data *pdata = dev->platform_data;
29
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020030 /*
31 * As of now platform drivers don't provide MSI support so we ensure
32 * here that the generic code does not try to make a pci_dev from our
33 * dev struct in order to setup MSI
34 */
35 xhci->quirks |= XHCI_BROKEN_MSI;
Pavankumar Kondetibd348b62012-06-12 13:38:59 +053036
37 if (!pdata)
38 return;
39 else if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&
40 pdata->revision < 0x230A)
41 xhci->quirks |= XHCI_PORTSC_DELAY;
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +020042}
43
44/* called during probe() after chip reset completes */
45static int xhci_plat_setup(struct usb_hcd *hcd)
46{
47 return xhci_gen_setup(hcd, xhci_plat_quirks);
48}
49
50static const struct hc_driver xhci_plat_xhci_driver = {
51 .description = "xhci-hcd",
52 .product_desc = "xHCI Host Controller",
53 .hcd_priv_size = sizeof(struct xhci_hcd *),
54
55 /*
56 * generic hardware linkage
57 */
58 .irq = xhci_irq,
59 .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
60
61 /*
62 * basic lifecycle operations
63 */
64 .reset = xhci_plat_setup,
65 .start = xhci_run,
66 .stop = xhci_stop,
67 .shutdown = xhci_shutdown,
68
69 /*
70 * managing i/o requests and associated device resources
71 */
72 .urb_enqueue = xhci_urb_enqueue,
73 .urb_dequeue = xhci_urb_dequeue,
74 .alloc_dev = xhci_alloc_dev,
75 .free_dev = xhci_free_dev,
76 .alloc_streams = xhci_alloc_streams,
77 .free_streams = xhci_free_streams,
78 .add_endpoint = xhci_add_endpoint,
79 .drop_endpoint = xhci_drop_endpoint,
80 .endpoint_reset = xhci_endpoint_reset,
81 .check_bandwidth = xhci_check_bandwidth,
82 .reset_bandwidth = xhci_reset_bandwidth,
83 .address_device = xhci_address_device,
84 .update_hub_device = xhci_update_hub_device,
85 .reset_device = xhci_discover_or_reset_device,
86
87 /*
88 * scheduling support
89 */
90 .get_frame_number = xhci_get_frame,
91
92 /* Root hub support */
93 .hub_control = xhci_hub_control,
94 .hub_status_data = xhci_hub_status_data,
95 .bus_suspend = xhci_bus_suspend,
96 .bus_resume = xhci_bus_resume,
97};
98
99static int xhci_plat_probe(struct platform_device *pdev)
100{
101 const struct hc_driver *driver;
102 struct xhci_hcd *xhci;
103 struct resource *res;
104 struct usb_hcd *hcd;
105 int ret;
106 int irq;
107
108 if (usb_disabled())
109 return -ENODEV;
110
111 driver = &xhci_plat_xhci_driver;
112
113 irq = platform_get_irq(pdev, 0);
114 if (irq < 0)
115 return -ENODEV;
116
117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
118 if (!res)
119 return -ENODEV;
120
121 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
122 if (!hcd)
123 return -ENOMEM;
124
125 hcd->rsrc_start = res->start;
126 hcd->rsrc_len = resource_size(res);
127
128 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
129 driver->description)) {
130 dev_dbg(&pdev->dev, "controller already in use\n");
131 ret = -EBUSY;
132 goto put_hcd;
133 }
134
135 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
136 if (!hcd->regs) {
137 dev_dbg(&pdev->dev, "error mapping memory\n");
138 ret = -EFAULT;
139 goto release_mem_region;
140 }
141
142 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
143 if (ret)
144 goto unmap_registers;
145
146 /* USB 2.0 roothub is stored in the platform_device now. */
147 hcd = dev_get_drvdata(&pdev->dev);
148 xhci = hcd_to_xhci(hcd);
149 xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
150 dev_name(&pdev->dev), hcd);
151 if (!xhci->shared_hcd) {
152 ret = -ENOMEM;
153 goto dealloc_usb2_hcd;
154 }
155
156 /*
157 * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
158 * is called by usb_add_hcd().
159 */
160 *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
161
162 ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
163 if (ret)
164 goto put_usb3_hcd;
165
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200166 phy = usb_get_transceiver();
167 if (phy && phy->otg) {
168 dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
169 hcd->driver->stop(hcd);
170 ret = otg_set_host(phy->otg, &hcd->self);
171 if (ret) {
172 dev_err(&pdev->dev, "%s otg_set_host failed\n",
173 __func__);
174 usb_put_transceiver(phy);
175 goto put_usb3_hcd;
176 }
Manu Gautamb5067272012-07-02 09:53:41 +0530177 } else {
178 pm_runtime_no_callbacks(&pdev->dev);
179 pm_runtime_set_active(&pdev->dev);
180 pm_runtime_enable(&pdev->dev);
181 pm_runtime_get(&pdev->dev);
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200182 }
183
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200184 return 0;
185
186put_usb3_hcd:
187 usb_put_hcd(xhci->shared_hcd);
188
189dealloc_usb2_hcd:
190 usb_remove_hcd(hcd);
191
192unmap_registers:
193 iounmap(hcd->regs);
194
195release_mem_region:
196 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
197
198put_hcd:
199 usb_put_hcd(hcd);
200
201 return ret;
202}
203
204static int xhci_plat_remove(struct platform_device *dev)
205{
206 struct usb_hcd *hcd = platform_get_drvdata(dev);
207 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
208
209 usb_remove_hcd(xhci->shared_hcd);
210 usb_put_hcd(xhci->shared_hcd);
211
212 usb_remove_hcd(hcd);
213 iounmap(hcd->regs);
214 usb_put_hcd(hcd);
215 kfree(xhci);
216
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200217 if (phy && phy->otg) {
218 otg_set_host(phy->otg, NULL);
219 usb_put_transceiver(phy);
Manu Gautamb5067272012-07-02 09:53:41 +0530220 } else {
221 pm_runtime_put(&dev->dev);
222 pm_runtime_disable(&dev->dev);
Ido Shayevitzcdeef4c2012-05-29 13:17:41 +0200223 }
224
Sebastian Andrzej Siewior3429e912012-03-13 16:57:41 +0200225 return 0;
226}
227
228static struct platform_driver usb_xhci_driver = {
229 .probe = xhci_plat_probe,
230 .remove = xhci_plat_remove,
231 .driver = {
232 .name = "xhci-hcd",
233 },
234};
235MODULE_ALIAS("platform:xhci-hcd");
236
237int xhci_register_plat(void)
238{
239 return platform_driver_register(&usb_xhci_driver);
240}
241
242void xhci_unregister_plat(void)
243{
244 platform_driver_unregister(&usb_xhci_driver);
245}