164cfd3f9SKuo-Jung Su /*
264cfd3f9SKuo-Jung Su * Faraday USB 2.0 OTG Controller
364cfd3f9SKuo-Jung Su *
464cfd3f9SKuo-Jung Su * (C) Copyright 2010 Faraday Technology
564cfd3f9SKuo-Jung Su * Dante Su <dantesu@faraday-tech.com>
664cfd3f9SKuo-Jung Su *
71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
864cfd3f9SKuo-Jung Su */
964cfd3f9SKuo-Jung Su
1064cfd3f9SKuo-Jung Su #include <common.h>
1164cfd3f9SKuo-Jung Su #include <command.h>
1264cfd3f9SKuo-Jung Su #include <config.h>
1364cfd3f9SKuo-Jung Su #include <net.h>
1464cfd3f9SKuo-Jung Su #include <malloc.h>
1564cfd3f9SKuo-Jung Su #include <asm/io.h>
16*1221ce45SMasahiro Yamada #include <linux/errno.h>
1764cfd3f9SKuo-Jung Su #include <linux/types.h>
1864cfd3f9SKuo-Jung Su #include <linux/usb/ch9.h>
1964cfd3f9SKuo-Jung Su #include <linux/usb/gadget.h>
2064cfd3f9SKuo-Jung Su
2164cfd3f9SKuo-Jung Su #include <usb/fotg210.h>
2264cfd3f9SKuo-Jung Su
2364cfd3f9SKuo-Jung Su #define CFG_NUM_ENDPOINTS 4
2464cfd3f9SKuo-Jung Su #define CFG_EP0_MAX_PACKET_SIZE 64
2564cfd3f9SKuo-Jung Su #define CFG_EPX_MAX_PACKET_SIZE 512
2664cfd3f9SKuo-Jung Su
2764cfd3f9SKuo-Jung Su #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 2) /* 250 ms */
2864cfd3f9SKuo-Jung Su
2964cfd3f9SKuo-Jung Su struct fotg210_chip;
3064cfd3f9SKuo-Jung Su
3164cfd3f9SKuo-Jung Su struct fotg210_ep {
3264cfd3f9SKuo-Jung Su struct usb_ep ep;
3364cfd3f9SKuo-Jung Su
3464cfd3f9SKuo-Jung Su uint maxpacket;
3564cfd3f9SKuo-Jung Su uint id;
3664cfd3f9SKuo-Jung Su uint stopped;
3764cfd3f9SKuo-Jung Su
3864cfd3f9SKuo-Jung Su struct list_head queue;
3964cfd3f9SKuo-Jung Su struct fotg210_chip *chip;
4064cfd3f9SKuo-Jung Su const struct usb_endpoint_descriptor *desc;
4164cfd3f9SKuo-Jung Su };
4264cfd3f9SKuo-Jung Su
4364cfd3f9SKuo-Jung Su struct fotg210_request {
4464cfd3f9SKuo-Jung Su struct usb_request req;
4564cfd3f9SKuo-Jung Su struct list_head queue;
4664cfd3f9SKuo-Jung Su struct fotg210_ep *ep;
4764cfd3f9SKuo-Jung Su };
4864cfd3f9SKuo-Jung Su
4964cfd3f9SKuo-Jung Su struct fotg210_chip {
5064cfd3f9SKuo-Jung Su struct usb_gadget gadget;
5164cfd3f9SKuo-Jung Su struct usb_gadget_driver *driver;
5264cfd3f9SKuo-Jung Su struct fotg210_regs *regs;
5364cfd3f9SKuo-Jung Su uint8_t irq;
5464cfd3f9SKuo-Jung Su uint16_t addr;
5564cfd3f9SKuo-Jung Su int pullup;
5664cfd3f9SKuo-Jung Su enum usb_device_state state;
5764cfd3f9SKuo-Jung Su struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS];
5864cfd3f9SKuo-Jung Su };
5964cfd3f9SKuo-Jung Su
6064cfd3f9SKuo-Jung Su static struct usb_endpoint_descriptor ep0_desc = {
6164cfd3f9SKuo-Jung Su .bLength = sizeof(struct usb_endpoint_descriptor),
6264cfd3f9SKuo-Jung Su .bDescriptorType = USB_DT_ENDPOINT,
6364cfd3f9SKuo-Jung Su .bEndpointAddress = USB_DIR_IN,
6464cfd3f9SKuo-Jung Su .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
6564cfd3f9SKuo-Jung Su };
6664cfd3f9SKuo-Jung Su
fifo_to_ep(struct fotg210_chip * chip,int id,int in)6764cfd3f9SKuo-Jung Su static inline int fifo_to_ep(struct fotg210_chip *chip, int id, int in)
6864cfd3f9SKuo-Jung Su {
6964cfd3f9SKuo-Jung Su return (id < 0) ? 0 : ((id & 0x03) + 1);
7064cfd3f9SKuo-Jung Su }
7164cfd3f9SKuo-Jung Su
ep_to_fifo(struct fotg210_chip * chip,int id)7264cfd3f9SKuo-Jung Su static inline int ep_to_fifo(struct fotg210_chip *chip, int id)
7364cfd3f9SKuo-Jung Su {
7464cfd3f9SKuo-Jung Su return (id <= 0) ? -1 : ((id - 1) & 0x03);
7564cfd3f9SKuo-Jung Su }
7664cfd3f9SKuo-Jung Su
ep_reset(struct fotg210_chip * chip,uint8_t ep_addr)7764cfd3f9SKuo-Jung Su static inline int ep_reset(struct fotg210_chip *chip, uint8_t ep_addr)
7864cfd3f9SKuo-Jung Su {
7964cfd3f9SKuo-Jung Su int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK;
8064cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
8164cfd3f9SKuo-Jung Su
8264cfd3f9SKuo-Jung Su if (ep_addr & USB_DIR_IN) {
8364cfd3f9SKuo-Jung Su /* reset endpoint */
8464cfd3f9SKuo-Jung Su setbits_le32(®s->iep[ep - 1], IEP_RESET);
8564cfd3f9SKuo-Jung Su mdelay(1);
8664cfd3f9SKuo-Jung Su clrbits_le32(®s->iep[ep - 1], IEP_RESET);
8764cfd3f9SKuo-Jung Su /* clear endpoint stall */
8864cfd3f9SKuo-Jung Su clrbits_le32(®s->iep[ep - 1], IEP_STALL);
8964cfd3f9SKuo-Jung Su } else {
9064cfd3f9SKuo-Jung Su /* reset endpoint */
9164cfd3f9SKuo-Jung Su setbits_le32(®s->oep[ep - 1], OEP_RESET);
9264cfd3f9SKuo-Jung Su mdelay(1);
9364cfd3f9SKuo-Jung Su clrbits_le32(®s->oep[ep - 1], OEP_RESET);
9464cfd3f9SKuo-Jung Su /* clear endpoint stall */
9564cfd3f9SKuo-Jung Su clrbits_le32(®s->oep[ep - 1], OEP_STALL);
9664cfd3f9SKuo-Jung Su }
9764cfd3f9SKuo-Jung Su
9864cfd3f9SKuo-Jung Su return 0;
9964cfd3f9SKuo-Jung Su }
10064cfd3f9SKuo-Jung Su
fotg210_reset(struct fotg210_chip * chip)10164cfd3f9SKuo-Jung Su static int fotg210_reset(struct fotg210_chip *chip)
10264cfd3f9SKuo-Jung Su {
10364cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
10464cfd3f9SKuo-Jung Su uint32_t i;
10564cfd3f9SKuo-Jung Su
10664cfd3f9SKuo-Jung Su chip->state = USB_STATE_POWERED;
10764cfd3f9SKuo-Jung Su
10864cfd3f9SKuo-Jung Su /* chip enable */
10964cfd3f9SKuo-Jung Su writel(DEVCTRL_EN, ®s->dev_ctrl);
11064cfd3f9SKuo-Jung Su
11164cfd3f9SKuo-Jung Su /* device address reset */
11264cfd3f9SKuo-Jung Su chip->addr = 0;
11364cfd3f9SKuo-Jung Su writel(0, ®s->dev_addr);
11464cfd3f9SKuo-Jung Su
11564cfd3f9SKuo-Jung Su /* set idle counter to 7ms */
11664cfd3f9SKuo-Jung Su writel(7, ®s->idle);
11764cfd3f9SKuo-Jung Su
11864cfd3f9SKuo-Jung Su /* disable all interrupts */
11964cfd3f9SKuo-Jung Su writel(IMR_MASK, ®s->imr);
12064cfd3f9SKuo-Jung Su writel(GIMR_MASK, ®s->gimr);
12164cfd3f9SKuo-Jung Su writel(GIMR0_MASK, ®s->gimr0);
12264cfd3f9SKuo-Jung Su writel(GIMR1_MASK, ®s->gimr1);
12364cfd3f9SKuo-Jung Su writel(GIMR2_MASK, ®s->gimr2);
12464cfd3f9SKuo-Jung Su
12564cfd3f9SKuo-Jung Su /* clear interrupts */
12664cfd3f9SKuo-Jung Su writel(ISR_MASK, ®s->isr);
12764cfd3f9SKuo-Jung Su writel(0, ®s->gisr);
12864cfd3f9SKuo-Jung Su writel(0, ®s->gisr0);
12964cfd3f9SKuo-Jung Su writel(0, ®s->gisr1);
13064cfd3f9SKuo-Jung Su writel(0, ®s->gisr2);
13164cfd3f9SKuo-Jung Su
13264cfd3f9SKuo-Jung Su /* chip reset */
13364cfd3f9SKuo-Jung Su setbits_le32(®s->dev_ctrl, DEVCTRL_RESET);
13464cfd3f9SKuo-Jung Su mdelay(10);
13564cfd3f9SKuo-Jung Su if (readl(®s->dev_ctrl) & DEVCTRL_RESET) {
13664cfd3f9SKuo-Jung Su printf("fotg210: chip reset failed\n");
13764cfd3f9SKuo-Jung Su return -1;
13864cfd3f9SKuo-Jung Su }
13964cfd3f9SKuo-Jung Su
14064cfd3f9SKuo-Jung Su /* CX FIFO reset */
14164cfd3f9SKuo-Jung Su setbits_le32(®s->cxfifo, CXFIFO_CXFIFOCLR);
14264cfd3f9SKuo-Jung Su mdelay(10);
14364cfd3f9SKuo-Jung Su if (readl(®s->cxfifo) & CXFIFO_CXFIFOCLR) {
14464cfd3f9SKuo-Jung Su printf("fotg210: ep0 fifo reset failed\n");
14564cfd3f9SKuo-Jung Su return -1;
14664cfd3f9SKuo-Jung Su }
14764cfd3f9SKuo-Jung Su
14864cfd3f9SKuo-Jung Su /* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */
14964cfd3f9SKuo-Jung Su writel(EPMAP14_DEFAULT, ®s->epmap14);
15064cfd3f9SKuo-Jung Su writel(EPMAP58_DEFAULT, ®s->epmap58);
15164cfd3f9SKuo-Jung Su writel(FIFOMAP_DEFAULT, ®s->fifomap);
15264cfd3f9SKuo-Jung Su writel(0, ®s->fifocfg);
15364cfd3f9SKuo-Jung Su for (i = 0; i < 8; ++i) {
15464cfd3f9SKuo-Jung Su writel(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]);
15564cfd3f9SKuo-Jung Su writel(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]);
15664cfd3f9SKuo-Jung Su }
15764cfd3f9SKuo-Jung Su
15864cfd3f9SKuo-Jung Su /* FIFO reset */
15964cfd3f9SKuo-Jung Su for (i = 0; i < 4; ++i) {
16064cfd3f9SKuo-Jung Su writel(FIFOCSR_RESET, ®s->fifocsr[i]);
16164cfd3f9SKuo-Jung Su mdelay(10);
16264cfd3f9SKuo-Jung Su if (readl(®s->fifocsr[i]) & FIFOCSR_RESET) {
16364cfd3f9SKuo-Jung Su printf("fotg210: fifo%d reset failed\n", i);
16464cfd3f9SKuo-Jung Su return -1;
16564cfd3f9SKuo-Jung Su }
16664cfd3f9SKuo-Jung Su }
16764cfd3f9SKuo-Jung Su
16864cfd3f9SKuo-Jung Su /* enable only device interrupt and triggered at level-high */
16964cfd3f9SKuo-Jung Su writel(IMR_IRQLH | IMR_HOST | IMR_OTG, ®s->imr);
17064cfd3f9SKuo-Jung Su writel(ISR_MASK, ®s->isr);
17164cfd3f9SKuo-Jung Su /* disable EP0 IN/OUT interrupt */
17264cfd3f9SKuo-Jung Su writel(GIMR0_CXOUT | GIMR0_CXIN, ®s->gimr0);
17364cfd3f9SKuo-Jung Su /* disable EPX IN+SPK+OUT interrupts */
17464cfd3f9SKuo-Jung Su writel(GIMR1_MASK, ®s->gimr1);
17564cfd3f9SKuo-Jung Su /* disable wakeup+idle+dma+zlp interrupts */
17664cfd3f9SKuo-Jung Su writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN
17764cfd3f9SKuo-Jung Su | GIMR2_ZLPRX | GIMR2_ZLPTX, ®s->gimr2);
17864cfd3f9SKuo-Jung Su /* enable all group interrupt */
17964cfd3f9SKuo-Jung Su writel(0, ®s->gimr);
18064cfd3f9SKuo-Jung Su
18164cfd3f9SKuo-Jung Su /* suspend delay = 3 ms */
18264cfd3f9SKuo-Jung Su writel(3, ®s->idle);
18364cfd3f9SKuo-Jung Su
18464cfd3f9SKuo-Jung Su /* turn-on device interrupts */
18564cfd3f9SKuo-Jung Su setbits_le32(®s->dev_ctrl, DEVCTRL_GIRQ_EN);
18664cfd3f9SKuo-Jung Su
18764cfd3f9SKuo-Jung Su return 0;
18864cfd3f9SKuo-Jung Su }
18964cfd3f9SKuo-Jung Su
fotg210_cxwait(struct fotg210_chip * chip,uint32_t mask)19064cfd3f9SKuo-Jung Su static inline int fotg210_cxwait(struct fotg210_chip *chip, uint32_t mask)
19164cfd3f9SKuo-Jung Su {
19264cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
19364cfd3f9SKuo-Jung Su int ret = -1;
19464cfd3f9SKuo-Jung Su ulong ts;
19564cfd3f9SKuo-Jung Su
19664cfd3f9SKuo-Jung Su for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
19764cfd3f9SKuo-Jung Su if ((readl(®s->cxfifo) & mask) != mask)
19864cfd3f9SKuo-Jung Su continue;
19964cfd3f9SKuo-Jung Su ret = 0;
20064cfd3f9SKuo-Jung Su break;
20164cfd3f9SKuo-Jung Su }
20264cfd3f9SKuo-Jung Su
20364cfd3f9SKuo-Jung Su if (ret)
20464cfd3f9SKuo-Jung Su printf("fotg210: cx/ep0 timeout\n");
20564cfd3f9SKuo-Jung Su
20664cfd3f9SKuo-Jung Su return ret;
20764cfd3f9SKuo-Jung Su }
20864cfd3f9SKuo-Jung Su
fotg210_dma(struct fotg210_ep * ep,struct fotg210_request * req)20964cfd3f9SKuo-Jung Su static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req)
21064cfd3f9SKuo-Jung Su {
21164cfd3f9SKuo-Jung Su struct fotg210_chip *chip = ep->chip;
21264cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
21364cfd3f9SKuo-Jung Su uint32_t tmp, ts;
21464cfd3f9SKuo-Jung Su uint8_t *buf = req->req.buf + req->req.actual;
21564cfd3f9SKuo-Jung Su uint32_t len = req->req.length - req->req.actual;
21664cfd3f9SKuo-Jung Su int fifo = ep_to_fifo(chip, ep->id);
21764cfd3f9SKuo-Jung Su int ret = -EBUSY;
21864cfd3f9SKuo-Jung Su
21964cfd3f9SKuo-Jung Su /* 1. init dma buffer */
22064cfd3f9SKuo-Jung Su if (len > ep->maxpacket)
22164cfd3f9SKuo-Jung Su len = ep->maxpacket;
22264cfd3f9SKuo-Jung Su
22364cfd3f9SKuo-Jung Su /* 2. wait for dma ready (hardware) */
22464cfd3f9SKuo-Jung Su for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
22564cfd3f9SKuo-Jung Su if (!(readl(®s->dma_ctrl) & DMACTRL_START)) {
22664cfd3f9SKuo-Jung Su ret = 0;
22764cfd3f9SKuo-Jung Su break;
22864cfd3f9SKuo-Jung Su }
22964cfd3f9SKuo-Jung Su }
23064cfd3f9SKuo-Jung Su if (ret) {
23164cfd3f9SKuo-Jung Su printf("fotg210: dma busy\n");
23264cfd3f9SKuo-Jung Su req->req.status = ret;
23364cfd3f9SKuo-Jung Su return ret;
23464cfd3f9SKuo-Jung Su }
23564cfd3f9SKuo-Jung Su
23664cfd3f9SKuo-Jung Su /* 3. DMA target setup */
23764cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN)
23864cfd3f9SKuo-Jung Su flush_dcache_range((ulong)buf, (ulong)buf + len);
23964cfd3f9SKuo-Jung Su else
24064cfd3f9SKuo-Jung Su invalidate_dcache_range((ulong)buf, (ulong)buf + len);
24164cfd3f9SKuo-Jung Su
24264cfd3f9SKuo-Jung Su writel(virt_to_phys(buf), ®s->dma_addr);
24364cfd3f9SKuo-Jung Su
24464cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN) {
24564cfd3f9SKuo-Jung Su if (ep->id == 0) {
24664cfd3f9SKuo-Jung Su /* Wait until cx/ep0 fifo empty */
24764cfd3f9SKuo-Jung Su fotg210_cxwait(chip, CXFIFO_CXFIFOE);
248dcad2800SKuo-Jung Su udelay(1);
24964cfd3f9SKuo-Jung Su writel(DMAFIFO_CX, ®s->dma_fifo);
25064cfd3f9SKuo-Jung Su } else {
25164cfd3f9SKuo-Jung Su /* Wait until epx fifo empty */
25264cfd3f9SKuo-Jung Su fotg210_cxwait(chip, CXFIFO_FIFOE(fifo));
25364cfd3f9SKuo-Jung Su writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo);
25464cfd3f9SKuo-Jung Su }
25564cfd3f9SKuo-Jung Su writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, ®s->dma_ctrl);
25664cfd3f9SKuo-Jung Su } else {
25764cfd3f9SKuo-Jung Su uint32_t blen;
25864cfd3f9SKuo-Jung Su
25964cfd3f9SKuo-Jung Su if (ep->id == 0) {
26064cfd3f9SKuo-Jung Su writel(DMAFIFO_CX, ®s->dma_fifo);
26164cfd3f9SKuo-Jung Su do {
26264cfd3f9SKuo-Jung Su blen = CXFIFO_BYTES(readl(®s->cxfifo));
26364cfd3f9SKuo-Jung Su } while (blen < len);
26464cfd3f9SKuo-Jung Su } else {
26564cfd3f9SKuo-Jung Su writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo);
26664cfd3f9SKuo-Jung Su blen = FIFOCSR_BYTES(readl(®s->fifocsr[fifo]));
26764cfd3f9SKuo-Jung Su }
26864cfd3f9SKuo-Jung Su len = (len < blen) ? len : blen;
26964cfd3f9SKuo-Jung Su writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, ®s->dma_ctrl);
27064cfd3f9SKuo-Jung Su }
27164cfd3f9SKuo-Jung Su
27264cfd3f9SKuo-Jung Su /* 4. DMA start */
27364cfd3f9SKuo-Jung Su setbits_le32(®s->dma_ctrl, DMACTRL_START);
27464cfd3f9SKuo-Jung Su
27564cfd3f9SKuo-Jung Su /* 5. DMA wait */
27664cfd3f9SKuo-Jung Su ret = -EBUSY;
27764cfd3f9SKuo-Jung Su for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
27864cfd3f9SKuo-Jung Su tmp = readl(®s->gisr2);
27964cfd3f9SKuo-Jung Su /* DMA complete */
28064cfd3f9SKuo-Jung Su if (tmp & GISR2_DMAFIN) {
28164cfd3f9SKuo-Jung Su ret = 0;
28264cfd3f9SKuo-Jung Su break;
28364cfd3f9SKuo-Jung Su }
28464cfd3f9SKuo-Jung Su /* DMA error */
28564cfd3f9SKuo-Jung Su if (tmp & GISR2_DMAERR) {
28664cfd3f9SKuo-Jung Su printf("fotg210: dma error\n");
28764cfd3f9SKuo-Jung Su break;
28864cfd3f9SKuo-Jung Su }
28964cfd3f9SKuo-Jung Su /* resume, suspend, reset */
29064cfd3f9SKuo-Jung Su if (tmp & (GISR2_RESUME | GISR2_SUSPEND | GISR2_RESET)) {
29164cfd3f9SKuo-Jung Su printf("fotg210: dma reset by host\n");
29264cfd3f9SKuo-Jung Su break;
29364cfd3f9SKuo-Jung Su }
29464cfd3f9SKuo-Jung Su }
29564cfd3f9SKuo-Jung Su
29664cfd3f9SKuo-Jung Su /* 7. DMA target reset */
29764cfd3f9SKuo-Jung Su if (ret)
29864cfd3f9SKuo-Jung Su writel(DMACTRL_ABORT | DMACTRL_CLRFF, ®s->dma_ctrl);
29964cfd3f9SKuo-Jung Su
30064cfd3f9SKuo-Jung Su writel(0, ®s->gisr2);
30164cfd3f9SKuo-Jung Su writel(0, ®s->dma_fifo);
30264cfd3f9SKuo-Jung Su
30364cfd3f9SKuo-Jung Su req->req.status = ret;
30464cfd3f9SKuo-Jung Su if (!ret)
30564cfd3f9SKuo-Jung Su req->req.actual += len;
30664cfd3f9SKuo-Jung Su else
30764cfd3f9SKuo-Jung Su printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret);
30864cfd3f9SKuo-Jung Su
30964cfd3f9SKuo-Jung Su return len;
31064cfd3f9SKuo-Jung Su }
31164cfd3f9SKuo-Jung Su
31264cfd3f9SKuo-Jung Su /*
31364cfd3f9SKuo-Jung Su * result of setup packet
31464cfd3f9SKuo-Jung Su */
31564cfd3f9SKuo-Jung Su #define CX_IDLE 0
31664cfd3f9SKuo-Jung Su #define CX_FINISH 1
31764cfd3f9SKuo-Jung Su #define CX_STALL 2
31864cfd3f9SKuo-Jung Su
fotg210_setup(struct fotg210_chip * chip)31964cfd3f9SKuo-Jung Su static void fotg210_setup(struct fotg210_chip *chip)
32064cfd3f9SKuo-Jung Su {
32164cfd3f9SKuo-Jung Su int id, ret = CX_IDLE;
32264cfd3f9SKuo-Jung Su uint32_t tmp[2];
32364cfd3f9SKuo-Jung Su struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp;
32464cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
32564cfd3f9SKuo-Jung Su
32664cfd3f9SKuo-Jung Su /*
32764cfd3f9SKuo-Jung Su * If this is the first Cx 8 byte command,
32864cfd3f9SKuo-Jung Su * we can now query USB mode (high/full speed; USB 2.0/USB 1.0)
32964cfd3f9SKuo-Jung Su */
33064cfd3f9SKuo-Jung Su if (chip->state == USB_STATE_POWERED) {
33164cfd3f9SKuo-Jung Su chip->state = USB_STATE_DEFAULT;
33264cfd3f9SKuo-Jung Su if (readl(®s->otgcsr) & OTGCSR_DEV_B) {
33364cfd3f9SKuo-Jung Su /* Mini-B */
33464cfd3f9SKuo-Jung Su if (readl(®s->dev_ctrl) & DEVCTRL_HS) {
33564cfd3f9SKuo-Jung Su puts("fotg210: HS\n");
33664cfd3f9SKuo-Jung Su chip->gadget.speed = USB_SPEED_HIGH;
33764cfd3f9SKuo-Jung Su /* SOF mask timer = 1100 ticks */
33864cfd3f9SKuo-Jung Su writel(SOFMTR_TMR(1100), ®s->sof_mtr);
33964cfd3f9SKuo-Jung Su } else {
34064cfd3f9SKuo-Jung Su puts("fotg210: FS\n");
34164cfd3f9SKuo-Jung Su chip->gadget.speed = USB_SPEED_FULL;
34264cfd3f9SKuo-Jung Su /* SOF mask timer = 10000 ticks */
34364cfd3f9SKuo-Jung Su writel(SOFMTR_TMR(10000), ®s->sof_mtr);
34464cfd3f9SKuo-Jung Su }
34564cfd3f9SKuo-Jung Su } else {
34664cfd3f9SKuo-Jung Su printf("fotg210: mini-A?\n");
34764cfd3f9SKuo-Jung Su }
34864cfd3f9SKuo-Jung Su }
34964cfd3f9SKuo-Jung Su
35064cfd3f9SKuo-Jung Su /* switch data port to ep0 */
35164cfd3f9SKuo-Jung Su writel(DMAFIFO_CX, ®s->dma_fifo);
35264cfd3f9SKuo-Jung Su /* fetch 8 bytes setup packet */
35364cfd3f9SKuo-Jung Su tmp[0] = readl(®s->ep0_data);
35464cfd3f9SKuo-Jung Su tmp[1] = readl(®s->ep0_data);
35564cfd3f9SKuo-Jung Su /* release data port */
35664cfd3f9SKuo-Jung Su writel(0, ®s->dma_fifo);
35764cfd3f9SKuo-Jung Su
35864cfd3f9SKuo-Jung Su if (req->bRequestType & USB_DIR_IN)
35964cfd3f9SKuo-Jung Su ep0_desc.bEndpointAddress = USB_DIR_IN;
36064cfd3f9SKuo-Jung Su else
36164cfd3f9SKuo-Jung Su ep0_desc.bEndpointAddress = USB_DIR_OUT;
36264cfd3f9SKuo-Jung Su
36364cfd3f9SKuo-Jung Su ret = CX_IDLE;
36464cfd3f9SKuo-Jung Su
36564cfd3f9SKuo-Jung Su if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
36664cfd3f9SKuo-Jung Su switch (req->bRequest) {
36764cfd3f9SKuo-Jung Su case USB_REQ_SET_CONFIGURATION:
36864cfd3f9SKuo-Jung Su debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF);
36964cfd3f9SKuo-Jung Su if (!(req->wValue & 0x00FF)) {
37064cfd3f9SKuo-Jung Su chip->state = USB_STATE_ADDRESS;
37164cfd3f9SKuo-Jung Su writel(chip->addr, ®s->dev_addr);
37264cfd3f9SKuo-Jung Su } else {
37364cfd3f9SKuo-Jung Su chip->state = USB_STATE_CONFIGURED;
37464cfd3f9SKuo-Jung Su writel(chip->addr | DEVADDR_CONF,
37564cfd3f9SKuo-Jung Su ®s->dev_addr);
37664cfd3f9SKuo-Jung Su }
37764cfd3f9SKuo-Jung Su ret = CX_IDLE;
37864cfd3f9SKuo-Jung Su break;
37964cfd3f9SKuo-Jung Su
38064cfd3f9SKuo-Jung Su case USB_REQ_SET_ADDRESS:
38164cfd3f9SKuo-Jung Su debug("fotg210: set_addr(0x%04X)\n", req->wValue);
38264cfd3f9SKuo-Jung Su chip->state = USB_STATE_ADDRESS;
38364cfd3f9SKuo-Jung Su chip->addr = req->wValue & DEVADDR_ADDR_MASK;
38464cfd3f9SKuo-Jung Su ret = CX_FINISH;
38564cfd3f9SKuo-Jung Su writel(chip->addr, ®s->dev_addr);
38664cfd3f9SKuo-Jung Su break;
38764cfd3f9SKuo-Jung Su
38864cfd3f9SKuo-Jung Su case USB_REQ_CLEAR_FEATURE:
38964cfd3f9SKuo-Jung Su debug("fotg210: clr_feature(%d, %d)\n",
39064cfd3f9SKuo-Jung Su req->bRequestType & 0x03, req->wValue);
39164cfd3f9SKuo-Jung Su switch (req->wValue) {
39264cfd3f9SKuo-Jung Su case 0: /* [Endpoint] halt */
39364cfd3f9SKuo-Jung Su ep_reset(chip, req->wIndex);
39464cfd3f9SKuo-Jung Su ret = CX_FINISH;
39564cfd3f9SKuo-Jung Su break;
39664cfd3f9SKuo-Jung Su case 1: /* [Device] remote wake-up */
39764cfd3f9SKuo-Jung Su case 2: /* [Device] test mode */
39864cfd3f9SKuo-Jung Su default:
39964cfd3f9SKuo-Jung Su ret = CX_STALL;
40064cfd3f9SKuo-Jung Su break;
40164cfd3f9SKuo-Jung Su }
40264cfd3f9SKuo-Jung Su break;
40364cfd3f9SKuo-Jung Su
40464cfd3f9SKuo-Jung Su case USB_REQ_SET_FEATURE:
40564cfd3f9SKuo-Jung Su debug("fotg210: set_feature(%d, %d)\n",
40664cfd3f9SKuo-Jung Su req->wValue, req->wIndex & 0xf);
40764cfd3f9SKuo-Jung Su switch (req->wValue) {
40864cfd3f9SKuo-Jung Su case 0: /* Endpoint Halt */
40964cfd3f9SKuo-Jung Su id = req->wIndex & 0xf;
41064cfd3f9SKuo-Jung Su setbits_le32(®s->iep[id - 1], IEP_STALL);
41164cfd3f9SKuo-Jung Su setbits_le32(®s->oep[id - 1], OEP_STALL);
41264cfd3f9SKuo-Jung Su ret = CX_FINISH;
41364cfd3f9SKuo-Jung Su break;
41464cfd3f9SKuo-Jung Su case 1: /* Remote Wakeup */
41564cfd3f9SKuo-Jung Su case 2: /* Test Mode */
41664cfd3f9SKuo-Jung Su default:
41764cfd3f9SKuo-Jung Su ret = CX_STALL;
41864cfd3f9SKuo-Jung Su break;
41964cfd3f9SKuo-Jung Su }
42064cfd3f9SKuo-Jung Su break;
42164cfd3f9SKuo-Jung Su
42264cfd3f9SKuo-Jung Su case USB_REQ_GET_STATUS:
42364cfd3f9SKuo-Jung Su debug("fotg210: get_status\n");
42464cfd3f9SKuo-Jung Su ret = CX_STALL;
42564cfd3f9SKuo-Jung Su break;
42664cfd3f9SKuo-Jung Su
42764cfd3f9SKuo-Jung Su case USB_REQ_SET_DESCRIPTOR:
42864cfd3f9SKuo-Jung Su debug("fotg210: set_descriptor\n");
42964cfd3f9SKuo-Jung Su ret = CX_STALL;
43064cfd3f9SKuo-Jung Su break;
43164cfd3f9SKuo-Jung Su
43264cfd3f9SKuo-Jung Su case USB_REQ_SYNCH_FRAME:
43364cfd3f9SKuo-Jung Su debug("fotg210: sync frame\n");
43464cfd3f9SKuo-Jung Su ret = CX_STALL;
43564cfd3f9SKuo-Jung Su break;
43664cfd3f9SKuo-Jung Su }
43764cfd3f9SKuo-Jung Su } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */
43864cfd3f9SKuo-Jung Su
43964cfd3f9SKuo-Jung Su if (ret == CX_IDLE && chip->driver->setup) {
44064cfd3f9SKuo-Jung Su if (chip->driver->setup(&chip->gadget, req) < 0)
44164cfd3f9SKuo-Jung Su ret = CX_STALL;
44264cfd3f9SKuo-Jung Su else
44364cfd3f9SKuo-Jung Su ret = CX_FINISH;
44464cfd3f9SKuo-Jung Su }
44564cfd3f9SKuo-Jung Su
44664cfd3f9SKuo-Jung Su switch (ret) {
44764cfd3f9SKuo-Jung Su case CX_FINISH:
44864cfd3f9SKuo-Jung Su setbits_le32(®s->cxfifo, CXFIFO_CXFIN);
44964cfd3f9SKuo-Jung Su break;
45064cfd3f9SKuo-Jung Su
45164cfd3f9SKuo-Jung Su case CX_STALL:
45264cfd3f9SKuo-Jung Su setbits_le32(®s->cxfifo, CXFIFO_CXSTALL | CXFIFO_CXFIN);
45364cfd3f9SKuo-Jung Su printf("fotg210: cx_stall!\n");
45464cfd3f9SKuo-Jung Su break;
45564cfd3f9SKuo-Jung Su
45664cfd3f9SKuo-Jung Su case CX_IDLE:
45764cfd3f9SKuo-Jung Su debug("fotg210: cx_idle?\n");
45864cfd3f9SKuo-Jung Su default:
45964cfd3f9SKuo-Jung Su break;
46064cfd3f9SKuo-Jung Su }
46164cfd3f9SKuo-Jung Su }
46264cfd3f9SKuo-Jung Su
46364cfd3f9SKuo-Jung Su /*
46464cfd3f9SKuo-Jung Su * fifo - FIFO id
46564cfd3f9SKuo-Jung Su * zlp - zero length packet
46664cfd3f9SKuo-Jung Su */
fotg210_recv(struct fotg210_chip * chip,int ep_id)46764cfd3f9SKuo-Jung Su static void fotg210_recv(struct fotg210_chip *chip, int ep_id)
46864cfd3f9SKuo-Jung Su {
46964cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
47064cfd3f9SKuo-Jung Su struct fotg210_ep *ep = chip->ep + ep_id;
47164cfd3f9SKuo-Jung Su struct fotg210_request *req;
47264cfd3f9SKuo-Jung Su int len;
47364cfd3f9SKuo-Jung Su
47464cfd3f9SKuo-Jung Su if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) {
47564cfd3f9SKuo-Jung Su printf("fotg210: ep%d recv, invalid!\n", ep->id);
47664cfd3f9SKuo-Jung Su return;
47764cfd3f9SKuo-Jung Su }
47864cfd3f9SKuo-Jung Su
47964cfd3f9SKuo-Jung Su if (list_empty(&ep->queue)) {
48064cfd3f9SKuo-Jung Su printf("fotg210: ep%d recv, drop!\n", ep->id);
48164cfd3f9SKuo-Jung Su return;
48264cfd3f9SKuo-Jung Su }
48364cfd3f9SKuo-Jung Su
48464cfd3f9SKuo-Jung Su req = list_first_entry(&ep->queue, struct fotg210_request, queue);
48564cfd3f9SKuo-Jung Su len = fotg210_dma(ep, req);
48664cfd3f9SKuo-Jung Su if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) {
48764cfd3f9SKuo-Jung Su list_del_init(&req->queue);
48864cfd3f9SKuo-Jung Su if (req->req.complete)
48964cfd3f9SKuo-Jung Su req->req.complete(&ep->ep, &req->req);
49064cfd3f9SKuo-Jung Su }
49164cfd3f9SKuo-Jung Su
49264cfd3f9SKuo-Jung Su if (ep->id > 0 && list_empty(&ep->queue)) {
49364cfd3f9SKuo-Jung Su setbits_le32(®s->gimr1,
49464cfd3f9SKuo-Jung Su GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id)));
49564cfd3f9SKuo-Jung Su }
49664cfd3f9SKuo-Jung Su }
49764cfd3f9SKuo-Jung Su
49864cfd3f9SKuo-Jung Su /*
49964cfd3f9SKuo-Jung Su * USB Gadget Layer
50064cfd3f9SKuo-Jung Su */
fotg210_ep_enable(struct usb_ep * _ep,const struct usb_endpoint_descriptor * desc)50164cfd3f9SKuo-Jung Su static int fotg210_ep_enable(
50264cfd3f9SKuo-Jung Su struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
50364cfd3f9SKuo-Jung Su {
50464cfd3f9SKuo-Jung Su struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
50564cfd3f9SKuo-Jung Su struct fotg210_chip *chip = ep->chip;
50664cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
50764cfd3f9SKuo-Jung Su int id = ep_to_fifo(chip, ep->id);
50864cfd3f9SKuo-Jung Su int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
50964cfd3f9SKuo-Jung Su
51064cfd3f9SKuo-Jung Su if (!_ep || !desc
51164cfd3f9SKuo-Jung Su || desc->bDescriptorType != USB_DT_ENDPOINT
51264cfd3f9SKuo-Jung Su || le16_to_cpu(desc->wMaxPacketSize) == 0) {
51364cfd3f9SKuo-Jung Su printf("fotg210: bad ep or descriptor\n");
51464cfd3f9SKuo-Jung Su return -EINVAL;
51564cfd3f9SKuo-Jung Su }
51664cfd3f9SKuo-Jung Su
51764cfd3f9SKuo-Jung Su ep->desc = desc;
51864cfd3f9SKuo-Jung Su ep->stopped = 0;
51964cfd3f9SKuo-Jung Su
52064cfd3f9SKuo-Jung Su if (in)
52164cfd3f9SKuo-Jung Su setbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_IN));
52264cfd3f9SKuo-Jung Su
52364cfd3f9SKuo-Jung Su switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
52464cfd3f9SKuo-Jung Su case USB_ENDPOINT_XFER_CONTROL:
52564cfd3f9SKuo-Jung Su return -EINVAL;
52664cfd3f9SKuo-Jung Su
52764cfd3f9SKuo-Jung Su case USB_ENDPOINT_XFER_ISOC:
52864cfd3f9SKuo-Jung Su setbits_le32(®s->fifocfg,
52964cfd3f9SKuo-Jung Su FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC));
53064cfd3f9SKuo-Jung Su break;
53164cfd3f9SKuo-Jung Su
53264cfd3f9SKuo-Jung Su case USB_ENDPOINT_XFER_BULK:
53364cfd3f9SKuo-Jung Su setbits_le32(®s->fifocfg,
53464cfd3f9SKuo-Jung Su FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK));
53564cfd3f9SKuo-Jung Su break;
53664cfd3f9SKuo-Jung Su
53764cfd3f9SKuo-Jung Su case USB_ENDPOINT_XFER_INT:
53864cfd3f9SKuo-Jung Su setbits_le32(®s->fifocfg,
53964cfd3f9SKuo-Jung Su FIFOCFG(id, FIFOCFG_EN | FIFOCFG_INTR));
54064cfd3f9SKuo-Jung Su break;
54164cfd3f9SKuo-Jung Su }
54264cfd3f9SKuo-Jung Su
54364cfd3f9SKuo-Jung Su return 0;
54464cfd3f9SKuo-Jung Su }
54564cfd3f9SKuo-Jung Su
fotg210_ep_disable(struct usb_ep * _ep)54664cfd3f9SKuo-Jung Su static int fotg210_ep_disable(struct usb_ep *_ep)
54764cfd3f9SKuo-Jung Su {
54864cfd3f9SKuo-Jung Su struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
54964cfd3f9SKuo-Jung Su struct fotg210_chip *chip = ep->chip;
55064cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
55164cfd3f9SKuo-Jung Su int id = ep_to_fifo(chip, ep->id);
55264cfd3f9SKuo-Jung Su
55364cfd3f9SKuo-Jung Su ep->desc = NULL;
55464cfd3f9SKuo-Jung Su ep->stopped = 1;
55564cfd3f9SKuo-Jung Su
55664cfd3f9SKuo-Jung Su clrbits_le32(®s->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK));
55764cfd3f9SKuo-Jung Su clrbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_DIR_MASK));
55864cfd3f9SKuo-Jung Su
55964cfd3f9SKuo-Jung Su return 0;
56064cfd3f9SKuo-Jung Su }
56164cfd3f9SKuo-Jung Su
fotg210_ep_alloc_request(struct usb_ep * _ep,gfp_t gfp_flags)56264cfd3f9SKuo-Jung Su static struct usb_request *fotg210_ep_alloc_request(
56364cfd3f9SKuo-Jung Su struct usb_ep *_ep, gfp_t gfp_flags)
56464cfd3f9SKuo-Jung Su {
56564cfd3f9SKuo-Jung Su struct fotg210_request *req = malloc(sizeof(*req));
56664cfd3f9SKuo-Jung Su
56764cfd3f9SKuo-Jung Su if (req) {
56864cfd3f9SKuo-Jung Su memset(req, 0, sizeof(*req));
56964cfd3f9SKuo-Jung Su INIT_LIST_HEAD(&req->queue);
57064cfd3f9SKuo-Jung Su }
57164cfd3f9SKuo-Jung Su return &req->req;
57264cfd3f9SKuo-Jung Su }
57364cfd3f9SKuo-Jung Su
fotg210_ep_free_request(struct usb_ep * _ep,struct usb_request * _req)57464cfd3f9SKuo-Jung Su static void fotg210_ep_free_request(
57564cfd3f9SKuo-Jung Su struct usb_ep *_ep, struct usb_request *_req)
57664cfd3f9SKuo-Jung Su {
57764cfd3f9SKuo-Jung Su struct fotg210_request *req;
57864cfd3f9SKuo-Jung Su
57964cfd3f9SKuo-Jung Su req = container_of(_req, struct fotg210_request, req);
58064cfd3f9SKuo-Jung Su free(req);
58164cfd3f9SKuo-Jung Su }
58264cfd3f9SKuo-Jung Su
fotg210_ep_queue(struct usb_ep * _ep,struct usb_request * _req,gfp_t gfp_flags)58364cfd3f9SKuo-Jung Su static int fotg210_ep_queue(
58464cfd3f9SKuo-Jung Su struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
58564cfd3f9SKuo-Jung Su {
58664cfd3f9SKuo-Jung Su struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
58764cfd3f9SKuo-Jung Su struct fotg210_chip *chip = ep->chip;
58864cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
58964cfd3f9SKuo-Jung Su struct fotg210_request *req;
59064cfd3f9SKuo-Jung Su
59164cfd3f9SKuo-Jung Su req = container_of(_req, struct fotg210_request, req);
59264cfd3f9SKuo-Jung Su if (!_req || !_req->complete || !_req->buf
59364cfd3f9SKuo-Jung Su || !list_empty(&req->queue)) {
59464cfd3f9SKuo-Jung Su printf("fotg210: invalid request to ep%d\n", ep->id);
59564cfd3f9SKuo-Jung Su return -EINVAL;
59664cfd3f9SKuo-Jung Su }
59764cfd3f9SKuo-Jung Su
59864cfd3f9SKuo-Jung Su if (!chip || chip->state == USB_STATE_SUSPENDED) {
59964cfd3f9SKuo-Jung Su printf("fotg210: request while chip suspended\n");
60064cfd3f9SKuo-Jung Su return -EINVAL;
60164cfd3f9SKuo-Jung Su }
60264cfd3f9SKuo-Jung Su
60364cfd3f9SKuo-Jung Su req->req.actual = 0;
60464cfd3f9SKuo-Jung Su req->req.status = -EINPROGRESS;
60564cfd3f9SKuo-Jung Su
60664cfd3f9SKuo-Jung Su if (req->req.length == 0) {
60764cfd3f9SKuo-Jung Su req->req.status = 0;
60864cfd3f9SKuo-Jung Su if (req->req.complete)
60964cfd3f9SKuo-Jung Su req->req.complete(&ep->ep, &req->req);
61064cfd3f9SKuo-Jung Su return 0;
61164cfd3f9SKuo-Jung Su }
61264cfd3f9SKuo-Jung Su
61364cfd3f9SKuo-Jung Su if (ep->id == 0) {
61464cfd3f9SKuo-Jung Su do {
61564cfd3f9SKuo-Jung Su int len = fotg210_dma(ep, req);
61664cfd3f9SKuo-Jung Su if (len < ep->ep.maxpacket)
61764cfd3f9SKuo-Jung Su break;
61864cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN)
61964cfd3f9SKuo-Jung Su udelay(100);
62064cfd3f9SKuo-Jung Su } while (req->req.length > req->req.actual);
62164cfd3f9SKuo-Jung Su } else {
62264cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN) {
62364cfd3f9SKuo-Jung Su do {
62464cfd3f9SKuo-Jung Su int len = fotg210_dma(ep, req);
62564cfd3f9SKuo-Jung Su if (len < ep->ep.maxpacket)
62664cfd3f9SKuo-Jung Su break;
62764cfd3f9SKuo-Jung Su } while (req->req.length > req->req.actual);
62864cfd3f9SKuo-Jung Su } else {
62964cfd3f9SKuo-Jung Su list_add_tail(&req->queue, &ep->queue);
63064cfd3f9SKuo-Jung Su clrbits_le32(®s->gimr1,
63164cfd3f9SKuo-Jung Su GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id)));
63264cfd3f9SKuo-Jung Su }
63364cfd3f9SKuo-Jung Su }
63464cfd3f9SKuo-Jung Su
63564cfd3f9SKuo-Jung Su if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) {
63664cfd3f9SKuo-Jung Su if (req->req.complete)
63764cfd3f9SKuo-Jung Su req->req.complete(&ep->ep, &req->req);
63864cfd3f9SKuo-Jung Su }
63964cfd3f9SKuo-Jung Su
64064cfd3f9SKuo-Jung Su return 0;
64164cfd3f9SKuo-Jung Su }
64264cfd3f9SKuo-Jung Su
fotg210_ep_dequeue(struct usb_ep * _ep,struct usb_request * _req)64364cfd3f9SKuo-Jung Su static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
64464cfd3f9SKuo-Jung Su {
64564cfd3f9SKuo-Jung Su struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
64664cfd3f9SKuo-Jung Su struct fotg210_request *req;
64764cfd3f9SKuo-Jung Su
64864cfd3f9SKuo-Jung Su /* make sure it's actually queued on this endpoint */
64964cfd3f9SKuo-Jung Su list_for_each_entry(req, &ep->queue, queue) {
65064cfd3f9SKuo-Jung Su if (&req->req == _req)
65164cfd3f9SKuo-Jung Su break;
65264cfd3f9SKuo-Jung Su }
65364cfd3f9SKuo-Jung Su if (&req->req != _req)
65464cfd3f9SKuo-Jung Su return -EINVAL;
65564cfd3f9SKuo-Jung Su
65664cfd3f9SKuo-Jung Su /* remove the request */
65764cfd3f9SKuo-Jung Su list_del_init(&req->queue);
65864cfd3f9SKuo-Jung Su
65964cfd3f9SKuo-Jung Su /* update status & invoke complete callback */
66064cfd3f9SKuo-Jung Su if (req->req.status == -EINPROGRESS) {
66164cfd3f9SKuo-Jung Su req->req.status = -ECONNRESET;
66264cfd3f9SKuo-Jung Su if (req->req.complete)
66364cfd3f9SKuo-Jung Su req->req.complete(_ep, &req->req);
66464cfd3f9SKuo-Jung Su }
66564cfd3f9SKuo-Jung Su
66664cfd3f9SKuo-Jung Su return 0;
66764cfd3f9SKuo-Jung Su }
66864cfd3f9SKuo-Jung Su
fotg210_ep_halt(struct usb_ep * _ep,int halt)66964cfd3f9SKuo-Jung Su static int fotg210_ep_halt(struct usb_ep *_ep, int halt)
67064cfd3f9SKuo-Jung Su {
67164cfd3f9SKuo-Jung Su struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
67264cfd3f9SKuo-Jung Su struct fotg210_chip *chip = ep->chip;
67364cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
67464cfd3f9SKuo-Jung Su int ret = -1;
67564cfd3f9SKuo-Jung Su
67664cfd3f9SKuo-Jung Su debug("fotg210: ep%d halt=%d\n", ep->id, halt);
67764cfd3f9SKuo-Jung Su
67864cfd3f9SKuo-Jung Su /* Endpoint STALL */
67964cfd3f9SKuo-Jung Su if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) {
68064cfd3f9SKuo-Jung Su if (halt) {
68164cfd3f9SKuo-Jung Su /* wait until all ep fifo empty */
68264cfd3f9SKuo-Jung Su fotg210_cxwait(chip, 0xf00);
68364cfd3f9SKuo-Jung Su /* stall */
68464cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN) {
68564cfd3f9SKuo-Jung Su setbits_le32(®s->iep[ep->id - 1],
68664cfd3f9SKuo-Jung Su IEP_STALL);
68764cfd3f9SKuo-Jung Su } else {
68864cfd3f9SKuo-Jung Su setbits_le32(®s->oep[ep->id - 1],
68964cfd3f9SKuo-Jung Su OEP_STALL);
69064cfd3f9SKuo-Jung Su }
69164cfd3f9SKuo-Jung Su } else {
69264cfd3f9SKuo-Jung Su if (ep->desc->bEndpointAddress & USB_DIR_IN) {
69364cfd3f9SKuo-Jung Su clrbits_le32(®s->iep[ep->id - 1],
69464cfd3f9SKuo-Jung Su IEP_STALL);
69564cfd3f9SKuo-Jung Su } else {
69664cfd3f9SKuo-Jung Su clrbits_le32(®s->oep[ep->id - 1],
69764cfd3f9SKuo-Jung Su OEP_STALL);
69864cfd3f9SKuo-Jung Su }
69964cfd3f9SKuo-Jung Su }
70064cfd3f9SKuo-Jung Su ret = 0;
70164cfd3f9SKuo-Jung Su }
70264cfd3f9SKuo-Jung Su
70364cfd3f9SKuo-Jung Su return ret;
70464cfd3f9SKuo-Jung Su }
70564cfd3f9SKuo-Jung Su
70664cfd3f9SKuo-Jung Su /*
70764cfd3f9SKuo-Jung Su * activate/deactivate link with host.
70864cfd3f9SKuo-Jung Su */
pullup(struct fotg210_chip * chip,int is_on)70964cfd3f9SKuo-Jung Su static void pullup(struct fotg210_chip *chip, int is_on)
71064cfd3f9SKuo-Jung Su {
71164cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
71264cfd3f9SKuo-Jung Su
71364cfd3f9SKuo-Jung Su if (is_on) {
71464cfd3f9SKuo-Jung Su if (!chip->pullup) {
71564cfd3f9SKuo-Jung Su chip->state = USB_STATE_POWERED;
71664cfd3f9SKuo-Jung Su chip->pullup = 1;
71764cfd3f9SKuo-Jung Su /* enable the chip */
71864cfd3f9SKuo-Jung Su setbits_le32(®s->dev_ctrl, DEVCTRL_EN);
71964cfd3f9SKuo-Jung Su /* clear unplug bit (BIT0) */
72064cfd3f9SKuo-Jung Su clrbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG);
72164cfd3f9SKuo-Jung Su }
72264cfd3f9SKuo-Jung Su } else {
72364cfd3f9SKuo-Jung Su chip->state = USB_STATE_NOTATTACHED;
72464cfd3f9SKuo-Jung Su chip->pullup = 0;
72564cfd3f9SKuo-Jung Su chip->addr = 0;
72664cfd3f9SKuo-Jung Su writel(chip->addr, ®s->dev_addr);
72764cfd3f9SKuo-Jung Su /* set unplug bit (BIT0) */
72864cfd3f9SKuo-Jung Su setbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG);
72964cfd3f9SKuo-Jung Su /* disable the chip */
73064cfd3f9SKuo-Jung Su clrbits_le32(®s->dev_ctrl, DEVCTRL_EN);
73164cfd3f9SKuo-Jung Su }
73264cfd3f9SKuo-Jung Su }
73364cfd3f9SKuo-Jung Su
fotg210_pullup(struct usb_gadget * _gadget,int is_on)73464cfd3f9SKuo-Jung Su static int fotg210_pullup(struct usb_gadget *_gadget, int is_on)
73564cfd3f9SKuo-Jung Su {
73664cfd3f9SKuo-Jung Su struct fotg210_chip *chip;
73764cfd3f9SKuo-Jung Su
73864cfd3f9SKuo-Jung Su chip = container_of(_gadget, struct fotg210_chip, gadget);
73964cfd3f9SKuo-Jung Su
74064cfd3f9SKuo-Jung Su debug("fotg210: pullup=%d\n", is_on);
74164cfd3f9SKuo-Jung Su
74264cfd3f9SKuo-Jung Su pullup(chip, is_on);
74364cfd3f9SKuo-Jung Su
74464cfd3f9SKuo-Jung Su return 0;
74564cfd3f9SKuo-Jung Su }
74664cfd3f9SKuo-Jung Su
fotg210_get_frame(struct usb_gadget * _gadget)74764cfd3f9SKuo-Jung Su static int fotg210_get_frame(struct usb_gadget *_gadget)
74864cfd3f9SKuo-Jung Su {
74964cfd3f9SKuo-Jung Su struct fotg210_chip *chip;
75064cfd3f9SKuo-Jung Su struct fotg210_regs *regs;
75164cfd3f9SKuo-Jung Su
75264cfd3f9SKuo-Jung Su chip = container_of(_gadget, struct fotg210_chip, gadget);
75364cfd3f9SKuo-Jung Su regs = chip->regs;
75464cfd3f9SKuo-Jung Su
75564cfd3f9SKuo-Jung Su return SOFFNR_FNR(readl(®s->sof_fnr));
75664cfd3f9SKuo-Jung Su }
75764cfd3f9SKuo-Jung Su
75864cfd3f9SKuo-Jung Su static struct usb_gadget_ops fotg210_gadget_ops = {
75964cfd3f9SKuo-Jung Su .get_frame = fotg210_get_frame,
76064cfd3f9SKuo-Jung Su .pullup = fotg210_pullup,
76164cfd3f9SKuo-Jung Su };
76264cfd3f9SKuo-Jung Su
76364cfd3f9SKuo-Jung Su static struct usb_ep_ops fotg210_ep_ops = {
76464cfd3f9SKuo-Jung Su .enable = fotg210_ep_enable,
76564cfd3f9SKuo-Jung Su .disable = fotg210_ep_disable,
76664cfd3f9SKuo-Jung Su .queue = fotg210_ep_queue,
76764cfd3f9SKuo-Jung Su .dequeue = fotg210_ep_dequeue,
76864cfd3f9SKuo-Jung Su .set_halt = fotg210_ep_halt,
76964cfd3f9SKuo-Jung Su .alloc_request = fotg210_ep_alloc_request,
77064cfd3f9SKuo-Jung Su .free_request = fotg210_ep_free_request,
77164cfd3f9SKuo-Jung Su };
77264cfd3f9SKuo-Jung Su
77364cfd3f9SKuo-Jung Su static struct fotg210_chip controller = {
77464cfd3f9SKuo-Jung Su .regs = (void __iomem *)CONFIG_FOTG210_BASE,
77564cfd3f9SKuo-Jung Su .gadget = {
77664cfd3f9SKuo-Jung Su .name = "fotg210_udc",
77764cfd3f9SKuo-Jung Su .ops = &fotg210_gadget_ops,
77864cfd3f9SKuo-Jung Su .ep0 = &controller.ep[0].ep,
77964cfd3f9SKuo-Jung Su .speed = USB_SPEED_UNKNOWN,
78064cfd3f9SKuo-Jung Su .is_dualspeed = 1,
78164cfd3f9SKuo-Jung Su .is_otg = 0,
78264cfd3f9SKuo-Jung Su .is_a_peripheral = 0,
78364cfd3f9SKuo-Jung Su .b_hnp_enable = 0,
78464cfd3f9SKuo-Jung Su .a_hnp_support = 0,
78564cfd3f9SKuo-Jung Su .a_alt_hnp_support = 0,
78664cfd3f9SKuo-Jung Su },
78764cfd3f9SKuo-Jung Su .ep[0] = {
78864cfd3f9SKuo-Jung Su .id = 0,
78964cfd3f9SKuo-Jung Su .ep = {
79064cfd3f9SKuo-Jung Su .name = "ep0",
79164cfd3f9SKuo-Jung Su .ops = &fotg210_ep_ops,
79264cfd3f9SKuo-Jung Su },
79364cfd3f9SKuo-Jung Su .desc = &ep0_desc,
79464cfd3f9SKuo-Jung Su .chip = &controller,
79564cfd3f9SKuo-Jung Su .maxpacket = CFG_EP0_MAX_PACKET_SIZE,
79664cfd3f9SKuo-Jung Su },
79764cfd3f9SKuo-Jung Su .ep[1] = {
79864cfd3f9SKuo-Jung Su .id = 1,
79964cfd3f9SKuo-Jung Su .ep = {
80064cfd3f9SKuo-Jung Su .name = "ep1",
80164cfd3f9SKuo-Jung Su .ops = &fotg210_ep_ops,
80264cfd3f9SKuo-Jung Su },
80364cfd3f9SKuo-Jung Su .chip = &controller,
80464cfd3f9SKuo-Jung Su .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
80564cfd3f9SKuo-Jung Su },
80664cfd3f9SKuo-Jung Su .ep[2] = {
80764cfd3f9SKuo-Jung Su .id = 2,
80864cfd3f9SKuo-Jung Su .ep = {
80964cfd3f9SKuo-Jung Su .name = "ep2",
81064cfd3f9SKuo-Jung Su .ops = &fotg210_ep_ops,
81164cfd3f9SKuo-Jung Su },
81264cfd3f9SKuo-Jung Su .chip = &controller,
81364cfd3f9SKuo-Jung Su .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
81464cfd3f9SKuo-Jung Su },
81564cfd3f9SKuo-Jung Su .ep[3] = {
81664cfd3f9SKuo-Jung Su .id = 3,
81764cfd3f9SKuo-Jung Su .ep = {
81864cfd3f9SKuo-Jung Su .name = "ep3",
81964cfd3f9SKuo-Jung Su .ops = &fotg210_ep_ops,
82064cfd3f9SKuo-Jung Su },
82164cfd3f9SKuo-Jung Su .chip = &controller,
82264cfd3f9SKuo-Jung Su .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
82364cfd3f9SKuo-Jung Su },
82464cfd3f9SKuo-Jung Su .ep[4] = {
82564cfd3f9SKuo-Jung Su .id = 4,
82664cfd3f9SKuo-Jung Su .ep = {
82764cfd3f9SKuo-Jung Su .name = "ep4",
82864cfd3f9SKuo-Jung Su .ops = &fotg210_ep_ops,
82964cfd3f9SKuo-Jung Su },
83064cfd3f9SKuo-Jung Su .chip = &controller,
83164cfd3f9SKuo-Jung Su .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
83264cfd3f9SKuo-Jung Su },
83364cfd3f9SKuo-Jung Su };
83464cfd3f9SKuo-Jung Su
usb_gadget_handle_interrupts(int index)8352d48aa69SKishon Vijay Abraham I int usb_gadget_handle_interrupts(int index)
83664cfd3f9SKuo-Jung Su {
83764cfd3f9SKuo-Jung Su struct fotg210_chip *chip = &controller;
83864cfd3f9SKuo-Jung Su struct fotg210_regs *regs = chip->regs;
83964cfd3f9SKuo-Jung Su uint32_t id, st, isr, gisr;
84064cfd3f9SKuo-Jung Su
84164cfd3f9SKuo-Jung Su isr = readl(®s->isr) & (~readl(®s->imr));
84264cfd3f9SKuo-Jung Su gisr = readl(®s->gisr) & (~readl(®s->gimr));
84364cfd3f9SKuo-Jung Su if (!(isr & ISR_DEV) || !gisr)
84464cfd3f9SKuo-Jung Su return 0;
84564cfd3f9SKuo-Jung Su
84664cfd3f9SKuo-Jung Su writel(ISR_DEV, ®s->isr);
84764cfd3f9SKuo-Jung Su
84864cfd3f9SKuo-Jung Su /* CX interrupts */
84964cfd3f9SKuo-Jung Su if (gisr & GISR_GRP0) {
85064cfd3f9SKuo-Jung Su st = readl(®s->gisr0);
851bd5e301dSKuo-Jung Su /*
852bd5e301dSKuo-Jung Su * Write 1 and then 0 works for both W1C & RW.
853bd5e301dSKuo-Jung Su *
854bd5e301dSKuo-Jung Su * HW v1.11.0+: It's a W1C register (write 1 clear)
855bd5e301dSKuo-Jung Su * HW v1.10.0-: It's a R/W register (write 0 clear)
856bd5e301dSKuo-Jung Su */
857bd5e301dSKuo-Jung Su writel(st & GISR0_CXABORT, ®s->gisr0);
85864cfd3f9SKuo-Jung Su writel(0, ®s->gisr0);
85964cfd3f9SKuo-Jung Su
86064cfd3f9SKuo-Jung Su if (st & GISR0_CXERR)
86164cfd3f9SKuo-Jung Su printf("fotg210: cmd error\n");
86264cfd3f9SKuo-Jung Su
86364cfd3f9SKuo-Jung Su if (st & GISR0_CXABORT)
86464cfd3f9SKuo-Jung Su printf("fotg210: cmd abort\n");
86564cfd3f9SKuo-Jung Su
86664cfd3f9SKuo-Jung Su if (st & GISR0_CXSETUP) /* setup */
86764cfd3f9SKuo-Jung Su fotg210_setup(chip);
86864cfd3f9SKuo-Jung Su else if (st & GISR0_CXEND) /* command finish */
86964cfd3f9SKuo-Jung Su setbits_le32(®s->cxfifo, CXFIFO_CXFIN);
87064cfd3f9SKuo-Jung Su }
87164cfd3f9SKuo-Jung Su
87264cfd3f9SKuo-Jung Su /* FIFO interrupts */
87364cfd3f9SKuo-Jung Su if (gisr & GISR_GRP1) {
87464cfd3f9SKuo-Jung Su st = readl(®s->gisr1);
87564cfd3f9SKuo-Jung Su for (id = 0; id < 4; ++id) {
87664cfd3f9SKuo-Jung Su if (st & GISR1_RX_FIFO(id))
87764cfd3f9SKuo-Jung Su fotg210_recv(chip, fifo_to_ep(chip, id, 0));
87864cfd3f9SKuo-Jung Su }
87964cfd3f9SKuo-Jung Su }
88064cfd3f9SKuo-Jung Su
88164cfd3f9SKuo-Jung Su /* Device Status Interrupts */
88264cfd3f9SKuo-Jung Su if (gisr & GISR_GRP2) {
88364cfd3f9SKuo-Jung Su st = readl(®s->gisr2);
884bd5e301dSKuo-Jung Su /*
885bd5e301dSKuo-Jung Su * Write 1 and then 0 works for both W1C & RW.
886bd5e301dSKuo-Jung Su *
887bd5e301dSKuo-Jung Su * HW v1.11.0+: It's a W1C register (write 1 clear)
888bd5e301dSKuo-Jung Su * HW v1.10.0-: It's a R/W register (write 0 clear)
889bd5e301dSKuo-Jung Su */
890bd5e301dSKuo-Jung Su writel(st, ®s->gisr2);
89164cfd3f9SKuo-Jung Su writel(0, ®s->gisr2);
89264cfd3f9SKuo-Jung Su
89364cfd3f9SKuo-Jung Su if (st & GISR2_RESET)
89464cfd3f9SKuo-Jung Su printf("fotg210: reset by host\n");
89564cfd3f9SKuo-Jung Su else if (st & GISR2_SUSPEND)
89664cfd3f9SKuo-Jung Su printf("fotg210: suspend/removed\n");
89764cfd3f9SKuo-Jung Su else if (st & GISR2_RESUME)
89864cfd3f9SKuo-Jung Su printf("fotg210: resume\n");
89964cfd3f9SKuo-Jung Su
90064cfd3f9SKuo-Jung Su /* Errors */
90164cfd3f9SKuo-Jung Su if (st & GISR2_ISOCERR)
90264cfd3f9SKuo-Jung Su printf("fotg210: iso error\n");
90364cfd3f9SKuo-Jung Su if (st & GISR2_ISOCABT)
90464cfd3f9SKuo-Jung Su printf("fotg210: iso abort\n");
90564cfd3f9SKuo-Jung Su if (st & GISR2_DMAERR)
90664cfd3f9SKuo-Jung Su printf("fotg210: dma error\n");
90764cfd3f9SKuo-Jung Su }
90864cfd3f9SKuo-Jung Su
90964cfd3f9SKuo-Jung Su return 0;
91064cfd3f9SKuo-Jung Su }
91164cfd3f9SKuo-Jung Su
usb_gadget_register_driver(struct usb_gadget_driver * driver)91264cfd3f9SKuo-Jung Su int usb_gadget_register_driver(struct usb_gadget_driver *driver)
91364cfd3f9SKuo-Jung Su {
91464cfd3f9SKuo-Jung Su int i, ret = 0;
91564cfd3f9SKuo-Jung Su struct fotg210_chip *chip = &controller;
91664cfd3f9SKuo-Jung Su
91764cfd3f9SKuo-Jung Su if (!driver || !driver->bind || !driver->setup) {
91864cfd3f9SKuo-Jung Su puts("fotg210: bad parameter.\n");
91964cfd3f9SKuo-Jung Su return -EINVAL;
92064cfd3f9SKuo-Jung Su }
92164cfd3f9SKuo-Jung Su
92264cfd3f9SKuo-Jung Su INIT_LIST_HEAD(&chip->gadget.ep_list);
92364cfd3f9SKuo-Jung Su for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) {
92464cfd3f9SKuo-Jung Su struct fotg210_ep *ep = chip->ep + i;
92564cfd3f9SKuo-Jung Su
92664cfd3f9SKuo-Jung Su ep->ep.maxpacket = ep->maxpacket;
92764cfd3f9SKuo-Jung Su INIT_LIST_HEAD(&ep->queue);
92864cfd3f9SKuo-Jung Su
92964cfd3f9SKuo-Jung Su if (ep->id == 0) {
93064cfd3f9SKuo-Jung Su ep->stopped = 0;
93164cfd3f9SKuo-Jung Su } else {
93264cfd3f9SKuo-Jung Su ep->stopped = 1;
93364cfd3f9SKuo-Jung Su list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list);
93464cfd3f9SKuo-Jung Su }
93564cfd3f9SKuo-Jung Su }
93664cfd3f9SKuo-Jung Su
93764cfd3f9SKuo-Jung Su if (fotg210_reset(chip)) {
93864cfd3f9SKuo-Jung Su puts("fotg210: reset failed.\n");
93964cfd3f9SKuo-Jung Su return -EINVAL;
94064cfd3f9SKuo-Jung Su }
94164cfd3f9SKuo-Jung Su
94264cfd3f9SKuo-Jung Su ret = driver->bind(&chip->gadget);
94364cfd3f9SKuo-Jung Su if (ret) {
94464cfd3f9SKuo-Jung Su debug("fotg210: driver->bind() returned %d\n", ret);
94564cfd3f9SKuo-Jung Su return ret;
94664cfd3f9SKuo-Jung Su }
94764cfd3f9SKuo-Jung Su chip->driver = driver;
94864cfd3f9SKuo-Jung Su
94964cfd3f9SKuo-Jung Su return ret;
95064cfd3f9SKuo-Jung Su }
95164cfd3f9SKuo-Jung Su
usb_gadget_unregister_driver(struct usb_gadget_driver * driver)95264cfd3f9SKuo-Jung Su int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
95364cfd3f9SKuo-Jung Su {
95464cfd3f9SKuo-Jung Su struct fotg210_chip *chip = &controller;
95564cfd3f9SKuo-Jung Su
95664cfd3f9SKuo-Jung Su driver->unbind(&chip->gadget);
95764cfd3f9SKuo-Jung Su chip->driver = NULL;
95864cfd3f9SKuo-Jung Su
95964cfd3f9SKuo-Jung Su pullup(chip, 0);
96064cfd3f9SKuo-Jung Su
96164cfd3f9SKuo-Jung Su return 0;
96264cfd3f9SKuo-Jung Su }
963