📄 pxa2xx_udc.c
字号:
/* the rest was statically initialized, and is read-only */
}
/* until it's enabled, this UDC should be completely invisible
* to any USB host.
*/
static void udc_enable (struct pxa2xx_udc *dev)
{
udc_clear_mask_UDCCR(UDCCR_UDE);
#ifdef CONFIG_ARCH_PXA
/* Enable clock for USB device */
pxa_set_cken(CKEN11_USB, 1);
udelay(5);
#endif
/* try to clear these bits before we enable the udc */
udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
ep0_idle(dev);
dev->gadget.speed = USB_SPEED_UNKNOWN;
dev->stats.irqs = 0;
/*
* sequence taken from chapter 12.5.10, PXA250 AppProcDevManual:
* - enable UDC
* - if RESET is already in progress, ack interrupt
* - unmask reset interrupt
*/
udc_set_mask_UDCCR(UDCCR_UDE);
if (!(UDCCR & UDCCR_UDA))
udc_ack_int_UDCCR(UDCCR_RSTIR);
if (dev->has_cfr /* UDC_RES2 is defined */) {
/* pxa255 (a0+) can avoid a set_config race that could
* prevent gadget drivers from configuring correctly
*/
UDCCFR = UDCCFR_ACM | UDCCFR_MB1;
} else {
/* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1)
* which could result in missing packets and interrupts.
* supposedly one bit per endpoint, controlling whether it
* double buffers or not; ACM/AREN bits fit into the holes.
* zero bits (like USIR0_IRx) disable double buffering.
*/
UDC_RES1 = 0x00;
UDC_RES2 = 0x00;
}
#ifdef DISABLE_TEST_MODE
/* "test mode" seems to have become the default in later chip
* revs, preventing double buffering (and invalidating docs).
* this EXPERIMENT enables it for bulk endpoints by tweaking
* undefined/reserved register bits (that other drivers clear).
* Belcarra code comments noted this usage.
*/
if (fifo_mode & 1) { /* IN endpoints */
UDC_RES1 |= USIR0_IR1|USIR0_IR6;
UDC_RES2 |= USIR1_IR11;
}
if (fifo_mode & 2) { /* OUT endpoints */
UDC_RES1 |= USIR0_IR2|USIR0_IR7;
UDC_RES2 |= USIR1_IR12;
}
#endif
/* enable suspend/resume and reset irqs */
udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM);
/* enable ep0 irqs */
UICR0 &= ~UICR0_IM0;
/* if hardware supports it, pullup D+ and wait for reset */
pullup_on();
}
/* when a driver is successfully registered, it will receive
* control requests including set_configuration(), which enables
* non-control requests. then usb traffic follows until a
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct pxa2xx_udc *dev = the_controller;
int retval;
if (!driver
|| driver->speed != USB_SPEED_FULL
|| !driver->bind
|| !driver->unbind
|| !driver->disconnect
|| !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
/* first hook up the driver ... */
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
dev->pullup = 1;
device_add (&dev->gadget.dev);
retval = driver->bind(&dev->gadget);
if (retval) {
DMSG("bind to driver %s --> error %d\n",
driver->driver.name, retval);
device_del (&dev->gadget.dev);
dev->driver = NULL;
dev->gadget.dev.driver = NULL;
return retval;
}
device_create_file(dev->dev, &dev_attr_function);
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
DMSG("registered gadget driver '%s'\n", driver->driver.name);
pullup(dev, 1);
dump_state(dev);
return 0;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
static void
stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver)
{
int i;
/* don't disconnect drivers more than once */
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = NULL;
dev->gadget.speed = USB_SPEED_UNKNOWN;
/* prevent new request submissions, kill any outstanding requests */
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
struct pxa2xx_ep *ep = &dev->ep[i];
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
del_timer_sync(&dev->timer);
/* report disconnect; the driver is already quiesced */
LED_CONNECTED_OFF;
if (driver)
driver->disconnect(&dev->gadget);
/* re-init driver-visible data structures */
udc_reinit(dev);
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct pxa2xx_udc *dev = the_controller;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver)
return -EINVAL;
local_irq_disable();
pullup(dev, 0);
stop_activity(dev, driver);
local_irq_enable();
driver->unbind(&dev->gadget);
dev->driver = NULL;
device_del (&dev->gadget.dev);
device_remove_file(dev->dev, &dev_attr_function);
DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
dump_state(dev);
return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_ARCH_LUBBOCK
/* Lubbock has separate connect and disconnect irqs. More typical designs
* use one GPIO as the VBUS IRQ, and another to control the D+ pullup.
*/
static irqreturn_t
lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
{
struct pxa2xx_udc *dev = _dev;
int vbus;
dev->stats.irqs++;
HEX_DISPLAY(dev->stats.irqs);
switch (irq) {
case LUBBOCK_USB_IRQ:
LED_CONNECTED_ON;
vbus = 1;
disable_irq(LUBBOCK_USB_IRQ);
enable_irq(LUBBOCK_USB_DISC_IRQ);
break;
case LUBBOCK_USB_DISC_IRQ:
LED_CONNECTED_OFF;
vbus = 0;
disable_irq(LUBBOCK_USB_DISC_IRQ);
enable_irq(LUBBOCK_USB_IRQ);
break;
default:
return IRQ_NONE;
}
pxa2xx_udc_vbus_session(&dev->gadget, vbus);
return IRQ_HANDLED;
}
#endif
/*-------------------------------------------------------------------------*/
static inline void clear_ep_state (struct pxa2xx_udc *dev)
{
unsigned i;
/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
* fifos, and pending transactions mustn't be continued in any case.
*/
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)
nuke(&dev->ep[i], -ECONNABORTED);
}
static void udc_watchdog(unsigned long _dev)
{
struct pxa2xx_udc *dev = (void *)_dev;
local_irq_disable();
if (dev->ep0state == EP0_STALL
&& (UDCCS0 & UDCCS0_FST) == 0
&& (UDCCS0 & UDCCS0_SST) == 0) {
UDCCS0 = UDCCS0_FST|UDCCS0_FTF;
DBG(DBG_VERBOSE, "ep0 re-stall\n");
start_watchdog(dev);
}
local_irq_enable();
}
static void handle_ep0 (struct pxa2xx_udc *dev)
{
u32 udccs0 = UDCCS0;
struct pxa2xx_ep *ep = &dev->ep [0];
struct pxa2xx_request *req;
union {
struct usb_ctrlrequest r;
u8 raw [8];
u32 word [2];
} u;
if (list_empty(&ep->queue))
req = NULL;
else
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
/* clear stall status */
if (udccs0 & UDCCS0_SST) {
nuke(ep, -EPIPE);
UDCCS0 = UDCCS0_SST;
del_timer(&dev->timer);
ep0_idle(dev);
}
/* previous request unfinished? non-error iff back-to-back ... */
if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
nuke(ep, 0);
del_timer(&dev->timer);
ep0_idle(dev);
}
switch (dev->ep0state) {
case EP0_IDLE:
/* late-breaking status? */
udccs0 = UDCCS0;
/* start control request? */
if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))
== (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {
int i;
nuke (ep, -EPROTO);
/* read SETUP packet */
for (i = 0; i < 8; i++) {
if (unlikely(!(UDCCS0 & UDCCS0_RNE))) {
bad_setup:
DMSG("SETUP %d!\n", i);
goto stall;
}
u.raw [i] = (u8) UDDR0;
}
if (unlikely((UDCCS0 & UDCCS0_RNE) != 0))
goto bad_setup;
got_setup:
DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
u.r.bRequestType, u.r.bRequest,
le16_to_cpu(u.r.wValue),
le16_to_cpu(u.r.wIndex),
le16_to_cpu(u.r.wLength));
/* cope with automagic for some standard requests. */
dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
== USB_TYPE_STANDARD;
dev->req_config = 0;
dev->req_pending = 1;
switch (u.r.bRequest) {
/* hardware restricts gadget drivers here! */
case USB_REQ_SET_CONFIGURATION:
if (u.r.bRequestType == USB_RECIP_DEVICE) {
/* reflect hardware's automagic
* up to the gadget driver.
*/
config_change:
dev->req_config = 1;
clear_ep_state(dev);
/* if !has_cfr, there's no synch
* else use AREN (later) not SA|OPR
* USIR0_IR0 acts edge sensitive
*/
}
break;
/* ... and here, even more ... */
case USB_REQ_SET_INTERFACE:
if (u.r.bRequestType == USB_RECIP_INTERFACE) {
/* udc hardware is broken by design:
* - altsetting may only be zero;
* - hw resets all interfaces' eps;
* - ep reset doesn't include halt(?).
*/
DMSG("broken set_interface (%d/%d)\n",
le16_to_cpu(u.r.wIndex),
le16_to_cpu(u.r.wValue));
goto config_change;
}
break;
/* hardware was supposed to hide this */
case USB_REQ_SET_ADDRESS:
if (u.r.bRequestType == USB_RECIP_DEVICE) {
ep0start(dev, 0, "address");
return;
}
break;
}
if (u.r.bRequestType & USB_DIR_IN)
dev->ep0state = EP0_IN_DATA_PHASE;
else
dev->ep0state = EP0_OUT_DATA_PHASE;
i = dev->driver->setup(&dev->gadget, &u.r);
if (i < 0) {
/* hardware automagic preventing STALL... */
if (dev->req_config) {
/* hardware sometimes neglects to tell
* tell us about config change events,
* so later ones may fail...
*/
WARN("config change %02x fail %d?\n",
u.r.bRequest, i);
return;
/* TODO experiment: if has_cfr,
* hardware didn't ACK; maybe we
* could actually STALL!
*/
}
DBG(DBG_VERBOSE, "protocol STALL, "
"%02x err %d\n", UDCCS0, i);
stall:
/* the watchdog timer helps deal with cases
* where udc seems to clear FST wrongly, and
* then NAKs instead of STALLing.
*/
ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
start_watchdog(dev);
dev->ep0state = EP0_STALL;
/* deferred i/o == no response yet */
} else if (dev->req_pending) {
if (likely(dev->ep0state == EP0_IN_DATA_PHASE
|| dev->req_std || u.r.wLength))
ep0start(dev, 0, "defer");
else
ep0start(dev, UDCCS0_IPR, "defer/IPR");
}
/* expect at least one data or status stage irq */
return;
} else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))
== (UDCCS0_OPR|UDCCS0_SA))) {
unsigned i;
/* pxa210/250 erratum 131 for B0/B1 says RNE lies.
* still observed on a pxa255 a0.
*/
DBG(DBG_VERBOSE, "e131\n");
nuke(ep, -EPROTO);
/* read SETUP data, but don't trust it too much */
for (i = 0; i < 8; i++)
u.raw [i] = (u8) UDDR0;
if ((u.r.bRequestType & USB_RECIP_MASK)
> USB_RECIP_OTHER)
goto stall;
if (u.word [0] == 0 && u.word [1] == 0)
goto stall;
goto got_setup;
} else {
/* some random early IRQ:
* - we acked FST
* - IPR cleared
* - OPR got set, without SA (likely status stage)
*/
UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR);
}
break;
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
if (udccs0 & UDCCS0_OPR) {
UDCCS0 = UDCCS0_OPR|UDCCS0_FTF;
DBG(DBG_VERBOSE, "ep0in premature status\n");
if (req)
done(ep, req, 0);
ep0_idle(dev);
} else /* irq was IPR clearing */ {
if (req) {
/* this IN packet might finish the request */
(void) write_ep0_fifo(ep, req);
} /* else IN token before response was written */
}
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
if (udccs0 & UDCCS0_OPR) {
if (req) {
/* this OUT packet might finish the request */
if (read_ep0_fifo(ep, req))
done(ep, req, 0);
/* else more OUT packets expected */
} /* else OUT token before read was issued */
} else /* irq was IPR clearing */ {
DBG(DBG_VERBOSE, "ep0out premature status\n");
if (req)
done(ep, req, 0);
ep0_idle(dev);
}
break;
case EP0_END_XFER:
if (req)
done(ep, req, 0);
/* ack control-IN status (maybe in-zlp was skipped)
* also appears after some config change events.
*/
if (udccs0 & UDCCS0_OPR)
UDCCS0 = UDCCS0_OPR;
ep0_idle(dev);
break;
case EP0_STALL:
UDCCS0 = UDCCS0_FST;
break;
}
USIR0 = USIR0_IR0;
}
static void handle_ep(struct pxa2xx_ep *ep)
{
struct pxa2xx_request *req;
int is_in = ep->bEndpointAddress & USB_DIR_IN;
int completed;
u32 udccs, tmp;
do {
completed = 0;
if (likely (!list_empty(&ep->queue)))
req = list_entry(ep->queue.next,
struct pxa2xx_request, queue);
else
req = NULL;
// TODO check FST handling
udccs = *ep->reg_udccs;
if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */
tmp = UDCCS_BI_TUR;
if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
tmp |= UDCCS_BI_SST;
tmp &= udccs;
if (likely (tmp))
*ep->reg_udccs = tmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -