⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 m66592-udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -