/** 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", __func__); 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(KERN_WARNING "%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; }
/** 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; } }
/** Read to request from FIFO (max read == bytes in fifo) * Return: 0 = still running, 1 = completed, negative = errno */ static int read_fifo(struct elfin_ep *ep, struct elfin_request *req) { u32 csr; u8 *buf; unsigned bufferspace, count, is_short; void* fifo = ep->fifo; /* make sure there's a packet in the FIFO. */ csr = usb_read(ep->csr1, ep_index(ep)); if (!(csr & S3C2410_UDC_OCSR1_PKTRDY)) { DPRINTK("%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(S3C2410_UDC_OUT_FIFO_CNT2_REG, ep_index(ep)) & 0xff ) << 8) | (usb_read(S3C2410_UDC_OUT_FIFO_CNT1_REG, ep_index(ep)) & 0xff)); req->req.actual += min(count, bufferspace); is_short = (count < ep->ep.maxpacket); DPRINTK("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) __raw_readl(fifo); 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(S3C2410_UDC_OCSR1_PKTRDY, ep->csr1, ep_index(ep)); /* completion */ if (is_short || req->req.actual == req->req.length) { done(ep, req, 0); return 1; } /* finished that packet. the next one may be waiting... */ return 0; }
/* until it's enabled, this UDC should be completely invisible * to any USB host. */ static void udc_enable(struct lh7a40x_udc *dev) { int ep; DEBUG("%s, %p\n", __FUNCTION__, dev); dev->gadget.speed = USB_SPEED_UNKNOWN; #ifdef CONFIG_ARCH_LH7A404 /* Set Port C bit 1 & 2 as output */ set_portc_ddr(1, 1); set_portc_ddr(2, 1); /* Enable USB power */ set_portc_dr(1, 0); #endif /* * C.f Chapter 18.1.3.1 Initializing the USB */ /* Disable the USB */ usb_clear(PM_USB_ENABLE, USB_PM); /* Reset APB & I/O sides of the USB */ usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET); mdelay(5); usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); /* Set MAXP values for each */ for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { struct lh7a40x_ep *ep_reg = &dev->ep[ep]; u32 csr; usb_set_index(ep); switch (ep_reg->ep_type) { case ep_bulk_in: case ep_interrupt: usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, ep_reg->csr2); /* Fall through */ case ep_control: usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), USB_IN_MAXP); break; case ep_bulk_out: usb_clear(USB_OUT_CSR2_USB_DMA_EN | USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), USB_OUT_MAXP); break; } /* Read & Write CSR1, just in case */ csr = usb_read(ep_reg->csr1); usb_write(csr, ep_reg->csr1); flush(ep_reg); } /* Disable interrupts */ usb_write(0, USB_IN_INT_EN); usb_write(0, USB_OUT_INT_EN); usb_write(0, USB_INT_EN); /* Enable interrupts */ usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); /* Dont enable rest of the interrupts */ /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ /* Enable SUSPEND */ usb_set(PM_ENABLE_SUSPEND, USB_PM); /* Enable the USB */ usb_set(PM_USB_ENABLE, USB_PM); #ifdef CONFIG_ARCH_LH7A404 /* NOTE: DOES NOT WORK! */ /* Let host detect UDC: * Software must write a 0 to the PMR:DCP_CTRL bit to turn this * transistor on and pull the USBDP pin HIGH. */ /* usb_clear(PM_USB_DCP, USB_PM); usb_set(PM_USB_DCP, USB_PM); */ #endif }