| Commit | Line | Data |
|---|---|---|
| 10f97674 AP |
1 | /*- |
| 2 | * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org> | |
| 3 | * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org> | |
| 5ed44076 MD |
4 | * Copyright (c) 2000, BSDi |
| 5 | * All rights reserved. | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * 1. Redistributions of source code must retain the above copyright | |
| 11 | * notice unmodified, this list of conditions, and the following | |
| 12 | * disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in the | |
| 15 | * documentation and/or other materials provided with the distribution. | |
| 16 | * | |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 32af04f7 SW |
27 | * |
| 28 | * $FreeBSD: src/sys/dev/acpica/acpi_pci.c,v 1.31.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $ | |
| 5ed44076 MD |
29 | */ |
| 30 | ||
| 5ed44076 MD |
31 | #include <sys/param.h> |
| 32 | #include <sys/systm.h> | |
| 33 | #include <sys/bus.h> | |
| 34 | #include <sys/kernel.h> | |
| 35 | #include <sys/malloc.h> | |
| 36 | #include <sys/module.h> | |
| 37 | ||
| 38 | #include "acpi.h" | |
| 5ed44076 MD |
39 | #include "acpivar.h" |
| 40 | ||
| 41 | #include <sys/pciio.h> | |
| 42 | #include <bus/pci/pcireg.h> | |
| 43 | #include <bus/pci/pcivar.h> | |
| 44 | #include <bus/pci/pci_private.h> | |
| 45 | ||
| 46 | #include "pcib_if.h" | |
| 47 | #include "pci_if.h" | |
| 48 | ||
| 49e48b8a | 49 | /* Hooks for the ACPI CA debugging infrastructure. */ |
| 5ed44076 MD |
50 | #define _COMPONENT ACPI_BUS |
| 51 | ACPI_MODULE_NAME("PCI") | |
| 52 | ||
| 53 | struct acpi_pci_devinfo { | |
| 49e48b8a MD |
54 | struct pci_devinfo ap_dinfo; |
| 55 | ACPI_HANDLE ap_handle; | |
| 10f97674 | 56 | int ap_flags; |
| 5ed44076 MD |
57 | }; |
| 58 | ||
| 10f97674 AP |
59 | ACPI_SERIAL_DECL(pci_powerstate, "ACPI PCI power methods"); |
| 60 | ||
| 61 | /* Be sure that ACPI and PCI power states are equivalent. */ | |
| 62 | CTASSERT(ACPI_STATE_D0 == PCI_POWERSTATE_D0); | |
| 63 | CTASSERT(ACPI_STATE_D1 == PCI_POWERSTATE_D1); | |
| 64 | CTASSERT(ACPI_STATE_D2 == PCI_POWERSTATE_D2); | |
| 65 | CTASSERT(ACPI_STATE_D3 == PCI_POWERSTATE_D3); | |
| 66 | ||
| 5ed44076 | 67 | static int acpi_pci_attach(device_t dev); |
| 10f97674 AP |
68 | static int acpi_pci_suspend(device_t dev); |
| 69 | static int acpi_pci_resume(device_t dev); | |
| f9d8cd12 | 70 | static int acpi_pci_child_location_str_method(device_t cbdev, |
| 49e48b8a | 71 | device_t child, char *buf, size_t buflen); |
| 10f97674 AP |
72 | static int acpi_pci_probe(device_t dev); |
| 73 | static int acpi_pci_read_ivar(device_t dev, device_t child, int which, | |
| 74 | uintptr_t *result); | |
| 75 | static int acpi_pci_write_ivar(device_t dev, device_t child, int which, | |
| 76 | uintptr_t value); | |
| 5ed44076 | 77 | static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, |
| 49e48b8a | 78 | void *context, void **status); |
| 10f97674 AP |
79 | static int acpi_pci_set_powerstate_method(device_t dev, device_t child, |
| 80 | int state); | |
| 81 | static void acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child); | |
| 5ed44076 MD |
82 | |
| 83 | static device_method_t acpi_pci_methods[] = { | |
| 84 | /* Device interface */ | |
| 85 | DEVMETHOD(device_probe, acpi_pci_probe), | |
| 86 | DEVMETHOD(device_attach, acpi_pci_attach), | |
| 6388f614 SZ |
87 | DEVMETHOD(device_shutdown, bus_generic_shutdown), |
| 88 | DEVMETHOD(device_suspend, acpi_pci_suspend), | |
| 89 | DEVMETHOD(device_resume, acpi_pci_resume), | |
| 5ed44076 MD |
90 | |
| 91 | /* Bus interface */ | |
| 6388f614 SZ |
92 | DEVMETHOD(bus_print_child, pci_print_child), |
| 93 | DEVMETHOD(bus_get_resource_list,pci_get_resource_list), | |
| 94 | DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), | |
| 95 | DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), | |
| 96 | DEVMETHOD(bus_delete_resource, pci_delete_resource), | |
| 97 | DEVMETHOD(bus_alloc_resource, pci_alloc_resource), | |
| 98 | DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), | |
| 99 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 100 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 5ed44076 | 101 | DEVMETHOD(bus_read_ivar, acpi_pci_read_ivar), |
| 10f97674 | 102 | DEVMETHOD(bus_write_ivar, acpi_pci_write_ivar), |
| 6388f614 SZ |
103 | DEVMETHOD(bus_driver_added, bus_generic_driver_added), |
| 104 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 105 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| f9d8cd12 | 106 | DEVMETHOD(bus_child_location_str, acpi_pci_child_location_str_method), |
| 5ed44076 MD |
107 | |
| 108 | /* PCI interface */ | |
| 6388f614 SZ |
109 | DEVMETHOD(pci_read_config, pci_read_config_method), |
| 110 | DEVMETHOD(pci_write_config, pci_write_config_method), | |
| 111 | DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method), | |
| 112 | DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), | |
| 113 | DEVMETHOD(pci_enable_io, pci_enable_io_method), | |
| 114 | DEVMETHOD(pci_disable_io, pci_disable_io_method), | |
| 115 | DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method), | |
| f9d8cd12 | 116 | DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method), |
| 6388f614 | 117 | DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), |
| 5ed44076 MD |
118 | |
| 119 | { 0, 0 } | |
| 120 | }; | |
| 121 | ||
| 122 | static driver_t acpi_pci_driver = { | |
| 6388f614 SZ |
123 | "pci", |
| 124 | acpi_pci_methods, | |
| 125 | 0, /* no softc */ | |
| 5ed44076 MD |
126 | }; |
| 127 | ||
| e20af2f4 AP |
128 | static devclass_t pci_devclass; |
| 129 | ||
| 5ed44076 | 130 | DRIVER_MODULE(acpi_pci, pcib, acpi_pci_driver, pci_devclass, 0, 0); |
| f9d8cd12 | 131 | MODULE_DEPEND(acpi_pci, acpi, 1, 1, 1); |
| 10f97674 | 132 | MODULE_DEPEND(acpi_pci, pci, 1, 1, 1); |
| 5ed44076 | 133 | MODULE_VERSION(acpi_pci, 1); |
| 5ed44076 MD |
134 | |
| 135 | static int | |
| 136 | acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) | |
| 137 | { | |
| 138 | struct acpi_pci_devinfo *dinfo; | |
| 139 | ||
| 10f97674 | 140 | dinfo = device_get_ivars(child); |
| 5ed44076 | 141 | switch (which) { |
| f9d8cd12 | 142 | case ACPI_IVAR_HANDLE: |
| 5ed44076 | 143 | *result = (uintptr_t)dinfo->ap_handle; |
| f9d8cd12 | 144 | return (0); |
| 10f97674 AP |
145 | case ACPI_IVAR_FLAGS: |
| 146 | *result = (uintptr_t)dinfo->ap_flags; | |
| 147 | return (0); | |
| 5ed44076 | 148 | } |
| f9d8cd12 MD |
149 | return (pci_read_ivar(dev, child, which, result)); |
| 150 | } | |
| 151 | ||
| 152 | static int | |
| 10f97674 AP |
153 | acpi_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) |
| 154 | { | |
| 155 | struct acpi_pci_devinfo *dinfo; | |
| 156 | ||
| 157 | dinfo = device_get_ivars(child); | |
| 158 | switch (which) { | |
| 159 | case ACPI_IVAR_HANDLE: | |
| 160 | dinfo->ap_handle = (ACPI_HANDLE)value; | |
| 161 | return (0); | |
| 162 | case ACPI_IVAR_FLAGS: | |
| 163 | dinfo->ap_flags = (int)value; | |
| 164 | return (0); | |
| 165 | } | |
| 166 | return (pci_write_ivar(dev, child, which, value)); | |
| 167 | } | |
| 168 | ||
| 169 | static int | |
| f9d8cd12 MD |
170 | acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf, |
| 171 | size_t buflen) | |
| 172 | { | |
| 173 | struct acpi_pci_devinfo *dinfo = device_get_ivars(child); | |
| 174 | ||
| 175 | pci_child_location_str_method(cbdev, child, buf, buflen); | |
| 176 | ||
| 177 | if (dinfo->ap_handle) { | |
| 10f97674 | 178 | strlcat(buf, " handle=", buflen); |
| f9d8cd12 MD |
179 | strlcat(buf, acpi_name(dinfo->ap_handle), buflen); |
| 180 | } | |
| 181 | return (0); | |
| 5ed44076 MD |
182 | } |
| 183 | ||
| 5ed44076 MD |
184 | /* |
| 185 | * PCI power manangement | |
| 186 | */ | |
| 187 | static int | |
| 188 | acpi_pci_set_powerstate_method(device_t dev, device_t child, int state) | |
| 189 | { | |
| f9d8cd12 MD |
190 | ACPI_HANDLE h; |
| 191 | ACPI_STATUS status; | |
| 10f97674 AP |
192 | int old_state, error; |
| 193 | ||
| 194 | error = 0; | |
| 195 | if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) | |
| f9d8cd12 | 196 | return (EINVAL); |
| f9d8cd12 MD |
197 | |
| 198 | /* | |
| 199 | * We set the state using PCI Power Management outside of setting | |
| 200 | * the ACPI state. This means that when powering down a device, we | |
| 201 | * first shut it down using PCI, and then using ACPI, which lets ACPI | |
| 202 | * try to power down any Power Resources that are now no longer used. | |
| 203 | * When powering up a device, we let ACPI set the state first so that | |
| 204 | * it can enable any needed Power Resources before changing the PCI | |
| 205 | * power state. | |
| 206 | */ | |
| 10f97674 | 207 | ACPI_SERIAL_BEGIN(pci_powerstate); |
| f9d8cd12 MD |
208 | old_state = pci_get_powerstate(child); |
| 209 | if (old_state < state) { | |
| 210 | error = pci_set_powerstate_method(dev, child, state); | |
| 211 | if (error) | |
| 10f97674 | 212 | goto out; |
| f9d8cd12 MD |
213 | } |
| 214 | h = acpi_get_handle(child); | |
| 10f97674 AP |
215 | status = acpi_pwr_switch_consumer(h, state); |
| 216 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) | |
| 217 | device_printf(dev, | |
| 218 | "Failed to set ACPI power state D%d on %s: %s\n", | |
| 219 | state, acpi_name(h), AcpiFormatException(status)); | |
| f9d8cd12 | 220 | if (old_state > state) |
| 10f97674 AP |
221 | error = pci_set_powerstate_method(dev, child, state); |
| 222 | ||
| 223 | out: | |
| 224 | ACPI_SERIAL_END(pci_powerstate); | |
| 225 | return (error); | |
| 226 | } | |
| 227 | ||
| 228 | static void | |
| 229 | acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child) | |
| 230 | { | |
| 231 | ACPI_STATUS status; | |
| 232 | device_t child; | |
| 233 | ||
| 234 | /* | |
| 235 | * Lookup and remove the unused device that acpi0 creates when it walks | |
| 236 | * the namespace creating devices. | |
| 237 | */ | |
| 238 | child = acpi_get_device(handle); | |
| 239 | if (child != NULL) { | |
| 240 | if (device_is_alive(child)) { | |
| 241 | /* | |
| 242 | * The TabletPC TC1000 has a second PCI-ISA bridge | |
| 243 | * that has a _HID for an acpi_sysresource device. | |
| 244 | * In that case, leave ACPI-CA's device data pointing | |
| 245 | * at the ACPI-enumerated device. | |
| 246 | */ | |
| 247 | device_printf(child, | |
| 248 | "Conflicts with PCI device %d:%d:%d\n", | |
| 249 | pci_get_bus(pci_child), pci_get_slot(pci_child), | |
| 250 | pci_get_function(pci_child)); | |
| 251 | return; | |
| 252 | } | |
| 253 | KASSERT(device_get_parent(child) == | |
| 254 | devclass_get_device(devclass_find("acpi"), 0), | |
| 255 | ("%s: child (%s)'s parent is not acpi0", __func__, | |
| 256 | acpi_name(handle))); | |
| 257 | device_delete_child(device_get_parent(child), child); | |
| 258 | } | |
| 259 | ||
| 260 | /* | |
| 261 | * Update ACPI-CA to use the PCI enumerated device_t for this handle. | |
| 262 | */ | |
| 263 | status = AcpiDetachData(handle, acpi_fake_objhandler); | |
| 264 | if (ACPI_FAILURE(status)) | |
| 265 | kprintf("WARNING: Unable to detach object data from %s - %s\n", | |
| 266 | acpi_name(handle), AcpiFormatException(status)); | |
| 267 | status = AcpiAttachData(handle, acpi_fake_objhandler, pci_child); | |
| 268 | if (ACPI_FAILURE(status)) | |
| 269 | kprintf("WARNING: Unable to attach object data to %s - %s\n", | |
| 270 | acpi_name(handle), AcpiFormatException(status)); | |
| 5ed44076 | 271 | } |
| 5ed44076 MD |
272 | |
| 273 | static ACPI_STATUS | |
| 274 | acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, | |
| 275 | void **status) | |
| 276 | { | |
| 277 | struct acpi_pci_devinfo *dinfo; | |
| 278 | device_t *devlist; | |
| 279 | int devcount, i, func, slot; | |
| 280 | UINT32 address; | |
| 281 | ||
| 282 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
| 283 | ||
| f9d8cd12 | 284 | if (ACPI_FAILURE(acpi_GetInteger(handle, "_ADR", &address))) |
| 49e48b8a | 285 | return_ACPI_STATUS (AE_OK); |
| 10f97674 AP |
286 | slot = ACPI_ADR_PCI_SLOT(address); |
| 287 | func = ACPI_ADR_PCI_FUNC(address); | |
| 5ed44076 | 288 | if (device_get_children((device_t)context, &devlist, &devcount) != 0) |
| 49e48b8a | 289 | return_ACPI_STATUS (AE_OK); |
| 5ed44076 MD |
290 | for (i = 0; i < devcount; i++) { |
| 291 | dinfo = device_get_ivars(devlist[i]); | |
| 292 | if (dinfo->ap_dinfo.cfg.func == func && | |
| 293 | dinfo->ap_dinfo.cfg.slot == slot) { | |
| 294 | dinfo->ap_handle = handle; | |
| 10f97674 AP |
295 | acpi_pci_update_device(handle, devlist[i]); |
| 296 | break; | |
| 5ed44076 MD |
297 | } |
| 298 | } | |
| efda3bd0 | 299 | kfree(devlist, M_TEMP); |
| 49e48b8a | 300 | return_ACPI_STATUS (AE_OK); |
| 5ed44076 MD |
301 | } |
| 302 | ||
| 303 | static int | |
| 304 | acpi_pci_probe(device_t dev) | |
| 305 | { | |
| 10f97674 AP |
306 | |
| 307 | if (pcib_get_bus(dev) < 0) | |
| 5ed44076 | 308 | return (ENXIO); |
| 5ed44076 MD |
309 | if (acpi_get_handle(dev) == NULL) |
| 310 | return (ENXIO); | |
| 49e48b8a | 311 | device_set_desc(dev, "ACPI PCI bus"); |
| 5ed44076 MD |
312 | return (0); |
| 313 | } | |
| 314 | ||
| 315 | static int | |
| 316 | acpi_pci_attach(device_t dev) | |
| 317 | { | |
| e20af2f4 | 318 | int busno, domain; |
| 5ed44076 MD |
319 | |
| 320 | /* | |
| 321 | * Since there can be multiple independantly numbered PCI | |
| 10f97674 AP |
322 | * busses on systems with multiple PCI domains, we can't use |
| 323 | * the unit number to decide which bus we are probing. We ask | |
| 324 | * the parent pcib what our domain and bus numbers are. | |
| 5ed44076 | 325 | */ |
| 10f97674 AP |
326 | busno = pcib_get_bus(dev); |
| 327 | domain = pcib_get_domain(dev); | |
| 6388f614 SZ |
328 | if (bootverbose) { |
| 329 | device_printf(dev, "domain=%d, physical bus=%d\n", | |
| 330 | domain, busno); | |
| 331 | } | |
| e20af2f4 | 332 | |
| 5c7ffd75 AP |
333 | ACPI_SERIAL_INIT(pci_powerstate); |
| 334 | ||
| 5ed44076 MD |
335 | /* |
| 336 | * First, PCI devices are added as in the normal PCI bus driver. | |
| 337 | * Afterwards, the ACPI namespace under the bridge driver is | |
| 338 | * walked to save ACPI handles to all the devices that appear in | |
| 339 | * the ACPI namespace as immediate descendants of the bridge. | |
| 340 | * | |
| 341 | * XXX: Sometimes PCI devices show up in the ACPI namespace that | |
| 342 | * pci_add_children() doesn't find. We currently just ignore | |
| 343 | * these devices. | |
| 344 | */ | |
| e20af2f4 | 345 | pci_add_children(dev, domain, busno, sizeof(struct acpi_pci_devinfo)); |
| 49e48b8a | 346 | AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 1, |
| 4efc8147 | 347 | NULL, acpi_pci_save_handle, dev, NULL); |
| 5ed44076 MD |
348 | |
| 349 | return (bus_generic_attach(dev)); | |
| 350 | } | |
| 10f97674 AP |
351 | |
| 352 | int | |
| 353 | acpi_pci_suspend(device_t dev) | |
| 354 | { | |
| 6388f614 SZ |
355 | int dstate, error, i, numdevs; |
| 356 | device_t acpi_dev, child, *devlist; | |
| 357 | struct pci_devinfo *dinfo; | |
| 358 | ||
| 10f97674 | 359 | acpi_dev = devclass_get_device(devclass_find("acpi"), 0); |
| 6388f614 SZ |
360 | device_get_children(dev, &devlist, &numdevs); |
| 361 | ||
| 362 | /* | |
| 363 | * Save the PCI configuration space for each child and set the | |
| 364 | * device in the appropriate power state for this sleep state. | |
| 365 | */ | |
| 366 | for (i = 0; i < numdevs; i++) { | |
| 367 | child = devlist[i]; | |
| 368 | dinfo = (struct pci_devinfo *)device_get_ivars(child); | |
| 369 | pci_cfg_save(child, dinfo, 0); | |
| 370 | } | |
| 371 | ||
| 372 | /* | |
| 373 | * Suspend devices before potentially powering them down. | |
| 374 | */ | |
| 375 | error = bus_generic_suspend(dev); | |
| 376 | if (error) { | |
| 377 | kfree(devlist, M_TEMP); | |
| 378 | return (error); | |
| 379 | } | |
| 380 | ||
| 381 | /* | |
| 382 | * Always set the device to D3. If ACPI suggests a different | |
| 383 | * power state, use it instead. If ACPI is not present, the | |
| 384 | * firmware is responsible for managing device power. Skip | |
| 385 | * children who aren't attached since they are powered down | |
| 386 | * separately. Only manage type 0 devices for now. | |
| 387 | */ | |
| 388 | for (i = 0; acpi_dev && i < numdevs; i++) { | |
| 389 | child = devlist[i]; | |
| 390 | dinfo = (struct pci_devinfo *)device_get_ivars(child); | |
| 391 | if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) { | |
| 392 | dstate = PCI_POWERSTATE_D3; | |
| 393 | ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate); | |
| 394 | pci_set_powerstate(child, dstate); | |
| 395 | } | |
| 396 | } | |
| 397 | ||
| 398 | kfree(devlist, M_TEMP); | |
| 399 | return (0); | |
| 10f97674 AP |
400 | } |
| 401 | ||
| 402 | int | |
| 403 | acpi_pci_resume(device_t dev) | |
| 404 | { | |
| 6388f614 SZ |
405 | int i, numdevs; |
| 406 | device_t acpi_dev, child, *devlist; | |
| 407 | struct pci_devinfo *dinfo; | |
| 408 | ||
| 10f97674 | 409 | acpi_dev = devclass_get_device(devclass_find("acpi"), 0); |
| 6388f614 SZ |
410 | device_get_children(dev, &devlist, &numdevs); |
| 411 | ||
| 412 | /* | |
| 413 | * Set each child to D0 and restore its PCI configuration space. | |
| 414 | */ | |
| 415 | for (i = 0; i < numdevs; i++) { | |
| 416 | /* | |
| 417 | * Notify ACPI we're going to D0 but ignore the result. If | |
| 418 | * ACPI is not present, the firmware is responsible for | |
| 419 | * managing device power. Only manage type 0 devices for now. | |
| 420 | */ | |
| 421 | child = devlist[i]; | |
| 422 | dinfo = (struct pci_devinfo *) device_get_ivars(child); | |
| 423 | if (acpi_dev && device_is_attached(child) && | |
| 424 | dinfo->cfg.hdrtype == 0) { | |
| 10f97674 | 425 | ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL); |
| 6388f614 SZ |
426 | pci_set_powerstate(child, PCI_POWERSTATE_D0); |
| 427 | } | |
| 428 | ||
| 429 | /* | |
| 430 | * Now the device is powered up, restore its config space. | |
| 431 | */ | |
| 432 | pci_cfg_restore(child, dinfo); | |
| 433 | } | |
| 434 | ||
| 435 | kfree(devlist, M_TEMP); | |
| 436 | return (bus_generic_resume(dev)); | |
| 10f97674 | 437 | } |