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

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6:
  PCI: AMD 813x B2 devices do not need boot interrupt quirk
  PCI: Enable PCIe AER only after checking firmware support
  PCI: pciehp: Handle interrupts that happen during initialization.
  PCI: don't enable too many HT MSI mappings
  PCI: add some sysfs ABI docs
  PCI quirk: enable MSI on 8132
This commit is contained in:
Linus Torvalds 2009-02-26 14:43:42 -08:00
commit 4bdc1b9650
7 changed files with 204 additions and 35 deletions

View File

@ -1,3 +1,46 @@
What: /sys/bus/pci/drivers/.../bind
Date: December 2003
Contact: linux-pci@vger.kernel.org
Description:
Writing a device location to this file will cause
the driver to attempt to bind to the device found at
this location. This is useful for overriding default
bindings. The format for the location is: DDDD:BB:DD.F.
That is Domain:Bus:Device.Function and is the same as
found in /sys/bus/pci/devices/. For example:
# echo 0000:00:19.0 > /sys/bus/pci/drivers/foo/bind
(Note: kernels before 2.6.28 may require echo -n).
What: /sys/bus/pci/drivers/.../unbind
Date: December 2003
Contact: linux-pci@vger.kernel.org
Description:
Writing a device location to this file will cause the
driver to attempt to unbind from the device found at
this location. This may be useful when overriding default
bindings. The format for the location is: DDDD:BB:DD.F.
That is Domain:Bus:Device.Function and is the same as
found in /sys/bus/pci/devices/. For example:
# echo 0000:00:19.0 > /sys/bus/pci/drivers/foo/unbind
(Note: kernels before 2.6.28 may require echo -n).
What: /sys/bus/pci/drivers/.../new_id
Date: December 2003
Contact: linux-pci@vger.kernel.org
Description:
Writing a device ID to this file will attempt to
dynamically add a new device ID to a PCI device driver.
This may allow the driver to support more hardware than
was included in the driver's static device ID support
table at compile time. The format for the device ID is:
VVVV DDDD SVVV SDDD CCCC MMMM PPPP. That is Vendor ID,
Device ID, Subsystem Vendor ID, Subsystem Device ID,
Class, Class Mask, and Private Driver Data. The Vendor ID
and Device ID fields are required, the rest are optional.
Upon successfully adding an ID, the driver will probe
for the device and attempt to bind to it. For example:
# echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id
What: /sys/bus/pci/devices/.../vpd What: /sys/bus/pci/devices/.../vpd
Date: February 2008 Date: February 2008
Contact: Ben Hutchings <bhutchings@solarflare.com> Contact: Ben Hutchings <bhutchings@solarflare.com>

View File

@ -111,6 +111,7 @@ struct controller {
int cmd_busy; int cmd_busy;
unsigned int no_cmd_complete:1; unsigned int no_cmd_complete:1;
unsigned int link_active_reporting:1; unsigned int link_active_reporting:1;
unsigned int notification_enabled:1;
}; };
#define INT_BUTTON_IGNORE 0 #define INT_BUTTON_IGNORE 0
@ -170,6 +171,7 @@ extern int pciehp_configure_device(struct slot *p_slot);
extern int pciehp_unconfigure_device(struct slot *p_slot); extern int pciehp_unconfigure_device(struct slot *p_slot);
extern void pciehp_queue_pushbutton_work(struct work_struct *work); extern void pciehp_queue_pushbutton_work(struct work_struct *work);
struct controller *pcie_init(struct pcie_device *dev); struct controller *pcie_init(struct pcie_device *dev);
int pcie_init_notification(struct controller *ctrl);
int pciehp_enable_slot(struct slot *p_slot); int pciehp_enable_slot(struct slot *p_slot);
int pciehp_disable_slot(struct slot *p_slot); int pciehp_disable_slot(struct slot *p_slot);
int pcie_enable_notification(struct controller *ctrl); int pcie_enable_notification(struct controller *ctrl);

View File

@ -434,6 +434,13 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
goto err_out_release_ctlr; goto err_out_release_ctlr;
} }
/* Enable events after we have setup the data structures */
rc = pcie_init_notification(ctrl);
if (rc) {
ctrl_err(ctrl, "Notification initialization failed\n");
goto err_out_release_ctlr;
}
/* Check if slot is occupied */ /* Check if slot is occupied */
t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
t_slot->hpc_ops->get_adapter_status(t_slot, &value); t_slot->hpc_ops->get_adapter_status(t_slot, &value);

View File

@ -934,7 +934,7 @@ static void pcie_disable_notification(struct controller *ctrl)
ctrl_warn(ctrl, "Cannot disable software notification\n"); ctrl_warn(ctrl, "Cannot disable software notification\n");
} }
static int pcie_init_notification(struct controller *ctrl) int pcie_init_notification(struct controller *ctrl)
{ {
if (pciehp_request_irq(ctrl)) if (pciehp_request_irq(ctrl))
return -1; return -1;
@ -942,13 +942,17 @@ static int pcie_init_notification(struct controller *ctrl)
pciehp_free_irq(ctrl); pciehp_free_irq(ctrl);
return -1; return -1;
} }
ctrl->notification_enabled = 1;
return 0; return 0;
} }
static void pcie_shutdown_notification(struct controller *ctrl) static void pcie_shutdown_notification(struct controller *ctrl)
{ {
pcie_disable_notification(ctrl); if (ctrl->notification_enabled) {
pciehp_free_irq(ctrl); pcie_disable_notification(ctrl);
pciehp_free_irq(ctrl);
ctrl->notification_enabled = 0;
}
} }
static int pcie_init_slot(struct controller *ctrl) static int pcie_init_slot(struct controller *ctrl)
@ -1110,13 +1114,8 @@ struct controller *pcie_init(struct pcie_device *dev)
if (pcie_init_slot(ctrl)) if (pcie_init_slot(ctrl))
goto abort_ctrl; goto abort_ctrl;
if (pcie_init_notification(ctrl))
goto abort_slot;
return ctrl; return ctrl;
abort_slot:
pcie_cleanup_slot(ctrl);
abort_ctrl: abort_ctrl:
kfree(ctrl); kfree(ctrl);
abort: abort:

View File

@ -108,6 +108,34 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
} }
#endif /* 0 */ #endif /* 0 */
static void set_device_error_reporting(struct pci_dev *dev, void *data)
{
bool enable = *((bool *)data);
if (dev->pcie_type != PCIE_RC_PORT &&
dev->pcie_type != PCIE_SW_UPSTREAM_PORT &&
dev->pcie_type != PCIE_SW_DOWNSTREAM_PORT)
return;
if (enable)
pci_enable_pcie_error_reporting(dev);
else
pci_disable_pcie_error_reporting(dev);
}
/**
* 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);
pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
}
static int find_device_iter(struct device *device, void *data) static int find_device_iter(struct device *device, void *data)
{ {
struct pci_dev *dev; struct pci_dev *dev;
@ -525,15 +553,11 @@ void aer_enable_rootport(struct aer_rpc *rpc)
pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_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); pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
/* Enable Root Port device reporting error itself */ /*
pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16); * Enable error reporting for the root port device and downstream port
reg16 = reg16 | * devices.
PCI_EXP_DEVCTL_CERE | */
PCI_EXP_DEVCTL_NFERE | set_downstream_devices_error_reporting(pdev, true);
PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_URRE;
pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
reg16);
/* Enable Root Port's interrupt in response to error messages */ /* Enable Root Port's interrupt in response to error messages */
pci_write_config_dword(pdev, pci_write_config_dword(pdev,
@ -553,6 +577,12 @@ static void disable_root_aer(struct aer_rpc *rpc)
u32 reg32; u32 reg32;
int pos; 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); pos = pci_find_ext_capability(pdev, 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(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);

View File

@ -97,8 +97,6 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
pcie_portdrv_save_config(dev); pcie_portdrv_save_config(dev);
pci_enable_pcie_error_reporting(dev);
return 0; return 0;
} }

View File

@ -1584,6 +1584,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_
*/ */
#define AMD_813X_MISC 0x40 #define AMD_813X_MISC 0x40
#define AMD_813X_NOIOAMODE (1<<0) #define AMD_813X_NOIOAMODE (1<<0)
#define AMD_813X_REV_B2 0x13
static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev) static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev)
{ {
@ -1591,6 +1592,8 @@ static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev)
if (noioapicquirk) if (noioapicquirk)
return; return;
if (dev->revision == AMD_813X_REV_B2)
return;
pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword); pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword);
pci_config_dword &= ~AMD_813X_NOIOAMODE; pci_config_dword &= ~AMD_813X_NOIOAMODE;
@ -1981,7 +1984,6 @@ static void __devinit quirk_msi_ht_cap(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE, DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
quirk_msi_ht_cap); quirk_msi_ht_cap);
/* The nVidia CK804 chipset may have 2 HT MSI mappings. /* The nVidia CK804 chipset may have 2 HT MSI mappings.
* MSI are supported if the MSI capability set in any of these mappings. * MSI are supported if the MSI capability set in any of these mappings.
*/ */
@ -2032,6 +2034,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB, PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB,
ht_enable_msi_mapping); ht_enable_msi_mapping);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE,
ht_enable_msi_mapping);
/* The P5N32-SLI Premium motherboard from Asus has a problem with msi /* The P5N32-SLI Premium motherboard from Asus has 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.
@ -2048,10 +2053,100 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
PCI_DEVICE_ID_NVIDIA_NVENET_15, PCI_DEVICE_ID_NVIDIA_NVENET_15,
nvenet_msi_disable); nvenet_msi_disable);
static void __devinit nv_ht_enable_msi_mapping(struct pci_dev *dev)
{
struct pci_dev *host_bridge;
int pos;
int i, dev_no;
int found = 0;
dev_no = dev->devfn >> 3;
for (i = dev_no; i >= 0; i--) {
host_bridge = pci_get_slot(dev->bus, PCI_DEVFN(i, 0));
if (!host_bridge)
continue;
pos = pci_find_ht_capability(host_bridge, HT_CAPTYPE_SLAVE);
if (pos != 0) {
found = 1;
break;
}
pci_dev_put(host_bridge);
}
if (!found)
return;
/* root did that ! */
if (msi_ht_cap_enabled(host_bridge))
goto out;
ht_enable_msi_mapping(dev);
out:
pci_dev_put(host_bridge);
}
static void __devinit ht_disable_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING);
while (pos && ttl--) {
u8 flags;
if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
&flags) == 0) {
dev_info(&dev->dev, "Enabling HT MSI Mapping\n");
pci_write_config_byte(dev, pos + HT_MSI_FLAGS,
flags & ~HT_MSI_FLAGS_ENABLE);
}
pos = pci_find_next_ht_capability(dev, pos,
HT_CAPTYPE_MSI_MAPPING);
}
}
static int __devinit ht_check_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
int found = 0;
/* check if there is HT MSI cap or enabled on this device */
pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING);
while (pos && ttl--) {
u8 flags;
if (found < 1)
found = 1;
if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
&flags) == 0) {
if (flags & HT_MSI_FLAGS_ENABLE) {
if (found < 2) {
found = 2;
break;
}
}
}
pos = pci_find_next_ht_capability(dev, pos,
HT_CAPTYPE_MSI_MAPPING);
}
return found;
}
static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev)
{ {
struct pci_dev *host_bridge; struct pci_dev *host_bridge;
int pos, ttl = 48; int pos;
int found;
/* check if there is HT MSI cap or enabled on this device */
found = ht_check_msi_mapping(dev);
/* no HT MSI CAP */
if (found == 0)
return;
/* /*
* HT MSI mapping should be disabled on devices that are below * HT MSI mapping should be disabled on devices that are below
@ -2067,24 +2162,19 @@ static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev)
pos = pci_find_ht_capability(host_bridge, HT_CAPTYPE_SLAVE); pos = pci_find_ht_capability(host_bridge, HT_CAPTYPE_SLAVE);
if (pos != 0) { if (pos != 0) {
/* Host bridge is to HT */ /* Host bridge is to HT */
ht_enable_msi_mapping(dev); if (found == 1) {
/* it is not enabled, try to enable it */
nv_ht_enable_msi_mapping(dev);
}
return; return;
} }
/* Host bridge is not to HT, disable HT MSI mapping on this device */ /* HT MSI is not enabled */
pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING); if (found == 1)
while (pos && ttl--) { return;
u8 flags;
if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS, /* Host bridge is not to HT, disable HT MSI mapping on this device */
&flags) == 0) { ht_disable_msi_mapping(dev);
dev_info(&dev->dev, "Disabling HT MSI mapping");
pci_write_config_byte(dev, pos + HT_MSI_FLAGS,
flags & ~HT_MSI_FLAGS_ENABLE);
}
pos = pci_find_next_ht_capability(dev, pos,
HT_CAPTYPE_MSI_MAPPING);
}
} }
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk);