Implement a rudimentary suspend/resume methods for PCI P2P bridge.

Reviewed by:	jhb, imp
This commit is contained in:
Jung-uk Kim 2009-12-10 01:01:53 +00:00
parent 2354bdcf94
commit e36af2929a
2 changed files with 200 additions and 57 deletions

View File

@ -253,7 +253,7 @@ disable. 1 means conservatively place devices into D3 state. 2 means\n\
agressively place devices into D3 state. 3 means put absolutely everything\n\
in D3 state.");
static int pci_do_power_resume = 1;
int pci_do_power_resume = 1;
TUNABLE_INT("hw.pci.do_power_resume", &pci_do_power_resume);
SYSCTL_INT(_hw_pci, OID_AUTO, do_power_resume, CTLFLAG_RW,
&pci_do_power_resume, 1,

View File

@ -52,7 +52,18 @@ __FBSDID("$FreeBSD$");
#include "pcib_if.h"
#ifdef __HAVE_ACPI
#include <contrib/dev/acpica/include/acpi.h>
#include "acpi_if.h"
#else
#define ACPI_PWR_FOR_SLEEP(x, y, z)
#endif
extern int pci_do_power_resume;
static int pcib_probe(device_t dev);
static int pcib_suspend(device_t dev);
static int pcib_resume(device_t dev);
static device_method_t pcib_methods[] = {
/* Device interface */
@ -60,8 +71,8 @@ static device_method_t pcib_methods[] = {
DEVMETHOD(device_attach, pcib_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_suspend, pcib_suspend),
DEVMETHOD(device_resume, pcib_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
@ -120,6 +131,154 @@ pcib_is_io_open(struct pcib_softc *sc)
return (sc->iobase > 0 && sc->iobase < sc->iolimit);
}
/*
* Get current I/O decode.
*/
static void
pcib_get_io_decode(struct pcib_softc *sc)
{
device_t dev;
uint32_t iolow;
dev = sc->dev;
iolow = pci_read_config(dev, PCIR_IOBASEL_1, 1);
if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32)
sc->iobase = PCI_PPBIOBASE(
pci_read_config(dev, PCIR_IOBASEH_1, 2), iolow);
else
sc->iobase = PCI_PPBIOBASE(0, iolow);
iolow = pci_read_config(dev, PCIR_IOLIMITL_1, 1);
if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32)
sc->iolimit = PCI_PPBIOLIMIT(
pci_read_config(dev, PCIR_IOLIMITH_1, 2), iolow);
else
sc->iolimit = PCI_PPBIOLIMIT(0, iolow);
}
/*
* Get current memory decode.
*/
static void
pcib_get_mem_decode(struct pcib_softc *sc)
{
device_t dev;
pci_addr_t pmemlow;
dev = sc->dev;
sc->membase = PCI_PPBMEMBASE(0,
pci_read_config(dev, PCIR_MEMBASE_1, 2));
sc->memlimit = PCI_PPBMEMLIMIT(0,
pci_read_config(dev, PCIR_MEMLIMIT_1, 2));
pmemlow = pci_read_config(dev, PCIR_PMBASEL_1, 2);
if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64)
sc->pmembase = PCI_PPBMEMBASE(
pci_read_config(dev, PCIR_PMBASEH_1, 4), pmemlow);
else
sc->pmembase = PCI_PPBMEMBASE(0, pmemlow);
pmemlow = pci_read_config(dev, PCIR_PMLIMITL_1, 2);
if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64)
sc->pmemlimit = PCI_PPBMEMLIMIT(
pci_read_config(dev, PCIR_PMLIMITH_1, 4), pmemlow);
else
sc->pmemlimit = PCI_PPBMEMLIMIT(0, pmemlow);
}
/*
* Restore previous I/O decode.
*/
static void
pcib_set_io_decode(struct pcib_softc *sc)
{
device_t dev;
uint32_t iohi;
dev = sc->dev;
iohi = sc->iobase >> 16;
if (iohi > 0)
pci_write_config(dev, PCIR_IOBASEH_1, iohi, 2);
pci_write_config(dev, PCIR_IOBASEL_1, sc->iobase >> 8, 1);
iohi = sc->iolimit >> 16;
if (iohi > 0)
pci_write_config(dev, PCIR_IOLIMITH_1, iohi, 2);
pci_write_config(dev, PCIR_IOLIMITL_1, sc->iolimit >> 8, 1);
}
/*
* Restore previous memory decode.
*/
static void
pcib_set_mem_decode(struct pcib_softc *sc)
{
device_t dev;
pci_addr_t pmemhi;
dev = sc->dev;
pci_write_config(dev, PCIR_MEMBASE_1, sc->membase >> 16, 2);
pci_write_config(dev, PCIR_MEMLIMIT_1, sc->memlimit >> 16, 2);
pmemhi = sc->pmembase >> 32;
if (pmemhi > 0)
pci_write_config(dev, PCIR_PMBASEH_1, pmemhi, 4);
pci_write_config(dev, PCIR_PMBASEL_1, sc->pmembase >> 16, 2);
pmemhi = sc->pmemlimit >> 32;
if (pmemhi > 0)
pci_write_config(dev, PCIR_PMLIMITH_1, pmemhi, 4);
pci_write_config(dev, PCIR_PMLIMITL_1, sc->pmemlimit >> 16, 2);
}
/*
* Get current bridge configuration.
*/
static void
pcib_cfg_save(struct pcib_softc *sc)
{
device_t dev;
dev = sc->dev;
sc->command = pci_read_config(dev, PCIR_COMMAND, 2);
sc->pribus = pci_read_config(dev, PCIR_PRIBUS_1, 1);
sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1);
sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1);
if (sc->command & PCIM_CMD_PORTEN)
pcib_get_io_decode(sc);
if (sc->command & PCIM_CMD_MEMEN)
pcib_get_mem_decode(sc);
}
/*
* Restore previous bridge configuration.
*/
static void
pcib_cfg_restore(struct pcib_softc *sc)
{
device_t dev;
dev = sc->dev;
pci_write_config(dev, PCIR_COMMAND, sc->command, 2);
pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1);
pci_write_config(dev, PCIR_SECBUS_1, sc->secbus, 1);
pci_write_config(dev, PCIR_SUBBUS_1, sc->subbus, 1);
pci_write_config(dev, PCIR_BRIDGECTL_1, sc->bridgectl, 2);
pci_write_config(dev, PCIR_SECLAT_1, sc->seclat, 1);
if (sc->command & PCIM_CMD_PORTEN)
pcib_set_io_decode(sc);
if (sc->command & PCIM_CMD_MEMEN)
pcib_set_mem_decode(sc);
}
/*
* Generic device interface
*/
@ -138,7 +297,6 @@ void
pcib_attach_common(device_t dev)
{
struct pcib_softc *sc;
uint8_t iolow;
struct sysctl_ctx_list *sctx;
struct sysctl_oid *soid;
@ -148,14 +306,9 @@ pcib_attach_common(device_t dev)
/*
* Get current bridge configuration.
*/
sc->command = pci_read_config(dev, PCIR_COMMAND, 1);
sc->domain = pci_get_domain(dev);
sc->pribus = pci_read_config(dev, PCIR_PRIBUS_1, 1);
sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1);
sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
sc->secstat = pci_read_config(dev, PCIR_SECSTAT_1, 2);
sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1);
sc->domain = pci_get_domain(dev);
sc->secstat = pci_read_config(dev, PCIR_SECSTAT_1, 2);
pcib_cfg_save(sc);
/*
* Setup sysctl reporting nodes
@ -171,51 +324,6 @@ pcib_attach_common(device_t dev)
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number");
/*
* Determine current I/O decode.
*/
if (sc->command & PCIM_CMD_PORTEN) {
iolow = pci_read_config(dev, PCIR_IOBASEL_1, 1);
if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) {
sc->iobase = PCI_PPBIOBASE(pci_read_config(dev, PCIR_IOBASEH_1, 2),
pci_read_config(dev, PCIR_IOBASEL_1, 1));
} else {
sc->iobase = PCI_PPBIOBASE(0, pci_read_config(dev, PCIR_IOBASEL_1, 1));
}
iolow = pci_read_config(dev, PCIR_IOLIMITL_1, 1);
if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) {
sc->iolimit = PCI_PPBIOLIMIT(pci_read_config(dev, PCIR_IOLIMITH_1, 2),
pci_read_config(dev, PCIR_IOLIMITL_1, 1));
} else {
sc->iolimit = PCI_PPBIOLIMIT(0, pci_read_config(dev, PCIR_IOLIMITL_1, 1));
}
}
/*
* Determine current memory decode.
*/
if (sc->command & PCIM_CMD_MEMEN) {
sc->membase = PCI_PPBMEMBASE(0, pci_read_config(dev, PCIR_MEMBASE_1, 2));
sc->memlimit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_MEMLIMIT_1, 2));
iolow = pci_read_config(dev, PCIR_PMBASEL_1, 1);
if ((iolow & PCIM_BRPM_MASK) == PCIM_BRPM_64)
sc->pmembase = PCI_PPBMEMBASE(
pci_read_config(dev, PCIR_PMBASEH_1, 4),
pci_read_config(dev, PCIR_PMBASEL_1, 2));
else
sc->pmembase = PCI_PPBMEMBASE(0,
pci_read_config(dev, PCIR_PMBASEL_1, 2));
iolow = pci_read_config(dev, PCIR_PMLIMITL_1, 1);
if ((iolow & PCIM_BRPM_MASK) == PCIM_BRPM_64)
sc->pmemlimit = PCI_PPBMEMLIMIT(
pci_read_config(dev, PCIR_PMLIMITH_1, 4),
pci_read_config(dev, PCIR_PMLIMITL_1, 2));
else
sc->pmemlimit = PCI_PPBMEMLIMIT(0,
pci_read_config(dev, PCIR_PMLIMITL_1, 2));
}
/*
* Quirk handling.
*/
@ -336,6 +444,41 @@ pcib_attach(device_t dev)
return(0);
}
int
pcib_suspend(device_t dev)
{
device_t acpi_dev;
int dstate, error;
pcib_cfg_save(device_get_softc(dev));
error = bus_generic_suspend(dev);
if (error == 0 && pci_do_power_resume) {
acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
if (acpi_dev != NULL) {
dstate = PCI_POWERSTATE_D3;
ACPI_PWR_FOR_SLEEP(acpi_dev, dev, &dstate);
pci_set_powerstate(dev, dstate);
}
}
return (error);
}
int
pcib_resume(device_t dev)
{
device_t acpi_dev;
if (pci_do_power_resume) {
acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
if (acpi_dev != NULL) {
ACPI_PWR_FOR_SLEEP(acpi_dev, dev, NULL);
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
}
}
pcib_cfg_restore(device_get_softc(dev));
return (bus_generic_resume(dev));
}
int
pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
{