diff --git a/src/libs/compat/freebsd_network/Jamfile b/src/libs/compat/freebsd_network/Jamfile index 432c3d3d01..84fa65053b 100644 --- a/src/libs/compat/freebsd_network/Jamfile +++ b/src/libs/compat/freebsd_network/Jamfile @@ -41,6 +41,7 @@ KernelStaticLibrary libfreebsd_network.a : mbuf.c mii.c mutex.c + pci.cpp priv.cpp smp.c subr_autoconf.cpp diff --git a/src/libs/compat/freebsd_network/bus.cpp b/src/libs/compat/freebsd_network/bus.cpp index 90e85e59c8..dc7fe5f517 100644 --- a/src/libs/compat/freebsd_network/bus.cpp +++ b/src/libs/compat/freebsd_network/bus.cpp @@ -9,11 +9,11 @@ extern "C" { #include "device.h" } -#include - -#include +#include +#include #include +#include extern "C" { #include @@ -25,11 +25,6 @@ extern "C" { #include } -// private kernel header to get B_NO_HANDLED_INFO -#include - -#include - //#define DEBUG_BUS_SPACE_RW #ifdef DEBUG_BUS_SPACE_RW @@ -38,13 +33,6 @@ extern "C" { # define TRACE_BUS_SPACE_RW(x) #endif -//#define DEBUG_PCI -#ifdef DEBUG_PCI -# define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args) -#else -# define TRACE_PCI(dev, format, args...) do { } while (0) -#endif - struct internal_intr { device_t dev; @@ -580,450 +568,3 @@ bus_enumerate_hinted_children(device_t bus) UNIMPLEMENTED(); #endif } - - - -// #pragma mark - PCI functions - - -uint32_t -pci_read_config(device_t dev, int offset, int size) -{ - pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; - - uint32_t value = gPci->read_pci_config(info->bus, info->device, - info->function, offset, size); - TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value); - return value; -} - - -void -pci_write_config(device_t dev, int offset, uint32_t value, int size) -{ - pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; - - TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size); - - gPci->write_pci_config(info->bus, info->device, info->function, offset, - size, value); -} - - -uint16_t -pci_get_vendor(device_t dev) -{ - return pci_read_config(dev, PCI_vendor_id, 2); -} - - -uint16_t -pci_get_device(device_t dev) -{ - return pci_read_config(dev, PCI_device_id, 2); -} - - -uint16_t -pci_get_subvendor(device_t dev) -{ - return pci_read_config(dev, PCI_subsystem_vendor_id, 2); -} - - -uint16_t -pci_get_subdevice(device_t dev) -{ - return pci_read_config(dev, PCI_subsystem_id, 2); -} - - -uint8_t -pci_get_revid(device_t dev) -{ - return pci_read_config(dev, PCI_revision, 1); -} - - -uint32_t -pci_get_domain(device_t dev) -{ - return 0; -} - -uint32_t -pci_get_devid(device_t dev) -{ - return pci_read_config(dev, PCI_device_id, 2) << 16 | - pci_read_config(dev, PCI_vendor_id, 2); -} - -uint8_t -pci_get_cachelnsz(device_t dev) -{ - return pci_read_config(dev, PCI_line_size, 1); -} - -uint8_t * -pci_get_ether(device_t dev) -{ - /* used in if_dc to get the MAC from CardBus CIS for Xircom card */ - return NULL; /* NULL is handled in the caller correctly */ -} - -uint8_t -pci_get_bus(device_t dev) -{ - pci_info *info - = &((struct root_device_softc *)dev->root->softc)->pci_info; - return info->bus; -} - - -uint8_t -pci_get_slot(device_t dev) -{ - pci_info *info - = &((struct root_device_softc *)dev->root->softc)->pci_info; - return info->device; -} - - -uint8_t -pci_get_function(device_t dev) -{ - pci_info *info - = &((struct root_device_softc *)dev->root->softc)->pci_info; - return info->function; -} - - -device_t -pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func) -{ - // We don't support that yet - if we want to support the multi port - // feature of the Broadcom BCM 570x driver, we would have to change - // that. - return NULL; -} - - -static void -pci_set_command_bit(device_t dev, uint16_t bit) -{ - uint16_t command = pci_read_config(dev, PCI_command, 2); - pci_write_config(dev, PCI_command, command | bit, 2); -} - - -int -pci_enable_busmaster(device_t dev) -{ - pci_set_command_bit(dev, PCI_command_master); - return 0; -} - - -int -pci_enable_io(device_t dev, int space) -{ - /* adapted from FreeBSD's pci_enable_io_method */ - int bit = 0; - - switch (space) { - case SYS_RES_IOPORT: - bit = PCI_command_io; - break; - case SYS_RES_MEMORY: - bit = PCI_command_memory; - break; - default: - return EINVAL; - } - - pci_set_command_bit(dev, bit); - if (pci_read_config(dev, PCI_command, 2) & bit) - return 0; - - device_printf(dev, "pci_enable_io(%d) failed.\n", space); - - return ENXIO; -} - - -int -pci_find_cap(device_t dev, int capability, int *capreg) -{ - return pci_find_extcap(dev, capability, capreg); -} - - -int -pci_find_extcap(device_t child, int capability, int *_capabilityRegister) -{ - uint8 capabilityPointer; - uint8 headerType; - uint16 status; - - status = pci_read_config(child, PCIR_STATUS, 2); - if ((status & PCIM_STATUS_CAPPRESENT) == 0) - return ENXIO; - - headerType = pci_read_config(child, PCI_header_type, 1); - switch (headerType & PCIM_HDRTYPE) { - case 0: - case 1: - capabilityPointer = PCIR_CAP_PTR; - break; - case 2: - capabilityPointer = PCIR_CAP_PTR_2; - break; - default: - return ENXIO; - } - capabilityPointer = pci_read_config(child, capabilityPointer, 1); - - while (capabilityPointer != 0) { - if (pci_read_config(child, capabilityPointer + PCICAP_ID, 1) - == capability) { - if (_capabilityRegister != NULL) - *_capabilityRegister = capabilityPointer; - return 0; - } - capabilityPointer = pci_read_config(child, - capabilityPointer + PCICAP_NEXTPTR, 1); - } - - return ENOENT; -} - - -int -pci_msi_count(device_t dev) -{ - pci_info *info; - if (gPCIx86 == NULL) - return 0; - - info = &((struct root_device_softc *)dev->root->softc)->pci_info; - return gPCIx86->get_msi_count(info->bus, info->device, info->function); -} - - -int -pci_alloc_msi(device_t dev, int *count) -{ - pci_info *info; - uint8 startVector = 0; - if (gPCIx86 == NULL) - return ENODEV; - - info = &((struct root_device_softc *)dev->root->softc)->pci_info; - - if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, - &startVector) != B_OK) { - return ENODEV; - } - - ((struct root_device_softc *)dev->root->softc)->is_msi = true; - info->u.h0.interrupt_line = startVector; - return EOK; -} - - -int -pci_release_msi(device_t dev) -{ - pci_info *info; - if (gPCIx86 == NULL) - return ENODEV; - - info = &((struct root_device_softc *)dev->root->softc)->pci_info; - gPCIx86->unconfigure_msi(info->bus, info->device, info->function); - ((struct root_device_softc *)dev->root->softc)->is_msi = false; - ((struct root_device_softc *)dev->root->softc)->is_msix = false; - return EOK; -} - - -int -pci_msix_table_bar(device_t dev) -{ - pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; - - uint8 capability_offset; - if (gPci->find_pci_capability(info->bus, info->device, info->function, - PCI_cap_id_msix, &capability_offset) != B_OK) - return -1; - - uint32 table_value = gPci->read_pci_config(info->bus, info->device, info->function, - capability_offset + PCI_msix_table, 4); - - uint32 bar = table_value & PCI_msix_bir_mask; - return PCIR_BAR(bar); -} - - -int -pci_msix_count(device_t dev) -{ - pci_info *info; - if (gPCIx86 == NULL) - return 0; - - info = &((struct root_device_softc *)dev->root->softc)->pci_info; - return gPCIx86->get_msix_count(info->bus, info->device, info->function); -} - - -int -pci_alloc_msix(device_t dev, int *count) -{ - pci_info *info; - uint8 startVector = 0; - if (gPCIx86 == NULL) - return ENODEV; - - info = &((struct root_device_softc *)dev->root->softc)->pci_info; - - if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count, - &startVector) != B_OK) { - return ENODEV; - } - - ((struct root_device_softc *)dev->root->softc)->is_msix = true; - info->u.h0.interrupt_line = startVector; - return EOK; -} - - -int -pci_get_max_read_req(device_t dev) -{ - int cap; - uint16_t val; - - if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) - return (0); - val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); - val &= PCIM_EXP_CTL_MAX_READ_REQUEST; - val >>= 12; - return (1 << (val + 7)); -} - - -int -pci_set_max_read_req(device_t dev, int size) -{ - int cap; - uint16_t val; - - if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) - return (0); - if (size < 128) - size = 128; - if (size > 4096) - size = 4096; - size = (1 << (fls(size) - 1)); - val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); - val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; - val |= (fls(size) - 8) << 12; - pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); - return (size); -} - - -int -pci_get_powerstate(device_t dev) -{ - int capabilityRegister; - uint16 status; - int powerState = PCI_POWERSTATE_D0; - - if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) - return powerState; - - status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2); - switch (status & PCI_pm_mask) { - case PCI_pm_state_d0: - break; - case PCI_pm_state_d1: - powerState = PCI_POWERSTATE_D1; - break; - case PCI_pm_state_d2: - powerState = PCI_POWERSTATE_D2; - break; - case PCI_pm_state_d3: - powerState = PCI_POWERSTATE_D3; - break; - default: - powerState = PCI_POWERSTATE_UNKNOWN; - break; - } - - TRACE_PCI(dev, "%s: D%i\n", __func__, powerState); - return powerState; -} - - -int -pci_set_powerstate(device_t dev, int newPowerState) -{ - int capabilityRegister; - int oldPowerState; - uint8 currentPowerManagementStatus; - uint8 newPowerManagementStatus; - uint16 powerManagementCapabilities; - bigtime_t stateTransitionDelayInUs = 0; - - if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) - return EOPNOTSUPP; - - oldPowerState = pci_get_powerstate(dev); - if (oldPowerState == newPowerState) - return EOK; - - switch (std::max(oldPowerState, newPowerState)) { - case PCI_POWERSTATE_D2: - stateTransitionDelayInUs = 200; - break; - case PCI_POWERSTATE_D3: - stateTransitionDelayInUs = 10000; - break; - } - - currentPowerManagementStatus = pci_read_config(dev, capabilityRegister - + PCIR_POWER_STATUS, 2); - newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask; - powerManagementCapabilities = pci_read_config(dev, capabilityRegister - + PCIR_POWER_CAP, 2); - - switch (newPowerState) { - case PCI_POWERSTATE_D0: - newPowerManagementStatus |= PCIM_PSTAT_D0; - break; - case PCI_POWERSTATE_D1: - if ((powerManagementCapabilities & PCI_pm_d1supp) == 0) - return EOPNOTSUPP; - newPowerManagementStatus |= PCIM_PSTAT_D1; - break; - case PCI_POWERSTATE_D2: - if ((powerManagementCapabilities & PCI_pm_d2supp) == 0) - return EOPNOTSUPP; - newPowerManagementStatus |= PCIM_PSTAT_D2; - break; - case PCI_POWERSTATE_D3: - newPowerManagementStatus |= PCIM_PSTAT_D3; - break; - default: - return EINVAL; - } - - TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState); - pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, - newPowerManagementStatus, 2); - if (stateTransitionDelayInUs != 0) - snooze(stateTransitionDelayInUs); - - return EOK; -} diff --git a/src/libs/compat/freebsd_network/pci.cpp b/src/libs/compat/freebsd_network/pci.cpp new file mode 100644 index 0000000000..cbf2b721a5 --- /dev/null +++ b/src/libs/compat/freebsd_network/pci.cpp @@ -0,0 +1,466 @@ +/* + * Copyright 2022, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +extern "C" { +#include "device.h" + +#include +#include +#include +} + +#include +#include + + +//#define DEBUG_PCI +#ifdef DEBUG_PCI +# define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args) +#else +# define TRACE_PCI(dev, format, args...) do { } while (0) +#endif + + +uint32_t +pci_read_config(device_t dev, int offset, int size) +{ + pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + uint32_t value = gPci->read_pci_config(info->bus, info->device, + info->function, offset, size); + TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value); + return value; +} + + +void +pci_write_config(device_t dev, int offset, uint32_t value, int size) +{ + pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size); + + gPci->write_pci_config(info->bus, info->device, info->function, offset, + size, value); +} + + +uint16_t +pci_get_vendor(device_t dev) +{ + return pci_read_config(dev, PCI_vendor_id, 2); +} + + +uint16_t +pci_get_device(device_t dev) +{ + return pci_read_config(dev, PCI_device_id, 2); +} + + +uint16_t +pci_get_subvendor(device_t dev) +{ + return pci_read_config(dev, PCI_subsystem_vendor_id, 2); +} + + +uint16_t +pci_get_subdevice(device_t dev) +{ + return pci_read_config(dev, PCI_subsystem_id, 2); +} + + +uint8_t +pci_get_revid(device_t dev) +{ + return pci_read_config(dev, PCI_revision, 1); +} + + +uint32_t +pci_get_domain(device_t dev) +{ + return 0; +} + +uint32_t +pci_get_devid(device_t dev) +{ + return pci_read_config(dev, PCI_device_id, 2) << 16 | + pci_read_config(dev, PCI_vendor_id, 2); +} + +uint8_t +pci_get_cachelnsz(device_t dev) +{ + return pci_read_config(dev, PCI_line_size, 1); +} + +uint8_t * +pci_get_ether(device_t dev) +{ + /* used in if_dc to get the MAC from CardBus CIS for Xircom card */ + return NULL; /* NULL is handled in the caller correctly */ +} + +uint8_t +pci_get_bus(device_t dev) +{ + pci_info *info + = &((struct root_device_softc *)dev->root->softc)->pci_info; + return info->bus; +} + + +uint8_t +pci_get_slot(device_t dev) +{ + pci_info *info + = &((struct root_device_softc *)dev->root->softc)->pci_info; + return info->device; +} + + +uint8_t +pci_get_function(device_t dev) +{ + pci_info *info + = &((struct root_device_softc *)dev->root->softc)->pci_info; + return info->function; +} + + +device_t +pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func) +{ + // We don't support that yet - if we want to support the multi port + // feature of the Broadcom BCM 570x driver, we would have to change + // that. + return NULL; +} + + +static void +pci_set_command_bit(device_t dev, uint16_t bit) +{ + uint16_t command = pci_read_config(dev, PCI_command, 2); + pci_write_config(dev, PCI_command, command | bit, 2); +} + + +int +pci_enable_busmaster(device_t dev) +{ + pci_set_command_bit(dev, PCI_command_master); + return 0; +} + + +int +pci_enable_io(device_t dev, int space) +{ + /* adapted from FreeBSD's pci_enable_io_method */ + int bit = 0; + + switch (space) { + case SYS_RES_IOPORT: + bit = PCI_command_io; + break; + case SYS_RES_MEMORY: + bit = PCI_command_memory; + break; + default: + return EINVAL; + } + + pci_set_command_bit(dev, bit); + if (pci_read_config(dev, PCI_command, 2) & bit) + return 0; + + device_printf(dev, "pci_enable_io(%d) failed.\n", space); + + return ENXIO; +} + + +int +pci_find_cap(device_t dev, int capability, int *capreg) +{ + return pci_find_extcap(dev, capability, capreg); +} + + +int +pci_find_extcap(device_t child, int capability, int *_capabilityRegister) +{ + uint8 capabilityPointer; + uint8 headerType; + uint16 status; + + status = pci_read_config(child, PCIR_STATUS, 2); + if ((status & PCIM_STATUS_CAPPRESENT) == 0) + return ENXIO; + + headerType = pci_read_config(child, PCI_header_type, 1); + switch (headerType & PCIM_HDRTYPE) { + case 0: + case 1: + capabilityPointer = PCIR_CAP_PTR; + break; + case 2: + capabilityPointer = PCIR_CAP_PTR_2; + break; + default: + return ENXIO; + } + capabilityPointer = pci_read_config(child, capabilityPointer, 1); + + while (capabilityPointer != 0) { + if (pci_read_config(child, capabilityPointer + PCICAP_ID, 1) + == capability) { + if (_capabilityRegister != NULL) + *_capabilityRegister = capabilityPointer; + return 0; + } + capabilityPointer = pci_read_config(child, + capabilityPointer + PCICAP_NEXTPTR, 1); + } + + return ENOENT; +} + + +int +pci_msi_count(device_t dev) +{ + pci_info *info; + if (gPCIx86 == NULL) + return 0; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + return gPCIx86->get_msi_count(info->bus, info->device, info->function); +} + + +int +pci_alloc_msi(device_t dev, int *count) +{ + pci_info *info; + uint8 startVector = 0; + if (gPCIx86 == NULL) + return ENODEV; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, + &startVector) != B_OK) { + return ENODEV; + } + + ((struct root_device_softc *)dev->root->softc)->is_msi = true; + info->u.h0.interrupt_line = startVector; + return EOK; +} + + +int +pci_release_msi(device_t dev) +{ + pci_info *info; + if (gPCIx86 == NULL) + return ENODEV; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + gPCIx86->unconfigure_msi(info->bus, info->device, info->function); + ((struct root_device_softc *)dev->root->softc)->is_msi = false; + ((struct root_device_softc *)dev->root->softc)->is_msix = false; + return EOK; +} + + +int +pci_msix_table_bar(device_t dev) +{ + pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + uint8 capability_offset; + if (gPci->find_pci_capability(info->bus, info->device, info->function, + PCI_cap_id_msix, &capability_offset) != B_OK) + return -1; + + uint32 table_value = gPci->read_pci_config(info->bus, info->device, info->function, + capability_offset + PCI_msix_table, 4); + + uint32 bar = table_value & PCI_msix_bir_mask; + return PCIR_BAR(bar); +} + + +int +pci_msix_count(device_t dev) +{ + pci_info *info; + if (gPCIx86 == NULL) + return 0; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + return gPCIx86->get_msix_count(info->bus, info->device, info->function); +} + + +int +pci_alloc_msix(device_t dev, int *count) +{ + pci_info *info; + uint8 startVector = 0; + if (gPCIx86 == NULL) + return ENODEV; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count, + &startVector) != B_OK) { + return ENODEV; + } + + ((struct root_device_softc *)dev->root->softc)->is_msix = true; + info->u.h0.interrupt_line = startVector; + return EOK; +} + + +int +pci_get_max_read_req(device_t dev) +{ + int cap; + uint16_t val; + + if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) + return (0); + val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); + val &= PCIM_EXP_CTL_MAX_READ_REQUEST; + val >>= 12; + return (1 << (val + 7)); +} + + +int +pci_set_max_read_req(device_t dev, int size) +{ + int cap; + uint16_t val; + + if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) + return (0); + if (size < 128) + size = 128; + if (size > 4096) + size = 4096; + size = (1 << (fls(size) - 1)); + val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); + val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; + val |= (fls(size) - 8) << 12; + pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); + return (size); +} + + +int +pci_get_powerstate(device_t dev) +{ + int capabilityRegister; + uint16 status; + int powerState = PCI_POWERSTATE_D0; + + if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) + return powerState; + + status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2); + switch (status & PCI_pm_mask) { + case PCI_pm_state_d0: + break; + case PCI_pm_state_d1: + powerState = PCI_POWERSTATE_D1; + break; + case PCI_pm_state_d2: + powerState = PCI_POWERSTATE_D2; + break; + case PCI_pm_state_d3: + powerState = PCI_POWERSTATE_D3; + break; + default: + powerState = PCI_POWERSTATE_UNKNOWN; + break; + } + + TRACE_PCI(dev, "%s: D%i\n", __func__, powerState); + return powerState; +} + + +int +pci_set_powerstate(device_t dev, int newPowerState) +{ + int capabilityRegister; + int oldPowerState; + uint8 currentPowerManagementStatus; + uint8 newPowerManagementStatus; + uint16 powerManagementCapabilities; + bigtime_t stateTransitionDelayInUs = 0; + + if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) + return EOPNOTSUPP; + + oldPowerState = pci_get_powerstate(dev); + if (oldPowerState == newPowerState) + return EOK; + + switch (max_c(oldPowerState, newPowerState)) { + case PCI_POWERSTATE_D2: + stateTransitionDelayInUs = 200; + break; + case PCI_POWERSTATE_D3: + stateTransitionDelayInUs = 10000; + break; + } + + currentPowerManagementStatus = pci_read_config(dev, capabilityRegister + + PCIR_POWER_STATUS, 2); + newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask; + powerManagementCapabilities = pci_read_config(dev, capabilityRegister + + PCIR_POWER_CAP, 2); + + switch (newPowerState) { + case PCI_POWERSTATE_D0: + newPowerManagementStatus |= PCIM_PSTAT_D0; + break; + case PCI_POWERSTATE_D1: + if ((powerManagementCapabilities & PCI_pm_d1supp) == 0) + return EOPNOTSUPP; + newPowerManagementStatus |= PCIM_PSTAT_D1; + break; + case PCI_POWERSTATE_D2: + if ((powerManagementCapabilities & PCI_pm_d2supp) == 0) + return EOPNOTSUPP; + newPowerManagementStatus |= PCIM_PSTAT_D2; + break; + case PCI_POWERSTATE_D3: + newPowerManagementStatus |= PCIM_PSTAT_D3; + break; + default: + return EINVAL; + } + + TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState); + pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, + newPowerManagementStatus, 2); + if (stateTransitionDelayInUs != 0) + snooze(stateTransitionDelayInUs); + + return EOK; +}