diff options
author | rajesh.shah@intel.com <rajesh.shah@intel.com> | 2005-10-31 19:20:12 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-11-10 19:09:15 -0500 |
commit | a3a45ec8f8edaf088449e37fe81c99cbf580b9bd (patch) | |
tree | a6aaadb26ee068609b9520755e58a0fcdff588fd /drivers/pci/hotplug/pciehprm_acpi.c | |
parent | 427bf532b5ad6db5addc2bce675d13f874397c0c (diff) |
[PATCH] pciehp: clean-up how we request control of hotplug hardware
This patch further tweaks how we request control of hotplug
controller hardware from BIOS. We first search the ACPI namespace
corresponding to a specific hotplug controller looking for an
_OSC or OSHP method. On failure, we successively move to the
ACPI parent object, till we hit the highest level host bridge
in the hierarchy. This allows for different types of BIOS's
which place the _OSC/OSHP methods at various places in the acpi
namespace, while still not encroaching on the namespace of
some other root level host bridge.
This patch also introduces a new load time option (pciehp_force)
that allows us to bypass all _OSC/OSHP checking. Not supporting
these methods seems to be be the most common ACPI firmware problem
we've run into. This will still _not_ allow the pciehp driver to
work correctly if the BIOS really doesn't support pciehp (i.e. if
it doesn't generate a hotplug interrupt). Use this option with
caution. Some BIOS's may deliberately not build any _OSC/OSHP
methods to make sure it retains control the hotplug hardware.
Using the pciehp_force parameter for such systems can lead to
two separate entities trying to control the same hardware.
Signed-off-by: Rajesh Shah <rajesh.shah@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/hotplug/pciehprm_acpi.c')
-rw-r--r-- | drivers/pci/hotplug/pciehprm_acpi.c | 92 |
1 files changed, 76 insertions, 16 deletions
diff --git a/drivers/pci/hotplug/pciehprm_acpi.c b/drivers/pci/hotplug/pciehprm_acpi.c index 5acdae3d52b1..4d013f86b1a5 100644 --- a/drivers/pci/hotplug/pciehprm_acpi.c +++ b/drivers/pci/hotplug/pciehprm_acpi.c | |||
@@ -132,7 +132,7 @@ static acpi_status acpi_run_oshp(acpi_handle handle) | |||
132 | /* run OSHP */ | 132 | /* run OSHP */ |
133 | status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); | 133 | status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); |
134 | if (ACPI_FAILURE(status)) { | 134 | if (ACPI_FAILURE(status)) { |
135 | err("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name, | 135 | dbg("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name, |
136 | status); | 136 | status); |
137 | } else { | 137 | } else { |
138 | dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name); | 138 | dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name); |
@@ -140,32 +140,92 @@ static acpi_status acpi_run_oshp(acpi_handle handle) | |||
140 | return status; | 140 | return status; |
141 | } | 141 | } |
142 | 142 | ||
143 | static int is_root_bridge(acpi_handle handle) | ||
144 | { | ||
145 | acpi_status status; | ||
146 | struct acpi_device_info *info; | ||
147 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
148 | int i; | ||
149 | |||
150 | status = acpi_get_object_info(handle, &buffer); | ||
151 | if (ACPI_SUCCESS(status)) { | ||
152 | info = buffer.pointer; | ||
153 | if ((info->valid & ACPI_VALID_HID) && | ||
154 | !strcmp(PCI_ROOT_HID_STRING, | ||
155 | info->hardware_id.value)) { | ||
156 | acpi_os_free(buffer.pointer); | ||
157 | return 1; | ||
158 | } | ||
159 | if (info->valid & ACPI_VALID_CID) { | ||
160 | for (i=0; i < info->compatibility_id.count; i++) { | ||
161 | if (!strcmp(PCI_ROOT_HID_STRING, | ||
162 | info->compatibility_id.id[i].value)) { | ||
163 | acpi_os_free(buffer.pointer); | ||
164 | return 1; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | return 0; | ||
170 | } | ||
171 | |||
143 | int get_hp_hw_control_from_firmware(struct pci_dev *dev) | 172 | int get_hp_hw_control_from_firmware(struct pci_dev *dev) |
144 | { | 173 | { |
145 | acpi_status status; | 174 | acpi_status status; |
146 | acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev)); | 175 | acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); |
176 | struct pci_dev *pdev = dev; | ||
177 | u8 *path_name; | ||
147 | /* | 178 | /* |
148 | * Per PCI firmware specification, we should run the ACPI _OSC | 179 | * Per PCI firmware specification, we should run the ACPI _OSC |
149 | * method to get control of hotplug hardware before using it | 180 | * method to get control of hotplug hardware before using it. |
181 | * If an _OSC is missing, we look for an OSHP to do the same thing. | ||
182 | * To handle different BIOS behavior, we look for _OSC and OSHP | ||
183 | * within the scope of the hotplug controller and its parents, upto | ||
184 | * the host bridge under which this controller exists. | ||
150 | */ | 185 | */ |
151 | status = pci_osc_control_set(handle, | 186 | while (!handle) { |
152 | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); | ||
153 | |||
154 | /* Fixme: fail native hotplug if _OSC does not exist for root ports */ | ||
155 | if (status == AE_NOT_FOUND) { | ||
156 | /* | 187 | /* |
157 | * Some older BIOS's don't support _OSC but support | 188 | * This hotplug controller was not listed in the ACPI name |
158 | * OSHP to do the same thing | 189 | * space at all. Try to get acpi handle of parent pci bus. |
159 | */ | 190 | */ |
160 | status = acpi_run_oshp(handle); | 191 | if (!pdev || !pdev->bus->parent) |
192 | break; | ||
193 | dbg("Could not find %s in acpi namespace, trying parent\n", | ||
194 | pci_name(pdev)); | ||
195 | if (!pdev->bus->parent->self) | ||
196 | /* Parent must be a host bridge */ | ||
197 | handle = acpi_get_pci_rootbridge_handle( | ||
198 | pci_domain_nr(pdev->bus->parent), | ||
199 | pdev->bus->parent->number); | ||
200 | else | ||
201 | handle = DEVICE_ACPI_HANDLE( | ||
202 | &(pdev->bus->parent->self->dev)); | ||
203 | pdev = pdev->bus->parent->self; | ||
161 | } | 204 | } |
162 | if (ACPI_FAILURE(status)) { | 205 | |
163 | err("Cannot get control of hotplug hardware\n"); | 206 | while (handle) { |
164 | return -1; | 207 | path_name = acpi_path_name(handle); |
208 | dbg("Trying to get hotplug control for %s \n", path_name); | ||
209 | status = pci_osc_control_set(handle, | ||
210 | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); | ||
211 | if (status == AE_NOT_FOUND) | ||
212 | status = acpi_run_oshp(handle); | ||
213 | if (ACPI_SUCCESS(status)) { | ||
214 | dbg("Gained control for hotplug HW for pci %s (%s)\n", | ||
215 | pci_name(dev), path_name); | ||
216 | return 0; | ||
217 | } | ||
218 | if (is_root_bridge(handle)) | ||
219 | break; | ||
220 | chandle = handle; | ||
221 | status = acpi_get_parent(chandle, &handle); | ||
222 | if (ACPI_FAILURE(status)) | ||
223 | break; | ||
165 | } | 224 | } |
166 | 225 | ||
167 | dbg("Sucess getting control of hotplug hardware\n"); | 226 | err("Cannot get control of hotplug hardware for pci %s\n", |
168 | return 0; | 227 | pci_name(dev)); |
228 | return -1; | ||
169 | } | 229 | } |
170 | 230 | ||
171 | void get_hp_params_from_firmware(struct pci_dev *dev, | 231 | void get_hp_params_from_firmware(struct pci_dev *dev, |