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

PCI: pciehp: add auto option to pciehp_detect_mode

ACPI based hot-pluggable PCIe slot detection logic was added to
prevent the problem non hot-pluggable PCIe slot was detected as
hot-pluggable. The slot detection logic can be selected through
'pciehp_detect_mode', but it would be better if it is selected
automatically.

This patch adds 'auto' option for 'pciehp_detect_mode'. When it is
specified, pciehp judges which 'acpi' or 'pcie' should be used. It
seems that the physical slot number is duplicated among some slots on
most of the platforms with the above-mentioned problem. So 'auto' mode
uses this information to judge which 'acpi' or 'pcie' should be
used. That is, if duplicated physical slot numbers are detected,
'acpi' mode is used. This method is not perfect, but it's realistic.

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
e046cbd6 c9ffa5a5

+76 -4
+76 -4
drivers/pci/hotplug/pciehp_acpi.c
··· 28 28 29 29 #define PCIEHP_DETECT_PCIE (0) 30 30 #define PCIEHP_DETECT_ACPI (1) 31 - #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE 31 + #define PCIEHP_DETECT_AUTO (2) 32 + #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO 32 33 33 34 static int slot_detection_mode; 34 35 static char *pciehp_detect_mode; 35 36 module_param(pciehp_detect_mode, charp, 0444); 36 37 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"); 38 + "Slot detection mode: pcie, acpi, auto\n" 39 + " pcie - Use PCIe based slot detection\n" 40 + " acpi - Use ACPI for slot detection\n" 41 + " auto(default) - Auto select mode. Use acpi option if duplicate\n" 42 + " slot ids are found. Otherwise, use pcie option\n"); 40 43 41 44 static int is_ejectable(acpi_handle handle) 42 45 { ··· 106 103 return PCIEHP_DETECT_PCIE; 107 104 if (!strcmp(pciehp_detect_mode, "acpi")) 108 105 return PCIEHP_DETECT_ACPI; 106 + if (!strcmp(pciehp_detect_mode, "auto")) 107 + return PCIEHP_DETECT_AUTO; 109 108 warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", 110 109 pciehp_detect_mode); 111 110 return PCIEHP_DETECT_DEFAULT; 112 111 } 113 112 113 + static struct pcie_port_service_id __initdata port_pci_ids[] = { 114 + { 115 + .vendor = PCI_ANY_ID, 116 + .device = PCI_ANY_ID, 117 + .port_type = PCIE_ANY_PORT, 118 + .service_type = PCIE_PORT_SERVICE_HP, 119 + .driver_data = 0, 120 + }, { /* end: all zeroes */ } 121 + }; 122 + 123 + static int __initdata dup_slot_id; 124 + static int __initdata acpi_slot_detected; 125 + static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); 126 + 127 + /* Dummy driver for dumplicate name detection */ 128 + static int __init dummy_probe(struct pcie_device *dev, 129 + const struct pcie_port_service_id *id) 130 + { 131 + int pos; 132 + u32 slot_cap; 133 + struct slot *slot, *tmp; 134 + struct pci_dev *pdev = dev->port; 135 + if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) 136 + return -ENOMEM; 137 + /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ 138 + if (pciehp_get_hp_hw_control_from_firmware(pdev)) 139 + return -ENODEV; 140 + if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP))) 141 + return -ENODEV; 142 + pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); 143 + slot->number = slot_cap >> 19; 144 + list_for_each_entry(tmp, &dummy_slots, slot_list) { 145 + if (tmp->number == slot->number) 146 + dup_slot_id++; 147 + } 148 + list_add_tail(&slot->slot_list, &dummy_slots); 149 + if (!acpi_slot_detected && pciehp_detect_acpi_slot(pdev->subordinate)) 150 + acpi_slot_detected = 1; 151 + return -ENODEV; /* dummy driver always returns error */ 152 + } 153 + 154 + static struct pcie_port_service_driver __initdata dummy_driver = { 155 + .name = "pciehp_dummy", 156 + .id_table = port_pci_ids, 157 + .probe = dummy_probe, 158 + }; 159 + 160 + static int __init select_detection_mode(void) 161 + { 162 + struct slot *slot, *tmp; 163 + pcie_port_service_register(&dummy_driver); 164 + pcie_port_service_unregister(&dummy_driver); 165 + list_for_each_entry_safe(slot, tmp, &dummy_slots, slot_list) { 166 + list_del(&slot->slot_list); 167 + kfree(slot); 168 + } 169 + if (acpi_slot_detected && dup_slot_id) 170 + return PCIEHP_DETECT_ACPI; 171 + return PCIEHP_DETECT_PCIE; 172 + } 173 + 114 174 void __init pciehp_acpi_slot_detection_init(void) 115 175 { 116 176 slot_detection_mode = parse_detect_mode(); 177 + if (slot_detection_mode != PCIEHP_DETECT_AUTO) 178 + goto out; 179 + slot_detection_mode = select_detection_mode(); 180 + out: 181 + if (slot_detection_mode == PCIEHP_DETECT_ACPI) 182 + info("Using ACPI for slot detection.\n"); 117 183 }