Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6

* 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: (36 commits)
  PCI: hotplug: pciehp: Removed check for hotplug of display devices
  PCI: read memory ranges out of Broadcom CNB20LE host bridge
  PCI: Allow manual resource allocation for PCI hotplug bridges
  x86/PCI: make ACPI MCFG reserved error messages ACPI specific
  PCI hotplug: Use kmemdup
  PM/PCI: Update PCI power management documentation
  PCI: output FW warning in pci_read/write_vpd
  PCI: fix typos pci_device_dis/enable to pci_dis/enable_device in comments
  PCI quirks: disable msi on AMD rs4xx internal gfx bridges
  PCI: Disable MSI for MCP55 on P5N32-E SLI
  x86/PCI: irq and pci_ids patch for additional Intel Cougar Point DeviceIDs
  PCI: aerdrv: trivial cleanup for aerdrv_core.c
  PCI: aerdrv: trivial cleanup for aerdrv.c
  PCI: aerdrv: introduce default_downstream_reset_link
  PCI: aerdrv: rework find_aer_service
  PCI: aerdrv: remove is_downstream
  PCI: aerdrv: remove magical ROOT_ERR_STATUS_MASKS
  PCI: aerdrv: redefine PCI_ERR_ROOT_*_SRC
  PCI: aerdrv: rework do_recovery
  PCI: aerdrv: rework get_e_source()
  ...
This commit is contained in:
Linus Torvalds 2010-05-21 18:58:52 -07:00
Родитель 0961d6581c ac81860ea0
Коммит 6109e2ce26
33 изменённых файлов: 1714 добавлений и 728 удалений

Просмотреть файл

@ -133,6 +133,46 @@ Description:
The symbolic link points to the PCI device sysfs entry of the The symbolic link points to the PCI device sysfs entry of the
Physical Function this device associates with. Physical Function this device associates with.
What: /sys/bus/pci/slots/...
Date: April 2005 (possibly older)
KernelVersion: 2.6.12 (possibly older)
Contact: linux-pci@vger.kernel.org
Description:
When the appropriate driver is loaded, it will create a
directory per claimed physical PCI slot in
/sys/bus/pci/slots/. The names of these directories are
specific to the driver, which in turn, are specific to the
platform, but in general, should match the label on the
machine's physical chassis.
The drivers that can create slot directories include the
PCI hotplug drivers, and as of 2.6.27, the pci_slot driver.
The slot directories contain, at a minimum, a file named
'address' which contains the PCI bus:device:function tuple.
Other files may appear as well, but are specific to the
driver.
What: /sys/bus/pci/slots/.../function[0-7]
Date: March 2010
KernelVersion: 2.6.35
Contact: linux-pci@vger.kernel.org
Description:
If PCI slot directories (as described above) are created,
and the physical slot is actually populated with a device,
symbolic links in the slot directory pointing to the
device's PCI functions are created as well.
What: /sys/bus/pci/devices/.../slot
Date: March 2010
KernelVersion: 2.6.35
Contact: linux-pci@vger.kernel.org
Description:
If PCI slot directories (as described above) are created,
a symbolic link pointing to the slot directory will be
created as well.
What: /sys/bus/pci/slots/.../module What: /sys/bus/pci/slots/.../module
Date: June 2009 Date: June 2009
Contact: linux-pci@vger.kernel.org Contact: linux-pci@vger.kernel.org

Просмотреть файл

@ -13,7 +13,7 @@ Reporting (AER) driver and provides information on how to use it, as
well as how to enable the drivers of endpoint devices to conform with well as how to enable the drivers of endpoint devices to conform with
PCI Express AER driver. PCI Express AER driver.
1.2 Copyright © Intel Corporation 2006. 1.2 Copyright (C) Intel Corporation 2006.
1.3 What is the PCI Express AER Driver? 1.3 What is the PCI Express AER Driver?
@ -71,15 +71,11 @@ console. If it's a correctable error, it is outputed as a warning.
Otherwise, it is printed as an error. So users could choose different Otherwise, it is printed as an error. So users could choose different
log level to filter out correctable error messages. log level to filter out correctable error messages.
Below shows an example. Below shows an example:
+------ PCI-Express Device Error -----+ 0000:50:00.0: PCIe Bus Error: severity=Uncorrected (Fatal), type=Transaction Layer, id=0500(Requester ID)
Error Severity : Uncorrected (Fatal) 0000:50:00.0: device [8086:0329] error status/mask=00100000/00000000
PCIE Bus Error type : Transaction Layer 0000:50:00.0: [20] Unsupported Request (First)
Unsupported Request : First 0000:50:00.0: TLP Header: 04000001 00200a03 05010000 00050100
Requester ID : 0500
VendorID=8086h, DeviceID=0329h, Bus=05h, Device=00h, Function=00h
TLB Header:
04000001 00200a03 05010000 00050100
In the example, 'Requester ID' means the ID of the device who sends In the example, 'Requester ID' means the ID of the device who sends
the error message to root port. Pls. refer to pci express specs for the error message to root port. Pls. refer to pci express specs for
@ -112,7 +108,7 @@ but the PCI Express link itself is fully functional. Fatal errors, on
the other hand, cause the link to be unreliable. the other hand, cause the link to be unreliable.
When AER is enabled, a PCI Express device will automatically send an When AER is enabled, a PCI Express device will automatically send an
error message to the PCIE root port above it when the device captures error message to the PCIe root port above it when the device captures
an error. The Root Port, upon receiving an error reporting message, an error. The Root Port, upon receiving an error reporting message,
internally processes and logs the error message in its PCI Express internally processes and logs the error message in its PCI Express
capability structure. Error information being logged includes storing capability structure. Error information being logged includes storing
@ -198,8 +194,9 @@ to reset link, AER port service driver is required to provide the
function to reset link. Firstly, kernel looks for if the upstream function to reset link. Firstly, kernel looks for if the upstream
component has an aer driver. If it has, kernel uses the reset_link component has an aer driver. If it has, kernel uses the reset_link
callback of the aer driver. If the upstream component has no aer driver callback of the aer driver. If the upstream component has no aer driver
and the port is downstream port, we will use the aer driver of the and the port is downstream port, we will perform a hot reset as the
root port who reports the AER error. As for upstream ports, default by setting the Secondary Bus Reset bit of the Bridge Control
register associated with the downstream port. As for upstream ports,
they should provide their own aer service drivers with reset_link they should provide their own aer service drivers with reset_link
function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
@ -253,11 +250,11 @@ cleanup uncorrectable status register. Pls. refer to section 3.3.
4. Software error injection 4. Software error injection
Debugging PCIE AER error recovery code is quite difficult because it Debugging PCIe AER error recovery code is quite difficult because it
is hard to trigger real hardware errors. Software based error is hard to trigger real hardware errors. Software based error
injection can be used to fake various kinds of PCIE errors. injection can be used to fake various kinds of PCIe errors.
First you should enable PCIE AER software error injection in kernel First you should enable PCIe AER software error injection in kernel
configuration, that is, following item should be in your .config. configuration, that is, following item should be in your .config.
CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1923,6 +1923,14 @@ config PCI_MMCONFIG
bool "Support mmconfig PCI config space access" bool "Support mmconfig PCI config space access"
depends on X86_64 && PCI && ACPI depends on X86_64 && PCI && ACPI
config PCI_CNB20LE_QUIRK
bool "Read CNB20LE Host Bridge Windows"
depends on PCI
help
Read the PCI windows out of the CNB20LE host bridge. This allows
PCI hotplug to work on systems with the CNB20LE chipset which do
not have ACPI.
config DMAR config DMAR
bool "Support for DMA Remapping Devices (EXPERIMENTAL)" bool "Support for DMA Remapping Devices (EXPERIMENTAL)"
depends on PCI_MSI && ACPI && EXPERIMENTAL depends on PCI_MSI && ACPI && EXPERIMENTAL

Просмотреть файл

@ -83,7 +83,7 @@ struct irq_routing_table {
extern unsigned int pcibios_irq_mask; extern unsigned int pcibios_irq_mask;
extern spinlock_t pci_config_lock; extern raw_spinlock_t pci_config_lock;
extern int (*pcibios_enable_irq)(struct pci_dev *dev); extern int (*pcibios_enable_irq)(struct pci_dev *dev);
extern void (*pcibios_disable_irq)(struct pci_dev *dev); extern void (*pcibios_disable_irq)(struct pci_dev *dev);

Просмотреть файл

@ -18,6 +18,8 @@ obj-$(CONFIG_X86_MRST) += mrst.o
obj-y += common.o early.o obj-y += common.o early.o
obj-y += amd_bus.o bus_numa.o obj-y += amd_bus.o bus_numa.o
obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o
ifeq ($(CONFIG_PCI_DEBUG),y) ifeq ($(CONFIG_PCI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
endif endif

101
arch/x86/pci/broadcom_bus.c Normal file
Просмотреть файл

@ -0,0 +1,101 @@
/*
* Read address ranges from a Broadcom CNB20LE Host Bridge
*
* Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
*
* 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.
*/
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/pci_x86.h>
#include "bus_numa.h"
static void __devinit cnb20le_res(struct pci_dev *dev)
{
struct pci_root_info *info;
struct resource res;
u16 word1, word2;
u8 fbus, lbus;
int i;
/*
* The x86_pci_root_bus_res_quirks() function already refuses to use
* this information if ACPI _CRS was used. Therefore, we don't bother
* checking if ACPI is enabled, and just generate the information
* for both the ACPI _CRS and no ACPI cases.
*/
info = &pci_root_info[pci_root_num];
pci_root_num++;
/* read the PCI bus numbers */
pci_read_config_byte(dev, 0x44, &fbus);
pci_read_config_byte(dev, 0x45, &lbus);
info->bus_min = fbus;
info->bus_max = lbus;
/*
* Add the legacy IDE ports on bus 0
*
* These do not exist anywhere in the bridge registers, AFAICT. I do
* not have the datasheet, so this is the best I can do.
*/
if (fbus == 0) {
update_res(info, 0x01f0, 0x01f7, IORESOURCE_IO, 0);
update_res(info, 0x03f6, 0x03f6, IORESOURCE_IO, 0);
update_res(info, 0x0170, 0x0177, IORESOURCE_IO, 0);
update_res(info, 0x0376, 0x0376, IORESOURCE_IO, 0);
update_res(info, 0xffa0, 0xffaf, IORESOURCE_IO, 0);
}
/* read the non-prefetchable memory window */
pci_read_config_word(dev, 0xc0, &word1);
pci_read_config_word(dev, 0xc2, &word2);
if (word1 != word2) {
res.start = (word1 << 16) | 0x0000;
res.end = (word2 << 16) | 0xffff;
res.flags = IORESOURCE_MEM;
update_res(info, res.start, res.end, res.flags, 0);
}
/* read the prefetchable memory window */
pci_read_config_word(dev, 0xc4, &word1);
pci_read_config_word(dev, 0xc6, &word2);
if (word1 != word2) {
res.start = (word1 << 16) | 0x0000;
res.end = (word2 << 16) | 0xffff;
res.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
update_res(info, res.start, res.end, res.flags, 0);
}
/* read the IO port window */
pci_read_config_word(dev, 0xd0, &word1);
pci_read_config_word(dev, 0xd2, &word2);
if (word1 != word2) {
res.start = word1;
res.end = word2;
res.flags = IORESOURCE_IO;
update_res(info, res.start, res.end, res.flags, 0);
}
/* print information about this host bridge */
res.start = fbus;
res.end = lbus;
res.flags = IORESOURCE_BUS;
dev_info(&dev->dev, "CNB20LE PCI Host Bridge (domain %04x %pR)\n",
pci_domain_nr(dev->bus), &res);
for (i = 0; i < info->res_num; i++)
dev_info(&dev->dev, "host bridge window %pR\n", &info->res[i]);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE,
cnb20le_res);

Просмотреть файл

@ -76,7 +76,7 @@ struct pci_ops pci_root_ops = {
* This interrupt-safe spinlock protects all accesses to PCI * This interrupt-safe spinlock protects all accesses to PCI
* configuration space. * configuration space.
*/ */
DEFINE_SPINLOCK(pci_config_lock); DEFINE_RAW_SPINLOCK(pci_config_lock);
static int __devinit can_skip_ioresource_align(const struct dmi_system_id *d) static int __devinit can_skip_ioresource_align(const struct dmi_system_id *d)
{ {

Просмотреть файл

@ -27,7 +27,7 @@ static int pci_conf1_read(unsigned int seg, unsigned int bus,
return -EINVAL; return -EINVAL;
} }
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
@ -43,7 +43,7 @@ static int pci_conf1_read(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }
@ -56,7 +56,7 @@ static int pci_conf1_write(unsigned int seg, unsigned int bus,
if ((bus > 255) || (devfn > 255) || (reg > 4095)) if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
@ -72,7 +72,7 @@ static int pci_conf1_write(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }
@ -108,7 +108,7 @@ static int pci_conf2_read(unsigned int seg, unsigned int bus,
if (dev & 0x10) if (dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
outb((u8)(0xF0 | (fn << 1)), 0xCF8); outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA); outb((u8)bus, 0xCFA);
@ -127,7 +127,7 @@ static int pci_conf2_read(unsigned int seg, unsigned int bus,
outb(0, 0xCF8); outb(0, 0xCF8);
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }
@ -147,7 +147,7 @@ static int pci_conf2_write(unsigned int seg, unsigned int bus,
if (dev & 0x10) if (dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
outb((u8)(0xF0 | (fn << 1)), 0xCF8); outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA); outb((u8)bus, 0xCFA);
@ -166,7 +166,7 @@ static int pci_conf2_write(unsigned int seg, unsigned int bus,
outb(0, 0xCF8); outb(0, 0xCF8);
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }

Просмотреть файл

@ -589,8 +589,6 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
case PCI_DEVICE_ID_INTEL_ICH10_1: case PCI_DEVICE_ID_INTEL_ICH10_1:
case PCI_DEVICE_ID_INTEL_ICH10_2: case PCI_DEVICE_ID_INTEL_ICH10_2:
case PCI_DEVICE_ID_INTEL_ICH10_3: case PCI_DEVICE_ID_INTEL_ICH10_3:
case PCI_DEVICE_ID_INTEL_CPT_LPC1:
case PCI_DEVICE_ID_INTEL_CPT_LPC2:
r->name = "PIIX/ICH"; r->name = "PIIX/ICH";
r->get = pirq_piix_get; r->get = pirq_piix_get;
r->set = pirq_piix_set; r->set = pirq_piix_set;
@ -605,6 +603,13 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
return 1; return 1;
} }
if ((device >= PCI_DEVICE_ID_INTEL_CPT_LPC_MIN) &&
(device <= PCI_DEVICE_ID_INTEL_CPT_LPC_MAX)) {
r->name = "PIIX/ICH";
r->get = pirq_piix_get;
r->set = pirq_piix_set;
return 1;
}
return 0; return 0;
} }

Просмотреть файл

@ -483,16 +483,17 @@ static void __init pci_mmcfg_reject_broken(int early)
list_for_each_entry(cfg, &pci_mmcfg_list, list) { list_for_each_entry(cfg, &pci_mmcfg_list, list) {
int valid = 0; int valid = 0;
if (!early && !acpi_disabled) if (!early && !acpi_disabled) {
valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0); valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
if (valid) if (valid)
continue; continue;
else
if (!early) printk(KERN_ERR FW_BUG PREFIX
printk(KERN_ERR FW_BUG PREFIX "MMCONFIG at %pR not reserved in "
"MMCONFIG at %pR not reserved in " "ACPI motherboard resources\n",
"ACPI motherboard resources\n", &cfg->res); &cfg->res);
}
/* Don't try to do this check unless configuration /* Don't try to do this check unless configuration
type 1 is available. how about type 2 ?*/ type 1 is available. how about type 2 ?*/

Просмотреть файл

@ -64,7 +64,7 @@ err: *value = -1;
if (!base) if (!base)
goto err; goto err;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(base, bus, devfn); pci_exp_set_dev_base(base, bus, devfn);
@ -79,7 +79,7 @@ err: *value = -1;
*value = mmio_config_readl(mmcfg_virt_addr + reg); *value = mmio_config_readl(mmcfg_virt_addr + reg);
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }
@ -97,7 +97,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
if (!base) if (!base)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(base, bus, devfn); pci_exp_set_dev_base(base, bus, devfn);
@ -112,7 +112,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
mmio_config_writel(mmcfg_virt_addr + reg, value); mmio_config_writel(mmcfg_virt_addr + reg, value);
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }

Просмотреть файл

@ -37,7 +37,7 @@ static int pci_conf1_mq_read(unsigned int seg, unsigned int bus,
if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
write_cf8(bus, devfn, reg); write_cf8(bus, devfn, reg);
@ -62,7 +62,7 @@ static int pci_conf1_mq_read(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }
@ -76,7 +76,7 @@ static int pci_conf1_mq_write(unsigned int seg, unsigned int bus,
if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
write_cf8(bus, devfn, reg); write_cf8(bus, devfn, reg);
@ -101,7 +101,7 @@ static int pci_conf1_mq_write(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return 0; return 0;
} }

Просмотреть файл

@ -162,7 +162,7 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) if (!value || (bus > 255) || (devfn > 255) || (reg > 255))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
switch (len) { switch (len) {
case 1: case 1:
@ -213,7 +213,7 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return (int)((result & 0xff00) >> 8); return (int)((result & 0xff00) >> 8);
} }
@ -228,7 +228,7 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
if ((bus > 255) || (devfn > 255) || (reg > 255)) if ((bus > 255) || (devfn > 255) || (reg > 255))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags); raw_spin_lock_irqsave(&pci_config_lock, flags);
switch (len) { switch (len) {
case 1: case 1:
@ -269,7 +269,7 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
break; break;
} }
spin_unlock_irqrestore(&pci_config_lock, flags); raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return (int)((result & 0xff00) >> 8); return (int)((result & 0xff00) >> 8);
} }

Просмотреть файл

@ -294,7 +294,7 @@ static int __devinit amd76x_init_one(struct pci_dev *pdev,
{ {
debugf0("%s()\n", __func__); debugf0("%s()\n", __func__);
/* don't need to call pci_device_enable() */ /* don't need to call pci_enable_device() */
return amd76x_probe1(pdev, ent->driver_data); return amd76x_probe1(pdev, ent->driver_data);
} }

Просмотреть файл

@ -354,7 +354,7 @@ static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev,
debugf0("MC: " __FILE__ ": %s()\n", __func__); debugf0("MC: " __FILE__ ": %s()\n", __func__);
/* don't need to call pci_device_enable() */ /* don't need to call pci_enable_device() */
rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data); rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data);
if (mci_pdev == NULL) if (mci_pdev == NULL)

Просмотреть файл

@ -354,7 +354,7 @@ static int __devinit r82600_init_one(struct pci_dev *pdev,
{ {
debugf0("%s()\n", __func__); debugf0("%s()\n", __func__);
/* don't need to call pci_device_enable() */ /* don't need to call pci_enable_device() */
return r82600_probe1(pdev, ent->driver_data); return r82600_probe1(pdev, ent->driver_data);
} }

Просмотреть файл

@ -19,7 +19,7 @@ config PCI_MSI
by using the 'pci=nomsi' option. This disables MSI for the by using the 'pci=nomsi' option. This disables MSI for the
entire system. entire system.
If you don't know what to do here, say N. If you don't know what to do here, say Y.
config PCI_DEBUG config PCI_DEBUG
bool "PCI Debugging" bool "PCI Debugging"

Просмотреть файл

@ -13,7 +13,7 @@
* configuration space. * configuration space.
*/ */
static DEFINE_SPINLOCK(pci_lock); static DEFINE_RAW_SPINLOCK(pci_lock);
/* /*
* Wrappers for all PCI configuration access functions. They just check * Wrappers for all PCI configuration access functions. They just check
@ -33,10 +33,10 @@ int pci_bus_read_config_##size \
unsigned long flags; \ unsigned long flags; \
u32 data = 0; \ u32 data = 0; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \ raw_spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->read(bus, devfn, pos, len, &data); \ res = bus->ops->read(bus, devfn, pos, len, &data); \
*value = (type)data; \ *value = (type)data; \
spin_unlock_irqrestore(&pci_lock, flags); \ raw_spin_unlock_irqrestore(&pci_lock, flags); \
return res; \ return res; \
} }
@ -47,9 +47,9 @@ int pci_bus_write_config_##size \
int res; \ int res; \
unsigned long flags; \ unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \ raw_spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->write(bus, devfn, pos, len, value); \ res = bus->ops->write(bus, devfn, pos, len, value); \
spin_unlock_irqrestore(&pci_lock, flags); \ raw_spin_unlock_irqrestore(&pci_lock, flags); \
return res; \ return res; \
} }
@ -79,10 +79,10 @@ struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops)
struct pci_ops *old_ops; struct pci_ops *old_ops;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&pci_lock, flags); raw_spin_lock_irqsave(&pci_lock, flags);
old_ops = bus->ops; old_ops = bus->ops;
bus->ops = ops; bus->ops = ops;
spin_unlock_irqrestore(&pci_lock, flags); raw_spin_unlock_irqrestore(&pci_lock, flags);
return old_ops; return old_ops;
} }
EXPORT_SYMBOL(pci_bus_set_ops); EXPORT_SYMBOL(pci_bus_set_ops);
@ -136,9 +136,9 @@ static noinline void pci_wait_ucfg(struct pci_dev *dev)
__add_wait_queue(&pci_ucfg_wait, &wait); __add_wait_queue(&pci_ucfg_wait, &wait);
do { do {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&pci_lock); raw_spin_unlock_irq(&pci_lock);
schedule(); schedule();
spin_lock_irq(&pci_lock); raw_spin_lock_irq(&pci_lock);
} while (dev->block_ucfg_access); } while (dev->block_ucfg_access);
__remove_wait_queue(&pci_ucfg_wait, &wait); __remove_wait_queue(&pci_ucfg_wait, &wait);
} }
@ -150,11 +150,11 @@ int pci_user_read_config_##size \
int ret = 0; \ int ret = 0; \
u32 data = -1; \ u32 data = -1; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irq(&pci_lock); \ raw_spin_lock_irq(&pci_lock); \
if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
ret = dev->bus->ops->read(dev->bus, dev->devfn, \ ret = dev->bus->ops->read(dev->bus, dev->devfn, \
pos, sizeof(type), &data); \ pos, sizeof(type), &data); \
spin_unlock_irq(&pci_lock); \ raw_spin_unlock_irq(&pci_lock); \
*val = (type)data; \ *val = (type)data; \
return ret; \ return ret; \
} }
@ -165,11 +165,11 @@ int pci_user_write_config_##size \
{ \ { \
int ret = -EIO; \ int ret = -EIO; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irq(&pci_lock); \ raw_spin_lock_irq(&pci_lock); \
if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
ret = dev->bus->ops->write(dev->bus, dev->devfn, \ ret = dev->bus->ops->write(dev->bus, dev->devfn, \
pos, sizeof(type), val); \ pos, sizeof(type), val); \
spin_unlock_irq(&pci_lock); \ raw_spin_unlock_irq(&pci_lock); \
return ret; \ return ret; \
} }
@ -220,8 +220,13 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev)
return 0; return 0;
} }
if (time_after(jiffies, timeout)) if (time_after(jiffies, timeout)) {
dev_printk(KERN_DEBUG, &dev->dev,
"vpd r/w failed. This is likely a firmware "
"bug on this device. Contact the card "
"vendor for a firmware update.");
return -ETIMEDOUT; return -ETIMEDOUT;
}
if (fatal_signal_pending(current)) if (fatal_signal_pending(current))
return -EINTR; return -EINTR;
if (!cond_resched()) if (!cond_resched())
@ -396,10 +401,10 @@ void pci_block_user_cfg_access(struct pci_dev *dev)
unsigned long flags; unsigned long flags;
int was_blocked; int was_blocked;
spin_lock_irqsave(&pci_lock, flags); raw_spin_lock_irqsave(&pci_lock, flags);
was_blocked = dev->block_ucfg_access; was_blocked = dev->block_ucfg_access;
dev->block_ucfg_access = 1; dev->block_ucfg_access = 1;
spin_unlock_irqrestore(&pci_lock, flags); raw_spin_unlock_irqrestore(&pci_lock, flags);
/* If we BUG() inside the pci_lock, we're guaranteed to hose /* If we BUG() inside the pci_lock, we're guaranteed to hose
* the machine */ * the machine */
@ -417,7 +422,7 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&pci_lock, flags); raw_spin_lock_irqsave(&pci_lock, flags);
/* This indicates a problem in the caller, but we don't need /* This indicates a problem in the caller, but we don't need
* to kill them, unlike a double-block above. */ * to kill them, unlike a double-block above. */
@ -425,6 +430,6 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev)
dev->block_ucfg_access = 0; dev->block_ucfg_access = 0;
wake_up_all(&pci_ucfg_wait); wake_up_all(&pci_ucfg_wait);
spin_unlock_irqrestore(&pci_lock, flags); raw_spin_unlock_irqrestore(&pci_lock, flags);
} }
EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access); EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access);

Просмотреть файл

@ -1075,13 +1075,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* make our own copy of the pci bus structure, /* make our own copy of the pci bus structure,
* as we like tweaking it a lot */ * as we like tweaking it a lot */
ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL); ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) { if (!ctrl->pci_bus) {
err("out of memory\n"); err("out of memory\n");
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_ctrl; goto err_free_ctrl;
} }
memcpy(ctrl->pci_bus, pdev->bus, sizeof(*ctrl->pci_bus));
ctrl->bus = pdev->bus->number; ctrl->bus = pdev->bus->number;
ctrl->rev = pdev->revision; ctrl->rev = pdev->revision;

Просмотреть файл

@ -84,12 +84,6 @@ int pciehp_configure_device(struct slot *p_slot)
dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); dev = pci_get_slot(parent, PCI_DEVFN(0, fn));
if (!dev) if (!dev)
continue; continue;
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
ctrl_err(ctrl, "Cannot hot-add display device %s\n",
pci_name(dev));
pci_dev_put(dev);
continue;
}
if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
pciehp_add_bridge(dev); pciehp_add_bridge(dev);
@ -133,15 +127,9 @@ int pciehp_unconfigure_device(struct slot *p_slot)
presence = 0; presence = 0;
for (j = 0; j < 8; j++) { for (j = 0; j < 8; j++) {
struct pci_dev* temp = pci_get_slot(parent, PCI_DEVFN(0, j)); struct pci_dev *temp = pci_get_slot(parent, PCI_DEVFN(0, j));
if (!temp) if (!temp)
continue; continue;
if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
ctrl_err(ctrl, "Cannot remove display device %s\n",
pci_name(temp));
pci_dev_put(temp);
continue;
}
if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) {
pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl);
if (bctl & PCI_BRIDGE_CTL_VGA) { if (bctl & PCI_BRIDGE_CTL_VGA) {
@ -149,7 +137,8 @@ int pciehp_unconfigure_device(struct slot *p_slot)
"Cannot remove display device %s\n", "Cannot remove display device %s\n",
pci_name(temp)); pci_name(temp));
pci_dev_put(temp); pci_dev_put(temp);
continue; rc = EINVAL;
break;
} }
} }
pci_remove_bus_device(temp); pci_remove_bus_device(temp);

Просмотреть файл

@ -979,7 +979,12 @@ static ssize_t reset_store(struct device *dev,
if (val != 1) if (val != 1)
return -EINVAL; return -EINVAL;
return pci_reset_function(pdev);
result = pci_reset_function(pdev);
if (result < 0)
return result;
return count;
} }
static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store); static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store);
@ -1030,6 +1035,39 @@ error:
return retval; return retval;
} }
static void pci_remove_slot_links(struct pci_dev *dev)
{
char func[10];
struct pci_slot *slot;
sysfs_remove_link(&dev->dev.kobj, "slot");
list_for_each_entry(slot, &dev->bus->slots, list) {
if (slot->number != PCI_SLOT(dev->devfn))
continue;
snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn));
sysfs_remove_link(&slot->kobj, func);
}
}
static int pci_create_slot_links(struct pci_dev *dev)
{
int result = 0;
char func[10];
struct pci_slot *slot;
list_for_each_entry(slot, &dev->bus->slots, list) {
if (slot->number != PCI_SLOT(dev->devfn))
continue;
result = sysfs_create_link(&dev->dev.kobj, &slot->kobj, "slot");
if (result)
goto out;
snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn));
result = sysfs_create_link(&slot->kobj, &dev->dev.kobj, func);
}
out:
return result;
}
int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
{ {
int retval; int retval;
@ -1092,6 +1130,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
if (retval) if (retval)
goto err_vga_file; goto err_vga_file;
pci_create_slot_links(pdev);
return 0; return 0;
err_vga_file: err_vga_file:
@ -1141,6 +1181,8 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
if (!sysfs_initialized) if (!sysfs_initialized)
return; return;
pci_remove_slot_links(pdev);
pci_remove_capabilities_sysfs(pdev); pci_remove_capabilities_sysfs(pdev);
if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)

Просмотреть файл

@ -1193,7 +1193,7 @@ void pci_disable_enabled_device(struct pci_dev *dev)
* anymore. This only involves disabling PCI bus-mastering, if active. * anymore. This only involves disabling PCI bus-mastering, if active.
* *
* Note we don't actually disable the device until all callers of * Note we don't actually disable the device until all callers of
* pci_device_enable() have called pci_device_disable(). * pci_enable_device() have called pci_disable_device().
*/ */
void void
pci_disable_device(struct pci_dev *dev) pci_disable_device(struct pci_dev *dev)
@ -1631,7 +1631,6 @@ void pci_pm_init(struct pci_dev *dev)
* let the user space enable it to wake up the system as needed. * let the user space enable it to wake up the system as needed.
*/ */
device_set_wakeup_capable(&dev->dev, true); device_set_wakeup_capable(&dev->dev, true);
device_set_wakeup_enable(&dev->dev, false);
/* Disable the PME# generation functionality */ /* Disable the PME# generation functionality */
pci_pme_active(dev, false); pci_pme_active(dev, false);
} else { } else {
@ -1655,7 +1654,6 @@ void platform_pci_wakeup_init(struct pci_dev *dev)
return; return;
device_set_wakeup_capable(&dev->dev, true); device_set_wakeup_capable(&dev->dev, true);
device_set_wakeup_enable(&dev->dev, false);
platform_pci_sleep_wake(dev, false); platform_pci_sleep_wake(dev, false);
} }

Просмотреть файл

@ -168,7 +168,7 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where,
target = &err->root_status; target = &err->root_status;
rw1cs = 1; rw1cs = 1;
break; break;
case PCI_ERR_ROOT_COR_SRC: case PCI_ERR_ROOT_ERR_SRC:
target = &err->source_id; target = &err->source_id;
break; break;
} }

Просмотреть файл

@ -72,13 +72,120 @@ void pci_no_aer(void)
pcie_aer_disable = 1; /* has priority over 'forceload' */ pcie_aer_disable = 1; /* has priority over 'forceload' */
} }
static int set_device_error_reporting(struct pci_dev *dev, void *data)
{
bool enable = *((bool *)data);
if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
(dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
(dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
if (enable)
pci_enable_pcie_error_reporting(dev);
else
pci_disable_pcie_error_reporting(dev);
}
if (enable)
pcie_set_ecrc_checking(dev);
return 0;
}
/**
* set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports.
* @dev: pointer to root port's pci_dev data structure
* @enable: true = enable error reporting, false = disable error reporting.
*/
static void set_downstream_devices_error_reporting(struct pci_dev *dev,
bool enable)
{
set_device_error_reporting(dev, &enable);
if (!dev->subordinate)
return;
pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
}
/**
* aer_enable_rootport - enable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
*
* Invoked when PCIe bus loads AER service driver.
*/
static void aer_enable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
int pos, aer_pos;
u16 reg16;
u32 reg32;
pos = pci_pcie_cap(pdev);
/* Clear PCIe Capability's Device Status */
pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
/* Disable system error generation in response to error messages */
pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Clear error status */
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
/*
* Enable error reporting for the root port device and downstream port
* devices.
*/
set_downstream_devices_error_reporting(pdev, true);
/* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, &reg32);
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
}
/**
* aer_disable_rootport - disable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
*
* Invoked when PCIe bus unloads AER service driver.
*/
static void aer_disable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
u32 reg32;
int pos;
/*
* Disable error reporting for the root port device and downstream port
* devices.
*/
set_downstream_devices_error_reporting(pdev, false);
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Disable Root's interrupt in response to error messages */
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32);
/* Clear Root's error status reg */
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
}
/** /**
* aer_irq - Root Port's ISR * aer_irq - Root Port's ISR
* @irq: IRQ assigned to Root Port * @irq: IRQ assigned to Root Port
* @context: pointer to Root Port data structure * @context: pointer to Root Port data structure
* *
* Invoked when Root Port detects AER messages. * Invoked when Root Port detects AER messages.
**/ */
irqreturn_t aer_irq(int irq, void *context) irqreturn_t aer_irq(int irq, void *context)
{ {
unsigned int status, id; unsigned int status, id;
@ -97,13 +204,13 @@ irqreturn_t aer_irq(int irq, void *context)
/* Read error status */ /* Read error status */
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
if (!(status & ROOT_ERR_STATUS_MASKS)) { if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
spin_unlock_irqrestore(&rpc->e_lock, flags); spin_unlock_irqrestore(&rpc->e_lock, flags);
return IRQ_NONE; return IRQ_NONE;
} }
/* Read error source and clear error status */ /* Read error source and clear error status */
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id); pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
/* Store error source for later DPC handler */ /* Store error source for later DPC handler */
@ -135,7 +242,7 @@ EXPORT_SYMBOL_GPL(aer_irq);
* @dev: pointer to the pcie_dev data structure * @dev: pointer to the pcie_dev data structure
* *
* Invoked when Root Port's AER service is loaded. * Invoked when Root Port's AER service is loaded.
**/ */
static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
{ {
struct aer_rpc *rpc; struct aer_rpc *rpc;
@ -144,15 +251,11 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
if (!rpc) if (!rpc)
return NULL; return NULL;
/* /* Initialize Root lock access, e_lock, to Root Error Status Reg */
* Initialize Root lock access, e_lock, to Root Error Status Reg,
* Root Error ID Reg, and Root error producer/consumer index.
*/
spin_lock_init(&rpc->e_lock); spin_lock_init(&rpc->e_lock);
rpc->rpd = dev; rpc->rpd = dev;
INIT_WORK(&rpc->dpc_handler, aer_isr); INIT_WORK(&rpc->dpc_handler, aer_isr);
rpc->prod_idx = rpc->cons_idx = 0;
mutex_init(&rpc->rpc_mutex); mutex_init(&rpc->rpc_mutex);
init_waitqueue_head(&rpc->wait_release); init_waitqueue_head(&rpc->wait_release);
@ -167,7 +270,7 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
* @dev: pointer to the pcie_dev data structure * @dev: pointer to the pcie_dev data structure
* *
* Invoked when PCI Express bus unloads or AER probe fails. * Invoked when PCI Express bus unloads or AER probe fails.
**/ */
static void aer_remove(struct pcie_device *dev) static void aer_remove(struct pcie_device *dev)
{ {
struct aer_rpc *rpc = get_service_data(dev); struct aer_rpc *rpc = get_service_data(dev);
@ -179,7 +282,8 @@ static void aer_remove(struct pcie_device *dev)
wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
aer_delete_rootport(rpc); aer_disable_rootport(rpc);
kfree(rpc);
set_service_data(dev, NULL); set_service_data(dev, NULL);
} }
} }
@ -190,7 +294,7 @@ static void aer_remove(struct pcie_device *dev)
* @id: pointer to the service id data structure * @id: pointer to the service id data structure
* *
* Invoked when PCI Express bus loads AER service driver. * Invoked when PCI Express bus loads AER service driver.
**/ */
static int __devinit aer_probe(struct pcie_device *dev) static int __devinit aer_probe(struct pcie_device *dev)
{ {
int status; int status;
@ -230,47 +334,30 @@ static int __devinit aer_probe(struct pcie_device *dev)
* @dev: pointer to Root Port's pci_dev data structure * @dev: pointer to Root Port's pci_dev data structure
* *
* Invoked by Port Bus driver when performing link reset at Root Port. * Invoked by Port Bus driver when performing link reset at Root Port.
**/ */
static pci_ers_result_t aer_root_reset(struct pci_dev *dev) static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
{ {
u16 p2p_ctrl; u32 reg32;
u32 status;
int pos; int pos;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
/* Disable Root's interrupt in response to error messages */ /* Disable Root's interrupt in response to error messages */
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0); pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
/* Assert Secondary Bus Reset */ aer_do_secondary_bus_reset(dev);
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
/*
* we should send hot reset message for 2ms to allow it time to
* propogate to all downstream ports
*/
msleep(2);
/* De-assert Secondary Bus Reset */
p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
/*
* System software must wait for at least 100ms from the end
* of a reset of one or more device before it is permitted
* to issue Configuration Requests to those devices.
*/
msleep(200);
dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
/* Clear Root Error Status */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
/* Enable Root Port's interrupt in response to error messages */ /* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(dev, pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
pos + PCI_ERR_ROOT_COMMAND,
ROOT_PORT_INTR_ON_MESG_MASK);
return PCI_ERS_RESULT_RECOVERED; return PCI_ERS_RESULT_RECOVERED;
} }
@ -281,7 +368,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
* @error: error severity being notified by port bus * @error: error severity being notified by port bus
* *
* Invoked by Port Bus driver during error recovery. * Invoked by Port Bus driver during error recovery.
**/ */
static pci_ers_result_t aer_error_detected(struct pci_dev *dev, static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
enum pci_channel_state error) enum pci_channel_state error)
{ {
@ -294,7 +381,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
* @dev: pointer to Root Port's pci_dev data structure * @dev: pointer to Root Port's pci_dev data structure
* *
* Invoked by Port Bus driver during nonfatal recovery. * Invoked by Port Bus driver during nonfatal recovery.
**/ */
static void aer_error_resume(struct pci_dev *dev) static void aer_error_resume(struct pci_dev *dev)
{ {
int pos; int pos;
@ -321,7 +408,7 @@ static void aer_error_resume(struct pci_dev *dev)
* aer_service_init - register AER root service driver * aer_service_init - register AER root service driver
* *
* Invoked when AER root service driver is loaded. * Invoked when AER root service driver is loaded.
**/ */
static int __init aer_service_init(void) static int __init aer_service_init(void)
{ {
if (pcie_aer_disable) if (pcie_aer_disable)
@ -335,7 +422,7 @@ static int __init aer_service_init(void)
* aer_service_exit - unregister AER root service driver * aer_service_exit - unregister AER root service driver
* *
* Invoked when AER root service driver is unloaded. * Invoked when AER root service driver is unloaded.
**/ */
static void __exit aer_service_exit(void) static void __exit aer_service_exit(void)
{ {
pcie_port_service_unregister(&aerdriver); pcie_port_service_unregister(&aerdriver);

Просмотреть файл

@ -17,9 +17,6 @@
#define AER_FATAL 1 #define AER_FATAL 1
#define AER_CORRECTABLE 2 #define AER_CORRECTABLE 2
/* Root Error Status Register Bits */
#define ROOT_ERR_STATUS_MASKS 0x0f
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SENFEE| \
PCI_EXP_RTCTL_SEFEE) PCI_EXP_RTCTL_SEFEE)
@ -117,8 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
} }
extern struct bus_type pcie_port_bus_type; extern struct bus_type pcie_port_bus_type;
extern void aer_enable_rootport(struct aer_rpc *rpc); extern void aer_do_secondary_bus_reset(struct pci_dev *dev);
extern void aer_delete_rootport(struct aer_rpc *rpc);
extern int aer_init(struct pcie_device *dev); extern int aer_init(struct pcie_device *dev);
extern void aer_isr(struct work_struct *work); extern void aer_isr(struct work_struct *work);
extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);

Просмотреть файл

@ -47,13 +47,12 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev)
if (!pos) if (!pos)
return -EIO; return -EIO;
pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16); pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
reg16 = reg16 | reg16 |= (PCI_EXP_DEVCTL_CERE |
PCI_EXP_DEVCTL_CERE |
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_URRE; PCI_EXP_DEVCTL_URRE);
pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
return 0; return 0;
} }
@ -71,12 +70,12 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev)
if (!pos) if (!pos)
return -EIO; return -EIO;
pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16); pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE | reg16 &= ~(PCI_EXP_DEVCTL_CERE |
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_URRE); PCI_EXP_DEVCTL_URRE);
pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
return 0; return 0;
} }
@ -99,99 +98,46 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
} }
EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
static int set_device_error_reporting(struct pci_dev *dev, void *data)
{
bool enable = *((bool *)data);
if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
(dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
(dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
if (enable)
pci_enable_pcie_error_reporting(dev);
else
pci_disable_pcie_error_reporting(dev);
}
if (enable)
pcie_set_ecrc_checking(dev);
return 0;
}
/** /**
* set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. * add_error_device - list device to be handled
* @dev: pointer to root port's pci_dev data structure * @e_info: pointer to error info
* @enable: true = enable error reporting, false = disable error reporting. * @dev: pointer to pci_dev to be added
*/ */
static void set_downstream_devices_error_reporting(struct pci_dev *dev,
bool enable)
{
set_device_error_reporting(dev, &enable);
if (!dev->subordinate)
return;
pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
}
static inline int compare_device_id(struct pci_dev *dev,
struct aer_err_info *e_info)
{
if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) {
/*
* Device ID match
*/
return 1;
}
return 0;
}
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
{ {
if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
e_info->dev[e_info->error_dev_num] = dev; e_info->dev[e_info->error_dev_num] = dev;
e_info->error_dev_num++; e_info->error_dev_num++;
return 1; return 0;
} }
return -ENOSPC;
return 0;
} }
#define PCI_BUS(x) (((x) >> 8) & 0xff) #define PCI_BUS(x) (((x) >> 8) & 0xff)
static int find_device_iter(struct pci_dev *dev, void *data) /**
* is_error_source - check whether the device is source of reported error
* @dev: pointer to pci_dev to be checked
* @e_info: pointer to reported error info
*/
static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
{ {
int pos; int pos;
u32 status; u32 status, mask;
u32 mask;
u16 reg16; u16 reg16;
int result;
struct aer_err_info *e_info = (struct aer_err_info *)data;
/* /*
* When bus id is equal to 0, it might be a bad id * When bus id is equal to 0, it might be a bad id
* reported by root port. * reported by root port.
*/ */
if (!nosourceid && (PCI_BUS(e_info->id) != 0)) { if (!nosourceid && (PCI_BUS(e_info->id) != 0)) {
result = compare_device_id(dev, e_info); /* Device ID match? */
if (result) if (e_info->id == ((dev->bus->number << 8) | dev->devfn))
add_error_device(e_info, dev); return true;
/* /* Continue id comparing if there is no multiple error */
* If there is no multiple error, we stop
* or continue based on the id comparing.
*/
if (!e_info->multi_error_valid) if (!e_info->multi_error_valid)
return result; return false;
/*
* If there are multiple errors and id does match,
* We need continue to search other devices under
* the root port. Return 0 means that.
*/
if (result)
return 0;
} }
/* /*
@ -200,71 +146,94 @@ static int find_device_iter(struct pci_dev *dev, void *data)
* 2) bus id is equal to 0. Some ports might lose the bus * 2) bus id is equal to 0. Some ports might lose the bus
* id of error source id; * id of error source id;
* 3) There are multiple errors and prior id comparing fails; * 3) There are multiple errors and prior id comparing fails;
* We check AER status registers to find the initial reporter. * We check AER status registers to find possible reporter.
*/ */
if (atomic_read(&dev->enable_cnt) == 0) if (atomic_read(&dev->enable_cnt) == 0)
return 0; return false;
pos = pci_pcie_cap(dev); pos = pci_pcie_cap(dev);
if (!pos) if (!pos)
return 0; return false;
/* Check if AER is enabled */ /* Check if AER is enabled */
pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16); pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
if (!(reg16 & ( if (!(reg16 & (
PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_CERE |
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_URRE))) PCI_EXP_DEVCTL_URRE)))
return 0; return false;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos) if (!pos)
return 0; return false;
status = 0; /* Check if error is recorded */
mask = 0;
if (e_info->severity == AER_CORRECTABLE) { if (e_info->severity == AER_CORRECTABLE) {
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask);
if (status & ~mask) {
add_error_device(e_info, dev);
goto added;
}
} else { } else {
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
if (status & ~mask) {
add_error_device(e_info, dev);
goto added;
}
} }
if (status & ~mask)
return true;
return false;
}
static int find_device_iter(struct pci_dev *dev, void *data)
{
struct aer_err_info *e_info = (struct aer_err_info *)data;
if (is_error_source(dev, e_info)) {
/* List this device */
if (add_error_device(e_info, dev)) {
/* We cannot handle more... Stop iteration */
/* TODO: Should print error message here? */
return 1;
}
/* If there is only a single error, stop iteration */
if (!e_info->multi_error_valid)
return 1;
}
return 0; return 0;
added:
if (e_info->multi_error_valid)
return 0;
else
return 1;
} }
/** /**
* find_source_device - search through device hierarchy for source device * find_source_device - search through device hierarchy for source device
* @parent: pointer to Root Port pci_dev data structure * @parent: pointer to Root Port pci_dev data structure
* @err_info: including detailed error information such like id * @e_info: including detailed error information such like id
* *
* Invoked when error is detected at the Root Port. * Return true if found.
*
* Invoked by DPC when error is detected at the Root Port.
* Caller of this function must set id, severity, and multi_error_valid of
* struct aer_err_info pointed by @e_info properly. This function must fill
* e_info->error_dev_num and e_info->dev[], based on the given information.
*/ */
static void find_source_device(struct pci_dev *parent, static bool find_source_device(struct pci_dev *parent,
struct aer_err_info *e_info) struct aer_err_info *e_info)
{ {
struct pci_dev *dev = parent; struct pci_dev *dev = parent;
int result; int result;
/* Must reset in this function */
e_info->error_dev_num = 0;
/* Is Root Port an agent that sends error message? */ /* Is Root Port an agent that sends error message? */
result = find_device_iter(dev, e_info); result = find_device_iter(dev, e_info);
if (result) if (result)
return; return true;
pci_walk_bus(parent->subordinate, find_device_iter, e_info); pci_walk_bus(parent->subordinate, find_device_iter, e_info);
if (!e_info->error_dev_num) {
dev_printk(KERN_DEBUG, &parent->dev,
"can't find device of ID%04x\n",
e_info->id);
return false;
}
return true;
} }
static int report_error_detected(struct pci_dev *dev, void *data) static int report_error_detected(struct pci_dev *dev, void *data)
@ -403,43 +372,77 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
return result_data.result; return result_data.result;
} }
struct find_aer_service_data { /**
struct pcie_port_service_driver *aer_driver; * aer_do_secondary_bus_reset - perform secondary bus reset
int is_downstream; * @dev: pointer to bridge's pci_dev data structure
}; *
* Invoked when performing link reset at Root Port or Downstream Port.
*/
void aer_do_secondary_bus_reset(struct pci_dev *dev)
{
u16 p2p_ctrl;
/* Assert Secondary Bus Reset */
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
/*
* we should send hot reset message for 2ms to allow it time to
* propagate to all downstream ports
*/
msleep(2);
/* De-assert Secondary Bus Reset */
p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
/*
* System software must wait for at least 100ms from the end
* of a reset of one or more device before it is permitted
* to issue Configuration Requests to those devices.
*/
msleep(200);
}
/**
* default_downstream_reset_link - default reset function for Downstream Port
* @dev: pointer to downstream port's pci_dev data structure
*
* Invoked when performing link reset at Downstream Port w/ no aer driver.
*/
static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev)
{
aer_do_secondary_bus_reset(dev);
dev_printk(KERN_DEBUG, &dev->dev,
"Downstream Port link has been reset\n");
return PCI_ERS_RESULT_RECOVERED;
}
static int find_aer_service_iter(struct device *device, void *data) static int find_aer_service_iter(struct device *device, void *data)
{ {
struct device_driver *driver; struct pcie_port_service_driver *service_driver, **drv;
struct pcie_port_service_driver *service_driver;
struct find_aer_service_data *result;
result = (struct find_aer_service_data *) data; drv = (struct pcie_port_service_driver **) data;
if (device->bus == &pcie_port_bus_type) { if (device->bus == &pcie_port_bus_type && device->driver) {
struct pcie_device *pcie = to_pcie_device(device); service_driver = to_service_driver(device->driver);
if (service_driver->service == PCIE_PORT_SERVICE_AER) {
if (pcie->port->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) *drv = service_driver;
result->is_downstream = 1; return 1;
driver = device->driver;
if (driver) {
service_driver = to_service_driver(driver);
if (service_driver->service == PCIE_PORT_SERVICE_AER) {
result->aer_driver = service_driver;
return 1;
}
} }
} }
return 0; return 0;
} }
static void find_aer_service(struct pci_dev *dev, static struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev)
struct find_aer_service_data *data)
{ {
int retval; struct pcie_port_service_driver *drv = NULL;
retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
device_for_each_child(&dev->dev, &drv, find_aer_service_iter);
return drv;
} }
static pci_ers_result_t reset_link(struct pcie_device *aerdev, static pci_ers_result_t reset_link(struct pcie_device *aerdev,
@ -447,38 +450,34 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
{ {
struct pci_dev *udev; struct pci_dev *udev;
pci_ers_result_t status; pci_ers_result_t status;
struct find_aer_service_data data; struct pcie_port_service_driver *driver;
if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
/* Reset this port for all subordinates */
udev = dev; udev = dev;
else } else {
/* Reset the upstream component (likely downstream port) */
udev = dev->bus->self; udev = dev->bus->self;
data.is_downstream = 0;
data.aer_driver = NULL;
find_aer_service(udev, &data);
/*
* Use the aer driver of the error agent firstly.
* If it hasn't the aer driver, use the root port's
*/
if (!data.aer_driver || !data.aer_driver->reset_link) {
if (data.is_downstream &&
aerdev->device.driver &&
to_service_driver(aerdev->device.driver)->reset_link) {
data.aer_driver =
to_service_driver(aerdev->device.driver);
} else {
dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
"support\n");
return PCI_ERS_RESULT_DISCONNECT;
}
} }
status = data.aer_driver->reset_link(udev); /* Use the aer driver of the component firstly */
driver = find_aer_service(udev);
if (driver && driver->reset_link) {
status = driver->reset_link(udev);
} else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
status = default_downstream_reset_link(udev);
} else {
dev_printk(KERN_DEBUG, &dev->dev,
"no link-reset support at upstream device %s\n",
pci_name(udev));
return PCI_ERS_RESULT_DISCONNECT;
}
if (status != PCI_ERS_RESULT_RECOVERED) { if (status != PCI_ERS_RESULT_RECOVERED) {
dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream " dev_printk(KERN_DEBUG, &dev->dev,
"device %s failed\n", pci_name(udev)); "link reset at upstream device %s failed\n",
pci_name(udev));
return PCI_ERS_RESULT_DISCONNECT; return PCI_ERS_RESULT_DISCONNECT;
} }
@ -495,8 +494,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
* error detected message to all downstream drivers within a hierarchy in * error detected message to all downstream drivers within a hierarchy in
* question and return the returned code. * question and return the returned code.
*/ */
static pci_ers_result_t do_recovery(struct pcie_device *aerdev, static void do_recovery(struct pcie_device *aerdev, struct pci_dev *dev,
struct pci_dev *dev,
int severity) int severity)
{ {
pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
@ -514,10 +512,8 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
if (severity == AER_FATAL) { if (severity == AER_FATAL) {
result = reset_link(aerdev, dev); result = reset_link(aerdev, dev);
if (result != PCI_ERS_RESULT_RECOVERED) { if (result != PCI_ERS_RESULT_RECOVERED)
/* TODO: Should panic here? */ goto failed;
return result;
}
} }
if (status == PCI_ERS_RESULT_CAN_RECOVER) if (status == PCI_ERS_RESULT_CAN_RECOVER)
@ -538,13 +534,22 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
report_slot_reset); report_slot_reset);
} }
if (status == PCI_ERS_RESULT_RECOVERED) if (status != PCI_ERS_RESULT_RECOVERED)
broadcast_error_message(dev, goto failed;
broadcast_error_message(dev,
state, state,
"resume", "resume",
report_resume); report_resume);
return status; dev_printk(KERN_DEBUG, &dev->dev,
"AER driver successfully recovered\n");
return;
failed:
/* TODO: Should kernel panic here? */
dev_printk(KERN_DEBUG, &dev->dev,
"AER driver didn't recover\n");
} }
/** /**
@ -559,7 +564,6 @@ static void handle_error_source(struct pcie_device *aerdev,
struct pci_dev *dev, struct pci_dev *dev,
struct aer_err_info *info) struct aer_err_info *info)
{ {
pci_ers_result_t status = 0;
int pos; int pos;
if (info->severity == AER_CORRECTABLE) { if (info->severity == AER_CORRECTABLE) {
@ -571,114 +575,8 @@ static void handle_error_source(struct pcie_device *aerdev,
if (pos) if (pos)
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
info->status); info->status);
} else { } else
status = do_recovery(aerdev, dev, info->severity); do_recovery(aerdev, dev, info->severity);
if (status == PCI_ERS_RESULT_RECOVERED) {
dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
"successfully recovered\n");
} else {
/* TODO: Should kernel panic here? */
dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
"recover\n");
}
}
}
/**
* aer_enable_rootport - enable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
*
* Invoked when PCIe bus loads AER service driver.
*/
void aer_enable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
int pos, aer_pos;
u16 reg16;
u32 reg32;
pos = pci_pcie_cap(pdev);
/* Clear PCIe Capability's Device Status */
pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
/* Disable system error generation in response to error messages */
pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Clear error status */
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
/*
* Enable error reporting for the root port device and downstream port
* devices.
*/
set_downstream_devices_error_reporting(pdev, true);
/* Enable Root Port's interrupt in response to error messages */
pci_write_config_dword(pdev,
aer_pos + PCI_ERR_ROOT_COMMAND,
ROOT_PORT_INTR_ON_MESG_MASK);
}
/**
* disable_root_aer - disable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
*
* Invoked when PCIe bus unloads AER service driver.
*/
static void disable_root_aer(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
u32 reg32;
int pos;
/*
* Disable error reporting for the root port device and downstream port
* devices.
*/
set_downstream_devices_error_reporting(pdev, false);
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Disable Root's interrupt in response to error messages */
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
/* Clear Root's error status reg */
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
}
/**
* get_e_source - retrieve an error source
* @rpc: pointer to the root port which holds an error
*
* Invoked by DPC handler to consume an error.
*/
static struct aer_err_source *get_e_source(struct aer_rpc *rpc)
{
struct aer_err_source *e_source;
unsigned long flags;
/* Lock access to Root error producer/consumer index */
spin_lock_irqsave(&rpc->e_lock, flags);
if (rpc->prod_idx == rpc->cons_idx) {
spin_unlock_irqrestore(&rpc->e_lock, flags);
return NULL;
}
e_source = &rpc->e_sources[rpc->cons_idx];
rpc->cons_idx++;
if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
rpc->cons_idx = 0;
spin_unlock_irqrestore(&rpc->e_lock, flags);
return e_source;
} }
/** /**
@ -687,11 +585,14 @@ static struct aer_err_source *get_e_source(struct aer_rpc *rpc)
* @info: pointer to structure to store the error record * @info: pointer to structure to store the error record
* *
* Return 1 on success, 0 on error. * Return 1 on success, 0 on error.
*
* Note that @info is reused among all error devices. Clear fields properly.
*/ */
static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
{ {
int pos, temp; int pos, temp;
/* Must reset in this function */
info->status = 0; info->status = 0;
info->tlp_header_valid = 0; info->tlp_header_valid = 0;
@ -744,12 +645,6 @@ static inline void aer_process_err_devices(struct pcie_device *p_device,
{ {
int i; int i;
if (!e_info->dev[0]) {
dev_printk(KERN_DEBUG, &p_device->port->dev,
"can't find device of ID%04x\n",
e_info->id);
}
/* Report all before handle them, not to lost records by reset etc. */ /* Report all before handle them, not to lost records by reset etc. */
for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
if (get_device_error_info(e_info->dev[i], e_info)) if (get_device_error_info(e_info->dev[i], e_info))
@ -770,11 +665,10 @@ static void aer_isr_one_error(struct pcie_device *p_device,
struct aer_err_source *e_src) struct aer_err_source *e_src)
{ {
struct aer_err_info *e_info; struct aer_err_info *e_info;
int i;
/* struct aer_err_info might be big, so we allocate it with slab */ /* struct aer_err_info might be big, so we allocate it with slab */
e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL); e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL);
if (e_info == NULL) { if (!e_info) {
dev_printk(KERN_DEBUG, &p_device->port->dev, dev_printk(KERN_DEBUG, &p_device->port->dev,
"Can't allocate mem when processing AER errors\n"); "Can't allocate mem when processing AER errors\n");
return; return;
@ -784,36 +678,71 @@ static void aer_isr_one_error(struct pcie_device *p_device,
* There is a possibility that both correctable error and * There is a possibility that both correctable error and
* uncorrectable error being logged. Report correctable error first. * uncorrectable error being logged. Report correctable error first.
*/ */
for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
if (i > 4) e_info->id = ERR_COR_ID(e_src->id);
break; e_info->severity = AER_CORRECTABLE;
if (!(e_src->status & i))
continue;
memset(e_info, 0, sizeof(struct aer_err_info)); if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
/* Init comprehensive error information */
if (i & PCI_ERR_ROOT_COR_RCV) {
e_info->id = ERR_COR_ID(e_src->id);
e_info->severity = AER_CORRECTABLE;
} else {
e_info->id = ERR_UNCOR_ID(e_src->id);
e_info->severity = ((e_src->status >> 6) & 1);
}
if (e_src->status &
(PCI_ERR_ROOT_MULTI_COR_RCV |
PCI_ERR_ROOT_MULTI_UNCOR_RCV))
e_info->multi_error_valid = 1; e_info->multi_error_valid = 1;
else
e_info->multi_error_valid = 0;
aer_print_port_info(p_device->port, e_info); aer_print_port_info(p_device->port, e_info);
find_source_device(p_device->port, e_info); if (find_source_device(p_device->port, e_info))
aer_process_err_devices(p_device, e_info); aer_process_err_devices(p_device, e_info);
}
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
e_info->id = ERR_UNCOR_ID(e_src->id);
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
e_info->severity = AER_FATAL;
else
e_info->severity = AER_NONFATAL;
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
e_info->multi_error_valid = 1;
else
e_info->multi_error_valid = 0;
aer_print_port_info(p_device->port, e_info);
if (find_source_device(p_device->port, e_info))
aer_process_err_devices(p_device, e_info);
} }
kfree(e_info); kfree(e_info);
} }
/**
* get_e_source - retrieve an error source
* @rpc: pointer to the root port which holds an error
* @e_src: pointer to store retrieved error source
*
* Return 1 if an error source is retrieved, otherwise 0.
*
* Invoked by DPC handler to consume an error.
*/
static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src)
{
unsigned long flags;
int ret = 0;
/* Lock access to Root error producer/consumer index */
spin_lock_irqsave(&rpc->e_lock, flags);
if (rpc->prod_idx != rpc->cons_idx) {
*e_src = rpc->e_sources[rpc->cons_idx];
rpc->cons_idx++;
if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
rpc->cons_idx = 0;
ret = 1;
}
spin_unlock_irqrestore(&rpc->e_lock, flags);
return ret;
}
/** /**
* aer_isr - consume errors detected by root port * aer_isr - consume errors detected by root port
* @work: definition of this work item * @work: definition of this work item
@ -824,33 +753,16 @@ void aer_isr(struct work_struct *work)
{ {
struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
struct pcie_device *p_device = rpc->rpd; struct pcie_device *p_device = rpc->rpd;
struct aer_err_source *e_src; struct aer_err_source e_src;
mutex_lock(&rpc->rpc_mutex); mutex_lock(&rpc->rpc_mutex);
e_src = get_e_source(rpc); while (get_e_source(rpc, &e_src))
while (e_src) { aer_isr_one_error(p_device, &e_src);
aer_isr_one_error(p_device, e_src);
e_src = get_e_source(rpc);
}
mutex_unlock(&rpc->rpc_mutex); mutex_unlock(&rpc->rpc_mutex);
wake_up(&rpc->wait_release); wake_up(&rpc->wait_release);
} }
/**
* aer_delete_rootport - disable root port aer and delete service data
* @rpc: pointer to a root port device being deleted
*
* Invoked when AER service unloaded on a specific Root Port
*/
void aer_delete_rootport(struct aer_rpc *rpc)
{
/* Disable root port AER itself */
disable_root_aer(rpc);
kfree(rpc);
}
/** /**
* aer_init - provide AER initialization * aer_init - provide AER initialization
* @dev: pointer to AER pcie device * @dev: pointer to AER pcie device

Просмотреть файл

@ -2127,6 +2127,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x9602, quirk_disable_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ASUSTEK, 0x9602, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ASUSTEK, 0x9602, quirk_disable_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AI, 0x9602, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AI, 0x9602, quirk_disable_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0xa238, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0xa238, quirk_disable_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x5a3f, quirk_disable_msi);
/* Go through the list of Hypertransport capabilities and /* Go through the list of Hypertransport capabilities and
* return 1 if a HT MSI capability is found and enabled */ * return 1 if a HT MSI capability is found and enabled */
@ -2218,15 +2219,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS,
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE,
ht_enable_msi_mapping); ht_enable_msi_mapping);
/* The P5N32-SLI Premium motherboard from Asus has a problem with msi /* The P5N32-SLI motherboards from Asus have a problem with msi
* for the MCP55 NIC. It is not yet determined whether the msi problem * for the MCP55 NIC. It is not yet determined whether the msi problem
* also affects other devices. As for now, turn off msi for this device. * also affects other devices. As for now, turn off msi for this device.
*/ */
static void __devinit nvenet_msi_disable(struct pci_dev *dev) static void __devinit nvenet_msi_disable(struct pci_dev *dev)
{ {
if (dmi_name_in_vendors("P5N32-SLI PREMIUM")) { if (dmi_name_in_vendors("P5N32-SLI PREMIUM") ||
dmi_name_in_vendors("P5N32-E SLI")) {
dev_info(&dev->dev, dev_info(&dev->dev,
"Disabling msi for MCP55 NIC on P5N32-SLI Premium\n"); "Disabling msi for MCP55 NIC on P5N32-SLI\n");
dev->no_msi = 1; dev->no_msi = 1;
} }
} }
@ -2552,6 +2554,19 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov);
#endif /* CONFIG_PCI_IOV */ #endif /* CONFIG_PCI_IOV */
/* Allow manual resource allocation for PCI hotplug bridges
* via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For
* some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6),
* kernel fails to allocate resources when hotplug device is
* inserted and PCI bus is rescanned.
*/
static void __devinit quirk_hotplug_bridge(struct pci_dev *dev)
{
dev->is_hotplug_bridge = 1;
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0020, quirk_hotplug_bridge);
/* /*
* This is a quirk for the Ricoh MMC controller found as a part of * This is a quirk for the Ricoh MMC controller found as a part of
* some mulifunction chips. * some mulifunction chips.

Просмотреть файл

@ -97,6 +97,50 @@ static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf)
return bus_speed_read(slot->bus->cur_bus_speed, buf); return bus_speed_read(slot->bus->cur_bus_speed, buf);
} }
static void remove_sysfs_files(struct pci_slot *slot)
{
char func[10];
struct list_head *tmp;
list_for_each(tmp, &slot->bus->devices) {
struct pci_dev *dev = pci_dev_b(tmp);
if (PCI_SLOT(dev->devfn) != slot->number)
continue;
sysfs_remove_link(&dev->dev.kobj, "slot");
snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn));
sysfs_remove_link(&slot->kobj, func);
}
}
static int create_sysfs_files(struct pci_slot *slot)
{
int result;
char func[10];
struct list_head *tmp;
list_for_each(tmp, &slot->bus->devices) {
struct pci_dev *dev = pci_dev_b(tmp);
if (PCI_SLOT(dev->devfn) != slot->number)
continue;
result = sysfs_create_link(&dev->dev.kobj, &slot->kobj, "slot");
if (result)
goto fail;
snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn));
result = sysfs_create_link(&slot->kobj, &dev->dev.kobj, func);
if (result)
goto fail;
}
return 0;
fail:
remove_sysfs_files(slot);
return result;
}
static void pci_slot_release(struct kobject *kobj) static void pci_slot_release(struct kobject *kobj)
{ {
struct pci_dev *dev; struct pci_dev *dev;
@ -109,6 +153,8 @@ static void pci_slot_release(struct kobject *kobj)
if (PCI_SLOT(dev->devfn) == slot->number) if (PCI_SLOT(dev->devfn) == slot->number)
dev->slot = NULL; dev->slot = NULL;
remove_sysfs_files(slot);
list_del(&slot->list); list_del(&slot->list);
kfree(slot); kfree(slot);
@ -300,6 +346,8 @@ placeholder:
INIT_LIST_HEAD(&slot->list); INIT_LIST_HEAD(&slot->list);
list_add(&slot->list, &parent->slots); list_add(&slot->list, &parent->slots);
create_sysfs_files(slot);
list_for_each_entry(dev, &parent->devices, bus_list) list_for_each_entry(dev, &parent->devices, bus_list)
if (PCI_SLOT(dev->devfn) == slot_nr) if (PCI_SLOT(dev->devfn) == slot_nr)
dev->slot = slot; dev->slot = slot;

Просмотреть файл

@ -52,6 +52,7 @@ struct resource_list {
#define IORESOURCE_MEM_64 0x00100000 #define IORESOURCE_MEM_64 0x00100000
#define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */ #define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */
#define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */
#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
#define IORESOURCE_DISABLED 0x10000000 #define IORESOURCE_DISABLED 0x10000000
@ -143,7 +144,8 @@ static inline unsigned long resource_type(const struct resource *res)
} }
/* Convenience shorthand with allocation */ /* Convenience shorthand with allocation */
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0) #define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), IORESOURCE_MUXED)
#define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl) #define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl)
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0) #define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
#define request_mem_region_exclusive(start,n,name) \ #define request_mem_region_exclusive(start,n,name) \

Просмотреть файл

@ -2419,8 +2419,8 @@
#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30
#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 #define PCI_DEVICE_ID_INTEL_IOAT 0x1a38
#define PCI_DEVICE_ID_INTEL_CPT_SMBUS 0x1c22 #define PCI_DEVICE_ID_INTEL_CPT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_CPT_LPC1 0x1c42 #define PCI_DEVICE_ID_INTEL_CPT_LPC_MIN 0x1c41
#define PCI_DEVICE_ID_INTEL_CPT_LPC2 0x1c43 #define PCI_DEVICE_ID_INTEL_CPT_LPC_MAX 0x1c5f
#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410
#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411
#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413

Просмотреть файл

@ -566,8 +566,7 @@
#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ #define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */
#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ #define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
#define PCI_ERR_ROOT_COR_SRC 52 #define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
#define PCI_ERR_ROOT_SRC 54
/* Virtual Channel */ /* Virtual Channel */
#define PCI_VC_PORT_REG1 4 #define PCI_VC_PORT_REG1 4

Просмотреть файл

@ -15,6 +15,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/pfn.h> #include <linux/pfn.h>
@ -681,6 +682,8 @@ resource_size_t resource_alignment(struct resource *res)
* release_region releases a matching busy region. * release_region releases a matching busy region.
*/ */
static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait);
/** /**
* __request_region - create a new busy resource region * __request_region - create a new busy resource region
* @parent: parent resource descriptor * @parent: parent resource descriptor
@ -693,6 +696,7 @@ struct resource * __request_region(struct resource *parent,
resource_size_t start, resource_size_t n, resource_size_t start, resource_size_t n,
const char *name, int flags) const char *name, int flags)
{ {
DECLARE_WAITQUEUE(wait, current);
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
if (!res) if (!res)
@ -717,7 +721,15 @@ struct resource * __request_region(struct resource *parent,
if (!(conflict->flags & IORESOURCE_BUSY)) if (!(conflict->flags & IORESOURCE_BUSY))
continue; continue;
} }
if (conflict->flags & flags & IORESOURCE_MUXED) {
add_wait_queue(&muxed_resource_wait, &wait);
write_unlock(&resource_lock);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
remove_wait_queue(&muxed_resource_wait, &wait);
write_lock(&resource_lock);
continue;
}
/* Uhhuh, that didn't work out.. */ /* Uhhuh, that didn't work out.. */
kfree(res); kfree(res);
res = NULL; res = NULL;
@ -791,6 +803,8 @@ void __release_region(struct resource *parent, resource_size_t start,
break; break;
*p = res->sibling; *p = res->sibling;
write_unlock(&resource_lock); write_unlock(&resource_lock);
if (res->flags & IORESOURCE_MUXED)
wake_up(&muxed_resource_wait);
kfree(res); kfree(res);
return; return;
} }