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