plat_uds.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 2,070 行 · 第 1/4 页

C
2,070
字号
/***************************************************************** * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006 by Nokia Corporation * * This file is part of the Inventra Controller Driver for Linux. * * The Inventra Controller Driver for Linux is free software; you * can redistribute it and/or modify it under the terms of the GNU * General Public License version 2 as published by the Free Software * Foundation. * * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, * write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA  02111-1307  USA * * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT.  MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR * GRAPHICS SUPPORT CUSTOMER. ******************************************************************//* * Inventra (Multipoint) Dual-Role Controller Driver for Linux. * * This consists of a Host Controller Driver (HCD) and a peripheral * controller driver implementing the "Gadget" API; OTG support is * in the works.  These are normal Linux-USB controller drivers which * use IRQs and have no dedicated thread. * * This version of the driver has only been used with products from * Texas Instruments.  Those products integrate the Inventra logic * with other DMA, IRQ, and bus modules, as well as other logic that * needs to be reflected in this driver. * * * NOTE:  the original Mentor code here was pretty much a collection * of mechanisms that don't seem to have been fully integrated/working * for *any* Linux kernel version.  This version aims at Linux 2.6.now, * Key open issues include: * *  - Lack of host-side transaction scheduling, for all transfer types. *    The hardware doesn't do it; instead, software must. * *    This is not an issue for OTG devices that don't support external *    hubs, but for more "normal" USB hosts it's a user issue that the *    "multipoint" support doesn't scale in the expected ways.  That *    includes DaVinci EVM in a common non-OTG mode. * *      * Control and bulk use dedicated endpoints, and there's as *        yet no mechanism to either (a) reclaim the hardware when *        peripherals are NAKing, which gets complicated with bulk *        endpoints, or (b) use more than a single bulk endpoint in *        each direction. * *        RESULT:  one device may be perceived as blocking another one. * *      * Interrupt and isochronous will dynamically allocate endpoint *        hardware, but (a) there's no record keeping for bandwidth; *        (b) in the common case that few endpoints are available, there *        is no mechanism to reuse endpoints to talk to multiple devices. * *        RESULT:  At one extreme, bandwidth can be overcommitted in *        some hardware configurations, no faults will be reported. *        At the other extreme, the bandwidth capabilities which do *        exist tend to be severely undercommitted.  You can't yet hook *        up both a keyboard and a mouse to an external USB hub. *//* * This gets many kinds of configuration information: *	- Kconfig for everything user-configurable *	- <asm/arch/hdrc_cnf.h> for SOC or family details *	- platform_device for addressing, irq, and platform_data *	- platform_data is mostly for board-specific informarion * * Most of the conditional compilation will (someday) vanish. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/list.h>#include <linux/kobject.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/arch/hardware.h>#include <asm/arch/memory.h>#include <asm/mach-types.h>#include "musbdefs.h"//Deviation from Open Source - //musb_procfs.c has the definition for now.//we need the procfs interface even in non-debug mode//const char *otg_state_string(struct musb *musb)//{//	static char buf[8];////	snprintf(buf, sizeof buf, "otg-%d", musb->xceiv.state);//	return buf;//}#define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"#define DRIVER_DESC "Inventra Dual-Role USB Controller Driver"#define MUSB_VERSION_BASE "2.2a/db-0.5.2"#define MUSB_VERSION_SUFFIX	""#define MUSB_VERSION	MUSB_VERSION_BASE MUSB_VERSION_SUFFIX#define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSIONconst char musb_driver_name[] = "musb_hdrc";MODULE_DESCRIPTION(DRIVER_INFO);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_LICENSE("GPL");/*-------------------------------------------------------------------------*/static inline struct musb *dev_to_musb(struct device *dev){	/* usbcore insists dev->driver_data is a "struct hcd *" */	return hcd_to_musb(dev_get_drvdata(dev));}/*-------------------------------------------------------------------------*//* * Load an endpoint's FIFO */void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 wCount, const u8 *pSource){	void __iomem *fifo = hw_ep->fifo;	prefetch((u8 *)pSource);	DBG(4, "%cX ep%d fifo %p count %d buf %p\n",			'T', hw_ep->bLocalEnd, fifo, wCount, pSource);	/* we can't assume unaligned reads work */	if (likely((0x01 & (unsigned long) pSource) == 0)) {		u16	index = 0;		/* best case is 32bit-aligned source address */		if ((0x02 & (unsigned long) pSource) == 0) {			if (wCount >= 4) {				writesl(fifo, pSource + index, wCount >> 2);				index += wCount & ~0x03;			}			if (wCount & 0x02) {				musb_writew(fifo, 0, *(u16*)&pSource[index]);				index += 2;			}		} else {			if (wCount >= 2) {				writesw(fifo, pSource + index, wCount >> 1);				index += wCount & ~0x01;			}		}		if (wCount & 0x01)			musb_writeb(fifo, 0, pSource[index]);	} else  {		/* byte aligned */		writesb(fifo, pSource, wCount);	}}/* * Unload an endpoint's FIFO */void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 wCount, u8 *pDest){	void __iomem *fifo = hw_ep->fifo;	DBG(4, "%cX ep%d fifo %p count %d buf %p\n",			'R', hw_ep->bLocalEnd, fifo, wCount, pDest);	/* we can't assume unaligned writes work */	if (likely((0x01 & (unsigned long) pDest) == 0)) {		u16	index = 0;		/* best case is 32bit-aligned destination address */		if ((0x02 & (unsigned long) pDest) == 0) {			if (wCount >= 4) {				readsl(fifo, pDest, wCount >> 2);				index = wCount & ~0x03;			}			if (wCount & 0x02) {				*(u16*)&pDest[index] = musb_readw(fifo, 0);				index += 2;			}		} else {			if (wCount >= 2) {				readsw(fifo, pDest, wCount >> 1);				index = wCount & ~0x01;			}		}		if (wCount & 0x01)			pDest[index] = musb_readb(fifo, 0);	} else  {		/* byte aligned */		readsb(fifo, pDest, wCount);	}}/*-------------------------------------------------------------------------*/void musb_load_testpacket(struct musb *musb){	void __iomem	*regs = musb->aLocalEnd[0].regs;	MGC_SelectEnd(musb->pRegs, 0);	musb_write_fifo(musb->control_ep,			sizeof(musb_test_packet), musb_test_packet);	musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_TXPKTRDY);}/*-------------------------------------------------------------------------*///#ifdef	CONFIG_USB_MUSB_OTG/* * See also USB_OTG_1-3.pdf 6.6.5 Timers * REVISIT: Are the other timers done in the hardware? */#define TB_ASE0_BRST		100	/* Min 3.125 ms *//* * Handles OTG hnp timeouts, such as b_ase0_brst */void musb_otg_timer_func(unsigned long data){	struct musb	*musb = (struct musb *)data;	unsigned long	flags;	u8	power;	u8	devctl;	devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);	spin_lock_irqsave(&musb->Lock, flags);	switch (musb->xceiv.state) {	case OTG_STATE_B_PERIPHERAL:		/* Clear RESUME if it is set for REMOTE-WAKEUP/SRP*/		if( MGC_M_POWER_RESUME & (power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER)) ){			musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power & ~MGC_M_POWER_RESUME);		}                break;	case OTG_STATE_B_WAIT_ACON:		DBG(1, "HNP: B_WAIT_ACON timeout, going back to B_PERIPHERAL\n");		musb_g_disconnect(musb);		musb->xceiv.state = OTG_STATE_B_PERIPHERAL;		musb->is_active = 0;		break;	case OTG_STATE_A_WAIT_BCON:		del_timer(&musb_otg_timer);		INFO("No response from B-device\n");		devctl &= ~MGC_M_DEVCTL_SESSION;		musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);		devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);		if (devctl & MGC_M_DEVCTL_BDEVICE) {			musb->xceiv.state = OTG_STATE_B_IDLE;			MUSB_DEV_MODE(musb);		}		else {			musb->xceiv.state = OTG_STATE_A_IDLE;			MUSB_HST_MODE(musb);		}                break;	case OTG_STATE_A_SUSPEND:		/* finish RESUME signaling? */		if (musb->port1_status & MUSB_PORT_STAT_RESUME){			power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);			power &= ~MGC_M_POWER_RESUME;			DBG(1, "root port resume stopped, power %02x\n",									power);			musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);			musb->is_active = 1;			musb->port1_status &= ~(USB_PORT_STAT_SUSPEND						| MUSB_PORT_STAT_RESUME);			musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;			usb_hcd_poll_rh_status(musb_to_hcd(musb));			/* NOTE: it might really be A_WAIT_BCON ... */			musb->xceiv.state = OTG_STATE_A_HOST;		}                break;	case OTG_STATE_A_WAIT_VFALL:		break;	case OTG_STATE_B_IDLE:		break;	default:		break;	}	spin_unlock_irqrestore(&musb->Lock, flags);	return;}DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0);/* * Stops the B-device HNP state. Caller must take care of locking. */void musb_hnp_stop(struct musb *musb){	struct usb_hcd	*hcd = musb_to_hcd(musb);	void __iomem	*pBase = musb->pRegs;	u8	reg;	switch (musb->xceiv.state) {	case OTG_STATE_A_PERIPHERAL:	case OTG_STATE_A_WAIT_VFALL:		DBG(1, "HNP: Switching back to A-host\n");		musb_g_disconnect(musb);		musb_root_disconnect(musb);		musb->xceiv.state = OTG_STATE_A_IDLE;		musb->is_active = 0;		break;	case OTG_STATE_B_HOST:		DBG(1, "HNP: Disabling HR\n");		musb_root_disconnect(musb);		hcd->self.is_b_host = 0;		musb->xceiv.state = OTG_STATE_B_PERIPHERAL;		reg = musb_readb(pBase, MGC_O_HDRC_POWER);		reg |= MGC_M_POWER_SUSPENDM;		musb_writeb(pBase, MGC_O_HDRC_POWER, reg);		/* REVISIT: Start SESSION_REQUEST here? */		break;	default:		DBG(1, "HNP: Stopping in unknown state %s\n",			otg_state_string(musb));	}}//#endif/* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of * paramount importance, it seems OK to check them individually; * the order of the tests is specified in the manual * * @param pThis instance pointer * @param bIntrUSB register contents * @param devctl * @param power */#define STAGE0_MASK (MGC_M_INTR_RESUME | MGC_M_INTR_SESSREQ \		| MGC_M_INTR_VBUSERROR | MGC_M_INTR_CONNECT \		| MGC_M_INTR_RESET )static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,				u8 devctl, u8 power){	irqreturn_t handled = IRQ_NONE;//#ifdef CONFIG_USB_MUSB_HDRC_HCD	void __iomem *pBase = pThis->pRegs;//#endif	DBG(3, "<== Power=%02x, DevCtl=%02x, bIntrUSB=0x%x\n", power, devctl,		bIntrUSB);	/* in host mode, the peripheral may issue remote wakeup.	 * in peripheral mode, the host may resume the link.	 * spurious RESUME irqs happen too, paired with SUSPEND.	 */	if (bIntrUSB & MGC_M_INTR_RESUME) {		handled = IRQ_HANDLED;		DBG(3, "RESUME (%s)\n", otg_state_string(pThis));		if (devctl & MGC_M_DEVCTL_HM) {			switch (pThis->xceiv.state) {			case OTG_STATE_A_SUSPEND:				/* remote wakeup?  later, GetPortStatus				 * will stop RESUME signaling				 */				if (power & MGC_M_POWER_RESUME) {					power &= ~MGC_M_POWER_SUSPENDM;					musb_writeb(pBase, MGC_O_HDRC_POWER,						power | MGC_M_POWER_RESUME);					pThis->port1_status |=						(USB_PORT_STAT_C_SUSPEND << 16)						| MUSB_PORT_STAT_RESUME;					/* Start RESUME for 50ms for OPT to pass				 	 */					musb_otg_timer.data = (unsigned long)pThis;					mod_timer( &musb_otg_timer, jiffies +						 msecs_to_jiffies(50));				} else if (power & MGC_M_POWER_SUSPENDM) {					/* spurious */					pThis->int_usb &= ~MGC_M_INTR_SUSPEND;				}				break;			case OTG_STATE_B_WAIT_ACON:				pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;				pThis->is_active = 1;				MUSB_DEV_MODE(pThis);				break;			default:				WARN("bogus %s RESUME (%s)\n",					"host",					otg_state_string(pThis));			}		} else {			switch (pThis->xceiv.state) {			case OTG_STATE_A_SUSPEND:				/* possibly DISCONNECT is upcoming */				pThis->xceiv.state = OTG_STATE_A_HOST;				usb_hcd_resume_root_hub(musb_to_hcd(pThis));				break;			case OTG_STATE_B_WAIT_ACON:			case OTG_STATE_B_PERIPHERAL:				/* disconnect while suspended?  we may				 * not get a disconnect irq...				 */				if ((devctl & MGC_M_DEVCTL_VBUS)						!= (3 << MGC_S_DEVCTL_VBUS)) {					pThis->int_usb |= MGC_M_INTR_DISCONNECT;					pThis->int_usb &= ~MGC_M_INTR_SUSPEND;					break;				}				musb_g_resume(pThis);				break;			case OTG_STATE_B_IDLE:				pThis->int_usb &= ~MGC_M_INTR_SUSPEND;				break;			default:				WARN("bogus %s RESUME (%s)\n",					"peripheral",					otg_state_string(pThis));			}		}	}	/* see manual for the order of the tests */	if (bIntrUSB & MGC_M_INTR_SESSREQ) {		DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(pThis));		/* IRQ arrives from ID pin sense or (later, if VBUS power		 * is removed) SRP.  responses are time critical:		 *  - turn on VBUS (with silicon-specific mechanism)		 *  - go through A_WAIT_VRISE		 *  - ... to A_WAIT_BCON.		 * a_wait_vrise_tmout triggers VBUS_ERROR transitions		 */		musb_writeb(pBase, MGC_O_HDRC_DEVCTL, MGC_M_DEVCTL_SESSION);		pThis->bEnd0Stage = MGC_END0_START;		pThis->xceiv.state = OTG_STATE_A_IDLE;		MUSB_HST_MODE(pThis);		musb_set_vbus(pThis, 1);		handled = IRQ_HANDLED;	}	if (bIntrUSB & MGC_M_INTR_VBUSERROR) {		int	ignore = 0;		/* During connection as an A-Device, we may see a short		 * current spikes causing voltage drop, because of cable		 * and peripheral capacitance combined with vbus draw.		 * (So: less common with truly self-powered devices, where		 * vbus doesn't act like a power supply.)		 *		 * Such spikes are short; usually less than ~500 usec, max		 * of ~2 msec.  That is, they're not sustained overcurrent		 * errors, though they're reported using VBUSERROR irqs.		 *		 * Workarounds:  (a) hardware: use self powered devices.		 * (b) software:  ignore non-repeated VBUS errors.		 *		 * REVISIT:  do delays from lots of DEBUG_KERNEL checks		 * make trouble here, keeping VBUS < 4.4V ?		 */		switch (pThis->xceiv.state) {		case OTG_STATE_B_IDLE:				DBG(1, "OTG STATE: B_idle\n");		case OTG_STATE_A_IDLE:				DBG(1, "OTG STATE: A_idle\n");		case OTG_STATE_A_HOST:			/* recovery is dicey once we've gotten past the			 * initial stages of enumeration, but if VBUS			 * stayed ok at the other end of the link, and

⌨️ 快捷键说明

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