Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] pciehp: clean-up how we request control of hotplug hardware

This patch further tweaks how we request control of hotplug
controller hardware from BIOS. We first search the ACPI namespace
corresponding to a specific hotplug controller looking for an
_OSC or OSHP method. On failure, we successively move to the
ACPI parent object, till we hit the highest level host bridge
in the hierarchy. This allows for different types of BIOS's
which place the _OSC/OSHP methods at various places in the acpi
namespace, while still not encroaching on the namespace of
some other root level host bridge.

This patch also introduces a new load time option (pciehp_force)
that allows us to bypass all _OSC/OSHP checking. Not supporting
these methods seems to be be the most common ACPI firmware problem
we've run into. This will still _not_ allow the pciehp driver to
work correctly if the BIOS really doesn't support pciehp (i.e. if
it doesn't generate a hotplug interrupt). Use this option with
caution. Some BIOS's may deliberately not build any _OSC/OSHP
methods to make sure it retains control the hotplug hardware.
Using the pciehp_force parameter for such systems can lead to
two separate entities trying to control the same hardware.

Signed-off-by: Rajesh Shah <rajesh.shah@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

rajesh.shah@intel.com and committed by
Greg Kroah-Hartman
a3a45ec8 427bf532

+90 -20
+1
drivers/acpi/glue.c
··· 203 203 acpi_get_devices(PCI_ROOT_HID_STRING, find_pci_rootbridge, &find, NULL); 204 204 return find.handle; 205 205 } 206 + EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); 206 207 207 208 /* Get device's handler per its address under its parent */ 208 209 struct acpi_find_child {
+1
drivers/pci/hotplug/pciehp.h
··· 40 40 extern int pciehp_poll_mode; 41 41 extern int pciehp_poll_time; 42 42 extern int pciehp_debug; 43 + extern int pciehp_force; 43 44 44 45 /*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ 45 46 #define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
+3
drivers/pci/hotplug/pciehp_core.c
··· 39 39 int pciehp_debug; 40 40 int pciehp_poll_mode; 41 41 int pciehp_poll_time; 42 + int pciehp_force; 42 43 struct controller *pciehp_ctrl_list; 43 44 44 45 #define DRIVER_VERSION "0.4" ··· 53 52 module_param(pciehp_debug, bool, 0644); 54 53 module_param(pciehp_poll_mode, bool, 0644); 55 54 module_param(pciehp_poll_time, int, 0644); 55 + module_param(pciehp_force, bool, 0644); 56 56 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); 57 57 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); 58 58 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); 59 + MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing"); 59 60 60 61 #define PCIE_MODULE_NAME "pciehp" 61 62
+8 -3
drivers/pci/hotplug/pciehp_hpc.c
··· 1417 1417 goto abort_free_ctlr; 1418 1418 } 1419 1419 1420 - rc = get_hp_hw_control_from_firmware(ctrl->pci_dev); 1421 - if (rc) 1422 - goto abort_free_ctlr; 1420 + if (pciehp_force) { 1421 + dbg("Bypassing BIOS check for pciehp use on %s\n", 1422 + pci_name(ctrl->pci_dev)); 1423 + } else { 1424 + rc = get_hp_hw_control_from_firmware(ctrl->pci_dev); 1425 + if (rc) 1426 + goto abort_free_ctlr; 1427 + } 1423 1428 1424 1429 /* Add this HPC instance into the HPC list */ 1425 1430 spin_lock(&list_lock);
+77 -17
drivers/pci/hotplug/pciehprm_acpi.c
··· 132 132 /* run OSHP */ 133 133 status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); 134 134 if (ACPI_FAILURE(status)) { 135 - err("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name, 135 + dbg("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name, 136 136 status); 137 137 } else { 138 138 dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name); ··· 140 140 return status; 141 141 } 142 142 143 + static int is_root_bridge(acpi_handle handle) 144 + { 145 + acpi_status status; 146 + struct acpi_device_info *info; 147 + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 148 + int i; 149 + 150 + status = acpi_get_object_info(handle, &buffer); 151 + if (ACPI_SUCCESS(status)) { 152 + info = buffer.pointer; 153 + if ((info->valid & ACPI_VALID_HID) && 154 + !strcmp(PCI_ROOT_HID_STRING, 155 + info->hardware_id.value)) { 156 + acpi_os_free(buffer.pointer); 157 + return 1; 158 + } 159 + if (info->valid & ACPI_VALID_CID) { 160 + for (i=0; i < info->compatibility_id.count; i++) { 161 + if (!strcmp(PCI_ROOT_HID_STRING, 162 + info->compatibility_id.id[i].value)) { 163 + acpi_os_free(buffer.pointer); 164 + return 1; 165 + } 166 + } 167 + } 168 + } 169 + return 0; 170 + } 171 + 143 172 int get_hp_hw_control_from_firmware(struct pci_dev *dev) 144 173 { 145 174 acpi_status status; 146 - acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev)); 175 + acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); 176 + struct pci_dev *pdev = dev; 177 + u8 *path_name; 147 178 /* 148 179 * Per PCI firmware specification, we should run the ACPI _OSC 149 - * method to get control of hotplug hardware before using it 180 + * method to get control of hotplug hardware before using it. 181 + * If an _OSC is missing, we look for an OSHP to do the same thing. 182 + * To handle different BIOS behavior, we look for _OSC and OSHP 183 + * within the scope of the hotplug controller and its parents, upto 184 + * the host bridge under which this controller exists. 150 185 */ 151 - status = pci_osc_control_set(handle, 152 - OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); 153 - 154 - /* Fixme: fail native hotplug if _OSC does not exist for root ports */ 155 - if (status == AE_NOT_FOUND) { 186 + while (!handle) { 156 187 /* 157 - * Some older BIOS's don't support _OSC but support 158 - * OSHP to do the same thing 188 + * This hotplug controller was not listed in the ACPI name 189 + * space at all. Try to get acpi handle of parent pci bus. 159 190 */ 160 - status = acpi_run_oshp(handle); 161 - } 162 - if (ACPI_FAILURE(status)) { 163 - err("Cannot get control of hotplug hardware\n"); 164 - return -1; 191 + if (!pdev || !pdev->bus->parent) 192 + break; 193 + dbg("Could not find %s in acpi namespace, trying parent\n", 194 + pci_name(pdev)); 195 + if (!pdev->bus->parent->self) 196 + /* Parent must be a host bridge */ 197 + handle = acpi_get_pci_rootbridge_handle( 198 + pci_domain_nr(pdev->bus->parent), 199 + pdev->bus->parent->number); 200 + else 201 + handle = DEVICE_ACPI_HANDLE( 202 + &(pdev->bus->parent->self->dev)); 203 + pdev = pdev->bus->parent->self; 165 204 } 166 205 167 - dbg("Sucess getting control of hotplug hardware\n"); 168 - return 0; 206 + while (handle) { 207 + path_name = acpi_path_name(handle); 208 + dbg("Trying to get hotplug control for %s \n", path_name); 209 + status = pci_osc_control_set(handle, 210 + OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); 211 + if (status == AE_NOT_FOUND) 212 + status = acpi_run_oshp(handle); 213 + if (ACPI_SUCCESS(status)) { 214 + dbg("Gained control for hotplug HW for pci %s (%s)\n", 215 + pci_name(dev), path_name); 216 + return 0; 217 + } 218 + if (is_root_bridge(handle)) 219 + break; 220 + chandle = handle; 221 + status = acpi_get_parent(chandle, &handle); 222 + if (ACPI_FAILURE(status)) 223 + break; 224 + } 225 + 226 + err("Cannot get control of hotplug hardware for pci %s\n", 227 + pci_name(dev)); 228 + return -1; 169 229 } 170 230 171 231 void get_hp_params_from_firmware(struct pci_dev *dev,