[PATCH] imx: add udc driver
Matt Reimer <[email protected]>
| Newsgroups | gmane.linux.usb.devel |
|---|---|
| Message-ID | <[email protected]> |
From: Matt Reimer <[email protected]> Add an i.MX UDC driver, written by Mike Lee with patches by Torsten Koschorrek and me. Signed-off-by: Matt Reimer <[email protected]> --- arch/arm/mach-imx/generic.c | 76 ++ drivers/usb/gadget/Kconfig | 21 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/ether.c | 4 + drivers/usb/gadget/imx_udc.c | 1980 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/imx_udc.h | 281 +++++ include/asm-arm/arch-imx/hardware.h | 3 + include/asm-arm/arch-imx/udc.h | 14 + 8 files changed, 2380 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/imx_udc.c create mode 100644 drivers/usb/gadget/imx_udc.h create mode 100644 include/asm-arm/arch-imx/udc.h diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c index efef69f..7382c54 100644 --- a/arch/arm/mach-imx/generic.c +++ b/arch/arm/mach-imx/generic.c @@ -36,6 +36,7 @@ #include <asm/mach/map.h> #include <asm/arch/mmc.h> #include <asm/arch/gpio.h> +#include <asm/arch/udc.h> /* rate in Hz of the external clock source on pin EXTAL16M * @@ -302,6 +303,16 @@ unsigned int imx_get_hclk(void) } EXPORT_SYMBOL(imx_get_hclk); +/* + * get usb_clk + */ +unsigned int imx_get_usb_clk(void) +{ + return imx_get_system_clk() / (((CSCR>>26) & 0x7)+1); +} +EXPORT_SYMBOL(imx_get_usb_clk); + + static struct resource imx_mmc_resources[] = { [0] = { .start = 0x00214000, @@ -367,9 +378,74 @@ static struct platform_device imxfb_device = { .resource = imxfb_resources, }; +static struct imx_udc_mach_info imx_udc_info; + +void __init imx_set_udc_info(struct imx_udc_mach_info *info) +{ + memcpy(&imx_udc_info, info, sizeof *info); +} + +static struct resource imx_udc_resources[] = { + [0] = { + .start = 0x00212000, + .end = 0x00212FFF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = USBD_INT0, + .end = USBD_INT0, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = USBD_INT1, + .end = USBD_INT1, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = USBD_INT2, + .end = USBD_INT2, + .flags = IORESOURCE_IRQ, + }, + [4] = { + .start = USBD_INT3, + .end = USBD_INT3, + .flags = IORESOURCE_IRQ, + }, + [5] = { + .start = USBD_INT4, + .end = USBD_INT4, + .flags = IORESOURCE_IRQ, + }, + [6] = { + .start = USBD_INT5, + .end = USBD_INT5, + .flags = IORESOURCE_IRQ, + }, + [7] = { + .start = USBD_INT6, + .end = USBD_INT6, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dma_mask = ~(u32)0; + +static struct platform_device imx_udc_device = { + .name = "imx_udc", + .id = -1, + .num_resources = ARRAY_SIZE(imx_udc_resources), + .resource = imx_udc_resources, + .dev = { + .platform_data = &imx_udc_info, + .dma_mask = &udc_dma_mask, + } +}; + + static struct platform_device *devices[] __initdata = { &imx_mmc_device, &imxfb_device, + &imx_udc_device, }; static struct map_desc imx_io_desc[] __initdata = { diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 767aed5..be46d41 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -300,6 +300,27 @@ config USB_AT91 depends on USB_GADGET_AT91 default USB_GADGET +config USB_GADGET_IMX + boolean "IMX: MX1, MXL" + depends on ARCH_IMX + help + Freescale's IMX series include an integrated full speed + USB 1.1 device controller. The controller in the IMX series + is register-compatible. + + It has six fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "imx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_IMX + tristate + depends on USB_GADGET_IMX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1bc0f03..6e383ad 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_IMX) += imx_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 593e235..51ac3c1 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -285,6 +285,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_IMX +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. */ diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c new file mode 100644 index 0000000..999dceb --- /dev/null +++ b/drivers/usb/gadget/imx_udc.c @@ -0,0 +1,1980 @@ +/* + * driver/usb/gadget/imx_udc.c + * + * Copyright (C) 2005 Mike Lee([email protected]) + * + * This udc driver is now under testing and code is based on pxa2xx_udc.c + * Please use it with your own risk! + * + * 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. + */ + +/* + * support control, bulk, int transfer + * no power management, double buffering, dma + * + */ + +#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/platform_device.h> +#include <linux/dma-mapping.h> + +#include <asm/byteorder.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/mach-types.h> +#include <asm/unaligned.h> +#include <asm/hardware.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/udc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb_gadget.h> + +#define DRIVER_VERSION "0.5" +#define DRIVER_DESC "IMX USB Device Controller driver" + + +static const char driver_name [] = "imx_udc"; + +static const char ep0name [] = "ep0"; + + +#ifdef CONFIG_ARCH_IMX +#undef USE_DMA +#else +#error "Only support on IMX platform" +#endif + +#include "imx_udc.h" + +static struct imx_udc memory; +static inline void ep0_idle (const char* label,struct imx_udc *dev); + +/* + * Hardware related function + */ + +void imx_usb_disable(struct imx_udc *dev) +{ + USB_CTRL &= ~(USB_FE_ENA | USB_AFE_ENA); + + ep0_idle (__FUNCTION__,dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +void imx_usb_gpio(void) +{ + imx_gpio_mode(PB20_PF_USBD_AFE); + imx_gpio_mode(PB21_PF_USBD_OE); + imx_gpio_mode(PB22_PF_USBD_RCV); + imx_gpio_mode(PB23_PF_USBD_SUSPND); + imx_gpio_mode(PB24_PF_USBD_VP); + imx_gpio_mode(PB25_PF_USBD_VM); + imx_gpio_mode(PB26_PF_USBD_VPO); + imx_gpio_mode(PB27_PF_USBD_VMO); +} + +void imx_usb_reset(struct imx_udc *dev) +{ + /* set RST bit */ + USB_ENAB |= USB_RST; + + /* wait RST bit to clear */ + do {} while (USB_ENAB & USB_RST); + + /* wait CFG bit to assert */ + do {} while (!(USB_DADR & USB_CFG)); + + /* udc module is now full reset */ +} + +int imx_download_conf(struct imx_udc *dev) +{ + u8 ep_conf[5]; + u8 i,j,intf_alt; + struct imx_ep *ep; + + /* wait CFG bit to assert */ + do {} while (!(USB_DADR & USB_CFG)); + + /* Download the endpoint buffer for endpoint 0. */ + for (j=0;j<5;j++) { + USB_DDAT = (u32)(j==2?dev->ep[0].wMaxPacketSize:0x00); + do {} while(USB_DADR & USB_BSY); + } + + /* Download the endpoint buffers for endpoints 1-5. + * We specify one configuration, two interfaces: + * interface 0, alternate 0 + * interface 1, alternate 0 (no endpoints) + * interface 1, alternate 1 + */ + for (intf_alt = 0; intf_alt <= 1; intf_alt++) { + for (i = 1; i < IMX_USB_NB_EP; i++){ + ep = &dev->ep[i]; + /* EP no | configuration no | Interface no */ + ep_conf[0] = ((EP_NO(ep) << 4) | 1 << 2 | intf_alt) & 0xFF; + /* Alt setting | type | Direction | max pkt size(partly)*/ + ep_conf[1] = ((intf_alt << 5) | + (ep->bmAttributes << 3) | + (EP_DIR(ep) << 2) | (0)) & 0xFF; + /* max pkt size(partly) */ + ep_conf[2] = ep->wMaxPacketSize & 0xFF; + /* TRXTYP */ + ep_conf[3] = 0xc0; + /* FIFO no */ + ep_conf[4] = i; + D3("ep_conf[%d] : [%02x-%02x-%02x-%02x-%02x]\n",i, + ep_conf[0],ep_conf[1],ep_conf[2],ep_conf[3],ep_conf[4]); + + for (j=0;j<5;j++) { + USB_DDAT = (u8)ep_conf[j]; + do {} while(USB_DADR & USB_BSY); + } + } + } + + /* Wait for the config download to complete. */ + do {} while (USB_DADR & USB_CFG); + + return (USB_DADR & USB_CFG) ? -1 : 0; +} + +void imx_init_intr(void) +{ + int i; + + /* block all irqs */ + USB_MASK = 0xFFFFFFFF; + for (i=0;i<IMX_USB_NB_EP;i++){ + USB_EP_MASK(i) = 0xFFFFFFFF; + } + + USB_MASK = USB_MSOF | USB_SOF | USB_FRAME_MATCH; + USB_INTR = 0x800000ff; + + USB_EP_MASK(0) = 0x1FF & ~(USB_DEVREQ | USB_EOT | USB_EOF); + USB_EP_INTR(0) = 0x1FF; + + for (i=1;i<IMX_USB_NB_EP;i++){ + USB_EP_MASK(i) = 0x1FF & ~(USB_EOT | USB_EOF); + USB_EP_INTR(i) = 0x1FF; + } +} + +void imx_init_ep(struct imx_udc *dev) +{ + int i,max; + struct imx_ep *ep; + for (i=0;i<IMX_USB_NB_EP;i++){ + ep = &dev->ep[i]; + switch (ep->wMaxPacketSize) { + case 8: max=0;break; + default: + case 16: max=1;break; + case 32: max=2;break; + case 64: max=3;break; + } + USB_EP_STAT(i) = (EP_DIR(ep) << 7) | + (max << 5) | + (ep->bmAttributes << 3) | + (0); + USB_EP_STAT(i) = USB_EP_STAT(i) | USB_FLUSH; + D3("ep%d_stat %08x\n",i,USB_EP_STAT(i)); + } +} + +void imx_init_fifo(struct imx_udc *dev) +{ + int i; + struct imx_ep *ep; + for (i=0;i<IMX_USB_NB_EP;i++){ + ep = &dev->ep[i]; + USB_EP_FCTRL(i) = EP_DIR(ep) == EP_DIR_IN ? 0x0B000000 : 0x0F000000; + D3("ep%d_fctrl %08x\n",i,USB_EP_FCTRL(i)); + // alarm set when w/wo max pkt size data + USB_EP_FALRM(i) = (i == 0 ? 0 : ep->wMaxPacketSize - 1); + D3("ep%d_falrm %08x\n",i,USB_EP_FALRM(i)); + } +} + +void imx_usb_enable(struct imx_udc *dev) +{ + USB_CTRL |= USB_FE_ENA | USB_AFE_ENA; +} + +int imx_ep_empty(struct imx_ep *ep) +{ + return (USB_EP_FSTAT(EP_NO(ep)) & USB_EMPTY); +} + +unsigned imx_fifo_bcount(struct imx_ep *ep) +{ + return ((USB_EP_STAT(EP_NO(ep)) & USB_FIFO_BCOUNT) >> 16); +} + +void imx_flush(struct imx_ep *ep) +{ + USB_EP_STAT(EP_NO(ep)) |= USB_FLUSH; +} + +void imx_force_stall(struct imx_ep *ep) +{ + unsigned i; + + imx_flush(ep); + + USB_EP_STAT(EP_NO(ep)) |= USB_STALL; + + for (i = 0; i < 1000; i += 20) { + if (!(USB_EP_STAT(EP_NO(ep)) & USB_STALL)) + break; + udelay(20); + } + if (i == 1000) + D3("Non finished stall on %s\n",ep->ep.name); +} + +void ep0_chg_stat(const char* label, struct imx_udc *dev, enum ep0_state stat) +{ + if (dev->ep0state == stat) + return; + + dev->ep0state = stat; + + D(label,"FROM %15s TO %15s\n",state_name[old],state_name[stat]); +} + + +/* --------------------------------------------------------------------------- + * endpoint related parts of the api to the usb controller hardware, + * used by gadget driver; and the inner talker-to-hardware core. + * --------------------------------------------------------------------------- + */ + +static void imx_ep_fifo_flush (struct usb_ep *ep); +static void nuke (struct imx_ep *, int status); + +/* + * endpoint enable/disable + * + * we need to verify the descriptors used to enable endpoints. since imx + * endpoint configurations are fixed, and are pretty much always enabled, + * there's not a lot to manage here. + * + */ +static int imx_ep_enable (struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + struct imx_udc *dev; + unsigned long flags; + + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep->wMaxPacketSize < le16_to_cpu + (desc->wMaxPacketSize)) { + D1("%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) { + D1("%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_MAX_SIZE) + || !desc->wMaxPacketSize) { + D1("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + D1("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + local_irq_save(flags); + + ep->desc = desc; + ep->stopped = 0; + ep->irqs = 0; + ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + + /* flush fifo (mostly for OUT buffers) */ + imx_flush(ep); + + local_irq_restore(flags); + + D1("enabled %s\n", _ep->name); + return 0; +} + +static int imx_ep_disable (struct usb_ep *_ep) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + unsigned long flags; + + if (!_ep || !ep->desc) { + D1("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + nuke (ep, -ESHUTDOWN); + + local_irq_save(flags); + + /* flush fifo (mostly for IN buffers) */ + imx_flush(ep); + + ep->desc = 0; + ep->stopped = 1; + + local_irq_restore(flags); + + D1("%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * imx_ep_alloc_request - allocate a request data structure + */ +static struct usb_request * +imx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct imx_request *req; + + req = kmalloc (sizeof *req, gfp_flags); + if (!req) + return 0; + + memset (req, 0, sizeof *req); + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + + +/* + * imx_ep_free_request - deallocate a request data structure + */ +static void +imx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) +{ + struct imx_request *req; + + req = container_of (_req, struct imx_request, req); + if (!req) { + D1("req doesn't exist\n"); + return; + } + WARN_ON (!list_empty (&req->queue)); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct imx_ep *ep, struct imx_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) + D3("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 (const char* label, struct imx_udc *dev) +{ + ep0_chg_stat(label, dev, EP0_IDLE); +} + +static int +write_packet(struct imx_ep *ep, struct imx_request *req) +{ + u8 *buf,tmp; + 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, ep->wMaxPacketSize); + + if (imx_fifo_bcount(ep)+length > ep->fifosize) { + D4("packet overfill %s fifo, wait.. \n",ep->ep.name); + return -1; + } + + req->req.actual += length; + + count = length; + D4("count<%d>, fifo remain<%d>\n",count, + ep->fifosize - imx_fifo_bcount(ep)); + + if (!count && req->req.zero) {/* zero byte */ + USB_EP_STAT(EP_NO(ep)) |= USB_ZLPS; + D3("zero packet\n"); + return 0; + } + +//FIXME: find better solution +#if 0 + u32 *tmp1; + while (count >> 2) { + if (count == 4) { + USB_EP_FCTRL(EP_NO(ep)) = USB_EP_FCTRL(EP_NO(ep)) | USB_WFR; + //D4("end of frame marked\n"); + } + tmp1 = (u32*)buf; + USB_EP_FDAT(EP_NO(ep)) = cpu_to_be32(*tmp1); + count -= 4; + buf += 4; + D4("byte wrote<%08x>;fifo count <%d>\n", + *tmp1,imx_fifo_bcount(ep)); + } +#endif + while (count--){ + if (imx_fifo_bcount(ep)==ep->fifosize) + D3("fifo reach limit\n"); + + if (count == 0/* last byte */) { + USB_EP_FCTRL(EP_NO(ep)) |= USB_WFR; + //D4("end of frame marked\n"); + } + tmp = *buf++; + USB_EP_FDAT0(EP_NO(ep)) = tmp; //byte write action + D4("byte written<%02x>;fstat<%08x>;fifo count <%d>\n", + tmp,USB_EP_FSTAT(EP_NO(ep)),imx_fifo_bcount(ep)); + } + + return length; +} + +static int write_fifo (struct imx_ep *ep, struct imx_request *req) +{ + int count; + int is_short; + + is_short = 0; + D3("write %s req %08x, fifo stat<%08x>\n", + ep->ep.name,req,USB_EP_FSTAT(EP_NO(ep))); + while(!is_short) + { + D4("fifostat<%08x> fifoctrl<%08x> fcount<%d>\n", + USB_EP_FSTAT(EP_NO(ep)), + USB_EP_FCTRL(EP_NO(ep)), + imx_fifo_bcount(ep)); + + count = write_packet(ep, req); + if (count < 0) return is_short; /* busy */ + //if (count < 0) continue; /* busy */ + ep->dev->stats.write.bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != ep->wMaxPacketSize); + + D4("%s %d bytes %d left\n",ep->ep.name,count, + req->req.length - req->req.actual); + } + + /* return whenever short packet sent, or fifo full*/ + return is_short; +} + +static inline void ep0start(struct imx_udc *dev, u32 flags, const char *tag) +{ + USB_EP_STAT(0) = USB_EP_STAT(0) | flags; + D2("ep0 change status to %s\n",tag); +} + +static int write_ep0_fifo (struct imx_ep *ep, struct imx_request *req) +{ + int count; + int is_short; + + is_short = 0; + while(!is_short) + { + D4("fifostat<%08x> fifoctrl<%08x> fcount<%d>\n", + USB_EP_FSTAT(EP_NO(ep)), + USB_EP_FCTRL(EP_NO(ep)), + imx_fifo_bcount(ep)); + + count = write_packet(ep, req); + if (count < 0) break; + ep->dev->stats.write.bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != ep->wMaxPacketSize); + + D4("ep0in %d bytes %d left %p\n", count, + req->req.length - req->req.actual, req); + + if (is_short) { + done (ep, req, 0); + //dump_ep_stat(__FUNCTION__,ep); + if (EP_NO(ep) == 0) + ep0_idle(__FUNCTION__,ep->dev); + } + } + + /* return whenever short packet sent, or fifo full*/ + return is_short; +} + + +static int +read_fifo (struct imx_ep *ep, struct imx_request *req) +{ + u32 tmp; + int i,last; + + D3("read %s fifo, fifo count<%d> fifo stat<%08x>\n", + ep->ep.name,imx_fifo_bcount(ep),USB_EP_FSTAT(EP_NO(ep))); + for (;;) + { + u8 *buf,byte; + unsigned bufferspace, count, is_short; + + if (!(USB_EP_FSTAT(EP_NO(ep)) & USB_FR)) { + /* + special care! I.MX seem to ignore some EOF, so + ensure there is no data in fifo and then out + */ + if (imx_fifo_bcount(ep)) continue; + D3("no frame ready, fstat<%08x>\n", + USB_EP_FSTAT(EP_NO(ep))); + + return 0; + } + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (!(USB_EP_FSTAT(EP_NO(ep)) & USB_EMPTY)) { + //check for available data + count = min(imx_fifo_bcount(ep),ep->wMaxPacketSize); + req->req.actual += min (count, bufferspace); + } else {/* zlp */ + count = 0; + } + is_short = (count < ep->wMaxPacketSize); + D4("read %s , %d bytes%s req %p %d/%d\n", + ep->ep.name, count, + is_short ? "/S" : "", + req, req->req.actual, req->req.length); + i=0; + last=0; + while (count>0) { +#if 0 + if (!(i%4)) + //tmp = be32_to_cpu(USB_EP_FDAT(EP_NO(ep))); + tmp = (USB_EP_FDAT(EP_NO(ep))); +#else + if (last && i) { + /* + IMX seem not clear the frame bit if it does + not perform a dump read so if it is the + start of frame, it will be misunderstood + */ + //D2("host sent frame smaller than the req ," + // "req remain %d\n",count); + //is_short = 1; + //break; + } + if ((USB_EP_FSTAT(EP_NO(ep)) & USB_FRAME_STAT) == + 0x08000000) { + last=1; + } +#if 0 + int t=imx_fifo_bcount(ep); +#endif + D4("frame boundary stat <%08x>\n", + USB_EP_FSTAT(EP_NO(ep))); + tmp = (USB_EP_FDAT0(EP_NO(ep))); +#if 0 + if (imx_fifo_bcount(ep)-t > 1) + D1("byte read but fifo count drop %d bytes\n", + t); +#endif +#endif + + 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) + D1("%s overflow %d\n", + ep->ep.name, count); + req->req.status = -EOVERFLOW; + } else { + byte = (u8) tmp&0xFF; + *buf++ = byte; + bufferspace--; + tmp = tmp >> 8; + } + i++; + count--; + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + if (imx_fifo_bcount(ep) > 0) + D3("complete read but fifo byte count %d\n", + imx_fifo_bcount(ep)); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + } + + return 0; +} + +#if 0 +static int +read_ep0_fifo (struct imx_ep *ep, struct imx_request *req) +{ + u8 *buf, byte; + unsigned bufferspace; + + POSITION; + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + + if (USB_EP_FSTAT(EP_NO(ep)) & USB_EMPTY) { + return -1; + } + { + //FIXME: check frame boundary + //if ((USB_EP_FSTAT(EP_NO(ep))&USB_FRAME_STAT)== 0xF) + + byte = USB_EP_FDAT3(EP_NO(ep)); + if (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) + D1("%s overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + req->req.actual++; + bufferspace--; + } + } + + /* completion */ + if (req->req.actual >= req->req.length) + return 1; + + /* finished that packet. the next one may be waiting... */ + return 0; +} +#endif + +/*-------------------------------------------------------------------------*/ + + + +static int +imx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + struct imx_request *req = + container_of(_req, struct imx_request, req); + struct imx_udc *dev; + unsigned long flags; + + if (ep->dev->set_config && EP_NO(ep) == 0){ + /* + Special care on IMX udc. + Ignore enqueue when after set configuration from the + host. This assume all gadget drivers reply set + configuration with the next ep0 req enqueue. + */ + local_irq_save(flags); + ep->dev->set_config = 0; + local_irq_restore(flags); + D2("gadget driver reply set configuration\n"); + return 0; + } + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue) + ) { + D1("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + D1("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + D1("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + +// D3( "%s queue req %p, len %d buf %p\n", +// _ep->name, _req, _req->length, _req->buf); + + if ((ep->dev->ep0state==EP0_IN_DATA_PHASE && EP_NO(ep) == 0) + || (EP_NO(ep) != 0 && EP_DIR(ep) == EP_DIR_IN)) + dump_req(_req); + + local_irq_save(flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) + { + if (EP_NO(ep) == 0 /* ep0 */) { + unsigned length = _req->length; + + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + dev->stats.write.ops++; + if (write_ep0_fifo(ep, req)) { + req = 0; + /* Enable SOF for setting CMD_OVER. */ + USB_MASK &= ~USB_SOF; + } + break; + + case EP0_OUT_DATA_PHASE: + dev->stats.read.ops++; + /* + read_ep0 should handle read zero packet + instead serve here + */ + if (length == 0 || read_fifo(ep, req)) { + ep0_idle(__FUNCTION__,dev); + done(ep, req, 0); + req = 0; + } + break; + + default: + D1("ep0 i/o, odd state %d\n", dev->ep0state); + local_irq_restore (flags); + return -EL2HLT; + } + } + else if (EP_DIR(ep) == EP_DIR_IN + /* can the FIFO satisfy the request immediately? */ + && write_fifo(ep, req)) { +// D3("%s req<%08x> completed\n",ep->ep.name,req); + done (ep, req, 0); + req = 0; + //dump_ep_stat(__FUNCTION__,ep); + } else if (EP_DIR(ep) == EP_DIR_OUT && (read_fifo(ep, req))) { +// D3("%s req<%08x> completed\n",ep->ep.name,req); + done (ep, req, 0); + req = 0; + //dump_ep_stat(__FUNCTION__,ep); + } + } + + if (req != 0) { + list_add_tail(&req->queue, &ep->queue); +// D3("%s not finish req %p %d bytes %d left\n",ep->ep.name,req, +// req->req.actual,req->req.length - req->req.actual); + } +// D3("End of enqueue\n"); + + local_irq_restore(flags); + + return 0; +} + + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct imx_ep *ep, int status) +{ + struct imx_request *req; + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct imx_request, queue); + done(ep, req, status); + } +} + +/* + * dequeue JUST ONE request + */ +static int imx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + struct imx_request *req; + unsigned long flags; + + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; + +} + +/*-------------------------------------------------------------------------*/ + +static int imx_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + unsigned long flags; + + if (unlikely (!_ep + || (!ep->desc && ep->ep.name != ep0name)) + || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + D1("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + local_irq_save(flags); + + if ((ep->bEndpointAddress & USB_DIR_IN) != 0 + && (!list_empty(&ep->queue))) { + local_irq_restore(flags); + return -EAGAIN; + } + + //TODO: flush ep fifo + + /* ep0 needs special care */ + if (EP_NO(ep) == 0) { + /* XXX Force protocol stall? */ + ep0_chg_stat(__FUNCTION__,ep->dev,EP0_STALL); + /* and bulk/intr endpoints like dropping stalls too */ + } else { + imx_force_stall(ep); + } + local_irq_restore(flags); + + D1("%s halt\n", _ep->name); + return 0; +} + +static int imx_ep_fifo_status(struct usb_ep *_ep) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + + if (!_ep) { + D1("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return 0; + else + return imx_fifo_bcount(ep); +} + +static void imx_ep_fifo_flush(struct usb_ep *_ep) +{ + struct imx_ep *ep = container_of(_ep, struct imx_ep, ep); + + if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { + D1("%s, bad ep\n", __FUNCTION__); + return; + } + + /* toggle and halt bits stay unchanged */ + imx_flush(ep); +} + +static struct usb_ep_ops imx_ep_ops = { + .enable = imx_ep_enable, + .disable = imx_ep_disable, + + .alloc_request = imx_ep_alloc_request, + .free_request = imx_ep_free_request, + + .queue = imx_ep_queue, + .dequeue = imx_ep_dequeue, + + .set_halt = imx_ep_set_halt, + .fifo_status = imx_ep_fifo_status, + .fifo_flush = imx_ep_fifo_flush, +}; + +static int imx_udc_get_frame(struct usb_gadget *_gadget) +{ + return 0; +} + +static int imx_udc_wakeup(struct usb_gadget *_gadget) +{ + return 0; +} + +static const struct usb_gadget_ops imx_udc_ops = { + .get_frame = imx_udc_get_frame, + .wakeup = imx_udc_wakeup, + // I.MX udc must always be self-powered +}; + +/*-------------------------------------------------------------------------*/ + +/* + * "function" sysfs attribute + */ +static ssize_t show_function (struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct imx_udc *dev = dev_get_drvdata (_dev); + + if (!dev->driver + || !dev->driver->function + || strlen (dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct imx_udc *dev) +{ + imx_usb_disable(dev); + ep0_idle (__FUNCTION__,dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct imx_udc *dev) +{ + u32 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD (&dev->gadget.ep_list); + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + ep0_chg_stat(__FUNCTION__,dev,EP0_IDLE); + + /* basic endpoint records init */ + for (i = 0; i < IMX_USB_NB_EP; i++) { + struct imx_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD (&ep->queue); + ep->irqs = 0; + } +} + +static void udc_hardinit(struct imx_udc *dev) +{ + int ret; + imx_usb_gpio(); + + imx_usb_reset(dev); + + ret = imx_download_conf(dev); + if (ret) + D1("download conf return <%d>\n",ret); + + /* Set desired interrupt mask */ + imx_init_intr(); + + /* Set EP status: direction , type, max ps */ + imx_init_ep(dev); + + /* Set fifo register: frame mode, granularity, alarm level */ + imx_init_fifo(dev); + + /* Wait for reset signalling to stop. */ + do {} while (USB_INTR & USB_RESET_STOP); +} + +/* + * until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable (struct imx_udc *dev) +{ + imx_usb_enable(dev); + dev->gadget.speed = USB_SPEED_FULL; +} + + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc *dev = the_controller; + int retval; + + if (!driver + || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + device_add (&dev->gadget.dev); + retval = driver->bind(&dev->gadget); + if (retval) { + D1("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del (&dev->gadget.dev); + + dev->driver = 0; + dev->gadget.dev.driver = 0; + return retval; + } + device_create_file(dev->dev, &dev_attr_function); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + * NOTE: this shouldn't power up until later. + */ + D1("registered gadget driver '%s'\n", driver->driver.name); + udc_disable(dev); + udc_reinit(dev); + udc_enable(dev); + dev->dev_config = 0; + dev->dev_interface = 0; + dev->dev_altsetting = 0; + dump_state(__FUNCTION__); + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void +stop_activity(struct imx_udc *dev, struct usb_gadget_driver *driver) +{ + int i; + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < IMX_USB_NB_EP; i++) { + struct imx_ep *ep = &dev->ep[i]; + + imx_flush(ep); + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + dev->gadget.speed = USB_SPEED_UNKNOWN; + if (driver) { + dev->dev_config = 0; + dev->dev_interface = 0; + dev->dev_altsetting = 0; + driver->disconnect(&dev->gadget); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + local_irq_disable(); + udc_disable(dev); + stop_activity(dev, driver); + local_irq_enable(); + + driver->unbind(&dev->gadget); + dev->driver = 0; + + device_del (&dev->gadget.dev); + device_remove_file(dev->dev, &dev_attr_function); + + D1("unregistered gadget driver '%s'\n", driver->driver.name); + dump_state(__FUNCTION__); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +static int handle_ep0 (struct imx_udc *dev) +{ + int i; + struct imx_ep *ep = &dev->ep [0]; + struct imx_request *req=0; + union { + struct usb_ctrlrequest r; + u8 raw [8]; + u32 word [2]; + } u; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct imx_request, queue); + + /* previous request unfinished? non-error iff back-to-back ... */ + if ((USB_EP_INTR(EP_NO(ep)) & (USB_DEVREQ | USB_MDEVREQ)) && + dev->ep0state != EP0_IDLE) { + nuke(ep, 0); + ep0_idle(__FUNCTION__,dev); + } + + switch (dev->ep0state) { + case EP0_STALL: //stall is clear by hardware auto + ep0_chg_stat(__FUNCTION__,dev,EP0_IDLE); + //dev->ep0state = EP0_IDLE; + case EP0_IDLE: + /* start control request? */ + + /* Discard the first setup packet if a second is queued. */ + if (USB_EP_INTR(EP_NO(ep)) & USB_MDEVREQ) { + u.word[0] = USB_EP_FDAT(EP_NO(ep)); + u.word[1] = USB_EP_FDAT(EP_NO(ep)); + D2("discarding SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex, + u.r.wLength); + } + + if (!(USB_EP_INTR(EP_NO(ep)) & USB_DEVREQ)) + break; + + nuke (ep, -EPROTO); + /* read SETUP packet */ + for (i = 0; i < 2; i++) { + if (imx_ep_empty(ep)) { + bad_setup: + D1("SETUP %d!\n", i); + + le16_to_cpus (&u.r.wValue); + le16_to_cpus (&u.r.wIndex); + le16_to_cpus (&u.r.wLength); + D2("SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + goto stall; + } + u.word[i] = USB_EP_FDAT(EP_NO(ep)); + ep->dev->stats.read.bytes += 4; + //u.word[i] = be32_to_cpu(tmp); + } + if (!imx_ep_empty(ep)) + goto bad_setup; + + le16_to_cpus (&u.r.wValue); + le16_to_cpus (&u.r.wIndex); + le16_to_cpus (&u.r.wLength); + ep->dev->stats.read.ops++; + + USB_INTR = USB_DEVREQ | USB_EOF; + + D2("SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + + if (dev->set_config) { + /* if set config is pending , we must NACK the host + by using CMDOVER + */ + USB_CTRL = USB_CTRL | USB_CMDOVER; + D2("set config req is pending,NACK the host\n"); + return 0; + } + + if (u.r.bRequestType & USB_DIR_IN) + ep0_chg_stat(__FUNCTION__, dev, EP0_IN_DATA_PHASE); + else + ep0_chg_stat(__FUNCTION__, dev, EP0_OUT_DATA_PHASE); + + if (!dev->driver) + i = -1; + else + i = dev->driver->setup(&dev->gadget, &u.r); + if (i < 0) { + D1("protocol STALL, " + "ep0 err %d\n", i); + stall: + /* Force a protocol stall. */ + USB_CTRL |= USB_CMDOVER | USB_CMDERROR; + do { } while (USB_CTRL & USB_CMDOVER); + USB_CTRL &= ~USB_CMDERROR; + + ep0_chg_stat(__FUNCTION__,dev,EP0_STALL); + //dump_ep_stat(__FUNCTION__,ep); + } + + break; + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + if (req) { + /* this IN packet might finish the request */ + if (write_ep0_fifo(ep, req)) { + /* Enable SOF for setting CMD_OVER. */ + USB_MASK &= ~USB_SOF; + //done(ep,req,0); + //ep0_idle(__FUNCTION__,dev); + } + } /* else IN token before response was written */ + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + if (req) { + /* this OUT packet might finish the request */ + if (read_fifo(ep, req)) { + done(ep, req, 0); + ep0_idle(__FUNCTION__,dev); + } + /* else more OUT packets expected */ + } /* else OUT token before read was issued */ + break; + } + return 0; +} + +/* XXX What's the point of the return value? Only returns 0. */ +static int handle_ep(struct imx_ep *ep) +{ + struct imx_request *req; + int completed; + + do { + completed = 0; + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct imx_request, queue); + else { + D3("no request on %s\n",ep->ep.name); + return 0; + } + + if (EP_DIR(ep) == EP_DIR_IN) { /* to host */ + completed = write_fifo(ep, req); +#if 0 + //write fifo need to wait for available of the fifo, + //so if not completed, just go out and wait eof/eot + if (!completed) + return 0; +#endif + + } else { /* to device */ + /* fifos can hold packets, ready for reading... */ + completed = read_fifo(ep, req); + } + D3("%s req<%08x> %s\n",ep->ep.name,req, + completed?"completed":"not completed"); + if (completed) { + done (ep, req, 0); + //dump_ep_stat(__FUNCTION__,ep); + if (EP_NO(ep) == 0) + ep0_idle(__FUNCTION__,ep->dev); + } + //} while (1); + } while (completed); + return 0; + +} + +/* + * imx_udc_irq - interrupt handler + * + * avoid delays in ep0 processing. the control handshaking isn't always + * under software control + */ +static irqreturn_t +imx_udc_irq(int irq, void *_dev) +{ + struct imx_udc *dev = _dev; + u32 intr = USB_INTR; + + dev->stats.irqs++; + + if (!dev->driver) { + udc_disable(dev); + USB_INTR = intr; + + return IRQ_HANDLED; + } + + do { +#ifdef DEBUG + if ((intr != USB_SOF) //reduce dumping sof-only intr + && (intr != (USB_SOF | USB_FRAME_MATCH)) + && (intr != (USB_SOF | USB_MSOF))) { + dump_intr(__FUNCTION__); + } +#endif + + if (intr & USB_CFG_CHG){ + struct usb_ctrlrequest u; + unsigned long flags; + int cfg, intf, alt; + + /* Clear the CFG_CHG interrupt right away, + * so we don't NAK the next requests. */ + USB_INTR = USB_CFG_CHG; + + D2("host set config req, USB_STAT<%08x>\n",USB_STAT); + + cfg = (USB_STAT & USB_STAT_CFG) >> 5; + intf = (USB_STAT & USB_STAT_INTF) >> 3; + alt = USB_STAT & USB_STAT_ALTSET; + + D2("orig config #%d/%d/%d , req config #%d/%d/%d\n", + dev->dev_config, dev->dev_interface, + dev->dev_altsetting, cfg, intf, alt); + + if (cfg != 1 && cfg != 2) + break; /* can not happen */ + + local_irq_save(flags); + + /* should make it aware to gadget driver, but + some gadget drivers will perform an async on + set config req, so 2 solutions: + 1) disable all gadget configuration response + in the gadget driver + 2) neglect any set_config response before enqueue + + I choose to use (2) which don't need to change + gadget driver + */ + + dev->set_config = 0; + + /* Fake a SET_CONFIGURATION request if the interface + * or altsetting changed. */ + if (dev->dev_config != cfg) { + + u.bRequest = USB_REQ_SET_CONFIGURATION; + u.bRequestType = (USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_DEVICE); + u.wValue = cfg; + u.wIndex = 0; + u.wLength = 0; + + dev->dev_config = cfg; + dev->set_config = 1; + dev->driver->setup(&dev->gadget,&u); + } + + /* Fake a SET_INTERFACE request if the interface or + * altsetting changed. */ + if ((dev->dev_interface != intf) || + (dev->dev_altsetting != alt)) { + + u.bRequest = USB_REQ_SET_INTERFACE; + u.bRequestType = (USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_INTERFACE); + u.wValue = alt; + u.wIndex = intf; + u.wLength = 0; + + dev->dev_interface = intf; + dev->dev_altsetting = alt; + dev->set_config = 1; + dev->driver->setup(&dev->gadget,&u); + } + + local_irq_restore(flags); + break; + + } + + if (intr & USB_SOF) { + /* copy from motorola bsp. + We must enable SOF intr and signal CMDOVER. + Datasheet don't specifiy this action, but it + is done in motorola bsp, so just copy it + */ + if (dev->ep0state==EP0_IDLE) + { + USB_CTRL |= USB_CMDOVER; + //ep0_idle(__FUNCTION__,dev); + } + USB_MASK |= USB_SOF; + } + + if (intr & USB_WAKEUP) { + if (dev->gadget.speed == USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) + dev->driver->resume(&dev->gadget); + /* ignore all other interrupt */ + USB_INTR = intr; + dev->set_config = 0; + dev->gadget.speed = USB_SPEED_FULL; + } + + if (intr & USB_SUSP) { +//TODO: suspend handle + //stop_activity (dev, dev->driver); + //udc_disable(dev); + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + dev->gadget.speed = USB_SPEED_UNKNOWN; + } + + if (intr & USB_RES){ +//TODO: resume handle + //udc_reinit(dev); + //udc_enable(dev); + + } + + if (intr & USB_RESET_START){ +//TODO: reset handle +#if 1 + stop_activity (dev, dev->driver); + dev->set_config = 0; + /* ignore all other interrupt */ + USB_INTR = intr; + dev->gadget.speed = USB_SPEED_FULL; +#endif + } + + if (intr & USB_RESET_STOP) { +#if 1 + dev->gadget.speed = USB_SPEED_FULL; + //dev->driver->resume(&dev->gadget); +#endif + } + + break; /* XXX This bypasses the following while(1). */ + + } while (1); + USB_INTR= intr; + + return IRQ_HANDLED; +} + +/* + * imx_udc_ctrl_irq - interrupt handler + */ +static irqreturn_t imx_udc_ctrl_irq(int irq, void *_dev) +{ + struct imx_udc *dev = _dev; + u32 intr; + //unsigned long flags; + + dev->stats.irqs++; + + if (!dev->driver) { + udc_disable(dev); + USB_EP_INTR(0)= USB_EP_INTR(0); + + return IRQ_HANDLED; + } + + //local_irq_save(flags); + intr = USB_EP_INTR(0); + if (intr & (USB_DEVREQ | USB_MDEVREQ)){ + if (handle_ep0(dev) == -1) /* XXX handle_ep0() always returns 0. */ + imx_force_stall(&dev->ep[0]); + } else if ((intr & USB_EOF) && (dev->ep0state != EP0_IDLE)) { + if (handle_ep0(dev)==-1) + imx_force_stall(&dev->ep[0]); + } + //local_irq_restore(flags); + USB_EP_INTR(0)= intr; + return IRQ_HANDLED; +} + +/* + * imx_udc_bulk_irq - interrupt handler + * + */ +static irqreturn_t imx_udc_bulk_irq(int irq, void *_dev) +{ + struct imx_udc *dev = _dev; + struct imx_ep *ep = &dev->ep[irq-USBD_INT0]; + unsigned long flags; + u32 ep_intr; + + ep_intr = USB_EP_INTR(EP_NO(ep)); + dev->stats.irqs++; + + if (!dev->driver) { + udc_disable(dev); + USB_EP_INTR(EP_NO(ep)) = ep_intr; + + return IRQ_HANDLED; + } + local_irq_save(flags); + if (handle_ep(ep)== -1) + imx_force_stall(ep); + local_irq_restore(flags); + USB_EP_INTR(EP_NO(ep)) = ep_intr; + + return IRQ_HANDLED; +} + +static irqreturn_t imx_udc_int_irq(int irq, void *_dev) +{ + imx_udc_bulk_irq(irq,_dev); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static void nop_release (struct device *dev) +{ + D1("%s %s\n", __FUNCTION__, dev->bus_id); +} + +/* this uses load-time allocation and initialization (instead of + * doing it at run-time) to save code, eliminate fault paths, and + * be more obviously correct. + */ +static struct imx_udc memory = { + .gadget = { + .ops = &imx_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &imx_ep_ops, + .maxpacket = EP0_MAX_SIZE, + }, + .dev = &memory, + .wMaxPacketSize = EP0_MAX_SIZE, + .fifosize = 32, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &imx_ep_ops, + .maxpacket = BULK_MAX_SIZE, + }, + .dev = &memory, + .wMaxPacketSize = BULK_MAX_SIZE, + .fifosize = 64, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &imx_ep_ops, + .maxpacket = BULK_MAX_SIZE, + }, + .dev = &memory, + .wMaxPacketSize = BULK_MAX_SIZE, + .fifosize = 64, + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, +#ifndef CONFIG_USB_IMX_SMALL + .ep[3] = { + .ep = { + .name = "ep3out-bulk", + .ops = &imx_ep_ops, + .maxpacket = BULK_MAX_SIZE/2, + }, + .dev = &memory, + .wMaxPacketSize = BULK_MAX_SIZE/2, + .fifosize = 32, + .bEndpointAddress = 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[4] = { + .ep = { + .name = "ep4in-int", + .ops = &imx_ep_ops, + .maxpacket = INT_MAX_SIZE, + }, + .dev = &memory, + .wMaxPacketSize = INT_MAX_SIZE, + .fifosize = 32, + .bEndpointAddress = USB_DIR_IN | 4, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, + .ep[5] = { + .ep = { + .name = "ep5out-int", + .ops = &imx_ep_ops, + .maxpacket = INT_MAX_SIZE, + }, + .dev = &memory, + .wMaxPacketSize = INT_MAX_SIZE, + .fifosize = 32, + .bEndpointAddress = 5, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, + +#endif /* !CONFIG_USB_IMX_SMALL */ +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static const char proc_node_name [] = "driver/udc"; + +static int +udc_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + struct imx_udc *dev = _dev; + char *next = buf; + unsigned size = count; + unsigned long flags; + int i, t; + + if (off != 0) + return 0; + + local_irq_save(flags); + + /* basic device status */ + t = scnprintf(next, size, DRIVER_DESC "\n" + "%s version: %s\nGadget driver: %s\nHost %s\n", + driver_name, DRIVER_VERSION, + dev->driver ? dev->driver->driver.name : "(none)", + is_usb_connected() ? "full speed" : "disconnected"); + size -= t; + next += t; + + /* registers for device and ep0 */ + t = scnprintf(next, size, + "USB_CTRL %08x, USB_STAT %08x, USB_ENAB %08x " + "USB_FRAME %08x, USB_INTR %08x, USB_MASK %08x\n", + USB_CTRL,USB_STAT,USB_ENAB,USB_FRAME,USB_INTR,USB_MASK); + size -= t; + next += t; + + if (!is_usb_connected() || !dev->driver) + goto done; + + t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops, + dev->stats.irqs); + size -= t; + next += t; + + /* dump endpoint queues */ + for (i = 0; i < IMX_USB_NB_EP; i++) { + struct imx_ep *ep = &dev->ep [i]; + struct imx_request *req; + + if (i != 0) { + const struct usb_endpoint_descriptor *d; + + d = ep->desc; + if (!d) + continue; + t = scnprintf(next, size, "%s\n", ep->ep.name); + } else /* ep0 should only have one transfer queued */ + t = scnprintf(next, size, "ep0 irqs %lu\n", + ep->irqs); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "\t(nothing queued)\n"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + continue; + } + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } + } + + t = scnprintf(next, size, + "USB_EP1_FCTRL %08x, USB_EP1_STAT %08x, USB_EP1_FSTAT %08x " + "USB_EP1_INTR %08x, USB_EP1_MASK %08x, USB_EP1_FALRM %08x\n", + USB_EP_FCTRL(1),USB_EP_STAT(1),USB_EP_FSTAT(1),USB_EP_INTR(1), + USB_EP_MASK(1),USB_EP_FALRM(1)); + size -= t; + next += t; + + t = scnprintf(next, size, + "USB_EP2_FCTRL %08x, USB_EP2_STAT %08x, USB_EP2_FSTAT %08x " + "USB_EP2_INTR %08x, USB_EP2_MASK %08x, USB_EP2_FALRM %08x\n", + USB_EP_FCTRL(2),USB_EP_STAT(2),USB_EP_FSTAT(2),USB_EP_INTR(2), + USB_EP_MASK(2),USB_EP_FALRM(2)); + size -= t; + next += t; + +// t = scnprintf(next, size,"position flags <%s>; loop flags <%s>\n",p_flags,l_flags); +// size -= t; +// next += t; + +done: + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +#define create_proc_files() \ + create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) +#define remove_proc_files() \ + remove_proc_entry(proc_node_name, NULL) + +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +irqreturn_t(*intr_handler(int i))(int, void *) { + switch (i) { + case 0: + return imx_udc_ctrl_irq; + case 1: + case 2: + return imx_udc_bulk_irq; + case 3: + case 4: + case 5: + return imx_udc_int_irq; + case 6: + return imx_udc_irq; + } + return imx_udc_irq; +} + +/* + * probe - binds to the platform device + */ +static int __init imx_udc_probe(struct platform_device *pdev) +{ + struct imx_udc *dev = &memory; + int retval = 0; + int i; + + /* TODO: probe for IMX cpu */ + + if (!imx_get_usb_clk()) { + WARN("USB clock should be 48MHz, but is not\n"); + return -EIO; + } else + D2("USB clock %uHz detected\n",imx_get_usb_clk()); + + /* + * USB general purpose IRQ: ep0, state changes, dma, etc + * irq setup after old hardware state is cleaned up + */ + for (i = 0; i < IMX_USB_NB_EP + 1; i++) { + retval = request_irq(USBD_INT0 + i, intr_handler(i), + IRQF_DISABLED, driver_name, dev); + if (retval != 0) { + printk(KERN_ERR "%s: can't get irq %i, err %d\n", + driver_name, USBD_INT0 + i, retval); + break; + } + } + if (retval) { + for (--i; i >= 0; i--) + free_irq(USBD_INT0 + i, dev); + return -EBUSY; + } + + dev->dev = &pdev->dev; + dev->mach = pdev->dev.platform_data; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_controller = dev; + platform_set_drvdata(pdev, dev); + + udc_disable(dev); + udc_hardinit(dev); + udc_reinit(dev); + + create_proc_files(); + + return 0; +} + +static int __exit imx_udc_remove(struct platform_device *pdev) +{ + struct imx_udc *dev = platform_get_drvdata(pdev); + + udc_disable(dev); + remove_proc_files(); + usb_gadget_unregister_driver(dev->driver); + + free_irq(USBD_INT6, dev); + free_irq(USBD_INT5, dev); + free_irq(USBD_INT4, dev); + free_irq(USBD_INT3, dev); + free_irq(USBD_INT2, dev); + free_irq(USBD_INT1, dev); + free_irq(USBD_INT0, dev); + platform_set_drvdata(pdev, NULL); + the_controller = NULL; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM +int imx_udc_suspend(struct platform_device *junk, pm_message_t state) +{ + USB_CTRL &= ~(USB_FE_ENA | USB_AFE_ENA); + return 0; +} +int imx_udc_resume(struct platform_device *junk) +{ + USB_CTRL |= USB_FE_ENA | USB_AFE_ENA; + return 0; +} +#else +#define imx_udc_suspend NULL +#define imx_udc_resume NULL +#endif + +/*-------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .probe = imx_udc_probe, + .remove = __exit_p(imx_udc_remove), + .suspend = imx_udc_suspend, + .resume = imx_udc_resume, + .driver = { + .owner = THIS_MODULE, + .name = "imx_udc", + }, +}; + + +static int __init udc_init(void) +{ + INFO("%s version: %s\n", driver_name, DRIVER_VERSION); + return platform_driver_register(&udc_driver); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mike Lee, Torsten Koschorrek"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h new file mode 100644 index 0000000..07641e8 --- /dev/null +++ b/drivers/usb/gadget/imx_udc.h @@ -0,0 +1,281 @@ +/* + * driver/usb/gadget/imx_udc.h + * + * Copyright (C) 2005 Mike Lee([email protected]) + * + * This udc driver is now under testing and code is based on pxa2xx_udc.h + * Please use it with your own risk! + * + * 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. + */ + +#ifndef __LINUX_USB_GADGET_IMX_H +#define __LINUX_USB_GADGET_IMX_H + +#include <linux/types.h> + +/*-------------------------------------------------------------------------*/ + +struct imx_udc; + +#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) + +#define EP_DIR_OUT 0 +#define EP_DIR_IN 1 +#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? EP_DIR_IN : EP_DIR_OUT) + +struct imx_ep { + struct usb_ep ep; + struct imx_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long irqs; + + unsigned wMaxPacketSize; + unsigned fifosize; + u8 bEndpointAddress; + u8 bmAttributes; + + unsigned stopped : 1; +}; + +struct imx_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_STALL, +}; + +/* + * not yeah finish double buffering + * so use full fifo size be the max packet size + */ +#define EP0_MAX_SIZE ((unsigned)8) +#define BULK_MAX_SIZE ((unsigned)64) +#define ISO_MAX_SIZE ((unsigned)64) +#define INT_MAX_SIZE ((unsigned)32) + +#define IMX_USB_NB_EP 6 +struct udc_stats { + struct ep0stats { + unsigned long ops; + unsigned long bytes; + } read, write; + unsigned long irqs; +}; + + +struct imx_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + spinlock_t lock; + enum ep0_state ep0state; + struct udc_stats stats; + unsigned got_irq : 1, + set_config : 1; + int dev_config; + int dev_interface; + int dev_altsetting; + + + struct device *dev; + struct imx_udc_mach_info *mach; + struct imx_ep ep[IMX_USB_NB_EP]; +}; + +/*-------------------------------------------------------------------------*/ + +static struct imx_udc *the_controller; + +static inline int is_usb_connected(void) +{ + if (!the_controller->mach->udc_is_connected) + return 1; + return the_controller->mach->udc_is_connected(); +} + +static inline void make_usb_disappear(void) +{ + USB_CTRL = USB_CTRL | USB_FE_ENA; +} + +static inline void let_usb_appear(void) +{ + USB_CTRL = USB_CTRL & ~USB_FE_ENA; +} + +#define irq_to_ep(irq) (irq >= USBD_INT0) || (irq <= USBD_INT6) ? (irq - USBD_INT0) \ + : (USBD_INT6) /*should not happen*/ +#define ep_to_irq(ep) EP_NO(ep) + USBD_INT0 + +/*-------------------------------------------------------------------------*/ + +//#define DEBUG + +#ifdef DEBUG +//#define LV4 +//#define LV3 +#define LV2 +#define LV1 +#define LV0 + +#ifdef LV0 +#define D(label,fmt,args...) printk("udc (%20s) " fmt,label,## args) +#else /* LV0 */ +#define D(fmt,args...) do{}while(0) +#endif /* LV0 */ + +#ifdef LV1 +#define D1(fmt,args...) printk("udc lv1(%20s) " fmt,__FUNCTION__,## args) +#else /* LV1 */ +#define D1(fmt,args...) do{}while(0) +#endif /* LV1 */ + +#ifdef LV2 +#define D2(fmt,args...) printk("udc lv2(%20s) " fmt,__FUNCTION__,## args) +#else /* LV2 */ +#define D2(fmt,args...) do{}while(0) +#endif /* LV2 */ + +#ifdef LV3 +#define D3(fmt,args...) printk("udc lv3(%20s) " fmt,__FUNCTION__,## args) +#else /* LV3 */ +#define D3(fmt,args...) do{}while(0) +#endif /* LV3 */ + +#ifdef LV4 +#define D4(fmt,args...) printk("udc lv4(%20s) " fmt,__FUNCTION__,## args) +#else /* LV4 */ +#define D4(fmt,args...) do{}while(0) +#endif /* LV4 */ + +static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", + "EP0_STALL" +}; + +static void __attribute__ ((__unused__)) +dump_ep_stat(const char *label,struct imx_ep *ep) +{ + int nb = EP_NO(ep); + D(label,"ep0[%s] ep%d_stat<%08x>=[%s%s%s%s%s]\n", + state_name[the_controller->ep0state], nb, + USB_EP_STAT(nb), + (USB_EP_STAT(nb) & USB_SIP) ? " sip" : "", + (USB_EP_STAT(nb) & USB_DIR) ? " in" : "", + (USB_EP_STAT(nb) & USB_ZLPS) ? " zlp" : "", + (USB_EP_STAT(nb) & USB_FLUSH) ? " fsh" : "", + (USB_EP_STAT(nb) & USB_STALL) ? " stall" : ""); +} +static void __attribute__ ((__unused__)) +dump_ep_intr(const char *label,struct imx_ep *ep) +{ + int nb = EP_NO(ep); + D(label,"ep%d_intr<%08x>=[%s%s%s%s%s%s%s%s%s]\n", + nb,USB_EP_INTR(nb), + (USB_EP_INTR(nb) & USB_FIFO_FULL) ? " full" : "", + (USB_EP_INTR(nb) & USB_FIFO_EMPTY) ? " fempty" : "", + (USB_EP_INTR(nb) & USB_FIFO_ERROR) ? " ferr" : "", + (USB_EP_INTR(nb) & USB_FIFO_HIGH) ? " fhigh" : "", + (USB_EP_INTR(nb) & USB_FIFO_LOW) ? " flow" : "", + (USB_EP_INTR(nb) & USB_MDEVREQ) ? " mreq" : "", + (USB_EP_INTR(nb) & USB_EOF) ? " eof" : "", + (USB_EP_INTR(nb) & USB_DEVREQ) ? " req" : "", + (USB_EP_INTR(nb) & USB_EOT) ? " eot" : "" + ); +} +static void __attribute__ ((__unused__)) +dump_intr(const char *label) +{ + D(label,"usb_intr<%08x>=[%s%s%s%s%s%s%s%s%s]\n", + USB_INTR, + (USB_INTR & USB_WAKEUP) ? " wak" : "", + (USB_INTR & USB_MSOF) ? " msof" : "", + (USB_INTR & USB_SOF) ? " sof" : "", + (USB_INTR & USB_RES) ? " res" : "", + (USB_INTR & USB_SUSP) ? " sus" : "", + (USB_INTR & USB_RESET_STOP) ? " res_stop" : "", + (USB_INTR & USB_RESET_START) ? " res_start" : "", + (USB_INTR & USB_FRAME_MATCH) ? " f_match" : "", + (USB_INTR & USB_CFG_CHG) ? " cfg" : "" + ); +} + +static void __attribute__ ((__unused__)) +dump_ep_fstat(const char *label,struct imx_ep *ep) +{ + int nb = EP_NO(ep); + D(label,"%s %08X =framebit[%04x],[%s%s%s%s%s%s%s]\n", + state_name[the_controller->ep0state], USB_EP_FSTAT(nb), + (USB_EP_FSTAT(nb) & USB_FRAME_STAT) >> 24, + (USB_EP_FSTAT(nb) & USB_ERR) ? " err" : "", + (USB_EP_FSTAT(nb) & USB_UF) ? " uf" : "", + (USB_EP_FSTAT(nb) & USB_OF) ? " of" : "", + (USB_EP_FSTAT(nb) & USB_FR) ? " fr" : "", + (USB_EP_FSTAT(nb) & USB_FULL) ? " full" : "", + (USB_EP_FSTAT(nb) & USB_ALRM) ? " alrm" : "", + (USB_EP_FSTAT(nb) & USB_EMPTY) ? " empty" : ""); +} +static void __attribute__ ((__unused__)) +dump_state(const char *label) +{ +} + +static void __attribute__ ((__unused__)) +dump_req(struct usb_request *req){ +#ifdef LV4 + if (!req || !req->buf) { + D(__FUNCTION__, "req or req buf is free\n"); + return; + } + + int i=0; + printk("dump req <"); + for (;i<req->length;i++){ + printk("%02x-",*((u8*)req->buf+i)); + } + printk(">\n"); +#endif /* LV4 */ +} + + +#else /* DEBUG */ + +#define D(label,fmt,args...) do{}while(0) +#define D1(fmt,args...) do{}while(0) +#define D2(fmt,args...) do{}while(0) +#define D3(fmt,args...) do{}while(0) +#define D4(fmt,args...) do{}while(0) + +#define dump_ep_stat(x,y) do{}while(0) +#define dump_ep_fstat(x,y) do{}while(0) +#define dump_ep_intr(x,y) do{}while(0) +#define dump_intr(x) do{}while(0) +#define dump_ep_fstat(x,y) do{}while(0) +#define dump_req(req) do{}while(0) +#define dump_state(x) do{}while(0) + +#endif /* DEBUG */ + +#define WARN(stuff...) printk(KERN_WARNING "usb: " stuff) +#define INFO(stuff...) printk(KERN_INFO "usb: " stuff) + + +#endif /* __LINUX_USB_GADGET_IMX_H */ + diff --git a/include/asm-arm/arch-imx/hardware.h b/include/asm-arm/arch-imx/hardware.h index 944b577..5cecb58 100644 --- a/include/asm-arm/arch-imx/hardware.h +++ b/include/asm-arm/arch-imx/hardware.h @@ -28,6 +28,8 @@ # define __REG2(x,y) (*(volatile u32 *)((u32)&__REG(x) + (y))) +# define __REG8(x) (*((volatile u8 *)IO_ADDRESS(x))) + #else # define __REG(x) (IO_ADDRESS(x)) #endif @@ -84,6 +86,7 @@ extern unsigned int imx_get_perclk2(void); /* LCD, SD, SPI[12] */ extern unsigned int imx_get_perclk3(void); /* SSI */ extern unsigned int imx_get_hclk(void); /* SDRAM, CSI, Memory Stick,*/ /* I2C, DMA */ +extern unsigned int imx_get_usb_clk(void); /* USB */ #endif #define MAXIRQNUM 62 diff --git a/include/asm-arm/arch-imx/udc.h b/include/asm-arm/arch-imx/udc.h new file mode 100644 index 0000000..479a1ec --- /dev/null +++ b/include/asm-arm/arch-imx/udc.h @@ -0,0 +1,14 @@ +/* + * linux/include/asm-arm/arch-imx/udc.h + * + * This supports machine-specific differences in how the i.MX + * USB Device Controller (UDC) is wired. + * + * It is set in linux/arch/arm/mach-imx/<machine>.c and used in + * the probe routine of linux/drivers/usb/gadget/imx_udc.c + */ +struct imx_udc_mach_info { + int (*udc_is_connected)(void); /* do we see the host? */ +}; + +extern void imx_set_udc_info(struct imx_udc_mach_info *info); -- 1.5.3.2 ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2005. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ [email protected] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel