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

📄 fsl_usb2_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. * * Author: Li Yang <leoli@freescale.com> *         Jiang Bo <tanya.jiang@freescale.com> * * Description: * Freescale high-speed USB SOC DR module device controller driver. * This can be found on MPC8349E/MPC8313E cpus. * The driver is previously named as mpc_udc.  Based on bare board * code from Dave Liu and Shlomi Gridish. * * 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;  either version 2 of the  License, or (at your * option) any later version. */#undef VERBOSE#include <linux/module.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/mm.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include <linux/usb/otg.h>#include <linux/dma-mapping.h>#include <linux/platform_device.h>#include <linux/fsl_devices.h>#include <linux/dmapool.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include <asm/dma.h>#include <asm/cacheflush.h>#include "fsl_usb2_udc.h"#define	DRIVER_DESC	"Freescale High-Speed USB SOC Device Controller driver"#define	DRIVER_AUTHOR	"Li Yang/Jiang Bo"#define	DRIVER_VERSION	"Apr 20, 2007"#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)static const char driver_name[] = "fsl-usb2-udc";static const char driver_desc[] = DRIVER_DESC;volatile static struct usb_dr_device *dr_regs = NULL;volatile static struct usb_sys_interface *usb_sys_regs = NULL;/* it is initialized in probe()  */static struct fsl_udc *udc_controller = NULL;static const struct usb_endpoint_descriptorfsl_ep0_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT,	.bEndpointAddress =	0,	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,};static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);static int fsl_udc_resume(struct platform_device *pdev);static void fsl_ep_fifo_flush(struct usb_ep *_ep);#ifdef CONFIG_PPC32#define fsl_readl(addr)		in_le32(addr)#define fsl_writel(addr, val32) out_le32(val32, addr)#else#define fsl_readl(addr)		readl(addr)#define fsl_writel(addr, val32) writel(addr, val32)#endif/******************************************************************** *	Internal Used Function********************************************************************//*----------------------------------------------------------------- * done() - retire a request; caller blocked irqs * @status : request status to be set, only works when *	request is still in progress. *--------------------------------------------------------------*/static void done(struct fsl_ep *ep, struct fsl_req *req, int status){	struct fsl_udc *udc = NULL;	unsigned char stopped = ep->stopped;	struct ep_td_struct *curr_td, *next_td;	int j;	udc = (struct fsl_udc *)ep->udc;	/* Removed the req from fsl_ep->queue */	list_del_init(&req->queue);	/* req.status should be set as -EINPROGRESS in ep_queue() */	if (req->req.status == -EINPROGRESS)		req->req.status = status;	else		status = req->req.status;	/* Free dtd for the request */	next_td = req->head;	for (j = 0; j < req->dtd_count; j++) {		curr_td = next_td;		if (j != req->dtd_count - 1) {			next_td = curr_td->next_td_virt;		}		dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);	}	if (req->mapped) {		dma_unmap_single(ep->udc->gadget.dev.parent,			req->req.dma, req->req.length,			ep_is_in(ep)				? DMA_TO_DEVICE				: DMA_FROM_DEVICE);		req->req.dma = DMA_ADDR_INVALID;		req->mapped = 0;	} else		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,			req->req.dma, req->req.length,			ep_is_in(ep)				? DMA_TO_DEVICE				: DMA_FROM_DEVICE);	if (status && (status != -ESHUTDOWN))		VDBG("complete %s req %p stat %d len %u/%u",			ep->ep.name, &req->req, status,			req->req.actual, req->req.length);	ep->stopped = 1;	spin_unlock(&ep->udc->lock);	/* complete() is from gadget layer,	 * eg fsg->bulk_in_complete() */	if (req->req.complete)		req->req.complete(&ep->ep, &req->req);	spin_lock(&ep->udc->lock);	ep->stopped = stopped;}/*----------------------------------------------------------------- * nuke(): delete all requests related to this ep * called with spinlock held *--------------------------------------------------------------*/static void nuke(struct fsl_ep *ep, int status){	ep->stopped = 1;	/* Flush fifo */	fsl_ep_fifo_flush(&ep->ep);	/* Whether this eq has request linked */	while (!list_empty(&ep->queue)) {		struct fsl_req *req = NULL;		req = list_entry(ep->queue.next, struct fsl_req, queue);		done(ep, req, status);	}}/*------------------------------------------------------------------	Internal Hardware related function ------------------------------------------------------------------*/static int dr_controller_setup(struct fsl_udc *udc){	unsigned int tmp = 0, portctrl = 0, ctrl = 0;	unsigned long timeout;#define FSL_UDC_RESET_TIMEOUT 1000	/* before here, make sure dr_regs has been initialized */	if (!udc)		return -EINVAL;	/* Stop and reset the usb controller */	tmp = fsl_readl(&dr_regs->usbcmd);	tmp &= ~USB_CMD_RUN_STOP;	fsl_writel(tmp, &dr_regs->usbcmd);	tmp = fsl_readl(&dr_regs->usbcmd);	tmp |= USB_CMD_CTRL_RESET;	fsl_writel(tmp, &dr_regs->usbcmd);	/* Wait for reset to complete */	timeout = jiffies + FSL_UDC_RESET_TIMEOUT;	while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {		if (time_after(jiffies, timeout)) {			ERR("udc reset timeout! \n");			return -ETIMEDOUT;		}		cpu_relax();	}	/* Set the controller as device mode */	tmp = fsl_readl(&dr_regs->usbmode);	tmp |= USB_MODE_CTRL_MODE_DEVICE;	/* Disable Setup Lockout */	tmp |= USB_MODE_SETUP_LOCK_OFF;	fsl_writel(tmp, &dr_regs->usbmode);	/* Clear the setup status */	fsl_writel(0, &dr_regs->usbsts);	tmp = udc->ep_qh_dma;	tmp &= USB_EP_LIST_ADDRESS_MASK;	fsl_writel(tmp, &dr_regs->endpointlistaddr);	VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",		(int)udc->ep_qh, (int)tmp,		fsl_readl(&dr_regs->endpointlistaddr));	/* Config PHY interface */	portctrl = fsl_readl(&dr_regs->portsc1);	portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);	switch (udc->phy_mode) {	case FSL_USB2_PHY_ULPI:		portctrl |= PORTSCX_PTS_ULPI;		break;	case FSL_USB2_PHY_UTMI_WIDE:		portctrl |= PORTSCX_PTW_16BIT;		/* fall through */	case FSL_USB2_PHY_UTMI:		portctrl |= PORTSCX_PTS_UTMI;		break;	case FSL_USB2_PHY_SERIAL:		portctrl |= PORTSCX_PTS_FSLS;		break;	default:		return -EINVAL;	}	fsl_writel(portctrl, &dr_regs->portsc1);	/* Config control enable i/o output, cpu endian register */	ctrl = __raw_readl(&usb_sys_regs->control);	ctrl |= USB_CTRL_IOENB;	__raw_writel(ctrl, &usb_sys_regs->control);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)	/* Turn on cache snooping hardware, since some PowerPC platforms	 * wholly rely on hardware to deal with cache coherent. */	/* Setup Snooping for all the 4GB space */	tmp = SNOOP_SIZE_2GB;	/* starts from 0x0, size 2G */	__raw_writel(tmp, &usb_sys_regs->snoop1);	tmp |= 0x80000000;	/* starts from 0x8000000, size 2G */	__raw_writel(tmp, &usb_sys_regs->snoop2);#endif	return 0;}/* Enable DR irq and set controller to run state */static void dr_controller_run(struct fsl_udc *udc){	u32 temp;	/* Enable DR irq reg */	temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN		| USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN		| USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;	fsl_writel(temp, &dr_regs->usbintr);	/* Clear stopped bit */	udc->stopped = 0;	/* Set the controller as device mode */	temp = fsl_readl(&dr_regs->usbmode);	temp |= USB_MODE_CTRL_MODE_DEVICE;	fsl_writel(temp, &dr_regs->usbmode);	/* Set controller to Run */	temp = fsl_readl(&dr_regs->usbcmd);	temp |= USB_CMD_RUN_STOP;	fsl_writel(temp, &dr_regs->usbcmd);	return;}static void dr_controller_stop(struct fsl_udc *udc){	unsigned int tmp;	/* disable all INTR */	fsl_writel(0, &dr_regs->usbintr);	/* Set stopped bit for isr */	udc->stopped = 1;	/* disable IO output *//*	usb_sys_regs->control = 0; */	/* set controller to Stop */	tmp = fsl_readl(&dr_regs->usbcmd);	tmp &= ~USB_CMD_RUN_STOP;	fsl_writel(tmp, &dr_regs->usbcmd);	return;}void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type){	unsigned int tmp_epctrl = 0;	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);	if (dir) {		if (ep_num)			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;		tmp_epctrl |= EPCTRL_TX_ENABLE;		tmp_epctrl |= ((unsigned int)(ep_type)				<< EPCTRL_TX_EP_TYPE_SHIFT);	} else {		if (ep_num)			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;		tmp_epctrl |= EPCTRL_RX_ENABLE;		tmp_epctrl |= ((unsigned int)(ep_type)				<< EPCTRL_RX_EP_TYPE_SHIFT);	}	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);}static voiddr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value){	u32 tmp_epctrl = 0;	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);	if (value) {		/* set the stall bit */		if (dir)			tmp_epctrl |= EPCTRL_TX_EP_STALL;		else			tmp_epctrl |= EPCTRL_RX_EP_STALL;	} else {		/* clear the stall bit and reset data toggle */		if (dir) {			tmp_epctrl &= ~EPCTRL_TX_EP_STALL;			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;		} else {			tmp_epctrl &= ~EPCTRL_RX_EP_STALL;			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;		}	}	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);}/* Get stall status of a specific ep   Return: 0: not stalled; 1:stalled */static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir){	u32 epctrl;	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);	if (dir)		return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;	else		return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;}/********************************************************************	Internal Structure Build up functions********************************************************************//*------------------------------------------------------------------* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH * @zlt: Zero Length Termination Select (1: disable; 0: enable) * @mult: Mult field ------------------------------------------------------------------*/static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,		unsigned char dir, unsigned char ep_type,		unsigned int max_pkt_len,		unsigned int zlt, unsigned char mult){	struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];	unsigned int tmp = 0;	/* set the Endpoint Capabilites in QH */	switch (ep_type) {	case USB_ENDPOINT_XFER_CONTROL:		/* Interrupt On Setup (IOS). for control ep  */		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)			| EP_QUEUE_HEAD_IOS;		break;	case USB_ENDPOINT_XFER_ISOC:		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)			| (mult << EP_QUEUE_HEAD_MULT_POS);		break;	case USB_ENDPOINT_XFER_BULK:	case USB_ENDPOINT_XFER_INT:		tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;		break;	default:		VDBG("error ep type is %d", ep_type);		return;	}	if (zlt)		tmp |= EP_QUEUE_HEAD_ZLT_SEL;	p_QH->max_pkt_length = cpu_to_le32(tmp);	return;}/* Setup qh structure and ep register for ep0. */static void ep0_setup(struct fsl_udc *udc){	/* the intialization of an ep includes: fields in QH, Regs,	 * fsl_ep struct */	struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,			USB_MAX_CTRL_PAYLOAD, 0, 0);	struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,			USB_MAX_CTRL_PAYLOAD, 0, 0);	dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);	dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);	return;}/***********************************************************************		Endpoint Management Functions***********************************************************************//*------------------------------------------------------------------------- * when configurations are set, or when interface settings change * for example the do_set_interface() in gadget layer, * the driver will enable or disable the relevant endpoints * ep0 doesn't use this routine. It is always enabled.-------------------------------------------------------------------------*/static int fsl_ep_enable(struct usb_ep *_ep,		const struct usb_endpoint_descriptor *desc){	struct fsl_udc *udc = NULL;	struct fsl_ep *ep = NULL;	unsigned short max = 0;	unsigned char mult = 0, zlt;	int retval = -EINVAL;	unsigned long flags = 0;	ep = container_of(_ep, struct fsl_ep, ep);	/* catch various bogus parameters */	if (!_ep || !desc || ep->desc			|| (desc->bDescriptorType != USB_DT_ENDPOINT))		return -EINVAL;	udc = ep->udc;	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))		return -ESHUTDOWN;	max = le16_to_cpu(desc->wMaxPacketSize);	/* Disable automatic zlp generation.  Driver is reponsible to indicate	 * explicitly through req->req.zero.  This is needed to enable multi-td	 * request. */	zlt = 1;	/* Assume the max packet size from gadget is always correct */	switch (desc->bmAttributes & 0x03) {	case USB_ENDPOINT_XFER_CONTROL:	case USB_ENDPOINT_XFER_BULK:	case USB_ENDPOINT_XFER_INT:		/* mult = 0.  Execute N Transactions as demonstrated by		 * the USB variable length packet protocol where N is		 * computed using the Maximum Packet Length (dQH) and		 * the Total Bytes field (dTD) */		mult = 0;		break;	case USB_ENDPOINT_XFER_ISOC:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -