From: Kuo-Jung Su <dant...@faraday-tech.com> The Faraday FOTG210 is an OTG chip which could operate as either an EHCI Host or a USB Device as a time.
Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> CC: Marek Vasut <ma...@denx.de> --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/fotg210.c | 922 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/gadget_chips.h | 8 + 3 files changed, 931 insertions(+) create mode 100644 drivers/usb/gadget/fotg210.c 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..d662387 --- /dev/null +++ b/drivers/usb/gadget/fotg210.c @@ -0,0 +1,922 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dant...@faraday-tech.com> + * + * 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 <common.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <linux/types.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <usb/fotg210.h> + +#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; + struct fotg210_chip *chip; + const struct usb_endpoint_descriptor *desc; +}; + +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; + struct fotg210_regs *iobase; + uint8_t irq; + uint16_t addr; + int pullup; + enum usb_device_state state; + struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS]; +}; + +#define USB_READ(r) le32_to_cpu(readl(r)) +#define USB_WRITE(v, r) writel(cpu_to_le32(v), r) +#define USB_SETBITS(m, r) setbits_le32(r, m) +#define USB_CLRBITS(m, r) clrbits_le32(r, m) + +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, +}; + +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; + struct fotg210_regs *regs = chip->iobase; + + if (ep_addr & USB_DIR_IN) { + /* reset endpoint */ + USB_SETBITS(BIT_MASK(12), ®s->iep[ep - 1]); + mdelay(1); + USB_CLRBITS(BIT_MASK(12), ®s->iep[ep - 1]); + /* clear endpoint stall */ + USB_CLRBITS(BIT_MASK(11), ®s->iep[ep - 1]); + } else { + /* reset endpoint */ + USB_SETBITS(BIT_MASK(12), ®s->oep[ep - 1]); + mdelay(1); + USB_CLRBITS(BIT_MASK(12), ®s->oep[ep - 1]); + /* clear endpoint stall */ + USB_CLRBITS(BIT_MASK(11), ®s->oep[ep - 1]); + } + + return 0; +} + +static int fotg210_reset(struct fotg210_chip *chip) +{ + struct fotg210_regs *regs = chip->iobase; + uint32_t i; + + chip->state = USB_STATE_POWERED; + + /* chip enable */ + USB_WRITE(BIT_MASK(5), ®s->dev_ctrl); + + /* device address reset */ + chip->addr = 0; + USB_WRITE(chip->addr, ®s->dev_addr); + + /* set idle counter to 7ms */ + USB_WRITE(7, ®s->idle); + + /* disable interrupts */ + USB_WRITE(0x0f, ®s->imr); + USB_WRITE(0x07, ®s->gimr); + USB_WRITE(0x3f, ®s->gimr0); + USB_WRITE(0xf00ff, ®s->gimr1); + USB_WRITE(0x7ff, ®s->gimr2); + + /* clear interrupts */ + USB_WRITE(0x07, ®s->isr); + USB_WRITE(0x00, ®s->gisr); + USB_WRITE(0x00, ®s->gisr0); + USB_WRITE(0x00, ®s->gisr1); + USB_WRITE(0x00, ®s->gisr2); + + /* chip reset */ + USB_SETBITS(BIT_MASK(4), ®s->dev_ctrl); + while (USB_READ(®s->dev_ctrl) & BIT_MASK(4)) + ; + + /* CX FIFO reset */ + USB_SETBITS(BIT_MASK(3), ®s->cxfifo); + while (USB_READ(®s->cxfifo) & BIT_MASK(3)) + ; + + /* create static ep-fifo map (EP1-FIFO0, EP2-FIFO1 ...)*/ + USB_WRITE(0x33221100, ®s->epmap14); + USB_WRITE(0x00000000, ®s->epmap58); + USB_WRITE(0x04030201, ®s->fifomap); + USB_WRITE(0x00000000, ®s->fifocfg); + for (i = 0; i < 8; ++i) { + USB_WRITE(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]); + USB_WRITE(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]); + } + + /* FIFO reset */ + for (i = 0; i < 4; ++i) { + USB_WRITE(BIT_MASK(12), ®s->fifocsr[i]); + while (USB_READ(®s->fifocsr[i]) & BIT_MASK(12)) + ; + } + + /* enable only device interrupt, interrupt=level,high active */ + USB_WRITE(0x0e, ®s->imr); + USB_WRITE(0x07, ®s->isr); + + /* disable EP0 IN/OUT interrupt */ + USB_WRITE(0x06, ®s->gimr0); + /* disable EPX IN+SPK+OUT interrupts */ + USB_WRITE(0xf00ff, ®s->gimr1); + /* disable wakeup+idle+dma+zlp interrupts */ + USB_WRITE(0x7e0, ®s->gimr2); + /* enable all group interrupt */ + USB_WRITE(0x00, ®s->gimr); + + /* suspend delay = 3 ms */ + USB_WRITE(3, ®s->idle); + + /* turn-on device interrupts */ + USB_SETBITS(BIT_MASK(2), ®s->dev_ctrl); + + return 0; +} + +static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req) +{ + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->iobase; + uint32_t tmp, ts; + 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 ret = -EBUSY; + + /* 1. init dma buffer */ + if (len > ep->maxpacket) + len = ep->maxpacket; + + /* 2. wait for dma ready (hardware) */ + for (ts = get_timer(0); get_timer(ts) < 100; ) { + if (!(USB_READ(®s->dma_ctrl) & 0x01)) { + ret = 0; + break; + } + } + if (ret) { + printf("fotg210: dma busy\n"); + req->req.status = ret; + return ret; + } + + /* 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_WRITE(virt_to_phys(buf), ®s->dma_addr); + + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + if (ep->id == 0) { + /* Wait until fifo empty */ + while (!(USB_READ(®s->cxfifo) & 0x20)) + ; + USB_WRITE(BIT_MASK(4), ®s->dma_fifo); + } else { + /* Wait until fifo empty */ + while (!(USB_READ(®s->cxfifo) & (1 << (8 + fifo)))) + ; + USB_WRITE(1 << fifo, ®s->dma_fifo); + } + USB_WRITE((len << 8) | BIT_MASK(1), ®s->dma_ctrl); + } else { + uint32_t blen; + if (ep->id == 0) { + USB_WRITE(0x10, ®s->dma_fifo); + do { + blen = (USB_READ(®s->cxfifo) >> 24) & 0x7f; + } while (blen < len); + } else { + USB_WRITE(1 << fifo, ®s->dma_fifo); + blen = USB_READ(®s->fifocsr[fifo]) & 0x7ff; + } + len = (len < blen) ? len : blen; + USB_WRITE(len << 8, ®s->dma_ctrl); + } + + /* 4. DMA start */ + USB_SETBITS(BIT_MASK(0), ®s->dma_ctrl); + + /* 5. DMA wait */ + ret = -EBUSY; + for (ts = get_timer(0); get_timer(ts) < 100; ) { + tmp = USB_READ(®s->gisr2); + /* DMA complete */ + if (tmp & 0x80) { + ret = 0; + break; + } + /* DMA error */ + if (tmp & 0x100) { + printf("fotg210: dma error\n"); + break; + } + /* resume, suspend, reset */ + if (tmp & 0x07) { + printf("fotg210: dma reset by host\n"); + break; + } + } + + /* 7. DMA target reset */ + if (ret) + USB_WRITE(BIT_MASK(4) | BIT_MASK(3), ®s->dma_ctrl); + + USB_WRITE(0, ®s->gisr2); + USB_WRITE(0, ®s->dma_fifo); + + req->req.status = ret; + if (!ret) + req->req.actual += len; + else + printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret); + + 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 id, ret = CX_IDLE; + uint32_t tmp[2]; + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp; + struct fotg210_regs *regs = chip->iobase; + + /* + * 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_READ(®s->otgcsr) & BIT_MASK(21)) { + /* Mini-B */ + if (USB_READ(®s->dev_ctrl) & BIT_MASK(6)) { + puts("fotg210: HS\n"); + chip->gadget.speed = USB_SPEED_HIGH; + USB_WRITE(0x044c, ®s->sof_mtr); + } else { + puts("fotg210: FS\n"); + chip->gadget.speed = USB_SPEED_FULL; + USB_WRITE(0x2710, ®s->sof_mtr); + } + } else { + printf("fotg210: mini-A?\n"); + } + } + + /* switch data port to ep0 */ + USB_WRITE(0x10, ®s->dma_fifo); + /* fetch 8 bytes setup packet */ + tmp[0] = USB_READ(®s->dma_data); + tmp[1] = USB_READ(®s->dma_data); + /* release data port */ + USB_WRITE(0x00, ®s->dma_fifo); + + if (req->bRequestType & USB_DIR_IN) + ep0_desc.bEndpointAddress = USB_DIR_IN; + else + ep0_desc.bEndpointAddress = USB_DIR_OUT; + + ret = CX_IDLE; + + if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF); + if (!(req->wValue & 0x00FF)) { + chip->state = USB_STATE_ADDRESS; + USB_WRITE(chip->addr, ®s->dev_addr); + } else { + chip->state = USB_STATE_CONFIGURED; + USB_WRITE(chip->addr | BIT_MASK(7), + ®s->dev_addr); + } + ret = CX_IDLE; + break; + + case USB_REQ_SET_ADDRESS: + debug("fotg210: set_addr(0x%04X)\n", req->wValue); + chip->state = USB_STATE_ADDRESS; + chip->addr = req->wValue; + ret = CX_FINISH; + USB_WRITE(chip->addr, ®s->dev_addr); + break; + + case USB_REQ_CLEAR_FEATURE: + debug("fotg210: clr_feature(%d, %d)\n", + req->bRequestType & 0x03, req->wValue); + switch (req->wValue) { + case 0: /* [Endpoint] halt */ + ep_reset(chip, req->wIndex); + ret = CX_FINISH; + break; + case 1: /* [Device] remote wake-up */ + case 2: /* [Device] test mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_SET_FEATURE: + debug("fotg210: set_feature(%d, %d)\n", + req->wValue, req->wIndex & 0xf); + switch (req->wValue) { + case 0: /* Endpoint Halt */ + id = req->wIndex & 0xf; + USB_SETBITS(BIT_MASK(11), ®s->iep[id - 1]); + USB_SETBITS(BIT_MASK(11), ®s->oep[id - 1]); + ret = CX_FINISH; + break; + case 1: /* Remote Wakeup */ + case 2: /* Test Mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_GET_STATUS: + debug("fotg210: get_status\n"); + ret = CX_STALL; + break; + + case USB_REQ_SET_DESCRIPTOR: + debug("fotg210: set_descriptor\n"); + ret = CX_STALL; + break; + + case USB_REQ_SYNCH_FRAME: + debug("fotg210: sync frame\n"); + ret = CX_STALL; + break; + } + } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */ + + if (ret == CX_IDLE && chip->driver->setup) { + if (chip->driver->setup(&chip->gadget, req) < 0) + ret = CX_STALL; + else + ret = CX_FINISH; + } + + switch (ret) { + case CX_FINISH: + USB_SETBITS(BIT_MASK(0), ®s->cxfifo); + break; + + case CX_STALL: + USB_SETBITS(BIT_MASK(2) | BIT_MASK(0), ®s->cxfifo); + printf("fotg210: cx_stall!\n"); + break; + + case CX_IDLE: + debug("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_regs *regs = chip->iobase; + 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_SETBITS(3 << (ep_to_fifo(chip, ep->id) * 2), ®s->gimr1); +} + +/* + * 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; + struct fotg210_regs *regs = chip->iobase; + 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_SETBITS(0x10 << (8 * id), ®s->fifomap); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + return -EINVAL; + + case USB_ENDPOINT_XFER_ISOC: + USB_SETBITS(0x21 << (8 * id), ®s->fifocfg); + break; + + case USB_ENDPOINT_XFER_BULK: + USB_SETBITS(0x22 << (8 * id), ®s->fifocfg); + break; + + case USB_ENDPOINT_XFER_INT: + USB_SETBITS(0x23 << (8 * id), ®s->fifocfg); + 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; + struct fotg210_regs *regs = chip->iobase; + int id = ep_to_fifo(chip, ep->id); + + ep->desc = NULL; + ep->stopped = 1; + + USB_CLRBITS(0x23 << (8 * id), ®s->fifocfg); + USB_CLRBITS(0x30 << (8 * id), ®s->fifomap); + + 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; + 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_regs *regs = chip->iobase; + struct fotg210_request *req; + + 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 suspended\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_CLRBITS(3 << (ep_to_fifo(chip, ep->id) * 2), + ®s->gimr1); + } + } + + 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; + struct fotg210_regs *regs = chip->iobase; + int ret = -1; + + debug("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_READ(®s->cxfifo) & 0xf00) != 0xf00) + ; + /* stall */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + USB_SETBITS(BIT_MASK(11), + regs->iep[ep->id - 1]); + } else { + USB_SETBITS(BIT_MASK(11), + regs->oep[ep->id - 1]); + } + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + USB_CLRBITS(BIT_MASK(11), + regs->iep[ep->id - 1]); + } else { + USB_CLRBITS(BIT_MASK(11), + regs->oep[ep->id - 1]); + } + } + ret = 0; + } + + return ret; +} + +/* + * activate/deactivate link with host. + */ +static void pullup(struct fotg210_chip *chip, int is_on) +{ + struct fotg210_regs *regs = chip->iobase; + if (is_on) { + if (!chip->pullup) { + chip->state = USB_STATE_POWERED; + chip->pullup = 1; + /* enable the chip */ + USB_SETBITS(BIT_MASK(5), ®s->dev_ctrl); + /* clear unplug bit (BIT0) */ + USB_CLRBITS(BIT_MASK(0), ®s->phy_tmsr); + } + } else { + chip->state = USB_STATE_NOTATTACHED; + chip->pullup = 0; + chip->addr = 0; + USB_WRITE(chip->addr, ®s->dev_addr); + /* set unplug bit (BIT0) */ + USB_SETBITS(BIT_MASK(0), ®s->phy_tmsr); + /* disable the chip */ + USB_CLRBITS(BIT_MASK(5), ®s->dev_ctrl); + } +} + +static int fotg210_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct fotg210_chip *chip; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + + debug("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; + struct fotg210_regs *regs; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + regs = chip->iobase; + + return USB_READ(®s->sof_fnr) & 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 = (void *)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; + struct fotg210_regs *regs = chip->iobase; + uint32_t isr, gisr; + + isr = USB_READ(®s->isr) & (~USB_READ(®s->imr)); + gisr = USB_READ(®s->gisr) & (~USB_READ(®s->gimr)); + if (!(isr & BIT_MASK(0)) || !gisr) + return 0; + + USB_WRITE(BIT_MASK(0), ®s->isr); + + /* CX interrupts */ + if (gisr & BIT_MASK(0)) { + uint32_t st = USB_READ(®s->gisr0); + USB_WRITE(0, ®s->gisr0); + + if (st & BIT_MASK(4)) + printf("fotg210: cmd error\n"); + + if (st & BIT_MASK(5)) + printf("fotg210: cmd abort\n"); + + if (st & BIT_MASK(0)) /* setup */ + fotg210_setup(chip); + else if (st & BIT_MASK(3)) /* command finish */ + USB_SETBITS(BIT_MASK(0), ®s->cxfifo); + } + + /* FIFO interrupts */ + if (gisr & BIT_MASK(1)) { + uint32_t id; + uint32_t st = USB_READ(®s->gisr1); + for (id = 0; id < 4; ++id) { + if (st & (3 << (id * 2))) + fotg210_recv(chip, fifo_to_ep(chip, id, 0)); + } + } + + /* Device Status Interrupts */ + if (gisr & BIT_MASK(2)) { + uint32_t st = USB_READ(®s->gisr2); + USB_WRITE(0, ®s->gisr2); + + if (st & BIT_MASK(0)) + printf("fotg210: reset by host\n"); + else if (st & BIT_MASK(1)) + printf("fotg210: suspend/removed\n"); + else if (st & BIT_MASK(2)) + printf("fotg210: resume\n"); + + /* Errors */ + if (st & BIT_MASK(3)) + printf("fotg210: iso error\n"); + if (st & BIT_MASK(4)) + printf("fotg210: iso abort\n"); + if (st & BIT_MASK(8)) + printf("fotg210: dma error\n"); + } + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int i, ret = 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; + } + + ret = driver->bind(&chip->gadget); + if (ret) { + debug("fotg210: driver->bind() returned %d\n", ret); + dcache_enable(); + return ret; + } + chip->driver = driver; + + return ret; +} + +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/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; } -- 1.7.9.5 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot