1/* 2 * pci_slot.c - ACPI PCI Slot Driver 3 * 4 * The code here is heavily leveraged from the acpiphp module. 5 * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance. 6 * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code 7 * review and fixes. 8 * 9 * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. 10 * Alex Chiang <achiang@hp.com> 11 * 12 * Copyright (C) 2013 Huawei Tech. Co., Ltd. 13 * Jiang Liu <jiang.liu@huawei.com> 14 * 15 * This program is free software; you can redistribute it and/or modify it 16 * under the terms and conditions of the GNU General Public License, 17 * version 2, as published by the Free Software Foundation. 18 * 19 * This program is distributed in the hope that it will be useful, but 20 * WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 * General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License along 25 * with this program; if not, write to the Free Software Foundation, Inc., 26 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 27 */ 28 29#include <linux/kernel.h> 30#include <linux/module.h> 31#include <linux/init.h> 32#include <linux/slab.h> 33#include <linux/types.h> 34#include <linux/list.h> 35#include <linux/pci.h> 36#include <linux/acpi.h> 37#include <linux/dmi.h> 38#include <linux/pci-acpi.h> 39 40static bool debug; 41static int check_sta_before_sun; 42 43#define DRIVER_VERSION "0.1" 44#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>" 45#define DRIVER_DESC "ACPI PCI Slot Detection Driver" 46MODULE_AUTHOR(DRIVER_AUTHOR); 47MODULE_DESCRIPTION(DRIVER_DESC); 48MODULE_LICENSE("GPL"); 49MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 50module_param(debug, bool, 0644); 51 52#define _COMPONENT ACPI_PCI_COMPONENT 53ACPI_MODULE_NAME("pci_slot"); 54 55#define MY_NAME "pci_slot" 56#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg) 57#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg) 58#define dbg(format, arg...) \ 59 do { \ 60 if (debug) \ 61 pr_debug("%s: " format, MY_NAME , ## arg); \ 62 } while (0) 63 64#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ 65 66struct acpi_pci_slot { 67 struct pci_slot *pci_slot; /* corresponding pci_slot */ 68 struct list_head list; /* node in the list of slots */ 69}; 70 71static LIST_HEAD(slot_list); 72static DEFINE_MUTEX(slot_list_lock); 73 74static int 75check_slot(acpi_handle handle, unsigned long long *sun) 76{ 77 int device = -1; 78 unsigned long long adr, sta; 79 acpi_status status; 80 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 81 82 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); 83 dbg("Checking slot on path: %s\n", (char *)buffer.pointer); 84 85 if (check_sta_before_sun) { 86 /* If SxFy doesn't have _STA, we just assume it's there */ 87 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); 88 if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) 89 goto out; 90 } 91 92 status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); 93 if (ACPI_FAILURE(status)) { 94 dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer); 95 goto out; 96 } 97 98 /* No _SUN == not a slot == bail */ 99 status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); 100 if (ACPI_FAILURE(status)) { 101 dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer); 102 goto out; 103 } 104 105 device = (adr >> 16) & 0xffff; 106out: 107 kfree(buffer.pointer); 108 return device; 109} 110 111/* 112 * Check whether handle has an associated slot and create PCI slot if it has. 113 */ 114static acpi_status 115register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) 116{ 117 int device; 118 unsigned long long sun; 119 char name[SLOT_NAME_SIZE]; 120 struct acpi_pci_slot *slot; 121 struct pci_slot *pci_slot; 122 struct pci_bus *pci_bus = context; 123 124 device = check_slot(handle, &sun); 125 if (device < 0) 126 return AE_OK; 127 128 /* 129 * There may be multiple PCI functions associated with the same slot. 130 * Check whether PCI slot has already been created for this PCI device. 131 */ 132 list_for_each_entry(slot, &slot_list, list) { 133 pci_slot = slot->pci_slot; 134 if (pci_slot->bus == pci_bus && pci_slot->number == device) 135 return AE_OK; 136 } 137 138 slot = kmalloc(sizeof(*slot), GFP_KERNEL); 139 if (!slot) { 140 err("%s: cannot allocate memory\n", __func__); 141 return AE_OK; 142 } 143 144 snprintf(name, sizeof(name), "%llu", sun); 145 pci_slot = pci_create_slot(pci_bus, device, name, NULL); 146 if (IS_ERR(pci_slot)) { 147 err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); 148 kfree(slot); 149 return AE_OK; 150 } 151 152 slot->pci_slot = pci_slot; 153 list_add(&slot->list, &slot_list); 154 155 get_device(&pci_bus->dev); 156 157 dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n", 158 pci_slot, pci_bus->number, device, name); 159 160 return AE_OK; 161} 162 163void acpi_pci_slot_enumerate(struct pci_bus *bus) 164{ 165 acpi_handle handle = ACPI_HANDLE(bus->bridge); 166 167 if (handle) { 168 mutex_lock(&slot_list_lock); 169 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 170 register_slot, NULL, bus, NULL); 171 mutex_unlock(&slot_list_lock); 172 } 173} 174 175void acpi_pci_slot_remove(struct pci_bus *bus) 176{ 177 struct acpi_pci_slot *slot, *tmp; 178 179 mutex_lock(&slot_list_lock); 180 list_for_each_entry_safe(slot, tmp, &slot_list, list) { 181 if (slot->pci_slot->bus == bus) { 182 list_del(&slot->list); 183 pci_destroy_slot(slot->pci_slot); 184 put_device(&bus->dev); 185 kfree(slot); 186 } 187 } 188 mutex_unlock(&slot_list_lock); 189} 190 191static int do_sta_before_sun(const struct dmi_system_id *d) 192{ 193 info("%s detected: will evaluate _STA before calling _SUN\n", d->ident); 194 check_sta_before_sun = 1; 195 return 0; 196} 197 198static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { 199 /* 200 * Fujitsu Primequest machines will return 1023 to indicate an 201 * error if the _SUN method is evaluated on SxFy objects that 202 * are not present (as indicated by _STA), so for those machines, 203 * we want to check _STA before evaluating _SUN. 204 */ 205 { 206 .callback = do_sta_before_sun, 207 .ident = "Fujitsu PRIMEQUEST", 208 .matches = { 209 DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"), 210 DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"), 211 }, 212 }, 213 {} 214}; 215 216void __init acpi_pci_slot_init(void) 217{ 218 dmi_check_system(acpi_pci_slot_dmi_table); 219} 220