diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-11-12 14:09:12 -0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-11-12 14:09:12 -0800 |
| commit | 4a20bb93f0bb12d0a34b7f9bf370207547103df1 (patch) | |
| tree | f4cf54708dcec86e90f88a41424101c27af9449c /usb | |
| parent | add940f2a10dbe8e4961aebad62a331d4cb8fe10 (diff) | |
| download | patches-4a20bb93f0bb12d0a34b7f9bf370207547103df1.tar.gz | |
1 usb patch, and a bunch of staging w35und patches
Diffstat (limited to 'usb')
| -rw-r--r-- | usb/usb-add-imx-udc-gadget-driver.patch | 2130 |
1 files changed, 2130 insertions, 0 deletions
diff --git a/usb/usb-add-imx-udc-gadget-driver.patch b/usb/usb-add-imx-udc-gadget-driver.patch new file mode 100644 index 00000000000000..1662d4c64d54f7 --- /dev/null +++ b/usb/usb-add-imx-udc-gadget-driver.patch @@ -0,0 +1,2130 @@ +From foo@baz Wed Nov 12 13:38:31 PST 2008 +Date: Wed, 12 Nov 2008 13:38:31 -0800 +To: Greg KH <greg@kroah.com> +From: Darius Augulis <augulis.darius@gmail.com> +Subject: USB: add imx udc gadget driver + +Not quite working, still get errors with the MXLADS board as it can't +read the device descriptor, but it's a good start. + + +From: Darius Augulis <augulis.darius@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/arm/plat-mxc/include/mach/usb.h | 23 + drivers/usb/gadget/Kconfig | 21 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 1 + drivers/usb/gadget/imx_udc.c | 1705 +++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/imx_udc.h | 321 ++++++ + 6 files changed, 2071 insertions(+), 1 deletion(-) + +--- /dev/null ++++ b/arch/arm/plat-mxc/include/mach/usb.h +@@ -0,0 +1,23 @@ ++/* ++ * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com> ++ * ++ * 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 __ASM_ARCH_MXC_USB ++#define __ASM_ARCH_MXC_USB ++ ++struct imxusb_platform_data { ++ int (*init)(struct device *); ++ int (*exit)(struct device *); ++}; ++ ++#endif /* __ASM_ARCH_MXC_USB */ +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -110,7 +110,6 @@ + #define gadget_is_at91(g) 0 + #endif + +-/* status unclear */ + #ifdef CONFIG_USB_GADGET_IMX + #define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) + #else +--- /dev/null ++++ b/drivers/usb/gadget/imx_udc.c +@@ -0,0 +1,1705 @@ ++/* ++ * driver/usb/gadget/imx_udc.c ++ * ++ * Copyright (C) 2005 Mike Lee(eemike@gmail.com) ++ * Copyright (C) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> ++ * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com> ++ * ++ * 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 ++ */ ++ ++/* ++ * ToDo: ++ * - iso ++ * - get platform-data from platform ++ */ ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/ioport.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/mm.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/clk.h> ++ ++#include <asm/dma.h> ++#include <asm/system.h> ++#include <asm/mach-types.h> ++#include <asm/unaligned.h> ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++ ++#include <mach/hardware.h> ++#include <mach/usb.h> ++ ++#include "imx_udc.h" ++ ++static const char driver_name[] = "imx_udc"; ++static const char ep0name[] = "ep0"; ++static inline void ep0_idle(const char *label, struct imx_udc_struct *imx_usb); ++ ++/* ++ * Hardware related function ++ */ ++ ++void imx_usb_disable(struct imx_udc_struct *imx_usb) ++{ ++ int temp = __raw_readl(imx_usb->base + USB_CTRL); ++ ++ __raw_writel(temp & ~(USB_FE_ENA | USB_AFE_ENA), ++ imx_usb->base + USB_CTRL); ++ ++ ep0_idle(__func__, imx_usb); ++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN; ++} ++ ++void imx_usb_reset(struct imx_udc_struct *imx_usb) ++{ ++ int temp = __raw_readl(imx_usb->base + USB_ENAB); ++ ++ /* set RST bit */ ++ __raw_writel(temp | USB_RST, imx_usb->base + USB_ENAB); ++ ++ /* wait RST bit to clear */ ++ do {} while (__raw_readl(imx_usb->base + USB_ENAB) & USB_RST); ++ ++ /* wait CFG bit to assert */ ++ do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & USB_CFG)); ++ ++ /* udc module is now full reset */ ++} ++ ++int imx_download_conf(struct imx_udc_struct *imx_usb) ++{ ++ u8 ep_conf[5]; ++ u8 i, j; ++ struct imx_ep_struct *imx_ep; ++ ++ /* wait CFG bit to assert */ ++ do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & USB_CFG)); ++ ++ for (j = 0; j < 5; j++) { ++ i = (u8)(j == 2 ? imx_usb->imx_ep[0].wMaxPacketSize : 0x00); ++ __raw_writel(i, imx_usb->base + USB_DDAT); ++ do {} while (__raw_readl(imx_usb->base + USB_DADR) & USB_BSY); ++ } ++ ++ /* only default the configuration to I.MX platform */ ++ for (i = 1; i < IMX_USB_NB_EP; i++) { ++ imx_ep = &imx_usb->imx_ep[i]; ++ /* EP no | configuration no | Interface no */ ++ ep_conf[0] = ((EP_NO(imx_ep) << 4) | 1 << 2 | 0) & 0xFF; ++ /* Alt setting | type | Direction | max pkt size(partly)*/ ++ ep_conf[1] = (0 | (imx_ep->bmAttributes << 3) | ++ (EP_DIR(imx_ep) << 2) | (0)) & 0xFF; ++ /* max pkt size(partly) */ ++ ep_conf[2] = imx_ep->wMaxPacketSize & 0xFF; ++ /* TRXTYP */ ++ ep_conf[3] = i ? 0xFF : 0; ++ /* 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++) { ++ __raw_writel(ep_conf[j], imx_usb->base + USB_DDAT); ++ do {} while (__raw_readl(imx_usb->base + USB_DADR) ++ & USB_BSY); ++ } ++ } ++ ++ for (i = 1; i < IMX_USB_NB_EP; i++) { ++ for (j = 0; j < 5; j++) { ++ __raw_writel(0, imx_usb->base + USB_DDAT); ++ do {} while (__raw_readl(imx_usb->base + USB_DADR) ++ & USB_BSY); ++ } ++ } ++ ++ return (__raw_readl(imx_usb->base + USB_DADR) & USB_CFG) ? -1 : 0; ++} ++ ++void imx_init_intr(struct imx_udc_struct *imx_usb) ++{ ++ int i; ++ ++ /* block all irqs */ ++ __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); ++ for (i = 0; i < IMX_USB_NB_EP; i++) ++ __raw_writel(0xFFFFFFFF, imx_usb->base + USB_EP_MASK(i)); ++ ++ __raw_writel(USB_MSOF | USB_FRAME_MATCH, imx_usb->base + USB_MASK); ++ ++ __raw_writel(0x1FF & ~USB_DEVREQ & ~USB_EOT & ~USB_EOF, ++ imx_usb->base + USB_EP_MASK(0)); ++ ++ for (i = 1; i < IMX_USB_NB_EP; i++) ++ __raw_writel(0x1FA, imx_usb->base + USB_EP_MASK(i)); ++} ++ ++void imx_init_ep(struct imx_udc_struct *imx_usb) ++{ ++ int i, max, temp; ++ struct imx_ep_struct *imx_ep; ++ for (i = 0; i < IMX_USB_NB_EP; i++) { ++ imx_ep = &imx_usb->imx_ep[i]; ++ switch (imx_ep->wMaxPacketSize) { ++ case 8: ++ max = 0; ++ break; ++ default: ++ case 16: ++ max = 1; ++ break; ++ case 32: ++ max = 2; ++ break; ++ case 64: ++ max = 3; ++ break; ++ } ++ temp = (EP_DIR(imx_ep) << 7) | ++ (max << 5) | ++ (imx_ep->bmAttributes << 3) | ++ (0); ++ __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); ++ __raw_writel(temp | USB_FLUSH, imx_usb->base + USB_EP_STAT(i)); ++ D3("ep%d_stat %08x\n", i, ++ __raw_readl(imx_usb->base + USB_EP_STAT(i))); ++ } ++} ++ ++void imx_init_fifo(struct imx_udc_struct *imx_usb) ++{ ++ int i, temp; ++ struct imx_ep_struct *imx_ep; ++ for (i = 0; i < IMX_USB_NB_EP; i++) { ++ imx_ep = &imx_usb->imx_ep[i]; ++ temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; ++ __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); ++ D3("ep%d_fctrl %08x\n", i, ++ __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); ++ /* alarm set when w/wo max pkt size data */ ++ temp = (i ? imx_ep->wMaxPacketSize : 0); ++ __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); ++ D3("ep%d_falrm %08x\n", i, ++ __raw_readl(imx_usb->base + USB_EP_FALRM(i))); ++ } ++} ++ ++void imx_usb_enable(struct imx_udc_struct *imx_usb) ++{ ++ int temp = __raw_readl(imx_usb->base + USB_CTRL); ++ __raw_writel(temp | USB_FE_ENA | USB_AFE_ENA, imx_usb->base + USB_CTRL); ++} ++ ++int imx_ep_empty(struct imx_ep_struct *imx_ep) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ ++ return __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))) & USB_EMPTY; ++} ++ ++unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ ++ return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) ++ & USB_FIFO_BCOUNT) >> 16; ++} ++ ++void imx_flush(struct imx_ep_struct *imx_ep) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ ++ int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++ __raw_writel(temp | USB_FLUSH, ++ imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++} ++ ++void imx_force_stall(struct imx_ep_struct *imx_ep) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ unsigned i; ++ int temp; ++ ++ imx_flush(imx_ep); ++ temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++ __raw_writel(temp | USB_STALL, ++ imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++ ++ for (i = 0; i < 1000; i += 20) { ++ if (!(__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) ++ & USB_STALL)) ++ break; ++ udelay(20); ++ } ++ if (i == 1000) ++ D3("Non finished stall on %s\n", imx_ep->ep.name); ++} ++ ++void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, ++ enum ep0_state stat) ++{ ++#ifdef DEBUG ++ enum ep0_state old = imx_usb->ep0state; ++#endif ++ if (imx_usb->ep0state == stat) ++ return; ++ ++ imx_usb->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 nuke(struct imx_ep_struct *, 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 *usb_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct imx_ep_struct *imx_ep; ++ struct imx_udc_struct *imx_usb; ++ unsigned long flags; ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ imx_usb = imx_ep->imx_usb; ++ if (!desc || imx_ep->desc || usb_ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || imx_ep->bEndpointAddress != desc->bEndpointAddress ++ || imx_ep->wMaxPacketSize < le16_to_cpu(desc->wMaxPacketSize)) { ++ D1("%s, bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* xfer types must match, except that interrupt ~= bulk */ ++ if (imx_ep->bmAttributes != desc->bmAttributes ++ && imx_ep->bmAttributes != USB_ENDPOINT_XFER_BULK ++ && imx_ep->bmAttributes != USB_ENDPOINT_XFER_ISOC ++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { ++ D1("%s, %s type mismatch\n", __func__, usb_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", __func__, usb_ep->name); ++ return -ERANGE; ++ } ++ ++ if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { ++ D1("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ local_irq_save(flags); ++ ++ imx_ep->desc = desc; ++ imx_ep->stopped = 0; ++ imx_ep->irqs = 0; ++ imx_ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); ++ ++ local_irq_restore(flags); ++ ++ D1("enabled %s\n", usb_ep->name); ++ return 0; ++} ++ ++static int imx_ep_disable(struct usb_ep *usb_ep) ++{ ++ struct imx_ep_struct *imx_ep; ++ unsigned long flags; ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ if (!imx_ep->desc) { ++ D1("%s, %s not enabled\n", __func__, ++ usb_ep ? imx_ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ nuke(imx_ep, -ESHUTDOWN); ++ ++ local_irq_save(flags); ++ ++ imx_ep->desc = 0; ++ imx_ep->stopped = 1; ++ ++ local_irq_restore(flags); ++ ++ D1("%s disabled\n", usb_ep->name); ++ return 0; ++} ++ ++/* ++ * imx_ep_alloc_request - allocate a request data structure ++ */ ++static struct usb_request *imx_ep_alloc_request(struct usb_ep *usb_ep, ++ gfp_t gfp_flags) ++{ ++ struct imx_request *req; ++ ++ req = kzalloc(sizeof *req, gfp_flags); ++ if (!req) ++ return 0; ++ ++ 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 *usb_ep, ++ struct usb_request *usb_req) ++{ ++ struct imx_request *req; ++ ++ req = container_of(usb_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_struct *imx_ep, struct imx_request *req, ++ int status) ++{ ++ unsigned stopped = imx_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", ++ imx_ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ imx_ep->stopped = 1; ++ req->req.complete(&imx_ep->ep, &req->req); ++ imx_ep->stopped = stopped; ++} ++ ++static inline void ep0_idle(const char *label, struct imx_udc_struct *imx_usb) ++{ ++ ep0_chg_stat(label, imx_usb, EP0_IDLE); ++} ++ ++static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req, ++ unsigned max) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ u8 *buf, tmp; ++ unsigned length, count; ++ int temp; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetch(buf); ++ ++ /* how big will this packet be? */ ++ length = min(req->req.length - req->req.actual, max); ++ ++ if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { ++ D4("packet overfill %s fifo, wait.. \n", imx_ep->ep.name); ++ imx_flush(imx_ep); ++ return -1; ++ } ++ ++ req->req.actual += length; ++ ++ count = length; ++ D4("count<%d>, fifo remain<%d>\n", count, ++ imx_ep->fifosize - imx_fifo_bcount(imx_ep)); ++ ++ if (!count && req->req.zero) { /* zero byte */ ++ temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++ __raw_writel(temp | USB_ZLPS, ++ imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); ++ D3("zero packet\n"); ++ return 0; ++ } ++ ++ while (count--) { ++ if (imx_fifo_bcount(imx_ep) == imx_ep->fifosize) ++ D3("fifo reach limit\n"); ++ ++ if (count == 0) { /* last byte */ ++ temp = __raw_readl(imx_usb->base + ++ USB_EP_FCTRL(EP_NO(imx_ep))); ++ __raw_writel(temp | USB_WFR, ++ imx_usb->base + USB_EP_FCTRL(EP_NO(imx_ep))); ++ } ++ ++ tmp = *buf++; ++ __raw_writeb(tmp, imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); ++ D4("byte written<%02x>;fstat<%08x>;fifo count <%d>\n", tmp, ++ __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))), ++ imx_fifo_bcount(imx_ep)); ++ } ++ ++ return length; ++} ++ ++static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ int count; ++ int is_short = 0; ++ ++ D3("write %s req %p, fifo stat<%08x>\n", imx_ep->ep.name, req, ++ __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)))); ++ while (!is_short) { ++ D4("fifostat<%08x> fifoctrl<%08x> fcount<%d>\n", ++ __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))), ++ __raw_readl(imx_usb->base + ++ USB_EP_FCTRL(EP_NO(imx_ep))), ++ imx_fifo_bcount(imx_ep)); ++ ++ count = write_packet(imx_ep, req, imx_ep->wMaxPacketSize); ++ if (count < 0) ++ break;; /* busy */ ++ ++ /* last packet "must be" short (or a zlp) */ ++ is_short = count != imx_ep->wMaxPacketSize; ++ ++ D4("%s %d bytes %d left\n", imx_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_struct *imx_usb, u32 flags, ++ const char *tag) ++{ ++ int temp = __raw_readl(imx_usb->base + USB_EP_STAT(0)); ++ __raw_writel(temp | flags, imx_usb->base + USB_EP_STAT(0)); ++ D2("ep0 change status to %s\n", tag); ++} ++ ++static int write_ep0_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ int count; ++ int is_short = 0; ++ ++ while (!is_short) { ++ D4("fifostat<%08x> fifoctrl<%08x> fcount<%d>\n", ++ __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))), ++ __raw_readl(imx_usb->base + ++ USB_EP_FCTRL(EP_NO(imx_ep))), ++ imx_fifo_bcount(imx_ep)); ++ ++ count = write_packet(imx_ep, req, imx_ep->wMaxPacketSize); ++ if (count < 0) ++ break; ++ ++ /* last packet "must be" short (or a zlp) */ ++ is_short = count != imx_ep->wMaxPacketSize; ++ ++ D4("ep0in %d bytes %d left %p\n", count, ++ req->req.length - req->req.actual, req); ++ ++ if (is_short) { ++ done(imx_ep, req, 0); ++ if (!EP_NO(imx_ep)) ++ ep0_idle(__func__, imx_ep->imx_usb); ++ } ++ } ++ ++ /* return whenever short packet sent, or fifo full*/ ++ return is_short; ++} ++ ++static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) ++{ ++ struct imx_udc_struct *imx_usb = imx_ep->imx_usb; ++ u32 tmp; ++ int i, last; ++ int t = imx_fifo_bcount(imx_ep); ++ ++ for (;;) { ++ u8 *buf, byte; ++ unsigned bufferspace, count, is_short; ++ ++ if (!(__raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_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(imx_ep)) ++ continue; ++ D3("no frame ready, fstat<%08x>\n", ++ __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_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 (!(__raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))) & USB_EMPTY)) { ++ /* check for available data */ ++ count = min(imx_fifo_bcount(imx_ep), ++ imx_ep->wMaxPacketSize); ++ req->req.actual += min(count, bufferspace); ++ } else { /* zlp */ ++ count = 0; ++ } ++ is_short = (count < imx_ep->wMaxPacketSize); ++ D4("read %s , %d bytes%s req %p %d/%d\n", ++ imx_ep->ep.name, count, ++ is_short ? "/S" : "", ++ req, req->req.actual, req->req.length); ++ i = 0; ++ last = 0; ++ while (count > 0) { ++ if ((__raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep))) & ++ USB_FRAME_STAT) == 0x08000000) { ++ last = 1; ++ } ++ D4("frame boundary stat <%08x>\n", ++ __raw_readl(imx_usb->base + ++ USB_EP_FSTAT(EP_NO(imx_ep)))); ++ tmp = __raw_readl(imx_usb->base + ++ USB_EP_FDAT0(EP_NO(imx_ep))); ++ if (imx_fifo_bcount(imx_ep)-t > 1) ++ D1("byte read but fifo count drop %d bytes\n", ++ t); ++ ++ 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", ++ imx_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(imx_ep) > 0) ++ D3("complete read but fifo byte count %d\n", ++ imx_fifo_bcount(imx_ep)); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ } ++ ++ return 0; ++} ++ ++static int imx_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, ++ gfp_t gfp_flags) ++{ ++ struct imx_ep_struct *imx_ep; ++ struct imx_request *req; ++ struct imx_udc_struct *imx_usb; ++ unsigned long flags; ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ req = container_of(usb_req, struct imx_request, req); ++ imx_usb = imx_ep->imx_usb; ++ ++ if (imx_ep->imx_usb->set_config) { ++ if (!EP_NO(imx_ep)) { ++ /* ++ 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); ++ imx_ep->imx_usb->set_config = 0; ++ local_irq_restore(flags); ++ D2("gadget driver reply set configuration\n"); ++ return 0; ++ } ++ } ++ if (!usb_req || !usb_req->complete || !usb_req->buf ++ || !list_empty(&req->queue)) { ++ D1("%s, bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!imx_ep->desc && imx_ep->ep.name != ep0name) { ++ D1("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { ++ D1("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ if ((imx_usb->ep0state == EP0_IN_DATA_PHASE && !EP_NO(imx_ep)) ++ || (EP_NO(imx_ep) && EP_DIR(imx_ep))) ++ dump_req(usb_req); ++ ++ local_irq_save(flags); ++ ++ usb_req->status = -EINPROGRESS; ++ usb_req->actual = 0; ++ ++ /* kickstart this i/o queue? */ ++ if (list_empty(&imx_ep->queue) && !imx_ep->stopped) { ++ if (EP_NO(imx_ep) == 0 /* ep0 */) { ++ unsigned length = usb_req->length; ++ ++ switch (imx_usb->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ if (write_ep0_fifo(imx_ep, req)) ++ req = 0; ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ /* ++ read_ep0 should handle read zero packet ++ instead serve here ++ */ ++ if (length == 0 || read_fifo(imx_ep, req)) { ++ ep0_idle(__func__, imx_usb); ++ done(imx_ep, req, 0); ++ req = 0; ++ } ++ break; ++ ++ default: ++ D1("ep0 i/o, odd state %d\n", ++ imx_usb->ep0state); ++ local_irq_restore(flags); ++ return -EL2HLT; ++ } ++ } else if (EP_DIR(imx_ep) /* IN:1 */ ++ /* can the FIFO satisfy the request immediately? */ ++ && write_fifo(imx_ep, req)) { ++ done(imx_ep, req, 0); ++ req = 0; ++ } else if (!EP_DIR(imx_ep) && (read_fifo(imx_ep, req))) { ++ done(imx_ep, req, 0); ++ req = 0; ++ } ++ } ++ ++ if (req) ++ list_add_tail(&req->queue, &imx_ep->queue); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* ++ * nuke - dequeue ALL requests ++ */ ++static void nuke(struct imx_ep_struct *imx_ep, int status) ++{ ++ struct imx_request *req; ++ ++ while (!list_empty(&imx_ep->queue)) { ++ req = list_entry(imx_ep->queue.next, ++ struct imx_request, ++ queue); ++ done(imx_ep, req, status); ++ } ++} ++ ++/* ++ * dequeue JUST ONE request ++ */ ++static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ ++ struct imx_ep_struct *imx_ep; ++ struct imx_request *req; ++ unsigned long flags; ++ ++ if (!usb_ep) ++ return -EINVAL; ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ if (imx_ep->ep.name == ep0name) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &imx_ep->queue, queue) { ++ if (&req->req == usb_req) ++ break; ++ } ++ if (&req->req != usb_req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++ done(imx_ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++ ++static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) ++{ ++ struct imx_ep_struct *imx_ep; ++ unsigned long flags; ++ ++ if (!usb_ep) { ++ D1("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ if (!imx_ep->desc && imx_ep->ep.name != ep0name) { ++ D1("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ local_irq_save(flags); ++ ++ if ((imx_ep->bEndpointAddress & USB_DIR_IN) != 0 ++ && (!list_empty(&imx_ep->queue))) { ++ local_irq_restore(flags); ++ return -EAGAIN; ++ } ++ ++ /* TODO: flush ep fifo */ ++ ++ /* ep0 needs special care */ ++ if (!EP_NO(imx_ep)) ++ ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_STALL); ++ /* and bulk/intr endpoints like dropping stalls too */ ++ else ++ imx_force_stall(imx_ep); ++ ++ local_irq_restore(flags); ++ ++ D1("%s halt\n", usb_ep->name); ++ return 0; ++} ++ ++static int imx_ep_fifo_status(struct usb_ep *usb_ep) ++{ ++ struct imx_ep_struct *imx_ep; ++ ++ if (!usb_ep) { ++ D1("%s, bad ep\n", __func__); ++ return -ENODEV; ++ } ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) ++ return 0; ++ else ++ return imx_fifo_bcount(imx_ep); ++} ++ ++static void imx_ep_fifo_flush(struct usb_ep *usb_ep) ++{ ++ struct imx_ep_struct *imx_ep; ++ ++ imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); ++ if (!usb_ep || imx_ep->ep.name == ep0name || ++ !list_empty(&imx_ep->queue)) { ++ D1("%s, bad ep\n", __func__); ++ return; ++ } ++ ++ /* toggle and halt bits stay unchanged */ ++ imx_flush(imx_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 */ ++}; ++ ++/* ++ * udc_disable - disable USB device controller ++ */ ++static void udc_disable(struct imx_udc_struct *imx_usb) ++{ ++ imx_usb_disable(imx_usb); ++ ep0_idle(__func__, imx_usb); ++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN; ++} ++ ++/* ++ * udc_reinit - initialize software state ++ */ ++static void udc_reinit(struct imx_udc_struct *imx_usb) ++{ ++ u32 i; ++ ++ /* device/ep0 records init */ ++ INIT_LIST_HEAD(&imx_usb->gadget.ep_list); ++ INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); ++ ep0_idle(__func__, imx_usb); ++ ++ /* basic endpoint records init */ ++ for (i = 0; i < IMX_USB_NB_EP; i++) { ++ struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[i]; ++ ++ if (i != 0) ++ list_add_tail(&imx_ep->ep.ep_list, ++ &imx_usb->gadget.ep_list); ++ ++ imx_ep->desc = 0; ++ imx_ep->stopped = 0; ++ INIT_LIST_HEAD(&imx_ep->queue); ++ imx_ep->irqs = 0; ++ } ++} ++ ++static void udc_hardinit(struct imx_udc_struct *imx_usb) ++{ ++ int ret; ++ ++ imx_usb_reset(imx_usb); ++ ++ ret = imx_download_conf(imx_usb); ++ if (ret) ++ D1("download conf return <%d>\n", ret); ++ ++ /* Set desired interrupt mask */ ++ imx_init_intr(imx_usb); ++ ++ /* Set EP status: direction , type, max ps */ ++ imx_init_ep(imx_usb); ++ ++ /* Set fifo register: frame mode, granularity, alarm level */ ++ imx_init_fifo(imx_usb); ++} ++ ++/* ++ * until it's enabled, this UDC should be completely invisible ++ * to any USB host. ++ */ ++static void udc_enable(struct imx_udc_struct *imx_usb) ++{ ++ imx_usb_enable(imx_usb); ++ imx_usb->gadget.speed = USB_SPEED_FULL; ++} ++ ++static void stop_activity(struct imx_udc_struct *imx_usb, ++ struct usb_gadget_driver *driver) ++{ ++ int i; ++ if (imx_usb->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_struct *imx_ep = &imx_usb->imx_ep[i]; ++ ++ imx_flush(imx_ep); ++ imx_ep->stopped = 1; ++ nuke(imx_ep, -ESHUTDOWN); ++ } ++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN; ++ if (driver) { ++ imx_usb->dev_config = 0; ++ driver->disconnect(&imx_usb->gadget); ++ } ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(imx_usb); ++} ++ ++static int handle_ep0(struct imx_udc_struct *imx_usb) ++{ ++ struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; ++ struct imx_request *req = 0; ++ union { ++ struct usb_ctrlrequest r; ++ u8 raw[8]; ++ u32 word[2]; ++ } u; ++ int temp; ++ ++ if (!list_empty(&imx_ep->queue)) ++ req = list_entry(imx_ep->queue.next, struct imx_request, queue); ++ ++ /* previous request unfinished? non-error iff back-to-back ... */ ++ if ((__raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))) ++ & USB_DEVREQ) && imx_usb->ep0state != EP0_IDLE) { ++ nuke(imx_ep, 0); ++ ep0_idle(__func__, imx_usb); ++ } ++ ++ switch (imx_usb->ep0state) { ++ case EP0_STALL: /* stall is clear by hardware auto */ ++ ep0_idle(__func__, imx_usb); ++ case EP0_IDLE: ++ /* start control request? */ ++ { ++ int i; ++ ++ nuke(imx_ep, -EPROTO); ++ /* read SETUP packet */ ++ for (i = 0; i < 2; i++) { ++ if (imx_ep_empty(imx_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] = __raw_readl(imx_usb->base + ++ USB_EP_FDAT(EP_NO(imx_ep))); ++ } ++ if (!imx_ep_empty(imx_ep)) ++ goto bad_setup; ++ ++ 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); ++ ++ if (imx_usb->set_config) { ++ /* ++ * if set config is pending , we must NACK the host ++ * by using CMDOVER ++ */ ++ temp = __raw_readl(imx_usb->base + USB_CTRL); ++ __raw_writel(temp | USB_CMDOVER, ++ imx_usb->base + USB_CTRL); ++ ++ D2("set config req is pending,NACK the host\n"); ++ return 0; ++ } ++ ++ if (u.r.bRequestType & USB_DIR_IN) ++ ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); ++ else ++ ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); ++ ++ if (!imx_usb->driver) ++ i = -1; ++ else ++ i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); ++ if (i < 0) { ++ D1("protocol STALL, " ++ "ep0 err %d\n", i); ++stall: ++ imx_force_stall(imx_ep); ++ ep0_chg_stat(__func__, imx_usb, EP0_STALL); ++ } ++ ++ return 0; ++ } ++ break; ++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ ++ if (req) /* this IN packet might finish the request */ ++ write_ep0_fifo(imx_ep, req); ++ /* 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(imx_ep, req)) { ++ done(imx_ep, req, 0); ++ ep0_idle(__func__, imx_usb); ++ } ++ /* else more OUT packets expected */ ++ } /* else OUT token before read was issued */ ++ break; ++ case EP0_END_XFER: ++ if (req) ++ done(imx_ep, req, 0); ++ /* ack control-IN status (maybe in-zlp was skipped) ++ * also appears after some config change events. ++ */ ++ ep0_idle(__func__, imx_usb); ++ break; ++ } ++ return 0; ++} ++ ++static int handle_ep(struct imx_ep_struct *imx_ep) ++{ ++ struct imx_request *req; ++ int completed; ++ ++ do { ++ if (!list_empty(&imx_ep->queue)) ++ req = list_entry(imx_ep->queue.next, ++ struct imx_request, queue); ++ else { ++ D3("no request on %s\n", imx_ep->ep.name); ++ return 0; ++ } ++ ++ if (EP_DIR(imx_ep)) /* to host */ ++ completed = write_fifo(imx_ep, req); ++ else /* to device */ ++ /* fifos can hold packets, ready for reading... */ ++ completed = read_fifo(imx_ep, req); ++ ++ D3("%s req<%p> %s\n", imx_ep->ep.name, req, ++ completed ? "completed" : "not completed"); ++ if (completed) { ++ done(imx_ep, req, 0); ++ if (!EP_NO(imx_ep)) ++ ep0_idle(__func__, imx_ep->imx_usb); ++ } ++ } 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_struct *imx_usb = dev; ++ int temp; ++ ++ temp = __raw_readl(imx_usb->base + USB_INTR); ++ __raw_writel(temp, imx_usb->base + USB_INTR); ++ ++ if (!imx_usb->driver) { ++ udc_disable(imx_usb); ++ return IRQ_HANDLED; ++ } ++ ++ if (!(temp & USB_SOF)) ++ dump_intr(__func__); ++ ++ if (temp & USB_WAKEUP) { ++ if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN && ++ imx_usb->driver && ++ imx_usb->driver->resume) ++ imx_usb->driver->resume(&imx_usb->gadget); ++ imx_usb->set_config = 0; ++ imx_usb->gadget.speed = USB_SPEED_FULL; ++ } ++ ++ if (temp & USB_SUSP) { ++ if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN ++ && imx_usb->driver ++ && imx_usb->driver->suspend) ++ imx_usb->driver->suspend(&imx_usb->gadget); ++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN; ++ } ++ ++ if (temp & USB_RESET_START) { ++ stop_activity(imx_usb, imx_usb->driver); ++ imx_usb->set_config = 0; ++ imx_usb->gadget.speed = USB_SPEED_FULL; ++ } ++ ++ if (temp & USB_RESET_STOP) ++ imx_usb->gadget.speed = USB_SPEED_FULL; ++ ++ if (temp & USB_CFG_CHG) { ++ struct usb_ctrlrequest u; ++ unsigned long flags; ++ ++ D2("host set config req, USB_STAT<%08x>\n", ++ __raw_readl(imx_usb->base + USB_STAT)); ++ u.bRequest = USB_REQ_SET_CONFIGURATION; ++ u.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD ++ | USB_RECIP_DEVICE; ++ u.wValue = (__raw_readl(imx_usb->base + USB_STAT) ++ & (3 << 5)) >> 5; ++ D2("orig config #%d , req config #%d\n", ++ imx_usb->dev_config, u.wValue); ++ if (u.wValue != 1 && u.wValue != 2) ++ goto end_irq; /* 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 ++ */ ++ if (imx_usb->dev_config != u.wValue) { ++ imx_usb->dev_config = u.wValue; ++ imx_usb->set_config = 1; ++ imx_usb->driver->setup(&imx_usb->gadget, &u); ++ } else { ++ imx_usb->set_config = 0; ++ } ++ ++ local_irq_restore(flags); ++ goto end_irq; ++ } ++ ++ if (temp & 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 (imx_usb->ep0state == EP0_IDLE) { ++ temp = __raw_readl(imx_usb->base + USB_CTRL); ++ __raw_writel(temp | USB_CMDOVER, ++ imx_usb->base + USB_CTRL); ++ } ++ } ++ ++end_irq: ++ /*__raw_writel(temp, imx_usb->base + USB_INTR); */ ++ ++ return IRQ_HANDLED; ++} ++ ++/* imx_udc_ctrl_irq - interrupt handler */ ++static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) ++{ ++ struct imx_udc_struct *imx_usb = dev; ++ int temp; ++ ++ temp = __raw_readl(imx_usb->base + USB_EP_INTR(0)); ++ __raw_writel(temp, imx_usb->base + USB_EP_INTR(0)); ++ ++ if (!imx_usb->driver) { ++ udc_disable(imx_usb); ++ return IRQ_HANDLED; ++ } ++ ++ if (temp & USB_DEVREQ) { ++ if (handle_ep0(imx_usb) == -1) ++ imx_force_stall(&imx_usb->imx_ep[0]); ++ } else if ((temp & USB_EOF) && (imx_usb->ep0state != EP0_IDLE)) { ++ if (handle_ep0(imx_usb) == -1) ++ imx_force_stall(&imx_usb->imx_ep[0]); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* imx_udc_bulk_irq - interrupt handler */ ++static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) ++{ ++ struct imx_udc_struct *imx_usb = dev; ++ struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0]; ++ unsigned long flags; ++ int temp; ++ ++ temp = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); ++ __raw_writel(temp, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); ++ ++ if (!imx_usb->driver) { ++ udc_disable(imx_usb); ++ return IRQ_HANDLED; ++ } ++ ++ local_irq_save(flags); ++ if (handle_ep(imx_ep) == -1) ++ imx_force_stall(imx_ep); ++ local_irq_restore(flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t imx_udc_int_irq(int irq, void *dev) ++{ ++ imx_udc_bulk_irq(irq, dev); ++ ++ return IRQ_HANDLED; ++} ++ ++irq_handler_t intr_handler(int i) ++{ ++ 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; ++ default: ++ return imx_udc_irq; ++ } ++} ++ ++static void nop_release(struct device *dev) ++{ ++ D1("%s %s\n", __func__, 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_struct controller = { ++ .gadget = { ++ .ops = &imx_udc_ops, ++ .ep0 = &controller.imx_ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ .release = nop_release, ++ }, ++ }, ++ ++ /* control endpoint */ ++ .imx_ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &imx_ep_ops, ++ .maxpacket = EP0_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = EP0_MAX_SIZE, ++ .fifosize = 32, ++ .bEndpointAddress = 0, ++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, ++ }, ++ ++ .imx_ep[1] = { ++ .ep = { ++ .name = "ep1in-bulk", ++ .ops = &imx_ep_ops, ++ .maxpacket = BULK_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = BULK_MAX_SIZE, ++ .fifosize = 64, ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ }, ++ .imx_ep[2] = { ++ .ep = { ++ .name = "ep2out-iso", ++ .ops = &imx_ep_ops, ++ .maxpacket = ISO_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = ISO_MAX_SIZE, ++ .fifosize = 64, ++ .bEndpointAddress = 2, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ }, ++ .imx_ep[3] = { ++ .ep = { ++ .name = "ep3out-bulk", ++ .ops = &imx_ep_ops, ++ .maxpacket = BULK_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = BULK_MAX_SIZE, ++ .fifosize = 32, ++ .bEndpointAddress = 3, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ }, ++ .imx_ep[4] = { ++ .ep = { ++ .name = "ep4in-int", ++ .ops = &imx_ep_ops, ++ .maxpacket = INT_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = INT_MAX_SIZE, ++ .fifosize = 32, ++ .bEndpointAddress = USB_DIR_OUT | 4, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ }, ++ .imx_ep[5] = { ++ .ep = { ++ .name = "ep5out-int", ++ .ops = &imx_ep_ops, ++ .maxpacket = INT_MAX_SIZE, ++ }, ++ .imx_usb = &controller, ++ .wMaxPacketSize = INT_MAX_SIZE, ++ .fifosize = 32, ++ .bEndpointAddress = USB_DIR_OUT | 5, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, ++ }, ++}; ++ ++/* ++ * 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_struct *imx_usb = &controller; ++ int retval; ++ ++ if (!driver ++ || driver->speed < USB_SPEED_FULL ++ || !driver->bind ++ || !driver->disconnect ++ || !driver->setup) ++ return -EINVAL; ++ if (!imx_usb) ++ return -ENODEV; ++ if (imx_usb->driver) ++ return -EBUSY; ++ ++ /* first hook up the driver ... */ ++ imx_usb->driver = driver; ++ imx_usb->gadget.dev.driver = &driver->driver; ++ ++ retval = device_add(&imx_usb->gadget.dev); ++ if (retval) ++ goto fail; ++ retval = driver->bind(&imx_usb->gadget); ++ if (retval) { ++ D1("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ device_del(&imx_usb->gadget.dev); ++ ++ goto fail; ++ } ++ ++ /* ++ * ... 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(imx_usb); ++ udc_reinit(imx_usb); ++ udc_enable(imx_usb); ++ imx_usb->dev_config = 0; ++ ++ return 0; ++ ++fail: ++ imx_usb->driver = NULL; ++ imx_usb->gadget.dev.driver = NULL; ++ return retval; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct imx_udc_struct *imx_usb = &controller; ++ ++ if (!imx_usb) ++ return -ENODEV; ++ if (!driver || driver != imx_usb->driver || !driver->unbind) ++ return -EINVAL; ++ ++ local_irq_disable(); ++ udc_disable(imx_usb); ++ stop_activity(imx_usb, driver); ++ local_irq_enable(); ++ ++ driver->unbind(&imx_usb->gadget); ++ imx_usb->gadget.dev.driver = NULL; ++ imx_usb->driver = 0; ++ ++ device_del(&imx_usb->gadget.dev); ++ ++ D1("unregistered gadget driver '%s'\n", driver->driver.name); ++ ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++/* probe - binds to the platform device */ ++static int __init imx_udc_probe(struct platform_device *pdev) ++{ ++ struct imx_udc_struct *imx_usb = &controller; ++ struct resource *res; ++ struct imxusb_platform_data *pdata; ++ struct clk *clk; ++ void __iomem *base; ++ int ret = 0; ++ int i, res_size; ++ ++ dev_dbg(&pdev->dev, "Get resources\n"); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "can't get device resources\n"); ++ return -ENODEV; ++ } ++ ++ dev_dbg(&pdev->dev, "Get pdata\n"); ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "driver needs platform data\n"); ++ return -ENODEV; ++ } ++ ++ dev_dbg(&pdev->dev, "Request memory\n"); ++ res_size = res->end - res->start + 1; ++ if (!request_mem_region(res->start, res_size, res->name)) { ++ dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", ++ res_size, res->start); ++ return -ENOMEM; ++ } ++ ++ dev_dbg(&pdev->dev, "GPIO init\n"); ++ if (pdata->init) { ++ ret = pdata->init(&pdev->dev); ++ if (ret) ++ goto fail0; ++ } ++ ++ dev_dbg(&pdev->dev, "Ioremap\n"); ++ base = ioremap(res->start, res_size); ++ if (!base) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ ret = -EIO; ++ goto fail1; ++ } ++ ++ dev_dbg(&pdev->dev, "Get clock\n"); ++ clk = clk_get(NULL, "usbd_clk"); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ dev_err(&pdev->dev, "can't get USB clock\n"); ++ goto fail2; ++ } ++ clk_enable(clk); ++ ++ dev_dbg(&pdev->dev, "Get USB clock rate\n"); ++ if (clk_get_rate(clk) != 48000000) { ++ if (clk_set_rate(clk, 48000000)) { ++ dev_err(&pdev->dev, ++ "USB clock should be 48MHz, but it is not\n"); ++ ret = -EIO; ++ goto fail3; ++ } ++ } ++ ++ for (i = 0; i < IMX_USB_NB_EP + 1; i++) { ++ imx_usb->usbd_int[i] = platform_get_irq(pdev, i); ++ dev_dbg(&pdev->dev, "Get irq %d\n", imx_usb->usbd_int[i]); ++ if (imx_usb->usbd_int[i] < 0) { ++ dev_err(&pdev->dev, "can't get irq number\n"); ++ ret = -ENODEV; ++ goto fail3; ++ } ++ } ++ ++ for (i = 0; i < IMX_USB_NB_EP + 1; i++) { ++ dev_dbg(&pdev->dev, "Request irq %d\n", imx_usb->usbd_int[i]); ++ ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), ++ IRQF_DISABLED, driver_name, imx_usb); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get irq %i, err %d\n", ++ imx_usb->usbd_int[i], ret); ++ for (--i; i >= 0; i--) ++ free_irq(imx_usb->usbd_int[i], imx_usb); ++ goto fail3; ++ } ++ } ++ ++ imx_usb->res = res; ++ imx_usb->base = base; ++ imx_usb->clk = clk; ++ imx_usb->dev = &pdev->dev; ++ ++ device_initialize(&imx_usb->gadget.dev); ++ ++ imx_usb->gadget.dev.parent = &pdev->dev; ++ imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ ++ platform_set_drvdata(pdev, imx_usb); ++ ++ udc_disable(imx_usb); ++ udc_hardinit(imx_usb); ++ udc_reinit(imx_usb); ++ ++ return 0; ++ ++fail3: ++ clk_put(clk); ++ clk_disable(clk); ++fail2: ++ iounmap(base); ++fail1: ++ if (pdata->exit) ++ pdata->exit(&pdev->dev); ++fail0: ++ release_mem_region(res->start, res_size); ++ return ret; ++} ++ ++static int __exit imx_udc_remove(struct platform_device *pdev) ++{ ++ struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); ++ struct imxusb_platform_data *pdata = pdev->dev.platform_data; ++ int i; ++ ++ if (imx_usb->driver) ++ return -EBUSY; ++ ++ udc_disable(imx_usb); ++ ++ for (i = 0; i < IMX_USB_NB_EP + 1; i++) ++ free_irq(imx_usb->usbd_int[i], imx_usb); ++ ++ clk_put(imx_usb->clk); ++ clk_disable(imx_usb->clk); ++ iounmap(imx_usb->base); ++ ++ release_mem_region(imx_usb->res->start, ++ imx_usb->res->end - imx_usb->res->start + 1); ++ ++ if (pdata->exit) ++ pdata->exit(&pdev->dev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_PM ++#define imx_udc_suspend NULL ++#define imx_udc_resume NULL ++#else ++#define imx_udc_suspend NULL ++#define imx_udc_resume NULL ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct platform_driver udc_driver = { ++ .driver = { ++ .name = driver_name, ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(imx_udc_remove), ++ .suspend = imx_udc_suspend, ++ .resume = imx_udc_resume, ++}; ++ ++static int __init udc_init(void) ++{ ++ return platform_driver_probe(&udc_driver, imx_udc_probe); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++ platform_driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION("IMX USB Device Controller driver"); ++MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:imx_udc"); +--- /dev/null ++++ b/drivers/usb/gadget/imx_udc.h +@@ -0,0 +1,321 @@ ++/* ++ * Copyright (C) 2005 Mike Lee(eemike@gmail.com) ++ * ++ * 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> ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* IN:1 , OUT:0 */ ++#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) ++#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) ++ ++/* ++ * 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)1023) ++#define INT_MAX_SIZE ((unsigned)32) ++#define IMX_USB_NB_EP 6 ++ ++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_END_XFER, ++ EP0_STALL, ++}; ++ ++struct imx_ep_struct { ++ struct usb_ep ep; ++ struct imx_udc_struct *imx_usb; ++ struct list_head queue; ++ const struct usb_endpoint_descriptor *desc; ++ unsigned long irqs; ++ unsigned stopped:1; ++ unsigned wMaxPacketSize; ++ unsigned fifosize; ++ u8 bEndpointAddress; ++ u8 bmAttributes; ++}; ++ ++struct imx_udc_struct { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; ++ struct clk *clk; ++ enum ep0_state ep0state; ++ struct resource *res; ++ void __iomem *base; ++ spinlock_t lock; ++ unsigned got_irq:1; ++ unsigned set_config:1; ++ int dev_config; ++ int usbd_int[7]; ++}; ++ ++#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) ++ ++/* ++ * USB registers ++ */ ++#define USB_FRAME (0x00) /* USB frame */ ++#define USB_SPEC (0x04) /* USB Spec */ ++#define USB_STAT (0x08) /* USB Status */ ++#define USB_CTRL (0x0C) /* USB Control */ ++#define USB_DADR (0x10) /* USB Desc RAM addr */ ++#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */ ++#define USB_INTR (0x18) /* USB interrupt */ ++#define USB_MASK (0x1C) /* USB Mask */ ++#define USB_ENAB (0x24) /* USB Enable */ ++#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */ ++#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */ ++#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */ ++#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */ ++#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */ ++#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */ ++#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */ ++#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */ ++#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */ ++#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */ ++#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */ ++#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */ ++#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */ ++#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */ ++#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */ ++/* USB Control Register Bit Fields.*/ ++#define USB_CMDOVER (1<<6) /* UDC status */ ++#define USB_CMDERROR (1<<5) /* UDC status */ ++#define USB_FE_ENA (1<<3) /* Enable Font End logic */ ++#define USB_UDC_RST (1<<2) /* UDC reset */ ++#define USB_AFE_ENA (1<<1) /* Analog Font end enable */ ++#define USB_RESUME (1<<0) /* UDC resume */ ++/* USB Descriptor Ram Bit Fields */ ++#define USB_CFG (1<<31) /* Configuration */ ++#define USB_BSY (1<<30) /* Busy status */ ++#define USB_DADR_DESC (0x1FF) /* Descriptor Ram Address */ ++#define USB_DDAT_DESC (0xFF) /* Descriptor Endpoint Buffer */ ++/* USB Endpoint Bit fields */ ++/* USB Endpoint status bit fields */ ++#define USB_FIFO_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */ ++#define USB_SIP (1<<8) /* Endpoint setup in progress */ ++#define USB_DIR (1<<7) /* Endpoint transfer direction */ ++#define USB_MAX (3<<5) /* Endpoint Max packet size */ ++#define USB_TYP (3<<3) /* Endpoint type */ ++#define USB_ZLPS (1<<2) /* Send zero length packet */ ++#define USB_FLUSH (1<<1) /* Endpoint FIFO Flush */ ++#define USB_STALL (1<<0) /* Force stall */ ++/* USB Endpoint FIFO status bit fields */ ++#define USB_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */ ++#define USB_ERR (1<<22) /* FIFO error */ ++#define USB_UF (1<<21) /* FIFO underflow */ ++#define USB_OF (1<<20) /* FIFO overflow */ ++#define USB_FR (1<<19) /* FIFO frame ready */ ++#define USB_FULL (1<<18) /* FIFO full */ ++#define USB_ALRM (1<<17) /* FIFO alarm */ ++#define USB_EMPTY (1<<16) /* FIFO empty */ ++/* USB Endpoint FIFO control bit fields */ ++#define USB_WFR (1<<29) /* Write frame end */ ++/* USB Endpoint FIFO interrupt bit fields */ ++#define USB_FIFO_FULL (1<<8) /* fifo full */ ++#define USB_FIFO_EMPTY (1<<7) /* fifo empty */ ++#define USB_FIFO_ERROR (1<<6) /* fifo error */ ++#define USB_FIFO_HIGH (1<<5) /* fifo high */ ++#define USB_FIFO_LOW (1<<4) /* fifo low */ ++#define USB_MDEVREQ (1<<3) /* multi Device request */ ++#define USB_EOT (1<<2) /* fifo end of transfer */ ++#define USB_DEVREQ (1<<1) /* Device request */ ++#define USB_EOF (1<<0) /* fifo end of frame */ ++/* USB Interrupt Bit fields */ ++#define USB_WAKEUP (1<<31) /* Wake up Interrupt */ ++#define USB_MSOF (1<<7) /* Missed Start of Frame */ ++#define USB_SOF (1<<6) /* Start of Frame */ ++#define USB_RESET_STOP (1<<5) /* Reset Signaling stop */ ++#define USB_RESET_START (1<<4) /* Reset Signaling start */ ++#define USB_RES (1<<3) /* Suspend to resume */ ++#define USB_SUSP (1<<2) /* Active to suspend */ ++#define USB_FRAME_MATCH (1<<1) /* Frame matched */ ++#define USB_CFG_CHG (1<<0) /* Configuration change occurred */ ++/* USB Enable Register Bit Fields.*/ ++#define USB_RST (1<<31) /* Reset USB modules */ ++#define USB_ENA (1<<30) /* Enable USB modules*/ ++#define USB_SUSPEND (1<<29) /* Suspend USB modules */ ++#define USB_ENDIAN (1<<28) /* Endian of USB modules */ ++#define USB_POWER (1<<0) /* Power mode of USB modules */ ++ ++/*------------------------ D E B U G -----------------------------------------*/ ++ ++#ifdef DEBUG ++#define LV4 ++#define LV3 ++#define LV2 ++#define LV1 ++#define LV0 ++ ++#ifdef LV0 ++#define D(label, fmt, args...) \ ++ printk(KERN_INFO "udc (%20s) " fmt, label, ## args) ++#else /* LV0 */ ++#define D(fmt, args...) do {} while (0) ++#endif /* LV0 */ ++ ++#ifdef LV1 ++#define D1(fmt, args...) \ ++ printk(KERN_INFO "udc lv1(%20s) " fmt, __func__, ## args) ++#else /* LV1 */ ++#define D1(fmt, args...) do {} while (0) ++#endif /* LV1 */ ++ ++#ifdef LV2 ++#define D2(fmt, args...) \ ++ printk(KERN_INFO "udc lv2(%20s) " fmt, __func__, ## args) ++#else /* LV2 */ ++#define D2(fmt, args...) do {} while (0) ++#endif /* LV2 */ ++ ++#ifdef LV3 ++#define D3(fmt, args...) \ ++ printk(KERN_INFO "udc lv3(%20s) " fmt, __func__, ## args) ++#else /* LV3 */ ++#define D3(fmt, args...) do {} while (0) ++#endif /* LV3 */ ++ ++#ifdef LV4 ++#define D4(fmt, args...) \ ++ printk(KERN_INFO "udc lv4(%20s) " fmt, __func__, ## 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_END_XFER", "EP0_STALL" ++}; ++ ++static void __attribute__ ((__unused__)) ++dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep) ++{ ++ int nb = EP_NO(imx_ep); ++ D(label, "ep0[%s] ep%d_stat<%08x>=[%s%s%s%s%s]\n", ++ state_name[imx_ep->imx_usb->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_struct *imx_ep) ++{ ++ int nb = EP_NO(imx_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_struct *imx_ep) ++{ ++ int nb = EP_NO(imx_ep); ++ D(label, "%s %08X =framebit[%04x],[%s%s%s%s%s%s%s]\n", ++ state_name[imx_ep->imx_usb->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_req(struct usb_request *req) { ++#ifdef LV4 ++ int i = 0; ++ ++ if (!req || !req->buf) { ++ D(__func__, "req or req buf is free\n"); ++ return; ++ } ++ ++ 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) ++ ++#endif /* DEBUG */ ++ ++#endif /* __LINUX_USB_GADGET_IMX_H */ +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -305,6 +305,27 @@ config USB_GADGET_MUSB_HDRC + This OTG-capable silicon IP is used in dual designs including + the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. + ++config USB_GADGET_IMX ++ boolean "Freescale IMX USB Peripheral Controller" ++ depends on ARCH_MX1 ++ 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_M66592 + boolean "Renesas M66592 USB Peripheral Controller" + select USB_GADGET_DUALSPEED +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280) += net2280.o + obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o + obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o + obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o ++obj-$(CONFIG_USB_IMX) += imx_udc.o + obj-$(CONFIG_USB_GOKU) += goku_udc.o + obj-$(CONFIG_USB_OMAP) += omap_udc.o + obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o |
