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