📄 net2280.c
字号:
if (ep->dma)
start_dma (ep, req);
else {
/* maybe there's no control data, just status ack */
if (ep->num == 0 && _req->length == 0) {
allow_status (ep);
done (ep, req, 0);
VDEBUG (dev, "%s status ack\n", ep->ep.name);
goto done;
}
/* PIO ... stuff the fifo, or unblock it. */
if (ep->is_in)
write_fifo (ep, _req);
else if (list_empty (&ep->queue)) {
u32 s;
/* OUT FIFO might have packet(s) buffered */
s = readl (&ep->regs->ep_stat);
if ((s & (1 << FIFO_EMPTY)) == 0) {
/* note: _req->short_not_ok is
* ignored here since PIO _always_
* stops queue advance here, and
* _req->status doesn't change for
* short reads (only _req->actual)
*/
if (read_fifo (ep, req)) {
done (ep, req, 0);
if (ep->num == 0)
allow_status (ep);
/* don't queue it */
req = NULL;
} else
s = readl (&ep->regs->ep_stat);
}
/* don't NAK, let the fifo fill */
if (req && (s & (1 << NAK_OUT_PACKETS)))
writel ((1 << CLEAR_NAK_OUT_PACKETS),
&ep->regs->ep_rsp);
}
}
} else if (ep->dma) {
int valid = 1;
if (ep->is_in) {
int expect;
/* preventing magic zlps is per-engine state, not
* per-transfer; irq logic must recover hiccups.
*/
expect = likely (req->req.zero
|| (req->req.length % ep->ep.maxpacket) != 0);
if (expect != ep->in_fifo_validate)
valid = 0;
}
queue_dma (ep, req, valid);
} /* else the irq handler advances the queue. */
if (req)
list_add_tail (&req->queue, &ep->queue);
done:
spin_unlock_irqrestore (&dev->lock, flags);
/* pci writes may still be posted */
return 0;
}
static inline void
dma_done (
struct net2280_ep *ep,
struct net2280_request *req,
u32 dmacount,
int status
)
{
req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
done (ep, req, status);
}
static void restart_dma (struct net2280_ep *ep);
static void scan_dma_completions (struct net2280_ep *ep)
{
/* only look at descriptors that were "naturally" retired,
* so fifo and list head state won't matter
*/
while (!list_empty (&ep->queue)) {
struct net2280_request *req;
u32 tmp;
req = list_entry (ep->queue.next,
struct net2280_request, queue);
if (!req->valid)
break;
rmb ();
tmp = le32_to_cpup (&req->td->dmacount);
if ((tmp & (1 << VALID_BIT)) != 0)
break;
/* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
* cases where DMA must be aborted; this code handles
* all non-abort DMA completions.
*/
if (unlikely (req->td->dmadesc == 0)) {
/* paranoia */
tmp = readl (&ep->dma->dmacount);
if (tmp & DMA_BYTE_COUNT_MASK)
break;
/* single transfer mode */
dma_done (ep, req, tmp, 0);
break;
} else if (!ep->is_in
&& (req->req.length % ep->ep.maxpacket) != 0) {
tmp = readl (&ep->regs->ep_stat);
/* AVOID TROUBLE HERE by not issuing short reads from
* your gadget driver. That helps avoids errata 0121,
* 0122, and 0124; not all cases trigger the warning.
*/
if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
WARN (ep->dev, "%s lost packet sync!\n",
ep->ep.name);
req->req.status = -EOVERFLOW;
} else if ((tmp = readl (&ep->regs->ep_avail)) != 0) {
/* fifo gets flushed later */
ep->out_overflow = 1;
DEBUG (ep->dev, "%s dma, discard %d len %d\n",
ep->ep.name, tmp,
req->req.length);
req->req.status = -EOVERFLOW;
}
}
dma_done (ep, req, tmp, 0);
}
}
static void restart_dma (struct net2280_ep *ep)
{
struct net2280_request *req;
u32 dmactl = dmactl_default;
if (ep->stopped)
return;
req = list_entry (ep->queue.next, struct net2280_request, queue);
if (!use_dma_chaining) {
start_dma (ep, req);
return;
}
/* the 2280 will be processing the queue unless queue hiccups after
* the previous transfer:
* IN: wanted automagic zlp, head doesn't (or vice versa)
* DMA_FIFO_VALIDATE doesn't init from dma descriptors.
* OUT: was "usb-short", we must restart.
*/
if (ep->is_in && !req->valid) {
struct net2280_request *entry, *prev = NULL;
int reqmode, done = 0;
DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
ep->in_fifo_validate = likely (req->req.zero
|| (req->req.length % ep->ep.maxpacket) != 0);
if (ep->in_fifo_validate)
dmactl |= (1 << DMA_FIFO_VALIDATE);
list_for_each_entry (entry, &ep->queue, queue) {
__le32 dmacount;
if (entry == req)
continue;
dmacount = entry->td->dmacount;
if (!done) {
reqmode = likely (entry->req.zero
|| (entry->req.length
% ep->ep.maxpacket) != 0);
if (reqmode == ep->in_fifo_validate) {
entry->valid = 1;
dmacount |= valid_bit;
entry->td->dmacount = dmacount;
prev = entry;
continue;
} else {
/* force a hiccup */
prev->td->dmacount |= dma_done_ie;
done = 1;
}
}
/* walk the rest of the queue so unlinks behave */
entry->valid = 0;
dmacount &= ~valid_bit;
entry->td->dmacount = dmacount;
prev = entry;
}
}
writel (0, &ep->dma->dmactl);
start_queue (ep, dmactl, req->td_dma);
}
static void abort_dma (struct net2280_ep *ep)
{
/* abort the current transfer */
if (likely (!list_empty (&ep->queue))) {
/* FIXME work around errata 0121, 0122, 0124 */
writel ((1 << DMA_ABORT), &ep->dma->dmastat);
spin_stop_dma (ep->dma);
} else
stop_dma (ep->dma);
scan_dma_completions (ep);
}
/* dequeue ALL requests */
static void nuke (struct net2280_ep *ep)
{
struct net2280_request *req;
/* called with spinlock held */
ep->stopped = 1;
if (ep->dma)
abort_dma (ep);
while (!list_empty (&ep->queue)) {
req = list_entry (ep->queue.next,
struct net2280_request,
queue);
done (ep, req, -ESHUTDOWN);
}
}
/* dequeue JUST ONE request */
static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
{
struct net2280_ep *ep;
struct net2280_request *req;
unsigned long flags;
u32 dmactl;
int stopped;
ep = container_of (_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0) || !_req)
return -EINVAL;
spin_lock_irqsave (&ep->dev->lock, flags);
stopped = ep->stopped;
/* quiesce dma while we patch the queue */
dmactl = 0;
ep->stopped = 1;
if (ep->dma) {
dmactl = readl (&ep->dma->dmactl);
/* WARNING erratum 0127 may kick in ... */
stop_dma (ep->dma);
scan_dma_completions (ep);
}
/* make sure it's still queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
if (&req->req != _req) {
spin_unlock_irqrestore (&ep->dev->lock, flags);
return -EINVAL;
}
/* queue head may be partially complete. */
if (ep->queue.next == &req->queue) {
if (ep->dma) {
DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
_req->status = -ECONNRESET;
abort_dma (ep);
if (likely (ep->queue.next == &req->queue)) {
// NOTE: misreports single-transfer mode
req->td->dmacount = 0; /* invalidate */
dma_done (ep, req,
readl (&ep->dma->dmacount),
-ECONNRESET);
}
} else {
DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
done (ep, req, -ECONNRESET);
}
req = NULL;
/* patch up hardware chaining data */
} else if (ep->dma && use_dma_chaining) {
if (req->queue.prev == ep->queue.next) {
writel (le32_to_cpu (req->td->dmadesc),
&ep->dma->dmadesc);
if (req->td->dmacount & dma_done_ie)
writel (readl (&ep->dma->dmacount)
| le32_to_cpu(dma_done_ie),
&ep->dma->dmacount);
} else {
struct net2280_request *prev;
prev = list_entry (req->queue.prev,
struct net2280_request, queue);
prev->td->dmadesc = req->td->dmadesc;
if (req->td->dmacount & dma_done_ie)
prev->td->dmacount |= dma_done_ie;
}
}
if (req)
done (ep, req, -ECONNRESET);
ep->stopped = stopped;
if (ep->dma) {
/* turn off dma on inactive queues */
if (list_empty (&ep->queue))
stop_dma (ep->dma);
else if (!ep->stopped) {
/* resume current request, or start new one */
if (req)
writel (dmactl, &ep->dma->dmactl);
else
start_dma (ep, list_entry (ep->queue.next,
struct net2280_request, queue));
}
}
spin_unlock_irqrestore (&ep->dev->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
static int net2280_fifo_status (struct usb_ep *_ep);
static int
net2280_set_halt (struct usb_ep *_ep, int value)
{
struct net2280_ep *ep;
unsigned long flags;
int retval = 0;
ep = container_of (_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
return -EINVAL;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
spin_lock_irqsave (&ep->dev->lock, flags);
if (!list_empty (&ep->queue))
retval = -EAGAIN;
else if (ep->is_in && value && net2280_fifo_status (_ep) != 0)
retval = -EAGAIN;
else {
VDEBUG (ep->dev, "%s %s halt\n", _ep->name,
value ? "set" : "clear");
/* set/clear, then synch memory views with the device */
if (value) {
if (ep->num == 0)
ep->dev->protocol_stall = 1;
else
set_halt (ep);
} else
clear_halt (ep);
(void) readl (&ep->regs->ep_rsp);
}
spin_unlock_irqrestore (&ep->dev->lock, flags);
return retval;
}
static int
net2280_fifo_status (struct usb_ep *_ep)
{
struct net2280_ep *ep;
u32 avail;
ep = container_of (_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
return -ENODEV;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1);
if (avail > ep->fifo_size)
return -EOVERFLOW;
if (ep->is_in)
avail = ep->fifo_size - avail;
return avail;
}
static void
net2280_fifo_flush (struct usb_ep *_ep)
{
struct net2280_ep *ep;
ep = container_of (_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
return;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return;
writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
(void) readl (&ep->regs->ep_rsp);
}
static struct usb_ep_ops net2280_ep_ops = {
.enable = net2280_enable,
.disable = net2280_disable,
.alloc_request = net2280_alloc_request,
.free_request = net2280_free_request,
.alloc_buffer = net2280_alloc_buffer,
.free_buffer = net2280_free_buffer,
.queue = net2280_queue,
.dequeue = net2280_dequeue,
.set_halt = net2280_set_halt,
.fifo_status = net2280_fifo_status,
.fifo_flush = net2280_fifo_flush,
};
/*-------------------------------------------------------------------------*/
static int net2280_get_frame (struct usb_gadget *_gadget)
{
struct net2280 *dev;
unsigned long flags;
u16 retval;
if (!_gadget)
return -ENODEV;
dev = container_of (_gadget, struct net2280, gadget);
spin_lock_irqsave (&dev->lock, flags);
retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff;
spin_unlock_irqrestore (&dev->lock, flags);
return retval;
}
static int net2280_wakeup (struct usb_gadget *_gadget)
{
struct net2280 *dev;
u32 tmp;
unsigned long flags;
if (!_gadget)
return 0;
dev = container_of (_gadget, struct net2280, gadget);
spin_lock_irqsave (&dev->lock, flags);
tmp = readl (&dev->usb->usbctl);
if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE))
writel (1 << GENERATE_RESUME, &dev->usb->usbstat);
spin_unlock_irqrestore (&dev->lock, flags);
/* pci writes may still be posted */
return 0;
}
static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value)
{
struct net2280 *dev;
u32 tmp;
unsigned long flags;
if (!_gadget)
return 0;
dev = container_of (_gadget, struct net2280, gadget);
spin_lock_irqsave (&dev->lock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -