diff options
Diffstat (limited to 'drivers/usb/gadget')
27 files changed, 8834 insertions, 1020 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f771a7cae9e..767aed5b4be 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -42,6 +42,20 @@ config USB_GADGET For more information, see <http://www.linux-usb.org/gadget> and the kernel DocBook documentation for this API. +config USB_GADGET_DEBUG + boolean "Debugging messages" + depends on USB_GADGET && DEBUG_KERNEL && EXPERIMENTAL + help + Many controller and gadget drivers will print some debugging + messages if you use this option to ask for those messages. + + Avoid enabling these messages, even if you're actively + debugging such a driver. Many drivers will emit so many + messages that the driver timings are affected, which will + either create new failure modes or remove the one you're + trying to track down. Never enable these messages for a + production build. + config USB_GADGET_DEBUG_FILES boolean "Debugging information files" depends on USB_GADGET && PROC_FS @@ -68,6 +82,27 @@ choice Many controller drivers are platform-specific; these often need board-specific hooks. +config USB_GADGET_AMD5536UDC + boolean "AMD5536 UDC" + depends on PCI + select USB_GADGET_DUALSPEED + help + The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. + It is a USB Highspeed DMA capable USB device controller. Beside ep0 + it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + The UDC port supports OTG operation, and may be used as a host port + if it's not being used to implement peripheral or OTG roles. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "amd5536udc" and force all + gadget drivers to also be dynamically linked. + +config USB_AMD5536UDC + tristate + depends on USB_GADGET_AMD5536UDC + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_FSL_USB2 boolean "Freescale Highspeed USB DR Peripheral Controller" depends on MPC834x || PPC_MPC831x @@ -142,6 +177,24 @@ config USB_PXA2XX_SMALL default y if USB_ETH default y if USB_G_SERIAL +config USB_GADGET_M66592 + boolean "Renesas M66592 USB Peripheral Controller" + select USB_GADGET_DUALSPEED + help + M66592 is a discrete USB peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has seven configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "m66592_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_M66592 + tristate + depends on USB_GADGET_M66592 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_GOKU boolean "Toshiba TC86C001 'Goku-S'" depends on PCI @@ -208,6 +261,27 @@ config USB_OTG Select this only if your OMAP board has a Mini-AB connector. +config USB_GADGET_S3C2410 + boolean "S3C2410 USB Device Controller" + depends on ARCH_S3C2410 + help + Samsung's S3C2410 is an ARM-4 processor with an integrated + full speed USB 1.1 device controller. It has 4 configurable + endpoints, as well as endpoint zero (for control transfers). + + This driver has been tested on the S3C2410, S3C2412, and + S3C2440 processors. + +config USB_S3C2410 + tristate + depends on USB_GADGET_S3C2410 + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_S3C2410_DEBUG + boolean "S3C2410 udc debug messages" + depends on USB_GADGET_S3C2410 + config USB_GADGET_AT91 boolean "AT91 USB Device Port" depends on ARCH_AT91 && !ARCH_AT91SAM9RL diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5db19396631..1bc0f03550c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,14 +1,21 @@ # # USB peripheral controller drivers # +ifeq ($(CONFIG_USB_GADGET_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2280) += net2280.o +obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +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 # # USB gadget drivers diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c new file mode 100644 index 00000000000..714156ca8fe --- /dev/null +++ b/drivers/usb/gadget/amd5536udc.c @@ -0,0 +1,3454 @@ +/* + * amd5536.c -- AMD 5536 UDC high/full speed USB device controller + * + * Copyright (C) 2005-2007 AMD (http://www.amd.com) + * Author: Thomas Dahlmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. + * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it + * provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + * + * Make sure that UDC is assigned to port 4 by BIOS settings (port can also + * be used as host port) and UOC bits PAD_EN and APU are set (should be done + * by BIOS init). + * + * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not + * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0") + * can be used with gadget ether. + */ + +/* debug control */ +/* #define UDC_VERBOSE */ + +/* Driver strings */ +#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" +#define UDC_DRIVER_VERSION_STRING "01.00.0206 - $Revision: #3 $" + +/* system */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/dmapool.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/irq.h> + +#include <asm/byteorder.h> +#include <asm/system.h> +#include <asm/unaligned.h> + +/* gadget stack */ +#include <linux/usb/ch9.h> +#include <linux/usb_gadget.h> + +/* udc specific */ +#include "amd5536udc.h" + + +static void udc_tasklet_disconnect(unsigned long); +static void empty_req_queue(struct udc_ep *); +static int udc_probe(struct udc *dev); +static void udc_basic_init(struct udc *dev); +static void udc_setup_endpoints(struct udc *dev); +static void udc_soft_reset(struct udc *dev); +static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep); +static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq); +static int udc_free_dma_chain(struct udc *dev, struct udc_request *req); +static int udc_create_dma_chain(struct udc_ep *ep, struct udc_request *req, + unsigned long buf_len, gfp_t gfp_flags); +static int udc_remote_wakeup(struct udc *dev); +static int udc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void udc_pci_remove(struct pci_dev *pdev); + +/* description */ +static const char mod_desc[] = UDC_MOD_DESCRIPTION; +static const char name[] = "amd5536udc"; + +/* structure to hold endpoint function pointers */ +static const struct usb_ep_ops udc_ep_ops; + +/* received setup data */ +static union udc_setup_data setup_data; + +/* pointer to device object */ +static struct udc *udc; + +/* irq spin lock for soft reset */ +static DEFINE_SPINLOCK(udc_irq_spinlock); +/* stall spin lock */ +static DEFINE_SPINLOCK(udc_stall_spinlock); + +/* +* slave mode: pending bytes in rx fifo after nyet, +* used if EPIN irq came but no req was available +*/ +static unsigned int udc_rxfifo_pending; + +/* count soft resets after suspend to avoid loop */ +static int soft_reset_occured; +static int soft_reset_after_usbreset_occured; + +/* timer */ +static struct timer_list udc_timer; +static int stop_timer; + +/* set_rde -- Is used to control enabling of RX DMA. Problem is + * that UDC has only one bit (RDE) to enable/disable RX DMA for + * all OUT endpoints. So we have to handle race conditions like + * when OUT data reaches the fifo but no request was queued yet. + * This cannot be solved by letting the RX DMA disabled until a + * request gets queued because there may be other OUT packets + * in the FIFO (important for not blocking control traffic). + * The value of set_rde controls the correspondig timer. + * + * set_rde -1 == not used, means it is alloed to be set to 0 or 1 + * set_rde 0 == do not touch RDE, do no start the RDE timer + * set_rde 1 == timer function will look whether FIFO has data + * set_rde 2 == set by timer function to enable RX DMA on next call + */ +static int set_rde = -1; + +static DECLARE_COMPLETION(on_exit); +static struct timer_list udc_pollstall_timer; +static int stop_pollstall_timer; +static DECLARE_COMPLETION(on_pollstall_exit); + +/* tasklet for usb disconnect */ +static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, + (unsigned long) &udc); + + +/* endpoint names used for print */ +static const char ep0_string[] = "ep0in"; +static const char *ep_string[] = { + ep0_string, + "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", + "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk", + "ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk", + "ep15in-bulk", "ep0out", "ep1out-bulk", "ep2out-bulk", "ep3out-bulk", + "ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk", + "ep8out-bulk", "ep9out-bulk", "ep10out-bulk", "ep11out-bulk", + "ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk" +}; + +/* DMA usage flag */ +static int use_dma = 1; +/* packet per buffer dma */ +static int use_dma_ppb = 1; +/* with per descr. update */ +static int use_dma_ppb_du; +/* buffer fill mode */ +static int use_dma_bufferfill_mode; +/* full speed only mode */ +static int use_fullspeed; +/* tx buffer size for high speed */ +static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE; + +/* module parameters */ +module_param(use_dma, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma, "true for DMA"); +module_param(use_dma_ppb, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode"); +module_param(use_dma_ppb_du, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma_ppb_du, + "true for DMA in packet per buffer mode with descriptor update"); +module_param(use_fullspeed, bool, S_IRUGO); +MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only"); + +/*---------------------------------------------------------------------------*/ +/* Prints UDC device registers and endpoint irq registers */ +static void print_regs(struct udc *dev) +{ + DBG(dev, "------- Device registers -------\n"); + DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg)); + DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl)); + DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts)); + DBG(dev, "\n"); + DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts)); + DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk)); + DBG(dev, "\n"); + DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts)); + DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk)); + DBG(dev, "\n"); + DBG(dev, "USE DMA = %d\n", use_dma); + if (use_dma && use_dma_ppb && !use_dma_ppb_du) { + DBG(dev, "DMA mode = PPBNDU (packet per buffer " + "WITHOUT desc. update)\n"); + dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU"); + } else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) { + DBG(dev, "DMA mode = PPBDU (packet per buffer " + "WITH desc. update)\n"); + dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU"); + } + if (use_dma && use_dma_bufferfill_mode) { + DBG(dev, "DMA mode = BF (buffer fill mode)\n"); + dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF"); + } + if (!use_dma) { + dev_info(&dev->pdev->dev, "FIFO mode\n"); + } + DBG(dev, "-------------------------------------------------------\n"); +} + +/* Masks unused interrupts */ +static int udc_mask_unused_interrupts(struct udc *dev) +{ + u32 tmp; + + /* mask all dev interrupts */ + tmp = AMD_BIT(UDC_DEVINT_SVC) | + AMD_BIT(UDC_DEVINT_ENUM) | + AMD_BIT(UDC_DEVINT_US) | + AMD_BIT(UDC_DEVINT_UR) | + AMD_BIT(UDC_DEVINT_ES) | + AMD_BIT(UDC_DEVINT_SI) | + AMD_BIT(UDC_DEVINT_SOF)| + AMD_BIT(UDC_DEVINT_SC); + writel(tmp, &dev->regs->irqmsk); + + /* mask all ep interrupts */ + writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk); + + return 0; +} + +/* Enables endpoint 0 interrupts */ +static int udc_enable_ep0_interrupts(struct udc *dev) +{ + u32 tmp; + + DBG(dev, "udc_enable_ep0_interrupts()\n"); + + /* read irq mask */ + tmp = readl(&dev->regs->ep_irqmsk); + /* enable ep0 irq's */ + tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0) + & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0); + writel(tmp, &dev->regs->ep_irqmsk); + + return 0; +} + +/* Enables device interrupts for SET_INTF and SET_CONFIG */ +static int udc_enable_dev_setup_interrupts(struct udc *dev) +{ + u32 tmp; + + DBG(dev, "enable device interrupts for setup data\n"); + + /* read irq mask */ + tmp = readl(&dev->regs->irqmsk); + + /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */ + tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI) + & AMD_UNMASK_BIT(UDC_DEVINT_SC) + & AMD_UNMASK_BIT(UDC_DEVINT_UR) + & AMD_UNMASK_BIT(UDC_DEVINT_SVC) + & AMD_UNMASK_BIT(UDC_DEVINT_ENUM); + writel(tmp, &dev->regs->irqmsk); + + return 0; +} + +/* Calculates fifo start of endpoint based on preceeding endpoints */ +static int udc_set_txfifo_addr(struct udc_ep *ep) +{ + struct udc *dev; + u32 tmp; + int i; + + if (!ep || !(ep->in)) + return -EINVAL; + + dev = ep->dev; + ep->txfifo = dev->txfifo; + + /* traverse ep's */ + for (i = 0; i < ep->num; i++) { + if (dev->ep[i].regs) { + /* read fifo size */ + tmp = readl(&dev->ep[i].regs->bufin_framenum); + tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE); + ep->txfifo += tmp; + } + } + return 0; +} + +/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */ +static u32 cnak_pending; + +static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num) +{ + if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) { + DBG(ep->dev, "NAK could not be cleared for ep%d\n", num); + cnak_pending |= 1 << (num); + ep->naking = 1; + } else + cnak_pending = cnak_pending & (~(1 << (num))); +} + + +/* Enables endpoint, is called by gadget driver */ +static int +udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) +{ + struct udc_ep *ep; + struct udc *dev; + u32 tmp; + unsigned long iflags; + u8 udc_csr_epix; + + if (!usbep + || usbep->name == ep0_string + || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + ep = container_of(usbep, struct udc_ep, ep); + dev = ep->dev; + + DBG(dev, "udc_ep_enable() ep %d\n", ep->num); + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&dev->lock, iflags); + ep->desc = desc; + + ep->halted = 0; + + /* set traffic type */ + tmp = readl(&dev->ep[ep->num].regs->ctl); + tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET); + writel(tmp, &dev->ep[ep->num].regs->ctl); + + /* set max packet size */ + tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE); + ep->ep.maxpacket = desc->wMaxPacketSize; + writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt); + + /* IN ep */ + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + /* set buffer size (tx fifo entries) */ + tmp = readl(&dev->ep[ep->num].regs->bufin_framenum); + /* double buffering: fifo size = 2 x max packet size */ + tmp = AMD_ADDBITS( + tmp, + desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT + / UDC_DWORD_BYTES, + UDC_EPIN_BUFF_SIZE); + writel(tmp, &dev->ep[ep->num].regs->bufin_framenum); + + /* calc. tx fifo base addr */ + udc_set_txfifo_addr(ep); + + /* flush fifo */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &ep->regs->ctl); + + /* OUT ep */ + } else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + + /* set max packet size UDC CSR */ + tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, + UDC_CSR_NE_MAX_PKT); + writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); + + if (use_dma && !ep->in) { + /* alloc and init BNA dummy request */ + ep->bna_dummy_req = udc_alloc_bna_dummy(ep); + ep->bna_occurred = 0; + } + + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_enabled = 1; + } + + /* set ep values */ + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* max packet */ + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT); + /* ep number */ + tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM); + /* ep direction */ + tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR); + /* ep type */ + tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE); + /* ep config */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG); + /* ep interface */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF); + /* ep alt */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT); + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + + /* + * clear NAK by writing CNAK + * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written + */ + if (!use_dma || ep->in) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + tmp = desc->bEndpointAddress; + DBG(dev, "%s enabled\n", usbep->name); + + spin_unlock_irqrestore(&dev->lock, iflags); + return 0; +} + +/* Resets endpoint */ +static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep) +{ + u32 tmp; + + VDBG(ep->dev, "ep-%d reset\n", ep->num); + ep->desc = NULL; + ep->ep.ops = &udc_ep_ops; + INIT_LIST_HEAD(&ep->queue); + + ep->ep.maxpacket = (u16) ~0; + /* set NAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 1; + + /* disable interrupt */ + tmp = readl(®s->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, ®s->ep_irqmsk); + + if (ep->in) { + /* unset P and IN bit of potential former DMA */ + tmp = readl(&ep->regs->ctl); + tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P); + writel(tmp, &ep->regs->ctl); + + tmp = readl(&ep->regs->sts); + tmp |= AMD_BIT(UDC_EPSTS_IN); + writel(tmp, &ep->regs->sts); + + /* flush the fifo */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &ep->regs->ctl); + + } + /* reset desc pointer */ + writel(0, &ep->regs->desptr); +} + +/* Disables endpoint, is called by gadget driver */ +static int udc_ep_disable(struct usb_ep *usbep) +{ + struct udc_ep *ep = NULL; + unsigned long iflags; + + if (!usbep) + return -EINVAL; + + ep = container_of(usbep, struct udc_ep, ep); + if (usbep->name == ep0_string || !ep->desc) + return -EINVAL; + + DBG(ep->dev, "Disable ep-%d\n", ep->num); + + spin_lock_irqsave(&ep->dev->lock, iflags); + udc_free_request(&ep->ep, &ep->bna_dummy_req->req); + empty_req_queue(ep); + ep_init(ep->dev->regs, ep); + spin_unlock_irqrestore(&ep->dev->lock, iflags); + + return 0; +} + +/* Allocates request packet, called by gadget driver */ +static struct usb_request * +udc_alloc_request(struct usb_ep *usbep, gfp_t gfp) +{ + struct udc_request *req; + struct udc_data_dma *dma_desc; + struct udc_ep *ep; + + if (!usbep) + return NULL; + + ep = container_of(usbep, struct udc_ep, ep); + + VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num); + req = kzalloc(sizeof(struct udc_request), gfp); + if (!req) + return NULL; + + req->req.dma = DMA_DONT_USE; + INIT_LIST_HEAD(&req->queue); + + if (ep->dma) { + /* ep0 in requests are allocated from data pool here */ + dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp, + &req->td_phys); + if (!dma_desc) { + kfree(req); + return NULL; + } + + VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, " + "td_phys = %lx\n", + req, dma_desc, + (unsigned long)req->td_phys); + /* prevent from using desc. - set HOST BUSY */ + dma_desc->status = AMD_ADDBITS(dma_desc->status, + UDC_DMA_STP_STS_BS_HOST_BUSY, + UDC_DMA_STP_STS_BS); + dma_desc->bufptr = __constant_cpu_to_le32(DMA_DONT_USE); + req->td_data = dma_desc; + req->td_data_last = NULL; + req->chain_len = 1; + } + + return &req->req; +} + +/* Frees request packet, called by gadget driver */ +static void +udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq) +{ + struct udc_ep *ep; + struct udc_request *req; + + if (!usbep || !usbreq) + return; + + ep = container_of(usbep, struct udc_ep, ep); + req = container_of(usbreq, struct udc_request, req); + VDBG(ep->dev, "free_req req=%p\n", req); + BUG_ON(!list_empty(&req->queue)); + if (req->td_data) { + VDBG(ep->dev, "req->td_data=%p\n", req->td_data); + + /* free dma chain if created */ + if (req->chain_len > 1) { + udc_free_dma_chain(ep->dev, req); + } + + pci_pool_free(ep->dev->data_requests, req->td_data, + req->td_phys); + } + kfree(req); +} + +/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */ +static void udc_init_bna_dummy(struct udc_request *req) +{ + if (req) { + /* set last bit */ + req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L); + /* set next pointer to itself */ + req->td_data->next = req->td_phys; + /* set HOST BUSY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_DMA_DONE, + UDC_DMA_STP_STS_BS); +#ifdef UDC_VERBOSE + pr_debug("bna desc = %p, sts = %08x\n", + req->td_data, req->td_data->status); +#endif + } +} + +/* Allocate BNA dummy descriptor */ +static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep) +{ + struct udc_request *req = NULL; + struct usb_request *_req = NULL; + + /* alloc the dummy request */ + _req = udc_alloc_request(&ep->ep, GFP_ATOMIC); + if (_req) { + req = container_of(_req, struct udc_request, req); + ep->bna_dummy_req = req; + udc_init_bna_dummy(req); + } + return req; +} + +/* Write data to TX fifo for IN packets */ +static void +udc_txfifo_write(struct udc_ep *ep, struct usb_request *req) +{ + u8 *req_buf; + u32 *buf; + int i, j; + unsigned bytes = 0; + unsigned remaining = 0; + + if (!req || !ep) + return; + + req_buf = req->buf + req->actual; + prefetch(req_buf); + remaining = req->length - req->actual; + + buf = (u32 *) req_buf; + + bytes = ep->ep.maxpacket; + if (bytes > remaining) + bytes = remaining; + + /* dwords first */ + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + writel(*(buf + i), ep->txfifo); + } + + /* remaining bytes must be written by byte access */ + for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { + writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)), + ep->txfifo); + } + + /* dummy write confirm */ + writel(0, &ep->regs->confirm); +} + +/* Read dwords from RX fifo for OUT transfers */ +static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords) +{ + int i; + + VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords); + + for (i = 0; i < dwords; i++) { + *(buf + i) = readl(dev->rxfifo); + } + return 0; +} + +/* Read bytes from RX fifo for OUT transfers */ +static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes) +{ + int i, j; + u32 tmp; + + VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes); + + /* dwords first */ + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo); + } + + /* remaining bytes must be read by byte access */ + if (bytes % UDC_DWORD_BYTES) { + tmp = readl(dev->rxfifo); + for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { + *(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK); + tmp = tmp >> UDC_BITS_PER_BYTE; + } + } + + return 0; +} + +/* Read data from RX fifo for OUT transfers */ +static int +udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req) +{ + u8 *buf; + unsigned buf_space; + unsigned bytes = 0; + unsigned finished = 0; + + /* received number bytes */ + bytes = readl(&ep->regs->sts); + bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE); + + buf_space = req->req.length - req->req.actual; + buf = req->req.buf + req->req.actual; + if (bytes > buf_space) { + if ((buf_space % ep->ep.maxpacket) != 0) { + DBG(ep->dev, + "%s: rx %d bytes, rx-buf space = %d bytesn\n", + ep->ep.name, bytes, buf_space); + req->req.status = -EOVERFLOW; + } + bytes = buf_space; + } + req->req.actual += bytes; + + /* last packet ? */ + if (((bytes % ep->ep.maxpacket) != 0) || (!bytes) + || ((req->req.actual == req->req.length) && !req->req.zero)) + finished = 1; + + /* read rx fifo bytes */ + VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes); + udc_rxfifo_read_bytes(ep->dev, buf, bytes); + + return finished; +} + +/* create/re-init a DMA descriptor or a DMA descriptor chain */ +static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp) +{ + int retval = 0; + u32 tmp; + + VDBG(ep->dev, "prep_dma\n"); + VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n", + ep->num, req->td_data); + + /* set buffer pointer */ + req->td_data->bufptr = req->req.dma; + + /* set last bit */ + req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L); + + /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */ + if (use_dma_ppb) { + + retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp); + if (retval != 0) { + if (retval == -ENOMEM) + DBG(ep->dev, "Out of DMA memory\n"); + return retval; + } + if (ep->in) { + if (req->req.length == ep->ep.maxpacket) { + /* write tx bytes */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + ep->ep.maxpacket, + UDC_DMA_IN_STS_TXBYTES); + + } + } + + } + + if (ep->in) { + VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d " + "maxpacket=%d ep%d\n", + use_dma_ppb, req->req.length, + ep->ep.maxpacket, ep->num); + /* + * if bytes < max packet then tx bytes must + * be written in packet per buffer mode + */ + if (!use_dma_ppb || req->req.length < ep->ep.maxpacket + || ep->num == UDC_EP0OUT_IX + || ep->num == UDC_EP0IN_IX) { + /* write tx bytes */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + req->req.length, + UDC_DMA_IN_STS_TXBYTES); + /* reset frame num */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + 0, + UDC_DMA_IN_STS_FRAMENUM); + } + /* set HOST BUSY */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_BUSY, + UDC_DMA_STP_STS_BS); + } else { + VDBG(ep->dev, "OUT set host ready\n"); + /* set HOST READY */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_READY, + UDC_DMA_STP_STS_BS); + + + /* clear NAK by writing CNAK */ + if (ep->naking) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + + } + + return retval; +} + +/* Completes request packet ... caller MUST hold lock */ +static void +complete_req(struct udc_ep *ep, struct udc_request *req, int sts) +__releases(ep->dev->lock) +__acquires(ep->dev->lock) +{ + struct udc *dev; + unsigned halted; + + VDBG(ep->dev, "complete_req(): ep%d\n", ep->num); + + dev = ep->dev; + /* unmap DMA */ + if (req->dma_mapping) { + if (ep->in) + pci_unmap_single(dev->pdev, + req->req.dma, + req->req.length, + PCI_DMA_TODEVICE); + else + pci_unmap_single(dev->pdev, + req->req.dma, + req->req.length, + PCI_DMA_FROMDEVICE); + req->dma_mapping = 0; + req->req.dma = DMA_DONT_USE; + } + + halted = ep->halted; + ep->halted = 1; + + /* set new status if pending */ + if (req->req.status == -EINPROGRESS) + req->req.status = sts; + + /* remove from ep queue */ + list_del_init(&req->queue); + + VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n", + &req->req, req->req.length, ep->ep.name, sts); + + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->halted = halted; +} + +/* frees pci pool descriptors of a DMA chain */ +static int udc_free_dma_chain(struct udc *dev, struct udc_request *req) +{ + + int ret_val = 0; + struct udc_data_dma *td; + struct udc_data_dma *td_last = NULL; + unsigned int i; + + DBG(dev, "free chain req = %p\n", req); + + /* do not free first desc., will be done by free for request */ + td_last = req->td_data; + td = phys_to_virt(td_last->next); + + for (i = 1; i < req->chain_len; i++) { + + pci_pool_free(dev->data_requests, td, + (dma_addr_t) td_last->next); + td_last = td; + td = phys_to_virt(td_last->next); + } + + return ret_val; +} + +/* Iterates to the end of a DMA chain and returns last descriptor */ +static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req) +{ + struct udc_data_dma *td; + + td = req->td_data; + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { + td = phys_to_virt(td->next); + } + + return td; + +} + +/* Iterates to the end of a DMA chain and counts bytes received */ +static u32 udc_get_ppbdu_rxbytes(struct udc_request *req) +{ + struct udc_data_dma *td; + u32 count; + + td = req->td_data; + /* received number bytes */ + count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES); + + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { + td = phys_to_virt(td->next); + /* received number bytes */ + if (td) { + count += AMD_GETBITS(td->status, + UDC_DMA_OUT_STS_RXBYTES); + } + } + + return count; + +} + +/* Creates or re-inits a DMA chain */ +static int udc_create_dma_chain( + struct udc_ep *ep, + struct udc_request *req, + unsigned long buf_len, gfp_t gfp_flags +) +{ + unsigned long bytes = req->req.length; + unsigned int i; + dma_addr_t dma_addr; + struct udc_data_dma *td = NULL; + struct udc_data_dma *last = NULL; + unsigned long txbytes; + unsigned create_new_chain = 0; + unsigned len; + + VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n", + bytes, buf_len); + dma_addr = DMA_DONT_USE; + + /* unset L bit in first desc for OUT */ + if (!ep->in) { + req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); + } + + /* alloc only new desc's if not already available */ + len = req->req.length / ep->ep.maxpacket; + if (req->req.length % ep->ep.maxpacket) { + len++; + } + + if (len > req->chain_len) { + /* shorter chain already allocated before */ + if (req->chain_len > 1) { + udc_free_dma_chain(ep->dev, req); + } + req->chain_len = len; + create_new_chain = 1; + } + + td = req->td_data; + /* gen. required number of descriptors and buffers */ + for (i = buf_len; i < bytes; i += buf_len) { + /* create or determine next desc. */ + if (create_new_chain) { + + td = pci_pool_alloc(ep->dev->data_requests, + gfp_flags, &dma_addr); + if (!td) + return -ENOMEM; + + td->status = 0; + } else if (i == buf_len) { + /* first td */ + td = (struct udc_data_dma *) phys_to_virt( + req->td_data->next); + td->status = 0; + } else { + td = (struct udc_data_dma *) phys_to_virt(last->next); + td->status = 0; + } + + + if (td) + td->bufptr = req->req.dma + i; /* assign buffer */ + else + break; + + /* short packet ? */ + if ((bytes - i) >= buf_len) { + txbytes = buf_len; + } else { + /* short packet */ + txbytes = bytes - i; + } + + /* link td and assign tx bytes */ + if (i == buf_len) { + if (create_new_chain) { + req->td_data->next = dma_addr; + } else { + /* req->td_data->next = virt_to_phys(td); */ + } + /* write tx bytes */ + if (ep->in) { + /* first desc */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + ep->ep.maxpacket, + UDC_DMA_IN_STS_TXBYTES); + /* second desc */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } + } else { + if (create_new_chain) { + last->next = dma_addr; + } else { + /* last->next = virt_to_phys(td); */ + } + if (ep->in) { + /* write tx bytes */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } + } + last = td; + } + /* set last bit */ + if (td) { + td->status |= AMD_BIT(UDC_DMA_IN_STS_L); + /* last desc. points to itself */ + req->td_data_last = td; + } + + return 0; +} + +/* Enabling RX DMA */ +static void udc_set_rde(struct udc *dev) +{ + u32 tmp; + + VDBG(dev, "udc_set_rde()\n"); + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* set RDE */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RDE); + writel(tmp, &dev->regs->ctl); +} + +/* Queues a request packet, called by gadget driver */ +static int +udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) +{ + int retval = 0; + u8 open_rxfifo = 0; + unsigned long iflags; + struct udc_ep *ep; + struct udc_request *req; + struct udc *dev; + u32 tmp; + + /* check the inputs */ + req = container_of(usbreq, struct udc_request, req); + + if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf + || !list_empty(&req->queue)) + return -EINVAL; + + ep = container_of(usbep, struct udc_ep, ep); + if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + return -EINVAL; + + VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in); + dev = ep->dev; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* map dma (usually done before) */ + if (ep->dma && usbreq->length != 0 + && (usbreq->dma == DMA_DONT_USE || usbreq->dma == 0)) { + VDBG(dev, "DMA map req %p\n", req); + if (ep->in) + usbreq->dma = pci_map_single(dev->pdev, + usbreq->buf, + usbreq->length, + PCI_DMA_TODEVICE); + else + usbreq->dma = pci_map_single(dev->pdev, + usbreq->buf, + usbreq->length, + PCI_DMA_FROMDEVICE); + req->dma_mapping = 1; + } + + VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n", + usbep->name, usbreq, usbreq->length, + req->td_data, usbreq->buf); + + spin_lock_irqsave(&dev->lock, iflags); + usbreq->actual = 0; + usbreq->status = -EINPROGRESS; + req->dma_done = 0; + + /* on empty queue just do first transfer */ + if (list_empty(&ep->queue)) { + /* zlp */ + if (usbreq->length == 0) { + /* IN zlp's are handled by hardware */ + complete_req(ep, req, 0); + VDBG(dev, "%s: zlp\n", ep->ep.name); + /* + * if set_config or set_intf is waiting for ack by zlp + * then set CSR_DONE + */ + if (dev->set_cfg_not_acked) { + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE); + writel(tmp, &dev->regs->ctl); + dev->set_cfg_not_acked = 0; + } + /* setup command is ACK'ed now by zlp */ + if (dev->waiting_zlp_ack_ep0in) { + /* clear NAK by writing CNAK in EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], + UDC_EP0IN_IX); + dev->waiting_zlp_ack_ep0in = 0; + } + goto finished; + } + if (ep->dma) { + retval = prep_dma(ep, req, gfp); + if (retval != 0) + goto finished; + /* write desc pointer to enable DMA */ + if (ep->in) { + /* set HOST READY */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + UDC_DMA_IN_STS_BS_HOST_READY, + UDC_DMA_IN_STS_BS); + } + + /* disabled rx dma while descriptor update */ + if (!ep->in) { + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* clear RDE */ + tmp = readl(&dev->regs->ctl); + tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE); + writel(tmp, &dev->regs->ctl); + open_rxfifo = 1; + + /* + * if BNA occurred then let BNA dummy desc. + * point to current desc. + */ + if (ep->bna_occurred) { + VDBG(dev, "copy to BNA dummy desc.\n"); + memcpy(ep->bna_dummy_req->td_data, + req->td_data, + sizeof(struct udc_data_dma)); + } + } + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + + /* clear NAK by writing CNAK */ + if (ep->naking) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + + if (ep->in) { + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + } + } + + } else if (ep->dma) { + + /* + * prep_dma not used for OUT ep's, this is not possible + * for PPB modes, because of chain creation reasons + */ + if (ep->in) { + retval = prep_dma(ep, req, gfp); + if (retval != 0) + goto finished; + } + } + VDBG(dev, "list_add\n"); + /* add request to ep queue */ + if (req) { + + list_add_tail(&req->queue, &ep->queue); + + /* open rxfifo if out data queued */ + if (open_rxfifo) { + /* enable DMA */ + req->dma_going = 1; + udc_set_rde(dev); + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_queued = 1; + } + /* stop OUT naking */ + if (!ep->in) { + if (!use_dma && udc_rxfifo_pending) { + DBG(dev, "udc_queue(): pending bytes in" + "rxfifo after nyet\n"); + /* + * read pending bytes afer nyet: + * referring to isr + */ + if (udc_rxfifo_read(ep, req)) { + /* finish */ + complete_req(ep, req, 0); + } + udc_rxfifo_pending = 0; + + } + } + } + +finished: + spin_unlock_irqrestore(&dev->lock, iflags); + return retval; +} + +/* Empty request queue of an endpoint; caller holds spinlock */ +static void empty_req_queue(struct udc_ep *ep) +{ + struct udc_request *req; + + ep->halted = 1; + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct udc_request, + queue); + complete_req(ep, req, -ESHUTDOWN); + } +} + +/* Dequeues a request packet, called by gadget driver */ +static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq) +{ + struct udc_ep *ep; + struct udc_request *req; + unsigned halted; + unsigned long iflags; + + ep = container_of(usbep, struct udc_ep, ep); + if (!usbep || !usbreq || (!ep->desc && (ep->num != 0 + && ep->num != UDC_EP0OUT_IX))) + return -EINVAL; + + req = container_of(usbreq, struct udc_request, req); + + spin_lock_irqsave(&ep->dev->lock, iflags); + halted = ep->halted; + ep->halted = 1; + /* request in processing or next one */ + if (ep->queue.next == &req->queue) { + if (ep->dma && req->dma_going) { + if (ep->in) + ep->cancel_transfer = 1; + else { + u32 tmp; + u32 dma_sts; + /* stop potential receive DMA */ + tmp = readl(&udc->regs->ctl); + writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE), + &udc->regs->ctl); + /* + * Cancel transfer later in ISR + * if descriptor was touched. + */ + dma_sts = AMD_GETBITS(req->td_data->status, + UDC_DMA_OUT_STS_BS); + if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY) + ep->cancel_transfer = 1; + else { + udc_init_bna_dummy(ep->req); + writel(ep->bna_dummy_req->td_phys, + &ep->regs->desptr); + } + writel(tmp, &udc->regs->ctl); + } + } + } + complete_req(ep, req, -ECONNRESET); + ep->halted = halted; + + spin_unlock_irqrestore(&ep->dev->lock, iflags); + return 0; +} + +/* Halt or clear halt of endpoint */ +static int +udc_set_halt(struct usb_ep *usbep, int halt) +{ + struct udc_ep *ep; + u32 tmp; + unsigned long iflags; + int retval = 0; + + if (!usbep) + return -EINVAL; + + pr_debug("set_halt %s: halt=%d\n", usbep->name, halt); + + ep = container_of(usbep, struct udc_ep, ep); + if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&udc_stall_spinlock, iflags); + /* halt or clear halt */ + if (halt) { + if (ep->num == 0) + ep->dev->stall_ep0in = 1; + else { + /* + * set STALL + * rxfifo empty not taken into acount + */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + ep->halted = 1; + + /* setup poll timer */ + if (!timer_pending(&udc_pollstall_timer)) { + udc_pollstall_timer.expires = jiffies + + HZ * UDC_POLLSTALL_TIMER_USECONDS + / (1000 * 1000); + if (!stop_pollstall_timer) { + DBG(ep->dev, "start polltimer\n"); + add_timer(&udc_pollstall_timer); + } + } + } + } else { + /* ep is halted by set_halt() before */ + if (ep->halted) { + tmp = readl(&ep->regs->ctl); + /* clear stall bit */ + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + /* clear NAK by writing CNAK */ + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->halted = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + } + spin_unlock_irqrestore(&udc_stall_spinlock, iflags); + return retval; +} + +/* gadget interface */ +static const struct usb_ep_ops udc_ep_ops = { + .enable = udc_ep_enable, + .disable = udc_ep_disable, + + .alloc_request = udc_alloc_request, + .free_request = udc_free_request, + + .queue = udc_queue, + .dequeue = udc_dequeue, + + .set_halt = udc_set_halt, + /* fifo ops not implemented */ +}; + +/*-------------------------------------------------------------------------*/ + +/* Get frame counter (not implemented) */ +static int udc_get_frame(struct usb_gadget *gadget) +{ + return -EOPNOTSUPP; +} + +/* Remote wakeup gadget interface */ +static int udc_wakeup(struct usb_gadget *gadget) +{ + struct udc *dev; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct udc, gadget); + udc_remote_wakeup(dev); + + return 0; +} + +/* gadget operations */ +static const struct usb_gadget_ops udc_ops = { + .wakeup = udc_wakeup, + .get_frame = udc_get_frame, +}; + +/* Setups endpoint parameters, adds endpoints to linked list */ +static void make_ep_lists(struct udc *dev) +{ + /* make gadget ep lists */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list, + &dev->gadget.ep_list); + list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list, + &dev->gadget.ep_list); + list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list, + &dev->gadget.ep_list); + + /* fifo config */ + dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE; + if (dev->gadget.speed == USB_SPEED_FULL) + dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE; + else if (dev->gadget.speed == USB_SPEED_HIGH) + dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf; + dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE; +} + +/* init registers at driver load time */ +static int startup_registers(struct udc *dev) +{ + u32 tmp; + + /* init controller by soft reset */ + udc_soft_reset(dev); + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + /* put into initial config */ + udc_basic_init(dev); + /* link up all endpoints */ + udc_setup_endpoints(dev); + + /* program speed */ + tmp = readl(&dev->regs->cfg); + if (use_fullspeed) { + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); + } else { + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); + } + writel(tmp, &dev->regs->cfg); + + return 0; +} + +/* Inits UDC context */ +static void udc_basic_init(struct udc *dev) +{ + u32 tmp; + + DBG(dev, "udc_basic_init()\n"); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* stop poll stall timer */ + if (timer_pending(&udc_pollstall_timer)) { + mod_timer(&udc_pollstall_timer, jiffies - 1); + } + /* disable DMA */ + tmp = readl(&dev->regs->ctl); + tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE); + tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE); + writel(tmp, &dev->regs->ctl); + + /* enable dynamic CSR programming */ + tmp = readl(&dev->regs->cfg); + tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG); + /* set self powered */ + tmp |= AMD_BIT(UDC_DEVCFG_SP); + /* set remote wakeupable */ + tmp |= AMD_BIT(UDC_DEVCFG_RWKP); + writel(tmp, &dev->regs->cfg); + + make_ep_lists(dev); + + dev->data_ep_enabled = 0; + dev->data_ep_queued = 0; +} + +/* Sets initial endpoint parameters */ +static void udc_setup_endpoints(struct udc *dev) +{ + struct udc_ep *ep; + u32 tmp; + u32 reg; + + DBG(dev, "udc_setup_endpoints()\n"); + + /* read enum speed */ + tmp = readl(&dev->regs->sts); + tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED); + if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) { + dev->gadget.speed = USB_SPEED_HIGH; + } else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) { + dev->gadget.speed = USB_SPEED_FULL; + } + + /* set basic ep parameters */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + ep = &dev->ep[tmp]; + ep->dev = dev; + ep->ep.name = ep_string[tmp]; + ep->num = tmp; + /* txfifo size is calculated at enable time */ + ep->txfifo = dev->txfifo; + + /* fifo size */ + if (tmp < UDC_EPIN_NUM) { + ep->fifo_depth = UDC_TXFIFO_SIZE; + ep->in = 1; + } else { + ep->fifo_depth = UDC_RXFIFO_SIZE; + ep->in = 0; + + } + ep->regs = &dev->ep_regs[tmp]; + /* + * ep will be reset only if ep was not enabled before to avoid + * disabling ep interrupts when ENUM interrupt occurs but ep is + * not enabled by gadget driver + */ + if (!ep->desc) { + ep_init(dev->regs, ep); + } + + if (use_dma) { + /* + * ep->dma is not really used, just to indicate that + * DMA is active: remove this + * dma regs = dev control regs + */ + ep->dma = &dev->regs->ctl; + + /* nak OUT endpoints until enable - not for ep0 */ + if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX + && tmp > UDC_EPIN_NUM) { + /* set NAK */ + reg = readl(&dev->ep[tmp].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_SNAK); + writel(reg, &dev->ep[tmp].regs->ctl); + dev->ep[tmp].naking = 1; + + } + } + } + /* EP0 max packet */ + if (dev->gadget.speed == USB_SPEED_FULL) { + dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE; + dev->ep[UDC_EP0OUT_IX].ep.maxpacket = + UDC_FS_EP0OUT_MAX_PKT_SIZE; + } else if (dev->gadget.speed == USB_SPEED_HIGH) { + dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; + dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + } + + /* + * with suspend bug workaround, ep0 params for gadget driver + * are set at gadget driver bind() call + */ + dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep; + dev->ep[UDC_EP0IN_IX].halted = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* init cfg/alt/int */ + dev->cur_config = 0; + dev->cur_intf = 0; + dev->cur_alt = 0; +} + +/* Bringup after Connect event, initial bringup to be ready for ep0 events */ +static void usb_connect(struct udc *dev) +{ + + dev_info(&dev->pdev->dev, "USB Connect\n"); + + dev->connected = 1; + + /* put into initial config */ + udc_basic_init(dev); + + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); +} + +/* + * Calls gadget with disconnect event and resets the UDC and makes + * initial bringup to be ready for ep0 events + */ +static void usb_disconnect(struct udc *dev) +{ + + dev_info(&dev->pdev->dev, "USB Disconnect\n"); + + dev->connected = 0; + + /* mask interrupts */ + udc_mask_unused_interrupts(dev); + + /* REVISIT there doesn't seem to be a point to having this + * talk to a tasklet ... do it directly, we already hold + * the spinlock needed to process the disconnect. + */ + + tasklet_schedule(&disconnect_tasklet); +} + +/* Tasklet for disconnect to be outside of interrupt context */ +static void udc_tasklet_disconnect(unsigned long par) +{ + struct udc *dev = (struct udc *)(*((struct udc **) par)); + u32 tmp; + + DBG(dev, "Tasklet disconnect\n"); + spin_lock_irq(&dev->lock); + + if (dev->driver) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + + /* empty queues */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + empty_req_queue(&dev->ep[tmp]); + } + + } + + /* disable ep0 */ + ep_init(dev->regs, + &dev->ep[UDC_EP0IN_IX]); + + + if (!soft_reset_occured) { + /* init controller by soft reset */ + udc_soft_reset(dev); + soft_reset_occured++; + } + + /* re-enable dev interrupts */ + udc_enable_dev_setup_interrupts(dev); + /* back to full speed ? */ + if (use_fullspeed) { + tmp = readl(&dev->regs->cfg); + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); + writel(tmp, &dev->regs->cfg); + } + + spin_unlock_irq(&dev->lock); +} + +/* Reset the UDC core */ +static void udc_soft_reset(struct udc *dev) +{ + unsigned long flags; + + DBG(dev, "Soft reset\n"); + /* + * reset possible waiting interrupts, because int. + * status is lost after soft reset, + * ep int. status reset + */ + writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts); + /* device int. status reset */ + writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts); + + spin_lock_irqsave(&udc_irq_spinlock, flags); + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + readl(&dev->regs->cfg); + spin_unlock_irqrestore(&udc_irq_spinlock, flags); + +} + +/* RDE timer callback to set RDE bit */ +static void udc_timer_function(unsigned long v) +{ + u32 tmp; + + spin_lock_irq(&udc_irq_spinlock); + + if (set_rde > 0) { + /* + * open the fifo if fifo was filled on last timer call + * conditionally + */ + if (set_rde > 1) { + /* set RDE to receive setup data */ + tmp = readl(&udc->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RDE); + writel(tmp, &udc->regs->ctl); + set_rde = -1; + } else if (readl(&udc->regs->sts) + & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + /* + * if fifo empty setup polling, do not just + * open the fifo + */ + udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; + if (!stop_timer) { + add_timer(&udc_timer); + } + } else { + /* + * fifo contains data now, setup timer for opening + * the fifo when timer expires to be able to receive + * setup packets, when data packets gets queued by + * gadget layer then timer will forced to expire with + * set_rde=0 (RDE is set in udc_queue()) + */ + set_rde++; + /* debug: lhadmot_timer_start = 221070 */ + udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + + } else + set_rde = -1; /* RDE was set by udc_queue() */ + spin_unlock_irq(&udc_irq_spinlock); + if (stop_timer) + complete(&on_exit); + +} + +/* Handle halt state, used in stall poll timer */ +static void udc_handle_halt_state(struct udc_ep *ep) +{ + u32 tmp; + /* set stall as long not halted */ + if (ep->halted == 1) { + tmp = readl(&ep->regs->ctl); + /* STALL cleared ? */ + if (!(tmp & AMD_BIT(UDC_EPCTL_S))) { + /* + * FIXME: MSC spec requires that stall remains + * even on receivng of CLEAR_FEATURE HALT. So + * we would set STALL again here to be compliant. + * But with current mass storage drivers this does + * not work (would produce endless host retries). + * So we clear halt on CLEAR_FEATURE. + * + DBG(ep->dev, "ep %d: set STALL again\n", ep->num); + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl);*/ + + /* clear NAK by writing CNAK */ + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->halted = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + } +} + +/* Stall timer callback to poll S bit and set it again after */ +static void udc_pollstall_timer_function(unsigned long v) +{ + struct udc_ep *ep; + int halted = 0; + + spin_lock_irq(&udc_stall_spinlock); + /* + * only one IN and OUT endpoints are handled + * IN poll stall + */ + ep = &udc->ep[UDC_EPIN_IX]; + udc_handle_halt_state(ep); + if (ep->halted) + halted = 1; + /* OUT poll stall */ + ep = &udc->ep[UDC_EPOUT_IX]; + udc_handle_halt_state(ep); + if (ep->halted) + halted = 1; + + /* setup timer again when still halted */ + if (!stop_pollstall_timer && halted) { + udc_pollstall_timer.expires = jiffies + + HZ * UDC_POLLSTALL_TIMER_USECONDS + / (1000 * 1000); + add_timer(&udc_pollstall_timer); + } + spin_unlock_irq(&udc_stall_spinlock); + + if (stop_pollstall_timer) + complete(&on_pollstall_exit); +} + +/* Inits endpoint 0 so that SETUP packets are processed */ +static void activate_control_endpoints(struct udc *dev) +{ + u32 tmp; + + DBG(dev, "activate_control_endpoints\n"); + + /* flush fifo */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + + /* set ep0 directions */ + dev->ep[UDC_EP0IN_IX].in = 1; + dev->ep[UDC_EP0OUT_IX].in = 0; + + /* set buffer size (tx fifo entries) of EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE, + UDC_EPIN_BUFF_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE, + UDC_EPIN_BUFF_SIZE); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum); + + /* set max packet size of EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE, + UDC_EP_MAX_PKT_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE, + UDC_EP_MAX_PKT_SIZE); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt); + + /* set max packet size of EP0_OUT */ + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, + UDC_EP_MAX_PKT_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, + UDC_EP_MAX_PKT_SIZE); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt); + + /* set max packet size of EP0 in UDC CSR */ + tmp = readl(&dev->csr->ne[0]); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, + UDC_CSR_NE_MAX_PKT); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, + UDC_CSR_NE_MAX_PKT); + writel(tmp, &dev->csr->ne[0]); + + if (use_dma) { + dev->ep[UDC_EP0OUT_IX].td->status |= + AMD_BIT(UDC_DMA_OUT_STS_L); + /* write dma desc address */ + writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma, + &dev->ep[UDC_EP0OUT_IX].regs->subptr); + writel(dev->ep[UDC_EP0OUT_IX].td_phys, + &dev->ep[UDC_EP0OUT_IX].regs->desptr); + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* stop pollstall timer */ + if (timer_pending(&udc_pollstall_timer)) { + mod_timer(&udc_pollstall_timer, jiffies - 1); + } + /* enable DMA */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_MODE) + | AMD_BIT(UDC_DEVCTL_RDE) + | AMD_BIT(UDC_DEVCTL_TDE); + if (use_dma_bufferfill_mode) { + tmp |= AMD_BIT(UDC_DEVCTL_BF); + } else if (use_dma_ppb_du) { + tmp |= AMD_BIT(UDC_DEVCTL_DU); + } + writel(tmp, &dev->regs->ctl); + } + + /* clear NAK by writing CNAK for EP0IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX); + + /* clear NAK by writing CNAK for EP0OUT */ + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX); +} + +/* Make endpoint 0 ready for control traffic */ +static int setup_ep0(struct udc *dev) +{ + activate_control_endpoints(dev); + /* enable ep0 interrupts */ + udc_enable_ep0_interrupts(dev); + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); + + return 0; +} + +/* Called by gadget driver to register itself */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct udc *dev = udc; + int retval; + u32 tmp; + + if (!driver || !driver->bind || !driver->setup + || driver->speed != USB_SPEED_HIGH) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + driver->driver.bus = NULL; + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + retval = driver->bind(&dev->gadget); + + /* Some gadget drivers use both ep0 directions. + * NOTE: to gadget driver, ep0 is just one endpoint... + */ + dev->ep[UDC_EP0OUT_IX].ep.driver_data = + dev->ep[UDC_EP0IN_IX].ep.driver_data; + + if (retval) { + DBG(dev, "binding to %s returning %d\n", + driver->driver.name, retval); + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } + + /* get ready for ep0 traffic */ + setup_ep0(dev); + + /* clear SD */ + tmp = readl(&dev->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + + usb_connect(dev); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +/* shutdown requests and disconnect from gadget */ +static void +shutdown(struct udc *dev, struct usb_gadget_driver *driver) +__releases(dev->lock) +__acquires(dev->lock) +{ + int tmp; + + /* empty queues and init hardware */ + udc_basic_init(dev); + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + empty_req_queue(&dev->ep[tmp]); + } + + if (dev->gadget.speed != USB_SPEED_UNKNOWN) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + /* init */ + udc_setup_endpoints(dev); +} + +/* Called by gadget driver to unregister itself */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct udc *dev = udc; + unsigned long flags; + u32 tmp; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + udc_mask_unused_interrupts(dev); + shutdown(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + dev->driver = NULL; + + /* set SD */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + + + DBG(dev, "%s: unregistered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/* Clear pending NAK bits */ +static void udc_process_cnak_queue(struct udc *dev) +{ + u32 tmp; + u32 reg; + + /* check epin's */ + DBG(dev, "CNAK pending queue processing\n"); + for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) { + if (cnak_pending & (1 << tmp)) { + DBG(dev, "CNAK pending for ep%d\n", tmp); + /* clear NAK by writing CNAK */ + reg = readl(&dev->ep[tmp].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_CNAK); + writel(reg, &dev->ep[tmp].regs->ctl); + dev->ep[tmp].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num); + } + } + /* ... and ep0out */ + if (cnak_pending & (1 << UDC_EP0OUT_IX)) { + DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX); + /* clear NAK by writing CNAK */ + reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_CNAK); + writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], + dev->ep[UDC_EP0OUT_IX].num); + } +} + +/* Enabling RX DMA after setup packet */ +static void udc_ep0_set_rde(struct udc *dev) +{ + if (use_dma) { + /* + * only enable RXDMA when no data endpoint enabled + * or data is queued + */ + if (!dev->data_ep_enabled || dev->data_ep_queued) { + udc_set_rde(dev); + } else { + /* + * setup timer for enabling RDE (to not enable + * RXFIFO DMA for data endpoints to early) + */ + if (set_rde != 0 && !timer_pending(&udc_timer)) { + udc_timer.expires = + jiffies + HZ/UDC_RDE_TIMER_DIV; + set_rde = 1; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + } + } +} + + +/* Interrupt handler for data OUT traffic */ +static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) +{ + irqreturn_t ret_val = IRQ_NONE; + u32 tmp; + struct udc_ep *ep; + struct udc_request *req; + unsigned int count; + struct udc_data_dma *td = NULL; + unsigned dma_done; + + VDBG(dev, "ep%d irq\n", ep_ix); + ep = &dev->ep[ep_ix]; + + tmp = readl(&ep->regs->sts); + if (use_dma) { + /* BNA event ? */ + if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { + DBG(dev, "BNA ep%dout occured - DESPTR = %x \n", + ep->num, readl(&ep->regs->desptr)); + /* clear BNA */ + writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); + if (!ep->cancel_transfer) + ep->bna_occurred = 1; + else + ep->cancel_transfer = 0; + ret_val = IRQ_HANDLED; + goto finished; + } + } + /* HE event ? */ + if (tmp & AMD_BIT(UDC_EPSTS_HE)) { + dev_err(&dev->pdev->dev, "HE ep%dout occured\n", ep->num); + + /* clear HE */ + writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); + ret_val = IRQ_HANDLED; + goto finished; + } + + if (!list_empty(&ep->queue)) { + + /* next request */ + req = list_entry(ep->queue.next, + struct udc_request, queue); + } else { + req = NULL; + udc_rxfifo_pending = 1; + } + VDBG(dev, "req = %p\n", req); + /* fifo mode */ + if (!use_dma) { + + /* read fifo */ + if (req && udc_rxfifo_read(ep, req)) { + ret_val = IRQ_HANDLED; + + /* finish */ + complete_req(ep, req, 0); + /* next request */ + if (!list_empty(&ep->queue) && !ep->halted) { + req = list_entry(ep->queue.next, + struct udc_request, queue); + } else + req = NULL; + } + + /* DMA */ + } else if (!ep->cancel_transfer && req != NULL) { + ret_val = IRQ_HANDLED; + + /* check for DMA done */ + if (!use_dma_ppb) { + dma_done = AMD_GETBITS(req->td_data->status, + UDC_DMA_OUT_STS_BS); + /* packet per buffer mode - rx bytes */ + } else { + /* + * if BNA occurred then recover desc. from + * BNA dummy desc. + */ + if (ep->bna_occurred) { + VDBG(dev, "Recover desc. from BNA dummy\n"); + memcpy(req->td_data, ep->bna_dummy_req->td_data, + sizeof(struct udc_data_dma)); + ep->bna_occurred = 0; + udc_init_bna_dummy(ep->req); + } + td = udc_get_last_dma_desc(req); + dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS); + } + if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) { + /* buffer fill mode - rx bytes */ + if (!use_dma_ppb) { + /* received number bytes */ + count = AMD_GETBITS(req->td_data->status, + UDC_DMA_OUT_STS_RXBYTES); + VDBG(dev, "rx bytes=%u\n", count); + /* packet per buffer mode - rx bytes */ + } else { + VDBG(dev, "req->td_data=%p\n", req->td_data); + VDBG(dev, "last desc = %p\n", td); + /* received number bytes */ + if (use_dma_ppb_du) { + /* every desc. counts bytes */ + count = udc_get_ppbdu_rxbytes(req); + } else { + /* last desc. counts bytes */ + count = AMD_GETBITS(td->status, + UDC_DMA_OUT_STS_RXBYTES); + if (!count && req->req.length + == UDC_DMA_MAXPACKET) { + /* + * on 64k packets the RXBYTES + * field is zero + */ + count = UDC_DMA_MAXPACKET; + } + } + VDBG(dev, "last desc rx bytes=%u\n", count); + } + + tmp = req->req.length - req->req.actual; + if (count > tmp) { + if ((tmp % ep->ep.maxpacket) != 0) { + DBG(dev, "%s: rx %db, space=%db\n", + ep->ep.name, count, tmp); + req->req.status = -EOVERFLOW; + } + count = tmp; + } + req->req.actual += count; + req->dma_going = 0; + /* complete request */ + complete_req(ep, req, 0); + + /* next request */ + if (!list_empty(&ep->queue) && !ep->halted) { + req = list_entry(ep->queue.next, + struct udc_request, + queue); + /* + * DMA may be already started by udc_queue() + * called by gadget drivers completion + * routine. This happens when queue + * holds one request only. + */ + if (req->dma_going == 0) { + /* next dma */ + if (prep_dma(ep, req, GFP_ATOMIC) != 0) + goto finished; + /* write desc pointer */ + writel(req->td_phys, + &ep->regs->desptr); + req->dma_going = 1; + /* enable DMA */ + udc_set_rde(dev); + } + } else { + /* + * implant BNA dummy descriptor to allow + * RXFIFO opening by RDE + */ + if (ep->bna_dummy_req) { + /* write desc pointer */ + writel(ep->bna_dummy_req->td_phys, + &ep->regs->desptr); + ep->bna_occurred = 0; + } + + /* + * schedule timer for setting RDE if queue + * remains empty to allow ep0 packets pass + * through + */ + if (set_rde != 0 + && !timer_pending(&udc_timer)) { + udc_timer.expires = + jiffies + + HZ*UDC_RDE_TIMER_SECONDS; + set_rde = 1; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_queued = 0; + } + + } else { + /* + * RX DMA must be reenabled for each desc in PPBDU mode + * and must be enabled for PPBNDU mode in case of BNA + */ + udc_set_rde(dev); + } + + } else if (ep->cancel_transfer) { + ret_val = IRQ_HANDLED; + ep->cancel_transfer = 0; + } + + /* check pending CNAKS */ + if (cnak_pending) { + /* CNAk processing when rxfifo empty only */ + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + udc_process_cnak_queue(dev); + } + } + + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts); +finished: + return ret_val; +} + +/* Interrupt handler for data IN traffic */ +static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) +{ + irqreturn_t ret_val = IRQ_NONE; + u32 tmp; + u32 epsts; + struct udc_ep *ep; + struct udc_request *req; + struct udc_data_dma *td; + unsigned dma_done; + unsigned len; + + ep = &dev->ep[ep_ix]; + + epsts = readl(&ep->regs->sts); + if (use_dma) { + /* BNA ? */ + if (epsts & AMD_BIT(UDC_EPSTS_BNA)) { + dev_err(&dev->pdev->dev, + "BNA ep%din occured - DESPTR = %08lx \n", + ep->num, + (unsigned long) readl(&ep->regs->desptr)); + + /* clear BNA */ + writel(epsts, &ep->regs->sts); + ret_val = IRQ_HANDLED; + goto finished; + } + } + /* HE event ? */ + if (epsts & AMD_BIT(UDC_EPSTS_HE)) { + dev_err(&dev->pdev->dev, + "HE ep%dn occured - DESPTR = %08lx \n", + ep->num, (unsigned long) readl(&ep->regs->desptr)); + + /* clear HE */ + writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); + ret_val = IRQ_HANDLED; + goto finished; + } + + /* DMA completion */ + if (epsts & AMD_BIT(UDC_EPSTS_TDC)) { + VDBG(dev, "TDC set- completion\n"); + ret_val = IRQ_HANDLED; + if (!ep->cancel_transfer && !list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct udc_request, queue); + if (req) { + /* + * length bytes transfered + * check dma done of last desc. in PPBDU mode + */ + if (use_dma_ppb_du) { + td = udc_get_last_dma_desc(req); + if (td) { + dma_done = + AMD_GETBITS(td->status, + UDC_DMA_IN_STS_BS); + /* don't care DMA done */ + req->req.actual = + req->req.length; + } + } else { + /* assume all bytes transferred */ + req->req.actual = req->req.length; + } + + if (req->req.actual == req->req.length) { + /* complete req */ + complete_req(ep, req, 0); + req->dma_going = 0; + /* further request available ? */ + if (list_empty(&ep->queue)) { + /* disable interrupt */ + tmp = readl( + &dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, + &dev->regs->ep_irqmsk); + } + + } + } + } + ep->cancel_transfer = 0; + + } + /* + * status reg has IN bit set and TDC not set (if TDC was handled, + * IN must not be handled (UDC defect) ? + */ + if ((epsts & AMD_BIT(UDC_EPSTS_IN)) + && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) { + ret_val = IRQ_HANDLED; + if (!list_empty(&ep->queue)) { + /* next request */ + req = list_entry(ep->queue.next, + struct udc_request, queue); + /* FIFO mode */ + if (!use_dma) { + /* write fifo */ + udc_txfifo_write(ep, &req->req); + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; + if (req->req.actual == req->req.length + || (len != ep->ep.maxpacket)) { + /* complete req */ + complete_req(ep, req, 0); + } + /* DMA */ + } else if (req && !req->dma_going) { + VDBG(dev, "IN DMA : req=%p req->td_data=%p\n", + req, req->td_data); + if (req->td_data) { + + req->dma_going = 1; + + /* + * unset L bit of first desc. + * for chain + */ + if (use_dma_ppb && req->req.length > + ep->ep.maxpacket) { + req->td_data->status &= + AMD_CLEAR_BIT( + UDC_DMA_IN_STS_L); + } + + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + + /* set HOST READY */ + req->td_data->status = + AMD_ADDBITS( + req->td_data->status, + UDC_DMA_IN_STS_BS_HOST_READY, + UDC_DMA_IN_STS_BS); + + /* set poll demand bit */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_P); + writel(tmp, &ep->regs->ctl); + } + } + + } + } + /* clear status bits */ + writel(epsts, &ep->regs->sts); + +finished: + return ret_val; + +} + +/* Interrupt handler for Control OUT traffic */ +static irqreturn_t udc_control_out_isr(struct udc *dev) +__releases(dev->lock) +__acquires(dev->lock) +{ + irqreturn_t ret_val = IRQ_NONE; + u32 tmp; + int setup_supported; + u32 count; + int set = 0; + struct udc_ep *ep; + struct udc_ep *ep_tmp; + + ep = &dev->ep[UDC_EP0OUT_IX]; + + /* clear irq */ + writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts); + + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts); + /* check BNA and clear if set */ + if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { + VDBG(dev, "ep0: BNA set\n"); + writel(AMD_BIT(UDC_EPSTS_BNA), + &dev->ep[UDC_EP0OUT_IX].regs->sts); + ep->bna_occurred = 1; + ret_val = IRQ_HANDLED; + goto finished; + } + + /* type of data: SETUP or DATA 0 bytes */ + tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT); + VDBG(dev, "data_typ = %x\n", tmp); + + /* setup data */ + if (tmp == UDC_EPSTS_OUT_SETUP) { + ret_val = IRQ_HANDLED; + + ep->dev->stall_ep0in = 0; + dev->waiting_zlp_ack_ep0in = 0; + + /* set NAK for EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 1; + /* get setup data */ + if (use_dma) { + + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_CLEAR, + &dev->ep[UDC_EP0OUT_IX].regs->sts); + + setup_data.data[0] = + dev->ep[UDC_EP0OUT_IX].td_stp->data12; + setup_data.data[1] = + dev->ep[UDC_EP0OUT_IX].td_stp->data34; + /* set HOST READY */ + dev->ep[UDC_EP0OUT_IX].td_stp->status = + UDC_DMA_STP_STS_BS_HOST_READY; + } else { + /* read fifo */ + udc_rxfifo_read_dwords(dev, setup_data.data, 2); + } + + /* determine direction of control data */ + if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) { + dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep; + /* enable RDE */ + udc_ep0_set_rde(dev); + set = 0; + } else { + dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep; + /* + * implant BNA dummy descriptor to allow RXFIFO opening + * by RDE + */ + if (ep->bna_dummy_req) { + /* write desc pointer */ + writel(ep->bna_dummy_req->td_phys, + &dev->ep[UDC_EP0OUT_IX].regs->desptr); + ep->bna_occurred = 0; + } + + set = 1; + dev->ep[UDC_EP0OUT_IX].naking = 1; + /* + * setup timer for enabling RDE (to not enable + * RXFIFO DMA for data to early) + */ + set_rde = 1; + if (!timer_pending(&udc_timer)) { + udc_timer.expires = jiffies + + HZ/UDC_RDE_TIMER_DIV; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + } + + /* + * mass storage reset must be processed here because + * next packet may be a CLEAR_FEATURE HALT which would not + * clear the stall bit when no STALL handshake was received + * before (autostall can cause this) + */ + if (setup_data.data[0] == UDC_MSCRES_DWORD0 + && setup_data.data[1] == UDC_MSCRES_DWORD1) { + DBG(dev, "MSC Reset\n"); + /* + * clear stall bits + * only one IN and OUT endpoints are handled + */ + ep_tmp = &udc->ep[UDC_EPIN_IX]; + udc_set_halt(&ep_tmp->ep, 0); + ep_tmp = &udc->ep[UDC_EPOUT_IX]; + udc_set_halt(&ep_tmp->ep, 0); + } + + /* call gadget with setup data received */ + spin_unlock(&dev->lock); + setup_supported = dev->driver->setup(&dev->gadget, + &setup_data.request); + spin_lock(&dev->lock); + + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + /* ep0 in returns data (not zlp) on IN phase */ + if (setup_supported >= 0 && setup_supported < + UDC_EP0IN_MAXPACKET) { + /* clear NAK by writing CNAK in EP0_IN */ + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX); + + /* if unsupported request then stall */ + } else if (setup_supported < 0) { + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + } else + dev->waiting_zlp_ack_ep0in = 1; + + + /* clear NAK by writing CNAK in EP0_OUT */ + if (!set) { + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX); + } + + if (!use_dma) { + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_CLEAR, + &dev->ep[UDC_EP0OUT_IX].regs->sts); + } + + /* data packet 0 bytes */ + } else if (tmp == UDC_EPSTS_OUT_DATA) { + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts); + + /* get setup data: only 0 packet */ + if (use_dma) { + /* no req if 0 packet, just reactivate */ + if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) { + VDBG(dev, "ZLP\n"); + + /* set HOST READY */ + dev->ep[UDC_EP0OUT_IX].td->status = + AMD_ADDBITS( + dev->ep[UDC_EP0OUT_IX].td->status, + UDC_DMA_OUT_STS_BS_HOST_READY, + UDC_DMA_OUT_STS_BS); + /* enable RDE */ + udc_ep0_set_rde(dev); + ret_val = IRQ_HANDLED; + + } else { + /* control write */ + ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX); + /* re-program desc. pointer for possible ZLPs */ + writel(dev->ep[UDC_EP0OUT_IX].td_phys, + &dev->ep[UDC_EP0OUT_IX].regs->desptr); + /* enable RDE */ + udc_ep0_set_rde(dev); + } + } else { + + /* received number bytes */ + count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts); + count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE); + /* out data for fifo mode not working */ + count = 0; + + /* 0 packet or real data ? */ + if (count != 0) { + ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX); + } else { + /* dummy read confirm */ + readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm); + ret_val = IRQ_HANDLED; + } + } + } + + /* check pending CNAKS */ + if (cnak_pending) { + /* CNAk processing when rxfifo empty only */ + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + udc_process_cnak_queue(dev); + } + } + +finished: + return ret_val; +} + +/* Interrupt handler for Control IN traffic */ +static irqreturn_t udc_control_in_isr(struct udc *dev) +{ + irqreturn_t ret_val = IRQ_NONE; + u32 tmp; + struct udc_ep *ep; + struct udc_request *req; + unsigned len; + + ep = &dev->ep[UDC_EP0IN_IX]; + + /* clear irq */ + writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts); + + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts); + /* DMA completion */ + if (tmp & AMD_BIT(UDC_EPSTS_TDC)) { + VDBG(dev, "isr: TDC clear \n"); + ret_val = IRQ_HANDLED; + + /* clear TDC bit */ + writel(AMD_BIT(UDC_EPSTS_TDC), + &dev->ep[UDC_EP0IN_IX].regs->sts); + + /* status reg has IN bit set ? */ + } else if (tmp & AMD_BIT(UDC_EPSTS_IN)) { + ret_val = IRQ_HANDLED; + + if (ep->dma) { + /* clear IN bit */ + writel(AMD_BIT(UDC_EPSTS_IN), + &dev->ep[UDC_EP0IN_IX].regs->sts); + } + if (dev->stall_ep0in) { + DBG(dev, "stall ep0in\n"); + /* halt ep0in */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } else { + if (!list_empty(&ep->queue)) { + /* next request */ + req = list_entry(ep->queue.next, + struct udc_request, queue); + + if (ep->dma) { + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + /* set HOST READY */ + req->td_data->status = + AMD_ADDBITS( + req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_READY, + UDC_DMA_STP_STS_BS); + + /* set poll demand bit */ + tmp = + readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_P); + writel(tmp, + &dev->ep[UDC_EP0IN_IX].regs->ctl); + + /* all bytes will be transferred */ + req->req.actual = req->req.length; + + /* complete req */ + complete_req(ep, req, 0); + + } else { + /* write fifo */ + udc_txfifo_write(ep, &req->req); + + /* lengh bytes transfered */ + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + + req->req.actual += len; + if (req->req.actual == req->req.length + || (len != ep->ep.maxpacket)) { + /* complete req */ + complete_req(ep, req, 0); + } + } + + } + } + ep->halted = 0; + dev->stall_ep0in = 0; + if (!ep->dma) { + /* clear IN bit */ + writel(AMD_BIT(UDC_EPSTS_IN), + &dev->ep[UDC_EP0IN_IX].regs->sts); + } + } + + return ret_val; +} + + +/* Interrupt handler for global device events */ +static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq) +__releases(dev->lock) +__acquires(dev->lock) +{ + irqreturn_t ret_val = IRQ_NONE; + u32 tmp; + u32 cfg; + struct udc_ep *ep; + u16 i; + u8 udc_csr_epix; + + /* SET_CONFIG irq ? */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) { + ret_val = IRQ_HANDLED; + + /* read config value */ + tmp = readl(&dev->regs->sts); + cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG); + DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg); + dev->cur_config = cfg; + dev->set_cfg_not_acked = 1; + + /* make usb request for gadget driver */ + memset(&setup_data, 0 , sizeof(union udc_setup_data)); + setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.request.wValue = dev->cur_config; + + /* programm the NE registers */ + for (i = 0; i < UDC_EP_NUM; i++) { + ep = &dev->ep[i]; + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + + /* OUT ep */ + } else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + } + + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* ep cfg */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, + UDC_CSR_NE_CFG); + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* clear stall bits */ + ep->halted = 0; + tmp = readl(&ep->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + /* call gadget zero with setup data received */ + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &setup_data.request); + spin_lock(&dev->lock); + + } /* SET_INTERFACE ? */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) { + ret_val = IRQ_HANDLED; + + dev->set_cfg_not_acked = 1; + /* read interface and alt setting values */ + tmp = readl(&dev->regs->sts); + dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT); + dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF); + + /* make usb request for gadget driver */ + memset(&setup_data, 0 , sizeof(union udc_setup_data)); + setup_data.request.bRequest = USB_REQ_SET_INTERFACE; + setup_data.request.bRequestType = USB_RECIP_INTERFACE; + setup_data.request.wValue = dev->cur_alt; + setup_data.request.wIndex = dev->cur_intf; + + DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n", + dev->cur_alt, dev->cur_intf); + + /* programm the NE registers */ + for (i = 0; i < UDC_EP_NUM; i++) { + ep = &dev->ep[i]; + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + + /* OUT ep */ + } else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + } + + /* UDC CSR reg */ + /* set ep values */ + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* ep interface */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, + UDC_CSR_NE_INTF); + /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */ + /* ep alt */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, + UDC_CSR_NE_ALT); + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* clear stall bits */ + ep->halted = 0; + tmp = readl(&ep->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + + /* call gadget zero with setup data received */ + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &setup_data.request); + spin_lock(&dev->lock); + + } /* USB reset */ + if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) { + DBG(dev, "USB Reset interrupt\n"); + ret_val = IRQ_HANDLED; + + /* allow soft reset when suspend occurs */ + soft_reset_occured = 0; + + dev->waiting_zlp_ack_ep0in = 0; + dev->set_cfg_not_acked = 0; + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + /* call gadget to resume and reset configs etc. */ + spin_unlock(&dev->lock); + if (dev->sys_suspended && dev->driver->resume) { + dev->driver->resume(&dev->gadget); + dev->sys_suspended = 0; + } + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + + /* disable ep0 to empty req queue */ + empty_req_queue(&dev->ep[UDC_EP0IN_IX]); + ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]); + + /* soft reset when rxfifo not empty */ + tmp = readl(&dev->regs->sts); + if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) + && !soft_reset_after_usbreset_occured) { + udc_soft_reset(dev); + soft_reset_after_usbreset_occured++; + } + + /* + * DMA reset to kill potential old DMA hw hang, + * POLL bit is already reset by ep_init() through + * disconnect() + */ + DBG(dev, "DMA machine reset\n"); + tmp = readl(&dev->regs->cfg); + writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg); + writel(tmp, &dev->regs->cfg); + + /* put into initial config */ + udc_basic_init(dev); + + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); + + /* enable suspend interrupt */ + tmp = readl(&dev->regs->irqmsk); + tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US); + writel(tmp, &dev->regs->irqmsk); + + } /* USB suspend */ + if (dev_irq & AMD_BIT(UDC_DEVINT_US)) { + DBG(dev, "USB Suspend interrupt\n"); + ret_val = IRQ_HANDLED; + if (dev->driver->suspend) { + spin_unlock(&dev->lock); + dev->sys_suspended = 1; + dev->driver->suspend(&dev->gadget); + spin_lock(&dev->lock); + } + } /* new speed ? */ + if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) { + DBG(dev, "ENUM interrupt\n"); + ret_val = IRQ_HANDLED; + soft_reset_after_usbreset_occured = 0; + + /* disable ep0 to empty req queue */ + empty_req_queue(&dev->ep[UDC_EP0IN_IX]); + ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]); + + /* link up all endpoints */ + udc_setup_endpoints(dev); + if (dev->gadget.speed == USB_SPEED_HIGH) { + dev_info(&dev->pdev->dev, "Connect: speed = %s\n", + "high"); + } else if (dev->gadget.speed == USB_SPEED_FULL) { + dev_info(&dev->pdev->dev, "Connect: speed = %s\n", + "full"); + } + + /* init ep 0 */ + activate_control_endpoints(dev); + + /* enable ep0 interrupts */ + udc_enable_ep0_interrupts(dev); + } + /* session valid change interrupt */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) { + DBG(dev, "USB SVC interrupt\n"); + ret_val = IRQ_HANDLED; + + /* check that session is not valid to detect disconnect */ + tmp = readl(&dev->regs->sts); + if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) { + /* disable suspend interrupt */ + tmp = readl(&dev->regs->irqmsk); + tmp |= AMD_BIT(UDC_DEVINT_US); + writel(tmp, &dev->regs->irqmsk); + DBG(dev, "USB Disconnect (session valid low)\n"); + /* cleanup on disconnect */ + usb_disconnect(udc); + } + + } + + return ret_val; +} + +/* Interrupt Service Routine, see Linux Kernel Doc for parameters */ +static irqreturn_t udc_irq(int irq, void *pdev) +{ + struct udc *dev = pdev; + u32 reg; + u16 i; + u32 ep_irq; + irqreturn_t ret_val = IRQ_NONE; + + spin_lock(&dev->lock); + + /* check for ep irq */ + reg = readl(&dev->regs->ep_irqsts); + if (reg) { + if (reg & AMD_BIT(UDC_EPINT_OUT_EP0)) + ret_val |= udc_control_out_isr(dev); + if (reg & AMD_BIT(UDC_EPINT_IN_EP0)) + ret_val |= udc_control_in_isr(dev); + + /* + * data endpoint + * iterate ep's + */ + for (i = 1; i < UDC_EP_NUM; i++) { + ep_irq = 1 << i; + if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0) + continue; + + /* clear irq status */ + writel(ep_irq, &dev->regs->ep_irqsts); + + /* irq for out ep ? */ + if (i > UDC_EPIN_NUM) + ret_val |= udc_data_out_isr(dev, i); + else + ret_val |= udc_data_in_isr(dev, i); + } + + } + + + /* check for dev irq */ + reg = readl(&dev->regs->irqsts); + if (reg) { + /* clear irq */ + writel(reg, &dev->regs->irqsts); + ret_val |= udc_dev_isr(dev, reg); + } + + + spin_unlock(&dev->lock); + return ret_val; +} + +/* Tears down device */ +static void gadget_release(struct device *pdev) +{ + struct amd5536udc *dev = dev_get_drvdata(pdev); + kfree(dev); +} + +/* Cleanup on device remove */ +static void udc_remove(struct udc *dev) +{ + /* remove timer */ + stop_timer++; + if (timer_pending(&udc_timer)) + wait_for_completion(&on_exit); + if (udc_timer.data) + del_timer_sync(&udc_timer); + /* remove pollstall timer */ + stop_pollstall_timer++; + if (timer_pending(&udc_pollstall_timer)) + wait_for_completion(&on_pollstall_exit); + if (udc_pollstall_timer.data) + del_timer_sync(&udc_pollstall_timer); + udc = NULL; +} + +/* Reset all pci context */ +static void udc_pci_remove(struct pci_dev *pdev) +{ + struct udc *dev; + + dev = pci_get_drvdata(pdev); + + /* gadget driver must not be registered */ + BUG_ON(dev->driver != NULL); + + /* dma pool cleanup */ + if (dev->data_requests) + pci_pool_destroy(dev->data_requests); + + if (dev->stp_requests) { + /* cleanup DMA desc's for ep0in */ + pci_pool_free(dev->stp_requests, + dev->ep[UDC_EP0OUT_IX].td_stp, + dev->ep[UDC_EP0OUT_IX].td_stp_dma); + pci_pool_free(dev->stp_requests, + dev->ep[UDC_EP0OUT_IX].td, + dev->ep[UDC_EP0OUT_IX].td_phys); + + pci_pool_destroy(dev->stp_requests); + } + + /* reset controller */ + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + if (dev->irq_registered) + free_irq(pdev->irq, dev); + if (dev->regs) + iounmap(dev->regs); + if (dev->mem_region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (dev->active) + pci_disable_device(pdev); + + device_unregister(&dev->gadget.dev); + pci_set_drvdata(pdev, NULL); + + udc_remove(dev); +} + +/* create dma pools on init */ +static int init_dma_pools(struct udc *dev) +{ + struct udc_stp_dma *td_stp; + struct udc_data_dma *td_data; + int retval; + + /* consistent DMA mode setting ? */ + if (use_dma_ppb) { + use_dma_bufferfill_mode = 0; + } else { + use_dma_ppb_du = 0; + use_dma_bufferfill_mode = 1; + } + + /* DMA setup */ + dev->data_requests = dma_pool_create("data_requests", NULL, + sizeof(struct udc_data_dma), 0, 0); + if (!dev->data_requests) { + DBG(dev, "can't get request data pool\n"); + retval = -ENOMEM; + goto finished; + } + + /* EP0 in dma regs = dev control regs */ + dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl; + + /* dma desc for setup data */ + dev->stp_requests = dma_pool_create("setup requests", NULL, + sizeof(struct udc_stp_dma), 0, 0); + if (!dev->stp_requests) { + DBG(dev, "can't get stp request pool\n"); + retval = -ENOMEM; + goto finished; + } + /* setup */ + td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL, + &dev->ep[UDC_EP0OUT_IX].td_stp_dma); + if (td_stp == NULL) { + retval = -ENOMEM; + goto finished; + } + dev->ep[UDC_EP0OUT_IX].td_stp = td_stp; + + /* data: 0 packets !? */ + td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL, + &dev->ep[UDC_EP0OUT_IX].td_phys); + if (td_data == NULL) { + retval = -ENOMEM; + goto finished; + } + dev->ep[UDC_EP0OUT_IX].td = td_data; + return 0; + +finished: + return retval; +} + +/* Called by pci bus driver to init pci context */ +static int udc_pci_probe( + struct pci_dev *pdev, + const struct pci_device_id *id +) +{ + struct udc *dev; + unsigned long resource; + unsigned long len; + int retval = 0; + + /* one udc only */ + if (udc) { + dev_dbg(&pdev->dev, "already probed\n"); + return -EBUSY; + } + + /* init */ + dev = kzalloc(sizeof(struct udc), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto finished; + } + memset(dev, 0, sizeof(struct udc)); + + /* pci setup */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto finished; + } + dev->active = 1; + + /* PCI resource allocation */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + + if (!request_mem_region(resource, len, name)) { + dev_dbg(&pdev->dev, "pci device used already\n"); + retval = -EBUSY; + goto finished; + } + dev->mem_region = 1; + + dev->virt_addr = ioremap_nocache(resource, len); + if (dev->virt_addr == NULL) { + dev_dbg(&pdev->dev, "start address cannot be mapped\n"); + retval = -EFAULT; + goto finished; + } + + if (!pdev->irq) { + dev_err(&dev->pdev->dev, "irq not set\n"); + retval = -ENODEV; + goto finished; + } + + if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { + dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq); + retval = -EBUSY; + goto finished; + } + dev->irq_registered = 1; + + pci_set_drvdata(pdev, dev); + + /* chip revision */ + dev->chiprev = 0; + + pci_set_master(pdev); + pci_set_mwi(pdev); + + /* chip rev for Hs AMD5536 */ + pci_read_config_byte(pdev, PCI_REVISION_ID, (u8 *) &dev->chiprev); + /* init dma pools */ + if (use_dma) { + retval = init_dma_pools(dev); + if (retval != 0) + goto finished; + } + + dev->phys_addr = resource; + dev->irq = pdev->irq; + dev->pdev = pdev; + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + + /* general probing */ + if (udc_probe(dev) == 0) + return 0; + +finished: + if (dev) + udc_pci_remove(pdev); + return retval; +} + +/* general probe */ +static int udc_probe(struct udc *dev) +{ + char tmp[128]; + u32 reg; + int retval; + + /* mark timer as not initialized */ + udc_timer.data = 0; + udc_pollstall_timer.data = 0; + + /* device struct setup */ + spin_lock_init(&dev->lock); + dev->gadget.ops = &udc_ops; + + strcpy(dev->gadget.dev.bus_id, "gadget"); + dev->gadget.dev.release = gadget_release; + dev->gadget.name = name; + dev->gadget.name = name; + dev->gadget.is_dualspeed = 1; + + /* udc csr registers base */ + dev->csr = dev->virt_addr + UDC_CSR_ADDR; + /* dev registers base */ + dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; + /* ep registers base */ + dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; + /* fifo's base */ + dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); + dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); + + /* init registers, interrupts, ... */ + startup_registers(dev); + + dev_info(&dev->pdev->dev, "%s\n", mod_desc); + + snprintf(tmp, sizeof tmp, "%d", dev->irq); + dev_info(&dev->pdev->dev, + "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", + tmp, dev->phys_addr, dev->chiprev, + (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); + strcpy(tmp, UDC_DRIVER_VERSION_STRING); + if (dev->chiprev == UDC_HSA0_REV) { + dev_err(&dev->pdev->dev, "chip revision is A0; too old\n"); + retval = -ENODEV; + goto finished; + } + dev_info(&dev->pdev->dev, + "driver version: %s(for Geode5536 B1)\n", tmp); + udc = dev; + + retval = device_register(&dev->gadget.dev); + if (retval) + goto finished; + + /* timer init */ + init_timer(&udc_timer); + udc_timer.function = udc_timer_function; + udc_timer.data = 1; + /* timer pollstall init */ + init_timer(&udc_pollstall_timer); + udc_pollstall_timer.function = udc_pollstall_timer_function; + udc_pollstall_timer.data = 1; + + /* set SD */ + reg = readl(&dev->regs->ctl); + reg |= AMD_BIT(UDC_DEVCTL_SD); + writel(reg, &dev->regs->ctl); + + /* print dev register info */ + print_regs(dev); + + return 0; + +finished: + return retval; +} + +/* Initiates a remote wakeup */ +static int udc_remote_wakeup(struct udc *dev) +{ + unsigned long flags; + u32 tmp; + + DBG(dev, "UDC initiates remote wakeup\n"); + + spin_lock_irqsave(&dev->lock, flags); + + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +/* PCI device parameters */ +static const struct pci_device_id pci_id[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, pci_id); + +/* PCI functions */ +static struct pci_driver udc_pci_driver = { + .name = (char *) name, + .id_table = pci_id, + .probe = udc_pci_probe, + .remove = udc_pci_remove, +}; + +/* Inits driver */ +static int __init init(void) +{ + return pci_register_driver(&udc_pci_driver); +} +module_init(init); + +/* Cleans driver */ +static void __exit cleanup(void) +{ + pci_unregister_driver(&udc_pci_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); +MODULE_AUTHOR("Thomas Dahlmann"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h new file mode 100644 index 00000000000..4bbabbbfc93 --- /dev/null +++ b/drivers/usb/gadget/amd5536udc.h @@ -0,0 +1,626 @@ +/* + * amd5536.h -- header for AMD 5536 UDC high/full speed USB device controller + * + * Copyright (C) 2007 AMD (http://www.amd.com) + * Author: Thomas Dahlmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef AMD5536UDC_H +#define AMD5536UDC_H + +/* various constants */ +#define UDC_RDE_TIMER_SECONDS 1 +#define UDC_RDE_TIMER_DIV 10 +#define UDC_POLLSTALL_TIMER_USECONDS 500 + +/* Hs AMD5536 chip rev. */ +#define UDC_HSA0_REV 1 +#define UDC_HSB1_REV 2 + +/* + * SETUP usb commands + * needed, because some SETUP's are handled in hw, but must be passed to + * gadget driver above + * SET_CONFIG + */ +#define UDC_SETCONFIG_DWORD0 0x00000900 +#define UDC_SETCONFIG_DWORD0_VALUE_MASK 0xffff0000 +#define UDC_SETCONFIG_DWORD0_VALUE_OFS 16 + +#define UDC_SETCONFIG_DWORD1 0x00000000 + +/* SET_INTERFACE */ +#define UDC_SETINTF_DWORD0 0x00000b00 +#define UDC_SETINTF_DWORD0_ALT_MASK 0xffff0000 +#define UDC_SETINTF_DWORD0_ALT_OFS 16 + +#define UDC_SETINTF_DWORD1 0x00000000 +#define UDC_SETINTF_DWORD1_INTF_MASK 0x0000ffff +#define UDC_SETINTF_DWORD1_INTF_OFS 0 + +/* Mass storage reset */ +#define UDC_MSCRES_DWORD0 0x0000ff21 +#define UDC_MSCRES_DWORD1 0x00000000 + +/* Global CSR's -------------------------------------------------------------*/ +#define UDC_CSR_ADDR 0x500 + +/* EP NE bits */ +/* EP number */ +#define UDC_CSR_NE_NUM_MASK 0x0000000f +#define UDC_CSR_NE_NUM_OFS 0 +/* EP direction */ +#define UDC_CSR_NE_DIR_MASK 0x00000010 +#define UDC_CSR_NE_DIR_OFS 4 +/* EP type */ +#define UDC_CSR_NE_TYPE_MASK 0x00000060 +#define UDC_CSR_NE_TYPE_OFS 5 +/* EP config number */ +#define UDC_CSR_NE_CFG_MASK 0x00000780 +#define UDC_CSR_NE_CFG_OFS 7 +/* EP interface number */ +#define UDC_CSR_NE_INTF_MASK 0x00007800 +#define UDC_CSR_NE_INTF_OFS 11 +/* EP alt setting */ +#define UDC_CSR_NE_ALT_MASK 0x00078000 +#define UDC_CSR_NE_ALT_OFS 15 + +/* max pkt */ +#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000 +#define UDC_CSR_NE_MAX_PKT_OFS 19 + +/* Device Config Register ---------------------------------------------------*/ +#define UDC_DEVCFG_ADDR 0x400 + +#define UDC_DEVCFG_SOFTRESET 31 +#define UDC_DEVCFG_HNPSFEN 30 +#define UDC_DEVCFG_DMARST 29 +#define UDC_DEVCFG_SET_DESC 18 +#define UDC_DEVCFG_CSR_PRG 17 +#define UDC_DEVCFG_STATUS 7 +#define UDC_DEVCFG_DIR 6 +#define UDC_DEVCFG_PI 5 +#define UDC_DEVCFG_SS 4 +#define UDC_DEVCFG_SP 3 +#define UDC_DEVCFG_RWKP 2 + +#define UDC_DEVCFG_SPD_MASK 0x3 +#define UDC_DEVCFG_SPD_OFS 0 +#define UDC_DEVCFG_SPD_HS 0x0 +#define UDC_DEVCFG_SPD_FS 0x1 +#define UDC_DEVCFG_SPD_LS 0x2 +/*#define UDC_DEVCFG_SPD_FS 0x3*/ + + +/* Device Control Register --------------------------------------------------*/ +#define UDC_DEVCTL_ADDR 0x404 + +#define UDC_DEVCTL_THLEN_MASK 0xff000000 +#define UDC_DEVCTL_THLEN_OFS 24 + +#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000 +#define UDC_DEVCTL_BRLEN_OFS 16 + +#define UDC_DEVCTL_CSR_DONE 13 +#define UDC_DEVCTL_DEVNAK 12 +#define UDC_DEVCTL_SD 10 +#define UDC_DEVCTL_MODE 9 +#define UDC_DEVCTL_BREN 8 +#define UDC_DEVCTL_THE 7 +#define UDC_DEVCTL_BF 6 +#define UDC_DEVCTL_BE 5 +#define UDC_DEVCTL_DU 4 +#define UDC_DEVCTL_TDE 3 +#define UDC_DEVCTL_RDE 2 +#define UDC_DEVCTL_RES 0 + + +/* Device Status Register ---------------------------------------------------*/ +#define UDC_DEVSTS_ADDR 0x408 + +#define UDC_DEVSTS_TS_MASK 0xfffc0000 +#define UDC_DEVSTS_TS_OFS 18 + +#define UDC_DEVSTS_SESSVLD 17 +#define UDC_DEVSTS_PHY_ERROR 16 +#define UDC_DEVSTS_RXFIFO_EMPTY 15 + +#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000 +#define UDC_DEVSTS_ENUM_SPEED_OFS 13 +#define UDC_DEVSTS_ENUM_SPEED_FULL 1 +#define UDC_DEVSTS_ENUM_SPEED_HIGH 0 + +#define UDC_DEVSTS_SUSP 12 + +#define UDC_DEVSTS_ALT_MASK 0x00000f00 +#define UDC_DEVSTS_ALT_OFS 8 + +#define UDC_DEVSTS_INTF_MASK 0x000000f0 +#define UDC_DEVSTS_INTF_OFS 4 + +#define UDC_DEVSTS_CFG_MASK 0x0000000f +#define UDC_DEVSTS_CFG_OFS 0 + + +/* Device Interrupt Register ------------------------------------------------*/ +#define UDC_DEVINT_ADDR 0x40c + +#define UDC_DEVINT_SVC 7 +#define UDC_DEVINT_ENUM 6 +#define UDC_DEVINT_SOF 5 +#define UDC_DEVINT_US 4 +#define UDC_DEVINT_UR 3 +#define UDC_DEVINT_ES 2 +#define UDC_DEVINT_SI 1 +#define UDC_DEVINT_SC 0 + +/* Device Interrupt Mask Register -------------------------------------------*/ +#define UDC_DEVINT_MSK_ADDR 0x410 + +#define UDC_DEVINT_MSK 0x7f + +/* Endpoint Interrupt Register ----------------------------------------------*/ +#define UDC_EPINT_ADDR 0x414 + +#define UDC_EPINT_OUT_MASK 0xffff0000 +#define UDC_EPINT_OUT_OFS 16 +#define UDC_EPINT_IN_MASK 0x0000ffff +#define UDC_EPINT_IN_OFS 0 + +#define UDC_EPINT_IN_EP0 0 +#define UDC_EPINT_IN_EP1 1 +#define UDC_EPINT_IN_EP2 2 +#define UDC_EPINT_IN_EP3 3 +#define UDC_EPINT_OUT_EP0 16 +#define UDC_EPINT_OUT_EP1 17 +#define UDC_EPINT_OUT_EP2 18 +#define UDC_EPINT_OUT_EP3 19 + +#define UDC_EPINT_EP0_ENABLE_MSK 0x001e001e + +/* Endpoint Interrupt Mask Register -----------------------------------------*/ +#define UDC_EPINT_MSK_ADDR 0x418 + +#define UDC_EPINT_OUT_MSK_MASK 0xffff0000 +#define UDC_EPINT_OUT_MSK_OFS 16 +#define UDC_EPINT_IN_MSK_MASK 0x0000ffff +#define UDC_EPINT_IN_MSK_OFS 0 + +#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff +/* mask non-EP0 endpoints */ +#define UDC_EPDATAINT_MSK_DISABLE 0xfffefffe +/* mask all dev interrupts */ +#define UDC_DEV_MSK_DISABLE 0x7f + +/* Endpoint-specific CSR's --------------------------------------------------*/ +#define UDC_EPREGS_ADDR 0x0 +#define UDC_EPIN_REGS_ADDR 0x0 +#define UDC_EPOUT_REGS_ADDR 0x200 + +#define UDC_EPCTL_ADDR 0x0 + +#define UDC_EPCTL_RRDY 9 +#define UDC_EPCTL_CNAK 8 +#define UDC_EPCTL_SNAK 7 +#define UDC_EPCTL_NAK 6 + +#define UDC_EPCTL_ET_MASK 0x00000030 +#define UDC_EPCTL_ET_OFS 4 +#define UDC_EPCTL_ET_CONTROL 0 +#define UDC_EPCTL_ET_ISO 1 +#define UDC_EPCTL_ET_BULK 2 +#define UDC_EPCTL_ET_INTERRUPT 3 + +#define UDC_EPCTL_P 3 +#define UDC_EPCTL_SN 2 +#define UDC_EPCTL_F 1 +#define UDC_EPCTL_S 0 + +/* Endpoint Status Registers ------------------------------------------------*/ +#define UDC_EPSTS_ADDR 0x4 + +#define UDC_EPSTS_RX_PKT_SIZE_MASK 0x007ff800 +#define UDC_EPSTS_RX_PKT_SIZE_OFS 11 + +#define UDC_EPSTS_TDC 10 +#define UDC_EPSTS_HE 9 +#define UDC_EPSTS_BNA 7 +#define UDC_EPSTS_IN 6 + +#define UDC_EPSTS_OUT_MASK 0x00000030 +#define UDC_EPSTS_OUT_OFS 4 +#define UDC_EPSTS_OUT_DATA 1 +#define UDC_EPSTS_OUT_DATA_CLEAR 0x10 +#define UDC_EPSTS_OUT_SETUP 2 +#define UDC_EPSTS_OUT_SETUP_CLEAR 0x20 +#define UDC_EPSTS_OUT_CLEAR 0x30 + +/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers ------*/ +#define UDC_EPIN_BUFF_SIZE_ADDR 0x8 +#define UDC_EPOUT_FRAME_NUMBER_ADDR 0x8 + +#define UDC_EPIN_BUFF_SIZE_MASK 0x0000ffff +#define UDC_EPIN_BUFF_SIZE_OFS 0 +/* EP0in txfifo = 128 bytes*/ +#define UDC_EPIN0_BUFF_SIZE 32 +/* EP0in fullspeed txfifo = 128 bytes*/ +#define UDC_FS_EPIN0_BUFF_SIZE 32 + +/* fifo size mult = fifo size / max packet */ +#define UDC_EPIN_BUFF_SIZE_MULT 2 + +/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */ +#define UDC_EPIN_BUFF_SIZE 256 +/* EPin small INT data fifo size = 128 bytes */ +#define UDC_EPIN_SMALLINT_BUFF_SIZE 32 + +/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */ +#define UDC_FS_EPIN_BUFF_SIZE 32 + +#define UDC_EPOUT_FRAME_NUMBER_MASK 0x0000ffff +#define UDC_EPOUT_FRAME_NUMBER_OFS 0 + +/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/ +#define UDC_EPOUT_BUFF_SIZE_ADDR 0x0c +#define UDC_EP_MAX_PKT_SIZE_ADDR 0x0c + +#define UDC_EPOUT_BUFF_SIZE_MASK 0xffff0000 +#define UDC_EPOUT_BUFF_SIZE_OFS 16 +#define UDC_EP_MAX_PKT_SIZE_MASK 0x0000ffff +#define UDC_EP_MAX_PKT_SIZE_OFS 0 +/* EP0in max packet size = 64 bytes */ +#define UDC_EP0IN_MAX_PKT_SIZE 64 +/* EP0out max packet size = 64 bytes */ +#define UDC_EP0OUT_MAX_PKT_SIZE 64 +/* EP0in fullspeed max packet size = 64 bytes */ +#define UDC_FS_EP0IN_MAX_PKT_SIZE 64 +/* EP0out fullspeed max packet size = 64 bytes */ +#define UDC_FS_EP0OUT_MAX_PKT_SIZE 64 + +/* + * Endpoint dma descriptors ------------------------------------------------ + * + * Setup data, Status dword + */ +#define UDC_DMA_STP_STS_CFG_MASK 0x0fff0000 +#define UDC_DMA_STP_STS_CFG_OFS 16 +#define UDC_DMA_STP_STS_CFG_ALT_MASK 0x000f0000 +#define UDC_DMA_STP_STS_CFG_ALT_OFS 16 +#define UDC_DMA_STP_STS_CFG_INTF_MASK 0x00f00000 +#define UDC_DMA_STP_STS_CFG_INTF_OFS 20 +#define UDC_DMA_STP_STS_CFG_NUM_MASK 0x0f000000 +#define UDC_DMA_STP_STS_CFG_NUM_OFS 24 +#define UDC_DMA_STP_STS_RX_MASK 0x30000000 +#define UDC_DMA_STP_STS_RX_OFS 28 +#define UDC_DMA_STP_STS_BS_MASK 0xc0000000 +#define UDC_DMA_STP_STS_BS_OFS 30 +#define UDC_DMA_STP_STS_BS_HOST_READY 0 +#define UDC_DMA_STP_STS_BS_DMA_BUSY 1 +#define UDC_DMA_STP_STS_BS_DMA_DONE 2 +#define UDC_DMA_STP_STS_BS_HOST_BUSY 3 +/* IN data, Status dword */ +#define UDC_DMA_IN_STS_TXBYTES_MASK 0x0000ffff +#define UDC_DMA_IN_STS_TXBYTES_OFS 0 +#define UDC_DMA_IN_STS_FRAMENUM_MASK 0x07ff0000 +#define UDC_DMA_IN_STS_FRAMENUM_OFS 0 +#define UDC_DMA_IN_STS_L 27 +#define UDC_DMA_IN_STS_TX_MASK 0x30000000 +#define UDC_DMA_IN_STS_TX_OFS 28 +#define UDC_DMA_IN_STS_BS_MASK 0xc0000000 +#define UDC_DMA_IN_STS_BS_OFS 30 +#define UDC_DMA_IN_STS_BS_HOST_READY 0 +#define UDC_DMA_IN_STS_BS_DMA_BUSY 1 +#define UDC_DMA_IN_STS_BS_DMA_DONE 2 +#define UDC_DMA_IN_STS_BS_HOST_BUSY 3 +/* OUT data, Status dword */ +#define UDC_DMA_OUT_STS_RXBYTES_MASK 0x0000ffff +#define UDC_DMA_OUT_STS_RXBYTES_OFS 0 +#define UDC_DMA_OUT_STS_FRAMENUM_MASK 0x07ff0000 +#define UDC_DMA_OUT_STS_FRAMENUM_OFS 0 +#define UDC_DMA_OUT_STS_L 27 +#define UDC_DMA_OUT_STS_RX_MASK 0x30000000 +#define UDC_DMA_OUT_STS_RX_OFS 28 +#define UDC_DMA_OUT_STS_BS_MASK 0xc0000000 +#define UDC_DMA_OUT_STS_BS_OFS 30 +#define UDC_DMA_OUT_STS_BS_HOST_READY 0 +#define UDC_DMA_OUT_STS_BS_DMA_BUSY 1 +#define UDC_DMA_OUT_STS_BS_DMA_DONE 2 +#define UDC_DMA_OUT_STS_BS_HOST_BUSY 3 +/* max ep0in packet */ +#define UDC_EP0IN_MAXPACKET 1000 +/* max dma packet */ +#define UDC_DMA_MAXPACKET 65536 + +/* un-usable DMA address */ +#define DMA_DONT_USE (~(dma_addr_t) 0 ) + +/* other Endpoint register addresses and values-----------------------------*/ +#define UDC_EP_SUBPTR_ADDR 0x10 +#define UDC_EP_DESPTR_ADDR 0x14 +#define UDC_EP_WRITE_CONFIRM_ADDR 0x1c + +/* EP number as layouted in AHB space */ +#define UDC_EP_NUM 32 +#define UDC_EPIN_NUM 16 +#define UDC_EPIN_NUM_USED 5 +#define UDC_EPOUT_NUM 16 +/* EP number of EP's really used = EP0 + 8 data EP's */ +#define UDC_USED_EP_NUM 9 +/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */ +#define UDC_CSR_EP_OUT_IX_OFS 12 + +#define UDC_EP0OUT_IX 16 +#define UDC_EP0IN_IX 0 + +/* Rx fifo address and size = 1k -------------------------------------------*/ +#define UDC_RXFIFO_ADDR 0x800 +#define UDC_RXFIFO_SIZE 0x400 + +/* Tx fifo address and size = 1.5k -----------------------------------------*/ +#define UDC_TXFIFO_ADDR 0xc00 +#define UDC_TXFIFO_SIZE 0x600 + +/* default data endpoints --------------------------------------------------*/ +#define UDC_EPIN_STATUS_IX 1 +#define UDC_EPIN_IX 2 +#define UDC_EPOUT_IX 18 + +/* general constants -------------------------------------------------------*/ +#define UDC_DWORD_BYTES 4 +#define UDC_BITS_PER_BYTE_SHIFT 3 +#define UDC_BYTE_MASK 0xff +#define UDC_BITS_PER_BYTE 8 + +/*---------------------------------------------------------------------------*/ +/* UDC CSR's */ +struct udc_csrs { + + /* sca - setup command address */ + u32 sca; + + /* ep ne's */ + u32 ne[UDC_USED_EP_NUM]; +} __attribute__ ((packed)); + +/* AHB subsystem CSR registers */ +struct udc_regs { + + /* device configuration */ + u32 cfg; + + /* device control */ + u32 ctl; + + /* device status */ + u32 sts; + + /* device interrupt */ + u32 irqsts; + + /* device interrupt mask */ + u32 irqmsk; + + /* endpoint interrupt */ + u32 ep_irqsts; + + /* endpoint interrupt mask */ + u32 ep_irqmsk; +} __attribute__ ((packed)); + +/* endpoint specific registers */ +struct udc_ep_regs { + + /* endpoint control */ + u32 ctl; + + /* endpoint status */ + u32 sts; + + /* endpoint buffer size in/ receive packet frame number out */ + u32 bufin_framenum; + + /* endpoint buffer size out/max packet size */ + u32 bufout_maxpkt; + + /* endpoint setup buffer pointer */ + u32 subptr; + + /* endpoint data descriptor pointer */ + u32 desptr; + + /* reserverd */ + u32 reserved; + + /* write/read confirmation */ + u32 confirm; + +} __attribute__ ((packed)); + +/* control data DMA desc */ +struct udc_stp_dma { + /* status quadlet */ + u32 status; + /* reserved */ + u32 _reserved; + /* first setup word */ + u32 data12; + /* second setup word */ + u32 data34; +} __attribute__ ((aligned (16))); + +/* normal data DMA desc */ +struct udc_data_dma { + /* status quadlet */ + u32 status; + /* reserved */ + u32 _reserved; + /* buffer pointer */ + u32 bufptr; + /* next descriptor pointer */ + u32 next; +} __attribute__ ((aligned (16))); + +/* request packet */ +struct udc_request { + /* embedded gadget ep */ + struct usb_request req; + + /* flags */ + unsigned dma_going : 1, + dma_mapping : 1, + dma_done : 1; + /* phys. address */ + dma_addr_t td_phys; + /* first dma desc. of chain */ + struct udc_data_dma *td_data; + /* last dma desc. of chain */ + struct udc_data_dma *td_data_last; + struct list_head queue; + + /* chain length */ + unsigned chain_len; + +}; + +/* UDC specific endpoint parameters */ +struct udc_ep { + struct usb_ep ep; + struct udc_ep_regs __iomem *regs; + u32 __iomem *txfifo; + u32 __iomem *dma; + dma_addr_t td_phys; + dma_addr_t td_stp_dma; + struct udc_stp_dma *td_stp; + struct udc_data_dma *td; + /* temp request */ + struct udc_request *req; + unsigned req_used; + unsigned req_completed; + /* dummy DMA desc for BNA dummy */ + struct udc_request *bna_dummy_req; + unsigned bna_occurred; + + /* NAK state */ + unsigned naking; + + struct udc *dev; + + /* queue for requests */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned halted; + unsigned cancel_transfer; + unsigned num : 5, + fifo_depth : 14, + in : 1; +}; + +/* device struct */ +struct udc { + struct usb_gadget gadget; + spinlock_t lock; /* protects all state */ + /* all endpoints */ + struct udc_ep ep[UDC_EP_NUM]; + struct usb_gadget_driver *driver; + /* operational flags */ + unsigned active : 1, + stall_ep0in : 1, + waiting_zlp_ack_ep0in : 1, + set_cfg_not_acked : 1, + irq_registered : 1, + data_ep_enabled : 1, + data_ep_queued : 1, + mem_region : 1, + sys_suspended : 1, + connected; + + u16 chiprev; + + /* registers */ + struct pci_dev *pdev; + struct udc_csrs __iomem *csr; + struct udc_regs __iomem *regs; + struct udc_ep_regs __iomem *ep_regs; + u32 __iomem *rxfifo; + u32 __iomem *txfifo; + + /* DMA desc pools */ + struct pci_pool *data_requests; + struct pci_pool *stp_requests; + + /* device data */ + unsigned long phys_addr; + void __iomem *virt_addr; + unsigned irq; + + /* states */ + u16 cur_config; + u16 cur_intf; + u16 cur_alt; +}; + +/* setup request data */ +union udc_setup_data { + u32 data[2]; + struct usb_ctrlrequest request; +}; + +/* + *--------------------------------------------------------------------------- + * SET and GET bitfields in u32 values + * via constants for mask/offset: + * <bit_field_stub_name> is the text between + * UDC_ and _MASK|_OFS of appropiate + * constant + * + * set bitfield value in u32 u32Val + */ +#define AMD_ADDBITS(u32Val, bitfield_val, bitfield_stub_name) \ + (((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK)))) \ + | (((bitfield_val) << ((u32) bitfield_stub_name##_OFS)) \ + & ((u32) bitfield_stub_name##_MASK))) + +/* + * set bitfield value in zero-initialized u32 u32Val + * => bitfield bits in u32Val are all zero + */ +#define AMD_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name) \ + ((u32Val) \ + | (((bitfield_val) << ((u32) bitfield_stub_name##_OFS)) \ + & ((u32) bitfield_stub_name##_MASK))) + +/* get bitfield value from u32 u32Val */ +#define AMD_GETBITS(u32Val, bitfield_stub_name) \ + ((u32Val & ((u32) bitfield_stub_name##_MASK)) \ + >> ((u32) bitfield_stub_name##_OFS)) + +/* SET and GET bits in u32 values ------------------------------------------*/ +#define AMD_BIT(bit_stub_name) (1 << bit_stub_name) +#define AMD_UNMASK_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name)) +#define AMD_CLEAR_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name)) + +/* debug macros ------------------------------------------------------------*/ + +#define DBG(udc , args...) dev_dbg(&(udc)->pdev->dev, args) + +#ifdef UDC_VERBOSE +#define VDBG DBG +#else +#define VDBG(udc , args...) do {} while (0) +#endif + +#endif /* #ifdef AMD5536UDC_H */ diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index ba163f35bf2..63d7d656869 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -601,25 +601,6 @@ static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -static void *at91_ep_alloc_buffer( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags) -{ - *dma = ~0; - return kmalloc(bytes, gfp_flags); -} - -static void at91_ep_free_buffer( - struct usb_ep *ep, - void *buf, - dma_addr_t dma, - unsigned bytes) -{ - kfree(buf); -} - static int at91_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { @@ -788,8 +769,6 @@ static const struct usb_ep_ops at91_ep_ops = { .disable = at91_ep_disable, .alloc_request = at91_ep_alloc_request, .free_request = at91_ep_free_request, - .alloc_buffer = at91_ep_alloc_buffer, - .free_buffer = at91_ep_free_buffer, .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index fcb5526cb08..f2fbdc7fe37 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -497,38 +497,6 @@ dummy_free_request (struct usb_ep *_ep, struct usb_request *_req) kfree (req); } -static void * -dummy_alloc_buffer ( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t mem_flags -) { - char *retval; - struct dummy_ep *ep; - struct dummy *dum; - - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); - - if (!dum->driver) - return NULL; - retval = kmalloc (bytes, mem_flags); - *dma = (dma_addr_t) retval; - return retval; -} - -static void -dummy_free_buffer ( - struct usb_ep *_ep, - void *buf, - dma_addr_t dma, - unsigned bytes -) { - if (bytes) - kfree (buf); -} - static void fifo_complete (struct usb_ep *ep, struct usb_request *req) { @@ -659,10 +627,6 @@ static const struct usb_ep_ops dummy_ep_ops = { .alloc_request = dummy_alloc_request, .free_request = dummy_free_request, - .alloc_buffer = dummy_alloc_buffer, - .free_buffer = dummy_free_buffer, - /* map, unmap, ... eventually hook the "generic" dma calls */ - .queue = dummy_queue, .dequeue = dummy_dequeue, @@ -1784,8 +1748,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd) spin_lock_irq (&dum->lock); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { - dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n"); - rc = -ENODEV; + rc = -ESHUTDOWN; } else { dum->rh_state = DUMMY_RH_RUNNING; set_link_state (dum); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 325bf7cfb83..a3376739a81 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -277,7 +277,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif -#ifdef CONFIG_USB_GADGET_HUSB2DEV +#ifdef CONFIG_USB_GADGET_ATMEL_USBA #define DEV_CONFIG_CDC #endif @@ -292,7 +292,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_SUBSET #endif -#ifdef CONFIG_USB_GADGET_SH +#ifdef CONFIG_USB_GADGET_SUPERH #define DEV_CONFIG_SUBSET #endif @@ -301,6 +301,14 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_SUBSET #endif +#ifdef CONFIG_USB_GADGET_M66592 +#define DEV_CONFIG_CDC +#endif + +#ifdef CONFIG_USB_GADGET_AMD5536UDC +#define DEV_CONFIG_CDC +#endif + /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c6b6479fa4d..be7a1bd2823 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -686,7 +686,6 @@ struct fsg_dev { int thread_wakeup_needed; struct completion thread_notifier; struct task_struct *thread_task; - sigset_t thread_signal_mask; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; @@ -3277,8 +3276,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Clear the existing signals. Anything but SIGUSR1 is converted * into a high-priority EXIT exception. */ for (;;) { - sig = dequeue_signal_lock(current, &fsg->thread_signal_mask, - &info); + sig = dequeue_signal_lock(current, ¤t->blocked, &info); if (!sig) break; if (sig != SIGUSR1) { @@ -3431,10 +3429,13 @@ static int fsg_main_thread(void *fsg_) /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ - siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) | - sigmask(SIGTERM) | sigmask(SIGKILL) | - sigmask(SIGUSR1)); - sigprocmask(SIG_SETMASK, &fsg->thread_signal_mask, NULL); + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); /* Arrange for userspace references to be interpreted as kernel * pointers. That way we can pass a kernel pointer to a routine @@ -3735,19 +3736,12 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } /* Free the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if (bh->buf) - usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma, - mod_data.buflen); - } + for (i = 0; i < NUM_BUFFERS; ++i) + kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ if (req) { - if (req->buf) - usb_ep_free_buffer(fsg->ep0, req->buf, - req->dma, EP0_BUFSIZE); + kfree(req->buf); usb_ep_free_request(fsg->ep0, req); } @@ -3965,8 +3959,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) #endif if (gadget->is_otg) { - otg_desc.bmAttributes |= USB_OTG_HNP, - config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + otg_desc.bmAttributes |= USB_OTG_HNP; } rc = -ENOMEM; @@ -3975,8 +3968,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); if (!req) goto out; - req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE, - &req->dma, GFP_KERNEL); + req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); if (!req->buf) goto out; req->complete = ep0_complete; @@ -3988,8 +3980,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Allocate for the bulk-in endpoint. We assume that * the buffer will also work with the bulk-out (and * interrupt-in) endpoint. */ - bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen, - &bh->dma, GFP_KERNEL); + bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); if (!bh->buf) goto out; bh->next = bh + 1; diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 3ca2b3159f0..10b2b33b869 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -228,7 +228,7 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Config PHY interface */ portctrl = fsl_readl(&dr_regs->portsc1); - portctrl &= ~(PORTSCX_PHY_TYPE_SEL & PORTSCX_PORT_WIDTH); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); switch (udc->phy_mode) { case FSL_USB2_PHY_ULPI: portctrl |= PORTSCX_PTS_ULPI; @@ -601,39 +601,6 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -/*------------------------------------------------------------------ - * Allocate an I/O buffer -*---------------------------------------------------------------------*/ -static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - struct fsl_ep *ep; - - if (!_ep) - return NULL; - - ep = container_of(_ep, struct fsl_ep, ep); - - return dma_alloc_coherent(ep->udc->gadget.dev.parent, - bytes, dma, gfp_flags); -} - -/*------------------------------------------------------------------ - * frees an i/o buffer -*---------------------------------------------------------------------*/ -static void fsl_free_buffer(struct usb_ep *_ep, void *buf, - dma_addr_t dma, unsigned bytes) -{ - struct fsl_ep *ep; - - if (!_ep) - return; - - ep = container_of(_ep, struct fsl_ep, ep); - - dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); -} - /*-------------------------------------------------------------------------*/ static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { @@ -1047,9 +1014,6 @@ static struct usb_ep_ops fsl_ep_ops = { .alloc_request = fsl_alloc_request, .free_request = fsl_free_request, - .alloc_buffer = fsl_alloc_buffer, - .free_buffer = fsl_free_buffer, - .queue = fsl_ep_queue, .dequeue = fsl_ep_dequeue, @@ -2189,27 +2153,19 @@ static void fsl_udc_release(struct device *dev) * init resource for globle controller * Return the udc handle on success or NULL on failure ------------------------------------------------------------------*/ -static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) +static int __init struct_udc_setup(struct fsl_udc *udc, + struct platform_device *pdev) { - struct fsl_udc *udc; struct fsl_usb2_platform_data *pdata; size_t size; - udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); - if (udc == NULL) { - ERR("malloc udc failed\n"); - return NULL; - } - pdata = pdev->dev.platform_data; udc->phy_mode = pdata->phy_mode; - /* max_ep_nr is bidirectional ep number, max_ep doubles the number */ - udc->max_ep = pdata->max_ep_nr * 2; udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); if (!udc->eps) { ERR("malloc fsl_ep failed\n"); - goto cleanup; + return -1; } /* initialized QHs, take care of alignment */ @@ -2225,7 +2181,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) if (!udc->ep_qh) { ERR("malloc QHs for udc failed\n"); kfree(udc->eps); - goto cleanup; + return -1; } udc->ep_qh_size = size; @@ -2244,11 +2200,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) udc->remote_wakeup = 0; /* default to 0 on reset */ spin_lock_init(&udc->lock); - return udc; - -cleanup: - kfree(udc); - return NULL; + return 0; } /*---------------------------------------------------------------- @@ -2287,35 +2239,37 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, } /* Driver probe function - * all intialize operations implemented here except enabling usb_intr reg + * all intialization operations implemented here except enabling usb_intr reg + * board setup should have been done in the platform code */ static int __init fsl_udc_probe(struct platform_device *pdev) { struct resource *res; int ret = -ENODEV; unsigned int i; + u32 dccparams; if (strcmp(pdev->name, driver_name)) { VDBG("Wrong device\n"); return -ENODEV; } - /* board setup should have been done in the platform code */ - - /* Initialize the udc structure including QH member and other member */ - udc_controller = struct_udc_setup(pdev); - if (!udc_controller) { - VDBG("udc_controller is NULL \n"); + udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); + if (udc_controller == NULL) { + ERR("malloc udc failed\n"); return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + if (!res) { + kfree(udc_controller); return -ENXIO; + } if (!request_mem_region(res->start, res->end - res->start + 1, driver_name)) { ERR("request mem region for %s failed \n", pdev->name); + kfree(udc_controller); return -EBUSY; } @@ -2328,13 +2282,24 @@ static int __init fsl_udc_probe(struct platform_device *pdev) usb_sys_regs = (struct usb_sys_interface *) ((u32)dr_regs + USB_DR_SYS_OFFSET); + /* Read Device Controller Capability Parameters register */ + dccparams = fsl_readl(&dr_regs->dccparams); + if (!(dccparams & DCCPARAMS_DC)) { + ERR("This SOC doesn't support device role\n"); + ret = -ENODEV; + goto err2; + } + /* Get max device endpoints */ + /* DEN is bidirectional ep number, max_ep doubles the number */ + udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; + udc_controller->irq = platform_get_irq(pdev, 0); if (!udc_controller->irq) { ret = -ENODEV; goto err2; } - ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ, + ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); if (ret != 0) { ERR("cannot request irq %d err %d \n", @@ -2342,6 +2307,13 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err2; } + /* Initialize the udc structure including QH member and other member */ + if (struct_udc_setup(udc_controller, pdev)) { + ERR("Can't initialize udc data structure\n"); + ret = -ENOMEM; + goto err3; + } + /* initialize usb hw reg except for regs for EP, * leave usbintr reg untouched */ dr_controller_setup(udc_controller); @@ -2403,6 +2375,7 @@ err2: iounmap(dr_regs); err1: release_mem_region(res->start, res->end - res->start + 1); + kfree(udc_controller); return ret; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index c6291e04650..832ab82b488 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -101,6 +101,10 @@ struct usb_sys_interface { #define WAIT_FOR_OUT_STATUS 3 #define DATA_STATE_RECV 4 +/* Device Controller Capability Parameter register */ +#define DCCPARAMS_DC 0x00000080 +#define DCCPARAMS_DEN_MASK 0x0000001f + /* Frame Index Register Bit Masks */ #define USB_FRINDEX_MASKS 0x3fff /* USB CMD Register Bit Masks */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index d041b919e7b..f7f159c1002 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -8,6 +8,8 @@ * (And avoiding all runtime comparisons in typical one-choice configs!) * * NOTE: some of these controller drivers may not be available yet. + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. */ #ifdef CONFIG_USB_GADGET_NET2280 #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) @@ -15,6 +17,12 @@ #define gadget_is_net2280(g) 0 #endif +#ifdef CONFIG_USB_GADGET_AMD5536UDC +#define gadget_is_amd5536udc(g) !strcmp("amd5536udc", (g)->name) +#else +#define gadget_is_amd5536udc(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_DUMMY_HCD #define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) #else @@ -33,12 +41,14 @@ #define gadget_is_goku(g) 0 #endif +/* SH3 UDC -- not yet ported 2.4 --> 2.6 */ #ifdef CONFIG_USB_GADGET_SUPERH #define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) #else #define gadget_is_sh(g) 0 #endif +/* not yet stable on 2.6 (would help "original Zaurus") */ #ifdef CONFIG_USB_GADGET_SA1100 #define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) #else @@ -51,6 +61,7 @@ #define gadget_is_lh7a40x(g) 0 #endif +/* handhelds.org tree (?) */ #ifdef CONFIG_USB_GADGET_MQ11XX #define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) #else @@ -63,22 +74,24 @@ #define gadget_is_omap(g) 0 #endif +/* not yet ported 2.4 --> 2.6 */ #ifdef CONFIG_USB_GADGET_N9604 #define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) #else #define gadget_is_n9604(g) 0 #endif +/* various unstable versions available */ #ifdef CONFIG_USB_GADGET_PXA27X #define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) #else #define gadget_is_pxa27x(g) 0 #endif -#ifdef CONFIG_USB_GADGET_HUSB2DEV -#define gadget_is_husb2dev(g) !strcmp("husb2_udc", (g)->name) +#ifdef CONFIG_USB_GADGET_ATMEL_USBA +#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name) #else -#define gadget_is_husb2dev(g) 0 +#define gadget_is_atmel_usba(g) 0 #endif #ifdef CONFIG_USB_GADGET_S3C2410 @@ -93,6 +106,7 @@ #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 @@ -106,6 +120,7 @@ #endif /* Mentor high speed function controller */ +/* from Montavista kernel (?) */ #ifdef CONFIG_USB_GADGET_MUSBHSFC #define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) #else @@ -119,12 +134,20 @@ #define gadget_is_musbhdrc(g) 0 #endif +/* from Montavista kernel (?) */ #ifdef CONFIG_USB_GADGET_MPC8272 #define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) #else #define gadget_is_mpc8272(g) 0 #endif +#ifdef CONFIG_USB_GADGET_M66592 +#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name) +#else +#define gadget_is_m66592(g) 0 +#endif + + // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 // ... @@ -181,9 +204,13 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x16; else if (gadget_is_mpc8272(gadget)) return 0x17; - else if (gadget_is_husb2dev(gadget)) + else if (gadget_is_atmel_usba(gadget)) return 0x18; else if (gadget_is_fsl_usb2(gadget)) return 0x19; + else if (gadget_is_amd5536udc(gadget)) + return 0x20; + else if (gadget_is_m66592(gadget)) + return 0x21; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index d08a8d0e642..1c5aa49d743 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -1248,17 +1248,11 @@ autoconf_fail: tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); /* preallocate control response and buffer */ - dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); if (!dev->req) { err = -ENOMEM; goto fail; } - dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ, - &dev->req->dma, GFP_KERNEL); - if (!dev->req->buf) { - err = -ENOMEM; - goto fail; - } dev->req->complete = gmidi_setup_complete; diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index ae931af05ce..349b8166f34 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -20,7 +20,6 @@ * - DMA works with ep1 (OUT transfers) and ep2 (IN transfers). */ -#undef DEBUG // #define VERBOSE /* extra debug messages (success too) */ // #define USB_TRACE /* packet-level success messages */ @@ -296,51 +295,6 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* allocating buffers this way eliminates dma mapping overhead, which - * on some platforms will mean eliminating a per-io buffer copy. with - * some kinds of system caches, further tweaks may still be needed. - */ -static void * -goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - void *retval; - struct goku_ep *ep; - - ep = container_of(_ep, struct goku_ep, ep); - if (!_ep) - return NULL; - *dma = DMA_ADDR_INVALID; - - if (ep->dma) { - /* the main problem with this call is that it wastes memory - * on typical 1/N page allocations: it allocates 1-N pages. - */ -#warning Using dma_alloc_coherent even with buffers smaller than a page. - retval = dma_alloc_coherent(&ep->dev->pdev->dev, - bytes, dma, gfp_flags); - } else - retval = kmalloc(bytes, gfp_flags); - return retval; -} - -static void -goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) -{ - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct goku_ep *ep; - - ep = container_of(_ep, struct goku_ep, ep); - if (!_ep) - return; - dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma); - } else - kfree (buf); -} - -/*-------------------------------------------------------------------------*/ - static void done(struct goku_ep *ep, struct goku_request *req, int status) { @@ -485,7 +439,7 @@ top: /* use ep1/ep2 double-buffering for OUT */ if (!(size & PACKET_ACTIVE)) size = readl(®s->EPxSizeLB[ep->num]); - if (!(size & PACKET_ACTIVE)) // "can't happen" + if (!(size & PACKET_ACTIVE)) /* "can't happen" */ break; size &= DATASIZE; /* EPxSizeH == 0 */ @@ -1026,9 +980,6 @@ static struct usb_ep_ops goku_ep_ops = { .alloc_request = goku_alloc_request, .free_request = goku_free_request, - .alloc_buffer = goku_alloc_buffer, - .free_buffer = goku_free_buffer, - .queue = goku_queue, .dequeue = goku_dequeue, @@ -1140,17 +1091,17 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, is_usb_connected ? ((tmp & PW_PULLUP) ? "full speed" : "powered") : "disconnected", - ({char *tmp; + ({char *state; switch(dev->ep0state){ - case EP0_DISCONNECT: tmp = "ep0_disconnect"; break; - case EP0_IDLE: tmp = "ep0_idle"; break; - case EP0_IN: tmp = "ep0_in"; break; - case EP0_OUT: tmp = "ep0_out"; break; - case EP0_STATUS: tmp = "ep0_status"; break; - case EP0_STALL: tmp = "ep0_stall"; break; - case EP0_SUSPEND: tmp = "ep0_suspend"; break; - default: tmp = "ep0_?"; break; - } tmp; }) + case EP0_DISCONNECT: state = "ep0_disconnect"; break; + case EP0_IDLE: state = "ep0_idle"; break; + case EP0_IN: state = "ep0_in"; break; + case EP0_OUT: state = "ep0_out"; break; + case EP0_STATUS: state = "ep0_status"; break; + case EP0_STALL: state = "ep0_stall"; break; + case EP0_SUSPEND: state = "ep0_suspend"; break; + default: state = "ep0_?"; break; + } state; }) ); size -= t; next += t; @@ -1195,7 +1146,6 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, for (i = 0; i < 4; i++) { struct goku_ep *ep = &dev->ep [i]; struct goku_request *req; - int t; if (i && !ep->desc) continue; @@ -1283,7 +1233,7 @@ done: static void udc_reinit (struct goku_udc *dev) { static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" }; - + unsigned i; INIT_LIST_HEAD (&dev->gadget.ep_list); @@ -1827,14 +1777,13 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* alloc, and start init */ - dev = kmalloc (sizeof *dev, GFP_KERNEL); + dev = kzalloc (sizeof *dev, GFP_KERNEL); if (dev == NULL){ pr_debug("enomem %s\n", pci_name(pdev)); retval = -ENOMEM; goto done; } - memset(dev, 0, sizeof *dev); spin_lock_init(&dev->lock); dev->pdev = pdev; dev->gadget.ops = &goku_ops; @@ -1896,9 +1845,9 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* done */ the_controller = dev; - device_register(&dev->gadget.dev); - - return 0; + retval = device_register(&dev->gadget.dev); + if (retval == 0) + return 0; done: if (dev) @@ -1910,8 +1859,8 @@ done: /*-------------------------------------------------------------------------*/ static struct pci_device_id pci_ids [] = { { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, .vendor = 0x102f, /* Toshiba */ .device = 0x0107, /* this UDC */ .subvendor = PCI_ANY_ID, diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index ea8c8e58cab..bc4eb1e0b50 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -41,8 +41,10 @@ struct goku_udc_regs { #define INT_SYSERROR 0x40000 #define INT_PWRDETECT 0x80000 -#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND) -#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK) +#define INT_DEVWIDE \ + (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND) +#define INT_EP0 \ + (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK) u32 dma_master; #define MST_EOPB_DIS 0x0800 @@ -231,7 +233,7 @@ struct goku_request { enum ep0state { EP0_DISCONNECT, /* no host */ EP0_IDLE, /* between STATUS ack and SETUP report */ - EP0_IN, EP0_OUT, /* data stage */ + EP0_IN, EP0_OUT, /* data stage */ EP0_STATUS, /* status stage */ EP0_STALL, /* data or status stages */ EP0_SUSPEND, /* usb suspend */ @@ -242,7 +244,7 @@ struct goku_udc { struct usb_gadget gadget; spinlock_t lock; struct goku_ep ep[4]; - struct usb_gadget_driver *driver; + struct usb_gadget_driver *driver; enum ep0state ep0state; unsigned got_irq:1, diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 46d0e525274..e60745ffaf8 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -37,7 +37,7 @@ #include <linux/device.h> #include <linux/moduleparam.h> -#include <linux/usb_gadgetfs.h> +#include <linux/usb/gadgetfs.h> #include <linux/usb_gadget.h> @@ -923,7 +923,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req) struct dev_data *dev = ep->driver_data; if (req->buf != dev->rbuf) { - usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + kfree(req->buf); req->buf = dev->rbuf; req->dma = DMA_ADDR_INVALID; } @@ -963,7 +963,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len) return -EBUSY; } if (len > sizeof (dev->rbuf)) - req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC); + req->buf = kmalloc(len, GFP_ATOMIC); if (req->buf == 0) { req->buf = dev->rbuf; return -ENOMEM; @@ -1505,7 +1505,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } break; -#ifndef CONFIG_USB_GADGETFS_PXA2XX +#ifndef CONFIG_USB_GADGET_PXA2XX /* PXA automagically handles this request too */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != 0x80) diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index a0a73c08a34..e78c2ddc1f8 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -75,10 +75,6 @@ static int lh7a40x_ep_enable(struct usb_ep *ep, static int lh7a40x_ep_disable(struct usb_ep *ep); static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t); static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *); -static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *, - gfp_t); -static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t, - unsigned); static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t); static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *); static int lh7a40x_set_halt(struct usb_ep *ep, int); @@ -104,9 +100,6 @@ static struct usb_ep_ops lh7a40x_ep_ops = { .alloc_request = lh7a40x_alloc_request, .free_request = lh7a40x_free_request, - .alloc_buffer = lh7a40x_alloc_buffer, - .free_buffer = lh7a40x_free_buffer, - .queue = lh7a40x_queue, .dequeue = lh7a40x_dequeue, @@ -1134,26 +1127,6 @@ static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req) kfree(req); } -static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes, - dma_addr_t * dma, gfp_t gfp_flags) -{ - char *retval; - - DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags); - - retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); - if (retval) - *dma = virt_to_bus(retval); - return retval; -} - -static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma, - unsigned bytes) -{ - DEBUG("%s, %p\n", __FUNCTION__, ep); - kfree(buf); -} - /** Queue one request * Kickstart transfer if needed * NOTE: Sets INDEX register diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c new file mode 100644 index 00000000000..700dda8a915 --- /dev/null +++ b/drivers/usb/gadget/m66592-udc.c @@ -0,0 +1,1633 @@ +/* + * M66592 UDC (USB gadget) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include <linux/usb/ch9.h> +#include <linux/usb_gadget.h> + +#include "m66592-udc.h" + + +MODULE_DESCRIPTION("M66592 USB gadget driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); + +#define DRIVER_VERSION "29 May 2007" + +/* module parameters */ +static unsigned short clock = M66592_XTAL24; +module_param(clock, ushort, 0644); +MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 " + "(default=16384)"); + +static unsigned short vif = M66592_LDRV; +module_param(vif, ushort, 0644); +MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0 (default=32768)"); + +static unsigned short endian; +module_param(endian, ushort, 0644); +MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)"); + +static unsigned short irq_sense = M66592_INTL; +module_param(irq_sense, ushort, 0644); +MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0 " + "(default=2)"); + +static const char udc_name[] = "m66592_udc"; +static const char *m66592_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7" +}; + +static void disable_controller(struct m66592 *m66592); +static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req); +static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req); +static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags); + +static void transfer_complete(struct m66592_ep *ep, + struct m66592_request *req, int status); + +/*-------------------------------------------------------------------------*/ +static inline u16 get_usb_speed(struct m66592 *m66592) +{ + return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST); +} + +static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, + M66592_INTENB0); + m66592_bset(m66592, (1 << pipenum), reg); + m66592_write(m66592, tmp, M66592_INTENB0); +} + +static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, + M66592_INTENB0); + m66592_bclr(m66592, (1 << pipenum), reg); + m66592_write(m66592, tmp, M66592_INTENB0); +} + +static void m66592_usb_connect(struct m66592 *m66592) +{ + m66592_bset(m66592, M66592_CTRE, M66592_INTENB0); + m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, + M66592_INTENB0); + m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); + + m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG); +} + +static void m66592_usb_disconnect(struct m66592 *m66592) +__releases(m66592->lock) +__acquires(m66592->lock) +{ + m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0); + m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, + M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + + m66592->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&m66592->lock); + m66592->driver->disconnect(&m66592->gadget); + spin_lock(&m66592->lock); + + disable_controller(m66592); + INIT_LIST_HEAD(&m66592->ep[0].queue); +} + +static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum) +{ + u16 pid = 0; + unsigned long offset; + + if (pipenum == 0) + pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID; + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + pid = m66592_read(m66592, offset) & M66592_PID; + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return pid; +} + +static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum, + u16 pid) +{ + unsigned long offset; + + if (pipenum == 0) + m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + m66592_mdfy(m66592, pid, M66592_PID, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); +} + +static inline void pipe_start(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_BUF); +} + +static inline void pipe_stop(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_NAK); +} + +static inline void pipe_stall(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_STALL); +} + +static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum) +{ + u16 ret = 0; + unsigned long offset; + + if (pipenum == 0) + ret = m66592_read(m66592, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + ret = m66592_read(m66592, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return ret; +} + +static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(m66592, pipenum); + + if (pipenum == 0) + m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + m66592_bset(m66592, M66592_SQCLR, offset); + } else + printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); +} + +static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum) +{ + u16 tmp; + int size; + + if (pipenum == 0) { + tmp = m66592_read(m66592, M66592_DCPCFG); + if ((tmp & M66592_CNTMD) != 0) + size = 256; + else { + tmp = m66592_read(m66592, M66592_DCPMAXP); + size = tmp & M66592_MAXP; + } + } else { + m66592_write(m66592, pipenum, M66592_PIPESEL); + tmp = m66592_read(m66592, M66592_PIPECFG); + if ((tmp & M66592_CNTMD) != 0) { + tmp = m66592_read(m66592, M66592_PIPEBUF); + size = ((tmp >> 10) + 1) * 64; + } else { + tmp = m66592_read(m66592, M66592_PIPEMAXP); + size = tmp & M66592_MXPS; + } + } + + return size; +} + +static inline void pipe_change(struct m66592 *m66592, u16 pipenum) +{ + struct m66592_ep *ep = m66592->pipenum2ep[pipenum]; + + if (ep->use_dma) + return; + + m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel); + + ndelay(450); + + m66592_bset(m66592, M66592_MBW, ep->fifosel); +} + +static int pipe_buffer_setting(struct m66592 *m66592, + struct m66592_pipe_info *info) +{ + u16 bufnum = 0, buf_bsize = 0; + u16 pipecfg = 0; + + if (info->pipe == 0) + return -EINVAL; + + m66592_write(m66592, info->pipe, M66592_PIPESEL); + + if (info->dir_in) + pipecfg |= M66592_DIR; + pipecfg |= info->type; + pipecfg |= info->epnum; + switch (info->type) { + case M66592_INT: + bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT); + buf_bsize = 0; + break; + case M66592_BULK: + bufnum = m66592->bi_bufnum + + (info->pipe - M66592_BASE_PIPENUM_BULK) * 16; + m66592->bi_bufnum += 16; + buf_bsize = 7; + pipecfg |= M66592_DBLB; + if (!info->dir_in) + pipecfg |= M66592_SHTNAK; + break; + case M66592_ISO: + bufnum = m66592->bi_bufnum + + (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16; + m66592->bi_bufnum += 16; + buf_bsize = 7; + break; + } + if (m66592->bi_bufnum > M66592_MAX_BUFNUM) { + printk(KERN_ERR "m66592 pipe memory is insufficient(%d)\n", + m66592->bi_bufnum); + return -ENOMEM; + } + + m66592_write(m66592, pipecfg, M66592_PIPECFG); + m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF); + m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP); + if (info->interval) + info->interval--; + m66592_write(m66592, info->interval, M66592_PIPEPERI); + + return 0; +} + +static void pipe_buffer_release(struct m66592 *m66592, + struct m66592_pipe_info *info) +{ + if (info->pipe == 0) + return; + + switch (info->type) { + case M66592_BULK: + if (is_bulk_pipe(info->pipe)) + m66592->bi_bufnum -= 16; + break; + case M66592_ISO: + if (is_isoc_pipe(info->pipe)) + m66592->bi_bufnum -= 16; + break; + } + + if (is_bulk_pipe(info->pipe)) { + m66592->bulk--; + } else if (is_interrupt_pipe(info->pipe)) + m66592->interrupt--; + else if (is_isoc_pipe(info->pipe)) { + m66592->isochronous--; + if (info->type == M66592_BULK) + m66592->bulk--; + } else + printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", + info->pipe); +} + +static void pipe_initialize(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + + m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel); + + m66592_write(m66592, M66592_ACLRM, ep->pipectr); + m66592_write(m66592, 0, ep->pipectr); + m66592_write(m66592, M66592_SQCLR, ep->pipectr); + if (ep->use_dma) { + m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel); + + ndelay(450); + + m66592_bset(m66592, M66592_MBW, ep->fifosel); + } +} + +static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, + const struct usb_endpoint_descriptor *desc, + u16 pipenum, int dma) +{ + if ((pipenum != 0) && dma) { + if (m66592->num_dma == 0) { + m66592->num_dma++; + ep->use_dma = 1; + ep->fifoaddr = M66592_D0FIFO; + ep->fifosel = M66592_D0FIFOSEL; + ep->fifoctr = M66592_D0FIFOCTR; + ep->fifotrn = M66592_D0FIFOTRN; + } else if (m66592->num_dma == 1) { + m66592->num_dma++; + ep->use_dma = 1; + ep->fifoaddr = M66592_D1FIFO; + ep->fifosel = M66592_D1FIFOSEL; + ep->fifoctr = M66592_D1FIFOCTR; + ep->fifotrn = M66592_D1FIFOTRN; + } else { + ep->use_dma = 0; + ep->fifoaddr = M66592_CFIFO; + ep->fifosel = M66592_CFIFOSEL; + ep->fifoctr = M66592_CFIFOCTR; + ep->fifotrn = 0; + } + } else { + ep->use_dma = 0; + ep->fifoaddr = M66592_CFIFO; + ep->fifosel = M66592_CFIFOSEL; + ep->fifoctr = M66592_CFIFOCTR; + ep->fifotrn = 0; + } + + ep->pipectr = get_pipectr_addr(pipenum); + ep->pipenum = pipenum; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + m66592->pipenum2ep[pipenum] = ep; + m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; + INIT_LIST_HEAD(&ep->queue); +} + +static void m66592_ep_release(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + u16 pipenum = ep->pipenum; + + if (pipenum == 0) + return; + + if (ep->use_dma) + m66592->num_dma--; + ep->pipenum = 0; + ep->busy = 0; + ep->use_dma = 0; +} + +static int alloc_pipe_config(struct m66592_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct m66592 *m66592 = ep->m66592; + struct m66592_pipe_info info; + int dma = 0; + int *counter; + int ret; + + ep->desc = desc; + + BUG_ON(ep->pipenum); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (m66592->bulk >= M66592_MAX_NUM_BULK) { + if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { + printk(KERN_ERR "bulk pipe is insufficient\n"); + return -ENODEV; + } else { + info.pipe = M66592_BASE_PIPENUM_ISOC + + m66592->isochronous; + counter = &m66592->isochronous; + } + } else { + info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk; + counter = &m66592->bulk; + } + info.type = M66592_BULK; + dma = 1; + break; + case USB_ENDPOINT_XFER_INT: + if (m66592->interrupt >= M66592_MAX_NUM_INT) { + printk(KERN_ERR "interrupt pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt; + info.type = M66592_INT; + counter = &m66592->interrupt; + break; + case USB_ENDPOINT_XFER_ISOC: + if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { + printk(KERN_ERR "isochronous pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous; + info.type = M66592_ISO; + counter = &m66592->isochronous; + break; + default: + printk(KERN_ERR "unexpect xfer type\n"); + return -EINVAL; + } + ep->type = info.type; + + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.interval = desc->bInterval; + if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + info.dir_in = 1; + else + info.dir_in = 0; + + ret = pipe_buffer_setting(m66592, &info); + if (ret < 0) { + printk(KERN_ERR "pipe_buffer_setting fail\n"); + return ret; + } + + (*counter)++; + if ((counter == &m66592->isochronous) && info.type == M66592_BULK) + m66592->bulk++; + + m66592_ep_setting(m66592, ep, desc, info.pipe, dma); + pipe_initialize(ep); + + return 0; +} + +static int free_pipe_config(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + struct m66592_pipe_info info; + + info.pipe = ep->pipenum; + info.type = ep->type; + pipe_buffer_release(m66592, &info); + m66592_ep_release(ep); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum) +{ + enable_irq_ready(m66592, pipenum); + enable_irq_nrdy(m66592, pipenum); +} + +static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum) +{ + disable_irq_ready(m66592, pipenum); + disable_irq_nrdy(m66592, pipenum); +} + +/* if complete is true, gadget driver complete function is not call */ +static void control_end(struct m66592 *m66592, unsigned ccpl) +{ + m66592->ep[0].internal_ccpl = ccpl; + pipe_start(m66592, 0); + m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR); +} + +static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, ep->pipenum); + m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0, + (M66592_ISEL | M66592_CURPIPE), + M66592_CFIFOSEL); + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + if (req->req.length == 0) { + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + pipe_start(m66592, 0); + transfer_complete(ep, req, 0); + } else { + m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS); + irq_ep0_write(ep, req); + } +} + +static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + u16 tmp; + + pipe_change(m66592, ep->pipenum); + disable_irq_empty(m66592, ep->pipenum); + pipe_start(m66592, ep->pipenum); + + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) + pipe_irq_enable(m66592, ep->pipenum); + else + irq_packet_write(ep, req); +} + +static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + u16 pipenum = ep->pipenum; + + if (ep->pipenum == 0) { + m66592_mdfy(m66592, M66592_PIPE0, + (M66592_ISEL | M66592_CURPIPE), + M66592_CFIFOSEL); + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + pipe_start(m66592, pipenum); + pipe_irq_enable(m66592, pipenum); + } else { + if (ep->use_dma) { + m66592_bset(m66592, M66592_TRCLR, ep->fifosel); + pipe_change(m66592, pipenum); + m66592_bset(m66592, M66592_TRENB, ep->fifosel); + m66592_write(m66592, + (req->req.length + ep->ep.maxpacket - 1) + / ep->ep.maxpacket, + ep->fifotrn); + } + pipe_start(m66592, pipenum); /* trigger once */ + pipe_irq_enable(m66592, pipenum); + } +} + +static void start_packet(struct m66592_ep *ep, struct m66592_request *req) +{ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + start_packet_write(ep, req); + else + start_packet_read(ep, req); +} + +static void start_ep0(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 ctsq; + + ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ; + + switch (ctsq) { + case M66592_CS_RDDS: + start_ep0_write(ep, req); + break; + case M66592_CS_WRDS: + start_packet_read(ep, req); + break; + + case M66592_CS_WRND: + control_end(ep->m66592, 0); + break; + default: + printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static void init_controller(struct m66592 *m66592) +{ + m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND), + M66592_PINCFG); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG); + + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + + msleep(3); + + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + + msleep(1); + + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + + m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); + m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, + M66592_DMA0CFG); +} + +static void disable_controller(struct m66592 *m66592) +{ + m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); +} + +static void m66592_start_xclock(struct m66592 *m66592) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_XCKE)) + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); +} + +/*-------------------------------------------------------------------------*/ +static void transfer_complete(struct m66592_ep *ep, + struct m66592_request *req, int status) +__releases(m66592->lock) +__acquires(m66592->lock) +{ + int restart = 0; + + if (unlikely(ep->pipenum == 0)) { + if (ep->internal_ccpl) { + ep->internal_ccpl = 0; + return; + } + } + + list_del_init(&req->queue); + if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + if (!list_empty(&ep->queue)) + restart = 1; + + spin_unlock(&ep->m66592->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->m66592->lock); + + if (restart) { + req = list_entry(ep->queue.next, struct m66592_request, queue); + if (ep->desc) + start_packet(ep, req); + } +} + +static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req) +{ + int i; + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, pipenum); + m66592_bset(m66592, M66592_ISEL, ep->fifosel); + + i = 0; + do { + tmp = m66592_read(m66592, ep->fifoctr); + if (i++ > 100000) { + printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + "conflict. please power off this controller."); + return; + } + ndelay(1); + } while ((tmp & M66592_FRDY) == 0); + + /* prepare parameters */ + bufsize = get_buffer_size(m66592, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + if (size > 0) + m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(m66592, pipenum); + disable_irq_empty(m66592, pipenum); + } else { + disable_irq_ready(m66592, pipenum); + enable_irq_empty(m66592, pipenum); + } + pipe_start(m66592, pipenum); +} + +static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, pipenum); + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) { + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + return; + } + + /* prepare parameters */ + bufsize = get_buffer_size(m66592, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + if ((size == 0) + || ((size % ep->ep.maxpacket) != 0) + || ((bufsize != ep->ep.maxpacket) + && (bufsize > size))) + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(m66592, pipenum); + enable_irq_empty(m66592, pipenum); + } else { + disable_irq_empty(m66592, pipenum); + pipe_irq_enable(m66592, pipenum); + } +} + +static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 tmp; + int rcv_len, bufsize, req_len; + int size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + int finish = 0; + + pipe_change(m66592, pipenum); + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) { + req->req.status = -EPIPE; + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + printk(KERN_ERR "read fifo not ready"); + return; + } + + /* prepare parameters */ + rcv_len = tmp & M66592_DTLN; + bufsize = get_buffer_size(m66592, pipenum); + + buf = req->req.buf + req->req.actual; + req_len = req->req.length - req->req.actual; + if (rcv_len < bufsize) + size = min(rcv_len, req_len); + else + size = min(bufsize, req_len); + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + finish = 1; + } + + /* read fifo */ + if (req->req.buf) { + if (size == 0) + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + else + m66592_read_fifo(m66592, ep->fifoaddr, buf, size); + } + + if ((ep->pipenum != 0) && finish) + transfer_complete(ep, req, 0); +} + +static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb) +{ + u16 check; + u16 pipenum; + struct m66592_ep *ep; + struct m66592_request *req; + + if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) { + m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS); + m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE, + M66592_CFIFOSEL); + + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + irq_packet_read(ep, req); + } else { + for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + m66592_write(m66592, ~check, M66592_BRDYSTS); + ep = m66592->pipenum2ep[pipenum]; + req = list_entry(ep->queue.next, + struct m66592_request, queue); + if (ep->desc->bEndpointAddress & USB_DIR_IN) + irq_packet_write(ep, req); + else + irq_packet_read(ep, req); + } + } + } +} + +static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb) +{ + u16 tmp; + u16 check; + u16 pipenum; + struct m66592_ep *ep; + struct m66592_request *req; + + if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) { + m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS); + + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + irq_ep0_write(ep, req); + } else { + for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + m66592_write(m66592, ~check, M66592_BEMPSTS); + tmp = control_reg_get(m66592, pipenum); + if ((tmp & M66592_INBUFM) == 0) { + disable_irq_empty(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + pipe_stop(m66592, pipenum); + ep = m66592->pipenum2ep[pipenum]; + req = list_entry(ep->queue.next, + struct m66592_request, + queue); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, 0); + } + } + } + } +} + +static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +__releases(m66592->lock) +__acquires(m66592->lock) +{ + struct m66592_ep *ep; + u16 pid; + u16 status = 0; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pid = control_reg_get_pid(m66592, ep->pipenum); + if (pid == M66592_PID_STALL) + status = 1 << USB_ENDPOINT_HALT; + else + status = 0; + break; + default: + pipe_stall(m66592, 0); + return; /* exit */ + } + + m66592->ep0_data = cpu_to_le16(status); + m66592->ep0_req->buf = &m66592->ep0_data; + m66592->ep0_req->length = 2; + /* AV: what happens if we get called again before that gets through? */ + spin_unlock(&m66592->lock); + m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL); + spin_lock(&m66592->lock); +} + +static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(m66592, 1); + break; + case USB_RECIP_INTERFACE: + control_end(m66592, 1); + break; + case USB_RECIP_ENDPOINT: { + struct m66592_ep *ep; + struct m66592_request *req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pipe_stop(m66592, ep->pipenum); + control_reg_sqclr(m66592, ep->pipenum); + + control_end(m66592, 1); + + req = list_entry(ep->queue.next, + struct m66592_request, queue); + if (ep->busy) { + ep->busy = 0; + if (list_empty(&ep->queue)) + break; + start_packet(ep, req); + } else if (!list_empty(&ep->queue)) + pipe_start(m66592, ep->pipenum); + } + break; + default: + pipe_stall(m66592, 0); + break; + } +} + +static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(m66592, 1); + break; + case USB_RECIP_INTERFACE: + control_end(m66592, 1); + break; + case USB_RECIP_ENDPOINT: { + struct m66592_ep *ep; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pipe_stall(m66592, ep->pipenum); + + control_end(m66592, 1); + } + break; + default: + pipe_stall(m66592, 0); + break; + } +} + +/* if return value is true, call class driver's setup() */ +static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + u16 *p = (u16 *)ctrl; + unsigned long offset = M66592_USBREQ; + int i, ret = 0; + + /* read fifo */ + m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0); + + for (i = 0; i < 4; i++) + p[i] = m66592_read(m66592, offset + i*2); + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(m66592, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(m66592, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(m66592, ctrl); + break; + default: + ret = 1; + break; + } + } else + ret = 1; + return ret; +} + +static void m66592_update_usb_speed(struct m66592 *m66592) +{ + u16 speed = get_usb_speed(m66592); + + switch (speed) { + case M66592_HSMODE: + m66592->gadget.speed = USB_SPEED_HIGH; + break; + case M66592_FSMODE: + m66592->gadget.speed = USB_SPEED_FULL; + break; + default: + m66592->gadget.speed = USB_SPEED_UNKNOWN; + printk(KERN_ERR "USB speed unknown\n"); + } +} + +static void irq_device_state(struct m66592 *m66592) +{ + u16 dvsq; + + dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ; + m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0); + + if (dvsq == M66592_DS_DFLT) { /* bus reset */ + m66592->driver->disconnect(&m66592->gadget); + m66592_update_usb_speed(m66592); + } + if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG) + m66592_update_usb_speed(m66592); + if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS) + && m66592->gadget.speed == USB_SPEED_UNKNOWN) + m66592_update_usb_speed(m66592); + + m66592->old_dvsq = dvsq; +} + +static void irq_control_stage(struct m66592 *m66592) +__releases(m66592->lock) +__acquires(m66592->lock) +{ + struct usb_ctrlrequest ctrl; + u16 ctsq; + + ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ; + m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0); + + switch (ctsq) { + case M66592_CS_IDST: { + struct m66592_ep *ep; + struct m66592_request *req; + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + transfer_complete(ep, req, 0); + } + break; + + case M66592_CS_RDDS: + case M66592_CS_WRDS: + case M66592_CS_WRND: + if (setup_packet(m66592, &ctrl)) { + spin_unlock(&m66592->lock); + if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0) + pipe_stall(m66592, 0); + spin_lock(&m66592->lock); + } + break; + case M66592_CS_RDSS: + case M66592_CS_WRSS: + control_end(m66592, 0); + break; + default: + printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static irqreturn_t m66592_irq(int irq, void *_m66592) +{ + struct m66592 *m66592 = _m66592; + u16 intsts0; + u16 intenb0; + u16 brdysts, nrdysts, bempsts; + u16 brdyenb, nrdyenb, bempenb; + u16 savepipe; + u16 mask0; + + spin_lock(&m66592->lock); + + intsts0 = m66592_read(m66592, M66592_INTSTS0); + intenb0 = m66592_read(m66592, M66592_INTENB0); + + savepipe = m66592_read(m66592, M66592_CFIFOSEL); + + mask0 = intsts0 & intenb0; + if (mask0) { + brdysts = m66592_read(m66592, M66592_BRDYSTS); + nrdysts = m66592_read(m66592, M66592_NRDYSTS); + bempsts = m66592_read(m66592, M66592_BEMPSTS); + brdyenb = m66592_read(m66592, M66592_BRDYENB); + nrdyenb = m66592_read(m66592, M66592_NRDYENB); + bempenb = m66592_read(m66592, M66592_BEMPENB); + + if (mask0 & M66592_VBINT) { + m66592_write(m66592, 0xffff & ~M66592_VBINT, + M66592_INTSTS0); + m66592_start_xclock(m66592); + + /* start vbus sampling */ + m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0) + & M66592_VBSTS; + m66592->scount = M66592_MAX_SAMPLING; + + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + if (intsts0 & M66592_DVSQ) + irq_device_state(m66592); + + if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE) + && (brdysts & brdyenb)) { + irq_pipe_ready(m66592, brdysts, brdyenb); + } + if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE) + && (bempsts & bempenb)) { + irq_pipe_empty(m66592, bempsts, bempenb); + } + + if (intsts0 & M66592_CTRT) + irq_control_stage(m66592); + } + + m66592_write(m66592, savepipe, M66592_CFIFOSEL); + + spin_unlock(&m66592->lock); + return IRQ_HANDLED; +} + +static void m66592_timer(unsigned long _m66592) +{ + struct m66592 *m66592 = (struct m66592 *)_m66592; + unsigned long flags; + u16 tmp; + + spin_lock_irqsave(&m66592->lock, flags); + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_RCKE)) { + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + udelay(10); + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + } + if (m66592->scount > 0) { + tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS; + if (tmp == m66592->old_vbus) { + m66592->scount--; + if (m66592->scount == 0) { + if (tmp == M66592_VBSTS) + m66592_usb_connect(m66592); + else + m66592_usb_disconnect(m66592); + } else { + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + } else { + m66592->scount = M66592_MAX_SAMPLING; + m66592->old_vbus = tmp; + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + } + spin_unlock_irqrestore(&m66592->lock, flags); +} + +/*-------------------------------------------------------------------------*/ +static int m66592_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct m66592_ep *ep; + + ep = container_of(_ep, struct m66592_ep, ep); + return alloc_pipe_config(ep, desc); +} + +static int m66592_disable(struct usb_ep *_ep) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct m66592_request, queue); + spin_lock_irqsave(&ep->m66592->lock, flags); + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->m66592->lock, flags); + } + + pipe_irq_disable(ep->m66592, ep->pipenum); + return free_pipe_config(ep); +} + +static struct usb_request *m66592_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct m66592_request *req; + + req = kzalloc(sizeof(struct m66592_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct m66592_request *req; + + req = container_of(_req, struct m66592_request, req); + kfree(req); +} + +static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct m66592_ep, ep); + req = container_of(_req, struct m66592_request, req); + + if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->m66592->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (ep->desc == 0) /* control */ + start_ep0(ep, req); + else { + if (request && !ep->busy) + start_packet(ep, req); + } + + spin_unlock_irqrestore(&ep->m66592->lock, flags); + + return 0; +} + +static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + req = container_of(_req, struct m66592_request, req); + + spin_lock_irqsave(&ep->m66592->lock, flags); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->m66592->lock, flags); + + return 0; +} + +static int m66592_set_halt(struct usb_ep *_ep, int value) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct m66592_ep, ep); + req = list_entry(ep->queue.next, struct m66592_request, queue); + + spin_lock_irqsave(&ep->m66592->lock, flags); + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + if (value) { + ep->busy = 1; + pipe_stall(ep->m66592, ep->pipenum); + } else { + ep->busy = 0; + pipe_stop(ep->m66592, ep->pipenum); + } + +out: + spin_unlock_irqrestore(&ep->m66592->lock, flags); + return ret; +} + +static void m66592_fifo_flush(struct usb_ep *_ep) +{ + struct m66592_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + spin_lock_irqsave(&ep->m66592->lock, flags); + if (list_empty(&ep->queue) && !ep->busy) { + pipe_stop(ep->m66592, ep->pipenum); + m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr); + } + spin_unlock_irqrestore(&ep->m66592->lock, flags); +} + +static struct usb_ep_ops m66592_ep_ops = { + .enable = m66592_enable, + .disable = m66592_disable, + + .alloc_request = m66592_alloc_request, + .free_request = m66592_free_request, + + .queue = m66592_queue, + .dequeue = m66592_dequeue, + + .set_halt = m66592_set_halt, + .fifo_flush = m66592_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ +static struct m66592 *the_controller; + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct m66592 *m66592 = the_controller; + int retval; + + if (!driver + || driver->speed != USB_SPEED_HIGH + || !driver->bind + || !driver->setup) + return -EINVAL; + if (!m66592) + return -ENODEV; + if (m66592->driver) + return -EBUSY; + + /* hook up the driver */ + driver->driver.bus = NULL; + m66592->driver = driver; + m66592->gadget.dev.driver = &driver->driver; + + retval = device_add(&m66592->gadget.dev); + if (retval) { + printk(KERN_ERR "device_add error (%d)\n", retval); + goto error; + } + + retval = driver->bind (&m66592->gadget); + if (retval) { + printk(KERN_ERR "bind to driver error (%d)\n", retval); + device_del(&m66592->gadget.dev); + goto error; + } + + m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); + if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) { + m66592_start_xclock(m66592); + /* start vbus sampling */ + m66592->old_vbus = m66592_read(m66592, + M66592_INTSTS0) & M66592_VBSTS; + m66592->scount = M66592_MAX_SAMPLING; + mod_timer(&m66592->timer, jiffies + msecs_to_jiffies(50)); + } + + return 0; + +error: + m66592->driver = NULL; + m66592->gadget.dev.driver = NULL; + + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct m66592 *m66592 = the_controller; + unsigned long flags; + + if (driver != m66592->driver || !driver->unbind) + return -EINVAL; + + spin_lock_irqsave(&m66592->lock, flags); + if (m66592->gadget.speed != USB_SPEED_UNKNOWN) + m66592_usb_disconnect(m66592); + spin_unlock_irqrestore(&m66592->lock, flags); + + m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); + + driver->unbind(&m66592->gadget); + + init_controller(m66592); + disable_controller(m66592); + + device_del(&m66592->gadget.dev); + m66592->driver = NULL; + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ +static int m66592_get_frame(struct usb_gadget *_gadget) +{ + struct m66592 *m66592 = gadget_to_m66592(_gadget); + return m66592_read(m66592, M66592_FRMNUM) & 0x03FF; +} + +static struct usb_gadget_ops m66592_gadget_ops = { + .get_frame = m66592_get_frame, +}; + +static int __exit m66592_remove(struct platform_device *pdev) +{ + struct m66592 *m66592 = dev_get_drvdata(&pdev->dev); + + del_timer_sync(&m66592->timer); + iounmap(m66592->reg); + free_irq(platform_get_irq(pdev, 0), m66592); + m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); + kfree(m66592); + return 0; +} + +static void nop_completion(struct usb_ep *ep, struct usb_request *r) +{ +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init m66592_probe(struct platform_device *pdev) +{ + struct resource *res; + int irq; + void __iomem *reg = NULL; + struct m66592 *m66592 = NULL; + int ret = 0; + int i; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (char *)udc_name); + if (!res) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_resource_byname error.\n"); + goto clean_up; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_irq error.\n"); + goto clean_up; + } + + reg = ioremap(res->start, resource_len(res)); + if (reg == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "ioremap error.\n"); + goto clean_up; + } + + /* initialize ucd */ + m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); + if (m66592 == NULL) { + printk(KERN_ERR "kzalloc error\n"); + goto clean_up; + } + + spin_lock_init(&m66592->lock); + dev_set_drvdata(&pdev->dev, m66592); + + m66592->gadget.ops = &m66592_gadget_ops; + device_initialize(&m66592->gadget.dev); + strcpy(m66592->gadget.dev.bus_id, "gadget"); + m66592->gadget.is_dualspeed = 1; + m66592->gadget.dev.parent = &pdev->dev; + m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; + m66592->gadget.dev.release = pdev->dev.release; + m66592->gadget.name = udc_name; + + init_timer(&m66592->timer); + m66592->timer.function = m66592_timer; + m66592->timer.data = (unsigned long)m66592; + m66592->reg = reg; + + m66592->bi_bufnum = M66592_BASE_BUFNUM; + + ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + udc_name, m66592); + if (ret < 0) { + printk(KERN_ERR "request_irq error (%d)\n", ret); + goto clean_up; + } + + INIT_LIST_HEAD(&m66592->gadget.ep_list); + m66592->gadget.ep0 = &m66592->ep[0].ep; + INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); + for (i = 0; i < M66592_MAX_NUM_PIPE; i++) { + struct m66592_ep *ep = &m66592->ep[i]; + + if (i != 0) { + INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list); + list_add_tail(&m66592->ep[i].ep.ep_list, + &m66592->gadget.ep_list); + } + ep->m66592 = m66592; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = m66592_ep_name[i]; + ep->ep.ops = &m66592_ep_ops; + ep->ep.maxpacket = 512; + } + m66592->ep[0].ep.maxpacket = 64; + m66592->ep[0].pipenum = 0; + m66592->ep[0].fifoaddr = M66592_CFIFO; + m66592->ep[0].fifosel = M66592_CFIFOSEL; + m66592->ep[0].fifoctr = M66592_CFIFOCTR; + m66592->ep[0].fifotrn = 0; + m66592->ep[0].pipectr = get_pipectr_addr(0); + m66592->pipenum2ep[0] = &m66592->ep[0]; + m66592->epaddr2ep[0] = &m66592->ep[0]; + + the_controller = m66592; + + m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL); + if (m66592->ep0_req == NULL) + goto clean_up2; + m66592->ep0_req->complete = nop_completion; + + init_controller(m66592); + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + return 0; + +clean_up2: + free_irq(irq, m66592); +clean_up: + if (m66592) { + if (m66592->ep0_req) + m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); + kfree(m66592); + } + if (reg) + iounmap(reg); + + return ret; +} + +/*-------------------------------------------------------------------------*/ +static struct platform_driver m66592_driver = { + .remove = __exit_p(m66592_remove), + .driver = { + .name = (char *) udc_name, + }, +}; + +static int __init m66592_udc_init(void) +{ + return platform_driver_probe(&m66592_driver, m66592_probe); +} +module_init(m66592_udc_init); + +static void __exit m66592_udc_cleanup(void) +{ + platform_driver_unregister(&m66592_driver); +} +module_exit(m66592_udc_cleanup); diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h new file mode 100644 index 00000000000..bfa0c645f22 --- /dev/null +++ b/drivers/usb/gadget/m66592-udc.h @@ -0,0 +1,575 @@ +/* + * M66592 UDC (USB gadget) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __M66592_UDC_H__ +#define __M66592_UDC_H__ + +#define M66592_SYSCFG 0x00 +#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */ +#define M66592_XTAL48 0x8000 /* 48MHz */ +#define M66592_XTAL24 0x4000 /* 24MHz */ +#define M66592_XTAL12 0x0000 /* 12MHz */ +#define M66592_XCKE 0x2000 /* b13: External clock enable */ +#define M66592_RCKE 0x1000 /* b12: Register clock enable */ +#define M66592_PLLC 0x0800 /* b11: PLL control */ +#define M66592_SCKE 0x0400 /* b10: USB clock enable */ +#define M66592_ATCKM 0x0100 /* b8: Automatic clock supply */ +#define M66592_HSE 0x0080 /* b7: Hi-speed enable */ +#define M66592_DCFM 0x0040 /* b6: Controller function select */ +#define M66592_DMRPD 0x0020 /* b5: D- pull down control */ +#define M66592_DPRPU 0x0010 /* b4: D+ pull up control */ +#define M66592_FSRPC 0x0004 /* b2: Full-speed receiver enable */ +#define M66592_PCUT 0x0002 /* b1: Low power sleep enable */ +#define M66592_USBE 0x0001 /* b0: USB module operation enable */ + +#define M66592_SYSSTS 0x02 +#define M66592_LNST 0x0003 /* b1-0: D+, D- line status */ +#define M66592_SE1 0x0003 /* SE1 */ +#define M66592_KSTS 0x0002 /* K State */ +#define M66592_JSTS 0x0001 /* J State */ +#define M66592_SE0 0x0000 /* SE0 */ + +#define M66592_DVSTCTR 0x04 +#define M66592_WKUP 0x0100 /* b8: Remote wakeup */ +#define M66592_RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define M66592_USBRST 0x0040 /* b6: USB reset enable */ +#define M66592_RESUME 0x0020 /* b5: Resume enable */ +#define M66592_UACT 0x0010 /* b4: USB bus enable */ +#define M66592_RHST 0x0003 /* b1-0: Reset handshake status */ +#define M66592_HSMODE 0x0003 /* Hi-Speed mode */ +#define M66592_FSMODE 0x0002 /* Full-Speed mode */ +#define M66592_HSPROC 0x0001 /* HS handshake is processing */ + +#define M66592_TESTMODE 0x06 +#define M66592_UTST 0x000F /* b4-0: Test select */ +#define M66592_H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define M66592_H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define M66592_H_TST_K 0x000A /* HOST TEST K */ +#define M66592_H_TST_J 0x0009 /* HOST TEST J */ +#define M66592_H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define M66592_P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define M66592_P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define M66592_P_TST_K 0x0002 /* PERI TEST K */ +#define M66592_P_TST_J 0x0001 /* PERI TEST J */ +#define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +#define M66592_PINCFG 0x0A +#define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */ +#define M66592_BIGEND 0x0100 /* b8: Big endian mode */ + +#define M66592_DMA0CFG 0x0C +#define M66592_DMA1CFG 0x0E +#define M66592_DREQA 0x4000 /* b14: Dreq active select */ +#define M66592_BURST 0x2000 /* b13: Burst mode */ +#define M66592_DACKA 0x0400 /* b10: Dack active select */ +#define M66592_DFORM 0x0380 /* b9-7: DMA mode select */ +#define M66592_CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define M66592_CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define M66592_CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define M66592_SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define M66592_SPLIT_DACK_DSTB 0x0300 /* DACK + DSTB0 mode (SPLIT bus) */ +#define M66592_DENDA 0x0040 /* b6: Dend active select */ +#define M66592_PKTM 0x0020 /* b5: Packet mode */ +#define M66592_DENDE 0x0010 /* b4: Dend enable */ +#define M66592_OBUS 0x0004 /* b2: OUTbus mode */ + +#define M66592_CFIFO 0x10 +#define M66592_D0FIFO 0x14 +#define M66592_D1FIFO 0x18 + +#define M66592_CFIFOSEL 0x1E +#define M66592_D0FIFOSEL 0x24 +#define M66592_D1FIFOSEL 0x2A +#define M66592_RCNT 0x8000 /* b15: Read count mode */ +#define M66592_REW 0x4000 /* b14: Buffer rewind */ +#define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define M66592_DREQE 0x1000 /* b12: DREQ output enable */ +#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO */ +#define M66592_MBW_8 0x0000 /* 8bit */ +#define M66592_MBW_16 0x0400 /* 16bit */ +#define M66592_TRENB 0x0200 /* b9: Transaction counter enable */ +#define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */ +#define M66592_DEZPM 0x0080 /* b7: Zero-length packet mode */ +#define M66592_ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define M66592_CURPIPE 0x0007 /* b2-0: PIPE select */ + +#define M66592_CFIFOCTR 0x20 +#define M66592_D0FIFOCTR 0x26 +#define M66592_D1FIFOCTR 0x2c +#define M66592_BVAL 0x8000 /* b15: Buffer valid flag */ +#define M66592_BCLR 0x4000 /* b14: Buffer clear */ +#define M66592_FRDY 0x2000 /* b13: FIFO ready */ +#define M66592_DTLN 0x0FFF /* b11-0: FIFO received data length */ + +#define M66592_CFIFOSIE 0x22 +#define M66592_TGL 0x8000 /* b15: Buffer toggle */ +#define M66592_SCLR 0x4000 /* b14: Buffer clear */ +#define M66592_SBUSY 0x2000 /* b13: SIE_FIFO busy */ + +#define M66592_D0FIFOTRN 0x28 +#define M66592_D1FIFOTRN 0x2E +#define M66592_TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +#define M66592_INTENB0 0x30 +#define M66592_VBSE 0x8000 /* b15: VBUS interrupt */ +#define M66592_RSME 0x4000 /* b14: Resume interrupt */ +#define M66592_SOFE 0x2000 /* b13: Frame update interrupt */ +#define M66592_DVSE 0x1000 /* b12: Device state transition interrupt */ +#define M66592_CTRE 0x0800 /* b11: Control transfer stage transition irq */ +#define M66592_BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define M66592_NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define M66592_BRDYE 0x0100 /* b8: Buffer ready interrupt */ +#define M66592_URST 0x0080 /* b7: USB reset detected interrupt */ +#define M66592_SADR 0x0040 /* b6: Set address executed interrupt */ +#define M66592_SCFG 0x0020 /* b5: Set configuration executed interrupt */ +#define M66592_SUSP 0x0010 /* b4: Suspend detected interrupt */ +#define M66592_WDST 0x0008 /* b3: Control write data stage completed irq */ +#define M66592_RDST 0x0004 /* b2: Control read data stage completed irq */ +#define M66592_CMPL 0x0002 /* b1: Control transfer complete interrupt */ +#define M66592_SERR 0x0001 /* b0: Sequence error interrupt */ + +#define M66592_INTENB1 0x32 +#define M66592_BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define M66592_DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define M66592_SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define M66592_SACKE 0x0010 /* b4: SETUP ACK interrupt */ +#define M66592_BRDYM 0x0004 /* b2: BRDY clear timing */ +#define M66592_INTL 0x0002 /* b1: Interrupt sense select */ +#define M66592_PCSE 0x0001 /* b0: PCUT enable by CS assert */ + +#define M66592_BRDYENB 0x36 +#define M66592_BRDYSTS 0x46 +#define M66592_BRDY7 0x0080 /* b7: PIPE7 */ +#define M66592_BRDY6 0x0040 /* b6: PIPE6 */ +#define M66592_BRDY5 0x0020 /* b5: PIPE5 */ +#define M66592_BRDY4 0x0010 /* b4: PIPE4 */ +#define M66592_BRDY3 0x0008 /* b3: PIPE3 */ +#define M66592_BRDY2 0x0004 /* b2: PIPE2 */ +#define M66592_BRDY1 0x0002 /* b1: PIPE1 */ +#define M66592_BRDY0 0x0001 /* b1: PIPE0 */ + +#define M66592_NRDYENB 0x38 +#define M66592_NRDYSTS 0x48 +#define M66592_NRDY7 0x0080 /* b7: PIPE7 */ +#define M66592_NRDY6 0x0040 /* b6: PIPE6 */ +#define M66592_NRDY5 0x0020 /* b5: PIPE5 */ +#define M66592_NRDY4 0x0010 /* b4: PIPE4 */ +#define M66592_NRDY3 0x0008 /* b3: PIPE3 */ +#define M66592_NRDY2 0x0004 /* b2: PIPE2 */ +#define M66592_NRDY1 0x0002 /* b1: PIPE1 */ +#define M66592_NRDY0 0x0001 /* b1: PIPE0 */ + +#define M66592_BEMPENB 0x3A +#define M66592_BEMPSTS 0x4A +#define M66592_BEMP7 0x0080 /* b7: PIPE7 */ +#define M66592_BEMP6 0x0040 /* b6: PIPE6 */ +#define M66592_BEMP5 0x0020 /* b5: PIPE5 */ +#define M66592_BEMP4 0x0010 /* b4: PIPE4 */ +#define M66592_BEMP3 0x0008 /* b3: PIPE3 */ +#define M66592_BEMP2 0x0004 /* b2: PIPE2 */ +#define M66592_BEMP1 0x0002 /* b1: PIPE1 */ +#define M66592_BEMP0 0x0001 /* b0: PIPE0 */ + +#define M66592_SOFCFG 0x3C +#define M66592_SOFM 0x000C /* b3-2: SOF palse mode */ +#define M66592_SOF_125US 0x0008 /* SOF OUT 125us uFrame Signal */ +#define M66592_SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define M66592_SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +#define M66592_INTSTS0 0x40 +#define M66592_VBINT 0x8000 /* b15: VBUS interrupt */ +#define M66592_RESM 0x4000 /* b14: Resume interrupt */ +#define M66592_SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define M66592_DVST 0x1000 /* b12: Device state transition */ +#define M66592_CTRT 0x0800 /* b11: Control stage transition */ +#define M66592_BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define M66592_NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define M66592_BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define M66592_VBSTS 0x0080 /* b7: VBUS input port */ +#define M66592_DVSQ 0x0070 /* b6-4: Device state */ +#define M66592_DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define M66592_DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define M66592_DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define M66592_DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define M66592_DS_SUSP 0x0040 /* Suspend */ +#define M66592_DS_CNFG 0x0030 /* Configured */ +#define M66592_DS_ADDS 0x0020 /* Address */ +#define M66592_DS_DFLT 0x0010 /* Default */ +#define M66592_DS_POWR 0x0000 /* Powered */ +#define M66592_DVSQS 0x0030 /* b5-4: Device state */ +#define M66592_VALID 0x0008 /* b3: Setup packet detected flag */ +#define M66592_CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define M66592_CS_SQER 0x0006 /* Sequence error */ +#define M66592_CS_WRND 0x0005 /* Control write nodata status */ +#define M66592_CS_WRSS 0x0004 /* Control write status stage */ +#define M66592_CS_WRDS 0x0003 /* Control write data stage */ +#define M66592_CS_RDSS 0x0002 /* Control read status stage */ +#define M66592_CS_RDDS 0x0001 /* Control read data stage */ +#define M66592_CS_IDST 0x0000 /* Idle or setup stage */ + +#define M66592_INTSTS1 0x42 +#define M66592_BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define M66592_DTCH 0x1000 /* b12: Detach sense interrupt */ +#define M66592_SIGN 0x0020 /* b5: SETUP IGNORE interrupt */ +#define M66592_SACK 0x0010 /* b4: SETUP ACK interrupt */ + +#define M66592_FRMNUM 0x4C +#define M66592_OVRN 0x8000 /* b15: Overrun error */ +#define M66592_CRCE 0x4000 /* b14: Received data error */ +#define M66592_SOFRM 0x0800 /* b11: SOF output mode */ +#define M66592_FRNM 0x07FF /* b10-0: Frame number */ + +#define M66592_UFRMNUM 0x4E +#define M66592_UFRNM 0x0007 /* b2-0: Micro frame number */ + +#define M66592_RECOVER 0x50 +#define M66592_STSRECOV 0x0700 /* Status recovery */ +#define M66592_STSR_HI 0x0400 /* FULL(0) or HI(1) Speed */ +#define M66592_STSR_DEFAULT 0x0100 /* Default state */ +#define M66592_STSR_ADDRESS 0x0200 /* Address state */ +#define M66592_STSR_CONFIG 0x0300 /* Configured state */ +#define M66592_USBADDR 0x007F /* b6-0: USB address */ + +#define M66592_USBREQ 0x54 +#define M66592_bRequest 0xFF00 /* b15-8: bRequest */ +#define M66592_GET_STATUS 0x0000 +#define M66592_CLEAR_FEATURE 0x0100 +#define M66592_ReqRESERVED 0x0200 +#define M66592_SET_FEATURE 0x0300 +#define M66592_ReqRESERVED1 0x0400 +#define M66592_SET_ADDRESS 0x0500 +#define M66592_GET_DESCRIPTOR 0x0600 +#define M66592_SET_DESCRIPTOR 0x0700 +#define M66592_GET_CONFIGURATION 0x0800 +#define M66592_SET_CONFIGURATION 0x0900 +#define M66592_GET_INTERFACE 0x0A00 +#define M66592_SET_INTERFACE 0x0B00 +#define M66592_SYNCH_FRAME 0x0C00 +#define M66592_bmRequestType 0x00FF /* b7-0: bmRequestType */ +#define M66592_bmRequestTypeDir 0x0080 /* b7 : Data direction */ +#define M66592_HOST_TO_DEVICE 0x0000 +#define M66592_DEVICE_TO_HOST 0x0080 +#define M66592_bmRequestTypeType 0x0060 /* b6-5: Type */ +#define M66592_STANDARD 0x0000 +#define M66592_CLASS 0x0020 +#define M66592_VENDOR 0x0040 +#define M66592_bmRequestTypeRecip 0x001F /* b4-0: Recipient */ +#define M66592_DEVICE 0x0000 +#define M66592_INTERFACE 0x0001 +#define M66592_ENDPOINT 0x0002 + +#define M66592_USBVAL 0x56 +#define M66592_wValue 0xFFFF /* b15-0: wValue */ +/* Standard Feature Selector */ +#define M66592_ENDPOINT_HALT 0x0000 +#define M66592_DEVICE_REMOTE_WAKEUP 0x0001 +#define M66592_TEST_MODE 0x0002 +/* Descriptor Types */ +#define M66592_DT_TYPE 0xFF00 +#define M66592_GET_DT_TYPE(v) (((v) & DT_TYPE) >> 8) +#define M66592_DT_DEVICE 0x01 +#define M66592_DT_CONFIGURATION 0x02 +#define M66592_DT_STRING 0x03 +#define M66592_DT_INTERFACE 0x04 +#define M66592_DT_ENDPOINT 0x05 +#define M66592_DT_DEVICE_QUALIFIER 0x06 +#define M66592_DT_OTHER_SPEED_CONFIGURATION 0x07 +#define M66592_DT_INTERFACE_POWER 0x08 +#define M66592_DT_INDEX 0x00FF +#define M66592_CONF_NUM 0x00FF +#define M66592_ALT_SET 0x00FF + +#define M66592_USBINDEX 0x58 +#define M66592_wIndex 0xFFFF /* b15-0: wIndex */ +#define M66592_TEST_SELECT 0xFF00 /* b15-b8: Test Mode */ +#define M66592_TEST_J 0x0100 /* Test_J */ +#define M66592_TEST_K 0x0200 /* Test_K */ +#define M66592_TEST_SE0_NAK 0x0300 /* Test_SE0_NAK */ +#define M66592_TEST_PACKET 0x0400 /* Test_Packet */ +#define M66592_TEST_FORCE_ENABLE 0x0500 /* Test_Force_Enable */ +#define M66592_TEST_STSelectors 0x0600 /* Standard test selectors */ +#define M66592_TEST_Reserved 0x4000 /* Reserved */ +#define M66592_TEST_VSTModes 0xC000 /* Vendor-specific tests */ +#define M66592_EP_DIR 0x0080 /* b7: Endpoint Direction */ +#define M66592_EP_DIR_IN 0x0080 +#define M66592_EP_DIR_OUT 0x0000 + +#define M66592_USBLENG 0x5A +#define M66592_wLength 0xFFFF /* b15-0: wLength */ + +#define M66592_DCPCFG 0x5C +#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode */ +#define M66592_DIR 0x0010 /* b4: Control transfer DIR select */ + +#define M66592_DCPMAXP 0x5E +#define M66592_DEVSEL 0xC000 /* b15-14: Device address select */ +#define M66592_DEVICE_0 0x0000 /* Device address 0 */ +#define M66592_DEVICE_1 0x4000 /* Device address 1 */ +#define M66592_DEVICE_2 0x8000 /* Device address 2 */ +#define M66592_DEVICE_3 0xC000 /* Device address 3 */ +#define M66592_MAXP 0x007F /* b6-0: Maxpacket size of ep0 */ + +#define M66592_DCPCTR 0x60 +#define M66592_BSTS 0x8000 /* b15: Buffer status */ +#define M66592_SUREQ 0x4000 /* b14: Send USB request */ +#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define M66592_CCPL 0x0004 /* b2: control transfer complete */ +#define M66592_PID 0x0003 /* b1-0: Response PID */ +#define M66592_PID_STALL 0x0002 /* STALL */ +#define M66592_PID_BUF 0x0001 /* BUF */ +#define M66592_PID_NAK 0x0000 /* NAK */ + +#define M66592_PIPESEL 0x64 +#define M66592_PIPENM 0x0007 /* b2-0: Pipe select */ +#define M66592_PIPE0 0x0000 /* PIPE 0 */ +#define M66592_PIPE1 0x0001 /* PIPE 1 */ +#define M66592_PIPE2 0x0002 /* PIPE 2 */ +#define M66592_PIPE3 0x0003 /* PIPE 3 */ +#define M66592_PIPE4 0x0004 /* PIPE 4 */ +#define M66592_PIPE5 0x0005 /* PIPE 5 */ +#define M66592_PIPE6 0x0006 /* PIPE 6 */ +#define M66592_PIPE7 0x0007 /* PIPE 7 */ + +#define M66592_PIPECFG 0x66 +#define M66592_TYP 0xC000 /* b15-14: Transfer type */ +#define M66592_ISO 0xC000 /* Isochronous */ +#define M66592_INT 0x8000 /* Interrupt */ +#define M66592_BULK 0x4000 /* Bulk */ +#define M66592_BFRE 0x0400 /* b10: Buffer ready interrupt mode */ +#define M66592_DBLB 0x0200 /* b9: Double buffer mode select */ +#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode */ +#define M66592_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define M66592_DIR 0x0010 /* b4: Transfer direction select */ +#define M66592_DIR_H_OUT 0x0010 /* HOST OUT */ +#define M66592_DIR_P_IN 0x0010 /* PERI IN */ +#define M66592_DIR_H_IN 0x0000 /* HOST IN */ +#define M66592_DIR_P_OUT 0x0000 /* PERI OUT */ +#define M66592_EPNUM 0x000F /* b3-0: Eendpoint number select */ +#define M66592_EP1 0x0001 +#define M66592_EP2 0x0002 +#define M66592_EP3 0x0003 +#define M66592_EP4 0x0004 +#define M66592_EP5 0x0005 +#define M66592_EP6 0x0006 +#define M66592_EP7 0x0007 +#define M66592_EP8 0x0008 +#define M66592_EP9 0x0009 +#define M66592_EP10 0x000A +#define M66592_EP11 0x000B +#define M66592_EP12 0x000C +#define M66592_EP13 0x000D +#define M66592_EP14 0x000E +#define M66592_EP15 0x000F + +#define M66592_PIPEBUF 0x68 +#define M66592_BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define M66592_BUF_SIZE(x) ((((x) / 64) - 1) << 10) +#define M66592_BUFNMB 0x00FF /* b7-0: Pipe buffer number */ + +#define M66592_PIPEMAXP 0x6A +#define M66592_MXPS 0x07FF /* b10-0: Maxpacket size */ + +#define M66592_PIPEPERI 0x6C +#define M66592_IFIS 0x1000 /* b12: ISO in-buffer flush mode */ +#define M66592_IITV 0x0007 /* b2-0: ISO interval */ + +#define M66592_PIPE1CTR 0x70 +#define M66592_PIPE2CTR 0x72 +#define M66592_PIPE3CTR 0x74 +#define M66592_PIPE4CTR 0x76 +#define M66592_PIPE5CTR 0x78 +#define M66592_PIPE6CTR 0x7A +#define M66592_PIPE7CTR 0x7C +#define M66592_BSTS 0x8000 /* b15: Buffer status */ +#define M66592_INBUFM 0x4000 /* b14: IN buffer monitor (PIPE 1-5) */ +#define M66592_ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define M66592_PID 0x0003 /* b1-0: Response PID */ + +#define M66592_INVALID_REG 0x7E + + +#define get_pipectr_addr(pipenum) (M66592_PIPE1CTR + (pipenum - 1) * 2) + +#define M66592_MAX_SAMPLING 10 + +#define M66592_MAX_NUM_PIPE 8 +#define M66592_MAX_NUM_BULK 3 +#define M66592_MAX_NUM_ISOC 2 +#define M66592_MAX_NUM_INT 2 + +#define M66592_BASE_PIPENUM_BULK 3 +#define M66592_BASE_PIPENUM_ISOC 1 +#define M66592_BASE_PIPENUM_INT 6 + +#define M66592_BASE_BUFNUM 6 +#define M66592_MAX_BUFNUM 0x4F + +struct m66592_pipe_info { + u16 pipe; + u16 epnum; + u16 maxpacket; + u16 type; + u16 interval; + u16 dir_in; +}; + +struct m66592_request { + struct usb_request req; + struct list_head queue; +}; + +struct m66592_ep { + struct usb_ep ep; + struct m66592 *m66592; + + struct list_head queue; + unsigned busy:1; + unsigned internal_ccpl:1; /* use only control */ + + /* this member can able to after m66592_enable */ + unsigned use_dma:1; + u16 pipenum; + u16 type; + const struct usb_endpoint_descriptor *desc; + /* register address */ + unsigned long fifoaddr; + unsigned long fifosel; + unsigned long fifoctr; + unsigned long fifotrn; + unsigned long pipectr; +}; + +struct m66592 { + spinlock_t lock; + void __iomem *reg; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct m66592_ep ep[M66592_MAX_NUM_PIPE]; + struct m66592_ep *pipenum2ep[M66592_MAX_NUM_PIPE]; + struct m66592_ep *epaddr2ep[16]; + + struct usb_request *ep0_req; /* for internal request */ + u16 ep0_data; /* for internal request */ + + struct timer_list timer; + + u16 old_vbus; + int scount; + + int old_dvsq; + + /* pipe config */ + int bulk; + int interrupt; + int isochronous; + int num_dma; + int bi_bufnum; /* bulk and isochronous's bufnum */ +}; + +#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget) +#define m66592_to_gadget(m66592) (&m66592->gadget) + +#define is_bulk_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_BULK) && \ + (pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK))) +#define is_interrupt_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_INT) && \ + (pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT))) +#define is_isoc_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_ISOC) && \ + (pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC))) + +#define enable_irq_ready(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_BRDYENB) +#define disable_irq_ready(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_BRDYENB) +#define enable_irq_empty(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_BEMPENB) +#define disable_irq_empty(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_BEMPENB) +#define enable_irq_nrdy(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_NRDYENB) +#define disable_irq_nrdy(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_NRDYENB) + +/*-------------------------------------------------------------------------*/ +static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset) +{ + return inw((unsigned long)m66592->reg + offset); +} + +static inline void m66592_read_fifo(struct m66592 *m66592, + unsigned long offset, + void *buf, unsigned long len) +{ + unsigned long fifoaddr = (unsigned long)m66592->reg + offset; + + len = (len + 1) / 2; + insw(fifoaddr, buf, len); +} + +static inline void m66592_write(struct m66592 *m66592, u16 val, + unsigned long offset) +{ + outw(val, (unsigned long)m66592->reg + offset); +} + +static inline void m66592_write_fifo(struct m66592 *m66592, + unsigned long offset, + void *buf, unsigned long len) +{ + unsigned long fifoaddr = (unsigned long)m66592->reg + offset; + unsigned long odd = len & 0x0001; + + len = len / 2; + outsw(fifoaddr, buf, len); + if (odd) { + unsigned char *p = buf + len*2; + outb(*p, fifoaddr); + } +} + +static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, + unsigned long offset) +{ + u16 tmp; + tmp = m66592_read(m66592, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + m66592_write(m66592, tmp, offset); +} + +#define m66592_bclr(m66592, val, offset) \ + m66592_mdfy(m66592, 0, val, offset) +#define m66592_bset(m66592, val, offset) \ + m66592_mdfy(m66592, val, 0, offset) + +#endif /* ifndef __M66592_UDC_H__ */ + + diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index d975ecf18e0..c3d364ecd4f 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -450,100 +450,6 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* - * dma-coherent memory allocation (for dma-capable endpoints) - * - * NOTE: the dma_*_coherent() API calls suck. Most implementations are - * (a) page-oriented, so small buffers lose big; and (b) asymmetric with - * respect to calls with irqs disabled: alloc is safe, free is not. - * We currently work around (b), but not (a). - */ - -static void * -net2280_alloc_buffer ( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags -) -{ - void *retval; - struct net2280_ep *ep; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep) - return NULL; - *dma = DMA_ADDR_INVALID; - - if (ep->dma) - retval = dma_alloc_coherent(&ep->dev->pdev->dev, - bytes, dma, gfp_flags); - else - retval = kmalloc(bytes, gfp_flags); - return retval; -} - -static DEFINE_SPINLOCK(buflock); -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - -static void do_free(unsigned long ignored) -{ - spin_lock_irq(&buflock); - while (!list_empty(&buffers)) { - struct free_record *buf; - - buf = list_entry(buffers.next, struct free_record, list); - list_del(&buf->list); - spin_unlock_irq(&buflock); - - dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); - - spin_lock_irq(&buflock); - } - spin_unlock_irq(&buflock); -} - -static DECLARE_TASKLET(deferred_free, do_free, 0); - -static void -net2280_free_buffer ( - struct usb_ep *_ep, - void *address, - dma_addr_t dma, - unsigned bytes -) { - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct net2280_ep *ep; - struct free_record *buf = address; - unsigned long flags; - - ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep) - return; - - ep = container_of (_ep, struct net2280_ep, ep); - buf->dev = &ep->dev->pdev->dev; - buf->bytes = bytes; - buf->dma = dma; - - spin_lock_irqsave(&buflock, flags); - list_add_tail(&buf->list, &buffers); - tasklet_schedule(&deferred_free); - spin_unlock_irqrestore(&buflock, flags); - } else - kfree (address); -} - -/*-------------------------------------------------------------------------*/ - /* load a packet into the fifo we use for usb IN transfers. * works for all endpoints. * @@ -1392,9 +1298,6 @@ static const struct usb_ep_ops net2280_ep_ops = { .alloc_request = net2280_alloc_request, .free_request = net2280_free_request, - .alloc_buffer = net2280_alloc_buffer, - .free_buffer = net2280_free_buffer, - .queue = net2280_queue, .dequeue = net2280_dequeue, @@ -2964,7 +2867,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) , &dev->pci->pcimstctl); /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ pci_set_master (pdev); - pci_set_mwi (pdev); + pci_try_set_mwi (pdev); /* ... also flushes any posted pci writes */ dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index c4975a6cf77..9b0f0925ddd 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -296,111 +296,6 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* - * dma-coherent memory allocation (for dma-capable endpoints) - * - * NOTE: the dma_*_coherent() API calls suck. Most implementations are - * (a) page-oriented, so small buffers lose big; and (b) asymmetric with - * respect to calls with irqs disabled: alloc is safe, free is not. - * We currently work around (b), but not (a). - */ - -static void * -omap_alloc_buffer( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags -) -{ - void *retval; - struct omap_ep *ep; - - if (!_ep) - return NULL; - - ep = container_of(_ep, struct omap_ep, ep); - if (use_dma && ep->has_dma) { - static int warned; - if (!warned && bytes < PAGE_SIZE) { - dev_warn(ep->udc->gadget.dev.parent, - "using dma_alloc_coherent for " - "small allocations wastes memory\n"); - warned++; - } - return dma_alloc_coherent(ep->udc->gadget.dev.parent, - bytes, dma, gfp_flags); - } - - retval = kmalloc(bytes, gfp_flags); - if (retval) - *dma = virt_to_phys(retval); - return retval; -} - -static DEFINE_SPINLOCK(buflock); -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - -static void do_free(unsigned long ignored) -{ - spin_lock_irq(&buflock); - while (!list_empty(&buffers)) { - struct free_record *buf; - - buf = list_entry(buffers.next, struct free_record, list); - list_del(&buf->list); - spin_unlock_irq(&buflock); - - dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); - - spin_lock_irq(&buflock); - } - spin_unlock_irq(&buflock); -} - -static DECLARE_TASKLET(deferred_free, do_free, 0); - -static void omap_free_buffer( - struct usb_ep *_ep, - void *buf, - dma_addr_t dma, - unsigned bytes -) -{ - if (!_ep) { - WARN_ON(1); - return; - } - - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct omap_ep *ep; - struct free_record *rec = buf; - unsigned long flags; - - ep = container_of(_ep, struct omap_ep, ep); - - rec->dev = ep->udc->gadget.dev.parent; - rec->bytes = bytes; - rec->dma = dma; - - spin_lock_irqsave(&buflock, flags); - list_add_tail(&rec->list, &buffers); - tasklet_schedule(&deferred_free); - spin_unlock_irqrestore(&buflock, flags); - } else - kfree(buf); -} - -/*-------------------------------------------------------------------------*/ - static void done(struct omap_ep *ep, struct omap_req *req, int status) { @@ -1271,9 +1166,6 @@ static struct usb_ep_ops omap_ep_ops = { .alloc_request = omap_alloc_request, .free_request = omap_free_request, - .alloc_buffer = omap_alloc_buffer, - .free_buffer = omap_free_buffer, - .queue = omap_ep_queue, .dequeue = omap_ep_dequeue, diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 84392e835d5..63b9521c132 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -24,9 +24,9 @@ * */ -#undef DEBUG // #define VERBOSE DBG_VERBOSE +#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -46,19 +46,17 @@ #include <asm/byteorder.h> #include <asm/dma.h> +#include <asm/gpio.h> #include <asm/io.h> #include <asm/system.h> #include <asm/mach-types.h> #include <asm/unaligned.h> #include <asm/hardware.h> -#ifdef CONFIG_ARCH_PXA -#include <asm/arch/pxa-regs.h> -#endif #include <linux/usb/ch9.h> #include <linux/usb_gadget.h> -#include <asm/arch/udc.h> +#include <asm/mach/udc_pxa2xx.h> /* @@ -76,9 +74,17 @@ * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + * + * Note that the UDC hardware supports DMA (except on IXP) but that's + * not used here. IN-DMA (to host) is simple enough, when the data is + * suitably aligned (16 bytes) ... the network stack doesn't do that, + * other software can. OUT-DMA is buggy in most chip versions, as well + * as poorly designed (data toggle not automatic). So this driver won't + * bother using DMA. (Mostly-working IN-DMA support was available in + * kernels before 2.6.23, but was never enabled or well tested.) */ -#define DRIVER_VERSION "4-May-2005" +#define DRIVER_VERSION "30-June-2007" #define DRIVER_DESC "PXA 25x USB Device Controller driver" @@ -87,12 +93,9 @@ static const char driver_name [] = "pxa2xx_udc"; static const char ep0name [] = "ep0"; -// #define USE_DMA -// #define USE_OUT_DMA // #define DISABLE_TEST_MODE #ifdef CONFIG_ARCH_IXP4XX -#undef USE_DMA /* cpu-specific register addresses are compiled in to this code */ #ifdef CONFIG_ARCH_PXA @@ -104,25 +107,6 @@ static const char ep0name [] = "ep0"; #include "pxa2xx_udc.h" -#ifdef USE_DMA -static int use_dma = 1; -module_param(use_dma, bool, 0); -MODULE_PARM_DESC (use_dma, "true to use dma"); - -static void dma_nodesc_handler (int dmach, void *_ep); -static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req); - -#ifdef USE_OUT_DMA -#define DMASTR " (dma support)" -#else -#define DMASTR " (dma in)" -#endif - -#else /* !USE_DMA */ -#define DMASTR " (pio only)" -#undef USE_OUT_DMA -#endif - #ifdef CONFIG_USB_PXA2XX_SMALL #define SIZE_STR " (small)" #else @@ -155,7 +139,7 @@ static int is_vbus_present(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_vbus) - return udc_gpio_get(mach->gpio_vbus); + return gpio_get_value(mach->gpio_vbus); if (mach->udc_is_connected) return mach->udc_is_connected(); return 1; @@ -167,7 +151,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - udc_gpio_set(mach->gpio_pullup, 0); + gpio_set_value(mach->gpio_pullup, 0); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } @@ -177,7 +161,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - udc_gpio_set(mach->gpio_pullup, 1); + gpio_set_value(mach->gpio_pullup, 1); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } @@ -281,9 +265,8 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, } ep->desc = desc; - ep->dma = -1; ep->stopped = 0; - ep->pio_irqs = ep->dma_irqs = 0; + ep->pio_irqs = 0; ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); /* flush fifo (mostly for OUT buffers) */ @@ -291,30 +274,6 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, /* ... reset halt state too, if we could ... */ -#ifdef USE_DMA - /* for (some) bulk and ISO endpoints, try to get a DMA channel and - * bind it to the endpoint. otherwise use PIO. - */ - switch (ep->bmAttributes) { - case USB_ENDPOINT_XFER_ISOC: - if (le16_to_cpu(desc->wMaxPacketSize) % 32) - break; - // fall through - case USB_ENDPOINT_XFER_BULK: - if (!use_dma || !ep->reg_drcmr) - break; - ep->dma = pxa_request_dma ((char *)_ep->name, - (le16_to_cpu (desc->wMaxPacketSize) > 64) - ? DMA_PRIO_MEDIUM /* some iso */ - : DMA_PRIO_LOW, - dma_nodesc_handler, ep); - if (ep->dma >= 0) { - *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; - DMSG("%s using dma%d\n", _ep->name, ep->dma); - } - } -#endif - DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); return 0; } @@ -334,14 +293,6 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) nuke (ep, -ESHUTDOWN); -#ifdef USE_DMA - if (ep->dma >= 0) { - *ep->reg_drcmr = 0; - pxa_free_dma (ep->dma); - ep->dma = -1; - } -#endif - /* flush fifo (mostly for IN buffers) */ pxa2xx_ep_fifo_flush (_ep); @@ -390,35 +341,6 @@ pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) kfree(req); } - -/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's - * no device-affinity and the heap works perfectly well for i/o buffers. - * It wastes much less memory than dma_alloc_coherent() would, and even - * prevents cacheline (32 bytes wide) sharing problems. - */ -static void * -pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - char *retval; - - retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); - if (retval) -#ifdef USE_DMA - *dma = virt_to_bus (retval); -#else - *dma = (dma_addr_t)~0; -#endif - return retval; -} - -static void -pxa2xx_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, - unsigned bytes) -{ - kfree (buf); -} - /*-------------------------------------------------------------------------*/ /* @@ -518,18 +440,8 @@ write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) /* requests complete when all IN data is in the FIFO */ if (is_last) { done (ep, req, 0); - if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { + if (list_empty(&ep->queue)) pio_irq_disable (ep->bEndpointAddress); -#ifdef USE_DMA - /* unaligned data and zlps couldn't use dma */ - if (unlikely(!list_empty(&ep->queue))) { - req = list_entry(ep->queue.next, - struct pxa2xx_request, queue); - kick_dma(ep,req); - return 0; - } -#endif - } return 1; } @@ -728,182 +640,6 @@ read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) return 0; } -#ifdef USE_DMA - -#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE) - -static void -start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in) -{ - u32 dcmd = req->req.length; - u32 buf = req->req.dma; - u32 fifo = io_v2p ((u32)ep->reg_uddr); - - /* caller guarantees there's a packet or more remaining - * - IN may end with a short packet (TSP set separately), - * - OUT is always full length - */ - buf += req->req.actual; - dcmd -= req->req.actual; - ep->dma_fixup = 0; - - /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */ - DCSR(ep->dma) = DCSR_NODESC; - if (is_in) { - DSADR(ep->dma) = buf; - DTADR(ep->dma) = fifo; - if (dcmd > MAX_IN_DMA) - dcmd = MAX_IN_DMA; - else - ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0; - dcmd |= DCMD_BURST32 | DCMD_WIDTH1 - | DCMD_FLOWTRG | DCMD_INCSRCADDR; - } else { -#ifdef USE_OUT_DMA - DSADR(ep->dma) = fifo; - DTADR(ep->dma) = buf; - if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) - dcmd = ep->ep.maxpacket; - dcmd |= DCMD_BURST32 | DCMD_WIDTH1 - | DCMD_FLOWSRC | DCMD_INCTRGADDR; -#endif - } - DCMD(ep->dma) = dcmd; - DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC - | (unlikely(is_in) - ? DCSR_STOPIRQEN /* use dma_nodesc_handler() */ - : 0); /* use handle_ep() */ -} - -static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - int is_in = ep->bEndpointAddress & USB_DIR_IN; - - if (is_in) { - /* unaligned tx buffers and zlps only work with PIO */ - if ((req->req.dma & 0x0f) != 0 - || unlikely((req->req.length - req->req.actual) - == 0)) { - pio_irq_enable(ep->bEndpointAddress); - if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0) - (void) write_fifo(ep, req); - } else { - start_dma_nodesc(ep, req, USB_DIR_IN); - } - } else { - if ((req->req.length - req->req.actual) < ep->ep.maxpacket) { - DMSG("%s short dma read...\n", ep->ep.name); - /* we're always set up for pio out */ - read_fifo (ep, req); - } else { - *ep->reg_udccs = UDCCS_BO_DME - | (*ep->reg_udccs & UDCCS_BO_FST); - start_dma_nodesc(ep, req, USB_DIR_OUT); - } - } -} - -static void cancel_dma(struct pxa2xx_ep *ep) -{ - struct pxa2xx_request *req; - u32 tmp; - - if (DCSR(ep->dma) == 0 || list_empty(&ep->queue)) - return; - - DCSR(ep->dma) = 0; - while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0) - cpu_relax(); - - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - tmp = DCMD(ep->dma) & DCMD_LENGTH; - req->req.actual = req->req.length - (tmp & DCMD_LENGTH); - - /* the last tx packet may be incomplete, so flush the fifo. - * FIXME correct req.actual if we can - */ - if (ep->bEndpointAddress & USB_DIR_IN) - *ep->reg_udccs = UDCCS_BI_FTF; -} - -/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */ -static void dma_nodesc_handler(int dmach, void *_ep) -{ - struct pxa2xx_ep *ep = _ep; - struct pxa2xx_request *req; - u32 tmp, completed; - - local_irq_disable(); - - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - - ep->dma_irqs++; - ep->dev->stats.irqs++; - HEX_DISPLAY(ep->dev->stats.irqs); - - /* ack/clear */ - tmp = DCSR(ep->dma); - DCSR(ep->dma) = tmp; - if ((tmp & DCSR_STOPSTATE) == 0 - || (DDADR(ep->dma) & DDADR_STOP) != 0) { - DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n", - ep->ep.name, DCSR(ep->dma), DDADR(ep->dma)); - goto done; - } - DCSR(ep->dma) = 0; /* clear DCSR_STOPSTATE */ - - /* update transfer status */ - completed = tmp & DCSR_BUSERR; - if (ep->bEndpointAddress & USB_DIR_IN) - tmp = DSADR(ep->dma); - else - tmp = DTADR(ep->dma); - req->req.actual = tmp - req->req.dma; - - /* FIXME seems we sometimes see partial transfers... */ - - if (unlikely(completed != 0)) - req->req.status = -EIO; - else if (req->req.actual) { - /* these registers have zeroes in low bits; they miscount - * some (end-of-transfer) short packets: tx 14 as tx 12 - */ - if (ep->dma_fixup) - req->req.actual = min(req->req.actual + 3, - req->req.length); - - tmp = (req->req.length - req->req.actual); - completed = (tmp == 0); - if (completed && (ep->bEndpointAddress & USB_DIR_IN)) { - - /* maybe validate final short packet ... */ - if ((req->req.actual % ep->ep.maxpacket) != 0) - *ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/; - - /* ... or zlp, using pio fallback */ - else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK - && req->req.zero) { - DMSG("%s zlp terminate ...\n", ep->ep.name); - completed = 0; - } - } - } - - if (likely(completed)) { - done(ep, req, 0); - - /* maybe re-activate after completion */ - if (ep->stopped || list_empty(&ep->queue)) - goto done; - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - } - kick_dma(ep, req); -done: - local_irq_enable(); -} - -#endif - /*-------------------------------------------------------------------------*/ static int @@ -942,19 +678,8 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) (ep->desc->wMaxPacketSize))) return -EMSGSIZE; -#ifdef USE_DMA - // FIXME caller may already have done the dma mapping - if (ep->dma >= 0) { - _req->dma = dma_map_single(dev->dev, - _req->buf, _req->length, - ((ep->bEndpointAddress & USB_DIR_IN) != 0) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - } -#endif - DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); + _ep->name, _req, _req->length, _req->buf); local_irq_save(flags); @@ -1002,11 +727,6 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) local_irq_restore (flags); return -EL2HLT; } -#ifdef USE_DMA - /* either start dma or prime pio pump */ - } else if (ep->dma >= 0) { - kick_dma(ep, req); -#endif /* can the FIFO can satisfy the request immediately? */ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 @@ -1017,7 +737,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req = NULL; } - if (likely (req && ep->desc) && ep->dma < 0) + if (likely (req && ep->desc)) pio_irq_enable(ep->bEndpointAddress); } @@ -1038,10 +758,6 @@ static void nuke(struct pxa2xx_ep *ep, int status) struct pxa2xx_request *req; /* called with irqs blocked */ -#ifdef USE_DMA - if (ep->dma >= 0 && !ep->stopped) - cancel_dma(ep); -#endif while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa2xx_request, @@ -1076,19 +792,7 @@ static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return -EINVAL; } -#ifdef USE_DMA - if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { - cancel_dma(ep); - done(ep, req, -ECONNRESET); - /* restart i/o */ - if (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct pxa2xx_request, queue); - kick_dma(ep, req); - } - } else -#endif - done(ep, req, -ECONNRESET); + done(ep, req, -ECONNRESET); local_irq_restore(flags); return 0; @@ -1203,9 +907,6 @@ static struct usb_ep_ops pxa2xx_ep_ops = { .alloc_request = pxa2xx_ep_alloc_request, .free_request = pxa2xx_ep_free_request, - .alloc_buffer = pxa2xx_ep_alloc_buffer, - .free_buffer = pxa2xx_ep_free_buffer, - .queue = pxa2xx_ep_queue, .dequeue = pxa2xx_ep_dequeue, @@ -1325,7 +1026,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, /* basic device status */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\nGadget driver: %s\nHost %s\n\n", - driver_name, DRIVER_VERSION SIZE_STR DMASTR, + driver_name, DRIVER_VERSION SIZE_STR "(pio)", dev->driver ? dev->driver->driver.name : "(none)", is_vbus_present() ? "full speed" : "disconnected"); size -= t; @@ -1390,7 +1091,6 @@ udc_proc_read(char *page, char **start, off_t off, int count, for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep [i]; struct pxa2xx_request *req; - int t; if (i != 0) { const struct usb_endpoint_descriptor *d; @@ -1400,10 +1100,9 @@ udc_proc_read(char *page, char **start, off_t off, int count, continue; tmp = *dev->ep [i].reg_udccs; t = scnprintf(next, size, - "%s max %d %s udccs %02x irqs %lu/%lu\n", + "%s max %d %s udccs %02x irqs %lu\n", ep->ep.name, le16_to_cpu (d->wMaxPacketSize), - (ep->dma >= 0) ? "dma" : "pio", tmp, - ep->pio_irqs, ep->dma_irqs); + "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ } else /* ep0 should only have one transfer queued */ @@ -1423,19 +1122,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, continue; } list_for_each_entry(req, &ep->queue, queue) { -#ifdef USE_DMA - if (ep->dma >= 0 && req->queue.prev == &ep->queue) - t = scnprintf(next, size, - "\treq %p len %d/%d " - "buf %p (dma%d dcmd %08x)\n", - &req->req, req->req.actual, - req->req.length, req->req.buf, - ep->dma, DCMD(ep->dma) - // low 13 bits == bytes-to-go - ); - else -#endif - t = scnprintf(next, size, + t = scnprintf(next, size, "\treq %p len %d/%d buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); @@ -1488,7 +1175,6 @@ static void udc_disable(struct pxa2xx_udc *dev) ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN; - LED_CONNECTED_OFF; } @@ -1514,7 +1200,7 @@ static void udc_reinit(struct pxa2xx_udc *dev) ep->desc = NULL; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); - ep->pio_irqs = ep->dma_irqs = 0; + ep->pio_irqs = 0; } /* the rest was statically initialized, and is read-only */ @@ -1666,7 +1352,6 @@ stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) del_timer_sync(&dev->timer); /* report disconnect; the driver is already quiesced */ - LED_CONNECTED_OFF; if (driver) driver->disconnect(&dev->gadget); @@ -1715,16 +1400,13 @@ lubbock_vbus_irq(int irq, void *_dev) int vbus; dev->stats.irqs++; - HEX_DISPLAY(dev->stats.irqs); switch (irq) { case LUBBOCK_USB_IRQ: - LED_CONNECTED_ON; vbus = 1; disable_irq(LUBBOCK_USB_IRQ); enable_irq(LUBBOCK_USB_DISC_IRQ); break; case LUBBOCK_USB_DISC_IRQ: - LED_CONNECTED_OFF; vbus = 0; disable_irq(LUBBOCK_USB_DISC_IRQ); enable_irq(LUBBOCK_USB_IRQ); @@ -1742,7 +1424,7 @@ lubbock_vbus_irq(int irq, void *_dev) static irqreturn_t udc_vbus_irq(int irq, void *_dev) { struct pxa2xx_udc *dev = _dev; - int vbus = udc_gpio_get(dev->mach->gpio_vbus); + int vbus = gpio_get_value(dev->mach->gpio_vbus); pxa2xx_udc_vbus_session(&dev->gadget, vbus); return IRQ_HANDLED; @@ -2040,18 +1722,6 @@ static void handle_ep(struct pxa2xx_ep *ep) /* fifos can hold packets, ready for reading... */ if (likely(req)) { -#ifdef USE_OUT_DMA -// TODO didn't yet debug out-dma. this approach assumes -// the worst about short packets and RPC; it might be better. - - if (likely(ep->dma >= 0)) { - if (!(udccs & UDCCS_BO_RSP)) { - *ep->reg_udccs = UDCCS_BO_RPC; - ep->dma_irqs++; - return; - } - } -#endif completed = read_fifo(ep, req); } else pio_irq_disable (ep->bEndpointAddress); @@ -2074,7 +1744,6 @@ pxa2xx_udc_irq(int irq, void *_dev) int handled; dev->stats.irqs++; - HEX_DISPLAY(dev->stats.irqs); do { u32 udccr = UDCCR; @@ -2125,7 +1794,6 @@ pxa2xx_udc_irq(int irq, void *_dev) } else { DBG(DBG_VERBOSE, "USB reset end\n"); dev->gadget.speed = USB_SPEED_FULL; - LED_CONNECTED_ON; memset(&dev->stats, 0, sizeof dev->stats); /* driver and endpoints are still reset */ } @@ -2217,7 +1885,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS1, .reg_uddr = &UDDR1, - drcmr (25) }, .ep[2] = { .ep = { @@ -2232,7 +1899,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS2, .reg_ubcr = &UBCR2, .reg_uddr = &UDDR2, - drcmr (26) }, #ifndef CONFIG_USB_PXA2XX_SMALL .ep[3] = { @@ -2247,7 +1913,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS3, .reg_uddr = &UDDR3, - drcmr (27) }, .ep[4] = { .ep = { @@ -2262,7 +1927,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS4, .reg_ubcr = &UBCR4, .reg_uddr = &UDDR4, - drcmr (28) }, .ep[5] = { .ep = { @@ -2291,7 +1955,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS6, .reg_uddr = &UDDR6, - drcmr (30) }, .ep[7] = { .ep = { @@ -2306,7 +1969,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS7, .reg_ubcr = &UBCR7, .reg_uddr = &UDDR7, - drcmr (31) }, .ep[8] = { .ep = { @@ -2320,7 +1982,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS8, .reg_uddr = &UDDR8, - drcmr (32) }, .ep[9] = { .ep = { @@ -2335,7 +1996,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS9, .reg_ubcr = &UBCR9, .reg_uddr = &UDDR9, - drcmr (33) }, .ep[10] = { .ep = { @@ -2364,7 +2024,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS11, .reg_uddr = &UDDR11, - drcmr (35) }, .ep[12] = { .ep = { @@ -2379,7 +2038,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS12, .reg_ubcr = &UBCR12, .reg_uddr = &UDDR12, - drcmr (36) }, .ep[13] = { .ep = { @@ -2393,7 +2051,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS13, .reg_uddr = &UDDR13, - drcmr (37) }, .ep[14] = { .ep = { @@ -2408,7 +2065,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS14, .reg_ubcr = &UBCR14, .reg_uddr = &UDDR14, - drcmr (38) }, .ep[15] = { .ep = { @@ -2466,7 +2122,7 @@ static struct pxa2xx_udc memory = { static int __init pxa2xx_udc_probe(struct platform_device *pdev) { struct pxa2xx_udc *dev = &memory; - int retval, out_dma = 1, vbus_irq, irq; + int retval, vbus_irq, irq; u32 chiprev; /* insist on Intel/ARM/XScale */ @@ -2489,7 +2145,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) case PXA250_B2: case PXA210_B2: case PXA250_B1: case PXA210_B1: case PXA250_B0: case PXA210_B0: - out_dma = 0; + /* OUT-DMA is broken ... */ /* fall through */ case PXA250_C0: case PXA210_C0: break; @@ -2498,11 +2154,9 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) case IXP425_B0: case IXP465_AD: dev->has_cfr = 1; - out_dma = 0; break; #endif default: - out_dma = 0; printk(KERN_ERR "%s: unrecognized processor: %08x\n", driver_name, chiprev); /* iop3xx, ixp4xx, ... */ @@ -2513,36 +2167,41 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq, + pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", - out_dma ? "" : " (broken dma-out)", - SIZE_STR DMASTR + SIZE_STR "(pio)" ); -#ifdef USE_DMA -#ifndef USE_OUT_DMA - out_dma = 0; -#endif - /* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */ - if (!out_dma) { - DMSG("disabled OUT dma\n"); - dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0; - dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0; - dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0; - } -#endif - /* other non-static parts of init */ dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; + if (dev->mach->gpio_vbus) { - udc_gpio_init_vbus(dev->mach->gpio_vbus); - vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus); + if ((retval = gpio_request(dev->mach->gpio_vbus, + "pxa2xx_udc GPIO VBUS"))) { + dev_dbg(&pdev->dev, + "can't get vbus gpio %d, err: %d\n", + dev->mach->gpio_vbus, retval); + return -EBUSY; + } + gpio_direction_input(dev->mach->gpio_vbus); + vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); set_irq_type(vbus_irq, IRQT_BOTHEDGE); } else vbus_irq = 0; - if (dev->mach->gpio_pullup) - udc_gpio_init_pullup(dev->mach->gpio_pullup); + + if (dev->mach->gpio_pullup) { + if ((retval = gpio_request(dev->mach->gpio_pullup, + "pca2xx_udc GPIO PULLUP"))) { + dev_dbg(&pdev->dev, + "can't get pullup gpio %d, err: %d\n", + dev->mach->gpio_pullup, retval); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); + return -EBUSY; + } + gpio_direction_output(dev->mach->gpio_pullup, 0); + } init_timer(&dev->timer); dev->timer.function = udc_watchdog; @@ -2566,6 +2225,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) if (retval != 0) { printk(KERN_ERR "%s: can't get irq %d, err %d\n", driver_name, irq, retval); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } dev->got_irq = 1; @@ -2581,6 +2244,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) driver_name, LUBBOCK_USB_DISC_IRQ, retval); lubbock_fail0: free_irq(irq, dev); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } retval = request_irq(LUBBOCK_USB_IRQ, @@ -2593,11 +2260,6 @@ lubbock_fail0: free_irq(LUBBOCK_USB_DISC_IRQ, dev); goto lubbock_fail0; } -#ifdef DEBUG - /* with U-Boot (but not BLOB), hex is off by default */ - HEX_DISPLAY(dev->stats.irqs); - LUB_DISC_BLNK_LED &= 0xff; -#endif } else #endif if (vbus_irq) { @@ -2608,6 +2270,10 @@ lubbock_fail0: printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, vbus_irq, retval); free_irq(irq, dev); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } } @@ -2641,8 +2307,13 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) free_irq(LUBBOCK_USB_IRQ, dev); } #endif - if (dev->mach->gpio_vbus) - free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev); + if (dev->mach->gpio_vbus) { + free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); + gpio_free(dev->mach->gpio_vbus); + } + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + platform_set_drvdata(pdev, NULL); the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 773e549aff3..0e5d0e6fb0e 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -54,8 +54,6 @@ struct pxa2xx_ep { const struct usb_endpoint_descriptor *desc; struct list_head queue; unsigned long pio_irqs; - unsigned long dma_irqs; - short dma; unsigned short fifo_size; u8 bEndpointAddress; @@ -63,7 +61,7 @@ struct pxa2xx_ep { unsigned stopped : 1; unsigned dma_fixup : 1; - + /* UDCCS = UDC Control/Status for this EP * UBCR = UDC Byte Count Remaining (contents of OUT fifo) * UDDR = UDC Endpoint Data Register (the fifo) @@ -72,12 +70,6 @@ struct pxa2xx_ep { volatile u32 *reg_udccs; volatile u32 *reg_ubcr; volatile u32 *reg_uddr; -#ifdef USE_DMA - volatile u32 *reg_drcmr; -#define drcmr(n) .reg_drcmr = & DRCMR ## n , -#else -#define drcmr(n) -#endif }; struct pxa2xx_request { @@ -85,7 +77,7 @@ struct pxa2xx_request { struct list_head queue; }; -enum ep0_state { +enum ep0_state { EP0_IDLE, EP0_IN_DATA_PHASE, EP0_OUT_DATA_PHASE, @@ -108,7 +100,6 @@ struct udc_stats { #ifdef CONFIG_USB_PXA2XX_SMALL /* when memory's tight, SMALL config saves code+data. */ -#undef USE_DMA #define PXA_UDC_NUM_ENDPOINTS 3 #endif @@ -144,37 +135,8 @@ struct pxa2xx_udc { #ifdef CONFIG_ARCH_LUBBOCK #include <asm/arch/lubbock.h> /* lubbock can also report usb connect/disconnect irqs */ - -#ifdef DEBUG -#define HEX_DISPLAY(n) if (machine_is_lubbock()) { LUB_HEXLED = (n); } #endif -#endif - -/*-------------------------------------------------------------------------*/ - -/* LEDs are only for debug */ -#ifndef HEX_DISPLAY -#define HEX_DISPLAY(n) do {} while(0) -#endif - -#ifdef DEBUG -#include <asm/leds.h> - -#define LED_CONNECTED_ON leds_event(led_green_on) -#define LED_CONNECTED_OFF do { \ - leds_event(led_green_off); \ - HEX_DISPLAY(0); \ - } while(0) -#endif - -#ifndef LED_CONNECTED_ON -#define LED_CONNECTED_ON do {} while(0) -#define LED_CONNECTED_OFF do {} while(0) -#endif - -/*-------------------------------------------------------------------------*/ - static struct pxa2xx_udc *the_controller; /*-------------------------------------------------------------------------*/ @@ -204,7 +166,7 @@ static const char *state_name[] = { # define UDC_DEBUG DBG_NORMAL #endif -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_udccr(const char *label) { u32 udccr = UDCCR; @@ -220,7 +182,7 @@ dump_udccr(const char *label) (udccr & UDCCR_UDE) ? " ude" : ""); } -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_udccs0(const char *label) { u32 udccs0 = UDCCS0; @@ -237,7 +199,7 @@ dump_udccs0(const char *label) (udccs0 & UDCCS0_OPR) ? " opr" : ""); } -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_state(struct pxa2xx_udc *dev) { u32 tmp; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 708657c8913..db1b2bfcee4 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -53,7 +53,7 @@ */ #if 0 -#define DEBUG(str,args...) do { \ +#define DBG(str,args...) do { \ if (rndis_debug) \ printk(KERN_DEBUG str , ## args ); \ } while (0) @@ -65,7 +65,7 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #else #define rndis_debug 0 -#define DEBUG(str,args...) do{}while(0) +#define DBG(str,args...) do{}while(0) #endif #define RNDIS_MAX_CONFIGS 1 @@ -183,9 +183,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, if (!resp) return -ENOMEM; if (buf_len && rndis_debug > 1) { - DEBUG("query OID %08x value, len %d:\n", OID, buf_len); + DBG("query OID %08x value, len %d:\n", OID, buf_len); for (i = 0; i < buf_len; i += 16) { - DEBUG ("%03d: %08x %08x %08x %08x\n", i, + DBG("%03d: %08x %08x %08x %08x\n", i, le32_to_cpu(get_unaligned((__le32 *) &buf[i])), le32_to_cpu(get_unaligned((__le32 *) @@ -207,7 +207,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_SUPPORTED_LIST: - DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); + DBG("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); length = sizeof (oid_supported_list); count = length / sizeof (u32); for (i = 0; i < count; i++) @@ -217,7 +217,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_HARDWARE_STATUS: - DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); + DBG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); /* Bogus question! * Hardware must be ready to receive high level protocols. * BTW: @@ -230,14 +230,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MEDIA_SUPPORTED: - DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; break; /* mandatory */ case OID_GEN_MEDIA_IN_USE: - DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); /* one medium, one transport... (maybe you do it better) */ *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; @@ -245,7 +245,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MAXIMUM_FRAME_SIZE: - DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -256,7 +256,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_LINK_SPEED: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); + DBG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); if (rndis_per_dev_params [configNr].media_state == NDIS_MEDIA_STATE_DISCONNECTED) *outbuf = __constant_cpu_to_le32 (0); @@ -268,7 +268,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_TRANSMIT_BLOCK_SIZE: - DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -278,7 +278,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RECEIVE_BLOCK_SIZE: - DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -288,7 +288,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_VENDOR_ID: - DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].vendorID); retval = 0; @@ -296,7 +296,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_VENDOR_DESCRIPTION: - DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); length = strlen (rndis_per_dev_params [configNr].vendorDescr); memcpy (outbuf, rndis_per_dev_params [configNr].vendorDescr, length); @@ -304,7 +304,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_VENDOR_DRIVER_VERSION: - DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); /* Created as LE */ *outbuf = rndis_driver_version; retval = 0; @@ -312,14 +312,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_CURRENT_PACKET_FILTER: - DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter); retval = 0; break; /* mandatory */ case OID_GEN_MAXIMUM_TOTAL_SIZE: - DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); retval = 0; break; @@ -327,14 +327,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .media_state); retval = 0; break; case OID_GEN_PHYSICAL_MEDIUM: - DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); + DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -344,7 +344,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! */ case OID_GEN_MAC_OPTIONS: /* from WinME */ - DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); + DBG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32( NDIS_MAC_OPTION_RECEIVE_SERIALIZED | NDIS_MAC_OPTION_FULL_DUPLEX); @@ -356,7 +356,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_XMIT_OK: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + DBG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->tx_packets - @@ -369,7 +369,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_OK: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->rx_packets - @@ -382,7 +382,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_XMIT_ERROR: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_errors); @@ -393,7 +393,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_ERROR: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_errors); @@ -403,7 +403,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_NO_BUFFER: - DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_dropped); @@ -413,7 +413,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, #ifdef RNDIS_OPTIONAL_STATS case OID_GEN_DIRECTED_BYTES_XMIT: - DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); /* * Aunt Tilly's size of shoes * minus antarctica count of penguins @@ -433,7 +433,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_DIRECTED_FRAMES_XMIT: - DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); /* dito */ if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( @@ -449,7 +449,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_BYTES_XMIT: - DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast*1234); @@ -458,7 +458,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_FRAMES_XMIT: - DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); @@ -467,7 +467,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_BYTES_XMIT: - DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42*255); @@ -476,7 +476,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_FRAMES_XMIT: - DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42); @@ -485,19 +485,19 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_DIRECTED_BYTES_RCV: - DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_DIRECTED_FRAMES_RCV: - DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_MULTICAST_BYTES_RCV: - DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast * 1111); @@ -506,7 +506,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_FRAMES_RCV: - DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); @@ -515,7 +515,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_BYTES_RCV: - DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42*255); @@ -524,7 +524,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_FRAMES_RCV: - DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42); @@ -533,7 +533,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_RCV_CRC_ERROR: - DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_crc_errors); @@ -542,7 +542,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_TRANSMIT_QUEUE_LENGTH: - DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); + DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -552,7 +552,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_PERMANENT_ADDRESS: - DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); + DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; memcpy (outbuf, @@ -564,7 +564,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_CURRENT_ADDRESS: - DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); + DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; memcpy (outbuf, @@ -576,7 +576,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_MULTICAST_LIST: - DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); /* Multicast base address only */ *outbuf = __constant_cpu_to_le32 (0xE0000000); retval = 0; @@ -584,21 +584,21 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_MAXIMUM_LIST_SIZE: - DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); + DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); /* Multicast base address only */ *outbuf = __constant_cpu_to_le32 (1); retval = 0; break; case OID_802_3_MAC_OPTIONS: - DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); break; /* ieee802.3 statistics OIDs (table 4-4) */ /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: - DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); + DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_frame_errors); @@ -608,51 +608,51 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_XMIT_ONE_COLLISION: - DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; /* mandatory */ case OID_802_3_XMIT_MORE_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; #ifdef RNDIS_OPTIONAL_STATS case OID_802_3_XMIT_DEFERRED: - DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_MAX_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); /* TODO */ break; case OID_802_3_RCV_OVERRUN: - DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); + DBG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_UNDERRUN: - DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_HEARTBEAT_FAILURE: - DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_TIMES_CRS_LOST: - DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_LATE_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); /* TODO */ break; #endif /* RNDIS_OPTIONAL_STATS */ @@ -660,7 +660,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, #ifdef RNDIS_PM /* power management OIDs (table 4-5) */ case OID_PNP_CAPABILITIES: - DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); + DBG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); /* for now, no wakeup capabilities */ length = sizeof (struct NDIS_PNP_CAPABILITIES); @@ -668,7 +668,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, retval = 0; break; case OID_PNP_QUERY_POWER: - DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__, + DBG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__, le32_to_cpu(get_unaligned((__le32 *)buf)) - 1); /* only suspend is a real power state, and * it can't be entered by OID_PNP_SET_POWER... @@ -705,9 +705,9 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, return -ENOMEM; if (buf_len && rndis_debug > 1) { - DEBUG("set OID %08x value, len %d:\n", OID, buf_len); + DBG("set OID %08x value, len %d:\n", OID, buf_len); for (i = 0; i < buf_len; i += 16) { - DEBUG ("%03d: %08x %08x %08x %08x\n", i, + DBG("%03d: %08x %08x %08x %08x\n", i, le32_to_cpu(get_unaligned((__le32 *) &buf[i])), le32_to_cpu(get_unaligned((__le32 *) @@ -731,7 +731,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, */ *params->filter = (u16) le32_to_cpu(get_unaligned( (__le32 *)buf)); - DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", __FUNCTION__, *params->filter); /* this call has a significant side effect: it's @@ -756,7 +756,7 @@ update_linkstate: case OID_802_3_MULTICAST_LIST: /* I think we can ignore this */ - DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); retval = 0; break; #if 0 @@ -764,7 +764,7 @@ update_linkstate: { struct rndis_config_parameter *param; param = (struct rndis_config_parameter *) buf; - DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", + DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", __FUNCTION__, min(cpu_to_le32(param->ParameterNameLength),80), buf + param->ParameterNameOffset); @@ -781,7 +781,7 @@ update_linkstate: * FIXME ... then things go batty; Windows wedges itself. */ i = le32_to_cpu(get_unaligned((__le32 *)buf)); - DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1); + DBG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1); switch (i) { case NdisDeviceStateD0: *params->filter = params->saved_filter; @@ -858,7 +858,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) rndis_query_cmplt_type *resp; rndis_resp_t *r; - // DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); + // DBG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; /* @@ -911,15 +911,15 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) BufOffset = le32_to_cpu (buf->InformationBufferOffset); #ifdef VERBOSE - DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); - DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); - DEBUG("%s: InfoBuffer: ", __FUNCTION__); + DBG("%s: Length: %d\n", __FUNCTION__, BufLength); + DBG("%s: Offset: %d\n", __FUNCTION__, BufOffset); + DBG("%s: InfoBuffer: ", __FUNCTION__); for (i = 0; i < BufLength; i++) { - DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); + DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); } - DEBUG ("\n"); + DBG("\n"); #endif resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); @@ -1082,14 +1082,14 @@ int rndis_msg_parser (u8 configNr, u8 *buf) /* For USB: responses may take up to 10 seconds */ switch (MsgType) { case REMOTE_NDIS_INITIALIZE_MSG: - DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", + DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", __FUNCTION__ ); params->state = RNDIS_INITIALIZED; return rndis_init_response (configNr, (rndis_init_msg_type *) buf); case REMOTE_NDIS_HALT_MSG: - DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", + DBG("%s: REMOTE_NDIS_HALT_MSG\n", __FUNCTION__ ); params->state = RNDIS_UNINITIALIZED; if (params->dev) { @@ -1107,7 +1107,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) (rndis_set_msg_type *) buf); case REMOTE_NDIS_RESET_MSG: - DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", + DBG("%s: REMOTE_NDIS_RESET_MSG\n", __FUNCTION__ ); return rndis_reset_response (configNr, (rndis_reset_msg_type *) buf); @@ -1115,7 +1115,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) case REMOTE_NDIS_KEEPALIVE_MSG: /* For USB: host does this every 5 seconds */ if (rndis_debug > 1) - DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", __FUNCTION__ ); return rndis_keepalive_response (configNr, (rndis_keepalive_msg_type *) @@ -1132,7 +1132,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) { unsigned i; for (i = 0; i < MsgLength; i += 16) { - DEBUG ("%03d: " + DBG("%03d: " " %02x %02x %02x %02x" " %02x %02x %02x %02x" " %02x %02x %02x %02x" @@ -1163,18 +1163,18 @@ int rndis_register (int (* rndis_control_ack) (struct net_device *)) if (!rndis_per_dev_params [i].used) { rndis_per_dev_params [i].used = 1; rndis_per_dev_params [i].ack = rndis_control_ack; - DEBUG("%s: configNr = %d\n", __FUNCTION__, i); + DBG("%s: configNr = %d\n", __FUNCTION__, i); return i; } } - DEBUG("failed\n"); + DBG("failed\n"); return -1; } void rndis_deregister (int configNr) { - DEBUG("%s: \n", __FUNCTION__ ); + DBG("%s: \n", __FUNCTION__ ); if (configNr >= RNDIS_MAX_CONFIGS) return; rndis_per_dev_params [configNr].used = 0; @@ -1186,7 +1186,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, struct net_device_stats *stats, u16 *cdc_filter) { - DEBUG("%s:\n", __FUNCTION__ ); + DBG("%s:\n", __FUNCTION__ ); if (!dev || !stats) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -1199,7 +1199,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) { - DEBUG("%s:\n", __FUNCTION__ ); + DBG("%s:\n", __FUNCTION__ ); if (!vendorDescr) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -1211,7 +1211,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) { - DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed); + DBG("%s: %u %u\n", __FUNCTION__, medium, speed); if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].medium = medium; @@ -1390,7 +1390,7 @@ static int rndis_proc_write (struct file *file, const char __user *buffer, break; default: if (fl_speed) p->speed = speed; - else DEBUG ("%c is not valid\n", c); + else DBG("%c is not valid\n", c); break; } @@ -1419,12 +1419,12 @@ int __devinit rndis_init (void) if (!(rndis_connect_state [i] = create_proc_entry (name, 0660, NULL))) { - DEBUG ("%s :remove entries", __FUNCTION__); + DBG("%s :remove entries", __FUNCTION__); while (i) { sprintf (name, NAME_TEMPLATE, --i); remove_proc_entry (name, NULL); } - DEBUG ("\n"); + DBG("\n"); return -EIO; } diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c new file mode 100644 index 00000000000..0be80c635c4 --- /dev/null +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -0,0 +1,2045 @@ +/* + * linux/drivers/usb/gadget/s3c2410_udc.c + * + * Samsung S3C24xx series on-chip full speed USB device controllers + * + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Additional cleanups by Ben Dooks <ben-linux@fluff.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/version.h> +#include <linux/clk.h> + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#include <linux/usb.h> +#include <linux/usb_gadget.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> +#include <asm/arch/irqs.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-udc.h> +#include <asm/arch/udc.h> + +#include <asm/mach-types.h> + +#include "s3c2410_udc.h" + +#define DRIVER_DESC "S3C2410 USB Device Controller Gadget" +#define DRIVER_VERSION "29 Apr 2007" +#define DRIVER_AUTHOR "Herbert Pötzl <herbert@13thfloor.at>, " \ + "Arnaud Patard <arnaud.patard@rtp-net.org>" + +static const char gadget_name[] = "s3c2410_udc"; +static const char driver_desc[] = DRIVER_DESC; + +static struct s3c2410_udc *the_controller; +static struct clk *udc_clock; +static struct clk *usb_bus_clock; +static void __iomem *base_addr; +static u64 rsrc_start; +static u64 rsrc_len; +static struct dentry *s3c2410_udc_debugfs_root; + +static inline u32 udc_read(u32 reg) +{ + return readb(base_addr + reg); +} + +static inline void udc_write(u32 value, u32 reg) +{ + writeb(value, base_addr + reg); +} + +static inline void udc_writeb(void __iomem *base, u32 value, u32 reg) +{ + writeb(value, base + reg); +} + +static struct s3c2410_udc_mach_info *udc_info; + +/*************************** DEBUG FUNCTION ***************************/ +#define DEBUG_NORMAL 1 +#define DEBUG_VERBOSE 2 + +#ifdef CONFIG_USB_S3C2410_DEBUG +#define USB_S3C2410_DEBUG_LEVEL 0 + +static uint32_t s3c2410_ticks = 0; + +static int dprintk(int level, const char *fmt, ...) +{ + static char printk_buf[1024]; + static long prevticks; + static int invocation; + va_list args; + int len; + + if (level > USB_S3C2410_DEBUG_LEVEL) + return 0; + + if (s3c2410_ticks != prevticks) { + prevticks = s3c2410_ticks; + invocation = 0; + } + + len = scnprintf(printk_buf, + sizeof(printk_buf), "%1lu.%02d USB: ", + prevticks, invocation++); + + va_start(args, fmt); + len = vscnprintf(printk_buf+len, + sizeof(printk_buf)-len, fmt, args); + va_end(args); + + return printk(KERN_DEBUG "%s", printk_buf); +} +#else +static int dprintk(int level, const char *fmt, ...) +{ + return 0; +} +#endif +static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) +{ + u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg; + u32 ep_int_en_reg, usb_int_en_reg, ep0_csr; + u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2; + u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2; + + addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG); + pwr_reg = udc_read(S3C2410_UDC_PWR_REG); + ep_int_reg = udc_read(S3C2410_UDC_EP_INT_REG); + usb_int_reg = udc_read(S3C2410_UDC_USB_INT_REG); + ep_int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG); + udc_write(0, S3C2410_UDC_INDEX_REG); + ep0_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(1, S3C2410_UDC_INDEX_REG); + ep1_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep1_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + ep1_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep1_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + udc_write(2, S3C2410_UDC_INDEX_REG); + ep2_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep2_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + ep2_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep2_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + + seq_printf(m, "FUNC_ADDR_REG : 0x%04X\n" + "PWR_REG : 0x%04X\n" + "EP_INT_REG : 0x%04X\n" + "USB_INT_REG : 0x%04X\n" + "EP_INT_EN_REG : 0x%04X\n" + "USB_INT_EN_REG : 0x%04X\n" + "EP0_CSR : 0x%04X\n" + "EP1_I_CSR1 : 0x%04X\n" + "EP1_I_CSR2 : 0x%04X\n" + "EP1_O_CSR1 : 0x%04X\n" + "EP1_O_CSR2 : 0x%04X\n" + "EP2_I_CSR1 : 0x%04X\n" + "EP2_I_CSR2 : 0x%04X\n" + "EP2_O_CSR1 : 0x%04X\n" + "EP2_O_CSR2 : 0x%04X\n", + addr_reg,pwr_reg,ep_int_reg,usb_int_reg, + ep_int_en_reg, usb_int_en_reg, ep0_csr, + ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, + ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2 + ); + + return 0; +} + +static int s3c2410_udc_debugfs_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, s3c2410_udc_debugfs_seq_show, NULL); +} + +static const struct file_operations s3c2410_udc_debugfs_fops = { + .open = s3c2410_udc_debugfs_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +/* io macros */ + +static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY, + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_clear_ep0_se(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG); +} + +inline void s3c2410_udc_set_ep0_ss(void __iomem *b) +{ + udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + + udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY + | S3C2410_UDC_EP0_CSR_DE), + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY + | S3C2410_UDC_EP0_CSR_SSE), + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY + | S3C2410_UDC_EP0_CSR_DE), + S3C2410_UDC_EP0_CSR_REG); +} + +/*------------------------- I/O ----------------------------------*/ + +/* + * s3c2410_udc_done + */ +static void s3c2410_udc_done(struct s3c2410_ep *ep, + struct s3c2410_request *req, int status) +{ + unsigned halted = ep->halted; + + list_del_init(&req->queue); + + if (likely (req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + ep->halted = 1; + req->req.complete(&ep->ep, &req->req); + ep->halted = halted; +} + +static void s3c2410_udc_nuke(struct s3c2410_udc *udc, + struct s3c2410_ep *ep, int status) +{ + /* Sanity check */ + if (&ep->queue == NULL) + return; + + while (!list_empty (&ep->queue)) { + struct s3c2410_request *req; + req = list_entry (ep->queue.next, struct s3c2410_request, + queue); + s3c2410_udc_done(ep, req, status); + } +} + +static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev) +{ + unsigned i; + + /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint + * fifos, and pending transactions mustn't be continued in any case. + */ + + for (i = 1; i < S3C2410_ENDPOINTS; i++) + s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED); +} + +static inline int s3c2410_udc_fifo_count_out(void) +{ + int tmp; + + tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8; + tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG); + return tmp; +} + +/* + * s3c2410_udc_write_packet + */ +static inline int s3c2410_udc_write_packet(int fifo, + struct s3c2410_request *req, + unsigned max) +{ + unsigned len = min(req->req.length - req->req.actual, max); + u8 *buf = req->req.buf + req->req.actual; + + prefetch(buf); + + dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__, + req->req.actual, req->req.length, len, req->req.actual + len); + + req->req.actual += len; + + udelay(5); + writesb(base_addr + fifo, buf, len); + return len; +} + +/* + * s3c2410_udc_write_fifo + * + * return: 0 = still running, 1 = completed, negative = errno + */ +static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, + struct s3c2410_request *req) +{ + unsigned count; + int is_last; + u32 idx; + int fifo_reg; + u32 ep_csr; + + idx = ep->bEndpointAddress & 0x7F; + switch (idx) { + default: + idx = 0; + case 0: + fifo_reg = S3C2410_UDC_EP0_FIFO_REG; + break; + case 1: + fifo_reg = S3C2410_UDC_EP1_FIFO_REG; + break; + case 2: + fifo_reg = S3C2410_UDC_EP2_FIFO_REG; + break; + case 3: + fifo_reg = S3C2410_UDC_EP3_FIFO_REG; + break; + case 4: + fifo_reg = S3C2410_UDC_EP4_FIFO_REG; + break; + } + + count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket); + + /* last packet is often short (sometimes a zlp) */ + if (count != ep->ep.maxpacket) + is_last = 1; + else if (req->req.length != req->req.actual || req->req.zero) + is_last = 0; + else + is_last = 2; + + /* Only ep0 debug messages are interesting */ + if (idx == 0) + dprintk(DEBUG_NORMAL, + "Written ep%d %d.%d of %d b [last %d,z %d]\n", + idx, count, req->req.actual, req->req.length, + is_last, req->req.zero); + + if (is_last) { + /* The order is important. It prevents sending 2 packets + * at the same time */ + + if (idx == 0) { + /* Reset signal => no need to say 'data sent' */ + if (! (udc_read(S3C2410_UDC_USB_INT_REG) + & S3C2410_UDC_USBINT_RESET)) + s3c2410_udc_set_ep0_de_in(base_addr); + ep->dev->ep0state=EP0_IDLE; + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY, + S3C2410_UDC_IN_CSR1_REG); + } + + s3c2410_udc_done(ep, req, 0); + is_last = 1; + } else { + if (idx == 0) { + /* Reset signal => no need to say 'data sent' */ + if (! (udc_read(S3C2410_UDC_USB_INT_REG) + & S3C2410_UDC_USBINT_RESET)) + s3c2410_udc_set_ep0_ipr(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY, + S3C2410_UDC_IN_CSR1_REG); + } + } + + return is_last; +} + +static inline int s3c2410_udc_read_packet(int fifo, u8 *buf, + struct s3c2410_request *req, unsigned avail) +{ + unsigned len; + + len = min(req->req.length - req->req.actual, avail); + req->req.actual += len; + + readsb(fifo + base_addr, buf, len); + return len; +} + +/* + * return: 0 = still running, 1 = queue empty, negative = errno + */ +static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, + struct s3c2410_request *req) +{ + u8 *buf; + u32 ep_csr; + unsigned bufferspace; + int is_last=1; + unsigned avail; + int fifo_count = 0; + u32 idx; + int fifo_reg; + + idx = ep->bEndpointAddress & 0x7F; + + switch (idx) { + default: + idx = 0; + case 0: + fifo_reg = S3C2410_UDC_EP0_FIFO_REG; + break; + case 1: + fifo_reg = S3C2410_UDC_EP1_FIFO_REG; + break; + case 2: + fifo_reg = S3C2410_UDC_EP2_FIFO_REG; + break; + case 3: + fifo_reg = S3C2410_UDC_EP3_FIFO_REG; + break; + case 4: + fifo_reg = S3C2410_UDC_EP4_FIFO_REG; + break; + } + + if (!req->req.length) + return 1; + + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + if (!bufferspace) { + dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__); + return -1; + } + + udc_write(idx, S3C2410_UDC_INDEX_REG); + + fifo_count = s3c2410_udc_fifo_count_out(); + dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count); + + if (fifo_count > ep->ep.maxpacket) + avail = ep->ep.maxpacket; + else + avail = fifo_count; + + fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail); + + /* checking this with ep0 is not accurate as we already + * read a control request + **/ + if (idx != 0 && fifo_count < ep->ep.maxpacket) { + is_last = 1; + /* overflowed this request? flush extra data */ + if (fifo_count != avail) + req->req.status = -EOVERFLOW; + } else { + is_last = (req->req.length <= req->req.actual) ? 1 : 0; + } + + udc_write(idx, S3C2410_UDC_INDEX_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + + /* Only ep0 debug messages are interesting */ + if (idx == 0) + dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n", + __func__, fifo_count,is_last); + + if (is_last) { + if (idx == 0) { + s3c2410_udc_set_ep0_de_out(base_addr); + ep->dev->ep0state = EP0_IDLE; + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, + S3C2410_UDC_OUT_CSR1_REG); + } + + s3c2410_udc_done(ep, req, 0); + } else { + if (idx == 0) { + s3c2410_udc_clear_ep0_opr(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, + S3C2410_UDC_OUT_CSR1_REG); + } + } + + return is_last; +} + +static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq) +{ + unsigned char *outbuf = (unsigned char*)crq; + int bytes_read = 0; + + udc_write(0, S3C2410_UDC_INDEX_REG); + + bytes_read = s3c2410_udc_fifo_count_out(); + + dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read); + + if (bytes_read > sizeof(struct usb_ctrlrequest)) + bytes_read = sizeof(struct usb_ctrlrequest); + + readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read); + + dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__, + bytes_read, crq->bRequest, crq->bRequestType, + crq->wValue, crq->wIndex, crq->wLength); + + return bytes_read; +} + +static int s3c2410_udc_get_status(struct s3c2410_udc *dev, + struct usb_ctrlrequest *crq) +{ + u16 status = 0; + u8 ep_num = crq->wIndex & 0x7F; + u8 is_in = crq->wIndex & USB_DIR_IN; + + switch (crq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + break; + + case USB_RECIP_DEVICE: + status = dev->devstatus; + break; + + case USB_RECIP_ENDPOINT: + if (ep_num > 4 || crq->wLength > 2) + return 1; + + if (ep_num == 0) { + udc_write(0, S3C2410_UDC_INDEX_REG); + status = udc_read(S3C2410_UDC_IN_CSR1_REG); + status = status & S3C2410_UDC_EP0_CSR_SENDSTL; + } else { + udc_write(ep_num, S3C2410_UDC_INDEX_REG); + if (is_in) { + status = udc_read(S3C2410_UDC_IN_CSR1_REG); + status = status & S3C2410_UDC_ICSR1_SENDSTL; + } else { + status = udc_read(S3C2410_UDC_OUT_CSR1_REG); + status = status & S3C2410_UDC_OCSR1_SENDSTL; + } + } + + status = status ? 1 : 0; + break; + + default: + return 1; + } + + /* Seems to be needed to get it working. ouch :( */ + udelay(5); + udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG); + udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG); + s3c2410_udc_set_ep0_de_in(base_addr); + + return 0; +} +/*------------------------- usb state machine -------------------------------*/ +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value); + +static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, + struct s3c2410_ep *ep, + struct usb_ctrlrequest *crq, + u32 ep0csr) +{ + int len, ret, tmp; + + /* start control request? */ + if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY)) + return; + + s3c2410_udc_nuke(dev, ep, -EPROTO); + + len = s3c2410_udc_read_fifo_crq(crq); + if (len != sizeof(*crq)) { + dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR" + " wanted %d bytes got %d. Stalling out...\n", + sizeof(*crq), len); + s3c2410_udc_set_ep0_ss(base_addr); + return; + } + + dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", + crq->bRequest, crq->bRequestType, crq->wLength); + + /* cope with automagic for some standard requests. */ + dev->req_std = (crq->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + + switch (crq->bRequest) { + case USB_REQ_SET_CONFIGURATION: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n"); + + if (crq->bRequestType == USB_RECIP_DEVICE) { + dev->req_config = 1; + s3c2410_udc_set_ep0_de_out(base_addr); + } + break; + + case USB_REQ_SET_INTERFACE: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n"); + + if (crq->bRequestType == USB_RECIP_INTERFACE) { + dev->req_config = 1; + s3c2410_udc_set_ep0_de_out(base_addr); + } + break; + + case USB_REQ_SET_ADDRESS: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n"); + + if (crq->bRequestType == USB_RECIP_DEVICE) { + tmp = crq->wValue & 0x7F; + dev->address = tmp; + udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE), + S3C2410_UDC_FUNC_ADDR_REG); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + } + break; + + case USB_REQ_GET_STATUS: + dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n"); + s3c2410_udc_clear_ep0_opr(base_addr); + + if (dev->req_std) { + if (!s3c2410_udc_get_status(dev, crq)) { + return; + } + } + break; + + case USB_REQ_CLEAR_FEATURE: + s3c2410_udc_clear_ep0_opr(base_addr); + + if (crq->bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) + break; + + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + + case USB_REQ_SET_FEATURE: + s3c2410_udc_clear_ep0_opr(base_addr); + + if (crq->bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) + break; + + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + + default: + s3c2410_udc_clear_ep0_opr(base_addr); + break; + } + + if (crq->bRequestType & USB_DIR_IN) + dev->ep0state = EP0_IN_DATA_PHASE; + else + dev->ep0state = EP0_OUT_DATA_PHASE; + + ret = dev->driver->setup(&dev->gadget, crq); + if (ret < 0) { + if (dev->req_config) { + dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n", + crq->bRequest, ret); + return; + } + + if (ret == -EOPNOTSUPP) + dprintk(DEBUG_NORMAL, "Operation not supported\n"); + else + dprintk(DEBUG_NORMAL, + "dev->driver->setup failed. (%d)\n", ret); + + udelay(5); + s3c2410_udc_set_ep0_ss(base_addr); + s3c2410_udc_set_ep0_de_out(base_addr); + dev->ep0state = EP0_IDLE; + /* deferred i/o == no response yet */ + } else if (dev->req_pending) { + dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n"); + dev->req_pending=0; + } + + dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]); +} + +static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev) +{ + u32 ep0csr; + struct s3c2410_ep *ep = &dev->ep[0]; + struct s3c2410_request *req; + struct usb_ctrlrequest crq; + + if (list_empty(&ep->queue)) + req = NULL; + else + req = list_entry(ep->queue.next, struct s3c2410_request, queue); + + /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to + * S3C2410_UDC_EP0_CSR_REG when index is zero */ + + udc_write(0, S3C2410_UDC_INDEX_REG); + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + + dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", + ep0csr, ep0states[dev->ep0state]); + + /* clear stall status */ + if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { + s3c2410_udc_nuke(dev, ep, -EPIPE); + dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n"); + s3c2410_udc_clear_ep0_sst(base_addr); + dev->ep0state = EP0_IDLE; + return; + } + + /* clear setup end */ + if (ep0csr & S3C2410_UDC_EP0_CSR_SE) { + dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n"); + s3c2410_udc_nuke(dev, ep, 0); + s3c2410_udc_clear_ep0_se(base_addr); + dev->ep0state = EP0_IDLE; + } + + switch (dev->ep0state) { + case EP0_IDLE: + s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); + break; + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n"); + if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) { + s3c2410_udc_write_fifo(ep, req); + } + break; + + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n"); + if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) { + s3c2410_udc_read_fifo(ep,req); + } + break; + + case EP0_END_XFER: + dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n"); + dev->ep0state = EP0_IDLE; + break; + + case EP0_STALL: + dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n"); + dev->ep0state = EP0_IDLE; + break; + } +} + +/* + * handle_ep - Manage I/O endpoints + */ + +static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) +{ + struct s3c2410_request *req; + int is_in = ep->bEndpointAddress & USB_DIR_IN; + u32 ep_csr1; + u32 idx; + + if (likely (!list_empty(&ep->queue))) + req = list_entry(ep->queue.next, + struct s3c2410_request, queue); + else + req = NULL; + + idx = ep->bEndpointAddress & 0x7F; + + if (is_in) { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", + idx, ep_csr1, req ? 1 : 0); + + if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) { + dprintk(DEBUG_VERBOSE, "st\n"); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, + S3C2410_UDC_IN_CSR1_REG); + return; + } + + if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) { + s3c2410_udc_write_fifo(ep,req); + } + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG); + dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1); + + if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) { + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, + S3C2410_UDC_OUT_CSR1_REG); + return; + } + + if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { + s3c2410_udc_read_fifo(ep,req); + } + } +} + +#include <asm/arch/regs-irq.h> + +/* + * s3c2410_udc_irq - interrupt handler + */ +static irqreturn_t s3c2410_udc_irq(int irq, void *_dev) +{ + struct s3c2410_udc *dev = _dev; + int usb_status; + int usbd_status; + int pwr_reg; + int ep0csr; + int i; + u32 idx; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + /* Driver connected ? */ + if (!dev->driver) { + /* Clear interrupts */ + udc_write(udc_read(S3C2410_UDC_USB_INT_REG), + S3C2410_UDC_USB_INT_REG); + udc_write(udc_read(S3C2410_UDC_EP_INT_REG), + S3C2410_UDC_EP_INT_REG); + } + + /* Save index */ + idx = udc_read(S3C2410_UDC_INDEX_REG); + + /* Read status registers */ + usb_status = udc_read(S3C2410_UDC_USB_INT_REG); + usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); + pwr_reg = udc_read(S3C2410_UDC_PWR_REG); + + udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + + dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", + usb_status, usbd_status, pwr_reg, ep0csr); + + /* + * Now, handle interrupts. There's two types : + * - Reset, Resume, Suspend coming -> usb_int_reg + * - EP -> ep_int_reg + */ + + /* RESET */ + if (usb_status & S3C2410_UDC_USBINT_RESET) { + /* two kind of reset : + * - reset start -> pwr reg = 8 + * - reset end -> pwr reg = 0 + **/ + dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", + ep0csr, pwr_reg); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + udc_write(0x00, S3C2410_UDC_INDEX_REG); + udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, + S3C2410_UDC_MAXP_REG); + dev->address = 0; + + dev->ep0state = EP0_IDLE; + dev->gadget.speed = USB_SPEED_FULL; + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_RESET, + S3C2410_UDC_USB_INT_REG); + + udc_write(idx, S3C2410_UDC_INDEX_REG); + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + /* RESUME */ + if (usb_status & S3C2410_UDC_USBINT_RESUME) { + dprintk(DEBUG_NORMAL, "USB resume\n"); + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_RESUME, + S3C2410_UDC_USB_INT_REG); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + + /* SUSPEND */ + if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { + dprintk(DEBUG_NORMAL, "USB suspend\n"); + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_REG); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + + dev->ep0state = EP0_IDLE; + } + + /* EP */ + /* control traffic */ + /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready + * generate an interrupt + */ + if (usbd_status & S3C2410_UDC_INT_EP0) { + dprintk(DEBUG_VERBOSE, "USB ep0 irq\n"); + /* Clear the interrupt bit by setting it to 1 */ + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG); + s3c2410_udc_handle_ep0(dev); + } + + /* endpoint data transfers */ + for (i = 1; i < S3C2410_ENDPOINTS; i++) { + u32 tmp = 1 << i; + if (usbd_status & tmp) { + dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i); + + /* Clear the interrupt bit by setting it to 1 */ + udc_write(tmp, S3C2410_UDC_EP_INT_REG); + s3c2410_udc_handle_ep(&dev->ep[i]); + } + } + + dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq); + + /* Restore old index */ + udc_write(idx, S3C2410_UDC_INDEX_REG); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} +/*------------------------- s3c2410_ep_ops ----------------------------------*/ + +static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep) +{ + return container_of(ep, struct s3c2410_ep, ep); +} + +static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct s3c2410_udc, gadget); +} + +static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req) +{ + return container_of(req, struct s3c2410_request, req); +} + +/* + * s3c2410_udc_ep_enable + */ +static int s3c2410_udc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c2410_udc *dev; + struct s3c2410_ep *ep; + u32 max, tmp; + unsigned long flags; + u32 csr1,csr2; + u32 int_en_reg; + + ep = to_s3c2410_ep(_ep); + + if (!_ep || !desc || ep->desc + || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + + local_irq_save (flags); + _ep->maxpacket = max & 0x7ff; + ep->desc = desc; + ep->halted = 0; + ep->bEndpointAddress = desc->bEndpointAddress; + + /* set max packet */ + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(max >> 3, S3C2410_UDC_MAXP_REG); + + /* set type, direction, address; reset fifo counters */ + if (desc->bEndpointAddress & USB_DIR_IN) { + csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT; + csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); + } else { + /* don't flush in fifo or it will cause endpoint interrupt */ + csr1 = S3C2410_UDC_ICSR1_CLRDT; + csr2 = S3C2410_UDC_ICSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); + + csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT; + csr2 = S3C2410_UDC_OCSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG); + } + + /* enable irqs */ + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG); + + /* print some debug message */ + tmp = desc->bEndpointAddress; + dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", + _ep->name,ep->num, tmp, + desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max); + + local_irq_restore (flags); + s3c2410_udc_set_halt(_ep, 0); + + return 0; +} + +/* + * s3c2410_udc_ep_disable + */ +static int s3c2410_udc_ep_disable(struct usb_ep *_ep) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + unsigned long flags; + u32 int_en_reg; + + if (!_ep || !ep->desc) { + dprintk(DEBUG_NORMAL, "%s not enabled\n", + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + local_irq_save(flags); + + dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); + + ep->desc = NULL; + ep->halted = 1; + + s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); + + /* disable irqs */ + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG); + + local_irq_restore(flags); + + dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name); + + return 0; +} + +/* + * s3c2410_udc_alloc_request + */ +static struct usb_request * +s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) +{ + struct s3c2410_request *req; + + dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags); + + if (!_ep) + return NULL; + + req = kzalloc (sizeof(struct s3c2410_request), mem_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + +/* + * s3c2410_udc_free_request + */ +static void +s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_request *req = to_s3c2410_req(_req); + + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); + + if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) + return; + + WARN_ON (!list_empty (&req->queue)); + kfree(req); +} + +/* + * s3c2410_udc_queue + */ +static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c2410_request *req = to_s3c2410_req(_req); + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_udc *dev; + u32 ep_csr = 0; + int fifo_count = 0; + unsigned long flags; + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely (!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + return -ESHUTDOWN; + } + + local_irq_save (flags); + + if (unlikely(!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue))) { + if (!_req) + dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__); + else { + dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", + __func__, !_req->complete,!_req->buf, + !list_empty(&req->queue)); + } + + local_irq_restore(flags); + return -EINVAL; + } + + _req->status = -EINPROGRESS; + _req->actual = 0; + + dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", + __func__, ep->bEndpointAddress, _req->length); + + if (ep->bEndpointAddress) { + udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG); + + ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) + ? S3C2410_UDC_IN_CSR1_REG + : S3C2410_UDC_OUT_CSR1_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + } else { + udc_write(0, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + } + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->halted) { + if (ep->bEndpointAddress == 0 /* ep0 */) { + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) + && s3c2410_udc_write_fifo(ep, + req)) { + dev->ep0state = EP0_IDLE; + req = NULL; + } + break; + + case EP0_OUT_DATA_PHASE: + if ((!_req->length) + || ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) + && s3c2410_udc_read_fifo(ep, + req))) { + dev->ep0state = EP0_IDLE; + req = NULL; + } + break; + + default: + local_irq_restore(flags); + return -EL2HLT; + } + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 + && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) + && s3c2410_udc_write_fifo(ep, req)) { + req = NULL; + } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) + && fifo_count + && s3c2410_udc_read_fifo(ep, req)) { + req = NULL; + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely (req != 0)) + list_add_tail(&req->queue, &ep->queue); + + local_irq_restore(flags); + + dprintk(DEBUG_VERBOSE, "%s ok\n", __func__); + return 0; +} + +/* + * s3c2410_udc_dequeue + */ +static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_udc *udc; + int retval = -EINVAL; + unsigned long flags; + struct s3c2410_request *req = NULL; + + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); + + if (!the_controller->driver) + return -ESHUTDOWN; + + if (!_ep || !_req) + return retval; + + udc = to_s3c2410_udc(ep->gadget); + + local_irq_save (flags); + + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) { + list_del_init (&req->queue); + _req->status = -ECONNRESET; + retval = 0; + break; + } + } + + if (retval == 0) { + dprintk(DEBUG_VERBOSE, + "dequeued req %p from %s, len %d buf %p\n", + req, _ep->name, _req->length, _req->buf); + + s3c2410_udc_done(ep, req, -ECONNRESET); + } + + local_irq_restore (flags); + return retval; +} + +/* + * s3c2410_udc_set_halt + */ +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + u32 ep_csr = 0; + unsigned long flags; + u32 idx; + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__); + return -EINVAL; + } + + local_irq_save (flags); + + idx = ep->bEndpointAddress & 0x7F; + + if (idx == 0) { + s3c2410_udc_set_ep0_ss(base_addr); + s3c2410_udc_set_ep0_de_out(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) + ? S3C2410_UDC_IN_CSR1_REG + : S3C2410_UDC_OUT_CSR1_REG); + + if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if (value) + udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL, + S3C2410_UDC_IN_CSR1_REG); + else { + ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL; + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); + ep_csr |= S3C2410_UDC_ICSR1_CLRDT; + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); + } + } else { + if (value) + udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL, + S3C2410_UDC_OUT_CSR1_REG); + else { + ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL; + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); + ep_csr |= S3C2410_UDC_OCSR1_CLRDT; + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); + } + } + } + + ep->halted = value ? 1 : 0; + local_irq_restore (flags); + + return 0; +} + +static const struct usb_ep_ops s3c2410_ep_ops = { + .enable = s3c2410_udc_ep_enable, + .disable = s3c2410_udc_ep_disable, + + .alloc_request = s3c2410_udc_alloc_request, + .free_request = s3c2410_udc_free_request, + + .queue = s3c2410_udc_queue, + .dequeue = s3c2410_udc_dequeue, + + .set_halt = s3c2410_udc_set_halt, +}; + +/*------------------------- usb_gadget_ops ----------------------------------*/ + +/* + * s3c2410_udc_get_frame + */ +static int s3c2410_udc_get_frame(struct usb_gadget *_gadget) +{ + int tmp; + + dprintk(DEBUG_VERBOSE, "%s()\n", __func__); + + tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8; + tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG); + return tmp; +} + +/* + * s3c2410_udc_wakeup + */ +static int s3c2410_udc_wakeup(struct usb_gadget *_gadget) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + return 0; +} + +/* + * s3c2410_udc_set_selfpowered + */ +static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (value) + udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + + return 0; +} + +static void s3c2410_udc_disable(struct s3c2410_udc *dev); +static void s3c2410_udc_enable(struct s3c2410_udc *dev); + +static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (udc_info && udc_info->udc_command) { + if (is_on) + s3c2410_udc_enable(udc); + else { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + if (udc->driver && udc->driver->disconnect) + udc->driver->disconnect(&udc->gadget); + + } + s3c2410_udc_disable(udc); + } + } + else + return -EOPNOTSUPP; + + return 0; +} + +static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + udc->vbus = (is_active != 0); + s3c2410_udc_set_pullup(udc, is_active); + return 0; +} + +static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + s3c2410_udc_set_pullup(udc, is_on ? 0 : 1); + return 0; +} + +static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) +{ + struct s3c2410_udc *dev = _dev; + unsigned int value; + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + value = s3c2410_gpio_getpin(udc_info->vbus_pin); + + if (udc_info->vbus_pin_inverted) + value = !value; + + if (value != dev->vbus) + s3c2410_udc_vbus_session(&dev->gadget, value); + + return IRQ_HANDLED; +} + +static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (udc_info && udc_info->vbus_draw) { + udc_info->vbus_draw(ma); + return 0; + } + + return -ENOTSUPP; +} + +static const struct usb_gadget_ops s3c2410_ops = { + .get_frame = s3c2410_udc_get_frame, + .wakeup = s3c2410_udc_wakeup, + .set_selfpowered = s3c2410_udc_set_selfpowered, + .pullup = s3c2410_udc_pullup, + .vbus_session = s3c2410_udc_vbus_session, + .vbus_draw = s3c2410_vbus_draw, +}; + +/*------------------------- gadget driver handling---------------------------*/ +/* + * s3c2410_udc_disable + */ +static void s3c2410_udc_disable(struct s3c2410_udc *dev) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + /* Disable all interrupts */ + udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG); + udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG); + + /* Clear the interrupt registers */ + udc_write(S3C2410_UDC_USBINT_RESET + | S3C2410_UDC_USBINT_RESUME + | S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_REG); + + udc_write(0x1F, S3C2410_UDC_EP_INT_REG); + + /* Good bye, cruel world */ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_DISABLE); + + /* Set speed to unknown */ + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * s3c2410_udc_reinit + */ +static void s3c2410_udc_reinit(struct s3c2410_udc *dev) +{ + u32 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD (&dev->gadget.ep_list); + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + dev->ep0state = EP0_IDLE; + + for (i = 0; i < S3C2410_ENDPOINTS; i++) { + struct s3c2410_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->dev = dev; + ep->desc = NULL; + ep->halted = 0; + INIT_LIST_HEAD (&ep->queue); + } +} + +/* + * s3c2410_udc_enable + */ +static void s3c2410_udc_enable(struct s3c2410_udc *dev) +{ + int i; + + dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n"); + + /* dev->gadget.speed = USB_SPEED_UNKNOWN; */ + dev->gadget.speed = USB_SPEED_FULL; + + /* Set MAXP for all endpoints */ + for (i = 0; i < S3C2410_ENDPOINTS; i++) { + udc_write(i, S3C2410_UDC_INDEX_REG); + udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3, + S3C2410_UDC_MAXP_REG); + } + + /* Set default power state */ + udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG); + + /* Enable reset and suspend interrupt interrupts */ + udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_EN_REG); + + /* Enable ep0 interrupt */ + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG); + + /* time to say "hello, world" */ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_ENABLE); +} + +/* + * usb_gadget_register_driver + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c2410_udc *udc = the_controller; + int retval; + + dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n", + driver->driver.name); + + /* Sanity checks */ + if (!udc) + return -ENODEV; + + if (udc->driver) + return -EBUSY; + + if (!driver->bind || !driver->setup + || driver->speed != USB_SPEED_FULL) { + printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", + driver->bind, driver->setup, driver->speed); + return -EINVAL; + } +#if defined(MODULE) + if (!driver->unbind) { + printk(KERN_ERR "Invalid driver: no unbind method\n"); + return -EINVAL; + } +#endif + + /* Hook the driver */ + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + + /* Bind the driver */ + if ((retval = device_add(&udc->gadget.dev)) != 0) { + printk(KERN_ERR "Error in device_add() : %d\n",retval); + goto register_error; + } + + dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", + driver->driver.name); + + if ((retval = driver->bind (&udc->gadget)) != 0) { + device_del(&udc->gadget.dev); + goto register_error; + } + + /* Enable udc */ + s3c2410_udc_enable(udc); + + return 0; + +register_error: + udc->driver = NULL; + udc->gadget.dev.driver = NULL; + return retval; +} + +/* + * usb_gadget_unregister_driver + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c2410_udc *udc = the_controller; + + if (!udc) + return -ENODEV; + + if (!driver || driver != udc->driver || !driver->unbind) + return -EINVAL; + + dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", + driver->driver.name); + + if (driver->disconnect) + driver->disconnect(&udc->gadget); + + device_del(&udc->gadget.dev); + udc->driver = NULL; + + /* Disable udc */ + s3c2410_udc_disable(udc); + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static struct s3c2410_udc memory = { + .gadget = { + .ops = &s3c2410_ops, + .ep0 = &memory.ep[0].ep, + .name = gadget_name, + .dev = { + .bus_id = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .num = 0, + .ep = { + .name = ep0name, + .ops = &s3c2410_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + }, + + /* first group of endpoints */ + .ep[1] = { + .num = 1, + .ep = { + .name = "ep1-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[2] = { + .num = 2, + .ep = { + .name = "ep2-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[3] = { + .num = 3, + .ep = { + .name = "ep3-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[4] = { + .num = 4, + .ep = { + .name = "ep4-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 4, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + } + +}; + +/* + * probe - binds to the platform device + */ +static int s3c2410_udc_probe(struct platform_device *pdev) +{ + struct s3c2410_udc *udc = &memory; + struct device *dev = &pdev->dev; + int retval; + unsigned int irq; + + dev_dbg(dev, "%s()\n", __func__); + + usb_bus_clock = clk_get(NULL, "usb-bus-gadget"); + if (IS_ERR(usb_bus_clock)) { + dev_err(dev, "failed to get usb bus clock source\n"); + return PTR_ERR(usb_bus_clock); + } + + clk_enable(usb_bus_clock); + + udc_clock = clk_get(NULL, "usb-device"); + if (IS_ERR(udc_clock)) { + dev_err(dev, "failed to get udc clock source\n"); + return PTR_ERR(udc_clock); + } + + clk_enable(udc_clock); + + mdelay(10); + + dev_dbg(dev, "got and enabled clocks\n"); + + if (strncmp(pdev->name, "s3c2440", 7) == 0) { + dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n"); + memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE; + } + + spin_lock_init (&udc->lock); + udc_info = pdev->dev.platform_data; + + rsrc_start = S3C2410_PA_USBDEV; + rsrc_len = S3C24XX_SZ_USBDEV; + + if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) + return -EBUSY; + + base_addr = ioremap(rsrc_start, rsrc_len); + if (!base_addr) { + retval = -ENOMEM; + goto err_mem; + } + + device_initialize(&udc->gadget.dev); + udc->gadget.dev.parent = &pdev->dev; + udc->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_controller = udc; + platform_set_drvdata(pdev, udc); + + s3c2410_udc_disable(udc); + s3c2410_udc_reinit(udc); + + /* irq setup after old hardware state is cleaned up */ + retval = request_irq(IRQ_USBD, s3c2410_udc_irq, + IRQF_DISABLED, gadget_name, udc); + + if (retval != 0) { + dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); + retval = -EBUSY; + goto err_map; + } + + dev_dbg(dev, "got irq %i\n", IRQ_USBD); + + if (udc_info && udc_info->vbus_pin > 0) { + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + retval = request_irq(irq, s3c2410_udc_vbus_irq, + IRQF_DISABLED | IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING, + gadget_name, udc); + + if (retval != 0) { + dev_err(dev, "can't get vbus irq %i, err %d\n", + irq, retval); + retval = -EBUSY; + goto err_int; + } + + dev_dbg(dev, "got irq %i\n", irq); + } else { + udc->vbus = 1; + } + + if (s3c2410_udc_debugfs_root) { + udc->regs_info = debugfs_create_file("registers", S_IRUGO, + s3c2410_udc_debugfs_root, + udc, &s3c2410_udc_debugfs_fops); + if (IS_ERR(udc->regs_info)) { + dev_warn(dev, "debugfs file creation failed %ld\n", + PTR_ERR(udc->regs_info)); + udc->regs_info = NULL; + } + } + + dev_dbg(dev, "probe ok\n"); + + return 0; + +err_int: + free_irq(IRQ_USBD, udc); +err_map: + iounmap(base_addr); +err_mem: + release_mem_region(rsrc_start, rsrc_len); + + return retval; +} + +/* + * s3c2410_udc_remove + */ +static int s3c2410_udc_remove(struct platform_device *pdev) +{ + struct s3c2410_udc *udc = platform_get_drvdata(pdev); + unsigned int irq; + + dev_dbg(&pdev->dev, "%s()\n", __func__); + if (udc->driver) + return -EBUSY; + + debugfs_remove(udc->regs_info); + + if (udc_info && udc_info->vbus_pin > 0) { + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + free_irq(irq, udc); + } + + free_irq(IRQ_USBD, udc); + + iounmap(base_addr); + release_mem_region(rsrc_start, rsrc_len); + + platform_set_drvdata(pdev, NULL); + + if (!IS_ERR(udc_clock) && udc_clock != NULL) { + clk_disable(udc_clock); + clk_put(udc_clock); + udc_clock = NULL; + } + + if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { + clk_disable(usb_bus_clock); + clk_put(usb_bus_clock); + usb_bus_clock = NULL; + } + + dev_dbg(&pdev->dev, "%s: remove ok\n", __func__); + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message) +{ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_DISABLE); + + return 0; +} + +static int s3c2410_udc_resume(struct platform_device *pdev) +{ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_ENABLE); + + return 0; +} +#else +#define s3c2410_udc_suspend NULL +#define s3c2410_udc_resume NULL +#endif + +static struct platform_driver udc_driver_2410 = { + .driver = { + .name = "s3c2410-usbgadget", + .owner = THIS_MODULE, + }, + .probe = s3c2410_udc_probe, + .remove = s3c2410_udc_remove, + .suspend = s3c2410_udc_suspend, + .resume = s3c2410_udc_resume, +}; + +static struct platform_driver udc_driver_2440 = { + .driver = { + .name = "s3c2440-usbgadget", + .owner = THIS_MODULE, + }, + .probe = s3c2410_udc_probe, + .remove = s3c2410_udc_remove, + .suspend = s3c2410_udc_suspend, + .resume = s3c2410_udc_resume, +}; + +static int __init udc_init(void) +{ + int retval; + + dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION); + + s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); + if (IS_ERR(s3c2410_udc_debugfs_root)) { + printk(KERN_ERR "%s: debugfs dir creation failed %ld\n", + gadget_name, PTR_ERR(s3c2410_udc_debugfs_root)); + s3c2410_udc_debugfs_root = NULL; + } + + retval = platform_driver_register(&udc_driver_2410); + if (retval) + goto err; + + retval = platform_driver_register(&udc_driver_2440); + if (retval) + goto err; + + return 0; + +err: + debugfs_remove(s3c2410_udc_debugfs_root); + return retval; +} + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver_2410); + platform_driver_unregister(&udc_driver_2440); + debugfs_remove(s3c2410_udc_debugfs_root); +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); +EXPORT_SYMBOL(usb_gadget_register_driver); + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h new file mode 100644 index 00000000000..9e0bece4f24 --- /dev/null +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -0,0 +1,110 @@ +/* + * linux/drivers/usb/gadget/s3c2410_udc.h + * Samsung on-chip full speed USB device controllers + * + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Additional cleanups by Ben Dooks <ben-linux@fluff.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _S3C2410_UDC_H +#define _S3C2410_UDC_H + +struct s3c2410_ep { + struct list_head queue; + unsigned long last_io; /* jiffies timestamp */ + struct usb_gadget *gadget; + struct s3c2410_udc *dev; + const struct usb_endpoint_descriptor *desc; + struct usb_ep ep; + u8 num; + + unsigned short fifo_size; + u8 bEndpointAddress; + u8 bmAttributes; + + unsigned halted : 1; + unsigned already_seen : 1; + unsigned setup_stage : 1; +}; + + +/* Warning : ep0 has a fifo of 16 bytes */ +/* Don't try to set 32 or 64 */ +/* also testusb 14 fails wit 16 but is */ +/* fine with 8 */ +#define EP0_FIFO_SIZE 8 +#define EP_FIFO_SIZE 64 +#define DEFAULT_POWER_STATE 0x00 + +#define S3C2440_EP_FIFO_SIZE 128 + +static const char ep0name [] = "ep0"; + +static const char *const ep_name[] = { + ep0name, /* everyone has ep0 */ + /* s3c2410 four bidirectional bulk endpoints */ + "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk", +}; + +#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name) + +struct s3c2410_request { + struct list_head queue; /* ep's requests */ + struct usb_request req; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +static const char *ep0states[]= { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", + "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", + "EP0_STALL", +}; + +struct s3c2410_udc { + spinlock_t lock; + + struct s3c2410_ep ep[S3C2410_ENDPOINTS]; + int address; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct s3c2410_request fifo_req; + u8 fifo_buf[EP_FIFO_SIZE]; + u16 devstatus; + + u32 port_status; + int ep0state; + + unsigned got_irq : 1; + + unsigned req_std : 1; + unsigned req_config : 1; + unsigned req_pending : 1; + u8 vbus; + struct dentry *regs_info; +}; + +#endif diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f847c3414be..9cd98e73dc1 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -33,6 +33,7 @@ #include <linux/device.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/mutex.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -258,7 +259,7 @@ static const char *EP_IN_NAME; static const char *EP_OUT_NAME; static const char *EP_NOTIFY_NAME; -static struct semaphore gs_open_close_sem[GS_NUM_PORTS]; +static struct mutex gs_open_close_lock[GS_NUM_PORTS]; static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE; static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE; @@ -595,7 +596,7 @@ static int __init gs_module_init(void) tty_set_operations(gs_tty_driver, &gs_tty_ops); for (i=0; i < GS_NUM_PORTS; i++) - sema_init(&gs_open_close_sem[i], 1); + mutex_init(&gs_open_close_lock[i]); retval = tty_register_driver(gs_tty_driver); if (retval) { @@ -635,7 +636,7 @@ static int gs_open(struct tty_struct *tty, struct file *file) struct gs_port *port; struct gs_dev *dev; struct gs_buf *buf; - struct semaphore *sem; + struct mutex *mtx; int ret; port_num = tty->index; @@ -656,10 +657,10 @@ static int gs_open(struct tty_struct *tty, struct file *file) return -ENODEV; } - sem = &gs_open_close_sem[port_num]; - if (down_interruptible(sem)) { + mtx = &gs_open_close_lock[port_num]; + if (mutex_lock_interruptible(mtx)) { printk(KERN_ERR - "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n", + "gs_open: (%d,%p,%p) interrupted waiting for mutex\n", port_num, tty, file); return -ERESTARTSYS; } @@ -754,12 +755,12 @@ static int gs_open(struct tty_struct *tty, struct file *file) exit_unlock_port: spin_unlock_irqrestore(&port->port_lock, flags); - up(sem); + mutex_unlock(mtx); return ret; exit_unlock_dev: spin_unlock_irqrestore(&dev->dev_lock, flags); - up(sem); + mutex_unlock(mtx); return ret; } @@ -781,7 +782,7 @@ exit_unlock_dev: static void gs_close(struct tty_struct *tty, struct file *file) { struct gs_port *port = tty->driver_data; - struct semaphore *sem; + struct mutex *mtx; if (port == NULL) { printk(KERN_ERR "gs_close: NULL port pointer\n"); @@ -790,8 +791,8 @@ static void gs_close(struct tty_struct *tty, struct file *file) gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); - sem = &gs_open_close_sem[port->port_num]; - down(sem); + mtx = &gs_open_close_lock[port->port_num]; + mutex_lock(mtx); spin_lock_irq(&port->port_lock); @@ -846,7 +847,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) exit: spin_unlock_irq(&port->port_lock); - up(sem); + mutex_unlock(mtx); } /* @@ -1427,7 +1428,7 @@ static int __init gs_bind(struct usb_gadget *gadget) gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - gs_device = dev = kmalloc(sizeof(struct gs_dev), GFP_KERNEL); + gs_device = dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; @@ -1435,7 +1436,6 @@ static int __init gs_bind(struct usb_gadget *gadget) init_utsname()->sysname, init_utsname()->release, gadget->name); - memset(dev, 0, sizeof(struct gs_dev)); dev->dev_gadget = gadget; spin_lock_init(&dev->dev_lock); INIT_LIST_HEAD(&dev->dev_req_list); @@ -2215,7 +2215,7 @@ static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) * * Free the buffer and all associated memory. */ -void gs_buf_free(struct gs_buf *gb) +static void gs_buf_free(struct gs_buf *gb) { if (gb) { kfree(gb->buf_buf); @@ -2228,7 +2228,7 @@ void gs_buf_free(struct gs_buf *gb) * * Clear out all data in the circular buffer. */ -void gs_buf_clear(struct gs_buf *gb) +static void gs_buf_clear(struct gs_buf *gb) { if (gb != NULL) gb->buf_get = gb->buf_put; @@ -2241,7 +2241,7 @@ void gs_buf_clear(struct gs_buf *gb) * Return the number of bytes of data available in the circular * buffer. */ -unsigned int gs_buf_data_avail(struct gs_buf *gb) +static unsigned int gs_buf_data_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; @@ -2255,7 +2255,7 @@ unsigned int gs_buf_data_avail(struct gs_buf *gb) * Return the number of bytes of space available in the circular * buffer. */ -unsigned int gs_buf_space_avail(struct gs_buf *gb) +static unsigned int gs_buf_space_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; @@ -2271,7 +2271,8 @@ unsigned int gs_buf_space_avail(struct gs_buf *gb) * * Return the number of bytes copied. */ -unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) +static unsigned int +gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) { unsigned int len; @@ -2309,7 +2310,8 @@ unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) * * Return the number of bytes copied. */ -unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) +static unsigned int +gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) { unsigned int len; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 7078374d0b7..a2e6e3fc8c8 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -481,8 +481,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length) req = usb_ep_alloc_request (ep, GFP_ATOMIC); if (req) { req->length = length; - req->buf = usb_ep_alloc_buffer (ep, length, - &req->dma, GFP_ATOMIC); + req->buf = kmalloc(length, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request (ep, req); req = NULL; @@ -493,8 +492,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length) static void free_ep_req (struct usb_ep *ep, struct usb_request *req) { - if (req->buf) - usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + kfree(req->buf); usb_ep_free_request (ep, req); } @@ -1199,8 +1197,7 @@ autoconf_fail: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); if (!dev->req) goto enomem; - dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, - &dev->req->dma, GFP_KERNEL); + dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); if (!dev->req->buf) goto enomem; |