📄 omap_udc.c
字号:
UDC_IRQ_SRC_REG = UDC_EP0_TX;
UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
stat = UDC_STAT_FLG_REG;
if (stat & UDC_ACK) {
if (udc->ep0_in) {
/* write next IN packet from response,
* or set up the status stage.
*/
if (req)
stat = write_fifo(ep0, req);
UDC_EP_NUM_REG = UDC_EP_DIR;
if (!req && udc->ep0_pending) {
UDC_EP_NUM_REG = UDC_EP_SEL;
UDC_CTRL_REG = UDC_CLR_EP;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
UDC_EP_NUM_REG = 0;
udc->ep0_pending = 0;
} /* else: 6 wait states before it'll tx */
} else {
/* ack status stage of OUT transfer */
UDC_EP_NUM_REG = UDC_EP_DIR;
if (req)
done(ep0, req, 0);
}
req = NULL;
} else if (stat & UDC_STALL) {
UDC_CTRL_REG = UDC_CLR_HALT;
UDC_EP_NUM_REG = UDC_EP_DIR;
} else {
UDC_EP_NUM_REG = UDC_EP_DIR;
}
}
/* OUT == RX from host */
if (irq_src & UDC_EP0_RX) {
int stat;
UDC_IRQ_SRC_REG = UDC_EP0_RX;
UDC_EP_NUM_REG = UDC_EP_SEL;
stat = UDC_STAT_FLG_REG;
if (stat & UDC_ACK) {
if (!udc->ep0_in) {
stat = 0;
/* read next OUT packet of request, maybe
* reactiviting the fifo; stall on errors.
*/
if (!req || (stat = read_fifo(ep0, req)) < 0) {
UDC_SYSCON2_REG = UDC_STALL_CMD;
udc->ep0_pending = 0;
stat = 0;
} else if (stat == 0)
UDC_CTRL_REG = UDC_SET_FIFO_EN;
UDC_EP_NUM_REG = 0;
/* activate status stage */
if (stat == 1) {
done(ep0, req, 0);
/* that may have STALLed ep0... */
UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
UDC_CTRL_REG = UDC_CLR_EP;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
UDC_EP_NUM_REG = UDC_EP_DIR;
udc->ep0_pending = 0;
}
} else {
/* ack status stage of IN transfer */
UDC_EP_NUM_REG = 0;
if (req)
done(ep0, req, 0);
}
} else if (stat & UDC_STALL) {
UDC_CTRL_REG = UDC_CLR_HALT;
UDC_EP_NUM_REG = 0;
} else {
UDC_EP_NUM_REG = 0;
}
}
/* SETUP starts all control transfers */
if (irq_src & UDC_SETUP) {
union u {
u16 word[4];
struct usb_ctrlrequest r;
} u;
int status = -EINVAL;
struct omap_ep *ep;
/* read the (latest) SETUP message */
do {
UDC_EP_NUM_REG = UDC_SETUP_SEL;
/* two bytes at a time */
u.word[0] = UDC_DATA_REG;
u.word[1] = UDC_DATA_REG;
u.word[2] = UDC_DATA_REG;
u.word[3] = UDC_DATA_REG;
UDC_EP_NUM_REG = 0;
} while (UDC_IRQ_SRC_REG & UDC_SETUP);
#define w_value le16_to_cpup (&u.r.wValue)
#define w_index le16_to_cpup (&u.r.wIndex)
#define w_length le16_to_cpup (&u.r.wLength)
/* Delegate almost all control requests to the gadget driver,
* except for a handful of ch9 status/feature requests that
* hardware doesn't autodecode _and_ the gadget API hides.
*/
udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0;
udc->ep0_set_config = 0;
udc->ep0_pending = 1;
ep0->stopped = 0;
ep0->ackwait = 0;
switch (u.r.bRequest) {
case USB_REQ_SET_CONFIGURATION:
/* udc needs to know when ep != 0 is valid */
if (u.r.bRequestType != USB_RECIP_DEVICE)
goto delegate;
if (w_length != 0)
goto do_stall;
udc->ep0_set_config = 1;
udc->ep0_reset_config = (w_value == 0);
VDBG("set config %d\n", w_value);
/* update udc NOW since gadget driver may start
* queueing requests immediately; clear config
* later if it fails the request.
*/
if (udc->ep0_reset_config)
UDC_SYSCON2_REG = UDC_CLR_CFG;
else
UDC_SYSCON2_REG = UDC_DEV_CFG;
update_otg(udc);
goto delegate;
case USB_REQ_CLEAR_FEATURE:
/* clear endpoint halt */
if (u.r.bRequestType != USB_RECIP_ENDPOINT)
goto delegate;
if (w_value != USB_ENDPOINT_HALT
|| w_length != 0)
goto do_stall;
ep = &udc->ep[w_index & 0xf];
if (ep != ep0) {
if (w_index & USB_DIR_IN)
ep += 16;
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|| !ep->desc)
goto do_stall;
use_ep(ep, 0);
UDC_CTRL_REG = udc->clr_halt;
ep->ackwait = 0;
if (!(ep->bEndpointAddress & USB_DIR_IN)) {
UDC_CTRL_REG = UDC_SET_FIFO_EN;
ep->ackwait = 1 + ep->double_buf;
}
/* NOTE: assumes the host behaves sanely,
* only clearing real halts. Else we may
* need to kill pending transfers and then
* restart the queue... very messy for DMA!
*/
}
VDBG("%s halt cleared by host\n", ep->name);
goto ep0out_status_stage;
case USB_REQ_SET_FEATURE:
/* set endpoint halt */
if (u.r.bRequestType != USB_RECIP_ENDPOINT)
goto delegate;
if (w_value != USB_ENDPOINT_HALT
|| w_length != 0)
goto do_stall;
ep = &udc->ep[w_index & 0xf];
if (w_index & USB_DIR_IN)
ep += 16;
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|| ep == ep0 || !ep->desc)
goto do_stall;
if (use_dma && ep->has_dma) {
/* this has rude side-effects (aborts) and
* can't really work if DMA-IN is active
*/
DBG("%s host set_halt, NYET \n", ep->name);
goto do_stall;
}
use_ep(ep, 0);
/* can't halt if fifo isn't empty... */
UDC_CTRL_REG = UDC_CLR_EP;
UDC_CTRL_REG = UDC_SET_HALT;
VDBG("%s halted by host\n", ep->name);
ep0out_status_stage:
status = 0;
UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
UDC_CTRL_REG = UDC_CLR_EP;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
UDC_EP_NUM_REG = UDC_EP_DIR;
udc->ep0_pending = 0;
break;
case USB_REQ_GET_STATUS:
/* return interface status. if we were pedantic,
* we'd detect non-existent interfaces, and stall.
*/
if (u.r.bRequestType
!= (USB_DIR_IN|USB_RECIP_INTERFACE))
goto delegate;
/* return two zero bytes */
UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
UDC_DATA_REG = 0;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
UDC_EP_NUM_REG = UDC_EP_DIR;
status = 0;
VDBG("GET_STATUS, interface %d\n", w_index);
/* next, status stage */
break;
default:
delegate:
/* activate the ep0out fifo right away */
if (!udc->ep0_in && w_length) {
UDC_EP_NUM_REG = 0;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
}
/* gadget drivers see class/vendor specific requests,
* {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION},
* and more
*/
VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
u.r.bRequestType, u.r.bRequest,
w_value, w_index, w_length);
#undef w_value
#undef w_index
#undef w_length
/* The gadget driver may return an error here,
* causing an immediate protocol stall.
*
* Else it must issue a response, either queueing a
* response buffer for the DATA stage, or halting ep0
* (causing a protocol stall, not a real halt). A
* zero length buffer means no DATA stage.
*
* It's fine to issue that response after the setup()
* call returns, and this IRQ was handled.
*/
udc->ep0_setup = 1;
spin_unlock(&udc->lock);
status = udc->driver->setup (&udc->gadget, &u.r);
spin_lock(&udc->lock);
udc->ep0_setup = 0;
}
if (status < 0) {
do_stall:
VDBG("req %02x.%02x protocol STALL; stat %d\n",
u.r.bRequestType, u.r.bRequest, status);
if (udc->ep0_set_config) {
if (udc->ep0_reset_config)
WARN("error resetting config?\n");
else
UDC_SYSCON2_REG = UDC_CLR_CFG;
}
UDC_SYSCON2_REG = UDC_STALL_CMD;
udc->ep0_pending = 0;
}
}
}
/*-------------------------------------------------------------------------*/
#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT)
static void devstate_irq(struct omap_udc *udc, u16 irq_src)
{
u16 devstat, change;
devstat = UDC_DEVSTAT_REG;
change = devstat ^ udc->devstat;
udc->devstat = devstat;
if (change & (UDC_USB_RESET|UDC_ATT)) {
udc_quiesce(udc);
if (change & UDC_ATT) {
/* driver for any external transceiver will
* have called omap_vbus_session() already
*/
if (devstat & UDC_ATT) {
udc->gadget.speed = USB_SPEED_FULL;
VDBG("connect\n");
if (!udc->transceiver)
pullup_enable(udc);
// if (driver->connect) call it
} else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
udc->gadget.speed = USB_SPEED_UNKNOWN;
if (!udc->transceiver)
pullup_disable(udc);
DBG("disconnect, gadget %s\n",
udc->driver->driver.name);
if (udc->driver->disconnect) {
spin_unlock(&udc->lock);
udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
}
}
change &= ~UDC_ATT;
}
if (change & UDC_USB_RESET) {
if (devstat & UDC_USB_RESET) {
VDBG("RESET=1\n");
} else {
udc->gadget.speed = USB_SPEED_FULL;
INFO("USB reset done, gadget %s\n",
udc->driver->driver.name);
/* ep0 traffic is legal from now on */
UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE;
}
change &= ~UDC_USB_RESET;
}
}
if (change & UDC_SUS) {
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
// FIXME tell isp1301 to suspend/resume (?)
if (devstat & UDC_SUS) {
VDBG("suspend\n");
update_otg(udc);
/* HNP could be under way already */
if (udc->gadget.speed == USB_SPEED_FULL
&& udc->driver->suspend) {
spin_unlock(&udc->lock);
udc->driver->suspend(&udc->gadget);
spin_lock(&udc->lock);
}
} else {
VDBG("resume\n");
if (udc->gadget.speed == USB_SPEED_FULL
&& udc->driver->resume) {
spin_unlock(&udc->lock);
udc->driver->resume(&udc->gadget);
spin_lock(&udc->lock);
}
}
}
change &= ~UDC_SUS;
}
if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) {
update_otg(udc);
change &= ~OTG_FLAGS;
}
change &= ~(UDC_CFG|UDC_DEF|UDC_ADD);
if (change)
VDBG("devstat %03x, ignore change %03x\n",
devstat, change);
UDC_IRQ_SRC_REG = UDC_DS_CHG;
}
static irqreturn_t
omap_udc_irq(int irq, void *_udc, struct pt_regs *r)
{
struct omap_udc *udc = _udc;
u16 irq_src;
irqreturn_t status = IRQ_NONE;
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
irq_src = UDC_IRQ_SRC_REG;
/* Device state change (usb ch9 stuff) */
if (irq_src & UDC_DS_CHG) {
devstate_irq(_udc, irq_src);
status = IRQ_HANDLED;
irq_src &= ~UDC_DS_CHG;
}
/* EP0 control transfers */
if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) {
ep0_irq(_udc, irq_src);
status = IRQ_HANDLED;
irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX);
}
/* DMA transfer completion */
if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) {
dma_irq(_udc, irq_src);
status = IRQ_HANDLED;
irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT);
}
irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX);
if (irq_src)
DBG("udc_irq, unhandled %03x\n", irq_src);
spin_unlock_irqrestore(&udc->lock, flags);
return status;
}
/* workaround for seemingly-lost IRQs for RX ACKs... */
#define PIO_OUT_TIMEOUT (jiffies + HZ/3)
#define HALF_FULL(f) (!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY)))
static void pio_out_timer(unsigned long _ep)
{
struct omap_ep *ep = (void *) _ep;
unsigned long flags;
u16 stat_flg;
spin_lock_irqsave(&ep->udc->lock, flags);
if (!list_empty(&ep->queue) && ep->ackwait) {
use_ep(ep, 0);
stat_flg = UDC_STAT_FLG_REG;
if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN)
|| (ep->double_buf && HALF_FULL(stat_flg)))) {
struct omap_req *req;
VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg);
req = container_of(ep->queue.next,
struct omap_req, queue);
UDC_EP_NUM_REG = ep->bEndpointAddress | UDC_EP_SEL;
(void) read_fifo(ep, req);
UDC_EP_NUM_REG = ep->bEndpointAddress;
UDC_CTRL_REG = UDC_SET_FIFO_EN;
ep->ackwait = 1 + ep->double_buf;
}
}
mod_timer(&ep->timer, PIO_OUT_TIMEOUT);
spin_unlock_irqrestore(&ep->udc->lock, flags);
}
static irqreturn_t
omap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r)
{
u16 epn_stat, irq_src;
irqreturn_t status = IRQ_NONE;
struct omap_ep *ep;
int epnum;
struct omap_udc *udc = _dev;
struct omap_req *req;
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
epn_stat = UDC_EPN_STAT_REG;
irq_src = UDC_IRQ_SRC_REG;
/* handle OUT first, to avoid some wasteful NAKs */
if (irq_src & UDC_EPN_RX) {
epnum = (epn_stat >> 8) & 0x0f;
UDC_IRQ_SRC_REG = UDC_EPN_RX;
status = IRQ_HANDLED;
ep = &udc->ep[epnum];
ep->irqs++;
UDC_EP_NUM_REG = epnum | UDC_EP_SEL;
ep->fnf = 0;
if ((UDC_STAT_FLG_REG & UDC_ACK)) {
ep->ackwait--;
if (!list_empty(&ep->queue)) {
int stat;
req = container_of(ep->queue.next,
struct omap_req, queue);
stat = read_fifo(ep, req);
if (!ep->double_buf)
ep->fnf = 1;
}
}
/* min 6 clock delay before clearing EP_SEL ... */
epn_stat = UDC_EPN_STAT_REG;
epn_stat = UDC_EPN_STAT_REG;
UDC_EP_NUM_REG = epnum;
/* enabling fifo _after_ clearing ACK, contrary to docs,
* reduces lossage; timer still needed though (sigh).
*/
if (ep->fnf) {
UDC_CTRL_REG = UDC_SET_FIFO_EN;
ep->ackwait = 1 + ep->double_buf;
}
mod_timer(&ep->timer, PIO_OUT_TIMEOUT);
}
/* then IN transfers */
else if (irq_src & UDC_EPN_TX) {
epnum = epn_stat & 0x0f;
UDC_IRQ_SRC_REG = UDC_EPN_TX;
status = IRQ_HANDLED;
ep = &udc->ep[16 + epnum];
ep->irqs++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -