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

📄 ehci-pci.c

📁 linux 内核源代码
💻 C
字号:
/* * EHCI HCD (Host Controller Driver) PCI Bus Glue. * * Copyright (c) 2000-2004 by David Brownell * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */#ifndef CONFIG_PCI#error "This file is PCI bus glue.  CONFIG_PCI must be defined."#endif/*-------------------------------------------------------------------------*//* called after powerup, by probe or system-pm "wakeup" */static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev){	u32			temp;	int			retval;	/* optional debug port, normally in the first BAR */	temp = pci_find_capability(pdev, 0x0a);	if (temp) {		pci_read_config_dword(pdev, temp, &temp);		temp >>= 16;		if ((temp & (3 << 13)) == (1 << 13)) {			temp &= 0x1fff;			ehci->debug = ehci_to_hcd(ehci)->regs + temp;			temp = ehci_readl(ehci, &ehci->debug->control);			ehci_info(ehci, "debug port %d%s\n",				HCS_DEBUG_PORT(ehci->hcs_params),				(temp & DBGP_ENABLED)					? " IN USE"					: "");			if (!(temp & DBGP_ENABLED))				ehci->debug = NULL;		}	}	/* we expect static quirk code to handle the "extended capabilities"	 * (currently just BIOS handoff) allowed starting with EHCI 0.96	 */	/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */	retval = pci_set_mwi(pdev);	if (!retval)		ehci_dbg(ehci, "MWI active\n");	return 0;}/* called during probe() after chip reset completes */static int ehci_pci_setup(struct usb_hcd *hcd){	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);	u32			temp;	int			retval;	switch (pdev->vendor) {	case PCI_VENDOR_ID_TOSHIBA_2:		/* celleb's companion chip */		if (pdev->device == 0x01b5) {#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO			ehci->big_endian_mmio = 1;#else			ehci_warn(ehci,				  "unsupported big endian Toshiba quirk\n");#endif		}		break;	}	ehci->caps = hcd->regs;	ehci->regs = hcd->regs +		HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));	dbg_hcs_params(ehci, "reset");	dbg_hcc_params(ehci, "reset");        /* ehci_init() causes memory for DMA transfers to be         * allocated.  Thus, any vendor-specific workarounds based on         * limiting the type of memory used for DMA transfers must         * happen before ehci_init() is called. */	switch (pdev->vendor) {	case PCI_VENDOR_ID_NVIDIA:		/* NVidia reports that certain chips don't handle		 * QH, ITD, or SITD addresses above 2GB.  (But TD,		 * data buffer, and periodic schedule are normal.)		 */		switch (pdev->device) {		case 0x003c:	/* MCP04 */		case 0x005b:	/* CK804 */		case 0x00d8:	/* CK8 */		case 0x00e8:	/* CK8S */			if (pci_set_consistent_dma_mask(pdev,						DMA_31BIT_MASK) < 0)				ehci_warn(ehci, "can't enable NVidia "					"workaround for >2GB RAM\n");			break;		}		break;	}	/* cache this readonly data; minimize chip reads */	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);	retval = ehci_halt(ehci);	if (retval)		return retval;	/* data structure init */	retval = ehci_init(hcd);	if (retval)		return retval;	switch (pdev->vendor) {	case PCI_VENDOR_ID_TDI:		if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {			ehci->is_tdi_rh_tt = 1;			tdi_reset(ehci);		}		break;	case PCI_VENDOR_ID_AMD:		/* AMD8111 EHCI doesn't work, according to AMD errata */		if (pdev->device == 0x7463) {			ehci_info(ehci, "ignoring AMD8111 (errata)\n");			retval = -EIO;			goto done;		}		break;	case PCI_VENDOR_ID_NVIDIA:		switch (pdev->device) {		/* Some NForce2 chips have problems with selective suspend;		 * fixed in newer silicon.		 */		case 0x0068:			if (pdev->revision < 0xa4)				ehci->no_selective_suspend = 1;			break;		}		break;	}	ehci_reset(ehci);	/* at least the Genesys GL880S needs fixup here */	temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);	temp &= 0x0f;	if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) {		ehci_dbg(ehci, "bogus port configuration: "			"cc=%d x pcc=%d < ports=%d\n",			HCS_N_CC(ehci->hcs_params),			HCS_N_PCC(ehci->hcs_params),			HCS_N_PORTS(ehci->hcs_params));		switch (pdev->vendor) {		case 0x17a0:		/* GENESYS */			/* GL880S: should be PORTS=2 */			temp |= (ehci->hcs_params & ~0xf);			ehci->hcs_params = temp;			break;		case PCI_VENDOR_ID_NVIDIA:			/* NF4: should be PCC=10 */			break;		}	}	/* Serial Bus Release Number is at PCI 0x60 offset */	pci_read_config_byte(pdev, 0x60, &ehci->sbrn);	/* Workaround current PCI init glitch:  wakeup bits aren't	 * being set from PCI PM capability.	 */	if (!device_can_wakeup(&pdev->dev)) {		u16	port_wake;		pci_read_config_word(pdev, 0x62, &port_wake);		if (port_wake & 0x0001)			device_init_wakeup(&pdev->dev, 1);	}#ifdef	CONFIG_USB_SUSPEND	/* REVISIT: the controller works fine for wakeup iff the root hub	 * itself is "globally" suspended, but usbcore currently doesn't	 * understand such things.	 *	 * System suspend currently expects to be able to suspend the entire	 * device tree, device-at-a-time.  If we failed selective suspend	 * reports, system suspend would fail; so the root hub code must claim	 * success.  That's lying to usbcore, and it matters for for runtime	 * PM scenarios with selective suspend and remote wakeup...	 */	if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev))		ehci_warn(ehci, "selective suspend/wakeup unavailable\n");#endif	retval = ehci_pci_reinit(ehci, pdev);done:	return retval;}/*-------------------------------------------------------------------------*/#ifdef	CONFIG_PM/* suspend/resume, section 4.3 *//* These routines rely on the PCI bus glue * to handle powerdown and wakeup, and currently also on * transceivers that don't need any software attention to set up * the right sort of wakeup. * Also they depend on separate root hub suspend/resume. */static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message){	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);	unsigned long		flags;	int			rc = 0;	if (time_before(jiffies, ehci->next_statechange))		msleep(10);	/* Root hub was already suspended. Disable irq emission and	 * mark HW unaccessible, bail out if RH has been resumed. Use	 * the spinlock to properly synchronize with possible pending	 * RH suspend or resume activity.	 *	 * This is still racy as hcd->state is manipulated outside of	 * any locks =P But that will be a different fix.	 */	spin_lock_irqsave (&ehci->lock, flags);	if (hcd->state != HC_STATE_SUSPENDED) {		rc = -EINVAL;		goto bail;	}	ehci_writel(ehci, 0, &ehci->regs->intr_enable);	(void)ehci_readl(ehci, &ehci->regs->intr_enable);	/* make sure snapshot being resumed re-enumerates everything */	if (message.event == PM_EVENT_PRETHAW) {		ehci_halt(ehci);		ehci_reset(ehci);	}	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); bail:	spin_unlock_irqrestore (&ehci->lock, flags);	// could save FLADJ in case of Vaux power loss	// ... we'd only use it to handle clock skew	return rc;}static int ehci_pci_resume(struct usb_hcd *hcd){	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);	// maybe restore FLADJ	if (time_before(jiffies, ehci->next_statechange))		msleep(100);	/* Mark hardware accessible again as we are out of D3 state by now */	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);	/* If CF is still set, we maintained PCI Vaux power.	 * Just undo the effect of ehci_pci_suspend().	 */	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {		int	mask = INTR_MASK;		if (!device_may_wakeup(&hcd->self.root_hub->dev))			mask &= ~STS_PCD;		ehci_writel(ehci, mask, &ehci->regs->intr_enable);		ehci_readl(ehci, &ehci->regs->intr_enable);		return 0;	}	ehci_dbg(ehci, "lost power, restarting\n");	usb_root_hub_lost_power(hcd->self.root_hub);	/* Else reset, to cope with power loss or flush-to-storage	 * style "resume" having let BIOS kick in during reboot.	 */	(void) ehci_halt(ehci);	(void) ehci_reset(ehci);	(void) ehci_pci_reinit(ehci, pdev);	/* emptying the schedule aborts any urbs */	spin_lock_irq(&ehci->lock);	if (ehci->reclaim)		ehci->reclaim_ready = 1;	ehci_work(ehci);	spin_unlock_irq(&ehci->lock);	ehci_writel(ehci, ehci->command, &ehci->regs->command);	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */	/* here we "know" root ports should always stay powered */	ehci_port_power(ehci, 1);	ehci_handover_companion_ports(ehci);	hcd->state = HC_STATE_SUSPENDED;	return 0;}#endifstatic const struct hc_driver ehci_pci_hc_driver = {	.description =		hcd_name,	.product_desc =		"EHCI Host Controller",	.hcd_priv_size =	sizeof(struct ehci_hcd),	/*	 * generic hardware linkage	 */	.irq =			ehci_irq,	.flags =		HCD_MEMORY | HCD_USB2,	/*	 * basic lifecycle operations	 */	.reset =		ehci_pci_setup,	.start =		ehci_run,#ifdef	CONFIG_PM	.suspend =		ehci_pci_suspend,	.resume =		ehci_pci_resume,#endif	.stop =			ehci_stop,	.shutdown =		ehci_shutdown,	/*	 * managing i/o requests and associated device resources	 */	.urb_enqueue =		ehci_urb_enqueue,	.urb_dequeue =		ehci_urb_dequeue,	.endpoint_disable =	ehci_endpoint_disable,	/*	 * scheduling support	 */	.get_frame_number =	ehci_get_frame,	/*	 * root hub support	 */	.hub_status_data =	ehci_hub_status_data,	.hub_control =		ehci_hub_control,	.bus_suspend =		ehci_bus_suspend,	.bus_resume =		ehci_bus_resume,};/*-------------------------------------------------------------------------*//* PCI driver selection metadata; PCI hotplugging uses this */static const struct pci_device_id pci_ids [] = { {	/* handle any USB 2.0 EHCI controller */	PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),	.driver_data =	(unsigned long) &ehci_pci_hc_driver,	},	{ /* end: all zeroes */ }};MODULE_DEVICE_TABLE(pci, pci_ids);/* pci driver glue; this is a "new style" PCI driver module */static struct pci_driver ehci_pci_driver = {	.name =		(char *) hcd_name,	.id_table =	pci_ids,	.probe =	usb_hcd_pci_probe,	.remove =	usb_hcd_pci_remove,#ifdef	CONFIG_PM	.suspend =	usb_hcd_pci_suspend,	.resume =	usb_hcd_pci_resume,#endif	.shutdown = 	usb_hcd_pci_shutdown,};

⌨️ 快捷键说明

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