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

📄 ohci-hcd.c

📁 ohci-hcd linux代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	case OHCI_USB_OPER:		temp = 0;		break;	case OHCI_USB_SUSPEND:	case OHCI_USB_RESUME:		ohci->hc_control &= OHCI_CTRL_RWC;		ohci->hc_control |= OHCI_USB_RESUME;		temp = 10 /* msec wait */;		break;	// case OHCI_USB_RESET:	default:		ohci->hc_control &= OHCI_CTRL_RWC;		ohci->hc_control |= OHCI_USB_RESET;		temp = 50 /* msec wait */;		break;	}	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);	// flush the writes	(void) ohci_readl (ohci, &ohci->regs->control);	msleep(temp);	memset (ohci->hcca, 0, sizeof (struct ohci_hcca));	/* 2msec timelimit here means no irqs/preempt */	spin_lock_irq (&ohci->lock);retry:	/* HC Reset requires max 10 us delay */	ohci_writel (ohci, OHCI_HCR,  &ohci->regs->cmdstatus);	temp = 30;	/* ... allow extra time */	while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {		if (--temp == 0) {			spin_unlock_irq (&ohci->lock);			ohci_err (ohci, "USB HC reset timed out!\n");			return -1;		}		udelay (1);	}	/* now we're in the SUSPEND state ... must go OPERATIONAL	 * within 2msec else HC enters RESUME	 *	 * ... but some hardware won't init fmInterval "by the book"	 * (SiS, OPTi ...), so reset again instead.  SiS doesn't need	 * this if we write fmInterval after we're OPERATIONAL.	 * Unclear about ALi, ServerWorks, and others ... this could	 * easily be a longstanding bug in chip init on Linux.	 */	if (ohci->flags & OHCI_QUIRK_INITRESET) {		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);		// flush those writes		(void) ohci_readl (ohci, &ohci->regs->control);	}	/* Tell the controller where the control and bulk lists are	 * The lists are empty now. */	ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);	ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);	/* a reset clears this */	ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);	periodic_reinit (ohci);	/* some OHCI implementations are finicky about how they init.	 * bogus values here mean not even enumeration could work.	 */	if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0			|| !ohci_readl (ohci, &ohci->regs->periodicstart)) {		if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {			ohci->flags |= OHCI_QUIRK_INITRESET;			ohci_dbg (ohci, "enabling initreset quirk\n");			goto retry;		}		spin_unlock_irq (&ohci->lock);		ohci_err (ohci, "init err (%08x %04x)\n",			ohci_readl (ohci, &ohci->regs->fminterval),			ohci_readl (ohci, &ohci->regs->periodicstart));		return -EOVERFLOW;	}	/* use rhsc irqs after khubd is fully initialized */	hcd->poll_rh = 1;	hcd->uses_new_polling = 1;	/* start controller operations */	ohci->hc_control &= OHCI_CTRL_RWC;	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);	hcd->state = HC_STATE_RUNNING;	/* wake on ConnectStatusChange, matching external hubs */	ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);	/* Choose the interrupts we care about now, others later on demand */	mask = OHCI_INTR_INIT;	ohci_writel (ohci, ~0, &ohci->regs->intrstatus);	ohci_writel (ohci, mask, &ohci->regs->intrenable);	/* handle root hub init quirks ... */	temp = roothub_a (ohci);	temp &= ~(RH_A_PSM | RH_A_OCPM);	if (ohci->flags & OHCI_QUIRK_SUPERIO) {		/* NSC 87560 and maybe others */		temp |= RH_A_NOCP;		temp &= ~(RH_A_POTPGT | RH_A_NPS);		ohci_writel (ohci, temp, &ohci->regs->roothub.a);	} else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {		/* hub power always on; required for AMD-756 and some		 * Mac platforms.  ganged overcurrent reporting, if any.		 */		temp |= RH_A_NPS;		ohci_writel (ohci, temp, &ohci->regs->roothub.a);	}	ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);	ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,						&ohci->regs->roothub.b);	// flush those writes	(void) ohci_readl (ohci, &ohci->regs->control);	ohci->next_statechange = jiffies + STATECHANGE_DELAY;	spin_unlock_irq (&ohci->lock);	// POTPGT delay is bits 24-31, in 2 ms units.	mdelay ((temp >> 23) & 0x1fe);	hcd->state = HC_STATE_RUNNING;	if (quirk_zfmicro(ohci)) {		/* Create timer to watch for bad queue state on ZF Micro */		setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,				(unsigned long) ohci);		ohci->eds_scheduled = 0;		ohci->ed_to_check = NULL;	}	ohci_dump (ohci, 1);	return 0;}/*-------------------------------------------------------------------------*//* an interrupt happens */static irqreturn_t ohci_irq (struct usb_hcd *hcd){	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);	struct ohci_regs __iomem *regs = ohci->regs;	int			ints;	/* Read interrupt status (and flush pending writes).  We ignore the	 * optimization of checking the LSB of hcca->done_head; it doesn't	 * work on all systems (edge triggering for OHCI can be a factor).	 */	ints = ohci_readl(ohci, &regs->intrstatus);	/* Check for an all 1's result which is a typical consequence	 * of dead, unclocked, or unplugged (CardBus...) devices	 */	if (ints == ~(u32)0) {		disable (ohci);		ohci_dbg (ohci, "device removed!\n");		return IRQ_HANDLED;	}	/* We only care about interrupts that are enabled */	ints &= ohci_readl(ohci, &regs->intrenable);	/* interrupt for some other device? */	if (ints == 0)		return IRQ_NOTMINE;	if (ints & OHCI_INTR_UE) {		// e.g. due to PCI Master/Target Abort		if (quirk_nec(ohci)) {			/* Workaround for a silicon bug in some NEC chips used			 * in Apple's PowerBooks. Adapted from Darwin code.			 */			ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");			ohci_writel (ohci, OHCI_INTR_UE, &regs->intrdisable);			schedule_work (&ohci->nec_work);		} else {			disable (ohci);			ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");		}		ohci_dump (ohci, 1);		ohci_usb_reset (ohci);	}	if (ints & OHCI_INTR_RHSC) {		ohci_vdbg(ohci, "rhsc\n");		ohci->next_statechange = jiffies + STATECHANGE_DELAY;		ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,				&regs->intrstatus);		/* NOTE: Vendors didn't always make the same implementation		 * choices for RHSC.  Many followed the spec; RHSC triggers		 * on an edge, like setting and maybe clearing a port status		 * change bit.  With others it's level-triggered, active		 * until khubd clears all the port status change bits.  We'll		 * always disable it here and rely on polling until khubd		 * re-enables it.		 */		ohci_writel(ohci, OHCI_INTR_RHSC, &regs->intrdisable);		usb_hcd_poll_rh_status(hcd);	}	/* For connect and disconnect events, we expect the controller	 * to turn on RHSC along with RD.  But for remote wakeup events	 * this might not happen.	 */	else if (ints & OHCI_INTR_RD) {		ohci_vdbg(ohci, "resume detect\n");		ohci_writel(ohci, OHCI_INTR_RD, &regs->intrstatus);		hcd->poll_rh = 1;		if (ohci->autostop) {			spin_lock (&ohci->lock);			ohci_rh_resume (ohci);			spin_unlock (&ohci->lock);		} else			usb_hcd_resume_root_hub(hcd);	}	if (ints & OHCI_INTR_WDH) {		if (HC_IS_RUNNING(hcd->state))			ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);		spin_lock (&ohci->lock);		dl_done_list (ohci);		spin_unlock (&ohci->lock);		if (HC_IS_RUNNING(hcd->state))			ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrenable);	}	if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {		spin_lock(&ohci->lock);		if (ohci->ed_to_check) {			struct ed *ed = ohci->ed_to_check;			if (check_ed(ohci, ed)) {				/* HC thinks the TD list is empty; HCD knows				 * at least one TD is outstanding				 */				if (--ohci->zf_delay == 0) {					struct td *td = list_entry(						ed->td_list.next,						struct td, td_list);					ohci_warn(ohci,						  "Reclaiming orphan TD %p\n",						  td);					takeback_td(ohci, td);					ohci->ed_to_check = NULL;				}			} else				ohci->ed_to_check = NULL;		}		spin_unlock(&ohci->lock);	}	/* could track INTR_SO to reduce available PCI/... bandwidth */	/* handle any pending URB/ED unlinks, leaving INTR_SF enabled	 * when there's still unlinking to be done (next frame).	 */	spin_lock (&ohci->lock);	if (ohci->ed_rm_list)		finish_unlinks (ohci, ohci_frame_no(ohci));	if ((ints & OHCI_INTR_SF) != 0			&& !ohci->ed_rm_list			&& !ohci->ed_to_check			&& HC_IS_RUNNING(hcd->state))		ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);	spin_unlock (&ohci->lock);	if (HC_IS_RUNNING(hcd->state)) {		ohci_writel (ohci, ints, &regs->intrstatus);		ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);		// flush those writes		(void) ohci_readl (ohci, &ohci->regs->control);	}	return IRQ_HANDLED;}/*-------------------------------------------------------------------------*/static void ohci_stop (struct usb_hcd *hcd){	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);	ohci_dump (ohci, 1);	flush_scheduled_work();	ohci_usb_reset (ohci);	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);	free_irq(hcd->irq, hcd);	hcd->irq = -1;	if (quirk_zfmicro(ohci))		del_timer(&ohci->unlink_watchdog);	remove_debug_files (ohci);	ohci_mem_cleanup (ohci);	if (ohci->hcca) {		dma_free_coherent (hcd->self.controller,				sizeof *ohci->hcca,				ohci->hcca, ohci->hcca_dma);		ohci->hcca = NULL;		ohci->hcca_dma = 0;	}}/*-------------------------------------------------------------------------*/#if defined(CONFIG_PM) || defined(CONFIG_PCI)/* must not be called from interrupt context */static int ohci_restart (struct ohci_hcd *ohci){	int temp;	int i;	struct urb_priv *priv;	spin_lock_irq(&ohci->lock);	disable (ohci);	/* Recycle any "live" eds/tds (and urbs). */	if (!list_empty (&ohci->pending))		ohci_dbg(ohci, "abort schedule...\n");	list_for_each_entry (priv, &ohci->pending, pending) {		struct urb	*urb = priv->td[0]->urb;		struct ed	*ed = priv->ed;		switch (ed->state) {		case ED_OPER:			ed->state = ED_UNLINK;			ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);			ed_deschedule (ohci, ed);			ed->ed_next = ohci->ed_rm_list;			ed->ed_prev = NULL;			ohci->ed_rm_list = ed;			/* FALLTHROUGH */		case ED_UNLINK:			break;		default:			ohci_dbg(ohci, "bogus ed %p state %d\n",					ed, ed->state);		}		if (!urb->unlinked)			urb->unlinked = -ESHUTDOWN;	}	finish_unlinks (ohci, 0);	spin_unlock_irq(&ohci->lock);	/* paranoia, in case that didn't work: */	/* empty the interrupt branches */	for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;	/* no EDs to remove */	ohci->ed_rm_list = NULL;	/* empty control and bulk lists */	ohci->ed_controltail = NULL;	ohci->ed_bulktail    = NULL;	if ((temp = ohci_run (ohci)) < 0) {		ohci_err (ohci, "can't restart, %d\n", temp);		return temp;	}	ohci_dbg(ohci, "restart complete\n");	return 0;}#endif/*-------------------------------------------------------------------------*/#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESCMODULE_AUTHOR (DRIVER_AUTHOR);MODULE_DESCRIPTION (DRIVER_INFO);MODULE_LICENSE ("GPL");#ifdef CONFIG_PCI#include "ohci-pci.c"#define PCI_DRIVER		ohci_pci_driver#endif#ifdef CONFIG_JZSOC#include "ohci-jz.c"#define PLATFORM_DRIVER		ohci_hcd_jz_driver#endif#ifdef CONFIG_SA1111#include "ohci-sa1111.c"#define SA1111_DRIVER		ohci_hcd_sa1111_driver#endif#ifdef CONFIG_ARCH_S3C2410#include "ohci-s3c2410.c"#define PLATFORM_DRIVER		ohci_hcd_s3c2410_driver#endif#ifdef CONFIG_ARCH_OMAP#include "ohci-omap.c"#define PLATFORM_DRIVER		ohci_hcd_omap_driver#endif#ifdef CONFIG_ARCH_LH7A404#include "ohci-lh7a404.c"#define PLATFORM_DRIVER		ohci_hcd_lh7a404_driver#endif#ifdef CONFIG_PXA27x#include "ohci-pxa27x.c"#define PLATFORM_DRIVER		ohci_hcd_pxa27x_driver#endif#ifdef CONFIG_ARCH_EP93XX#include "ohci-ep93xx.c"#define PLATFORM_DRIVER		ohci_hcd_ep93xx_driver#endif#ifdef CONFIG_SOC_AU1X00#include "ohci-au1xxx.c"#define PLATFORM_DRIVER		ohci_hcd_au1xxx_driver#endif#ifdef CONFIG_PNX8550#include "ohci-pnx8550.c"#define PLATFORM_DRIVER		ohci_hcd_pnx8550_driver#endif#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC#include "ohci-ppc-soc.c"#define PLATFORM_DRIVER		ohci_hcd_ppc_soc_driver#endif#ifdef CONFIG_ARCH_AT91#include "ohci-at91.c"#define PLATFORM_DRIVER		ohci_hcd_at91_driver#endif#ifdef CONFIG_ARCH_PNX4008#include "ohci-pnx4008.c"#define PLATFORM_DRIVER		usb_hcd_pnx4008_driver#endif#ifdef CONFIG_USB_OHCI_HCD_PPC_OF#include "ohci-ppc-of.c"#define OF_PLATFORM_DRIVER	ohci_hcd_ppc_of_driver#endif#ifdef CONFIG_PPC_PS3#include "ohci-ps3.c"#define PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver#endif#ifdef CONFIG_USB_OHCI_HCD_SSB#include "ohci-ssb.c"#define SSB_OHCI_DRIVER		ssb_ohci_driver#endif#if	!defined(PCI_DRIVER) &&		\	!defined(PLATFORM_DRIVER) &&	\	!defined(OF_PLATFORM_DRIVER) &&	\	!defined(SA1111_DRIVER) &&	\	!defined(PS3_SYSTEM_BUS_DRIVER) && \	!defined(SSB_OHCI_DRIVER)#error "missing bus glue for ohci-hcd"#endifstatic int __init ohci_hcd_mod_init(void){	int retval = 0;	if (usb_disabled())		return -ENODEV;	printk (KERN_DEBUG "%s: " DRIVER_INFO "\n", hcd_name);	pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,		sizeof (struct ed), sizeof (struct td));#ifdef PS3_SYSTEM_BUS_DRIVER	retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);	if (retval < 0)		goto error_ps3;#endif#ifdef PLATFORM_DRIVER	retval = platform_driver_register(&PLATFORM_DRIVER);	if (retval < 0)		goto error_platform;#endif#ifdef OF_PLATFORM_DRIVER	retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);	if (retval < 0)		goto error_of_platform;#endif#ifdef SA1111_DRIVER	retval = sa1111_driver_register(&SA1111_DRIVER);	if (retval < 0)		goto error_sa1111;#endif#ifdef PCI_DRIVER	retval = pci_register_driver(&PCI_DRIVER);	if (retval < 0)		goto error_pci;#endif#ifdef SSB_OHCI_DRIVER	retval = ssb_driver_register(&SSB_OHCI_DRIVER);	if (retval)		goto error_ssb;#endif	return retval;	/* Error path */#ifdef SSB_OHCI_DRIVER error_ssb:#endif#ifdef PCI_DRIVER	pci_unregister_driver(&PCI_DRIVER); error_pci:#endif#ifdef SA1111_DRIVER	sa1111_driver_unregister(&SA1111_DRIVER); error_sa1111:#endif#ifdef OF_PLATFORM_DRIVER	of_unregister_platform_driver(&OF_PLATFORM_DRIVER); error_of_platform:#endif#ifdef PLATFORM_DRIVER	platform_driver_unregister(&PLATFORM_DRIVER); error_platform:#endif#ifdef PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3:#endif	return retval;}module_init(ohci_hcd_mod_init);static void __exit ohci_hcd_mod_exit(void){#ifdef SSB_OHCI_DRIVER	ssb_driver_unregister(&SSB_OHCI_DRIVER);#endif#ifdef PCI_DRIVER	pci_unregister_driver(&PCI_DRIVER);#endif#ifdef SA1111_DRIVER	sa1111_driver_unregister(&SA1111_DRIVER);#endif#ifdef OF_PLATFORM_DRIVER	of_unregister_platform_driver(&OF_PLATFORM_DRIVER);#endif#ifdef PLATFORM_DRIVER	platform_driver_unregister(&PLATFORM_DRIVER);#endif#ifdef PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);#endif}module_exit(ohci_hcd_mod_exit);

⌨️ 快捷键说明

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