diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-04 11:09:45 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-04 11:09:45 -0700 |
| commit | 8fb27ba3e8bedc65d94dad70455e507043faf6b1 (patch) | |
| tree | 7ad1eccaa1421ccb54a1133373fd1bfa3fd66e4d /usb | |
| parent | a071bc3b65bcad3b691b868652930b4cadb09d15 (diff) | |
| download | patches-8fb27ba3e8bedc65d94dad70455e507043faf6b1.tar.gz | |
2 usb drivers and some staging stuff
Diffstat (limited to 'usb')
| -rw-r--r-- | usb/usb-add-intel-langwell-usb-device-controller-driver.patch | 4047 | ||||
| -rw-r--r-- | usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch | 2173 |
2 files changed, 6220 insertions, 0 deletions
diff --git a/usb/usb-add-intel-langwell-usb-device-controller-driver.patch b/usb/usb-add-intel-langwell-usb-device-controller-driver.patch new file mode 100644 index 00000000000000..5ce6a79ac7f08e --- /dev/null +++ b/usb/usb-add-intel-langwell-usb-device-controller-driver.patch @@ -0,0 +1,4047 @@ +From xiaochen.shen@intel.com Thu Jun 4 10:58:51 2009 +From: Xiaochen Shen <xiaochen.shen@intel.com> +Date: Thu, 4 Jun 2009 15:34:49 +0800 +Subject: USB: Add Intel Langwell USB Device Controller driver +Message-ID: <20090604073449.GA24218@intel.com> + + +From: Xiaochen Shen <xiaochen.shen@intel.com> + +Intel Langwell USB Device Controller is a High-Speed USB OTG device +controller in Intel Moorestown platform. It can work in OTG device mode +with Intel Langwell USB OTG transceiver driver as well as device-only +mode. The number of programmable endpoints is different through +controller revision. + +NOTE: +This patch is the first version Intel Langwell USB OTG device controller +driver. The bug fixing is on going for some hardware and software +issues. Intel Langwell USB OTG transceiver driver and EHCI driver +patches will be submitted later. + +Supported features: + - USB OTG protocol support with Intel Langwell USB OTG transceiver + driver (turn on CONFIG_USB_LANGWELL_OTG) + - Support control, bulk, interrupt and isochronous endpoints + (isochronous not tested) + - PCI D0/D3 power management support + - Link Power Management (LPM) support + +Tested gadget drivers: + - g_file_storage + - g_ether + - g_zero + +The passed tests: + - g_file_storage: USBCV Chapter 9 tests + - g_file_storage: USBCV MSC tests + - g_file_storage: from/to host files copying + - g_ether: ping, ftp and scp files from/to host + - Hotplug, with and without hubs + +Known issues: + - g_ether: failed part of USBCV chap9 tests + - LPM support not fully tested + +TODO: + - g_ether: pass all USBCV chap9 tests + - g_zero: pass usbtest tests + - Stress tests on different gadget drivers + - On-chip private SRAM caching support + +Signed-off-by: Xiaochen Shen <xiaochen.shen@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Kconfig | 21 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 8 + drivers/usb/gadget/langwell_udc.c | 3373 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/langwell_udc.h | 228 ++ + include/linux/usb/langwell_udc.h | 310 +++ + 6 files changed, 3941 insertions(+) + +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -137,6 +137,12 @@ + #define gadget_is_musbhdrc(g) 0 + #endif + ++#ifdef CONFIG_USB_GADGET_LANGWELL ++#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name)) ++#else ++#define gadget_is_langwell(g) 0 ++#endif ++ + /* from Montavista kernel (?) */ + #ifdef CONFIG_USB_GADGET_MPC8272 + #define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) +@@ -231,6 +237,8 @@ static inline int usb_gadget_controller_ + return 0x22; + else if (gadget_is_ci13xxx(gadget)) + return 0x23; ++ else if (gadget_is_langwell(gadget)) ++ return 0x24; + return -ENOENT; + } + +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -474,6 +474,27 @@ config USB_GOKU + default USB_GADGET + select USB_GADGET_SELECTED + ++config USB_GADGET_LANGWELL ++ boolean "Intel Langwell USB Device Controller" ++ depends on PCI ++ select USB_GADGET_DUALSPEED ++ help ++ Intel Langwell USB Device Controller is a High-Speed USB ++ On-The-Go device controller. ++ ++ The number of programmable endpoints is different through ++ controller revision. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "langwell_udc" and force all ++ gadget drivers to also be dynamically linked. ++ ++config USB_LANGWELL ++ tristate ++ depends on USB_GADGET_LANGWELL ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ + + # + # LAST -- dummy/emulated controller +--- /dev/null ++++ b/drivers/usb/gadget/langwell_udc.c +@@ -0,0 +1,3373 @@ ++/* ++ * Intel Langwell USB Device Controller driver ++ * Copyright (C) 2008-2009, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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. ++ * ++ */ ++ ++ ++/* #undef DEBUG */ ++/* #undef VERBOSE */ ++ ++#if defined(CONFIG_USB_LANGWELL_OTG) ++#define OTG_TRANSCEIVER ++#endif ++ ++ ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/dma-mapping.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/moduleparam.h> ++#include <linux/device.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++#include <linux/usb/otg.h> ++#include <linux/pm.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <asm/system.h> ++#include <asm/unaligned.h> ++ ++#include "langwell_udc.h" ++ ++ ++#define DRIVER_DESC "Intel Langwell USB Device Controller driver" ++#define DRIVER_VERSION "16 May 2009" ++ ++static const char driver_name[] = "langwell_udc"; ++static const char driver_desc[] = DRIVER_DESC; ++ ++ ++/* controller device global variable */ ++static struct langwell_udc *the_controller; ++ ++/* for endpoint 0 operations */ ++static const struct usb_endpoint_descriptor ++langwell_ep0_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = 0, ++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, ++ .wMaxPacketSize = EP0_MAX_PKT_SIZE, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++/* debugging */ ++ ++#ifdef DEBUG ++#define DBG(dev, fmt, args...) \ ++ pr_debug("%s %s: " fmt , driver_name, \ ++ pci_name(dev->pdev), ## args) ++#else ++#define DBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++ ++#ifdef VERBOSE ++#define VDBG DBG ++#else ++#define VDBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* VERBOSE */ ++ ++ ++#define ERROR(dev, fmt, args...) \ ++ pr_err("%s %s: " fmt , driver_name, \ ++ pci_name(dev->pdev), ## args) ++ ++#define WARNING(dev, fmt, args...) \ ++ pr_warning("%s %s: " fmt , driver_name, \ ++ pci_name(dev->pdev), ## args) ++ ++#define INFO(dev, fmt, args...) \ ++ pr_info("%s %s: " fmt , driver_name, \ ++ pci_name(dev->pdev), ## args) ++ ++ ++#ifdef VERBOSE ++static inline void print_all_registers(struct langwell_udc *dev) ++{ ++ int i; ++ ++ /* Capability Registers */ ++ printk(KERN_DEBUG "Capability Registers (offset: " ++ "0x%04x, length: 0x%08x)\n", ++ CAP_REG_OFFSET, ++ (u32)sizeof(struct langwell_cap_regs)); ++ printk(KERN_DEBUG "caplength=0x%02x\n", ++ readb(&dev->cap_regs->caplength)); ++ printk(KERN_DEBUG "hciversion=0x%04x\n", ++ readw(&dev->cap_regs->hciversion)); ++ printk(KERN_DEBUG "hcsparams=0x%08x\n", ++ readl(&dev->cap_regs->hcsparams)); ++ printk(KERN_DEBUG "hccparams=0x%08x\n", ++ readl(&dev->cap_regs->hccparams)); ++ printk(KERN_DEBUG "dciversion=0x%04x\n", ++ readw(&dev->cap_regs->dciversion)); ++ printk(KERN_DEBUG "dccparams=0x%08x\n", ++ readl(&dev->cap_regs->dccparams)); ++ ++ /* Operational Registers */ ++ printk(KERN_DEBUG "Operational Registers (offset: " ++ "0x%04x, length: 0x%08x)\n", ++ OP_REG_OFFSET, ++ (u32)sizeof(struct langwell_op_regs)); ++ printk(KERN_DEBUG "extsts=0x%08x\n", ++ readl(&dev->op_regs->extsts)); ++ printk(KERN_DEBUG "extintr=0x%08x\n", ++ readl(&dev->op_regs->extintr)); ++ printk(KERN_DEBUG "usbcmd=0x%08x\n", ++ readl(&dev->op_regs->usbcmd)); ++ printk(KERN_DEBUG "usbsts=0x%08x\n", ++ readl(&dev->op_regs->usbsts)); ++ printk(KERN_DEBUG "usbintr=0x%08x\n", ++ readl(&dev->op_regs->usbintr)); ++ printk(KERN_DEBUG "frindex=0x%08x\n", ++ readl(&dev->op_regs->frindex)); ++ printk(KERN_DEBUG "ctrldssegment=0x%08x\n", ++ readl(&dev->op_regs->ctrldssegment)); ++ printk(KERN_DEBUG "deviceaddr=0x%08x\n", ++ readl(&dev->op_regs->deviceaddr)); ++ printk(KERN_DEBUG "endpointlistaddr=0x%08x\n", ++ readl(&dev->op_regs->endpointlistaddr)); ++ printk(KERN_DEBUG "ttctrl=0x%08x\n", ++ readl(&dev->op_regs->ttctrl)); ++ printk(KERN_DEBUG "burstsize=0x%08x\n", ++ readl(&dev->op_regs->burstsize)); ++ printk(KERN_DEBUG "txfilltuning=0x%08x\n", ++ readl(&dev->op_regs->txfilltuning)); ++ printk(KERN_DEBUG "txttfilltuning=0x%08x\n", ++ readl(&dev->op_regs->txttfilltuning)); ++ printk(KERN_DEBUG "ic_usb=0x%08x\n", ++ readl(&dev->op_regs->ic_usb)); ++ printk(KERN_DEBUG "ulpi_viewport=0x%08x\n", ++ readl(&dev->op_regs->ulpi_viewport)); ++ printk(KERN_DEBUG "configflag=0x%08x\n", ++ readl(&dev->op_regs->configflag)); ++ printk(KERN_DEBUG "portsc1=0x%08x\n", ++ readl(&dev->op_regs->portsc1)); ++ printk(KERN_DEBUG "devlc=0x%08x\n", ++ readl(&dev->op_regs->devlc)); ++ printk(KERN_DEBUG "otgsc=0x%08x\n", ++ readl(&dev->op_regs->otgsc)); ++ printk(KERN_DEBUG "usbmode=0x%08x\n", ++ readl(&dev->op_regs->usbmode)); ++ printk(KERN_DEBUG "endptnak=0x%08x\n", ++ readl(&dev->op_regs->endptnak)); ++ printk(KERN_DEBUG "endptnaken=0x%08x\n", ++ readl(&dev->op_regs->endptnaken)); ++ printk(KERN_DEBUG "endptsetupstat=0x%08x\n", ++ readl(&dev->op_regs->endptsetupstat)); ++ printk(KERN_DEBUG "endptprime=0x%08x\n", ++ readl(&dev->op_regs->endptprime)); ++ printk(KERN_DEBUG "endptflush=0x%08x\n", ++ readl(&dev->op_regs->endptflush)); ++ printk(KERN_DEBUG "endptstat=0x%08x\n", ++ readl(&dev->op_regs->endptstat)); ++ printk(KERN_DEBUG "endptcomplete=0x%08x\n", ++ readl(&dev->op_regs->endptcomplete)); ++ ++ for (i = 0; i < dev->ep_max / 2; i++) { ++ printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n", ++ i, readl(&dev->op_regs->endptctrl[i])); ++ } ++} ++#endif /* VERBOSE */ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") ++ ++#define is_in(ep) (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \ ++ USB_DIR_IN) : ((ep)->desc->bEndpointAddress \ ++ & USB_DIR_IN) == USB_DIR_IN) ++ ++ ++#ifdef DEBUG ++static char *type_string(u8 bmAttributes) ++{ ++ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ return "bulk"; ++ case USB_ENDPOINT_XFER_ISOC: ++ return "iso"; ++ case USB_ENDPOINT_XFER_INT: ++ return "int"; ++ }; ++ ++ return "control"; ++} ++#endif ++ ++ ++/* configure endpoint control registers */ ++static void ep_reset(struct langwell_ep *ep, unsigned char ep_num, ++ unsigned char is_in, unsigned char ep_type) ++{ ++ struct langwell_udc *dev; ++ u32 endptctrl; ++ ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); ++ if (is_in) { /* TX */ ++ if (ep_num) ++ endptctrl |= EPCTRL_TXR; ++ endptctrl |= EPCTRL_TXE; ++ endptctrl |= ep_type << EPCTRL_TXT_SHIFT; ++ } else { /* RX */ ++ if (ep_num) ++ endptctrl |= EPCTRL_RXR; ++ endptctrl |= EPCTRL_RXE; ++ endptctrl |= ep_type << EPCTRL_RXT_SHIFT; ++ } ++ ++ writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* reset ep0 dQH and endptctrl */ ++static void ep0_reset(struct langwell_udc *dev) ++{ ++ struct langwell_ep *ep; ++ int i; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* ep0 in and out */ ++ for (i = 0; i < 2; i++) { ++ ep = &dev->ep[i]; ++ ep->dev = dev; ++ ++ /* ep0 dQH */ ++ ep->dqh = &dev->ep_dqh[i]; ++ ++ /* configure ep0 endpoint capabilities in dQH */ ++ ep->dqh->dqh_ios = 1; ++ ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE; ++ ++ /* FIXME: enable ep0-in HW zero length termination select */ ++ if (is_in(ep)) ++ ep->dqh->dqh_zlt = 0; ++ ep->dqh->dqh_mult = 0; ++ ++ /* configure ep0 control registers */ ++ ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL); ++ } ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* endpoints operations */ ++ ++/* configure endpoint, making it usable */ ++static int langwell_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct langwell_udc *dev; ++ struct langwell_ep *ep; ++ u16 max = 0; ++ unsigned long flags; ++ int retval = 0; ++ unsigned char zlt, ios = 0, mult = 0; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !desc || ep->desc ++ || desc->bDescriptorType != USB_DT_ENDPOINT) ++ return -EINVAL; ++ ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ max = le16_to_cpu(desc->wMaxPacketSize); ++ ++ /* ++ * disable HW zero length termination select ++ * driver handles zero length packet through req->req.zero ++ */ ++ zlt = 1; ++ ++ /* ++ * sanity check type, direction, address, and then ++ * initialize the endpoint capabilities fields in dQH ++ */ ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ ios = 1; ++ break; ++ case USB_ENDPOINT_XFER_BULK: ++ if ((dev->gadget.speed == USB_SPEED_HIGH ++ && max != 512) ++ || (dev->gadget.speed == USB_SPEED_FULL ++ && max > 64)) { ++ goto done; ++ } ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ ++ goto done; ++ ++ switch (dev->gadget.speed) { ++ case USB_SPEED_HIGH: ++ if (max <= 1024) ++ break; ++ case USB_SPEED_FULL: ++ if (max <= 64) ++ break; ++ default: ++ if (max <= 8) ++ break; ++ goto done; ++ } ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ if (strstr(ep->ep.name, "-bulk") ++ || strstr(ep->ep.name, "-int")) ++ goto done; ++ ++ switch (dev->gadget.speed) { ++ case USB_SPEED_HIGH: ++ if (max <= 1024) ++ break; ++ case USB_SPEED_FULL: ++ if (max <= 1023) ++ break; ++ default: ++ goto done; ++ } ++ /* ++ * FIXME: ++ * calculate transactions needed for high bandwidth iso ++ */ ++ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); ++ max = max & 0x8ff; /* bit 0~10 */ ++ /* 3 transactions at most */ ++ if (mult > 3) ++ goto done; ++ break; ++ default: ++ goto done; ++ } ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* configure endpoint capabilities in dQH */ ++ ep->dqh->dqh_ios = ios; ++ ep->dqh->dqh_mpl = cpu_to_le16(max); ++ ep->dqh->dqh_zlt = zlt; ++ ep->dqh->dqh_mult = mult; ++ ++ ep->ep.maxpacket = max; ++ ep->desc = desc; ++ ep->stopped = 0; ++ ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ++ ++ /* ep_type */ ++ ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ ++ /* configure endpoint control registers */ ++ ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type); ++ ++ DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n", ++ _ep->name, ++ ep->ep_num, ++ DIR_STRING(desc->bEndpointAddress), ++ type_string(desc->bmAttributes), ++ max); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++done: ++ VDBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* retire a request */ ++static void done(struct langwell_ep *ep, struct langwell_request *req, ++ int status) ++{ ++ struct langwell_udc *dev = ep->dev; ++ unsigned stopped = ep->stopped; ++ struct langwell_dtd *curr_dtd, *next_dtd; ++ int i; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* remove the req from ep->queue */ ++ list_del_init(&req->queue); ++ ++ if (req->req.status == -EINPROGRESS) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ /* free dTD for the request */ ++ next_dtd = req->head; ++ for (i = 0; i < req->dtd_count; i++) { ++ curr_dtd = next_dtd; ++ if (i != req->dtd_count - 1) ++ next_dtd = curr_dtd->next_dtd_virt; ++ dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma); ++ } ++ ++ if (req->mapped) { ++ dma_unmap_single(&dev->pdev->dev, req->req.dma, req->req.length, ++ is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); ++ req->req.dma = DMA_ADDR_INVALID; ++ req->mapped = 0; ++ } else ++ dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma, ++ req->req.length, ++ is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ ++ if (status != -ESHUTDOWN) ++ DBG(dev, "complete %s, req %p, stat %d, len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ ++ spin_unlock(&dev->lock); ++ /* complete routine from gadget driver */ ++ if (req->req.complete) ++ req->req.complete(&ep->ep, &req->req); ++ ++ spin_lock(&dev->lock); ++ ep->stopped = stopped; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++static void langwell_ep_fifo_flush(struct usb_ep *_ep); ++ ++/* delete all endpoint requests, called with spinlock held */ ++static void nuke(struct langwell_ep *ep, int status) ++{ ++ /* called with spinlock held */ ++ ep->stopped = 1; ++ ++ /* endpoint fifo flush */ ++ if (&ep->ep && ep->desc) ++ langwell_ep_fifo_flush(&ep->ep); ++ ++ while (!list_empty(&ep->queue)) { ++ struct langwell_request *req = NULL; ++ req = list_entry(ep->queue.next, struct langwell_request, ++ queue); ++ done(ep, req, status); ++ } ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* endpoint is no longer usable */ ++static int langwell_ep_disable(struct usb_ep *_ep) ++{ ++ struct langwell_ep *ep; ++ unsigned long flags; ++ struct langwell_udc *dev; ++ int ep_num; ++ u32 endptctrl; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !ep->desc) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* disable endpoint control register */ ++ ep_num = ep->ep_num; ++ endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); ++ if (is_in(ep)) ++ endptctrl &= ~EPCTRL_TXE; ++ else ++ endptctrl &= ~EPCTRL_RXE; ++ writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); ++ ++ /* nuke all pending requests (does flush) */ ++ nuke(ep, -ESHUTDOWN); ++ ++ ep->desc = NULL; ++ ep->stopped = 1; ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ DBG(dev, "disabled %s\n", _ep->name); ++ VDBG(dev, "<--- %s()\n", __func__); ++ ++ return 0; ++} ++ ++ ++/* allocate a request object to use with this endpoint */ ++static struct usb_request *langwell_alloc_request(struct usb_ep *_ep, ++ gfp_t gfp_flags) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ struct langwell_request *req = NULL; ++ ++ if (!_ep) ++ return NULL; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ req = kzalloc(sizeof(*req), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ req->req.dma = DMA_ADDR_INVALID; ++ INIT_LIST_HEAD(&req->queue); ++ ++ VDBG(dev, "alloc request for %s\n", _ep->name); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return &req->req; ++} ++ ++ ++/* free a request object */ ++static void langwell_free_request(struct usb_ep *_ep, ++ struct usb_request *_req) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ struct langwell_request *req = NULL; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !_req) ++ return; ++ ++ req = container_of(_req, struct langwell_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ ++ if (_req) ++ kfree(req); ++ ++ VDBG(dev, "free request for %s\n", _ep->name); ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* queue dTD and PRIME endpoint */ ++static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req) ++{ ++ u32 bit_mask, usbcmd, endptstat, dtd_dma; ++ u8 dtd_status; ++ int i; ++ struct langwell_dqh *dqh; ++ struct langwell_udc *dev; ++ ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ i = ep->ep_num * 2 + is_in(ep); ++ dqh = &dev->ep_dqh[i]; ++ ++ if (ep->ep_num) ++ VDBG(dev, "%s\n", ep->name); ++ else ++ /* ep0 */ ++ VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out"); ++ ++ VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i])); ++ ++ bit_mask = is_in(ep) ? ++ (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num)); ++ ++ VDBG(dev, "bit_mask = 0x%08x\n", bit_mask); ++ ++ /* check if the pipe is empty */ ++ if (!(list_empty(&ep->queue))) { ++ /* add dTD to the end of linked list */ ++ struct langwell_request *lastreq; ++ lastreq = list_entry(ep->queue.prev, ++ struct langwell_request, queue); ++ ++ lastreq->tail->dtd_next = ++ cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK); ++ ++ /* read prime bit, if 1 goto out */ ++ if (readl(&dev->op_regs->endptprime) & bit_mask) ++ goto out; ++ ++ do { ++ /* set ATDTW bit in USBCMD */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd); ++ ++ /* read correct status bit */ ++ endptstat = readl(&dev->op_regs->endptstat) & bit_mask; ++ ++ } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW)); ++ ++ /* write ATDTW bit to 0 */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd); ++ ++ if (endptstat) ++ goto out; ++ } ++ ++ /* write dQH next pointer and terminate bit to 0 */ ++ dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK; ++ dqh->dtd_next = cpu_to_le32(dtd_dma); ++ ++ /* clear active and halt bit */ ++ dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED); ++ dqh->dtd_status &= dtd_status; ++ VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status); ++ ++ /* write 1 to endptprime register to PRIME endpoint */ ++ bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num); ++ VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask); ++ writel(bit_mask, &dev->op_regs->endptprime); ++out: ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* fill in the dTD structure to build a transfer descriptor */ ++static struct langwell_dtd *build_dtd(struct langwell_request *req, ++ unsigned *length, dma_addr_t *dma, int *is_last) ++{ ++ u32 buf_ptr; ++ struct langwell_dtd *dtd; ++ struct langwell_udc *dev; ++ int i; ++ ++ dev = req->ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* the maximum transfer length, up to 16k bytes */ ++ *length = min(req->req.length - req->req.actual, ++ (unsigned)DTD_MAX_TRANSFER_LENGTH); ++ ++ /* create dTD dma_pool resource */ ++ dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma); ++ if (dtd == NULL) ++ return dtd; ++ dtd->dtd_dma = *dma; ++ ++ /* initialize buffer page pointers */ ++ buf_ptr = (u32)(req->req.dma + req->req.actual); ++ for (i = 0; i < 5; i++) ++ dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE); ++ ++ req->req.actual += *length; ++ ++ /* fill in total bytes with transfer size */ ++ dtd->dtd_total = cpu_to_le16(*length); ++ VDBG(dev, "dtd->dtd_total = %d\n", dtd->dtd_total); ++ ++ /* set is_last flag if req->req.zero is set or not */ ++ if (req->req.zero) { ++ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) ++ *is_last = 1; ++ else ++ *is_last = 0; ++ } else if (req->req.length == req->req.actual) { ++ *is_last = 1; ++ } else ++ *is_last = 0; ++ ++ if (*is_last == 0) ++ VDBG(dev, "multi-dtd request!\n"); ++ ++ /* set interrupt on complete bit for the last dTD */ ++ if (*is_last && !req->req.no_interrupt) ++ dtd->dtd_ioc = 1; ++ ++ /* set multiplier override 0 for non-ISO and non-TX endpoint */ ++ dtd->dtd_multo = 0; ++ ++ /* set the active bit of status field to 1 */ ++ dtd->dtd_status = DTD_STS_ACTIVE; ++ VDBG(dev, "dtd->dtd_status = 0x%02x\n", dtd->dtd_status); ++ ++ VDBG(dev, "length = %d, dma addr= 0x%08x\n", *length, (int)*dma); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return dtd; ++} ++ ++ ++/* generate dTD linked list for a request */ ++static int req_to_dtd(struct langwell_request *req) ++{ ++ unsigned count; ++ int is_last, is_first = 1; ++ struct langwell_dtd *dtd, *last_dtd = NULL; ++ struct langwell_udc *dev; ++ dma_addr_t dma; ++ ++ dev = req->ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ do { ++ dtd = build_dtd(req, &count, &dma, &is_last); ++ if (dtd == NULL) ++ return -ENOMEM; ++ ++ if (is_first) { ++ is_first = 0; ++ req->head = dtd; ++ } else { ++ last_dtd->dtd_next = cpu_to_le32(dma); ++ last_dtd->next_dtd_virt = dtd; ++ } ++ last_dtd = dtd; ++ req->dtd_count++; ++ } while (!is_last); ++ ++ /* set terminate bit to 1 for the last dTD */ ++ dtd->dtd_next = DTD_TERM; ++ ++ req->tail = dtd; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* queue (submits) an I/O requests to an endpoint */ ++static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, ++ gfp_t gfp_flags) ++{ ++ struct langwell_request *req; ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ unsigned long flags; ++ int is_iso = 0, zlflag = 0; ++ ++ /* always require a cpu-view buffer */ ++ req = container_of(_req, struct langwell_request, req); ++ ep = container_of(_ep, struct langwell_ep, ep); ++ ++ if (!_req || !_req->complete || !_req->buf ++ || !list_empty(&req->queue)) { ++ return -EINVAL; ++ } ++ ++ if (unlikely(!_ep || !ep->desc)) ++ return -EINVAL; ++ ++ dev = ep->dev; ++ req->ep = ep; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { ++ if (req->req.length > ep->ep.maxpacket) ++ return -EMSGSIZE; ++ is_iso = 1; ++ } ++ ++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) ++ return -ESHUTDOWN; ++ ++ /* set up dma mapping in case the caller didn't */ ++ if (_req->dma == DMA_ADDR_INVALID) { ++ /* WORKAROUND: WARN_ON(size == 0) */ ++ if (_req->length == 0) { ++ VDBG(dev, "req->length: 0->1\n"); ++ zlflag = 1; ++ _req->length++; ++ } ++ ++ _req->dma = dma_map_single(&dev->pdev->dev, ++ _req->buf, _req->length, ++ is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ if (zlflag && (_req->length == 1)) { ++ VDBG(dev, "req->length: 1->0\n"); ++ zlflag = 0; ++ _req->length = 0; ++ } ++ ++ req->mapped = 1; ++ VDBG(dev, "req->mapped = 1\n"); ++ } else { ++ dma_sync_single_for_device(&dev->pdev->dev, ++ _req->dma, _req->length, ++ is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ req->mapped = 0; ++ VDBG(dev, "req->mapped = 0\n"); ++ } ++ ++ DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", ++ _ep->name, ++ _req, _req->length, _req->buf, _req->dma); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ req->dtd_count = 0; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* build and put dTDs to endpoint queue */ ++ if (!req_to_dtd(req)) { ++ queue_dtd(ep, req); ++ } else { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return -ENOMEM; ++ } ++ ++ /* update ep0 state */ ++ if (ep->ep_num == 0) ++ dev->ep0_state = DATA_STATE_XMIT; ++ ++ if (likely(req != NULL)) { ++ list_add_tail(&req->queue, &ep->queue); ++ VDBG(dev, "list_add_tail() \n"); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* dequeue (cancels, unlinks) an I/O request from an endpoint */ ++static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ struct langwell_request *req; ++ unsigned long flags; ++ int stopped, ep_num, retval = 0; ++ u32 endptctrl; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !ep->desc || !_req) ++ return -EINVAL; ++ ++ if (!dev->driver) ++ return -ESHUTDOWN; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ stopped = ep->stopped; ++ ++ /* quiesce dma while we patch the queue */ ++ ep->stopped = 1; ++ ep_num = ep->ep_num; ++ ++ /* disable endpoint control register */ ++ endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); ++ if (is_in(ep)) ++ endptctrl &= ~EPCTRL_TXE; ++ else ++ endptctrl &= ~EPCTRL_RXE; ++ writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); ++ ++ /* make sure it's still queued on this endpoint */ ++ list_for_each_entry(req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ ++ if (&req->req != _req) { ++ retval = -EINVAL; ++ goto done; ++ } ++ ++ /* queue head may be partially complete. */ ++ if (ep->queue.next == &req->queue) { ++ DBG(dev, "unlink (%s) dma\n", _ep->name); ++ _req->status = -ECONNRESET; ++ langwell_ep_fifo_flush(&ep->ep); ++ ++ /* not the last request in endpoint queue */ ++ if (likely(ep->queue.next == &req->queue)) { ++ struct langwell_dqh *dqh; ++ struct langwell_request *next_req; ++ ++ dqh = ep->dqh; ++ next_req = list_entry(req->queue.next, ++ struct langwell_request, queue); ++ ++ /* point the dQH to the first dTD of next request */ ++ writel((u32) next_req->head, &dqh->dqh_current); ++ } ++ } else { ++ struct langwell_request *prev_req; ++ ++ prev_req = list_entry(req->queue.prev, ++ struct langwell_request, queue); ++ writel(readl(&req->tail->dtd_next), ++ &prev_req->tail->dtd_next); ++ } ++ ++ done(ep, req, -ECONNRESET); ++ ++done: ++ /* enable endpoint again */ ++ endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); ++ if (is_in(ep)) ++ endptctrl |= EPCTRL_TXE; ++ else ++ endptctrl |= EPCTRL_RXE; ++ writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); ++ ++ ep->stopped = stopped; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* endpoint set/clear halt */ ++static void ep_set_halt(struct langwell_ep *ep, int value) ++{ ++ u32 endptctrl = 0; ++ int ep_num; ++ struct langwell_udc *dev = ep->dev; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ ep_num = ep->ep_num; ++ endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); ++ ++ /* value: 1 - set halt, 0 - clear halt */ ++ if (value) { ++ /* set the stall bit */ ++ if (is_in(ep)) ++ endptctrl |= EPCTRL_TXS; ++ else ++ endptctrl |= EPCTRL_RXS; ++ } else { ++ /* clear the stall bit and reset data toggle */ ++ if (is_in(ep)) { ++ endptctrl &= ~EPCTRL_TXS; ++ endptctrl |= EPCTRL_TXR; ++ } else { ++ endptctrl &= ~EPCTRL_RXS; ++ endptctrl |= EPCTRL_RXR; ++ } ++ } ++ ++ writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* set the endpoint halt feature */ ++static int langwell_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ unsigned long flags; ++ int retval = 0; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !ep->desc) ++ return -EINVAL; ++ ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_ISOC) ++ return -EOPNOTSUPP; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* ++ * attempt to halt IN ep will fail if any transfer requests ++ * are still queue ++ */ ++ if (!list_empty(&ep->queue) && is_in(ep) && value) { ++ /* IN endpoint FIFO holds bytes */ ++ DBG(dev, "%s FIFO holds bytes\n", _ep->name); ++ retval = -EAGAIN; ++ goto done; ++ } ++ ++ /* endpoint set/clear halt */ ++ if (ep->ep_num) { ++ ep_set_halt(ep, value); ++ } else { /* endpoint 0 */ ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->ep0_dir = USB_DIR_OUT; ++ } ++done: ++ spin_unlock_irqrestore(&dev->lock, flags); ++ DBG(dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++ ++ ++/* set the halt feature and ignores clear requests */ ++static int langwell_ep_set_wedge(struct usb_ep *_ep) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !ep->desc) ++ return -EINVAL; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return usb_ep_set_halt(_ep); ++} ++ ++ ++/* flush contents of a fifo */ ++static void langwell_ep_fifo_flush(struct usb_ep *_ep) ++{ ++ struct langwell_ep *ep; ++ struct langwell_udc *dev; ++ u32 flush_bit; ++ unsigned long timeout; ++ ++ ep = container_of(_ep, struct langwell_ep, ep); ++ dev = ep->dev; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (!_ep || !ep->desc) { ++ VDBG(dev, "ep or ep->desc is NULL\n"); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return; ++ } ++ ++ VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out"); ++ ++ /* flush endpoint buffer */ ++ if (ep->ep_num == 0) ++ flush_bit = (1 << 16) | 1; ++ else if (is_in(ep)) ++ flush_bit = 1 << (ep->ep_num + 16); /* TX */ ++ else ++ flush_bit = 1 << ep->ep_num; /* RX */ ++ ++ /* wait until flush complete */ ++ timeout = jiffies + FLUSH_TIMEOUT; ++ do { ++ writel(flush_bit, &dev->op_regs->endptflush); ++ while (readl(&dev->op_regs->endptflush)) { ++ if (time_after(jiffies, timeout)) { ++ ERROR(dev, "ep flush timeout\n"); ++ goto done; ++ } ++ cpu_relax(); ++ } ++ } while (readl(&dev->op_regs->endptstat) & flush_bit); ++done: ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* endpoints operations structure */ ++static const struct usb_ep_ops langwell_ep_ops = { ++ ++ /* configure endpoint, making it usable */ ++ .enable = langwell_ep_enable, ++ ++ /* endpoint is no longer usable */ ++ .disable = langwell_ep_disable, ++ ++ /* allocate a request object to use with this endpoint */ ++ .alloc_request = langwell_alloc_request, ++ ++ /* free a request object */ ++ .free_request = langwell_free_request, ++ ++ /* queue (submits) an I/O requests to an endpoint */ ++ .queue = langwell_ep_queue, ++ ++ /* dequeue (cancels, unlinks) an I/O request from an endpoint */ ++ .dequeue = langwell_ep_dequeue, ++ ++ /* set the endpoint halt feature */ ++ .set_halt = langwell_ep_set_halt, ++ ++ /* set the halt feature and ignores clear requests */ ++ .set_wedge = langwell_ep_set_wedge, ++ ++ /* flush contents of a fifo */ ++ .fifo_flush = langwell_ep_fifo_flush, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* device controller usb_gadget_ops structure */ ++ ++/* returns the current frame number */ ++static int langwell_get_frame(struct usb_gadget *_gadget) ++{ ++ struct langwell_udc *dev; ++ u16 retval; ++ ++ if (!_gadget) ++ return -ENODEV; ++ ++ dev = container_of(_gadget, struct langwell_udc, gadget); ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++ ++ ++/* tries to wake up the host connected to this gadget */ ++static int langwell_wakeup(struct usb_gadget *_gadget) ++{ ++ struct langwell_udc *dev; ++ u32 portsc1, devlc; ++ unsigned long flags; ++ ++ if (!_gadget) ++ return 0; ++ ++ dev = container_of(_gadget, struct langwell_udc, gadget); ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* Remote Wakeup feature not enabled by host */ ++ if (!dev->remote_wakeup) ++ return -ENOTSUPP; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ portsc1 = readl(&dev->op_regs->portsc1); ++ if (!(portsc1 & PORTS_SUSP)) { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return 0; ++ } ++ ++ /* LPM L1 to L0, remote wakeup */ ++ if (dev->lpm && dev->lpm_state == LPM_L1) { ++ portsc1 |= PORTS_SLP; ++ writel(portsc1, &dev->op_regs->portsc1); ++ } ++ ++ /* force port resume */ ++ if (dev->usb_state == USB_STATE_SUSPENDED) { ++ portsc1 |= PORTS_FPR; ++ writel(portsc1, &dev->op_regs->portsc1); ++ } ++ ++ /* exit PHY low power suspend */ ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "devlc = 0x%08x\n", devlc); ++ devlc &= ~LPM_PHCD; ++ writel(devlc, &dev->op_regs->devlc); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* notify controller that VBUS is powered or not */ ++static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active) ++{ ++ struct langwell_udc *dev; ++ unsigned long flags; ++ u32 usbcmd; ++ ++ if (!_gadget) ++ return -ENODEV; ++ ++ dev = container_of(_gadget, struct langwell_udc, gadget); ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ VDBG(dev, "VBUS status: %s\n", is_active ? "on" : "off"); ++ ++ dev->vbus_active = (is_active != 0); ++ if (dev->driver && dev->softconnected && dev->vbus_active) { ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd |= CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ } else { ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd &= ~CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* constrain controller's VBUS power usage */ ++static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA) ++{ ++ struct langwell_udc *dev; ++ ++ if (!_gadget) ++ return -ENODEV; ++ ++ dev = container_of(_gadget, struct langwell_udc, gadget); ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (dev->transceiver) { ++ VDBG(dev, "otg_set_power\n"); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return otg_set_power(dev->transceiver, mA); ++ } ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return -ENOTSUPP; ++} ++ ++ ++/* D+ pullup, software-controlled connect/disconnect to USB host */ ++static int langwell_pullup(struct usb_gadget *_gadget, int is_on) ++{ ++ struct langwell_udc *dev; ++ u32 usbcmd; ++ unsigned long flags; ++ ++ if (!_gadget) ++ return -ENODEV; ++ ++ dev = container_of(_gadget, struct langwell_udc, gadget); ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->softconnected = (is_on != 0); ++ ++ if (dev->driver && dev->softconnected && dev->vbus_active) { ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd |= CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ } else { ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd &= ~CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* device controller usb_gadget_ops structure */ ++static const struct usb_gadget_ops langwell_ops = { ++ ++ /* returns the current frame number */ ++ .get_frame = langwell_get_frame, ++ ++ /* tries to wake up the host connected to this gadget */ ++ .wakeup = langwell_wakeup, ++ ++ /* set the device selfpowered feature, always selfpowered */ ++ /* .set_selfpowered = langwell_set_selfpowered, */ ++ ++ /* notify controller that VBUS is powered or not */ ++ .vbus_session = langwell_vbus_session, ++ ++ /* constrain controller's VBUS power usage */ ++ .vbus_draw = langwell_vbus_draw, ++ ++ /* D+ pullup, software-controlled connect/disconnect to USB host */ ++ .pullup = langwell_pullup, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* device controller operations */ ++ ++/* reset device controller */ ++static int langwell_udc_reset(struct langwell_udc *dev) ++{ ++ u32 usbcmd, usbmode, devlc, endpointlistaddr; ++ unsigned long timeout; ++ ++ if (!dev) ++ return -EINVAL; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* set controller to stop state */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd &= ~CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ ++ /* reset device controller */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd |= CMD_RST; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ ++ /* wait for reset to complete */ ++ timeout = jiffies + RESET_TIMEOUT; ++ while (readl(&dev->op_regs->usbcmd) & CMD_RST) { ++ if (time_after(jiffies, timeout)) { ++ ERROR(dev, "device reset timeout\n"); ++ return -ETIMEDOUT; ++ } ++ cpu_relax(); ++ } ++ ++ /* set controller to device mode */ ++ usbmode = readl(&dev->op_regs->usbmode); ++ usbmode |= MODE_DEVICE; ++ ++ /* turn setup lockout off, require setup tripwire in usbcmd */ ++ usbmode |= MODE_SLOM; ++ ++ writel(usbmode, &dev->op_regs->usbmode); ++ usbmode = readl(&dev->op_regs->usbmode); ++ VDBG(dev, "usbmode=0x%08x\n", usbmode); ++ ++ /* Write-Clear setup status */ ++ writel(0, &dev->op_regs->usbsts); ++ ++ /* if support USB LPM, ACK all LPM token */ ++ if (dev->lpm) { ++ devlc = readl(&dev->op_regs->devlc); ++ devlc &= ~LPM_STL; /* don't STALL LPM token */ ++ devlc &= ~LPM_NYT_ACK; /* ACK LPM token */ ++ writel(devlc, &dev->op_regs->devlc); ++ } ++ ++ /* fill endpointlistaddr register */ ++ endpointlistaddr = dev->ep_dqh_dma; ++ endpointlistaddr &= ENDPOINTLISTADDR_MASK; ++ writel(endpointlistaddr, &dev->op_regs->endpointlistaddr); ++ ++ VDBG(dev, "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n", ++ dev->ep_dqh, endpointlistaddr, ++ readl(&dev->op_regs->endpointlistaddr)); ++ DBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* reinitialize device controller endpoints */ ++static int eps_reinit(struct langwell_udc *dev) ++{ ++ struct langwell_ep *ep; ++ char name[14]; ++ int i; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* initialize ep0 */ ++ ep = &dev->ep[0]; ++ ep->dev = dev; ++ strncpy(ep->name, "ep0", sizeof(ep->name)); ++ ep->ep.name = ep->name; ++ ep->ep.ops = &langwell_ep_ops; ++ ep->stopped = 0; ++ ep->ep.maxpacket = EP0_MAX_PKT_SIZE; ++ ep->ep_num = 0; ++ ep->desc = &langwell_ep0_desc; ++ INIT_LIST_HEAD(&ep->queue); ++ ++ ep->ep_type = USB_ENDPOINT_XFER_CONTROL; ++ ++ /* initialize other endpoints */ ++ for (i = 2; i < dev->ep_max; i++) { ++ ep = &dev->ep[i]; ++ if (i % 2) ++ snprintf(name, sizeof(name), "ep%din", i / 2); ++ else ++ snprintf(name, sizeof(name), "ep%dout", i / 2); ++ ep->dev = dev; ++ strncpy(ep->name, name, sizeof(ep->name)); ++ ep->ep.name = ep->name; ++ ++ ep->ep.ops = &langwell_ep_ops; ++ ep->stopped = 0; ++ ep->ep.maxpacket = (unsigned short) ~0; ++ ep->ep_num = i / 2; ++ ++ INIT_LIST_HEAD(&ep->queue); ++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ++ ++ ep->dqh = &dev->ep_dqh[i]; ++ } ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* enable interrupt and set controller to run state */ ++static void langwell_udc_start(struct langwell_udc *dev) ++{ ++ u32 usbintr, usbcmd; ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* enable interrupts */ ++ usbintr = INTR_ULPIE /* ULPI */ ++ | INTR_SLE /* suspend */ ++ /* | INTR_SRE SOF received */ ++ | INTR_URE /* USB reset */ ++ | INTR_AAE /* async advance */ ++ | INTR_SEE /* system error */ ++ | INTR_FRE /* frame list rollover */ ++ | INTR_PCE /* port change detect */ ++ | INTR_UEE /* USB error interrupt */ ++ | INTR_UE; /* USB interrupt */ ++ writel(usbintr, &dev->op_regs->usbintr); ++ ++ /* clear stopped bit */ ++ dev->stopped = 0; ++ ++ /* set controller to run */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd |= CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ return; ++} ++ ++ ++/* disable interrupt and set controller to stop state */ ++static void langwell_udc_stop(struct langwell_udc *dev) ++{ ++ u32 usbcmd; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* disable all interrupts */ ++ writel(0, &dev->op_regs->usbintr); ++ ++ /* set stopped bit */ ++ dev->stopped = 1; ++ ++ /* set controller to stop state */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ usbcmd &= ~CMD_RUNSTOP; ++ writel(usbcmd, &dev->op_regs->usbcmd); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ return; ++} ++ ++ ++/* stop all USB activities */ ++static void stop_activity(struct langwell_udc *dev, ++ struct usb_gadget_driver *driver) ++{ ++ struct langwell_ep *ep; ++ DBG(dev, "---> %s()\n", __func__); ++ ++ nuke(&dev->ep[0], -ESHUTDOWN); ++ ++ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { ++ nuke(ep, -ESHUTDOWN); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) { ++ spin_unlock(&dev->lock); ++ driver->disconnect(&dev->gadget); ++ spin_lock(&dev->lock); ++ } ++ ++ DBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* device "function" sysfs attribute file */ ++static ssize_t show_function(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct langwell_udc *dev = the_controller; ++ ++ if (!dev->driver || !dev->driver->function ++ || strlen(dev->driver->function) > PAGE_SIZE) ++ return 0; ++ ++ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); ++} ++static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); ++ ++ ++/* device "langwell_udc" sysfs attribute file */ ++static ssize_t show_langwell_udc(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct langwell_udc *dev = the_controller; ++ struct langwell_request *req; ++ struct langwell_ep *ep = NULL; ++ char *next; ++ unsigned size; ++ unsigned t; ++ unsigned i; ++ unsigned long flags; ++ u32 tmp_reg; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* driver basic information */ ++ t = scnprintf(next, size, ++ DRIVER_DESC "\n" ++ "%s version: %s\n" ++ "Gadget driver: %s\n\n", ++ driver_name, DRIVER_VERSION, ++ dev->driver ? dev->driver->driver.name : "(none)"); ++ size -= t; ++ next += t; ++ ++ /* device registers */ ++ tmp_reg = readl(&dev->op_regs->usbcmd); ++ t = scnprintf(next, size, ++ "USBCMD reg:\n" ++ "SetupTW: %d\n" ++ "Run/Stop: %s\n\n", ++ (tmp_reg & CMD_SUTW) ? 1 : 0, ++ (tmp_reg & CMD_RUNSTOP) ? "Run" : "Stop"); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->usbsts); ++ t = scnprintf(next, size, ++ "USB Status Reg:\n" ++ "Device Suspend: %d\n" ++ "Reset Received: %d\n" ++ "System Error: %s\n" ++ "USB Error Interrupt: %s\n\n", ++ (tmp_reg & STS_SLI) ? 1 : 0, ++ (tmp_reg & STS_URI) ? 1 : 0, ++ (tmp_reg & STS_SEI) ? "Error" : "No error", ++ (tmp_reg & STS_UEI) ? "Error detected" : "No error"); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->usbintr); ++ t = scnprintf(next, size, ++ "USB Intrrupt Enable Reg:\n" ++ "Sleep Enable: %d\n" ++ "SOF Received Enable: %d\n" ++ "Reset Enable: %d\n" ++ "System Error Enable: %d\n" ++ "Port Change Dectected Enable: %d\n" ++ "USB Error Intr Enable: %d\n" ++ "USB Intr Enable: %d\n\n", ++ (tmp_reg & INTR_SLE) ? 1 : 0, ++ (tmp_reg & INTR_SRE) ? 1 : 0, ++ (tmp_reg & INTR_URE) ? 1 : 0, ++ (tmp_reg & INTR_SEE) ? 1 : 0, ++ (tmp_reg & INTR_PCE) ? 1 : 0, ++ (tmp_reg & INTR_UEE) ? 1 : 0, ++ (tmp_reg & INTR_UE) ? 1 : 0); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->frindex); ++ t = scnprintf(next, size, ++ "USB Frame Index Reg:\n" ++ "Frame Number is 0x%08x\n\n", ++ (tmp_reg & FRINDEX_MASK)); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->deviceaddr); ++ t = scnprintf(next, size, ++ "USB Device Address Reg:\n" ++ "Device Addr is 0x%x\n\n", ++ USBADR(tmp_reg)); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->endpointlistaddr); ++ t = scnprintf(next, size, ++ "USB Endpoint List Address Reg:\n" ++ "Endpoint List Pointer is 0x%x\n\n", ++ EPBASE(tmp_reg)); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->portsc1); ++ t = scnprintf(next, size, ++ "USB Port Status & Control Reg:\n" ++ "Port Reset: %s\n" ++ "Port Suspend Mode: %s\n" ++ "Over-current Change: %s\n" ++ "Port Enable/Disable Change: %s\n" ++ "Port Enabled/Disabled: %s\n" ++ "Current Connect Status: %s\n\n", ++ (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset", ++ (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend", ++ (tmp_reg & PORTS_OCC) ? "Detected" : "No", ++ (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed", ++ (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct", ++ (tmp_reg & PORTS_CCS) ? "Attached" : "Not Attached"); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->devlc); ++ t = scnprintf(next, size, ++ "Device LPM Control Reg:\n" ++ "Parallel Transceiver : %d\n" ++ "Serial Transceiver : %d\n" ++ "Port Speed: %s\n" ++ "Port Force Full Speed Connenct: %s\n" ++ "PHY Low Power Suspend Clock Disable: %s\n" ++ "BmAttributes: %d\n\n", ++ LPM_PTS(tmp_reg), ++ (tmp_reg & LPM_STS) ? 1 : 0, ++ ({ ++ char *s; ++ switch (LPM_PSPD(tmp_reg)) { ++ case LPM_SPEED_FULL: ++ s = "Full Speed"; break; ++ case LPM_SPEED_LOW: ++ s = "Low Speed"; break; ++ case LPM_SPEED_HIGH: ++ s = "High Speed"; break; ++ default: ++ s = "Unknown Speed"; break; ++ } ++ s; ++ }), ++ (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force", ++ (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled", ++ LPM_BA(tmp_reg)); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->usbmode); ++ t = scnprintf(next, size, ++ "USB Mode Reg:\n" ++ "Controller Mode is : %s\n\n", ({ ++ char *s; ++ switch (MODE_CM(tmp_reg)) { ++ case MODE_IDLE: ++ s = "Idle"; break; ++ case MODE_DEVICE: ++ s = "Device Controller"; break; ++ case MODE_HOST: ++ s = "Host Controller"; break; ++ default: ++ s = "None"; break; ++ } ++ s; ++ })); ++ size -= t; ++ next += t; ++ ++ tmp_reg = readl(&dev->op_regs->endptsetupstat); ++ t = scnprintf(next, size, ++ "Endpoint Setup Status Reg:\n" ++ "SETUP on ep 0x%04x\n\n", ++ tmp_reg & SETUPSTAT_MASK); ++ size -= t; ++ next += t; ++ ++ for (i = 0; i < dev->ep_max / 2; i++) { ++ tmp_reg = readl(&dev->op_regs->endptctrl[i]); ++ t = scnprintf(next, size, "EP Ctrl Reg [%d]: 0x%08x\n", ++ i, tmp_reg); ++ size -= t; ++ next += t; ++ } ++ tmp_reg = readl(&dev->op_regs->endptprime); ++ t = scnprintf(next, size, "EP Prime Reg: 0x%08x\n\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ /* langwell_udc, langwell_ep, langwell_request structure information */ ++ ep = &dev->ep[0]; ++ t = scnprintf(next, size, "%s MaxPacketSize: 0x%x, ep_num: %d\n", ++ ep->ep.name, ep->ep.maxpacket, ep->ep_num); ++ size -= t; ++ next += t; ++ ++ if (list_empty(&ep->queue)) { ++ t = scnprintf(next, size, "its req queue is empty\n\n"); ++ size -= t; ++ next += t; ++ } else { ++ list_for_each_entry(req, &ep->queue, queue) { ++ t = scnprintf(next, size, ++ "req %p actual 0x%x length 0x%x buf %p\n", ++ &req->req, req->req.actual, ++ req->req.length, req->req.buf); ++ size -= t; ++ next += t; ++ } ++ } ++ /* other gadget->eplist ep */ ++ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { ++ if (ep->desc) { ++ t = scnprintf(next, size, ++ "\n%s MaxPacketSize: 0x%x, " ++ "ep_num: %d\n", ++ ep->ep.name, ep->ep.maxpacket, ++ ep->ep_num); ++ size -= t; ++ next += t; ++ ++ if (list_empty(&ep->queue)) { ++ t = scnprintf(next, size, ++ "its req queue is empty\n\n"); ++ size -= t; ++ next += t; ++ } else { ++ list_for_each_entry(req, &ep->queue, queue) { ++ t = scnprintf(next, size, ++ "req %p actual 0x%x length " ++ "0x%x buf %p\n", ++ &req->req, req->req.actual, ++ req->req.length, req->req.buf); ++ size -= t; ++ next += t; ++ } ++ } ++ } ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return PAGE_SIZE - size; ++} ++static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * when a driver is successfully registered, it will receive ++ * control requests including set_configuration(), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct langwell_udc *dev = the_controller; ++ unsigned long flags; ++ int retval; ++ ++ if (!dev) ++ return -ENODEV; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ if (dev->driver) ++ return -EBUSY; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* hook up the driver ... */ ++ driver->driver.bus = NULL; ++ dev->driver = driver; ++ dev->gadget.dev.driver = &driver->driver; ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ retval = driver->bind(&dev->gadget); ++ if (retval) { ++ DBG(dev, "bind to driver %s --> %d\n", ++ driver->driver.name, retval); ++ dev->driver = NULL; ++ dev->gadget.dev.driver = NULL; ++ return retval; ++ } ++ ++ retval = device_create_file(&dev->pdev->dev, &dev_attr_function); ++ if (retval) ++ goto err_unbind; ++ ++ dev->usb_state = USB_STATE_ATTACHED; ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->ep0_dir = USB_DIR_OUT; ++ ++ /* enable interrupt and set controller to run state */ ++ if (dev->got_irq) ++ langwell_udc_start(dev); ++ ++ VDBG(dev, "After langwell_udc_start(), print all registers:\n"); ++#ifdef VERBOSE ++ print_all_registers(dev); ++#endif ++ ++ INFO(dev, "register driver: %s\n", driver->driver.name); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++ ++err_unbind: ++ driver->unbind(&dev->gadget); ++ dev->gadget.dev.driver = NULL; ++ dev->driver = NULL; ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++ ++/* unregister gadget driver */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct langwell_udc *dev = the_controller; ++ unsigned long flags; ++ ++ if (!dev) ++ return -ENODEV; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ if (unlikely(!driver || !driver->bind || !driver->unbind)) ++ return -EINVAL; ++ ++ /* unbind OTG transceiver */ ++ if (dev->transceiver) ++ (void)otg_set_peripheral(dev->transceiver, 0); ++ ++ /* disable interrupt and set controller to stop state */ ++ langwell_udc_stop(dev); ++ ++ dev->usb_state = USB_STATE_ATTACHED; ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->ep0_dir = USB_DIR_OUT; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* stop all usb activities */ ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ stop_activity(dev, driver); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* unbind gadget driver */ ++ driver->unbind(&dev->gadget); ++ dev->gadget.dev.driver = NULL; ++ dev->driver = NULL; ++ ++ device_remove_file(&dev->pdev->dev, &dev_attr_function); ++ ++ INFO(dev, "unregistered driver '%s'\n", driver->driver.name); ++ DBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * setup tripwire is used as a semaphore to ensure that the setup data ++ * payload is extracted from a dQH without being corrupted ++ */ ++static void setup_tripwire(struct langwell_udc *dev) ++{ ++ u32 usbcmd, ++ endptsetupstat; ++ unsigned long timeout; ++ struct langwell_dqh *dqh; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* ep0 OUT dQH */ ++ dqh = &dev->ep_dqh[EP_DIR_OUT]; ++ ++ /* Write-Clear endptsetupstat */ ++ endptsetupstat = readl(&dev->op_regs->endptsetupstat); ++ writel(endptsetupstat, &dev->op_regs->endptsetupstat); ++ ++ /* wait until endptsetupstat is cleared */ ++ timeout = jiffies + SETUPSTAT_TIMEOUT; ++ while (readl(&dev->op_regs->endptsetupstat)) { ++ if (time_after(jiffies, timeout)) { ++ ERROR(dev, "setup_tripwire timeout\n"); ++ break; ++ } ++ cpu_relax(); ++ } ++ ++ /* while a hazard exists when setup packet arrives */ ++ do { ++ /* set setup tripwire bit */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ writel(usbcmd | CMD_SUTW, &dev->op_regs->usbcmd); ++ ++ /* copy the setup packet to local buffer */ ++ memcpy(&dev->local_setup_buff, &dqh->dqh_setup, 8); ++ } while (!(readl(&dev->op_regs->usbcmd) & CMD_SUTW)); ++ ++ /* Write-Clear setup tripwire bit */ ++ usbcmd = readl(&dev->op_regs->usbcmd); ++ writel(usbcmd & ~CMD_SUTW, &dev->op_regs->usbcmd); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* protocol ep0 stall, will automatically be cleared on new transaction */ ++static void ep0_stall(struct langwell_udc *dev) ++{ ++ u32 endptctrl; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* set TX and RX to stall */ ++ endptctrl = readl(&dev->op_regs->endptctrl[0]); ++ endptctrl |= EPCTRL_TXS | EPCTRL_RXS; ++ writel(endptctrl, &dev->op_regs->endptctrl[0]); ++ ++ /* update ep0 state */ ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->ep0_dir = USB_DIR_OUT; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* PRIME a status phase for ep0 */ ++static int prime_status_phase(struct langwell_udc *dev, int dir) ++{ ++ struct langwell_request *req; ++ struct langwell_ep *ep; ++ int status = 0; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (dir == EP_DIR_IN) ++ dev->ep0_dir = USB_DIR_IN; ++ else ++ dev->ep0_dir = USB_DIR_OUT; ++ ++ ep = &dev->ep[0]; ++ dev->ep0_state = WAIT_FOR_OUT_STATUS; ++ ++ req = dev->status_req; ++ ++ req->ep = ep; ++ req->req.length = 0; ++ req->req.status = -EINPROGRESS; ++ req->req.actual = 0; ++ req->req.complete = NULL; ++ req->dtd_count = 0; ++ ++ if (!req_to_dtd(req)) ++ status = queue_dtd(ep, req); ++ else ++ return -ENOMEM; ++ ++ if (status) ++ ERROR(dev, "can't queue ep0 status request\n"); ++ ++ list_add_tail(&req->queue, &ep->queue); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return status; ++} ++ ++ ++/* SET_ADDRESS request routine */ ++static void set_address(struct langwell_udc *dev, u16 value, ++ u16 index, u16 length) ++{ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* save the new address to device struct */ ++ dev->dev_addr = (u8) value; ++ VDBG(dev, "dev->dev_addr = %d\n", dev->dev_addr); ++ ++ /* update usb state */ ++ dev->usb_state = USB_STATE_ADDRESS; ++ ++ /* STATUS phase */ ++ if (prime_status_phase(dev, EP_DIR_IN)) ++ ep0_stall(dev); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* return endpoint by windex */ ++static struct langwell_ep *get_ep_by_windex(struct langwell_udc *dev, ++ u16 wIndex) ++{ ++ struct langwell_ep *ep; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) ++ return &dev->ep[0]; ++ ++ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { ++ u8 bEndpointAddress; ++ if (!ep->desc) ++ continue; ++ ++ bEndpointAddress = ep->desc->bEndpointAddress; ++ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) ++ continue; ++ ++ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) ++ == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) ++ return ep; ++ } ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return NULL; ++} ++ ++ ++/* return whether endpoint is stalled, 0: not stalled; 1: stalled */ ++static int ep_is_stall(struct langwell_ep *ep) ++{ ++ struct langwell_udc *dev = ep->dev; ++ u32 endptctrl; ++ int retval; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ endptctrl = readl(&dev->op_regs->endptctrl[ep->ep_num]); ++ if (is_in(ep)) ++ retval = endptctrl & EPCTRL_TXS ? 1 : 0; ++ else ++ retval = endptctrl & EPCTRL_RXS ? 1 : 0; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return retval; ++} ++ ++ ++/* GET_STATUS request routine */ ++static void get_status(struct langwell_udc *dev, u8 request_type, u16 value, ++ u16 index, u16 length) ++{ ++ struct langwell_request *req; ++ struct langwell_ep *ep; ++ u16 status_data = 0; /* 16 bits cpu view status data */ ++ int status = 0; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ ep = &dev->ep[0]; ++ ++ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { ++ /* get device status */ ++ status_data = 1 << USB_DEVICE_SELF_POWERED; ++ status_data |= dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; ++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { ++ /* get interface status */ ++ status_data = 0; ++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { ++ /* get endpoint status */ ++ struct langwell_ep *epn; ++ epn = get_ep_by_windex(dev, index); ++ /* stall if endpoint doesn't exist */ ++ if (!epn) ++ goto stall; ++ ++ status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT; ++ } ++ ++ dev->ep0_dir = USB_DIR_IN; ++ ++ /* borrow the per device status_req */ ++ req = dev->status_req; ++ ++ /* fill in the reqest structure */ ++ *((u16 *) req->req.buf) = cpu_to_le16(status_data); ++ req->ep = ep; ++ req->req.length = 2; ++ req->req.status = -EINPROGRESS; ++ req->req.actual = 0; ++ req->req.complete = NULL; ++ req->dtd_count = 0; ++ ++ /* prime the data phase */ ++ if (!req_to_dtd(req)) ++ status = queue_dtd(ep, req); ++ else /* no mem */ ++ goto stall; ++ ++ if (status) { ++ ERROR(dev, "response error on GET_STATUS request\n"); ++ goto stall; ++ } ++ ++ list_add_tail(&req->queue, &ep->queue); ++ dev->ep0_state = DATA_STATE_XMIT; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return; ++stall: ++ ep0_stall(dev); ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* setup packet interrupt handler */ ++static void handle_setup_packet(struct langwell_udc *dev, ++ struct usb_ctrlrequest *setup) ++{ ++ u16 wValue = le16_to_cpu(setup->wValue); ++ u16 wIndex = le16_to_cpu(setup->wIndex); ++ u16 wLength = le16_to_cpu(setup->wLength); ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* ep0 fifo flush */ ++ nuke(&dev->ep[0], -ESHUTDOWN); ++ ++ DBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ setup->bRequestType, setup->bRequest, ++ wValue, wIndex, wLength); ++ ++ /* RNDIS gadget delegate */ ++ if ((setup->bRequestType == 0x21) && (setup->bRequest == 0x00)) { ++ /* USB_CDC_SEND_ENCAPSULATED_COMMAND */ ++ goto delegate; ++ } ++ ++ /* USB_CDC_GET_ENCAPSULATED_RESPONSE */ ++ if ((setup->bRequestType == 0xa1) && (setup->bRequest == 0x01)) { ++ /* USB_CDC_GET_ENCAPSULATED_RESPONSE */ ++ goto delegate; ++ } ++ ++ /* We process some stardard setup requests here */ ++ switch (setup->bRequest) { ++ case USB_REQ_GET_STATUS: ++ DBG(dev, "SETUP: USB_REQ_GET_STATUS\n"); ++ /* get status, DATA and STATUS phase */ ++ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) ++ != (USB_DIR_IN | USB_TYPE_STANDARD)) ++ break; ++ get_status(dev, setup->bRequestType, wValue, wIndex, wLength); ++ goto end; ++ ++ case USB_REQ_SET_ADDRESS: ++ DBG(dev, "SETUP: USB_REQ_SET_ADDRESS\n"); ++ /* STATUS phase */ ++ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD ++ | USB_RECIP_DEVICE)) ++ break; ++ set_address(dev, wValue, wIndex, wLength); ++ goto end; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ /* STATUS phase */ ++ { ++ int rc = -EOPNOTSUPP; ++ if (setup->bRequest == USB_REQ_SET_FEATURE) ++ DBG(dev, "SETUP: USB_REQ_SET_FEATURE\n"); ++ else if (setup->bRequest == USB_REQ_CLEAR_FEATURE) ++ DBG(dev, "SETUP: USB_REQ_CLEAR_FEATURE\n"); ++ ++ if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) ++ == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { ++ struct langwell_ep *epn; ++ epn = get_ep_by_windex(dev, wIndex); ++ /* stall if endpoint doesn't exist */ ++ if (!epn) { ++ ep0_stall(dev); ++ goto end; ++ } ++ ++ if (wValue != 0 || wLength != 0 ++ || epn->ep_num > dev->ep_max) ++ break; ++ ++ spin_unlock(&dev->lock); ++ rc = langwell_ep_set_halt(&epn->ep, ++ (setup->bRequest == USB_REQ_SET_FEATURE) ++ ? 1 : 0); ++ spin_lock(&dev->lock); ++ ++ } else if ((setup->bRequestType & (USB_RECIP_MASK ++ | USB_TYPE_MASK)) == (USB_RECIP_DEVICE ++ | USB_TYPE_STANDARD)) { ++ if (!gadget_is_otg(&dev->gadget)) ++ break; ++ else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) { ++ dev->gadget.b_hnp_enable = 1; ++#ifdef OTG_TRANSCEIVER ++ if (!dev->lotg->otg.default_a) ++ dev->lotg->hsm.b_hnp_enable = 1; ++#endif ++ } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) ++ dev->gadget.a_hnp_support = 1; ++ else if (setup->bRequest == ++ USB_DEVICE_A_ALT_HNP_SUPPORT) ++ dev->gadget.a_alt_hnp_support = 1; ++ else ++ break; ++ rc = 0; ++ } else ++ break; ++ ++ if (rc == 0) { ++ if (prime_status_phase(dev, EP_DIR_IN)) ++ ep0_stall(dev); ++ } ++ goto end; ++ } ++ ++ case USB_REQ_GET_DESCRIPTOR: ++ DBG(dev, "SETUP: USB_REQ_GET_DESCRIPTOR\n"); ++ goto delegate; ++ ++ case USB_REQ_SET_DESCRIPTOR: ++ DBG(dev, "SETUP: USB_REQ_SET_DESCRIPTOR unsupported\n"); ++ goto delegate; ++ ++ case USB_REQ_GET_CONFIGURATION: ++ DBG(dev, "SETUP: USB_REQ_GET_CONFIGURATION\n"); ++ goto delegate; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ DBG(dev, "SETUP: USB_REQ_SET_CONFIGURATION\n"); ++ goto delegate; ++ ++ case USB_REQ_GET_INTERFACE: ++ DBG(dev, "SETUP: USB_REQ_GET_INTERFACE\n"); ++ goto delegate; ++ ++ case USB_REQ_SET_INTERFACE: ++ DBG(dev, "SETUP: USB_REQ_SET_INTERFACE\n"); ++ goto delegate; ++ ++ case USB_REQ_SYNCH_FRAME: ++ DBG(dev, "SETUP: USB_REQ_SYNCH_FRAME unsupported\n"); ++ goto delegate; ++ ++ default: ++ /* delegate USB standard requests to the gadget driver */ ++ goto delegate; ++delegate: ++ /* USB requests handled by gadget */ ++ if (wLength) { ++ /* DATA phase from gadget, STATUS phase from udc */ ++ dev->ep0_dir = (setup->bRequestType & USB_DIR_IN) ++ ? USB_DIR_IN : USB_DIR_OUT; ++ VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n", ++ dev->ep0_dir, wLength); ++ spin_unlock(&dev->lock); ++ if (dev->driver->setup(&dev->gadget, ++ &dev->local_setup_buff) < 0) ++ ep0_stall(dev); ++ spin_lock(&dev->lock); ++ dev->ep0_state = (setup->bRequestType & USB_DIR_IN) ++ ? DATA_STATE_XMIT : DATA_STATE_RECV; ++ } else { ++ /* no DATA phase, IN STATUS phase from gadget */ ++ dev->ep0_dir = USB_DIR_IN; ++ VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n", ++ dev->ep0_dir, wLength); ++ spin_unlock(&dev->lock); ++ if (dev->driver->setup(&dev->gadget, ++ &dev->local_setup_buff) < 0) ++ ep0_stall(dev); ++ spin_lock(&dev->lock); ++ dev->ep0_state = WAIT_FOR_OUT_STATUS; ++ } ++ break; ++ } ++end: ++ VDBG(dev, "<--- %s()\n", __func__); ++ return; ++} ++ ++ ++/* transfer completion, process endpoint request and free the completed dTDs ++ * for this request ++ */ ++static int process_ep_req(struct langwell_udc *dev, int index, ++ struct langwell_request *curr_req) ++{ ++ struct langwell_dtd *curr_dtd; ++ struct langwell_dqh *curr_dqh; ++ int td_complete, actual, remaining_length; ++ int i, dir; ++ u8 dtd_status = 0; ++ int retval = 0; ++ ++ curr_dqh = &dev->ep_dqh[index]; ++ dir = index % 2; ++ ++ curr_dtd = curr_req->head; ++ td_complete = 0; ++ actual = curr_req->req.length; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ for (i = 0; i < curr_req->dtd_count; i++) { ++ remaining_length = le16_to_cpu(curr_dtd->dtd_total); ++ actual -= remaining_length; ++ ++ /* command execution states by dTD */ ++ dtd_status = curr_dtd->dtd_status; ++ ++ if (!dtd_status) { ++ /* transfers completed successfully */ ++ if (!remaining_length) { ++ td_complete++; ++ VDBG(dev, "dTD transmitted successfully\n"); ++ } else { ++ if (dir) { ++ VDBG(dev, "TX dTD remains data\n"); ++ retval = -EPROTO; ++ break; ++ ++ } else { ++ td_complete++; ++ break; ++ } ++ } ++ } else { ++ /* transfers completed with errors */ ++ if (dtd_status & DTD_STS_ACTIVE) { ++ DBG(dev, "request not completed\n"); ++ retval = 1; ++ return retval; ++ } else if (dtd_status & DTD_STS_HALTED) { ++ ERROR(dev, "dTD error %08x dQH[%d]\n", ++ dtd_status, index); ++ /* clear the errors and halt condition */ ++ curr_dqh->dtd_status = 0; ++ retval = -EPIPE; ++ break; ++ } else if (dtd_status & DTD_STS_DBE) { ++ DBG(dev, "data buffer (overflow) error\n"); ++ retval = -EPROTO; ++ break; ++ } else if (dtd_status & DTD_STS_TRE) { ++ DBG(dev, "transaction(ISO) error\n"); ++ retval = -EILSEQ; ++ break; ++ } else ++ ERROR(dev, "unknown error (0x%x)!\n", ++ dtd_status); ++ } ++ ++ if (i != curr_req->dtd_count - 1) ++ curr_dtd = (struct langwell_dtd *) ++ curr_dtd->next_dtd_virt; ++ } ++ ++ if (retval) ++ return retval; ++ ++ curr_req->req.actual = actual; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* complete DATA or STATUS phase of ep0 prime status phase if needed */ ++static void ep0_req_complete(struct langwell_udc *dev, ++ struct langwell_ep *ep0, struct langwell_request *req) ++{ ++ u32 new_addr; ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (dev->usb_state == USB_STATE_ADDRESS) { ++ /* set the new address */ ++ new_addr = (u32)dev->dev_addr; ++ writel(new_addr << USBADR_SHIFT, &dev->op_regs->deviceaddr); ++ ++ new_addr = USBADR(readl(&dev->op_regs->deviceaddr)); ++ VDBG(dev, "new_addr = %d\n", new_addr); ++ } ++ ++ done(ep0, req, 0); ++ ++ switch (dev->ep0_state) { ++ case DATA_STATE_XMIT: ++ /* receive status phase */ ++ if (prime_status_phase(dev, EP_DIR_OUT)) ++ ep0_stall(dev); ++ break; ++ case DATA_STATE_RECV: ++ /* send status phase */ ++ if (prime_status_phase(dev, EP_DIR_IN)) ++ ep0_stall(dev); ++ break; ++ case WAIT_FOR_OUT_STATUS: ++ dev->ep0_state = WAIT_FOR_SETUP; ++ break; ++ case WAIT_FOR_SETUP: ++ ERROR(dev, "unexpect ep0 packets\n"); ++ break; ++ default: ++ ep0_stall(dev); ++ break; ++ } ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* USB transfer completion interrupt */ ++static void handle_trans_complete(struct langwell_udc *dev) ++{ ++ u32 complete_bits; ++ int i, ep_num, dir, bit_mask, status; ++ struct langwell_ep *epn; ++ struct langwell_request *curr_req, *temp_req; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ complete_bits = readl(&dev->op_regs->endptcomplete); ++ VDBG(dev, "endptcomplete register: 0x%08x\n", complete_bits); ++ ++ /* Write-Clear the bits in endptcomplete register */ ++ writel(complete_bits, &dev->op_regs->endptcomplete); ++ ++ if (!complete_bits) { ++ DBG(dev, "complete_bits = 0\n"); ++ goto done; ++ } ++ ++ for (i = 0; i < dev->ep_max; i++) { ++ ep_num = i / 2; ++ dir = i % 2; ++ ++ bit_mask = 1 << (ep_num + 16 * dir); ++ ++ if (!(complete_bits & bit_mask)) ++ continue; ++ ++ /* ep0 */ ++ if (i == 1) ++ epn = &dev->ep[0]; ++ else ++ epn = &dev->ep[i]; ++ ++ if (epn->name == NULL) { ++ WARNING(dev, "invalid endpoint\n"); ++ continue; ++ } ++ ++ if (i < 2) ++ /* ep0 in and out */ ++ DBG(dev, "%s-%s transfer completed\n", ++ epn->name, ++ is_in(epn) ? "in" : "out"); ++ else ++ DBG(dev, "%s transfer completed\n", epn->name); ++ ++ /* process the req queue until an uncomplete request */ ++ list_for_each_entry_safe(curr_req, temp_req, ++ &epn->queue, queue) { ++ status = process_ep_req(dev, i, curr_req); ++ VDBG(dev, "%s req status: %d\n", epn->name, status); ++ ++ if (status) ++ break; ++ ++ /* write back status to req */ ++ curr_req->req.status = status; ++ ++ /* ep0 request completion */ ++ if (ep_num == 0) { ++ ep0_req_complete(dev, epn, curr_req); ++ break; ++ } else { ++ done(epn, curr_req, status); ++ } ++ } ++ } ++done: ++ VDBG(dev, "<--- %s()\n", __func__); ++ return; ++} ++ ++ ++/* port change detect interrupt handler */ ++static void handle_port_change(struct langwell_udc *dev) ++{ ++ u32 portsc1, devlc; ++ u32 speed; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (dev->bus_reset) ++ dev->bus_reset = 0; ++ ++ portsc1 = readl(&dev->op_regs->portsc1); ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "portsc1 = 0x%08x, devlc = 0x%08x\n", ++ portsc1, devlc); ++ ++ /* bus reset is finished */ ++ if (!(portsc1 & PORTS_PR)) { ++ /* get the speed */ ++ speed = LPM_PSPD(devlc); ++ switch (speed) { ++ case LPM_SPEED_HIGH: ++ dev->gadget.speed = USB_SPEED_HIGH; ++ break; ++ case LPM_SPEED_FULL: ++ dev->gadget.speed = USB_SPEED_FULL; ++ break; ++ case LPM_SPEED_LOW: ++ dev->gadget.speed = USB_SPEED_LOW; ++ break; ++ default: ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ break; ++ } ++ VDBG(dev, "speed = %d, dev->gadget.speed = %d\n", ++ speed, dev->gadget.speed); ++ } ++ ++ /* LPM L0 to L1 */ ++ if (dev->lpm && dev->lpm_state == LPM_L0) ++ if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) { ++ INFO(dev, "LPM L0 to L1\n"); ++ dev->lpm_state = LPM_L1; ++ } ++ ++ /* LPM L1 to L0, force resume or remote wakeup finished */ ++ if (dev->lpm && dev->lpm_state == LPM_L1) ++ if (!(portsc1 & PORTS_SUSP)) { ++ if (portsc1 & PORTS_SLP) ++ INFO(dev, "LPM L1 to L0, force resume\n"); ++ else ++ INFO(dev, "LPM L1 to L0, remote wakeup\n"); ++ ++ dev->lpm_state = LPM_L0; ++ } ++ ++ /* update USB state */ ++ if (!dev->resume_state) ++ dev->usb_state = USB_STATE_DEFAULT; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* USB reset interrupt handler */ ++static void handle_usb_reset(struct langwell_udc *dev) ++{ ++ u32 deviceaddr, ++ endptsetupstat, ++ endptcomplete; ++ unsigned long timeout; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ /* Write-Clear the device address */ ++ deviceaddr = readl(&dev->op_regs->deviceaddr); ++ writel(deviceaddr & ~USBADR_MASK, &dev->op_regs->deviceaddr); ++ ++ dev->dev_addr = 0; ++ ++ /* clear usb state */ ++ dev->resume_state = 0; ++ ++ /* LPM L1 to L0, reset */ ++ if (dev->lpm) ++ dev->lpm_state = LPM_L0; ++ ++ dev->ep0_dir = USB_DIR_OUT; ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->remote_wakeup = 0; /* default to 0 on reset */ ++ dev->gadget.b_hnp_enable = 0; ++ dev->gadget.a_hnp_support = 0; ++ dev->gadget.a_alt_hnp_support = 0; ++ ++ /* Write-Clear all the setup token semaphores */ ++ endptsetupstat = readl(&dev->op_regs->endptsetupstat); ++ writel(endptsetupstat, &dev->op_regs->endptsetupstat); ++ ++ /* Write-Clear all the endpoint complete status bits */ ++ endptcomplete = readl(&dev->op_regs->endptcomplete); ++ writel(endptcomplete, &dev->op_regs->endptcomplete); ++ ++ /* wait until all endptprime bits cleared */ ++ timeout = jiffies + PRIME_TIMEOUT; ++ while (readl(&dev->op_regs->endptprime)) { ++ if (time_after(jiffies, timeout)) { ++ ERROR(dev, "USB reset timeout\n"); ++ break; ++ } ++ cpu_relax(); ++ } ++ ++ /* write 1s to endptflush register to clear any primed buffers */ ++ writel((u32) ~0, &dev->op_regs->endptflush); ++ ++ if (readl(&dev->op_regs->portsc1) & PORTS_PR) { ++ VDBG(dev, "USB bus reset\n"); ++ /* bus is reseting */ ++ dev->bus_reset = 1; ++ ++ /* reset all the queues, stop all USB activities */ ++ stop_activity(dev, dev->driver); ++ dev->usb_state = USB_STATE_DEFAULT; ++ } else { ++ VDBG(dev, "device controller reset\n"); ++ /* controller reset */ ++ langwell_udc_reset(dev); ++ ++ /* reset all the queues, stop all USB activities */ ++ stop_activity(dev, dev->driver); ++ ++ /* reset ep0 dQH and endptctrl */ ++ ep0_reset(dev); ++ ++ /* enable interrupt and set controller to run state */ ++ langwell_udc_start(dev); ++ ++ dev->usb_state = USB_STATE_ATTACHED; ++ } ++ ++#ifdef OTG_TRANSCEIVER ++ /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */ ++ if (!dev->lotg->otg.default_a) ++ dev->lotg->hsm.b_hnp_enable = 0; ++#endif ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* USB bus suspend/resume interrupt */ ++static void handle_bus_suspend(struct langwell_udc *dev) ++{ ++ u32 devlc; ++ DBG(dev, "---> %s()\n", __func__); ++ ++ dev->resume_state = dev->usb_state; ++ dev->usb_state = USB_STATE_SUSPENDED; ++ ++#ifdef OTG_TRANSCEIVER ++ if (dev->lotg->otg.default_a) { ++ if (dev->lotg->hsm.b_bus_suspend_vld == 1) { ++ dev->lotg->hsm.b_bus_suspend = 1; ++ /* notify transceiver the state changes */ ++ if (spin_trylock(&dev->lotg->wq_lock)) { ++ langwell_update_transceiver(); ++ spin_unlock(&dev->lotg->wq_lock); ++ } ++ } ++ dev->lotg->hsm.b_bus_suspend_vld++; ++ } else { ++ if (!dev->lotg->hsm.a_bus_suspend) { ++ dev->lotg->hsm.a_bus_suspend = 1; ++ /* notify transceiver the state changes */ ++ if (spin_trylock(&dev->lotg->wq_lock)) { ++ langwell_update_transceiver(); ++ spin_unlock(&dev->lotg->wq_lock); ++ } ++ } ++ } ++#endif ++ ++ /* report suspend to the driver */ ++ if (dev->driver) { ++ if (dev->driver->suspend) { ++ spin_unlock(&dev->lock); ++ dev->driver->suspend(&dev->gadget); ++ spin_lock(&dev->lock); ++ DBG(dev, "suspend %s\n", dev->driver->driver.name); ++ } ++ } ++ ++ /* enter PHY low power suspend */ ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "devlc = 0x%08x\n", devlc); ++ devlc |= LPM_PHCD; ++ writel(devlc, &dev->op_regs->devlc); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++static void handle_bus_resume(struct langwell_udc *dev) ++{ ++ u32 devlc; ++ DBG(dev, "---> %s()\n", __func__); ++ ++ dev->usb_state = dev->resume_state; ++ dev->resume_state = 0; ++ ++ /* exit PHY low power suspend */ ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "devlc = 0x%08x\n", devlc); ++ devlc &= ~LPM_PHCD; ++ writel(devlc, &dev->op_regs->devlc); ++ ++#ifdef OTG_TRANSCEIVER ++ if (dev->lotg->otg.default_a == 0) ++ dev->lotg->hsm.a_bus_suspend = 0; ++#endif ++ ++ /* report resume to the driver */ ++ if (dev->driver) { ++ if (dev->driver->resume) { ++ spin_unlock(&dev->lock); ++ dev->driver->resume(&dev->gadget); ++ spin_lock(&dev->lock); ++ DBG(dev, "resume %s\n", dev->driver->driver.name); ++ } ++ } ++ ++ DBG(dev, "<--- %s()\n", __func__); ++} ++ ++ ++/* USB device controller interrupt handler */ ++static irqreturn_t langwell_irq(int irq, void *_dev) ++{ ++ struct langwell_udc *dev = _dev; ++ u32 usbsts, ++ usbintr, ++ irq_sts, ++ portsc1; ++ ++ VDBG(dev, "---> %s()\n", __func__); ++ ++ if (dev->stopped) { ++ VDBG(dev, "handle IRQ_NONE\n"); ++ VDBG(dev, "<--- %s()\n", __func__); ++ return IRQ_NONE; ++ } ++ ++ spin_lock(&dev->lock); ++ ++ /* USB status */ ++ usbsts = readl(&dev->op_regs->usbsts); ++ ++ /* USB interrupt enable */ ++ usbintr = readl(&dev->op_regs->usbintr); ++ ++ irq_sts = usbsts & usbintr; ++ VDBG(dev, "usbsts = 0x%08x, usbintr = 0x%08x, irq_sts = 0x%08x\n", ++ usbsts, usbintr, irq_sts); ++ ++ if (!irq_sts) { ++ VDBG(dev, "handle IRQ_NONE\n"); ++ VDBG(dev, "<--- %s()\n", __func__); ++ spin_unlock(&dev->lock); ++ return IRQ_NONE; ++ } ++ ++ /* Write-Clear interrupt status bits */ ++ writel(irq_sts, &dev->op_regs->usbsts); ++ ++ /* resume from suspend */ ++ portsc1 = readl(&dev->op_regs->portsc1); ++ if (dev->usb_state == USB_STATE_SUSPENDED) ++ if (!(portsc1 & PORTS_SUSP)) ++ handle_bus_resume(dev); ++ ++ /* USB interrupt */ ++ if (irq_sts & STS_UI) { ++ VDBG(dev, "USB interrupt\n"); ++ ++ /* setup packet received from ep0 */ ++ if (readl(&dev->op_regs->endptsetupstat) ++ & EP0SETUPSTAT_MASK) { ++ VDBG(dev, "USB SETUP packet received interrupt\n"); ++ /* setup tripwire semaphone */ ++ setup_tripwire(dev); ++ handle_setup_packet(dev, &dev->local_setup_buff); ++ } ++ ++ /* USB transfer completion */ ++ if (readl(&dev->op_regs->endptcomplete)) { ++ VDBG(dev, "USB transfer completion interrupt\n"); ++ handle_trans_complete(dev); ++ } ++ } ++ ++ /* SOF received interrupt (for ISO transfer) */ ++ if (irq_sts & STS_SRI) { ++ /* FIXME */ ++ /* VDBG(dev, "SOF received interrupt\n"); */ ++ } ++ ++ /* port change detect interrupt */ ++ if (irq_sts & STS_PCI) { ++ VDBG(dev, "port change detect interrupt\n"); ++ handle_port_change(dev); ++ } ++ ++ /* suspend interrrupt */ ++ if (irq_sts & STS_SLI) { ++ VDBG(dev, "suspend interrupt\n"); ++ handle_bus_suspend(dev); ++ } ++ ++ /* USB reset interrupt */ ++ if (irq_sts & STS_URI) { ++ VDBG(dev, "USB reset interrupt\n"); ++ handle_usb_reset(dev); ++ } ++ ++ /* USB error or system error interrupt */ ++ if (irq_sts & (STS_UEI | STS_SEI)) { ++ /* FIXME */ ++ WARNING(dev, "error IRQ, irq_sts: %x\n", irq_sts); ++ } ++ ++ spin_unlock(&dev->lock); ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return IRQ_HANDLED; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* release device structure */ ++static void gadget_release(struct device *_dev) ++{ ++ struct langwell_udc *dev = the_controller; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ complete(dev->done); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ kfree(dev); ++} ++ ++ ++/* tear down the binding between this driver and the pci device */ ++static void langwell_udc_remove(struct pci_dev *pdev) ++{ ++ struct langwell_udc *dev = the_controller; ++ ++ DECLARE_COMPLETION(done); ++ ++ BUG_ON(dev->driver); ++ DBG(dev, "---> %s()\n", __func__); ++ ++ dev->done = &done; ++ ++ /* free memory allocated in probe */ ++ if (dev->dtd_pool) ++ dma_pool_destroy(dev->dtd_pool); ++ ++ if (dev->status_req) { ++ kfree(dev->status_req->req.buf); ++ kfree(dev->status_req); ++ } ++ ++ if (dev->ep_dqh) ++ dma_free_coherent(&pdev->dev, dev->ep_dqh_size, ++ dev->ep_dqh, dev->ep_dqh_dma); ++ ++ kfree(dev->ep); ++ ++ /* diable IRQ handler */ ++ if (dev->got_irq) ++ free_irq(pdev->irq, dev); ++ ++#ifndef OTG_TRANSCEIVER ++ if (dev->cap_regs) ++ iounmap(dev->cap_regs); ++ ++ if (dev->region) ++ release_mem_region(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0)); ++ ++ if (dev->enabled) ++ pci_disable_device(pdev); ++#else ++ if (dev->transceiver) { ++ otg_put_transceiver(dev->transceiver); ++ dev->transceiver = NULL; ++ dev->lotg = NULL; ++ } ++#endif ++ ++ dev->cap_regs = NULL; ++ ++ INFO(dev, "unbind\n"); ++ DBG(dev, "<--- %s()\n", __func__); ++ ++ device_unregister(&dev->gadget.dev); ++ device_remove_file(&pdev->dev, &dev_attr_langwell_udc); ++ ++#ifndef OTG_TRANSCEIVER ++ pci_set_drvdata(pdev, NULL); ++#endif ++ ++ /* free dev, wait for the release() finished */ ++ wait_for_completion(&done); ++ ++ the_controller = NULL; ++} ++ ++ ++/* ++ * wrap this driver around the specified device, but ++ * don't respond over USB until a gadget driver binds to us. ++ */ ++static int langwell_udc_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ struct langwell_udc *dev; ++#ifndef OTG_TRANSCEIVER ++ unsigned long resource, len; ++#endif ++ void __iomem *base = NULL; ++ size_t size; ++ int retval; ++ ++ if (the_controller) { ++ dev_warn(&pdev->dev, "ignoring\n"); ++ return -EBUSY; ++ } ++ ++ /* alloc, and start init */ ++ dev = kzalloc(sizeof *dev, GFP_KERNEL); ++ if (dev == NULL) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ /* initialize device spinlock */ ++ spin_lock_init(&dev->lock); ++ ++ dev->pdev = pdev; ++ DBG(dev, "---> %s()\n", __func__); ++ ++#ifdef OTG_TRANSCEIVER ++ /* PCI device is already enabled by otg_transceiver driver */ ++ dev->enabled = 1; ++ ++ /* mem region and register base */ ++ dev->region = 1; ++ dev->transceiver = otg_get_transceiver(); ++ dev->lotg = otg_to_langwell(dev->transceiver); ++ base = dev->lotg->regs; ++#else ++ pci_set_drvdata(pdev, dev); ++ ++ /* now all the pci goodies ... */ ++ if (pci_enable_device(pdev) < 0) { ++ retval = -ENODEV; ++ goto error; ++ } ++ dev->enabled = 1; ++ ++ /* control register: BAR 0 */ ++ resource = pci_resource_start(pdev, 0); ++ len = pci_resource_len(pdev, 0); ++ if (!request_mem_region(resource, len, driver_name)) { ++ ERROR(dev, "controller already in use\n"); ++ retval = -EBUSY; ++ goto error; ++ } ++ dev->region = 1; ++ ++ base = ioremap_nocache(resource, len); ++#endif ++ if (base == NULL) { ++ ERROR(dev, "can't map memory\n"); ++ retval = -EFAULT; ++ goto error; ++ } ++ ++ dev->cap_regs = (struct langwell_cap_regs __iomem *) base; ++ VDBG(dev, "dev->cap_regs: %p\n", dev->cap_regs); ++ dev->op_regs = (struct langwell_op_regs __iomem *) ++ (base + OP_REG_OFFSET); ++ VDBG(dev, "dev->op_regs: %p\n", dev->op_regs); ++ ++ /* irq setup after old hardware is cleaned up */ ++ if (!pdev->irq) { ++ ERROR(dev, "No IRQ. Check PCI setup!\n"); ++ retval = -ENODEV; ++ goto error; ++ } ++ ++#ifndef OTG_TRANSCEIVER ++ INFO(dev, "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n", ++ pdev->irq, resource, len, base); ++ /* enables bus-mastering for device dev */ ++ pci_set_master(pdev); ++ ++ if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, ++ driver_name, dev) != 0) { ++ ERROR(dev, "request interrupt %d failed\n", pdev->irq); ++ retval = -EBUSY; ++ goto error; ++ } ++ dev->got_irq = 1; ++#endif ++ ++ /* set stopped bit */ ++ dev->stopped = 1; ++ ++ /* capabilities and endpoint number */ ++ dev->lpm = (readl(&dev->cap_regs->hccparams) & HCC_LEN) ? 1 : 0; ++ dev->dciversion = readw(&dev->cap_regs->dciversion); ++ dev->devcap = (readl(&dev->cap_regs->dccparams) & DEVCAP) ? 1 : 0; ++ VDBG(dev, "dev->lpm: %d\n", dev->lpm); ++ VDBG(dev, "dev->dciversion: 0x%04x\n", dev->dciversion); ++ VDBG(dev, "dccparams: 0x%08x\n", readl(&dev->cap_regs->dccparams)); ++ VDBG(dev, "dev->devcap: %d\n", dev->devcap); ++ if (!dev->devcap) { ++ ERROR(dev, "can't support device mode\n"); ++ retval = -ENODEV; ++ goto error; ++ } ++ ++ /* a pair of endpoints (out/in) for each address */ ++ dev->ep_max = DEN(readl(&dev->cap_regs->dccparams)) * 2; ++ VDBG(dev, "dev->ep_max: %d\n", dev->ep_max); ++ ++ /* allocate endpoints memory */ ++ dev->ep = kzalloc(sizeof(struct langwell_ep) * dev->ep_max, ++ GFP_KERNEL); ++ if (!dev->ep) { ++ ERROR(dev, "allocate endpoints memory failed\n"); ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ /* allocate device dQH memory */ ++ size = dev->ep_max * sizeof(struct langwell_dqh); ++ VDBG(dev, "orig size = %d\n", size); ++ if (size < DQH_ALIGNMENT) ++ size = DQH_ALIGNMENT; ++ else if ((size % DQH_ALIGNMENT) != 0) { ++ size += DQH_ALIGNMENT + 1; ++ size &= ~(DQH_ALIGNMENT - 1); ++ } ++ dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size, ++ &dev->ep_dqh_dma, GFP_KERNEL); ++ if (!dev->ep_dqh) { ++ ERROR(dev, "allocate dQH memory failed\n"); ++ retval = -ENOMEM; ++ goto error; ++ } ++ dev->ep_dqh_size = size; ++ VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size); ++ ++ /* initialize ep0 status request structure */ ++ dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL); ++ if (!dev->status_req) { ++ ERROR(dev, "allocate status_req memory failed\n"); ++ retval = -ENOMEM; ++ goto error; ++ } ++ INIT_LIST_HEAD(&dev->status_req->queue); ++ ++ /* allocate a small amount of memory to get valid address */ ++ dev->status_req->req.buf = kmalloc(8, GFP_KERNEL); ++ dev->status_req->req.dma = virt_to_phys(dev->status_req->req.buf); ++ ++ dev->resume_state = USB_STATE_NOTATTACHED; ++ dev->usb_state = USB_STATE_POWERED; ++ dev->ep0_dir = USB_DIR_OUT; ++ dev->remote_wakeup = 0; /* default to 0 on reset */ ++ ++#ifndef OTG_TRANSCEIVER ++ /* reset device controller */ ++ langwell_udc_reset(dev); ++#endif ++ ++ /* initialize gadget structure */ ++ dev->gadget.ops = &langwell_ops; /* usb_gadget_ops */ ++ dev->gadget.ep0 = &dev->ep[0].ep; /* gadget ep0 */ ++ INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */ ++ dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ ++ dev->gadget.is_dualspeed = 1; /* support dual speed */ ++#ifdef OTG_TRANSCEIVER ++ dev->gadget.is_otg = 1; /* support otg mode */ ++#endif ++ ++ /* the "gadget" abstracts/virtualizes the controller */ ++ dev_set_name(&dev->gadget.dev, "gadget"); ++ dev->gadget.dev.parent = &pdev->dev; ++ dev->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ dev->gadget.dev.release = gadget_release; ++ dev->gadget.name = driver_name; /* gadget name */ ++ ++ /* controller endpoints reinit */ ++ eps_reinit(dev); ++ ++#ifndef OTG_TRANSCEIVER ++ /* reset ep0 dQH and endptctrl */ ++ ep0_reset(dev); ++#endif ++ ++ /* create dTD dma_pool resource */ ++ dev->dtd_pool = dma_pool_create("langwell_dtd", ++ &dev->pdev->dev, ++ sizeof(struct langwell_dtd), ++ DTD_ALIGNMENT, ++ DMA_BOUNDARY); ++ ++ if (!dev->dtd_pool) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ /* done */ ++ INFO(dev, "%s\n", driver_desc); ++ INFO(dev, "irq %d, pci mem %p\n", pdev->irq, base); ++ INFO(dev, "Driver version: " DRIVER_VERSION "\n"); ++ INFO(dev, "Support (max) %d endpoints\n", dev->ep_max); ++ INFO(dev, "Device interface version: 0x%04x\n", dev->dciversion); ++ INFO(dev, "Controller mode: %s\n", dev->devcap ? "Device" : "Host"); ++ INFO(dev, "Support USB LPM: %s\n", dev->lpm ? "Yes" : "No"); ++ ++ VDBG(dev, "After langwell_udc_probe(), print all registers:\n"); ++#ifdef VERBOSE ++ print_all_registers(dev); ++#endif ++ ++ the_controller = dev; ++ ++ retval = device_register(&dev->gadget.dev); ++ if (retval) ++ goto error; ++ ++ retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc); ++ if (retval) ++ goto error; ++ ++ VDBG(dev, "<--- %s()\n", __func__); ++ return 0; ++ ++error: ++ if (dev) { ++ DBG(dev, "<--- %s()\n", __func__); ++ langwell_udc_remove(pdev); ++ } ++ ++ return retval; ++} ++ ++ ++/* device controller suspend */ ++static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct langwell_udc *dev = the_controller; ++ u32 devlc; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* disable interrupt and set controller to stop state */ ++ langwell_udc_stop(dev); ++ ++ /* diable IRQ handler */ ++ if (dev->got_irq) ++ free_irq(pdev->irq, dev); ++ dev->got_irq = 0; ++ ++ ++ /* save PCI state */ ++ pci_save_state(pdev); ++ ++ /* set device power state */ ++ pci_set_power_state(pdev, PCI_D3hot); ++ ++ /* enter PHY low power suspend */ ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "devlc = 0x%08x\n", devlc); ++ devlc |= LPM_PHCD; ++ writel(devlc, &dev->op_regs->devlc); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* device controller resume */ ++static int langwell_udc_resume(struct pci_dev *pdev) ++{ ++ struct langwell_udc *dev = the_controller; ++ u32 devlc; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* exit PHY low power suspend */ ++ devlc = readl(&dev->op_regs->devlc); ++ VDBG(dev, "devlc = 0x%08x\n", devlc); ++ devlc &= ~LPM_PHCD; ++ writel(devlc, &dev->op_regs->devlc); ++ ++ /* set device D0 power state */ ++ pci_set_power_state(pdev, PCI_D0); ++ ++ /* restore PCI state */ ++ pci_restore_state(pdev); ++ ++ /* enable IRQ handler */ ++ if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, driver_name, dev) ++ != 0) { ++ ERROR(dev, "request interrupt %d failed\n", pdev->irq); ++ return -1; ++ } ++ dev->got_irq = 1; ++ ++ /* reset and start controller to run state */ ++ if (dev->stopped) { ++ /* reset device controller */ ++ langwell_udc_reset(dev); ++ ++ /* reset ep0 dQH and endptctrl */ ++ ep0_reset(dev); ++ ++ /* start device if gadget is loaded */ ++ if (dev->driver) ++ langwell_udc_start(dev); ++ } ++ ++ /* reset USB status */ ++ dev->usb_state = USB_STATE_ATTACHED; ++ dev->ep0_state = WAIT_FOR_SETUP; ++ dev->ep0_dir = USB_DIR_OUT; ++ ++ DBG(dev, "<--- %s()\n", __func__); ++ return 0; ++} ++ ++ ++/* pci driver shutdown */ ++static void langwell_udc_shutdown(struct pci_dev *pdev) ++{ ++ struct langwell_udc *dev = the_controller; ++ u32 usbmode; ++ ++ DBG(dev, "---> %s()\n", __func__); ++ ++ /* reset controller mode to IDLE */ ++ usbmode = readl(&dev->op_regs->usbmode); ++ DBG(dev, "usbmode = 0x%08x\n", usbmode); ++ usbmode &= (~3 | MODE_IDLE); ++ writel(usbmode, &dev->op_regs->usbmode); ++ ++ DBG(dev, "<--- %s()\n", __func__); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct pci_device_id pci_ids[] = { { ++ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), ++ .class_mask = ~0, ++ .vendor = 0x8086, ++ .device = 0x0811, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++}, { /* end: all zeroes */ } ++}; ++ ++ ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++ ++static struct pci_driver langwell_pci_driver = { ++ .name = (char *) driver_name, ++ .id_table = pci_ids, ++ ++ .probe = langwell_udc_probe, ++ .remove = langwell_udc_remove, ++ ++ /* device controller suspend/resume */ ++ .suspend = langwell_udc_suspend, ++ .resume = langwell_udc_resume, ++ ++ .shutdown = langwell_udc_shutdown, ++}; ++ ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Xiaochen Shen <xiaochen.shen@intel.com>"); ++MODULE_VERSION(DRIVER_VERSION); ++MODULE_LICENSE("GPL"); ++ ++ ++static int __init init(void) ++{ ++#ifdef OTG_TRANSCEIVER ++ return langwell_register_peripheral(&langwell_pci_driver); ++#else ++ return pci_register_driver(&langwell_pci_driver); ++#endif ++} ++module_init(init); ++ ++ ++static void __exit cleanup(void) ++{ ++#ifdef OTG_TRANSCEIVER ++ return langwell_unregister_peripheral(&langwell_pci_driver); ++#else ++ pci_unregister_driver(&langwell_pci_driver); ++#endif ++} ++module_exit(cleanup); ++ +--- /dev/null ++++ b/drivers/usb/gadget/langwell_udc.h +@@ -0,0 +1,228 @@ ++/* ++ * Intel Langwell USB Device Controller driver ++ * Copyright (C) 2008-2009, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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/usb/langwell_udc.h> ++ ++#if defined(CONFIG_USB_LANGWELL_OTG) ++#include <linux/usb/langwell_otg.h> ++#endif ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* driver data structures and utilities */ ++ ++/* ++ * dTD: Device Endpoint Transfer Descriptor ++ * describe to the device controller the location and quantity of ++ * data to be send/received for given transfer ++ */ ++struct langwell_dtd { ++ u32 dtd_next; ++/* bits 31:5, next transfer element pointer */ ++#define DTD_NEXT(d) (((d)>>5)&0x7ffffff) ++#define DTD_NEXT_MASK (0x7ffffff << 5) ++/* terminate */ ++#define DTD_TERM BIT(0) ++ /* bits 7:0, execution back states */ ++ u32 dtd_status:8; ++#define DTD_STATUS(d) (((d)>>0)&0xff) ++#define DTD_STS_ACTIVE BIT(7) /* active */ ++#define DTD_STS_HALTED BIT(6) /* halted */ ++#define DTD_STS_DBE BIT(5) /* data buffer error */ ++#define DTD_STS_TRE BIT(3) /* transaction error */ ++ /* bits 9:8 */ ++ u32 dtd_res0:2; ++ /* bits 11:10, multipier override */ ++ u32 dtd_multo:2; ++#define DTD_MULTO (BIT(11) | BIT(10)) ++ /* bits 14:12 */ ++ u32 dtd_res1:3; ++ /* bit 15, interrupt on complete */ ++ u32 dtd_ioc:1; ++#define DTD_IOC BIT(15) ++ /* bits 30:16, total bytes */ ++ u32 dtd_total:15; ++#define DTD_TOTAL(d) (((d)>>16)&0x7fff) ++#define DTD_MAX_TRANSFER_LENGTH 0x4000 ++ /* bit 31 */ ++ u32 dtd_res2:1; ++ /* dTD buffer pointer page 0 to 4 */ ++ u32 dtd_buf[5]; ++#define DTD_OFFSET_MASK 0xfff ++/* bits 31:12, buffer pointer */ ++#define DTD_BUFFER(d) (((d)>>12)&0x3ff) ++/* bits 11:0, current offset */ ++#define DTD_C_OFFSET(d) (((d)>>0)&0xfff) ++/* bits 10:0, frame number */ ++#define DTD_FRAME(d) (((d)>>0)&0x7ff) ++ ++ /* driver-private parts */ ++ ++ /* dtd dma address */ ++ dma_addr_t dtd_dma; ++ /* next dtd virtual address */ ++ struct langwell_dtd *next_dtd_virt; ++}; ++ ++ ++/* ++ * dQH: Device Endpoint Queue Head ++ * describe where all transfers are managed ++ * 48-byte data structure, aligned on 64-byte boundary ++ * ++ * These are associated with dTD structure ++ */ ++struct langwell_dqh { ++ /* endpoint capabilities and characteristics */ ++ u32 dqh_res0:15; /* bits 14:0 */ ++ u32 dqh_ios:1; /* bit 15, interrupt on setup */ ++#define DQH_IOS BIT(15) ++ u32 dqh_mpl:11; /* bits 26:16, maximum packet length */ ++#define DQH_MPL (0x7ff << 16) ++ u32 dqh_res1:2; /* bits 28:27 */ ++ u32 dqh_zlt:1; /* bit 29, zero length termination */ ++#define DQH_ZLT BIT(29) ++ u32 dqh_mult:2; /* bits 31:30 */ ++#define DQH_MULT (BIT(30) | BIT(31)) ++ ++ /* current dTD pointer */ ++ u32 dqh_current; /* locate the transfer in progress */ ++#define DQH_C_DTD(e) \ ++ (((e)>>5)&0x7ffffff) /* bits 31:5, current dTD pointer */ ++ ++ /* transfer overlay, hardware parts of a struct langwell_dtd */ ++ u32 dtd_next; ++ u32 dtd_status:8; /* bits 7:0, execution back states */ ++ u32 dtd_res0:2; /* bits 9:8 */ ++ u32 dtd_multo:2; /* bits 11:10, multipier override */ ++ u32 dtd_res1:3; /* bits 14:12 */ ++ u32 dtd_ioc:1; /* bit 15, interrupt on complete */ ++ u32 dtd_total:15; /* bits 30:16, total bytes */ ++ u32 dtd_res2:1; /* bit 31 */ ++ u32 dtd_buf[5]; /* dTD buffer pointer page 0 to 4 */ ++ ++ u32 dqh_res2; ++ struct usb_ctrlrequest dqh_setup; /* setup packet buffer */ ++} __attribute__ ((aligned(64))); ++ ++ ++/* endpoint data structure */ ++struct langwell_ep { ++ struct usb_ep ep; ++ dma_addr_t dma; ++ struct langwell_udc *dev; ++ unsigned long irqs; ++ struct list_head queue; ++ struct langwell_dqh *dqh; ++ const struct usb_endpoint_descriptor *desc; ++ char name[14]; ++ unsigned stopped:1, ++ ep_type:2, ++ ep_num:8; ++}; ++ ++ ++/* request data structure */ ++struct langwell_request { ++ struct usb_request req; ++ struct langwell_dtd *dtd, *head, *tail; ++ struct langwell_ep *ep; ++ dma_addr_t dtd_dma; ++ struct list_head queue; ++ unsigned dtd_count; ++ unsigned mapped:1; ++}; ++ ++ ++/* ep0 transfer state */ ++enum ep0_state { ++ WAIT_FOR_SETUP, ++ DATA_STATE_XMIT, ++ DATA_STATE_NEED_ZLP, ++ WAIT_FOR_OUT_STATUS, ++ DATA_STATE_RECV, ++}; ++ ++ ++/* device suspend state */ ++enum lpm_state { ++ LPM_L0, /* on */ ++ LPM_L1, /* LPM L1 sleep */ ++ LPM_L2, /* suspend */ ++ LPM_L3, /* off */ ++}; ++ ++ ++/* device data structure */ ++struct langwell_udc { ++ /* each pci device provides one gadget, several endpoints */ ++ struct usb_gadget gadget; ++ spinlock_t lock; /* device lock */ ++ struct langwell_ep *ep; ++ struct usb_gadget_driver *driver; ++ struct otg_transceiver *transceiver; ++ u8 dev_addr; ++ u32 usb_state; ++ u32 resume_state; ++ u32 bus_reset; ++ enum lpm_state lpm_state; ++ enum ep0_state ep0_state; ++ u32 ep0_dir; ++ u16 dciversion; ++ unsigned ep_max; ++ unsigned devcap:1, ++ enabled:1, ++ region:1, ++ got_irq:1, ++ powered:1, ++ remote_wakeup:1, ++ rate:1, ++ is_reset:1, ++ softconnected:1, ++ vbus_active:1, ++ suspended:1, ++ stopped:1, ++ lpm:1; /* LPM capability */ ++ ++ /* pci state used to access those endpoints */ ++ struct pci_dev *pdev; ++ ++ /* Langwell otg transceiver */ ++ struct langwell_otg *lotg; ++ ++ /* control registers */ ++ struct langwell_cap_regs __iomem *cap_regs; ++ struct langwell_op_regs __iomem *op_regs; ++ ++ struct usb_ctrlrequest local_setup_buff; ++ struct langwell_dqh *ep_dqh; ++ size_t ep_dqh_size; ++ dma_addr_t ep_dqh_dma; ++ ++ /* ep0 status request */ ++ struct langwell_request *status_req; ++ ++ /* dma pool */ ++ struct dma_pool *dtd_pool; ++ ++ /* make sure release() is done */ ++ struct completion *done; ++}; ++ +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o + obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o + obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o + obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o ++obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o + + # + # USB gadget drivers +--- /dev/null ++++ b/include/linux/usb/langwell_udc.h +@@ -0,0 +1,310 @@ ++/* ++ * Intel Langwell USB Device Controller driver ++ * Copyright (C) 2008-2009, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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 __LANGWELL_UDC_H ++#define __LANGWELL_UDC_H ++ ++ ++/* MACRO defines */ ++#define CAP_REG_OFFSET 0x0 ++#define OP_REG_OFFSET 0x28 ++ ++#define DMA_ADDR_INVALID (~(dma_addr_t)0) ++ ++#define DQH_ALIGNMENT 2048 ++#define DTD_ALIGNMENT 64 ++#define DMA_BOUNDARY 4096 ++ ++#define EP0_MAX_PKT_SIZE 64 ++#define EP_DIR_IN 1 ++#define EP_DIR_OUT 0 ++ ++#define FLUSH_TIMEOUT 1000 ++#define RESET_TIMEOUT 1000 ++#define SETUPSTAT_TIMEOUT 100 ++#define PRIME_TIMEOUT 100 ++ ++ ++/* device memory space registers */ ++ ++/* Capability Registers, BAR0 + CAP_REG_OFFSET */ ++struct langwell_cap_regs { ++ /* offset: 0x0 */ ++ u8 caplength; /* offset of Operational Register */ ++ u8 _reserved3; ++ u16 hciversion; /* H: BCD encoding of host version */ ++ u32 hcsparams; /* H: host port steering logic capability */ ++ u32 hccparams; /* H: host multiple mode control capability */ ++#define HCC_LEN BIT(17) /* Link power management (LPM) capability */ ++ u8 _reserved4[0x20-0xc]; ++ /* offset: 0x20 */ ++ u16 dciversion; /* BCD encoding of device version */ ++ u8 _reserved5[0x24-0x22]; ++ u32 dccparams; /* overall device controller capability */ ++#define HOSTCAP BIT(8) /* host capable */ ++#define DEVCAP BIT(7) /* device capable */ ++#define DEN(d) \ ++ (((d)>>0)&0x1f) /* bits 4:0, device endpoint number */ ++} __attribute__ ((packed)); ++ ++ ++/* Operational Registers, BAR0 + OP_REG_OFFSET */ ++struct langwell_op_regs { ++ /* offset: 0x28 */ ++ u32 extsts; ++#define EXTS_TI1 BIT(4) /* general purpose timer interrupt 1 */ ++#define EXTS_TI1TI0 BIT(3) /* general purpose timer interrupt 0 */ ++#define EXTS_TI1UPI BIT(2) /* USB host periodic interrupt */ ++#define EXTS_TI1UAI BIT(1) /* USB host asynchronous interrupt */ ++#define EXTS_TI1NAKI BIT(0) /* NAK interrupt */ ++ u32 extintr; ++#define EXTI_TIE1 BIT(4) /* general purpose timer interrupt enable 1 */ ++#define EXTI_TIE0 BIT(3) /* general purpose timer interrupt enable 0 */ ++#define EXTI_UPIE BIT(2) /* USB host periodic interrupt enable */ ++#define EXTI_UAIE BIT(1) /* USB host asynchronous interrupt enable */ ++#define EXTI_NAKE BIT(0) /* NAK interrupt enable */ ++ /* offset: 0x30 */ ++ u32 usbcmd; ++#define CMD_HIRD(u) \ ++ (((u)>>24)&0xf) /* bits 27:24, host init resume duration */ ++#define CMD_ITC(u) \ ++ (((u)>>16)&0xff) /* bits 23:16, interrupt threshold control */ ++#define CMD_PPE BIT(15) /* per-port change events enable */ ++#define CMD_ATDTW BIT(14) /* add dTD tripwire */ ++#define CMD_SUTW BIT(13) /* setup tripwire */ ++#define CMD_ASPE BIT(11) /* asynchronous schedule park mode enable */ ++#define CMD_FS2 BIT(10) /* frame list size */ ++#define CMD_ASP1 BIT(9) /* asynchronous schedule park mode count */ ++#define CMD_ASP0 BIT(8) ++#define CMD_LR BIT(7) /* light host/device controller reset */ ++#define CMD_IAA BIT(6) /* interrupt on async advance doorbell */ ++#define CMD_ASE BIT(5) /* asynchronous schedule enable */ ++#define CMD_PSE BIT(4) /* periodic schedule enable */ ++#define CMD_FS1 BIT(3) ++#define CMD_FS0 BIT(2) ++#define CMD_RST BIT(1) /* controller reset */ ++#define CMD_RUNSTOP BIT(0) /* run/stop */ ++ u32 usbsts; ++#define STS_PPCI(u) \ ++ (((u)>>16)&0xffff) /* bits 31:16, port-n change detect */ ++#define STS_AS BIT(15) /* asynchronous schedule status */ ++#define STS_PS BIT(14) /* periodic schedule status */ ++#define STS_RCL BIT(13) /* reclamation */ ++#define STS_HCH BIT(12) /* HC halted */ ++#define STS_ULPII BIT(10) /* ULPI interrupt */ ++#define STS_SLI BIT(8) /* DC suspend */ ++#define STS_SRI BIT(7) /* SOF received */ ++#define STS_URI BIT(6) /* USB reset received */ ++#define STS_AAI BIT(5) /* interrupt on async advance */ ++#define STS_SEI BIT(4) /* system error */ ++#define STS_FRI BIT(3) /* frame list rollover */ ++#define STS_PCI BIT(2) /* port change detect */ ++#define STS_UEI BIT(1) /* USB error interrupt */ ++#define STS_UI BIT(0) /* USB interrupt */ ++ u32 usbintr; ++/* bits 31:16, per-port interrupt enable */ ++#define INTR_PPCE(u) (((u)>>16)&0xffff) ++#define INTR_ULPIE BIT(10) /* ULPI enable */ ++#define INTR_SLE BIT(8) /* DC sleep/suspend enable */ ++#define INTR_SRE BIT(7) /* SOF received enable */ ++#define INTR_URE BIT(6) /* USB reset enable */ ++#define INTR_AAE BIT(5) /* interrupt on async advance enable */ ++#define INTR_SEE BIT(4) /* system error enable */ ++#define INTR_FRE BIT(3) /* frame list rollover enable */ ++#define INTR_PCE BIT(2) /* port change detect enable */ ++#define INTR_UEE BIT(1) /* USB error interrupt enable */ ++#define INTR_UE BIT(0) /* USB interrupt enable */ ++ u32 frindex; /* frame index */ ++#define FRINDEX_MASK (0x3fff << 0) ++ u32 ctrldssegment; /* not used */ ++ u32 deviceaddr; ++#define USBADR_SHIFT 25 ++#define USBADR(d) \ ++ (((d)>>25)&0x7f) /* bits 31:25, device address */ ++#define USBADR_MASK (0x7f << 25) ++#define USBADRA BIT(24) /* device address advance */ ++ u32 endpointlistaddr;/* endpoint list top memory address */ ++/* bits 31:11, endpoint list pointer */ ++#define EPBASE(d) (((d)>>11)&0x1fffff) ++#define ENDPOINTLISTADDR_MASK (0x1fffff << 11) ++ u32 ttctrl; /* H: TT operatin, not used */ ++ /* offset: 0x50 */ ++ u32 burstsize; /* burst size of data movement */ ++#define TXPBURST(b) \ ++ (((b)>>8)&0xff) /* bits 15:8, TX burst length */ ++#define RXPBURST(b) \ ++ (((b)>>0)&0xff) /* bits 7:0, RX burst length */ ++ u32 txfilltuning; /* TX tuning */ ++ u32 txttfilltuning; /* H: TX TT tuning */ ++ u32 ic_usb; /* control the IC_USB FS/LS transceiver */ ++ /* offset: 0x60 */ ++ u32 ulpi_viewport; /* indirect access to ULPI PHY */ ++#define ULPIWU BIT(31) /* ULPI wakeup */ ++#define ULPIRUN BIT(30) /* ULPI read/write run */ ++#define ULPIRW BIT(29) /* ULPI read/write control */ ++#define ULPISS BIT(27) /* ULPI sync state */ ++#define ULPIPORT(u) \ ++ (((u)>>24)&7) /* bits 26:24, ULPI port number */ ++#define ULPIADDR(u) \ ++ (((u)>>16)&0xff) /* bits 23:16, ULPI data address */ ++#define ULPIDATRD(u) \ ++ (((u)>>8)&0xff) /* bits 15:8, ULPI data read */ ++#define ULPIDATWR(u) \ ++ (((u)>>0)&0xff) /* bits 7:0, ULPI date write */ ++ u8 _reserved6[0x70-0x64]; ++ /* offset: 0x70 */ ++ u32 configflag; /* H: not used */ ++ u32 portsc1; /* port status */ ++#define DA(p) \ ++ (((p)>>25)&0x7f) /* bits 31:25, device address */ ++#define PORTS_SSTS (BIT(24) | BIT(23)) /* suspend status */ ++#define PORTS_WKOC BIT(22) /* wake on over-current enable */ ++#define PORTS_WKDS BIT(21) /* wake on disconnect enable */ ++#define PORTS_WKCN BIT(20) /* wake on connect enable */ ++#define PORTS_PTC(p) (((p)>>16)&0xf) /* bits 19:16, port test control */ ++#define PORTS_PIC (BIT(15) | BIT(14)) /* port indicator control */ ++#define PORTS_PO BIT(13) /* port owner */ ++#define PORTS_PP BIT(12) /* port power */ ++#define PORTS_LS (BIT(11) | BIT(10)) /* line status */ ++#define PORTS_SLP BIT(9) /* suspend using L1 */ ++#define PORTS_PR BIT(8) /* port reset */ ++#define PORTS_SUSP BIT(7) /* suspend */ ++#define PORTS_FPR BIT(6) /* force port resume */ ++#define PORTS_OCC BIT(5) /* over-current change */ ++#define PORTS_OCA BIT(4) /* over-current active */ ++#define PORTS_PEC BIT(3) /* port enable/disable change */ ++#define PORTS_PE BIT(2) /* port enable/disable */ ++#define PORTS_CSC BIT(1) /* connect status change */ ++#define PORTS_CCS BIT(0) /* current connect status */ ++ u8 _reserved7[0xb4-0x78]; ++ /* offset: 0xb4 */ ++ u32 devlc; /* control LPM and each USB port behavior */ ++/* bits 31:29, parallel transceiver select */ ++#define LPM_PTS(d) (((d)>>29)&7) ++#define LPM_STS BIT(28) /* serial transceiver select */ ++#define LPM_PTW BIT(27) /* parallel transceiver width */ ++#define LPM_PSPD(d) (((d)>>25)&3) /* bits 26:25, port speed */ ++#define LPM_PSPD_MASK (BIT(26) | BIT(25)) ++#define LPM_SPEED_FULL 0 ++#define LPM_SPEED_LOW 1 ++#define LPM_SPEED_HIGH 2 ++#define LPM_SRT BIT(24) /* shorten reset time */ ++#define LPM_PFSC BIT(23) /* port force full speed connect */ ++#define LPM_PHCD BIT(22) /* PHY low power suspend clock disable */ ++#define LPM_STL BIT(16) /* STALL reply to LPM token */ ++#define LPM_BA(d) \ ++ (((d)>>1)&0x7ff) /* bits 11:1, BmAttributes */ ++#define LPM_NYT_ACK BIT(0) /* NYET/ACK reply to LPM token */ ++ u8 _reserved8[0xf4-0xb8]; ++ /* offset: 0xf4 */ ++ u32 otgsc; /* On-The-Go status and control */ ++#define OTGSC_DPIE BIT(30) /* data pulse interrupt enable */ ++#define OTGSC_MSE BIT(29) /* 1 ms timer interrupt enable */ ++#define OTGSC_BSEIE BIT(28) /* B session end interrupt enable */ ++#define OTGSC_BSVIE BIT(27) /* B session valid interrupt enable */ ++#define OTGSC_ASVIE BIT(26) /* A session valid interrupt enable */ ++#define OTGSC_AVVIE BIT(25) /* A VBUS valid interrupt enable */ ++#define OTGSC_IDIE BIT(24) /* USB ID interrupt enable */ ++#define OTGSC_DPIS BIT(22) /* data pulse interrupt status */ ++#define OTGSC_MSS BIT(21) /* 1 ms timer interrupt status */ ++#define OTGSC_BSEIS BIT(20) /* B session end interrupt status */ ++#define OTGSC_BSVIS BIT(19) /* B session valid interrupt status */ ++#define OTGSC_ASVIS BIT(18) /* A session valid interrupt status */ ++#define OTGSC_AVVIS BIT(17) /* A VBUS valid interrupt status */ ++#define OTGSC_IDIS BIT(16) /* USB ID interrupt status */ ++#define OTGSC_DPS BIT(14) /* data bus pulsing status */ ++#define OTGSC_MST BIT(13) /* 1 ms timer toggle */ ++#define OTGSC_BSE BIT(12) /* B session end */ ++#define OTGSC_BSV BIT(11) /* B session valid */ ++#define OTGSC_ASV BIT(10) /* A session valid */ ++#define OTGSC_AVV BIT(9) /* A VBUS valid */ ++#define OTGSC_USBID BIT(8) /* USB ID */ ++#define OTGSC_HABA BIT(7) /* hw assist B-disconnect to A-connect */ ++#define OTGSC_HADP BIT(6) /* hw assist data pulse */ ++#define OTGSC_IDPU BIT(5) /* ID pullup */ ++#define OTGSC_DP BIT(4) /* data pulsing */ ++#define OTGSC_OT BIT(3) /* OTG termination */ ++#define OTGSC_HAAR BIT(2) /* hw assist auto reset */ ++#define OTGSC_VC BIT(1) /* VBUS charge */ ++#define OTGSC_VD BIT(0) /* VBUS discharge */ ++ u32 usbmode; ++#define MODE_VBPS BIT(5) /* R/W VBUS power select */ ++#define MODE_SDIS BIT(4) /* R/W stream disable mode */ ++#define MODE_SLOM BIT(3) /* R/W setup lockout mode */ ++#define MODE_ENSE BIT(2) /* endian select */ ++#define MODE_CM(u) (((u)>>0)&3) /* bits 1:0, controller mode */ ++#define MODE_IDLE 0 ++#define MODE_DEVICE 2 ++#define MODE_HOST 3 ++ u8 _reserved9[0x100-0xfc]; ++ /* offset: 0x100 */ ++ u32 endptnak; ++#define EPTN(e) \ ++ (((e)>>16)&0xffff) /* bits 31:16, TX endpoint NAK */ ++#define EPRN(e) \ ++ (((e)>>0)&0xffff) /* bits 15:0, RX endpoint NAK */ ++ u32 endptnaken; ++#define EPTNE(e) \ ++ (((e)>>16)&0xffff) /* bits 31:16, TX endpoint NAK enable */ ++#define EPRNE(e) \ ++ (((e)>>0)&0xffff) /* bits 15:0, RX endpoint NAK enable */ ++ u32 endptsetupstat; ++#define SETUPSTAT_MASK (0xffff << 0) /* bits 15:0 */ ++#define EP0SETUPSTAT_MASK 1 ++ u32 endptprime; ++/* bits 31:16, prime endpoint transmit buffer */ ++#define PETB(e) (((e)>>16)&0xffff) ++/* bits 15:0, prime endpoint receive buffer */ ++#define PERB(e) (((e)>>0)&0xffff) ++ /* offset: 0x110 */ ++ u32 endptflush; ++/* bits 31:16, flush endpoint transmit buffer */ ++#define FETB(e) (((e)>>16)&0xffff) ++/* bits 15:0, flush endpoint receive buffer */ ++#define FERB(e) (((e)>>0)&0xffff) ++ u32 endptstat; ++/* bits 31:16, endpoint transmit buffer ready */ ++#define ETBR(e) (((e)>>16)&0xffff) ++/* bits 15:0, endpoint receive buffer ready */ ++#define ERBR(e) (((e)>>0)&0xffff) ++ u32 endptcomplete; ++/* bits 31:16, endpoint transmit complete event */ ++#define ETCE(e) (((e)>>16)&0xffff) ++/* bits 15:0, endpoint receive complete event */ ++#define ERCE(e) (((e)>>0)&0xffff) ++ /* offset: 0x11c */ ++ u32 endptctrl[16]; ++#define EPCTRL_TXE BIT(23) /* TX endpoint enable */ ++#define EPCTRL_TXR BIT(22) /* TX data toggle reset */ ++#define EPCTRL_TXI BIT(21) /* TX data toggle inhibit */ ++#define EPCTRL_TXT(e) (((e)>>18)&3) /* bits 19:18, TX endpoint type */ ++#define EPCTRL_TXT_SHIFT 18 ++#define EPCTRL_TXD BIT(17) /* TX endpoint data source */ ++#define EPCTRL_TXS BIT(16) /* TX endpoint STALL */ ++#define EPCTRL_RXE BIT(7) /* RX endpoint enable */ ++#define EPCTRL_RXR BIT(6) /* RX data toggle reset */ ++#define EPCTRL_RXI BIT(5) /* RX data toggle inhibit */ ++#define EPCTRL_RXT(e) (((e)>>2)&3) /* bits 3:2, RX endpoint type */ ++#define EPCTRL_RXT_SHIFT 2 /* bits 19:18, TX endpoint type */ ++#define EPCTRL_RXD BIT(1) /* RX endpoint data sink */ ++#define EPCTRL_RXS BIT(0) /* RX endpoint STALL */ ++} __attribute__ ((packed)); ++ ++#endif /* __LANGWELL_UDC_H */ ++ diff --git a/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch b/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch new file mode 100644 index 00000000000000..c2e01521cd8be4 --- /dev/null +++ b/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch @@ -0,0 +1,2173 @@ +From hao.wu@intel.com Thu Jun 4 11:01:04 2009 +From: Hao Wu <hao.wu@intel.com> +Date: Thu, 4 Jun 2009 16:06:50 +0800 +Subject: USB: Add Intel Langwell USB OTG Transceiver Drive +Message-ID: <20090604080650.GA15482@intel.com> + +From: Hao Wu <hao.wu@intel.com> + +Description: +This driver is used for Intel Langwell* USB OTG controller in Intel +Moorestown* platform. It tries to implement host/device role switch +according to OTG spec. The actual hsot and device functions are +accomplished in modified EHCI driver and Intel Langwell USB OTG client +controller driver. + +* Langwell and Moorestown are names used in development. They are not + approved official name. + +Note: +This patch is the first version Intel Langwell USB OTG Transceiver +driver. The development is not finished, and the bug fixing is on going +for some hardware and software issues. The main purpose of this +submission is for code view. + +Supported features: +- Data-line Pulsing SRP +- Support HNP to switch roles +- PCI D0/D3 power management support + +Known issues: +- HNP is only tested with another Moorestown platform. +- PCI D0/D3 power management support is not fully tested. +- VBus Pulsing SRP is not support in current version. + +Signed-off-by: Hao Wu <hao.wu@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/otg/Kconfig | 14 + drivers/usb/otg/Makefile | 1 + drivers/usb/otg/langwell_otg.c | 1915 +++++++++++++++++++++++++++++++++++++++ + include/linux/usb/langwell_otg.h | 177 +++ + 4 files changed, 2107 insertions(+) + +--- a/drivers/usb/otg/Kconfig ++++ b/drivers/usb/otg/Kconfig +@@ -59,4 +59,18 @@ config NOP_USB_XCEIV + built-in with usb ip or which are autonomous and doesn't require any + phy programming such as ISP1x04 etc. + ++config USB_LANGWELL_OTG ++ tristate "Intel Langwell USB OTG dual-role support" ++ depends on USB && MRST ++ select USB_OTG ++ select USB_OTG_UTILS ++ help ++ Say Y here if you want to build Intel Langwell USB OTG ++ transciever driver in kernel. This driver implements role ++ switch between EHCI host driver and Langwell USB OTG ++ client driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called langwell_otg. ++ + endif # USB || OTG +--- /dev/null ++++ b/drivers/usb/otg/langwell_otg.c +@@ -0,0 +1,1915 @@ ++/* ++ * Intel Langwell USB OTG transceiver driver ++ * Copyright (C) 2008 - 2009, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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. ++ * ++ */ ++/* This driver helps to switch Langwell OTG controller function between host ++ * and peripheral. It works with EHCI driver and Langwell client controller ++ * driver together. ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/pci.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/moduleparam.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++#include <linux/usb.h> ++#include <linux/usb/otg.h> ++#include <linux/notifier.h> ++#include <asm/ipc_defs.h> ++#include <linux/delay.h> ++#include "../core/hcd.h" ++ ++#include <linux/usb/langwell_otg.h> ++ ++#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" ++#define DRIVER_VERSION "3.0.0.32L.0002" ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>"); ++MODULE_VERSION(DRIVER_VERSION); ++MODULE_LICENSE("GPL"); ++ ++static const char driver_name[] = "langwell_otg"; ++ ++static int langwell_otg_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id); ++static void langwell_otg_remove(struct pci_dev *pdev); ++static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); ++static int langwell_otg_resume(struct pci_dev *pdev); ++ ++static int langwell_otg_set_host(struct otg_transceiver *otg, ++ struct usb_bus *host); ++static int langwell_otg_set_peripheral(struct otg_transceiver *otg, ++ struct usb_gadget *gadget); ++static int langwell_otg_start_srp(struct otg_transceiver *otg); ++ ++static const struct pci_device_id pci_ids[] = {{ ++ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), ++ .class_mask = ~0, ++ .vendor = 0x8086, ++ .device = 0x0811, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++}, { /* end: all zeroes */ } ++}; ++ ++static struct pci_driver otg_pci_driver = { ++ .name = (char *) driver_name, ++ .id_table = pci_ids, ++ ++ .probe = langwell_otg_probe, ++ .remove = langwell_otg_remove, ++ ++ .suspend = langwell_otg_suspend, ++ .resume = langwell_otg_resume, ++}; ++ ++static const char *state_string(enum usb_otg_state state) ++{ ++ switch (state) { ++ case OTG_STATE_A_IDLE: ++ return "a_idle"; ++ case OTG_STATE_A_WAIT_VRISE: ++ return "a_wait_vrise"; ++ case OTG_STATE_A_WAIT_BCON: ++ return "a_wait_bcon"; ++ case OTG_STATE_A_HOST: ++ return "a_host"; ++ case OTG_STATE_A_SUSPEND: ++ return "a_suspend"; ++ case OTG_STATE_A_PERIPHERAL: ++ return "a_peripheral"; ++ case OTG_STATE_A_WAIT_VFALL: ++ return "a_wait_vfall"; ++ case OTG_STATE_A_VBUS_ERR: ++ return "a_vbus_err"; ++ case OTG_STATE_B_IDLE: ++ return "b_idle"; ++ case OTG_STATE_B_SRP_INIT: ++ return "b_srp_init"; ++ case OTG_STATE_B_PERIPHERAL: ++ return "b_peripheral"; ++ case OTG_STATE_B_WAIT_ACON: ++ return "b_wait_acon"; ++ case OTG_STATE_B_HOST: ++ return "b_host"; ++ default: ++ return "UNDEFINED"; ++ } ++} ++ ++/* HSM timers */ ++static inline struct langwell_otg_timer *otg_timer_initializer ++(void (*function)(unsigned long), unsigned long expires, unsigned long data) ++{ ++ struct langwell_otg_timer *timer; ++ timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); ++ timer->function = function; ++ timer->expires = expires; ++ timer->data = data; ++ return timer; ++} ++ ++static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, ++ *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr, ++ *b_bus_suspend_tmr; ++ ++static struct list_head active_timers; ++ ++static struct langwell_otg *the_transceiver; ++ ++/* host/client notify transceiver when event affects HNP state */ ++void langwell_update_transceiver() ++{ ++ otg_dbg("transceiver driver is notified\n"); ++ queue_work(the_transceiver->qwork, &the_transceiver->work); ++} ++EXPORT_SYMBOL(langwell_update_transceiver); ++ ++static int langwell_otg_set_host(struct otg_transceiver *otg, ++ struct usb_bus *host) ++{ ++ otg->host = host; ++ ++ return 0; ++} ++ ++static int langwell_otg_set_peripheral(struct otg_transceiver *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->gadget = gadget; ++ ++ return 0; ++} ++ ++static int langwell_otg_set_power(struct otg_transceiver *otg, ++ unsigned mA) ++{ ++ return 0; ++} ++ ++/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ ++static void langwell_otg_drv_vbus(int on) ++{ ++ struct ipc_pmic_reg_data pmic_data = {0}; ++ struct ipc_pmic_reg_data battery_data; ++ ++ /* Check if battery is attached or not */ ++ battery_data.pmic_reg_data[0].register_address = 0xd2; ++ battery_data.ioc = 0; ++ battery_data.num_entries = 1; ++ if (ipc_pmic_register_read(&battery_data)) { ++ otg_dbg("Failed to read PMIC register 0xd2.\n"); ++ return; ++ } ++ ++ if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) { ++ otg_dbg("no battery attached\n"); ++ return; ++ } ++ ++ /* Workaround for battery attachment issue */ ++ if (battery_data.pmic_reg_data[0].value == 0x34) { ++ otg_dbg("battery \n"); ++ return; ++ } ++ ++ otg_dbg("battery attached\n"); ++ ++ pmic_data.ioc = 0; ++ pmic_data.pmic_reg_data[0].register_address = 0xD4; ++ pmic_data.num_entries = 1; ++ if (on) ++ pmic_data.pmic_reg_data[0].value = 0x20; ++ else ++ pmic_data.pmic_reg_data[0].value = 0xc0; ++ ++ if (ipc_pmic_register_write(&pmic_data, TRUE)) ++ otg_dbg("Failed to write PMIC.\n"); ++ ++} ++ ++/* charge vbus or discharge vbus through a resistor to ground */ ++static void langwell_otg_chrg_vbus(int on) ++{ ++ ++ u32 val; ++ ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ ++ if (on) ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, ++ the_transceiver->regs + CI_OTGSC); ++ else ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, ++ the_transceiver->regs + CI_OTGSC); ++ ++} ++ ++/* Start SRP */ ++static int langwell_otg_start_srp(struct otg_transceiver *otg) ++{ ++ u32 val; ++ ++ otg_dbg("Start SRP ->\n"); ++ ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, ++ the_transceiver->regs + CI_OTGSC); ++ ++ /* Check if the data plus is finished or not */ ++ msleep(8); ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ if (val & (OTGSC_HADP | OTGSC_DP)) ++ otg_dbg("DataLine SRP Error\n"); ++ ++ /* FIXME: VBus SRP */ ++ ++ return 0; ++} ++ ++ ++/* stop SOF via bus_suspend */ ++static void langwell_otg_loc_sof(int on) ++{ ++ struct usb_hcd *hcd; ++ int err; ++ ++ otg_dbg("loc_sof -> %d\n", on); ++ ++ hcd = bus_to_hcd(the_transceiver->otg.host); ++ if (on) ++ err = hcd->driver->bus_resume(hcd); ++ else ++ err = hcd->driver->bus_suspend(hcd); ++ ++ if (err) ++ otg_dbg("Failed to resume/suspend bus - %d\n", err); ++} ++ ++static void langwell_otg_phy_low_power(int on) ++{ ++ u32 val; ++ ++ otg_dbg("phy low power mode-> %d\n", on); ++ ++ val = readl(the_transceiver->regs + CI_HOSTPC1); ++ if (on) ++ writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); ++ else ++ writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); ++} ++ ++/* Enable/Disable OTG interrupt */ ++static void langwell_otg_intr(int on) ++{ ++ u32 val; ++ ++ otg_dbg("interrupt -> %d\n", on); ++ ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ if (on) { ++ val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU); ++ writel(val, the_transceiver->regs + CI_OTGSC); ++ } else { ++ val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU); ++ writel(val, the_transceiver->regs + CI_OTGSC); ++ } ++} ++ ++/* set HAAR: Hardware Assist Auto-Reset */ ++static void langwell_otg_HAAR(int on) ++{ ++ u32 val; ++ ++ otg_dbg("HAAR -> %d\n", on); ++ ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ if (on) ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, ++ the_transceiver->regs + CI_OTGSC); ++ else ++ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, ++ the_transceiver->regs + CI_OTGSC); ++} ++ ++/* set HABA: Hardware Assist B-Disconnect to A-Connect */ ++static void langwell_otg_HABA(int on) ++{ ++ u32 val; ++ ++ otg_dbg("HABA -> %d\n", on); ++ ++ val = readl(the_transceiver->regs + CI_OTGSC); ++ if (on) ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, ++ the_transceiver->regs + CI_OTGSC); ++ else ++ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, ++ the_transceiver->regs + CI_OTGSC); ++} ++ ++static int langwell_otg_check_se0_srp(int on) ++{ ++ u32 val; ++ ++ int delay_time = TB_SE0_SRP * 10; /* step is 100us */ ++ ++ otg_dbg("check_se0_srp -> \n"); ++ ++ do { ++ udelay(100); ++ if (!delay_time--) ++ break; ++ val = readl(the_transceiver->regs + CI_PORTSC1); ++ val &= PORTSC_LS; ++ } while (!val); ++ ++ otg_dbg("check_se0_srp <- \n"); ++ return val; ++} ++ ++/* The timeout callback function to set time out bit */ ++static void set_tmout(unsigned long indicator) ++{ ++ *(int *)indicator = 1; ++} ++ ++void langwell_otg_nsf_msg(unsigned long indicator) ++{ ++ switch (indicator) { ++ case 2: ++ case 4: ++ case 6: ++ case 7: ++ printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n", ++ indicator); ++ break; ++ case 3: ++ printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n", ++ indicator); ++ break; ++ default: ++ printk(KERN_ERR "Do not have this kind of NSF\n"); ++ break; ++ } ++} ++ ++/* Initialize timers */ ++static void langwell_otg_init_timers(struct otg_hsm *hsm) ++{ ++ /* HSM used timers */ ++ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, ++ (unsigned long)&hsm->a_wait_vrise_tmout); ++ a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, ++ (unsigned long)&hsm->a_wait_bcon_tmout); ++ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, ++ (unsigned long)&hsm->a_aidl_bdis_tmout); ++ b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, ++ (unsigned long)&hsm->b_ase0_brst_tmout); ++ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, ++ (unsigned long)&hsm->b_se0_srp); ++ b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES, ++ (unsigned long)&hsm->b_srp_res_tmout); ++ b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND, ++ (unsigned long)&hsm->b_bus_suspend_tmout); ++} ++ ++/* Free timers */ ++static void langwell_otg_free_timers(void) ++{ ++ kfree(a_wait_vrise_tmr); ++ kfree(a_wait_bcon_tmr); ++ kfree(a_aidl_bdis_tmr); ++ kfree(b_ase0_brst_tmr); ++ kfree(b_se0_srp_tmr); ++ kfree(b_srp_res_tmr); ++ kfree(b_bus_suspend_tmr); ++} ++ ++/* Add timer to timer list */ ++static void langwell_otg_add_timer(void *gtimer) ++{ ++ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; ++ struct langwell_otg_timer *tmp_timer; ++ u32 val32; ++ ++ /* Check if the timer is already in the active list, ++ * if so update timer count ++ */ ++ list_for_each_entry(tmp_timer, &active_timers, list) ++ if (tmp_timer == timer) { ++ timer->count = timer->expires; ++ return; ++ } ++ timer->count = timer->expires; ++ ++ if (list_empty(&active_timers)) { ++ val32 = readl(the_transceiver->regs + CI_OTGSC); ++ writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); ++ } ++ ++ list_add_tail(&timer->list, &active_timers); ++} ++ ++/* Remove timer from the timer list; clear timeout status */ ++static void langwell_otg_del_timer(void *gtimer) ++{ ++ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; ++ struct langwell_otg_timer *tmp_timer, *del_tmp; ++ u32 val32; ++ ++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) ++ if (tmp_timer == timer) ++ list_del(&timer->list); ++ ++ if (list_empty(&active_timers)) { ++ val32 = readl(the_transceiver->regs + CI_OTGSC); ++ writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); ++ } ++} ++ ++/* Reduce timer count by 1, and find timeout conditions.*/ ++static int langwell_otg_tick_timer(u32 *int_sts) ++{ ++ struct langwell_otg_timer *tmp_timer, *del_tmp; ++ int expired = 0; ++ ++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { ++ tmp_timer->count--; ++ /* check if timer expires */ ++ if (!tmp_timer->count) { ++ list_del(&tmp_timer->list); ++ tmp_timer->function(tmp_timer->data); ++ expired = 1; ++ } ++ } ++ ++ if (list_empty(&active_timers)) { ++ otg_dbg("tick timer: disable 1ms int\n"); ++ *int_sts = *int_sts & ~OTGSC_1MSE; ++ } ++ return expired; ++} ++ ++static void reset_otg(void) ++{ ++ u32 val; ++ int delay_time = 1000; ++ ++ otg_dbg("reseting OTG controller ...\n"); ++ val = readl(the_transceiver->regs + CI_USBCMD); ++ writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD); ++ do { ++ udelay(100); ++ if (!delay_time--) ++ otg_dbg("reset timeout\n"); ++ val = readl(the_transceiver->regs + CI_USBCMD); ++ val &= USBCMD_RST; ++ } while (val != 0); ++ otg_dbg("reset done.\n"); ++} ++ ++static void set_host_mode(void) ++{ ++ u32 val; ++ ++ reset_otg(); ++ val = readl(the_transceiver->regs + CI_USBMODE); ++ val = (val & (~USBMODE_CM)) | USBMODE_HOST; ++ writel(val, the_transceiver->regs + CI_USBMODE); ++} ++ ++static void set_client_mode(void) ++{ ++ u32 val; ++ ++ reset_otg(); ++ val = readl(the_transceiver->regs + CI_USBMODE); ++ val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; ++ writel(val, the_transceiver->regs + CI_USBMODE); ++} ++ ++static void init_hsm(void) ++{ ++ struct langwell_otg *langwell = the_transceiver; ++ u32 val32; ++ ++ /* read OTGSC after reset */ ++ val32 = readl(langwell->regs + CI_OTGSC); ++ otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32); ++ ++ /* set init state */ ++ if (val32 & OTGSC_ID) { ++ langwell->hsm.id = 1; ++ langwell->otg.default_a = 0; ++ set_client_mode(); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ langwell_otg_drv_vbus(0); ++ } else { ++ langwell->hsm.id = 0; ++ langwell->otg.default_a = 1; ++ set_host_mode(); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ } ++ ++ /* set session indicator */ ++ if (val32 & OTGSC_BSE) ++ langwell->hsm.b_sess_end = 1; ++ if (val32 & OTGSC_BSV) ++ langwell->hsm.b_sess_vld = 1; ++ if (val32 & OTGSC_ASV) ++ langwell->hsm.a_sess_vld = 1; ++ if (val32 & OTGSC_AVV) ++ langwell->hsm.a_vbus_vld = 1; ++ ++ /* defautly power the bus */ ++ langwell->hsm.a_bus_req = 1; ++ langwell->hsm.a_bus_drop = 0; ++ /* defautly don't request bus as B device */ ++ langwell->hsm.b_bus_req = 0; ++ /* no system error */ ++ langwell->hsm.a_clr_err = 0; ++} ++ ++static irqreturn_t otg_dummy_irq(int irq, void *_dev) ++{ ++ void __iomem *reg_base = _dev; ++ u32 val; ++ u32 int_mask = 0; ++ ++ val = readl(reg_base + CI_USBMODE); ++ if ((val & USBMODE_CM) != USBMODE_DEVICE) ++ return IRQ_NONE; ++ ++ val = readl(reg_base + CI_USBSTS); ++ int_mask = val & INTR_DUMMY_MASK; ++ ++ if (int_mask == 0) ++ return IRQ_NONE; ++ ++ /* clear hsm.b_conn here since host driver can't detect it ++ * otg_dummy_irq called means B-disconnect happened. ++ */ ++ if (the_transceiver->hsm.b_conn) { ++ the_transceiver->hsm.b_conn = 0; ++ if (spin_trylock(&the_transceiver->wq_lock)) { ++ queue_work(the_transceiver->qwork, ++ &the_transceiver->work); ++ spin_unlock(&the_transceiver->wq_lock); ++ } ++ } ++ /* Clear interrupts */ ++ writel(int_mask, reg_base + CI_USBSTS); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t otg_irq(int irq, void *_dev) ++{ ++ struct langwell_otg *langwell = _dev; ++ u32 int_sts, int_en; ++ u32 int_mask = 0; ++ int flag = 0; ++ ++ int_sts = readl(langwell->regs + CI_OTGSC); ++ int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; ++ int_mask = int_sts & int_en; ++ if (int_mask == 0) ++ return IRQ_NONE; ++ ++ if (int_mask & OTGSC_IDIS) { ++ otg_dbg("%s: id change int\n", __func__); ++ langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; ++ flag = 1; ++ } ++ if (int_mask & OTGSC_DPIS) { ++ otg_dbg("%s: data pulse int\n", __func__); ++ langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; ++ flag = 1; ++ } ++ if (int_mask & OTGSC_BSEIS) { ++ otg_dbg("%s: b session end int\n", __func__); ++ langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; ++ flag = 1; ++ } ++ if (int_mask & OTGSC_BSVIS) { ++ otg_dbg("%s: b session valid int\n", __func__); ++ langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; ++ flag = 1; ++ } ++ if (int_mask & OTGSC_ASVIS) { ++ otg_dbg("%s: a session valid int\n", __func__); ++ langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; ++ flag = 1; ++ } ++ if (int_mask & OTGSC_AVVIS) { ++ otg_dbg("%s: a vbus valid int\n", __func__); ++ langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; ++ flag = 1; ++ } ++ ++ if (int_mask & OTGSC_1MSS) { ++ /* need to schedule otg_work if any timer is expired */ ++ if (langwell_otg_tick_timer(&int_sts)) ++ flag = 1; ++ } ++ ++ writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, ++ langwell->regs + CI_OTGSC); ++ if (flag) ++ queue_work(langwell->qwork, &langwell->work); ++ ++ return IRQ_HANDLED; ++} ++ ++static void langwell_otg_work(struct work_struct *work) ++{ ++ struct langwell_otg *langwell = container_of(work, ++ struct langwell_otg, work); ++ int retval; ++ ++ otg_dbg("%s: old state = %s\n", __func__, ++ state_string(langwell->otg.state)); ++ ++ switch (langwell->otg.state) { ++ case OTG_STATE_UNDEFINED: ++ case OTG_STATE_B_IDLE: ++ if (!langwell->hsm.id) { ++ langwell_otg_del_timer(b_srp_res_tmr); ++ langwell->otg.default_a = 1; ++ langwell->hsm.a_srp_det = 0; ++ ++ langwell_otg_chrg_vbus(0); ++ langwell_otg_drv_vbus(0); ++ ++ set_host_mode(); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.b_srp_res_tmout) { ++ langwell->hsm.b_srp_res_tmout = 0; ++ langwell->hsm.b_bus_req = 0; ++ langwell_otg_nsf_msg(6); ++ } else if (langwell->hsm.b_sess_vld) { ++ langwell_otg_del_timer(b_srp_res_tmr); ++ langwell->hsm.b_sess_end = 0; ++ langwell->hsm.a_bus_suspend = 0; ++ ++ langwell_otg_chrg_vbus(0); ++ if (langwell->client_ops) { ++ langwell->client_ops->resume(langwell->pdev); ++ langwell->otg.state = OTG_STATE_B_PERIPHERAL; ++ } else ++ otg_dbg("client driver not loaded.\n"); ++ ++ } else if (langwell->hsm.b_bus_req && ++ (langwell->hsm.b_sess_end)) { ++ /* workaround for b_se0_srp detection */ ++ retval = langwell_otg_check_se0_srp(0); ++ if (retval) { ++ langwell->hsm.b_bus_req = 0; ++ otg_dbg("LS is not SE0, try again later\n"); ++ } else { ++ /* Start SRP */ ++ langwell_otg_start_srp(&langwell->otg); ++ langwell_otg_add_timer(b_srp_res_tmr); ++ } ++ } ++ break; ++ case OTG_STATE_B_SRP_INIT: ++ if (!langwell->hsm.id) { ++ langwell->otg.default_a = 1; ++ langwell->hsm.a_srp_det = 0; ++ ++ langwell_otg_drv_vbus(0); ++ langwell_otg_chrg_vbus(0); ++ ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.b_sess_vld) { ++ langwell_otg_chrg_vbus(0); ++ if (langwell->client_ops) { ++ langwell->client_ops->resume(langwell->pdev); ++ langwell->otg.state = OTG_STATE_B_PERIPHERAL; ++ } else ++ otg_dbg("client driver not loaded.\n"); ++ } ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (!langwell->hsm.id) { ++ langwell->otg.default_a = 1; ++ langwell->hsm.a_srp_det = 0; ++ ++ langwell_otg_drv_vbus(0); ++ langwell_otg_chrg_vbus(0); ++ set_host_mode(); ++ ++ if (langwell->client_ops) { ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ } else ++ otg_dbg("client driver has been removed.\n"); ++ ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.b_sess_vld) { ++ langwell->hsm.b_hnp_enable = 0; ++ ++ if (langwell->client_ops) { ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ } else ++ otg_dbg("client driver has been removed.\n"); ++ ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable ++ && langwell->hsm.a_bus_suspend) { ++ ++ if (langwell->client_ops) { ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ } else ++ otg_dbg("client driver has been removed.\n"); ++ ++ langwell_otg_HAAR(1); ++ langwell->hsm.a_conn = 0; ++ ++ if (langwell->host_ops) { ++ langwell->host_ops->probe(langwell->pdev, ++ langwell->host_ops->id_table); ++ langwell->otg.state = OTG_STATE_B_WAIT_ACON; ++ } else ++ otg_dbg("host driver not loaded.\n"); ++ ++ langwell->hsm.a_bus_resume = 0; ++ langwell->hsm.b_ase0_brst_tmout = 0; ++ langwell_otg_add_timer(b_ase0_brst_tmr); ++ } ++ break; ++ ++ case OTG_STATE_B_WAIT_ACON: ++ if (!langwell->hsm.id) { ++ langwell_otg_del_timer(b_ase0_brst_tmr); ++ langwell->otg.default_a = 1; ++ langwell->hsm.a_srp_det = 0; ++ ++ langwell_otg_drv_vbus(0); ++ langwell_otg_chrg_vbus(0); ++ set_host_mode(); ++ ++ langwell_otg_HAAR(0); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.b_sess_vld) { ++ langwell_otg_del_timer(b_ase0_brst_tmr); ++ langwell->hsm.b_hnp_enable = 0; ++ langwell->hsm.b_bus_req = 0; ++ langwell_otg_chrg_vbus(0); ++ langwell_otg_HAAR(0); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ } else if (langwell->hsm.a_conn) { ++ langwell_otg_del_timer(b_ase0_brst_tmr); ++ langwell_otg_HAAR(0); ++ langwell->otg.state = OTG_STATE_B_HOST; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_bus_resume || ++ langwell->hsm.b_ase0_brst_tmout) { ++ langwell_otg_del_timer(b_ase0_brst_tmr); ++ langwell_otg_HAAR(0); ++ langwell_otg_nsf_msg(7); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ ++ langwell->hsm.a_bus_suspend = 0; ++ langwell->hsm.b_bus_req = 0; ++ ++ if (langwell->client_ops) ++ langwell->client_ops->resume(langwell->pdev); ++ else ++ otg_dbg("client driver not loaded.\n"); ++ ++ langwell->otg.state = OTG_STATE_B_PERIPHERAL; ++ } ++ break; ++ ++ case OTG_STATE_B_HOST: ++ if (!langwell->hsm.id) { ++ langwell->otg.default_a = 1; ++ langwell->hsm.a_srp_det = 0; ++ ++ langwell_otg_drv_vbus(0); ++ langwell_otg_chrg_vbus(0); ++ set_host_mode(); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.b_sess_vld) { ++ langwell->hsm.b_hnp_enable = 0; ++ langwell->hsm.b_bus_req = 0; ++ langwell_otg_chrg_vbus(0); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ } else if ((!langwell->hsm.b_bus_req) || ++ (!langwell->hsm.a_conn)) { ++ langwell->hsm.b_bus_req = 0; ++ langwell_otg_loc_sof(0); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ ++ langwell->hsm.a_bus_suspend = 0; ++ ++ if (langwell->client_ops) ++ langwell->client_ops->resume(langwell->pdev); ++ else ++ otg_dbg("client driver not loaded.\n"); ++ ++ langwell->otg.state = OTG_STATE_B_PERIPHERAL; ++ } ++ break; ++ ++ case OTG_STATE_A_IDLE: ++ langwell->otg.default_a = 1; ++ if (langwell->hsm.id) { ++ langwell->otg.default_a = 0; ++ langwell->hsm.b_bus_req = 0; ++ langwell_otg_drv_vbus(0); ++ langwell_otg_chrg_vbus(0); ++ ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_sess_vld) { ++ langwell_otg_drv_vbus(1); ++ langwell->hsm.a_srp_det = 1; ++ langwell->hsm.a_wait_vrise_tmout = 0; ++ langwell_otg_add_timer(a_wait_vrise_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.a_bus_drop && ++ (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) { ++ langwell_otg_drv_vbus(1); ++ langwell->hsm.a_wait_vrise_tmout = 0; ++ langwell_otg_add_timer(a_wait_vrise_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; ++ queue_work(langwell->qwork, &langwell->work); ++ } ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ if (langwell->hsm.id) { ++ langwell_otg_del_timer(a_wait_vrise_tmr); ++ langwell->hsm.b_bus_req = 0; ++ langwell->otg.default_a = 0; ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ } else if (langwell->hsm.a_vbus_vld) { ++ langwell_otg_del_timer(a_wait_vrise_tmr); ++ if (langwell->host_ops) ++ langwell->host_ops->probe(langwell->pdev, ++ langwell->host_ops->id_table); ++ else ++ otg_dbg("host driver not loaded.\n"); ++ langwell->hsm.b_conn = 0; ++ langwell->hsm.a_set_b_hnp_en = 0; ++ langwell->hsm.a_wait_bcon_tmout = 0; ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_BCON; ++ } else if (langwell->hsm.a_wait_vrise_tmout) { ++ if (langwell->hsm.a_vbus_vld) { ++ if (langwell->host_ops) ++ langwell->host_ops->probe( ++ langwell->pdev, ++ langwell->host_ops->id_table); ++ else ++ otg_dbg("host driver not loaded.\n"); ++ langwell->hsm.b_conn = 0; ++ langwell->hsm.a_set_b_hnp_en = 0; ++ langwell->hsm.a_wait_bcon_tmout = 0; ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_BCON; ++ } else { ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_VBUS_ERR; ++ } ++ } ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ if (langwell->hsm.id) { ++ langwell_otg_del_timer(a_wait_bcon_tmr); ++ ++ langwell->otg.default_a = 0; ++ langwell->hsm.b_bus_req = 0; ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.a_vbus_vld) { ++ langwell_otg_del_timer(a_wait_bcon_tmr); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (langwell->hsm.a_bus_drop || ++ (langwell->hsm.a_wait_bcon_tmout && ++ !langwell->hsm.a_bus_req)) { ++ langwell_otg_del_timer(a_wait_bcon_tmr); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (langwell->hsm.b_conn) { ++ langwell_otg_del_timer(a_wait_bcon_tmr); ++ ++ langwell->hsm.a_suspend_req = 0; ++ langwell->otg.state = OTG_STATE_A_HOST; ++ if (!langwell->hsm.a_bus_req && ++ langwell->hsm.a_set_b_hnp_en) { ++ /* It is not safe enough to do a fast ++ * transistion from A_WAIT_BCON to ++ * A_SUSPEND */ ++ msleep(10000); ++ if (langwell->hsm.a_bus_req) ++ break; ++ ++ if (request_irq(langwell->pdev->irq, ++ otg_dummy_irq, IRQF_SHARED, ++ driver_name, langwell->regs) != 0) { ++ otg_dbg("request interrupt %d fail\n", ++ langwell->pdev->irq); ++ } ++ ++ langwell_otg_HABA(1); ++ langwell->hsm.b_bus_resume = 0; ++ langwell->hsm.a_aidl_bdis_tmout = 0; ++ langwell_otg_add_timer(a_aidl_bdis_tmr); ++ ++ langwell_otg_loc_sof(0); ++ langwell->otg.state = OTG_STATE_A_SUSPEND; ++ } else if (!langwell->hsm.a_bus_req && ++ !langwell->hsm.a_set_b_hnp_en) { ++ struct pci_dev *pdev = langwell->pdev; ++ if (langwell->host_ops) ++ langwell->host_ops->remove(pdev); ++ else ++ otg_dbg("host driver removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } ++ } ++ break; ++ case OTG_STATE_A_HOST: ++ if (langwell->hsm.id) { ++ langwell->otg.default_a = 0; ++ langwell->hsm.b_bus_req = 0; ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_bus_drop || ++ (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) { ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (!langwell->hsm.a_vbus_vld) { ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (langwell->hsm.a_set_b_hnp_en ++ && !langwell->hsm.a_bus_req) { ++ /* Set HABA to enable hardware assistance to signal ++ * A-connect after receiver B-disconnect. Hardware ++ * will then set client mode and enable URE, SLE and ++ * PCE after the assistance. otg_dummy_irq is used to ++ * clean these ints when client driver is not resumed. ++ */ ++ if (request_irq(langwell->pdev->irq, ++ otg_dummy_irq, IRQF_SHARED, driver_name, ++ langwell->regs) != 0) { ++ otg_dbg("request interrupt %d failed\n", ++ langwell->pdev->irq); ++ } ++ ++ /* set HABA */ ++ langwell_otg_HABA(1); ++ langwell->hsm.b_bus_resume = 0; ++ langwell->hsm.a_aidl_bdis_tmout = 0; ++ langwell_otg_add_timer(a_aidl_bdis_tmr); ++ langwell_otg_loc_sof(0); ++ langwell->otg.state = OTG_STATE_A_SUSPEND; ++ } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) { ++ langwell->hsm.a_wait_bcon_tmout = 0; ++ langwell->hsm.a_set_b_hnp_en = 0; ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_BCON; ++ } ++ break; ++ case OTG_STATE_A_SUSPEND: ++ if (langwell->hsm.id) { ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ free_irq(langwell->pdev->irq, langwell->regs); ++ langwell->otg.default_a = 0; ++ langwell->hsm.b_bus_req = 0; ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_bus_req || ++ langwell->hsm.b_bus_resume) { ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ free_irq(langwell->pdev->irq, langwell->regs); ++ langwell->hsm.a_suspend_req = 0; ++ langwell_otg_loc_sof(1); ++ langwell->otg.state = OTG_STATE_A_HOST; ++ } else if (langwell->hsm.a_aidl_bdis_tmout || ++ langwell->hsm.a_bus_drop) { ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ free_irq(langwell->pdev->irq, langwell->regs); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (!langwell->hsm.b_conn && ++ langwell->hsm.a_set_b_hnp_en) { ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ free_irq(langwell->pdev->irq, langwell->regs); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ ++ langwell->hsm.b_bus_suspend = 0; ++ langwell->hsm.b_bus_suspend_vld = 0; ++ langwell->hsm.b_bus_suspend_tmout = 0; ++ ++ /* msleep(200); */ ++ if (langwell->client_ops) ++ langwell->client_ops->resume(langwell->pdev); ++ else ++ otg_dbg("client driver not loaded.\n"); ++ ++ langwell_otg_add_timer(b_bus_suspend_tmr); ++ langwell->otg.state = OTG_STATE_A_PERIPHERAL; ++ break; ++ } else if (!langwell->hsm.a_vbus_vld) { ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ free_irq(langwell->pdev->irq, langwell->regs); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(langwell->pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_VBUS_ERR; ++ } ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ if (langwell->hsm.id) { ++ langwell_otg_del_timer(b_bus_suspend_tmr); ++ langwell->otg.default_a = 0; ++ langwell->hsm.b_bus_req = 0; ++ if (langwell->client_ops) ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (!langwell->hsm.a_vbus_vld) { ++ langwell_otg_del_timer(b_bus_suspend_tmr); ++ if (langwell->client_ops) ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (langwell->hsm.a_bus_drop) { ++ langwell_otg_del_timer(b_bus_suspend_tmr); ++ if (langwell->client_ops) ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (langwell->hsm.b_bus_suspend) { ++ langwell_otg_del_timer(b_bus_suspend_tmr); ++ if (langwell->client_ops) ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ ++ if (langwell->host_ops) ++ langwell->host_ops->probe(langwell->pdev, ++ langwell->host_ops->id_table); ++ else ++ otg_dbg("host driver not loaded.\n"); ++ langwell->hsm.a_set_b_hnp_en = 0; ++ langwell->hsm.a_wait_bcon_tmout = 0; ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_BCON; ++ } else if (langwell->hsm.b_bus_suspend_tmout) { ++ u32 val; ++ val = readl(langwell->regs + CI_PORTSC1); ++ if (!(val & PORTSC_SUSP)) ++ break; ++ if (langwell->client_ops) ++ langwell->client_ops->suspend(langwell->pdev, ++ PMSG_FREEZE); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ if (langwell->host_ops) ++ langwell->host_ops->probe(langwell->pdev, ++ langwell->host_ops->id_table); ++ else ++ otg_dbg("host driver not loaded.\n"); ++ langwell->hsm.a_set_b_hnp_en = 0; ++ langwell->hsm.a_wait_bcon_tmout = 0; ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_BCON; ++ } ++ break; ++ case OTG_STATE_A_VBUS_ERR: ++ if (langwell->hsm.id) { ++ langwell->otg.default_a = 0; ++ langwell->hsm.a_clr_err = 0; ++ langwell->hsm.a_srp_det = 0; ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_clr_err) { ++ langwell->hsm.a_clr_err = 0; ++ langwell->hsm.a_srp_det = 0; ++ reset_otg(); ++ init_hsm(); ++ if (langwell->otg.state == OTG_STATE_A_IDLE) ++ queue_work(langwell->qwork, &langwell->work); ++ } ++ break; ++ case OTG_STATE_A_WAIT_VFALL: ++ if (langwell->hsm.id) { ++ langwell->otg.default_a = 0; ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ queue_work(langwell->qwork, &langwell->work); ++ } else if (langwell->hsm.a_bus_req) { ++ langwell_otg_drv_vbus(1); ++ langwell->hsm.a_wait_vrise_tmout = 0; ++ langwell_otg_add_timer(a_wait_vrise_tmr); ++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; ++ } else if (!langwell->hsm.a_sess_vld) { ++ langwell->hsm.a_srp_det = 0; ++ langwell_otg_drv_vbus(0); ++ set_host_mode(); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ } ++ break; ++ default: ++ ; ++ } ++ ++ otg_dbg("%s: new state = %s\n", __func__, ++ state_string(langwell->otg.state)); ++} ++ ++ static ssize_t ++show_registers(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct langwell_otg *langwell; ++ char *next; ++ unsigned size; ++ unsigned t; ++ ++ langwell = the_transceiver; ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, ++ "\n" ++ "USBCMD = 0x%08x \n" ++ "USBSTS = 0x%08x \n" ++ "USBINTR = 0x%08x \n" ++ "ASYNCLISTADDR = 0x%08x \n" ++ "PORTSC1 = 0x%08x \n" ++ "HOSTPC1 = 0x%08x \n" ++ "OTGSC = 0x%08x \n" ++ "USBMODE = 0x%08x \n", ++ readl(langwell->regs + 0x30), ++ readl(langwell->regs + 0x34), ++ readl(langwell->regs + 0x38), ++ readl(langwell->regs + 0x48), ++ readl(langwell->regs + 0x74), ++ readl(langwell->regs + 0xb4), ++ readl(langwell->regs + 0xf4), ++ readl(langwell->regs + 0xf8) ++ ); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); ++ ++static ssize_t ++show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct langwell_otg *langwell; ++ char *next; ++ unsigned size; ++ unsigned t; ++ ++ langwell = the_transceiver; ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, ++ "\n" ++ "current state = %s\n" ++ "a_bus_resume = \t%d\n" ++ "a_bus_suspend = \t%d\n" ++ "a_conn = \t%d\n" ++ "a_sess_vld = \t%d\n" ++ "a_srp_det = \t%d\n" ++ "a_vbus_vld = \t%d\n" ++ "b_bus_resume = \t%d\n" ++ "b_bus_suspend = \t%d\n" ++ "b_conn = \t%d\n" ++ "b_se0_srp = \t%d\n" ++ "b_sess_end = \t%d\n" ++ "b_sess_vld = \t%d\n" ++ "id = \t%d\n" ++ "a_set_b_hnp_en = \t%d\n" ++ "b_srp_done = \t%d\n" ++ "b_hnp_enable = \t%d\n" ++ "a_wait_vrise_tmout = \t%d\n" ++ "a_wait_bcon_tmout = \t%d\n" ++ "a_aidl_bdis_tmout = \t%d\n" ++ "b_ase0_brst_tmout = \t%d\n" ++ "a_bus_drop = \t%d\n" ++ "a_bus_req = \t%d\n" ++ "a_clr_err = \t%d\n" ++ "a_suspend_req = \t%d\n" ++ "b_bus_req = \t%d\n" ++ "b_bus_suspend_tmout = \t%d\n" ++ "b_bus_suspend_vld = \t%d\n", ++ state_string(langwell->otg.state), ++ langwell->hsm.a_bus_resume, ++ langwell->hsm.a_bus_suspend, ++ langwell->hsm.a_conn, ++ langwell->hsm.a_sess_vld, ++ langwell->hsm.a_srp_det, ++ langwell->hsm.a_vbus_vld, ++ langwell->hsm.b_bus_resume, ++ langwell->hsm.b_bus_suspend, ++ langwell->hsm.b_conn, ++ langwell->hsm.b_se0_srp, ++ langwell->hsm.b_sess_end, ++ langwell->hsm.b_sess_vld, ++ langwell->hsm.id, ++ langwell->hsm.a_set_b_hnp_en, ++ langwell->hsm.b_srp_done, ++ langwell->hsm.b_hnp_enable, ++ langwell->hsm.a_wait_vrise_tmout, ++ langwell->hsm.a_wait_bcon_tmout, ++ langwell->hsm.a_aidl_bdis_tmout, ++ langwell->hsm.b_ase0_brst_tmout, ++ langwell->hsm.a_bus_drop, ++ langwell->hsm.a_bus_req, ++ langwell->hsm.a_clr_err, ++ langwell->hsm.a_suspend_req, ++ langwell->hsm.b_bus_req, ++ langwell->hsm.b_bus_suspend_tmout, ++ langwell->hsm.b_bus_suspend_vld ++ ); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); ++ ++static ssize_t ++get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct langwell_otg *langwell; ++ char *next; ++ unsigned size; ++ unsigned t; ++ ++ langwell = the_transceiver; ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_a_bus_req(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct langwell_otg *langwell; ++ langwell = the_transceiver; ++ if (!langwell->otg.default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ langwell->hsm.a_bus_req = 0; ++ otg_dbg("a_bus_req = 0\n"); ++ } else if (buf[0] == '1') { ++ /* If a_bus_drop is TRUE, a_bus_req can't be set */ ++ if (langwell->hsm.a_bus_drop) ++ return -1; ++ langwell->hsm.a_bus_req = 1; ++ otg_dbg("a_bus_req = 1\n"); ++ } ++ if (spin_trylock(&langwell->wq_lock)) { ++ queue_work(langwell->qwork, &langwell->work); ++ spin_unlock(&langwell->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); ++ ++static ssize_t ++get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct langwell_otg *langwell; ++ char *next; ++ unsigned size; ++ unsigned t; ++ ++ langwell = the_transceiver; ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_a_bus_drop(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct langwell_otg *langwell; ++ langwell = the_transceiver; ++ if (!langwell->otg.default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ langwell->hsm.a_bus_drop = 0; ++ otg_dbg("a_bus_drop = 0\n"); ++ } else if (buf[0] == '1') { ++ langwell->hsm.a_bus_drop = 1; ++ langwell->hsm.a_bus_req = 0; ++ otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n"); ++ } ++ if (spin_trylock(&langwell->wq_lock)) { ++ queue_work(langwell->qwork, &langwell->work); ++ spin_unlock(&langwell->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, ++ get_a_bus_drop, set_a_bus_drop); ++ ++static ssize_t ++get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct langwell_otg *langwell; ++ char *next; ++ unsigned size; ++ unsigned t; ++ ++ langwell = the_transceiver; ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_b_bus_req(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct langwell_otg *langwell; ++ langwell = the_transceiver; ++ ++ if (langwell->otg.default_a) ++ return -1; ++ ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ langwell->hsm.b_bus_req = 0; ++ otg_dbg("b_bus_req = 0\n"); ++ } else if (buf[0] == '1') { ++ langwell->hsm.b_bus_req = 1; ++ otg_dbg("b_bus_req = 1\n"); ++ } ++ if (spin_trylock(&langwell->wq_lock)) { ++ queue_work(langwell->qwork, &langwell->work); ++ spin_unlock(&langwell->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); ++ ++static ssize_t ++set_a_clr_err(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct langwell_otg *langwell; ++ langwell = the_transceiver; ++ ++ if (!langwell->otg.default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '1') { ++ langwell->hsm.a_clr_err = 1; ++ otg_dbg("a_clr_err = 1\n"); ++ } ++ if (spin_trylock(&langwell->wq_lock)) { ++ queue_work(langwell->qwork, &langwell->work); ++ spin_unlock(&langwell->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); ++ ++static struct attribute *inputs_attrs[] = { ++ &dev_attr_a_bus_req.attr, ++ &dev_attr_a_bus_drop.attr, ++ &dev_attr_b_bus_req.attr, ++ &dev_attr_a_clr_err.attr, ++ NULL, ++}; ++ ++static struct attribute_group debug_dev_attr_group = { ++ .name = "inputs", ++ .attrs = inputs_attrs, ++}; ++ ++int langwell_register_host(struct pci_driver *host_driver) ++{ ++ int ret = 0; ++ ++ the_transceiver->host_ops = host_driver; ++ queue_work(the_transceiver->qwork, &the_transceiver->work); ++ otg_dbg("host controller driver is registered\n"); ++ ++ return ret; ++} ++EXPORT_SYMBOL(langwell_register_host); ++ ++void langwell_unregister_host(struct pci_driver *host_driver) ++{ ++ if (the_transceiver->host_ops) ++ the_transceiver->host_ops->remove(the_transceiver->pdev); ++ the_transceiver->host_ops = NULL; ++ the_transceiver->hsm.a_bus_drop = 1; ++ queue_work(the_transceiver->qwork, &the_transceiver->work); ++ otg_dbg("host controller driver is unregistered\n"); ++} ++EXPORT_SYMBOL(langwell_unregister_host); ++ ++int langwell_register_peripheral(struct pci_driver *client_driver) ++{ ++ int ret = 0; ++ ++ if (client_driver) ++ ret = client_driver->probe(the_transceiver->pdev, ++ client_driver->id_table); ++ if (!ret) { ++ the_transceiver->client_ops = client_driver; ++ queue_work(the_transceiver->qwork, &the_transceiver->work); ++ otg_dbg("client controller driver is registered\n"); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(langwell_register_peripheral); ++ ++void langwell_unregister_peripheral(struct pci_driver *client_driver) ++{ ++ if (the_transceiver->client_ops) ++ the_transceiver->client_ops->remove(the_transceiver->pdev); ++ the_transceiver->client_ops = NULL; ++ the_transceiver->hsm.b_bus_req = 0; ++ queue_work(the_transceiver->qwork, &the_transceiver->work); ++ otg_dbg("client controller driver is unregistered\n"); ++} ++EXPORT_SYMBOL(langwell_unregister_peripheral); ++ ++static int langwell_otg_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ unsigned long resource, len; ++ void __iomem *base = NULL; ++ int retval; ++ u32 val32; ++ struct langwell_otg *langwell; ++ char qname[] = "langwell_otg_queue"; ++ ++ retval = 0; ++ otg_dbg("\notg controller is detected.\n"); ++ if (pci_enable_device(pdev) < 0) { ++ retval = -ENODEV; ++ goto done; ++ } ++ ++ langwell = kzalloc(sizeof *langwell, GFP_KERNEL); ++ if (langwell == NULL) { ++ retval = -ENOMEM; ++ goto done; ++ } ++ the_transceiver = langwell; ++ ++ /* control register: BAR 0 */ ++ resource = pci_resource_start(pdev, 0); ++ len = pci_resource_len(pdev, 0); ++ if (!request_mem_region(resource, len, driver_name)) { ++ retval = -EBUSY; ++ goto err; ++ } ++ langwell->region = 1; ++ ++ base = ioremap_nocache(resource, len); ++ if (base == NULL) { ++ retval = -EFAULT; ++ goto err; ++ } ++ langwell->regs = base; ++ ++ if (!pdev->irq) { ++ otg_dbg("No IRQ.\n"); ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ langwell->qwork = create_workqueue(qname); ++ if (!langwell->qwork) { ++ otg_dbg("cannot create workqueue %s\n", qname); ++ retval = -ENOMEM; ++ goto err; ++ } ++ INIT_WORK(&langwell->work, langwell_otg_work); ++ ++ /* OTG common part */ ++ langwell->pdev = pdev; ++ langwell->otg.dev = &pdev->dev; ++ langwell->otg.label = driver_name; ++ langwell->otg.set_host = langwell_otg_set_host; ++ langwell->otg.set_peripheral = langwell_otg_set_peripheral; ++ langwell->otg.set_power = langwell_otg_set_power; ++ langwell->otg.start_srp = langwell_otg_start_srp; ++ langwell->otg.state = OTG_STATE_UNDEFINED; ++ if (otg_set_transceiver(&langwell->otg)) { ++ otg_dbg("can't set transceiver\n"); ++ retval = -EBUSY; ++ goto err; ++ } ++ ++ reset_otg(); ++ init_hsm(); ++ ++ spin_lock_init(&langwell->lock); ++ spin_lock_init(&langwell->wq_lock); ++ INIT_LIST_HEAD(&active_timers); ++ langwell_otg_init_timers(&langwell->hsm); ++ ++ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, ++ driver_name, langwell) != 0) { ++ otg_dbg("request interrupt %d failed\n", pdev->irq); ++ retval = -EBUSY; ++ goto err; ++ } ++ ++ /* enable OTGSC int */ ++ val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | ++ OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; ++ writel(val32, langwell->regs + CI_OTGSC); ++ ++ retval = device_create_file(&pdev->dev, &dev_attr_registers); ++ if (retval < 0) { ++ otg_dbg("Can't register sysfs attribute: %d\n", retval); ++ goto err; ++ } ++ ++ retval = device_create_file(&pdev->dev, &dev_attr_hsm); ++ if (retval < 0) { ++ otg_dbg("Can't hsm sysfs attribute: %d\n", retval); ++ goto err; ++ } ++ ++ retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); ++ if (retval < 0) { ++ otg_dbg("Can't register sysfs attr group: %d\n", retval); ++ goto err; ++ } ++ ++ if (langwell->otg.state == OTG_STATE_A_IDLE) ++ queue_work(langwell->qwork, &langwell->work); ++ ++ return 0; ++ ++err: ++ if (the_transceiver) ++ langwell_otg_remove(pdev); ++done: ++ return retval; ++} ++ ++static void langwell_otg_remove(struct pci_dev *pdev) ++{ ++ struct langwell_otg *langwell; ++ ++ langwell = the_transceiver; ++ ++ if (langwell->qwork) { ++ flush_workqueue(langwell->qwork); ++ destroy_workqueue(langwell->qwork); ++ } ++ langwell_otg_free_timers(); ++ ++ /* disable OTGSC interrupt as OTGSC doesn't change in reset */ ++ writel(0, langwell->regs + CI_OTGSC); ++ ++ if (pdev->irq) ++ free_irq(pdev->irq, langwell); ++ if (langwell->regs) ++ iounmap(langwell->regs); ++ if (langwell->region) ++ release_mem_region(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0)); ++ ++ otg_set_transceiver(NULL); ++ pci_disable_device(pdev); ++ sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); ++ device_remove_file(&pdev->dev, &dev_attr_hsm); ++ device_remove_file(&pdev->dev, &dev_attr_registers); ++ kfree(langwell); ++ langwell = NULL; ++} ++ ++static void transceiver_suspend(struct pci_dev *pdev) ++{ ++ pci_save_state(pdev); ++ pci_set_power_state(pdev, PCI_D3hot); ++ langwell_otg_phy_low_power(1); ++} ++ ++static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) ++{ ++ int ret = 0; ++ struct langwell_otg *langwell; ++ ++ langwell = the_transceiver; ++ ++ /* Disbale OTG interrupts */ ++ langwell_otg_intr(0); ++ ++ if (pdev->irq) ++ free_irq(pdev->irq, langwell); ++ ++ /* Prevent more otg_work */ ++ flush_workqueue(langwell->qwork); ++ spin_lock(&langwell->wq_lock); ++ ++ /* start actions */ ++ switch (langwell->otg.state) { ++ case OTG_STATE_A_IDLE: ++ case OTG_STATE_B_IDLE: ++ case OTG_STATE_A_WAIT_VFALL: ++ case OTG_STATE_A_VBUS_ERR: ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ langwell_otg_del_timer(a_wait_vrise_tmr); ++ langwell->hsm.a_srp_det = 0; ++ langwell_otg_drv_vbus(0); ++ langwell->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ langwell_otg_del_timer(a_wait_bcon_tmr); ++ if (langwell->host_ops) ++ ret = langwell->host_ops->suspend(pdev, message); ++ langwell_otg_drv_vbus(0); ++ break; ++ case OTG_STATE_A_HOST: ++ if (langwell->host_ops) ++ ret = langwell->host_ops->suspend(pdev, message); ++ langwell_otg_drv_vbus(0); ++ langwell_otg_phy_low_power(1); ++ break; ++ case OTG_STATE_A_SUSPEND: ++ langwell_otg_del_timer(a_aidl_bdis_tmr); ++ langwell_otg_HABA(0); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ transceiver_suspend(pdev); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ if (langwell->client_ops) ++ ret = langwell->client_ops->suspend(pdev, message); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ langwell_otg_drv_vbus(0); ++ transceiver_suspend(pdev); ++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; ++ break; ++ case OTG_STATE_B_HOST: ++ if (langwell->host_ops) ++ langwell->host_ops->remove(pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->hsm.b_bus_req = 0; ++ transceiver_suspend(pdev); ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (langwell->client_ops) ++ ret = langwell->client_ops->suspend(pdev, message); ++ else ++ otg_dbg("client driver has been removed.\n"); ++ break; ++ case OTG_STATE_B_WAIT_ACON: ++ langwell_otg_del_timer(b_ase0_brst_tmr); ++ langwell_otg_HAAR(0); ++ if (langwell->host_ops) ++ langwell->host_ops->remove(pdev); ++ else ++ otg_dbg("host driver has been removed.\n"); ++ langwell->hsm.b_bus_req = 0; ++ langwell->otg.state = OTG_STATE_B_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ default: ++ otg_dbg("error state before suspend\n "); ++ break; ++ } ++ spin_unlock(&langwell->wq_lock); ++ ++ return ret; ++} ++ ++static void transceiver_resume(struct pci_dev *pdev) ++{ ++ pci_restore_state(pdev); ++ pci_set_power_state(pdev, PCI_D0); ++ langwell_otg_phy_low_power(0); ++} ++ ++static int langwell_otg_resume(struct pci_dev *pdev) ++{ ++ int ret = 0; ++ struct langwell_otg *langwell; ++ ++ langwell = the_transceiver; ++ ++ spin_lock(&langwell->wq_lock); ++ ++ switch (langwell->otg.state) { ++ case OTG_STATE_A_IDLE: ++ case OTG_STATE_B_IDLE: ++ case OTG_STATE_A_WAIT_VFALL: ++ case OTG_STATE_A_VBUS_ERR: ++ transceiver_resume(pdev); ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ langwell_otg_add_timer(a_wait_bcon_tmr); ++ langwell_otg_drv_vbus(1); ++ if (langwell->host_ops) ++ ret = langwell->host_ops->resume(pdev); ++ break; ++ case OTG_STATE_A_HOST: ++ langwell_otg_drv_vbus(1); ++ langwell_otg_phy_low_power(0); ++ if (langwell->host_ops) ++ ret = langwell->host_ops->resume(pdev); ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (langwell->client_ops) ++ ret = langwell->client_ops->resume(pdev); ++ else ++ otg_dbg("client driver not loaded.\n"); ++ break; ++ default: ++ otg_dbg("error state before suspend\n "); ++ break; ++ } ++ ++ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, ++ driver_name, the_transceiver) != 0) { ++ otg_dbg("request interrupt %d failed\n", pdev->irq); ++ ret = -EBUSY; ++ } ++ ++ /* enable OTG interrupts */ ++ langwell_otg_intr(1); ++ ++ spin_unlock(&langwell->wq_lock); ++ ++ queue_work(langwell->qwork, &langwell->work); ++ ++ ++ return ret; ++} ++ ++static int __init langwell_otg_init(void) ++{ ++ return pci_register_driver(&otg_pci_driver); ++} ++module_init(langwell_otg_init); ++ ++static void __exit langwell_otg_cleanup(void) ++{ ++ pci_unregister_driver(&otg_pci_driver); ++} ++module_exit(langwell_otg_cleanup); +--- a/drivers/usb/otg/Makefile ++++ b/drivers/usb/otg/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o + obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o + obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o + obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o ++obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o + obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o + + ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG +--- /dev/null ++++ b/include/linux/usb/langwell_otg.h +@@ -0,0 +1,177 @@ ++/* ++ * Intel Langwell USB OTG transceiver driver ++ * Copyright (C) 2008, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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 __LANGWELL_OTG_H__ ++#define __LANGWELL_OTG_H__ ++ ++/* notify transceiver driver about OTG events */ ++extern void langwell_update_transceiver(void); ++/* HCD register bus driver */ ++extern int langwell_register_host(struct pci_driver *host_driver); ++/* HCD unregister bus driver */ ++extern void langwell_unregister_host(struct pci_driver *host_driver); ++/* DCD register bus driver */ ++extern int langwell_register_peripheral(struct pci_driver *client_driver); ++/* DCD unregister bus driver */ ++extern void langwell_unregister_peripheral(struct pci_driver *client_driver); ++/* No silent failure, output warning message */ ++extern void langwell_otg_nsf_msg(unsigned long message); ++ ++#define CI_USBCMD 0x30 ++# define USBCMD_RST BIT(1) ++# define USBCMD_RS BIT(0) ++#define CI_USBSTS 0x34 ++# define USBSTS_SLI BIT(8) ++# define USBSTS_URI BIT(6) ++# define USBSTS_PCI BIT(2) ++#define CI_PORTSC1 0x74 ++# define PORTSC_PP BIT(12) ++# define PORTSC_LS (BIT(11) | BIT(10)) ++# define PORTSC_SUSP BIT(7) ++# define PORTSC_CCS BIT(0) ++#define CI_HOSTPC1 0xb4 ++# define HOSTPC1_PHCD BIT(22) ++#define CI_OTGSC 0xf4 ++# define OTGSC_DPIE BIT(30) ++# define OTGSC_1MSE BIT(29) ++# define OTGSC_BSEIE BIT(28) ++# define OTGSC_BSVIE BIT(27) ++# define OTGSC_ASVIE BIT(26) ++# define OTGSC_AVVIE BIT(25) ++# define OTGSC_IDIE BIT(24) ++# define OTGSC_DPIS BIT(22) ++# define OTGSC_1MSS BIT(21) ++# define OTGSC_BSEIS BIT(20) ++# define OTGSC_BSVIS BIT(19) ++# define OTGSC_ASVIS BIT(18) ++# define OTGSC_AVVIS BIT(17) ++# define OTGSC_IDIS BIT(16) ++# define OTGSC_DPS BIT(14) ++# define OTGSC_1MST BIT(13) ++# define OTGSC_BSE BIT(12) ++# define OTGSC_BSV BIT(11) ++# define OTGSC_ASV BIT(10) ++# define OTGSC_AVV BIT(9) ++# define OTGSC_ID BIT(8) ++# define OTGSC_HABA BIT(7) ++# define OTGSC_HADP BIT(6) ++# define OTGSC_IDPU BIT(5) ++# define OTGSC_DP BIT(4) ++# define OTGSC_OT BIT(3) ++# define OTGSC_HAAR BIT(2) ++# define OTGSC_VC BIT(1) ++# define OTGSC_VD BIT(0) ++# define OTGSC_INTEN_MASK (0x7f << 24) ++# define OTGSC_INTSTS_MASK (0x7f << 16) ++#define CI_USBMODE 0xf8 ++# define USBMODE_CM (BIT(1) | BIT(0)) ++# define USBMODE_IDLE 0 ++# define USBMODE_DEVICE 0x2 ++# define USBMODE_HOST 0x3 ++ ++#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) ++ ++struct otg_hsm { ++ /* Input */ ++ int a_bus_resume; ++ int a_bus_suspend; ++ int a_conn; ++ int a_sess_vld; ++ int a_srp_det; ++ int a_vbus_vld; ++ int b_bus_resume; ++ int b_bus_suspend; ++ int b_conn; ++ int b_se0_srp; ++ int b_sess_end; ++ int b_sess_vld; ++ int id; ++ ++ /* Internal variables */ ++ int a_set_b_hnp_en; ++ int b_srp_done; ++ int b_hnp_enable; ++ ++ /* Timeout indicator for timers */ ++ int a_wait_vrise_tmout; ++ int a_wait_bcon_tmout; ++ int a_aidl_bdis_tmout; ++ int b_ase0_brst_tmout; ++ int b_bus_suspend_tmout; ++ int b_srp_res_tmout; ++ ++ /* Informative variables */ ++ int a_bus_drop; ++ int a_bus_req; ++ int a_clr_err; ++ int a_suspend_req; ++ int b_bus_req; ++ ++ /* Output */ ++ int drv_vbus; ++ int loc_conn; ++ int loc_sof; ++ ++ /* Others */ ++ int b_bus_suspend_vld; ++}; ++ ++#define TA_WAIT_VRISE 100 ++#define TA_WAIT_BCON 30000 ++#define TA_AIDL_BDIS 15000 ++#define TB_ASE0_BRST 5000 ++#define TB_SE0_SRP 2 ++#define TB_SRP_RES 100 ++#define TB_BUS_SUSPEND 500 ++ ++struct langwell_otg_timer { ++ unsigned long expires; /* Number of count increase to timeout */ ++ unsigned long count; /* Tick counter */ ++ void (*function)(unsigned long); /* Timeout function */ ++ unsigned long data; /* Data passed to function */ ++ struct list_head list; ++}; ++ ++struct langwell_otg { ++ struct otg_transceiver otg; ++ struct otg_hsm hsm; ++ void __iomem *regs; ++ unsigned region; ++ struct pci_driver *host_ops; ++ struct pci_driver *client_ops; ++ struct pci_dev *pdev; ++ struct work_struct work; ++ struct workqueue_struct *qwork; ++ spinlock_t lock; ++ spinlock_t wq_lock; ++}; ++ ++static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg) ++{ ++ return container_of(otg, struct langwell_otg, otg); ++} ++ ++#ifdef DEBUG ++#define otg_dbg(fmt, args...) \ ++ printk(KERN_DEBUG fmt , ## args) ++#else ++#define otg_dbg(fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++#endif /* __LANGWELL_OTG_H__ */ |
