📄 jz4730_udc.c
字号:
/* * JZ4730 USB Device Controller driver * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. *//* * This device has ep0 and six bulk/interrupt/iso endpoints. * * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep3in-bulk, * ep4in-iso, ep5out-bulk, ep6out-bulk, ep7out-iso. * - Gadget drivers can choose ep maxpacket (8/16/32/64). * - Just PIO mode currently. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/usb.h>#include <linux/usb/gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm-mips/mach-jz4730/regs.h>#include <asm-mips/mach-jz4730/ops.h>#include "jz4730_udc.h"//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)//#define DEBUG_EP0(fmt,args...) printk(KERN_DEBUG fmt , ## args)#ifndef DEBUG# define DEBUG(fmt,args...) do {} while(0)#endif#ifndef DEBUG_EP0# define DEBUG_EP0(fmt,args...) do {} while(0)#endif#define DRIVER_DESC "JZ4730 USB Device Controller"#define DRIVER_VERSION "20 Sep 2007"static const char driver_name [] = "jz4730_udc";static const char driver_desc [] = DRIVER_DESC;static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */module_param(udc_debug, int, 0);MODULE_PARM_DESC(udc_debug, "test udc cable type");#ifdef CONFIG_JZ_UDC_HOTPLUGextern int jz_udc_active; /* 0: No actions; 1: Have actions */#endif/* * Local declarations. */static void nuke(struct jz4730_ep *, int status);static inline void pio_irq_enable(struct jz4730_ep *ep);static inline void pio_irq_disable(struct jz4730_ep *ep);static void jz4730_udc_release (struct device *dev) {}/*-------------------------------------------------------------------------*/static int jz4730_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct jz4730_udc *dev; struct jz4730_ep *ep; unsigned long flags; u32 max; ep = container_of(_ep, struct jz4730_ep, ep); if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; if (ep == &dev->ep[0]) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; if (ep->index != (desc->bEndpointAddress & 0x0f)) return -EINVAL; switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: break; default: return -EINVAL; }// max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); max = 64; ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; spin_lock_irqsave(&ep->dev->lock, flags); ep->stopped = 0; ep->desc = desc; ep->ep.maxpacket = max; spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG("enable %s %s maxpacket %u\n", ep->ep.name, ep->is_in ? "IN" : "OUT", max); return 0;}static int jz4730_ep_disable(struct usb_ep *_ep){ struct jz4730_ep *ep; struct jz4730_udc *dev; unsigned long flags; ep = container_of(_ep, struct jz4730_ep, ep); if (!_ep || !ep->desc) return -ENODEV; dev = ep->dev; if (dev->ep0state == EP0_SUSPEND) return -EBUSY; DEBUG("disable %s\n", _ep->name); spin_lock_irqsave(&dev->lock, flags); /* Nuke all pending requests */ nuke(ep, -ESHUTDOWN); /* Disable ep IRQ */ pio_irq_disable(ep); ep->desc = 0; ep->stopped = 1; spin_unlock_irqrestore(&dev->lock, flags); return 0;}static struct usb_request *jz4730_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags){ struct jz4730_request *req; req = kzalloc(sizeof *req, gfp_flags); if (!req) return 0; INIT_LIST_HEAD(&req->queue); return &req->req;}static void jz4730_free_request(struct usb_ep *_ep, struct usb_request *_req){ struct jz4730_request *req; req = container_of(_req, struct jz4730_request, req); WARN_ON(!list_empty(&req->queue)); kfree(req);}static void *jz4730_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, gfp_t gfp_flags){ void *retval; retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); if (retval) *dma = virt_to_phys(retval); return retval;}static void jz4730_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes){ kfree(buf);}/* * done - retire a request; caller blocked irqs */static void done(struct jz4730_ep *ep, struct jz4730_request *req, int status){ struct jz4730_udc *dev; unsigned stopped = ep->stopped; 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); dev = ep->dev; /* don't modify queue heads during completion callback */ ep->stopped = 1; spin_unlock(&dev->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&dev->lock); ep->stopped = stopped;}/*-------------------------------------------------------------------------*/static __inline__ int write_packet(struct jz4730_ep *ep, struct jz4730_request *req, int max){ u8 *buf; int length, nlong, nbyte; volatile u32 *fifo = (volatile u32 *)ep->fifo; buf = req->req.buf + req->req.actual; prefetch(buf); length = req->req.length - req->req.actual; length = min(length, max); req->req.actual += length; DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); if (!length) { /* Send ZLP */ writel(0, (unsigned int *)UDC_TXZLP); writel(0x12345678, (unsigned int *)fifo); } else { nlong = length >> 2; nbyte = length & 0x3; while (nlong--) { *fifo = *((u32 *)buf); buf += 4; } while (nbyte--) { *((volatile u8 *)fifo) = *buf++; } } writel(0, (unsigned int *)UDC_TXCONFIRM); return length;}static __inline__ int read_packet(struct jz4730_ep *ep, struct jz4730_request *req, int count){ u8 *buf; int length, nlong, nbyte; volatile u32 *fifo = (volatile u32 *)ep->fifo; buf = req->req.buf + req->req.actual; prefetchw(buf); length = req->req.length - req->req.actual; length = min(length, count); req->req.actual += length; DEBUG("Read %d, fifo %p\n", length, fifo); nlong = length >> 2; nbyte = length & 0x3; while (nlong--) { *((u32 *)buf) = *fifo; buf += 4; } if (nbyte) { u32 data = *fifo; while (nbyte--) { *buf++ = data & 0x0ff; data >>= 8; } } REG32(UDC_RXCONFIRM); return length;}/** Write request to FIFO (max write == maxp size) * Return: 0 = still running, 1 = completed, negative = errno */static int write_fifo(struct jz4730_ep *ep, struct jz4730_request *req){ u32 max, count; int is_last; max = ep->ep.maxpacket; count = write_packet(ep, req, max); /* last packet often short (sometimes a zlp, especially on ep0) */ if (unlikely(count != max)) { is_last = 1; } else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; } DEBUG("write %s (%d)(IN) %d bytes%s req %p %d/%d is_last %d\n", ep->ep.name, ep->index, count, (count != ep->ep.maxpacket) ? " (short)" : "", req, req->req.actual, req->req.length, is_last); /* requests complete when all IN data is in the FIFO, * or sometimes later, if a zlp was needed. */ if (is_last) { done(ep, req, 0); return 1; } return 0;}/** Read to request from FIFO (max read == bytes in fifo) * Return: 0 = still running, 1 = completed, negative = errno */static int read_fifo(struct jz4730_ep *ep, struct jz4730_request *req, u32 count){ int is_short; is_short = (count < ep->ep.maxpacket); count = read_packet(ep, req, count); DEBUG("read %s %u bytes%s OUT req %p %u/%u is_short %d\n", ep->ep.name, count, (count < ep->ep.maxpacket) ? "(short)" : "", req, req->req.actual, req->req.length, is_short); /* 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;}static inline void pio_irq_enable(struct jz4730_ep *ep){ switch (ep->index) { case 0: REG_UDC_EPIntMR &= ~0x1; break; case 1: case 2: case 3: case 4: REG_UDC_EPIntMR &= ~(1 << ep->index); break; case 5: case 6: case 7: REG_UDC_EPIntMR &= ~(1 << (ep->index + 16)); break; }}static inline void pio_irq_disable(struct jz4730_ep *ep){ switch (ep->index) { case 0: REG_UDC_EPIntMR |= 0x1; break; case 1: case 2: case 3: case 4: REG_UDC_EPIntMR |= (1 << ep->index); break; case 5: case 6: case 7: REG_UDC_EPIntMR |= (1 << (ep->index + 16)); break; }}/*-------------------------------------------------------------------------*/static intjz4730_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct jz4730_request *req; struct jz4730_ep *ep; struct jz4730_udc *dev; unsigned long flags; int status; /* always require a cpu-view buffer so pio works */ req = container_of(_req, struct jz4730_request, req); if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) return -EINVAL; ep = container_of(_ep, struct jz4730_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->index != 0))) return -EINVAL; dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; DEBUG("%s queue req %p, len %u buf %p\n", _ep->name, _req, _req->length, _req->buf); spin_lock_irqsave(&dev->lock, flags); _req->status = -EINPROGRESS; _req->actual = 0; /* for ep0 IN without premature status, zlp is required and * writing EOP starts the status stage (OUT). */ if (unlikely(ep->index == 0 && ep->is_in)) _req->zero = 1; /* kickstart this i/o queue? */ status = 0; if (list_empty(&ep->queue) && likely(!ep->stopped)) { if (unlikely(ep->index == 0)) { pio_irq_enable(ep); if (ep->irq_pending || (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)) { u32 stats, count; stats = REG_UDC_EP0OutSR; if (stats & UDC_EPSR_OUT_RCVDATA) { ep->irq_pending = 0; REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0) REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; count = OUT_COUNT(stats); if (read_fifo(ep, req, count) == 1) req = 0; } } } else if (ep->is_in) { /* EP1 ~ EP4 */ if (ep->irq_pending || (REG_UDC_EPIntR & UDC_EPIntR_INEP2)) { if (REG_UDC_EP2InSR & UDC_EPSR_IN) { ep->irq_pending = 0; REG_UDC_EP2InSR &= ~UDC_EPSR_IN; if (REG_UDC_EPIntR & UDC_EPIntR_INEP2) REG_UDC_EPIntR = UDC_EPIntR_INEP2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -