diff options
| author | Tony Luck <tony.luck@intel.com> | 2005-07-13 15:15:43 -0400 |
|---|---|---|
| committer | Tony Luck <tony.luck@intel.com> | 2005-07-13 15:15:43 -0400 |
| commit | 99ad25a313bda566a346b46a6015afa65bc0a02b (patch) | |
| tree | b9443fed1ab74f320c4ee0791864ee96d7c069df /drivers/pci | |
| parent | f62c4a96f74d6c6dd56d1742697e94a5c2085e87 (diff) | |
| parent | 9a556e89081b0c1c2f83cee915363b15a68a6f2d (diff) | |
Auto merge with /home/aegl/GIT/linus
Diffstat (limited to 'drivers/pci')
| -rw-r--r-- | drivers/pci/pci-acpi.c | 110 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 22 | ||||
| -rw-r--r-- | drivers/pci/pci.h | 4 |
3 files changed, 130 insertions, 6 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); | ||
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d382bdb7b560..1b34fc56067e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -235,7 +235,7 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) | |||
| 235 | * -EIO if device does not support PCI PM. | 235 | * -EIO if device does not support PCI PM. |
| 236 | * 0 if we can successfully change the power state. | 236 | * 0 if we can successfully change the power state. |
| 237 | */ | 237 | */ |
| 238 | 238 | int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t); | |
| 239 | int | 239 | int |
| 240 | pci_set_power_state(struct pci_dev *dev, pci_power_t state) | 240 | pci_set_power_state(struct pci_dev *dev, pci_power_t state) |
| 241 | { | 241 | { |
| @@ -299,11 +299,20 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
| 299 | msleep(10); | 299 | msleep(10); |
| 300 | else if (state == PCI_D2 || dev->current_state == PCI_D2) | 300 | else if (state == PCI_D2 || dev->current_state == PCI_D2) |
| 301 | udelay(200); | 301 | udelay(200); |
| 302 | dev->current_state = state; | ||
| 303 | 302 | ||
| 303 | /* | ||
| 304 | * Give firmware a chance to be called, such as ACPI _PRx, _PSx | ||
| 305 | * Firmware method after natice method ? | ||
| 306 | */ | ||
| 307 | if (platform_pci_set_power_state) | ||
| 308 | platform_pci_set_power_state(dev, state); | ||
| 309 | |||
| 310 | dev->current_state = state; | ||
| 304 | return 0; | 311 | return 0; |
| 305 | } | 312 | } |
| 306 | 313 | ||
| 314 | int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state); | ||
| 315 | |||
| 307 | /** | 316 | /** |
| 308 | * pci_choose_state - Choose the power state of a PCI device | 317 | * pci_choose_state - Choose the power state of a PCI device |
| 309 | * @dev: PCI device to be suspended | 318 | * @dev: PCI device to be suspended |
| @@ -316,10 +325,17 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
| 316 | 325 | ||
| 317 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) | 326 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) |
| 318 | { | 327 | { |
| 328 | int ret; | ||
| 329 | |||
| 319 | if (!pci_find_capability(dev, PCI_CAP_ID_PM)) | 330 | if (!pci_find_capability(dev, PCI_CAP_ID_PM)) |
| 320 | return PCI_D0; | 331 | return PCI_D0; |
| 321 | 332 | ||
| 322 | switch (state) { | 333 | if (platform_pci_choose_state) { |
| 334 | ret = platform_pci_choose_state(dev, state); | ||
| 335 | if (ret >= 0) | ||
| 336 | state = ret; | ||
| 337 | } | ||
| 338 | switch (state) { | ||
| 323 | case 0: return PCI_D0; | 339 | case 0: return PCI_D0; |
| 324 | case 3: return PCI_D3hot; | 340 | case 3: return PCI_D3hot; |
| 325 | default: | 341 | default: |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 744da0d4ae5f..d94d7af4f7a0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
| @@ -11,6 +11,10 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
| 11 | void (*alignf)(void *, struct resource *, | 11 | void (*alignf)(void *, struct resource *, |
| 12 | unsigned long, unsigned long), | 12 | unsigned long, unsigned long), |
| 13 | void *alignf_data); | 13 | void *alignf_data); |
| 14 | /* Firmware callbacks */ | ||
| 15 | extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state); | ||
| 16 | extern int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t state); | ||
| 17 | |||
| 14 | /* PCI /proc functions */ | 18 | /* PCI /proc functions */ |
| 15 | #ifdef CONFIG_PROC_FS | 19 | #ifdef CONFIG_PROC_FS |
| 16 | extern int pci_proc_attach_device(struct pci_dev *dev); | 20 | extern int pci_proc_attach_device(struct pci_dev *dev); |
