162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * M66592 UDC (USB gadget)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2007 Renesas Solutions Corp.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/usb/ch9.h>
1862306a36Sopenharmony_ci#include <linux/usb/gadget.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "m66592-udc.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciMODULE_DESCRIPTION("M66592 USB gadget driver");
2362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2462306a36Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda");
2562306a36Sopenharmony_ciMODULE_ALIAS("platform:m66592_udc");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DRIVER_VERSION	"21 July 2009"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const char udc_name[] = "m66592_udc";
3062306a36Sopenharmony_cistatic const char *m66592_ep_name[] = {
3162306a36Sopenharmony_ci	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic void disable_controller(struct m66592 *m66592);
3562306a36Sopenharmony_cistatic void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);
3662306a36Sopenharmony_cistatic void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);
3762306a36Sopenharmony_cistatic int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
3862306a36Sopenharmony_ci			gfp_t gfp_flags);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void transfer_complete(struct m66592_ep *ep,
4162306a36Sopenharmony_ci		struct m66592_request *req, int status);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
4462306a36Sopenharmony_cistatic inline u16 get_usb_speed(struct m66592 *m66592)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void enable_pipe_irq(struct m66592 *m66592, u16 pipenum,
5062306a36Sopenharmony_ci		unsigned long reg)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	u16 tmp;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	tmp = m66592_read(m66592, M66592_INTENB0);
5562306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
5662306a36Sopenharmony_ci			M66592_INTENB0);
5762306a36Sopenharmony_ci	m66592_bset(m66592, (1 << pipenum), reg);
5862306a36Sopenharmony_ci	m66592_write(m66592, tmp, M66592_INTENB0);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void disable_pipe_irq(struct m66592 *m66592, u16 pipenum,
6262306a36Sopenharmony_ci		unsigned long reg)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	u16 tmp;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	tmp = m66592_read(m66592, M66592_INTENB0);
6762306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
6862306a36Sopenharmony_ci			M66592_INTENB0);
6962306a36Sopenharmony_ci	m66592_bclr(m66592, (1 << pipenum), reg);
7062306a36Sopenharmony_ci	m66592_write(m66592, tmp, M66592_INTENB0);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void m66592_usb_connect(struct m66592 *m66592)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	m66592_bset(m66592, M66592_CTRE, M66592_INTENB0);
7662306a36Sopenharmony_ci	m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
7762306a36Sopenharmony_ci			M66592_INTENB0);
7862306a36Sopenharmony_ci	m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void m66592_usb_disconnect(struct m66592 *m66592)
8462306a36Sopenharmony_ci__releases(m66592->lock)
8562306a36Sopenharmony_ci__acquires(m66592->lock)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0);
8862306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
8962306a36Sopenharmony_ci			M66592_INTENB0);
9062306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
9162306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	m66592->gadget.speed = USB_SPEED_UNKNOWN;
9462306a36Sopenharmony_ci	spin_unlock(&m66592->lock);
9562306a36Sopenharmony_ci	m66592->driver->disconnect(&m66592->gadget);
9662306a36Sopenharmony_ci	spin_lock(&m66592->lock);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	disable_controller(m66592);
9962306a36Sopenharmony_ci	INIT_LIST_HEAD(&m66592->ep[0].queue);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u16 pid = 0;
10562306a36Sopenharmony_ci	unsigned long offset;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (pipenum == 0)
10862306a36Sopenharmony_ci		pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID;
10962306a36Sopenharmony_ci	else if (pipenum < M66592_MAX_NUM_PIPE) {
11062306a36Sopenharmony_ci		offset = get_pipectr_addr(pipenum);
11162306a36Sopenharmony_ci		pid = m66592_read(m66592, offset) & M66592_PID;
11262306a36Sopenharmony_ci	} else
11362306a36Sopenharmony_ci		pr_err("unexpect pipe num (%d)\n", pipenum);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return pid;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum,
11962306a36Sopenharmony_ci		u16 pid)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	unsigned long offset;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (pipenum == 0)
12462306a36Sopenharmony_ci		m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR);
12562306a36Sopenharmony_ci	else if (pipenum < M66592_MAX_NUM_PIPE) {
12662306a36Sopenharmony_ci		offset = get_pipectr_addr(pipenum);
12762306a36Sopenharmony_ci		m66592_mdfy(m66592, pid, M66592_PID, offset);
12862306a36Sopenharmony_ci	} else
12962306a36Sopenharmony_ci		pr_err("unexpect pipe num (%d)\n", pipenum);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline void pipe_start(struct m66592 *m66592, u16 pipenum)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void pipe_stop(struct m66592 *m66592, u16 pipenum)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline void pipe_stall(struct m66592 *m66592, u16 pipenum)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	u16 ret = 0;
15062306a36Sopenharmony_ci	unsigned long offset;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (pipenum == 0)
15362306a36Sopenharmony_ci		ret = m66592_read(m66592, M66592_DCPCTR);
15462306a36Sopenharmony_ci	else if (pipenum < M66592_MAX_NUM_PIPE) {
15562306a36Sopenharmony_ci		offset = get_pipectr_addr(pipenum);
15662306a36Sopenharmony_ci		ret = m66592_read(m66592, offset);
15762306a36Sopenharmony_ci	} else
15862306a36Sopenharmony_ci		pr_err("unexpect pipe num (%d)\n", pipenum);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return ret;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	unsigned long offset;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	pipe_stop(m66592, pipenum);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (pipenum == 0)
17062306a36Sopenharmony_ci		m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR);
17162306a36Sopenharmony_ci	else if (pipenum < M66592_MAX_NUM_PIPE) {
17262306a36Sopenharmony_ci		offset = get_pipectr_addr(pipenum);
17362306a36Sopenharmony_ci		m66592_bset(m66592, M66592_SQCLR, offset);
17462306a36Sopenharmony_ci	} else
17562306a36Sopenharmony_ci		pr_err("unexpect pipe num(%d)\n", pipenum);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u16 tmp;
18162306a36Sopenharmony_ci	int size;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (pipenum == 0) {
18462306a36Sopenharmony_ci		tmp = m66592_read(m66592, M66592_DCPCFG);
18562306a36Sopenharmony_ci		if ((tmp & M66592_CNTMD) != 0)
18662306a36Sopenharmony_ci			size = 256;
18762306a36Sopenharmony_ci		else {
18862306a36Sopenharmony_ci			tmp = m66592_read(m66592, M66592_DCPMAXP);
18962306a36Sopenharmony_ci			size = tmp & M66592_MAXP;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci	} else {
19262306a36Sopenharmony_ci		m66592_write(m66592, pipenum, M66592_PIPESEL);
19362306a36Sopenharmony_ci		tmp = m66592_read(m66592, M66592_PIPECFG);
19462306a36Sopenharmony_ci		if ((tmp & M66592_CNTMD) != 0) {
19562306a36Sopenharmony_ci			tmp = m66592_read(m66592, M66592_PIPEBUF);
19662306a36Sopenharmony_ci			size = ((tmp >> 10) + 1) * 64;
19762306a36Sopenharmony_ci		} else {
19862306a36Sopenharmony_ci			tmp = m66592_read(m66592, M66592_PIPEMAXP);
19962306a36Sopenharmony_ci			size = tmp & M66592_MXPS;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return size;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic inline void pipe_change(struct m66592 *m66592, u16 pipenum)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
20962306a36Sopenharmony_ci	unsigned short mbw;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (ep->use_dma)
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ndelay(450);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (m66592->pdata->on_chip)
21962306a36Sopenharmony_ci		mbw = M66592_MBW_32;
22062306a36Sopenharmony_ci	else
22162306a36Sopenharmony_ci		mbw = M66592_MBW_16;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	m66592_bset(m66592, mbw, ep->fifosel);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int pipe_buffer_setting(struct m66592 *m66592,
22762306a36Sopenharmony_ci		struct m66592_pipe_info *info)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	u16 bufnum = 0, buf_bsize = 0;
23062306a36Sopenharmony_ci	u16 pipecfg = 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (info->pipe == 0)
23362306a36Sopenharmony_ci		return -EINVAL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	m66592_write(m66592, info->pipe, M66592_PIPESEL);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (info->dir_in)
23862306a36Sopenharmony_ci		pipecfg |= M66592_DIR;
23962306a36Sopenharmony_ci	pipecfg |= info->type;
24062306a36Sopenharmony_ci	pipecfg |= info->epnum;
24162306a36Sopenharmony_ci	switch (info->type) {
24262306a36Sopenharmony_ci	case M66592_INT:
24362306a36Sopenharmony_ci		bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT);
24462306a36Sopenharmony_ci		buf_bsize = 0;
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	case M66592_BULK:
24762306a36Sopenharmony_ci		/* isochronous pipes may be used as bulk pipes */
24862306a36Sopenharmony_ci		if (info->pipe >= M66592_BASE_PIPENUM_BULK)
24962306a36Sopenharmony_ci			bufnum = info->pipe - M66592_BASE_PIPENUM_BULK;
25062306a36Sopenharmony_ci		else
25162306a36Sopenharmony_ci			bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		bufnum = M66592_BASE_BUFNUM + (bufnum * 16);
25462306a36Sopenharmony_ci		buf_bsize = 7;
25562306a36Sopenharmony_ci		pipecfg |= M66592_DBLB;
25662306a36Sopenharmony_ci		if (!info->dir_in)
25762306a36Sopenharmony_ci			pipecfg |= M66592_SHTNAK;
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case M66592_ISO:
26062306a36Sopenharmony_ci		bufnum = M66592_BASE_BUFNUM +
26162306a36Sopenharmony_ci			 (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
26262306a36Sopenharmony_ci		buf_bsize = 7;
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (buf_bsize && ((bufnum + 16) >= M66592_MAX_BUFNUM)) {
26762306a36Sopenharmony_ci		pr_err("m66592 pipe memory is insufficient\n");
26862306a36Sopenharmony_ci		return -ENOMEM;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	m66592_write(m66592, pipecfg, M66592_PIPECFG);
27262306a36Sopenharmony_ci	m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF);
27362306a36Sopenharmony_ci	m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP);
27462306a36Sopenharmony_ci	if (info->interval)
27562306a36Sopenharmony_ci		info->interval--;
27662306a36Sopenharmony_ci	m66592_write(m66592, info->interval, M66592_PIPEPERI);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void pipe_buffer_release(struct m66592 *m66592,
28262306a36Sopenharmony_ci				struct m66592_pipe_info *info)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	if (info->pipe == 0)
28562306a36Sopenharmony_ci		return;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (is_bulk_pipe(info->pipe)) {
28862306a36Sopenharmony_ci		m66592->bulk--;
28962306a36Sopenharmony_ci	} else if (is_interrupt_pipe(info->pipe))
29062306a36Sopenharmony_ci		m66592->interrupt--;
29162306a36Sopenharmony_ci	else if (is_isoc_pipe(info->pipe)) {
29262306a36Sopenharmony_ci		m66592->isochronous--;
29362306a36Sopenharmony_ci		if (info->type == M66592_BULK)
29462306a36Sopenharmony_ci			m66592->bulk--;
29562306a36Sopenharmony_ci	} else
29662306a36Sopenharmony_ci		pr_err("ep_release: unexpect pipenum (%d)\n",
29762306a36Sopenharmony_ci				info->pipe);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic void pipe_initialize(struct m66592_ep *ep)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
30362306a36Sopenharmony_ci	unsigned short mbw;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	m66592_write(m66592, M66592_ACLRM, ep->pipectr);
30862306a36Sopenharmony_ci	m66592_write(m66592, 0, ep->pipectr);
30962306a36Sopenharmony_ci	m66592_write(m66592, M66592_SQCLR, ep->pipectr);
31062306a36Sopenharmony_ci	if (ep->use_dma) {
31162306a36Sopenharmony_ci		m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		ndelay(450);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (m66592->pdata->on_chip)
31662306a36Sopenharmony_ci			mbw = M66592_MBW_32;
31762306a36Sopenharmony_ci		else
31862306a36Sopenharmony_ci			mbw = M66592_MBW_16;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		m66592_bset(m66592, mbw, ep->fifosel);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
32562306a36Sopenharmony_ci		const struct usb_endpoint_descriptor *desc,
32662306a36Sopenharmony_ci		u16 pipenum, int dma)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	if ((pipenum != 0) && dma) {
32962306a36Sopenharmony_ci		if (m66592->num_dma == 0) {
33062306a36Sopenharmony_ci			m66592->num_dma++;
33162306a36Sopenharmony_ci			ep->use_dma = 1;
33262306a36Sopenharmony_ci			ep->fifoaddr = M66592_D0FIFO;
33362306a36Sopenharmony_ci			ep->fifosel = M66592_D0FIFOSEL;
33462306a36Sopenharmony_ci			ep->fifoctr = M66592_D0FIFOCTR;
33562306a36Sopenharmony_ci			ep->fifotrn = M66592_D0FIFOTRN;
33662306a36Sopenharmony_ci		} else if (!m66592->pdata->on_chip && m66592->num_dma == 1) {
33762306a36Sopenharmony_ci			m66592->num_dma++;
33862306a36Sopenharmony_ci			ep->use_dma = 1;
33962306a36Sopenharmony_ci			ep->fifoaddr = M66592_D1FIFO;
34062306a36Sopenharmony_ci			ep->fifosel = M66592_D1FIFOSEL;
34162306a36Sopenharmony_ci			ep->fifoctr = M66592_D1FIFOCTR;
34262306a36Sopenharmony_ci			ep->fifotrn = M66592_D1FIFOTRN;
34362306a36Sopenharmony_ci		} else {
34462306a36Sopenharmony_ci			ep->use_dma = 0;
34562306a36Sopenharmony_ci			ep->fifoaddr = M66592_CFIFO;
34662306a36Sopenharmony_ci			ep->fifosel = M66592_CFIFOSEL;
34762306a36Sopenharmony_ci			ep->fifoctr = M66592_CFIFOCTR;
34862306a36Sopenharmony_ci			ep->fifotrn = 0;
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci	} else {
35162306a36Sopenharmony_ci		ep->use_dma = 0;
35262306a36Sopenharmony_ci		ep->fifoaddr = M66592_CFIFO;
35362306a36Sopenharmony_ci		ep->fifosel = M66592_CFIFOSEL;
35462306a36Sopenharmony_ci		ep->fifoctr = M66592_CFIFOCTR;
35562306a36Sopenharmony_ci		ep->fifotrn = 0;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ep->pipectr = get_pipectr_addr(pipenum);
35962306a36Sopenharmony_ci	ep->pipenum = pipenum;
36062306a36Sopenharmony_ci	ep->ep.maxpacket = usb_endpoint_maxp(desc);
36162306a36Sopenharmony_ci	m66592->pipenum2ep[pipenum] = ep;
36262306a36Sopenharmony_ci	m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
36362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ep->queue);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void m66592_ep_release(struct m66592_ep *ep)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
36962306a36Sopenharmony_ci	u16 pipenum = ep->pipenum;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (pipenum == 0)
37262306a36Sopenharmony_ci		return;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (ep->use_dma)
37562306a36Sopenharmony_ci		m66592->num_dma--;
37662306a36Sopenharmony_ci	ep->pipenum = 0;
37762306a36Sopenharmony_ci	ep->busy = 0;
37862306a36Sopenharmony_ci	ep->use_dma = 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int alloc_pipe_config(struct m66592_ep *ep,
38262306a36Sopenharmony_ci		const struct usb_endpoint_descriptor *desc)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
38562306a36Sopenharmony_ci	struct m66592_pipe_info info;
38662306a36Sopenharmony_ci	int dma = 0;
38762306a36Sopenharmony_ci	int *counter;
38862306a36Sopenharmony_ci	int ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ep->ep.desc = desc;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	BUG_ON(ep->pipenum);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
39562306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_BULK:
39662306a36Sopenharmony_ci		if (m66592->bulk >= M66592_MAX_NUM_BULK) {
39762306a36Sopenharmony_ci			if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
39862306a36Sopenharmony_ci				pr_err("bulk pipe is insufficient\n");
39962306a36Sopenharmony_ci				return -ENODEV;
40062306a36Sopenharmony_ci			} else {
40162306a36Sopenharmony_ci				info.pipe = M66592_BASE_PIPENUM_ISOC
40262306a36Sopenharmony_ci						+ m66592->isochronous;
40362306a36Sopenharmony_ci				counter = &m66592->isochronous;
40462306a36Sopenharmony_ci			}
40562306a36Sopenharmony_ci		} else {
40662306a36Sopenharmony_ci			info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk;
40762306a36Sopenharmony_ci			counter = &m66592->bulk;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci		info.type = M66592_BULK;
41062306a36Sopenharmony_ci		dma = 1;
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:
41362306a36Sopenharmony_ci		if (m66592->interrupt >= M66592_MAX_NUM_INT) {
41462306a36Sopenharmony_ci			pr_err("interrupt pipe is insufficient\n");
41562306a36Sopenharmony_ci			return -ENODEV;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci		info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt;
41862306a36Sopenharmony_ci		info.type = M66592_INT;
41962306a36Sopenharmony_ci		counter = &m66592->interrupt;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:
42262306a36Sopenharmony_ci		if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
42362306a36Sopenharmony_ci			pr_err("isochronous pipe is insufficient\n");
42462306a36Sopenharmony_ci			return -ENODEV;
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci		info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous;
42762306a36Sopenharmony_ci		info.type = M66592_ISO;
42862306a36Sopenharmony_ci		counter = &m66592->isochronous;
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	default:
43162306a36Sopenharmony_ci		pr_err("unexpect xfer type\n");
43262306a36Sopenharmony_ci		return -EINVAL;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	ep->type = info.type;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
43762306a36Sopenharmony_ci	info.maxpacket = usb_endpoint_maxp(desc);
43862306a36Sopenharmony_ci	info.interval = desc->bInterval;
43962306a36Sopenharmony_ci	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
44062306a36Sopenharmony_ci		info.dir_in = 1;
44162306a36Sopenharmony_ci	else
44262306a36Sopenharmony_ci		info.dir_in = 0;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = pipe_buffer_setting(m66592, &info);
44562306a36Sopenharmony_ci	if (ret < 0) {
44662306a36Sopenharmony_ci		pr_err("pipe_buffer_setting fail\n");
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	(*counter)++;
45162306a36Sopenharmony_ci	if ((counter == &m66592->isochronous) && info.type == M66592_BULK)
45262306a36Sopenharmony_ci		m66592->bulk++;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	m66592_ep_setting(m66592, ep, desc, info.pipe, dma);
45562306a36Sopenharmony_ci	pipe_initialize(ep);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 0;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int free_pipe_config(struct m66592_ep *ep)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
46362306a36Sopenharmony_ci	struct m66592_pipe_info info;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	info.pipe = ep->pipenum;
46662306a36Sopenharmony_ci	info.type = ep->type;
46762306a36Sopenharmony_ci	pipe_buffer_release(m66592, &info);
46862306a36Sopenharmony_ci	m66592_ep_release(ep);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
47462306a36Sopenharmony_cistatic void pipe_irq_enable(struct m66592 *m66592, u16 pipenum)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	enable_irq_ready(m66592, pipenum);
47762306a36Sopenharmony_ci	enable_irq_nrdy(m66592, pipenum);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic void pipe_irq_disable(struct m66592 *m66592, u16 pipenum)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	disable_irq_ready(m66592, pipenum);
48362306a36Sopenharmony_ci	disable_irq_nrdy(m66592, pipenum);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* if complete is true, gadget driver complete function is not call */
48762306a36Sopenharmony_cistatic void control_end(struct m66592 *m66592, unsigned ccpl)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	m66592->ep[0].internal_ccpl = ccpl;
49062306a36Sopenharmony_ci	pipe_start(m66592, 0);
49162306a36Sopenharmony_ci	m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	pipe_change(m66592, ep->pipenum);
49962306a36Sopenharmony_ci	m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0,
50062306a36Sopenharmony_ci			(M66592_ISEL | M66592_CURPIPE),
50162306a36Sopenharmony_ci			M66592_CFIFOSEL);
50262306a36Sopenharmony_ci	m66592_write(m66592, M66592_BCLR, ep->fifoctr);
50362306a36Sopenharmony_ci	if (req->req.length == 0) {
50462306a36Sopenharmony_ci		m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
50562306a36Sopenharmony_ci		pipe_start(m66592, 0);
50662306a36Sopenharmony_ci		transfer_complete(ep, req, 0);
50762306a36Sopenharmony_ci	} else {
50862306a36Sopenharmony_ci		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
50962306a36Sopenharmony_ci		irq_ep0_write(ep, req);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void start_packet_write(struct m66592_ep *ep, struct m66592_request *req)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
51662306a36Sopenharmony_ci	u16 tmp;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	pipe_change(m66592, ep->pipenum);
51962306a36Sopenharmony_ci	disable_irq_empty(m66592, ep->pipenum);
52062306a36Sopenharmony_ci	pipe_start(m66592, ep->pipenum);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	tmp = m66592_read(m66592, ep->fifoctr);
52362306a36Sopenharmony_ci	if (unlikely((tmp & M66592_FRDY) == 0))
52462306a36Sopenharmony_ci		pipe_irq_enable(m66592, ep->pipenum);
52562306a36Sopenharmony_ci	else
52662306a36Sopenharmony_ci		irq_packet_write(ep, req);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
53262306a36Sopenharmony_ci	u16 pipenum = ep->pipenum;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (ep->pipenum == 0) {
53562306a36Sopenharmony_ci		m66592_mdfy(m66592, M66592_PIPE0,
53662306a36Sopenharmony_ci				(M66592_ISEL | M66592_CURPIPE),
53762306a36Sopenharmony_ci				M66592_CFIFOSEL);
53862306a36Sopenharmony_ci		m66592_write(m66592, M66592_BCLR, ep->fifoctr);
53962306a36Sopenharmony_ci		pipe_start(m66592, pipenum);
54062306a36Sopenharmony_ci		pipe_irq_enable(m66592, pipenum);
54162306a36Sopenharmony_ci	} else {
54262306a36Sopenharmony_ci		if (ep->use_dma) {
54362306a36Sopenharmony_ci			m66592_bset(m66592, M66592_TRCLR, ep->fifosel);
54462306a36Sopenharmony_ci			pipe_change(m66592, pipenum);
54562306a36Sopenharmony_ci			m66592_bset(m66592, M66592_TRENB, ep->fifosel);
54662306a36Sopenharmony_ci			m66592_write(m66592,
54762306a36Sopenharmony_ci				(req->req.length + ep->ep.maxpacket - 1)
54862306a36Sopenharmony_ci					/ ep->ep.maxpacket,
54962306a36Sopenharmony_ci				ep->fifotrn);
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci		pipe_start(m66592, pipenum);	/* trigger once */
55262306a36Sopenharmony_ci		pipe_irq_enable(m66592, pipenum);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic void start_packet(struct m66592_ep *ep, struct m66592_request *req)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
55962306a36Sopenharmony_ci		start_packet_write(ep, req);
56062306a36Sopenharmony_ci	else
56162306a36Sopenharmony_ci		start_packet_read(ep, req);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	u16 ctsq;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	switch (ctsq) {
57162306a36Sopenharmony_ci	case M66592_CS_RDDS:
57262306a36Sopenharmony_ci		start_ep0_write(ep, req);
57362306a36Sopenharmony_ci		break;
57462306a36Sopenharmony_ci	case M66592_CS_WRDS:
57562306a36Sopenharmony_ci		start_packet_read(ep, req);
57662306a36Sopenharmony_ci		break;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	case M66592_CS_WRND:
57962306a36Sopenharmony_ci		control_end(ep->m66592, 0);
58062306a36Sopenharmony_ci		break;
58162306a36Sopenharmony_ci	default:
58262306a36Sopenharmony_ci		pr_err("start_ep0: unexpect ctsq(%x)\n", ctsq);
58362306a36Sopenharmony_ci		break;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic void init_controller(struct m66592 *m66592)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	unsigned int endian;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (m66592->pdata->on_chip) {
59262306a36Sopenharmony_ci		if (m66592->pdata->endian)
59362306a36Sopenharmony_ci			endian = 0; /* big endian */
59462306a36Sopenharmony_ci		else
59562306a36Sopenharmony_ci			endian = M66592_LITTLE; /* little endian */
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
59862306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
59962306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
60062306a36Sopenharmony_ci		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		/* This is a workaound for SH7722 2nd cut */
60362306a36Sopenharmony_ci		m66592_bset(m66592, 0x8000, M66592_DVSTCTR);
60462306a36Sopenharmony_ci		m66592_bset(m66592, 0x1000, M66592_TESTMODE);
60562306a36Sopenharmony_ci		m66592_bclr(m66592, 0x8000, M66592_DVSTCTR);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		m66592_bset(m66592, M66592_INTL, M66592_INTENB1);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		m66592_write(m66592, 0, M66592_CFBCFG);
61062306a36Sopenharmony_ci		m66592_write(m66592, 0, M66592_D0FBCFG);
61162306a36Sopenharmony_ci		m66592_bset(m66592, endian, M66592_CFBCFG);
61262306a36Sopenharmony_ci		m66592_bset(m66592, endian, M66592_D0FBCFG);
61362306a36Sopenharmony_ci	} else {
61462306a36Sopenharmony_ci		unsigned int clock, vif, irq_sense;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if (m66592->pdata->endian)
61762306a36Sopenharmony_ci			endian = M66592_BIGEND; /* big endian */
61862306a36Sopenharmony_ci		else
61962306a36Sopenharmony_ci			endian = 0; /* little endian */
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		if (m66592->pdata->vif)
62262306a36Sopenharmony_ci			vif = M66592_LDRV; /* 3.3v */
62362306a36Sopenharmony_ci		else
62462306a36Sopenharmony_ci			vif = 0; /* 1.5v */
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		switch (m66592->pdata->xtal) {
62762306a36Sopenharmony_ci		case M66592_PLATDATA_XTAL_12MHZ:
62862306a36Sopenharmony_ci			clock = M66592_XTAL12;
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci		case M66592_PLATDATA_XTAL_24MHZ:
63162306a36Sopenharmony_ci			clock = M66592_XTAL24;
63262306a36Sopenharmony_ci			break;
63362306a36Sopenharmony_ci		case M66592_PLATDATA_XTAL_48MHZ:
63462306a36Sopenharmony_ci			clock = M66592_XTAL48;
63562306a36Sopenharmony_ci			break;
63662306a36Sopenharmony_ci		default:
63762306a36Sopenharmony_ci			pr_warn("m66592-udc: xtal configuration error\n");
63862306a36Sopenharmony_ci			clock = 0;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		switch (m66592->irq_trigger) {
64262306a36Sopenharmony_ci		case IRQF_TRIGGER_LOW:
64362306a36Sopenharmony_ci			irq_sense = M66592_INTL;
64462306a36Sopenharmony_ci			break;
64562306a36Sopenharmony_ci		case IRQF_TRIGGER_FALLING:
64662306a36Sopenharmony_ci			irq_sense = 0;
64762306a36Sopenharmony_ci			break;
64862306a36Sopenharmony_ci		default:
64962306a36Sopenharmony_ci			pr_warn("m66592-udc: irq trigger config error\n");
65062306a36Sopenharmony_ci			irq_sense = 0;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		m66592_bset(m66592,
65462306a36Sopenharmony_ci			    (vif & M66592_LDRV) | (endian & M66592_BIGEND),
65562306a36Sopenharmony_ci			    M66592_PINCFG);
65662306a36Sopenharmony_ci		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
65762306a36Sopenharmony_ci		m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL,
65862306a36Sopenharmony_ci			    M66592_SYSCFG);
65962306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
66062306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
66162306a36Sopenharmony_ci		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		msleep(3);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		msleep(1);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
67462306a36Sopenharmony_ci		m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
67562306a36Sopenharmony_ci			     M66592_DMA0CFG);
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic void disable_controller(struct m66592 *m66592)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_UTST, M66592_TESTMODE);
68262306a36Sopenharmony_ci	if (!m66592->pdata->on_chip) {
68362306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
68462306a36Sopenharmony_ci		udelay(1);
68562306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
68662306a36Sopenharmony_ci		udelay(1);
68762306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
68862306a36Sopenharmony_ci		udelay(1);
68962306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic void m66592_start_xclock(struct m66592 *m66592)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	u16 tmp;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!m66592->pdata->on_chip) {
69862306a36Sopenharmony_ci		tmp = m66592_read(m66592, M66592_SYSCFG);
69962306a36Sopenharmony_ci		if (!(tmp & M66592_XCKE))
70062306a36Sopenharmony_ci			m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
70562306a36Sopenharmony_cistatic void transfer_complete(struct m66592_ep *ep,
70662306a36Sopenharmony_ci		struct m66592_request *req, int status)
70762306a36Sopenharmony_ci__releases(m66592->lock)
70862306a36Sopenharmony_ci__acquires(m66592->lock)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	int restart = 0;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (unlikely(ep->pipenum == 0)) {
71362306a36Sopenharmony_ci		if (ep->internal_ccpl) {
71462306a36Sopenharmony_ci			ep->internal_ccpl = 0;
71562306a36Sopenharmony_ci			return;
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	list_del_init(&req->queue);
72062306a36Sopenharmony_ci	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
72162306a36Sopenharmony_ci		req->req.status = -ESHUTDOWN;
72262306a36Sopenharmony_ci	else
72362306a36Sopenharmony_ci		req->req.status = status;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (!list_empty(&ep->queue))
72662306a36Sopenharmony_ci		restart = 1;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	spin_unlock(&ep->m66592->lock);
72962306a36Sopenharmony_ci	usb_gadget_giveback_request(&ep->ep, &req->req);
73062306a36Sopenharmony_ci	spin_lock(&ep->m66592->lock);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (restart) {
73362306a36Sopenharmony_ci		req = list_entry(ep->queue.next, struct m66592_request, queue);
73462306a36Sopenharmony_ci		if (ep->ep.desc)
73562306a36Sopenharmony_ci			start_packet(ep, req);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	int i;
74262306a36Sopenharmony_ci	u16 tmp;
74362306a36Sopenharmony_ci	unsigned bufsize;
74462306a36Sopenharmony_ci	size_t size;
74562306a36Sopenharmony_ci	void *buf;
74662306a36Sopenharmony_ci	u16 pipenum = ep->pipenum;
74762306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	pipe_change(m66592, pipenum);
75062306a36Sopenharmony_ci	m66592_bset(m66592, M66592_ISEL, ep->fifosel);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	i = 0;
75362306a36Sopenharmony_ci	do {
75462306a36Sopenharmony_ci		tmp = m66592_read(m66592, ep->fifoctr);
75562306a36Sopenharmony_ci		if (i++ > 100000) {
75662306a36Sopenharmony_ci			pr_err("pipe0 is busy. maybe cpu i/o bus "
75762306a36Sopenharmony_ci				"conflict. please power off this controller.");
75862306a36Sopenharmony_ci			return;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci		ndelay(1);
76162306a36Sopenharmony_ci	} while ((tmp & M66592_FRDY) == 0);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* prepare parameters */
76462306a36Sopenharmony_ci	bufsize = get_buffer_size(m66592, pipenum);
76562306a36Sopenharmony_ci	buf = req->req.buf + req->req.actual;
76662306a36Sopenharmony_ci	size = min(bufsize, req->req.length - req->req.actual);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* write fifo */
76962306a36Sopenharmony_ci	if (req->req.buf) {
77062306a36Sopenharmony_ci		if (size > 0)
77162306a36Sopenharmony_ci			m66592_write_fifo(m66592, ep, buf, size);
77262306a36Sopenharmony_ci		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
77362306a36Sopenharmony_ci			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* update parameters */
77762306a36Sopenharmony_ci	req->req.actual += size;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* check transfer finish */
78062306a36Sopenharmony_ci	if ((!req->req.zero && (req->req.actual == req->req.length))
78162306a36Sopenharmony_ci			|| (size % ep->ep.maxpacket)
78262306a36Sopenharmony_ci			|| (size == 0)) {
78362306a36Sopenharmony_ci		disable_irq_ready(m66592, pipenum);
78462306a36Sopenharmony_ci		disable_irq_empty(m66592, pipenum);
78562306a36Sopenharmony_ci	} else {
78662306a36Sopenharmony_ci		disable_irq_ready(m66592, pipenum);
78762306a36Sopenharmony_ci		enable_irq_empty(m66592, pipenum);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	pipe_start(m66592, pipenum);
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	u16 tmp;
79562306a36Sopenharmony_ci	unsigned bufsize;
79662306a36Sopenharmony_ci	size_t size;
79762306a36Sopenharmony_ci	void *buf;
79862306a36Sopenharmony_ci	u16 pipenum = ep->pipenum;
79962306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	pipe_change(m66592, pipenum);
80262306a36Sopenharmony_ci	tmp = m66592_read(m66592, ep->fifoctr);
80362306a36Sopenharmony_ci	if (unlikely((tmp & M66592_FRDY) == 0)) {
80462306a36Sopenharmony_ci		pipe_stop(m66592, pipenum);
80562306a36Sopenharmony_ci		pipe_irq_disable(m66592, pipenum);
80662306a36Sopenharmony_ci		pr_err("write fifo not ready. pipnum=%d\n", pipenum);
80762306a36Sopenharmony_ci		return;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* prepare parameters */
81162306a36Sopenharmony_ci	bufsize = get_buffer_size(m66592, pipenum);
81262306a36Sopenharmony_ci	buf = req->req.buf + req->req.actual;
81362306a36Sopenharmony_ci	size = min(bufsize, req->req.length - req->req.actual);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* write fifo */
81662306a36Sopenharmony_ci	if (req->req.buf) {
81762306a36Sopenharmony_ci		m66592_write_fifo(m66592, ep, buf, size);
81862306a36Sopenharmony_ci		if ((size == 0)
81962306a36Sopenharmony_ci				|| ((size % ep->ep.maxpacket) != 0)
82062306a36Sopenharmony_ci				|| ((bufsize != ep->ep.maxpacket)
82162306a36Sopenharmony_ci					&& (bufsize > size)))
82262306a36Sopenharmony_ci			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* update parameters */
82662306a36Sopenharmony_ci	req->req.actual += size;
82762306a36Sopenharmony_ci	/* check transfer finish */
82862306a36Sopenharmony_ci	if ((!req->req.zero && (req->req.actual == req->req.length))
82962306a36Sopenharmony_ci			|| (size % ep->ep.maxpacket)
83062306a36Sopenharmony_ci			|| (size == 0)) {
83162306a36Sopenharmony_ci		disable_irq_ready(m66592, pipenum);
83262306a36Sopenharmony_ci		enable_irq_empty(m66592, pipenum);
83362306a36Sopenharmony_ci	} else {
83462306a36Sopenharmony_ci		disable_irq_empty(m66592, pipenum);
83562306a36Sopenharmony_ci		pipe_irq_enable(m66592, pipenum);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	u16 tmp;
84262306a36Sopenharmony_ci	int rcv_len, bufsize, req_len;
84362306a36Sopenharmony_ci	int size;
84462306a36Sopenharmony_ci	void *buf;
84562306a36Sopenharmony_ci	u16 pipenum = ep->pipenum;
84662306a36Sopenharmony_ci	struct m66592 *m66592 = ep->m66592;
84762306a36Sopenharmony_ci	int finish = 0;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	pipe_change(m66592, pipenum);
85062306a36Sopenharmony_ci	tmp = m66592_read(m66592, ep->fifoctr);
85162306a36Sopenharmony_ci	if (unlikely((tmp & M66592_FRDY) == 0)) {
85262306a36Sopenharmony_ci		req->req.status = -EPIPE;
85362306a36Sopenharmony_ci		pipe_stop(m66592, pipenum);
85462306a36Sopenharmony_ci		pipe_irq_disable(m66592, pipenum);
85562306a36Sopenharmony_ci		pr_err("read fifo not ready");
85662306a36Sopenharmony_ci		return;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* prepare parameters */
86062306a36Sopenharmony_ci	rcv_len = tmp & M66592_DTLN;
86162306a36Sopenharmony_ci	bufsize = get_buffer_size(m66592, pipenum);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	buf = req->req.buf + req->req.actual;
86462306a36Sopenharmony_ci	req_len = req->req.length - req->req.actual;
86562306a36Sopenharmony_ci	if (rcv_len < bufsize)
86662306a36Sopenharmony_ci		size = min(rcv_len, req_len);
86762306a36Sopenharmony_ci	else
86862306a36Sopenharmony_ci		size = min(bufsize, req_len);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/* update parameters */
87162306a36Sopenharmony_ci	req->req.actual += size;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* check transfer finish */
87462306a36Sopenharmony_ci	if ((!req->req.zero && (req->req.actual == req->req.length))
87562306a36Sopenharmony_ci			|| (size % ep->ep.maxpacket)
87662306a36Sopenharmony_ci			|| (size == 0)) {
87762306a36Sopenharmony_ci		pipe_stop(m66592, pipenum);
87862306a36Sopenharmony_ci		pipe_irq_disable(m66592, pipenum);
87962306a36Sopenharmony_ci		finish = 1;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* read fifo */
88362306a36Sopenharmony_ci	if (req->req.buf) {
88462306a36Sopenharmony_ci		if (size == 0)
88562306a36Sopenharmony_ci			m66592_write(m66592, M66592_BCLR, ep->fifoctr);
88662306a36Sopenharmony_ci		else
88762306a36Sopenharmony_ci			m66592_read_fifo(m66592, ep->fifoaddr, buf, size);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if ((ep->pipenum != 0) && finish)
89162306a36Sopenharmony_ci		transfer_complete(ep, req, 0);
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	u16 check;
89762306a36Sopenharmony_ci	u16 pipenum;
89862306a36Sopenharmony_ci	struct m66592_ep *ep;
89962306a36Sopenharmony_ci	struct m66592_request *req;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) {
90262306a36Sopenharmony_ci		m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS);
90362306a36Sopenharmony_ci		m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE,
90462306a36Sopenharmony_ci				M66592_CFIFOSEL);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		ep = &m66592->ep[0];
90762306a36Sopenharmony_ci		req = list_entry(ep->queue.next, struct m66592_request, queue);
90862306a36Sopenharmony_ci		irq_packet_read(ep, req);
90962306a36Sopenharmony_ci	} else {
91062306a36Sopenharmony_ci		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
91162306a36Sopenharmony_ci			check = 1 << pipenum;
91262306a36Sopenharmony_ci			if ((status & check) && (enb & check)) {
91362306a36Sopenharmony_ci				m66592_write(m66592, ~check, M66592_BRDYSTS);
91462306a36Sopenharmony_ci				ep = m66592->pipenum2ep[pipenum];
91562306a36Sopenharmony_ci				req = list_entry(ep->queue.next,
91662306a36Sopenharmony_ci						 struct m66592_request, queue);
91762306a36Sopenharmony_ci				if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
91862306a36Sopenharmony_ci					irq_packet_write(ep, req);
91962306a36Sopenharmony_ci				else
92062306a36Sopenharmony_ci					irq_packet_read(ep, req);
92162306a36Sopenharmony_ci			}
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	u16 tmp;
92962306a36Sopenharmony_ci	u16 check;
93062306a36Sopenharmony_ci	u16 pipenum;
93162306a36Sopenharmony_ci	struct m66592_ep *ep;
93262306a36Sopenharmony_ci	struct m66592_request *req;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) {
93562306a36Sopenharmony_ci		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		ep = &m66592->ep[0];
93862306a36Sopenharmony_ci		req = list_entry(ep->queue.next, struct m66592_request, queue);
93962306a36Sopenharmony_ci		irq_ep0_write(ep, req);
94062306a36Sopenharmony_ci	} else {
94162306a36Sopenharmony_ci		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
94262306a36Sopenharmony_ci			check = 1 << pipenum;
94362306a36Sopenharmony_ci			if ((status & check) && (enb & check)) {
94462306a36Sopenharmony_ci				m66592_write(m66592, ~check, M66592_BEMPSTS);
94562306a36Sopenharmony_ci				tmp = control_reg_get(m66592, pipenum);
94662306a36Sopenharmony_ci				if ((tmp & M66592_INBUFM) == 0) {
94762306a36Sopenharmony_ci					disable_irq_empty(m66592, pipenum);
94862306a36Sopenharmony_ci					pipe_irq_disable(m66592, pipenum);
94962306a36Sopenharmony_ci					pipe_stop(m66592, pipenum);
95062306a36Sopenharmony_ci					ep = m66592->pipenum2ep[pipenum];
95162306a36Sopenharmony_ci					req = list_entry(ep->queue.next,
95262306a36Sopenharmony_ci							 struct m66592_request,
95362306a36Sopenharmony_ci							 queue);
95462306a36Sopenharmony_ci					if (!list_empty(&ep->queue))
95562306a36Sopenharmony_ci						transfer_complete(ep, req, 0);
95662306a36Sopenharmony_ci				}
95762306a36Sopenharmony_ci			}
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
96362306a36Sopenharmony_ci__releases(m66592->lock)
96462306a36Sopenharmony_ci__acquires(m66592->lock)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	struct m66592_ep *ep;
96762306a36Sopenharmony_ci	u16 pid;
96862306a36Sopenharmony_ci	u16 status = 0;
96962306a36Sopenharmony_ci	u16 w_index = le16_to_cpu(ctrl->wIndex);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
97262306a36Sopenharmony_ci	case USB_RECIP_DEVICE:
97362306a36Sopenharmony_ci		status = 1 << USB_DEVICE_SELF_POWERED;
97462306a36Sopenharmony_ci		break;
97562306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
97662306a36Sopenharmony_ci		status = 0;
97762306a36Sopenharmony_ci		break;
97862306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT:
97962306a36Sopenharmony_ci		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
98062306a36Sopenharmony_ci		pid = control_reg_get_pid(m66592, ep->pipenum);
98162306a36Sopenharmony_ci		if (pid == M66592_PID_STALL)
98262306a36Sopenharmony_ci			status = 1 << USB_ENDPOINT_HALT;
98362306a36Sopenharmony_ci		else
98462306a36Sopenharmony_ci			status = 0;
98562306a36Sopenharmony_ci		break;
98662306a36Sopenharmony_ci	default:
98762306a36Sopenharmony_ci		pipe_stall(m66592, 0);
98862306a36Sopenharmony_ci		return;		/* exit */
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	m66592->ep0_data = cpu_to_le16(status);
99262306a36Sopenharmony_ci	m66592->ep0_req->buf = &m66592->ep0_data;
99362306a36Sopenharmony_ci	m66592->ep0_req->length = 2;
99462306a36Sopenharmony_ci	/* AV: what happens if we get called again before that gets through? */
99562306a36Sopenharmony_ci	spin_unlock(&m66592->lock);
99662306a36Sopenharmony_ci	m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL);
99762306a36Sopenharmony_ci	spin_lock(&m66592->lock);
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
100362306a36Sopenharmony_ci	case USB_RECIP_DEVICE:
100462306a36Sopenharmony_ci		control_end(m66592, 1);
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
100762306a36Sopenharmony_ci		control_end(m66592, 1);
100862306a36Sopenharmony_ci		break;
100962306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT: {
101062306a36Sopenharmony_ci		struct m66592_ep *ep;
101162306a36Sopenharmony_ci		struct m66592_request *req;
101262306a36Sopenharmony_ci		u16 w_index = le16_to_cpu(ctrl->wIndex);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
101562306a36Sopenharmony_ci		pipe_stop(m66592, ep->pipenum);
101662306a36Sopenharmony_ci		control_reg_sqclr(m66592, ep->pipenum);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		control_end(m66592, 1);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		req = list_entry(ep->queue.next,
102162306a36Sopenharmony_ci		struct m66592_request, queue);
102262306a36Sopenharmony_ci		if (ep->busy) {
102362306a36Sopenharmony_ci			ep->busy = 0;
102462306a36Sopenharmony_ci			if (list_empty(&ep->queue))
102562306a36Sopenharmony_ci				break;
102662306a36Sopenharmony_ci			start_packet(ep, req);
102762306a36Sopenharmony_ci		} else if (!list_empty(&ep->queue))
102862306a36Sopenharmony_ci			pipe_start(m66592, ep->pipenum);
102962306a36Sopenharmony_ci		}
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	default:
103262306a36Sopenharmony_ci		pipe_stall(m66592, 0);
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	u16 tmp;
104062306a36Sopenharmony_ci	int timeout = 3000;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
104362306a36Sopenharmony_ci	case USB_RECIP_DEVICE:
104462306a36Sopenharmony_ci		switch (le16_to_cpu(ctrl->wValue)) {
104562306a36Sopenharmony_ci		case USB_DEVICE_TEST_MODE:
104662306a36Sopenharmony_ci			control_end(m66592, 1);
104762306a36Sopenharmony_ci			/* Wait for the completion of status stage */
104862306a36Sopenharmony_ci			do {
104962306a36Sopenharmony_ci				tmp = m66592_read(m66592, M66592_INTSTS0) &
105062306a36Sopenharmony_ci								M66592_CTSQ;
105162306a36Sopenharmony_ci				udelay(1);
105262306a36Sopenharmony_ci			} while (tmp != M66592_CS_IDST && timeout-- > 0);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci			if (tmp == M66592_CS_IDST)
105562306a36Sopenharmony_ci				m66592_bset(m66592,
105662306a36Sopenharmony_ci					    le16_to_cpu(ctrl->wIndex >> 8),
105762306a36Sopenharmony_ci					    M66592_TESTMODE);
105862306a36Sopenharmony_ci			break;
105962306a36Sopenharmony_ci		default:
106062306a36Sopenharmony_ci			pipe_stall(m66592, 0);
106162306a36Sopenharmony_ci			break;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci		break;
106462306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
106562306a36Sopenharmony_ci		control_end(m66592, 1);
106662306a36Sopenharmony_ci		break;
106762306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT: {
106862306a36Sopenharmony_ci		struct m66592_ep *ep;
106962306a36Sopenharmony_ci		u16 w_index = le16_to_cpu(ctrl->wIndex);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
107262306a36Sopenharmony_ci		pipe_stall(m66592, ep->pipenum);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		control_end(m66592, 1);
107562306a36Sopenharmony_ci		}
107662306a36Sopenharmony_ci		break;
107762306a36Sopenharmony_ci	default:
107862306a36Sopenharmony_ci		pipe_stall(m66592, 0);
107962306a36Sopenharmony_ci		break;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci/* if return value is true, call class driver's setup() */
108462306a36Sopenharmony_cistatic int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	u16 *p = (u16 *)ctrl;
108762306a36Sopenharmony_ci	unsigned long offset = M66592_USBREQ;
108862306a36Sopenharmony_ci	int i, ret = 0;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* read fifo */
109162306a36Sopenharmony_ci	m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
109462306a36Sopenharmony_ci		p[i] = m66592_read(m66592, offset + i*2);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* check request */
109762306a36Sopenharmony_ci	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
109862306a36Sopenharmony_ci		switch (ctrl->bRequest) {
109962306a36Sopenharmony_ci		case USB_REQ_GET_STATUS:
110062306a36Sopenharmony_ci			get_status(m66592, ctrl);
110162306a36Sopenharmony_ci			break;
110262306a36Sopenharmony_ci		case USB_REQ_CLEAR_FEATURE:
110362306a36Sopenharmony_ci			clear_feature(m66592, ctrl);
110462306a36Sopenharmony_ci			break;
110562306a36Sopenharmony_ci		case USB_REQ_SET_FEATURE:
110662306a36Sopenharmony_ci			set_feature(m66592, ctrl);
110762306a36Sopenharmony_ci			break;
110862306a36Sopenharmony_ci		default:
110962306a36Sopenharmony_ci			ret = 1;
111062306a36Sopenharmony_ci			break;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci	} else
111362306a36Sopenharmony_ci		ret = 1;
111462306a36Sopenharmony_ci	return ret;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void m66592_update_usb_speed(struct m66592 *m66592)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	u16 speed = get_usb_speed(m66592);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	switch (speed) {
112262306a36Sopenharmony_ci	case M66592_HSMODE:
112362306a36Sopenharmony_ci		m66592->gadget.speed = USB_SPEED_HIGH;
112462306a36Sopenharmony_ci		break;
112562306a36Sopenharmony_ci	case M66592_FSMODE:
112662306a36Sopenharmony_ci		m66592->gadget.speed = USB_SPEED_FULL;
112762306a36Sopenharmony_ci		break;
112862306a36Sopenharmony_ci	default:
112962306a36Sopenharmony_ci		m66592->gadget.speed = USB_SPEED_UNKNOWN;
113062306a36Sopenharmony_ci		pr_err("USB speed unknown\n");
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic void irq_device_state(struct m66592 *m66592)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	u16 dvsq;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ;
113962306a36Sopenharmony_ci	m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (dvsq == M66592_DS_DFLT) {	/* bus reset */
114262306a36Sopenharmony_ci		usb_gadget_udc_reset(&m66592->gadget, m66592->driver);
114362306a36Sopenharmony_ci		m66592_update_usb_speed(m66592);
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci	if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
114662306a36Sopenharmony_ci		m66592_update_usb_speed(m66592);
114762306a36Sopenharmony_ci	if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS)
114862306a36Sopenharmony_ci			&& m66592->gadget.speed == USB_SPEED_UNKNOWN)
114962306a36Sopenharmony_ci		m66592_update_usb_speed(m66592);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	m66592->old_dvsq = dvsq;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic void irq_control_stage(struct m66592 *m66592)
115562306a36Sopenharmony_ci__releases(m66592->lock)
115662306a36Sopenharmony_ci__acquires(m66592->lock)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct usb_ctrlrequest ctrl;
115962306a36Sopenharmony_ci	u16 ctsq;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ;
116262306a36Sopenharmony_ci	m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	switch (ctsq) {
116562306a36Sopenharmony_ci	case M66592_CS_IDST: {
116662306a36Sopenharmony_ci		struct m66592_ep *ep;
116762306a36Sopenharmony_ci		struct m66592_request *req;
116862306a36Sopenharmony_ci		ep = &m66592->ep[0];
116962306a36Sopenharmony_ci		req = list_entry(ep->queue.next, struct m66592_request, queue);
117062306a36Sopenharmony_ci		transfer_complete(ep, req, 0);
117162306a36Sopenharmony_ci		}
117262306a36Sopenharmony_ci		break;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	case M66592_CS_RDDS:
117562306a36Sopenharmony_ci	case M66592_CS_WRDS:
117662306a36Sopenharmony_ci	case M66592_CS_WRND:
117762306a36Sopenharmony_ci		if (setup_packet(m66592, &ctrl)) {
117862306a36Sopenharmony_ci			spin_unlock(&m66592->lock);
117962306a36Sopenharmony_ci			if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0)
118062306a36Sopenharmony_ci				pipe_stall(m66592, 0);
118162306a36Sopenharmony_ci			spin_lock(&m66592->lock);
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci		break;
118462306a36Sopenharmony_ci	case M66592_CS_RDSS:
118562306a36Sopenharmony_ci	case M66592_CS_WRSS:
118662306a36Sopenharmony_ci		control_end(m66592, 0);
118762306a36Sopenharmony_ci		break;
118862306a36Sopenharmony_ci	default:
118962306a36Sopenharmony_ci		pr_err("ctrl_stage: unexpect ctsq(%x)\n", ctsq);
119062306a36Sopenharmony_ci		break;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic irqreturn_t m66592_irq(int irq, void *_m66592)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct m66592 *m66592 = _m66592;
119762306a36Sopenharmony_ci	u16 intsts0;
119862306a36Sopenharmony_ci	u16 intenb0;
119962306a36Sopenharmony_ci	u16 savepipe;
120062306a36Sopenharmony_ci	u16 mask0;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	spin_lock(&m66592->lock);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	intsts0 = m66592_read(m66592, M66592_INTSTS0);
120562306a36Sopenharmony_ci	intenb0 = m66592_read(m66592, M66592_INTENB0);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (m66592->pdata->on_chip && !intsts0 && !intenb0) {
120862306a36Sopenharmony_ci		/*
120962306a36Sopenharmony_ci		 * When USB clock stops, it cannot read register. Even if a
121062306a36Sopenharmony_ci		 * clock stops, the interrupt occurs. So this driver turn on
121162306a36Sopenharmony_ci		 * a clock by this timing and do re-reading of register.
121262306a36Sopenharmony_ci		 */
121362306a36Sopenharmony_ci		m66592_start_xclock(m66592);
121462306a36Sopenharmony_ci		intsts0 = m66592_read(m66592, M66592_INTSTS0);
121562306a36Sopenharmony_ci		intenb0 = m66592_read(m66592, M66592_INTENB0);
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	savepipe = m66592_read(m66592, M66592_CFIFOSEL);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	mask0 = intsts0 & intenb0;
122162306a36Sopenharmony_ci	if (mask0) {
122262306a36Sopenharmony_ci		u16 brdysts = m66592_read(m66592, M66592_BRDYSTS);
122362306a36Sopenharmony_ci		u16 bempsts = m66592_read(m66592, M66592_BEMPSTS);
122462306a36Sopenharmony_ci		u16 brdyenb = m66592_read(m66592, M66592_BRDYENB);
122562306a36Sopenharmony_ci		u16 bempenb = m66592_read(m66592, M66592_BEMPENB);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		if (mask0 & M66592_VBINT) {
122862306a36Sopenharmony_ci			m66592_write(m66592,  0xffff & ~M66592_VBINT,
122962306a36Sopenharmony_ci					M66592_INTSTS0);
123062306a36Sopenharmony_ci			m66592_start_xclock(m66592);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci			/* start vbus sampling */
123362306a36Sopenharmony_ci			m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0)
123462306a36Sopenharmony_ci					& M66592_VBSTS;
123562306a36Sopenharmony_ci			m66592->scount = M66592_MAX_SAMPLING;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci			mod_timer(&m66592->timer,
123862306a36Sopenharmony_ci					jiffies + msecs_to_jiffies(50));
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci		if (intsts0 & M66592_DVSQ)
124162306a36Sopenharmony_ci			irq_device_state(m66592);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci		if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE)
124462306a36Sopenharmony_ci				&& (brdysts & brdyenb)) {
124562306a36Sopenharmony_ci			irq_pipe_ready(m66592, brdysts, brdyenb);
124662306a36Sopenharmony_ci		}
124762306a36Sopenharmony_ci		if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE)
124862306a36Sopenharmony_ci				&& (bempsts & bempenb)) {
124962306a36Sopenharmony_ci			irq_pipe_empty(m66592, bempsts, bempenb);
125062306a36Sopenharmony_ci		}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		if (intsts0 & M66592_CTRT)
125362306a36Sopenharmony_ci			irq_control_stage(m66592);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	m66592_write(m66592, savepipe, M66592_CFIFOSEL);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	spin_unlock(&m66592->lock);
125962306a36Sopenharmony_ci	return IRQ_HANDLED;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic void m66592_timer(struct timer_list *t)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct m66592 *m66592 = from_timer(m66592, t, timer);
126562306a36Sopenharmony_ci	unsigned long flags;
126662306a36Sopenharmony_ci	u16 tmp;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	spin_lock_irqsave(&m66592->lock, flags);
126962306a36Sopenharmony_ci	tmp = m66592_read(m66592, M66592_SYSCFG);
127062306a36Sopenharmony_ci	if (!(tmp & M66592_RCKE)) {
127162306a36Sopenharmony_ci		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
127262306a36Sopenharmony_ci		udelay(10);
127362306a36Sopenharmony_ci		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci	if (m66592->scount > 0) {
127662306a36Sopenharmony_ci		tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS;
127762306a36Sopenharmony_ci		if (tmp == m66592->old_vbus) {
127862306a36Sopenharmony_ci			m66592->scount--;
127962306a36Sopenharmony_ci			if (m66592->scount == 0) {
128062306a36Sopenharmony_ci				if (tmp == M66592_VBSTS)
128162306a36Sopenharmony_ci					m66592_usb_connect(m66592);
128262306a36Sopenharmony_ci				else
128362306a36Sopenharmony_ci					m66592_usb_disconnect(m66592);
128462306a36Sopenharmony_ci			} else {
128562306a36Sopenharmony_ci				mod_timer(&m66592->timer,
128662306a36Sopenharmony_ci					jiffies + msecs_to_jiffies(50));
128762306a36Sopenharmony_ci			}
128862306a36Sopenharmony_ci		} else {
128962306a36Sopenharmony_ci			m66592->scount = M66592_MAX_SAMPLING;
129062306a36Sopenharmony_ci			m66592->old_vbus = tmp;
129162306a36Sopenharmony_ci			mod_timer(&m66592->timer,
129262306a36Sopenharmony_ci					jiffies + msecs_to_jiffies(50));
129362306a36Sopenharmony_ci		}
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci	spin_unlock_irqrestore(&m66592->lock, flags);
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
129962306a36Sopenharmony_cistatic int m66592_enable(struct usb_ep *_ep,
130062306a36Sopenharmony_ci			 const struct usb_endpoint_descriptor *desc)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct m66592_ep *ep;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	ep = container_of(_ep, struct m66592_ep, ep);
130562306a36Sopenharmony_ci	return alloc_pipe_config(ep, desc);
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cistatic int m66592_disable(struct usb_ep *_ep)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct m66592_ep *ep;
131162306a36Sopenharmony_ci	struct m66592_request *req;
131262306a36Sopenharmony_ci	unsigned long flags;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	ep = container_of(_ep, struct m66592_ep, ep);
131562306a36Sopenharmony_ci	BUG_ON(!ep);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	while (!list_empty(&ep->queue)) {
131862306a36Sopenharmony_ci		req = list_entry(ep->queue.next, struct m66592_request, queue);
131962306a36Sopenharmony_ci		spin_lock_irqsave(&ep->m66592->lock, flags);
132062306a36Sopenharmony_ci		transfer_complete(ep, req, -ECONNRESET);
132162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ep->m66592->lock, flags);
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	pipe_irq_disable(ep->m66592, ep->pipenum);
132562306a36Sopenharmony_ci	return free_pipe_config(ep);
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
132962306a36Sopenharmony_ci						gfp_t gfp_flags)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct m66592_request *req;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	req = kzalloc(sizeof(struct m66592_request), gfp_flags);
133462306a36Sopenharmony_ci	if (!req)
133562306a36Sopenharmony_ci		return NULL;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	INIT_LIST_HEAD(&req->queue);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return &req->req;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	struct m66592_request *req;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	req = container_of(_req, struct m66592_request, req);
134762306a36Sopenharmony_ci	kfree(req);
134862306a36Sopenharmony_ci}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistatic int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
135162306a36Sopenharmony_ci			gfp_t gfp_flags)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct m66592_ep *ep;
135462306a36Sopenharmony_ci	struct m66592_request *req;
135562306a36Sopenharmony_ci	unsigned long flags;
135662306a36Sopenharmony_ci	int request = 0;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	ep = container_of(_ep, struct m66592_ep, ep);
135962306a36Sopenharmony_ci	req = container_of(_req, struct m66592_request, req);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
136262306a36Sopenharmony_ci		return -ESHUTDOWN;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	spin_lock_irqsave(&ep->m66592->lock, flags);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (list_empty(&ep->queue))
136762306a36Sopenharmony_ci		request = 1;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	list_add_tail(&req->queue, &ep->queue);
137062306a36Sopenharmony_ci	req->req.actual = 0;
137162306a36Sopenharmony_ci	req->req.status = -EINPROGRESS;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (ep->ep.desc == NULL)	/* control */
137462306a36Sopenharmony_ci		start_ep0(ep, req);
137562306a36Sopenharmony_ci	else {
137662306a36Sopenharmony_ci		if (request && !ep->busy)
137762306a36Sopenharmony_ci			start_packet(ep, req);
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ep->m66592->lock, flags);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return 0;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct m66592_ep *ep;
138862306a36Sopenharmony_ci	struct m66592_request *req;
138962306a36Sopenharmony_ci	unsigned long flags;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	ep = container_of(_ep, struct m66592_ep, ep);
139262306a36Sopenharmony_ci	req = container_of(_req, struct m66592_request, req);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	spin_lock_irqsave(&ep->m66592->lock, flags);
139562306a36Sopenharmony_ci	if (!list_empty(&ep->queue))
139662306a36Sopenharmony_ci		transfer_complete(ep, req, -ECONNRESET);
139762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ep->m66592->lock, flags);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	return 0;
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cistatic int m66592_set_halt(struct usb_ep *_ep, int value)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	struct m66592_ep *ep = container_of(_ep, struct m66592_ep, ep);
140562306a36Sopenharmony_ci	unsigned long flags;
140662306a36Sopenharmony_ci	int ret = 0;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	spin_lock_irqsave(&ep->m66592->lock, flags);
140962306a36Sopenharmony_ci	if (!list_empty(&ep->queue)) {
141062306a36Sopenharmony_ci		ret = -EAGAIN;
141162306a36Sopenharmony_ci	} else if (value) {
141262306a36Sopenharmony_ci		ep->busy = 1;
141362306a36Sopenharmony_ci		pipe_stall(ep->m66592, ep->pipenum);
141462306a36Sopenharmony_ci	} else {
141562306a36Sopenharmony_ci		ep->busy = 0;
141662306a36Sopenharmony_ci		pipe_stop(ep->m66592, ep->pipenum);
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ep->m66592->lock, flags);
141962306a36Sopenharmony_ci	return ret;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic void m66592_fifo_flush(struct usb_ep *_ep)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	struct m66592_ep *ep;
142562306a36Sopenharmony_ci	unsigned long flags;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	ep = container_of(_ep, struct m66592_ep, ep);
142862306a36Sopenharmony_ci	spin_lock_irqsave(&ep->m66592->lock, flags);
142962306a36Sopenharmony_ci	if (list_empty(&ep->queue) && !ep->busy) {
143062306a36Sopenharmony_ci		pipe_stop(ep->m66592, ep->pipenum);
143162306a36Sopenharmony_ci		m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr);
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ep->m66592->lock, flags);
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic const struct usb_ep_ops m66592_ep_ops = {
143762306a36Sopenharmony_ci	.enable		= m66592_enable,
143862306a36Sopenharmony_ci	.disable	= m66592_disable,
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	.alloc_request	= m66592_alloc_request,
144162306a36Sopenharmony_ci	.free_request	= m66592_free_request,
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	.queue		= m66592_queue,
144462306a36Sopenharmony_ci	.dequeue	= m66592_dequeue,
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	.set_halt	= m66592_set_halt,
144762306a36Sopenharmony_ci	.fifo_flush	= m66592_fifo_flush,
144862306a36Sopenharmony_ci};
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
145162306a36Sopenharmony_cistatic int m66592_udc_start(struct usb_gadget *g,
145262306a36Sopenharmony_ci		struct usb_gadget_driver *driver)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct m66592 *m66592 = to_m66592(g);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* hook up the driver */
145762306a36Sopenharmony_ci	m66592->driver = driver;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
146062306a36Sopenharmony_ci	if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
146162306a36Sopenharmony_ci		m66592_start_xclock(m66592);
146262306a36Sopenharmony_ci		/* start vbus sampling */
146362306a36Sopenharmony_ci		m66592->old_vbus = m66592_read(m66592,
146462306a36Sopenharmony_ci					 M66592_INTSTS0) & M66592_VBSTS;
146562306a36Sopenharmony_ci		m66592->scount = M66592_MAX_SAMPLING;
146662306a36Sopenharmony_ci		mod_timer(&m66592->timer, jiffies + msecs_to_jiffies(50));
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	return 0;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic int m66592_udc_stop(struct usb_gadget *g)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct m66592 *m66592 = to_m66592(g);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	init_controller(m66592);
147962306a36Sopenharmony_ci	disable_controller(m66592);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	m66592->driver = NULL;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	return 0;
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
148762306a36Sopenharmony_cistatic int m66592_get_frame(struct usb_gadget *_gadget)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	struct m66592 *m66592 = gadget_to_m66592(_gadget);
149062306a36Sopenharmony_ci	return m66592_read(m66592, M66592_FRMNUM) & 0x03FF;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_cistatic int m66592_pullup(struct usb_gadget *gadget, int is_on)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct m66592 *m66592 = gadget_to_m66592(gadget);
149662306a36Sopenharmony_ci	unsigned long flags;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	spin_lock_irqsave(&m66592->lock, flags);
149962306a36Sopenharmony_ci	if (is_on)
150062306a36Sopenharmony_ci		m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
150162306a36Sopenharmony_ci	else
150262306a36Sopenharmony_ci		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
150362306a36Sopenharmony_ci	spin_unlock_irqrestore(&m66592->lock, flags);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	return 0;
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic const struct usb_gadget_ops m66592_gadget_ops = {
150962306a36Sopenharmony_ci	.get_frame		= m66592_get_frame,
151062306a36Sopenharmony_ci	.udc_start		= m66592_udc_start,
151162306a36Sopenharmony_ci	.udc_stop		= m66592_udc_stop,
151262306a36Sopenharmony_ci	.pullup			= m66592_pullup,
151362306a36Sopenharmony_ci};
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic void m66592_remove(struct platform_device *pdev)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct m66592		*m66592 = platform_get_drvdata(pdev);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	usb_del_gadget_udc(&m66592->gadget);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	timer_shutdown_sync(&m66592->timer);
152262306a36Sopenharmony_ci	iounmap(m66592->reg);
152362306a36Sopenharmony_ci	free_irq(platform_get_irq(pdev, 0), m66592);
152462306a36Sopenharmony_ci	m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
152562306a36Sopenharmony_ci	if (m66592->pdata->on_chip) {
152662306a36Sopenharmony_ci		clk_disable(m66592->clk);
152762306a36Sopenharmony_ci		clk_put(m66592->clk);
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci	kfree(m66592);
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic void nop_completion(struct usb_ep *ep, struct usb_request *r)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cistatic int m66592_probe(struct platform_device *pdev)
153762306a36Sopenharmony_ci{
153862306a36Sopenharmony_ci	struct resource *res, *ires;
153962306a36Sopenharmony_ci	void __iomem *reg = NULL;
154062306a36Sopenharmony_ci	struct m66592 *m66592 = NULL;
154162306a36Sopenharmony_ci	char clk_name[8];
154262306a36Sopenharmony_ci	int ret = 0;
154362306a36Sopenharmony_ci	int i;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
154662306a36Sopenharmony_ci	if (!res) {
154762306a36Sopenharmony_ci		ret = -ENODEV;
154862306a36Sopenharmony_ci		pr_err("platform_get_resource error.\n");
154962306a36Sopenharmony_ci		goto clean_up;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
155362306a36Sopenharmony_ci	if (!ires) {
155462306a36Sopenharmony_ci		ret = -ENODEV;
155562306a36Sopenharmony_ci		dev_err(&pdev->dev,
155662306a36Sopenharmony_ci			"platform_get_resource IORESOURCE_IRQ error.\n");
155762306a36Sopenharmony_ci		goto clean_up;
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	reg = ioremap(res->start, resource_size(res));
156162306a36Sopenharmony_ci	if (reg == NULL) {
156262306a36Sopenharmony_ci		ret = -ENOMEM;
156362306a36Sopenharmony_ci		pr_err("ioremap error.\n");
156462306a36Sopenharmony_ci		goto clean_up;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (dev_get_platdata(&pdev->dev) == NULL) {
156862306a36Sopenharmony_ci		dev_err(&pdev->dev, "no platform data\n");
156962306a36Sopenharmony_ci		ret = -ENODEV;
157062306a36Sopenharmony_ci		goto clean_up;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	/* initialize ucd */
157462306a36Sopenharmony_ci	m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
157562306a36Sopenharmony_ci	if (m66592 == NULL) {
157662306a36Sopenharmony_ci		ret = -ENOMEM;
157762306a36Sopenharmony_ci		goto clean_up;
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	m66592->pdata = dev_get_platdata(&pdev->dev);
158162306a36Sopenharmony_ci	m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	spin_lock_init(&m66592->lock);
158462306a36Sopenharmony_ci	platform_set_drvdata(pdev, m66592);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	m66592->gadget.ops = &m66592_gadget_ops;
158762306a36Sopenharmony_ci	m66592->gadget.max_speed = USB_SPEED_HIGH;
158862306a36Sopenharmony_ci	m66592->gadget.name = udc_name;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	timer_setup(&m66592->timer, m66592_timer, 0);
159162306a36Sopenharmony_ci	m66592->reg = reg;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	ret = request_irq(ires->start, m66592_irq, IRQF_SHARED,
159462306a36Sopenharmony_ci			udc_name, m66592);
159562306a36Sopenharmony_ci	if (ret < 0) {
159662306a36Sopenharmony_ci		pr_err("request_irq error (%d)\n", ret);
159762306a36Sopenharmony_ci		goto clean_up;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	if (m66592->pdata->on_chip) {
160162306a36Sopenharmony_ci		snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
160262306a36Sopenharmony_ci		m66592->clk = clk_get(&pdev->dev, clk_name);
160362306a36Sopenharmony_ci		if (IS_ERR(m66592->clk)) {
160462306a36Sopenharmony_ci			dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
160562306a36Sopenharmony_ci				clk_name);
160662306a36Sopenharmony_ci			ret = PTR_ERR(m66592->clk);
160762306a36Sopenharmony_ci			goto clean_up2;
160862306a36Sopenharmony_ci		}
160962306a36Sopenharmony_ci		clk_enable(m66592->clk);
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	INIT_LIST_HEAD(&m66592->gadget.ep_list);
161362306a36Sopenharmony_ci	m66592->gadget.ep0 = &m66592->ep[0].ep;
161462306a36Sopenharmony_ci	INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
161562306a36Sopenharmony_ci	for (i = 0; i < M66592_MAX_NUM_PIPE; i++) {
161662306a36Sopenharmony_ci		struct m66592_ep *ep = &m66592->ep[i];
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		if (i != 0) {
161962306a36Sopenharmony_ci			INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list);
162062306a36Sopenharmony_ci			list_add_tail(&m66592->ep[i].ep.ep_list,
162162306a36Sopenharmony_ci					&m66592->gadget.ep_list);
162262306a36Sopenharmony_ci		}
162362306a36Sopenharmony_ci		ep->m66592 = m66592;
162462306a36Sopenharmony_ci		INIT_LIST_HEAD(&ep->queue);
162562306a36Sopenharmony_ci		ep->ep.name = m66592_ep_name[i];
162662306a36Sopenharmony_ci		ep->ep.ops = &m66592_ep_ops;
162762306a36Sopenharmony_ci		usb_ep_set_maxpacket_limit(&ep->ep, 512);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci		if (i == 0) {
163062306a36Sopenharmony_ci			ep->ep.caps.type_control = true;
163162306a36Sopenharmony_ci		} else {
163262306a36Sopenharmony_ci			ep->ep.caps.type_iso = true;
163362306a36Sopenharmony_ci			ep->ep.caps.type_bulk = true;
163462306a36Sopenharmony_ci			ep->ep.caps.type_int = true;
163562306a36Sopenharmony_ci		}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci		ep->ep.caps.dir_in = true;
163862306a36Sopenharmony_ci		ep->ep.caps.dir_out = true;
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci	usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64);
164162306a36Sopenharmony_ci	m66592->ep[0].pipenum = 0;
164262306a36Sopenharmony_ci	m66592->ep[0].fifoaddr = M66592_CFIFO;
164362306a36Sopenharmony_ci	m66592->ep[0].fifosel = M66592_CFIFOSEL;
164462306a36Sopenharmony_ci	m66592->ep[0].fifoctr = M66592_CFIFOCTR;
164562306a36Sopenharmony_ci	m66592->ep[0].fifotrn = 0;
164662306a36Sopenharmony_ci	m66592->ep[0].pipectr = get_pipectr_addr(0);
164762306a36Sopenharmony_ci	m66592->pipenum2ep[0] = &m66592->ep[0];
164862306a36Sopenharmony_ci	m66592->epaddr2ep[0] = &m66592->ep[0];
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
165162306a36Sopenharmony_ci	if (m66592->ep0_req == NULL) {
165262306a36Sopenharmony_ci		ret = -ENOMEM;
165362306a36Sopenharmony_ci		goto clean_up3;
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci	m66592->ep0_req->complete = nop_completion;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	init_controller(m66592);
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget);
166062306a36Sopenharmony_ci	if (ret)
166162306a36Sopenharmony_ci		goto err_add_udc;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
166462306a36Sopenharmony_ci	return 0;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cierr_add_udc:
166762306a36Sopenharmony_ci	m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
166862306a36Sopenharmony_ci	m66592->ep0_req = NULL;
166962306a36Sopenharmony_ciclean_up3:
167062306a36Sopenharmony_ci	if (m66592->pdata->on_chip) {
167162306a36Sopenharmony_ci		clk_disable(m66592->clk);
167262306a36Sopenharmony_ci		clk_put(m66592->clk);
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ciclean_up2:
167562306a36Sopenharmony_ci	free_irq(ires->start, m66592);
167662306a36Sopenharmony_ciclean_up:
167762306a36Sopenharmony_ci	if (m66592) {
167862306a36Sopenharmony_ci		if (m66592->ep0_req)
167962306a36Sopenharmony_ci			m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
168062306a36Sopenharmony_ci		kfree(m66592);
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci	if (reg)
168362306a36Sopenharmony_ci		iounmap(reg);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return ret;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
168962306a36Sopenharmony_cistatic struct platform_driver m66592_driver = {
169062306a36Sopenharmony_ci	.remove_new =	m66592_remove,
169162306a36Sopenharmony_ci	.driver		= {
169262306a36Sopenharmony_ci		.name =	udc_name,
169362306a36Sopenharmony_ci	},
169462306a36Sopenharmony_ci};
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cimodule_platform_driver_probe(m66592_driver, m66592_probe);
1697