xref: /linux/drivers/usb/gadget/udc/m66592-udc.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
290fccb52SAndrzej Pietrasiewicz /*
390fccb52SAndrzej Pietrasiewicz  * M66592 UDC (USB gadget)
490fccb52SAndrzej Pietrasiewicz  *
590fccb52SAndrzej Pietrasiewicz  * Copyright (C) 2006-2007 Renesas Solutions Corp.
690fccb52SAndrzej Pietrasiewicz  *
790fccb52SAndrzej Pietrasiewicz  * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
890fccb52SAndrzej Pietrasiewicz  */
990fccb52SAndrzej Pietrasiewicz 
1090fccb52SAndrzej Pietrasiewicz #include <linux/module.h>
1190fccb52SAndrzej Pietrasiewicz #include <linux/interrupt.h>
1290fccb52SAndrzej Pietrasiewicz #include <linux/delay.h>
1390fccb52SAndrzej Pietrasiewicz #include <linux/io.h>
1490fccb52SAndrzej Pietrasiewicz #include <linux/platform_device.h>
1590fccb52SAndrzej Pietrasiewicz #include <linux/slab.h>
1690fccb52SAndrzej Pietrasiewicz #include <linux/err.h>
1790fccb52SAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
1890fccb52SAndrzej Pietrasiewicz #include <linux/usb/gadget.h>
1990fccb52SAndrzej Pietrasiewicz 
2090fccb52SAndrzej Pietrasiewicz #include "m66592-udc.h"
2190fccb52SAndrzej Pietrasiewicz 
2290fccb52SAndrzej Pietrasiewicz MODULE_DESCRIPTION("M66592 USB gadget driver");
2390fccb52SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
2490fccb52SAndrzej Pietrasiewicz MODULE_AUTHOR("Yoshihiro Shimoda");
2590fccb52SAndrzej Pietrasiewicz MODULE_ALIAS("platform:m66592_udc");
2690fccb52SAndrzej Pietrasiewicz 
2790fccb52SAndrzej Pietrasiewicz #define DRIVER_VERSION	"21 July 2009"
2890fccb52SAndrzej Pietrasiewicz 
2990fccb52SAndrzej Pietrasiewicz static const char udc_name[] = "m66592_udc";
3090fccb52SAndrzej Pietrasiewicz static const char *m66592_ep_name[] = {
3190fccb52SAndrzej Pietrasiewicz 	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
3290fccb52SAndrzej Pietrasiewicz };
3390fccb52SAndrzej Pietrasiewicz 
3490fccb52SAndrzej Pietrasiewicz static void disable_controller(struct m66592 *m66592);
3590fccb52SAndrzej Pietrasiewicz static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);
3690fccb52SAndrzej Pietrasiewicz static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);
3790fccb52SAndrzej Pietrasiewicz static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
3890fccb52SAndrzej Pietrasiewicz 			gfp_t gfp_flags);
3990fccb52SAndrzej Pietrasiewicz 
4090fccb52SAndrzej Pietrasiewicz static void transfer_complete(struct m66592_ep *ep,
4190fccb52SAndrzej Pietrasiewicz 		struct m66592_request *req, int status);
4290fccb52SAndrzej Pietrasiewicz 
4390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
get_usb_speed(struct m66592 * m66592)4490fccb52SAndrzej Pietrasiewicz static inline u16 get_usb_speed(struct m66592 *m66592)
4590fccb52SAndrzej Pietrasiewicz {
4690fccb52SAndrzej Pietrasiewicz 	return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);
4790fccb52SAndrzej Pietrasiewicz }
4890fccb52SAndrzej Pietrasiewicz 
enable_pipe_irq(struct m66592 * m66592,u16 pipenum,unsigned long reg)4990fccb52SAndrzej Pietrasiewicz static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum,
5090fccb52SAndrzej Pietrasiewicz 		unsigned long reg)
5190fccb52SAndrzej Pietrasiewicz {
5290fccb52SAndrzej Pietrasiewicz 	u16 tmp;
5390fccb52SAndrzej Pietrasiewicz 
5490fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, M66592_INTENB0);
5590fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
5690fccb52SAndrzej Pietrasiewicz 			M66592_INTENB0);
5790fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, (1 << pipenum), reg);
5890fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, tmp, M66592_INTENB0);
5990fccb52SAndrzej Pietrasiewicz }
6090fccb52SAndrzej Pietrasiewicz 
disable_pipe_irq(struct m66592 * m66592,u16 pipenum,unsigned long reg)6190fccb52SAndrzej Pietrasiewicz static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum,
6290fccb52SAndrzej Pietrasiewicz 		unsigned long reg)
6390fccb52SAndrzej Pietrasiewicz {
6490fccb52SAndrzej Pietrasiewicz 	u16 tmp;
6590fccb52SAndrzej Pietrasiewicz 
6690fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, M66592_INTENB0);
6790fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
6890fccb52SAndrzej Pietrasiewicz 			M66592_INTENB0);
6990fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, (1 << pipenum), reg);
7090fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, tmp, M66592_INTENB0);
7190fccb52SAndrzej Pietrasiewicz }
7290fccb52SAndrzej Pietrasiewicz 
m66592_usb_connect(struct m66592 * m66592)7390fccb52SAndrzej Pietrasiewicz static void m66592_usb_connect(struct m66592 *m66592)
7490fccb52SAndrzej Pietrasiewicz {
7590fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_CTRE, M66592_INTENB0);
7690fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
7790fccb52SAndrzej Pietrasiewicz 			M66592_INTENB0);
7890fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
7990fccb52SAndrzej Pietrasiewicz 
8090fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
8190fccb52SAndrzej Pietrasiewicz }
8290fccb52SAndrzej Pietrasiewicz 
m66592_usb_disconnect(struct m66592 * m66592)8390fccb52SAndrzej Pietrasiewicz static void m66592_usb_disconnect(struct m66592 *m66592)
8490fccb52SAndrzej Pietrasiewicz __releases(m66592->lock)
8590fccb52SAndrzej Pietrasiewicz __acquires(m66592->lock)
8690fccb52SAndrzej Pietrasiewicz {
8790fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0);
8890fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
8990fccb52SAndrzej Pietrasiewicz 			M66592_INTENB0);
9090fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
9190fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
9290fccb52SAndrzej Pietrasiewicz 
9390fccb52SAndrzej Pietrasiewicz 	m66592->gadget.speed = USB_SPEED_UNKNOWN;
9490fccb52SAndrzej Pietrasiewicz 	spin_unlock(&m66592->lock);
9590fccb52SAndrzej Pietrasiewicz 	m66592->driver->disconnect(&m66592->gadget);
9690fccb52SAndrzej Pietrasiewicz 	spin_lock(&m66592->lock);
9790fccb52SAndrzej Pietrasiewicz 
9890fccb52SAndrzej Pietrasiewicz 	disable_controller(m66592);
9990fccb52SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&m66592->ep[0].queue);
10090fccb52SAndrzej Pietrasiewicz }
10190fccb52SAndrzej Pietrasiewicz 
control_reg_get_pid(struct m66592 * m66592,u16 pipenum)10290fccb52SAndrzej Pietrasiewicz static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum)
10390fccb52SAndrzej Pietrasiewicz {
10490fccb52SAndrzej Pietrasiewicz 	u16 pid = 0;
10590fccb52SAndrzej Pietrasiewicz 	unsigned long offset;
10690fccb52SAndrzej Pietrasiewicz 
10790fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0)
10890fccb52SAndrzej Pietrasiewicz 		pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID;
10990fccb52SAndrzej Pietrasiewicz 	else if (pipenum < M66592_MAX_NUM_PIPE) {
11090fccb52SAndrzej Pietrasiewicz 		offset = get_pipectr_addr(pipenum);
11190fccb52SAndrzej Pietrasiewicz 		pid = m66592_read(m66592, offset) & M66592_PID;
11290fccb52SAndrzej Pietrasiewicz 	} else
11390fccb52SAndrzej Pietrasiewicz 		pr_err("unexpect pipe num (%d)\n", pipenum);
11490fccb52SAndrzej Pietrasiewicz 
11590fccb52SAndrzej Pietrasiewicz 	return pid;
11690fccb52SAndrzej Pietrasiewicz }
11790fccb52SAndrzej Pietrasiewicz 
control_reg_set_pid(struct m66592 * m66592,u16 pipenum,u16 pid)11890fccb52SAndrzej Pietrasiewicz static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum,
11990fccb52SAndrzej Pietrasiewicz 		u16 pid)
12090fccb52SAndrzej Pietrasiewicz {
12190fccb52SAndrzej Pietrasiewicz 	unsigned long offset;
12290fccb52SAndrzej Pietrasiewicz 
12390fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0)
12490fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR);
12590fccb52SAndrzej Pietrasiewicz 	else if (pipenum < M66592_MAX_NUM_PIPE) {
12690fccb52SAndrzej Pietrasiewicz 		offset = get_pipectr_addr(pipenum);
12790fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, pid, M66592_PID, offset);
12890fccb52SAndrzej Pietrasiewicz 	} else
12990fccb52SAndrzej Pietrasiewicz 		pr_err("unexpect pipe num (%d)\n", pipenum);
13090fccb52SAndrzej Pietrasiewicz }
13190fccb52SAndrzej Pietrasiewicz 
pipe_start(struct m66592 * m66592,u16 pipenum)13290fccb52SAndrzej Pietrasiewicz static inline void pipe_start(struct m66592 *m66592, u16 pipenum)
13390fccb52SAndrzej Pietrasiewicz {
13490fccb52SAndrzej Pietrasiewicz 	control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);
13590fccb52SAndrzej Pietrasiewicz }
13690fccb52SAndrzej Pietrasiewicz 
pipe_stop(struct m66592 * m66592,u16 pipenum)13790fccb52SAndrzej Pietrasiewicz static inline void pipe_stop(struct m66592 *m66592, u16 pipenum)
13890fccb52SAndrzej Pietrasiewicz {
13990fccb52SAndrzej Pietrasiewicz 	control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);
14090fccb52SAndrzej Pietrasiewicz }
14190fccb52SAndrzej Pietrasiewicz 
pipe_stall(struct m66592 * m66592,u16 pipenum)14290fccb52SAndrzej Pietrasiewicz static inline void pipe_stall(struct m66592 *m66592, u16 pipenum)
14390fccb52SAndrzej Pietrasiewicz {
14490fccb52SAndrzej Pietrasiewicz 	control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);
14590fccb52SAndrzej Pietrasiewicz }
14690fccb52SAndrzej Pietrasiewicz 
control_reg_get(struct m66592 * m66592,u16 pipenum)14790fccb52SAndrzej Pietrasiewicz static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum)
14890fccb52SAndrzej Pietrasiewicz {
14990fccb52SAndrzej Pietrasiewicz 	u16 ret = 0;
15090fccb52SAndrzej Pietrasiewicz 	unsigned long offset;
15190fccb52SAndrzej Pietrasiewicz 
15290fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0)
15390fccb52SAndrzej Pietrasiewicz 		ret = m66592_read(m66592, M66592_DCPCTR);
15490fccb52SAndrzej Pietrasiewicz 	else if (pipenum < M66592_MAX_NUM_PIPE) {
15590fccb52SAndrzej Pietrasiewicz 		offset = get_pipectr_addr(pipenum);
15690fccb52SAndrzej Pietrasiewicz 		ret = m66592_read(m66592, offset);
15790fccb52SAndrzej Pietrasiewicz 	} else
15890fccb52SAndrzej Pietrasiewicz 		pr_err("unexpect pipe num (%d)\n", pipenum);
15990fccb52SAndrzej Pietrasiewicz 
16090fccb52SAndrzej Pietrasiewicz 	return ret;
16190fccb52SAndrzej Pietrasiewicz }
16290fccb52SAndrzej Pietrasiewicz 
control_reg_sqclr(struct m66592 * m66592,u16 pipenum)16390fccb52SAndrzej Pietrasiewicz static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum)
16490fccb52SAndrzej Pietrasiewicz {
16590fccb52SAndrzej Pietrasiewicz 	unsigned long offset;
16690fccb52SAndrzej Pietrasiewicz 
16790fccb52SAndrzej Pietrasiewicz 	pipe_stop(m66592, pipenum);
16890fccb52SAndrzej Pietrasiewicz 
16990fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0)
17090fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR);
17190fccb52SAndrzej Pietrasiewicz 	else if (pipenum < M66592_MAX_NUM_PIPE) {
17290fccb52SAndrzej Pietrasiewicz 		offset = get_pipectr_addr(pipenum);
17390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_SQCLR, offset);
17490fccb52SAndrzej Pietrasiewicz 	} else
17590fccb52SAndrzej Pietrasiewicz 		pr_err("unexpect pipe num(%d)\n", pipenum);
17690fccb52SAndrzej Pietrasiewicz }
17790fccb52SAndrzej Pietrasiewicz 
get_buffer_size(struct m66592 * m66592,u16 pipenum)17890fccb52SAndrzej Pietrasiewicz static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
17990fccb52SAndrzej Pietrasiewicz {
18090fccb52SAndrzej Pietrasiewicz 	u16 tmp;
18190fccb52SAndrzej Pietrasiewicz 	int size;
18290fccb52SAndrzej Pietrasiewicz 
18390fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0) {
18490fccb52SAndrzej Pietrasiewicz 		tmp = m66592_read(m66592, M66592_DCPCFG);
18590fccb52SAndrzej Pietrasiewicz 		if ((tmp & M66592_CNTMD) != 0)
18690fccb52SAndrzej Pietrasiewicz 			size = 256;
18790fccb52SAndrzej Pietrasiewicz 		else {
18890fccb52SAndrzej Pietrasiewicz 			tmp = m66592_read(m66592, M66592_DCPMAXP);
18990fccb52SAndrzej Pietrasiewicz 			size = tmp & M66592_MAXP;
19090fccb52SAndrzej Pietrasiewicz 		}
19190fccb52SAndrzej Pietrasiewicz 	} else {
19290fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, pipenum, M66592_PIPESEL);
19390fccb52SAndrzej Pietrasiewicz 		tmp = m66592_read(m66592, M66592_PIPECFG);
19490fccb52SAndrzej Pietrasiewicz 		if ((tmp & M66592_CNTMD) != 0) {
19590fccb52SAndrzej Pietrasiewicz 			tmp = m66592_read(m66592, M66592_PIPEBUF);
19690fccb52SAndrzej Pietrasiewicz 			size = ((tmp >> 10) + 1) * 64;
19790fccb52SAndrzej Pietrasiewicz 		} else {
19890fccb52SAndrzej Pietrasiewicz 			tmp = m66592_read(m66592, M66592_PIPEMAXP);
19990fccb52SAndrzej Pietrasiewicz 			size = tmp & M66592_MXPS;
20090fccb52SAndrzej Pietrasiewicz 		}
20190fccb52SAndrzej Pietrasiewicz 	}
20290fccb52SAndrzej Pietrasiewicz 
20390fccb52SAndrzej Pietrasiewicz 	return size;
20490fccb52SAndrzej Pietrasiewicz }
20590fccb52SAndrzej Pietrasiewicz 
pipe_change(struct m66592 * m66592,u16 pipenum)20690fccb52SAndrzej Pietrasiewicz static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
20790fccb52SAndrzej Pietrasiewicz {
20890fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
20990fccb52SAndrzej Pietrasiewicz 	unsigned short mbw;
21090fccb52SAndrzej Pietrasiewicz 
21190fccb52SAndrzej Pietrasiewicz 	if (ep->use_dma)
21290fccb52SAndrzej Pietrasiewicz 		return;
21390fccb52SAndrzej Pietrasiewicz 
21490fccb52SAndrzej Pietrasiewicz 	m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel);
21590fccb52SAndrzej Pietrasiewicz 
21690fccb52SAndrzej Pietrasiewicz 	ndelay(450);
21790fccb52SAndrzej Pietrasiewicz 
21890fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip)
21990fccb52SAndrzej Pietrasiewicz 		mbw = M66592_MBW_32;
22090fccb52SAndrzej Pietrasiewicz 	else
22190fccb52SAndrzej Pietrasiewicz 		mbw = M66592_MBW_16;
22290fccb52SAndrzej Pietrasiewicz 
22390fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, mbw, ep->fifosel);
22490fccb52SAndrzej Pietrasiewicz }
22590fccb52SAndrzej Pietrasiewicz 
pipe_buffer_setting(struct m66592 * m66592,struct m66592_pipe_info * info)22690fccb52SAndrzej Pietrasiewicz static int pipe_buffer_setting(struct m66592 *m66592,
22790fccb52SAndrzej Pietrasiewicz 		struct m66592_pipe_info *info)
22890fccb52SAndrzej Pietrasiewicz {
22990fccb52SAndrzej Pietrasiewicz 	u16 bufnum = 0, buf_bsize = 0;
23090fccb52SAndrzej Pietrasiewicz 	u16 pipecfg = 0;
23190fccb52SAndrzej Pietrasiewicz 
23290fccb52SAndrzej Pietrasiewicz 	if (info->pipe == 0)
23390fccb52SAndrzej Pietrasiewicz 		return -EINVAL;
23490fccb52SAndrzej Pietrasiewicz 
23590fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, info->pipe, M66592_PIPESEL);
23690fccb52SAndrzej Pietrasiewicz 
23790fccb52SAndrzej Pietrasiewicz 	if (info->dir_in)
23890fccb52SAndrzej Pietrasiewicz 		pipecfg |= M66592_DIR;
23990fccb52SAndrzej Pietrasiewicz 	pipecfg |= info->type;
24090fccb52SAndrzej Pietrasiewicz 	pipecfg |= info->epnum;
24190fccb52SAndrzej Pietrasiewicz 	switch (info->type) {
24290fccb52SAndrzej Pietrasiewicz 	case M66592_INT:
24390fccb52SAndrzej Pietrasiewicz 		bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT);
24490fccb52SAndrzej Pietrasiewicz 		buf_bsize = 0;
24590fccb52SAndrzej Pietrasiewicz 		break;
24690fccb52SAndrzej Pietrasiewicz 	case M66592_BULK:
24790fccb52SAndrzej Pietrasiewicz 		/* isochronous pipes may be used as bulk pipes */
24890fccb52SAndrzej Pietrasiewicz 		if (info->pipe >= M66592_BASE_PIPENUM_BULK)
24990fccb52SAndrzej Pietrasiewicz 			bufnum = info->pipe - M66592_BASE_PIPENUM_BULK;
25090fccb52SAndrzej Pietrasiewicz 		else
25190fccb52SAndrzej Pietrasiewicz 			bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC;
25290fccb52SAndrzej Pietrasiewicz 
25390fccb52SAndrzej Pietrasiewicz 		bufnum = M66592_BASE_BUFNUM + (bufnum * 16);
25490fccb52SAndrzej Pietrasiewicz 		buf_bsize = 7;
25590fccb52SAndrzej Pietrasiewicz 		pipecfg |= M66592_DBLB;
25690fccb52SAndrzej Pietrasiewicz 		if (!info->dir_in)
25790fccb52SAndrzej Pietrasiewicz 			pipecfg |= M66592_SHTNAK;
25890fccb52SAndrzej Pietrasiewicz 		break;
25990fccb52SAndrzej Pietrasiewicz 	case M66592_ISO:
26090fccb52SAndrzej Pietrasiewicz 		bufnum = M66592_BASE_BUFNUM +
26190fccb52SAndrzej Pietrasiewicz 			 (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
26290fccb52SAndrzej Pietrasiewicz 		buf_bsize = 7;
26390fccb52SAndrzej Pietrasiewicz 		break;
26490fccb52SAndrzej Pietrasiewicz 	}
26590fccb52SAndrzej Pietrasiewicz 
26690fccb52SAndrzej Pietrasiewicz 	if (buf_bsize && ((bufnum + 16) >= M66592_MAX_BUFNUM)) {
26790fccb52SAndrzej Pietrasiewicz 		pr_err("m66592 pipe memory is insufficient\n");
26890fccb52SAndrzej Pietrasiewicz 		return -ENOMEM;
26990fccb52SAndrzej Pietrasiewicz 	}
27090fccb52SAndrzej Pietrasiewicz 
27190fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, pipecfg, M66592_PIPECFG);
27290fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF);
27390fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP);
27490fccb52SAndrzej Pietrasiewicz 	if (info->interval)
27590fccb52SAndrzej Pietrasiewicz 		info->interval--;
27690fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, info->interval, M66592_PIPEPERI);
27790fccb52SAndrzej Pietrasiewicz 
27890fccb52SAndrzej Pietrasiewicz 	return 0;
27990fccb52SAndrzej Pietrasiewicz }
28090fccb52SAndrzej Pietrasiewicz 
pipe_buffer_release(struct m66592 * m66592,struct m66592_pipe_info * info)28190fccb52SAndrzej Pietrasiewicz static void pipe_buffer_release(struct m66592 *m66592,
28290fccb52SAndrzej Pietrasiewicz 				struct m66592_pipe_info *info)
28390fccb52SAndrzej Pietrasiewicz {
28490fccb52SAndrzej Pietrasiewicz 	if (info->pipe == 0)
28590fccb52SAndrzej Pietrasiewicz 		return;
28690fccb52SAndrzej Pietrasiewicz 
28790fccb52SAndrzej Pietrasiewicz 	if (is_bulk_pipe(info->pipe)) {
28890fccb52SAndrzej Pietrasiewicz 		m66592->bulk--;
28990fccb52SAndrzej Pietrasiewicz 	} else if (is_interrupt_pipe(info->pipe))
29090fccb52SAndrzej Pietrasiewicz 		m66592->interrupt--;
29190fccb52SAndrzej Pietrasiewicz 	else if (is_isoc_pipe(info->pipe)) {
29290fccb52SAndrzej Pietrasiewicz 		m66592->isochronous--;
29390fccb52SAndrzej Pietrasiewicz 		if (info->type == M66592_BULK)
29490fccb52SAndrzej Pietrasiewicz 			m66592->bulk--;
29590fccb52SAndrzej Pietrasiewicz 	} else
29690fccb52SAndrzej Pietrasiewicz 		pr_err("ep_release: unexpect pipenum (%d)\n",
29790fccb52SAndrzej Pietrasiewicz 				info->pipe);
29890fccb52SAndrzej Pietrasiewicz }
29990fccb52SAndrzej Pietrasiewicz 
pipe_initialize(struct m66592_ep * ep)30090fccb52SAndrzej Pietrasiewicz static void pipe_initialize(struct m66592_ep *ep)
30190fccb52SAndrzej Pietrasiewicz {
30290fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
30390fccb52SAndrzej Pietrasiewicz 	unsigned short mbw;
30490fccb52SAndrzej Pietrasiewicz 
30590fccb52SAndrzej Pietrasiewicz 	m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
30690fccb52SAndrzej Pietrasiewicz 
30790fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, M66592_ACLRM, ep->pipectr);
30890fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, 0, ep->pipectr);
30990fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, M66592_SQCLR, ep->pipectr);
31090fccb52SAndrzej Pietrasiewicz 	if (ep->use_dma) {
31190fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel);
31290fccb52SAndrzej Pietrasiewicz 
31390fccb52SAndrzej Pietrasiewicz 		ndelay(450);
31490fccb52SAndrzej Pietrasiewicz 
31590fccb52SAndrzej Pietrasiewicz 		if (m66592->pdata->on_chip)
31690fccb52SAndrzej Pietrasiewicz 			mbw = M66592_MBW_32;
31790fccb52SAndrzej Pietrasiewicz 		else
31890fccb52SAndrzej Pietrasiewicz 			mbw = M66592_MBW_16;
31990fccb52SAndrzej Pietrasiewicz 
32090fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, mbw, ep->fifosel);
32190fccb52SAndrzej Pietrasiewicz 	}
32290fccb52SAndrzej Pietrasiewicz }
32390fccb52SAndrzej Pietrasiewicz 
m66592_ep_setting(struct m66592 * m66592,struct m66592_ep * ep,const struct usb_endpoint_descriptor * desc,u16 pipenum,int dma)32490fccb52SAndrzej Pietrasiewicz static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
32590fccb52SAndrzej Pietrasiewicz 		const struct usb_endpoint_descriptor *desc,
32690fccb52SAndrzej Pietrasiewicz 		u16 pipenum, int dma)
32790fccb52SAndrzej Pietrasiewicz {
32890fccb52SAndrzej Pietrasiewicz 	if ((pipenum != 0) && dma) {
32990fccb52SAndrzej Pietrasiewicz 		if (m66592->num_dma == 0) {
33090fccb52SAndrzej Pietrasiewicz 			m66592->num_dma++;
33190fccb52SAndrzej Pietrasiewicz 			ep->use_dma = 1;
33290fccb52SAndrzej Pietrasiewicz 			ep->fifoaddr = M66592_D0FIFO;
33390fccb52SAndrzej Pietrasiewicz 			ep->fifosel = M66592_D0FIFOSEL;
33490fccb52SAndrzej Pietrasiewicz 			ep->fifoctr = M66592_D0FIFOCTR;
33590fccb52SAndrzej Pietrasiewicz 			ep->fifotrn = M66592_D0FIFOTRN;
33690fccb52SAndrzej Pietrasiewicz 		} else if (!m66592->pdata->on_chip && m66592->num_dma == 1) {
33790fccb52SAndrzej Pietrasiewicz 			m66592->num_dma++;
33890fccb52SAndrzej Pietrasiewicz 			ep->use_dma = 1;
33990fccb52SAndrzej Pietrasiewicz 			ep->fifoaddr = M66592_D1FIFO;
34090fccb52SAndrzej Pietrasiewicz 			ep->fifosel = M66592_D1FIFOSEL;
34190fccb52SAndrzej Pietrasiewicz 			ep->fifoctr = M66592_D1FIFOCTR;
34290fccb52SAndrzej Pietrasiewicz 			ep->fifotrn = M66592_D1FIFOTRN;
34390fccb52SAndrzej Pietrasiewicz 		} else {
34490fccb52SAndrzej Pietrasiewicz 			ep->use_dma = 0;
34590fccb52SAndrzej Pietrasiewicz 			ep->fifoaddr = M66592_CFIFO;
34690fccb52SAndrzej Pietrasiewicz 			ep->fifosel = M66592_CFIFOSEL;
34790fccb52SAndrzej Pietrasiewicz 			ep->fifoctr = M66592_CFIFOCTR;
34890fccb52SAndrzej Pietrasiewicz 			ep->fifotrn = 0;
34990fccb52SAndrzej Pietrasiewicz 		}
35090fccb52SAndrzej Pietrasiewicz 	} else {
35190fccb52SAndrzej Pietrasiewicz 		ep->use_dma = 0;
35290fccb52SAndrzej Pietrasiewicz 		ep->fifoaddr = M66592_CFIFO;
35390fccb52SAndrzej Pietrasiewicz 		ep->fifosel = M66592_CFIFOSEL;
35490fccb52SAndrzej Pietrasiewicz 		ep->fifoctr = M66592_CFIFOCTR;
35590fccb52SAndrzej Pietrasiewicz 		ep->fifotrn = 0;
35690fccb52SAndrzej Pietrasiewicz 	}
35790fccb52SAndrzej Pietrasiewicz 
35890fccb52SAndrzej Pietrasiewicz 	ep->pipectr = get_pipectr_addr(pipenum);
35990fccb52SAndrzej Pietrasiewicz 	ep->pipenum = pipenum;
36090fccb52SAndrzej Pietrasiewicz 	ep->ep.maxpacket = usb_endpoint_maxp(desc);
36190fccb52SAndrzej Pietrasiewicz 	m66592->pipenum2ep[pipenum] = ep;
36290fccb52SAndrzej Pietrasiewicz 	m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
36390fccb52SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&ep->queue);
36490fccb52SAndrzej Pietrasiewicz }
36590fccb52SAndrzej Pietrasiewicz 
m66592_ep_release(struct m66592_ep * ep)36690fccb52SAndrzej Pietrasiewicz static void m66592_ep_release(struct m66592_ep *ep)
36790fccb52SAndrzej Pietrasiewicz {
36890fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
36990fccb52SAndrzej Pietrasiewicz 	u16 pipenum = ep->pipenum;
37090fccb52SAndrzej Pietrasiewicz 
37190fccb52SAndrzej Pietrasiewicz 	if (pipenum == 0)
37290fccb52SAndrzej Pietrasiewicz 		return;
37390fccb52SAndrzej Pietrasiewicz 
37490fccb52SAndrzej Pietrasiewicz 	if (ep->use_dma)
37590fccb52SAndrzej Pietrasiewicz 		m66592->num_dma--;
37690fccb52SAndrzej Pietrasiewicz 	ep->pipenum = 0;
37790fccb52SAndrzej Pietrasiewicz 	ep->busy = 0;
37890fccb52SAndrzej Pietrasiewicz 	ep->use_dma = 0;
37990fccb52SAndrzej Pietrasiewicz }
38090fccb52SAndrzej Pietrasiewicz 
alloc_pipe_config(struct m66592_ep * ep,const struct usb_endpoint_descriptor * desc)38190fccb52SAndrzej Pietrasiewicz static int alloc_pipe_config(struct m66592_ep *ep,
38290fccb52SAndrzej Pietrasiewicz 		const struct usb_endpoint_descriptor *desc)
38390fccb52SAndrzej Pietrasiewicz {
38490fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
38590fccb52SAndrzej Pietrasiewicz 	struct m66592_pipe_info info;
38690fccb52SAndrzej Pietrasiewicz 	int dma = 0;
38790fccb52SAndrzej Pietrasiewicz 	int *counter;
38890fccb52SAndrzej Pietrasiewicz 	int ret;
38990fccb52SAndrzej Pietrasiewicz 
39090fccb52SAndrzej Pietrasiewicz 	ep->ep.desc = desc;
39190fccb52SAndrzej Pietrasiewicz 
39290fccb52SAndrzej Pietrasiewicz 	BUG_ON(ep->pipenum);
39390fccb52SAndrzej Pietrasiewicz 
39490fccb52SAndrzej Pietrasiewicz 	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
39590fccb52SAndrzej Pietrasiewicz 	case USB_ENDPOINT_XFER_BULK:
39690fccb52SAndrzej Pietrasiewicz 		if (m66592->bulk >= M66592_MAX_NUM_BULK) {
39790fccb52SAndrzej Pietrasiewicz 			if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
39890fccb52SAndrzej Pietrasiewicz 				pr_err("bulk pipe is insufficient\n");
39990fccb52SAndrzej Pietrasiewicz 				return -ENODEV;
40090fccb52SAndrzej Pietrasiewicz 			} else {
40190fccb52SAndrzej Pietrasiewicz 				info.pipe = M66592_BASE_PIPENUM_ISOC
40290fccb52SAndrzej Pietrasiewicz 						+ m66592->isochronous;
40390fccb52SAndrzej Pietrasiewicz 				counter = &m66592->isochronous;
40490fccb52SAndrzej Pietrasiewicz 			}
40590fccb52SAndrzej Pietrasiewicz 		} else {
40690fccb52SAndrzej Pietrasiewicz 			info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk;
40790fccb52SAndrzej Pietrasiewicz 			counter = &m66592->bulk;
40890fccb52SAndrzej Pietrasiewicz 		}
40990fccb52SAndrzej Pietrasiewicz 		info.type = M66592_BULK;
41090fccb52SAndrzej Pietrasiewicz 		dma = 1;
41190fccb52SAndrzej Pietrasiewicz 		break;
41290fccb52SAndrzej Pietrasiewicz 	case USB_ENDPOINT_XFER_INT:
41390fccb52SAndrzej Pietrasiewicz 		if (m66592->interrupt >= M66592_MAX_NUM_INT) {
41490fccb52SAndrzej Pietrasiewicz 			pr_err("interrupt pipe is insufficient\n");
41590fccb52SAndrzej Pietrasiewicz 			return -ENODEV;
41690fccb52SAndrzej Pietrasiewicz 		}
41790fccb52SAndrzej Pietrasiewicz 		info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt;
41890fccb52SAndrzej Pietrasiewicz 		info.type = M66592_INT;
41990fccb52SAndrzej Pietrasiewicz 		counter = &m66592->interrupt;
42090fccb52SAndrzej Pietrasiewicz 		break;
42190fccb52SAndrzej Pietrasiewicz 	case USB_ENDPOINT_XFER_ISOC:
42290fccb52SAndrzej Pietrasiewicz 		if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
42390fccb52SAndrzej Pietrasiewicz 			pr_err("isochronous pipe is insufficient\n");
42490fccb52SAndrzej Pietrasiewicz 			return -ENODEV;
42590fccb52SAndrzej Pietrasiewicz 		}
42690fccb52SAndrzej Pietrasiewicz 		info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous;
42790fccb52SAndrzej Pietrasiewicz 		info.type = M66592_ISO;
42890fccb52SAndrzej Pietrasiewicz 		counter = &m66592->isochronous;
42990fccb52SAndrzej Pietrasiewicz 		break;
43090fccb52SAndrzej Pietrasiewicz 	default:
43190fccb52SAndrzej Pietrasiewicz 		pr_err("unexpect xfer type\n");
43290fccb52SAndrzej Pietrasiewicz 		return -EINVAL;
43390fccb52SAndrzej Pietrasiewicz 	}
43490fccb52SAndrzej Pietrasiewicz 	ep->type = info.type;
43590fccb52SAndrzej Pietrasiewicz 
43690fccb52SAndrzej Pietrasiewicz 	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
43790fccb52SAndrzej Pietrasiewicz 	info.maxpacket = usb_endpoint_maxp(desc);
43890fccb52SAndrzej Pietrasiewicz 	info.interval = desc->bInterval;
43990fccb52SAndrzej Pietrasiewicz 	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
44090fccb52SAndrzej Pietrasiewicz 		info.dir_in = 1;
44190fccb52SAndrzej Pietrasiewicz 	else
44290fccb52SAndrzej Pietrasiewicz 		info.dir_in = 0;
44390fccb52SAndrzej Pietrasiewicz 
44490fccb52SAndrzej Pietrasiewicz 	ret = pipe_buffer_setting(m66592, &info);
44590fccb52SAndrzej Pietrasiewicz 	if (ret < 0) {
44690fccb52SAndrzej Pietrasiewicz 		pr_err("pipe_buffer_setting fail\n");
44790fccb52SAndrzej Pietrasiewicz 		return ret;
44890fccb52SAndrzej Pietrasiewicz 	}
44990fccb52SAndrzej Pietrasiewicz 
45090fccb52SAndrzej Pietrasiewicz 	(*counter)++;
45190fccb52SAndrzej Pietrasiewicz 	if ((counter == &m66592->isochronous) && info.type == M66592_BULK)
45290fccb52SAndrzej Pietrasiewicz 		m66592->bulk++;
45390fccb52SAndrzej Pietrasiewicz 
45490fccb52SAndrzej Pietrasiewicz 	m66592_ep_setting(m66592, ep, desc, info.pipe, dma);
45590fccb52SAndrzej Pietrasiewicz 	pipe_initialize(ep);
45690fccb52SAndrzej Pietrasiewicz 
45790fccb52SAndrzej Pietrasiewicz 	return 0;
45890fccb52SAndrzej Pietrasiewicz }
45990fccb52SAndrzej Pietrasiewicz 
free_pipe_config(struct m66592_ep * ep)46090fccb52SAndrzej Pietrasiewicz static int free_pipe_config(struct m66592_ep *ep)
46190fccb52SAndrzej Pietrasiewicz {
46290fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
46390fccb52SAndrzej Pietrasiewicz 	struct m66592_pipe_info info;
46490fccb52SAndrzej Pietrasiewicz 
46590fccb52SAndrzej Pietrasiewicz 	info.pipe = ep->pipenum;
46690fccb52SAndrzej Pietrasiewicz 	info.type = ep->type;
46790fccb52SAndrzej Pietrasiewicz 	pipe_buffer_release(m66592, &info);
46890fccb52SAndrzej Pietrasiewicz 	m66592_ep_release(ep);
46990fccb52SAndrzej Pietrasiewicz 
47090fccb52SAndrzej Pietrasiewicz 	return 0;
47190fccb52SAndrzej Pietrasiewicz }
47290fccb52SAndrzej Pietrasiewicz 
47390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
pipe_irq_enable(struct m66592 * m66592,u16 pipenum)47490fccb52SAndrzej Pietrasiewicz static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum)
47590fccb52SAndrzej Pietrasiewicz {
47690fccb52SAndrzej Pietrasiewicz 	enable_irq_ready(m66592, pipenum);
47790fccb52SAndrzej Pietrasiewicz 	enable_irq_nrdy(m66592, pipenum);
47890fccb52SAndrzej Pietrasiewicz }
47990fccb52SAndrzej Pietrasiewicz 
pipe_irq_disable(struct m66592 * m66592,u16 pipenum)48090fccb52SAndrzej Pietrasiewicz static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum)
48190fccb52SAndrzej Pietrasiewicz {
48290fccb52SAndrzej Pietrasiewicz 	disable_irq_ready(m66592, pipenum);
48390fccb52SAndrzej Pietrasiewicz 	disable_irq_nrdy(m66592, pipenum);
48490fccb52SAndrzej Pietrasiewicz }
48590fccb52SAndrzej Pietrasiewicz 
48690fccb52SAndrzej Pietrasiewicz /* if complete is true, gadget driver complete function is not call */
control_end(struct m66592 * m66592,unsigned ccpl)48790fccb52SAndrzej Pietrasiewicz static void control_end(struct m66592 *m66592, unsigned ccpl)
48890fccb52SAndrzej Pietrasiewicz {
48990fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].internal_ccpl = ccpl;
49090fccb52SAndrzej Pietrasiewicz 	pipe_start(m66592, 0);
49190fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);
49290fccb52SAndrzej Pietrasiewicz }
49390fccb52SAndrzej Pietrasiewicz 
start_ep0_write(struct m66592_ep * ep,struct m66592_request * req)49490fccb52SAndrzej Pietrasiewicz static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
49590fccb52SAndrzej Pietrasiewicz {
49690fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
49790fccb52SAndrzej Pietrasiewicz 
49890fccb52SAndrzej Pietrasiewicz 	pipe_change(m66592, ep->pipenum);
49990fccb52SAndrzej Pietrasiewicz 	m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0,
50090fccb52SAndrzej Pietrasiewicz 			(M66592_ISEL | M66592_CURPIPE),
50190fccb52SAndrzej Pietrasiewicz 			M66592_CFIFOSEL);
50290fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, M66592_BCLR, ep->fifoctr);
50390fccb52SAndrzej Pietrasiewicz 	if (req->req.length == 0) {
50490fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
50590fccb52SAndrzej Pietrasiewicz 		pipe_start(m66592, 0);
50690fccb52SAndrzej Pietrasiewicz 		transfer_complete(ep, req, 0);
50790fccb52SAndrzej Pietrasiewicz 	} else {
50890fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
50990fccb52SAndrzej Pietrasiewicz 		irq_ep0_write(ep, req);
51090fccb52SAndrzej Pietrasiewicz 	}
51190fccb52SAndrzej Pietrasiewicz }
51290fccb52SAndrzej Pietrasiewicz 
start_packet_write(struct m66592_ep * ep,struct m66592_request * req)51390fccb52SAndrzej Pietrasiewicz static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req)
51490fccb52SAndrzej Pietrasiewicz {
51590fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
51690fccb52SAndrzej Pietrasiewicz 	u16 tmp;
51790fccb52SAndrzej Pietrasiewicz 
51890fccb52SAndrzej Pietrasiewicz 	pipe_change(m66592, ep->pipenum);
51990fccb52SAndrzej Pietrasiewicz 	disable_irq_empty(m66592, ep->pipenum);
52090fccb52SAndrzej Pietrasiewicz 	pipe_start(m66592, ep->pipenum);
52190fccb52SAndrzej Pietrasiewicz 
52290fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, ep->fifoctr);
52390fccb52SAndrzej Pietrasiewicz 	if (unlikely((tmp & M66592_FRDY) == 0))
52490fccb52SAndrzej Pietrasiewicz 		pipe_irq_enable(m66592, ep->pipenum);
52590fccb52SAndrzej Pietrasiewicz 	else
52690fccb52SAndrzej Pietrasiewicz 		irq_packet_write(ep, req);
52790fccb52SAndrzej Pietrasiewicz }
52890fccb52SAndrzej Pietrasiewicz 
start_packet_read(struct m66592_ep * ep,struct m66592_request * req)52990fccb52SAndrzej Pietrasiewicz static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
53090fccb52SAndrzej Pietrasiewicz {
53190fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
53290fccb52SAndrzej Pietrasiewicz 	u16 pipenum = ep->pipenum;
53390fccb52SAndrzej Pietrasiewicz 
53490fccb52SAndrzej Pietrasiewicz 	if (ep->pipenum == 0) {
53590fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, M66592_PIPE0,
53690fccb52SAndrzej Pietrasiewicz 				(M66592_ISEL | M66592_CURPIPE),
53790fccb52SAndrzej Pietrasiewicz 				M66592_CFIFOSEL);
53890fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, M66592_BCLR, ep->fifoctr);
53990fccb52SAndrzej Pietrasiewicz 		pipe_start(m66592, pipenum);
54090fccb52SAndrzej Pietrasiewicz 		pipe_irq_enable(m66592, pipenum);
54190fccb52SAndrzej Pietrasiewicz 	} else {
54290fccb52SAndrzej Pietrasiewicz 		if (ep->use_dma) {
54390fccb52SAndrzej Pietrasiewicz 			m66592_bset(m66592, M66592_TRCLR, ep->fifosel);
54490fccb52SAndrzej Pietrasiewicz 			pipe_change(m66592, pipenum);
54590fccb52SAndrzej Pietrasiewicz 			m66592_bset(m66592, M66592_TRENB, ep->fifosel);
54690fccb52SAndrzej Pietrasiewicz 			m66592_write(m66592,
54790fccb52SAndrzej Pietrasiewicz 				(req->req.length + ep->ep.maxpacket - 1)
54890fccb52SAndrzej Pietrasiewicz 					/ ep->ep.maxpacket,
54990fccb52SAndrzej Pietrasiewicz 				ep->fifotrn);
55090fccb52SAndrzej Pietrasiewicz 		}
55190fccb52SAndrzej Pietrasiewicz 		pipe_start(m66592, pipenum);	/* trigger once */
55290fccb52SAndrzej Pietrasiewicz 		pipe_irq_enable(m66592, pipenum);
55390fccb52SAndrzej Pietrasiewicz 	}
55490fccb52SAndrzej Pietrasiewicz }
55590fccb52SAndrzej Pietrasiewicz 
start_packet(struct m66592_ep * ep,struct m66592_request * req)55690fccb52SAndrzej Pietrasiewicz static void start_packet(struct m66592_ep *ep, struct m66592_request *req)
55790fccb52SAndrzej Pietrasiewicz {
55890fccb52SAndrzej Pietrasiewicz 	if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
55990fccb52SAndrzej Pietrasiewicz 		start_packet_write(ep, req);
56090fccb52SAndrzej Pietrasiewicz 	else
56190fccb52SAndrzej Pietrasiewicz 		start_packet_read(ep, req);
56290fccb52SAndrzej Pietrasiewicz }
56390fccb52SAndrzej Pietrasiewicz 
start_ep0(struct m66592_ep * ep,struct m66592_request * req)56490fccb52SAndrzej Pietrasiewicz static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
56590fccb52SAndrzej Pietrasiewicz {
56690fccb52SAndrzej Pietrasiewicz 	u16 ctsq;
56790fccb52SAndrzej Pietrasiewicz 
56890fccb52SAndrzej Pietrasiewicz 	ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ;
56990fccb52SAndrzej Pietrasiewicz 
57090fccb52SAndrzej Pietrasiewicz 	switch (ctsq) {
57190fccb52SAndrzej Pietrasiewicz 	case M66592_CS_RDDS:
57290fccb52SAndrzej Pietrasiewicz 		start_ep0_write(ep, req);
57390fccb52SAndrzej Pietrasiewicz 		break;
57490fccb52SAndrzej Pietrasiewicz 	case M66592_CS_WRDS:
57590fccb52SAndrzej Pietrasiewicz 		start_packet_read(ep, req);
57690fccb52SAndrzej Pietrasiewicz 		break;
57790fccb52SAndrzej Pietrasiewicz 
57890fccb52SAndrzej Pietrasiewicz 	case M66592_CS_WRND:
57990fccb52SAndrzej Pietrasiewicz 		control_end(ep->m66592, 0);
58090fccb52SAndrzej Pietrasiewicz 		break;
58190fccb52SAndrzej Pietrasiewicz 	default:
58290fccb52SAndrzej Pietrasiewicz 		pr_err("start_ep0: unexpect ctsq(%x)\n", ctsq);
58390fccb52SAndrzej Pietrasiewicz 		break;
58490fccb52SAndrzej Pietrasiewicz 	}
58590fccb52SAndrzej Pietrasiewicz }
58690fccb52SAndrzej Pietrasiewicz 
init_controller(struct m66592 * m66592)58790fccb52SAndrzej Pietrasiewicz static void init_controller(struct m66592 *m66592)
58890fccb52SAndrzej Pietrasiewicz {
58990fccb52SAndrzej Pietrasiewicz 	unsigned int endian;
59090fccb52SAndrzej Pietrasiewicz 
59190fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip) {
59290fccb52SAndrzej Pietrasiewicz 		if (m66592->pdata->endian)
59390fccb52SAndrzej Pietrasiewicz 			endian = 0; /* big endian */
59490fccb52SAndrzej Pietrasiewicz 		else
59590fccb52SAndrzej Pietrasiewicz 			endian = M66592_LITTLE; /* little endian */
59690fccb52SAndrzej Pietrasiewicz 
59790fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
59890fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
59990fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
60090fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
60190fccb52SAndrzej Pietrasiewicz 
60290fccb52SAndrzej Pietrasiewicz 		/* This is a workaound for SH7722 2nd cut */
60390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, 0x8000, M66592_DVSTCTR);
60490fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, 0x1000, M66592_TESTMODE);
60590fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, 0x8000, M66592_DVSTCTR);
60690fccb52SAndrzej Pietrasiewicz 
60790fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_INTL, M66592_INTENB1);
60890fccb52SAndrzej Pietrasiewicz 
60990fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, 0, M66592_CFBCFG);
61090fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, 0, M66592_D0FBCFG);
61190fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, endian, M66592_CFBCFG);
61290fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, endian, M66592_D0FBCFG);
61390fccb52SAndrzej Pietrasiewicz 	} else {
61490fccb52SAndrzej Pietrasiewicz 		unsigned int clock, vif, irq_sense;
61590fccb52SAndrzej Pietrasiewicz 
61690fccb52SAndrzej Pietrasiewicz 		if (m66592->pdata->endian)
61790fccb52SAndrzej Pietrasiewicz 			endian = M66592_BIGEND; /* big endian */
61890fccb52SAndrzej Pietrasiewicz 		else
61990fccb52SAndrzej Pietrasiewicz 			endian = 0; /* little endian */
62090fccb52SAndrzej Pietrasiewicz 
62190fccb52SAndrzej Pietrasiewicz 		if (m66592->pdata->vif)
62290fccb52SAndrzej Pietrasiewicz 			vif = M66592_LDRV; /* 3.3v */
62390fccb52SAndrzej Pietrasiewicz 		else
62490fccb52SAndrzej Pietrasiewicz 			vif = 0; /* 1.5v */
62590fccb52SAndrzej Pietrasiewicz 
62690fccb52SAndrzej Pietrasiewicz 		switch (m66592->pdata->xtal) {
62790fccb52SAndrzej Pietrasiewicz 		case M66592_PLATDATA_XTAL_12MHZ:
62890fccb52SAndrzej Pietrasiewicz 			clock = M66592_XTAL12;
62990fccb52SAndrzej Pietrasiewicz 			break;
63090fccb52SAndrzej Pietrasiewicz 		case M66592_PLATDATA_XTAL_24MHZ:
63190fccb52SAndrzej Pietrasiewicz 			clock = M66592_XTAL24;
63290fccb52SAndrzej Pietrasiewicz 			break;
63390fccb52SAndrzej Pietrasiewicz 		case M66592_PLATDATA_XTAL_48MHZ:
63490fccb52SAndrzej Pietrasiewicz 			clock = M66592_XTAL48;
63590fccb52SAndrzej Pietrasiewicz 			break;
63690fccb52SAndrzej Pietrasiewicz 		default:
637a4e6a852SJoe Perches 			pr_warn("m66592-udc: xtal configuration error\n");
63890fccb52SAndrzej Pietrasiewicz 			clock = 0;
63990fccb52SAndrzej Pietrasiewicz 		}
64090fccb52SAndrzej Pietrasiewicz 
64190fccb52SAndrzej Pietrasiewicz 		switch (m66592->irq_trigger) {
64290fccb52SAndrzej Pietrasiewicz 		case IRQF_TRIGGER_LOW:
64390fccb52SAndrzej Pietrasiewicz 			irq_sense = M66592_INTL;
64490fccb52SAndrzej Pietrasiewicz 			break;
64590fccb52SAndrzej Pietrasiewicz 		case IRQF_TRIGGER_FALLING:
64690fccb52SAndrzej Pietrasiewicz 			irq_sense = 0;
64790fccb52SAndrzej Pietrasiewicz 			break;
64890fccb52SAndrzej Pietrasiewicz 		default:
649a4e6a852SJoe Perches 			pr_warn("m66592-udc: irq trigger config error\n");
65090fccb52SAndrzej Pietrasiewicz 			irq_sense = 0;
65190fccb52SAndrzej Pietrasiewicz 		}
65290fccb52SAndrzej Pietrasiewicz 
65390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592,
65490fccb52SAndrzej Pietrasiewicz 			    (vif & M66592_LDRV) | (endian & M66592_BIGEND),
65590fccb52SAndrzej Pietrasiewicz 			    M66592_PINCFG);
65690fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
65790fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL,
65890fccb52SAndrzej Pietrasiewicz 			    M66592_SYSCFG);
65990fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
66090fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
66190fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
66290fccb52SAndrzej Pietrasiewicz 
66390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
66490fccb52SAndrzej Pietrasiewicz 
66590fccb52SAndrzej Pietrasiewicz 		msleep(3);
66690fccb52SAndrzej Pietrasiewicz 
66790fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
66890fccb52SAndrzej Pietrasiewicz 
66990fccb52SAndrzej Pietrasiewicz 		msleep(1);
67090fccb52SAndrzej Pietrasiewicz 
67190fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
67290fccb52SAndrzej Pietrasiewicz 
67390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
67490fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
67590fccb52SAndrzej Pietrasiewicz 			     M66592_DMA0CFG);
67690fccb52SAndrzej Pietrasiewicz 	}
67790fccb52SAndrzej Pietrasiewicz }
67890fccb52SAndrzej Pietrasiewicz 
disable_controller(struct m66592 * m66592)67990fccb52SAndrzej Pietrasiewicz static void disable_controller(struct m66592 *m66592)
68090fccb52SAndrzej Pietrasiewicz {
68190fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_UTST, M66592_TESTMODE);
68290fccb52SAndrzej Pietrasiewicz 	if (!m66592->pdata->on_chip) {
68390fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
68490fccb52SAndrzej Pietrasiewicz 		udelay(1);
68590fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
68690fccb52SAndrzej Pietrasiewicz 		udelay(1);
68790fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
68890fccb52SAndrzej Pietrasiewicz 		udelay(1);
68990fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
69090fccb52SAndrzej Pietrasiewicz 	}
69190fccb52SAndrzej Pietrasiewicz }
69290fccb52SAndrzej Pietrasiewicz 
m66592_start_xclock(struct m66592 * m66592)69390fccb52SAndrzej Pietrasiewicz static void m66592_start_xclock(struct m66592 *m66592)
69490fccb52SAndrzej Pietrasiewicz {
69590fccb52SAndrzej Pietrasiewicz 	u16 tmp;
69690fccb52SAndrzej Pietrasiewicz 
69790fccb52SAndrzej Pietrasiewicz 	if (!m66592->pdata->on_chip) {
69890fccb52SAndrzej Pietrasiewicz 		tmp = m66592_read(m66592, M66592_SYSCFG);
69990fccb52SAndrzej Pietrasiewicz 		if (!(tmp & M66592_XCKE))
70090fccb52SAndrzej Pietrasiewicz 			m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
70190fccb52SAndrzej Pietrasiewicz 	}
70290fccb52SAndrzej Pietrasiewicz }
70390fccb52SAndrzej Pietrasiewicz 
70490fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
transfer_complete(struct m66592_ep * ep,struct m66592_request * req,int status)70590fccb52SAndrzej Pietrasiewicz static void transfer_complete(struct m66592_ep *ep,
70690fccb52SAndrzej Pietrasiewicz 		struct m66592_request *req, int status)
70790fccb52SAndrzej Pietrasiewicz __releases(m66592->lock)
70890fccb52SAndrzej Pietrasiewicz __acquires(m66592->lock)
70990fccb52SAndrzej Pietrasiewicz {
71090fccb52SAndrzej Pietrasiewicz 	int restart = 0;
71190fccb52SAndrzej Pietrasiewicz 
71290fccb52SAndrzej Pietrasiewicz 	if (unlikely(ep->pipenum == 0)) {
71390fccb52SAndrzej Pietrasiewicz 		if (ep->internal_ccpl) {
71490fccb52SAndrzej Pietrasiewicz 			ep->internal_ccpl = 0;
71590fccb52SAndrzej Pietrasiewicz 			return;
71690fccb52SAndrzej Pietrasiewicz 		}
71790fccb52SAndrzej Pietrasiewicz 	}
71890fccb52SAndrzej Pietrasiewicz 
71990fccb52SAndrzej Pietrasiewicz 	list_del_init(&req->queue);
72090fccb52SAndrzej Pietrasiewicz 	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
72190fccb52SAndrzej Pietrasiewicz 		req->req.status = -ESHUTDOWN;
72290fccb52SAndrzej Pietrasiewicz 	else
72390fccb52SAndrzej Pietrasiewicz 		req->req.status = status;
72490fccb52SAndrzej Pietrasiewicz 
72590fccb52SAndrzej Pietrasiewicz 	if (!list_empty(&ep->queue))
72690fccb52SAndrzej Pietrasiewicz 		restart = 1;
72790fccb52SAndrzej Pietrasiewicz 
72890fccb52SAndrzej Pietrasiewicz 	spin_unlock(&ep->m66592->lock);
729304f7e5eSMichal Sojka 	usb_gadget_giveback_request(&ep->ep, &req->req);
73090fccb52SAndrzej Pietrasiewicz 	spin_lock(&ep->m66592->lock);
73190fccb52SAndrzej Pietrasiewicz 
73290fccb52SAndrzej Pietrasiewicz 	if (restart) {
73390fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next, struct m66592_request, queue);
73490fccb52SAndrzej Pietrasiewicz 		if (ep->ep.desc)
73590fccb52SAndrzej Pietrasiewicz 			start_packet(ep, req);
73690fccb52SAndrzej Pietrasiewicz 	}
73790fccb52SAndrzej Pietrasiewicz }
73890fccb52SAndrzej Pietrasiewicz 
irq_ep0_write(struct m66592_ep * ep,struct m66592_request * req)73990fccb52SAndrzej Pietrasiewicz static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
74090fccb52SAndrzej Pietrasiewicz {
74190fccb52SAndrzej Pietrasiewicz 	int i;
74290fccb52SAndrzej Pietrasiewicz 	u16 tmp;
74390fccb52SAndrzej Pietrasiewicz 	unsigned bufsize;
74490fccb52SAndrzej Pietrasiewicz 	size_t size;
74590fccb52SAndrzej Pietrasiewicz 	void *buf;
74690fccb52SAndrzej Pietrasiewicz 	u16 pipenum = ep->pipenum;
74790fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
74890fccb52SAndrzej Pietrasiewicz 
74990fccb52SAndrzej Pietrasiewicz 	pipe_change(m66592, pipenum);
75090fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_ISEL, ep->fifosel);
75190fccb52SAndrzej Pietrasiewicz 
75290fccb52SAndrzej Pietrasiewicz 	i = 0;
75390fccb52SAndrzej Pietrasiewicz 	do {
75490fccb52SAndrzej Pietrasiewicz 		tmp = m66592_read(m66592, ep->fifoctr);
75590fccb52SAndrzej Pietrasiewicz 		if (i++ > 100000) {
75690fccb52SAndrzej Pietrasiewicz 			pr_err("pipe0 is busy. maybe cpu i/o bus "
75790fccb52SAndrzej Pietrasiewicz 				"conflict. please power off this controller.");
75890fccb52SAndrzej Pietrasiewicz 			return;
75990fccb52SAndrzej Pietrasiewicz 		}
76090fccb52SAndrzej Pietrasiewicz 		ndelay(1);
76190fccb52SAndrzej Pietrasiewicz 	} while ((tmp & M66592_FRDY) == 0);
76290fccb52SAndrzej Pietrasiewicz 
76390fccb52SAndrzej Pietrasiewicz 	/* prepare parameters */
76490fccb52SAndrzej Pietrasiewicz 	bufsize = get_buffer_size(m66592, pipenum);
76590fccb52SAndrzej Pietrasiewicz 	buf = req->req.buf + req->req.actual;
76690fccb52SAndrzej Pietrasiewicz 	size = min(bufsize, req->req.length - req->req.actual);
76790fccb52SAndrzej Pietrasiewicz 
76890fccb52SAndrzej Pietrasiewicz 	/* write fifo */
76990fccb52SAndrzej Pietrasiewicz 	if (req->req.buf) {
77090fccb52SAndrzej Pietrasiewicz 		if (size > 0)
77190fccb52SAndrzej Pietrasiewicz 			m66592_write_fifo(m66592, ep, buf, size);
77290fccb52SAndrzej Pietrasiewicz 		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
77390fccb52SAndrzej Pietrasiewicz 			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
77490fccb52SAndrzej Pietrasiewicz 	}
77590fccb52SAndrzej Pietrasiewicz 
77690fccb52SAndrzej Pietrasiewicz 	/* update parameters */
77790fccb52SAndrzej Pietrasiewicz 	req->req.actual += size;
77890fccb52SAndrzej Pietrasiewicz 
77990fccb52SAndrzej Pietrasiewicz 	/* check transfer finish */
78090fccb52SAndrzej Pietrasiewicz 	if ((!req->req.zero && (req->req.actual == req->req.length))
78190fccb52SAndrzej Pietrasiewicz 			|| (size % ep->ep.maxpacket)
78290fccb52SAndrzej Pietrasiewicz 			|| (size == 0)) {
78390fccb52SAndrzej Pietrasiewicz 		disable_irq_ready(m66592, pipenum);
78490fccb52SAndrzej Pietrasiewicz 		disable_irq_empty(m66592, pipenum);
78590fccb52SAndrzej Pietrasiewicz 	} else {
78690fccb52SAndrzej Pietrasiewicz 		disable_irq_ready(m66592, pipenum);
78790fccb52SAndrzej Pietrasiewicz 		enable_irq_empty(m66592, pipenum);
78890fccb52SAndrzej Pietrasiewicz 	}
78990fccb52SAndrzej Pietrasiewicz 	pipe_start(m66592, pipenum);
79090fccb52SAndrzej Pietrasiewicz }
79190fccb52SAndrzej Pietrasiewicz 
irq_packet_write(struct m66592_ep * ep,struct m66592_request * req)79290fccb52SAndrzej Pietrasiewicz static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
79390fccb52SAndrzej Pietrasiewicz {
79490fccb52SAndrzej Pietrasiewicz 	u16 tmp;
79590fccb52SAndrzej Pietrasiewicz 	unsigned bufsize;
79690fccb52SAndrzej Pietrasiewicz 	size_t size;
79790fccb52SAndrzej Pietrasiewicz 	void *buf;
79890fccb52SAndrzej Pietrasiewicz 	u16 pipenum = ep->pipenum;
79990fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
80090fccb52SAndrzej Pietrasiewicz 
80190fccb52SAndrzej Pietrasiewicz 	pipe_change(m66592, pipenum);
80290fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, ep->fifoctr);
80390fccb52SAndrzej Pietrasiewicz 	if (unlikely((tmp & M66592_FRDY) == 0)) {
80490fccb52SAndrzej Pietrasiewicz 		pipe_stop(m66592, pipenum);
80590fccb52SAndrzej Pietrasiewicz 		pipe_irq_disable(m66592, pipenum);
80690fccb52SAndrzej Pietrasiewicz 		pr_err("write fifo not ready. pipnum=%d\n", pipenum);
80790fccb52SAndrzej Pietrasiewicz 		return;
80890fccb52SAndrzej Pietrasiewicz 	}
80990fccb52SAndrzej Pietrasiewicz 
81090fccb52SAndrzej Pietrasiewicz 	/* prepare parameters */
81190fccb52SAndrzej Pietrasiewicz 	bufsize = get_buffer_size(m66592, pipenum);
81290fccb52SAndrzej Pietrasiewicz 	buf = req->req.buf + req->req.actual;
81390fccb52SAndrzej Pietrasiewicz 	size = min(bufsize, req->req.length - req->req.actual);
81490fccb52SAndrzej Pietrasiewicz 
81590fccb52SAndrzej Pietrasiewicz 	/* write fifo */
81690fccb52SAndrzej Pietrasiewicz 	if (req->req.buf) {
81790fccb52SAndrzej Pietrasiewicz 		m66592_write_fifo(m66592, ep, buf, size);
81890fccb52SAndrzej Pietrasiewicz 		if ((size == 0)
81990fccb52SAndrzej Pietrasiewicz 				|| ((size % ep->ep.maxpacket) != 0)
82090fccb52SAndrzej Pietrasiewicz 				|| ((bufsize != ep->ep.maxpacket)
82190fccb52SAndrzej Pietrasiewicz 					&& (bufsize > size)))
82290fccb52SAndrzej Pietrasiewicz 			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
82390fccb52SAndrzej Pietrasiewicz 	}
82490fccb52SAndrzej Pietrasiewicz 
82590fccb52SAndrzej Pietrasiewicz 	/* update parameters */
82690fccb52SAndrzej Pietrasiewicz 	req->req.actual += size;
82790fccb52SAndrzej Pietrasiewicz 	/* check transfer finish */
82890fccb52SAndrzej Pietrasiewicz 	if ((!req->req.zero && (req->req.actual == req->req.length))
82990fccb52SAndrzej Pietrasiewicz 			|| (size % ep->ep.maxpacket)
83090fccb52SAndrzej Pietrasiewicz 			|| (size == 0)) {
83190fccb52SAndrzej Pietrasiewicz 		disable_irq_ready(m66592, pipenum);
83290fccb52SAndrzej Pietrasiewicz 		enable_irq_empty(m66592, pipenum);
83390fccb52SAndrzej Pietrasiewicz 	} else {
83490fccb52SAndrzej Pietrasiewicz 		disable_irq_empty(m66592, pipenum);
83590fccb52SAndrzej Pietrasiewicz 		pipe_irq_enable(m66592, pipenum);
83690fccb52SAndrzej Pietrasiewicz 	}
83790fccb52SAndrzej Pietrasiewicz }
83890fccb52SAndrzej Pietrasiewicz 
irq_packet_read(struct m66592_ep * ep,struct m66592_request * req)83990fccb52SAndrzej Pietrasiewicz static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req)
84090fccb52SAndrzej Pietrasiewicz {
84190fccb52SAndrzej Pietrasiewicz 	u16 tmp;
84290fccb52SAndrzej Pietrasiewicz 	int rcv_len, bufsize, req_len;
84390fccb52SAndrzej Pietrasiewicz 	int size;
84490fccb52SAndrzej Pietrasiewicz 	void *buf;
84590fccb52SAndrzej Pietrasiewicz 	u16 pipenum = ep->pipenum;
84690fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = ep->m66592;
84790fccb52SAndrzej Pietrasiewicz 	int finish = 0;
84890fccb52SAndrzej Pietrasiewicz 
84990fccb52SAndrzej Pietrasiewicz 	pipe_change(m66592, pipenum);
85090fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, ep->fifoctr);
85190fccb52SAndrzej Pietrasiewicz 	if (unlikely((tmp & M66592_FRDY) == 0)) {
85290fccb52SAndrzej Pietrasiewicz 		req->req.status = -EPIPE;
85390fccb52SAndrzej Pietrasiewicz 		pipe_stop(m66592, pipenum);
85490fccb52SAndrzej Pietrasiewicz 		pipe_irq_disable(m66592, pipenum);
85590fccb52SAndrzej Pietrasiewicz 		pr_err("read fifo not ready");
85690fccb52SAndrzej Pietrasiewicz 		return;
85790fccb52SAndrzej Pietrasiewicz 	}
85890fccb52SAndrzej Pietrasiewicz 
85990fccb52SAndrzej Pietrasiewicz 	/* prepare parameters */
86090fccb52SAndrzej Pietrasiewicz 	rcv_len = tmp & M66592_DTLN;
86190fccb52SAndrzej Pietrasiewicz 	bufsize = get_buffer_size(m66592, pipenum);
86290fccb52SAndrzej Pietrasiewicz 
86390fccb52SAndrzej Pietrasiewicz 	buf = req->req.buf + req->req.actual;
86490fccb52SAndrzej Pietrasiewicz 	req_len = req->req.length - req->req.actual;
86590fccb52SAndrzej Pietrasiewicz 	if (rcv_len < bufsize)
86690fccb52SAndrzej Pietrasiewicz 		size = min(rcv_len, req_len);
86790fccb52SAndrzej Pietrasiewicz 	else
86890fccb52SAndrzej Pietrasiewicz 		size = min(bufsize, req_len);
86990fccb52SAndrzej Pietrasiewicz 
87090fccb52SAndrzej Pietrasiewicz 	/* update parameters */
87190fccb52SAndrzej Pietrasiewicz 	req->req.actual += size;
87290fccb52SAndrzej Pietrasiewicz 
87390fccb52SAndrzej Pietrasiewicz 	/* check transfer finish */
87490fccb52SAndrzej Pietrasiewicz 	if ((!req->req.zero && (req->req.actual == req->req.length))
87590fccb52SAndrzej Pietrasiewicz 			|| (size % ep->ep.maxpacket)
87690fccb52SAndrzej Pietrasiewicz 			|| (size == 0)) {
87790fccb52SAndrzej Pietrasiewicz 		pipe_stop(m66592, pipenum);
87890fccb52SAndrzej Pietrasiewicz 		pipe_irq_disable(m66592, pipenum);
87990fccb52SAndrzej Pietrasiewicz 		finish = 1;
88090fccb52SAndrzej Pietrasiewicz 	}
88190fccb52SAndrzej Pietrasiewicz 
88290fccb52SAndrzej Pietrasiewicz 	/* read fifo */
88390fccb52SAndrzej Pietrasiewicz 	if (req->req.buf) {
88490fccb52SAndrzej Pietrasiewicz 		if (size == 0)
88590fccb52SAndrzej Pietrasiewicz 			m66592_write(m66592, M66592_BCLR, ep->fifoctr);
88690fccb52SAndrzej Pietrasiewicz 		else
88790fccb52SAndrzej Pietrasiewicz 			m66592_read_fifo(m66592, ep->fifoaddr, buf, size);
88890fccb52SAndrzej Pietrasiewicz 	}
88990fccb52SAndrzej Pietrasiewicz 
89090fccb52SAndrzej Pietrasiewicz 	if ((ep->pipenum != 0) && finish)
89190fccb52SAndrzej Pietrasiewicz 		transfer_complete(ep, req, 0);
89290fccb52SAndrzej Pietrasiewicz }
89390fccb52SAndrzej Pietrasiewicz 
irq_pipe_ready(struct m66592 * m66592,u16 status,u16 enb)89490fccb52SAndrzej Pietrasiewicz static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
89590fccb52SAndrzej Pietrasiewicz {
89690fccb52SAndrzej Pietrasiewicz 	u16 check;
89790fccb52SAndrzej Pietrasiewicz 	u16 pipenum;
89890fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
89990fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
90090fccb52SAndrzej Pietrasiewicz 
90190fccb52SAndrzej Pietrasiewicz 	if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) {
90290fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS);
90390fccb52SAndrzej Pietrasiewicz 		m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE,
90490fccb52SAndrzej Pietrasiewicz 				M66592_CFIFOSEL);
90590fccb52SAndrzej Pietrasiewicz 
90690fccb52SAndrzej Pietrasiewicz 		ep = &m66592->ep[0];
90790fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next, struct m66592_request, queue);
90890fccb52SAndrzej Pietrasiewicz 		irq_packet_read(ep, req);
90990fccb52SAndrzej Pietrasiewicz 	} else {
91090fccb52SAndrzej Pietrasiewicz 		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
91190fccb52SAndrzej Pietrasiewicz 			check = 1 << pipenum;
91290fccb52SAndrzej Pietrasiewicz 			if ((status & check) && (enb & check)) {
91390fccb52SAndrzej Pietrasiewicz 				m66592_write(m66592, ~check, M66592_BRDYSTS);
91490fccb52SAndrzej Pietrasiewicz 				ep = m66592->pipenum2ep[pipenum];
91590fccb52SAndrzej Pietrasiewicz 				req = list_entry(ep->queue.next,
91690fccb52SAndrzej Pietrasiewicz 						 struct m66592_request, queue);
91790fccb52SAndrzej Pietrasiewicz 				if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
91890fccb52SAndrzej Pietrasiewicz 					irq_packet_write(ep, req);
91990fccb52SAndrzej Pietrasiewicz 				else
92090fccb52SAndrzej Pietrasiewicz 					irq_packet_read(ep, req);
92190fccb52SAndrzej Pietrasiewicz 			}
92290fccb52SAndrzej Pietrasiewicz 		}
92390fccb52SAndrzej Pietrasiewicz 	}
92490fccb52SAndrzej Pietrasiewicz }
92590fccb52SAndrzej Pietrasiewicz 
irq_pipe_empty(struct m66592 * m66592,u16 status,u16 enb)92690fccb52SAndrzej Pietrasiewicz static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb)
92790fccb52SAndrzej Pietrasiewicz {
92890fccb52SAndrzej Pietrasiewicz 	u16 tmp;
92990fccb52SAndrzej Pietrasiewicz 	u16 check;
93090fccb52SAndrzej Pietrasiewicz 	u16 pipenum;
93190fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
93290fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
93390fccb52SAndrzej Pietrasiewicz 
93490fccb52SAndrzej Pietrasiewicz 	if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) {
93590fccb52SAndrzej Pietrasiewicz 		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
93690fccb52SAndrzej Pietrasiewicz 
93790fccb52SAndrzej Pietrasiewicz 		ep = &m66592->ep[0];
93890fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next, struct m66592_request, queue);
93990fccb52SAndrzej Pietrasiewicz 		irq_ep0_write(ep, req);
94090fccb52SAndrzej Pietrasiewicz 	} else {
94190fccb52SAndrzej Pietrasiewicz 		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
94290fccb52SAndrzej Pietrasiewicz 			check = 1 << pipenum;
94390fccb52SAndrzej Pietrasiewicz 			if ((status & check) && (enb & check)) {
94490fccb52SAndrzej Pietrasiewicz 				m66592_write(m66592, ~check, M66592_BEMPSTS);
94590fccb52SAndrzej Pietrasiewicz 				tmp = control_reg_get(m66592, pipenum);
94690fccb52SAndrzej Pietrasiewicz 				if ((tmp & M66592_INBUFM) == 0) {
94790fccb52SAndrzej Pietrasiewicz 					disable_irq_empty(m66592, pipenum);
94890fccb52SAndrzej Pietrasiewicz 					pipe_irq_disable(m66592, pipenum);
94990fccb52SAndrzej Pietrasiewicz 					pipe_stop(m66592, pipenum);
95090fccb52SAndrzej Pietrasiewicz 					ep = m66592->pipenum2ep[pipenum];
95190fccb52SAndrzej Pietrasiewicz 					req = list_entry(ep->queue.next,
95290fccb52SAndrzej Pietrasiewicz 							 struct m66592_request,
95390fccb52SAndrzej Pietrasiewicz 							 queue);
95490fccb52SAndrzej Pietrasiewicz 					if (!list_empty(&ep->queue))
95590fccb52SAndrzej Pietrasiewicz 						transfer_complete(ep, req, 0);
95690fccb52SAndrzej Pietrasiewicz 				}
95790fccb52SAndrzej Pietrasiewicz 			}
95890fccb52SAndrzej Pietrasiewicz 		}
95990fccb52SAndrzej Pietrasiewicz 	}
96090fccb52SAndrzej Pietrasiewicz }
96190fccb52SAndrzej Pietrasiewicz 
get_status(struct m66592 * m66592,struct usb_ctrlrequest * ctrl)96290fccb52SAndrzej Pietrasiewicz static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
96390fccb52SAndrzej Pietrasiewicz __releases(m66592->lock)
96490fccb52SAndrzej Pietrasiewicz __acquires(m66592->lock)
96590fccb52SAndrzej Pietrasiewicz {
96690fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
96790fccb52SAndrzej Pietrasiewicz 	u16 pid;
96890fccb52SAndrzej Pietrasiewicz 	u16 status = 0;
96990fccb52SAndrzej Pietrasiewicz 	u16 w_index = le16_to_cpu(ctrl->wIndex);
97090fccb52SAndrzej Pietrasiewicz 
97190fccb52SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
97290fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_DEVICE:
97390fccb52SAndrzej Pietrasiewicz 		status = 1 << USB_DEVICE_SELF_POWERED;
97490fccb52SAndrzej Pietrasiewicz 		break;
97590fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_INTERFACE:
97690fccb52SAndrzej Pietrasiewicz 		status = 0;
97790fccb52SAndrzej Pietrasiewicz 		break;
97890fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_ENDPOINT:
97990fccb52SAndrzej Pietrasiewicz 		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
98090fccb52SAndrzej Pietrasiewicz 		pid = control_reg_get_pid(m66592, ep->pipenum);
98190fccb52SAndrzej Pietrasiewicz 		if (pid == M66592_PID_STALL)
98290fccb52SAndrzej Pietrasiewicz 			status = 1 << USB_ENDPOINT_HALT;
98390fccb52SAndrzej Pietrasiewicz 		else
98490fccb52SAndrzej Pietrasiewicz 			status = 0;
98590fccb52SAndrzej Pietrasiewicz 		break;
98690fccb52SAndrzej Pietrasiewicz 	default:
98790fccb52SAndrzej Pietrasiewicz 		pipe_stall(m66592, 0);
98890fccb52SAndrzej Pietrasiewicz 		return;		/* exit */
98990fccb52SAndrzej Pietrasiewicz 	}
99090fccb52SAndrzej Pietrasiewicz 
99190fccb52SAndrzej Pietrasiewicz 	m66592->ep0_data = cpu_to_le16(status);
99290fccb52SAndrzej Pietrasiewicz 	m66592->ep0_req->buf = &m66592->ep0_data;
99390fccb52SAndrzej Pietrasiewicz 	m66592->ep0_req->length = 2;
99490fccb52SAndrzej Pietrasiewicz 	/* AV: what happens if we get called again before that gets through? */
99590fccb52SAndrzej Pietrasiewicz 	spin_unlock(&m66592->lock);
99690fccb52SAndrzej Pietrasiewicz 	m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL);
99790fccb52SAndrzej Pietrasiewicz 	spin_lock(&m66592->lock);
99890fccb52SAndrzej Pietrasiewicz }
99990fccb52SAndrzej Pietrasiewicz 
clear_feature(struct m66592 * m66592,struct usb_ctrlrequest * ctrl)100090fccb52SAndrzej Pietrasiewicz static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
100190fccb52SAndrzej Pietrasiewicz {
100290fccb52SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
100390fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_DEVICE:
100490fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 1);
100590fccb52SAndrzej Pietrasiewicz 		break;
100690fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_INTERFACE:
100790fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 1);
100890fccb52SAndrzej Pietrasiewicz 		break;
100990fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_ENDPOINT: {
101090fccb52SAndrzej Pietrasiewicz 		struct m66592_ep *ep;
101190fccb52SAndrzej Pietrasiewicz 		struct m66592_request *req;
101290fccb52SAndrzej Pietrasiewicz 		u16 w_index = le16_to_cpu(ctrl->wIndex);
101390fccb52SAndrzej Pietrasiewicz 
101490fccb52SAndrzej Pietrasiewicz 		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
101590fccb52SAndrzej Pietrasiewicz 		pipe_stop(m66592, ep->pipenum);
101690fccb52SAndrzej Pietrasiewicz 		control_reg_sqclr(m66592, ep->pipenum);
101790fccb52SAndrzej Pietrasiewicz 
101890fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 1);
101990fccb52SAndrzej Pietrasiewicz 
102090fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next,
102190fccb52SAndrzej Pietrasiewicz 		struct m66592_request, queue);
102290fccb52SAndrzej Pietrasiewicz 		if (ep->busy) {
102390fccb52SAndrzej Pietrasiewicz 			ep->busy = 0;
102490fccb52SAndrzej Pietrasiewicz 			if (list_empty(&ep->queue))
102590fccb52SAndrzej Pietrasiewicz 				break;
102690fccb52SAndrzej Pietrasiewicz 			start_packet(ep, req);
102790fccb52SAndrzej Pietrasiewicz 		} else if (!list_empty(&ep->queue))
102890fccb52SAndrzej Pietrasiewicz 			pipe_start(m66592, ep->pipenum);
102990fccb52SAndrzej Pietrasiewicz 		}
103090fccb52SAndrzej Pietrasiewicz 		break;
103190fccb52SAndrzej Pietrasiewicz 	default:
103290fccb52SAndrzej Pietrasiewicz 		pipe_stall(m66592, 0);
103390fccb52SAndrzej Pietrasiewicz 		break;
103490fccb52SAndrzej Pietrasiewicz 	}
103590fccb52SAndrzej Pietrasiewicz }
103690fccb52SAndrzej Pietrasiewicz 
set_feature(struct m66592 * m66592,struct usb_ctrlrequest * ctrl)103790fccb52SAndrzej Pietrasiewicz static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
103890fccb52SAndrzej Pietrasiewicz {
103990fccb52SAndrzej Pietrasiewicz 	u16 tmp;
104090fccb52SAndrzej Pietrasiewicz 	int timeout = 3000;
104190fccb52SAndrzej Pietrasiewicz 
104290fccb52SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
104390fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_DEVICE:
104490fccb52SAndrzej Pietrasiewicz 		switch (le16_to_cpu(ctrl->wValue)) {
104590fccb52SAndrzej Pietrasiewicz 		case USB_DEVICE_TEST_MODE:
104690fccb52SAndrzej Pietrasiewicz 			control_end(m66592, 1);
104790fccb52SAndrzej Pietrasiewicz 			/* Wait for the completion of status stage */
104890fccb52SAndrzej Pietrasiewicz 			do {
104990fccb52SAndrzej Pietrasiewicz 				tmp = m66592_read(m66592, M66592_INTSTS0) &
105090fccb52SAndrzej Pietrasiewicz 								M66592_CTSQ;
105190fccb52SAndrzej Pietrasiewicz 				udelay(1);
10525feb5d20SDan Carpenter 			} while (tmp != M66592_CS_IDST && timeout-- > 0);
105390fccb52SAndrzej Pietrasiewicz 
105490fccb52SAndrzej Pietrasiewicz 			if (tmp == M66592_CS_IDST)
105590fccb52SAndrzej Pietrasiewicz 				m66592_bset(m66592,
105690fccb52SAndrzej Pietrasiewicz 					    le16_to_cpu(ctrl->wIndex >> 8),
105790fccb52SAndrzej Pietrasiewicz 					    M66592_TESTMODE);
105890fccb52SAndrzej Pietrasiewicz 			break;
105990fccb52SAndrzej Pietrasiewicz 		default:
106090fccb52SAndrzej Pietrasiewicz 			pipe_stall(m66592, 0);
106190fccb52SAndrzej Pietrasiewicz 			break;
106290fccb52SAndrzej Pietrasiewicz 		}
106390fccb52SAndrzej Pietrasiewicz 		break;
106490fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_INTERFACE:
106590fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 1);
106690fccb52SAndrzej Pietrasiewicz 		break;
106790fccb52SAndrzej Pietrasiewicz 	case USB_RECIP_ENDPOINT: {
106890fccb52SAndrzej Pietrasiewicz 		struct m66592_ep *ep;
106990fccb52SAndrzej Pietrasiewicz 		u16 w_index = le16_to_cpu(ctrl->wIndex);
107090fccb52SAndrzej Pietrasiewicz 
107190fccb52SAndrzej Pietrasiewicz 		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
107290fccb52SAndrzej Pietrasiewicz 		pipe_stall(m66592, ep->pipenum);
107390fccb52SAndrzej Pietrasiewicz 
107490fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 1);
107590fccb52SAndrzej Pietrasiewicz 		}
107690fccb52SAndrzej Pietrasiewicz 		break;
107790fccb52SAndrzej Pietrasiewicz 	default:
107890fccb52SAndrzej Pietrasiewicz 		pipe_stall(m66592, 0);
107990fccb52SAndrzej Pietrasiewicz 		break;
108090fccb52SAndrzej Pietrasiewicz 	}
108190fccb52SAndrzej Pietrasiewicz }
108290fccb52SAndrzej Pietrasiewicz 
108390fccb52SAndrzej Pietrasiewicz /* if return value is true, call class driver's setup() */
setup_packet(struct m66592 * m66592,struct usb_ctrlrequest * ctrl)108490fccb52SAndrzej Pietrasiewicz static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
108590fccb52SAndrzej Pietrasiewicz {
108690fccb52SAndrzej Pietrasiewicz 	u16 *p = (u16 *)ctrl;
108790fccb52SAndrzej Pietrasiewicz 	unsigned long offset = M66592_USBREQ;
108890fccb52SAndrzej Pietrasiewicz 	int i, ret = 0;
108990fccb52SAndrzej Pietrasiewicz 
109090fccb52SAndrzej Pietrasiewicz 	/* read fifo */
109190fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0);
109290fccb52SAndrzej Pietrasiewicz 
109390fccb52SAndrzej Pietrasiewicz 	for (i = 0; i < 4; i++)
109490fccb52SAndrzej Pietrasiewicz 		p[i] = m66592_read(m66592, offset + i*2);
109590fccb52SAndrzej Pietrasiewicz 
109690fccb52SAndrzej Pietrasiewicz 	/* check request */
109790fccb52SAndrzej Pietrasiewicz 	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
109890fccb52SAndrzej Pietrasiewicz 		switch (ctrl->bRequest) {
109990fccb52SAndrzej Pietrasiewicz 		case USB_REQ_GET_STATUS:
110090fccb52SAndrzej Pietrasiewicz 			get_status(m66592, ctrl);
110190fccb52SAndrzej Pietrasiewicz 			break;
110290fccb52SAndrzej Pietrasiewicz 		case USB_REQ_CLEAR_FEATURE:
110390fccb52SAndrzej Pietrasiewicz 			clear_feature(m66592, ctrl);
110490fccb52SAndrzej Pietrasiewicz 			break;
110590fccb52SAndrzej Pietrasiewicz 		case USB_REQ_SET_FEATURE:
110690fccb52SAndrzej Pietrasiewicz 			set_feature(m66592, ctrl);
110790fccb52SAndrzej Pietrasiewicz 			break;
110890fccb52SAndrzej Pietrasiewicz 		default:
110990fccb52SAndrzej Pietrasiewicz 			ret = 1;
111090fccb52SAndrzej Pietrasiewicz 			break;
111190fccb52SAndrzej Pietrasiewicz 		}
111290fccb52SAndrzej Pietrasiewicz 	} else
111390fccb52SAndrzej Pietrasiewicz 		ret = 1;
111490fccb52SAndrzej Pietrasiewicz 	return ret;
111590fccb52SAndrzej Pietrasiewicz }
111690fccb52SAndrzej Pietrasiewicz 
m66592_update_usb_speed(struct m66592 * m66592)111790fccb52SAndrzej Pietrasiewicz static void m66592_update_usb_speed(struct m66592 *m66592)
111890fccb52SAndrzej Pietrasiewicz {
111990fccb52SAndrzej Pietrasiewicz 	u16 speed = get_usb_speed(m66592);
112090fccb52SAndrzej Pietrasiewicz 
112190fccb52SAndrzej Pietrasiewicz 	switch (speed) {
112290fccb52SAndrzej Pietrasiewicz 	case M66592_HSMODE:
112390fccb52SAndrzej Pietrasiewicz 		m66592->gadget.speed = USB_SPEED_HIGH;
112490fccb52SAndrzej Pietrasiewicz 		break;
112590fccb52SAndrzej Pietrasiewicz 	case M66592_FSMODE:
112690fccb52SAndrzej Pietrasiewicz 		m66592->gadget.speed = USB_SPEED_FULL;
112790fccb52SAndrzej Pietrasiewicz 		break;
112890fccb52SAndrzej Pietrasiewicz 	default:
112990fccb52SAndrzej Pietrasiewicz 		m66592->gadget.speed = USB_SPEED_UNKNOWN;
113090fccb52SAndrzej Pietrasiewicz 		pr_err("USB speed unknown\n");
113190fccb52SAndrzej Pietrasiewicz 	}
113290fccb52SAndrzej Pietrasiewicz }
113390fccb52SAndrzej Pietrasiewicz 
irq_device_state(struct m66592 * m66592)113490fccb52SAndrzej Pietrasiewicz static void irq_device_state(struct m66592 *m66592)
113590fccb52SAndrzej Pietrasiewicz {
113690fccb52SAndrzej Pietrasiewicz 	u16 dvsq;
113790fccb52SAndrzej Pietrasiewicz 
113890fccb52SAndrzej Pietrasiewicz 	dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ;
113990fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
114090fccb52SAndrzej Pietrasiewicz 
114190fccb52SAndrzej Pietrasiewicz 	if (dvsq == M66592_DS_DFLT) {	/* bus reset */
1142373ef692SPeter Chen 		usb_gadget_udc_reset(&m66592->gadget, m66592->driver);
114390fccb52SAndrzej Pietrasiewicz 		m66592_update_usb_speed(m66592);
114490fccb52SAndrzej Pietrasiewicz 	}
114590fccb52SAndrzej Pietrasiewicz 	if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
114690fccb52SAndrzej Pietrasiewicz 		m66592_update_usb_speed(m66592);
114790fccb52SAndrzej Pietrasiewicz 	if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS)
114890fccb52SAndrzej Pietrasiewicz 			&& m66592->gadget.speed == USB_SPEED_UNKNOWN)
114990fccb52SAndrzej Pietrasiewicz 		m66592_update_usb_speed(m66592);
115090fccb52SAndrzej Pietrasiewicz 
115190fccb52SAndrzej Pietrasiewicz 	m66592->old_dvsq = dvsq;
115290fccb52SAndrzej Pietrasiewicz }
115390fccb52SAndrzej Pietrasiewicz 
irq_control_stage(struct m66592 * m66592)115490fccb52SAndrzej Pietrasiewicz static void irq_control_stage(struct m66592 *m66592)
115590fccb52SAndrzej Pietrasiewicz __releases(m66592->lock)
115690fccb52SAndrzej Pietrasiewicz __acquires(m66592->lock)
115790fccb52SAndrzej Pietrasiewicz {
115890fccb52SAndrzej Pietrasiewicz 	struct usb_ctrlrequest ctrl;
115990fccb52SAndrzej Pietrasiewicz 	u16 ctsq;
116090fccb52SAndrzej Pietrasiewicz 
116190fccb52SAndrzej Pietrasiewicz 	ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ;
116290fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0);
116390fccb52SAndrzej Pietrasiewicz 
116490fccb52SAndrzej Pietrasiewicz 	switch (ctsq) {
116590fccb52SAndrzej Pietrasiewicz 	case M66592_CS_IDST: {
116690fccb52SAndrzej Pietrasiewicz 		struct m66592_ep *ep;
116790fccb52SAndrzej Pietrasiewicz 		struct m66592_request *req;
116890fccb52SAndrzej Pietrasiewicz 		ep = &m66592->ep[0];
116990fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next, struct m66592_request, queue);
117090fccb52SAndrzej Pietrasiewicz 		transfer_complete(ep, req, 0);
117190fccb52SAndrzej Pietrasiewicz 		}
117290fccb52SAndrzej Pietrasiewicz 		break;
117390fccb52SAndrzej Pietrasiewicz 
117490fccb52SAndrzej Pietrasiewicz 	case M66592_CS_RDDS:
117590fccb52SAndrzej Pietrasiewicz 	case M66592_CS_WRDS:
117690fccb52SAndrzej Pietrasiewicz 	case M66592_CS_WRND:
117790fccb52SAndrzej Pietrasiewicz 		if (setup_packet(m66592, &ctrl)) {
117890fccb52SAndrzej Pietrasiewicz 			spin_unlock(&m66592->lock);
117990fccb52SAndrzej Pietrasiewicz 			if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0)
118090fccb52SAndrzej Pietrasiewicz 				pipe_stall(m66592, 0);
118190fccb52SAndrzej Pietrasiewicz 			spin_lock(&m66592->lock);
118290fccb52SAndrzej Pietrasiewicz 		}
118390fccb52SAndrzej Pietrasiewicz 		break;
118490fccb52SAndrzej Pietrasiewicz 	case M66592_CS_RDSS:
118590fccb52SAndrzej Pietrasiewicz 	case M66592_CS_WRSS:
118690fccb52SAndrzej Pietrasiewicz 		control_end(m66592, 0);
118790fccb52SAndrzej Pietrasiewicz 		break;
118890fccb52SAndrzej Pietrasiewicz 	default:
118990fccb52SAndrzej Pietrasiewicz 		pr_err("ctrl_stage: unexpect ctsq(%x)\n", ctsq);
119090fccb52SAndrzej Pietrasiewicz 		break;
119190fccb52SAndrzej Pietrasiewicz 	}
119290fccb52SAndrzej Pietrasiewicz }
119390fccb52SAndrzej Pietrasiewicz 
m66592_irq(int irq,void * _m66592)119490fccb52SAndrzej Pietrasiewicz static irqreturn_t m66592_irq(int irq, void *_m66592)
119590fccb52SAndrzej Pietrasiewicz {
119690fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = _m66592;
119790fccb52SAndrzej Pietrasiewicz 	u16 intsts0;
119890fccb52SAndrzej Pietrasiewicz 	u16 intenb0;
119990fccb52SAndrzej Pietrasiewicz 	u16 savepipe;
120090fccb52SAndrzej Pietrasiewicz 	u16 mask0;
120190fccb52SAndrzej Pietrasiewicz 
120290fccb52SAndrzej Pietrasiewicz 	spin_lock(&m66592->lock);
120390fccb52SAndrzej Pietrasiewicz 
120490fccb52SAndrzej Pietrasiewicz 	intsts0 = m66592_read(m66592, M66592_INTSTS0);
120590fccb52SAndrzej Pietrasiewicz 	intenb0 = m66592_read(m66592, M66592_INTENB0);
120690fccb52SAndrzej Pietrasiewicz 
120790fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip && !intsts0 && !intenb0) {
120890fccb52SAndrzej Pietrasiewicz 		/*
120990fccb52SAndrzej Pietrasiewicz 		 * When USB clock stops, it cannot read register. Even if a
121090fccb52SAndrzej Pietrasiewicz 		 * clock stops, the interrupt occurs. So this driver turn on
121190fccb52SAndrzej Pietrasiewicz 		 * a clock by this timing and do re-reading of register.
121290fccb52SAndrzej Pietrasiewicz 		 */
121390fccb52SAndrzej Pietrasiewicz 		m66592_start_xclock(m66592);
121490fccb52SAndrzej Pietrasiewicz 		intsts0 = m66592_read(m66592, M66592_INTSTS0);
121590fccb52SAndrzej Pietrasiewicz 		intenb0 = m66592_read(m66592, M66592_INTENB0);
121690fccb52SAndrzej Pietrasiewicz 	}
121790fccb52SAndrzej Pietrasiewicz 
121890fccb52SAndrzej Pietrasiewicz 	savepipe = m66592_read(m66592, M66592_CFIFOSEL);
121990fccb52SAndrzej Pietrasiewicz 
122090fccb52SAndrzej Pietrasiewicz 	mask0 = intsts0 & intenb0;
122190fccb52SAndrzej Pietrasiewicz 	if (mask0) {
1222d58fcf81SMichal Nazarewicz 		u16 brdysts = m66592_read(m66592, M66592_BRDYSTS);
1223d58fcf81SMichal Nazarewicz 		u16 bempsts = m66592_read(m66592, M66592_BEMPSTS);
1224d58fcf81SMichal Nazarewicz 		u16 brdyenb = m66592_read(m66592, M66592_BRDYENB);
1225d58fcf81SMichal Nazarewicz 		u16 bempenb = m66592_read(m66592, M66592_BEMPENB);
122690fccb52SAndrzej Pietrasiewicz 
122790fccb52SAndrzej Pietrasiewicz 		if (mask0 & M66592_VBINT) {
122890fccb52SAndrzej Pietrasiewicz 			m66592_write(m66592,  0xffff & ~M66592_VBINT,
122990fccb52SAndrzej Pietrasiewicz 					M66592_INTSTS0);
123090fccb52SAndrzej Pietrasiewicz 			m66592_start_xclock(m66592);
123190fccb52SAndrzej Pietrasiewicz 
123290fccb52SAndrzej Pietrasiewicz 			/* start vbus sampling */
123390fccb52SAndrzej Pietrasiewicz 			m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0)
123490fccb52SAndrzej Pietrasiewicz 					& M66592_VBSTS;
123590fccb52SAndrzej Pietrasiewicz 			m66592->scount = M66592_MAX_SAMPLING;
123690fccb52SAndrzej Pietrasiewicz 
123790fccb52SAndrzej Pietrasiewicz 			mod_timer(&m66592->timer,
123890fccb52SAndrzej Pietrasiewicz 					jiffies + msecs_to_jiffies(50));
123990fccb52SAndrzej Pietrasiewicz 		}
124090fccb52SAndrzej Pietrasiewicz 		if (intsts0 & M66592_DVSQ)
124190fccb52SAndrzej Pietrasiewicz 			irq_device_state(m66592);
124290fccb52SAndrzej Pietrasiewicz 
124390fccb52SAndrzej Pietrasiewicz 		if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE)
124490fccb52SAndrzej Pietrasiewicz 				&& (brdysts & brdyenb)) {
124590fccb52SAndrzej Pietrasiewicz 			irq_pipe_ready(m66592, brdysts, brdyenb);
124690fccb52SAndrzej Pietrasiewicz 		}
124790fccb52SAndrzej Pietrasiewicz 		if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE)
124890fccb52SAndrzej Pietrasiewicz 				&& (bempsts & bempenb)) {
124990fccb52SAndrzej Pietrasiewicz 			irq_pipe_empty(m66592, bempsts, bempenb);
125090fccb52SAndrzej Pietrasiewicz 		}
125190fccb52SAndrzej Pietrasiewicz 
125290fccb52SAndrzej Pietrasiewicz 		if (intsts0 & M66592_CTRT)
125390fccb52SAndrzej Pietrasiewicz 			irq_control_stage(m66592);
125490fccb52SAndrzej Pietrasiewicz 	}
125590fccb52SAndrzej Pietrasiewicz 
125690fccb52SAndrzej Pietrasiewicz 	m66592_write(m66592, savepipe, M66592_CFIFOSEL);
125790fccb52SAndrzej Pietrasiewicz 
125890fccb52SAndrzej Pietrasiewicz 	spin_unlock(&m66592->lock);
125990fccb52SAndrzej Pietrasiewicz 	return IRQ_HANDLED;
126090fccb52SAndrzej Pietrasiewicz }
126190fccb52SAndrzej Pietrasiewicz 
m66592_timer(struct timer_list * t)1262e99e88a9SKees Cook static void m66592_timer(struct timer_list *t)
126390fccb52SAndrzej Pietrasiewicz {
1264e99e88a9SKees Cook 	struct m66592 *m66592 = from_timer(m66592, t, timer);
126590fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
126690fccb52SAndrzej Pietrasiewicz 	u16 tmp;
126790fccb52SAndrzej Pietrasiewicz 
126890fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&m66592->lock, flags);
126990fccb52SAndrzej Pietrasiewicz 	tmp = m66592_read(m66592, M66592_SYSCFG);
127090fccb52SAndrzej Pietrasiewicz 	if (!(tmp & M66592_RCKE)) {
127190fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
127290fccb52SAndrzej Pietrasiewicz 		udelay(10);
127390fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
127490fccb52SAndrzej Pietrasiewicz 	}
127590fccb52SAndrzej Pietrasiewicz 	if (m66592->scount > 0) {
127690fccb52SAndrzej Pietrasiewicz 		tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS;
127790fccb52SAndrzej Pietrasiewicz 		if (tmp == m66592->old_vbus) {
127890fccb52SAndrzej Pietrasiewicz 			m66592->scount--;
127990fccb52SAndrzej Pietrasiewicz 			if (m66592->scount == 0) {
128090fccb52SAndrzej Pietrasiewicz 				if (tmp == M66592_VBSTS)
128190fccb52SAndrzej Pietrasiewicz 					m66592_usb_connect(m66592);
128290fccb52SAndrzej Pietrasiewicz 				else
128390fccb52SAndrzej Pietrasiewicz 					m66592_usb_disconnect(m66592);
128490fccb52SAndrzej Pietrasiewicz 			} else {
128590fccb52SAndrzej Pietrasiewicz 				mod_timer(&m66592->timer,
128690fccb52SAndrzej Pietrasiewicz 					jiffies + msecs_to_jiffies(50));
128790fccb52SAndrzej Pietrasiewicz 			}
128890fccb52SAndrzej Pietrasiewicz 		} else {
128990fccb52SAndrzej Pietrasiewicz 			m66592->scount = M66592_MAX_SAMPLING;
129090fccb52SAndrzej Pietrasiewicz 			m66592->old_vbus = tmp;
129190fccb52SAndrzej Pietrasiewicz 			mod_timer(&m66592->timer,
129290fccb52SAndrzej Pietrasiewicz 					jiffies + msecs_to_jiffies(50));
129390fccb52SAndrzej Pietrasiewicz 		}
129490fccb52SAndrzej Pietrasiewicz 	}
129590fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&m66592->lock, flags);
129690fccb52SAndrzej Pietrasiewicz }
129790fccb52SAndrzej Pietrasiewicz 
129890fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
m66592_enable(struct usb_ep * _ep,const struct usb_endpoint_descriptor * desc)129990fccb52SAndrzej Pietrasiewicz static int m66592_enable(struct usb_ep *_ep,
130090fccb52SAndrzej Pietrasiewicz 			 const struct usb_endpoint_descriptor *desc)
130190fccb52SAndrzej Pietrasiewicz {
130290fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
130390fccb52SAndrzej Pietrasiewicz 
130490fccb52SAndrzej Pietrasiewicz 	ep = container_of(_ep, struct m66592_ep, ep);
130590fccb52SAndrzej Pietrasiewicz 	return alloc_pipe_config(ep, desc);
130690fccb52SAndrzej Pietrasiewicz }
130790fccb52SAndrzej Pietrasiewicz 
m66592_disable(struct usb_ep * _ep)130890fccb52SAndrzej Pietrasiewicz static int m66592_disable(struct usb_ep *_ep)
130990fccb52SAndrzej Pietrasiewicz {
131090fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
131190fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
131290fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
131390fccb52SAndrzej Pietrasiewicz 
131490fccb52SAndrzej Pietrasiewicz 	ep = container_of(_ep, struct m66592_ep, ep);
131590fccb52SAndrzej Pietrasiewicz 	BUG_ON(!ep);
131690fccb52SAndrzej Pietrasiewicz 
131790fccb52SAndrzej Pietrasiewicz 	while (!list_empty(&ep->queue)) {
131890fccb52SAndrzej Pietrasiewicz 		req = list_entry(ep->queue.next, struct m66592_request, queue);
131990fccb52SAndrzej Pietrasiewicz 		spin_lock_irqsave(&ep->m66592->lock, flags);
132090fccb52SAndrzej Pietrasiewicz 		transfer_complete(ep, req, -ECONNRESET);
132190fccb52SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&ep->m66592->lock, flags);
132290fccb52SAndrzej Pietrasiewicz 	}
132390fccb52SAndrzej Pietrasiewicz 
132490fccb52SAndrzej Pietrasiewicz 	pipe_irq_disable(ep->m66592, ep->pipenum);
132590fccb52SAndrzej Pietrasiewicz 	return free_pipe_config(ep);
132690fccb52SAndrzej Pietrasiewicz }
132790fccb52SAndrzej Pietrasiewicz 
m66592_alloc_request(struct usb_ep * _ep,gfp_t gfp_flags)132890fccb52SAndrzej Pietrasiewicz static struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
132990fccb52SAndrzej Pietrasiewicz 						gfp_t gfp_flags)
133090fccb52SAndrzej Pietrasiewicz {
133190fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
133290fccb52SAndrzej Pietrasiewicz 
133390fccb52SAndrzej Pietrasiewicz 	req = kzalloc(sizeof(struct m66592_request), gfp_flags);
133490fccb52SAndrzej Pietrasiewicz 	if (!req)
133590fccb52SAndrzej Pietrasiewicz 		return NULL;
133690fccb52SAndrzej Pietrasiewicz 
133790fccb52SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&req->queue);
133890fccb52SAndrzej Pietrasiewicz 
133990fccb52SAndrzej Pietrasiewicz 	return &req->req;
134090fccb52SAndrzej Pietrasiewicz }
134190fccb52SAndrzej Pietrasiewicz 
m66592_free_request(struct usb_ep * _ep,struct usb_request * _req)134290fccb52SAndrzej Pietrasiewicz static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req)
134390fccb52SAndrzej Pietrasiewicz {
134490fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
134590fccb52SAndrzej Pietrasiewicz 
134690fccb52SAndrzej Pietrasiewicz 	req = container_of(_req, struct m66592_request, req);
134790fccb52SAndrzej Pietrasiewicz 	kfree(req);
134890fccb52SAndrzej Pietrasiewicz }
134990fccb52SAndrzej Pietrasiewicz 
m66592_queue(struct usb_ep * _ep,struct usb_request * _req,gfp_t gfp_flags)135090fccb52SAndrzej Pietrasiewicz static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
135190fccb52SAndrzej Pietrasiewicz 			gfp_t gfp_flags)
135290fccb52SAndrzej Pietrasiewicz {
135390fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
135490fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
135590fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
135690fccb52SAndrzej Pietrasiewicz 	int request = 0;
135790fccb52SAndrzej Pietrasiewicz 
135890fccb52SAndrzej Pietrasiewicz 	ep = container_of(_ep, struct m66592_ep, ep);
135990fccb52SAndrzej Pietrasiewicz 	req = container_of(_req, struct m66592_request, req);
136090fccb52SAndrzej Pietrasiewicz 
136190fccb52SAndrzej Pietrasiewicz 	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
136290fccb52SAndrzej Pietrasiewicz 		return -ESHUTDOWN;
136390fccb52SAndrzej Pietrasiewicz 
136490fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&ep->m66592->lock, flags);
136590fccb52SAndrzej Pietrasiewicz 
136690fccb52SAndrzej Pietrasiewicz 	if (list_empty(&ep->queue))
136790fccb52SAndrzej Pietrasiewicz 		request = 1;
136890fccb52SAndrzej Pietrasiewicz 
136990fccb52SAndrzej Pietrasiewicz 	list_add_tail(&req->queue, &ep->queue);
137090fccb52SAndrzej Pietrasiewicz 	req->req.actual = 0;
137190fccb52SAndrzej Pietrasiewicz 	req->req.status = -EINPROGRESS;
137290fccb52SAndrzej Pietrasiewicz 
137390fccb52SAndrzej Pietrasiewicz 	if (ep->ep.desc == NULL)	/* control */
137490fccb52SAndrzej Pietrasiewicz 		start_ep0(ep, req);
137590fccb52SAndrzej Pietrasiewicz 	else {
137690fccb52SAndrzej Pietrasiewicz 		if (request && !ep->busy)
137790fccb52SAndrzej Pietrasiewicz 			start_packet(ep, req);
137890fccb52SAndrzej Pietrasiewicz 	}
137990fccb52SAndrzej Pietrasiewicz 
138090fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&ep->m66592->lock, flags);
138190fccb52SAndrzej Pietrasiewicz 
138290fccb52SAndrzej Pietrasiewicz 	return 0;
138390fccb52SAndrzej Pietrasiewicz }
138490fccb52SAndrzej Pietrasiewicz 
m66592_dequeue(struct usb_ep * _ep,struct usb_request * _req)138590fccb52SAndrzej Pietrasiewicz static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
138690fccb52SAndrzej Pietrasiewicz {
138790fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
138890fccb52SAndrzej Pietrasiewicz 	struct m66592_request *req;
138990fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
139090fccb52SAndrzej Pietrasiewicz 
139190fccb52SAndrzej Pietrasiewicz 	ep = container_of(_ep, struct m66592_ep, ep);
139290fccb52SAndrzej Pietrasiewicz 	req = container_of(_req, struct m66592_request, req);
139390fccb52SAndrzej Pietrasiewicz 
139490fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&ep->m66592->lock, flags);
139590fccb52SAndrzej Pietrasiewicz 	if (!list_empty(&ep->queue))
139690fccb52SAndrzej Pietrasiewicz 		transfer_complete(ep, req, -ECONNRESET);
139790fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&ep->m66592->lock, flags);
139890fccb52SAndrzej Pietrasiewicz 
139990fccb52SAndrzej Pietrasiewicz 	return 0;
140090fccb52SAndrzej Pietrasiewicz }
140190fccb52SAndrzej Pietrasiewicz 
m66592_set_halt(struct usb_ep * _ep,int value)140290fccb52SAndrzej Pietrasiewicz static int m66592_set_halt(struct usb_ep *_ep, int value)
140390fccb52SAndrzej Pietrasiewicz {
1404d58fcf81SMichal Nazarewicz 	struct m66592_ep *ep = container_of(_ep, struct m66592_ep, ep);
140590fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
140690fccb52SAndrzej Pietrasiewicz 	int ret = 0;
140790fccb52SAndrzej Pietrasiewicz 
140890fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&ep->m66592->lock, flags);
140990fccb52SAndrzej Pietrasiewicz 	if (!list_empty(&ep->queue)) {
141090fccb52SAndrzej Pietrasiewicz 		ret = -EAGAIN;
1411d58fcf81SMichal Nazarewicz 	} else if (value) {
141290fccb52SAndrzej Pietrasiewicz 		ep->busy = 1;
141390fccb52SAndrzej Pietrasiewicz 		pipe_stall(ep->m66592, ep->pipenum);
141490fccb52SAndrzej Pietrasiewicz 	} else {
141590fccb52SAndrzej Pietrasiewicz 		ep->busy = 0;
141690fccb52SAndrzej Pietrasiewicz 		pipe_stop(ep->m66592, ep->pipenum);
141790fccb52SAndrzej Pietrasiewicz 	}
141890fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&ep->m66592->lock, flags);
141990fccb52SAndrzej Pietrasiewicz 	return ret;
142090fccb52SAndrzej Pietrasiewicz }
142190fccb52SAndrzej Pietrasiewicz 
m66592_fifo_flush(struct usb_ep * _ep)142290fccb52SAndrzej Pietrasiewicz static void m66592_fifo_flush(struct usb_ep *_ep)
142390fccb52SAndrzej Pietrasiewicz {
142490fccb52SAndrzej Pietrasiewicz 	struct m66592_ep *ep;
142590fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
142690fccb52SAndrzej Pietrasiewicz 
142790fccb52SAndrzej Pietrasiewicz 	ep = container_of(_ep, struct m66592_ep, ep);
142890fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&ep->m66592->lock, flags);
142990fccb52SAndrzej Pietrasiewicz 	if (list_empty(&ep->queue) && !ep->busy) {
143090fccb52SAndrzej Pietrasiewicz 		pipe_stop(ep->m66592, ep->pipenum);
143190fccb52SAndrzej Pietrasiewicz 		m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr);
143290fccb52SAndrzej Pietrasiewicz 	}
143390fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&ep->m66592->lock, flags);
143490fccb52SAndrzej Pietrasiewicz }
143590fccb52SAndrzej Pietrasiewicz 
1436977ac789SBhumika Goyal static const struct usb_ep_ops m66592_ep_ops = {
143790fccb52SAndrzej Pietrasiewicz 	.enable		= m66592_enable,
143890fccb52SAndrzej Pietrasiewicz 	.disable	= m66592_disable,
143990fccb52SAndrzej Pietrasiewicz 
144090fccb52SAndrzej Pietrasiewicz 	.alloc_request	= m66592_alloc_request,
144190fccb52SAndrzej Pietrasiewicz 	.free_request	= m66592_free_request,
144290fccb52SAndrzej Pietrasiewicz 
144390fccb52SAndrzej Pietrasiewicz 	.queue		= m66592_queue,
144490fccb52SAndrzej Pietrasiewicz 	.dequeue	= m66592_dequeue,
144590fccb52SAndrzej Pietrasiewicz 
144690fccb52SAndrzej Pietrasiewicz 	.set_halt	= m66592_set_halt,
144790fccb52SAndrzej Pietrasiewicz 	.fifo_flush	= m66592_fifo_flush,
144890fccb52SAndrzej Pietrasiewicz };
144990fccb52SAndrzej Pietrasiewicz 
145090fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
m66592_udc_start(struct usb_gadget * g,struct usb_gadget_driver * driver)145190fccb52SAndrzej Pietrasiewicz static int m66592_udc_start(struct usb_gadget *g,
145290fccb52SAndrzej Pietrasiewicz 		struct usb_gadget_driver *driver)
145390fccb52SAndrzej Pietrasiewicz {
145490fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = to_m66592(g);
145590fccb52SAndrzej Pietrasiewicz 
145690fccb52SAndrzej Pietrasiewicz 	/* hook up the driver */
145790fccb52SAndrzej Pietrasiewicz 	m66592->driver = driver;
145890fccb52SAndrzej Pietrasiewicz 
145990fccb52SAndrzej Pietrasiewicz 	m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
146090fccb52SAndrzej Pietrasiewicz 	if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
146190fccb52SAndrzej Pietrasiewicz 		m66592_start_xclock(m66592);
146290fccb52SAndrzej Pietrasiewicz 		/* start vbus sampling */
146390fccb52SAndrzej Pietrasiewicz 		m66592->old_vbus = m66592_read(m66592,
146490fccb52SAndrzej Pietrasiewicz 					 M66592_INTSTS0) & M66592_VBSTS;
146590fccb52SAndrzej Pietrasiewicz 		m66592->scount = M66592_MAX_SAMPLING;
146690fccb52SAndrzej Pietrasiewicz 		mod_timer(&m66592->timer, jiffies + msecs_to_jiffies(50));
146790fccb52SAndrzej Pietrasiewicz 	}
146890fccb52SAndrzej Pietrasiewicz 
146990fccb52SAndrzej Pietrasiewicz 	return 0;
147090fccb52SAndrzej Pietrasiewicz }
147190fccb52SAndrzej Pietrasiewicz 
m66592_udc_stop(struct usb_gadget * g)147222835b80SFelipe Balbi static int m66592_udc_stop(struct usb_gadget *g)
147390fccb52SAndrzej Pietrasiewicz {
147490fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = to_m66592(g);
147590fccb52SAndrzej Pietrasiewicz 
147690fccb52SAndrzej Pietrasiewicz 	m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
147790fccb52SAndrzej Pietrasiewicz 
147890fccb52SAndrzej Pietrasiewicz 	init_controller(m66592);
147990fccb52SAndrzej Pietrasiewicz 	disable_controller(m66592);
148090fccb52SAndrzej Pietrasiewicz 
148190fccb52SAndrzej Pietrasiewicz 	m66592->driver = NULL;
148290fccb52SAndrzej Pietrasiewicz 
148390fccb52SAndrzej Pietrasiewicz 	return 0;
148490fccb52SAndrzej Pietrasiewicz }
148590fccb52SAndrzej Pietrasiewicz 
148690fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
m66592_get_frame(struct usb_gadget * _gadget)148790fccb52SAndrzej Pietrasiewicz static int m66592_get_frame(struct usb_gadget *_gadget)
148890fccb52SAndrzej Pietrasiewicz {
148990fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = gadget_to_m66592(_gadget);
149090fccb52SAndrzej Pietrasiewicz 	return m66592_read(m66592, M66592_FRMNUM) & 0x03FF;
149190fccb52SAndrzej Pietrasiewicz }
149290fccb52SAndrzej Pietrasiewicz 
m66592_pullup(struct usb_gadget * gadget,int is_on)149390fccb52SAndrzej Pietrasiewicz static int m66592_pullup(struct usb_gadget *gadget, int is_on)
149490fccb52SAndrzej Pietrasiewicz {
149590fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = gadget_to_m66592(gadget);
149690fccb52SAndrzej Pietrasiewicz 	unsigned long flags;
149790fccb52SAndrzej Pietrasiewicz 
149890fccb52SAndrzej Pietrasiewicz 	spin_lock_irqsave(&m66592->lock, flags);
149990fccb52SAndrzej Pietrasiewicz 	if (is_on)
150090fccb52SAndrzej Pietrasiewicz 		m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
150190fccb52SAndrzej Pietrasiewicz 	else
150290fccb52SAndrzej Pietrasiewicz 		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
150390fccb52SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&m66592->lock, flags);
150490fccb52SAndrzej Pietrasiewicz 
150590fccb52SAndrzej Pietrasiewicz 	return 0;
150690fccb52SAndrzej Pietrasiewicz }
150790fccb52SAndrzej Pietrasiewicz 
150890fccb52SAndrzej Pietrasiewicz static const struct usb_gadget_ops m66592_gadget_ops = {
150990fccb52SAndrzej Pietrasiewicz 	.get_frame		= m66592_get_frame,
151090fccb52SAndrzej Pietrasiewicz 	.udc_start		= m66592_udc_start,
151190fccb52SAndrzej Pietrasiewicz 	.udc_stop		= m66592_udc_stop,
151290fccb52SAndrzej Pietrasiewicz 	.pullup			= m66592_pullup,
151390fccb52SAndrzej Pietrasiewicz };
151490fccb52SAndrzej Pietrasiewicz 
m66592_remove(struct platform_device * pdev)15159c78fc7bSUwe Kleine-König static void m66592_remove(struct platform_device *pdev)
151690fccb52SAndrzej Pietrasiewicz {
151790fccb52SAndrzej Pietrasiewicz 	struct m66592		*m66592 = platform_get_drvdata(pdev);
151890fccb52SAndrzej Pietrasiewicz 
151990fccb52SAndrzej Pietrasiewicz 	usb_del_gadget_udc(&m66592->gadget);
152090fccb52SAndrzej Pietrasiewicz 
1521292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&m66592->timer);
152290fccb52SAndrzej Pietrasiewicz 	iounmap(m66592->reg);
152390fccb52SAndrzej Pietrasiewicz 	free_irq(platform_get_irq(pdev, 0), m66592);
152490fccb52SAndrzej Pietrasiewicz 	m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
152590fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip) {
152690fccb52SAndrzej Pietrasiewicz 		clk_disable(m66592->clk);
152790fccb52SAndrzej Pietrasiewicz 		clk_put(m66592->clk);
152890fccb52SAndrzej Pietrasiewicz 	}
152990fccb52SAndrzej Pietrasiewicz 	kfree(m66592);
153090fccb52SAndrzej Pietrasiewicz }
153190fccb52SAndrzej Pietrasiewicz 
nop_completion(struct usb_ep * ep,struct usb_request * r)153290fccb52SAndrzej Pietrasiewicz static void nop_completion(struct usb_ep *ep, struct usb_request *r)
153390fccb52SAndrzej Pietrasiewicz {
153490fccb52SAndrzej Pietrasiewicz }
153590fccb52SAndrzej Pietrasiewicz 
m66592_probe(struct platform_device * pdev)153690fccb52SAndrzej Pietrasiewicz static int m66592_probe(struct platform_device *pdev)
153790fccb52SAndrzej Pietrasiewicz {
153890fccb52SAndrzej Pietrasiewicz 	struct resource *res, *ires;
153990fccb52SAndrzej Pietrasiewicz 	void __iomem *reg = NULL;
154090fccb52SAndrzej Pietrasiewicz 	struct m66592 *m66592 = NULL;
154190fccb52SAndrzej Pietrasiewicz 	char clk_name[8];
154290fccb52SAndrzej Pietrasiewicz 	int ret = 0;
154390fccb52SAndrzej Pietrasiewicz 	int i;
154490fccb52SAndrzej Pietrasiewicz 
154590fccb52SAndrzej Pietrasiewicz 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
154690fccb52SAndrzej Pietrasiewicz 	if (!res) {
154790fccb52SAndrzej Pietrasiewicz 		ret = -ENODEV;
154890fccb52SAndrzej Pietrasiewicz 		pr_err("platform_get_resource error.\n");
154990fccb52SAndrzej Pietrasiewicz 		goto clean_up;
155090fccb52SAndrzej Pietrasiewicz 	}
155190fccb52SAndrzej Pietrasiewicz 
155290fccb52SAndrzej Pietrasiewicz 	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
155390fccb52SAndrzej Pietrasiewicz 	if (!ires) {
155490fccb52SAndrzej Pietrasiewicz 		ret = -ENODEV;
155590fccb52SAndrzej Pietrasiewicz 		dev_err(&pdev->dev,
155690fccb52SAndrzej Pietrasiewicz 			"platform_get_resource IORESOURCE_IRQ error.\n");
155790fccb52SAndrzej Pietrasiewicz 		goto clean_up;
155890fccb52SAndrzej Pietrasiewicz 	}
155990fccb52SAndrzej Pietrasiewicz 
156090fccb52SAndrzej Pietrasiewicz 	reg = ioremap(res->start, resource_size(res));
156190fccb52SAndrzej Pietrasiewicz 	if (reg == NULL) {
156290fccb52SAndrzej Pietrasiewicz 		ret = -ENOMEM;
156390fccb52SAndrzej Pietrasiewicz 		pr_err("ioremap error.\n");
156490fccb52SAndrzej Pietrasiewicz 		goto clean_up;
156590fccb52SAndrzej Pietrasiewicz 	}
156690fccb52SAndrzej Pietrasiewicz 
156790fccb52SAndrzej Pietrasiewicz 	if (dev_get_platdata(&pdev->dev) == NULL) {
156890fccb52SAndrzej Pietrasiewicz 		dev_err(&pdev->dev, "no platform data\n");
156990fccb52SAndrzej Pietrasiewicz 		ret = -ENODEV;
157090fccb52SAndrzej Pietrasiewicz 		goto clean_up;
157190fccb52SAndrzej Pietrasiewicz 	}
157290fccb52SAndrzej Pietrasiewicz 
157390fccb52SAndrzej Pietrasiewicz 	/* initialize ucd */
157490fccb52SAndrzej Pietrasiewicz 	m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
157590fccb52SAndrzej Pietrasiewicz 	if (m66592 == NULL) {
157690fccb52SAndrzej Pietrasiewicz 		ret = -ENOMEM;
157790fccb52SAndrzej Pietrasiewicz 		goto clean_up;
157890fccb52SAndrzej Pietrasiewicz 	}
157990fccb52SAndrzej Pietrasiewicz 
158090fccb52SAndrzej Pietrasiewicz 	m66592->pdata = dev_get_platdata(&pdev->dev);
158190fccb52SAndrzej Pietrasiewicz 	m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
158290fccb52SAndrzej Pietrasiewicz 
158390fccb52SAndrzej Pietrasiewicz 	spin_lock_init(&m66592->lock);
158490fccb52SAndrzej Pietrasiewicz 	platform_set_drvdata(pdev, m66592);
158590fccb52SAndrzej Pietrasiewicz 
158690fccb52SAndrzej Pietrasiewicz 	m66592->gadget.ops = &m66592_gadget_ops;
158790fccb52SAndrzej Pietrasiewicz 	m66592->gadget.max_speed = USB_SPEED_HIGH;
158890fccb52SAndrzej Pietrasiewicz 	m66592->gadget.name = udc_name;
158990fccb52SAndrzej Pietrasiewicz 
1590e99e88a9SKees Cook 	timer_setup(&m66592->timer, m66592_timer, 0);
159190fccb52SAndrzej Pietrasiewicz 	m66592->reg = reg;
159290fccb52SAndrzej Pietrasiewicz 
159390fccb52SAndrzej Pietrasiewicz 	ret = request_irq(ires->start, m66592_irq, IRQF_SHARED,
159490fccb52SAndrzej Pietrasiewicz 			udc_name, m66592);
159590fccb52SAndrzej Pietrasiewicz 	if (ret < 0) {
159690fccb52SAndrzej Pietrasiewicz 		pr_err("request_irq error (%d)\n", ret);
159790fccb52SAndrzej Pietrasiewicz 		goto clean_up;
159890fccb52SAndrzej Pietrasiewicz 	}
159990fccb52SAndrzej Pietrasiewicz 
160090fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip) {
160190fccb52SAndrzej Pietrasiewicz 		snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
160290fccb52SAndrzej Pietrasiewicz 		m66592->clk = clk_get(&pdev->dev, clk_name);
160390fccb52SAndrzej Pietrasiewicz 		if (IS_ERR(m66592->clk)) {
160490fccb52SAndrzej Pietrasiewicz 			dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
160590fccb52SAndrzej Pietrasiewicz 				clk_name);
160690fccb52SAndrzej Pietrasiewicz 			ret = PTR_ERR(m66592->clk);
160790fccb52SAndrzej Pietrasiewicz 			goto clean_up2;
160890fccb52SAndrzej Pietrasiewicz 		}
160990fccb52SAndrzej Pietrasiewicz 		clk_enable(m66592->clk);
161090fccb52SAndrzej Pietrasiewicz 	}
161190fccb52SAndrzej Pietrasiewicz 
161290fccb52SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&m66592->gadget.ep_list);
161390fccb52SAndrzej Pietrasiewicz 	m66592->gadget.ep0 = &m66592->ep[0].ep;
161490fccb52SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
161590fccb52SAndrzej Pietrasiewicz 	for (i = 0; i < M66592_MAX_NUM_PIPE; i++) {
161690fccb52SAndrzej Pietrasiewicz 		struct m66592_ep *ep = &m66592->ep[i];
161790fccb52SAndrzej Pietrasiewicz 
161890fccb52SAndrzej Pietrasiewicz 		if (i != 0) {
161990fccb52SAndrzej Pietrasiewicz 			INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list);
162090fccb52SAndrzej Pietrasiewicz 			list_add_tail(&m66592->ep[i].ep.ep_list,
162190fccb52SAndrzej Pietrasiewicz 					&m66592->gadget.ep_list);
162290fccb52SAndrzej Pietrasiewicz 		}
162390fccb52SAndrzej Pietrasiewicz 		ep->m66592 = m66592;
162490fccb52SAndrzej Pietrasiewicz 		INIT_LIST_HEAD(&ep->queue);
162590fccb52SAndrzej Pietrasiewicz 		ep->ep.name = m66592_ep_name[i];
162690fccb52SAndrzej Pietrasiewicz 		ep->ep.ops = &m66592_ep_ops;
162790fccb52SAndrzej Pietrasiewicz 		usb_ep_set_maxpacket_limit(&ep->ep, 512);
16288ddbf94fSRobert Baldyga 
16298ddbf94fSRobert Baldyga 		if (i == 0) {
16308ddbf94fSRobert Baldyga 			ep->ep.caps.type_control = true;
16318ddbf94fSRobert Baldyga 		} else {
16328ddbf94fSRobert Baldyga 			ep->ep.caps.type_iso = true;
16338ddbf94fSRobert Baldyga 			ep->ep.caps.type_bulk = true;
16348ddbf94fSRobert Baldyga 			ep->ep.caps.type_int = true;
16358ddbf94fSRobert Baldyga 		}
16368ddbf94fSRobert Baldyga 
16378ddbf94fSRobert Baldyga 		ep->ep.caps.dir_in = true;
16388ddbf94fSRobert Baldyga 		ep->ep.caps.dir_out = true;
163990fccb52SAndrzej Pietrasiewicz 	}
164090fccb52SAndrzej Pietrasiewicz 	usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64);
164190fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].pipenum = 0;
164290fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].fifoaddr = M66592_CFIFO;
164390fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].fifosel = M66592_CFIFOSEL;
164490fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].fifoctr = M66592_CFIFOCTR;
164590fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].fifotrn = 0;
164690fccb52SAndrzej Pietrasiewicz 	m66592->ep[0].pipectr = get_pipectr_addr(0);
164790fccb52SAndrzej Pietrasiewicz 	m66592->pipenum2ep[0] = &m66592->ep[0];
164890fccb52SAndrzej Pietrasiewicz 	m66592->epaddr2ep[0] = &m66592->ep[0];
164990fccb52SAndrzej Pietrasiewicz 
165090fccb52SAndrzej Pietrasiewicz 	m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
165190fccb52SAndrzej Pietrasiewicz 	if (m66592->ep0_req == NULL) {
165290fccb52SAndrzej Pietrasiewicz 		ret = -ENOMEM;
165390fccb52SAndrzej Pietrasiewicz 		goto clean_up3;
165490fccb52SAndrzej Pietrasiewicz 	}
165590fccb52SAndrzej Pietrasiewicz 	m66592->ep0_req->complete = nop_completion;
165690fccb52SAndrzej Pietrasiewicz 
165790fccb52SAndrzej Pietrasiewicz 	init_controller(m66592);
165890fccb52SAndrzej Pietrasiewicz 
165990fccb52SAndrzej Pietrasiewicz 	ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget);
166090fccb52SAndrzej Pietrasiewicz 	if (ret)
166190fccb52SAndrzej Pietrasiewicz 		goto err_add_udc;
166290fccb52SAndrzej Pietrasiewicz 
166390fccb52SAndrzej Pietrasiewicz 	dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
166490fccb52SAndrzej Pietrasiewicz 	return 0;
166590fccb52SAndrzej Pietrasiewicz 
166690fccb52SAndrzej Pietrasiewicz err_add_udc:
166790fccb52SAndrzej Pietrasiewicz 	m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
166844734a59SQiushi Wu 	m66592->ep0_req = NULL;
166990fccb52SAndrzej Pietrasiewicz clean_up3:
167090fccb52SAndrzej Pietrasiewicz 	if (m66592->pdata->on_chip) {
167190fccb52SAndrzej Pietrasiewicz 		clk_disable(m66592->clk);
167290fccb52SAndrzej Pietrasiewicz 		clk_put(m66592->clk);
167390fccb52SAndrzej Pietrasiewicz 	}
167490fccb52SAndrzej Pietrasiewicz clean_up2:
167590fccb52SAndrzej Pietrasiewicz 	free_irq(ires->start, m66592);
167690fccb52SAndrzej Pietrasiewicz clean_up:
167790fccb52SAndrzej Pietrasiewicz 	if (m66592) {
167890fccb52SAndrzej Pietrasiewicz 		if (m66592->ep0_req)
167990fccb52SAndrzej Pietrasiewicz 			m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
168090fccb52SAndrzej Pietrasiewicz 		kfree(m66592);
168190fccb52SAndrzej Pietrasiewicz 	}
168290fccb52SAndrzej Pietrasiewicz 	if (reg)
168390fccb52SAndrzej Pietrasiewicz 		iounmap(reg);
168490fccb52SAndrzej Pietrasiewicz 
168590fccb52SAndrzej Pietrasiewicz 	return ret;
168690fccb52SAndrzej Pietrasiewicz }
168790fccb52SAndrzej Pietrasiewicz 
168890fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
168990fccb52SAndrzej Pietrasiewicz static struct platform_driver m66592_driver = {
1690*b4822e23SUwe Kleine-König 	.probe =	m66592_probe,
16919c78fc7bSUwe Kleine-König 	.remove_new =	m66592_remove,
169290fccb52SAndrzej Pietrasiewicz 	.driver		= {
169393bc7363SCorentin Labbe 		.name =	udc_name,
169490fccb52SAndrzej Pietrasiewicz 	},
169590fccb52SAndrzej Pietrasiewicz };
169690fccb52SAndrzej Pietrasiewicz 
1697*b4822e23SUwe Kleine-König module_platform_driver(m66592_driver);
1698