📄 lh7a40x_udc.c
字号:
} 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 lh7a40x_ep *ep, struct lh7a40x_request *req){ u32 csr; u8 *buf; unsigned bufferspace, count, is_short; volatile u32 *fifo = (volatile u32 *)ep->fifo; /* make sure there's a packet in the FIFO. */ csr = usb_read(ep->csr1); if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) { DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); return -EINVAL; } buf = req->req.buf + req->req.actual; prefetchw(buf); bufferspace = req->req.length - req->req.actual; /* read all bytes from this packet */ count = usb_read(USB_OUT_FIFO_WC1); req->req.actual += min(count, bufferspace); is_short = (count < ep->ep.maxpacket); 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); while (likely(count-- != 0)) { u8 byte = (u8) (*fifo & 0xff); 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) printk("%s overflow %d\n", ep->ep.name, count); req->req.status = -EOVERFLOW; } else { *buf++ = byte; bufferspace--; } } usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1); /* completion */ if (is_short || req->req.actual == req->req.length) { done(ep, req, 0); usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); if (list_empty(&ep->queue)) pio_irq_disable(ep_index(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 lh7a40x_ep *ep, struct lh7a40x_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_read(USB_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(int ep){ DEBUG("%s: %d\n", __FUNCTION__, ep); switch (ep) { case 1: usb_set(USB_IN_INT_EP1, USB_IN_INT_EN); break; case 2: usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); break; case 3: usb_set(USB_IN_INT_EP3, USB_IN_INT_EN); break; default: DEBUG("Unknown endpoint: %d\n", ep); break; }}/** Disable EP interrupt */static void pio_irq_disable(int ep){ DEBUG("%s: %d\n", __FUNCTION__, ep); switch (ep) { case 1: usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN); break; case 2: usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN); break; case 3: usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN); break; default: DEBUG("Unknown endpoint: %d\n", ep); break; }}/* * nuke - dequeue ALL requests */void nuke(struct lh7a40x_ep *ep, int status){ struct lh7a40x_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 lh7a40x_request, queue); done(ep, req, status); } /* Disable IRQ if EP is enabled (has descriptor) */ if (ep->desc) pio_irq_disable(ep_index(ep));}/*void nuke_all(struct lh7a40x_udc *dev){ int n; for(n=0; n<UDC_MAX_ENDPOINTS; n++) { struct lh7a40x_ep *ep = &dev->ep[n]; usb_set_index(n); nuke(ep, 0); }}*//*static void flush_all(struct lh7a40x_udc *dev){ int n; for (n = 0; n < UDC_MAX_ENDPOINTS; n++) { struct lh7a40x_ep *ep = &dev->ep[n]; flush(ep); }}*//** Flush EP * NOTE: INDEX register must be set before this call */static void flush(struct lh7a40x_ep *ep){ DEBUG("%s, %p\n", __FUNCTION__, ep); switch (ep->ep_type) { case ep_control: /* check, by implication c.f. 15.1.2.11 */ break; case ep_bulk_in: case ep_interrupt: /* if(csr & USB_IN_CSR1_IN_PKT_RDY) */ usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1); break; case ep_bulk_out: /* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */ usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); break; }}/** * lh7a40x_in_epn - handle IN interrupt */static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr){ u32 csr; struct lh7a40x_ep *ep = &dev->ep[ep_idx]; struct lh7a40x_request *req; usb_set_index(ep_idx); csr = usb_read(ep->csr1); DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); if (csr & USB_IN_CSR1_SENT_STALL) { DEBUG("USB_IN_CSR1_SENT_STALL\n"); usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ , ep->csr1); 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 lh7a40x_request, queue); DEBUG("req: %p\n", req); if (!req) return; write_fifo(ep, req);}/* ********************************************************************************************* *//* Bulk OUT (recv) */static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr){ struct lh7a40x_ep *ep = &dev->ep[ep_idx]; struct lh7a40x_request *req; DEBUG("%s: %d\n", __FUNCTION__, ep_idx); usb_set_index(ep_idx); if (ep->desc) { u32 csr; csr = usb_read(ep->csr1); while ((csr = usb_read(ep-> csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY | USB_OUT_CSR1_SENT_STALL)) { DEBUG("%s: %x\n", __FUNCTION__, csr); if (csr & USB_OUT_CSR1_SENT_STALL) { DEBUG("%s: stall sent, flush fifo\n", __FUNCTION__); /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ flush(ep); } else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) { if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct lh7a40x_request, queue); if (!req) { printk("%s: NULL REQ %d\n", __FUNCTION__, ep_idx); flush(ep); break; } else { read_fifo(ep, req); } } } } else { /* Throw packet away.. */ printk("%s: No descriptor?!?\n", __FUNCTION__); flush(ep); }}static void stop_activity(struct lh7a40x_udc *dev, struct usb_gadget_driver *driver){ int i; /* don't disconnect drivers more than once */ if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = 0; dev->gadget.speed = USB_SPEED_UNKNOWN; /* prevent new request submissions, kill any outstanding requests */ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { struct lh7a40x_ep *ep = &dev->ep[i]; ep->stopped = 1; usb_set_index(i); nuke(ep, -ESHUTDOWN); } /* report disconnect; the driver is already quiesced */ if (driver) { spin_unlock(&dev->lock); driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } /* re-init driver-visible data structures */ udc_reinit(dev);}/** Handle USB RESET interrupt */static void lh7a40x_reset_intr(struct lh7a40x_udc *dev){#if 0 /* def CONFIG_ARCH_LH7A404 */ /* Does not work always... */ DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address); if (!dev->usb_address) { /*usb_set(USB_RESET_IO, USB_RESET); mdelay(5); usb_clear(USB_RESET_IO, USB_RESET); */ return; } /* Put the USB controller into reset. */ usb_set(USB_RESET_IO, USB_RESET); /* Set Device ID to 0 */ udc_set_address(dev, 0); /* Let PLL2 settle down */ mdelay(5); /* Release the USB controller from reset */ usb_clear(USB_RESET_IO, USB_RESET); /* Re-enable UDC */ udc_enable(dev);#endif dev->gadget.speed = USB_SPEED_FULL;}/* * lh7a40x usb client interrupt handler. */static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r){ struct lh7a40x_udc *dev = _dev; DEBUG("\n\n"); spin_lock(&dev->lock); for (;;) { u32 intr_in = usb_read(USB_IN_INT); u32 intr_out = usb_read(USB_OUT_INT); u32 intr_int = usb_read(USB_INT); /* Test also against enable bits.. (lh7a40x errata).. Sigh.. */ u32 in_en = usb_read(USB_IN_INT_EN); u32 out_en = usb_read(USB_OUT_INT_EN); if (!intr_out && !intr_in && !intr_int) break; DEBUG("%s (on state %s)\n", __FUNCTION__, state_names[dev->ep0state]); DEBUG("intr_out = %x\n", intr_out); DEBUG("intr_in = %x\n", intr_in); DEBUG("intr_int = %x\n", intr_int); if (intr_in) { usb_write(intr_in, USB_IN_INT); if ((intr_in & USB_IN_INT_EP1) && (in_en & USB_IN_INT_EP1)) { DEBUG("USB_IN_INT_EP1\n"); lh7a40x_in_epn(dev, 1, intr_in); } if ((intr_in & USB_IN_INT_EP3) && (in_en & USB_IN_INT_EP3)) { DEBUG("USB_IN_INT_EP3\n"); lh7a40x_in_epn(dev, 3, intr_in); } if (intr_in & USB_IN_INT_EP0) { DEBUG("USB_IN_INT_EP0 (control)\n"); lh7a40x_handle_ep0(dev, intr_in); } } if (intr_out) { usb_write(intr_out, USB_OUT_INT); if ((intr_out & USB_OUT_INT_EP2) && (out_en & USB_OUT_INT_EP2)) { DEBUG("USB_OUT_INT_EP2\n"); lh7a40x_out_epn(dev, 2, intr_out); } } if (intr_int) { usb_write(intr_int, USB_INT); if (intr_int & USB_INT_RESET_INT) { lh7a40x_reset_intr(dev); } if (intr_int & USB_INT_RESUME_INT) { DEBUG("USB resume\n"); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume && is_usb_connected()) { dev->driver->resume(&dev->gadget); } } if (intr_int & USB_INT_SUSPEND_INT) { DEBUG("USB suspend%s\n", is_usb_connected()? "" : "+disconnect"); if (!is_usb_connected()) { stop_activity(dev, dev->driver); } else if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) { dev->driver->suspend(&dev->gadget); } } } } spin_unlock(&dev->lock); return IRQ_HANDLED;}static int lh7a40x_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct lh7a40x_ep *ep; struct lh7a40x_udc *dev; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); ep = container_of(_ep, struct lh7a40x_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { 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; } /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) || !desc->wMaxPacketSize) { DEBUG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); return -ERANGE; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DEBUG("%s, bogus device state\n", __FUNCTION__); return -ESHUTDOWN; } spin_lock_irqsave(&ep->dev->lock, flags); ep->stopped = 0; ep->desc = desc; ep->pio_irqs = 0; ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); spin_unlock_irqrestore(&ep->dev->lock, flags); /* Reset halt state (does flush) */ lh7a40x_set_halt(_ep, 0); DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); return 0;}/** Disable EP * NOTE: Sets INDEX register */static int lh7a40x_ep_disable(struct usb_ep *_ep){ struct lh7a40x_ep *ep; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); ep = container_of(_ep, struct lh7a40x_ep, ep); if (!_ep || !ep->desc) { DEBUG("%s, %s not enabled\n", __FUNCTION__, _ep ? ep->ep.name : NULL); return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -