diff options
author | David Brownell <david-b@pacbell.net> | 2005-05-07 16:20:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-27 17:43:54 -0400 |
commit | 9198769363d4dc1d63d49ecb2e2b189aceb42d94 (patch) | |
tree | 9d031c4c97e652100438f59732db79e16d6dc2bc | |
parent | 988199fe34411b413d5a388fc751c91eb4686f36 (diff) |
[PATCH] USB: pxa2xx_udc updates
This has several small updates to the px2xx UDC driver:
* small fixes from Eugeny S. Mints <emints@ru.mvista.com>
- local_irq_save() around potential endpoint disable race
- fix handling of enqueue to OUT endpoints (potential oops)
* add shutdown() method to disable any D+ pullup
* rename methods accessing raw signals, referencing the signals
* describes itself as for "pxa25x", since pxa27x is different
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/gadget/pxa2xx_udc.c | 43 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa2xx_udc.h | 10 |
2 files changed, 32 insertions, 21 deletions
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index b8b4524ed746..6a0b957af335 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * linux/drivers/usb/gadget/pxa2xx_udc.c | 2 | * linux/drivers/usb/gadget/pxa2xx_udc.c |
3 | * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers | 3 | * Intel PXA25x and IXP4xx on-chip full speed USB device controllers |
4 | * | 4 | * |
5 | * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) | 5 | * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) |
6 | * Copyright (C) 2003 Robert Schwebel, Pengutronix | 6 | * Copyright (C) 2003 Robert Schwebel, Pengutronix |
@@ -63,7 +63,7 @@ | |||
63 | 63 | ||
64 | 64 | ||
65 | /* | 65 | /* |
66 | * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx | 66 | * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x |
67 | * series processors. The UDC for the IXP 4xx series is very similar. | 67 | * series processors. The UDC for the IXP 4xx series is very similar. |
68 | * There are fifteen endpoints, in addition to ep0. | 68 | * There are fifteen endpoints, in addition to ep0. |
69 | * | 69 | * |
@@ -79,8 +79,8 @@ | |||
79 | * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. | 79 | * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. |
80 | */ | 80 | */ |
81 | 81 | ||
82 | #define DRIVER_VERSION "14-Dec-2003" | 82 | #define DRIVER_VERSION "4-May-2005" |
83 | #define DRIVER_DESC "PXA 2xx USB Device Controller driver" | 83 | #define DRIVER_DESC "PXA 25x USB Device Controller driver" |
84 | 84 | ||
85 | 85 | ||
86 | static const char driver_name [] = "pxa2xx_udc"; | 86 | static const char driver_name [] = "pxa2xx_udc"; |
@@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, | |||
290 | static int pxa2xx_ep_disable (struct usb_ep *_ep) | 290 | static int pxa2xx_ep_disable (struct usb_ep *_ep) |
291 | { | 291 | { |
292 | struct pxa2xx_ep *ep; | 292 | struct pxa2xx_ep *ep; |
293 | unsigned long flags; | ||
293 | 294 | ||
294 | ep = container_of (_ep, struct pxa2xx_ep, ep); | 295 | ep = container_of (_ep, struct pxa2xx_ep, ep); |
295 | if (!_ep || !ep->desc) { | 296 | if (!_ep || !ep->desc) { |
@@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) | |||
297 | _ep ? ep->ep.name : NULL); | 298 | _ep ? ep->ep.name : NULL); |
298 | return -EINVAL; | 299 | return -EINVAL; |
299 | } | 300 | } |
301 | local_irq_save(flags); | ||
302 | |||
300 | nuke (ep, -ESHUTDOWN); | 303 | nuke (ep, -ESHUTDOWN); |
301 | 304 | ||
302 | #ifdef USE_DMA | 305 | #ifdef USE_DMA |
@@ -313,6 +316,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) | |||
313 | ep->desc = NULL; | 316 | ep->desc = NULL; |
314 | ep->stopped = 1; | 317 | ep->stopped = 1; |
315 | 318 | ||
319 | local_irq_restore(flags); | ||
316 | DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); | 320 | DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); |
317 | return 0; | 321 | return 0; |
318 | } | 322 | } |
@@ -971,10 +975,10 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) | |||
971 | kick_dma(ep, req); | 975 | kick_dma(ep, req); |
972 | #endif | 976 | #endif |
973 | /* can the FIFO can satisfy the request immediately? */ | 977 | /* can the FIFO can satisfy the request immediately? */ |
974 | } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 | 978 | } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { |
975 | && (*ep->reg_udccs & UDCCS_BI_TFS) != 0 | 979 | if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 |
976 | && write_fifo(ep, req)) { | 980 | && write_fifo(ep, req)) |
977 | req = NULL; | 981 | req = NULL; |
978 | } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 | 982 | } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 |
979 | && read_fifo(ep, req)) { | 983 | && read_fifo(ep, req)) { |
980 | req = NULL; | 984 | req = NULL; |
@@ -1290,7 +1294,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, | |||
1290 | "%s version: %s\nGadget driver: %s\nHost %s\n\n", | 1294 | "%s version: %s\nGadget driver: %s\nHost %s\n\n", |
1291 | driver_name, DRIVER_VERSION SIZE_STR DMASTR, | 1295 | driver_name, DRIVER_VERSION SIZE_STR DMASTR, |
1292 | dev->driver ? dev->driver->driver.name : "(none)", | 1296 | dev->driver ? dev->driver->driver.name : "(none)", |
1293 | is_usb_connected() ? "full speed" : "disconnected"); | 1297 | is_vbus_present() ? "full speed" : "disconnected"); |
1294 | size -= t; | 1298 | size -= t; |
1295 | next += t; | 1299 | next += t; |
1296 | 1300 | ||
@@ -1339,7 +1343,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, | |||
1339 | next += t; | 1343 | next += t; |
1340 | } | 1344 | } |
1341 | 1345 | ||
1342 | if (!is_usb_connected() || !dev->driver) | 1346 | if (!is_vbus_present() || !dev->driver) |
1343 | goto done; | 1347 | goto done; |
1344 | 1348 | ||
1345 | t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", | 1349 | t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", |
@@ -1454,7 +1458,7 @@ static void udc_disable(struct pxa2xx_udc *dev) | |||
1454 | UFNRH = UFNRH_SIM; | 1458 | UFNRH = UFNRH_SIM; |
1455 | 1459 | ||
1456 | /* if hardware supports it, disconnect from usb */ | 1460 | /* if hardware supports it, disconnect from usb */ |
1457 | make_usb_disappear(); | 1461 | pullup_off(); |
1458 | 1462 | ||
1459 | udc_clear_mask_UDCCR(UDCCR_UDE); | 1463 | udc_clear_mask_UDCCR(UDCCR_UDE); |
1460 | 1464 | ||
@@ -1567,7 +1571,7 @@ static void udc_enable (struct pxa2xx_udc *dev) | |||
1567 | UICR0 &= ~UICR0_IM0; | 1571 | UICR0 &= ~UICR0_IM0; |
1568 | 1572 | ||
1569 | /* if hardware supports it, pullup D+ and wait for reset */ | 1573 | /* if hardware supports it, pullup D+ and wait for reset */ |
1570 | let_usb_appear(); | 1574 | pullup_on(); |
1571 | } | 1575 | } |
1572 | 1576 | ||
1573 | 1577 | ||
@@ -2052,10 +2056,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) | |||
2052 | if (unlikely(udccr & UDCCR_SUSIR)) { | 2056 | if (unlikely(udccr & UDCCR_SUSIR)) { |
2053 | udc_ack_int_UDCCR(UDCCR_SUSIR); | 2057 | udc_ack_int_UDCCR(UDCCR_SUSIR); |
2054 | handled = 1; | 2058 | handled = 1; |
2055 | DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() | 2059 | DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() |
2056 | ? "" : "+disconnect"); | 2060 | ? "" : "+disconnect"); |
2057 | 2061 | ||
2058 | if (!is_usb_connected()) | 2062 | if (!is_vbus_present()) |
2059 | stop_activity(dev, dev->driver); | 2063 | stop_activity(dev, dev->driver); |
2060 | else if (dev->gadget.speed != USB_SPEED_UNKNOWN | 2064 | else if (dev->gadget.speed != USB_SPEED_UNKNOWN |
2061 | && dev->driver | 2065 | && dev->driver |
@@ -2073,7 +2077,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) | |||
2073 | if (dev->gadget.speed != USB_SPEED_UNKNOWN | 2077 | if (dev->gadget.speed != USB_SPEED_UNKNOWN |
2074 | && dev->driver | 2078 | && dev->driver |
2075 | && dev->driver->resume | 2079 | && dev->driver->resume |
2076 | && is_usb_connected()) | 2080 | && is_vbus_present()) |
2077 | dev->driver->resume(&dev->gadget); | 2081 | dev->driver->resume(&dev->gadget); |
2078 | } | 2082 | } |
2079 | 2083 | ||
@@ -2509,7 +2513,7 @@ static int __init pxa2xx_udc_probe(struct device *_dev) | |||
2509 | udc_disable(dev); | 2513 | udc_disable(dev); |
2510 | udc_reinit(dev); | 2514 | udc_reinit(dev); |
2511 | 2515 | ||
2512 | dev->vbus = is_usb_connected(); | 2516 | dev->vbus = is_vbus_present(); |
2513 | 2517 | ||
2514 | /* irq setup after old hardware state is cleaned up */ | 2518 | /* irq setup after old hardware state is cleaned up */ |
2515 | retval = request_irq(IRQ_USB, pxa2xx_udc_irq, | 2519 | retval = request_irq(IRQ_USB, pxa2xx_udc_irq, |
@@ -2555,6 +2559,12 @@ lubbock_fail0: | |||
2555 | 2559 | ||
2556 | return 0; | 2560 | return 0; |
2557 | } | 2561 | } |
2562 | |||
2563 | static void pxa2xx_udc_shutdown(struct device *_dev) | ||
2564 | { | ||
2565 | pullup_off(); | ||
2566 | } | ||
2567 | |||
2558 | static int __exit pxa2xx_udc_remove(struct device *_dev) | 2568 | static int __exit pxa2xx_udc_remove(struct device *_dev) |
2559 | { | 2569 | { |
2560 | struct pxa2xx_udc *dev = dev_get_drvdata(_dev); | 2570 | struct pxa2xx_udc *dev = dev_get_drvdata(_dev); |
@@ -2624,6 +2634,7 @@ static struct device_driver udc_driver = { | |||
2624 | .name = "pxa2xx-udc", | 2634 | .name = "pxa2xx-udc", |
2625 | .bus = &platform_bus_type, | 2635 | .bus = &platform_bus_type, |
2626 | .probe = pxa2xx_udc_probe, | 2636 | .probe = pxa2xx_udc_probe, |
2637 | .shutdown = pxa2xx_udc_shutdown, | ||
2627 | .remove = __exit_p(pxa2xx_udc_remove), | 2638 | .remove = __exit_p(pxa2xx_udc_remove), |
2628 | .suspend = pxa2xx_udc_suspend, | 2639 | .suspend = pxa2xx_udc_suspend, |
2629 | .resume = pxa2xx_udc_resume, | 2640 | .resume = pxa2xx_udc_resume, |
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 1f3a7d999da7..d0bc396a85d5 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h | |||
@@ -177,23 +177,23 @@ struct pxa2xx_udc { | |||
177 | 177 | ||
178 | static struct pxa2xx_udc *the_controller; | 178 | static struct pxa2xx_udc *the_controller; |
179 | 179 | ||
180 | /* one GPIO should be used to detect host disconnect */ | 180 | /* one GPIO should be used to detect VBUS from the host */ |
181 | static inline int is_usb_connected(void) | 181 | static inline int is_vbus_present(void) |
182 | { | 182 | { |
183 | if (!the_controller->mach->udc_is_connected) | 183 | if (!the_controller->mach->udc_is_connected) |
184 | return 1; | 184 | return 1; |
185 | return the_controller->mach->udc_is_connected(); | 185 | return the_controller->mach->udc_is_connected(); |
186 | } | 186 | } |
187 | 187 | ||
188 | /* one GPIO should force the host to see this device (or not) */ | 188 | /* one GPIO should control a D+ pullup, so host sees this device (or not) */ |
189 | static inline void make_usb_disappear(void) | 189 | static inline void pullup_off(void) |
190 | { | 190 | { |
191 | if (!the_controller->mach->udc_command) | 191 | if (!the_controller->mach->udc_command) |
192 | return; | 192 | return; |
193 | the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); | 193 | the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); |
194 | } | 194 | } |
195 | 195 | ||
196 | static inline void let_usb_appear(void) | 196 | static inline void pullup_on(void) |
197 | { | 197 | { |
198 | if (!the_controller->mach->udc_command) | 198 | if (!the_controller->mach->udc_command) |
199 | return; | 199 | return; |