1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * pci_slot.c - ACPI PCI Slot Driver |
4 | * |
5 | * The code here is heavily leveraged from the acpiphp module. |
6 | * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance. |
7 | * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code |
8 | * review and fixes. |
9 | * |
10 | * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. |
11 | * Alex Chiang <achiang@hp.com> |
12 | * |
13 | * Copyright (C) 2013 Huawei Tech. Co., Ltd. |
14 | * Jiang Liu <jiang.liu@huawei.com> |
15 | */ |
16 | |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | |
19 | #include <linux/kernel.h> |
20 | #include <linux/init.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/types.h> |
23 | #include <linux/list.h> |
24 | #include <linux/pci.h> |
25 | #include <linux/acpi.h> |
26 | #include <linux/dmi.h> |
27 | #include <linux/pci-acpi.h> |
28 | |
29 | static int check_sta_before_sun; |
30 | |
31 | #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ |
32 | |
33 | struct acpi_pci_slot { |
34 | struct pci_slot *pci_slot; /* corresponding pci_slot */ |
35 | struct list_head list; /* node in the list of slots */ |
36 | }; |
37 | |
38 | static LIST_HEAD(slot_list); |
39 | static DEFINE_MUTEX(slot_list_lock); |
40 | |
41 | static int |
42 | check_slot(acpi_handle handle, unsigned long long *sun) |
43 | { |
44 | int device = -1; |
45 | unsigned long long adr, sta; |
46 | acpi_status status; |
47 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
48 | |
49 | acpi_get_name(object: handle, ACPI_FULL_PATHNAME, ret_path_ptr: &buffer); |
50 | pr_debug("Checking slot on path: %s\n" , (char *)buffer.pointer); |
51 | |
52 | if (check_sta_before_sun) { |
53 | /* If SxFy doesn't have _STA, we just assume it's there */ |
54 | status = acpi_evaluate_integer(handle, pathname: "_STA" , NULL, data: &sta); |
55 | if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) |
56 | goto out; |
57 | } |
58 | |
59 | status = acpi_evaluate_integer(handle, pathname: "_ADR" , NULL, data: &adr); |
60 | if (ACPI_FAILURE(status)) { |
61 | pr_debug("_ADR returned %d on %s\n" , |
62 | status, (char *)buffer.pointer); |
63 | goto out; |
64 | } |
65 | |
66 | /* No _SUN == not a slot == bail */ |
67 | status = acpi_evaluate_integer(handle, pathname: "_SUN" , NULL, data: sun); |
68 | if (ACPI_FAILURE(status)) { |
69 | pr_debug("_SUN returned %d on %s\n" , |
70 | status, (char *)buffer.pointer); |
71 | goto out; |
72 | } |
73 | |
74 | device = (adr >> 16) & 0xffff; |
75 | out: |
76 | kfree(objp: buffer.pointer); |
77 | return device; |
78 | } |
79 | |
80 | /* |
81 | * Check whether handle has an associated slot and create PCI slot if it has. |
82 | */ |
83 | static acpi_status |
84 | register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) |
85 | { |
86 | int device; |
87 | unsigned long long sun; |
88 | char name[SLOT_NAME_SIZE]; |
89 | struct acpi_pci_slot *slot; |
90 | struct pci_slot *pci_slot; |
91 | struct pci_bus *pci_bus = context; |
92 | |
93 | device = check_slot(handle, sun: &sun); |
94 | if (device < 0) |
95 | return AE_OK; |
96 | |
97 | /* |
98 | * There may be multiple PCI functions associated with the same slot. |
99 | * Check whether PCI slot has already been created for this PCI device. |
100 | */ |
101 | list_for_each_entry(slot, &slot_list, list) { |
102 | pci_slot = slot->pci_slot; |
103 | if (pci_slot->bus == pci_bus && pci_slot->number == device) |
104 | return AE_OK; |
105 | } |
106 | |
107 | slot = kmalloc(size: sizeof(*slot), GFP_KERNEL); |
108 | if (!slot) |
109 | return AE_OK; |
110 | |
111 | snprintf(buf: name, size: sizeof(name), fmt: "%llu" , sun); |
112 | pci_slot = pci_create_slot(parent: pci_bus, slot_nr: device, name, NULL); |
113 | if (IS_ERR(ptr: pci_slot)) { |
114 | pr_err("pci_create_slot returned %ld\n" , PTR_ERR(pci_slot)); |
115 | kfree(objp: slot); |
116 | return AE_OK; |
117 | } |
118 | |
119 | slot->pci_slot = pci_slot; |
120 | list_add(new: &slot->list, head: &slot_list); |
121 | |
122 | get_device(dev: &pci_bus->dev); |
123 | |
124 | pr_debug("%p, pci_bus: %x, device: %d, name: %s\n" , |
125 | pci_slot, pci_bus->number, device, name); |
126 | |
127 | return AE_OK; |
128 | } |
129 | |
130 | void acpi_pci_slot_enumerate(struct pci_bus *bus) |
131 | { |
132 | acpi_handle handle = ACPI_HANDLE(bus->bridge); |
133 | |
134 | if (handle) { |
135 | mutex_lock(&slot_list_lock); |
136 | acpi_walk_namespace(ACPI_TYPE_DEVICE, start_object: handle, max_depth: 1, |
137 | descending_callback: register_slot, NULL, context: bus, NULL); |
138 | mutex_unlock(lock: &slot_list_lock); |
139 | } |
140 | } |
141 | |
142 | void acpi_pci_slot_remove(struct pci_bus *bus) |
143 | { |
144 | struct acpi_pci_slot *slot, *tmp; |
145 | |
146 | mutex_lock(&slot_list_lock); |
147 | list_for_each_entry_safe(slot, tmp, &slot_list, list) { |
148 | if (slot->pci_slot->bus == bus) { |
149 | list_del(entry: &slot->list); |
150 | pci_destroy_slot(slot: slot->pci_slot); |
151 | put_device(dev: &bus->dev); |
152 | kfree(objp: slot); |
153 | } |
154 | } |
155 | mutex_unlock(lock: &slot_list_lock); |
156 | } |
157 | |
158 | static int do_sta_before_sun(const struct dmi_system_id *d) |
159 | { |
160 | pr_info("%s detected: will evaluate _STA before calling _SUN\n" , |
161 | d->ident); |
162 | check_sta_before_sun = 1; |
163 | return 0; |
164 | } |
165 | |
166 | static const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = { |
167 | /* |
168 | * Fujitsu Primequest machines will return 1023 to indicate an |
169 | * error if the _SUN method is evaluated on SxFy objects that |
170 | * are not present (as indicated by _STA), so for those machines, |
171 | * we want to check _STA before evaluating _SUN. |
172 | */ |
173 | { |
174 | .callback = do_sta_before_sun, |
175 | .ident = "Fujitsu PRIMEQUEST" , |
176 | .matches = { |
177 | DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED" ), |
178 | DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST" ), |
179 | }, |
180 | }, |
181 | {} |
182 | }; |
183 | |
184 | void __init acpi_pci_slot_init(void) |
185 | { |
186 | dmi_check_system(list: acpi_pci_slot_dmi_table); |
187 | } |
188 | |