From patchwork Fri Mar 29 07:06:22 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 232384 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 22E232C00B3 for ; Fri, 29 Mar 2013 23:00:40 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D7DF54A098; Fri, 29 Mar 2013 12:59:48 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id IxcQdLc+w6Ei; Fri, 29 Mar 2013 12:59:48 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 503BD4A0BA; Fri, 29 Mar 2013 12:57:57 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DA6684A025 for ; Fri, 29 Mar 2013 08:13:46 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4gd0WPHsghQy for ; Fri, 29 Mar 2013 08:13:44 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pb0-f42.google.com (mail-pb0-f42.google.com [209.85.160.42]) by theia.denx.de (Postfix) with ESMTPS id 0E2584A023 for ; Fri, 29 Mar 2013 08:13:42 +0100 (CET) Received: by mail-pb0-f42.google.com with SMTP id xb4so152014pbc.15 for ; Fri, 29 Mar 2013 00:13:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:cc; bh=sM4frmiVxmeC/36O6UnRmio5VeGabdhLREDdVduXV5c=; b=06tjwLnSj25h/hPXOV1EY97QvbnTRdWRm9Vy8fGa/9w563zO1rsvmyXc0a27nDi3yZ Uqd9esBUzgqi42tpG1nMm7MVVUf6DhMTMAOwZz2yca52veRV8gsJ7aiYQ7ON+fCP5vsG zPXCPdRwD7h0Q4oiB2WMLPiGoZNa3Dcxf9UDXxHwcQR3WwjxryAO3JyBox0aObHZdBy7 /DrptpFgl9oUPKp5wkANBSkhf1a/UDS8q/IhGnrX2B7wPfDKQ4MGofeIr+EezvMjVEY6 3w2zWccFN5MxfnzgkLyA8FlSac/sxnHdtKSILnbetX3t4ADd9vqetfzTztrIiPhi9nU8 IgPQ== X-Received: by 10.66.216.198 with SMTP id os6mr2905432pac.145.1364540804169; Fri, 29 Mar 2013 00:06:44 -0700 (PDT) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id tm1sm1834226pbc.11.2013.03.29.00.06.42 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 29 Mar 2013 00:06:43 -0700 (PDT) From: Kuo-Jung Su To: u-boot@lists.denx.de Date: Fri, 29 Mar 2013 15:06:22 +0800 Message-Id: <1364540788-13943-6-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1364540788-13943-1-git-send-email-dantesu@gmail.com> References: <1364540788-13943-1-git-send-email-dantesu@gmail.com> X-Mailman-Approved-At: Fri, 29 Mar 2013 12:57:45 +0100 Cc: Marek Vasut , Kuo-Jung Su Subject: [U-Boot] [PATCH 05/11] usb-gadget: add FOTG210 USB gadget support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Kuo-Jung Su This patch would try to use Faraday FOTG210 to implement a USB RNDIS Ethernet. Signed-off-by: Kuo-Jung Su --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/fotg210.c | 926 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/fotg210.h | 99 ++++ drivers/usb/gadget/gadget_chips.h | 8 + 4 files changed, 1034 insertions(+) create mode 100644 drivers/usb/gadget/fotg210.c create mode 100644 drivers/usb/gadget/fotg210.h -- 1.7.9.5 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e545b6b..432cf17 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,6 +35,7 @@ endif # new USB gadget layer dependencies ifdef CONFIG_USB_GADGET COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif diff --git a/drivers/usb/gadget/fotg210.c b/drivers/usb/gadget/fotg210.c new file mode 100644 index 0000000..640ae55 --- /dev/null +++ b/drivers/usb/gadget/fotg210.c @@ -0,0 +1,926 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fotg210.h" + +#define CFG_HALF_SPEED 0 +#define CFG_LOW_TIMING 0 +#define CFG_NUM_ENDPOINTS 4 +#define CFG_EP0_MAX_PACKET_SIZE 64 +#define CFG_EPX_MAX_PACKET_SIZE 512 + +struct fotg210_chip; + +struct fotg210_ep { + struct usb_ep ep; + + uint32_t maxpacket:16; + uint32_t id:4; + uint32_t stopped:1; + uint32_t rsvd:11; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + struct fotg210_chip *chip; +}; + +struct fotg210_request { + struct usb_request req; + struct list_head queue; + struct fotg210_ep *ep; +}; + +struct fotg210_chip { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + uint32_t iobase; + uint8_t irq; + uint16_t addr; + int pullup; + enum usb_device_state state; + struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS]; +}; + +static struct usb_endpoint_descriptor ep0_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +#define USB_REG32(chip, off) *(volatile uint32_t *)((chip)->iobase + (off)) + +static inline int +fifo_to_ep(struct fotg210_chip *chip, int id, int in) +{ + return (id < 0) ? 0 : ((id % 4) + 1); +} + +static inline int +ep_to_fifo(struct fotg210_chip *chip, int id) +{ + return (id <= 0) ? -1 : ((id - 1) % 4); +} + +static inline int +ep_reset(struct fotg210_chip *chip, uint8_t ep_addr) +{ + int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + if (ep_addr & USB_DIR_IN) { + /* input */ + USB_REG32(chip, REG_IEP1 + (ep - 1) * 4) |= BIT(12); + USB_REG32(chip, REG_IEP1 + (ep - 1) * 4) &= ~BIT(12); + USB_REG32(chip, REG_IEP1 + (ep - 1) * 4) &= ~BIT(11); + } else { + /* output */ + USB_REG32(chip, REG_OEP1 + (ep - 1) * 4) |= BIT(12); + USB_REG32(chip, REG_OEP1 + (ep - 1) * 4) &= BIT(12); + USB_REG32(chip, REG_OEP1 + (ep - 1) * 4) &= BIT(11); + } + + return 0; +} + +static int +fotg210_reset(struct fotg210_chip *chip) +{ + chip->state = USB_STATE_POWERED; + + /* device address reset */ + chip->addr = 0; + USB_REG32(chip, REG_DCAR) = 0; + + /* enable the chip and perform a soft reset later */ + USB_REG32(chip, REG_DCCR) = BIT(5); + + /* set idle counter */ + USB_REG32(chip, REG_DCIDLE) = 7; + + /* disable interrupts */ + USB_REG32(chip, REG_HCIER) = 0; + USB_REG32(chip, REG_DCIMR) = 0x3; + USB_REG32(chip, REG_DCIMR0) = 0x3F; + USB_REG32(chip, REG_DCIMR1) = 0xF00FF; + USB_REG32(chip, REG_DCIMR2) = 0x7FF; + + /* clear interrupts */ + USB_REG32(chip, REG_HCISR) = 0x3F; + USB_REG32(chip, REG_OTGISR) = 0x1FFF; + USB_REG32(chip, REG_DCISR) = 0; + USB_REG32(chip, REG_DCISR0) = 0; + USB_REG32(chip, REG_DCISR1) = 0; + USB_REG32(chip, REG_DCISR2) = 0; + + /* soft reset */ + USB_REG32(chip, REG_DCCR) |= BIT(4); + while (USB_REG32(chip, REG_DCCR) & BIT(4)) + ; + + /* CX FIFO reset */ + USB_REG32(chip, REG_DCCXFIFO) |= BIT(3); + while (USB_REG32(chip, REG_DCCXFIFO) & BIT(3)) + ; + + /* create static ep-fifo map (EP1-FIFO0, EP2-FIFO1 ...)*/ + USB_REG32(chip, REG_EPMAP14) = 0x33221100; + USB_REG32(chip, REG_EPMAP58) = 0x00000000; + USB_REG32(chip, REG_FIFOMAP) = 0x04030201; + USB_REG32(chip, REG_FIFOCFG) = 0x00000000; + USB_REG32(chip, REG_IEP1) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP2) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP3) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP4) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP5) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP6) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP7) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_IEP8) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP1) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP2) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP3) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP4) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP5) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP6) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP7) = CFG_EPX_MAX_PACKET_SIZE; + USB_REG32(chip, REG_OEP8) = CFG_EPX_MAX_PACKET_SIZE; + + /* reset EP FIFO */ + USB_REG32(chip, REG_FIFO0) |= BIT(12); + while (USB_REG32(chip, REG_FIFO0) & BIT(12)) + ; + USB_REG32(chip, REG_FIFO1) |= BIT(12); + while (USB_REG32(chip, REG_FIFO1) & BIT(12)) + ; + USB_REG32(chip, REG_FIFO2) |= BIT(12); + while (USB_REG32(chip, REG_FIFO2) & BIT(12)) + ; + USB_REG32(chip, REG_FIFO3) |= BIT(12); + while (USB_REG32(chip, REG_FIFO3) & BIT(12)) + ; + + /* disable OTG interrupts */ + USB_REG32(chip, REG_OTGIER) = 0; + USB_REG32(chip, REG_ISR) = BIT(2) | BIT(1) | BIT(0); + USB_REG32(chip, REG_IMR) = BIT(3) | BIT(2) | BIT(1); /* active high, no OTG & Host */ + + /* disable EP0 IN/OUT interrupt */ + USB_REG32(chip, REG_DCIMR0) = BIT(2) | BIT(1); + /* disable EPX IN+SPK+OUT interrupts */ + USB_REG32(chip, REG_DCIMR1) = 0xF00FF; + /* disable wakeup+idle+dma+zlp interrupts */ + USB_REG32(chip, REG_DCIMR2) = BIT(10) | BIT(9) | BIT(8) | BIT(7) | BIT(6) | BIT(5); + /* enable all interrupt source group */ + USB_REG32(chip, REG_DCIMR) = 0; + + /* suspend delay = 7 ms */ + USB_REG32(chip, REG_DCIDLE) = 3; + + /* turn-on global interrupts */ + USB_REG32(chip, REG_DCCR) |= BIT(2); + +#if CFG_HALF_SPEED + /* half-speed */ + USB_REG32(chip, REG_DCCR) |= BIT(1); +#endif + +#if CFG_LOW_TIMING + /* low timing */ + USB_REG32(chip, REG_OTGCSR) |= BIT(11) | BIT(10) | BIT(9); +#endif + + return 0; +} + +static int +fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req) +{ + struct fotg210_chip *chip = ep->chip; + uint32_t tmp, timeout; + uint8_t *buf = req->req.buf + req->req.actual; + uint32_t len = req->req.length - req->req.actual; + uint32_t fifo = ep_to_fifo(chip, ep->id); + int rc = 0; + + /* 1. init dma buffer */ + if (len > ep->maxpacket) + len = ep->maxpacket; + + /* 2. wait for dma ready (hardware) */ + for (timeout = 5000; timeout; --timeout) { + if (!(USB_REG32(chip, REG_DMAPARAM1) & BIT(0))) + break; + udelay(1); + } + if (timeout == 0) { + printf("fotg210: dma busy (0x%08X)\n", USB_REG32(chip, REG_DMAFIFO)); + req->req.status = -EBUSY; + return -EBUSY; + } + + /* 3. DMA target setup */ +#ifndef CONFIG_SYS_DCACHE_OFF + if (ep->desc->bEndpointAddress & USB_DIR_IN) + flush_dcache_range((uint32_t)buf, (uint32_t)buf + len); + else + invalidate_dcache_range((uint32_t)buf, (uint32_t)buf + len); +#endif + USB_REG32(chip, REG_DMAPARAM2) = virt_to_phys(buf); + + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + if (ep->id == 0) { + /* Wait until fifo empty */ + while (!(USB_REG32(chip, REG_DCCXFIFO) & BIT(5))) + ; + USB_REG32(chip, REG_DMAFIFO) = BIT(4); + } else { + /* Wait until fifo empty */ + while (!(USB_REG32(chip, REG_DCCXFIFO) & (1 << (8 + fifo)))) + ; + USB_REG32(chip, REG_DMAFIFO) = 1 << fifo; + } + USB_REG32(chip, REG_DMAPARAM1) = len << 8 | BIT(1); + } else { + uint32_t blen; + if (ep->id == 0) { + USB_REG32(chip, REG_DMAFIFO) = BIT(4); + do { + blen = USB_REG32(chip, REG_DCCXFIFO) >> 24; + } while (blen < len); + } else { + USB_REG32(chip, REG_DMAFIFO) = 1 << fifo; + blen = USB_REG32(chip, REG_FIFO0 + (4 * fifo)) & 0x7FF; + } + len = (len < blen) ? len : blen; + USB_REG32(chip, REG_DMAPARAM1) = len << 8; + } + + /* 4. DMA kick-off */ + USB_REG32(chip, REG_DMAPARAM1) |= BIT(0); + + /* 5. DMA wait */ + rc = -EBUSY; + for (timeout = 250000; timeout; --timeout) { + tmp = USB_REG32(chip, REG_DCISR2); + /* DMA complete */ + if (tmp & BIT(7)) { + rc = 0; + break; + } + /* DMA error */ + if (tmp & BIT(8)) { + printf("fotg210: dma error\n"); + break; + } + /* resume, suspend, reset */ + if (tmp & (BIT(2) | BIT(1) | BIT(0))) { + printf("fotg210: dma reset by host\n"); + break; + } + udelay(1); + } + + /* 7. DMA target reset */ + if (rc) + USB_REG32(chip, REG_DMAPARAM1) |= BIT(3); + USB_REG32(chip, REG_DCISR2) &= 0x7f; + USB_REG32(chip, REG_DMAFIFO) = 0; + + req->req.status = rc; + if (rc == 0) + req->req.actual += len; + else + printf("fotg210: ep%d dma error(code=%d)\n", ep->id, rc); + + return len; +} + +/* + * result of setup packet + */ +#define CX_IDLE 0 +#define CX_FINISH 1 +#define CX_STALL 2 + +static void +fotg210_setup(struct fotg210_chip *chip) +{ + int rc = CX_IDLE; + uint32_t tmp[2]; + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp; + + /* + * If this is the first Cx 8 byte command, + * we can now query USB mode (high/full speed; USB 2.0/USB 1.0) + */ + if (chip->state == USB_STATE_POWERED) { + chip->state = USB_STATE_DEFAULT; + if (USB_REG32(chip, REG_OTGCSR) & BIT(21)) { + /* Mini-B */ + if (USB_REG32(chip, REG_DCCR) & BIT(6)) { + puts("fotg210: HS\n"); + chip->gadget.speed = USB_SPEED_HIGH; + USB_REG32(chip, REG_DCSOFMTR) = 0x044C; + } else { + puts("fotg210: FS\n"); + chip->gadget.speed = USB_SPEED_FULL; + USB_REG32(chip, REG_DCSOFMTR) = 0x2710; + } + } else { + printf("fotg210: mini-A?\n"); + } + } + + /* switch data port to ep0 */ + USB_REG32(chip, REG_DMAFIFO) = BIT(4); + /* fetch 8 bytes setup packet */ + tmp[0] = USB_REG32(chip, REG_DMAPARAM3); + tmp[1] = USB_REG32(chip, REG_DMAPARAM3); + /* release data port */ + USB_REG32(chip, REG_DMAFIFO) = 0; + + if (req->bRequestType & USB_DIR_IN) + ep0_desc.bEndpointAddress = USB_DIR_IN; + else + ep0_desc.bEndpointAddress = USB_DIR_OUT; + + rc = CX_IDLE; + + if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + printf("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF); + if ((req->wValue & 0x00FF) == 0) { + chip->state = USB_STATE_ADDRESS; + USB_REG32(chip, REG_DCAR) = chip->addr; + } else { + chip->state = USB_STATE_CONFIGURED; + USB_REG32(chip, REG_DCAR) = chip->addr | BIT(7); + } + rc = CX_IDLE; + break; + + case USB_REQ_SET_ADDRESS: + printf("fotg210: set_addr(0x%04X)\n", req->wValue); + chip->state = USB_STATE_ADDRESS; + chip->addr = req->wValue; + rc = CX_FINISH; + USB_REG32(chip, REG_DCAR) = chip->addr; + break; + + case USB_REQ_CLEAR_FEATURE: + printf("fotg210: clr_feature(%d, %d)\n", req->bRequestType & 0x03, req->wValue); + switch (req->wValue) { + case 0: /* [Endpoint] halt */ + ep_reset(chip, req->wIndex); + rc = CX_FINISH; + break; + + case 1: /* [Device] remote wake-up */ + case 2: /* [Device] test mode */ + default: + rc = CX_STALL; + break; + } + break; + + case USB_REQ_SET_FEATURE: + printf("fotg210: set_feature(%d, %d)\n", req->wValue, req->wIndex & 0xf); + switch (req->wValue) { + case 0: /* Endpoint Halt */ + do { + int id = req->wIndex & 0xf; + USB_REG32(chip, REG_IEP1 + (id - 1) * 4) |= BIT(11); + USB_REG32(chip, REG_OEP1 + (id - 1) * 4) |= BIT(11); + } while (0); + rc = CX_FINISH; + break; + case 1: /* Remote Wakeup */ + case 2: /* Test Mode */ + default: + rc = CX_STALL; + break; + } + break; + + case USB_REQ_GET_STATUS: + printf("fotg210: get_status\n"); + rc = CX_STALL; + break; + + case USB_REQ_SET_DESCRIPTOR: + printf("fotg210: set_descriptor\n"); + rc = CX_STALL; + break; + + case USB_REQ_SYNCH_FRAME: + printf("fotg210: sync frame\n"); + rc = CX_STALL; + break; + } + } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */ + + if (rc == CX_IDLE && chip->driver->setup) { + if (chip->driver->setup(&chip->gadget, req) < 0) + rc = CX_STALL; + else + rc = CX_FINISH; + } + + switch (rc) { + case CX_FINISH: + USB_REG32(chip, REG_DCCXFIFO) |= BIT(0); + break; + + case CX_STALL: + USB_REG32(chip, REG_DCCXFIFO) |= (BIT(2) | BIT(0)); + printf("fotg210: cx_stall?\n"); + break; + + case CX_IDLE: + printf("fotg210: cx_idle?\n"); + default: + break; + } +} + +/* + * fifo - FIFO id + * zlp - zero length packet + */ +static void +fotg210_recv(struct fotg210_chip *chip, int ep_id) +{ + struct fotg210_ep *ep = chip->ep + ep_id; + struct fotg210_request *req; + int len; + + if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + printf("fotg210: ep%d recv, invalid!\n", ep->id); + return; + } + + if (list_empty(&ep->queue)) { + printf("fotg210: ep%d recv, drop!\n", ep->id); + return; + } + + req = list_first_entry(&ep->queue, struct fotg210_request, queue); + len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) { + list_del_init(&req->queue); + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + if (ep->id > 0 && list_empty(&ep->queue)) + USB_REG32(chip, REG_DCIMR1) |= 0x03 << (ep_to_fifo(chip, ep->id) * 2); +} + +/* + * USB Gadget Layer + */ +static int +fotg210_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + int id = ep_to_fifo(chip, ep->id); + int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + + if (!_ep || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT + || le16_to_cpu(desc->wMaxPacketSize) == 0) { + printf("fotg210: bad ep or descriptor\n"); + return -EINVAL; + } + + ep->desc = desc; + ep->stopped = 0; + + if (in) + USB_REG32(chip, REG_FIFOMAP) |= 0x10 << (8 * id); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + return -EINVAL; + + case USB_ENDPOINT_XFER_ISOC: + USB_REG32(chip, REG_FIFOCFG) |= 0x21 << (8 * id); + break; + + case USB_ENDPOINT_XFER_BULK: + USB_REG32(chip, REG_FIFOCFG) |= 0x22 << (8 * id); + break; + + case USB_ENDPOINT_XFER_INT: + USB_REG32(chip, REG_FIFOCFG) |= 0x23 << (8 * id); + break; + } + + return 0; +} + +static int +fotg210_ep_disable (struct usb_ep *_ep) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + int id = ep_to_fifo(chip, ep->id); + + ep->desc = NULL; + ep->stopped = 1; + + USB_REG32(chip, REG_FIFOCFG) &= ~(0x23 << (8 * id)); + USB_REG32(chip, REG_FIFOMAP) &= ~(0x30 << (8 * id)); + + return 0; +} + +static struct usb_request * +fotg210_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) { + struct fotg210_request *req = malloc(sizeof(*req)); + if (req) { + memset(req, 0, sizeof(*req)); + INIT_LIST_HEAD(&req->queue); + } + return &req->req; +} + +static void +fotg210_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_request *req = container_of(_req, struct fotg210_request, req); + free(req); +} + +static int +fotg210_ep_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_request *req = container_of(_req, struct fotg210_request, req); + + if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { + printf("fotg210: invalid request to ep%d\n", ep->id); + return -EINVAL; + } + + if (!chip || chip->state == USB_STATE_SUSPENDED) { + printf("fotg210: request while chip suspend!?\n"); + return -EINVAL; + } + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (req->req.length == 0) { + req->req.status = 0; + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + return 0; + } + + if (ep->id == 0) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + if (ep->desc->bEndpointAddress & USB_DIR_IN) + udelay(100); + } while (req->req.length > req->req.actual); + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + } while (req->req.length > req->req.actual); + } else { + list_add_tail(&req->queue, &ep->queue); + USB_REG32(chip, REG_DCIMR1) &= ~(0x03 << (ep_to_fifo(chip, ep->id) * 2)); + } + } + + if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + return 0; +} + +static int +fotg210_ep_dequeue (struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_request *req; + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + return -EINVAL; + } + + /* remove the request */ + list_del_init(&req->queue); + + /* update status & invoke complete callback */ + if (req->req.status == -EINPROGRESS) { + req->req.status = -ECONNRESET; + if (req->req.complete) + req->req.complete(_ep, &req->req); + } + + return 0; +} + +static int +fotg210_ep_halt (struct usb_ep *_ep, int halt) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + int rc = -1; + + printf("fotg210: ep%d halt=%d\n", ep->id, halt); + + /* Endpoint STALL */ + if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) { + if (halt) { + /* wait until fifo empty */ + while ((USB_REG32(chip, REG_DCCXFIFO) & 0xf00) != 0xf00) + ; + /* stall */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + USB_REG32(chip, REG_IEP1 + (ep->id - 1) * 4) |= BIT(11); + else + USB_REG32(chip, REG_OEP1 + (ep->id - 1) * 4) |= BIT(11); + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) + USB_REG32(chip, REG_IEP1 + (ep->id - 1) * 4) &= ~BIT(11); + else + USB_REG32(chip, REG_OEP1 + (ep->id - 1) * 4) &= ~BIT(11); + } + rc = 0; + } + + return rc; +} + +/* + * activate/deactivate link with host. + */ +static void +pullup(struct fotg210_chip *chip, int is_on) +{ + if (is_on) { + if (!chip->pullup) { + chip->state = USB_STATE_POWERED; + chip->pullup = 1; + /* enable the chip */ + USB_REG32(chip, REG_DCCR) |= BIT(5); + /* clear unplug bit (BIT0) */ + USB_REG32(chip, 0x114) &= (~BIT(0)); + } + } else { + chip->state = USB_STATE_NOTATTACHED; + chip->pullup = 0; + USB_REG32(chip, REG_DCAR) = 0; + /* set unplug bit (BIT0) */ + USB_REG32(chip, 0x114) |= BIT(0); + /* disable the chip */ + USB_REG32(chip, REG_DCCR) &= ~BIT(5); + } +} + +static int +fotg210_pullup (struct usb_gadget *_gadget, int is_on) +{ + struct fotg210_chip *chip = container_of(_gadget, struct fotg210_chip, gadget); + + printf("fotg210: pullup=%d\n", is_on); + pullup(chip, is_on); + + return 0; +} + +static int +fotg210_get_frame(struct usb_gadget *_gadget) +{ + struct fotg210_chip *chip = container_of(_gadget, struct fotg210_chip, gadget); + return USB_REG32(chip, REG_DCSOFFNR) & 0x7ff; +} + +static struct usb_gadget_ops fotg210_gadget_ops = { + .get_frame = fotg210_get_frame, + .pullup = fotg210_pullup, +}; + +static struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + .set_halt = fotg210_ep_halt, + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, +}; + +static struct fotg210_chip controller = { + .iobase = CONFIG_FOTG210_BASE, + .gadget = { + .name = "fotg210_udc", + .ops = &fotg210_gadget_ops, + .ep0 = &controller.ep[0].ep, + .speed = USB_SPEED_UNKNOWN, + .is_dualspeed = 1, + .is_otg = 0, + .is_a_peripheral = 0, + .b_hnp_enable = 0, + .a_hnp_support = 0, + .a_alt_hnp_support = 0, + }, + .ep[0] = { + .id = 0, + .ep = { + .name = "ep0", + .ops = &fotg210_ep_ops, + }, + .desc = &ep0_desc, + .chip = &controller, + .maxpacket = CFG_EP0_MAX_PACKET_SIZE, + }, + .ep[1] = { + .id = 1, + .ep = { + .name = "ep1", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[2] = { + .id = 2, + .ep = { + .name = "ep2", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[3] = { + .id = 3, + .ep = { + .name = "ep3", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[4] = { + .id = 4, + .ep = { + .name = "ep4", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, +}; + +int usb_gadget_handle_interrupts(void) +{ + struct fotg210_chip *chip = &controller; + uint32_t isr, dcisr; + + isr = USB_REG32(chip, REG_ISR) & (~USB_REG32(chip, REG_IMR)); + dcisr = USB_REG32(chip, REG_DCISR) & (~USB_REG32(chip, REG_DCIMR)); + if (!(isr & BIT(0)) || dcisr == 0) + return 0; + + USB_REG32(chip, REG_ISR) = BIT(0); + + /* CX interrupts */ + if (dcisr & BIT(0)) { + uint32_t st = USB_REG32(chip, REG_DCISR0); + USB_REG32(chip, REG_DCISR0) = 0; + + if (st & BIT(4)) + printf("fotg210: cmd error\n"); + + if (st & BIT(5)) + printf("fotg210: cmd abort\n"); + + if (st & BIT(0)) /* setup */ + fotg210_setup(chip); + else if (st & BIT(3)) /* command finish */ + USB_REG32(chip, REG_DCCXFIFO) |= BIT(0); + } + + /* FIFO interrupts */ + if (dcisr & BIT(1)) { + uint32_t id; + uint32_t st = USB_REG32(chip, REG_DCISR1); + for (id = 0; id < 4; ++id) { + if (st & (3 << (id * 2))) + fotg210_recv(chip, fifo_to_ep(chip, id, 0)); + } + } + + /* Device Status Interrupts */ + if (dcisr & BIT(2)) { + uint32_t st = USB_REG32(chip, REG_DCISR2); + USB_REG32(chip, REG_DCISR2) = 0; + + if (st & BIT(0)) { + printf("fotg210: reset by host\n"); + } else if (st & BIT(1)) { + printf("fotg210: suspend/removed\n"); + } else if (st & BIT(2)) { + printf("fotg210: resume\n"); + } + + /* Errors */ + if (st & BIT(3)) + printf("fotg210: iso error\n"); + if (st & BIT(4)) + printf("fotg210: iso abort\n"); + if (st & BIT(8)) + printf("fotg210: dma error\n"); + } + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int i, rc = 0; + struct fotg210_chip *chip = &controller; + + if (!driver || !driver->bind || !driver->setup) { + puts("fotg210: bad parameter.\n"); + return -EINVAL; + } + + INIT_LIST_HEAD(&chip->gadget.ep_list); + for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) { + struct fotg210_ep *ep = chip->ep + i; + + ep->ep.maxpacket = ep->maxpacket; + INIT_LIST_HEAD(&ep->queue); + + if (ep->id == 0) { + ep->stopped = 0; + } else { + ep->stopped = 1; + list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list); + } + } + + if (fotg210_reset(chip)) { + puts("fotg210: reset failed.\n"); + return -EINVAL; + } + + rc = driver->bind(&chip->gadget); + if (rc) { + printf("fotg210: driver->bind() returned %d\n", rc); + dcache_enable(); + return rc; + } + chip->driver = driver; + + return rc; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct fotg210_chip *chip = &controller; + + driver->unbind(&chip->gadget); + chip->driver = NULL; + + pullup(chip, 0); + + return 0; +} diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/fotg210.h new file mode 100644 index 0000000..b5f9842 --- /dev/null +++ b/drivers/usb/gadget/fotg210.h @@ -0,0 +1,99 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#ifndef FOTG210_H +#define FOTG210_H + +#ifndef BIT +#define BIT(nr) (1UL << (nr)) +#endif + +#define REG_HCISR 0x014 /* Host Controller: Interupt Status Register */ +#define REG_HCIER 0x018 /* Host Controller: Interupt Enable Register */ + +/* + * Global Registers + */ +#define REG_ISR 0x0C0 /* Interrupt Status */ +#define REG_IMR 0x0C4 /* Interrupt Control */ + +/* + * OTG Registers + */ +#define REG_OTGCSR 0x080 /* OTG Control Status */ +#define REG_OTGISR 0x084 /* OTG Interrupt Status */ +#define REG_OTGIER 0x088 /* OTG Interrupt Enable Register */ + +/* + * Device Registers + */ +#define REG_DCCR 0x100 /* Device Controller: Control Register */ +#define REG_DCAR 0x104 /* Device Address */ +#define REG_DCTSTR 0x108 /* Device Test */ +#define REG_DCSOFFNR 0x10C /* Device SOF Frame Number */ +#define REG_DCSOFMTR 0x110 /* Device SOF Frame Number */ + +#define REG_DCCXR 0x11C /* Device CX Configuration */ +#define REG_DCCXFIFO 0x120 /* Device CX FIFO */ +#define REG_DCIDLE 0x124 /* Device Idle Counter */ + +#define REG_DCIMR 0x130 /* Device Group Interrupt Mask */ +#define REG_DCIMR0 0x134 /* Device Group 0 Interrupt Mask */ +#define REG_DCIMR1 0x138 /* Device Group 1 Interrupt Mask */ +#define REG_DCIMR2 0x13C /* Device Group 2 Interrupt Mask */ +#define REG_DCISR 0x140 /* Device Group Interrupt Status */ +#define REG_DCISR0 0x144 /* Device Group 0 Interrupt Status */ +#define REG_DCISR1 0x148 /* Device Group 1 Interrupt Status */ +#define REG_DCISR2 0x14C /* Device Group 2 Interrupt Status */ + +#define REG_DCRXZLPKT 0x150 /* Device Receive Zero-Length Packet */ +#define REG_DCTXZLPKT 0x154 /* Device Receive Zero-Length Packet */ +#define REG_DCISOERR 0x158 /* Device ISO Error/Abort */ + +#define REG_IEP1 0x160 /* Device IN Endpoint */ +#define REG_IEP2 0x164 /* Device IN Endpoint */ +#define REG_IEP3 0x168 /* Device IN Endpoint */ +#define REG_IEP4 0x16C /* Device IN Endpoint */ +#define REG_IEP5 0x170 /* Device IN Endpoint */ +#define REG_IEP6 0x174 /* Device IN Endpoint */ +#define REG_IEP7 0x178 /* Device IN Endpoint */ +#define REG_IEP8 0x17C /* Device IN Endpoint */ + +#define REG_OEP1 0x180 /* Device OUT Endpoint */ +#define REG_OEP2 0x184 /* Device OUT Endpoint */ +#define REG_OEP3 0x188 /* Device OUT Endpoint */ +#define REG_OEP4 0x18C /* Device OUT Endpoint */ +#define REG_OEP5 0x190 /* Device OUT Endpoint */ +#define REG_OEP6 0x194 /* Device OUT Endpoint */ +#define REG_OEP7 0x198 /* Device OUT Endpoint */ +#define REG_OEP8 0x19C /* Device OUT Endpoint */ + +#define REG_EPMAP14 0x1A0 /* Device Endpoint Map 1 ~ 4 */ +#define REG_EPMAP58 0x1A4 /* Device Endpoint Map 5 ~ 8 */ +#define REG_FIFOMAP 0x1A8 /* Device FIFO Map */ +#define REG_FIFOCFG 0x1AC /* Device FIFO Configuration */ + +#define REG_FIFO0 0x1B0 /* Device FIFO 0 */ +#define REG_FIFO1 0x1B4 /* Device FIFO 1 */ +#define REG_FIFO2 0x1B8 /* Device FIFO 2 */ +#define REG_FIFO3 0x1BC /* Device FIFO 3 */ + +#define REG_DMAFIFO 0x1C0 /* Device DMA Target FIFO */ +#define REG_DMAPARAM1 0x1C8 /* Device DMA Parameter 1 */ +#define REG_DMAPARAM2 0x1CC /* Device DMA Parameter 2 */ +#define REG_DMAPARAM3 0x1D0 /* Device DMA Parameter 3 */ + +#define ERR_ISOERR 0 +#define ERR_ISOABT 1 +#define ERR_DMAERR 2 +#define ERR_CMDERR 3 +#define ERR_CMDABT 4 + +#endif diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e570142..f038747 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -150,6 +150,12 @@ #define gadget_is_mv(g) 0 #endif +#ifdef CONFIG_USB_GADGET_FOTG210 +#define gadget_is_fotg210(g) (!strcmp("fotg210_udc", (g)->name)) +#else +#define gadget_is_fotg210(g) 0 +#endif + /* * CONFIG_USB_GADGET_SX2 * CONFIG_USB_GADGET_AU1X00 @@ -215,5 +221,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x20; else if (gadget_is_mv(gadget)) return 0x21; + else if (gadget_is_fotg210(gadget)) + return 0x22; return -ENOENT; }