mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 05:34:13 +08:00
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: (74 commits) PCI: make msi_free_irqs() to use msix_mask_irq() instead of open coded write PCI: Fix the NIU MSI-X problem in a better way PCI ASPM: remove get_root_port_link PCI ASPM: cleanup pcie_aspm_sanity_check PCI ASPM: remove has_switch field PCI ASPM: cleanup calc_Lx_latency PCI ASPM: cleanup pcie_aspm_get_cap_device PCI ASPM: cleanup clkpm checks PCI ASPM: cleanup __pcie_aspm_check_state_one PCI ASPM: cleanup initialization PCI ASPM: cleanup change input argument of aspm functions PCI ASPM: cleanup misc in struct pcie_link_state PCI ASPM: cleanup clkpm state in struct pcie_link_state PCI ASPM: cleanup latency field in struct pcie_link_state PCI ASPM: cleanup aspm state field in struct pcie_link_state PCI ASPM: fix typo in struct pcie_link_state PCI: drivers/pci/slot.c should depend on CONFIG_SYSFS PCI: remove redundant __msi_set_enable() PCI PM: consistently use type bool for wake enable variable x86/ACPI: Correct maximum allowed _CRS returned resources and warn if exceeded ...
This commit is contained in:
commit
59ef7a83f1
@ -122,3 +122,10 @@ Description:
|
||||
This symbolic link appears when a device is a Virtual Function.
|
||||
The symbolic link points to the PCI device sysfs entry of the
|
||||
Physical Function this device associates with.
|
||||
|
||||
What: /sys/bus/pci/slots/.../module
|
||||
Date: June 2009
|
||||
Contact: linux-pci@vger.kernel.org
|
||||
Description:
|
||||
This symbolic link points to the PCI hotplug controller driver
|
||||
module that manages the hotplug slot.
|
||||
|
@ -61,6 +61,10 @@ be initiated although firmwares have no _OSC support. To enable the
|
||||
walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line
|
||||
when booting kernel. Note that forceload=n by default.
|
||||
|
||||
nosourceid, another parameter of type bool, can be used when broken
|
||||
hardware (mostly chipsets) has root ports that cannot obtain the reporting
|
||||
source ID. nosourceid=n by default.
|
||||
|
||||
2.3 AER error output
|
||||
When a PCI-E AER error is captured, an error message will be outputed to
|
||||
console. If it's a correctable error, it is outputed as a warning.
|
||||
@ -246,3 +250,24 @@ with the PCI Express AER Root driver?
|
||||
A: It could call the helper functions to enable AER in devices and
|
||||
cleanup uncorrectable status register. Pls. refer to section 3.3.
|
||||
|
||||
|
||||
4. Software error injection
|
||||
|
||||
Debugging PCIE AER error recovery code is quite difficult because it
|
||||
is hard to trigger real hardware errors. Software based error
|
||||
injection can be used to fake various kinds of PCIE errors.
|
||||
|
||||
First you should enable PCIE AER software error injection in kernel
|
||||
configuration, that is, following item should be in your .config.
|
||||
|
||||
CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m
|
||||
|
||||
After reboot with new kernel or insert the module, a device file named
|
||||
/dev/aer_inject should be created.
|
||||
|
||||
Then, you need a user space tool named aer-inject, which can be gotten
|
||||
from:
|
||||
http://www.kernel.org/pub/linux/utils/pci/aer-inject/
|
||||
|
||||
More information about aer-inject can be found in the document comes
|
||||
with its source code.
|
||||
|
@ -1776,6 +1776,9 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
root domains (aka PCI segments, in ACPI-speak).
|
||||
nommconf [X86] Disable use of MMCONFIG for PCI
|
||||
Configuration
|
||||
check_enable_amd_mmconf [X86] check for and enable
|
||||
properly configured MMIO access to PCI
|
||||
config space on AMD family 10h CPU
|
||||
nomsi [MSI] If the PCI_MSI kernel config parameter is
|
||||
enabled, this kernel boot option can be used to
|
||||
disable the use of MSI interrupts system-wide.
|
||||
@ -1828,7 +1831,7 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
IRQ routing is enabled.
|
||||
noacpi [X86] Do not use ACPI for IRQ routing
|
||||
or for PCI scanning.
|
||||
use_crs [X86] Use _CRS for PCI resource
|
||||
nocrs [X86] Don't use _CRS for PCI resource
|
||||
allocation.
|
||||
routeirq Do IRQ routing for all PCI devices.
|
||||
This is normally done in pci_enable_device(),
|
||||
@ -1865,6 +1868,12 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
PAGE_SIZE is used as alignment.
|
||||
PCI-PCI bridge can be specified, if resource
|
||||
windows need to be expanded.
|
||||
ecrc= Enable/disable PCIe ECRC (transaction layer
|
||||
end-to-end CRC checking).
|
||||
bios: Use BIOS/firmware settings. This is the
|
||||
the default.
|
||||
off: Turn ECRC off
|
||||
on: Turn ECRC on.
|
||||
|
||||
pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power
|
||||
Management.
|
||||
|
@ -122,7 +122,7 @@ static void eeh_enable_irq(struct pci_dev *dev)
|
||||
* passed back in "userdata".
|
||||
*/
|
||||
|
||||
static void eeh_report_error(struct pci_dev *dev, void *userdata)
|
||||
static int eeh_report_error(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
enum pci_ers_result rc, *res = userdata;
|
||||
struct pci_driver *driver = dev->driver;
|
||||
@ -130,19 +130,21 @@ static void eeh_report_error(struct pci_dev *dev, void *userdata)
|
||||
dev->error_state = pci_channel_io_frozen;
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
eeh_disable_irq(dev);
|
||||
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->error_detected)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
rc = driver->err_handler->error_detected (dev, pci_channel_io_frozen);
|
||||
|
||||
/* A driver that needs a reset trumps all others */
|
||||
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
|
||||
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,7 +155,7 @@ static void eeh_report_error(struct pci_dev *dev, void *userdata)
|
||||
* Cumulative response passed back in "userdata".
|
||||
*/
|
||||
|
||||
static void eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
|
||||
static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
enum pci_ers_result rc, *res = userdata;
|
||||
struct pci_driver *driver = dev->driver;
|
||||
@ -161,26 +163,28 @@ static void eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
|
||||
if (!driver ||
|
||||
!driver->err_handler ||
|
||||
!driver->err_handler->mmio_enabled)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
rc = driver->err_handler->mmio_enabled (dev);
|
||||
|
||||
/* A driver that needs a reset trumps all others */
|
||||
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
|
||||
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_report_reset - tell device that slot has been reset
|
||||
*/
|
||||
|
||||
static void eeh_report_reset(struct pci_dev *dev, void *userdata)
|
||||
static int eeh_report_reset(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
enum pci_ers_result rc, *res = userdata;
|
||||
struct pci_driver *driver = dev->driver;
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dev->error_state = pci_channel_io_normal;
|
||||
|
||||
@ -188,35 +192,39 @@ static void eeh_report_reset(struct pci_dev *dev, void *userdata)
|
||||
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->slot_reset)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
rc = driver->err_handler->slot_reset(dev);
|
||||
if ((*res == PCI_ERS_RESULT_NONE) ||
|
||||
(*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
|
||||
if (*res == PCI_ERS_RESULT_DISCONNECT &&
|
||||
rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_report_resume - tell device to resume normal operations
|
||||
*/
|
||||
|
||||
static void eeh_report_resume(struct pci_dev *dev, void *userdata)
|
||||
static int eeh_report_resume(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
struct pci_driver *driver = dev->driver;
|
||||
|
||||
dev->error_state = pci_channel_io_normal;
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
eeh_enable_irq(dev);
|
||||
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->resume)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
driver->err_handler->resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,22 +234,24 @@ static void eeh_report_resume(struct pci_dev *dev, void *userdata)
|
||||
* dead, and that no further recovery attempts will be made on it.
|
||||
*/
|
||||
|
||||
static void eeh_report_failure(struct pci_dev *dev, void *userdata)
|
||||
static int eeh_report_failure(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
struct pci_driver *driver = dev->driver;
|
||||
|
||||
dev->error_state = pci_channel_io_perm_failure;
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
eeh_disable_irq(dev);
|
||||
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->error_detected)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
|
@ -130,6 +130,7 @@ extern void pci_iommu_alloc(void);
|
||||
|
||||
/* generic pci stuff */
|
||||
#include <asm-generic/pci.h>
|
||||
#define PCIBIOS_MAX_MEM_32 0xffffffff
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
/* Returns the node based on pci bus */
|
||||
|
@ -25,7 +25,7 @@
|
||||
#define PCI_BIOS_IRQ_SCAN 0x2000
|
||||
#define PCI_ASSIGN_ALL_BUSSES 0x4000
|
||||
#define PCI_CAN_SKIP_ISA_ALIGN 0x8000
|
||||
#define PCI_USE__CRS 0x10000
|
||||
#define PCI_NO_ROOT_CRS 0x10000
|
||||
#define PCI_CHECK_ENABLE_AMD_MMCONF 0x20000
|
||||
#define PCI_HAS_IO_ECS 0x40000
|
||||
#define PCI_NOASSIGN_ROMS 0x80000
|
||||
|
@ -38,15 +38,26 @@ count_resource(struct acpi_resource *acpi_res, void *data)
|
||||
struct acpi_resource_address64 addr;
|
||||
acpi_status status;
|
||||
|
||||
if (info->res_num >= PCI_BUS_NUM_RESOURCES)
|
||||
return AE_OK;
|
||||
|
||||
status = resource_to_addr(acpi_res, &addr);
|
||||
if (ACPI_SUCCESS(status))
|
||||
info->res_num++;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
bus_has_transparent_bridge(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
u16 class = dev->class >> 8;
|
||||
|
||||
if (class == PCI_CLASS_BRIDGE_PCI && dev->transparent)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||
{
|
||||
@ -56,9 +67,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||
acpi_status status;
|
||||
unsigned long flags;
|
||||
struct resource *root;
|
||||
|
||||
if (info->res_num >= PCI_BUS_NUM_RESOURCES)
|
||||
return AE_OK;
|
||||
int max_root_bus_resources = PCI_BUS_NUM_RESOURCES;
|
||||
|
||||
status = resource_to_addr(acpi_res, &addr);
|
||||
if (!ACPI_SUCCESS(status))
|
||||
@ -82,6 +91,18 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||
res->end = res->start + addr.address_length - 1;
|
||||
res->child = NULL;
|
||||
|
||||
if (bus_has_transparent_bridge(info->bus))
|
||||
max_root_bus_resources -= 3;
|
||||
if (info->res_num >= max_root_bus_resources) {
|
||||
printk(KERN_WARNING "PCI: Failed to allocate 0x%lx-0x%lx "
|
||||
"from %s for %s due to _CRS returning more than "
|
||||
"%d resource descriptors\n", (unsigned long) res->start,
|
||||
(unsigned long) res->end, root->name, info->name,
|
||||
max_root_bus_resources);
|
||||
info->res_num++;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
if (insert_resource(root, res)) {
|
||||
printk(KERN_ERR "PCI: Failed to allocate 0x%lx-0x%lx "
|
||||
"from %s for %s\n", (unsigned long) res->start,
|
||||
@ -217,7 +238,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bus && (pci_probe & PCI_USE__CRS))
|
||||
if (bus && !(pci_probe & PCI_NO_ROOT_CRS))
|
||||
get_current_resources(device, busnum, domain, bus);
|
||||
return bus;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ void x86_pci_root_bus_res_quirks(struct pci_bus *b)
|
||||
struct pci_root_info *info;
|
||||
|
||||
/* don't go for it if _CRS is used */
|
||||
if (pci_probe & PCI_USE__CRS)
|
||||
if (!(pci_probe & PCI_NO_ROOT_CRS))
|
||||
return;
|
||||
|
||||
/* if only one root bus, don't need to anything */
|
||||
|
@ -515,8 +515,8 @@ char * __devinit pcibios_setup(char *str)
|
||||
} else if (!strcmp(str, "assign-busses")) {
|
||||
pci_probe |= PCI_ASSIGN_ALL_BUSSES;
|
||||
return NULL;
|
||||
} else if (!strcmp(str, "use_crs")) {
|
||||
pci_probe |= PCI_USE__CRS;
|
||||
} else if (!strcmp(str, "nocrs")) {
|
||||
pci_probe |= PCI_NO_ROOT_CRS;
|
||||
return NULL;
|
||||
} else if (!strcmp(str, "earlydump")) {
|
||||
pci_early_dump_regs = 1;
|
||||
|
@ -266,6 +266,7 @@ config ACPI_DEBUG_FUNC_TRACE
|
||||
|
||||
config ACPI_PCI_SLOT
|
||||
tristate "PCI slot detection driver"
|
||||
depends on SYSFS
|
||||
default n
|
||||
help
|
||||
This driver creates entries in /sys/bus/pci/slots/ for all PCI
|
||||
|
@ -2,10 +2,11 @@
|
||||
# Makefile for the PCI bus specific drivers.
|
||||
#
|
||||
|
||||
obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
|
||||
obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
|
||||
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
|
||||
irq.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSFS) += slot.o
|
||||
|
||||
# Build PCI Express stuff if needed
|
||||
obj-$(CONFIG_PCIEPORTBUS) += pcie/
|
||||
|
@ -66,6 +66,25 @@ EXPORT_SYMBOL(pci_bus_write_config_byte);
|
||||
EXPORT_SYMBOL(pci_bus_write_config_word);
|
||||
EXPORT_SYMBOL(pci_bus_write_config_dword);
|
||||
|
||||
/**
|
||||
* pci_bus_set_ops - Set raw operations of pci bus
|
||||
* @bus: pci bus struct
|
||||
* @ops: new raw operations
|
||||
*
|
||||
* Return previous raw operations
|
||||
*/
|
||||
struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops)
|
||||
{
|
||||
struct pci_ops *old_ops;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pci_lock, flags);
|
||||
old_ops = bus->ops;
|
||||
bus->ops = ops;
|
||||
spin_unlock_irqrestore(&pci_lock, flags);
|
||||
return old_ops;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_bus_set_ops);
|
||||
|
||||
/**
|
||||
* pci_read_vpd - Read one entry from Vital Product Data
|
||||
|
@ -41,9 +41,14 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
|
||||
void *alignf_data)
|
||||
{
|
||||
int i, ret = -ENOMEM;
|
||||
resource_size_t max = -1;
|
||||
|
||||
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
|
||||
|
||||
/* don't allocate too high if the pref mem doesn't support 64bit*/
|
||||
if (!(res->flags & IORESOURCE_MEM_64))
|
||||
max = PCIBIOS_MAX_MEM_32;
|
||||
|
||||
for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
|
||||
struct resource *r = bus->resource[i];
|
||||
if (!r)
|
||||
@ -62,7 +67,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
|
||||
/* Ok, try it out.. */
|
||||
ret = allocate_resource(r, res, size,
|
||||
r->start ? : min,
|
||||
-1, align,
|
||||
max, align,
|
||||
alignf, alignf_data);
|
||||
if (ret == 0)
|
||||
break;
|
||||
@ -201,13 +206,18 @@ void pci_enable_bridges(struct pci_bus *bus)
|
||||
* Walk the given bus, including any bridged devices
|
||||
* on buses under this bus. Call the provided callback
|
||||
* on each device found.
|
||||
*
|
||||
* We check the return of @cb each time. If it returns anything
|
||||
* other than 0, we break out.
|
||||
*
|
||||
*/
|
||||
void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
|
||||
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
|
||||
void *userdata)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
struct pci_bus *bus;
|
||||
struct list_head *next;
|
||||
int retval;
|
||||
|
||||
bus = top;
|
||||
down_read(&pci_bus_sem);
|
||||
@ -231,8 +241,10 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
|
||||
|
||||
/* Run device routines with the device locked */
|
||||
down(&dev->dev.sem);
|
||||
cb(dev, userdata);
|
||||
retval = cb(dev, userdata);
|
||||
up(&dev->dev.sem);
|
||||
if (retval)
|
||||
break;
|
||||
}
|
||||
up_read(&pci_bus_sem);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
menuconfig HOTPLUG_PCI
|
||||
tristate "Support for PCI Hotplug"
|
||||
depends on PCI && HOTPLUG
|
||||
depends on PCI && HOTPLUG && SYSFS
|
||||
---help---
|
||||
Say Y here if you have a motherboard with a PCI Hotplug controller.
|
||||
This allows you to add and remove PCI cards while the machine is
|
||||
@ -41,7 +41,7 @@ config HOTPLUG_PCI_FAKE
|
||||
|
||||
config HOTPLUG_PCI_COMPAQ
|
||||
tristate "Compaq PCI Hotplug driver"
|
||||
depends on X86 && PCI_BIOS && PCI_LEGACY
|
||||
depends on X86 && PCI_BIOS
|
||||
help
|
||||
Say Y here if you have a motherboard with a Compaq PCI Hotplug
|
||||
controller.
|
||||
|
@ -77,7 +77,6 @@ static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
|
@ -72,7 +72,6 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
|
||||
static int get_latch_status(struct hotplug_slot *slot, u8 * value);
|
||||
|
||||
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
|
@ -150,25 +150,25 @@ struct ctrl_reg { /* offset */
|
||||
|
||||
/* offsets to the controller registers based on the above structure layout */
|
||||
enum ctrl_offsets {
|
||||
SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
|
||||
SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
|
||||
SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable),
|
||||
MISC = offsetof(struct ctrl_reg, misc),
|
||||
LED_CONTROL = offsetof(struct ctrl_reg, led_control),
|
||||
INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear),
|
||||
INT_MASK = offsetof(struct ctrl_reg, int_mask),
|
||||
CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
|
||||
INT_MASK = offsetof(struct ctrl_reg, int_mask),
|
||||
CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
|
||||
CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1),
|
||||
CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1),
|
||||
GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
|
||||
NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
|
||||
GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
|
||||
NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
|
||||
CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3),
|
||||
CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4),
|
||||
CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5),
|
||||
CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6),
|
||||
CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7),
|
||||
CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8),
|
||||
SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
|
||||
CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
|
||||
SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
|
||||
CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
|
||||
CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10),
|
||||
CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11),
|
||||
SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR),
|
||||
@ -190,7 +190,9 @@ struct hrt {
|
||||
u32 reserved2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the hotplug resource table registers based on the above structure layout */
|
||||
/* offsets to the hotplug resource table registers based on the above
|
||||
* structure layout
|
||||
*/
|
||||
enum hrt_offsets {
|
||||
SIG0 = offsetof(struct hrt, sig0),
|
||||
SIG1 = offsetof(struct hrt, sig1),
|
||||
@ -217,18 +219,20 @@ struct slot_rt {
|
||||
u16 pre_mem_length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the hotplug slot resource table registers based on the above structure layout */
|
||||
/* offsets to the hotplug slot resource table registers based on the above
|
||||
* structure layout
|
||||
*/
|
||||
enum slot_rt_offsets {
|
||||
DEV_FUNC = offsetof(struct slot_rt, dev_func),
|
||||
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
|
||||
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
|
||||
MAX_BUS = offsetof(struct slot_rt, max_bus),
|
||||
IO_BASE = offsetof(struct slot_rt, io_base),
|
||||
IO_LENGTH = offsetof(struct slot_rt, io_length),
|
||||
MEM_BASE = offsetof(struct slot_rt, mem_base),
|
||||
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
|
||||
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
|
||||
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
|
||||
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
|
||||
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
|
||||
MAX_BUS = offsetof(struct slot_rt, max_bus),
|
||||
IO_BASE = offsetof(struct slot_rt, io_base),
|
||||
IO_LENGTH = offsetof(struct slot_rt, io_length),
|
||||
MEM_BASE = offsetof(struct slot_rt, mem_base),
|
||||
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
|
||||
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
|
||||
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
|
||||
};
|
||||
|
||||
struct pci_func {
|
||||
@ -286,8 +290,8 @@ struct event_info {
|
||||
struct controller {
|
||||
struct controller *next;
|
||||
u32 ctrl_int_comp;
|
||||
struct mutex crit_sect; /* critical section mutex */
|
||||
void __iomem *hpc_reg; /* cookie for our pci controller location */
|
||||
struct mutex crit_sect; /* critical section mutex */
|
||||
void __iomem *hpc_reg; /* cookie for our pci controller location */
|
||||
struct pci_resource *mem_head;
|
||||
struct pci_resource *p_mem_head;
|
||||
struct pci_resource *io_head;
|
||||
@ -299,7 +303,7 @@ struct controller {
|
||||
u8 next_event;
|
||||
u8 interrupt;
|
||||
u8 cfgspc_irq;
|
||||
u8 bus; /* bus number for the pci hotplug controller */
|
||||
u8 bus; /* bus number for the pci hotplug controller */
|
||||
u8 rev;
|
||||
u8 slot_device_offset;
|
||||
u8 first_slot;
|
||||
@ -401,46 +405,57 @@ struct resource_lists {
|
||||
|
||||
|
||||
/* debugfs functions for the hotplug controller info */
|
||||
extern void cpqhp_initialize_debugfs (void);
|
||||
extern void cpqhp_shutdown_debugfs (void);
|
||||
extern void cpqhp_create_debugfs_files (struct controller *ctrl);
|
||||
extern void cpqhp_remove_debugfs_files (struct controller *ctrl);
|
||||
extern void cpqhp_initialize_debugfs(void);
|
||||
extern void cpqhp_shutdown_debugfs(void);
|
||||
extern void cpqhp_create_debugfs_files(struct controller *ctrl);
|
||||
extern void cpqhp_remove_debugfs_files(struct controller *ctrl);
|
||||
|
||||
/* controller functions */
|
||||
extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
|
||||
extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data);
|
||||
extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
|
||||
extern int cpqhp_event_start_thread (void);
|
||||
extern void cpqhp_event_stop_thread (void);
|
||||
extern struct pci_func *cpqhp_slot_create (unsigned char busnumber);
|
||||
extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
|
||||
extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_hardware_test (struct controller *ctrl, int test_num);
|
||||
extern void cpqhp_pushbutton_thread(unsigned long event_pointer);
|
||||
extern irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data);
|
||||
extern int cpqhp_find_available_resources(struct controller *ctrl,
|
||||
void __iomem *rom_start);
|
||||
extern int cpqhp_event_start_thread(void);
|
||||
extern void cpqhp_event_stop_thread(void);
|
||||
extern struct pci_func *cpqhp_slot_create(unsigned char busnumber);
|
||||
extern struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device,
|
||||
unsigned char index);
|
||||
extern int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_hardware_test(struct controller *ctrl, int test_num);
|
||||
|
||||
/* resource functions */
|
||||
extern int cpqhp_resource_sort_and_combine (struct pci_resource **head);
|
||||
|
||||
/* pci functions */
|
||||
extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
|
||||
extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot);
|
||||
extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug);
|
||||
extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
|
||||
extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func);
|
||||
extern void cpqhp_destroy_board_resources (struct pci_func * func);
|
||||
extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
|
||||
extern void cpqhp_destroy_resource_list (struct resource_lists * resources);
|
||||
extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func);
|
||||
extern int cpqhp_unconfigure_device (struct pci_func* func);
|
||||
extern int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
|
||||
extern int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num,
|
||||
u8 slot);
|
||||
extern int cpqhp_save_config(struct controller *ctrl, int busnumber,
|
||||
int is_hot_plug);
|
||||
extern int cpqhp_save_base_addr_length(struct controller *ctrl,
|
||||
struct pci_func *func);
|
||||
extern int cpqhp_save_used_resources(struct controller *ctrl,
|
||||
struct pci_func *func);
|
||||
extern int cpqhp_configure_board(struct controller *ctrl,
|
||||
struct pci_func *func);
|
||||
extern int cpqhp_save_slot_config(struct controller *ctrl,
|
||||
struct pci_func *new_slot);
|
||||
extern int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func);
|
||||
extern void cpqhp_destroy_board_resources(struct pci_func *func);
|
||||
extern int cpqhp_return_board_resources (struct pci_func *func,
|
||||
struct resource_lists *resources);
|
||||
extern void cpqhp_destroy_resource_list(struct resource_lists *resources);
|
||||
extern int cpqhp_configure_device(struct controller *ctrl,
|
||||
struct pci_func *func);
|
||||
extern int cpqhp_unconfigure_device(struct pci_func *func);
|
||||
|
||||
/* Global variables */
|
||||
extern int cpqhp_debug;
|
||||
extern int cpqhp_legacy_mode;
|
||||
extern struct controller *cpqhp_ctrl_list;
|
||||
extern struct pci_func *cpqhp_slot_list[256];
|
||||
extern struct irq_routing_table *cpqhp_routing_table;
|
||||
|
||||
/* these can be gotten rid of, but for debugging they are purty */
|
||||
extern u8 cpqhp_nic_irq;
|
||||
@ -449,7 +464,7 @@ extern u8 cpqhp_disk_irq;
|
||||
|
||||
/* inline functions */
|
||||
|
||||
static inline char *slot_name(struct slot *slot)
|
||||
static inline const char *slot_name(struct slot *slot)
|
||||
{
|
||||
return hotplug_slot_name(slot->hotplug_slot);
|
||||
}
|
||||
@ -458,9 +473,9 @@ static inline char *slot_name(struct slot *slot)
|
||||
* return_resource
|
||||
*
|
||||
* Puts node back in the resource list pointed to by head
|
||||
*
|
||||
*/
|
||||
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
|
||||
static inline void return_resource(struct pci_resource **head,
|
||||
struct pci_resource *node)
|
||||
{
|
||||
if (!node || !head)
|
||||
return;
|
||||
@ -471,7 +486,7 @@ static inline void return_resource(struct pci_resource **head, struct pci_resour
|
||||
static inline void set_SOGO(struct controller *ctrl)
|
||||
{
|
||||
u16 misc;
|
||||
|
||||
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
misc = (misc | 0x0001) & 0xFFFB;
|
||||
writew(misc, ctrl->hpc_reg + MISC);
|
||||
@ -481,7 +496,7 @@ static inline void set_SOGO(struct controller *ctrl)
|
||||
static inline void amber_LED_on(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control |= (0x01010000L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
@ -491,7 +506,7 @@ static inline void amber_LED_on(struct controller *ctrl, u8 slot)
|
||||
static inline void amber_LED_off(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x01010000L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
@ -504,7 +519,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot)
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= (0x01010000L << slot);
|
||||
|
||||
|
||||
return led_control ? 1 : 0;
|
||||
}
|
||||
|
||||
@ -512,7 +527,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot)
|
||||
static inline void green_LED_on(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control |= 0x0101L << slot;
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
@ -521,7 +536,7 @@ static inline void green_LED_on(struct controller *ctrl, u8 slot)
|
||||
static inline void green_LED_off(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x0101L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
@ -531,7 +546,7 @@ static inline void green_LED_off(struct controller *ctrl, u8 slot)
|
||||
static inline void green_LED_blink(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x0101L << slot);
|
||||
led_control |= (0x0001L << slot);
|
||||
@ -575,22 +590,21 @@ static inline u8 read_slot_enable(struct controller *ctrl)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* get_controller_speed - find the current frequency/mode of controller.
|
||||
*
|
||||
* @ctrl: controller to get frequency/mode for.
|
||||
*
|
||||
* Returns controller speed.
|
||||
*
|
||||
*/
|
||||
static inline u8 get_controller_speed(struct controller *ctrl)
|
||||
{
|
||||
u8 curr_freq;
|
||||
u16 misc;
|
||||
|
||||
u16 misc;
|
||||
|
||||
if (ctrl->pcix_support) {
|
||||
curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
|
||||
if ((curr_freq & 0xB0) == 0xB0)
|
||||
if ((curr_freq & 0xB0) == 0xB0)
|
||||
return PCI_SPEED_133MHz_PCIX;
|
||||
if ((curr_freq & 0xA0) == 0xA0)
|
||||
return PCI_SPEED_100MHz_PCIX;
|
||||
@ -602,19 +616,18 @@ static inline u8 get_controller_speed(struct controller *ctrl)
|
||||
return PCI_SPEED_33MHz;
|
||||
}
|
||||
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
/**
|
||||
* get_adapter_speed - find the max supported frequency/mode of adapter.
|
||||
*
|
||||
* @ctrl: hotplug controller.
|
||||
* @hp_slot: hotplug slot where adapter is installed.
|
||||
*
|
||||
* Returns adapter speed.
|
||||
*
|
||||
*/
|
||||
static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
|
||||
{
|
||||
@ -672,7 +685,8 @@ static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
|
||||
}
|
||||
|
||||
|
||||
static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot)
|
||||
static inline int cpq_get_latch_status(struct controller *ctrl,
|
||||
struct slot *slot)
|
||||
{
|
||||
u32 status;
|
||||
u8 hp_slot;
|
||||
@ -687,7 +701,8 @@ static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slo
|
||||
}
|
||||
|
||||
|
||||
static inline int get_presence_status(struct controller *ctrl, struct slot *slot)
|
||||
static inline int get_presence_status(struct controller *ctrl,
|
||||
struct slot *slot)
|
||||
{
|
||||
int presence_save = 0;
|
||||
u8 hp_slot;
|
||||
@ -696,7 +711,8 @@ static inline int get_presence_status(struct controller *ctrl, struct slot *slot
|
||||
hp_slot = slot->device - ctrl->slot_device_offset;
|
||||
|
||||
tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
|
||||
presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02;
|
||||
presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15))
|
||||
>> hp_slot) & 0x02;
|
||||
|
||||
return presence_save;
|
||||
}
|
||||
@ -718,5 +734,12 @@ static inline int wait_for_ctrl_irq(struct controller *ctrl)
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <asm/pci_x86.h>
|
||||
static inline int cpqhp_routing_table_length(void)
|
||||
{
|
||||
BUG_ON(cpqhp_routing_table == NULL);
|
||||
return ((cpqhp_routing_table->size - sizeof(struct irq_routing_table)) /
|
||||
sizeof(struct irq_info));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -81,14 +81,15 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl)
|
||||
|
||||
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
|
||||
if (change & (0x1L << hp_slot)) {
|
||||
/**********************************
|
||||
/*
|
||||
* this one changed.
|
||||
**********************************/
|
||||
*/
|
||||
func = cpqhp_slot_find(ctrl->bus,
|
||||
(hp_slot + ctrl->slot_device_offset), 0);
|
||||
|
||||
/* this is the structure that tells the worker thread
|
||||
*what to do */
|
||||
* what to do
|
||||
*/
|
||||
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
|
||||
ctrl->next_event = (ctrl->next_event + 1) % 10;
|
||||
taskInfo->hp_slot = hp_slot;
|
||||
@ -100,17 +101,17 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl)
|
||||
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
|
||||
|
||||
if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
|
||||
/**********************************
|
||||
/*
|
||||
* Switch opened
|
||||
**********************************/
|
||||
*/
|
||||
|
||||
func->switch_save = 0;
|
||||
|
||||
taskInfo->event_type = INT_SWITCH_OPEN;
|
||||
} else {
|
||||
/**********************************
|
||||
/*
|
||||
* Switch closed
|
||||
**********************************/
|
||||
*/
|
||||
|
||||
func->switch_save = 0x10;
|
||||
|
||||
@ -131,9 +132,8 @@ static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
|
||||
{
|
||||
struct slot *slot = ctrl->slot;
|
||||
|
||||
while (slot && (slot->device != device)) {
|
||||
while (slot && (slot->device != device))
|
||||
slot = slot->next;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
@ -152,17 +152,17 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
|
||||
if (!change)
|
||||
return 0;
|
||||
|
||||
/**********************************
|
||||
/*
|
||||
* Presence Change
|
||||
**********************************/
|
||||
*/
|
||||
dbg("cpqsbd: Presence/Notify input change.\n");
|
||||
dbg(" Changed bits are 0x%4.4x\n", change );
|
||||
|
||||
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
|
||||
if (change & (0x0101 << hp_slot)) {
|
||||
/**********************************
|
||||
/*
|
||||
* this one changed.
|
||||
**********************************/
|
||||
*/
|
||||
func = cpqhp_slot_find(ctrl->bus,
|
||||
(hp_slot + ctrl->slot_device_offset), 0);
|
||||
|
||||
@ -177,22 +177,23 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
|
||||
return 0;
|
||||
|
||||
/* If the switch closed, must be a button
|
||||
* If not in button mode, nevermind */
|
||||
* If not in button mode, nevermind
|
||||
*/
|
||||
if (func->switch_save && (ctrl->push_button == 1)) {
|
||||
temp_word = ctrl->ctrl_int_comp >> 16;
|
||||
temp_byte = (temp_word >> hp_slot) & 0x01;
|
||||
temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
|
||||
|
||||
if (temp_byte != func->presence_save) {
|
||||
/**************************************
|
||||
/*
|
||||
* button Pressed (doesn't do anything)
|
||||
**************************************/
|
||||
*/
|
||||
dbg("hp_slot %d button pressed\n", hp_slot);
|
||||
taskInfo->event_type = INT_BUTTON_PRESS;
|
||||
} else {
|
||||
/**********************************
|
||||
/*
|
||||
* button Released - TAKE ACTION!!!!
|
||||
**********************************/
|
||||
*/
|
||||
dbg("hp_slot %d button released\n", hp_slot);
|
||||
taskInfo->event_type = INT_BUTTON_RELEASE;
|
||||
|
||||
@ -210,7 +211,8 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
|
||||
}
|
||||
} else {
|
||||
/* Switch is open, assume a presence change
|
||||
* Save the presence state */
|
||||
* Save the presence state
|
||||
*/
|
||||
temp_word = ctrl->ctrl_int_comp >> 16;
|
||||
func->presence_save = (temp_word >> hp_slot) & 0x01;
|
||||
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
|
||||
@ -241,17 +243,17 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl)
|
||||
if (!change)
|
||||
return 0;
|
||||
|
||||
/**********************************
|
||||
/*
|
||||
* power fault
|
||||
**********************************/
|
||||
*/
|
||||
|
||||
info("power fault interrupt\n");
|
||||
|
||||
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
|
||||
if (change & (0x01 << hp_slot)) {
|
||||
/**********************************
|
||||
/*
|
||||
* this one changed.
|
||||
**********************************/
|
||||
*/
|
||||
func = cpqhp_slot_find(ctrl->bus,
|
||||
(hp_slot + ctrl->slot_device_offset), 0);
|
||||
|
||||
@ -262,16 +264,16 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl)
|
||||
rc++;
|
||||
|
||||
if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
|
||||
/**********************************
|
||||
/*
|
||||
* power fault Cleared
|
||||
**********************************/
|
||||
*/
|
||||
func->status = 0x00;
|
||||
|
||||
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
|
||||
} else {
|
||||
/**********************************
|
||||
/*
|
||||
* power fault
|
||||
**********************************/
|
||||
*/
|
||||
taskInfo->event_type = INT_POWER_FAULT;
|
||||
|
||||
if (ctrl->rev < 4) {
|
||||
@ -432,13 +434,15 @@ static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **h
|
||||
|
||||
|
||||
/* If we got here, there the bridge requires some of the resource, but
|
||||
* we may be able to split some off of the front */
|
||||
* we may be able to split some off of the front
|
||||
*/
|
||||
|
||||
node = *head;
|
||||
|
||||
if (node->length & (alignment -1)) {
|
||||
/* this one isn't an aligned length, so we'll make a new entry
|
||||
* and split it up. */
|
||||
* and split it up.
|
||||
*/
|
||||
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
|
||||
|
||||
if (!split_node)
|
||||
@ -544,10 +548,10 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
|
||||
if (!(*head))
|
||||
return NULL;
|
||||
|
||||
if ( cpqhp_resource_sort_and_combine(head) )
|
||||
if (cpqhp_resource_sort_and_combine(head))
|
||||
return NULL;
|
||||
|
||||
if ( sort_by_size(head) )
|
||||
if (sort_by_size(head))
|
||||
return NULL;
|
||||
|
||||
for (node = *head; node; node = node->next) {
|
||||
@ -556,7 +560,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
|
||||
|
||||
if (node->base & (size - 1)) {
|
||||
/* this one isn't base aligned properly
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
temp_dword = (node->base | (size-1)) + 1;
|
||||
|
||||
/* Short circuit if adjusted size is too small */
|
||||
@ -581,7 +586,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
|
||||
/* Don't need to check if too small since we already did */
|
||||
if (node->length > size) {
|
||||
/* this one is longer than we need
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
|
||||
|
||||
if (!split_node)
|
||||
@ -601,7 +607,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
|
||||
continue;
|
||||
|
||||
/* If we got here, then it is the right size
|
||||
* Now take it out of the list and break */
|
||||
* Now take it out of the list and break
|
||||
*/
|
||||
if (*head == node) {
|
||||
*head = node->next;
|
||||
} else {
|
||||
@ -642,14 +649,16 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
|
||||
return NULL;
|
||||
|
||||
for (max = *head; max; max = max->next) {
|
||||
/* If not big enough we could probably just bail,
|
||||
* instead we'll continue to the next. */
|
||||
/* If not big enough we could probably just bail,
|
||||
* instead we'll continue to the next.
|
||||
*/
|
||||
if (max->length < size)
|
||||
continue;
|
||||
|
||||
if (max->base & (size - 1)) {
|
||||
/* this one isn't base aligned properly
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
temp_dword = (max->base | (size-1)) + 1;
|
||||
|
||||
/* Short circuit if adjusted size is too small */
|
||||
@ -672,7 +681,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
|
||||
|
||||
if ((max->base + max->length) & (size - 1)) {
|
||||
/* this one isn't end aligned properly at the top
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
|
||||
|
||||
if (!split_node)
|
||||
@ -744,7 +754,8 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
|
||||
if (node->base & (size - 1)) {
|
||||
dbg("%s: not aligned\n", __func__);
|
||||
/* this one isn't base aligned properly
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
temp_dword = (node->base | (size-1)) + 1;
|
||||
|
||||
/* Short circuit if adjusted size is too small */
|
||||
@ -769,7 +780,8 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
|
||||
if (node->length > size) {
|
||||
dbg("%s: too big\n", __func__);
|
||||
/* this one is longer than we need
|
||||
* so we'll make a new entry and split it up */
|
||||
* so we'll make a new entry and split it up
|
||||
*/
|
||||
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
|
||||
|
||||
if (!split_node)
|
||||
@ -886,19 +898,19 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
|
||||
u32 Diff;
|
||||
u32 temp_dword;
|
||||
|
||||
|
||||
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
/***************************************
|
||||
/*
|
||||
* Check to see if it was our interrupt
|
||||
***************************************/
|
||||
*/
|
||||
if (!(misc & 0x000C)) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (misc & 0x0004) {
|
||||
/**********************************
|
||||
/*
|
||||
* Serial Output interrupt Pending
|
||||
**********************************/
|
||||
*/
|
||||
|
||||
/* Clear the interrupt */
|
||||
misc |= 0x0004;
|
||||
@ -961,11 +973,8 @@ struct pci_func *cpqhp_slot_create(u8 busnumber)
|
||||
struct pci_func *next;
|
||||
|
||||
new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
|
||||
if (new_slot == NULL) {
|
||||
/* I'm not dead yet!
|
||||
* You will be. */
|
||||
if (new_slot == NULL)
|
||||
return new_slot;
|
||||
}
|
||||
|
||||
new_slot->next = NULL;
|
||||
new_slot->configured = 1;
|
||||
@ -996,10 +1005,8 @@ static int slot_remove(struct pci_func * old_slot)
|
||||
return 1;
|
||||
|
||||
next = cpqhp_slot_list[old_slot->bus];
|
||||
|
||||
if (next == NULL) {
|
||||
if (next == NULL)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (next == old_slot) {
|
||||
cpqhp_slot_list[old_slot->bus] = old_slot->next;
|
||||
@ -1008,9 +1015,8 @@ static int slot_remove(struct pci_func * old_slot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((next->next != old_slot) && (next->next != NULL)) {
|
||||
while ((next->next != old_slot) && (next->next != NULL))
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
if (next->next == old_slot) {
|
||||
next->next = old_slot->next;
|
||||
@ -1040,9 +1046,8 @@ static int bridge_slot_remove(struct pci_func *bridge)
|
||||
for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
|
||||
next = cpqhp_slot_list[tempBus];
|
||||
|
||||
while (!slot_remove(next)) {
|
||||
while (!slot_remove(next))
|
||||
next = cpqhp_slot_list[tempBus];
|
||||
}
|
||||
}
|
||||
|
||||
next = cpqhp_slot_list[bridge->bus];
|
||||
@ -1130,39 +1135,43 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
|
||||
u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
|
||||
u16 reg16;
|
||||
u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
|
||||
|
||||
if (ctrl->speed == adapter_speed)
|
||||
return 0;
|
||||
|
||||
|
||||
/* We don't allow freq/mode changes if we find another adapter running
|
||||
* in another slot on this controller */
|
||||
* in another slot on this controller
|
||||
*/
|
||||
for(slot = ctrl->slot; slot; slot = slot->next) {
|
||||
if (slot->device == (hp_slot + ctrl->slot_device_offset))
|
||||
if (slot->device == (hp_slot + ctrl->slot_device_offset))
|
||||
continue;
|
||||
if (!slot->hotplug_slot || !slot->hotplug_slot->info)
|
||||
continue;
|
||||
if (slot->hotplug_slot->info->adapter_status == 0)
|
||||
if (slot->hotplug_slot->info->adapter_status == 0)
|
||||
continue;
|
||||
/* If another adapter is running on the same segment but at a
|
||||
* lower speed/mode, we allow the new adapter to function at
|
||||
* this rate if supported */
|
||||
if (ctrl->speed < adapter_speed)
|
||||
* this rate if supported
|
||||
*/
|
||||
if (ctrl->speed < adapter_speed)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* If the controller doesn't support freq/mode changes and the
|
||||
* controller is running at a higher mode, we bail */
|
||||
* controller is running at a higher mode, we bail
|
||||
*/
|
||||
if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability))
|
||||
return 1;
|
||||
|
||||
|
||||
/* But we allow the adapter to run at a lower rate if possible */
|
||||
if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability))
|
||||
return 0;
|
||||
|
||||
/* We try to set the max speed supported by both the adapter and
|
||||
* controller */
|
||||
* controller
|
||||
*/
|
||||
if (ctrl->speed_capability < adapter_speed) {
|
||||
if (ctrl->speed == ctrl->speed_capability)
|
||||
return 0;
|
||||
@ -1171,22 +1180,22 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
|
||||
|
||||
writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
|
||||
writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
set_SOGO(ctrl);
|
||||
wait_for_ctrl_irq(ctrl);
|
||||
|
||||
|
||||
if (adapter_speed != PCI_SPEED_133MHz_PCIX)
|
||||
reg = 0xF5;
|
||||
else
|
||||
reg = 0xF4;
|
||||
reg = 0xF4;
|
||||
pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
|
||||
|
||||
|
||||
reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
|
||||
reg16 &= ~0x000F;
|
||||
switch(adapter_speed) {
|
||||
case(PCI_SPEED_133MHz_PCIX):
|
||||
case(PCI_SPEED_133MHz_PCIX):
|
||||
reg = 0x75;
|
||||
reg16 |= 0xB;
|
||||
reg16 |= 0xB;
|
||||
break;
|
||||
case(PCI_SPEED_100MHz_PCIX):
|
||||
reg = 0x74;
|
||||
@ -1203,48 +1212,48 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
|
||||
default: /* 33MHz PCI 2.2 */
|
||||
reg = 0x71;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
reg16 |= 0xB << 12;
|
||||
writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
|
||||
|
||||
mdelay(5);
|
||||
|
||||
|
||||
mdelay(5);
|
||||
|
||||
/* Reenable interrupts */
|
||||
writel(0, ctrl->hpc_reg + INT_MASK);
|
||||
|
||||
pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
|
||||
|
||||
pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
|
||||
|
||||
/* Restart state machine */
|
||||
reg = ~0xF;
|
||||
pci_read_config_byte(ctrl->pci_dev, 0x43, ®);
|
||||
pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
|
||||
|
||||
|
||||
/* Only if mode change...*/
|
||||
if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
|
||||
((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
|
||||
set_SOGO(ctrl);
|
||||
|
||||
|
||||
wait_for_ctrl_irq(ctrl);
|
||||
mdelay(1100);
|
||||
|
||||
|
||||
/* Restore LED/Slot state */
|
||||
writel(leds, ctrl->hpc_reg + LED_CONTROL);
|
||||
writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
|
||||
|
||||
|
||||
set_SOGO(ctrl);
|
||||
wait_for_ctrl_irq(ctrl);
|
||||
|
||||
ctrl->speed = adapter_speed;
|
||||
slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
info("Successfully changed frequency/mode for adapter in slot %d\n",
|
||||
info("Successfully changed frequency/mode for adapter in slot %d\n",
|
||||
slot->number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the following routines constitute the bulk of the
|
||||
hotplug controller logic
|
||||
/* the following routines constitute the bulk of the
|
||||
* hotplug controller logic
|
||||
*/
|
||||
|
||||
|
||||
@ -1268,17 +1277,17 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
|
||||
|
||||
hp_slot = func->device - ctrl->slot_device_offset;
|
||||
|
||||
if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) {
|
||||
/**********************************
|
||||
* The switch is open.
|
||||
**********************************/
|
||||
/*
|
||||
* The switch is open.
|
||||
*/
|
||||
if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot))
|
||||
rc = INTERLOCK_OPEN;
|
||||
} else if (is_slot_enabled (ctrl, hp_slot)) {
|
||||
/**********************************
|
||||
* The board is already on
|
||||
**********************************/
|
||||
/*
|
||||
* The board is already on
|
||||
*/
|
||||
else if (is_slot_enabled (ctrl, hp_slot))
|
||||
rc = CARD_FUNCTIONING;
|
||||
} else {
|
||||
else {
|
||||
mutex_lock(&ctrl->crit_sect);
|
||||
|
||||
/* turn on board without attaching to the bus */
|
||||
@ -1299,7 +1308,7 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
|
||||
adapter_speed = get_adapter_speed(ctrl, hp_slot);
|
||||
if (ctrl->speed != adapter_speed)
|
||||
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
|
||||
@ -1352,7 +1361,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
|
||||
* Get slot won't work for devices behind
|
||||
* bridges, but in this case it will always be
|
||||
* called for the "base" bus/dev/func of an
|
||||
* adapter. */
|
||||
* adapter.
|
||||
*/
|
||||
|
||||
mutex_lock(&ctrl->crit_sect);
|
||||
|
||||
@ -1377,7 +1387,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
|
||||
|
||||
* Get slot won't work for devices behind bridges, but
|
||||
* in this case it will always be called for the "base"
|
||||
* bus/dev/func of an adapter. */
|
||||
* bus/dev/func of an adapter.
|
||||
*/
|
||||
|
||||
mutex_lock(&ctrl->crit_sect);
|
||||
|
||||
@ -1434,7 +1445,8 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
/* Change bits in slot power register to force another shift out
|
||||
* NOTE: this is to work around the timer bug */
|
||||
* NOTE: this is to work around the timer bug
|
||||
*/
|
||||
temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
|
||||
writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
|
||||
writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
|
||||
@ -1443,12 +1455,12 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
|
||||
adapter_speed = get_adapter_speed(ctrl, hp_slot);
|
||||
if (ctrl->speed != adapter_speed)
|
||||
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
|
||||
rc = WRONG_BUS_FREQUENCY;
|
||||
|
||||
|
||||
/* turn off board without attaching to the bus */
|
||||
disable_slot_power (ctrl, hp_slot);
|
||||
|
||||
@ -1461,7 +1473,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
||||
p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
/* turn on board and blink green LED */
|
||||
@ -1521,7 +1533,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
|
||||
}
|
||||
|
||||
/* All F's is an empty slot or an invalid board */
|
||||
if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
|
||||
if (temp_register != 0xFFFFFFFF) {
|
||||
res_lists.io_head = ctrl->io_head;
|
||||
res_lists.mem_head = ctrl->mem_head;
|
||||
res_lists.p_mem_head = ctrl->p_mem_head;
|
||||
@ -1570,9 +1582,8 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
|
||||
index = 0;
|
||||
do {
|
||||
new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
|
||||
if (new_slot && !new_slot->pci_dev) {
|
||||
if (new_slot && !new_slot->pci_dev)
|
||||
cpqhp_configure_device(ctrl, new_slot);
|
||||
}
|
||||
} while (new_slot);
|
||||
|
||||
mutex_lock(&ctrl->crit_sect);
|
||||
@ -1859,12 +1870,12 @@ static void interrupt_event_handler(struct controller *ctrl)
|
||||
info(msg_button_on, p_slot->number);
|
||||
}
|
||||
mutex_lock(&ctrl->crit_sect);
|
||||
|
||||
|
||||
dbg("blink green LED and turn off amber\n");
|
||||
|
||||
|
||||
amber_LED_off (ctrl, hp_slot);
|
||||
green_LED_blink (ctrl, hp_slot);
|
||||
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
@ -1958,7 +1969,7 @@ void cpqhp_pushbutton_thread(unsigned long slot)
|
||||
if (cpqhp_process_SI(ctrl, func) != 0) {
|
||||
amber_LED_on(ctrl, hp_slot);
|
||||
green_LED_off(ctrl, hp_slot);
|
||||
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
@ -2079,7 +2090,7 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
|
||||
struct pci_bus *pci_bus = ctrl->pci_bus;
|
||||
int physical_slot=0;
|
||||
|
||||
device = func->device;
|
||||
device = func->device;
|
||||
func = cpqhp_slot_find(ctrl->bus, device, index++);
|
||||
p_slot = cpqhp_find_slot(ctrl, device);
|
||||
if (p_slot) {
|
||||
@ -2113,9 +2124,8 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
|
||||
|
||||
/* If the VGA Enable bit is set, remove isn't
|
||||
* supported */
|
||||
if (BCR & PCI_BRIDGE_CTL_VGA) {
|
||||
if (BCR & PCI_BRIDGE_CTL_VGA)
|
||||
rc = REMOVE_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2183,67 +2193,67 @@ int cpqhp_hardware_test(struct controller *ctrl, int test_num)
|
||||
num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
|
||||
|
||||
switch (test_num) {
|
||||
case 1:
|
||||
/* Do stuff here! */
|
||||
case 1:
|
||||
/* Do stuff here! */
|
||||
|
||||
/* Do that funky LED thing */
|
||||
/* so we can restore them later */
|
||||
save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
work_LED = 0x01010101;
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
/* Do that funky LED thing */
|
||||
/* so we can restore them later */
|
||||
save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
work_LED = 0x01010101;
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
|
||||
work_LED = 0x01010000;
|
||||
work_LED = 0x01010000;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
work_LED = 0x00000101;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
|
||||
work_LED = 0x01010000;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
for (loop = 0; loop < num_of_slots; loop++) {
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOGO interrupt */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
/* Get ready for next iteration */
|
||||
long_delay((3*HZ)/10);
|
||||
work_LED = work_LED >> 16;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
work_LED = 0x00000101;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 0);
|
||||
switch_leds(ctrl, num_of_slots, &work_LED, 1);
|
||||
|
||||
work_LED = 0x01010000;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
for (loop = 0; loop < num_of_slots; loop++) {
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOGO interrupt */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
/* Get ready for next iteration */
|
||||
long_delay((3*HZ)/10);
|
||||
work_LED = work_LED >> 16;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOGO interrupt */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
|
||||
/* Get ready for next iteration */
|
||||
long_delay((3*HZ)/10);
|
||||
work_LED = work_LED << 16;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
work_LED = work_LED << 1;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
/* put it back the way it was */
|
||||
writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
/* Wait for SOGO interrupt */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
break;
|
||||
case 2:
|
||||
/* Do other stuff here! */
|
||||
break;
|
||||
case 3:
|
||||
/* and more... */
|
||||
break;
|
||||
|
||||
/* Get ready for next iteration */
|
||||
long_delay((3*HZ)/10);
|
||||
work_LED = work_LED << 16;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
work_LED = work_LED << 1;
|
||||
writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
/* put it back the way it was */
|
||||
writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
|
||||
|
||||
set_SOGO(ctrl);
|
||||
|
||||
/* Wait for SOBS to be unset */
|
||||
wait_for_ctrl_irq (ctrl);
|
||||
break;
|
||||
case 2:
|
||||
/* Do other stuff here! */
|
||||
break;
|
||||
case 3:
|
||||
/* and more... */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2312,9 +2322,9 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func
|
||||
while ((function < max_functions) && (!stop_it)) {
|
||||
pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
|
||||
|
||||
if (ID == 0xFFFFFFFF) { /* There's nothing there. */
|
||||
if (ID == 0xFFFFFFFF) {
|
||||
function++;
|
||||
} else { /* There's something there */
|
||||
} else {
|
||||
/* Setup slot structure. */
|
||||
new_slot = cpqhp_slot_create(func->bus);
|
||||
|
||||
@ -2339,8 +2349,8 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func
|
||||
|
||||
|
||||
/*
|
||||
Configuration logic that involves the hotplug data structures and
|
||||
their bookkeeping
|
||||
* Configuration logic that involves the hotplug data structures and
|
||||
* their bookkeeping
|
||||
*/
|
||||
|
||||
|
||||
@ -2393,7 +2403,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
|
||||
if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* set Primary bus */
|
||||
dbg("set Primary bus = %d\n", func->bus);
|
||||
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
|
||||
@ -2484,7 +2494,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
temp_resources.irqs = &irqs;
|
||||
|
||||
/* Make copies of the nodes we are going to pass down so that
|
||||
* if there is a problem,we can just use these to free resources */
|
||||
* if there is a problem,we can just use these to free resources
|
||||
*/
|
||||
hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
|
||||
hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
|
||||
hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
|
||||
@ -2556,7 +2567,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
|
||||
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
|
||||
|
||||
/* Adjust this to compensate for extra adjustment in first loop */
|
||||
/* Adjust this to compensate for extra adjustment in first loop
|
||||
*/
|
||||
irqs.barber_pole--;
|
||||
|
||||
rc = 0;
|
||||
@ -2917,27 +2929,26 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
} /* End of base register loop */
|
||||
if (cpqhp_legacy_mode) {
|
||||
/* Figure out which interrupt pin this function uses */
|
||||
rc = pci_bus_read_config_byte (pci_bus, devfn,
|
||||
rc = pci_bus_read_config_byte (pci_bus, devfn,
|
||||
PCI_INTERRUPT_PIN, &temp_byte);
|
||||
|
||||
/* If this function needs an interrupt and we are behind
|
||||
* a bridge and the pin is tied to something that's
|
||||
* alread mapped, set this one the same */
|
||||
if (temp_byte && resources->irqs &&
|
||||
(resources->irqs->valid_INT &
|
||||
if (temp_byte && resources->irqs &&
|
||||
(resources->irqs->valid_INT &
|
||||
(0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
|
||||
/* We have to share with something already set up */
|
||||
IRQ = resources->irqs->interrupt[(temp_byte +
|
||||
IRQ = resources->irqs->interrupt[(temp_byte +
|
||||
resources->irqs->barber_pole - 1) & 0x03];
|
||||
} else {
|
||||
/* Program IRQ based on card type */
|
||||
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
|
||||
|
||||
if (class_code == PCI_BASE_CLASS_STORAGE) {
|
||||
if (class_code == PCI_BASE_CLASS_STORAGE)
|
||||
IRQ = cpqhp_disk_irq;
|
||||
} else {
|
||||
else
|
||||
IRQ = cpqhp_nic_irq;
|
||||
}
|
||||
}
|
||||
|
||||
/* IRQ Line */
|
||||
|
@ -94,12 +94,13 @@ static u8 evbuffer[1024];
|
||||
|
||||
static void __iomem *compaq_int15_entry_point;
|
||||
|
||||
static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
|
||||
/* lock for ordering int15_bios_call() */
|
||||
static spinlock_t int15_lock;
|
||||
|
||||
|
||||
/* This is a series of function that deals with
|
||||
setting & getting the hotplug resource table in some environment variable.
|
||||
*/
|
||||
* setting & getting the hotplug resource table in some environment variable.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We really shouldn't be doing this unless there is a _very_ good reason to!!!
|
||||
@ -113,7 +114,7 @@ static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
|
||||
|
||||
if ((*used + 1) > *avail)
|
||||
return(1);
|
||||
|
||||
|
||||
*((u8*)*p_buffer) = value;
|
||||
tByte = (u8**)p_buffer;
|
||||
(*tByte)++;
|
||||
@ -170,10 +171,10 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
|
||||
unsigned long flags;
|
||||
int op = operation;
|
||||
int ret_val;
|
||||
|
||||
|
||||
if (!compaq_int15_entry_point)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
spin_lock_irqsave(&int15_lock, flags);
|
||||
__asm__ (
|
||||
"xorl %%ebx,%%ebx\n" \
|
||||
@ -187,7 +188,7 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
|
||||
"D" (buffer), "m" (compaq_int15_entry_point)
|
||||
: "%ebx", "%edx");
|
||||
spin_unlock_irqrestore(&int15_lock, flags);
|
||||
|
||||
|
||||
return((ret_val & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
@ -210,14 +211,16 @@ static int load_HRT (void __iomem *rom_start)
|
||||
|
||||
available = 1024;
|
||||
|
||||
// Now load the EV
|
||||
/* Now load the EV */
|
||||
temp_dword = available;
|
||||
|
||||
rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
|
||||
|
||||
evbuffer_length = temp_dword;
|
||||
|
||||
// We're maintaining the resource lists so write FF to invalidate old info
|
||||
/* We're maintaining the resource lists so write FF to invalidate old
|
||||
* info
|
||||
*/
|
||||
temp_dword = 1;
|
||||
|
||||
rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
|
||||
@ -263,13 +266,13 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
p_EV_header = (struct ev_hrt_header *) pFill;
|
||||
|
||||
ctrl = cpqhp_ctrl_list;
|
||||
|
||||
// The revision of this structure
|
||||
|
||||
/* The revision of this structure */
|
||||
rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The number of controllers
|
||||
/* The number of controllers */
|
||||
rc = add_byte( &pFill, 1, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
@ -279,27 +282,27 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
|
||||
numCtrl++;
|
||||
|
||||
// The bus number
|
||||
/* The bus number */
|
||||
rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The device Number
|
||||
/* The device Number */
|
||||
rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The function Number
|
||||
/* The function Number */
|
||||
rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// Skip the number of available entries
|
||||
/* Skip the number of available entries */
|
||||
rc = add_dword( &pFill, 0, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// Figure out memory Available
|
||||
/* Figure out memory Available */
|
||||
|
||||
resNode = ctrl->mem_head;
|
||||
|
||||
@ -308,12 +311,12 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
/* base */
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
/* length */
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
@ -321,10 +324,10 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
/* Fill in the number of entries */
|
||||
p_ev_ctrl->mem_avail = loop;
|
||||
|
||||
// Figure out prefetchable memory Available
|
||||
/* Figure out prefetchable memory Available */
|
||||
|
||||
resNode = ctrl->p_mem_head;
|
||||
|
||||
@ -333,12 +336,12 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
/* base */
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
/* length */
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
@ -346,10 +349,10 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
/* Fill in the number of entries */
|
||||
p_ev_ctrl->p_mem_avail = loop;
|
||||
|
||||
// Figure out IO Available
|
||||
/* Figure out IO Available */
|
||||
|
||||
resNode = ctrl->io_head;
|
||||
|
||||
@ -358,12 +361,12 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
/* base */
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
/* length */
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
@ -371,10 +374,10 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
/* Fill in the number of entries */
|
||||
p_ev_ctrl->io_avail = loop;
|
||||
|
||||
// Figure out bus Available
|
||||
/* Figure out bus Available */
|
||||
|
||||
resNode = ctrl->bus_head;
|
||||
|
||||
@ -383,12 +386,12 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
/* base */
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
/* length */
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
@ -396,15 +399,15 @@ static u32 store_HRT (void __iomem *rom_start)
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
/* Fill in the number of entries */
|
||||
p_ev_ctrl->bus_avail = loop;
|
||||
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
|
||||
p_EV_header->num_of_ctrl = numCtrl;
|
||||
|
||||
// Now store the EV
|
||||
/* Now store the EV */
|
||||
|
||||
temp_dword = usedbytes;
|
||||
|
||||
@ -449,20 +452,21 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
struct ev_hrt_header *p_EV_header;
|
||||
|
||||
if (!evbuffer_init) {
|
||||
// Read the resource list information in from NVRAM
|
||||
/* Read the resource list information in from NVRAM */
|
||||
if (load_HRT(rom_start))
|
||||
memset (evbuffer, 0, 1024);
|
||||
|
||||
evbuffer_init = 1;
|
||||
}
|
||||
|
||||
// If we saved information in NVRAM, use it now
|
||||
/* If we saved information in NVRAM, use it now */
|
||||
p_EV_header = (struct ev_hrt_header *) evbuffer;
|
||||
|
||||
// The following code is for systems where version 1.0 of this
|
||||
// driver has been loaded, but doesn't support the hardware.
|
||||
// In that case, the driver would incorrectly store something
|
||||
// in NVRAM.
|
||||
/* The following code is for systems where version 1.0 of this
|
||||
* driver has been loaded, but doesn't support the hardware.
|
||||
* In that case, the driver would incorrectly store something
|
||||
* in NVRAM.
|
||||
*/
|
||||
if ((p_EV_header->Version == 2) ||
|
||||
((p_EV_header->Version == 1) && !ctrl->push_flag)) {
|
||||
p_byte = &(p_EV_header->next);
|
||||
@ -479,7 +483,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
function = p_ev_ctrl->function;
|
||||
|
||||
while ((bus != ctrl->bus) ||
|
||||
(device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
|
||||
(device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
|
||||
(function != PCI_FUNC(ctrl->pci_dev->devfn))) {
|
||||
nummem = p_ev_ctrl->mem_avail;
|
||||
numpmem = p_ev_ctrl->p_mem_avail;
|
||||
@ -491,7 +495,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
// Skip forward to the next entry
|
||||
/* Skip forward to the next entry */
|
||||
p_byte += (nummem + numpmem + numio + numbus) * 8;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
@ -629,8 +633,9 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
ctrl->bus_head = bus_node;
|
||||
}
|
||||
|
||||
// If all of the following fail, we don't have any resources for
|
||||
// hot plug add
|
||||
/* If all of the following fail, we don't have any resources for
|
||||
* hot plug add
|
||||
*/
|
||||
rc = 1;
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
|
||||
@ -640,14 +645,14 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
if (rc)
|
||||
return(rc);
|
||||
} else {
|
||||
if ((evbuffer[0] != 0) && (!ctrl->push_flag))
|
||||
if ((evbuffer[0] != 0) && (!ctrl->push_flag))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int compaq_nvram_store (void __iomem *rom_start)
|
||||
{
|
||||
int rc = 1;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1318,7 +1318,6 @@ error:
|
||||
}
|
||||
|
||||
struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_attention_status = set_attention_status,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = ibmphp_disable_slot,
|
||||
@ -1421,3 +1420,4 @@ static void __exit ibmphp_exit(void)
|
||||
}
|
||||
|
||||
module_init(ibmphp_init);
|
||||
module_exit(ibmphp_exit);
|
||||
|
@ -347,125 +347,129 @@ static struct pci_slot_attribute hotplug_slot_attr_test = {
|
||||
.store = test_write_file
|
||||
};
|
||||
|
||||
static int has_power_file(struct pci_slot *pci_slot)
|
||||
static bool has_power_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if ((slot->ops->enable_slot) ||
|
||||
(slot->ops->disable_slot) ||
|
||||
(slot->ops->get_power_status))
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_attention_file(struct pci_slot *pci_slot)
|
||||
static bool has_attention_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if ((slot->ops->set_attention_status) ||
|
||||
(slot->ops->get_attention_status))
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_latch_file(struct pci_slot *pci_slot)
|
||||
static bool has_latch_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if (slot->ops->get_latch_status)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_adapter_file(struct pci_slot *pci_slot)
|
||||
static bool has_adapter_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if (slot->ops->get_adapter_status)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_max_bus_speed_file(struct pci_slot *pci_slot)
|
||||
static bool has_max_bus_speed_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if (slot->ops->get_max_bus_speed)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
|
||||
static bool has_cur_bus_speed_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if (slot->ops->get_cur_bus_speed)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int has_test_file(struct pci_slot *pci_slot)
|
||||
static bool has_test_file(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
return false;
|
||||
if (slot->ops->hardware_test)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int fs_add_slot(struct pci_slot *slot)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (has_power_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
/* Create symbolic link to the hotplug driver module */
|
||||
pci_hp_create_module_link(slot);
|
||||
|
||||
if (has_power_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_power.attr);
|
||||
if (retval)
|
||||
goto exit_power;
|
||||
}
|
||||
|
||||
if (has_attention_file(slot) == 0) {
|
||||
if (has_attention_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_attention.attr);
|
||||
if (retval)
|
||||
goto exit_attention;
|
||||
}
|
||||
|
||||
if (has_latch_file(slot) == 0) {
|
||||
if (has_latch_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_latch.attr);
|
||||
if (retval)
|
||||
goto exit_latch;
|
||||
}
|
||||
|
||||
if (has_adapter_file(slot) == 0) {
|
||||
if (has_adapter_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_presence.attr);
|
||||
if (retval)
|
||||
goto exit_adapter;
|
||||
}
|
||||
|
||||
if (has_max_bus_speed_file(slot) == 0) {
|
||||
if (has_max_bus_speed_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
if (retval)
|
||||
goto exit_max_speed;
|
||||
}
|
||||
|
||||
if (has_cur_bus_speed_file(slot) == 0) {
|
||||
if (has_cur_bus_speed_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
if (retval)
|
||||
goto exit_cur_speed;
|
||||
}
|
||||
|
||||
if (has_test_file(slot) == 0) {
|
||||
if (has_test_file(slot)) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_test.attr);
|
||||
if (retval)
|
||||
@ -475,55 +479,61 @@ static int fs_add_slot(struct pci_slot *slot)
|
||||
goto exit;
|
||||
|
||||
exit_test:
|
||||
if (has_cur_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
|
||||
|
||||
if (has_cur_bus_speed_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
exit_cur_speed:
|
||||
if (has_max_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
|
||||
|
||||
if (has_max_bus_speed_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
exit_max_speed:
|
||||
if (has_adapter_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
|
||||
|
||||
if (has_adapter_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_presence.attr);
|
||||
exit_adapter:
|
||||
if (has_latch_file(slot) == 0)
|
||||
if (has_latch_file(slot))
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
||||
|
||||
exit_latch:
|
||||
if (has_attention_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
|
||||
|
||||
if (has_attention_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_attention.attr);
|
||||
exit_attention:
|
||||
if (has_power_file(slot) == 0)
|
||||
if (has_power_file(slot))
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
exit_power:
|
||||
pci_hp_remove_module_link(slot);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void fs_remove_slot(struct pci_slot *slot)
|
||||
{
|
||||
if (has_power_file(slot) == 0)
|
||||
if (has_power_file(slot))
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
|
||||
if (has_attention_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
|
||||
if (has_attention_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_attention.attr);
|
||||
|
||||
if (has_latch_file(slot) == 0)
|
||||
if (has_latch_file(slot))
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
||||
|
||||
if (has_adapter_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
|
||||
if (has_adapter_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_presence.attr);
|
||||
|
||||
if (has_max_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
|
||||
if (has_max_bus_speed_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
|
||||
if (has_cur_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
|
||||
if (has_cur_bus_speed_file(slot))
|
||||
sysfs_remove_file(&slot->kobj,
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
|
||||
if (has_test_file(slot) == 0)
|
||||
if (has_test_file(slot))
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
|
||||
|
||||
pci_hp_remove_module_link(slot);
|
||||
}
|
||||
|
||||
static struct hotplug_slot *get_slot_from_name (const char *name)
|
||||
@ -540,10 +550,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
|
||||
* __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
|
||||
* @bus: bus this slot is on
|
||||
* @slot: pointer to the &struct hotplug_slot to register
|
||||
* @slot_nr: slot number
|
||||
* @devnr: device number
|
||||
* @name: name registered with kobject core
|
||||
*
|
||||
* Registers a hotplug slot with the pci hotplug subsystem, which will allow
|
||||
@ -551,8 +561,9 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
|
||||
*
|
||||
* Returns 0 if successful, anything else for an error.
|
||||
*/
|
||||
int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
|
||||
const char *name)
|
||||
int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
|
||||
int devnr, const char *name,
|
||||
struct module *owner, const char *mod_name)
|
||||
{
|
||||
int result;
|
||||
struct pci_slot *pci_slot;
|
||||
@ -567,14 +578,16 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&pci_hp_mutex);
|
||||
slot->ops->owner = owner;
|
||||
slot->ops->mod_name = mod_name;
|
||||
|
||||
mutex_lock(&pci_hp_mutex);
|
||||
/*
|
||||
* No problems if we call this interface from both ACPI_PCI_SLOT
|
||||
* driver and call it here again. If we've already created the
|
||||
* pci_slot, the interface will simply bump the refcount.
|
||||
*/
|
||||
pci_slot = pci_create_slot(bus, slot_nr, name, slot);
|
||||
pci_slot = pci_create_slot(bus, devnr, name, slot);
|
||||
if (IS_ERR(pci_slot)) {
|
||||
result = PTR_ERR(pci_slot);
|
||||
goto out;
|
||||
@ -684,6 +697,6 @@ MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
|
||||
EXPORT_SYMBOL_GPL(pci_hp_register);
|
||||
EXPORT_SYMBOL_GPL(__pci_hp_register);
|
||||
EXPORT_SYMBOL_GPL(pci_hp_deregister);
|
||||
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
|
||||
|
@ -81,7 +81,6 @@ struct slot {
|
||||
struct hpc_ops *hpc_ops;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head slot_list;
|
||||
unsigned long last_emi_toggle;
|
||||
struct delayed_work work; /* work for button event */
|
||||
struct mutex lock;
|
||||
};
|
||||
@ -203,8 +202,6 @@ struct hpc_ops {
|
||||
int (*set_attention_status)(struct slot *slot, u8 status);
|
||||
int (*get_latch_status)(struct slot *slot, u8 *status);
|
||||
int (*get_adapter_status)(struct slot *slot, u8 *status);
|
||||
int (*get_emi_status)(struct slot *slot, u8 *status);
|
||||
int (*toggle_emi)(struct slot *slot);
|
||||
int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val);
|
||||
|
@ -73,7 +73,6 @@ static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *val
|
||||
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
|
||||
static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_attention_status = set_attention_status,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
@ -85,99 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
|
||||
.get_cur_bus_speed = get_cur_bus_speed,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the status of the Electro Mechanical Interlock (EMI)
|
||||
*/
|
||||
static int get_lock_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
return (slot->hpc_ops->get_emi_status(slot, value));
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface for the Electro Mechanical Interlock (EMI)
|
||||
* 1 == locked, 0 == unlocked
|
||||
*/
|
||||
static ssize_t lock_read_file(struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_lock_status(slot, &value);
|
||||
if (retval)
|
||||
goto lock_read_exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
|
||||
lock_read_exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the status of the Electro Mechanical Interlock (EMI)
|
||||
* This is a toggle - in addition there must be at least 1 second
|
||||
* in between toggles.
|
||||
*/
|
||||
static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
mutex_lock(&slot->ctrl->crit_sect);
|
||||
|
||||
/* has it been >1 sec since our last toggle? */
|
||||
if ((get_seconds() - slot->last_emi_toggle) < 1) {
|
||||
mutex_unlock(&slot->ctrl->crit_sect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* see what our current state is */
|
||||
retval = get_lock_status(hotplug_slot, &value);
|
||||
if (retval || (value == status))
|
||||
goto set_lock_exit;
|
||||
|
||||
slot->hpc_ops->toggle_emi(slot);
|
||||
set_lock_exit:
|
||||
mutex_unlock(&slot->ctrl->crit_sect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface which allows the user to toggle the Electro Mechanical
|
||||
* Interlock. Valid values are either 0 or 1. 0 == unlock, 1 == lock
|
||||
*/
|
||||
static ssize_t lock_write_file(struct hotplug_slot *hotplug_slot,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
unsigned long llock;
|
||||
u8 lock;
|
||||
int retval = 0;
|
||||
|
||||
llock = simple_strtoul(buf, NULL, 10);
|
||||
lock = (u8)(llock & 0xff);
|
||||
|
||||
switch (lock) {
|
||||
case 0:
|
||||
case 1:
|
||||
retval = set_lock_status(hotplug_slot, lock);
|
||||
break;
|
||||
default:
|
||||
ctrl_err(slot->ctrl, "%d is an invalid lock value\n",
|
||||
lock);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_lock = {
|
||||
.attr = {.name = "lock", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
||||
.show = lock_read_file,
|
||||
.store = lock_write_file
|
||||
};
|
||||
|
||||
/**
|
||||
* release_slot - free up the memory used by a slot
|
||||
* @hotplug_slot: slot to free
|
||||
@ -236,17 +142,6 @@ static int init_slots(struct controller *ctrl)
|
||||
get_attention_status(hotplug_slot, &info->attention_status);
|
||||
get_latch_status(hotplug_slot, &info->latch_status);
|
||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
||||
/* create additional sysfs entries */
|
||||
if (EMI(ctrl)) {
|
||||
retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
|
||||
&hotplug_slot_attr_lock.attr);
|
||||
if (retval) {
|
||||
pci_hp_deregister(hotplug_slot);
|
||||
ctrl_err(ctrl, "Cannot create additional sysfs "
|
||||
"entries\n");
|
||||
goto error_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -261,13 +156,8 @@ error:
|
||||
static void cleanup_slots(struct controller *ctrl)
|
||||
{
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
|
||||
if (EMI(ctrl))
|
||||
sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
|
||||
&hotplug_slot_attr_lock.attr);
|
||||
list_for_each_entry(slot, &ctrl->slot_list, slot_list)
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -422,35 +422,6 @@ static int hpc_query_power_fault(struct slot *slot)
|
||||
return !!(slot_status & PCI_EXP_SLTSTA_PFD);
|
||||
}
|
||||
|
||||
static int hpc_get_emi_status(struct slot *slot, u8 *status)
|
||||
{
|
||||
struct controller *ctrl = slot->ctrl;
|
||||
u16 slot_status;
|
||||
int retval;
|
||||
|
||||
retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status);
|
||||
if (retval) {
|
||||
ctrl_err(ctrl, "Cannot check EMI status\n");
|
||||
return retval;
|
||||
}
|
||||
*status = !!(slot_status & PCI_EXP_SLTSTA_EIS);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hpc_toggle_emi(struct slot *slot)
|
||||
{
|
||||
u16 slot_cmd;
|
||||
u16 cmd_mask;
|
||||
int rc;
|
||||
|
||||
slot_cmd = PCI_EXP_SLTCTL_EIC;
|
||||
cmd_mask = PCI_EXP_SLTCTL_EIC;
|
||||
rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask);
|
||||
slot->last_emi_toggle = get_seconds();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hpc_set_attention_status(struct slot *slot, u8 value)
|
||||
{
|
||||
struct controller *ctrl = slot->ctrl;
|
||||
@ -874,8 +845,6 @@ static struct hpc_ops pciehp_hpc_ops = {
|
||||
.get_attention_status = hpc_get_attention_status,
|
||||
.get_latch_status = hpc_get_latch_status,
|
||||
.get_adapter_status = hpc_get_adapter_status,
|
||||
.get_emi_status = hpc_get_emi_status,
|
||||
.toggle_emi = hpc_toggle_emi,
|
||||
|
||||
.get_max_bus_speed = hpc_get_max_lnk_speed,
|
||||
.get_cur_bus_speed = hpc_get_cur_lnk_speed,
|
||||
|
@ -82,7 +82,6 @@ static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops skel_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
|
@ -423,7 +423,6 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
}
|
||||
|
||||
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
|
@ -83,7 +83,6 @@ static int disable_slot(struct hotplug_slot *slot);
|
||||
static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops sn_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.get_power_status = get_power_status,
|
||||
|
@ -69,7 +69,6 @@ static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *val
|
||||
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
|
||||
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_attention_status = set_attention_status,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
|
@ -110,7 +110,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
|
||||
}
|
||||
|
||||
if (reset)
|
||||
pci_execute_reset_function(virtfn);
|
||||
__pci_reset_function(virtfn);
|
||||
|
||||
pci_device_add(virtfn, virtfn->bus);
|
||||
mutex_unlock(&iov->dev->sriov->lock);
|
||||
@ -164,7 +164,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
|
||||
|
||||
if (reset) {
|
||||
device_release_driver(&virtfn->dev);
|
||||
pci_execute_reset_function(virtfn);
|
||||
__pci_reset_function(virtfn);
|
||||
}
|
||||
|
||||
sprintf(buf, "virtfn%u", id);
|
||||
@ -487,6 +487,8 @@ found:
|
||||
iov->self = dev;
|
||||
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
|
||||
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
|
||||
if (dev->pcie_type == PCI_EXP_TYPE_RC_END)
|
||||
iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
|
||||
|
||||
if (pdev)
|
||||
iov->dev = pci_dev_get(pdev);
|
||||
|
@ -75,22 +75,17 @@ void arch_teardown_msi_irqs(struct pci_dev *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
|
||||
static void msi_set_enable(struct pci_dev *dev, int pos, int enable)
|
||||
{
|
||||
u16 control;
|
||||
|
||||
if (pos) {
|
||||
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
|
||||
control &= ~PCI_MSI_FLAGS_ENABLE;
|
||||
if (enable)
|
||||
control |= PCI_MSI_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
|
||||
}
|
||||
}
|
||||
BUG_ON(!pos);
|
||||
|
||||
static void msi_set_enable(struct pci_dev *dev, int enable)
|
||||
{
|
||||
__msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
|
||||
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
|
||||
control &= ~PCI_MSI_FLAGS_ENABLE;
|
||||
if (enable)
|
||||
control |= PCI_MSI_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
|
||||
}
|
||||
|
||||
static void msix_set_enable(struct pci_dev *dev, int enable)
|
||||
@ -131,9 +126,6 @@ static inline __attribute_const__ u32 msi_enabled_mask(u16 control)
|
||||
* mask all MSI interrupts by clearing the MSI enable bit does not work
|
||||
* reliably as devices without an INTx disable bit will then generate a
|
||||
* level IRQ which will never be cleared.
|
||||
*
|
||||
* Returns 1 if it succeeded in masking the interrupt and 0 if the device
|
||||
* doesn't support MSI masking.
|
||||
*/
|
||||
static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
|
||||
{
|
||||
@ -303,7 +295,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
|
||||
pos = entry->msi_attrib.pos;
|
||||
|
||||
pci_intx_for_msi(dev, 0);
|
||||
msi_set_enable(dev, 0);
|
||||
msi_set_enable(dev, pos, 0);
|
||||
write_msi_msg(dev->irq, &entry->msg);
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
|
||||
@ -321,22 +313,22 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
|
||||
|
||||
if (!dev->msix_enabled)
|
||||
return;
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
entry = list_entry(dev->msi_list.next, struct msi_desc, list);
|
||||
pos = entry->msi_attrib.pos;
|
||||
pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
|
||||
|
||||
/* route the table */
|
||||
pci_intx_for_msi(dev, 0);
|
||||
msix_set_enable(dev, 0);
|
||||
control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL;
|
||||
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
|
||||
|
||||
list_for_each_entry(entry, &dev->msi_list, list) {
|
||||
write_msi_msg(entry->irq, &entry->msg);
|
||||
msix_mask_irq(entry, entry->masked);
|
||||
}
|
||||
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
entry = list_entry(dev->msi_list.next, struct msi_desc, list);
|
||||
pos = entry->msi_attrib.pos;
|
||||
pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
|
||||
control &= ~PCI_MSIX_FLAGS_MASKALL;
|
||||
control |= PCI_MSIX_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
|
||||
}
|
||||
|
||||
@ -365,9 +357,9 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
|
||||
u16 control;
|
||||
unsigned mask;
|
||||
|
||||
msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
|
||||
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
|
||||
msi_set_enable(dev, pos, 0); /* Disable MSI during set up */
|
||||
|
||||
pci_read_config_word(dev, msi_control_reg(pos), &control);
|
||||
/* MSI Entry Initialization */
|
||||
entry = alloc_msi_entry(dev);
|
||||
@ -381,7 +373,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
|
||||
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
|
||||
entry->msi_attrib.pos = pos;
|
||||
|
||||
entry->mask_pos = msi_mask_bits_reg(pos, entry->msi_attrib.is_64);
|
||||
entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64);
|
||||
/* All MSIs are unmasked by default, Mask them all */
|
||||
if (entry->msi_attrib.maskbit)
|
||||
pci_read_config_dword(dev, entry->mask_pos, &entry->masked);
|
||||
@ -399,7 +391,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
|
||||
|
||||
/* Set MSI enabled bits */
|
||||
pci_intx_for_msi(dev, 0);
|
||||
msi_set_enable(dev, 1);
|
||||
msi_set_enable(dev, pos, 1);
|
||||
dev->msi_enabled = 1;
|
||||
|
||||
dev->irq = entry->irq;
|
||||
@ -427,11 +419,14 @@ static int msix_capability_init(struct pci_dev *dev,
|
||||
u8 bir;
|
||||
void __iomem *base;
|
||||
|
||||
msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
|
||||
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
|
||||
pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
|
||||
|
||||
/* Ensure MSI-X is disabled while it is set up */
|
||||
control &= ~PCI_MSIX_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
|
||||
|
||||
/* Request & Map MSI-X table region */
|
||||
pci_read_config_word(dev, msi_control_reg(pos), &control);
|
||||
nr_entries = multi_msix_capable(control);
|
||||
|
||||
pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
|
||||
@ -442,7 +437,6 @@ static int msix_capability_init(struct pci_dev *dev,
|
||||
if (base == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* MSI-X Table Initialization */
|
||||
for (i = 0; i < nvec; i++) {
|
||||
entry = alloc_msi_entry(dev);
|
||||
if (!entry)
|
||||
@ -455,7 +449,6 @@ static int msix_capability_init(struct pci_dev *dev,
|
||||
entry->msi_attrib.default_irq = dev->irq;
|
||||
entry->msi_attrib.pos = pos;
|
||||
entry->mask_base = base;
|
||||
msix_mask_irq(entry, 1);
|
||||
|
||||
list_add_tail(&entry->list, &dev->msi_list);
|
||||
}
|
||||
@ -480,22 +473,31 @@ static int msix_capability_init(struct pci_dev *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some devices require MSI-X to be enabled before we can touch the
|
||||
* MSI-X registers. We need to mask all the vectors to prevent
|
||||
* interrupts coming in before they're fully set up.
|
||||
*/
|
||||
control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(entry, &dev->msi_list, list) {
|
||||
entries[i].vector = entry->irq;
|
||||
set_irq_msi(entry->irq, entry);
|
||||
j = entries[i].entry;
|
||||
entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE +
|
||||
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
|
||||
msix_mask_irq(entry, 1);
|
||||
i++;
|
||||
}
|
||||
/* Set MSI-X enabled bits */
|
||||
|
||||
/* Set MSI-X enabled bits and unmask the function */
|
||||
pci_intx_for_msi(dev, 0);
|
||||
msix_set_enable(dev, 1);
|
||||
dev->msix_enabled = 1;
|
||||
|
||||
list_for_each_entry(entry, &dev->msi_list, list) {
|
||||
int vector = entry->msi_attrib.entry_nr;
|
||||
entry->masked = readl(base + vector * PCI_MSIX_ENTRY_SIZE +
|
||||
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
|
||||
}
|
||||
control &= ~PCI_MSIX_FLAGS_MASKALL;
|
||||
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -596,17 +598,20 @@ void pci_msi_shutdown(struct pci_dev *dev)
|
||||
struct msi_desc *desc;
|
||||
u32 mask;
|
||||
u16 ctrl;
|
||||
unsigned pos;
|
||||
|
||||
if (!pci_msi_enable || !dev || !dev->msi_enabled)
|
||||
return;
|
||||
|
||||
msi_set_enable(dev, 0);
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
|
||||
pos = desc->msi_attrib.pos;
|
||||
|
||||
msi_set_enable(dev, pos, 0);
|
||||
pci_intx_for_msi(dev, 1);
|
||||
dev->msi_enabled = 0;
|
||||
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
|
||||
pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &ctrl);
|
||||
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl);
|
||||
mask = msi_capable_mask(ctrl);
|
||||
msi_mask_irq(desc, mask, ~mask);
|
||||
|
||||
@ -648,10 +653,7 @@ static int msi_free_irqs(struct pci_dev* dev)
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
|
||||
if (entry->msi_attrib.is_msix) {
|
||||
writel(1, entry->mask_base + entry->msi_attrib.entry_nr
|
||||
* PCI_MSIX_ENTRY_SIZE
|
||||
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
|
||||
|
||||
msix_mask_irq(entry, 1);
|
||||
if (list_is_last(&entry->list, &dev->msi_list))
|
||||
iounmap(entry->mask_base);
|
||||
}
|
||||
@ -691,8 +693,8 @@ int pci_msix_table_size(struct pci_dev *dev)
|
||||
* indicates the successful configuration of MSI-X capability structure
|
||||
* with new allocated MSI-X irqs. A return of < 0 indicates a failure.
|
||||
* Or a return of > 0 indicates that driver request is exceeding the number
|
||||
* of irqs available. Driver should use the returned value to re-send
|
||||
* its request.
|
||||
* of irqs or MSI-X vectors available. Driver should use the returned value to
|
||||
* re-send its request.
|
||||
**/
|
||||
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
|
||||
{
|
||||
@ -708,7 +710,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
|
||||
|
||||
nr_entries = pci_msix_table_size(dev);
|
||||
if (nvec > nr_entries)
|
||||
return -EINVAL;
|
||||
return nr_entries;
|
||||
|
||||
/* Check for any invalid entries */
|
||||
for (i = 0; i < nvec; i++) {
|
||||
|
@ -16,21 +16,15 @@
|
||||
#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO)
|
||||
#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI)
|
||||
#define msi_data_reg(base, is64bit) \
|
||||
( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 )
|
||||
#define msi_mask_bits_reg(base, is64bit) \
|
||||
( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
|
||||
#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE
|
||||
(base + ((is64bit == 1) ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32))
|
||||
#define msi_mask_reg(base, is64bit) \
|
||||
(base + ((is64bit == 1) ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32))
|
||||
#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
|
||||
#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
|
||||
|
||||
#define msix_table_offset_reg(base) (base + 0x04)
|
||||
#define msix_pba_offset_reg(base) (base + 0x08)
|
||||
#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE
|
||||
#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE
|
||||
#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
|
||||
#define multi_msix_capable msix_table_size
|
||||
#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK)
|
||||
#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
|
||||
#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
|
||||
#define multi_msix_capable(control) msix_table_size((control))
|
||||
|
||||
#endif /* MSI_H */
|
||||
|
@ -485,6 +485,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
||||
pmcsr |= state;
|
||||
break;
|
||||
case PCI_D3hot:
|
||||
case PCI_D3cold:
|
||||
case PCI_UNKNOWN: /* Boot-up */
|
||||
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
|
||||
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
|
||||
@ -1208,7 +1210,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
|
||||
* Error code depending on the platform is returned if both the platform and
|
||||
* the native mechanism fail to enable the generation of wake-up events
|
||||
*/
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
|
||||
{
|
||||
int error = 0;
|
||||
bool pme_done = false;
|
||||
@ -1287,15 +1289,14 @@ pci_power_t pci_target_state(struct pci_dev *dev)
|
||||
default:
|
||||
target_state = state;
|
||||
}
|
||||
} else if (!dev->pm_cap) {
|
||||
target_state = PCI_D0;
|
||||
} else if (device_may_wakeup(&dev->dev)) {
|
||||
/*
|
||||
* Find the deepest state from which the device can generate
|
||||
* wake-up events, make it the target state and enable device
|
||||
* to generate PME#.
|
||||
*/
|
||||
if (!dev->pm_cap)
|
||||
return PCI_POWER_ERROR;
|
||||
|
||||
if (dev->pme_support) {
|
||||
while (target_state
|
||||
&& !(dev->pme_support & (1 << target_state)))
|
||||
@ -1532,7 +1533,7 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
|
||||
if (!pin)
|
||||
return -1;
|
||||
|
||||
while (dev->bus->parent) {
|
||||
while (!pci_is_root_bus(dev->bus)) {
|
||||
pin = pci_swizzle_interrupt_pin(dev, pin);
|
||||
dev = dev->bus->self;
|
||||
}
|
||||
@ -1552,7 +1553,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp)
|
||||
{
|
||||
u8 pin = *pinp;
|
||||
|
||||
while (dev->bus->parent) {
|
||||
while (!pci_is_root_bus(dev->bus)) {
|
||||
pin = pci_swizzle_interrupt_pin(dev, pin);
|
||||
dev = dev->bus->self;
|
||||
}
|
||||
@ -2058,111 +2059,177 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
|
||||
EXPORT_SYMBOL(pci_set_dma_seg_boundary);
|
||||
#endif
|
||||
|
||||
static int __pcie_flr(struct pci_dev *dev, int probe)
|
||||
static int pcie_flr(struct pci_dev *dev, int probe)
|
||||
{
|
||||
u16 status;
|
||||
int i;
|
||||
int pos;
|
||||
u32 cap;
|
||||
int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP);
|
||||
u16 status;
|
||||
|
||||
if (!exppos)
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
|
||||
if (!pos)
|
||||
return -ENOTTY;
|
||||
pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
|
||||
if (!(cap & PCI_EXP_DEVCAP_FLR))
|
||||
return -ENOTTY;
|
||||
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
pci_block_user_cfg_access(dev);
|
||||
|
||||
/* Wait for Transaction Pending bit clean */
|
||||
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
|
||||
if (!(status & PCI_EXP_DEVSTA_TRPND))
|
||||
goto transaction_done;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i)
|
||||
msleep((1 << (i - 1)) * 100);
|
||||
|
||||
msleep(100);
|
||||
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
|
||||
if (!(status & PCI_EXP_DEVSTA_TRPND))
|
||||
goto transaction_done;
|
||||
pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
|
||||
if (!(status & PCI_EXP_DEVSTA_TRPND))
|
||||
goto clear;
|
||||
}
|
||||
|
||||
dev_info(&dev->dev, "Busy after 100ms while trying to reset; "
|
||||
"sleeping for 1 second\n");
|
||||
ssleep(1);
|
||||
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
|
||||
if (status & PCI_EXP_DEVSTA_TRPND)
|
||||
dev_info(&dev->dev, "Still busy after 1s; "
|
||||
"proceeding with reset anyway\n");
|
||||
dev_err(&dev->dev, "transaction is not cleared; "
|
||||
"proceeding with reset anyway\n");
|
||||
|
||||
transaction_done:
|
||||
pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
|
||||
clear:
|
||||
pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_BCR_FLR);
|
||||
mdelay(100);
|
||||
msleep(100);
|
||||
|
||||
pci_unblock_user_cfg_access(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __pci_af_flr(struct pci_dev *dev, int probe)
|
||||
static int pci_af_flr(struct pci_dev *dev, int probe)
|
||||
{
|
||||
int cappos = pci_find_capability(dev, PCI_CAP_ID_AF);
|
||||
u8 status;
|
||||
int i;
|
||||
int pos;
|
||||
u8 cap;
|
||||
u8 status;
|
||||
|
||||
if (!cappos)
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_AF);
|
||||
if (!pos)
|
||||
return -ENOTTY;
|
||||
pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap);
|
||||
|
||||
pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap);
|
||||
if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
|
||||
return -ENOTTY;
|
||||
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
pci_block_user_cfg_access(dev);
|
||||
|
||||
/* Wait for Transaction Pending bit clean */
|
||||
pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
|
||||
if (!(status & PCI_AF_STATUS_TP))
|
||||
goto transaction_done;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i)
|
||||
msleep((1 << (i - 1)) * 100);
|
||||
|
||||
pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status);
|
||||
if (!(status & PCI_AF_STATUS_TP))
|
||||
goto clear;
|
||||
}
|
||||
|
||||
dev_err(&dev->dev, "transaction is not cleared; "
|
||||
"proceeding with reset anyway\n");
|
||||
|
||||
clear:
|
||||
pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
|
||||
msleep(100);
|
||||
pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
|
||||
if (!(status & PCI_AF_STATUS_TP))
|
||||
goto transaction_done;
|
||||
|
||||
dev_info(&dev->dev, "Busy after 100ms while trying to"
|
||||
" reset; sleeping for 1 second\n");
|
||||
ssleep(1);
|
||||
pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
|
||||
if (status & PCI_AF_STATUS_TP)
|
||||
dev_info(&dev->dev, "Still busy after 1s; "
|
||||
"proceeding with reset anyway\n");
|
||||
|
||||
transaction_done:
|
||||
pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
|
||||
mdelay(100);
|
||||
|
||||
pci_unblock_user_cfg_access(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __pci_reset_function(struct pci_dev *pdev, int probe)
|
||||
static int pci_pm_reset(struct pci_dev *dev, int probe)
|
||||
{
|
||||
int res;
|
||||
u16 csr;
|
||||
|
||||
res = __pcie_flr(pdev, probe);
|
||||
if (res != -ENOTTY)
|
||||
return res;
|
||||
if (!dev->pm_cap)
|
||||
return -ENOTTY;
|
||||
|
||||
res = __pci_af_flr(pdev, probe);
|
||||
if (res != -ENOTTY)
|
||||
return res;
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr);
|
||||
if (csr & PCI_PM_CTRL_NO_SOFT_RESET)
|
||||
return -ENOTTY;
|
||||
|
||||
return res;
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
if (dev->current_state != PCI_D0)
|
||||
return -EINVAL;
|
||||
|
||||
csr &= ~PCI_PM_CTRL_STATE_MASK;
|
||||
csr |= PCI_D3hot;
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
|
||||
msleep(pci_pm_d3_delay);
|
||||
|
||||
csr &= ~PCI_PM_CTRL_STATE_MASK;
|
||||
csr |= PCI_D0;
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
|
||||
msleep(pci_pm_d3_delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
|
||||
{
|
||||
u16 ctrl;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (dev->subordinate)
|
||||
return -ENOTTY;
|
||||
|
||||
list_for_each_entry(pdev, &dev->bus->devices, bus_list)
|
||||
if (pdev != dev)
|
||||
return -ENOTTY;
|
||||
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
|
||||
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
||||
pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
|
||||
msleep(100);
|
||||
|
||||
ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
||||
pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
|
||||
msleep(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_dev_reset(struct pci_dev *dev, int probe)
|
||||
{
|
||||
int rc;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!probe) {
|
||||
pci_block_user_cfg_access(dev);
|
||||
/* block PM suspend, driver probe, etc. */
|
||||
down(&dev->dev.sem);
|
||||
}
|
||||
|
||||
rc = pcie_flr(dev, probe);
|
||||
if (rc != -ENOTTY)
|
||||
goto done;
|
||||
|
||||
rc = pci_af_flr(dev, probe);
|
||||
if (rc != -ENOTTY)
|
||||
goto done;
|
||||
|
||||
rc = pci_pm_reset(dev, probe);
|
||||
if (rc != -ENOTTY)
|
||||
goto done;
|
||||
|
||||
rc = pci_parent_bus_reset(dev, probe);
|
||||
done:
|
||||
if (!probe) {
|
||||
up(&dev->dev.sem);
|
||||
pci_unblock_user_cfg_access(dev);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_execute_reset_function() - Reset a PCI device function
|
||||
* @dev: Device function to reset
|
||||
* __pci_reset_function - reset a PCI device function
|
||||
* @dev: PCI device to reset
|
||||
*
|
||||
* Some devices allow an individual function to be reset without affecting
|
||||
* other functions in the same device. The PCI device must be responsive
|
||||
@ -2174,18 +2241,18 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe)
|
||||
* device including MSI, bus mastering, BARs, decoding IO and memory spaces,
|
||||
* etc.
|
||||
*
|
||||
* Returns 0 if the device function was successfully reset or -ENOTTY if the
|
||||
* Returns 0 if the device function was successfully reset or negative if the
|
||||
* device doesn't support resetting a single function.
|
||||
*/
|
||||
int pci_execute_reset_function(struct pci_dev *dev)
|
||||
int __pci_reset_function(struct pci_dev *dev)
|
||||
{
|
||||
return __pci_reset_function(dev, 0);
|
||||
return pci_dev_reset(dev, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_execute_reset_function);
|
||||
EXPORT_SYMBOL_GPL(__pci_reset_function);
|
||||
|
||||
/**
|
||||
* pci_reset_function() - quiesce and reset a PCI device function
|
||||
* @dev: Device function to reset
|
||||
* pci_reset_function - quiesce and reset a PCI device function
|
||||
* @dev: PCI device to reset
|
||||
*
|
||||
* Some devices allow an individual function to be reset without affecting
|
||||
* other functions in the same device. The PCI device must be responsive
|
||||
@ -2193,32 +2260,33 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function);
|
||||
*
|
||||
* This function does not just reset the PCI portion of a device, but
|
||||
* clears all the state associated with the device. This function differs
|
||||
* from pci_execute_reset_function in that it saves and restores device state
|
||||
* from __pci_reset_function in that it saves and restores device state
|
||||
* over the reset.
|
||||
*
|
||||
* Returns 0 if the device function was successfully reset or -ENOTTY if the
|
||||
* Returns 0 if the device function was successfully reset or negative if the
|
||||
* device doesn't support resetting a single function.
|
||||
*/
|
||||
int pci_reset_function(struct pci_dev *dev)
|
||||
{
|
||||
int r = __pci_reset_function(dev, 1);
|
||||
int rc;
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
rc = pci_dev_reset(dev, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0)
|
||||
disable_irq(dev->irq);
|
||||
pci_save_state(dev);
|
||||
|
||||
/*
|
||||
* both INTx and MSI are disabled after the Interrupt Disable bit
|
||||
* is set and the Bus Master bit is cleared.
|
||||
*/
|
||||
pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
||||
|
||||
r = pci_execute_reset_function(dev);
|
||||
rc = pci_dev_reset(dev, 0);
|
||||
|
||||
pci_restore_state(dev);
|
||||
if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0)
|
||||
enable_irq(dev->irq);
|
||||
|
||||
return r;
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_reset_function);
|
||||
|
||||
@ -2591,6 +2659,8 @@ static int __init pci_setup(char *str)
|
||||
} else if (!strncmp(str, "resource_alignment=", 19)) {
|
||||
pci_set_resource_alignment_param(str + 19,
|
||||
strlen(str + 19));
|
||||
} else if (!strncmp(str, "ecrc=", 5)) {
|
||||
pcie_ecrc_get_policy(str + 5);
|
||||
} else {
|
||||
printk(KERN_ERR "PCI: Unknown option `%s'\n",
|
||||
str);
|
||||
|
@ -10,3 +10,18 @@ config PCIEAER
|
||||
This enables PCI Express Root Port Advanced Error Reporting
|
||||
(AER) driver support. Error reporting messages sent to Root
|
||||
Port will be handled by PCI Express AER driver.
|
||||
|
||||
|
||||
#
|
||||
# PCI Express ECRC
|
||||
#
|
||||
config PCIE_ECRC
|
||||
bool "PCI Express ECRC settings control"
|
||||
depends on PCIEAER
|
||||
help
|
||||
Used to override firmware/bios settings for PCI Express ECRC
|
||||
(transaction layer end-to-end CRC checking).
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
source "drivers/pci/pcie/aer/Kconfig.debug"
|
||||
|
18
drivers/pci/pcie/aer/Kconfig.debug
Normal file
18
drivers/pci/pcie/aer/Kconfig.debug
Normal file
@ -0,0 +1,18 @@
|
||||
#
|
||||
# PCI Express Root Port Device AER Debug Configuration
|
||||
#
|
||||
|
||||
config PCIEAER_INJECT
|
||||
tristate "PCIE AER error injector support"
|
||||
depends on PCIEAER
|
||||
default n
|
||||
help
|
||||
This enables PCI Express Root Port Advanced Error Reporting
|
||||
(AER) software error injector.
|
||||
|
||||
Debuging PCIE AER code is quite difficult because it is hard
|
||||
to trigger various real hardware errors. Software based
|
||||
error injection can fake almost all kinds of errors with the
|
||||
help of a user space helper tool aer-inject, which can be
|
||||
gotten from:
|
||||
http://www.kernel.org/pub/linux/utils/pci/aer-inject/
|
@ -4,6 +4,9 @@
|
||||
|
||||
obj-$(CONFIG_PCIEAER) += aerdriver.o
|
||||
|
||||
obj-$(CONFIG_PCIE_ECRC) += ecrc.o
|
||||
|
||||
aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o
|
||||
aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o
|
||||
|
||||
obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
|
||||
|
473
drivers/pci/pcie/aer/aer_inject.c
Normal file
473
drivers/pci/pcie/aer/aer_inject.c
Normal file
@ -0,0 +1,473 @@
|
||||
/*
|
||||
* PCIE AER software error injection support.
|
||||
*
|
||||
* Debuging PCIE AER code is quite difficult because it is hard to
|
||||
* trigger various real hardware errors. Software based error
|
||||
* injection can fake almost all kinds of errors with the help of a
|
||||
* user space helper tool aer-inject, which can be gotten from:
|
||||
* http://www.kernel.org/pub/linux/utils/pci/aer-inject/
|
||||
*
|
||||
* Copyright 2009 Intel Corporation.
|
||||
* Huang Ying <ying.huang@intel.com>
|
||||
*
|
||||
* 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; version 2
|
||||
* of the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "aerdrv.h"
|
||||
|
||||
struct aer_error_inj
|
||||
{
|
||||
u8 bus;
|
||||
u8 dev;
|
||||
u8 fn;
|
||||
u32 uncor_status;
|
||||
u32 cor_status;
|
||||
u32 header_log0;
|
||||
u32 header_log1;
|
||||
u32 header_log2;
|
||||
u32 header_log3;
|
||||
};
|
||||
|
||||
struct aer_error
|
||||
{
|
||||
struct list_head list;
|
||||
unsigned int bus;
|
||||
unsigned int devfn;
|
||||
int pos_cap_err;
|
||||
|
||||
u32 uncor_status;
|
||||
u32 cor_status;
|
||||
u32 header_log0;
|
||||
u32 header_log1;
|
||||
u32 header_log2;
|
||||
u32 header_log3;
|
||||
u32 root_status;
|
||||
u32 source_id;
|
||||
};
|
||||
|
||||
struct pci_bus_ops
|
||||
{
|
||||
struct list_head list;
|
||||
struct pci_bus *bus;
|
||||
struct pci_ops *ops;
|
||||
};
|
||||
|
||||
static LIST_HEAD(einjected);
|
||||
|
||||
static LIST_HEAD(pci_bus_ops_list);
|
||||
|
||||
/* Protect einjected and pci_bus_ops_list */
|
||||
static DEFINE_SPINLOCK(inject_lock);
|
||||
|
||||
static void aer_error_init(struct aer_error *err, unsigned int bus,
|
||||
unsigned int devfn, int pos_cap_err)
|
||||
{
|
||||
INIT_LIST_HEAD(&err->list);
|
||||
err->bus = bus;
|
||||
err->devfn = devfn;
|
||||
err->pos_cap_err = pos_cap_err;
|
||||
}
|
||||
|
||||
/* inject_lock must be held before calling */
|
||||
static struct aer_error *__find_aer_error(unsigned int bus, unsigned int devfn)
|
||||
{
|
||||
struct aer_error *err;
|
||||
|
||||
list_for_each_entry(err, &einjected, list) {
|
||||
if (bus == err->bus && devfn == err->devfn)
|
||||
return err;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* inject_lock must be held before calling */
|
||||
static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
|
||||
{
|
||||
return __find_aer_error(dev->bus->number, dev->devfn);
|
||||
}
|
||||
|
||||
/* inject_lock must be held before calling */
|
||||
static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_bus_ops *bus_ops;
|
||||
|
||||
list_for_each_entry(bus_ops, &pci_bus_ops_list, list) {
|
||||
if (bus_ops->bus == bus)
|
||||
return bus_ops->ops;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct pci_bus_ops *pci_bus_ops_pop(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct pci_bus_ops *bus_ops = NULL;
|
||||
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
if (list_empty(&pci_bus_ops_list))
|
||||
bus_ops = NULL;
|
||||
else {
|
||||
struct list_head *lh = pci_bus_ops_list.next;
|
||||
list_del(lh);
|
||||
bus_ops = list_entry(lh, struct pci_bus_ops, list);
|
||||
}
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
return bus_ops;
|
||||
}
|
||||
|
||||
static u32 *find_pci_config_dword(struct aer_error *err, int where,
|
||||
int *prw1cs)
|
||||
{
|
||||
int rw1cs = 0;
|
||||
u32 *target = NULL;
|
||||
|
||||
if (err->pos_cap_err == -1)
|
||||
return NULL;
|
||||
|
||||
switch (where - err->pos_cap_err) {
|
||||
case PCI_ERR_UNCOR_STATUS:
|
||||
target = &err->uncor_status;
|
||||
rw1cs = 1;
|
||||
break;
|
||||
case PCI_ERR_COR_STATUS:
|
||||
target = &err->cor_status;
|
||||
rw1cs = 1;
|
||||
break;
|
||||
case PCI_ERR_HEADER_LOG:
|
||||
target = &err->header_log0;
|
||||
break;
|
||||
case PCI_ERR_HEADER_LOG+4:
|
||||
target = &err->header_log1;
|
||||
break;
|
||||
case PCI_ERR_HEADER_LOG+8:
|
||||
target = &err->header_log2;
|
||||
break;
|
||||
case PCI_ERR_HEADER_LOG+12:
|
||||
target = &err->header_log3;
|
||||
break;
|
||||
case PCI_ERR_ROOT_STATUS:
|
||||
target = &err->root_status;
|
||||
rw1cs = 1;
|
||||
break;
|
||||
case PCI_ERR_ROOT_COR_SRC:
|
||||
target = &err->source_id;
|
||||
break;
|
||||
}
|
||||
if (prw1cs)
|
||||
*prw1cs = rw1cs;
|
||||
return target;
|
||||
}
|
||||
|
||||
static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
int size, u32 *val)
|
||||
{
|
||||
u32 *sim;
|
||||
struct aer_error *err;
|
||||
unsigned long flags;
|
||||
struct pci_ops *ops;
|
||||
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
if (size != sizeof(u32))
|
||||
goto out;
|
||||
err = __find_aer_error(bus->number, devfn);
|
||||
if (!err)
|
||||
goto out;
|
||||
|
||||
sim = find_pci_config_dword(err, where, NULL);
|
||||
if (sim) {
|
||||
*val = *sim;
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
out:
|
||||
ops = __find_pci_bus_ops(bus);
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
return ops->read(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, int size,
|
||||
u32 val)
|
||||
{
|
||||
u32 *sim;
|
||||
struct aer_error *err;
|
||||
unsigned long flags;
|
||||
int rw1cs;
|
||||
struct pci_ops *ops;
|
||||
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
if (size != sizeof(u32))
|
||||
goto out;
|
||||
err = __find_aer_error(bus->number, devfn);
|
||||
if (!err)
|
||||
goto out;
|
||||
|
||||
sim = find_pci_config_dword(err, where, &rw1cs);
|
||||
if (sim) {
|
||||
if (rw1cs)
|
||||
*sim ^= val;
|
||||
else
|
||||
*sim = val;
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
out:
|
||||
ops = __find_pci_bus_ops(bus);
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
return ops->write(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static struct pci_ops pci_ops_aer = {
|
||||
.read = pci_read_aer,
|
||||
.write = pci_write_aer,
|
||||
};
|
||||
|
||||
static void pci_bus_ops_init(struct pci_bus_ops *bus_ops,
|
||||
struct pci_bus *bus,
|
||||
struct pci_ops *ops)
|
||||
{
|
||||
INIT_LIST_HEAD(&bus_ops->list);
|
||||
bus_ops->bus = bus;
|
||||
bus_ops->ops = ops;
|
||||
}
|
||||
|
||||
static int pci_bus_set_aer_ops(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_ops *ops;
|
||||
struct pci_bus_ops *bus_ops;
|
||||
unsigned long flags;
|
||||
|
||||
bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
|
||||
if (!bus_ops)
|
||||
return -ENOMEM;
|
||||
ops = pci_bus_set_ops(bus, &pci_ops_aer);
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
if (ops == &pci_ops_aer)
|
||||
goto out;
|
||||
pci_bus_ops_init(bus_ops, bus, ops);
|
||||
list_add(&bus_ops->list, &pci_bus_ops_list);
|
||||
bus_ops = NULL;
|
||||
out:
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
if (bus_ops)
|
||||
kfree(bus_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
|
||||
{
|
||||
while (1) {
|
||||
if (!dev->is_pcie)
|
||||
break;
|
||||
if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
|
||||
return dev;
|
||||
if (!dev->bus->self)
|
||||
break;
|
||||
dev = dev->bus->self;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_aer_device_iter(struct device *device, void *data)
|
||||
{
|
||||
struct pcie_device **result = data;
|
||||
struct pcie_device *pcie_dev;
|
||||
|
||||
if (device->bus == &pcie_port_bus_type) {
|
||||
pcie_dev = to_pcie_device(device);
|
||||
if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
|
||||
*result = pcie_dev;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
|
||||
{
|
||||
return device_for_each_child(&dev->dev, result, find_aer_device_iter);
|
||||
}
|
||||
|
||||
static int aer_inject(struct aer_error_inj *einj)
|
||||
{
|
||||
struct aer_error *err, *rperr;
|
||||
struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
|
||||
struct pci_dev *dev, *rpdev;
|
||||
struct pcie_device *edev;
|
||||
unsigned long flags;
|
||||
unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
|
||||
int pos_cap_err, rp_pos_cap_err;
|
||||
u32 sever;
|
||||
int ret = 0;
|
||||
|
||||
dev = pci_get_bus_and_slot(einj->bus, devfn);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
rpdev = pcie_find_root_port(dev);
|
||||
if (!rpdev) {
|
||||
ret = -EINVAL;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos_cap_err) {
|
||||
ret = -EIO;
|
||||
goto out_put;
|
||||
}
|
||||
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
|
||||
|
||||
rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!rp_pos_cap_err) {
|
||||
ret = -EIO;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
err_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL);
|
||||
if (!err_alloc) {
|
||||
ret = -ENOMEM;
|
||||
goto out_put;
|
||||
}
|
||||
rperr_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL);
|
||||
if (!rperr_alloc) {
|
||||
ret = -ENOMEM;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
|
||||
err = __find_aer_error_by_dev(dev);
|
||||
if (!err) {
|
||||
err = err_alloc;
|
||||
err_alloc = NULL;
|
||||
aer_error_init(err, einj->bus, devfn, pos_cap_err);
|
||||
list_add(&err->list, &einjected);
|
||||
}
|
||||
err->uncor_status |= einj->uncor_status;
|
||||
err->cor_status |= einj->cor_status;
|
||||
err->header_log0 = einj->header_log0;
|
||||
err->header_log1 = einj->header_log1;
|
||||
err->header_log2 = einj->header_log2;
|
||||
err->header_log3 = einj->header_log3;
|
||||
|
||||
rperr = __find_aer_error_by_dev(rpdev);
|
||||
if (!rperr) {
|
||||
rperr = rperr_alloc;
|
||||
rperr_alloc = NULL;
|
||||
aer_error_init(rperr, rpdev->bus->number, rpdev->devfn,
|
||||
rp_pos_cap_err);
|
||||
list_add(&rperr->list, &einjected);
|
||||
}
|
||||
if (einj->cor_status) {
|
||||
if (rperr->root_status & PCI_ERR_ROOT_COR_RCV)
|
||||
rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
|
||||
else
|
||||
rperr->root_status |= PCI_ERR_ROOT_COR_RCV;
|
||||
rperr->source_id &= 0xffff0000;
|
||||
rperr->source_id |= (einj->bus << 8) | devfn;
|
||||
}
|
||||
if (einj->uncor_status) {
|
||||
if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)
|
||||
rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
|
||||
if (sever & einj->uncor_status) {
|
||||
rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV;
|
||||
if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV))
|
||||
rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL;
|
||||
} else
|
||||
rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
|
||||
rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV;
|
||||
rperr->source_id &= 0x0000ffff;
|
||||
rperr->source_id |= ((einj->bus << 8) | devfn) << 16;
|
||||
}
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
|
||||
ret = pci_bus_set_aer_ops(dev->bus);
|
||||
if (ret)
|
||||
goto out_put;
|
||||
ret = pci_bus_set_aer_ops(rpdev->bus);
|
||||
if (ret)
|
||||
goto out_put;
|
||||
|
||||
if (find_aer_device(rpdev, &edev))
|
||||
aer_irq(-1, edev);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_put:
|
||||
if (err_alloc)
|
||||
kfree(err_alloc);
|
||||
if (rperr_alloc)
|
||||
kfree(rperr_alloc);
|
||||
pci_dev_put(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
|
||||
size_t usize, loff_t *off)
|
||||
{
|
||||
struct aer_error_inj einj;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (usize != sizeof(struct aer_error_inj))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&einj, ubuf, usize))
|
||||
return -EFAULT;
|
||||
|
||||
ret = aer_inject(&einj);
|
||||
return ret ? ret : usize;
|
||||
}
|
||||
|
||||
static const struct file_operations aer_inject_fops = {
|
||||
.write = aer_inject_write,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct miscdevice aer_inject_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "aer_inject",
|
||||
.fops = &aer_inject_fops,
|
||||
};
|
||||
|
||||
static int __init aer_inject_init(void)
|
||||
{
|
||||
return misc_register(&aer_inject_device);
|
||||
}
|
||||
|
||||
static void __exit aer_inject_exit(void)
|
||||
{
|
||||
struct aer_error *err, *err_next;
|
||||
unsigned long flags;
|
||||
struct pci_bus_ops *bus_ops;
|
||||
|
||||
misc_deregister(&aer_inject_device);
|
||||
|
||||
while ((bus_ops = pci_bus_ops_pop())) {
|
||||
pci_bus_set_ops(bus_ops->bus, bus_ops->ops);
|
||||
kfree(bus_ops);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&inject_lock, flags);
|
||||
list_for_each_entry_safe(err, err_next,
|
||||
&pci_bus_ops_list, list) {
|
||||
list_del(&err->list);
|
||||
kfree(err);
|
||||
}
|
||||
spin_unlock_irqrestore(&inject_lock, flags);
|
||||
}
|
||||
|
||||
module_init(aer_inject_init);
|
||||
module_exit(aer_inject_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCIE AER software error injector");
|
||||
MODULE_LICENSE("GPL");
|
@ -77,7 +77,7 @@ void pci_no_aer(void)
|
||||
*
|
||||
* Invoked when Root Port detects AER messages.
|
||||
**/
|
||||
static irqreturn_t aer_irq(int irq, void *context)
|
||||
irqreturn_t aer_irq(int irq, void *context)
|
||||
{
|
||||
unsigned int status, id;
|
||||
struct pcie_device *pdev = (struct pcie_device *)context;
|
||||
@ -126,6 +126,7 @@ static irqreturn_t aer_irq(int irq, void *context)
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aer_irq);
|
||||
|
||||
/**
|
||||
* aer_alloc_rpc - allocate Root Port data structure
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pcieport_if.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define AER_NONFATAL 0
|
||||
#define AER_FATAL 1
|
||||
@ -56,7 +57,11 @@ struct header_log_regs {
|
||||
unsigned int dw3;
|
||||
};
|
||||
|
||||
#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */
|
||||
struct aer_err_info {
|
||||
struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
|
||||
int error_dev_num;
|
||||
u16 id;
|
||||
int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */
|
||||
int flags;
|
||||
unsigned int status; /* COR/UNCOR Error Status */
|
||||
@ -120,6 +125,7 @@ extern void aer_delete_rootport(struct aer_rpc *rpc);
|
||||
extern int aer_init(struct pcie_device *dev);
|
||||
extern void aer_isr(struct work_struct *work);
|
||||
extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
||||
extern irqreturn_t aer_irq(int irq, void *context);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int aer_osc_setup(struct pcie_device *pciedev);
|
||||
|
@ -26,7 +26,9 @@
|
||||
#include "aerdrv.h"
|
||||
|
||||
static int forceload;
|
||||
static int nosourceid;
|
||||
module_param(forceload, bool, 0);
|
||||
module_param(nosourceid, bool, 0);
|
||||
|
||||
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
||||
{
|
||||
@ -109,19 +111,23 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
|
||||
#endif /* 0 */
|
||||
|
||||
|
||||
static void set_device_error_reporting(struct pci_dev *dev, void *data)
|
||||
static int 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 (dev->pcie_type == PCIE_RC_PORT ||
|
||||
dev->pcie_type == PCIE_SW_UPSTREAM_PORT ||
|
||||
dev->pcie_type == PCIE_SW_DOWNSTREAM_PORT) {
|
||||
if (enable)
|
||||
pci_enable_pcie_error_reporting(dev);
|
||||
else
|
||||
pci_disable_pcie_error_reporting(dev);
|
||||
}
|
||||
|
||||
if (enable)
|
||||
pci_enable_pcie_error_reporting(dev);
|
||||
else
|
||||
pci_disable_pcie_error_reporting(dev);
|
||||
pcie_set_ecrc_checking(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,73 +145,148 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev,
|
||||
pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
|
||||
}
|
||||
|
||||
static int find_device_iter(struct device *device, void *data)
|
||||
static inline int compare_device_id(struct pci_dev *dev,
|
||||
struct aer_err_info *e_info)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
u16 id = *(unsigned long *)data;
|
||||
u8 secondary, subordinate, d_bus = id >> 8;
|
||||
|
||||
if (device->bus == &pci_bus_type) {
|
||||
dev = to_pci_dev(device);
|
||||
if (id == ((dev->bus->number << 8) | dev->devfn)) {
|
||||
/*
|
||||
* Device ID match
|
||||
*/
|
||||
*(unsigned long*)data = (unsigned long)device;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) {
|
||||
/*
|
||||
* If device is P2P, check if it is an upstream?
|
||||
* Device ID match
|
||||
*/
|
||||
if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
|
||||
pci_read_config_byte(dev, PCI_SECONDARY_BUS,
|
||||
&secondary);
|
||||
pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
|
||||
&subordinate);
|
||||
if (d_bus >= secondary && d_bus <= subordinate) {
|
||||
*(unsigned long*)data = (unsigned long)device;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
e_info->dev[e_info->error_dev_num] = dev;
|
||||
e_info->error_dev_num++;
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define PCI_BUS(x) (((x) >> 8) & 0xff)
|
||||
|
||||
static int find_device_iter(struct pci_dev *dev, void *data)
|
||||
{
|
||||
int pos;
|
||||
u32 status;
|
||||
u32 mask;
|
||||
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
|
||||
* reported by root port.
|
||||
*/
|
||||
if (!nosourceid && (PCI_BUS(e_info->id) != 0)) {
|
||||
result = compare_device_id(dev, e_info);
|
||||
if (result)
|
||||
add_error_device(e_info, dev);
|
||||
|
||||
/*
|
||||
* If there is no multiple error, we stop
|
||||
* or continue based on the id comparing.
|
||||
*/
|
||||
if (!(e_info->flags & AER_MULTI_ERROR_VALID_FLAG))
|
||||
return result;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* When either
|
||||
* 1) nosourceid==y;
|
||||
* 2) bus id is equal to 0. Some ports might lose the bus
|
||||
* id of error source id;
|
||||
* 3) There are multiple errors and prior id comparing fails;
|
||||
* We check AER status registers to find the initial reporter.
|
||||
*/
|
||||
if (atomic_read(&dev->enable_cnt) == 0)
|
||||
return 0;
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
|
||||
if (!pos)
|
||||
return 0;
|
||||
/* Check if AER is enabled */
|
||||
pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16);
|
||||
if (!(reg16 & (
|
||||
PCI_EXP_DEVCTL_CERE |
|
||||
PCI_EXP_DEVCTL_NFERE |
|
||||
PCI_EXP_DEVCTL_FERE |
|
||||
PCI_EXP_DEVCTL_URRE)))
|
||||
return 0;
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
status = 0;
|
||||
mask = 0;
|
||||
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_MASK,
|
||||
&mask);
|
||||
if (status & ERR_CORRECTABLE_ERROR_MASK & ~mask) {
|
||||
add_error_device(e_info, dev);
|
||||
goto added;
|
||||
}
|
||||
} else {
|
||||
pci_read_config_dword(dev,
|
||||
pos + PCI_ERR_UNCOR_STATUS,
|
||||
&status);
|
||||
pci_read_config_dword(dev,
|
||||
pos + PCI_ERR_UNCOR_MASK,
|
||||
&mask);
|
||||
if (status & ERR_UNCORRECTABLE_ERROR_MASK & ~mask) {
|
||||
add_error_device(e_info, dev);
|
||||
goto added;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
added:
|
||||
if (e_info->flags & AER_MULTI_ERROR_VALID_FLAG)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_source_device - search through device hierarchy for source device
|
||||
* @parent: pointer to Root Port pci_dev data structure
|
||||
* @id: device ID of agent who sends an error message to this Root Port
|
||||
* @err_info: including detailed error information such like id
|
||||
*
|
||||
* Invoked when error is detected at the Root Port.
|
||||
*/
|
||||
static struct device* find_source_device(struct pci_dev *parent, u16 id)
|
||||
static void find_source_device(struct pci_dev *parent,
|
||||
struct aer_err_info *e_info)
|
||||
{
|
||||
struct pci_dev *dev = parent;
|
||||
struct device *device;
|
||||
unsigned long device_addr;
|
||||
int status;
|
||||
int result;
|
||||
|
||||
/* Is Root Port an agent that sends error message? */
|
||||
if (id == ((dev->bus->number << 8) | dev->devfn))
|
||||
return &dev->dev;
|
||||
result = find_device_iter(dev, e_info);
|
||||
if (result)
|
||||
return;
|
||||
|
||||
do {
|
||||
device_addr = id;
|
||||
if ((status = device_for_each_child(&dev->dev,
|
||||
&device_addr, find_device_iter))) {
|
||||
device = (struct device*)device_addr;
|
||||
dev = to_pci_dev(device);
|
||||
if (id == ((dev->bus->number << 8) | dev->devfn))
|
||||
return device;
|
||||
}
|
||||
}while (status);
|
||||
|
||||
return NULL;
|
||||
pci_walk_bus(parent->subordinate, find_device_iter, e_info);
|
||||
}
|
||||
|
||||
static void report_error_detected(struct pci_dev *dev, void *data)
|
||||
static int report_error_detected(struct pci_dev *dev, void *data)
|
||||
{
|
||||
pci_ers_result_t vote;
|
||||
struct pci_error_handlers *err_handler;
|
||||
@ -230,16 +311,16 @@ static void report_error_detected(struct pci_dev *dev, void *data)
|
||||
dev->driver ?
|
||||
"no AER-aware driver" : "no driver");
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_handler = dev->driver->err_handler;
|
||||
vote = err_handler->error_detected(dev, result_data->state);
|
||||
result_data->result = merge_result(result_data->result, vote);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_mmio_enabled(struct pci_dev *dev, void *data)
|
||||
static int report_mmio_enabled(struct pci_dev *dev, void *data)
|
||||
{
|
||||
pci_ers_result_t vote;
|
||||
struct pci_error_handlers *err_handler;
|
||||
@ -249,15 +330,15 @@ static void report_mmio_enabled(struct pci_dev *dev, void *data)
|
||||
if (!dev->driver ||
|
||||
!dev->driver->err_handler ||
|
||||
!dev->driver->err_handler->mmio_enabled)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
err_handler = dev->driver->err_handler;
|
||||
vote = err_handler->mmio_enabled(dev);
|
||||
result_data->result = merge_result(result_data->result, vote);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_slot_reset(struct pci_dev *dev, void *data)
|
||||
static int report_slot_reset(struct pci_dev *dev, void *data)
|
||||
{
|
||||
pci_ers_result_t vote;
|
||||
struct pci_error_handlers *err_handler;
|
||||
@ -267,15 +348,15 @@ static void report_slot_reset(struct pci_dev *dev, void *data)
|
||||
if (!dev->driver ||
|
||||
!dev->driver->err_handler ||
|
||||
!dev->driver->err_handler->slot_reset)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
err_handler = dev->driver->err_handler;
|
||||
vote = err_handler->slot_reset(dev);
|
||||
result_data->result = merge_result(result_data->result, vote);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_resume(struct pci_dev *dev, void *data)
|
||||
static int report_resume(struct pci_dev *dev, void *data)
|
||||
{
|
||||
struct pci_error_handlers *err_handler;
|
||||
|
||||
@ -284,11 +365,11 @@ static void report_resume(struct pci_dev *dev, void *data)
|
||||
if (!dev->driver ||
|
||||
!dev->driver->err_handler ||
|
||||
!dev->driver->err_handler->resume)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
err_handler = dev->driver->err_handler;
|
||||
err_handler->resume(dev);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,7 +386,7 @@ static void report_resume(struct pci_dev *dev, void *data)
|
||||
static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
|
||||
enum pci_channel_state state,
|
||||
char *error_mesg,
|
||||
void (*cb)(struct pci_dev *, void *))
|
||||
int (*cb)(struct pci_dev *, void *))
|
||||
{
|
||||
struct aer_broadcast_data result_data;
|
||||
|
||||
@ -497,12 +578,12 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
|
||||
*/
|
||||
static void handle_error_source(struct pcie_device * aerdev,
|
||||
struct pci_dev *dev,
|
||||
struct aer_err_info info)
|
||||
struct aer_err_info *info)
|
||||
{
|
||||
pci_ers_result_t status = 0;
|
||||
int pos;
|
||||
|
||||
if (info.severity == AER_CORRECTABLE) {
|
||||
if (info->severity == AER_CORRECTABLE) {
|
||||
/*
|
||||
* Correctable error does not need software intevention.
|
||||
* No need to go through error recovery process.
|
||||
@ -510,9 +591,9 @@ static void handle_error_source(struct pcie_device * aerdev,
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (pos)
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
|
||||
info.status);
|
||||
info->status);
|
||||
} else {
|
||||
status = do_recovery(aerdev, dev, info.severity);
|
||||
status = do_recovery(aerdev, dev, info->severity);
|
||||
if (status == PCI_ERS_RESULT_RECOVERED) {
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
|
||||
"successfully recovered\n");
|
||||
@ -661,6 +742,28 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
||||
return AER_SUCCESS;
|
||||
}
|
||||
|
||||
static inline void aer_process_err_devices(struct pcie_device *p_device,
|
||||
struct aer_err_info *e_info)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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) ==
|
||||
AER_SUCCESS) {
|
||||
aer_print_error(e_info->dev[i], e_info);
|
||||
handle_error_source(p_device,
|
||||
e_info->dev[i],
|
||||
e_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aer_isr_one_error - consume an error detected by root port
|
||||
* @p_device: pointer to error root port service device
|
||||
@ -669,10 +772,16 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
||||
static void aer_isr_one_error(struct pcie_device *p_device,
|
||||
struct aer_err_source *e_src)
|
||||
{
|
||||
struct device *s_device;
|
||||
struct aer_err_info e_info = {0, 0, 0,};
|
||||
struct aer_err_info *e_info;
|
||||
int i;
|
||||
u16 id;
|
||||
|
||||
/* struct aer_err_info might be big, so we allocate it with slab */
|
||||
e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL);
|
||||
if (e_info == NULL) {
|
||||
dev_printk(KERN_DEBUG, &p_device->port->dev,
|
||||
"Can't allocate mem when processing AER errors\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a possibility that both correctable error and
|
||||
@ -684,31 +793,26 @@ static void aer_isr_one_error(struct pcie_device *p_device,
|
||||
if (!(e_src->status & i))
|
||||
continue;
|
||||
|
||||
memset(e_info, 0, sizeof(struct aer_err_info));
|
||||
|
||||
/* Init comprehensive error information */
|
||||
if (i & PCI_ERR_ROOT_COR_RCV) {
|
||||
id = ERR_COR_ID(e_src->id);
|
||||
e_info.severity = AER_CORRECTABLE;
|
||||
e_info->id = ERR_COR_ID(e_src->id);
|
||||
e_info->severity = AER_CORRECTABLE;
|
||||
} else {
|
||||
id = ERR_UNCOR_ID(e_src->id);
|
||||
e_info.severity = ((e_src->status >> 6) & 1);
|
||||
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.flags |= AER_MULTI_ERROR_VALID_FLAG;
|
||||
if (!(s_device = find_source_device(p_device->port, id))) {
|
||||
printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
|
||||
__func__, id);
|
||||
continue;
|
||||
}
|
||||
if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
|
||||
AER_SUCCESS) {
|
||||
aer_print_error(to_pci_dev(s_device), &e_info);
|
||||
handle_error_source(p_device,
|
||||
to_pci_dev(s_device),
|
||||
e_info);
|
||||
}
|
||||
e_info->flags |= AER_MULTI_ERROR_VALID_FLAG;
|
||||
|
||||
find_source_device(p_device->port, e_info);
|
||||
aer_process_err_devices(p_device, e_info);
|
||||
}
|
||||
|
||||
kfree(e_info);
|
||||
}
|
||||
|
||||
/**
|
||||
|
131
drivers/pci/pcie/aer/ecrc.c
Normal file
131
drivers/pci/pcie/aer/ecrc.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Enables/disables PCIe ECRC checking.
|
||||
*
|
||||
* (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
|
||||
* Andrew Patterson <andrew.patterson@hp.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/errno.h>
|
||||
#include "../../pci.h"
|
||||
|
||||
#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */
|
||||
#define ECRC_POLICY_OFF 1 /* ECRC off for performance */
|
||||
#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */
|
||||
|
||||
static int ecrc_policy = ECRC_POLICY_DEFAULT;
|
||||
|
||||
static const char *ecrc_policy_str[] = {
|
||||
[ECRC_POLICY_DEFAULT] = "bios",
|
||||
[ECRC_POLICY_OFF] = "off",
|
||||
[ECRC_POLICY_ON] = "on"
|
||||
};
|
||||
|
||||
/**
|
||||
* enable_ercr_checking - enable PCIe ECRC checking for a device
|
||||
* @dev: the PCI device
|
||||
*
|
||||
* Returns 0 on success, or negative on failure.
|
||||
*/
|
||||
static int enable_ecrc_checking(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u32 reg32;
|
||||
|
||||
if (!dev->is_pcie)
|
||||
return -ENODEV;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return -ENODEV;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
||||
if (reg32 & PCI_ERR_CAP_ECRC_GENC)
|
||||
reg32 |= PCI_ERR_CAP_ECRC_GENE;
|
||||
if (reg32 & PCI_ERR_CAP_ECRC_CHKC)
|
||||
reg32 |= PCI_ERR_CAP_ECRC_CHKE;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable_ercr_checking - disables PCIe ECRC checking for a device
|
||||
* @dev: the PCI device
|
||||
*
|
||||
* Returns 0 on success, or negative on failure.
|
||||
*/
|
||||
static int disable_ecrc_checking(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u32 reg32;
|
||||
|
||||
if (!dev->is_pcie)
|
||||
return -ENODEV;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return -ENODEV;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
||||
reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
void pcie_set_ecrc_checking(struct pci_dev *dev)
|
||||
{
|
||||
switch (ecrc_policy) {
|
||||
case ECRC_POLICY_DEFAULT:
|
||||
return;
|
||||
case ECRC_POLICY_OFF:
|
||||
disable_ecrc_checking(dev);
|
||||
break;
|
||||
case ECRC_POLICY_ON:
|
||||
enable_ecrc_checking(dev);;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_ecrc_get_policy - parse kernel command-line ecrc option
|
||||
*/
|
||||
void pcie_ecrc_get_policy(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++)
|
||||
if (!strncmp(str, ecrc_policy_str[i],
|
||||
strlen(ecrc_policy_str[i])))
|
||||
break;
|
||||
if (i >= ARRAY_SIZE(ecrc_policy_str))
|
||||
return;
|
||||
|
||||
ecrc_policy = i;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -193,7 +193,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
|
||||
if (type == pci_bar_io) {
|
||||
l &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff;
|
||||
mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;
|
||||
} else {
|
||||
l &= PCI_BASE_ADDRESS_MEM_MASK;
|
||||
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
|
||||
@ -237,6 +237,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
dev_printk(KERN_DEBUG, &dev->dev,
|
||||
"reg %x 64bit mmio: %pR\n", pos, res);
|
||||
}
|
||||
|
||||
res->flags |= IORESOURCE_MEM_64;
|
||||
} else {
|
||||
sz = pci_size(l, sz, mask);
|
||||
|
||||
@ -287,7 +289,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
if (!child->parent) /* It's a host bus, nothing to read */
|
||||
if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
|
||||
return;
|
||||
|
||||
if (dev->transparent) {
|
||||
@ -362,7 +364,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
|
||||
}
|
||||
}
|
||||
if (base <= limit) {
|
||||
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
|
||||
IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
if (res->flags & PCI_PREF_RANGE_TYPE_64)
|
||||
res->flags |= IORESOURCE_MEM_64;
|
||||
res->start = base;
|
||||
res->end = limit + 0xfffff;
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n",
|
||||
|
@ -1133,6 +1133,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
|
||||
switch (dev->subsystem_device) {
|
||||
case 0x1751: /* M2N notebook */
|
||||
case 0x1821: /* M5N notebook */
|
||||
case 0x1897: /* A6L notebook */
|
||||
asus_hides_smbus = 1;
|
||||
}
|
||||
else if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
|
||||
@ -1163,6 +1164,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
|
||||
switch (dev->subsystem_device) {
|
||||
case 0x12bc: /* HP D330L */
|
||||
case 0x12bd: /* HP D530 */
|
||||
case 0x006a: /* HP Compaq nx9500 */
|
||||
asus_hides_smbus = 1;
|
||||
}
|
||||
else if (dev->device == PCI_DEVICE_ID_INTEL_82875_HB)
|
||||
@ -2016,6 +2018,28 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
|
||||
PCI_DEVICE_ID_NX2_5709S,
|
||||
quirk_brcm_570x_limit_vpd);
|
||||
|
||||
/* Originally in EDAC sources for i82875P:
|
||||
* Intel tells BIOS developers to hide device 6 which
|
||||
* configures the overflow device access containing
|
||||
* the DRBs - this is where we expose device 6.
|
||||
* http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm
|
||||
*/
|
||||
static void __devinit quirk_unhide_mch_dev6(struct pci_dev *dev)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if (pci_read_config_byte(dev, 0xF4, ®) == 0 && !(reg & 0x02)) {
|
||||
dev_info(&dev->dev, "Enabling MCH 'Overflow' Device\n");
|
||||
pci_write_config_byte(dev, 0xF4, reg | 0x02);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82865_HB,
|
||||
quirk_unhide_mch_dev6);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_HB,
|
||||
quirk_unhide_mch_dev6);
|
||||
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
/* Some chipsets do not support MSI. We cannot easily rely on setting
|
||||
* PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
|
||||
|
@ -32,8 +32,6 @@ static void pci_stop_dev(struct pci_dev *dev)
|
||||
|
||||
static void pci_destroy_dev(struct pci_dev *dev)
|
||||
{
|
||||
pci_stop_dev(dev);
|
||||
|
||||
/* Remove the device from the device lists, and prevent any further
|
||||
* list accesses from this device */
|
||||
down_write(&pci_bus_sem);
|
||||
|
@ -29,7 +29,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
|
||||
if (pdev->is_pcie)
|
||||
return NULL;
|
||||
while (1) {
|
||||
if (!pdev->bus->parent)
|
||||
if (pci_is_root_bus(pdev->bus))
|
||||
break;
|
||||
pdev = pdev->bus->self;
|
||||
/* a p2p bridge */
|
||||
@ -114,36 +114,6 @@ pci_find_next_bus(const struct pci_bus *from)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_LEGACY
|
||||
/**
|
||||
* pci_find_slot - locate PCI device from a given PCI slot
|
||||
* @bus: number of PCI bus on which desired PCI device resides
|
||||
* @devfn: encodes number of PCI slot in which the desired PCI
|
||||
* device resides and the logical device number within that slot
|
||||
* in case of multi-function devices.
|
||||
*
|
||||
* Given a PCI bus and slot/function number, the desired PCI device
|
||||
* is located in system global list of PCI devices. If the device
|
||||
* is found, a pointer to its data structure is returned. If no
|
||||
* device is found, %NULL is returned.
|
||||
*
|
||||
* NOTE: Do not use this function any more; use pci_get_slot() instead, as
|
||||
* the PCI device returned by this function can disappear at any moment in
|
||||
* time.
|
||||
*/
|
||||
struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
if (dev->bus->number == bus && dev->devfn == devfn) {
|
||||
pci_dev_put(dev);
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_find_slot);
|
||||
|
||||
/**
|
||||
* pci_find_device - begin or continue searching for a PCI device by vendor/device id
|
||||
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
|
||||
|
@ -58,7 +58,6 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus)
|
||||
res = list->res;
|
||||
idx = res - &list->dev->resource[0];
|
||||
if (pci_assign_resource(list->dev, idx)) {
|
||||
/* FIXME: get rid of this */
|
||||
res->start = 0;
|
||||
res->end = 0;
|
||||
res->flags = 0;
|
||||
@ -143,6 +142,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
|
||||
struct pci_dev *bridge = bus->self;
|
||||
struct pci_bus_region region;
|
||||
u32 l, bu, lu, io_upper16;
|
||||
int pref_mem64;
|
||||
|
||||
if (pci_is_enabled(bridge))
|
||||
return;
|
||||
@ -198,16 +198,22 @@ static void pci_setup_bridge(struct pci_bus *bus)
|
||||
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
|
||||
|
||||
/* Set up PREF base/limit. */
|
||||
pref_mem64 = 0;
|
||||
bu = lu = 0;
|
||||
pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]);
|
||||
if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
|
||||
int width = 8;
|
||||
l = (region.start >> 16) & 0xfff0;
|
||||
l |= region.end & 0xfff00000;
|
||||
bu = upper_32_bits(region.start);
|
||||
lu = upper_32_bits(region.end);
|
||||
dev_info(&bridge->dev, " PREFETCH window: %#016llx-%#016llx\n",
|
||||
(unsigned long long)region.start,
|
||||
(unsigned long long)region.end);
|
||||
if (bus->resource[2]->flags & IORESOURCE_MEM_64) {
|
||||
pref_mem64 = 1;
|
||||
bu = upper_32_bits(region.start);
|
||||
lu = upper_32_bits(region.end);
|
||||
width = 16;
|
||||
}
|
||||
dev_info(&bridge->dev, " PREFETCH window: %#0*llx-%#0*llx\n",
|
||||
width, (unsigned long long)region.start,
|
||||
width, (unsigned long long)region.end);
|
||||
}
|
||||
else {
|
||||
l = 0x0000fff0;
|
||||
@ -215,9 +221,11 @@ static void pci_setup_bridge(struct pci_bus *bus)
|
||||
}
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
|
||||
|
||||
/* Set the upper 32 bits of PREF base & limit. */
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
|
||||
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
|
||||
if (pref_mem64) {
|
||||
/* Set the upper 32 bits of PREF base & limit. */
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
|
||||
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
|
||||
}
|
||||
|
||||
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
|
||||
}
|
||||
@ -255,8 +263,25 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
|
||||
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
|
||||
}
|
||||
if (pmem)
|
||||
if (pmem) {
|
||||
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
|
||||
b_res[2].flags |= IORESOURCE_MEM_64;
|
||||
}
|
||||
|
||||
/* double check if bridge does support 64 bit pref */
|
||||
if (b_res[2].flags & IORESOURCE_MEM_64) {
|
||||
u32 mem_base_hi, tmp;
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
&mem_base_hi);
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
0xffffffff);
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
|
||||
if (!tmp)
|
||||
b_res[2].flags &= ~IORESOURCE_MEM_64;
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
mem_base_hi);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for sizing routines: find first available
|
||||
@ -336,6 +361,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
|
||||
resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */
|
||||
int order, max_order;
|
||||
struct resource *b_res = find_free_bus_resource(bus, type);
|
||||
unsigned int mem64_mask = 0;
|
||||
|
||||
if (!b_res)
|
||||
return 0;
|
||||
@ -344,9 +370,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
|
||||
max_order = 0;
|
||||
size = 0;
|
||||
|
||||
mem64_mask = b_res->flags & IORESOURCE_MEM_64;
|
||||
b_res->flags &= ~IORESOURCE_MEM_64;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
struct resource *r = &dev->resource[i];
|
||||
resource_size_t r_size;
|
||||
@ -372,6 +401,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
|
||||
aligns[order] += align;
|
||||
if (order > max_order)
|
||||
max_order = order;
|
||||
mem64_mask &= r->flags & IORESOURCE_MEM_64;
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,6 +426,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
|
||||
b_res->start = min_align;
|
||||
b_res->end = size + min_align - 1;
|
||||
b_res->flags |= IORESOURCE_STARTALIGN;
|
||||
b_res->flags |= mem64_mask;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -135,23 +135,16 @@ void pci_disable_bridge_window(struct pci_dev *dev)
|
||||
}
|
||||
#endif /* CONFIG_PCI_QUIRKS */
|
||||
|
||||
int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
||||
int resno)
|
||||
{
|
||||
struct pci_bus *bus = dev->bus;
|
||||
struct resource *res = dev->resource + resno;
|
||||
resource_size_t size, min, align;
|
||||
int ret;
|
||||
|
||||
size = resource_size(res);
|
||||
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
|
||||
|
||||
align = resource_alignment(res);
|
||||
if (!align) {
|
||||
dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus "
|
||||
"alignment) %pR flags %#lx\n",
|
||||
resno, res, res->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* First, try exact prefetching match.. */
|
||||
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
||||
@ -169,10 +162,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||
pcibios_align_resource, dev);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n",
|
||||
resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res);
|
||||
} else {
|
||||
if (!ret) {
|
||||
res->flags &= ~IORESOURCE_STARTALIGN;
|
||||
if (resno < PCI_BRIDGE_RESOURCES)
|
||||
pci_update_resource(dev, resno);
|
||||
@ -181,6 +171,39 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||
{
|
||||
struct resource *res = dev->resource + resno;
|
||||
resource_size_t align;
|
||||
struct pci_bus *bus;
|
||||
int ret;
|
||||
|
||||
align = resource_alignment(res);
|
||||
if (!align) {
|
||||
dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus "
|
||||
"alignment) %pR flags %#lx\n",
|
||||
resno, res, res->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bus = dev->bus;
|
||||
while ((ret = __pci_assign_resource(bus, dev, resno))) {
|
||||
if (bus->parent && bus->self->transparent)
|
||||
bus = bus->parent;
|
||||
else
|
||||
bus = NULL;
|
||||
if (bus)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n",
|
||||
resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int pci_assign_resource_fixed(struct pci_dev *dev, int resno)
|
||||
{
|
||||
|
@ -307,6 +307,45 @@ void pci_destroy_slot(struct pci_slot *slot)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_destroy_slot);
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE)
|
||||
#include <linux/pci_hotplug.h>
|
||||
/**
|
||||
* pci_hp_create_link - create symbolic link to the hotplug driver module.
|
||||
* @slot: struct pci_slot
|
||||
*
|
||||
* Helper function for pci_hotplug_core.c to create symbolic link to
|
||||
* the hotplug driver module.
|
||||
*/
|
||||
void pci_hp_create_module_link(struct pci_slot *pci_slot)
|
||||
{
|
||||
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||
struct kobject *kobj = NULL;
|
||||
int no_warn;
|
||||
|
||||
if (!slot || !slot->ops)
|
||||
return;
|
||||
kobj = kset_find_obj(module_kset, slot->ops->mod_name);
|
||||
if (!kobj)
|
||||
return;
|
||||
no_warn = sysfs_create_link(&pci_slot->kobj, kobj, "module");
|
||||
kobject_put(kobj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_hp_create_module_link);
|
||||
|
||||
/**
|
||||
* pci_hp_remove_link - remove symbolic link to the hotplug driver module.
|
||||
* @slot: struct pci_slot
|
||||
*
|
||||
* Helper function for pci_hotplug_core.c to remove symbolic link to
|
||||
* the hotplug driver module.
|
||||
*/
|
||||
void pci_hp_remove_module_link(struct pci_slot *pci_slot)
|
||||
{
|
||||
sysfs_remove_link(&pci_slot->kobj, "module");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_hp_remove_module_link);
|
||||
#endif
|
||||
|
||||
static int pci_slot_init(void)
|
||||
{
|
||||
struct kset *pci_bus_kset;
|
||||
|
@ -49,6 +49,8 @@ struct resource_list {
|
||||
#define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */
|
||||
#define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */
|
||||
|
||||
#define IORESOURCE_MEM_64 0x00100000
|
||||
|
||||
#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
|
||||
#define IORESOURCE_DISABLED 0x10000000
|
||||
#define IORESOURCE_UNSET 0x20000000
|
||||
|
@ -15,7 +15,7 @@ static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_bus *pbus = pdev->bus;
|
||||
/* Find a PCI root bus */
|
||||
while (pbus->parent)
|
||||
while (!pci_is_root_bus(pbus))
|
||||
pbus = pbus->parent;
|
||||
return acpi_get_pci_rootbridge_handle(pci_domain_nr(pbus),
|
||||
pbus->number);
|
||||
@ -23,7 +23,7 @@ static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
||||
|
||||
static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus)
|
||||
{
|
||||
if (pbus->parent)
|
||||
if (!pci_is_root_bus(pbus))
|
||||
return DEVICE_ACPI_HANDLE(&(pbus->self->dev));
|
||||
return acpi_get_pci_rootbridge_handle(pci_domain_nr(pbus),
|
||||
pbus->number);
|
||||
|
@ -607,8 +607,6 @@ extern void pci_sort_breadthfirst(void);
|
||||
struct pci_dev __deprecated *pci_find_device(unsigned int vendor,
|
||||
unsigned int device,
|
||||
struct pci_dev *from);
|
||||
struct pci_dev __deprecated *pci_find_slot(unsigned int bus,
|
||||
unsigned int devfn);
|
||||
#endif /* CONFIG_PCI_LEGACY */
|
||||
|
||||
enum pci_lost_interrupt_reason {
|
||||
@ -647,6 +645,7 @@ int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, u16 val);
|
||||
int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, u32 val);
|
||||
struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops);
|
||||
|
||||
static inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val)
|
||||
{
|
||||
@ -711,8 +710,8 @@ int pcix_get_mmrbc(struct pci_dev *dev);
|
||||
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
|
||||
int pcie_get_readrq(struct pci_dev *dev);
|
||||
int pcie_set_readrq(struct pci_dev *dev, int rq);
|
||||
int __pci_reset_function(struct pci_dev *dev);
|
||||
int pci_reset_function(struct pci_dev *dev);
|
||||
int pci_execute_reset_function(struct pci_dev *dev);
|
||||
void pci_update_resource(struct pci_dev *dev, int resno);
|
||||
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
||||
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
|
||||
@ -732,7 +731,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
|
||||
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
|
||||
bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
|
||||
void pci_pme_active(struct pci_dev *dev, bool enable);
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable);
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);
|
||||
int pci_wake_from_d3(struct pci_dev *dev, bool enable);
|
||||
pci_power_t pci_target_state(struct pci_dev *dev);
|
||||
int pci_prepare_to_sleep(struct pci_dev *dev);
|
||||
@ -798,7 +797,7 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
|
||||
int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
||||
int pass);
|
||||
|
||||
void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
|
||||
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
|
||||
void *userdata);
|
||||
int pci_cfg_space_size_ext(struct pci_dev *dev);
|
||||
int pci_cfg_space_size(struct pci_dev *dev);
|
||||
@ -888,6 +887,17 @@ static inline int pcie_aspm_enabled(void)
|
||||
extern int pcie_aspm_enabled(void);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PCIE_ECRC
|
||||
static inline void pcie_set_ecrc_checking(struct pci_dev *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static inline void pcie_ecrc_get_policy(char *str) {};
|
||||
#else
|
||||
extern void pcie_set_ecrc_checking(struct pci_dev *dev);
|
||||
extern void pcie_ecrc_get_policy(char *str);
|
||||
#endif
|
||||
|
||||
#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1)
|
||||
|
||||
#ifdef CONFIG_HT_IRQ
|
||||
@ -944,12 +954,6 @@ static inline struct pci_dev *pci_find_device(unsigned int vendor,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct pci_dev *pci_find_slot(unsigned int bus,
|
||||
unsigned int devfn)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct pci_dev *pci_get_device(unsigned int vendor,
|
||||
unsigned int device,
|
||||
struct pci_dev *from)
|
||||
@ -1105,6 +1109,10 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
|
||||
|
||||
#include <asm/pci.h>
|
||||
|
||||
#ifndef PCIBIOS_MAX_MEM_32
|
||||
#define PCIBIOS_MAX_MEM_32 (-1)
|
||||
#endif
|
||||
|
||||
/* these helpers provide future and backwards compatibility
|
||||
* for accessing popular PCI BAR info */
|
||||
#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)
|
||||
@ -1261,5 +1269,10 @@ static inline irqreturn_t pci_sriov_migration(struct pci_dev *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE)
|
||||
extern void pci_hp_create_module_link(struct pci_slot *pci_slot);
|
||||
extern void pci_hp_remove_module_link(struct pci_slot *pci_slot);
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* LINUX_PCI_H */
|
||||
|
@ -66,17 +66,10 @@ enum pcie_link_speed {
|
||||
PCIE_LNK_SPEED_UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
struct hotplug_slot;
|
||||
struct hotplug_slot_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct hotplug_slot *, char *);
|
||||
ssize_t (*store)(struct hotplug_slot *, const char *, size_t);
|
||||
};
|
||||
#define to_hotplug_attr(n) container_of(n, struct hotplug_slot_attribute, attr);
|
||||
|
||||
/**
|
||||
* struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
|
||||
* @owner: The module owner of this structure
|
||||
* @mod_name: The module name (KBUILD_MODNAME) of this structure
|
||||
* @enable_slot: Called when the user wants to enable a specific pci slot
|
||||
* @disable_slot: Called when the user wants to disable a specific pci slot
|
||||
* @set_attention_status: Called to set the specific slot's attention LED to
|
||||
@ -109,6 +102,7 @@ struct hotplug_slot_attribute {
|
||||
*/
|
||||
struct hotplug_slot_ops {
|
||||
struct module *owner;
|
||||
const char *mod_name;
|
||||
int (*enable_slot) (struct hotplug_slot *slot);
|
||||
int (*disable_slot) (struct hotplug_slot *slot);
|
||||
int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
|
||||
@ -167,12 +161,21 @@ static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
|
||||
return pci_slot_name(slot->pci_slot);
|
||||
}
|
||||
|
||||
extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr,
|
||||
const char *name);
|
||||
extern int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *pbus,
|
||||
int nr, const char *name,
|
||||
struct module *owner, const char *mod_name);
|
||||
extern int pci_hp_deregister(struct hotplug_slot *slot);
|
||||
extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
|
||||
struct hotplug_slot_info *info);
|
||||
|
||||
static inline int pci_hp_register(struct hotplug_slot *slot,
|
||||
struct pci_bus *pbus,
|
||||
int devnr, const char *name)
|
||||
{
|
||||
return __pci_hp_register(slot, pbus, devnr, name,
|
||||
THIS_MODULE, KBUILD_MODNAME);
|
||||
}
|
||||
|
||||
/* PCI Setting Record (Type 0) */
|
||||
struct hpp_type0 {
|
||||
u32 revision;
|
||||
|
@ -295,8 +295,9 @@
|
||||
#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
|
||||
#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
|
||||
#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
|
||||
#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
|
||||
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
|
||||
#define PCI_MSI_MASK_BIT 16 /* Mask bits register */
|
||||
#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */
|
||||
|
||||
/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */
|
||||
#define PCI_MSIX_FLAGS 2
|
||||
@ -304,7 +305,6 @@
|
||||
#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
|
||||
#define PCI_MSIX_FLAGS_MASKALL (1 << 14)
|
||||
#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
|
||||
#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
|
||||
|
||||
/* CompactPCI Hotswap Register */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user