summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2010-08-17 19:14:52 +0000
committerkettenis <kettenis@openbsd.org>2010-08-17 19:14:52 +0000
commit317ec9eaf434fb575d7e90bf859245a39fc69fb3 (patch)
treee86cb3df6da388aa52ef978db4178fede906db5d /sys/dev/pci/pci.c
parentRestore initialization of apcicnaddr which got accidentally removed in revision (diff)
downloadwireguard-openbsd-317ec9eaf434fb575d7e90bf859245a39fc69fb3.tar.xz
wireguard-openbsd-317ec9eaf434fb575d7e90bf859245a39fc69fb3.zip
Power management for PCI devices. For now just put everything in the D3
(deepest sleep) state upon suspend, and restore power upon resume. ok deraadt@
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c80
1 files changed, 55 insertions, 25 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index a6835016216..af08dd15021 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci.c,v 1.80 2010/07/02 04:03:31 kettenis Exp $ */
+/* $OpenBSD: pci.c,v 1.81 2010/08/17 19:14:52 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
@@ -56,12 +56,12 @@ int pciactivate(struct device *, int);
sizeof(pcireg_t))
struct pci_dev {
LIST_ENTRY(pci_dev) pd_next;
- struct device *pd_dev;
pcitag_t pd_tag; /* pci register tag */
pcireg_t pd_csr;
pcireg_t pd_bhlc;
pcireg_t pd_int;
pcireg_t pd_map[NMAPREG];
+ int pd_pmcsr_state;
};
#ifdef APERTURE
@@ -211,25 +211,52 @@ pcipower(int why, void *arg)
{
struct pci_softc *sc = (struct pci_softc *)arg;
struct pci_dev *pd;
- pcireg_t reg;
+ pcireg_t bhlc, csr, reg;
int i;
LIST_FOREACH(pd, &sc->sc_devs, pd_next) {
+ /*
+ * Only handle header type 0 here; PCI-PCI bridges and
+ * CardBus bridges need special handling, which will
+ * be done in their specific drivers.
+ */
+ bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag, PCI_BHLC_REG);
+ if (PCI_HDRTYPE_TYPE(bhlc) != 0)
+ continue;
+
if (why != PWR_RESUME) {
for (i = 0; i < NMAPREG; i++)
- pd->pd_map[i] = pci_conf_read(sc->sc_pc,
- pd->pd_tag, PCI_MAPREG_START + (i * 4));
+ pd->pd_map[i] = pci_conf_read(sc->sc_pc,
+ pd->pd_tag, PCI_MAPREG_START + (i * 4));
pd->pd_csr = pci_conf_read(sc->sc_pc, pd->pd_tag,
- PCI_COMMAND_STATUS_REG);
+ PCI_COMMAND_STATUS_REG);
pd->pd_bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag,
- PCI_BHLC_REG);
+ PCI_BHLC_REG);
pd->pd_int = pci_conf_read(sc->sc_pc, pd->pd_tag,
- PCI_INTERRUPT_REG);
+ PCI_INTERRUPT_REG);
+
+ /*
+ * Place the device into D3. The PCI Power
+ * Management spec says we should disable I/O
+ * and memory space as well as bus mastering
+ * before we do so.
+ */
+ csr = pd->pd_csr;
+ csr &= ~PCI_COMMAND_IO_ENABLE;
+ csr &= ~PCI_COMMAND_MEM_ENABLE;
+ csr &= ~PCI_COMMAND_MASTER_ENABLE;
+ pci_conf_write(sc->sc_pc, pd->pd_tag,
+ PCI_COMMAND_STATUS_REG, csr);
+ pd->pd_pmcsr_state = pci_get_powerstate(sc->sc_pc,
+ pd->pd_tag);
+ pci_set_powerstate(sc->sc_pc, pd->pd_tag,
+ PCI_PMCSR_STATE_D3);
} else {
+ pci_set_powerstate(sc->sc_pc, pd->pd_tag,
+ pd->pd_pmcsr_state);
for (i = 0; i < NMAPREG; i++)
pci_conf_write(sc->sc_pc, pd->pd_tag,
- PCI_MAPREG_START + (i * 4),
- pd->pd_map[i]);
+ PCI_MAPREG_START + (i * 4), pd->pd_map[i]);
reg = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_COMMAND_STATUS_REG);
pci_conf_write(sc->sc_pc, pd->pd_tag,
@@ -360,23 +387,13 @@ pci_probe_device(struct pci_softc *sc, pcitag_t tag,
if (ret != 0 && pap != NULL)
*pap = pa;
} else {
- if ((dev = config_found_sm(&sc->sc_dev, &pa, pciprint,
- pcisubmatch))) {
- pcireg_t reg;
+ pd = malloc(sizeof *pd, M_DEVBUF, M_ZERO | M_WAITOK);
+ pd->pd_tag = tag;
+ LIST_INSERT_HEAD(&sc->sc_devs, pd, pd_next);
+ if ((dev = config_found_sm(&sc->sc_dev, &pa, pciprint,
+ pcisubmatch)))
pci_dev_postattach(dev, &pa);
-
- /* skip header type != 0 */
- reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
- if (PCI_HDRTYPE_TYPE(reg) != 0)
- return(0);
- if (!(pd = malloc(sizeof *pd, M_DEVBUF,
- M_NOWAIT)))
- return(0);
- pd->pd_tag = tag;
- pd->pd_dev = dev;
- LIST_INSERT_HEAD(&sc->sc_devs, pd, pd_next);
- }
}
return (ret);
@@ -466,6 +483,19 @@ pci_find_device(struct pci_attach_args *pa,
}
int
+pci_get_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
+{
+ pcireg_t reg;
+ int offset;
+
+ if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, 0)) {
+ reg = pci_conf_read(pc, tag, offset + PCI_PMCSR);
+ return (reg & PCI_PMCSR_STATE_MASK);
+ }
+ return (PCI_PMCSR_STATE_D0);
+}
+
+int
pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, int state)
{
pcireg_t reg;