📄 pxa2xx_udc.c
字号:
}
return 1;
}
// TODO experiment: how robust can fifo mode tweaking be?
// double buffering is off in the default fifo mode, which
// prevents TFS from being set here.
} while (*ep->reg_udccs & UDCCS_BI_TFS);
return 0;
}
/* caller asserts req->pending (ep0 irq status nyet cleared); starts
* ep0 data stage. these chips want very simple state transitions.
*/
static inline
void ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag)
{
UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR;
USIR0 = USIR0_IR0;
dev->req_pending = 0;
DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
__FUNCTION__, tag, UDCCS0, flags);
}
static int
write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
{
unsigned count;
int is_short;
count = write_packet(&UDDR0, req, EP0_FIFO_SIZE);
ep->dev->stats.write.bytes += count;
/* last packet "must be" short (or a zlp) */
is_short = (count != EP0_FIFO_SIZE);
DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
req->req.length - req->req.actual, req);
if (unlikely (is_short)) {
if (ep->dev->req_pending)
ep0start(ep->dev, UDCCS0_IPR, "short IN");
else
UDCCS0 = UDCCS0_IPR;
count = req->req.length;
done (ep, req, 0);
ep0_idle(ep->dev);
#if 1
/* This seems to get rid of lost status irqs in some cases:
* host responds quickly, or next request involves config
* change automagic, or should have been hidden, or ...
*
* FIXME get rid of all udelays possible...
*/
if (count >= EP0_FIFO_SIZE) {
count = 100;
do {
if ((UDCCS0 & UDCCS0_OPR) != 0) {
/* clear OPR, generate ack */
UDCCS0 = UDCCS0_OPR;
break;
}
count--;
udelay(1);
} while (count);
}
#endif
} else if (ep->dev->req_pending)
ep0start(ep->dev, 0, "IN");
return is_short;
}
/*
* read_fifo - unload packet(s) from the fifo we use for usb OUT
* transfers and put them into the request. caller should have made
* sure there's at least one packet ready.
*
* returns true if the request completed because of short packet or the
* request buffer having filled (and maybe overran till end-of-packet).
*/
static int
read_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
{
for (;;) {
u32 udccs;
u8 *buf;
unsigned bufferspace, count, is_short;
/* make sure there's a packet in the FIFO.
* UDCCS_{BO,IO}_RPC are all the same bit value.
* UDCCS_{BO,IO}_RNE are all the same bit value.
*/
udccs = *ep->reg_udccs;
if (unlikely ((udccs & UDCCS_BO_RPC) == 0))
break;
buf = req->req.buf + req->req.actual;
prefetchw(buf);
bufferspace = req->req.length - req->req.actual;
/* read all bytes from this packet */
if (likely (udccs & UDCCS_BO_RNE)) {
count = 1 + (0x0ff & *ep->reg_ubcr);
req->req.actual += min (count, bufferspace);
} else /* zlp */
count = 0;
is_short = (count < ep->ep.maxpacket);
DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",
ep->ep.name, udccs, count,
is_short ? "/S" : "",
req, req->req.actual, req->req.length);
while (likely (count-- != 0)) {
u8 byte = (u8) *ep->reg_uddr;
if (unlikely (bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
DMSG("%s overflow %d\n",
ep->ep.name, count);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
*ep->reg_udccs = UDCCS_BO_RPC;
/* RPC/RSP/RNE could now reflect the other packet buffer */
/* iso is one request per packet */
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
if (udccs & UDCCS_IO_ROF)
req->req.status = -EHOSTUNREACH;
/* more like "is_done" */
is_short = 1;
}
/* completion */
if (is_short || req->req.actual == req->req.length) {
done (ep, req, 0);
if (list_empty(&ep->queue))
pio_irq_disable (ep->bEndpointAddress);
return 1;
}
/* finished that packet. the next one may be waiting... */
}
return 0;
}
/*
* special ep0 version of the above. no UBCR0 or double buffering; status
* handshaking is magic. most device protocols don't need control-OUT.
* CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
* protocols do use them.
*/
static int
read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
{
u8 *buf, byte;
unsigned bufferspace;
buf = req->req.buf + req->req.actual;
bufferspace = req->req.length - req->req.actual;
while (UDCCS0 & UDCCS0_RNE) {
byte = (u8) UDDR0;
if (unlikely (bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
DMSG("%s overflow\n", ep->ep.name);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
req->req.actual++;
bufferspace--;
}
}
UDCCS0 = UDCCS0_OPR | UDCCS0_IPR;
/* completion */
if (req->req.actual >= req->req.length)
return 1;
/* finished that packet. the next one may be waiting... */
return 0;
}
#ifdef USE_DMA
#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
static void
start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in)
{
u32 dcmd = req->req.length;
u32 buf = req->req.dma;
u32 fifo = io_v2p ((u32)ep->reg_uddr);
/* caller guarantees there's a packet or more remaining
* - IN may end with a short packet (TSP set separately),
* - OUT is always full length
*/
buf += req->req.actual;
dcmd -= req->req.actual;
ep->dma_fixup = 0;
/* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
DCSR(ep->dma) = DCSR_NODESC;
if (is_in) {
DSADR(ep->dma) = buf;
DTADR(ep->dma) = fifo;
if (dcmd > MAX_IN_DMA)
dcmd = MAX_IN_DMA;
else
ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0;
dcmd |= DCMD_BURST32 | DCMD_WIDTH1
| DCMD_FLOWTRG | DCMD_INCSRCADDR;
} else {
#ifdef USE_OUT_DMA
DSADR(ep->dma) = fifo;
DTADR(ep->dma) = buf;
if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)
dcmd = ep->ep.maxpacket;
dcmd |= DCMD_BURST32 | DCMD_WIDTH1
| DCMD_FLOWSRC | DCMD_INCTRGADDR;
#endif
}
DCMD(ep->dma) = dcmd;
DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC
| (unlikely(is_in)
? DCSR_STOPIRQEN /* use dma_nodesc_handler() */
: 0); /* use handle_ep() */
}
static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req)
{
int is_in = ep->bEndpointAddress & USB_DIR_IN;
if (is_in) {
/* unaligned tx buffers and zlps only work with PIO */
if ((req->req.dma & 0x0f) != 0
|| unlikely((req->req.length - req->req.actual)
== 0)) {
pio_irq_enable(ep->bEndpointAddress);
if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0)
(void) write_fifo(ep, req);
} else {
start_dma_nodesc(ep, req, USB_DIR_IN);
}
} else {
if ((req->req.length - req->req.actual) < ep->ep.maxpacket) {
DMSG("%s short dma read...\n", ep->ep.name);
/* we're always set up for pio out */
read_fifo (ep, req);
} else {
*ep->reg_udccs = UDCCS_BO_DME
| (*ep->reg_udccs & UDCCS_BO_FST);
start_dma_nodesc(ep, req, USB_DIR_OUT);
}
}
}
static void cancel_dma(struct pxa2xx_ep *ep)
{
struct pxa2xx_request *req;
u32 tmp;
if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
return;
DCSR(ep->dma) = 0;
while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
cpu_relax();
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
tmp = DCMD(ep->dma) & DCMD_LENGTH;
req->req.actual = req->req.length - (tmp & DCMD_LENGTH);
/* the last tx packet may be incomplete, so flush the fifo.
* FIXME correct req.actual if we can
*/
if (ep->bEndpointAddress & USB_DIR_IN)
*ep->reg_udccs = UDCCS_BI_FTF;
}
/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */
static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r)
{
struct pxa2xx_ep *ep = _ep;
struct pxa2xx_request *req;
u32 tmp, completed;
local_irq_disable();
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
ep->dma_irqs++;
ep->dev->stats.irqs++;
HEX_DISPLAY(ep->dev->stats.irqs);
/* ack/clear */
tmp = DCSR(ep->dma);
DCSR(ep->dma) = tmp;
if ((tmp & DCSR_STOPSTATE) == 0
|| (DDADR(ep->dma) & DDADR_STOP) != 0) {
DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n",
ep->ep.name, DCSR(ep->dma), DDADR(ep->dma));
goto done;
}
DCSR(ep->dma) = 0; /* clear DCSR_STOPSTATE */
/* update transfer status */
completed = tmp & DCSR_BUSERR;
if (ep->bEndpointAddress & USB_DIR_IN)
tmp = DSADR(ep->dma);
else
tmp = DTADR(ep->dma);
req->req.actual = tmp - req->req.dma;
/* FIXME seems we sometimes see partial transfers... */
if (unlikely(completed != 0))
req->req.status = -EIO;
else if (req->req.actual) {
/* these registers have zeroes in low bits; they miscount
* some (end-of-transfer) short packets: tx 14 as tx 12
*/
if (ep->dma_fixup)
req->req.actual = min(req->req.actual + 3,
req->req.length);
tmp = (req->req.length - req->req.actual);
completed = (tmp == 0);
if (completed && (ep->bEndpointAddress & USB_DIR_IN)) {
/* maybe validate final short packet ... */
if ((req->req.actual % ep->ep.maxpacket) != 0)
*ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/;
/* ... or zlp, using pio fallback */
else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK
&& req->req.zero) {
DMSG("%s zlp terminate ...\n", ep->ep.name);
completed = 0;
}
}
}
if (likely(completed)) {
done(ep, req, 0);
/* maybe re-activate after completion */
if (ep->stopped || list_empty(&ep->queue))
goto done;
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
}
kick_dma(ep, req);
done:
local_irq_enable();
}
#endif
/*-------------------------------------------------------------------------*/
static int
pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, unsigned gfp_flags)
{
struct pxa2xx_request *req;
struct pxa2xx_ep *ep;
struct pxa2xx_udc *dev;
unsigned long flags;
req = container_of(_req, struct pxa2xx_request, req);
if (unlikely (!_req || !_req->complete || !_req->buf
|| !list_empty(&req->queue))) {
DMSG("%s, bad params\n", __FUNCTION__);
return -EINVAL;
}
ep = container_of(_ep, struct pxa2xx_ep, ep);
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DMSG("%s, bad ep\n", __FUNCTION__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely (!dev->driver
|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
DMSG("%s, bogus device state\n", __FUNCTION__);
return -ESHUTDOWN;
}
/* iso is always one packet per request, that's the only way
* we can report per-packet status. that also helps with dma.
*/
if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
&& req->req.length > le16_to_cpu
(ep->desc->wMaxPacketSize)))
return -EMSGSIZE;
#ifdef USE_DMA
// FIXME caller may already have done the dma mapping
if (ep->dma >= 0) {
_req->dma = dma_map_single(dev->dev,
_req->buf, _req->length,
((ep->bEndpointAddress & USB_DIR_IN) != 0)
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
}
#endif
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
_ep->name, _req, _req->length, _req->buf);
local_irq_save(flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
/* kickstart this i/o queue? */
if (list_empty(&ep->queue) && !ep->stopped) {
if (ep->desc == 0 /* ep0 */) {
unsigned length = _req->length;
switch (dev->ep0state) {
case EP0_IN_DATA_PHASE:
dev->stats.write.ops++;
if (write_ep0_fifo(ep, req))
req = NULL;
break;
case EP0_OUT_DATA_PHASE:
dev->stats.read.ops++;
/* messy ... */
if (dev->req_config) {
DBG(DBG_VERBOSE, "ep0 config ack%s\n",
dev->has_cfr ? "" : " raced");
if (dev->has_cfr)
UDCCFR = UDCCFR_AREN|UDCCFR_ACM
|UDCCFR_MB1;
done(ep, req, 0);
dev->ep0state = EP0_END_XFER;
local_irq_restore (flags);
return 0;
}
if (dev->req_pending)
ep0start(dev, UDCCS0_IPR, "OUT");
if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0
&& read_ep0_fifo(ep, req))) {
ep0_idle(dev);
done(ep, req, 0);
req = NULL;
}
break;
default:
DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
local_irq_restore (flags);
return -EL2HLT;
}
#ifdef USE_DMA
/* either start dma or prime pio pump */
} else if (ep->dma >= 0) {
kick_dma(ep, req);
#endif
/* can the FIFO can satisfy the request immediately? */
} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
&& write_fifo(ep, req))
req = NULL;
} else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0
&& read_fifo(ep, req)) {
req = NULL;
}
if (likely (req && ep->desc) && ep->dma < 0)
pio_irq_enable(ep->bEndpointAddress);
}
/* pio or dma irq handler advances the queue. */
if (likely (req != 0))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
return 0;
}
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -