blob: 9a05bff230bbe03f0081ad83d1d96f1780bab37f [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0
Anoop22ced6872011-02-24 19:26:28 +05302/*
3 * PMC MSP EHCI (Host Controller Driver) for USB.
4 *
5 * (C) Copyright 2006-2010 PMC-Sierra Inc
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 *
11 */
12
13/* includes */
14#include <linux/platform_device.h>
15#include <linux/gpio.h>
16#include <linux/usb.h>
17#include <msp_usb.h>
18
19/* stream disable*/
20#define USB_CTRL_MODE_STREAM_DISABLE 0x10
21
22/* threshold */
23#define USB_CTRL_FIFO_THRESH 0x00300000
24
25/* register offset for usb_mode */
26#define USB_EHCI_REG_USB_MODE 0x68
27
28/* register offset for usb fifo */
29#define USB_EHCI_REG_USB_FIFO 0x24
30
31/* register offset for usb status */
32#define USB_EHCI_REG_USB_STATUS 0x44
33
34/* serial/parallel transceiver */
35#define USB_EHCI_REG_BIT_STAT_STS (1<<29)
36
37/* TWI USB0 host device pin */
38#define MSP_PIN_USB0_HOST_DEV 49
39
40/* TWI USB1 host device pin */
41#define MSP_PIN_USB1_HOST_DEV 50
42
43
44static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
45{
46 u8 *base;
47 u8 *statreg;
48 u8 *fiforeg;
49 u32 val;
50 struct ehci_regs *reg_base = ehci->regs;
51
52 /* get register base */
53 base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
54 statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
55 fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
56
57 /* Disable controller mode stream */
58 val = ehci_readl(ehci, (u32 *)base);
59 ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
60 (u32 *)base);
61
62 /* clear STS to select parallel transceiver interface */
63 val = ehci_readl(ehci, (u32 *)statreg);
64 val = val & ~USB_EHCI_REG_BIT_STAT_STS;
65 ehci_writel(ehci, val, (u32 *)statreg);
66
67 /* write to set the proper fifo threshold */
68 ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
69
70 /* set TWI GPIO USB_HOST_DEV pin high */
71 gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
Anoop22ced6872011-02-24 19:26:28 +053072}
73
74/* called during probe() after chip reset completes */
75static int ehci_msp_setup(struct usb_hcd *hcd)
76{
77 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
78 int retval;
Alan Stern1a49e2a2012-07-09 15:55:14 -040079
Anoop22ced6872011-02-24 19:26:28 +053080 ehci->big_endian_mmio = 1;
81 ehci->big_endian_desc = 1;
82
83 ehci->caps = hcd->regs;
Anoop22ced6872011-02-24 19:26:28 +053084 hcd->has_tt = 1;
85
Alan Stern1a49e2a2012-07-09 15:55:14 -040086 retval = ehci_setup(hcd);
Anoop22ced6872011-02-24 19:26:28 +053087 if (retval)
88 return retval;
89
90 usb_hcd_tdi_set_mode(ehci);
Anoop22ced6872011-02-24 19:26:28 +053091
92 return retval;
93}
94
95
96/* configure so an HC device and id are always provided
97 * always called with process context; sleeping is OK
98 */
99
100static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
101{
102 struct resource *res;
103 struct platform_device *pdev = &dev->dev;
104 u32 res_len;
105 int retval;
106
107 /* MAB register space */
108 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
109 if (res == NULL)
110 return -ENOMEM;
Joe Perches28f65c112011-06-09 09:13:32 -0700111 res_len = resource_size(res);
Anoop22ced6872011-02-24 19:26:28 +0530112 if (!request_mem_region(res->start, res_len, "mab regs"))
113 return -EBUSY;
114
115 dev->mab_regs = ioremap_nocache(res->start, res_len);
116 if (dev->mab_regs == NULL) {
117 retval = -ENOMEM;
118 goto err1;
119 }
120
121 /* MSP USB register space */
122 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
123 if (res == NULL) {
124 retval = -ENOMEM;
125 goto err2;
126 }
Joe Perches28f65c112011-06-09 09:13:32 -0700127 res_len = resource_size(res);
Anoop22ced6872011-02-24 19:26:28 +0530128 if (!request_mem_region(res->start, res_len, "usbid regs")) {
129 retval = -EBUSY;
130 goto err2;
131 }
132 dev->usbid_regs = ioremap_nocache(res->start, res_len);
133 if (dev->usbid_regs == NULL) {
134 retval = -ENOMEM;
135 goto err3;
136 }
137
138 return 0;
139err3:
140 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
Joe Perches28f65c112011-06-09 09:13:32 -0700141 res_len = resource_size(res);
Anoop22ced6872011-02-24 19:26:28 +0530142 release_mem_region(res->start, res_len);
143err2:
144 iounmap(dev->mab_regs);
145err1:
146 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Joe Perches28f65c112011-06-09 09:13:32 -0700147 res_len = resource_size(res);
Anoop22ced6872011-02-24 19:26:28 +0530148 release_mem_region(res->start, res_len);
149 dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
150 return retval;
151}
152
153/**
154 * usb_hcd_msp_probe - initialize PMC MSP-based HCDs
155 * Context: !in_interrupt()
156 *
157 * Allocates basic resources for this USB host controller, and
158 * then invokes the start() method for the HCD associated with it
159 * through the hotplug entry's driver_data.
160 *
161 */
162int usb_hcd_msp_probe(const struct hc_driver *driver,
163 struct platform_device *dev)
164{
165 int retval;
166 struct usb_hcd *hcd;
167 struct resource *res;
168 struct ehci_hcd *ehci ;
169
170 hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
171 if (!hcd)
172 return -ENOMEM;
173
174 res = platform_get_resource(dev, IORESOURCE_MEM, 0);
175 if (res == NULL) {
176 pr_debug("No IOMEM resource info for %s.\n", dev->name);
177 retval = -ENOMEM;
178 goto err1;
179 }
180 hcd->rsrc_start = res->start;
Joe Perches28f65c112011-06-09 09:13:32 -0700181 hcd->rsrc_len = resource_size(res);
Anoop22ced6872011-02-24 19:26:28 +0530182 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
183 retval = -EBUSY;
184 goto err1;
185 }
186 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
187 if (!hcd->regs) {
188 pr_debug("ioremap failed");
189 retval = -ENOMEM;
190 goto err2;
191 }
192
193 res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
194 if (res == NULL) {
195 dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
196 retval = -ENOMEM;
197 goto err3;
198 }
199
200 /* Map non-EHCI register spaces */
201 retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
202 if (retval != 0)
203 goto err3;
204
205 ehci = hcd_to_ehci(hcd);
206 ehci->big_endian_mmio = 1;
207 ehci->big_endian_desc = 1;
208
209
210 retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
Peter Chen3c9740a2013-11-05 10:46:02 +0800211 if (retval == 0) {
212 device_wakeup_enable(hcd->self.controller);
Anoop22ced6872011-02-24 19:26:28 +0530213 return 0;
Peter Chen3c9740a2013-11-05 10:46:02 +0800214 }
Anoop22ced6872011-02-24 19:26:28 +0530215
216 usb_remove_hcd(hcd);
217err3:
218 iounmap(hcd->regs);
219err2:
220 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
221err1:
222 usb_put_hcd(hcd);
223
224 return retval;
225}
226
227
228
229/**
230 * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
231 * @dev: USB Host Controller being removed
232 * Context: !in_interrupt()
233 *
234 * Reverses the effect of usb_hcd_msp_probe(), first invoking
235 * the HCD's stop() method. It is always called from a thread
236 * context, normally "rmmod", "apmd", or something similar.
237 *
238 * may be called without controller electrically present
239 * may be called with controller, bus, and devices active
240 */
241void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev)
242{
243 usb_remove_hcd(hcd);
244 iounmap(hcd->regs);
245 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
246 usb_put_hcd(hcd);
247}
248
Anoop22ced6872011-02-24 19:26:28 +0530249static const struct hc_driver ehci_msp_hc_driver = {
250 .description = hcd_name,
251 .product_desc = "PMC MSP EHCI",
252 .hcd_priv_size = sizeof(struct ehci_hcd),
253
254 /*
255 * generic hardware linkage
256 */
Anoop22ced6872011-02-24 19:26:28 +0530257 .irq = ehci_irq,
Greg Kroah-Hartmanc04ee4b2013-09-23 13:32:51 -0700258 .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
Anoop22ced6872011-02-24 19:26:28 +0530259
260 /*
261 * basic lifecycle operations
262 */
Emil Goodea3b0f9b2013-06-16 13:46:38 +0200263 .reset = ehci_msp_setup,
Anoop22ced6872011-02-24 19:26:28 +0530264 .shutdown = ehci_shutdown,
265 .start = ehci_run,
266 .stop = ehci_stop,
267
268 /*
269 * managing i/o requests and associated device resources
270 */
271 .urb_enqueue = ehci_urb_enqueue,
272 .urb_dequeue = ehci_urb_dequeue,
273 .endpoint_disable = ehci_endpoint_disable,
274 .endpoint_reset = ehci_endpoint_reset,
275
276 /*
277 * scheduling support
278 */
279 .get_frame_number = ehci_get_frame,
280
281 /*
282 * root hub support
283 */
284 .hub_status_data = ehci_hub_status_data,
285 .hub_control = ehci_hub_control,
286 .bus_suspend = ehci_bus_suspend,
287 .bus_resume = ehci_bus_resume,
288 .relinquish_port = ehci_relinquish_port,
289 .port_handed_over = ehci_port_handed_over,
290
291 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
292};
293
294static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
295{
296 int ret;
297
298 pr_debug("In ehci_hcd_msp_drv_probe");
299
300 if (usb_disabled())
301 return -ENODEV;
302
303 gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
Anoop22ced6872011-02-24 19:26:28 +0530304
305 ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
306
307 return ret;
308}
309
310static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
311{
312 struct usb_hcd *hcd = platform_get_drvdata(pdev);
313
314 usb_hcd_msp_remove(hcd, pdev);
315
316 /* free TWI GPIO USB_HOST_DEV pin */
317 gpio_free(MSP_PIN_USB0_HOST_DEV);
Anoop22ced6872011-02-24 19:26:28 +0530318
319 return 0;
320}
321
322MODULE_ALIAS("pmcmsp-ehci");
323
324static struct platform_driver ehci_hcd_msp_driver = {
325 .probe = ehci_hcd_msp_drv_probe,
326 .remove = ehci_hcd_msp_drv_remove,
327 .driver = {
328 .name = "pmcmsp-ehci",
Anoop22ced6872011-02-24 19:26:28 +0530329 },
330};