?? pxa2xx_udc.c
字號(hào):
/* * linux/drivers/usb/gadget/pxa2xx_udc.c * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers * * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) * Copyright (C) 2003 Robert Schwebel, Pengutronix * Copyright (C) 2003 Benedikt Spranger, Pengutronix * Copyright (C) 2003 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#define DEBUG 1// #define VERBOSE DBG_VERBOSE#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/types.h>#include <linux/version.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/mm.h>// #include <linux/device.h>#include <asm/byteorder.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include <asm/proc/cache.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>/* * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx * series processors. The UDC for the IXP 4xx series is very similar. * There are fifteen endpoints, in addition to ep0. * * Such controller drivers work with a gadget driver. The gadget driver * returns descriptors, implements configuration and data protocols used * by the host to interact with this device, and allocates endpoints to * the different protocol interfaces. The controller driver virtualizes * usb hardware so that the gadget drivers will be more portable. * * This UDC hardware wants to implement a bit too much USB protocol, so * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. *//* NOTE: the 2.6 driver is probably the most current version */#define DRIVER_VERSION "5-Jan-2004"#define DRIVER_DESC "PXA 2xx USB Device Controller driver"static const char driver_name [] = "pxa2xx_udc";static const char ep0name [] = "ep0";// #define USE_DMA// #define USE_OUT_DMA// #define DISABLE_TEST_MODE#ifdef CONFIG_PROC_FS#define UDC_PROC_FILE#endif#ifdef CONFIG_ARCH_IXP425#undef USE_DMA/* cpu-specific register addresses are compiled in to this code */#ifdef CONFIG_ARCH_PXA#error "Can't configure both IXP and PXA"#endif#endif#ifdef CONFIG_EMBEDDED/* few strings, and little code to use them */#undef DEBUG#undef UDC_PROC_FILE#endif#include "pxa2xx_udc.h"#ifdef USE_DMAstatic int use_dma = 1;MODULE_PARM (use_dma, "i");MODULE_PARM_DESC (use_dma, "true to use dma");static void dma_nodesc_handler (int dmach, void *_ep, struct pt_regs *r);static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req);#ifdef USE_OUT_DMA#define DMASTR " (dma support)"#else#define DMASTR " (dma in)"#endif#else /* !USE_DMA */#define DMASTR " (pio only)"#undef USE_OUT_DMA#endif#ifdef CONFIG_USB_PXA2XX_SMALL#define SIZE_STR " (small)"#else#define SIZE_STR ""#endif#ifdef DISABLE_TEST_MODE/* (mode == 0) == no undocumented chip tweaks * (mode & 1) == double buffer bulk IN * (mode & 2) == double buffer bulk OUT * ... so mode = 3 (or 7, 15, etc) does it for both */static ushort fifo_mode = 0;MODULE_PARM (fifo_mode, "h");MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");#endif/* --------------------------------------------------------------------------- * endpoint related parts of the api to the usb controller hardware, * used by gadget driver; and the inner talker-to-hardware core. * --------------------------------------------------------------------------- */static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);static void nuke (struct pxa2xx_ep *, int status);static void pio_irq_enable(int bEndpointAddress){ bEndpointAddress &= 0xf; if (bEndpointAddress < 8) UICR0 &= ~(1 << bEndpointAddress); else { bEndpointAddress -= 8; UICR1 &= ~(1 << bEndpointAddress); }}static void pio_irq_disable(int bEndpointAddress){ bEndpointAddress &= 0xf; if (bEndpointAddress < 8) UICR0 |= 1 << bEndpointAddress; else { bEndpointAddress -= 8; UICR1 |= 1 << bEndpointAddress; }}/* The UDCCR reg contains mask and interrupt status bits, * so using '|=' isn't safe as it may ack an interrupt. */#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)static inline void udc_set_mask_UDCCR(int mask){ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);}static inline void udc_clear_mask_UDCCR(int mask){ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);}static inline void udc_ack_int_UDCCR(int mask){ /* udccr contains the bits we dont want to change */ __u32 udccr = UDCCR & UDCCR_MASK_BITS; UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);}/* * endpoint enable/disable * * we need to verify the descriptors used to enable endpoints. since pxa2xx * endpoint configurations are fixed, and are pretty much always enabled, * there's not a lot to manage here. * * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints, * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except * for a single interface (with only the default altsetting) and for gadget * drivers that don't halt endpoints (not reset by set_interface). that also * means that if you use ISO, you must violate the USB spec rule that all * iso endpoints must be in non-default altsettings. */static int pxa2xx_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct pxa2xx_ep *ep; struct pxa2xx_udc *dev; ep = container_of (_ep, struct pxa2xx_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress || ep->fifo_size < le16_to_cpu (desc->wMaxPacketSize)) { DMSG("%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) { DMSG("%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) != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); return -ERANGE; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DMSG("%s, bogus device state\n", __FUNCTION__); return -ESHUTDOWN; } ep->desc = desc; ep->dma = -1; ep->stopped = 0; ep->pio_irqs = ep->dma_irqs = 0; ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); /* flush fifo (mostly for OUT buffers) */ pxa2xx_ep_fifo_flush (_ep); /* ... reset halt state too, if we could ... */#ifdef USE_DMA /* for (some) bulk and ISO endpoints, try to get a DMA channel and * bind it to the endpoint. otherwise use PIO. */ switch (ep->bmAttributes) { case USB_ENDPOINT_XFER_ISOC: if (le16_to_cpu(desc->wMaxPacketSize) % 32) break; // fall through case USB_ENDPOINT_XFER_BULK: if (!use_dma || !ep->reg_drcmr) break; /* no bulk-out dma yet (pointless w/o descriptors) */ if ((ep->bmAttributes == USB_ENDPOINT_XFER_BULK) && (ep->bEndpointAddress & USB_DIR_IN) == 0) { DMSG("%s dma-out NYI\n", _ep->name); break; } ep->dma = pxa_request_dma ((char *)_ep->name, (le16_to_cpu(desc->wMaxPacketSize) > 64) ? DMA_PRIO_MEDIUM /* some iso */ : DMA_PRIO_LOW, // FIXME or ep_out_dma .. .. dma_nodesc_handler, ep); if (ep->dma >= 0) { *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; DMSG("%s using dma%d\n", _ep->name, ep->dma); } }#endif DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); return 0;}static int pxa2xx_ep_disable (struct usb_ep *_ep){ struct pxa2xx_ep *ep; ep = container_of (_ep, struct pxa2xx_ep, ep); if (!_ep || !ep->desc) { DMSG("%s, %s not enabled\n", __FUNCTION__, _ep ? ep->ep.name : NULL); return -EINVAL; } nuke (ep, -ESHUTDOWN);#ifdef USE_DMA if (ep->dma >= 0) { *ep->reg_drcmr = 0; pxa_free_dma (ep->dma); ep->dma = -1; }#endif /* flush fifo (mostly for IN buffers) */ pxa2xx_ep_fifo_flush (_ep); ep->desc = 0; ep->stopped = 1; DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); return 0;}/*-------------------------------------------------------------------------*//* for the pxa2xx, these can just wrap kmalloc/kfree. gadget drivers * must still pass correctly initialized endpoints, since other controller * drivers may care about how it's currently set up (dma issues etc). *//* * pxa2xx_ep_alloc_request - allocate a request data structure */static struct usb_request *pxa2xx_ep_alloc_request (struct usb_ep *_ep, int gfp_flags){ struct pxa2xx_request *req; /* FIXME for bulk out-dma endpoints, preallocate a frame's worth of * (aligned) dma descriptors at the end of the request */ req = kmalloc (sizeof *req, gfp_flags); if (!req) return 0; memset (req, 0, sizeof *req); INIT_LIST_HEAD (&req->queue); return &req->req;}/* * pxa2xx_ep_free_request - deallocate a request data structure */static voidpxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req){ struct pxa2xx_request *req; req = container_of (_req, struct pxa2xx_request, req); WARN_ON (!list_empty (&req->queue)); kfree(req);}/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's * no device-affinity and the heap works perfectly well for i/o buffers. */static void *pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, int gfp_flags){ char *retval; retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); if (retval) *dma = virt_to_bus (retval); return retval;}static voidpxa2xx_ep_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 pxa2xx_ep *ep, struct pxa2xx_request *req, int status){ 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) DBG(DBG_VERBOSE, "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; req->req.complete(&ep->ep, &req->req); ep->stopped = stopped;}static inline void ep0_idle (struct pxa2xx_udc *dev){ dev->ep0state = EP0_IDLE; LED_EP0_OFF;}static intwrite_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max){ u8 *buf; unsigned length, count; buf = req->req.buf + req->req.actual; prefetch(buf); /* how big will this packet be? */ length = min(req->req.length - req->req.actual, max); req->req.actual += length; while (likely(count--)) *uddr = *buf++; return length;}/* * write to an IN endpoint fifo, as many packets as possible. * irqs will use this to write the rest later. * caller guarantees at least one packet buffer is ready (or a zlp). */static intwrite_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){ unsigned max; max = le16_to_cpu(ep->desc->wMaxPacketSize); do { unsigned count; int is_last, is_short; count = write_packet(ep->reg_uddr, req, max); /* 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->fifo_size); } DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", ep->ep.name, count, is_last ? "/L" : "", is_short ? "/S" : "", req->req.length - req->req.actual, req); /* let loose that packet. maybe try writing another one, * double buffering might work. TSP, TPC, and TFS * bit values are the same for all normal IN endpoints. */ *ep->reg_udccs = UDCCS_BI_TPC; if (is_short) *ep->reg_udccs = UDCCS_BI_TSP; /* requests complete when all IN data is in the FIFO */ if (is_last) { done (ep, req, 0); if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) pio_irq_disable (ep->bEndpointAddress);#ifdef USE_DMA
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -