📄 m66592-udc.c
字号:
/* * M66592 UDC (USB gadget) * * Copyright (C) 2006-2007 Renesas Solutions Corp. * * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */#include <linux/module.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/io.h>#include <linux/platform_device.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include "m66592-udc.h"MODULE_DESCRIPTION("M66592 USB gadget driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Yoshihiro Shimoda");#define DRIVER_VERSION "29 May 2007"/* module parameters */static unsigned short clock = M66592_XTAL24;module_param(clock, ushort, 0644);MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 " "(default=16384)");static unsigned short vif = M66592_LDRV;module_param(vif, ushort, 0644);MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0 (default=32768)");static unsigned short endian;module_param(endian, ushort, 0644);MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)");static unsigned short irq_sense = M66592_INTL;module_param(irq_sense, ushort, 0644);MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0 " "(default=2)");static const char udc_name[] = "m66592_udc";static const char *m66592_ep_name[] = { "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"};static void disable_controller(struct m66592 *m66592);static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags);static void transfer_complete(struct m66592_ep *ep, struct m66592_request *req, int status);/*-------------------------------------------------------------------------*/static inline u16 get_usb_speed(struct m66592 *m66592){ return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);}static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum, unsigned long reg){ u16 tmp; tmp = m66592_read(m66592, M66592_INTENB0); m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, M66592_INTENB0); m66592_bset(m66592, (1 << pipenum), reg); m66592_write(m66592, tmp, M66592_INTENB0);}static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum, unsigned long reg){ u16 tmp; tmp = m66592_read(m66592, M66592_INTENB0); m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, M66592_INTENB0); m66592_bclr(m66592, (1 << pipenum), reg); m66592_write(m66592, tmp, M66592_INTENB0);}static void m66592_usb_connect(struct m66592 *m66592){ m66592_bset(m66592, M66592_CTRE, M66592_INTENB0); m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, M66592_INTENB0); m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);}static void m66592_usb_disconnect(struct m66592 *m66592)__releases(m66592->lock)__acquires(m66592->lock){ m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0); m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, M66592_INTENB0); m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); m66592->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock(&m66592->lock); m66592->driver->disconnect(&m66592->gadget); spin_lock(&m66592->lock); disable_controller(m66592); INIT_LIST_HEAD(&m66592->ep[0].queue);}static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum){ u16 pid = 0; unsigned long offset; if (pipenum == 0) pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID; else if (pipenum < M66592_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); pid = m66592_read(m66592, offset) & M66592_PID; } else printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); return pid;}static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum, u16 pid){ unsigned long offset; if (pipenum == 0) m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR); else if (pipenum < M66592_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); m66592_mdfy(m66592, pid, M66592_PID, offset); } else printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);}static inline void pipe_start(struct m66592 *m66592, u16 pipenum){ control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);}static inline void pipe_stop(struct m66592 *m66592, u16 pipenum){ control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);}static inline void pipe_stall(struct m66592 *m66592, u16 pipenum){ control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);}static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum){ u16 ret = 0; unsigned long offset; if (pipenum == 0) ret = m66592_read(m66592, M66592_DCPCTR); else if (pipenum < M66592_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); ret = m66592_read(m66592, offset); } else printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); return ret;}static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum){ unsigned long offset; pipe_stop(m66592, pipenum); if (pipenum == 0) m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR); else if (pipenum < M66592_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); m66592_bset(m66592, M66592_SQCLR, offset); } else printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum);}static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum){ u16 tmp; int size; if (pipenum == 0) { tmp = m66592_read(m66592, M66592_DCPCFG); if ((tmp & M66592_CNTMD) != 0) size = 256; else { tmp = m66592_read(m66592, M66592_DCPMAXP); size = tmp & M66592_MAXP; } } else { m66592_write(m66592, pipenum, M66592_PIPESEL); tmp = m66592_read(m66592, M66592_PIPECFG); if ((tmp & M66592_CNTMD) != 0) { tmp = m66592_read(m66592, M66592_PIPEBUF); size = ((tmp >> 10) + 1) * 64; } else { tmp = m66592_read(m66592, M66592_PIPEMAXP); size = tmp & M66592_MXPS; } } return size;}static inline void pipe_change(struct m66592 *m66592, u16 pipenum){ struct m66592_ep *ep = m66592->pipenum2ep[pipenum]; if (ep->use_dma) return; m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel); ndelay(450); m66592_bset(m66592, M66592_MBW, ep->fifosel);}static int pipe_buffer_setting(struct m66592 *m66592, struct m66592_pipe_info *info){ u16 bufnum = 0, buf_bsize = 0; u16 pipecfg = 0; if (info->pipe == 0) return -EINVAL; m66592_write(m66592, info->pipe, M66592_PIPESEL); if (info->dir_in) pipecfg |= M66592_DIR; pipecfg |= info->type; pipecfg |= info->epnum; switch (info->type) { case M66592_INT: bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT); buf_bsize = 0; break; case M66592_BULK: bufnum = m66592->bi_bufnum + (info->pipe - M66592_BASE_PIPENUM_BULK) * 16; m66592->bi_bufnum += 16; buf_bsize = 7; pipecfg |= M66592_DBLB; if (!info->dir_in) pipecfg |= M66592_SHTNAK; break; case M66592_ISO: bufnum = m66592->bi_bufnum + (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16; m66592->bi_bufnum += 16; buf_bsize = 7; break; } if (m66592->bi_bufnum > M66592_MAX_BUFNUM) { printk(KERN_ERR "m66592 pipe memory is insufficient(%d)\n", m66592->bi_bufnum); return -ENOMEM; } m66592_write(m66592, pipecfg, M66592_PIPECFG); m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF); m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP); if (info->interval) info->interval--; m66592_write(m66592, info->interval, M66592_PIPEPERI); return 0;}static void pipe_buffer_release(struct m66592 *m66592, struct m66592_pipe_info *info){ if (info->pipe == 0) return; switch (info->type) { case M66592_BULK: if (is_bulk_pipe(info->pipe)) m66592->bi_bufnum -= 16; break; case M66592_ISO: if (is_isoc_pipe(info->pipe)) m66592->bi_bufnum -= 16; break; } if (is_bulk_pipe(info->pipe)) { m66592->bulk--; } else if (is_interrupt_pipe(info->pipe)) m66592->interrupt--; else if (is_isoc_pipe(info->pipe)) { m66592->isochronous--; if (info->type == M66592_BULK) m66592->bulk--; } else printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", info->pipe);}static void pipe_initialize(struct m66592_ep *ep){ struct m66592 *m66592 = ep->m66592; m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel); m66592_write(m66592, M66592_ACLRM, ep->pipectr); m66592_write(m66592, 0, ep->pipectr); m66592_write(m66592, M66592_SQCLR, ep->pipectr); if (ep->use_dma) { m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel); ndelay(450); m66592_bset(m66592, M66592_MBW, ep->fifosel); }}static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, const struct usb_endpoint_descriptor *desc, u16 pipenum, int dma){ if ((pipenum != 0) && dma) { if (m66592->num_dma == 0) { m66592->num_dma++; ep->use_dma = 1; ep->fifoaddr = M66592_D0FIFO; ep->fifosel = M66592_D0FIFOSEL; ep->fifoctr = M66592_D0FIFOCTR; ep->fifotrn = M66592_D0FIFOTRN; } else if (m66592->num_dma == 1) { m66592->num_dma++; ep->use_dma = 1; ep->fifoaddr = M66592_D1FIFO; ep->fifosel = M66592_D1FIFOSEL; ep->fifoctr = M66592_D1FIFOCTR; ep->fifotrn = M66592_D1FIFOTRN; } else { ep->use_dma = 0; ep->fifoaddr = M66592_CFIFO; ep->fifosel = M66592_CFIFOSEL; ep->fifoctr = M66592_CFIFOCTR; ep->fifotrn = 0; } } else { ep->use_dma = 0; ep->fifoaddr = M66592_CFIFO; ep->fifosel = M66592_CFIFOSEL; ep->fifoctr = M66592_CFIFOCTR; ep->fifotrn = 0; } ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); m66592->pipenum2ep[pipenum] = ep; m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; INIT_LIST_HEAD(&ep->queue);}static void m66592_ep_release(struct m66592_ep *ep){ struct m66592 *m66592 = ep->m66592; u16 pipenum = ep->pipenum; if (pipenum == 0) return; if (ep->use_dma) m66592->num_dma--; ep->pipenum = 0; ep->busy = 0; ep->use_dma = 0;}static int alloc_pipe_config(struct m66592_ep *ep, const struct usb_endpoint_descriptor *desc){ struct m66592 *m66592 = ep->m66592; struct m66592_pipe_info info; int dma = 0; int *counter; int ret; ep->desc = desc; BUG_ON(ep->pipenum); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: if (m66592->bulk >= M66592_MAX_NUM_BULK) { if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { printk(KERN_ERR "bulk pipe is insufficient\n"); return -ENODEV; } else { info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous; counter = &m66592->isochronous; } } else { info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk; counter = &m66592->bulk; } info.type = M66592_BULK; dma = 1; break; case USB_ENDPOINT_XFER_INT: if (m66592->interrupt >= M66592_MAX_NUM_INT) { printk(KERN_ERR "interrupt pipe is insufficient\n"); return -ENODEV; } info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt; info.type = M66592_INT; counter = &m66592->interrupt; break; case USB_ENDPOINT_XFER_ISOC: if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { printk(KERN_ERR "isochronous pipe is insufficient\n"); return -ENODEV; } info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous; info.type = M66592_ISO; counter = &m66592->isochronous; break; default: printk(KERN_ERR "unexpect xfer type\n"); return -EINVAL; } ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; else info.dir_in = 0; ret = pipe_buffer_setting(m66592, &info); if (ret < 0) { printk(KERN_ERR "pipe_buffer_setting fail\n"); return ret; } (*counter)++; if ((counter == &m66592->isochronous) && info.type == M66592_BULK) m66592->bulk++; m66592_ep_setting(m66592, ep, desc, info.pipe, dma); pipe_initialize(ep); return 0;}static int free_pipe_config(struct m66592_ep *ep){ struct m66592 *m66592 = ep->m66592; struct m66592_pipe_info info; info.pipe = ep->pipenum; info.type = ep->type; pipe_buffer_release(m66592, &info); m66592_ep_release(ep); return 0;}/*-------------------------------------------------------------------------*/static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum){ enable_irq_ready(m66592, pipenum); enable_irq_nrdy(m66592, pipenum);}static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum){ disable_irq_ready(m66592, pipenum); disable_irq_nrdy(m66592, pipenum);}/* if complete is true, gadget driver complete function is not call */static void control_end(struct m66592 *m66592, unsigned ccpl){ m66592->ep[0].internal_ccpl = ccpl; pipe_start(m66592, 0); m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);}static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req){ struct m66592 *m66592 = ep->m66592; pipe_change(m66592, ep->pipenum); m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0, (M66592_ISEL | M66592_CURPIPE), M66592_CFIFOSEL); m66592_write(m66592, M66592_BCLR, ep->fifoctr); if (req->req.length == 0) { m66592_bset(m66592, M66592_BVAL, ep->fifoctr); pipe_start(m66592, 0); transfer_complete(ep, req, 0); } else { m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS); irq_ep0_write(ep, req); }}static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req){ struct m66592 *m66592 = ep->m66592; u16 tmp; pipe_change(m66592, ep->pipenum);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -