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 + -
显示快捷键?