blob: aa8f4c72b6873d66c8fc4fb25eb92edd0e271132 [file] [log] [blame]
Alex Chiang8344b562008-06-10 15:30:42 -06001/*
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 *
Alex Chiange4268aa2008-07-17 11:13:32 -06009 * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P.
10 * Alex Chiang <achiang@hp.com>
Alex Chiang8344b562008-06-10 15:30:42 -060011 *
Jiang Liu5c0b04e2013-04-12 05:44:24 +000012 * Copyright (C) 2013 Huawei Tech. Co., Ltd.
13 * Jiang Liu <jiang.liu@huawei.com>
14 *
Alex Chiang8344b562008-06-10 15:30:42 -060015 * 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.
Alex Chiang8344b562008-06-10 15:30:42 -060023 */
24
Joe Perches2cfd93d2016-05-27 09:00:28 -070025#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
26
Alex Chiang8344b562008-06-10 15:30:42 -060027#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Alex Chiang8344b562008-06-10 15:30:42 -060031#include <linux/types.h>
Jiang Liu5c0b04e2013-04-12 05:44:24 +000032#include <linux/list.h>
Alex Chiang8344b562008-06-10 15:30:42 -060033#include <linux/pci.h>
34#include <linux/acpi.h>
Len Browneb27cae2009-07-06 23:40:19 -040035#include <linux/dmi.h>
Rashikae22e58a2013-12-17 14:48:19 +053036#include <linux/pci-acpi.h>
Alex Chiang8344b562008-06-10 15:30:42 -060037
Rusty Russell90ab5ee2012-01-13 09:32:20 +103038static bool debug;
Alex Chiang8344b562008-06-10 15:30:42 -060039static int check_sta_before_sun;
40
41#define DRIVER_VERSION "0.1"
42#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>"
43#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
44MODULE_AUTHOR(DRIVER_AUTHOR);
45MODULE_DESCRIPTION(DRIVER_DESC);
46MODULE_LICENSE("GPL");
47MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
48module_param(debug, bool, 0644);
49
50#define _COMPONENT ACPI_PCI_COMPONENT
51ACPI_MODULE_NAME("pci_slot");
52
Joe Perches2cfd93d2016-05-27 09:00:28 -070053#define dbg(fmt, ...) \
54do { \
55 if (debug) \
56 pr_debug(fmt, ##__VA_ARGS__); \
57} while (0)
Alex Chiang8344b562008-06-10 15:30:42 -060058
Alex Chiang7e24bc12009-08-04 14:44:17 -060059#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */
Alex Chiang8344b562008-06-10 15:30:42 -060060
61struct acpi_pci_slot {
Alex Chiang8344b562008-06-10 15:30:42 -060062 struct pci_slot *pci_slot; /* corresponding pci_slot */
63 struct list_head list; /* node in the list of slots */
64};
65
Alex Chiang8344b562008-06-10 15:30:42 -060066static LIST_HEAD(slot_list);
67static DEFINE_MUTEX(slot_list_lock);
Alex Chiang8344b562008-06-10 15:30:42 -060068
69static int
Matthew Wilcox27663c52008-10-10 02:22:59 -040070check_slot(acpi_handle handle, unsigned long long *sun)
Alex Chiang8344b562008-06-10 15:30:42 -060071{
Matthew Wilcox362b7072008-07-22 12:37:17 -060072 int device = -1;
Matthew Wilcox27663c52008-10-10 02:22:59 -040073 unsigned long long adr, sta;
Alex Chiang8344b562008-06-10 15:30:42 -060074 acpi_status status;
75 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
76
77 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
78 dbg("Checking slot on path: %s\n", (char *)buffer.pointer);
79
80 if (check_sta_before_sun) {
81 /* If SxFy doesn't have _STA, we just assume it's there */
82 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
Matthew Wilcox362b7072008-07-22 12:37:17 -060083 if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT))
Alex Chiang8344b562008-06-10 15:30:42 -060084 goto out;
Alex Chiang8344b562008-06-10 15:30:42 -060085 }
86
87 status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
88 if (ACPI_FAILURE(status)) {
89 dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer);
Alex Chiang8344b562008-06-10 15:30:42 -060090 goto out;
91 }
92
Alex Chiang8344b562008-06-10 15:30:42 -060093 /* No _SUN == not a slot == bail */
94 status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
95 if (ACPI_FAILURE(status)) {
96 dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer);
Alex Chiang8344b562008-06-10 15:30:42 -060097 goto out;
98 }
99
Matthew Wilcox362b7072008-07-22 12:37:17 -0600100 device = (adr >> 16) & 0xffff;
Alex Chiang8344b562008-06-10 15:30:42 -0600101out:
102 kfree(buffer.pointer);
Matthew Wilcox362b7072008-07-22 12:37:17 -0600103 return device;
Alex Chiang8344b562008-06-10 15:30:42 -0600104}
105
Alex Chiang8344b562008-06-10 15:30:42 -0600106/*
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000107 * Check whether handle has an associated slot and create PCI slot if it has.
Alex Chiang8344b562008-06-10 15:30:42 -0600108 */
109static acpi_status
110register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
111{
112 int device;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400113 unsigned long long sun;
Alex Chiang8344b562008-06-10 15:30:42 -0600114 char name[SLOT_NAME_SIZE];
115 struct acpi_pci_slot *slot;
116 struct pci_slot *pci_slot;
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000117 struct pci_bus *pci_bus = context;
Alex Chiang8344b562008-06-10 15:30:42 -0600118
Matthew Wilcox362b7072008-07-22 12:37:17 -0600119 device = check_slot(handle, &sun);
120 if (device < 0)
Alex Chiang8344b562008-06-10 15:30:42 -0600121 return AE_OK;
122
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000123 /*
124 * There may be multiple PCI functions associated with the same slot.
125 * Check whether PCI slot has already been created for this PCI device.
126 */
127 list_for_each_entry(slot, &slot_list, list) {
128 pci_slot = slot->pci_slot;
129 if (pci_slot->bus == pci_bus && pci_slot->number == device)
130 return AE_OK;
131 }
132
Alex Chiang8344b562008-06-10 15:30:42 -0600133 slot = kmalloc(sizeof(*slot), GFP_KERNEL);
Joe Perches2cfd93d2016-05-27 09:00:28 -0700134 if (!slot)
Alex Chiang8344b562008-06-10 15:30:42 -0600135 return AE_OK;
Alex Chiang8344b562008-06-10 15:30:42 -0600136
Alex Chiang7e24bc12009-08-04 14:44:17 -0600137 snprintf(name, sizeof(name), "%llu", sun);
Alex Chiang828f3762008-10-20 17:40:52 -0600138 pci_slot = pci_create_slot(pci_bus, device, name, NULL);
Alex Chiang8344b562008-06-10 15:30:42 -0600139 if (IS_ERR(pci_slot)) {
Joe Perches2cfd93d2016-05-27 09:00:28 -0700140 pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
Alex Chiang8344b562008-06-10 15:30:42 -0600141 kfree(slot);
Alex Chiange4268aa2008-07-17 11:13:32 -0600142 return AE_OK;
Alex Chiang8344b562008-06-10 15:30:42 -0600143 }
144
Alex Chiang8344b562008-06-10 15:30:42 -0600145 slot->pci_slot = pci_slot;
Alex Chiang8344b562008-06-10 15:30:42 -0600146 list_add(&slot->list, &slot_list);
Alex Chiang8344b562008-06-10 15:30:42 -0600147
Alex Chiang72800362009-03-30 10:50:19 -0600148 get_device(&pci_bus->dev);
149
Joe Perches2cfd93d2016-05-27 09:00:28 -0700150 dbg("%p, pci_bus: %x, device: %d, name: %s\n",
151 pci_slot, pci_bus->number, device, name);
Alex Chiang8344b562008-06-10 15:30:42 -0600152
153 return AE_OK;
154}
155
Rafael J. Wysockibe1c9de2013-07-13 23:27:23 +0200156void acpi_pci_slot_enumerate(struct pci_bus *bus)
Alex Chiang8344b562008-06-10 15:30:42 -0600157{
Rafael J. Wysockibe1c9de2013-07-13 23:27:23 +0200158 acpi_handle handle = ACPI_HANDLE(bus->bridge);
159
160 if (handle) {
161 mutex_lock(&slot_list_lock);
162 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
163 register_slot, NULL, bus, NULL);
164 mutex_unlock(&slot_list_lock);
165 }
Alex Chiang8344b562008-06-10 15:30:42 -0600166}
167
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000168void acpi_pci_slot_remove(struct pci_bus *bus)
Alex Chiang8344b562008-06-10 15:30:42 -0600169{
170 struct acpi_pci_slot *slot, *tmp;
171
172 mutex_lock(&slot_list_lock);
173 list_for_each_entry_safe(slot, tmp, &slot_list, list) {
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000174 if (slot->pci_slot->bus == bus) {
Alex Chiang8344b562008-06-10 15:30:42 -0600175 list_del(&slot->list);
176 pci_destroy_slot(slot->pci_slot);
Jiang Liu5c0b04e2013-04-12 05:44:24 +0000177 put_device(&bus->dev);
Alex Chiang8344b562008-06-10 15:30:42 -0600178 kfree(slot);
179 }
180 }
181 mutex_unlock(&slot_list_lock);
182}
183
184static int do_sta_before_sun(const struct dmi_system_id *d)
185{
Joe Perches2cfd93d2016-05-27 09:00:28 -0700186 pr_info("%s detected: will evaluate _STA before calling _SUN\n",
187 d->ident);
Alex Chiang8344b562008-06-10 15:30:42 -0600188 check_sta_before_sun = 1;
189 return 0;
190}
191
192static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = {
193 /*
194 * Fujitsu Primequest machines will return 1023 to indicate an
195 * error if the _SUN method is evaluated on SxFy objects that
196 * are not present (as indicated by _STA), so for those machines,
197 * we want to check _STA before evaluating _SUN.
198 */
199 {
200 .callback = do_sta_before_sun,
201 .ident = "Fujitsu PRIMEQUEST",
202 .matches = {
203 DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
204 DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
205 },
206 },
207 {}
208};
209
Jiang Liuab1a2e02013-01-19 00:07:42 +0800210void __init acpi_pci_slot_init(void)
Alex Chiang8344b562008-06-10 15:30:42 -0600211{
212 dmi_check_system(acpi_pci_slot_dmi_table);
Alex Chiang8344b562008-06-10 15:30:42 -0600213}