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