Commit 91987693 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 988199fe
/* /*
* linux/drivers/usb/gadget/pxa2xx_udc.c * linux/drivers/usb/gadget/pxa2xx_udc.c
* Intel PXA2xx and IXP4xx on-chip full speed USB device controllers * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
* *
* Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
* Copyright (C) 2003 Robert Schwebel, Pengutronix * Copyright (C) 2003 Robert Schwebel, Pengutronix
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
/* /*
* This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
* series processors. The UDC for the IXP 4xx series is very similar. * series processors. The UDC for the IXP 4xx series is very similar.
* There are fifteen endpoints, in addition to ep0. * There are fifteen endpoints, in addition to ep0.
* *
...@@ -79,8 +79,8 @@ ...@@ -79,8 +79,8 @@
* pxa250 a0/a1 b0/b1/b2 sure act like they're still there. * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
*/ */
#define DRIVER_VERSION "14-Dec-2003" #define DRIVER_VERSION "4-May-2005"
#define DRIVER_DESC "PXA 2xx USB Device Controller driver" #define DRIVER_DESC "PXA 25x USB Device Controller driver"
static const char driver_name [] = "pxa2xx_udc"; static const char driver_name [] = "pxa2xx_udc";
...@@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, ...@@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
static int pxa2xx_ep_disable (struct usb_ep *_ep) static int pxa2xx_ep_disable (struct usb_ep *_ep)
{ {
struct pxa2xx_ep *ep; struct pxa2xx_ep *ep;
unsigned long flags;
ep = container_of (_ep, struct pxa2xx_ep, ep); ep = container_of (_ep, struct pxa2xx_ep, ep);
if (!_ep || !ep->desc) { if (!_ep || !ep->desc) {
...@@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) ...@@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
_ep ? ep->ep.name : NULL); _ep ? ep->ep.name : NULL);
return -EINVAL; return -EINVAL;
} }
local_irq_save(flags);
nuke (ep, -ESHUTDOWN); nuke (ep, -ESHUTDOWN);
#ifdef USE_DMA #ifdef USE_DMA
...@@ -313,6 +316,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) ...@@ -313,6 +316,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
ep->desc = NULL; ep->desc = NULL;
ep->stopped = 1; ep->stopped = 1;
local_irq_restore(flags);
DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
return 0; return 0;
} }
...@@ -971,10 +975,10 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) ...@@ -971,10 +975,10 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
kick_dma(ep, req); kick_dma(ep, req);
#endif #endif
/* can the FIFO can satisfy the request immediately? */ /* can the FIFO can satisfy the request immediately? */
} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
&& (*ep->reg_udccs & UDCCS_BI_TFS) != 0 if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
&& write_fifo(ep, req)) { && write_fifo(ep, req))
req = NULL; req = NULL;
} else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0
&& read_fifo(ep, req)) { && read_fifo(ep, req)) {
req = NULL; req = NULL;
...@@ -1290,7 +1294,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, ...@@ -1290,7 +1294,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
"%s version: %s\nGadget driver: %s\nHost %s\n\n", "%s version: %s\nGadget driver: %s\nHost %s\n\n",
driver_name, DRIVER_VERSION SIZE_STR DMASTR, driver_name, DRIVER_VERSION SIZE_STR DMASTR,
dev->driver ? dev->driver->driver.name : "(none)", dev->driver ? dev->driver->driver.name : "(none)",
is_usb_connected() ? "full speed" : "disconnected"); is_vbus_present() ? "full speed" : "disconnected");
size -= t; size -= t;
next += t; next += t;
...@@ -1339,7 +1343,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, ...@@ -1339,7 +1343,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
next += t; next += t;
} }
if (!is_usb_connected() || !dev->driver) if (!is_vbus_present() || !dev->driver)
goto done; goto done;
t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", 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,7 +1458,7 @@ static void udc_disable(struct pxa2xx_udc *dev)
UFNRH = UFNRH_SIM; UFNRH = UFNRH_SIM;
/* if hardware supports it, disconnect from usb */ /* if hardware supports it, disconnect from usb */
make_usb_disappear(); pullup_off();
udc_clear_mask_UDCCR(UDCCR_UDE); udc_clear_mask_UDCCR(UDCCR_UDE);
...@@ -1567,7 +1571,7 @@ static void udc_enable (struct pxa2xx_udc *dev) ...@@ -1567,7 +1571,7 @@ static void udc_enable (struct pxa2xx_udc *dev)
UICR0 &= ~UICR0_IM0; UICR0 &= ~UICR0_IM0;
/* if hardware supports it, pullup D+ and wait for reset */ /* if hardware supports it, pullup D+ and wait for reset */
let_usb_appear(); pullup_on();
} }
...@@ -2052,10 +2056,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) ...@@ -2052,10 +2056,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r)
if (unlikely(udccr & UDCCR_SUSIR)) { if (unlikely(udccr & UDCCR_SUSIR)) {
udc_ack_int_UDCCR(UDCCR_SUSIR); udc_ack_int_UDCCR(UDCCR_SUSIR);
handled = 1; handled = 1;
DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present()
? "" : "+disconnect"); ? "" : "+disconnect");
if (!is_usb_connected()) if (!is_vbus_present())
stop_activity(dev, dev->driver); stop_activity(dev, dev->driver);
else if (dev->gadget.speed != USB_SPEED_UNKNOWN else if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver && dev->driver
...@@ -2073,7 +2077,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) ...@@ -2073,7 +2077,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r)
if (dev->gadget.speed != USB_SPEED_UNKNOWN if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver && dev->driver
&& dev->driver->resume && dev->driver->resume
&& is_usb_connected()) && is_vbus_present())
dev->driver->resume(&dev->gadget); dev->driver->resume(&dev->gadget);
} }
...@@ -2509,7 +2513,7 @@ static int __init pxa2xx_udc_probe(struct device *_dev) ...@@ -2509,7 +2513,7 @@ static int __init pxa2xx_udc_probe(struct device *_dev)
udc_disable(dev); udc_disable(dev);
udc_reinit(dev); udc_reinit(dev);
dev->vbus = is_usb_connected(); dev->vbus = is_vbus_present();
/* irq setup after old hardware state is cleaned up */ /* irq setup after old hardware state is cleaned up */
retval = request_irq(IRQ_USB, pxa2xx_udc_irq, retval = request_irq(IRQ_USB, pxa2xx_udc_irq,
...@@ -2555,6 +2559,12 @@ lubbock_fail0: ...@@ -2555,6 +2559,12 @@ lubbock_fail0:
return 0; return 0;
} }
static void pxa2xx_udc_shutdown(struct device *_dev)
{
pullup_off();
}
static int __exit pxa2xx_udc_remove(struct device *_dev) static int __exit pxa2xx_udc_remove(struct device *_dev)
{ {
struct pxa2xx_udc *dev = dev_get_drvdata(_dev); struct pxa2xx_udc *dev = dev_get_drvdata(_dev);
...@@ -2624,6 +2634,7 @@ static struct device_driver udc_driver = { ...@@ -2624,6 +2634,7 @@ static struct device_driver udc_driver = {
.name = "pxa2xx-udc", .name = "pxa2xx-udc",
.bus = &platform_bus_type, .bus = &platform_bus_type,
.probe = pxa2xx_udc_probe, .probe = pxa2xx_udc_probe,
.shutdown = pxa2xx_udc_shutdown,
.remove = __exit_p(pxa2xx_udc_remove), .remove = __exit_p(pxa2xx_udc_remove),
.suspend = pxa2xx_udc_suspend, .suspend = pxa2xx_udc_suspend,
.resume = pxa2xx_udc_resume, .resume = pxa2xx_udc_resume,
......
...@@ -177,23 +177,23 @@ struct pxa2xx_udc { ...@@ -177,23 +177,23 @@ struct pxa2xx_udc {
static struct pxa2xx_udc *the_controller; static struct pxa2xx_udc *the_controller;
/* one GPIO should be used to detect host disconnect */ /* one GPIO should be used to detect VBUS from the host */
static inline int is_usb_connected(void) static inline int is_vbus_present(void)
{ {
if (!the_controller->mach->udc_is_connected) if (!the_controller->mach->udc_is_connected)
return 1; return 1;
return the_controller->mach->udc_is_connected(); return the_controller->mach->udc_is_connected();
} }
/* one GPIO should force the host to see this device (or not) */ /* one GPIO should control a D+ pullup, so host sees this device (or not) */
static inline void make_usb_disappear(void) static inline void pullup_off(void)
{ {
if (!the_controller->mach->udc_command) if (!the_controller->mach->udc_command)
return; return;
the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
} }
static inline void let_usb_appear(void) static inline void pullup_on(void)
{ {
if (!the_controller->mach->udc_command) if (!the_controller->mach->udc_command)
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment