From 62508c531e0f19a9154670a3cd820164c486131f Mon Sep 17 00:00:00 2001
From: John Baldwin <jhb@FreeBSD.org>
Date: Tue, 17 Aug 2010 15:44:52 +0000
Subject: [PATCH] Add a new method to the PCI bridge interface,
 PCIB_POWER_FOR_SLEEP().  This method is used by the PCI bus driver to query
 the power management system to determine the proper device state to be used
 for a device during suspend and resume.  For the ACPI PCI bridge drivers this
 calls acpi_device_pwr_for_sleep().  This removes ACPI-specific knowledge from
 the PCI and PCI-PCI bridge drivers.

Reviewed by:	jkim
---
 sys/dev/acpica/acpi.c           |  4 +--
 sys/dev/acpica/acpi_pcib.c      | 11 +++++++
 sys/dev/acpica/acpi_pcib_acpi.c |  1 +
 sys/dev/acpica/acpi_pcib_pci.c  |  1 +
 sys/dev/acpica/acpi_pcibvar.h   |  2 ++
 sys/dev/acpica/acpivar.h        |  2 ++
 sys/dev/pci/pci.c               | 53 +++++++++++++--------------------
 sys/dev/pci/pci_pci.c           | 41 ++++++++++++-------------
 sys/dev/pci/pci_private.h       |  2 ++
 sys/dev/pci/pcib_if.m           | 10 +++++++
 10 files changed, 72 insertions(+), 55 deletions(-)

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index f173dc34eb73..feef2cc3fdd4 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -129,8 +129,6 @@ static char	*acpi_device_id_probe(device_t bus, device_t dev, char **ids);
 static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev,
 		    ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters,
 		    ACPI_BUFFER *ret);
-static int	acpi_device_pwr_for_sleep(device_t bus, device_t dev,
-		    int *dstate);
 static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level,
 		    void *context, void **retval);
 static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev,
@@ -1415,7 +1413,7 @@ acpi_device_eval_obj(device_t bus, device_t dev, ACPI_STRING pathname,
     return (AcpiEvaluateObject(h, pathname, parameters, ret));
 }
 
-static int
+int
 acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate)
 {
     struct acpi_softc *sc;
diff --git a/sys/dev/acpica/acpi_pcib.c b/sys/dev/acpica/acpi_pcib.c
index 65996f5b0660..1b26b4f6a1fb 100644
--- a/sys/dev/acpica/acpi_pcib.c
+++ b/sys/dev/acpica/acpi_pcib.c
@@ -275,3 +275,14 @@ acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
 
     return_VALUE (interrupt);
 }
+
+int
+acpi_pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate)
+{
+    device_t acpi_dev;
+
+    acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
+    acpi_device_pwr_for_sleep(acpi_dev, dev, pstate);
+    return (0);
+}
+
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index 72753303cc13..2ea9f2c1e1c8 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -116,6 +116,7 @@ static device_method_t acpi_pcib_acpi_methods[] = {
     DEVMETHOD(pcib_alloc_msix,		acpi_pcib_alloc_msix),
     DEVMETHOD(pcib_release_msix,	pcib_release_msix),
     DEVMETHOD(pcib_map_msi,		acpi_pcib_map_msi),
+    DEVMETHOD(pcib_power_for_sleep,	acpi_pcib_power_for_sleep),
 
     {0, 0}
 };
diff --git a/sys/dev/acpica/acpi_pcib_pci.c b/sys/dev/acpica/acpi_pcib_pci.c
index 2196940c721c..a76698141f08 100644
--- a/sys/dev/acpica/acpi_pcib_pci.c
+++ b/sys/dev/acpica/acpi_pcib_pci.c
@@ -80,6 +80,7 @@ static device_method_t acpi_pcib_pci_methods[] = {
 
     /* pcib interface */
     DEVMETHOD(pcib_route_interrupt,	acpi_pcib_pci_route_interrupt),
+    DEVMETHOD(pcib_power_for_sleep,	acpi_pcib_power_for_sleep),
 
     {0, 0}
 };
diff --git a/sys/dev/acpica/acpi_pcibvar.h b/sys/dev/acpica/acpi_pcibvar.h
index 00e03fa16bf8..9a4be077f589 100644
--- a/sys/dev/acpica/acpi_pcibvar.h
+++ b/sys/dev/acpica/acpi_pcibvar.h
@@ -38,6 +38,8 @@ int	acpi_pci_link_route_interrupt(device_t dev, int index);
 int	acpi_pcib_attach(device_t bus, ACPI_BUFFER *prt, int busno);
 int	acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
     ACPI_BUFFER *prtbuf);
+int	acpi_pcib_power_for_sleep(device_t pcib, device_t dev,
+    int *pstate);
 
 #endif /* _KERNEL */
 
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index adc9d6ea66c0..34284d5d8e13 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -393,6 +393,8 @@ EVENTHANDLER_DECLARE(acpi_wakeup_event, acpi_event_handler_t);
 /* Device power control. */
 ACPI_STATUS	acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable);
 ACPI_STATUS	acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state);
+int		acpi_device_pwr_for_sleep(device_t bus, device_t dev,
+		    int *dstate);
 
 /* Misc. */
 static __inline struct acpi_softc *
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index e1c7645821bc..071f257e6289 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -69,13 +69,6 @@ __FBSDID("$FreeBSD$");
 #include "pcib_if.h"
 #include "pci_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
-
 static pci_addr_t	pci_mapbase(uint64_t mapreg);
 static const char	*pci_maptype(uint64_t mapreg);
 static int		pci_mapsize(uint64_t testval);
@@ -2914,16 +2907,13 @@ int
 pci_suspend(device_t dev)
 {
 	int dstate, error, i, numdevs;
-	device_t acpi_dev, child, *devlist;
+	device_t child, *devlist, pcib;
 	struct pci_devinfo *dinfo;
 
 	/*
 	 * Save the PCI configuration space for each child and set the
 	 * device in the appropriate power state for this sleep state.
 	 */
-	acpi_dev = NULL;
-	if (pci_do_power_resume)
-		acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
 	if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
 		return (error);
 	for (i = 0; i < numdevs; i++) {
@@ -2940,22 +2930,23 @@ pci_suspend(device_t dev)
 	}
 
 	/*
-	 * Always set the device to D3.  If ACPI suggests a different
-	 * power state, use it instead.  If ACPI is not present, the
-	 * firmware is responsible for managing device power.  Skip
-	 * children who aren't attached since they are powered down
-	 * separately.  Only manage type 0 devices for now.
+	 * Always set the device to D3.  If the firmware suggests a
+	 * different power state, use it instead.  If power management
+	 * is not present, the firmware is responsible for managing
+	 * device power.  Skip children who aren't attached since they
+	 * are powered down separately.  Only manage type 0 devices
+	 * for now.
 	 */
-	for (i = 0; acpi_dev && i < numdevs; i++) {
+	pcib = device_get_parent(dev);
+	for (i = 0; pci_do_power_resume && i < numdevs; i++) {
 		child = devlist[i];
 		dinfo = (struct pci_devinfo *) device_get_ivars(child);
+		dstate = PCI_POWERSTATE_D3;
 		if (device_is_attached(child) &&
 		    (dinfo->cfg.hdrtype & PCIM_HDRTYPE) ==
-		    PCIM_HDRTYPE_NORMAL) {
-			dstate = PCI_POWERSTATE_D3;
-			ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate);
+		    PCIM_HDRTYPE_NORMAL &&
+		    PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
 			pci_set_powerstate(child, dstate);
-		}
 	}
 	free(devlist, M_TEMP);
 	return (0);
@@ -2965,31 +2956,29 @@ int
 pci_resume(device_t dev)
 {
 	int i, numdevs, error;
-	device_t acpi_dev, child, *devlist;
+	device_t child, *devlist, pcib;
 	struct pci_devinfo *dinfo;
 
 	/*
 	 * Set each child to D0 and restore its PCI configuration space.
 	 */
-	acpi_dev = NULL;
-	if (pci_do_power_resume)
-		acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
 	if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
 		return (error);
+	pcib = device_get_parent(dev);
 	for (i = 0; i < numdevs; i++) {
 		/*
-		 * Notify ACPI we're going to D0 but ignore the result.  If
-		 * ACPI is not present, the firmware is responsible for
-		 * managing device power.  Only manage type 0 devices for now.
+		 * Notify power managment we're going to D0 but ignore
+		 * the result.  If power management is not present,
+		 * the firmware is responsible for managing device
+		 * power.  Only manage type 0 devices for now.
 		 */
 		child = devlist[i];
 		dinfo = (struct pci_devinfo *) device_get_ivars(child);
-		if (acpi_dev && device_is_attached(child) &&
+		if (device_is_attached(child) &&
 		    (dinfo->cfg.hdrtype & PCIM_HDRTYPE) ==
-		    PCIM_HDRTYPE_NORMAL) {
-			ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL);
+		    PCIM_HDRTYPE_NORMAL &&
+		    PCIB_POWER_FOR_SLEEP(pcib, dev, NULL) == 0)
 			pci_set_powerstate(child, PCI_POWERSTATE_D0);
-		}
 
 		/* Now the device is powered up, restore its config space. */
 		pci_cfg_restore(child, dinfo);
diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c
index 996d27455270..9992b8119df7 100644
--- a/sys/dev/pci/pci_pci.c
+++ b/sys/dev/pci/pci_pci.c
@@ -48,22 +48,16 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/pci/pcivar.h>
 #include <dev/pci/pcireg.h>
+#include <dev/pci/pci_private.h>
 #include <dev/pci/pcib_private.h>
 
 #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 int		pcib_power_for_sleep(device_t pcib, device_t dev,
+			    int *pstate);
 
 static device_method_t pcib_methods[] = {
     /* Device interface */
@@ -95,6 +89,7 @@ static device_method_t pcib_methods[] = {
     DEVMETHOD(pcib_alloc_msix,		pcib_alloc_msix),
     DEVMETHOD(pcib_release_msix,	pcib_release_msix),
     DEVMETHOD(pcib_map_msi,		pcib_map_msi),
+    DEVMETHOD(pcib_power_for_sleep,	pcib_power_for_sleep),
 
     { 0, 0 }
 };
@@ -447,18 +442,16 @@ pcib_attach(device_t dev)
 int
 pcib_suspend(device_t dev)
 {
-	device_t	acpi_dev;
+	device_t	pcib;
 	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);
+		dstate = PCI_POWERSTATE_D3;
+		pcib = device_get_parent(device_get_parent(dev));
+		if (PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
 			pci_set_powerstate(dev, dstate);
-		}
 	}
 	return (error);
 }
@@ -466,14 +459,12 @@ pcib_suspend(device_t dev)
 int
 pcib_resume(device_t dev)
 {
-	device_t	acpi_dev;
+	device_t	pcib;
 
 	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);
+		pcib = device_get_parent(device_get_parent(dev));
+		if (PCIB_POWER_FOR_SLEEP(pcib, dev, NULL) == 0)
 			pci_set_powerstate(dev, PCI_POWERSTATE_D0);
-		}
 	}
 	pcib_cfg_restore(device_get_softc(dev));
 	return (bus_generic_resume(dev));
@@ -790,6 +781,16 @@ pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
 	return (0);
 }
 
+/* Pass request for device power state up to parent bridge. */
+int
+pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate)
+{
+	device_t bus;
+
+	bus = device_get_parent(pcib);
+	return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate));
+}
+
 /*
  * Try to read the bus number of a host-PCI bridge using appropriate config
  * registers.
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
index 2240b4f2c0d8..70d887b32322 100644
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -38,6 +38,8 @@
  */
 DECLARE_CLASS(pci_driver);
 
+extern int 	pci_do_power_resume;
+
 void		pci_add_children(device_t dev, int domain, int busno,
 		    size_t dinfo_size);
 void		pci_add_child(device_t bus, struct pci_devinfo *dinfo);
diff --git a/sys/dev/pci/pcib_if.m b/sys/dev/pci/pcib_if.m
index 0b7e8bc62c9b..b946c0f0541b 100644
--- a/sys/dev/pci/pcib_if.m
+++ b/sys/dev/pci/pcib_if.m
@@ -144,3 +144,13 @@ METHOD int map_msi {
 	uint64_t	*addr;
 	uint32_t	*data;
 };
+
+#
+# Return the device power state to be used during a system sleep state
+# transition such as suspend and resume.
+#
+METHOD int power_for_sleep {
+	device_t	pcib;
+	device_t	dev;
+	int		*pstate;
+};