[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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
988199fe34
commit
9198769363
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
* 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,
|
|||||||
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)
|
|||||||
_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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
"%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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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:
|
|||||||
|
|
||||||
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 = {
|
|||||||
.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 {
|
|||||||
|
|
||||||
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;
|
||||||
|
Reference in New Issue
Block a user