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 /*-------------------------------------------------------------------------*/ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 /*-------------------------------------------------------------------------*/ 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 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 */ 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 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 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 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 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 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 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 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 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 /*-------------------------------------------------------------------------*/ 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 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 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 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 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 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 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 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 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() */ 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 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 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 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 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 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 /*-------------------------------------------------------------------------*/ 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 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 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 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 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 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 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 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 /*-------------------------------------------------------------------------*/ 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 driver->driver.bus = NULL; 145890fccb52SAndrzej Pietrasiewicz m66592->driver = driver; 145990fccb52SAndrzej Pietrasiewicz 146090fccb52SAndrzej Pietrasiewicz m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); 146190fccb52SAndrzej Pietrasiewicz if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) { 146290fccb52SAndrzej Pietrasiewicz m66592_start_xclock(m66592); 146390fccb52SAndrzej Pietrasiewicz /* start vbus sampling */ 146490fccb52SAndrzej Pietrasiewicz m66592->old_vbus = m66592_read(m66592, 146590fccb52SAndrzej Pietrasiewicz M66592_INTSTS0) & M66592_VBSTS; 146690fccb52SAndrzej Pietrasiewicz m66592->scount = M66592_MAX_SAMPLING; 146790fccb52SAndrzej Pietrasiewicz mod_timer(&m66592->timer, jiffies + msecs_to_jiffies(50)); 146890fccb52SAndrzej Pietrasiewicz } 146990fccb52SAndrzej Pietrasiewicz 147090fccb52SAndrzej Pietrasiewicz return 0; 147190fccb52SAndrzej Pietrasiewicz } 147290fccb52SAndrzej Pietrasiewicz 147322835b80SFelipe Balbi static int m66592_udc_stop(struct usb_gadget *g) 147490fccb52SAndrzej Pietrasiewicz { 147590fccb52SAndrzej Pietrasiewicz struct m66592 *m66592 = to_m66592(g); 147690fccb52SAndrzej Pietrasiewicz 147790fccb52SAndrzej Pietrasiewicz m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); 147890fccb52SAndrzej Pietrasiewicz 147990fccb52SAndrzej Pietrasiewicz init_controller(m66592); 148090fccb52SAndrzej Pietrasiewicz disable_controller(m66592); 148190fccb52SAndrzej Pietrasiewicz 148290fccb52SAndrzej Pietrasiewicz m66592->driver = NULL; 148390fccb52SAndrzej Pietrasiewicz 148490fccb52SAndrzej Pietrasiewicz return 0; 148590fccb52SAndrzej Pietrasiewicz } 148690fccb52SAndrzej Pietrasiewicz 148790fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 148890fccb52SAndrzej Pietrasiewicz static int m66592_get_frame(struct usb_gadget *_gadget) 148990fccb52SAndrzej Pietrasiewicz { 149090fccb52SAndrzej Pietrasiewicz struct m66592 *m66592 = gadget_to_m66592(_gadget); 149190fccb52SAndrzej Pietrasiewicz return m66592_read(m66592, M66592_FRMNUM) & 0x03FF; 149290fccb52SAndrzej Pietrasiewicz } 149390fccb52SAndrzej Pietrasiewicz 149490fccb52SAndrzej Pietrasiewicz static int m66592_pullup(struct usb_gadget *gadget, int is_on) 149590fccb52SAndrzej Pietrasiewicz { 149690fccb52SAndrzej Pietrasiewicz struct m66592 *m66592 = gadget_to_m66592(gadget); 149790fccb52SAndrzej Pietrasiewicz unsigned long flags; 149890fccb52SAndrzej Pietrasiewicz 149990fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&m66592->lock, flags); 150090fccb52SAndrzej Pietrasiewicz if (is_on) 150190fccb52SAndrzej Pietrasiewicz m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG); 150290fccb52SAndrzej Pietrasiewicz else 150390fccb52SAndrzej Pietrasiewicz m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); 150490fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&m66592->lock, flags); 150590fccb52SAndrzej Pietrasiewicz 150690fccb52SAndrzej Pietrasiewicz return 0; 150790fccb52SAndrzej Pietrasiewicz } 150890fccb52SAndrzej Pietrasiewicz 150990fccb52SAndrzej Pietrasiewicz static const struct usb_gadget_ops m66592_gadget_ops = { 151090fccb52SAndrzej Pietrasiewicz .get_frame = m66592_get_frame, 151190fccb52SAndrzej Pietrasiewicz .udc_start = m66592_udc_start, 151290fccb52SAndrzej Pietrasiewicz .udc_stop = m66592_udc_stop, 151390fccb52SAndrzej Pietrasiewicz .pullup = m66592_pullup, 151490fccb52SAndrzej Pietrasiewicz }; 151590fccb52SAndrzej Pietrasiewicz 1516c94e289fSArnd Bergmann static int m66592_remove(struct platform_device *pdev) 151790fccb52SAndrzej Pietrasiewicz { 151890fccb52SAndrzej Pietrasiewicz struct m66592 *m66592 = platform_get_drvdata(pdev); 151990fccb52SAndrzej Pietrasiewicz 152090fccb52SAndrzej Pietrasiewicz usb_del_gadget_udc(&m66592->gadget); 152190fccb52SAndrzej Pietrasiewicz 152290fccb52SAndrzej Pietrasiewicz del_timer_sync(&m66592->timer); 152390fccb52SAndrzej Pietrasiewicz iounmap(m66592->reg); 152490fccb52SAndrzej Pietrasiewicz free_irq(platform_get_irq(pdev, 0), m66592); 152590fccb52SAndrzej Pietrasiewicz m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); 152690fccb52SAndrzej Pietrasiewicz if (m66592->pdata->on_chip) { 152790fccb52SAndrzej Pietrasiewicz clk_disable(m66592->clk); 152890fccb52SAndrzej Pietrasiewicz clk_put(m66592->clk); 152990fccb52SAndrzej Pietrasiewicz } 153090fccb52SAndrzej Pietrasiewicz kfree(m66592); 153190fccb52SAndrzej Pietrasiewicz return 0; 153290fccb52SAndrzej Pietrasiewicz } 153390fccb52SAndrzej Pietrasiewicz 153490fccb52SAndrzej Pietrasiewicz static void nop_completion(struct usb_ep *ep, struct usb_request *r) 153590fccb52SAndrzej Pietrasiewicz { 153690fccb52SAndrzej Pietrasiewicz } 153790fccb52SAndrzej Pietrasiewicz 153890fccb52SAndrzej Pietrasiewicz static int m66592_probe(struct platform_device *pdev) 153990fccb52SAndrzej Pietrasiewicz { 154090fccb52SAndrzej Pietrasiewicz struct resource *res, *ires; 154190fccb52SAndrzej Pietrasiewicz void __iomem *reg = NULL; 154290fccb52SAndrzej Pietrasiewicz struct m66592 *m66592 = NULL; 154390fccb52SAndrzej Pietrasiewicz char clk_name[8]; 154490fccb52SAndrzej Pietrasiewicz int ret = 0; 154590fccb52SAndrzej Pietrasiewicz int i; 154690fccb52SAndrzej Pietrasiewicz 154790fccb52SAndrzej Pietrasiewicz res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 154890fccb52SAndrzej Pietrasiewicz if (!res) { 154990fccb52SAndrzej Pietrasiewicz ret = -ENODEV; 155090fccb52SAndrzej Pietrasiewicz pr_err("platform_get_resource error.\n"); 155190fccb52SAndrzej Pietrasiewicz goto clean_up; 155290fccb52SAndrzej Pietrasiewicz } 155390fccb52SAndrzej Pietrasiewicz 155490fccb52SAndrzej Pietrasiewicz ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 155590fccb52SAndrzej Pietrasiewicz if (!ires) { 155690fccb52SAndrzej Pietrasiewicz ret = -ENODEV; 155790fccb52SAndrzej Pietrasiewicz dev_err(&pdev->dev, 155890fccb52SAndrzej Pietrasiewicz "platform_get_resource IORESOURCE_IRQ error.\n"); 155990fccb52SAndrzej Pietrasiewicz goto clean_up; 156090fccb52SAndrzej Pietrasiewicz } 156190fccb52SAndrzej Pietrasiewicz 156290fccb52SAndrzej Pietrasiewicz reg = ioremap(res->start, resource_size(res)); 156390fccb52SAndrzej Pietrasiewicz if (reg == NULL) { 156490fccb52SAndrzej Pietrasiewicz ret = -ENOMEM; 156590fccb52SAndrzej Pietrasiewicz pr_err("ioremap error.\n"); 156690fccb52SAndrzej Pietrasiewicz goto clean_up; 156790fccb52SAndrzej Pietrasiewicz } 156890fccb52SAndrzej Pietrasiewicz 156990fccb52SAndrzej Pietrasiewicz if (dev_get_platdata(&pdev->dev) == NULL) { 157090fccb52SAndrzej Pietrasiewicz dev_err(&pdev->dev, "no platform data\n"); 157190fccb52SAndrzej Pietrasiewicz ret = -ENODEV; 157290fccb52SAndrzej Pietrasiewicz goto clean_up; 157390fccb52SAndrzej Pietrasiewicz } 157490fccb52SAndrzej Pietrasiewicz 157590fccb52SAndrzej Pietrasiewicz /* initialize ucd */ 157690fccb52SAndrzej Pietrasiewicz m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); 157790fccb52SAndrzej Pietrasiewicz if (m66592 == NULL) { 157890fccb52SAndrzej Pietrasiewicz ret = -ENOMEM; 157990fccb52SAndrzej Pietrasiewicz goto clean_up; 158090fccb52SAndrzej Pietrasiewicz } 158190fccb52SAndrzej Pietrasiewicz 158290fccb52SAndrzej Pietrasiewicz m66592->pdata = dev_get_platdata(&pdev->dev); 158390fccb52SAndrzej Pietrasiewicz m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK; 158490fccb52SAndrzej Pietrasiewicz 158590fccb52SAndrzej Pietrasiewicz spin_lock_init(&m66592->lock); 158690fccb52SAndrzej Pietrasiewicz platform_set_drvdata(pdev, m66592); 158790fccb52SAndrzej Pietrasiewicz 158890fccb52SAndrzej Pietrasiewicz m66592->gadget.ops = &m66592_gadget_ops; 158990fccb52SAndrzej Pietrasiewicz m66592->gadget.max_speed = USB_SPEED_HIGH; 159090fccb52SAndrzej Pietrasiewicz m66592->gadget.name = udc_name; 159190fccb52SAndrzej Pietrasiewicz 1592e99e88a9SKees Cook timer_setup(&m66592->timer, m66592_timer, 0); 159390fccb52SAndrzej Pietrasiewicz m66592->reg = reg; 159490fccb52SAndrzej Pietrasiewicz 159590fccb52SAndrzej Pietrasiewicz ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, 159690fccb52SAndrzej Pietrasiewicz udc_name, m66592); 159790fccb52SAndrzej Pietrasiewicz if (ret < 0) { 159890fccb52SAndrzej Pietrasiewicz pr_err("request_irq error (%d)\n", ret); 159990fccb52SAndrzej Pietrasiewicz goto clean_up; 160090fccb52SAndrzej Pietrasiewicz } 160190fccb52SAndrzej Pietrasiewicz 160290fccb52SAndrzej Pietrasiewicz if (m66592->pdata->on_chip) { 160390fccb52SAndrzej Pietrasiewicz snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); 160490fccb52SAndrzej Pietrasiewicz m66592->clk = clk_get(&pdev->dev, clk_name); 160590fccb52SAndrzej Pietrasiewicz if (IS_ERR(m66592->clk)) { 160690fccb52SAndrzej Pietrasiewicz dev_err(&pdev->dev, "cannot get clock \"%s\"\n", 160790fccb52SAndrzej Pietrasiewicz clk_name); 160890fccb52SAndrzej Pietrasiewicz ret = PTR_ERR(m66592->clk); 160990fccb52SAndrzej Pietrasiewicz goto clean_up2; 161090fccb52SAndrzej Pietrasiewicz } 161190fccb52SAndrzej Pietrasiewicz clk_enable(m66592->clk); 161290fccb52SAndrzej Pietrasiewicz } 161390fccb52SAndrzej Pietrasiewicz 161490fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&m66592->gadget.ep_list); 161590fccb52SAndrzej Pietrasiewicz m66592->gadget.ep0 = &m66592->ep[0].ep; 161690fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); 161790fccb52SAndrzej Pietrasiewicz for (i = 0; i < M66592_MAX_NUM_PIPE; i++) { 161890fccb52SAndrzej Pietrasiewicz struct m66592_ep *ep = &m66592->ep[i]; 161990fccb52SAndrzej Pietrasiewicz 162090fccb52SAndrzej Pietrasiewicz if (i != 0) { 162190fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list); 162290fccb52SAndrzej Pietrasiewicz list_add_tail(&m66592->ep[i].ep.ep_list, 162390fccb52SAndrzej Pietrasiewicz &m66592->gadget.ep_list); 162490fccb52SAndrzej Pietrasiewicz } 162590fccb52SAndrzej Pietrasiewicz ep->m66592 = m66592; 162690fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->queue); 162790fccb52SAndrzej Pietrasiewicz ep->ep.name = m66592_ep_name[i]; 162890fccb52SAndrzej Pietrasiewicz ep->ep.ops = &m66592_ep_ops; 162990fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&ep->ep, 512); 16308ddbf94fSRobert Baldyga 16318ddbf94fSRobert Baldyga if (i == 0) { 16328ddbf94fSRobert Baldyga ep->ep.caps.type_control = true; 16338ddbf94fSRobert Baldyga } else { 16348ddbf94fSRobert Baldyga ep->ep.caps.type_iso = true; 16358ddbf94fSRobert Baldyga ep->ep.caps.type_bulk = true; 16368ddbf94fSRobert Baldyga ep->ep.caps.type_int = true; 16378ddbf94fSRobert Baldyga } 16388ddbf94fSRobert Baldyga 16398ddbf94fSRobert Baldyga ep->ep.caps.dir_in = true; 16408ddbf94fSRobert Baldyga ep->ep.caps.dir_out = true; 164190fccb52SAndrzej Pietrasiewicz } 164290fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64); 164390fccb52SAndrzej Pietrasiewicz m66592->ep[0].pipenum = 0; 164490fccb52SAndrzej Pietrasiewicz m66592->ep[0].fifoaddr = M66592_CFIFO; 164590fccb52SAndrzej Pietrasiewicz m66592->ep[0].fifosel = M66592_CFIFOSEL; 164690fccb52SAndrzej Pietrasiewicz m66592->ep[0].fifoctr = M66592_CFIFOCTR; 164790fccb52SAndrzej Pietrasiewicz m66592->ep[0].fifotrn = 0; 164890fccb52SAndrzej Pietrasiewicz m66592->ep[0].pipectr = get_pipectr_addr(0); 164990fccb52SAndrzej Pietrasiewicz m66592->pipenum2ep[0] = &m66592->ep[0]; 165090fccb52SAndrzej Pietrasiewicz m66592->epaddr2ep[0] = &m66592->ep[0]; 165190fccb52SAndrzej Pietrasiewicz 165290fccb52SAndrzej Pietrasiewicz m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL); 165390fccb52SAndrzej Pietrasiewicz if (m66592->ep0_req == NULL) { 165490fccb52SAndrzej Pietrasiewicz ret = -ENOMEM; 165590fccb52SAndrzej Pietrasiewicz goto clean_up3; 165690fccb52SAndrzej Pietrasiewicz } 165790fccb52SAndrzej Pietrasiewicz m66592->ep0_req->complete = nop_completion; 165890fccb52SAndrzej Pietrasiewicz 165990fccb52SAndrzej Pietrasiewicz init_controller(m66592); 166090fccb52SAndrzej Pietrasiewicz 166190fccb52SAndrzej Pietrasiewicz ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget); 166290fccb52SAndrzej Pietrasiewicz if (ret) 166390fccb52SAndrzej Pietrasiewicz goto err_add_udc; 166490fccb52SAndrzej Pietrasiewicz 166590fccb52SAndrzej Pietrasiewicz dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); 166690fccb52SAndrzej Pietrasiewicz return 0; 166790fccb52SAndrzej Pietrasiewicz 166890fccb52SAndrzej Pietrasiewicz err_add_udc: 166990fccb52SAndrzej Pietrasiewicz m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); 167090fccb52SAndrzej Pietrasiewicz 167190fccb52SAndrzej Pietrasiewicz clean_up3: 167290fccb52SAndrzej Pietrasiewicz if (m66592->pdata->on_chip) { 167390fccb52SAndrzej Pietrasiewicz clk_disable(m66592->clk); 167490fccb52SAndrzej Pietrasiewicz clk_put(m66592->clk); 167590fccb52SAndrzej Pietrasiewicz } 167690fccb52SAndrzej Pietrasiewicz clean_up2: 167790fccb52SAndrzej Pietrasiewicz free_irq(ires->start, m66592); 167890fccb52SAndrzej Pietrasiewicz clean_up: 167990fccb52SAndrzej Pietrasiewicz if (m66592) { 168090fccb52SAndrzej Pietrasiewicz if (m66592->ep0_req) 168190fccb52SAndrzej Pietrasiewicz m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); 168290fccb52SAndrzej Pietrasiewicz kfree(m66592); 168390fccb52SAndrzej Pietrasiewicz } 168490fccb52SAndrzej Pietrasiewicz if (reg) 168590fccb52SAndrzej Pietrasiewicz iounmap(reg); 168690fccb52SAndrzej Pietrasiewicz 168790fccb52SAndrzej Pietrasiewicz return ret; 168890fccb52SAndrzej Pietrasiewicz } 168990fccb52SAndrzej Pietrasiewicz 169090fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 169190fccb52SAndrzej Pietrasiewicz static struct platform_driver m66592_driver = { 1692c94e289fSArnd Bergmann .remove = m66592_remove, 169390fccb52SAndrzej Pietrasiewicz .driver = { 1694*93bc7363SCorentin Labbe .name = udc_name, 169590fccb52SAndrzej Pietrasiewicz }, 169690fccb52SAndrzej Pietrasiewicz }; 169790fccb52SAndrzej Pietrasiewicz 169890fccb52SAndrzej Pietrasiewicz module_platform_driver_probe(m66592_driver, m66592_probe); 1699