1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
39static bool debug;
40static int check_sta_before_sun;
41
42#define DRIVER_VERSION "0.1"
43#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>"
44#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
45MODULE_AUTHOR(DRIVER_AUTHOR);
46MODULE_DESCRIPTION(DRIVER_DESC);
47MODULE_LICENSE("GPL");
48MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
49module_param(debug, bool, 0644);
50
51#define _COMPONENT ACPI_PCI_COMPONENT
52ACPI_MODULE_NAME("pci_slot");
53
54#define MY_NAME "pci_slot"
55#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg)
56#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg)
57#define dbg(format, arg...) \
58 do { \
59 if (debug) \
60 pr_debug("%s: " format, MY_NAME , ## arg); \
61 } while (0)
62
63#define SLOT_NAME_SIZE 21
64
65struct acpi_pci_slot {
66 struct pci_slot *pci_slot;
67 struct list_head list;
68};
69
70static LIST_HEAD(slot_list);
71static DEFINE_MUTEX(slot_list_lock);
72
73static int
74check_slot(acpi_handle handle, unsigned long long *sun)
75{
76 int device = -1;
77 unsigned long long adr, sta;
78 acpi_status status;
79 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
80
81 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
82 dbg("Checking slot on path: %s\n", (char *)buffer.pointer);
83
84 if (check_sta_before_sun) {
85
86 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
87 if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT))
88 goto out;
89 }
90
91 status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
92 if (ACPI_FAILURE(status)) {
93 dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer);
94 goto out;
95 }
96
97
98 status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
99 if (ACPI_FAILURE(status)) {
100 dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer);
101 goto out;
102 }
103
104 device = (adr >> 16) & 0xffff;
105out:
106 kfree(buffer.pointer);
107 return device;
108}
109
110
111
112
113static acpi_status
114register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
115{
116 int device;
117 unsigned long long sun;
118 char name[SLOT_NAME_SIZE];
119 struct acpi_pci_slot *slot;
120 struct pci_slot *pci_slot;
121 struct pci_bus *pci_bus = context;
122
123 device = check_slot(handle, &sun);
124 if (device < 0)
125 return AE_OK;
126
127
128
129
130
131 list_for_each_entry(slot, &slot_list, list) {
132 pci_slot = slot->pci_slot;
133 if (pci_slot->bus == pci_bus && pci_slot->number == device)
134 return AE_OK;
135 }
136
137 slot = kmalloc(sizeof(*slot), GFP_KERNEL);
138 if (!slot) {
139 err("%s: cannot allocate memory\n", __func__);
140 return AE_OK;
141 }
142
143 snprintf(name, sizeof(name), "%llu", sun);
144 pci_slot = pci_create_slot(pci_bus, device, name, NULL);
145 if (IS_ERR(pci_slot)) {
146 err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
147 kfree(slot);
148 return AE_OK;
149 }
150
151 slot->pci_slot = pci_slot;
152 list_add(&slot->list, &slot_list);
153
154 get_device(&pci_bus->dev);
155
156 dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n",
157 pci_slot, pci_bus->number, device, name);
158
159 return AE_OK;
160}
161
162void acpi_pci_slot_enumerate(struct pci_bus *bus)
163{
164 acpi_handle handle = ACPI_HANDLE(bus->bridge);
165
166 if (handle) {
167 mutex_lock(&slot_list_lock);
168 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
169 register_slot, NULL, bus, NULL);
170 mutex_unlock(&slot_list_lock);
171 }
172}
173
174void acpi_pci_slot_remove(struct pci_bus *bus)
175{
176 struct acpi_pci_slot *slot, *tmp;
177
178 mutex_lock(&slot_list_lock);
179 list_for_each_entry_safe(slot, tmp, &slot_list, list) {
180 if (slot->pci_slot->bus == bus) {
181 list_del(&slot->list);
182 pci_destroy_slot(slot->pci_slot);
183 put_device(&bus->dev);
184 kfree(slot);
185 }
186 }
187 mutex_unlock(&slot_list_lock);
188}
189
190static int do_sta_before_sun(const struct dmi_system_id *d)
191{
192 info("%s detected: will evaluate _STA before calling _SUN\n", d->ident);
193 check_sta_before_sun = 1;
194 return 0;
195}
196
197static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = {
198
199
200
201
202
203
204 {
205 .callback = do_sta_before_sun,
206 .ident = "Fujitsu PRIMEQUEST",
207 .matches = {
208 DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
209 DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
210 },
211 },
212 {}
213};
214
215void __init acpi_pci_slot_init(void)
216{
217 dmi_check_system(acpi_pci_slot_dmi_table);
218}
219