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

PCI: pciehp: add ACPI based slot detection

There is a problem that some non hot-pluggable PCIe slots are detected
as hot-pluggable by pciehp on some platforms. The immediate cause of
this problem is that hot-plug capable bit in the Slot Capabilities
register is set even for non hot-pluggable slots on those platforms.
It seems a BIOS/hardware problem, but we need workaround about that.

Some of those platforms define hot-pluggable PCIe slots on ACPI
namespace properly, while hot-plug capable bit in the Slot
Capabilities register is set improperly. So using ACPI namespace
information in pciehp to detect PCIe hot-pluggable slots would be a
workaround.

This patch adds 'pciehp_detect_mode' module option. When 'acpi' is
specified, pciehp uses ACPI namespace information to detect PCIe
hot-pluggable slots.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by

Kenji Kaneshige and committed by
Jesse Barnes
c9ffa5a5 873392ca

+132 -1
+3
drivers/pci/hotplug/Makefile
··· 55 55 pciehp_ctrl.o \ 56 56 pciehp_pci.o \ 57 57 pciehp_hpc.o 58 + ifdef CONFIG_ACPI 59 + pciehp-objs += pciehp_acpi.o 60 + endif 58 61 59 62 shpchp-objs := shpchp_core.o \ 60 63 shpchp_ctrl.o \
+14 -1
drivers/pci/hotplug/pciehp.h
··· 220 220 #include <acpi/actypes.h> 221 221 #include <linux/pci-acpi.h> 222 222 223 + extern void __init pciehp_acpi_slot_detection_init(void); 224 + extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev); 225 + 226 + static inline void pciehp_firmware_init(void) 227 + { 228 + pciehp_acpi_slot_detection_init(); 229 + } 230 + 223 231 static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) 224 232 { 233 + int retval; 225 234 u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | 226 235 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); 227 - return acpi_get_hp_hw_control_from_firmware(dev, flags); 236 + retval = acpi_get_hp_hw_control_from_firmware(dev, flags); 237 + if (retval) 238 + return retval; 239 + return pciehp_acpi_slot_detection_check(dev); 228 240 } 229 241 230 242 static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, ··· 247 235 return 0; 248 236 } 249 237 #else 238 + #define pciehp_firmware_init() do {} while (0) 250 239 #define pciehp_get_hp_hw_control_from_firmware(dev) 0 251 240 #define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV) 252 241 #endif /* CONFIG_ACPI */
+114
drivers/pci/hotplug/pciehp_acpi.c
··· 1 + /* 2 + * ACPI related functions for PCI Express Hot Plug driver. 3 + * 4 + * Copyright (C) 2008 Kenji Kaneshige 5 + * Copyright (C) 2008 Fujitsu Limited. 6 + * 7 + * All rights reserved. 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or (at 12 + * your option) any later version. 13 + * 14 + * This program is distributed in the hope that it will be useful, but 15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 16 + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 17 + * NON INFRINGEMENT. See the GNU General Public License for more 18 + * details. 19 + * 20 + * You should have received a copy of the GNU General Public License 21 + * along with this program; if not, write to the Free Software 22 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 + * 24 + */ 25 + 26 + #include <linux/acpi.h> 27 + #include "pciehp.h" 28 + 29 + #define PCIEHP_DETECT_PCIE (0) 30 + #define PCIEHP_DETECT_ACPI (1) 31 + #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE 32 + 33 + static int slot_detection_mode; 34 + static char *pciehp_detect_mode; 35 + module_param(pciehp_detect_mode, charp, 0444); 36 + MODULE_PARM_DESC(pciehp_detect_mode, 37 + "Slot detection mode: pcie, acpi\n" 38 + " pcie - Use PCIe based slot detection (default)\n" 39 + " acpi - Use ACPI for slot detection\n"); 40 + 41 + static int is_ejectable(acpi_handle handle) 42 + { 43 + acpi_status status; 44 + acpi_handle tmp; 45 + unsigned long long removable; 46 + status = acpi_get_handle(handle, "_ADR", &tmp); 47 + if (ACPI_FAILURE(status)) 48 + return 0; 49 + status = acpi_get_handle(handle, "_EJ0", &tmp); 50 + if (ACPI_SUCCESS(status)) 51 + return 1; 52 + status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); 53 + if (ACPI_SUCCESS(status) && removable) 54 + return 1; 55 + return 0; 56 + } 57 + 58 + static acpi_status 59 + check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) 60 + { 61 + int *found = (int *)context; 62 + if (is_ejectable(handle)) { 63 + *found = 1; 64 + return AE_CTRL_TERMINATE; 65 + } 66 + return AE_OK; 67 + } 68 + 69 + static int pciehp_detect_acpi_slot(struct pci_bus *pbus) 70 + { 71 + acpi_handle handle; 72 + struct pci_dev *pdev = pbus->self; 73 + int found = 0; 74 + 75 + if (!pdev){ 76 + int seg = pci_domain_nr(pbus), busnr = pbus->number; 77 + handle = acpi_get_pci_rootbridge_handle(seg, busnr); 78 + } else 79 + handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); 80 + 81 + if (!handle) 82 + return 0; 83 + 84 + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, 85 + check_hotplug, (void *)&found, NULL); 86 + return found; 87 + } 88 + 89 + int pciehp_acpi_slot_detection_check(struct pci_dev *dev) 90 + { 91 + if (slot_detection_mode != PCIEHP_DETECT_ACPI) 92 + return 0; 93 + if (pciehp_detect_acpi_slot(dev->subordinate)) 94 + return 0; 95 + return -ENODEV; 96 + } 97 + 98 + static int __init parse_detect_mode(void) 99 + { 100 + if (!pciehp_detect_mode) 101 + return PCIEHP_DETECT_DEFAULT; 102 + if (!strcmp(pciehp_detect_mode, "pcie")) 103 + return PCIEHP_DETECT_PCIE; 104 + if (!strcmp(pciehp_detect_mode, "acpi")) 105 + return PCIEHP_DETECT_ACPI; 106 + warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", 107 + pciehp_detect_mode); 108 + return PCIEHP_DETECT_DEFAULT; 109 + } 110 + 111 + void __init pciehp_acpi_slot_detection_init(void) 112 + { 113 + slot_detection_mode = parse_detect_mode(); 114 + }
+1
drivers/pci/hotplug/pciehp_core.c
··· 522 522 { 523 523 int retval = 0; 524 524 525 + pciehp_firmware_init(); 525 526 retval = pcie_port_service_register(&hpdriver_portdrv); 526 527 dbg("pcie_port_service_register = %d\n", retval); 527 528 info(DRIVER_DESC " version: " DRIVER_VERSION "\n");