]> Git Repo - linux.git/blob - drivers/usb/host/ehci-pmcmsp.c
Merge tag 'powerpc-5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[linux.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(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(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  * @driver:     Pointer to hc driver instance
151  * @dev:        USB controller to probe
152  *
153  * Context: task context, might sleep
154  *
155  * Allocates basic resources for this USB host controller, and
156  * then invokes the start() method for the HCD associated with it
157  * through the hotplug entry's driver_data.
158  */
159 int usb_hcd_msp_probe(const struct hc_driver *driver,
160                           struct platform_device *dev)
161 {
162         int retval;
163         struct usb_hcd *hcd;
164         struct resource *res;
165         struct ehci_hcd         *ehci ;
166
167         hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
168         if (!hcd)
169                 return -ENOMEM;
170
171         res = platform_get_resource(dev, IORESOURCE_MEM, 0);
172         if (res == NULL) {
173                 pr_debug("No IOMEM resource info for %s.\n", dev->name);
174                 retval = -ENOMEM;
175                 goto err1;
176         }
177         hcd->rsrc_start = res->start;
178         hcd->rsrc_len = resource_size(res);
179         if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
180                 retval = -EBUSY;
181                 goto err1;
182         }
183         hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
184         if (!hcd->regs) {
185                 pr_debug("ioremap failed");
186                 retval = -ENOMEM;
187                 goto err2;
188         }
189
190         res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
191         if (res == NULL) {
192                 dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
193                 retval = -ENOMEM;
194                 goto err3;
195         }
196
197         /* Map non-EHCI register spaces */
198         retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
199         if (retval != 0)
200                 goto err3;
201
202         ehci = hcd_to_ehci(hcd);
203         ehci->big_endian_mmio = 1;
204         ehci->big_endian_desc = 1;
205
206
207         retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
208         if (retval == 0) {
209                 device_wakeup_enable(hcd->self.controller);
210                 return 0;
211         }
212
213         usb_remove_hcd(hcd);
214 err3:
215         iounmap(hcd->regs);
216 err2:
217         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
218 err1:
219         usb_put_hcd(hcd);
220
221         return retval;
222 }
223
224
225
226 /**
227  * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
228  * @hcd: USB Host Controller being removed
229  *
230  * Context: task context, might sleep
231  *
232  * Reverses the effect of usb_hcd_msp_probe(), first invoking
233  * the HCD's stop() method.  It is always called from a thread
234  * context, normally "rmmod", "apmd", or something similar.
235  *
236  * may be called without controller electrically present
237  * may be called with controller, bus, and devices active
238  */
239 static void usb_hcd_msp_remove(struct usb_hcd *hcd)
240 {
241         usb_remove_hcd(hcd);
242         iounmap(hcd->regs);
243         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
244         usb_put_hcd(hcd);
245 }
246
247 static const struct hc_driver ehci_msp_hc_driver = {
248         .description =          hcd_name,
249         .product_desc =         "PMC MSP EHCI",
250         .hcd_priv_size =        sizeof(struct ehci_hcd),
251
252         /*
253          * generic hardware linkage
254          */
255         .irq =                  ehci_irq,
256         .flags =                HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
257
258         /*
259          * basic lifecycle operations
260          */
261         .reset                  = ehci_msp_setup,
262         .shutdown               = ehci_shutdown,
263         .start                  = ehci_run,
264         .stop                   = ehci_stop,
265
266         /*
267          * managing i/o requests and associated device resources
268          */
269         .urb_enqueue            = ehci_urb_enqueue,
270         .urb_dequeue            = ehci_urb_dequeue,
271         .endpoint_disable       = ehci_endpoint_disable,
272         .endpoint_reset         = ehci_endpoint_reset,
273
274         /*
275          * scheduling support
276          */
277         .get_frame_number       = ehci_get_frame,
278
279         /*
280          * root hub support
281          */
282         .hub_status_data        = ehci_hub_status_data,
283         .hub_control            = ehci_hub_control,
284         .bus_suspend            = ehci_bus_suspend,
285         .bus_resume             = ehci_bus_resume,
286         .relinquish_port        = ehci_relinquish_port,
287         .port_handed_over       = ehci_port_handed_over,
288
289         .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
290 };
291
292 static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
293 {
294         int ret;
295
296         pr_debug("In ehci_hcd_msp_drv_probe");
297
298         if (usb_disabled())
299                 return -ENODEV;
300
301         gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
302
303         ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
304
305         return ret;
306 }
307
308 static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
309 {
310         struct usb_hcd *hcd = platform_get_drvdata(pdev);
311
312         usb_hcd_msp_remove(hcd);
313
314         /* free TWI GPIO USB_HOST_DEV pin */
315         gpio_free(MSP_PIN_USB0_HOST_DEV);
316
317         return 0;
318 }
319
320 MODULE_ALIAS("pmcmsp-ehci");
321
322 static struct platform_driver ehci_hcd_msp_driver = {
323         .probe          = ehci_hcd_msp_drv_probe,
324         .remove         = ehci_hcd_msp_drv_remove,
325         .driver         = {
326                 .name   = "pmcmsp-ehci",
327         },
328 };
This page took 0.049961 seconds and 4 git commands to generate.