xref: /rk3399_rockchip-uboot/drivers/usb/gadget/fotg210.c (revision 1221ce459d04a428f8880f58581f671b736c3c27)
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(&regs->iep[ep - 1], IEP_RESET);
8564cfd3f9SKuo-Jung Su 		mdelay(1);
8664cfd3f9SKuo-Jung Su 		clrbits_le32(&regs->iep[ep - 1], IEP_RESET);
8764cfd3f9SKuo-Jung Su 		/* clear endpoint stall */
8864cfd3f9SKuo-Jung Su 		clrbits_le32(&regs->iep[ep - 1], IEP_STALL);
8964cfd3f9SKuo-Jung Su 	} else {
9064cfd3f9SKuo-Jung Su 		/* reset endpoint */
9164cfd3f9SKuo-Jung Su 		setbits_le32(&regs->oep[ep - 1], OEP_RESET);
9264cfd3f9SKuo-Jung Su 		mdelay(1);
9364cfd3f9SKuo-Jung Su 		clrbits_le32(&regs->oep[ep - 1], OEP_RESET);
9464cfd3f9SKuo-Jung Su 		/* clear endpoint stall */
9564cfd3f9SKuo-Jung Su 		clrbits_le32(&regs->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, &regs->dev_ctrl);
11064cfd3f9SKuo-Jung Su 
11164cfd3f9SKuo-Jung Su 	/* device address reset */
11264cfd3f9SKuo-Jung Su 	chip->addr = 0;
11364cfd3f9SKuo-Jung Su 	writel(0, &regs->dev_addr);
11464cfd3f9SKuo-Jung Su 
11564cfd3f9SKuo-Jung Su 	/* set idle counter to 7ms */
11664cfd3f9SKuo-Jung Su 	writel(7, &regs->idle);
11764cfd3f9SKuo-Jung Su 
11864cfd3f9SKuo-Jung Su 	/* disable all interrupts */
11964cfd3f9SKuo-Jung Su 	writel(IMR_MASK, &regs->imr);
12064cfd3f9SKuo-Jung Su 	writel(GIMR_MASK, &regs->gimr);
12164cfd3f9SKuo-Jung Su 	writel(GIMR0_MASK, &regs->gimr0);
12264cfd3f9SKuo-Jung Su 	writel(GIMR1_MASK, &regs->gimr1);
12364cfd3f9SKuo-Jung Su 	writel(GIMR2_MASK, &regs->gimr2);
12464cfd3f9SKuo-Jung Su 
12564cfd3f9SKuo-Jung Su 	/* clear interrupts */
12664cfd3f9SKuo-Jung Su 	writel(ISR_MASK, &regs->isr);
12764cfd3f9SKuo-Jung Su 	writel(0, &regs->gisr);
12864cfd3f9SKuo-Jung Su 	writel(0, &regs->gisr0);
12964cfd3f9SKuo-Jung Su 	writel(0, &regs->gisr1);
13064cfd3f9SKuo-Jung Su 	writel(0, &regs->gisr2);
13164cfd3f9SKuo-Jung Su 
13264cfd3f9SKuo-Jung Su 	/* chip reset */
13364cfd3f9SKuo-Jung Su 	setbits_le32(&regs->dev_ctrl, DEVCTRL_RESET);
13464cfd3f9SKuo-Jung Su 	mdelay(10);
13564cfd3f9SKuo-Jung Su 	if (readl(&regs->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(&regs->cxfifo, CXFIFO_CXFIFOCLR);
14264cfd3f9SKuo-Jung Su 	mdelay(10);
14364cfd3f9SKuo-Jung Su 	if (readl(&regs->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, &regs->epmap14);
15064cfd3f9SKuo-Jung Su 	writel(EPMAP58_DEFAULT, &regs->epmap58);
15164cfd3f9SKuo-Jung Su 	writel(FIFOMAP_DEFAULT, &regs->fifomap);
15264cfd3f9SKuo-Jung Su 	writel(0, &regs->fifocfg);
15364cfd3f9SKuo-Jung Su 	for (i = 0; i < 8; ++i) {
15464cfd3f9SKuo-Jung Su 		writel(CFG_EPX_MAX_PACKET_SIZE, &regs->iep[i]);
15564cfd3f9SKuo-Jung Su 		writel(CFG_EPX_MAX_PACKET_SIZE, &regs->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, &regs->fifocsr[i]);
16164cfd3f9SKuo-Jung Su 		mdelay(10);
16264cfd3f9SKuo-Jung Su 		if (readl(&regs->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, &regs->imr);
17064cfd3f9SKuo-Jung Su 	writel(ISR_MASK, &regs->isr);
17164cfd3f9SKuo-Jung Su 	/* disable EP0 IN/OUT interrupt */
17264cfd3f9SKuo-Jung Su 	writel(GIMR0_CXOUT | GIMR0_CXIN, &regs->gimr0);
17364cfd3f9SKuo-Jung Su 	/* disable EPX IN+SPK+OUT interrupts */
17464cfd3f9SKuo-Jung Su 	writel(GIMR1_MASK, &regs->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, &regs->gimr2);
17864cfd3f9SKuo-Jung Su 	/* enable all group interrupt */
17964cfd3f9SKuo-Jung Su 	writel(0, &regs->gimr);
18064cfd3f9SKuo-Jung Su 
18164cfd3f9SKuo-Jung Su 	/* suspend delay = 3 ms */
18264cfd3f9SKuo-Jung Su 	writel(3, &regs->idle);
18364cfd3f9SKuo-Jung Su 
18464cfd3f9SKuo-Jung Su 	/* turn-on device interrupts */
18564cfd3f9SKuo-Jung Su 	setbits_le32(&regs->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(&regs->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(&regs->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), &regs->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, &regs->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), &regs->dma_fifo);
25464cfd3f9SKuo-Jung Su 		}
25564cfd3f9SKuo-Jung Su 		writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, &regs->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, &regs->dma_fifo);
26164cfd3f9SKuo-Jung Su 			do {
26264cfd3f9SKuo-Jung Su 				blen = CXFIFO_BYTES(readl(&regs->cxfifo));
26364cfd3f9SKuo-Jung Su 			} while (blen < len);
26464cfd3f9SKuo-Jung Su 		} else {
26564cfd3f9SKuo-Jung Su 			writel(DMAFIFO_FIFO(fifo), &regs->dma_fifo);
26664cfd3f9SKuo-Jung Su 			blen = FIFOCSR_BYTES(readl(&regs->fifocsr[fifo]));
26764cfd3f9SKuo-Jung Su 		}
26864cfd3f9SKuo-Jung Su 		len  = (len < blen) ? len : blen;
26964cfd3f9SKuo-Jung Su 		writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, &regs->dma_ctrl);
27064cfd3f9SKuo-Jung Su 	}
27164cfd3f9SKuo-Jung Su 
27264cfd3f9SKuo-Jung Su 	/* 4. DMA start */
27364cfd3f9SKuo-Jung Su 	setbits_le32(&regs->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(&regs->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, &regs->dma_ctrl);
29964cfd3f9SKuo-Jung Su 
30064cfd3f9SKuo-Jung Su 	writel(0, &regs->gisr2);
30164cfd3f9SKuo-Jung Su 	writel(0, &regs->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(&regs->otgcsr) & OTGCSR_DEV_B) {
33364cfd3f9SKuo-Jung Su 			/* Mini-B */
33464cfd3f9SKuo-Jung Su 			if (readl(&regs->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), &regs->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), &regs->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, &regs->dma_fifo);
35264cfd3f9SKuo-Jung Su 	/* fetch 8 bytes setup packet */
35364cfd3f9SKuo-Jung Su 	tmp[0] = readl(&regs->ep0_data);
35464cfd3f9SKuo-Jung Su 	tmp[1] = readl(&regs->ep0_data);
35564cfd3f9SKuo-Jung Su 	/* release data port */
35664cfd3f9SKuo-Jung Su 	writel(0, &regs->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, &regs->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 					&regs->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, &regs->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(&regs->iep[id - 1], IEP_STALL);
41164cfd3f9SKuo-Jung Su 				setbits_le32(&regs->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(&regs->cxfifo, CXFIFO_CXFIN);
44964cfd3f9SKuo-Jung Su 		break;
45064cfd3f9SKuo-Jung Su 
45164cfd3f9SKuo-Jung Su 	case CX_STALL:
45264cfd3f9SKuo-Jung Su 		setbits_le32(&regs->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(&regs->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(&regs->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(&regs->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(&regs->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(&regs->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(&regs->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK));
55764cfd3f9SKuo-Jung Su 	clrbits_le32(&regs->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(&regs->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(&regs->iep[ep->id - 1],
68664cfd3f9SKuo-Jung Su 					IEP_STALL);
68764cfd3f9SKuo-Jung Su 			} else {
68864cfd3f9SKuo-Jung Su 				setbits_le32(&regs->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(&regs->iep[ep->id - 1],
69464cfd3f9SKuo-Jung Su 					IEP_STALL);
69564cfd3f9SKuo-Jung Su 			} else {
69664cfd3f9SKuo-Jung Su 				clrbits_le32(&regs->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(&regs->dev_ctrl, DEVCTRL_EN);
71964cfd3f9SKuo-Jung Su 			/* clear unplug bit (BIT0) */
72064cfd3f9SKuo-Jung Su 			clrbits_le32(&regs->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, &regs->dev_addr);
72764cfd3f9SKuo-Jung Su 		/* set unplug bit (BIT0) */
72864cfd3f9SKuo-Jung Su 		setbits_le32(&regs->phy_tmsr, PHYTMSR_UNPLUG);
72964cfd3f9SKuo-Jung Su 		/* disable the chip */
73064cfd3f9SKuo-Jung Su 		clrbits_le32(&regs->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(&regs->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(&regs->isr) & (~readl(&regs->imr));
84264cfd3f9SKuo-Jung Su 	gisr = readl(&regs->gisr) & (~readl(&regs->gimr));
84364cfd3f9SKuo-Jung Su 	if (!(isr & ISR_DEV) || !gisr)
84464cfd3f9SKuo-Jung Su 		return 0;
84564cfd3f9SKuo-Jung Su 
84664cfd3f9SKuo-Jung Su 	writel(ISR_DEV, &regs->isr);
84764cfd3f9SKuo-Jung Su 
84864cfd3f9SKuo-Jung Su 	/* CX interrupts */
84964cfd3f9SKuo-Jung Su 	if (gisr & GISR_GRP0) {
85064cfd3f9SKuo-Jung Su 		st = readl(&regs->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, &regs->gisr0);
85864cfd3f9SKuo-Jung Su 		writel(0, &regs->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(&regs->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(&regs->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(&regs->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, &regs->gisr2);
89164cfd3f9SKuo-Jung Su 		writel(0, &regs->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