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 <linux/pci.h> 28#include <linux/pci_hotplug.h> 29#include <linux/slab.h> 30#include <linux/module.h> 31#include "pciehp.h" 32 33#define PCIEHP_DETECT_PCIE (0) 34#define PCIEHP_DETECT_ACPI (1) 35#define PCIEHP_DETECT_AUTO (2) 36#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO 37 38struct dummy_slot { 39 u32 number; 40 struct list_head list; 41}; 42 43static int slot_detection_mode; 44static char *pciehp_detect_mode; 45module_param(pciehp_detect_mode, charp, 0444); 46MODULE_PARM_DESC(pciehp_detect_mode, 47 "Slot detection mode: pcie, acpi, auto\n" 48 " pcie - Use PCIe based slot detection\n" 49 " acpi - Use ACPI for slot detection\n" 50 " auto(default) - Auto select mode. Use acpi option if duplicate\n" 51 " slot ids are found. Otherwise, use pcie option\n"); 52 53int pciehp_acpi_slot_detection_check(struct pci_dev *dev) 54{ 55 if (slot_detection_mode != PCIEHP_DETECT_ACPI) 56 return 0; 57 if (acpi_pci_detect_ejectable(ACPI_HANDLE(&dev->dev))) 58 return 0; 59 return -ENODEV; 60} 61 62static int __init parse_detect_mode(void) 63{ 64 if (!pciehp_detect_mode) 65 return PCIEHP_DETECT_DEFAULT; 66 if (!strcmp(pciehp_detect_mode, "pcie")) 67 return PCIEHP_DETECT_PCIE; 68 if (!strcmp(pciehp_detect_mode, "acpi")) 69 return PCIEHP_DETECT_ACPI; 70 if (!strcmp(pciehp_detect_mode, "auto")) 71 return PCIEHP_DETECT_AUTO; 72 warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", 73 pciehp_detect_mode); 74 return PCIEHP_DETECT_DEFAULT; 75} 76 77static int __initdata dup_slot_id; 78static int __initdata acpi_slot_detected; 79static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); 80 81/* Dummy driver for duplicate name detection */ 82static int __init dummy_probe(struct pcie_device *dev) 83{ 84 u32 slot_cap; 85 acpi_handle handle; 86 struct dummy_slot *slot, *tmp; 87 struct pci_dev *pdev = dev->port; 88 89 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); 90 slot = kzalloc(sizeof(*slot), GFP_KERNEL); 91 if (!slot) 92 return -ENOMEM; 93 slot->number = (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19; 94 list_for_each_entry(tmp, &dummy_slots, list) { 95 if (tmp->number == slot->number) 96 dup_slot_id++; 97 } 98 list_add_tail(&slot->list, &dummy_slots); 99 handle = ACPI_HANDLE(&pdev->dev); 100 if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle)) 101 acpi_slot_detected = 1; 102 return -ENODEV; /* dummy driver always returns error */ 103} 104 105static struct pcie_port_service_driver __initdata dummy_driver = { 106 .name = "pciehp_dummy", 107 .port_type = PCIE_ANY_PORT, 108 .service = PCIE_PORT_SERVICE_HP, 109 .probe = dummy_probe, 110}; 111 112static int __init select_detection_mode(void) 113{ 114 struct dummy_slot *slot, *tmp; 115 116 if (pcie_port_service_register(&dummy_driver)) 117 return PCIEHP_DETECT_ACPI; 118 pcie_port_service_unregister(&dummy_driver); 119 list_for_each_entry_safe(slot, tmp, &dummy_slots, list) { 120 list_del(&slot->list); 121 kfree(slot); 122 } 123 if (acpi_slot_detected && dup_slot_id) 124 return PCIEHP_DETECT_ACPI; 125 return PCIEHP_DETECT_PCIE; 126} 127 128void __init pciehp_acpi_slot_detection_init(void) 129{ 130 slot_detection_mode = parse_detect_mode(); 131 if (slot_detection_mode != PCIEHP_DETECT_AUTO) 132 goto out; 133 slot_detection_mode = select_detection_mode(); 134out: 135 if (slot_detection_mode == PCIEHP_DETECT_ACPI) 136 info("Using ACPI for slot detection.\n"); 137} 138