From: Prarit Bhargava <prarit@redhat.com> Date: Fri, 30 Oct 2009 12:47:35 -0400 Subject: [pci] aer: backport acpi osc functions Message-id: <20091030124613.6431.2487.sendpatchset@prarit.bos.redhat.com> Patchwork-id: 21268 O-Subject: [RHEL5 PATCH 4/8]: AER: Backport ACPI OSC functions [v2] Bugzilla: 517093 RH-Acked-by: Dean Nelson <dnelson@redhat.com> RH-Acked-by: Ivan Vecera <ivecera@redhat.com> RH-Acked-by: Andy Gospodarek <gospo@redhat.com> Based on upstream commit 0efe5e32c8729ef44b00d9a7203e4c99a6378b27 The ACPI _OSC functions in RHEL5 are stale and need to be updated. Resolves BZ 517093. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 46a2755..1be8288 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -66,6 +66,7 @@ parameter is applicable: PARIDE The ParIDE subsystem is enabled. PARISC The PA-RISC architecture is enabled. PCI PCI bus support is enabled. + PCIE PCI Express support is enabled. PCMCIA The PCMCIA subsystem is enabled. PNP Plug & Play support is enabled. PPC PowerPC architecture is enabled. @@ -1318,6 +1319,9 @@ running once the system is up. Mechanism 1. conf2 [IA-32] Force use of PCI Configuration Mechanism 2. + noaer [PCIE] If the PCIEAER kernel config parameter is + enabled, this kernel boot option can be used to + disable the use of PCIE advanced error reporting. nommconf [IA-32,X86_64] Disable use of MMCONFIG for PCI Configuration nomsi [MSI] If the PCI_MSI kernel config parameter is diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index 2007535..5961dc6 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -549,3 +549,12 @@ struct pci_bus *pci_scan_bus_with_sysdata(int busno) return bus; } + +int pci_ext_cfg_avail(struct pci_dev *dev) +{ + if (raw_pci_ops) + return 1; + else + return 0; +} + diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 56096eb..80f93ca 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -119,7 +119,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; @@ -127,19 +127,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; } /** @@ -150,7 +152,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; @@ -158,26 +160,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; @@ -185,35 +189,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; } /** @@ -223,22 +231,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; } /* ------------------------------------------------------- */ diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 5a6465b..4551b1f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -31,6 +31,7 @@ #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -177,6 +178,7 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_status status = AE_OK; unsigned long value = 0; acpi_handle handle = NULL; + u32 flags, base_flags; if (!device) @@ -198,6 +200,13 @@ static int acpi_pci_root_add(struct acpi_device *device) */ device->ops.bind = acpi_pci_bind; + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + pci_acpi_osc_support(device->handle, flags); + /* * Segment * ------- @@ -323,6 +332,11 @@ static int acpi_pci_root_add(struct acpi_device *device) if (ACPI_SUCCESS(status)) result = acpi_pci_irq_add_prt(device->handle, root->id.segment, root->id.bus); + /* Indicate support for various _OSC capabilities. */ + if (pci_ext_cfg_avail(root->bus->self)) + flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + if (flags != base_flags) + pci_acpi_osc_support(device->handle, flags); end: if (result) { diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index bb7456c..0186070 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -19,92 +19,51 @@ #include <linux/pci-acpi.h> #include "pci.h" -static u32 ctrlset_buf[3] = {0, 0, 0}; -static u32 global_ctrlsets = 0; -static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; - -static acpi_status -acpi_query_osc ( - acpi_handle handle, - u32 level, - void *context, - void **retval ) -{ - acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *out_obj; - u32 osc_dw0; - - - /* Setting up input parameters */ - input.count = 4; - input.pointer = in_params; - in_params[0].type = ACPI_TYPE_BUFFER; - in_params[0].buffer.length = 16; - in_params[0].buffer.pointer = OSC_UUID; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = 1; - in_params[2].type = ACPI_TYPE_INTEGER; - in_params[2].integer.value = 3; - in_params[3].type = ACPI_TYPE_BUFFER; - in_params[3].buffer.length = 12; - in_params[3].buffer.pointer = (u8 *)context; +struct acpi_osc_data { + acpi_handle handle; + u32 support_set; + u32 control_set; + u32 control_query; + int is_queried; + struct list_head sibiling; +}; +static LIST_HEAD(acpi_osc_data_list); - status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE (status)) { - printk(KERN_DEBUG - "Evaluate _OSC Set fails. Status = 0x%04x\n", status); - return status; - } - out_obj = output.pointer; +struct acpi_osc_args { + u32 capbuf[3]; +}; - if (out_obj->type != ACPI_TYPE_BUFFER) { - printk(KERN_DEBUG - "Evaluate _OSC returns wrong type\n"); - status = AE_TYPE; - goto query_osc_out; - } - osc_dw0 = *((u32 *) out_obj->buffer.pointer); - if (osc_dw0) { - if (osc_dw0 & OSC_REQUEST_ERROR) - printk(KERN_DEBUG "_OSC request fails\n"); - if (osc_dw0 & OSC_INVALID_UUID_ERROR) - printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (osc_dw0 & OSC_INVALID_REVISION_ERROR) - printk(KERN_DEBUG "_OSC invalid revision\n"); - if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { - /* Update Global Control Set */ - global_ctrlsets = *((u32 *)(out_obj->buffer.pointer+8)); - status = AE_OK; - goto query_osc_out; - } - status = AE_ERROR; - goto query_osc_out; - } +static DEFINE_MUTEX(pci_acpi_lock); - /* Update Global Control Set */ - global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8)); - status = AE_OK; +static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle) +{ + struct acpi_osc_data *data; -query_osc_out: - kfree(output.pointer); - return status; + list_for_each_entry(data, &acpi_osc_data_list, sibiling) { + if (data->handle == handle) + return data; + } + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + INIT_LIST_HEAD(&data->sibiling); + data->handle = handle; + list_add_tail(&data->sibiling, &acpi_osc_data_list); + return data; } +static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, + 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; -static acpi_status -acpi_run_osc ( - acpi_handle handle, - void *context) +static acpi_status acpi_run_osc(acpi_handle handle, + struct acpi_osc_args *osc_args, u32 *retval) { - acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *out_obj; - u32 osc_dw0; + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *out_obj; + u32 errors, flags = osc_args->capbuf[OSC_QUERY_TYPE]; /* Setting up input parameters */ input.count = 4; @@ -118,73 +77,98 @@ acpi_run_osc ( in_params[2].integer.value = 3; in_params[3].type = ACPI_TYPE_BUFFER; in_params[3].buffer.length = 12; - in_params[3].buffer.pointer = (u8 *)context; + in_params[3].buffer.pointer = (u8 *)osc_args->capbuf; status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE (status)) { - printk(KERN_DEBUG - "Evaluate _OSC Set fails. Status = 0x%04x\n", status); + if (ACPI_FAILURE(status)) return status; - } + out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_BUFFER) { - printk(KERN_DEBUG - "Evaluate _OSC returns wrong type\n"); + printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n"); status = AE_TYPE; - goto run_osc_out; + goto out_kfree; } - osc_dw0 = *((u32 *) out_obj->buffer.pointer); - if (osc_dw0) { - if (osc_dw0 & OSC_REQUEST_ERROR) + /* Need to ignore the bit0 in result code */ + errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + if (errors) { + if (errors & OSC_REQUEST_ERROR) printk(KERN_DEBUG "_OSC request fails\n"); - if (osc_dw0 & OSC_INVALID_UUID_ERROR) + if (errors & OSC_INVALID_UUID_ERROR) printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (osc_dw0 & OSC_INVALID_REVISION_ERROR) + if (errors & OSC_INVALID_REVISION_ERROR) printk(KERN_DEBUG "_OSC invalid revision\n"); - if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { + if (errors & OSC_CAPABILITIES_MASK_ERROR) { + if (flags & OSC_QUERY_ENABLE) + goto out_success; printk(KERN_DEBUG "_OSC FW not grant req. control\n"); status = AE_SUPPORT; - goto run_osc_out; + goto out_kfree; } status = AE_ERROR; - goto run_osc_out; + goto out_kfree; } +out_success: + *retval = *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; -run_osc_out: +out_kfree: kfree(output.pointer); return status; } -/** - * pci_osc_support_set - register OS support to Firmware - * @flags: OS support bits - * - * Update OS support fields and doing a _OSC Query to obtain an update - * from Firmware on supported control bits. - **/ -acpi_status pci_osc_support_set(u32 flags) +static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) { - u32 temp; + acpi_status status; + u32 support_set, result; + struct acpi_osc_args osc_args; - if (!(flags & OSC_SUPPORT_MASKS)) { - return AE_TYPE; + /* do _OSC query for all possible controls */ + support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS); + osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; + osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set; + osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; + + status = acpi_run_osc(osc_data->handle, &osc_args, &result); + if (ACPI_SUCCESS(status)) { + osc_data->support_set = support_set; + osc_data->control_query = result; + osc_data->is_queried = 1; } - ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS); - /* do _OSC query for all possible controls */ - temp = ctrlset_buf[OSC_CONTROL_TYPE]; - ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; - acpi_get_devices ( PCI_ROOT_HID_STRING, - acpi_query_osc, - ctrlset_buf, - NULL ); - ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE; - ctrlset_buf[OSC_CONTROL_TYPE] = temp; - return AE_OK; + return status; +} + +/* + * pci_acpi_osc_support: Invoke _OSC indicating support for the given feature + * @flags: Bitmask of flags to support + * + * See the ACPI spec for the definition of the flags + */ +int pci_acpi_osc_support(acpi_handle handle, u32 flags) +{ + acpi_status status; + acpi_handle tmp; + struct acpi_osc_data *osc_data; + int rc = 0; + + status = acpi_get_handle(handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return -ENOTTY; + + mutex_lock(&pci_acpi_lock); + osc_data = acpi_get_osc_data(handle); + if (!osc_data) { + printk(KERN_ERR "acpi osc data array is full\n"); + rc = -ENOMEM; + goto out; + } + + __acpi_query_osc(flags, osc_data); +out: + mutex_unlock(&pci_acpi_lock); + return rc; } -EXPORT_SYMBOL(pci_osc_support_set); /** * pci_osc_control_set - commit requested control to Firmware @@ -195,23 +179,54 @@ EXPORT_SYMBOL(pci_osc_support_set); **/ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) { - acpi_status status; - u32 ctrlset; + acpi_status status; + u32 control_req, control_set, result; + acpi_handle tmp; + struct acpi_osc_data *osc_data; + struct acpi_osc_args osc_args; + + status = acpi_get_handle(handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return status; + + mutex_lock(&pci_acpi_lock); + osc_data = acpi_get_osc_data(handle); + if (!osc_data) { + printk(KERN_ERR "acpi osc data array is full\n"); + status = AE_ERROR; + goto out; + } - ctrlset = (flags & OSC_CONTROL_MASKS); - if (!ctrlset) { - return AE_TYPE; + control_req = (flags & OSC_CONTROL_MASKS); + if (!control_req) { + status = AE_TYPE; + goto out; } - if (ctrlset_buf[OSC_SUPPORT_TYPE] && - ((global_ctrlsets & ctrlset) != ctrlset)) { - return AE_SUPPORT; + + /* No need to evaluate _OSC if the control was already granted. */ + if ((osc_data->control_set & control_req) == control_req) + goto out; + + if (!osc_data->is_queried) { + status = __acpi_query_osc(osc_data->support_set, osc_data); + if (ACPI_FAILURE(status)) + goto out; } - ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset; - status = acpi_run_osc(handle, ctrlset_buf); - if (ACPI_FAILURE (status)) { - ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset; + + if ((osc_data->control_query & control_req) != control_req) { + status = AE_SUPPORT; + goto out; } - + + control_set = osc_data->control_set | control_req; + osc_args.capbuf[OSC_QUERY_TYPE] = 0; + osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set; + osc_args.capbuf[OSC_CONTROL_TYPE] = control_set; + status = acpi_run_osc(handle, &osc_args, &result); + if (ACPI_SUCCESS(status)) + osc_data->control_set = result; +out: + mutex_unlock(&pci_acpi_lock); return status; } EXPORT_SYMBOL(pci_osc_control_set); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3ccf357..25610bd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1746,6 +1746,19 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) return 0; } +/** + * pci_ext_cfg_enabled - can we access extended PCI config space? + * @dev: The PCI device of the root bridge. + * + * Returns 1 if we can access PCI extended config space (offsets + * greater than 0xff). This is the default implementation. Architecture + * implementations can override this. + */ +int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +{ + return 1; +} + static int __devinit pci_init(void) { struct pci_dev *dev = NULL; @@ -1765,6 +1778,8 @@ static int __devinit pci_setup(char *str) if (*str && (str = pcibios_setup(str)) && *str) { if (!strcmp(str, "nomsi")) { pci_no_msi(); + } else if (!strcmp(str, "noaer")) { + pci_no_aer(); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8c8e51c..4c088b6 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -59,6 +59,12 @@ static inline void pci_restore_msi_state(struct pci_dev *dev) {} static inline void pci_restore_msix_state(struct pci_dev *dev) {} #endif +#ifdef CONFIG_PCIEAER +void pci_no_aer(void); +#else +static inline void pci_no_aer(void) { } +#endif + static inline int pci_no_d1d2(struct pci_dev *dev) { unsigned int parent_dstates = 0; diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 5b0bf39..9280d34 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -8,6 +8,8 @@ #ifndef _PCI_ACPI_H_ #define _PCI_ACPI_H_ +#include <linux/acpi.h> + #define OSC_QUERY_TYPE 0 #define OSC_SUPPORT_TYPE 1 #define OSC_CONTROL_TYPE 2 @@ -48,16 +50,15 @@ #ifdef CONFIG_ACPI extern acpi_status pci_osc_control_set(acpi_handle handle, u32 flags); -extern acpi_status pci_osc_support_set(u32 flags); - +int pci_acpi_osc_support(acpi_handle handle, u32 flags); 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 (!pci_is_root_bus(pbus)) - pbus = pbus->parent; - return acpi_get_pci_rootbridge_handle(pci_domain_nr(pbus), - pbus->number); + /* Find root host bridge */ + while (pdev->bus->self) + pdev = pdev->bus->self; + + return acpi_get_pci_rootbridge_handle(pci_domain_nr(pdev->bus), + pdev->bus->number); } static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) @@ -74,7 +75,6 @@ typedef u32 acpi_status; #endif static inline acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) {return AE_ERROR;} -static inline acpi_status pci_osc_support_set(u32 flags) {return AE_ERROR;} static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { return NULL; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 76b934b..c60622d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -933,6 +933,8 @@ extern int pci_pci_problems; #define PCIPCI_VSFX 16 #define PCIPCI_ALIMAGIK 32 +int pci_ext_cfg_avail(struct pci_dev *dev); + #ifdef CONFIG_PCI_IOV extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); extern void pci_disable_sriov(struct pci_dev *dev);