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> |
9f5404d8 | 33 | #include <linux/acpi.h> |
ac9c052d | 34 | #include <linux/pci-acpi.h> |
5a0e3ad6 | 35 | #include <linux/slab.h> |
1da177e4 | 36 | |
aad20cab KK |
37 | #define MY_NAME "acpi_pcihp" |
38 | ||
66bef8c0 | 39 | #define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0) |
aad20cab KK |
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 | 44 | #define METHOD_NAME__SUN "_SUN" |
1da177e4 LT |
45 | #define METHOD_NAME_OSHP "OSHP" |
46 | ||
90ab5ee9 | 47 | static bool debug_acpi; |
aad20cab | 48 | |
783c49fc KA |
49 | /* acpi_run_oshp - get control of hotplug from the firmware |
50 | * | |
51 | * @handle - the handle of the hotplug controller. | |
52 | */ | |
ac9c052d | 53 | static acpi_status acpi_run_oshp(acpi_handle handle) |
1da177e4 LT |
54 | { |
55 | acpi_status status; | |
b2e6e3ba MT |
56 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
57 | ||
58 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
1da177e4 LT |
59 | |
60 | /* run OSHP */ | |
a8a2be94 | 61 | status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); |
783c49fc | 62 | if (ACPI_FAILURE(status)) |
aad20cab KK |
63 | if (status != AE_NOT_FOUND) |
64 | printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", | |
66bef8c0 | 65 | __func__, (char *)string.pointer, status); |
aad20cab KK |
66 | else |
67 | dbg("%s:%s OSHP not found\n", | |
66bef8c0 | 68 | __func__, (char *)string.pointer); |
783c49fc | 69 | else |
66bef8c0 | 70 | pr_debug("%s:%s OSHP passes\n", __func__, |
b2e6e3ba MT |
71 | (char *)string.pointer); |
72 | ||
81b26bca | 73 | kfree(string.pointer); |
783c49fc KA |
74 | return status; |
75 | } | |
783c49fc | 76 | |
ac9c052d KK |
77 | /** |
78 | * acpi_get_hp_hw_control_from_firmware | |
79 | * @dev: the pci_dev of the bridge that has a hotplug controller | |
80 | * @flags: requested control bits for _OSC | |
81 | * | |
82 | * Attempt to take hotplug control from firmware. | |
83 | */ | |
d391f00f | 84 | int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) |
ac9c052d KK |
85 | { |
86 | acpi_status status; | |
e0d94bee | 87 | acpi_handle chandle, handle; |
ac9c052d KK |
88 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
89 | ||
7dab9ef4 | 90 | flags &= OSC_PCI_SHPC_NATIVE_HP_CONTROL; |
ac9c052d KK |
91 | if (!flags) { |
92 | err("Invalid flags %u specified!\n", flags); | |
93 | return -EINVAL; | |
94 | } | |
95 | ||
96 | /* | |
97 | * Per PCI firmware specification, we should run the ACPI _OSC | |
98 | * method to get control of hotplug hardware before using it. If | |
99 | * an _OSC is missing, we look for an OSHP to do the same thing. | |
e0d94bee JS |
100 | * To handle different BIOS behavior, we look for _OSC on a root |
101 | * bridge preferentially (according to PCI fw spec). Later for | |
102 | * OSHP within the scope of the hotplug controller and its parents, | |
25985edc | 103 | * up to the host bridge under which this controller exists. |
ac9c052d | 104 | */ |
056c58e8 | 105 | handle = acpi_find_root_bridge_handle(pdev); |
e0d94bee JS |
106 | if (handle) { |
107 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
108 | dbg("Trying to get hotplug control for %s\n", | |
109 | (char *)string.pointer); | |
75fb60f2 | 110 | status = acpi_pci_osc_control_set(handle, &flags, flags); |
e0d94bee JS |
111 | if (ACPI_SUCCESS(status)) |
112 | goto got_one; | |
9b536e0b KK |
113 | if (status == AE_SUPPORT) |
114 | goto no_control; | |
e0d94bee JS |
115 | kfree(string.pointer); |
116 | string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL }; | |
117 | } | |
118 | ||
3a83f992 | 119 | handle = ACPI_HANDLE(&pdev->dev); |
d391f00f | 120 | if (!handle) { |
ac9c052d KK |
121 | /* |
122 | * This hotplug controller was not listed in the ACPI name | |
123 | * space at all. Try to get acpi handle of parent pci bus. | |
124 | */ | |
d391f00f KK |
125 | struct pci_bus *pbus; |
126 | for (pbus = pdev->bus; pbus; pbus = pbus->parent) { | |
127 | handle = acpi_pci_get_bridge_handle(pbus); | |
128 | if (handle) | |
129 | break; | |
130 | } | |
ac9c052d KK |
131 | } |
132 | ||
133 | while (handle) { | |
134 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | |
135 | dbg("Trying to get hotplug control for %s \n", | |
136 | (char *)string.pointer); | |
e0d94bee JS |
137 | status = acpi_run_oshp(handle); |
138 | if (ACPI_SUCCESS(status)) | |
139 | goto got_one; | |
27558203 | 140 | if (acpi_is_root_bridge(handle)) |
ac9c052d KK |
141 | break; |
142 | chandle = handle; | |
143 | status = acpi_get_parent(chandle, &handle); | |
144 | if (ACPI_FAILURE(status)) | |
145 | break; | |
146 | } | |
9b536e0b | 147 | no_control: |
ac9c052d | 148 | dbg("Cannot get control of hotplug hardware for pci %s\n", |
d391f00f | 149 | pci_name(pdev)); |
ac9c052d KK |
150 | kfree(string.pointer); |
151 | return -ENODEV; | |
e0d94bee | 152 | got_one: |
d391f00f KK |
153 | dbg("Gained control for hotplug HW for pci %s (%s)\n", |
154 | pci_name(pdev), (char *)string.pointer); | |
e0d94bee JS |
155 | kfree(string.pointer); |
156 | return 0; | |
ac9c052d KK |
157 | } |
158 | EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); | |
1da177e4 | 159 | |
efe6d727 | 160 | static int pcihp_is_ejectable(acpi_handle handle) |
e8c331e9 KK |
161 | { |
162 | acpi_status status; | |
e8c331e9 | 163 | unsigned long long removable; |
2fd6f9c3 | 164 | if (!acpi_has_method(handle, "_ADR")) |
e8c331e9 | 165 | return 0; |
2fd6f9c3 | 166 | if (acpi_has_method(handle, "_EJ0")) |
e8c331e9 KK |
167 | return 1; |
168 | status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); | |
169 | if (ACPI_SUCCESS(status) && removable) | |
170 | return 1; | |
171 | return 0; | |
172 | } | |
173 | ||
174 | /** | |
175 | * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot | |
176 | * @pbus: the PCI bus of the PCI slot corresponding to 'handle' | |
177 | * @handle: ACPI handle to check | |
178 | * | |
179 | * Return 1 if handle is ejectable PCI slot, 0 otherwise. | |
180 | */ | |
181 | int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) | |
182 | { | |
183 | acpi_handle bridge_handle, parent_handle; | |
184 | ||
79e50e72 QL |
185 | bridge_handle = acpi_pci_get_bridge_handle(pbus); |
186 | if (!bridge_handle) | |
e8c331e9 KK |
187 | return 0; |
188 | if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle)))) | |
189 | return 0; | |
190 | if (bridge_handle != parent_handle) | |
191 | return 0; | |
efe6d727 | 192 | return pcihp_is_ejectable(handle); |
e8c331e9 KK |
193 | } |
194 | EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable); | |
195 | ||
196 | static acpi_status | |
197 | check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) | |
198 | { | |
199 | int *found = (int *)context; | |
efe6d727 | 200 | if (pcihp_is_ejectable(handle)) { |
e8c331e9 KK |
201 | *found = 1; |
202 | return AE_CTRL_TERMINATE; | |
203 | } | |
204 | return AE_OK; | |
205 | } | |
206 | ||
207 | /** | |
208 | * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots | |
7f538669 | 209 | * @handle - handle of the PCI bus to scan |
e8c331e9 KK |
210 | * |
211 | * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. | |
212 | */ | |
7f538669 | 213 | int acpi_pci_detect_ejectable(acpi_handle handle) |
e8c331e9 | 214 | { |
e8c331e9 KK |
215 | int found = 0; |
216 | ||
7f538669 AC |
217 | if (!handle) |
218 | return found; | |
219 | ||
220 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, | |
2263576c | 221 | check_hotplug, NULL, (void *)&found, NULL); |
e8c331e9 KK |
222 | return found; |
223 | } | |
224 | EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); | |
225 | ||
aad20cab KK |
226 | module_param(debug_acpi, bool, 0644); |
227 | MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); |