diff options
Diffstat (limited to 'drivers/pci/pci-acpi.c')
-rw-r--r-- | drivers/pci/pci-acpi.c | 110 |
1 files changed, 107 insertions, 3 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index bc01d34e2634..e9e37abe1f76 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
@@ -1,9 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * File: pci-acpi.c | 2 | * File: pci-acpi.c |
3 | * Purpose: Provide PCI supports in ACPI | 3 | * Purpose: Provide PCI support in ACPI |
4 | * | 4 | * |
5 | * Copyright (C) 2004 Intel | 5 | * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com> |
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | 6 | * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com> |
7 | * Copyright (C) 2004 Intel Corp. | ||
7 | */ | 8 | */ |
8 | 9 | ||
9 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
@@ -16,6 +17,7 @@ | |||
16 | #include <acpi/acpi_bus.h> | 17 | #include <acpi/acpi_bus.h> |
17 | 18 | ||
18 | #include <linux/pci-acpi.h> | 19 | #include <linux/pci-acpi.h> |
20 | #include "pci.h" | ||
19 | 21 | ||
20 | static u32 ctrlset_buf[3] = {0, 0, 0}; | 22 | static u32 ctrlset_buf[3] = {0, 0, 0}; |
21 | static u32 global_ctrlsets = 0; | 23 | static u32 global_ctrlsets = 0; |
@@ -207,3 +209,105 @@ acpi_status pci_osc_control_set(u32 flags) | |||
207 | return status; | 209 | return status; |
208 | } | 210 | } |
209 | EXPORT_SYMBOL(pci_osc_control_set); | 211 | EXPORT_SYMBOL(pci_osc_control_set); |
212 | |||
213 | /* | ||
214 | * _SxD returns the D-state with the highest power | ||
215 | * (lowest D-state number) supported in the S-state "x". | ||
216 | * | ||
217 | * If the devices does not have a _PRW | ||
218 | * (Power Resources for Wake) supporting system wakeup from "x" | ||
219 | * then the OS is free to choose a lower power (higher number | ||
220 | * D-state) than the return value from _SxD. | ||
221 | * | ||
222 | * But if _PRW is enabled at S-state "x", the OS | ||
223 | * must not choose a power lower than _SxD -- | ||
224 | * unless the device has an _SxW method specifying | ||
225 | * the lowest power (highest D-state number) the device | ||
226 | * may enter while still able to wake the system. | ||
227 | * | ||
228 | * ie. depending on global OS policy: | ||
229 | * | ||
230 | * if (_PRW at S-state x) | ||
231 | * choose from highest power _SxD to lowest power _SxW | ||
232 | * else // no _PRW at S-state x | ||
233 | * choose highest power _SxD or any lower power | ||
234 | * | ||
235 | * currently we simply return _SxD, if present. | ||
236 | */ | ||
237 | |||
238 | static int acpi_pci_choose_state(struct pci_dev *pdev, pm_message_t state) | ||
239 | { | ||
240 | /* TBD */ | ||
241 | |||
242 | return -ENODEV; | ||
243 | } | ||
244 | |||
245 | static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) | ||
246 | { | ||
247 | acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev); | ||
248 | static int state_conv[] = { | ||
249 | [0] = 0, | ||
250 | [1] = 1, | ||
251 | [2] = 2, | ||
252 | [3] = 3, | ||
253 | [4] = 3 | ||
254 | }; | ||
255 | int acpi_state = state_conv[(int __force) state]; | ||
256 | |||
257 | if (!handle) | ||
258 | return -ENODEV; | ||
259 | return acpi_bus_set_power(handle, acpi_state); | ||
260 | } | ||
261 | |||
262 | |||
263 | /* ACPI bus type */ | ||
264 | static int pci_acpi_find_device(struct device *dev, acpi_handle *handle) | ||
265 | { | ||
266 | struct pci_dev * pci_dev; | ||
267 | acpi_integer addr; | ||
268 | |||
269 | pci_dev = to_pci_dev(dev); | ||
270 | /* Please ref to ACPI spec for the syntax of _ADR */ | ||
271 | addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); | ||
272 | *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr); | ||
273 | if (!*handle) | ||
274 | return -ENODEV; | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static int pci_acpi_find_root_bridge(struct device *dev, acpi_handle *handle) | ||
279 | { | ||
280 | int num; | ||
281 | unsigned int seg, bus; | ||
282 | |||
283 | /* | ||
284 | * The string should be the same as root bridge's name | ||
285 | * Please look at 'pci_scan_bus_parented' | ||
286 | */ | ||
287 | num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus); | ||
288 | if (num != 2) | ||
289 | return -ENODEV; | ||
290 | *handle = acpi_get_pci_rootbridge_handle(seg, bus); | ||
291 | if (!*handle) | ||
292 | return -ENODEV; | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static struct acpi_bus_type pci_acpi_bus = { | ||
297 | .bus = &pci_bus_type, | ||
298 | .find_device = pci_acpi_find_device, | ||
299 | .find_bridge = pci_acpi_find_root_bridge, | ||
300 | }; | ||
301 | |||
302 | static int __init pci_acpi_init(void) | ||
303 | { | ||
304 | int ret; | ||
305 | |||
306 | ret = register_acpi_bus_type(&pci_acpi_bus); | ||
307 | if (ret) | ||
308 | return 0; | ||
309 | platform_pci_choose_state = acpi_pci_choose_state; | ||
310 | platform_pci_set_power_state = acpi_pci_set_power_state; | ||
311 | return 0; | ||
312 | } | ||
313 | arch_initcall(pci_acpi_init); | ||