OpenCloudOS-Kernel/drivers/usb/host/ehci-pci.c

555 lines
16 KiB
C
Raw Normal View History

/*
* 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
/* defined here to avoid adding to pci_ids.h for single instance use */
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
/*-------------------------------------------------------------------------*/
/* called after powerup, by probe or system-pm "wakeup" */
static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
{
int retval;
/* 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);
struct pci_dev *p_smbus;
u8 rev;
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, 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_BIT_MASK(31)) < 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;
if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) ||
(pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) {
/* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
* read/write memory space which does not belong to it when
* there is NULL pointer with T-bit set to 1 in the frame list
* table. To avoid the issue, the frame list link pointer
* should always contain a valid pointer to a inactive qh.
*/
ehci->use_dummy_qh = 1;
ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI "
"dummy qh workaround\n");
}
/* data structure init */
retval = ehci_init(hcd);
if (retval)
return retval;
switch (pdev->vendor) {
case PCI_VENDOR_ID_NEC:
ehci->need_io_watchdog = 0;
break;
case PCI_VENDOR_ID_INTEL:
ehci->need_io_watchdog = 0;
ehci->fs_i_thresh = 1;
if (pdev->device == 0x27cc) {
ehci->broken_periodic = 1;
ehci_info(ehci, "using broken periodic workaround\n");
}
if (pdev->device == 0x0806 || pdev->device == 0x0811
|| pdev->device == 0x0829) {
ehci_info(ehci, "disable lpm for langwell/penwell\n");
ehci->has_lpm = 0;
}
if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) {
hcd->has_tt = 1;
tdi_reset(ehci);
}
break;
case PCI_VENDOR_ID_TDI:
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
hcd->has_tt = 1;
tdi_reset(ehci);
}
break;
case PCI_VENDOR_ID_AMD:
USB host: Move AMD PLL quirk to pci-quirks.c This patch moves the AMD PLL quirk code in OHCI/EHCI driver to pci-quirks.c, and exports the functions to be used by xHCI driver later. AMD PLL quirk disable the optional PM feature inside specific SB700/SB800/Hudson-2/3 platforms under the following conditions: 1. If an isochronous device is connected to OHCI/EHCI/xHCI port and is active; 2. Optional PM feature that powers down the internal Bus PLL when the link is in low power state is enabled. Without AMD PLL quirk, USB isochronous stream may stutter or have breaks occasionally, which greatly impair the performance of audio/video streams. Currently AMD PLL quirk is implemented in OHCI and EHCI driver, and will be added to xHCI driver too. They are doing similar things actually, so move the quirk code to pci-quirks.c, which has several advantages: 1. Remove duplicate defines and functions in OHCI/EHCI (and xHCI) driver and make them cleaner; 2. AMD chipset information will be probed only once and then stored. Currently they're probed during every OHCI/EHCI initialization, move the detect code to pci-quirks.c saves the repeat detect cost; 3. Build up synchronization among OHCI/EHCI/xHCI driver. In current code, every host controller enable/disable PLL only according to its own status, and may enable PLL while there is still isoc transfer on other HCs. Move the quirk to pci-quirks.c prevents this issue. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Alex He <alex.he@amd.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-03-01 14:57:05 +08:00
/* AMD PLL quirk */
if (usb_amd_find_chipset_info())
ehci->amd_pll_fix = 1;
/* 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;
/* MCP89 chips on the MacBookAir3,1 give EPROTO when
* fetching device descriptors unless LPM is disabled.
* There are also intermittent problems enumerating
* devices with PPCD enabled.
*/
case 0x0d9d:
ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89");
ehci->has_lpm = 0;
ehci->has_ppcd = 0;
ehci->command &= ~CMD_PPCEE;
break;
}
break;
case PCI_VENDOR_ID_VIA:
if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x60) {
u8 tmp;
/* The VT6212 defaults to a 1 usec EHCI sleep time which
* hogs the PCI bus *badly*. Setting bit 5 of 0x4B makes
* that sleep time use the conventional 10 usec.
*/
pci_read_config_byte(pdev, 0x4b, &tmp);
if (tmp & 0x20)
break;
pci_write_config_byte(pdev, 0x4b, tmp | 0x20);
}
break;
case PCI_VENDOR_ID_ATI:
USB host: Move AMD PLL quirk to pci-quirks.c This patch moves the AMD PLL quirk code in OHCI/EHCI driver to pci-quirks.c, and exports the functions to be used by xHCI driver later. AMD PLL quirk disable the optional PM feature inside specific SB700/SB800/Hudson-2/3 platforms under the following conditions: 1. If an isochronous device is connected to OHCI/EHCI/xHCI port and is active; 2. Optional PM feature that powers down the internal Bus PLL when the link is in low power state is enabled. Without AMD PLL quirk, USB isochronous stream may stutter or have breaks occasionally, which greatly impair the performance of audio/video streams. Currently AMD PLL quirk is implemented in OHCI and EHCI driver, and will be added to xHCI driver too. They are doing similar things actually, so move the quirk code to pci-quirks.c, which has several advantages: 1. Remove duplicate defines and functions in OHCI/EHCI (and xHCI) driver and make them cleaner; 2. AMD chipset information will be probed only once and then stored. Currently they're probed during every OHCI/EHCI initialization, move the detect code to pci-quirks.c saves the repeat detect cost; 3. Build up synchronization among OHCI/EHCI/xHCI driver. In current code, every host controller enable/disable PLL only according to its own status, and may enable PLL while there is still isoc transfer on other HCs. Move the quirk to pci-quirks.c prevents this issue. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Alex He <alex.he@amd.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-03-01 14:57:05 +08:00
/* AMD PLL quirk */
if (usb_amd_find_chipset_info())
ehci->amd_pll_fix = 1;
/* SB600 and old version of SB700 have a bug in EHCI controller,
* which causes usb devices lose response in some cases.
*/
if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) {
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
NULL);
if (!p_smbus)
break;
rev = p_smbus->revision;
if ((pdev->device == 0x4386) || (rev == 0x3a)
|| (rev == 0x3b)) {
u8 tmp;
ehci_info(ehci, "applying AMD SB600/SB700 USB "
"freeze workaround\n");
pci_read_config_byte(pdev, 0x53, &tmp);
pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
}
pci_dev_put(p_smbus);
}
break;
case PCI_VENDOR_ID_NETMOS:
/* MosChip frame-index-register bug */
ehci_info(ehci, "applying MosChip frame-index workaround\n");
ehci->frame_index_bug = 1;
break;
}
/* 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;
}
}
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);
if (pdev->vendor == PCI_VENDOR_ID_STMICRO
&& pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST)
ehci->sbrn = 0x20; /* ConneXT has no sbrn register */
/* Keep this around for a while just in case some EHCI
* implementation uses legacy PCI PM support. This test
* can be removed on 17 Dec 2009 if the dev_warn() hasn't
* been triggered by then.
*/
if (!device_can_wakeup(&pdev->dev)) {
u16 port_wake;
pci_read_config_word(pdev, 0x62, &port_wake);
if (port_wake & 0x0001) {
dev_warn(&pdev->dev, "Enabling legacy PCI PM\n");
device_set_wakeup_capable(&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 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
ehci_port_power(ehci, 1);
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, bool do_wakeup)
{
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. The PM and USB cores make sure that
* the root hub is either suspended or stopped.
*/
ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
USB: EHCI: fix scheduling while atomic during suspend There is a msleep with spin lock held during ehci pci suspend, which will cause kernel BUG: scheduling while atomic. Fix that. [ 184.139620] BUG: scheduling while atomic: kworker/u:11/416/0x00000002 [ 184.139632] 4 locks held by kworker/u:11/416: [ 184.139640] #0: (events_unbound){+.+.+.}, at: [<c104ddd4>] process_one_work+0x1b3/0x4cb [ 184.139669] #1: ((&entry->work)){+.+.+.}, at: [<c104ddd4>] process_one_work+0x1b3/0x4cb [ 184.139686] #2: (&__lockdep_no_validate__){+.+.+.}, at: [<c127cde3>] __device_suspend+0x2c/0x154 [ 184.139706] #3: (&(&ehci->lock)->rlock){-.-...}, at: [<c132f3d8>] ehci_pci_suspend+0x35/0x7b [ 184.139725] Modules linked in: serio_raw pegasus joydev mrst_gfx(C) battery [ 184.139748] irq event stamp: 52 [ 184.139753] hardirqs last enabled at (51): [<c14fdaac>] mutex_lock_nested+0x258/0x293 [ 184.139766] hardirqs last disabled at (52): [<c14fe7b4>] _raw_spin_lock_irqsave+0xf/0x3e [ 184.139777] softirqs last enabled at (0): [<c10371c1>] copy_process+0x3d2/0x109d [ 184.139789] softirqs last disabled at (0): [< (null)>] (null) [ 184.139802] Pid: 416, comm: kworker/u:11 Tainted: G C 2.6.37-6.3-adaptation-oaktrail #37 [ 184.139809] Call Trace: [ 184.139820] [<c102eeff>] __schedule_bug+0x5e/0x65 [ 184.139829] [<c14fbca5>] schedule+0xac/0xc4c [ 184.139840] [<c11d4845>] ? string+0x37/0x8b [ 184.139853] [<c1044f21>] ? lock_timer_base+0x1f/0x3e [ 184.139863] [<c14fe7da>] ? _raw_spin_lock_irqsave+0x35/0x3e [ 184.139876] [<c1061590>] ? trace_hardirqs_off+0xb/0xd [ 184.139885] [<c14fccdc>] schedule_timeout+0x283/0x2d9 [ 184.139896] [<c104516f>] ? process_timeout+0x0/0xa [ 184.139906] [<c14fcd47>] schedule_timeout_uninterruptible+0x15/0x17 [ 184.139916] [<c104566a>] msleep+0x10/0x16 [ 184.139926] [<c132f316>] ehci_adjust_port_wakeup_flags+0x69/0xf6 [ 184.139937] [<c132f3eb>] ehci_pci_suspend+0x48/0x7b [ 184.139946] [<c1326587>] suspend_common+0x52/0xbb [ 184.139956] [<c1326625>] hcd_pci_suspend+0x26/0x28 [ 184.139967] [<c11e7182>] pci_pm_suspend+0x5f/0xd0 [ 184.139976] [<c127ca3a>] pm_op+0x5d/0xf0 [ 184.139986] [<c127ceac>] __device_suspend+0xf5/0x154 [ 184.139996] [<c127d2c8>] async_suspend+0x16/0x3a [ 184.140006] [<c1058f54>] async_run_entry_fn+0x89/0x111 [ 184.140016] [<c104deb6>] process_one_work+0x295/0x4cb [ 184.140026] [<c1058ecb>] ? async_run_entry_fn+0x0/0x111 [ 184.140036] [<c104e3d0>] worker_thread+0x17f/0x298 [ 184.140045] [<c104e251>] ? worker_thread+0x0/0x298 [ 184.140055] [<c105277f>] kthread+0x64/0x69 [ 184.140064] [<c105271b>] ? kthread+0x0/0x69 [ 184.140075] [<c1002efa>] kernel_thread_helper+0x6/0x1a Signed-off-by: Yin Kangkai <kangkai.yin@intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> CC: David Brownell <dbrownell@users.sourceforge.net> CC: stable@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-01-28 12:04:35 +08:00
spin_lock_irqsave (&ehci->lock, flags);
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
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;
}
Intel xhci: Support EHCI/xHCI port switching. The Intel Panther Point chipsets contain an EHCI and xHCI host controller that shares some number of skew-dependent ports. These ports can be switched from the EHCI to the xHCI host (and vice versa) by a hardware MUX that is controlled by registers in the xHCI PCI configuration space. The USB 3.0 SuperSpeed terminations on the xHCI ports can be controlled separately from the USB 2.0 data wires. This switchover mechanism is there to support users who do a custom install of certain non-Linux operating systems that don't have official USB 3.0 support. By default, the ports are under EHCI, SuperSpeed terminations are off, and USB 3.0 devices will show up under the EHCI controller at reduced speeds. (This was more palatable for the marketing folks than having completely dead USB 3.0 ports if no xHCI drivers are available.) Users should be able to turn on xHCI by default through a BIOS option, but users are happiest when they don't have to change random BIOS settings. This patch introduces a driver method to switchover the ports from EHCI to xHCI before the EHCI driver finishes PCI enumeration. We want to switch the ports over before the USB core has the chance to enumerate devices under EHCI, or boot from USB mass storage will fail if the boot device connects under EHCI first, and then gets disconnected when the port switches over to xHCI. Add code to the xHCI PCI quirk to switch the ports from EHCI to xHCI. The PCI quirks code will run before any other PCI probe function is called, so this avoids the issue with boot devices. Another issue is with BIOS behavior during system resume from hibernate. If the BIOS doesn't support xHCI, it may switch the devices under EHCI to allow use of the USB keyboard, mice, and mass storage devices. It's supposed to remember the value of the port routing registers and switch them back when the OS attempts to take control of the xHCI host controller, but we all know not to trust BIOS writers. Make both the xHCI driver and the EHCI driver attempt to switchover the ports in their PCI resume functions. We can't guarantee which PCI device will be resumed first, so this avoids any race conditions. Writing a '1' to an already set port switchover bit or a '0' to a cleared port switchover bit should have no effect. The xHCI PCI configuration registers will be documented in the EDS-level chipset spec, which is not public yet. I have permission from legal and the Intel chipset group to release this patch early to allow good Linux support at product launch. I've tried to document the registers as much as possible, so please let me know if anything is unclear. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2011-02-23 01:57:15 +08:00
static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
{
return pdev->class == PCI_CLASS_SERIAL_USB_EHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == 0x1E26;
}
static void ehci_enable_xhci_companion(void)
{
struct pci_dev *companion = NULL;
/* The xHCI and EHCI controllers are not on the same PCI slot */
for_each_pci_dev(companion) {
if (!usb_is_intel_switchable_xhci(companion))
continue;
usb_enable_xhci_ports(companion);
return;
}
}
static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
Intel xhci: Support EHCI/xHCI port switching. The Intel Panther Point chipsets contain an EHCI and xHCI host controller that shares some number of skew-dependent ports. These ports can be switched from the EHCI to the xHCI host (and vice versa) by a hardware MUX that is controlled by registers in the xHCI PCI configuration space. The USB 3.0 SuperSpeed terminations on the xHCI ports can be controlled separately from the USB 2.0 data wires. This switchover mechanism is there to support users who do a custom install of certain non-Linux operating systems that don't have official USB 3.0 support. By default, the ports are under EHCI, SuperSpeed terminations are off, and USB 3.0 devices will show up under the EHCI controller at reduced speeds. (This was more palatable for the marketing folks than having completely dead USB 3.0 ports if no xHCI drivers are available.) Users should be able to turn on xHCI by default through a BIOS option, but users are happiest when they don't have to change random BIOS settings. This patch introduces a driver method to switchover the ports from EHCI to xHCI before the EHCI driver finishes PCI enumeration. We want to switch the ports over before the USB core has the chance to enumerate devices under EHCI, or boot from USB mass storage will fail if the boot device connects under EHCI first, and then gets disconnected when the port switches over to xHCI. Add code to the xHCI PCI quirk to switch the ports from EHCI to xHCI. The PCI quirks code will run before any other PCI probe function is called, so this avoids the issue with boot devices. Another issue is with BIOS behavior during system resume from hibernate. If the BIOS doesn't support xHCI, it may switch the devices under EHCI to allow use of the USB keyboard, mice, and mass storage devices. It's supposed to remember the value of the port routing registers and switch them back when the OS attempts to take control of the xHCI host controller, but we all know not to trust BIOS writers. Make both the xHCI driver and the EHCI driver attempt to switchover the ports in their PCI resume functions. We can't guarantee which PCI device will be resumed first, so this avoids any race conditions. Writing a '1' to an already set port switchover bit or a '0' to a cleared port switchover bit should have no effect. The xHCI PCI configuration registers will be documented in the EDS-level chipset spec, which is not public yet. I have permission from legal and the Intel chipset group to release this patch early to allow good Linux support at product launch. I've tried to document the registers as much as possible, so please let me know if anything is unclear. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2011-02-23 01:57:15 +08:00
/* The BIOS on systems with the Intel Panther Point chipset may or may
* not support xHCI natively. That means that during system resume, it
* may switch the ports back to EHCI so that users can use their
* keyboard to select a kernel from GRUB after resume from hibernate.
*
* The BIOS is supposed to remember whether the OS had xHCI ports
* enabled before resume, and switch the ports back to xHCI when the
* BIOS/OS semaphore is written, but we all know we can't trust BIOS
* writers.
*
* Unconditionally switch the ports back to xHCI after a system resume.
* We can't tell whether the EHCI or xHCI controller will be resumed
* first, so we have to do the port switchover in both drivers. Writing
* a '1' to the port switchover registers should have no effect if the
* port was already switched over.
*/
if (usb_is_intel_switchable_ehci(pdev))
ehci_enable_xhci_companion();
// 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 and we aren't resuming from hibernation
* then we maintained PCI Vaux power.
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
* Just undo the effect of ehci_pci_suspend().
*/
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
!hibernated) {
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
int mask = INTR_MASK;
ehci_prepare_ports_for_controller_resume(ehci);
if (!hcd->self.root_hub->do_remote_wakeup)
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
mask &= ~STS_PCD;
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
ehci_readl(ehci, &ehci->regs->intr_enable);
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
return 0;
}
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)
end_unlink_async(ehci);
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
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 */
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
ehci->rh_state = EHCI_RH_SUSPENDED;
EHCI: Fix root-hub and port suspend/resume problems This patch (as738b) fixes numerous problems in the controller/root-hub suspend/resume/remote-wakeup support in ehci-hcd: The bus_resume() routine should wake up only the ports that were suspended by bus_suspend(). Ports that were already suspended should remain that way. The interrupt mask is used to detect loss of power in the bus_resume() routine (if the mask is 0 then power was lost). However bus_suspend() always sets the mask to 0. Instead the mask should retain its normal value, with port-change-detect interrupts disabled if remote wakeup is turned off. The interrupt mask should be reset to its correct value at the end of bus_resume() regardless of whether power was lost. bus_resume() reinitializes the operational registers if power was lost. However those registers are not in the aux power well, hence they can lose their values whenever the controller is put into D3. They should always be reinitialized. When a port-change interrupt occurs and the root hub is suspended, the interrupt handler should request a root-hub resume instead of starting up the controller all by itself. There's no need for the interrupt handler to request a root-hub resume every time a suspended port sends a remote-wakeup request. The pci_resume() method doesn't need to check for connected ports when deciding whether or not to reset the controller. It can make that decision based on whether Vaux power was maintained. Even when the controller does not need to be reset, pci_resume() must undo the effect of pci_suspend() by re-enabling the interrupt mask. If power was lost, pci_resume() must not call ehci_run(). At this point the root hub is still supposed to be suspended, not running. It's enough to rewrite the command register and set the configured_flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-11-10 03:42:16 +08:00
return 0;
}
#endif
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int rc = 0;
if (!udev->parent) /* udev is root hub itself, impossible */
rc = -1;
/* we only support lpm device connected to root hub yet */
if (ehci->has_lpm && !udev->parent->parent) {
rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
if (!rc)
rc = ehci_lpm_check(ehci, udev->portnum);
}
return rc;
}
static 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
.pci_suspend = ehci_pci_suspend,
.pci_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,
.endpoint_reset = ehci_endpoint_reset,
/*
* 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,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
/*
* call back when device connected and addressed
*/
.update_device = ehci_update_device,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
/*-------------------------------------------------------------------------*/
/* 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,
}, {
PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST),
.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,
.shutdown = usb_hcd_pci_shutdown,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
#endif
};