diff options
Diffstat (limited to 'drivers/acpi/pci_slot.c')
-rw-r--r-- | drivers/acpi/pci_slot.c | 170 |
1 files changed, 24 insertions, 146 deletions
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index cd1434eb1de8..033d1179bdb5 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c | |||
@@ -9,6 +9,9 @@ | |||
9 | * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. | 9 | * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. |
10 | * Alex Chiang <achiang@hp.com> | 10 | * Alex Chiang <achiang@hp.com> |
11 | * | 11 | * |
12 | * Copyright (C) 2013 Huawei Tech. Co., Ltd. | ||
13 | * Jiang Liu <jiang.liu@huawei.com> | ||
14 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | 15 | * This program is free software; you can redistribute it and/or modify it |
13 | * under the terms and conditions of the GNU General Public License, | 16 | * under the terms and conditions of the GNU General Public License, |
14 | * version 2, as published by the Free Software Foundation. | 17 | * version 2, as published by the Free Software Foundation. |
@@ -28,10 +31,9 @@ | |||
28 | #include <linux/init.h> | 31 | #include <linux/init.h> |
29 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 33 | #include <linux/types.h> |
34 | #include <linux/list.h> | ||
31 | #include <linux/pci.h> | 35 | #include <linux/pci.h> |
32 | #include <linux/acpi.h> | 36 | #include <linux/acpi.h> |
33 | #include <acpi/acpi_bus.h> | ||
34 | #include <acpi/acpi_drivers.h> | ||
35 | #include <linux/dmi.h> | 37 | #include <linux/dmi.h> |
36 | 38 | ||
37 | static bool debug; | 39 | static bool debug; |
@@ -61,20 +63,12 @@ ACPI_MODULE_NAME("pci_slot"); | |||
61 | #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ | 63 | #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ |
62 | 64 | ||
63 | struct acpi_pci_slot { | 65 | struct acpi_pci_slot { |
64 | acpi_handle root_handle; /* handle of the root bridge */ | ||
65 | struct pci_slot *pci_slot; /* corresponding pci_slot */ | 66 | struct pci_slot *pci_slot; /* corresponding pci_slot */ |
66 | struct list_head list; /* node in the list of slots */ | 67 | struct list_head list; /* node in the list of slots */ |
67 | }; | 68 | }; |
68 | 69 | ||
69 | static int acpi_pci_slot_add(struct acpi_pci_root *root); | ||
70 | static void acpi_pci_slot_remove(struct acpi_pci_root *root); | ||
71 | |||
72 | static LIST_HEAD(slot_list); | 70 | static LIST_HEAD(slot_list); |
73 | static DEFINE_MUTEX(slot_list_lock); | 71 | static DEFINE_MUTEX(slot_list_lock); |
74 | static struct acpi_pci_driver acpi_pci_slot_driver = { | ||
75 | .add = acpi_pci_slot_add, | ||
76 | .remove = acpi_pci_slot_remove, | ||
77 | }; | ||
78 | 72 | ||
79 | static int | 73 | static int |
80 | check_slot(acpi_handle handle, unsigned long long *sun) | 74 | check_slot(acpi_handle handle, unsigned long long *sun) |
@@ -113,21 +107,8 @@ out: | |||
113 | return device; | 107 | return device; |
114 | } | 108 | } |
115 | 109 | ||
116 | struct callback_args { | ||
117 | acpi_walk_callback user_function; /* only for walk_p2p_bridge */ | ||
118 | struct pci_bus *pci_bus; | ||
119 | acpi_handle root_handle; | ||
120 | }; | ||
121 | |||
122 | /* | 110 | /* |
123 | * register_slot | 111 | * Check whether handle has an associated slot and create PCI slot if it has. |
124 | * | ||
125 | * Called once for each SxFy object in the namespace. Don't worry about | ||
126 | * calling pci_create_slot multiple times for the same pci_bus:device, | ||
127 | * since each subsequent call simply bumps the refcount on the pci_slot. | ||
128 | * | ||
129 | * The number of calls to pci_destroy_slot from unregister_slot is | ||
130 | * symmetrical. | ||
131 | */ | 112 | */ |
132 | static acpi_status | 113 | static acpi_status |
133 | register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | 114 | register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) |
@@ -137,13 +118,22 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
137 | char name[SLOT_NAME_SIZE]; | 118 | char name[SLOT_NAME_SIZE]; |
138 | struct acpi_pci_slot *slot; | 119 | struct acpi_pci_slot *slot; |
139 | struct pci_slot *pci_slot; | 120 | struct pci_slot *pci_slot; |
140 | struct callback_args *parent_context = context; | 121 | struct pci_bus *pci_bus = context; |
141 | struct pci_bus *pci_bus = parent_context->pci_bus; | ||
142 | 122 | ||
143 | device = check_slot(handle, &sun); | 123 | device = check_slot(handle, &sun); |
144 | if (device < 0) | 124 | if (device < 0) |
145 | return AE_OK; | 125 | return AE_OK; |
146 | 126 | ||
127 | /* | ||
128 | * There may be multiple PCI functions associated with the same slot. | ||
129 | * Check whether PCI slot has already been created for this PCI device. | ||
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 | |||
147 | slot = kmalloc(sizeof(*slot), GFP_KERNEL); | 137 | slot = kmalloc(sizeof(*slot), GFP_KERNEL); |
148 | if (!slot) { | 138 | if (!slot) { |
149 | err("%s: cannot allocate memory\n", __func__); | 139 | err("%s: cannot allocate memory\n", __func__); |
@@ -158,12 +148,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
158 | return AE_OK; | 148 | return AE_OK; |
159 | } | 149 | } |
160 | 150 | ||
161 | slot->root_handle = parent_context->root_handle; | ||
162 | slot->pci_slot = pci_slot; | 151 | slot->pci_slot = pci_slot; |
163 | INIT_LIST_HEAD(&slot->list); | ||
164 | mutex_lock(&slot_list_lock); | ||
165 | list_add(&slot->list, &slot_list); | 152 | list_add(&slot->list, &slot_list); |
166 | mutex_unlock(&slot_list_lock); | ||
167 | 153 | ||
168 | get_device(&pci_bus->dev); | 154 | get_device(&pci_bus->dev); |
169 | 155 | ||
@@ -173,131 +159,24 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
173 | return AE_OK; | 159 | return AE_OK; |
174 | } | 160 | } |
175 | 161 | ||
176 | /* | 162 | void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle) |
177 | * walk_p2p_bridge - discover and walk p2p bridges | ||
178 | * @handle: points to an acpi_pci_root | ||
179 | * @context: p2p_bridge_context pointer | ||
180 | * | ||
181 | * Note that when we call ourselves recursively, we pass a different | ||
182 | * value of pci_bus in the child_context. | ||
183 | */ | ||
184 | static acpi_status | ||
185 | walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
186 | { | ||
187 | int device, function; | ||
188 | unsigned long long adr; | ||
189 | acpi_status status; | ||
190 | acpi_handle dummy_handle; | ||
191 | acpi_walk_callback user_function; | ||
192 | |||
193 | struct pci_dev *dev; | ||
194 | struct pci_bus *pci_bus; | ||
195 | struct callback_args child_context; | ||
196 | struct callback_args *parent_context = context; | ||
197 | |||
198 | pci_bus = parent_context->pci_bus; | ||
199 | user_function = parent_context->user_function; | ||
200 | |||
201 | status = acpi_get_handle(handle, "_ADR", &dummy_handle); | ||
202 | if (ACPI_FAILURE(status)) | ||
203 | return AE_OK; | ||
204 | |||
205 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); | ||
206 | if (ACPI_FAILURE(status)) | ||
207 | return AE_OK; | ||
208 | |||
209 | device = (adr >> 16) & 0xffff; | ||
210 | function = adr & 0xffff; | ||
211 | |||
212 | dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function)); | ||
213 | if (!dev || !dev->subordinate) | ||
214 | goto out; | ||
215 | |||
216 | child_context.pci_bus = dev->subordinate; | ||
217 | child_context.user_function = user_function; | ||
218 | child_context.root_handle = parent_context->root_handle; | ||
219 | |||
220 | dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number); | ||
221 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
222 | user_function, NULL, &child_context, NULL); | ||
223 | if (ACPI_FAILURE(status)) | ||
224 | goto out; | ||
225 | |||
226 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
227 | walk_p2p_bridge, NULL, &child_context, NULL); | ||
228 | out: | ||
229 | pci_dev_put(dev); | ||
230 | return AE_OK; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * walk_root_bridge - generic root bridge walker | ||
235 | * @root: poiner of an acpi_pci_root | ||
236 | * @user_function: user callback for slot objects | ||
237 | * | ||
238 | * Call user_function for all objects underneath this root bridge. | ||
239 | * Walk p2p bridges underneath us and call user_function on those too. | ||
240 | */ | ||
241 | static int | ||
242 | walk_root_bridge(struct acpi_pci_root *root, acpi_walk_callback user_function) | ||
243 | { | ||
244 | acpi_status status; | ||
245 | acpi_handle handle = root->device->handle; | ||
246 | struct pci_bus *pci_bus = root->bus; | ||
247 | struct callback_args context; | ||
248 | |||
249 | context.pci_bus = pci_bus; | ||
250 | context.user_function = user_function; | ||
251 | context.root_handle = handle; | ||
252 | |||
253 | dbg("root bridge walk, pci_bus = %x\n", pci_bus->number); | ||
254 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
255 | user_function, NULL, &context, NULL); | ||
256 | if (ACPI_FAILURE(status)) | ||
257 | return status; | ||
258 | |||
259 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
260 | walk_p2p_bridge, NULL, &context, NULL); | ||
261 | if (ACPI_FAILURE(status)) | ||
262 | err("%s: walk_p2p_bridge failure - %d\n", __func__, status); | ||
263 | |||
264 | return status; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * acpi_pci_slot_add | ||
269 | * @handle: points to an acpi_pci_root | ||
270 | */ | ||
271 | static int | ||
272 | acpi_pci_slot_add(struct acpi_pci_root *root) | ||
273 | { | 163 | { |
274 | acpi_status status; | 164 | mutex_lock(&slot_list_lock); |
275 | 165 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, | |
276 | status = walk_root_bridge(root, register_slot); | 166 | register_slot, NULL, bus, NULL); |
277 | if (ACPI_FAILURE(status)) | 167 | mutex_unlock(&slot_list_lock); |
278 | err("%s: register_slot failure - %d\n", __func__, status); | ||
279 | |||
280 | return status; | ||
281 | } | 168 | } |
282 | 169 | ||
283 | /* | 170 | void acpi_pci_slot_remove(struct pci_bus *bus) |
284 | * acpi_pci_slot_remove | ||
285 | * @handle: points to an acpi_pci_root | ||
286 | */ | ||
287 | static void | ||
288 | acpi_pci_slot_remove(struct acpi_pci_root *root) | ||
289 | { | 171 | { |
290 | struct acpi_pci_slot *slot, *tmp; | 172 | struct acpi_pci_slot *slot, *tmp; |
291 | struct pci_bus *pbus; | ||
292 | acpi_handle handle = root->device->handle; | ||
293 | 173 | ||
294 | mutex_lock(&slot_list_lock); | 174 | mutex_lock(&slot_list_lock); |
295 | list_for_each_entry_safe(slot, tmp, &slot_list, list) { | 175 | list_for_each_entry_safe(slot, tmp, &slot_list, list) { |
296 | if (slot->root_handle == handle) { | 176 | if (slot->pci_slot->bus == bus) { |
297 | list_del(&slot->list); | 177 | list_del(&slot->list); |
298 | pbus = slot->pci_slot->bus; | ||
299 | pci_destroy_slot(slot->pci_slot); | 178 | pci_destroy_slot(slot->pci_slot); |
300 | put_device(&pbus->dev); | 179 | put_device(&bus->dev); |
301 | kfree(slot); | 180 | kfree(slot); |
302 | } | 181 | } |
303 | } | 182 | } |
@@ -332,5 +211,4 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { | |||
332 | void __init acpi_pci_slot_init(void) | 211 | void __init acpi_pci_slot_init(void) |
333 | { | 212 | { |
334 | dmi_check_system(acpi_pci_slot_dmi_table); | 213 | dmi_check_system(acpi_pci_slot_dmi_table); |
335 | acpi_pci_register_driver(&acpi_pci_slot_driver); | ||
336 | } | 214 | } |