📄 jz4740_udc.c
字号:
u32 physaddr = virt_to_phys((void *)req->req.buf); max = le16_to_cpu(ep->desc->wMaxPacketSize); if (use_dma) { u32 dma_count; /* DMA interrupt generated due to the last packet loaded into the FIFO */ dma_count = usb_readl(ep->reg_addr) - physaddr; req->req.actual += dma_count; if (dma_count % max) { /* If the last packet is less than MAXP, set INPKTRDY manually */ usb_setb(ep->csr, USB_INCSR_INPKTRDY); } done(ep, req, 0); if (list_empty(&ep->queue)) { pio_irq_disable(ep); return 1; } else { /* advance the request queue */ req = list_entry(ep->queue.next, struct jz4740_request, queue); kick_dma(ep, req); return 0; } } /* * PIO mode handling starts here ... */ csr = usb_readb(ep->csr); if (!(csr & USB_INCSR_FFNOTEMPT)) { unsigned count; int is_last, is_short; count = write_packet(ep, req, max); usb_setb(ep->csr, USB_INCSR_INPKTRDY); /* last packet is usually short (or a zlp) */ if (unlikely(count != max)) is_last = is_short = 1; else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; /* interrupt/iso maxpacket may not fill the fifo */ is_short = unlikely(max < ep_maxpacket(ep)); } DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, ep->ep.name, count, is_last ? "/L" : "", is_short ? "/S" : "", req->req.length - req->req.actual, req); /* requests complete when all IN data is in the FIFO */ if (is_last) { done(ep, req, 0); if (list_empty(&ep->queue)) { pio_irq_disable(ep); } return 1; } } else { DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); } return 0;}/** Read to request from FIFO (max read == bytes in fifo) * Return: 0 = still running, 1 = completed, negative = errno * NOTE: INDEX register must be set for EP */static int read_fifo(struct jz4740_ep *ep, struct jz4740_request *req){ u32 csr; unsigned count, is_short; u32 physaddr = virt_to_phys((void *)req->req.buf); if (use_dma) { u32 dma_count; /* DMA interrupt generated due to a packet less than MAXP loaded into the FIFO */ dma_count = usb_readl(ep->reg_addr) - physaddr; req->req.actual += dma_count; /* Disable interrupt and DMA */ pio_irq_disable(ep); usb_writel(USB_REG_CNTL2, 0); /* Read all bytes from this packet */ count = usb_readw(USB_REG_OUTCOUNT); count = read_packet(ep, req, count); if (count) { /* If the last packet is greater than zero, clear OUTPKTRDY manually */ usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); } done(ep, req, 0); if (!list_empty(&ep->queue)) { /* advance the request queue */ req = list_entry(ep->queue.next, struct jz4740_request, queue); kick_dma(ep, req); } return 1; } /* * PIO mode handling starts here ... */ /* make sure there's a packet in the FIFO. */ csr = usb_readb(ep->csr); if (!(csr & USB_OUTCSR_OUTPKTRDY)) { DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); return -EINVAL; } /* read all bytes from this packet */ count = usb_readw(USB_REG_OUTCOUNT); is_short = (count < ep->ep.maxpacket); count = read_packet(ep, req, count); DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", ep->ep.name, csr, count, is_short ? "/S" : "", req, req->req.actual, req->req.length); /* Clear OutPktRdy */ usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); /* completion */ if (is_short || req->req.actual == req->req.length) { done(ep, req, 0); if (list_empty(&ep->queue)) pio_irq_disable(ep); return 1; } /* finished that packet. the next one may be waiting... */ return 0;}/* * done - retire a request; caller blocked irqs * INDEX register is preserved to keep same */static void done(struct jz4740_ep *ep, struct jz4740_request *req, int status){ unsigned int stopped = ep->stopped; u32 index; DEBUG("%s, %p\n", __FUNCTION__, ep); list_del_init(&req->queue); if (likely(req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; if (status && status != -ESHUTDOWN) DEBUG("complete %s req %p stat %d len %u/%u\n", ep->ep.name, &req->req, status, req->req.actual, req->req.length); /* don't modify queue heads during completion callback */ ep->stopped = 1; /* Read current index (completion may modify it) */ index = usb_readb(USB_REG_INDEX); spin_unlock(&ep->dev->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->dev->lock); /* Restore index */ usb_set_index(index); ep->stopped = stopped;}/** Enable EP interrupt */static void pio_irq_enable(struct jz4740_ep *ep){ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); if (ep_is_in(ep)) { switch (ep_index(ep)) { case 1: usb_setw(USB_REG_INTRINE, USB_INTR_INEP1); break; case 2: usb_setw(USB_REG_INTRINE, USB_INTR_INEP2); break; default: DEBUG("Unknown endpoint: %d\n", ep_index(ep)); break; } } else { switch (ep_index(ep)) { case 1: usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); break; default: DEBUG("Unknown endpoint: %d\n", ep_index(ep)); break; } }}/** Disable EP interrupt */static void pio_irq_disable(struct jz4740_ep *ep){ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); if (ep_is_in(ep)) { switch (ep_index(ep)) { case 1: usb_clearw(USB_REG_INTRINE, USB_INTR_INEP1); break; case 2: usb_clearw(USB_REG_INTRINE, USB_INTR_INEP2); break; default: DEBUG("Unknown endpoint: %d\n", ep_index(ep)); break; } } else { switch (ep_index(ep)) { case 1: usb_clearw(USB_REG_INTROUTE, USB_INTR_OUTEP1); break; default: DEBUG("Unknown endpoint: %d\n", ep_index(ep)); break; } }}/* * nuke - dequeue ALL requests */static void nuke(struct jz4740_ep *ep, int status){ struct jz4740_request *req; DEBUG("%s, %p\n", __FUNCTION__, ep); /* Flush FIFO */ flush(ep); /* called with irqs blocked */ while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct jz4740_request, queue); done(ep, req, status); } /* Disable IRQ if EP is enabled (has descriptor) */ if (ep->desc) pio_irq_disable(ep);}/** Flush EP FIFO * NOTE: INDEX register must be set before this call */static void flush(struct jz4740_ep *ep){ DEBUG("%s, %p\n", __FUNCTION__, ep); switch (ep->ep_type) { case ep_control: break; case ep_bulk_in: case ep_interrupt: usb_setb(ep->csr, USB_INCSR_FF); break; case ep_bulk_out: usb_setb(ep->csr, USB_OUTCSR_FF); break; }}/** * jz4740_in_epn - handle IN interrupt */static void jz4740_in_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr){ u32 csr; struct jz4740_ep *ep = &dev->ep[ep_idx + 1]; struct jz4740_request *req; usb_set_index(ep_index(ep)); csr = usb_readb(ep->csr); DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); if (csr & USB_INCSR_SENTSTALL) { DEBUG("USB_INCSR_SENTSTALL\n"); usb_clearb(ep->csr, USB_INCSR_SENTSTALL); return; } if (!ep->desc) { DEBUG("%s: NO EP DESC\n", __FUNCTION__); return; } if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct jz4740_request, queue); DEBUG("req: %p\n", req); if (!req) return; write_fifo(ep, req);}/* * Bulk OUT (recv) */static void jz4740_out_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr){ struct jz4740_ep *ep = &dev->ep[ep_idx]; struct jz4740_request *req; DEBUG("%s: %d\n", __FUNCTION__, ep_idx); usb_set_index(ep_index(ep)); if (ep->desc) { u32 csr; if (use_dma) { /* DMA starts here ... */ if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct jz4740_request, queue); if (req) read_fifo(ep, req); return; } /* * PIO mode starts here ... */ while ((csr = usb_readb(ep->csr)) & (USB_OUTCSR_OUTPKTRDY | USB_OUTCSR_SENTSTALL)) { DEBUG("%s: %x\n", __FUNCTION__, csr); if (csr & USB_OUTCSR_SENTSTALL) { DEBUG("%s: stall sent, flush fifo\n", __FUNCTION__); /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ flush(ep); } else if (csr & USB_OUTCSR_OUTPKTRDY) { if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct jz4740_request, queue); if (!req) { DEBUG("%s: NULL REQ %d\n", __FUNCTION__, ep_idx); break; } else { read_fifo(ep, req); } } } } else { /* Throw packet away.. */ printk("%s: ep %p ep_indx %d No descriptor?!?\n", __FUNCTION__, ep, ep_idx); flush(ep); }}static int jz4740_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct jz4740_ep *ep; struct jz4740_udc *dev; unsigned long flags; u32 max, csrh = 0; ep = container_of(_ep, struct jz4740_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress) { DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); return -EINVAL; } /* xfer types must match, except that interrupt ~= bulk */ if (ep->bmAttributes != desc->bmAttributes && ep->bmAttributes != USB_ENDPOINT_XFER_BULK && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); return -EINVAL; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DEBUG("%s, bogus device state\n", __FUNCTION__); return -ESHUTDOWN; } max = le16_to_cpu(desc->wMaxPacketSize); /* Configure the endpoint */ usb_set_index(desc->bEndpointAddress & 0x0F); if (ep_is_in(ep)) { usb_writew(USB_REG_INMAXP, max); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: csrh &= ~USB_INCSRH_ISO; break; case USB_ENDPOINT_XFER_ISOC: csrh |= USB_INCSRH_ISO; break; } usb_writeb(USB_REG_INCSRH, csrh); } else { usb_writew(USB_REG_OUTMAXP, max); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: csrh &= ~USB_OUTCSRH_ISO; break; case USB_ENDPOINT_XFER_INT: csrh &= ~USB_OUTCSRH_ISO; csrh |= USB_OUTCSRH_DNYT; break; case USB_ENDPOINT_XFER_ISOC: csrh |= USB_OUTCSRH_ISO; break; } usb_writeb(USB_REG_OUTCSRH, csrh); } spin_lock_irqsave(&ep->dev->lock, flags); ep->stopped = 0; ep->desc = desc; ep->pio_irqs = 0; ep->ep.maxpacket = max; spin_unlock_irqrestore(&ep->dev->lock, flags); /* Reset halt state (does flush) */ jz4740_set_halt(_ep, 0); DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); return 0;}/** Disable EP * NOTE: Sets INDEX register */static int jz4740_ep_disable(struct usb_ep *_ep){ struct jz4740_ep *ep; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); ep = container_of(_ep, struct jz4740_ep, ep); if (!_ep || !ep->desc) { DEBUG("%s, %s not enabled\n", __FUNCTION__, _ep ? ep->ep.name : NULL); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); usb_set_index(ep_index(ep)); /* Nuke all pending requests (does flush) */ nuke(ep, -ESHUTDOWN); /* Disable ep IRQ */ pio_irq_disable(ep); ep->desc = 0; ep->stopped = 1; spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); return 0;}static struct usb_request *jz4740_alloc_request(struct usb_ep *ep, gfp_t gfp_flags){ struct jz4740_request *req; DEBUG("%s, %p\n", __FUNCTION__, ep); req = kzalloc(sizeof(*req), gfp_flags); if (!req) return 0; INIT_LIST_HEAD(&req->queue); return &req->req;}static void jz4740_free_request(struct usb_ep *ep, struct usb_request *_req){ struct jz4740_request *req; DEBUG("%s, %p\n", __FUNCTION__, ep); req = container_of(_req, struct jz4740_request, req); WARN_ON(!list_empty(&req->queue)); kfree(req);}/*--------------------------------------------------------------------*//** Queue one request * Kickstart transfer if needed * NOTE: Sets INDEX register */static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct jz4740_request *req; struct jz4740_ep *ep; struct jz4740_udc *dev; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); req = container_of(_req, struct jz4740_request, req); if (unlikely
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -