]>
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> | |
783c49fc | 32 | #include <acpi/acpi.h> |
1da177e4 LT |
33 | #include <acpi/acpi_bus.h> |
34 | #include <acpi/actypes.h> | |
783c49fc | 35 | #include "pci_hotplug.h" |
1da177e4 | 36 | |
aad20cab KK |
37 | #define MY_NAME "acpi_pcihp" |
38 | ||
39 | #define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) | |
40 | #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) | |
41 | #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) | |
42 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) | |
43 | ||
1da177e4 LT |
44 | #define METHOD_NAME__SUN "_SUN" |
45 | #define METHOD_NAME__HPP "_HPP" | |
46 | #define METHOD_NAME_OSHP "OSHP" | |
47 | ||
aad20cab KK |
48 | static int debug_acpi; |
49 | ||
1da177e4 | 50 | |
a8a2be94 RS |
51 | static acpi_status |
52 | acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) | |
1da177e4 LT |
53 | { |
54 | acpi_status status; | |
55 | u8 nui[4]; | |
56 | struct acpi_buffer ret_buf = { 0, NULL}; | |
b2e6e3ba | 57 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
1da177e4 | 58 | union acpi_object *ext_obj, *package; |
1da177e4 LT |
59 | int i, len = 0; |
60 | ||
b2e6e3ba MT |
61 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); |
62 | ||
1da177e4 | 63 | /* get _hpp */ |
a8a2be94 | 64 | status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf); |
1da177e4 LT |
65 | switch (status) { |
66 | case AE_BUFFER_OVERFLOW: | |
67 | ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); | |
68 | if (!ret_buf.pointer) { | |
783c49fc | 69 | printk(KERN_ERR "%s:%s alloc for _HPP fail\n", |
b2e6e3ba | 70 | __FUNCTION__, (char *)string.pointer); |
81b26bca | 71 | kfree(string.pointer); |
a8a2be94 | 72 | return AE_NO_MEMORY; |
1da177e4 | 73 | } |
a8a2be94 RS |
74 | status = acpi_evaluate_object(handle, METHOD_NAME__HPP, |
75 | NULL, &ret_buf); | |
1da177e4 LT |
76 | if (ACPI_SUCCESS(status)) |
77 | break; | |
78 | default: | |
79 | if (ACPI_FAILURE(status)) { | |
783c49fc | 80 | pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__, |
b2e6e3ba | 81 | (char *)string.pointer, status); |
81b26bca | 82 | kfree(string.pointer); |
a8a2be94 | 83 | return status; |
1da177e4 LT |
84 | } |
85 | } | |
86 | ||
87 | ext_obj = (union acpi_object *) ret_buf.pointer; | |
88 | if (ext_obj->type != ACPI_TYPE_PACKAGE) { | |
783c49fc | 89 | printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__, |
b2e6e3ba | 90 | (char *)string.pointer); |
a8a2be94 | 91 | status = AE_ERROR; |
1da177e4 LT |
92 | goto free_and_return; |
93 | } | |
94 | ||
95 | len = ext_obj->package.count; | |
96 | package = (union acpi_object *) ret_buf.pointer; | |
97 | for ( i = 0; (i < len) || (i < 4); i++) { | |
98 | ext_obj = (union acpi_object *) &package->package.elements[i]; | |
99 | switch (ext_obj->type) { | |
100 | case ACPI_TYPE_INTEGER: | |
101 | nui[i] = (u8)ext_obj->integer.value; | |
102 | break; | |
103 | default: | |
783c49fc | 104 | printk(KERN_ERR "%s:%s _HPP obj type incorrect\n", |
b2e6e3ba | 105 | __FUNCTION__, (char *)string.pointer); |
a8a2be94 | 106 | status = AE_ERROR; |
1da177e4 LT |
107 | goto free_and_return; |
108 | } | |
109 | } | |
110 | ||
a8a2be94 RS |
111 | hpp->cache_line_size = nui[0]; |
112 | hpp->latency_timer = nui[1]; | |
113 | hpp->enable_serr = nui[2]; | |
114 | hpp->enable_perr = nui[3]; | |
1da177e4 | 115 | |
783c49fc KA |
116 | pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->cache_line_size); |
117 | pr_debug(" _HPP: latency timer =0x%x\n", hpp->latency_timer); | |
118 | pr_debug(" _HPP: enable SERR =0x%x\n", hpp->enable_serr); | |
119 | pr_debug(" _HPP: enable PERR =0x%x\n", hpp->enable_perr); | |
1da177e4 LT |
120 | |
121 | free_and_return: | |
81b26bca KA |
122 | kfree(string.pointer); |
123 | kfree(ret_buf.pointer); | |
a8a2be94 | 124 | return status; |
1da177e4 LT |
125 | } |
126 | ||
783c49fc KA |
127 | |
128 | ||
129 | /* acpi_run_oshp - get control of hotplug from the firmware | |
130 | * | |
131 | * @handle - the handle of the hotplug controller. | |
132 | */ | |
133 | acpi_status acpi_run_oshp(acpi_handle handle) | |
1da177e4 LT |
134 | { |
135 | acpi_status status; | |
b2e6e3ba MT |
136 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
137 | ||
138 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
1da177e4 LT |
139 | |
140 | /* run OSHP */ | |
a8a2be94 | 141 | status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); |
783c49fc | 142 | if (ACPI_FAILURE(status)) |
aad20cab KK |
143 | if (status != AE_NOT_FOUND) |
144 | printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", | |
145 | __FUNCTION__, (char *)string.pointer, status); | |
146 | else | |
147 | dbg("%s:%s OSHP not found\n", | |
148 | __FUNCTION__, (char *)string.pointer); | |
783c49fc | 149 | else |
b2e6e3ba MT |
150 | pr_debug("%s:%s OSHP passes\n", __FUNCTION__, |
151 | (char *)string.pointer); | |
152 | ||
81b26bca | 153 | kfree(string.pointer); |
783c49fc KA |
154 | return status; |
155 | } | |
156 | EXPORT_SYMBOL_GPL(acpi_run_oshp); | |
157 | ||
158 | ||
159 | ||
160 | /* acpi_get_hp_params_from_firmware | |
161 | * | |
7430e34c | 162 | * @bus - the pci_bus of the bus on which the device is newly added |
783c49fc KA |
163 | * @hpp - allocated by the caller |
164 | */ | |
7430e34c | 165 | acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, |
783c49fc KA |
166 | struct hotplug_params *hpp) |
167 | { | |
168 | acpi_status status = AE_NOT_FOUND; | |
7430e34c KK |
169 | acpi_handle handle, phandle; |
170 | struct pci_bus *pbus = bus; | |
171 | struct pci_dev *pdev; | |
172 | ||
173 | do { | |
174 | pdev = pbus->self; | |
175 | if (!pdev) { | |
176 | handle = acpi_get_pci_rootbridge_handle( | |
177 | pci_domain_nr(pbus), pbus->number); | |
178 | break; | |
179 | } | |
180 | handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); | |
181 | pbus = pbus->parent; | |
182 | } while (!handle); | |
783c49fc KA |
183 | |
184 | /* | |
185 | * _HPP settings apply to all child buses, until another _HPP is | |
186 | * encountered. If we don't find an _HPP for the input pci dev, | |
187 | * look for it in the parent device scope since that would apply to | |
188 | * this pci dev. If we don't find any _HPP, use hardcoded defaults | |
189 | */ | |
7430e34c | 190 | while (handle) { |
783c49fc | 191 | status = acpi_run_hpp(handle, hpp); |
7430e34c KK |
192 | if (ACPI_SUCCESS(status)) |
193 | break; | |
194 | if (acpi_root_bridge(handle)) | |
195 | break; | |
196 | status = acpi_get_parent(handle, &phandle); | |
197 | if (ACPI_FAILURE(status)) | |
783c49fc | 198 | break; |
7430e34c | 199 | handle = phandle; |
1da177e4 | 200 | } |
a8a2be94 | 201 | return status; |
1da177e4 | 202 | } |
783c49fc KA |
203 | EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); |
204 | ||
1da177e4 | 205 | |
783c49fc KA |
206 | /* acpi_root_bridge - check to see if this acpi object is a root bridge |
207 | * | |
208 | * @handle - the acpi object in question. | |
209 | */ | |
210 | int acpi_root_bridge(acpi_handle handle) | |
a3a45ec8 RS |
211 | { |
212 | acpi_status status; | |
213 | struct acpi_device_info *info; | |
214 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | |
215 | int i; | |
216 | ||
217 | status = acpi_get_object_info(handle, &buffer); | |
218 | if (ACPI_SUCCESS(status)) { | |
219 | info = buffer.pointer; | |
220 | if ((info->valid & ACPI_VALID_HID) && | |
221 | !strcmp(PCI_ROOT_HID_STRING, | |
222 | info->hardware_id.value)) { | |
81b26bca | 223 | kfree(buffer.pointer); |
a3a45ec8 RS |
224 | return 1; |
225 | } | |
226 | if (info->valid & ACPI_VALID_CID) { | |
227 | for (i=0; i < info->compatibility_id.count; i++) { | |
228 | if (!strcmp(PCI_ROOT_HID_STRING, | |
229 | info->compatibility_id.id[i].value)) { | |
81b26bca | 230 | kfree(buffer.pointer); |
a3a45ec8 RS |
231 | return 1; |
232 | } | |
233 | } | |
234 | } | |
81b26bca | 235 | kfree(buffer.pointer); |
a3a45ec8 RS |
236 | } |
237 | return 0; | |
238 | } | |
783c49fc | 239 | EXPORT_SYMBOL_GPL(acpi_root_bridge); |
aad20cab KK |
240 | |
241 | module_param(debug_acpi, bool, 0644); | |
242 | MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); |