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