From a537d866cf8e2a70c505ab9a193903ea70afd5fb Mon Sep 17 00:00:00 2001 From: David Reid Date: Thu, 25 Jul 2002 14:16:57 +0000 Subject: [PATCH] Another support function. This time we add the ability to change power management states for cards that support it. ISTR that Be forced all devices to full power, so we do the same for the moment. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@439 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kernel/core/addons/bus_managers/pci/pci.c | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/src/kernel/core/addons/bus_managers/pci/pci.c b/src/kernel/core/addons/bus_managers/pci/pci.c index 618cc0ee01..d217d65af7 100755 --- a/src/kernel/core/addons/bus_managers/pci/pci.c +++ b/src/kernel/core/addons/bus_managers/pci/pci.c @@ -15,6 +15,7 @@ #include /* for spinlocks */ #include #include +#include #include #include @@ -252,8 +253,62 @@ static int pci_get_capability(uint8 bus, uint8 dev, uint8 func, uint8 cap, return 0; } -//static int pci_set_power_state +/* pci_set_power_state + * This attempts to set the device into one of the 4 power management + * modes, where PCI_pm_state_d0 is "Full Power" and _d3 is the lowest. + * The delay's at the end come from code I've seen but they really need + * to be checked against the spec, which can be found at + * PCI PM 1.1, section 5.6.1 table 18 + * + * There is a note on Linux to say that whilst we can jump straight to + * D0 we can't move to D3 unless we're already at D1, though the code they + * use to check this seems suspect. We may want to check against the spec. + * + * Returns + * EIO if the device doesn't support the power management state requested + * + */ +static int pci_set_power_state(uint8 bus, uint8 dev, uint8 func, int state) +{ + uint8 pm_reg, cur_state; + uint16 cur_status; + if (pci_get_capability(bus, dev, func, PCI_cap_id_pm, &pm_reg) == 0) + return EIO; + + if (state > PCI_pm_state_d3) + state = PCI_pm_state_d3; + + cur_status = read_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 1); + cur_state = cur_status & PCI_pm_mask; + + if (cur_state == state) + return 0; + + if (state == PCI_pm_state_d1 || state == PCI_pm_state_d2) { + uint16 pmc; + pmc = read_pci_config(bus, dev, func, pm_reg + PCI_pm_ctrl, 2); + if (state == PCI_pm_state_d1 && !(pmc & PCI_pm_d1supp)) + return EIO; + if (state == PCI_pm_state_d2 && !(pmc & PCI_pm_d2supp)) + return EIO; + } + + if (cur_state != PCI_pm_state_d3) { + cur_status &= ~PCI_pm_mask; + cur_status |= state; + } + + write_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 2, cur_status); + + if (state == PCI_pm_state_d3 || cur_state == PCI_pm_state_d3) { + snooze(10 * 1000); + } else if (state == PCI_pm_state_d2 || cur_state == PCI_pm_state_d2) + snooze(200); + + return 0; +} + /* set_pci_mechanism() * Try to determine which configuration mechanism the PCI Host Bridge * wants to deal with. @@ -499,8 +554,7 @@ static void scan_pci(void) if (pci_get_capability(bus, dev, func, PCI_cap_id_agp, &offset)) dprintf("Device @ %d:%d:%d has AGP capability\n", bus, dev, func); - if (pci_get_capability(bus, dev, func, PCI_cap_id_pm, &offset)) - dprintf("Device @ %d:%d:%d has Power Management capability\n", bus, dev, func); + pci_set_power_state(bus, dev, func, PCI_pm_state_d0); /* basic header */ pcii->bus = bus;