summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpi/acpi.c
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2013-11-06 10:40:36 +0000
committermpi <mpi@openbsd.org>2013-11-06 10:40:36 +0000
commitb08441ed8c8612b2835f906a7ee162aa1a5fa4f2 (patch)
tree1fc0226b705d7cc67733f6e60d4b29351b2aa1d2 /sys/dev/acpi/acpi.c
parentfix format string (diff)
downloadwireguard-openbsd-b08441ed8c8612b2835f906a7ee162aa1a5fa4f2.tar.xz
wireguard-openbsd-b08441ed8c8612b2835f906a7ee162aa1a5fa4f2.zip
Add support for Power Resources for Dx states and the necessary hook
for PCI devices. This hook should be called twice, before and after changing the power state of a PCI device. Before setting the device to the new state, the ACPI layer will notify every power resources linked to the device for that state and make sure they are turned "_ON". After changing the state of the device, it will decrement the reference of every power resources linked to that device for the old state and turn them "_OFF" if they are no longer referenced. This fixes the no-USB after resume problem seen on various ThinkPad, problem initialy diagnosed with Alexander Polakov. ok kettenis@, deraadt@
Diffstat (limited to 'sys/dev/acpi/acpi.c')
-rw-r--r--sys/dev/acpi/acpi.c92
1 files changed, 84 insertions, 8 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c
index 9d65faa395c..1683dbdc36c 100644
--- a/sys/dev/acpi/acpi.c
+++ b/sys/dev/acpi/acpi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: acpi.c,v 1.246 2013/06/01 23:00:16 mlarkin Exp $ */
+/* $OpenBSD: acpi.c,v 1.247 2013/11/06 10:40:36 mpi Exp $ */
/*
* Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
@@ -74,6 +74,7 @@ int acpi_hasprocfvs;
void acpi_pci_match(struct device *, struct pci_attach_args *);
pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
+void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
int acpi_match(struct device *, void *, void *);
void acpi_attach(struct device *, struct device *, void *);
@@ -559,15 +560,26 @@ void
acpi_pci_match(struct device *dev, struct pci_attach_args *pa)
{
struct acpi_pci *pdev;
+ int state;
TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
- if (pdev->bus == pa->pa_bus &&
- pdev->dev == pa->pa_device &&
- pdev->fun == pa->pa_function) {
- dnprintf(10,"%s at acpi0 %s\n",
- dev->dv_xname, aml_nodename(pdev->node));
- pdev->device = dev;
- }
+ if (pdev->bus != pa->pa_bus ||
+ pdev->dev != pa->pa_device ||
+ pdev->fun != pa->pa_function)
+ continue;
+
+ dnprintf(10,"%s at acpi0 %s\n", dev->dv_xname,
+ aml_nodename(pdev->node));
+
+ pdev->device = dev;
+
+ /*
+ * If some Power Resources are dependent on this device
+ * initialize them.
+ */
+ state = pci_get_powerstate(pa->pa_pc, pa->pa_tag);
+ acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 1);
+ acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 0);
}
}
@@ -604,6 +616,66 @@ acpi_pci_min_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
}
void
+acpi_pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, int state, int pre)
+{
+#if NACPIPWRRES > 0
+ struct acpi_softc *sc = acpi_softc;
+ struct acpi_pwrres *pr;
+ struct acpi_pci *pdev;
+ int bus, dev, fun;
+ char name[5];
+
+ pci_decompose_tag(pc, tag, &bus, &dev, &fun);
+ TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
+ if (pdev->bus == bus && pdev->dev == dev && pdev->fun == fun)
+ break;
+ }
+
+ /* XXX Add a check to discard nodes without Power Resources? */
+ if (pdev == NULL)
+ return;
+
+ SIMPLEQ_FOREACH(pr, &sc->sc_pwrresdevs, p_next) {
+ if (pr->p_node != pdev->node)
+ continue;
+
+ /*
+ * If the firmware is already aware that the device
+ * is in the given state, there's nothing to do.
+ */
+ if (pr->p_state == state)
+ continue;
+
+ if (pre) {
+ /*
+ * If a Resource is dependent on this device for
+ * the given state, make sure it is turned "_ON".
+ */
+ if (pr->p_res_state == state)
+ acpipwrres_ref_incr(pr->p_res_sc, pr->p_node);
+ } else {
+ /*
+ * If a Resource was referenced for the state we
+ * left, drop a reference and turn it "_OFF" if
+ * it was the last one.
+ */
+ if (pr->p_res_state == pr->p_state)
+ acpipwrres_ref_decr(pr->p_res_sc, pr->p_node);
+
+ if (pr->p_res_state == state) {
+ snprintf(name, sizeof(name), "_PS%d", state);
+ aml_evalname(sc, pr->p_node, name, 0,
+ NULL, NULL);
+ }
+
+ pr->p_state = state;
+ }
+
+ }
+#endif /* NACPIPWRRES > 0 */
+}
+
+void
acpi_pciroots_attach(struct device *dev, void *aux, cfprint_t pr)
{
struct acpi_pci *pdev;
@@ -658,6 +730,10 @@ acpi_attach(struct device *parent, struct device *self, void *aux)
SIMPLEQ_INIT(&sc->sc_tables);
SIMPLEQ_INIT(&sc->sc_wakedevs);
+#if NACPIPWRRES > 0
+ SIMPLEQ_INIT(&sc->sc_pwrresdevs);
+#endif /* NACPIPWRRES > 0 */
+
#ifndef SMALL_KERNEL
sc->sc_note = malloc(sizeof(struct klist), M_DEVBUF, M_NOWAIT | M_ZERO);