📄 net2280.c
字号:
} } } 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 voiddma_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 = 0; 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) { u32 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 = 0; /* 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) | 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 req ? 0 : -EOPNOTSUPP;}/*-------------------------------------------------------------------------*/static int net2280_fifo_status (struct usb_ep *_ep);static intnet2280_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 intnet2280_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 voidnet2280_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); tmp = readl (&dev->usb->usbctl); if (value) tmp |= (1 << SELF_POWERED_STATUS); else tmp &= ~(1 << SELF_POWERED_STATUS); writel (tmp, &dev->usb->usbctl); spin_unlock_irqrestore (&dev->lock, flags); return 0;}static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered,};/*-------------------------------------------------------------------------*/#ifdef USE_SYSFS_DEBUG_FILES/* "function" sysfs attribute */static ssize_tshow_function (struct device *_dev, char *buf){ struct net2280 *dev = dev_get_drvdata (_dev); if (!dev->driver || !dev->driver->function || strlen (dev->driver->function) > PAGE_SIZE) return 0; return snprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);}static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);static ssize_tshow_registers (struct device *_dev, char *buf){ struct net2280 *dev; char *next; unsigned size, t; unsigned long flags; int i; u32 t1, t2; char *s; dev = dev_get_drvdata (_dev); next = buf; size = PAGE_SIZE; spin_lock_irqsave (&dev->lock, flags); if (dev->driver) s = dev->driver->driver.name; else s = "(none)"; /* Main Control Registers */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -