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(DEVICE_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 dumplicate name detection */ 82static int __init dummy_probe(struct pcie_device *dev) 83{ 84 int pos; 85 u32 slot_cap; 86 acpi_handle handle; 87 struct dummy_slot *slot, *tmp; 88 struct pci_dev *pdev = dev->port; 89 90 pos = pci_pcie_cap(pdev); 91 if (!pos) 92 return -ENODEV; 93 pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); 94 slot = kzalloc(sizeof(*slot), GFP_KERNEL); 95 if (!slot) 96 return -ENOMEM; 97 slot->number = slot_cap >> 19; 98 list_for_each_entry(tmp, &dummy_slots, list) { 99 if (tmp->number == slot->number) 100 dup_slot_id++; 101 } 102 list_add_tail(&slot->list, &dummy_slots); 103 handle = DEVICE_ACPI_HANDLE(&pdev->dev); 104 if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle)) 105 acpi_slot_detected = 1; 106 return -ENODEV; /* dummy driver always returns error */ 107} 108 109static struct pcie_port_service_driver __initdata dummy_driver = { 110 .name = "pciehp_dummy", 111 .port_type = PCIE_ANY_PORT, 112 .service = PCIE_PORT_SERVICE_HP, 113 .probe = dummy_probe, 114}; 115 116static int __init select_detection_mode(void) 117{ 118 struct dummy_slot *slot, *tmp; 119 if (pcie_port_service_register(&dummy_driver)) 120 return PCIEHP_DETECT_ACPI; 121 pcie_port_service_unregister(&dummy_driver); 122 list_for_each_entry_safe(slot, tmp, &dummy_slots, list) { 123 list_del(&slot->list); 124 kfree(slot); 125 } 126 if (acpi_slot_detected && dup_slot_id) 127 return PCIEHP_DETECT_ACPI; 128 return PCIEHP_DETECT_PCIE; 129} 130 131void __init pciehp_acpi_slot_detection_init(void) 132{ 133 slot_detection_mode = parse_detect_mode(); 134 if (slot_detection_mode != PCIEHP_DETECT_AUTO) 135 goto out; 136 slot_detection_mode = select_detection_mode(); 137out: 138 if (slot_detection_mode == PCIEHP_DETECT_ACPI) 139 info("Using ACPI for slot detection.\n"); 140} 141