Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
783c49fc | 2 | * Common ACPI functions for hot plug platforms |
1da177e4 | 3 | * |
783c49fc | 4 | * Copyright (C) 2006 Intel Corporation |
1da177e4 LT |
5 | * |
6 | * All rights reserved. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or (at | |
11 | * your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
16 | * NON INFRINGEMENT. See the GNU General Public License for more | |
17 | * details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
8cf4c195 | 23 | * Send feedback to <kristen.c.accardi@intel.com> |
1da177e4 LT |
24 | * |
25 | */ | |
26 | ||
1da177e4 | 27 | #include <linux/module.h> |
aad20cab | 28 | #include <linux/moduleparam.h> |
1da177e4 LT |
29 | #include <linux/kernel.h> |
30 | #include <linux/types.h> | |
31 | #include <linux/pci.h> | |
7a54f25c | 32 | #include <linux/pci_hotplug.h> |
ac9c052d | 33 | #include <linux/pci-acpi.h> |
783c49fc | 34 | #include <acpi/acpi.h> |
1da177e4 LT |
35 | #include <acpi/acpi_bus.h> |
36 | #include <acpi/actypes.h> | |
1da177e4 | 37 | |
aad20cab KK |
38 | #define MY_NAME "acpi_pcihp" |
39 | ||
66bef8c0 | 40 | #define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0) |
aad20cab KK |
41 | #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) |
42 | #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) | |
43 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) | |
44 | ||
1da177e4 LT |
45 | #define METHOD_NAME__SUN "_SUN" |
46 | #define METHOD_NAME__HPP "_HPP" | |
47 | #define METHOD_NAME_OSHP "OSHP" | |
48 | ||
aad20cab KK |
49 | static int debug_acpi; |
50 | ||
e22b7350 KK |
51 | static acpi_status |
52 | decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx) | |
53 | { | |
54 | int i; | |
55 | union acpi_object *fields = record->package.elements; | |
56 | u32 revision = fields[1].integer.value; | |
57 | ||
58 | switch (revision) { | |
59 | case 1: | |
60 | if (record->package.count != 6) | |
61 | return AE_ERROR; | |
62 | for (i = 2; i < 6; i++) | |
63 | if (fields[i].type != ACPI_TYPE_INTEGER) | |
64 | return AE_ERROR; | |
65 | hpx->t0 = &hpx->type0_data; | |
66 | hpx->t0->revision = revision; | |
67 | hpx->t0->cache_line_size = fields[2].integer.value; | |
68 | hpx->t0->latency_timer = fields[3].integer.value; | |
69 | hpx->t0->enable_serr = fields[4].integer.value; | |
70 | hpx->t0->enable_perr = fields[5].integer.value; | |
71 | break; | |
72 | default: | |
73 | printk(KERN_WARNING | |
74 | "%s: Type 0 Revision %d record not supported\n", | |
66bef8c0 | 75 | __func__, revision); |
e22b7350 KK |
76 | return AE_ERROR; |
77 | } | |
78 | return AE_OK; | |
79 | } | |
80 | ||
81 | static acpi_status | |
82 | decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx) | |
83 | { | |
84 | int i; | |
85 | union acpi_object *fields = record->package.elements; | |
86 | u32 revision = fields[1].integer.value; | |
87 | ||
88 | switch (revision) { | |
89 | case 1: | |
90 | if (record->package.count != 5) | |
91 | return AE_ERROR; | |
92 | for (i = 2; i < 5; i++) | |
93 | if (fields[i].type != ACPI_TYPE_INTEGER) | |
94 | return AE_ERROR; | |
95 | hpx->t1 = &hpx->type1_data; | |
96 | hpx->t1->revision = revision; | |
97 | hpx->t1->max_mem_read = fields[2].integer.value; | |
98 | hpx->t1->avg_max_split = fields[3].integer.value; | |
99 | hpx->t1->tot_max_split = fields[4].integer.value; | |
100 | break; | |
101 | default: | |
102 | printk(KERN_WARNING | |
103 | "%s: Type 1 Revision %d record not supported\n", | |
66bef8c0 | 104 | __func__, revision); |
e22b7350 KK |
105 | return AE_ERROR; |
106 | } | |
107 | return AE_OK; | |
108 | } | |
109 | ||
110 | static acpi_status | |
111 | decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx) | |
112 | { | |
113 | int i; | |
114 | union acpi_object *fields = record->package.elements; | |
115 | u32 revision = fields[1].integer.value; | |
116 | ||
117 | switch (revision) { | |
118 | case 1: | |
119 | if (record->package.count != 18) | |
120 | return AE_ERROR; | |
121 | for (i = 2; i < 18; i++) | |
122 | if (fields[i].type != ACPI_TYPE_INTEGER) | |
123 | return AE_ERROR; | |
124 | hpx->t2 = &hpx->type2_data; | |
125 | hpx->t2->revision = revision; | |
126 | hpx->t2->unc_err_mask_and = fields[2].integer.value; | |
127 | hpx->t2->unc_err_mask_or = fields[3].integer.value; | |
128 | hpx->t2->unc_err_sever_and = fields[4].integer.value; | |
129 | hpx->t2->unc_err_sever_or = fields[5].integer.value; | |
130 | hpx->t2->cor_err_mask_and = fields[6].integer.value; | |
131 | hpx->t2->cor_err_mask_or = fields[7].integer.value; | |
132 | hpx->t2->adv_err_cap_and = fields[8].integer.value; | |
133 | hpx->t2->adv_err_cap_or = fields[9].integer.value; | |
134 | hpx->t2->pci_exp_devctl_and = fields[10].integer.value; | |
135 | hpx->t2->pci_exp_devctl_or = fields[11].integer.value; | |
136 | hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value; | |
137 | hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value; | |
138 | hpx->t2->sec_unc_err_sever_and = fields[14].integer.value; | |
139 | hpx->t2->sec_unc_err_sever_or = fields[15].integer.value; | |
140 | hpx->t2->sec_unc_err_mask_and = fields[16].integer.value; | |
141 | hpx->t2->sec_unc_err_mask_or = fields[17].integer.value; | |
142 | break; | |
143 | default: | |
144 | printk(KERN_WARNING | |
145 | "%s: Type 2 Revision %d record not supported\n", | |
66bef8c0 | 146 | __func__, revision); |
e22b7350 KK |
147 | return AE_ERROR; |
148 | } | |
149 | return AE_OK; | |
150 | } | |
151 | ||
152 | static acpi_status | |
153 | acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) | |
154 | { | |
155 | acpi_status status; | |
156 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | |
157 | union acpi_object *package, *record, *fields; | |
158 | u32 type; | |
159 | int i; | |
160 | ||
161 | /* Clear the return buffer with zeros */ | |
162 | memset(hpx, 0, sizeof(struct hotplug_params)); | |
163 | ||
164 | status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); | |
165 | if (ACPI_FAILURE(status)) | |
166 | return status; | |
167 | ||
168 | package = (union acpi_object *)buffer.pointer; | |
169 | if (package->type != ACPI_TYPE_PACKAGE) { | |
170 | status = AE_ERROR; | |
171 | goto exit; | |
172 | } | |
173 | ||
174 | for (i = 0; i < package->package.count; i++) { | |
175 | record = &package->package.elements[i]; | |
176 | if (record->type != ACPI_TYPE_PACKAGE) { | |
177 | status = AE_ERROR; | |
178 | goto exit; | |
179 | } | |
180 | ||
181 | fields = record->package.elements; | |
182 | if (fields[0].type != ACPI_TYPE_INTEGER || | |
183 | fields[1].type != ACPI_TYPE_INTEGER) { | |
184 | status = AE_ERROR; | |
185 | goto exit; | |
186 | } | |
187 | ||
188 | type = fields[0].integer.value; | |
189 | switch (type) { | |
190 | case 0: | |
191 | status = decode_type0_hpx_record(record, hpx); | |
192 | if (ACPI_FAILURE(status)) | |
193 | goto exit; | |
194 | break; | |
195 | case 1: | |
196 | status = decode_type1_hpx_record(record, hpx); | |
197 | if (ACPI_FAILURE(status)) | |
198 | goto exit; | |
199 | break; | |
200 | case 2: | |
201 | status = decode_type2_hpx_record(record, hpx); | |
202 | if (ACPI_FAILURE(status)) | |
203 | goto exit; | |
204 | break; | |
205 | default: | |
206 | printk(KERN_ERR "%s: Type %d record not supported\n", | |
66bef8c0 | 207 | __func__, type); |
e22b7350 KK |
208 | status = AE_ERROR; |
209 | goto exit; | |
210 | } | |
211 | } | |
212 | exit: | |
213 | kfree(buffer.pointer); | |
214 | return status; | |
215 | } | |
1da177e4 | 216 | |
a8a2be94 | 217 | static acpi_status |
218 | acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) | |
1da177e4 LT |
219 | { |
220 | acpi_status status; | |
221 | u8 nui[4]; | |
222 | struct acpi_buffer ret_buf = { 0, NULL}; | |
b2e6e3ba | 223 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
1da177e4 | 224 | union acpi_object *ext_obj, *package; |
1da177e4 LT |
225 | int i, len = 0; |
226 | ||
b2e6e3ba MT |
227 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); |
228 | ||
e22b7350 KK |
229 | /* Clear the return buffer with zeros */ |
230 | memset(hpp, 0, sizeof(struct hotplug_params)); | |
231 | ||
1da177e4 | 232 | /* get _hpp */ |
a8a2be94 | 233 | status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf); |
1da177e4 LT |
234 | switch (status) { |
235 | case AE_BUFFER_OVERFLOW: | |
236 | ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); | |
237 | if (!ret_buf.pointer) { | |
783c49fc | 238 | printk(KERN_ERR "%s:%s alloc for _HPP fail\n", |
66bef8c0 | 239 | __func__, (char *)string.pointer); |
81b26bca | 240 | kfree(string.pointer); |
a8a2be94 | 241 | return AE_NO_MEMORY; |
1da177e4 | 242 | } |
a8a2be94 | 243 | status = acpi_evaluate_object(handle, METHOD_NAME__HPP, |
244 | NULL, &ret_buf); | |
1da177e4 LT |
245 | if (ACPI_SUCCESS(status)) |
246 | break; | |
247 | default: | |
248 | if (ACPI_FAILURE(status)) { | |
66bef8c0 | 249 | pr_debug("%s:%s _HPP fail=0x%x\n", __func__, |
b2e6e3ba | 250 | (char *)string.pointer, status); |
81b26bca | 251 | kfree(string.pointer); |
a8a2be94 | 252 | return status; |
1da177e4 LT |
253 | } |
254 | } | |
255 | ||
256 | ext_obj = (union acpi_object *) ret_buf.pointer; | |
257 | if (ext_obj->type != ACPI_TYPE_PACKAGE) { | |
66bef8c0 | 258 | printk(KERN_ERR "%s:%s _HPP obj not a package\n", __func__, |
b2e6e3ba | 259 | (char *)string.pointer); |
a8a2be94 | 260 | status = AE_ERROR; |
1da177e4 LT |
261 | goto free_and_return; |
262 | } | |
263 | ||
264 | len = ext_obj->package.count; | |
265 | package = (union acpi_object *) ret_buf.pointer; | |
266 | for ( i = 0; (i < len) || (i < 4); i++) { | |
267 | ext_obj = (union acpi_object *) &package->package.elements[i]; | |
268 | switch (ext_obj->type) { | |
269 | case ACPI_TYPE_INTEGER: | |
270 | nui[i] = (u8)ext_obj->integer.value; | |
271 | break; | |
272 | default: | |
783c49fc | 273 | printk(KERN_ERR "%s:%s _HPP obj type incorrect\n", |
66bef8c0 | 274 | __func__, (char *)string.pointer); |
a8a2be94 | 275 | status = AE_ERROR; |
1da177e4 LT |
276 | goto free_and_return; |
277 | } | |
278 | } | |
279 | ||
e22b7350 KK |
280 | hpp->t0 = &hpp->type0_data; |
281 | hpp->t0->cache_line_size = nui[0]; | |
282 | hpp->t0->latency_timer = nui[1]; | |
283 | hpp->t0->enable_serr = nui[2]; | |
284 | hpp->t0->enable_perr = nui[3]; | |
1da177e4 | 285 | |
e22b7350 KK |
286 | pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->t0->cache_line_size); |
287 | pr_debug(" _HPP: latency timer =0x%x\n", hpp->t0->latency_timer); | |
288 | pr_debug(" _HPP: enable SERR =0x%x\n", hpp->t0->enable_serr); | |
289 | pr_debug(" _HPP: enable PERR =0x%x\n", hpp->t0->enable_perr); | |
1da177e4 LT |
290 | |
291 | free_and_return: | |
81b26bca KA |
292 | kfree(string.pointer); |
293 | kfree(ret_buf.pointer); | |
a8a2be94 | 294 | return status; |
1da177e4 LT |
295 | } |
296 | ||
783c49fc KA |
297 | |
298 | ||
299 | /* acpi_run_oshp - get control of hotplug from the firmware | |
300 | * | |
301 | * @handle - the handle of the hotplug controller. | |
302 | */ | |
ac9c052d | 303 | static acpi_status acpi_run_oshp(acpi_handle handle) |
1da177e4 LT |
304 | { |
305 | acpi_status status; | |
b2e6e3ba MT |
306 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
307 | ||
308 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
1da177e4 LT |
309 | |
310 | /* run OSHP */ | |
a8a2be94 | 311 | status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); |
783c49fc | 312 | if (ACPI_FAILURE(status)) |
aad20cab KK |
313 | if (status != AE_NOT_FOUND) |
314 | printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", | |
66bef8c0 | 315 | __func__, (char *)string.pointer, status); |
aad20cab KK |
316 | else |
317 | dbg("%s:%s OSHP not found\n", | |
66bef8c0 | 318 | __func__, (char *)string.pointer); |
783c49fc | 319 | else |
66bef8c0 | 320 | pr_debug("%s:%s OSHP passes\n", __func__, |
b2e6e3ba MT |
321 | (char *)string.pointer); |
322 | ||
81b26bca | 323 | kfree(string.pointer); |
783c49fc KA |
324 | return status; |
325 | } | |
783c49fc KA |
326 | |
327 | /* acpi_get_hp_params_from_firmware | |
328 | * | |
7430e34c | 329 | * @bus - the pci_bus of the bus on which the device is newly added |
783c49fc KA |
330 | * @hpp - allocated by the caller |
331 | */ | |
7430e34c | 332 | acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, |
783c49fc KA |
333 | struct hotplug_params *hpp) |
334 | { | |
335 | acpi_status status = AE_NOT_FOUND; | |
7430e34c KK |
336 | acpi_handle handle, phandle; |
337 | struct pci_bus *pbus = bus; | |
338 | struct pci_dev *pdev; | |
339 | ||
340 | do { | |
341 | pdev = pbus->self; | |
342 | if (!pdev) { | |
343 | handle = acpi_get_pci_rootbridge_handle( | |
344 | pci_domain_nr(pbus), pbus->number); | |
345 | break; | |
346 | } | |
347 | handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); | |
348 | pbus = pbus->parent; | |
349 | } while (!handle); | |
783c49fc KA |
350 | |
351 | /* | |
352 | * _HPP settings apply to all child buses, until another _HPP is | |
353 | * encountered. If we don't find an _HPP for the input pci dev, | |
354 | * look for it in the parent device scope since that would apply to | |
355 | * this pci dev. If we don't find any _HPP, use hardcoded defaults | |
356 | */ | |
7430e34c | 357 | while (handle) { |
e22b7350 KK |
358 | status = acpi_run_hpx(handle, hpp); |
359 | if (ACPI_SUCCESS(status)) | |
360 | break; | |
783c49fc | 361 | status = acpi_run_hpp(handle, hpp); |
7430e34c KK |
362 | if (ACPI_SUCCESS(status)) |
363 | break; | |
364 | if (acpi_root_bridge(handle)) | |
365 | break; | |
366 | status = acpi_get_parent(handle, &phandle); | |
367 | if (ACPI_FAILURE(status)) | |
783c49fc | 368 | break; |
7430e34c | 369 | handle = phandle; |
1da177e4 | 370 | } |
a8a2be94 | 371 | return status; |
1da177e4 | 372 | } |
783c49fc KA |
373 | EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); |
374 | ||
ac9c052d KK |
375 | /** |
376 | * acpi_get_hp_hw_control_from_firmware | |
377 | * @dev: the pci_dev of the bridge that has a hotplug controller | |
378 | * @flags: requested control bits for _OSC | |
379 | * | |
380 | * Attempt to take hotplug control from firmware. | |
381 | */ | |
382 | int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) | |
383 | { | |
384 | acpi_status status; | |
385 | acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); | |
386 | struct pci_dev *pdev = dev; | |
387 | struct pci_bus *parent; | |
388 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; | |
389 | ||
390 | flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | | |
391 | OSC_SHPC_NATIVE_HP_CONTROL | | |
392 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); | |
393 | if (!flags) { | |
394 | err("Invalid flags %u specified!\n", flags); | |
395 | return -EINVAL; | |
396 | } | |
397 | ||
398 | /* | |
399 | * Per PCI firmware specification, we should run the ACPI _OSC | |
400 | * method to get control of hotplug hardware before using it. If | |
401 | * an _OSC is missing, we look for an OSHP to do the same thing. | |
402 | * To handle different BIOS behavior, we look for _OSC and OSHP | |
403 | * within the scope of the hotplug controller and its parents, | |
404 | * upto the host bridge under which this controller exists. | |
405 | */ | |
406 | while (!handle) { | |
407 | /* | |
408 | * This hotplug controller was not listed in the ACPI name | |
409 | * space at all. Try to get acpi handle of parent pci bus. | |
410 | */ | |
411 | if (!pdev || !pdev->bus->parent) | |
412 | break; | |
413 | parent = pdev->bus->parent; | |
414 | dbg("Could not find %s in acpi namespace, trying parent\n", | |
415 | pci_name(pdev)); | |
416 | if (!parent->self) | |
417 | /* Parent must be a host bridge */ | |
418 | handle = acpi_get_pci_rootbridge_handle( | |
419 | pci_domain_nr(parent), | |
420 | parent->number); | |
421 | else | |
422 | handle = DEVICE_ACPI_HANDLE(&(parent->self->dev)); | |
423 | pdev = parent->self; | |
424 | } | |
425 | ||
426 | while (handle) { | |
427 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
428 | dbg("Trying to get hotplug control for %s \n", | |
429 | (char *)string.pointer); | |
430 | status = pci_osc_control_set(handle, flags); | |
431 | if (status == AE_NOT_FOUND) | |
432 | status = acpi_run_oshp(handle); | |
433 | if (ACPI_SUCCESS(status)) { | |
434 | dbg("Gained control for hotplug HW for pci %s (%s)\n", | |
435 | pci_name(dev), (char *)string.pointer); | |
436 | kfree(string.pointer); | |
437 | return 0; | |
438 | } | |
439 | if (acpi_root_bridge(handle)) | |
440 | break; | |
441 | chandle = handle; | |
442 | status = acpi_get_parent(chandle, &handle); | |
443 | if (ACPI_FAILURE(status)) | |
444 | break; | |
445 | } | |
446 | ||
447 | dbg("Cannot get control of hotplug hardware for pci %s\n", | |
448 | pci_name(dev)); | |
449 | ||
450 | kfree(string.pointer); | |
451 | return -ENODEV; | |
452 | } | |
453 | EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); | |
1da177e4 | 454 | |
783c49fc KA |
455 | /* acpi_root_bridge - check to see if this acpi object is a root bridge |
456 | * | |
457 | * @handle - the acpi object in question. | |
458 | */ | |
459 | int acpi_root_bridge(acpi_handle handle) | |
a3a45ec8 | 460 | { |
461 | acpi_status status; | |
462 | struct acpi_device_info *info; | |
463 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | |
464 | int i; | |
465 | ||
466 | status = acpi_get_object_info(handle, &buffer); | |
467 | if (ACPI_SUCCESS(status)) { | |
468 | info = buffer.pointer; | |
469 | if ((info->valid & ACPI_VALID_HID) && | |
470 | !strcmp(PCI_ROOT_HID_STRING, | |
471 | info->hardware_id.value)) { | |
81b26bca | 472 | kfree(buffer.pointer); |
a3a45ec8 | 473 | return 1; |
474 | } | |
475 | if (info->valid & ACPI_VALID_CID) { | |
476 | for (i=0; i < info->compatibility_id.count; i++) { | |
477 | if (!strcmp(PCI_ROOT_HID_STRING, | |
478 | info->compatibility_id.id[i].value)) { | |
81b26bca | 479 | kfree(buffer.pointer); |
a3a45ec8 | 480 | return 1; |
481 | } | |
482 | } | |
483 | } | |
81b26bca | 484 | kfree(buffer.pointer); |
a3a45ec8 | 485 | } |
486 | return 0; | |
487 | } | |
783c49fc | 488 | EXPORT_SYMBOL_GPL(acpi_root_bridge); |
aad20cab KK |
489 | |
490 | module_param(debug_acpi, bool, 0644); | |
491 | MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); |