summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpica')
-rw-r--r--sys/dev/acpica/Osd/OsdDebug.c119
-rw-r--r--sys/dev/acpica/Osd/OsdHardware.c263
-rw-r--r--sys/dev/acpica/Osd/OsdInterrupt.c137
-rw-r--r--sys/dev/acpica/Osd/OsdMemory.c162
-rw-r--r--sys/dev/acpica/Osd/OsdSchedule.c282
-rw-r--r--sys/dev/acpica/Osd/OsdStream.c51
-rw-r--r--sys/dev/acpica/Osd/OsdSynch.c340
-rw-r--r--sys/dev/acpica/Osd/OsdTable.c96
-rw-r--r--sys/dev/acpica/acpi.c2191
-rw-r--r--sys/dev/acpica/acpi_acad.c282
-rw-r--r--sys/dev/acpica/acpi_battery.c266
-rw-r--r--sys/dev/acpica/acpi_button.c224
-rw-r--r--sys/dev/acpica/acpi_cmbat.c715
-rw-r--r--sys/dev/acpica/acpi_cpu.c415
-rw-r--r--sys/dev/acpica/acpi_ec.c848
-rw-r--r--sys/dev/acpica/acpi_lid.c181
-rw-r--r--sys/dev/acpica/acpi_pci.c235
-rw-r--r--sys/dev/acpica/acpi_pci_link.c1077
-rw-r--r--sys/dev/acpica/acpi_pcib.c338
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c291
-rw-r--r--sys/dev/acpica/acpi_pcib_pci.c166
-rw-r--r--sys/dev/acpica/acpi_pcibvar.h41
-rw-r--r--sys/dev/acpica/acpi_powerres.c654
-rw-r--r--sys/dev/acpica/acpi_resource.c592
-rw-r--r--sys/dev/acpica/acpi_thermal.c828
-rw-r--r--sys/dev/acpica/acpi_timer.c389
-rw-r--r--sys/dev/acpica/acpica_support.c106
-rw-r--r--sys/dev/acpica/acpica_support.h38
-rw-r--r--sys/dev/acpica/acpiio.h106
-rw-r--r--sys/dev/acpica/acpivar.h411
30 files changed, 11844 insertions, 0 deletions
diff --git a/sys/dev/acpica/Osd/OsdDebug.c b/sys/dev/acpica/Osd/OsdDebug.c
new file mode 100644
index 0000000..3df9e45
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdDebug.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.8 : Debugging support
+ */
+
+#include "opt_ddb.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+#include <sys/kernel.h>
+
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_output.h>
+
+#include "acpi.h"
+#include "acdebug.h"
+#include <dev/acpica/acpivar.h>
+
+UINT32
+AcpiOsGetLine(char *Buffer)
+{
+#ifdef DDB
+ char *cp;
+
+ db_readline(Buffer, 80);
+ for (cp = Buffer; *cp != 0; cp++)
+ if (*cp == '\n')
+ *cp = 0;
+ return(AE_OK);
+#else
+ printf("AcpiOsGetLine called but no input support");
+ return(AE_NOT_EXIST);
+#endif
+}
+
+void
+AcpiOsDbgAssert(void *FailedAssertion, void *FileName, UINT32 LineNumber, char *Message)
+{
+ printf("ACPI: %s:%d - %s\n", (char *)FileName, LineNumber, Message);
+ printf("ACPI: assertion %s\n", (char *)FailedAssertion);
+}
+
+ACPI_STATUS
+AcpiOsSignal (
+ UINT32 Function,
+ void *Info)
+{
+ ACPI_SIGNAL_FATAL_INFO *fatal;
+ char *message;
+
+ switch(Function) {
+ case ACPI_SIGNAL_FATAL:
+ fatal = (ACPI_SIGNAL_FATAL_INFO *)Info;
+ printf("ACPI fatal signal, type 0x%x code 0x%x argument 0x%x",
+ fatal->Type, fatal->Code, fatal->Argument);
+ Debugger("AcpiOsSignal");
+ break;
+
+ case ACPI_SIGNAL_BREAKPOINT:
+ message = (char *)Info;
+ Debugger(message);
+ break;
+
+ default:
+ return(AE_BAD_PARAMETER);
+ }
+ return(AE_OK);
+}
+
+#ifdef ACPI_DEBUGGER
+void
+acpi_EnterDebugger(void)
+{
+ ACPI_PARSE_OBJECT obj;
+ static int initted = 0;
+
+ if (!initted) {
+ printf("Initialising ACPICA debugger...\n");
+ AcpiDbInitialize();
+ initted = 1;
+ }
+
+ printf("Entering ACPICA debugger...\n");
+ AcpiDbUserCommands('A', &obj);
+}
+#endif
diff --git a/sys/dev/acpica/Osd/OsdHardware.c b/sys/dev/acpica/Osd/OsdHardware.c
new file mode 100644
index 0000000..ab996c6
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdHardware.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.7 : Hardware Abstraction
+ */
+
+#include "acpi.h"
+
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/pci_cfgreg.h>
+#if __FreeBSD_version >= 500000
+#include <dev/pci/pcireg.h>
+#else
+#include <pci/pcireg.h>
+#endif
+
+/*
+ * ACPICA's rather gung-ho approach to hardware resource ownership is a little
+ * troublesome insofar as there is no easy way for us to know in advance
+ * exactly which I/O resources it's going to want to use.
+ *
+ * In order to deal with this, we ignore resource ownership entirely, and simply
+ * use the native I/O space accessor functionality. This is Evil, but it works.
+ *
+ * XXX use an intermediate #define for the tag/handle
+ */
+
+#ifdef __i386__
+#define ACPI_BUS_SPACE_IO I386_BUS_SPACE_IO
+#define ACPI_BUS_HANDLE 0
+#endif
+#ifdef __ia64__
+#define ACPI_BUS_SPACE_IO IA64_BUS_SPACE_IO
+#define ACPI_BUS_HANDLE 0
+#endif
+#ifdef __amd64__
+#define ACPI_BUS_SPACE_IO AMD64_BUS_SPACE_IO
+#define ACPI_BUS_HANDLE 0
+#endif
+
+ACPI_STATUS
+AcpiOsReadPort (
+ ACPI_IO_ADDRESS InPort,
+ void *Value,
+ UINT32 Width)
+{
+ switch (Width) {
+ case 8:
+ *(u_int8_t *)Value = bus_space_read_1(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, InPort);
+ break;
+ case 16:
+ *(u_int16_t *)Value = bus_space_read_2(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, InPort);
+ break;
+ case 32:
+ *(u_int32_t *)Value = bus_space_read_4(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, InPort);
+ break;
+ default:
+ /* debug trap goes here */
+ break;
+ }
+
+ return(AE_OK);
+}
+
+ACPI_STATUS
+AcpiOsWritePort (
+ ACPI_IO_ADDRESS OutPort,
+ ACPI_INTEGER Value,
+ UINT32 Width)
+{
+ switch (Width) {
+ case 8:
+ bus_space_write_1(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
+ break;
+ case 16:
+ bus_space_write_2(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
+ break;
+ case 32:
+ bus_space_write_4(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
+ break;
+ default:
+ /* debug trap goes here */
+ break;
+ }
+
+ return(AE_OK);
+}
+
+ACPI_STATUS
+AcpiOsReadPciConfiguration (
+ ACPI_PCI_ID *PciId,
+ UINT32 Register,
+ void *Value,
+ UINT32 Width)
+{
+ u_int32_t byte_width = Width / 8;
+ u_int32_t val;
+
+ if (!pci_cfgregopen())
+ return(AE_NOT_EXIST);
+
+ val = pci_cfgregread(PciId->Bus, PciId->Device, PciId->Function, Register, byte_width);
+ switch (Width) {
+ case 8:
+ *(u_int8_t *)Value = val & 0xff;
+ break;
+ case 16:
+ *(u_int16_t *)Value = val & 0xffff;
+ break;
+ case 32:
+ *(u_int32_t *)Value = val;
+ break;
+ default:
+ /* debug trap goes here */
+ break;
+ }
+
+
+ return(AE_OK);
+}
+
+
+ACPI_STATUS
+AcpiOsWritePciConfiguration (
+ ACPI_PCI_ID *PciId,
+ UINT32 Register,
+ ACPI_INTEGER Value,
+ UINT32 Width)
+{
+ u_int32_t byte_width = Width / 8;
+
+ if (!pci_cfgregopen())
+ return(AE_NOT_EXIST);
+
+ pci_cfgregwrite(PciId->Bus, PciId->Device, PciId->Function, Register, Value, byte_width);
+
+ return(AE_OK);
+}
+
+/* XXX should use acpivar.h but too many include dependencies */
+extern ACPI_STATUS acpi_EvaluateInteger(ACPI_HANDLE handle, char *path, int
+ *number);
+
+/*
+ * Depth-first recursive case for finding the bus, given the slot/function.
+ */
+static int
+acpi_bus_number(ACPI_HANDLE root, ACPI_HANDLE curr, ACPI_PCI_ID *PciId)
+{
+ ACPI_HANDLE parent;
+ ACPI_OBJECT_TYPE type;
+ UINT32 adr;
+ int bus, slot, func, class, subclass, header;
+
+ /* Try to get the _BBN object of the root, otherwise assume it is 0 */
+ bus = 0;
+ if (root == curr) {
+ if (ACPI_FAILURE(acpi_EvaluateInteger(root, "_BBN", &bus)) &&
+ bootverbose)
+ printf("acpi_bus_number: root bus has no _BBN, assuming 0\n");
+ return (bus);
+ }
+ if (ACPI_FAILURE(AcpiGetParent(curr, &parent)))
+ return (bus);
+
+ /* First, recurse up the tree until we find the host bus */
+ bus = acpi_bus_number(root, parent, PciId);
+
+ /* Validate parent bus device type */
+ if (ACPI_FAILURE(AcpiGetType(parent, &type)) || type != ACPI_TYPE_DEVICE) {
+ printf("acpi_bus_number: not a device, type %d\n", type);
+ return (bus);
+ }
+ /* Get the parent's slot and function */
+ if (ACPI_FAILURE(acpi_EvaluateInteger(parent, "_ADR", &adr))) {
+ printf("acpi_bus_number: can't get _ADR\n");
+ return (bus);
+ }
+ slot = ACPI_HIWORD(adr);
+ func = ACPI_LOWORD(adr);
+
+ /* Is this a PCI-PCI or Cardbus-PCI bridge? */
+ class = pci_cfgregread(bus, slot, func, PCIR_CLASS, 1);
+ if (class != PCIC_BRIDGE)
+ return (bus);
+ subclass = pci_cfgregread(bus, slot, func, PCIR_SUBCLASS, 1);
+ /* Find the header type, masking off the multifunction bit */
+ header = pci_cfgregread(bus, slot, func, PCIR_HEADERTYPE, 1) & 0x7f;
+ if (header == 1 && subclass == PCIS_BRIDGE_PCI)
+ bus = pci_cfgregread(bus, slot, func, PCIR_SECBUS_1, 1);
+ if (header == 2 && subclass == PCIS_BRIDGE_CARDBUS)
+ bus = pci_cfgregread(bus, slot, func, PCIR_SECBUS_2, 1);
+ return (bus);
+}
+
+/*
+ * Find the bus number for a device
+ *
+ * rhandle: handle for the root bus
+ * chandle: handle for the device
+ * PciId: pointer to device slot and function, we fill out bus
+ */
+void
+AcpiOsDerivePciId (
+ ACPI_HANDLE rhandle,
+ ACPI_HANDLE chandle,
+ ACPI_PCI_ID **PciId)
+{
+ ACPI_HANDLE parent;
+ int bus;
+
+ if (pci_cfgregopen() == 0)
+ panic("AcpiOsDerivePciId unable to initialize pci bus");
+
+ /* Try to read _BBN for bus number if we're at the root */
+ bus = 0;
+ if (rhandle == chandle) {
+ if (ACPI_FAILURE(acpi_EvaluateInteger(rhandle, "_BBN", &bus)) &&
+ bootverbose)
+ printf("AcpiOsDerivePciId: root bus has no _BBN, assuming 0\n");
+ }
+ /*
+ * Get the parent handle and call the recursive case. It is not
+ * clear why we seem to be getting a chandle that points to a child
+ * of the desired slot/function but passing in the parent handle
+ * here works.
+ */
+ if (ACPI_SUCCESS(AcpiGetParent(chandle, &parent)))
+ bus = acpi_bus_number(rhandle, parent, *PciId);
+ (*PciId)->Bus = bus;
+ if (bootverbose) {
+ printf("AcpiOsDerivePciId: bus %d dev %d func %d\n",
+ (*PciId)->Bus, (*PciId)->Device, (*PciId)->Function);
+ }
+}
diff --git a/sys/dev/acpica/Osd/OsdInterrupt.c b/sys/dev/acpica/Osd/OsdInterrupt.c
new file mode 100644
index 0000000..d848145
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdInterrupt.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.5 : Interrupt handling
+ */
+
+#include "acpi.h"
+
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <dev/acpica/acpivar.h>
+
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("INTERRUPT")
+
+static void InterruptWrapper(void *arg);
+static OSD_HANDLER InterruptHandler;
+
+/*
+ * XXX this does not correctly free resources in the case of partically successful
+ * attachment.
+ */
+ACPI_STATUS
+AcpiOsInstallInterruptHandler(UINT32 InterruptNumber, OSD_HANDLER ServiceRoutine, void *Context)
+{
+ struct acpi_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if ((sc = devclass_get_softc(devclass_find("acpi"), 0)) == NULL)
+ panic("can't find ACPI device to register interrupt");
+ if (sc->acpi_dev == NULL)
+ panic("acpi softc has invalid device");
+
+ if ((InterruptNumber < 0) || (InterruptNumber > 255))
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ if (ServiceRoutine == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ if (InterruptHandler != NULL && InterruptHandler != ServiceRoutine) {
+ device_printf(sc->acpi_dev, "can't register more than one ACPI interrupt\n");
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ InterruptHandler = ServiceRoutine;
+
+ /*
+ * This isn't strictly true, as we ought to be able to handle > 1 interrupt. The ACPI
+ * spec doesn't call for this though.
+ */
+ if (sc->acpi_irq != NULL) {
+ device_printf(sc->acpi_dev, "attempt to register more than one interrupt handler\n");
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+ sc->acpi_irq_rid = 0;
+ bus_set_resource(sc->acpi_dev, SYS_RES_IRQ, 0, InterruptNumber, 1);
+ if ((sc->acpi_irq = bus_alloc_resource(sc->acpi_dev, SYS_RES_IRQ, &sc->acpi_irq_rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE)) == NULL) {
+ device_printf(sc->acpi_dev, "could not allocate SCI interrupt\n");
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+ if (bus_setup_intr(sc->acpi_dev, sc->acpi_irq, INTR_TYPE_MISC, (driver_intr_t *)InterruptWrapper,
+ Context, &sc->acpi_irq_handle)) {
+ device_printf(sc->acpi_dev, "could not set up SCI interrupt\n");
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_STATUS
+AcpiOsRemoveInterruptHandler (UINT32 InterruptNumber, OSD_HANDLER ServiceRoutine)
+{
+ struct acpi_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if ((InterruptNumber < 0) || (InterruptNumber > 255))
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ if (ServiceRoutine == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ if ((sc = devclass_get_softc(devclass_find("acpi"), 0)) == NULL)
+ panic("can't find ACPI device to deregister interrupt");
+
+ if (sc->acpi_irq == NULL)
+ return_ACPI_STATUS(AE_NOT_EXIST);
+
+ bus_teardown_intr(sc->acpi_dev, sc->acpi_irq, sc->acpi_irq_handle);
+ bus_release_resource(sc->acpi_dev, SYS_RES_IRQ, 0, sc->acpi_irq);
+ bus_delete_resource(sc->acpi_dev, SYS_RES_IRQ, 0);
+
+ sc->acpi_irq = NULL;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Interrupt handler wrapper.
+ */
+static void
+InterruptWrapper(void *arg)
+{
+ ACPI_LOCK_DECL;
+
+ ACPI_LOCK;
+ InterruptHandler(arg);
+ ACPI_UNLOCK;
+}
diff --git a/sys/dev/acpica/Osd/OsdMemory.c b/sys/dev/acpica/Osd/OsdMemory.c
new file mode 100644
index 0000000..2bb13cb
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdMemory.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2000 Mitsaru Iwasaki
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.2 : Memory Management
+ */
+
+#include "acpi.h"
+
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+static MALLOC_DEFINE(M_ACPICA, "acpica", "ACPI CA memory pool");
+
+void *
+AcpiOsAllocate(ACPI_SIZE Size)
+{
+ return(malloc(Size, M_ACPICA, M_NOWAIT));
+}
+
+void
+AcpiOsFree (void *Memory)
+{
+ free(Memory, M_ACPICA);
+}
+
+ACPI_STATUS
+AcpiOsMapMemory (ACPI_PHYSICAL_ADDRESS PhysicalAddress, ACPI_SIZE Length, void **LogicalAddress)
+{
+ *LogicalAddress = pmap_mapdev((vm_offset_t)PhysicalAddress, Length);
+ if (*LogicalAddress == NULL)
+ return(AE_BAD_ADDRESS);
+ return(AE_OK);
+}
+
+void
+AcpiOsUnmapMemory (void *LogicalAddress, ACPI_SIZE Length)
+{
+ pmap_unmapdev((vm_offset_t)LogicalAddress, Length);
+}
+
+ACPI_STATUS
+AcpiOsGetPhysicalAddress(void *LogicalAddress, ACPI_PHYSICAL_ADDRESS *PhysicalAddress)
+{
+ /* we can't necessarily do this, so cop out */
+ return(AE_BAD_ADDRESS);
+}
+
+/*
+ * There is no clean way to do this. We make the charitable assumption
+ * that callers will not pass garbage to us.
+ */
+BOOLEAN
+AcpiOsReadable (void *Pointer, UINT32 Length)
+{
+ return(TRUE);
+}
+
+BOOLEAN
+AcpiOsWritable (void *Pointer, UINT32 Length)
+{
+ return(TRUE);
+}
+
+ACPI_STATUS
+AcpiOsReadMemory (
+ ACPI_PHYSICAL_ADDRESS Address,
+ void *Value,
+ UINT32 Width)
+{
+ void *LogicalAddress;
+
+ if (AcpiOsMapMemory(Address, Width / 8, &LogicalAddress) != AE_OK) {
+ return(AE_NOT_EXIST);
+ }
+
+ switch (Width) {
+ case 8:
+ *(u_int8_t *)Value = (*(volatile u_int8_t *)LogicalAddress);
+ break;
+ case 16:
+ *(u_int16_t *)Value = (*(volatile u_int16_t *)LogicalAddress);
+ break;
+ case 32:
+ *(u_int32_t *)Value = (*(volatile u_int32_t *)LogicalAddress);
+ break;
+ case 64:
+ *(u_int64_t *)Value = (*(volatile u_int64_t *)LogicalAddress);
+ break;
+ default:
+ /* debug trap goes here */
+ break;
+ }
+
+ AcpiOsUnmapMemory(LogicalAddress, Width / 8);
+
+ return(AE_OK);
+}
+
+ACPI_STATUS
+AcpiOsWriteMemory (
+ ACPI_PHYSICAL_ADDRESS Address,
+ ACPI_INTEGER Value,
+ UINT32 Width)
+{
+ void *LogicalAddress;
+
+ if (AcpiOsMapMemory(Address, Width / 8, &LogicalAddress) != AE_OK) {
+ return(AE_NOT_EXIST);
+ }
+
+ switch (Width) {
+ case 8:
+ (*(volatile u_int8_t *)LogicalAddress) = Value & 0xff;
+ break;
+ case 16:
+ (*(volatile u_int16_t *)LogicalAddress) = Value & 0xffff;
+ break;
+ case 32:
+ (*(volatile u_int32_t *)LogicalAddress) = Value & 0xffffffff;
+ break;
+ case 64:
+ (*(volatile u_int64_t *)LogicalAddress) = Value;
+ break;
+ default:
+ /* debug trap goes here */
+ break;
+ }
+
+ AcpiOsUnmapMemory(LogicalAddress, Width / 8);
+
+ return(AE_OK);
+}
diff --git a/sys/dev/acpica/Osd/OsdSchedule.c b/sys/dev/acpica/Osd/OsdSchedule.c
new file mode 100644
index 0000000..ac29c1e
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdSchedule.c
@@ -0,0 +1,282 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.3 : Scheduling services
+ */
+
+#include "acpi.h"
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/taskqueue.h>
+#include <machine/clock.h>
+
+#include <sys/bus.h>
+
+#include <dev/acpica/acpivar.h>
+
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("SCHEDULE")
+
+/*
+ * This is a little complicated due to the fact that we need to build and then
+ * free a 'struct task' for each task we enqueue.
+ */
+
+MALLOC_DEFINE(M_ACPITASK, "acpitask", "ACPI deferred task");
+
+static void AcpiOsExecuteQueue(void *arg, int pending);
+
+struct acpi_task {
+ struct task at_task;
+ OSD_EXECUTION_CALLBACK at_function;
+ void *at_context;
+};
+
+struct acpi_task_queue {
+ STAILQ_ENTRY(acpi_task_queue) at_q;
+ struct acpi_task *at;
+};
+
+#if __FreeBSD_version >= 500000
+/*
+ * Private task queue definition for ACPI
+ */
+TASKQUEUE_DECLARE(acpi);
+static void *taskqueue_acpi_ih;
+
+static void
+taskqueue_acpi_enqueue(void *context)
+{
+ swi_sched(taskqueue_acpi_ih, 0);
+}
+
+static void
+taskqueue_acpi_run(void *dummy)
+{
+ taskqueue_run(taskqueue_acpi);
+}
+
+TASKQUEUE_DEFINE(acpi, taskqueue_acpi_enqueue, 0,
+ swi_add(NULL, "acpitaskq", taskqueue_acpi_run, NULL,
+ SWI_TQ, 0, &taskqueue_acpi_ih));
+
+#ifdef ACPI_USE_THREADS
+STAILQ_HEAD(, acpi_task_queue) acpi_task_queue;
+static struct mtx acpi_task_mtx;
+
+static void
+acpi_task_thread(void *arg)
+{
+ struct acpi_task_queue *atq;
+ OSD_EXECUTION_CALLBACK Function;
+ void *Context;
+
+ for (;;) {
+ mtx_lock(&acpi_task_mtx);
+ if ((atq = STAILQ_FIRST(&acpi_task_queue)) == NULL) {
+ msleep(&acpi_task_queue, &acpi_task_mtx, PCATCH, "actask", 0);
+ mtx_unlock(&acpi_task_mtx);
+ continue;
+ }
+
+ STAILQ_REMOVE_HEAD(&acpi_task_queue, at_q);
+ mtx_unlock(&acpi_task_mtx);
+
+ Function = (OSD_EXECUTION_CALLBACK)atq->at->at_function;
+ Context = atq->at->at_context;
+
+ mtx_lock(&Giant);
+ Function(Context);
+
+ free(atq->at, M_ACPITASK);
+ free(atq, M_ACPITASK);
+ mtx_unlock(&Giant);
+ }
+
+ kthread_exit(0);
+}
+
+int
+acpi_task_thread_init(void)
+{
+ int i, err;
+ struct proc *acpi_kthread_proc;
+
+ err = 0;
+ STAILQ_INIT(&acpi_task_queue);
+ mtx_init(&acpi_task_mtx, "ACPI task", NULL, MTX_DEF);
+
+ for (i = 0; i < ACPI_MAX_THREADS; i++) {
+ err = kthread_create(acpi_task_thread, NULL, &acpi_kthread_proc,
+ 0, 0, "acpi_task%d", i);
+ if (err != 0) {
+ printf("%s: kthread_create failed(%d)\n", __func__, err);
+ break;
+ }
+ }
+ return (err);
+}
+#endif
+#endif
+
+ACPI_STATUS
+AcpiOsQueueForExecution(UINT32 Priority, OSD_EXECUTION_CALLBACK Function, void *Context)
+{
+ struct acpi_task *at;
+ int pri;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (Function == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ at = malloc(sizeof(*at), M_ACPITASK, M_NOWAIT); /* Interrupt Context */
+ if (at == NULL)
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ bzero(at, sizeof(*at));
+
+ at->at_function = Function;
+ at->at_context = Context;
+ switch (Priority) {
+ case OSD_PRIORITY_GPE:
+ pri = 4;
+ break;
+ case OSD_PRIORITY_HIGH:
+ pri = 3;
+ break;
+ case OSD_PRIORITY_MED:
+ pri = 2;
+ break;
+ case OSD_PRIORITY_LO:
+ pri = 1;
+ break;
+ default:
+ free(at, M_ACPITASK);
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ TASK_INIT(&at->at_task, pri, AcpiOsExecuteQueue, at);
+
+#if __FreeBSD_version < 500000
+ taskqueue_enqueue(taskqueue_swi, (struct task *)at);
+#else
+ taskqueue_enqueue(taskqueue_acpi, (struct task *)at);
+#endif
+ return_ACPI_STATUS(AE_OK);
+}
+
+static void
+AcpiOsExecuteQueue(void *arg, int pending)
+{
+ struct acpi_task *at;
+ struct acpi_task_queue *atq;
+ OSD_EXECUTION_CALLBACK Function;
+ void *Context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ at = (struct acpi_task *)arg;
+ atq = NULL;
+ Function = NULL;
+ Context = NULL;
+
+#ifdef ACPI_USE_THREADS
+ atq = malloc(sizeof(*atq), M_ACPITASK, M_NOWAIT);
+ if (atq == NULL) {
+ printf("%s: no memory\n", __func__);
+ return;
+ }
+
+ atq->at = at;
+
+ mtx_lock(&acpi_task_mtx);
+ STAILQ_INSERT_TAIL(&acpi_task_queue, atq, at_q);
+ mtx_unlock(&acpi_task_mtx);
+ wakeup_one(&acpi_task_queue);
+#else
+ Function = (OSD_EXECUTION_CALLBACK)at->at_function;
+ Context = at->at_context;
+
+ Function(Context);
+ free(at, M_ACPITASK);
+#endif
+
+ return_VOID;
+}
+
+/*
+ * We don't have any sleep granularity better than hz, so
+ * make do with that.
+ */
+void
+AcpiOsSleep (UINT32 Seconds, UINT32 Milliseconds)
+{
+ int timo;
+ static int dummy;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ timo = (Seconds * hz) + Milliseconds * hz / 1000;
+ if (timo == 0)
+ timo = 1;
+ tsleep(&dummy, 0, "acpislp", timo);
+ return_VOID;
+}
+
+void
+AcpiOsStall (UINT32 Microseconds)
+{
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ DELAY(Microseconds);
+ return_VOID;
+}
+
+UINT32
+AcpiOsGetThreadId (void)
+{
+ struct proc *p;
+ /* XXX do not add FUNCTION_TRACE here, results in recursive call */
+
+ p = curproc;
+#if __FreeBSD_version < 500000
+ if (p == NULL)
+ p = &proc0;
+#endif
+ KASSERT(p != NULL, ("%s: curproc is NULL!", __func__));
+ return(p->p_pid + 1); /* can't return 0 */
+}
diff --git a/sys/dev/acpica/Osd/OsdStream.c b/sys/dev/acpica/Osd/OsdStream.c
new file mode 100644
index 0000000..7c9acde
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdStream.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.6 : Stream I/O
+ */
+
+#include "acpi.h"
+
+void
+AcpiOsPrintf (const char *Format, ...)
+{
+ va_list ap;
+
+ va_start(ap, Format);
+ vprintf(Format, ap);
+ va_end(ap);
+}
+
+void
+AcpiOsVprintf (const char *Format, va_list Args)
+{
+ vprintf(Format, Args);
+}
+
diff --git a/sys/dev/acpica/Osd/OsdSynch.c b/sys/dev/acpica/Osd/OsdSynch.c
new file mode 100644
index 0000000..718e3c7
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdSynch.c
@@ -0,0 +1,340 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6.1 : Mutual Exclusion and Synchronisation
+ */
+
+#include "acpi.h"
+
+#include "opt_acpi.h"
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#if __FreeBSD_version >= 500000
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#endif
+
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("SYNCH")
+
+static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
+
+#if __FreeBSD_version < 500000
+# define AS_LOCK(as) s = splhigh()
+# define AS_UNLOCK(as) splx(s)
+# define AS_LOCK_DECL int s
+# define msleep(a, b, c, d, e) tsleep(a, c, d, e)
+#else
+# define AS_LOCK(as) mtx_lock(&(as)->as_mtx)
+# define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx)
+# define AS_LOCK_DECL
+#endif
+
+/*
+ * Simple counting semaphore implemented using a mutex. (Subsequently used
+ * in the OSI code to implement a mutex. Go figure.)
+ */
+struct acpi_semaphore {
+#if __FreeBSD_version >= 500000
+ struct mtx as_mtx;
+#endif
+ UINT32 as_units;
+ UINT32 as_maxunits;
+ UINT32 as_pendings;
+ UINT32 as_resetting;
+ UINT32 as_timeouts;
+};
+
+#ifndef ACPI_NO_SEMAPHORES
+#ifndef ACPI_SEMAPHORES_MAX_PENDING
+#define ACPI_SEMAPHORES_MAX_PENDING 4
+#endif
+static int acpi_semaphore_debug = 0;
+TUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
+SYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW,
+ &acpi_semaphore_debug, 0, "");
+#endif
+
+ACPI_STATUS
+AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle)
+{
+#ifndef ACPI_NO_SEMAPHORES
+ struct acpi_semaphore *as;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (OutHandle == NULL)
+ return(AE_BAD_PARAMETER);
+ if (InitialUnits > MaxUnits)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL)
+ return_ACPI_STATUS(AE_NO_MEMORY);
+
+ bzero(as, sizeof(*as));
+#if __FreeBSD_version >= 500000
+ mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
+#endif
+ as->as_units = InitialUnits;
+ as->as_maxunits = MaxUnits;
+ as->as_pendings = as->as_resetting = as->as_timeouts = 0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "created semaphore %p max %d, initial %d\n",
+ as, InitialUnits, MaxUnits));
+
+ *OutHandle = (ACPI_HANDLE)as;
+ return_ACPI_STATUS(AE_OK);
+#else
+ *OutHandle = (ACPI_HANDLE)OutHandle;
+ return(AE_OK);
+#endif
+}
+
+ACPI_STATUS
+AcpiOsDeleteSemaphore (ACPI_HANDLE Handle)
+{
+#ifndef ACPI_NO_SEMAPHORES
+ struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&as->as_mtx);
+#endif
+ free(Handle, M_ACPISEM);
+ return_ACPI_STATUS(AE_OK);
+#else
+ return(AE_OK);
+#endif
+}
+
+/*
+ * This implementation has a bug, in that it has to stall for the entire
+ * timeout before it will return AE_TIME. A better implementation would
+ * use getmicrotime() to correctly adjust the timeout after being woken up.
+ */
+ACPI_STATUS
+AcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout)
+{
+#ifndef ACPI_NO_SEMAPHORES
+ struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
+ ACPI_STATUS result;
+ int rv, tmo;
+ struct timeval timeouttv, currenttv, timelefttv;
+ AS_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (as == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ if (cold)
+ return_ACPI_STATUS(AE_OK);
+
+#if 0
+ if (as->as_units < Units && as->as_timeouts > 10) {
+ printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
+ AS_LOCK(as);
+ as->as_units = as->as_maxunits;
+ if (as->as_pendings)
+ as->as_resetting = 1;
+ as->as_timeouts = 0;
+ wakeup(as);
+ AS_UNLOCK(as);
+ return_ACPI_STATUS(AE_TIME);
+ }
+
+ if (as->as_resetting) {
+ return_ACPI_STATUS(AE_TIME);
+ }
+#endif
+
+ /* a timeout of ACPI_WAIT_FOREVER means "forever" */
+ if (Timeout == ACPI_WAIT_FOREVER) {
+ tmo = 0;
+ timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */
+ timeouttv.tv_usec = 0;
+ } else {
+ /* compute timeout using microseconds per tick */
+ tmo = (Timeout * 1000) / (1000000 / hz);
+ if (tmo <= 0)
+ tmo = 1;
+ timeouttv.tv_sec = Timeout / 1000;
+ timeouttv.tv_usec = (Timeout % 1000) * 1000;
+ }
+
+ /* calculate timeout value in timeval */
+ getmicrotime(&currenttv);
+ timevaladd(&timeouttv, &currenttv);
+
+ AS_LOCK(as);
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "get %d units from semaphore %p (has %d), timeout %d\n",
+ Units, as, as->as_units, Timeout));
+ for (;;) {
+ if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
+ result = AE_OK;
+ break;
+ }
+ if (as->as_units >= Units) {
+ as->as_units -= Units;
+ result = AE_OK;
+ break;
+ }
+
+ /* limit number of pending treads */
+ if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
+ result = AE_TIME;
+ break;
+ }
+
+ /* if timeout values of zero is specified, return immediately */
+ if (Timeout == 0) {
+ result = AE_TIME;
+ break;
+ }
+
+#if __FreeBSD_version >= 500000
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
+ as, &as->as_mtx, PCATCH, tmo));
+#endif
+
+ as->as_pendings++;
+
+ if (acpi_semaphore_debug) {
+ printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
+ __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
+ }
+
+ rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
+
+ as->as_pendings--;
+
+#if 0
+ if (as->as_resetting) {
+ /* semaphore reset, return immediately */
+ if (as->as_pendings == 0) {
+ as->as_resetting = 0;
+ }
+ result = AE_TIME;
+ break;
+ }
+#endif
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
+ if (rv == EWOULDBLOCK) {
+ result = AE_TIME;
+ break;
+ }
+
+ /* check if we already awaited enough */
+ timelefttv = timeouttv;
+ getmicrotime(&currenttv);
+ timevalsub(&timelefttv, &currenttv);
+ if (timelefttv.tv_sec < 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as));
+ result = AE_TIME;
+ break;
+ }
+
+ /* adjust timeout for the next sleep */
+ tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz);
+ if (tmo <= 0)
+ tmo = 1;
+
+ if (acpi_semaphore_debug) {
+ printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n",
+ __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId());
+ }
+ }
+
+ if (acpi_semaphore_debug) {
+ if (result == AE_TIME && Timeout > 0) {
+ printf("%s: Timeout %d, pending %d, semaphore %p\n",
+ __func__, Timeout, as->as_pendings, as);
+ }
+ if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
+ printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n",
+ __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
+ }
+ }
+
+ if (result == AE_TIME) {
+ as->as_timeouts++;
+ } else {
+ as->as_timeouts = 0;
+ }
+
+ AS_UNLOCK(as);
+
+ return_ACPI_STATUS(result);
+#else
+ return(AE_OK);
+#endif
+}
+
+ACPI_STATUS
+AcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
+{
+#ifndef ACPI_NO_SEMAPHORES
+ struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
+ AS_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (as == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ AS_LOCK(as);
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "return %d units to semaphore %p (has %d)\n",
+ Units, as, as->as_units));
+ if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
+ as->as_units += Units;
+ if (as->as_units > as->as_maxunits)
+ as->as_units = as->as_maxunits;
+ }
+
+ if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
+ printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
+ __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
+ }
+
+ wakeup(as);
+ AS_UNLOCK(as);
+ return_ACPI_STATUS(AE_OK);
+#else
+ return(AE_OK);
+#endif
+}
diff --git a/sys/dev/acpica/Osd/OsdTable.c b/sys/dev/acpica/Osd/OsdTable.c
new file mode 100644
index 0000000..f24c5ea
--- /dev/null
+++ b/sys/dev/acpica/Osd/OsdTable.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 2002 Mitsaru Iwasaki
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * ACPI Table interfaces
+ */
+
+#include "acpi.h"
+
+#include <sys/kernel.h>
+#include <sys/linker.h>
+
+#undef _COMPONENT
+#define _COMPONENT ACPI_TABLES
+
+static char acpi_os_name[128];
+
+ACPI_STATUS
+AcpiOsPredefinedOverride (
+ const ACPI_PREDEFINED_NAMES *InitVal,
+ ACPI_STRING *NewVal)
+{
+ if (InitVal == NULL || NewVal == NULL)
+ return(AE_BAD_PARAMETER);
+
+ *NewVal = NULL;
+ if (strncmp(InitVal->Name, "_OS_", 4) == 0 &&
+ getenv_string("hw.acpi.os_name", acpi_os_name, sizeof(acpi_os_name))) {
+ printf("ACPI: Overriding _OS definition with \"%s\"\n", acpi_os_name);
+ *NewVal = acpi_os_name;
+ }
+
+ return(AE_OK);
+}
+
+ACPI_STATUS
+AcpiOsTableOverride (
+ ACPI_TABLE_HEADER *ExistingTable,
+ ACPI_TABLE_HEADER **NewTable)
+{
+ caddr_t acpi_dsdt, p;
+
+ if (ExistingTable == NULL || NewTable == NULL)
+ {
+ return(AE_BAD_PARAMETER);
+ }
+
+ (*NewTable) = NULL;
+
+ if (strncmp(ExistingTable->Signature, "DSDT", 4) != 0)
+ {
+ return(AE_OK);
+ }
+
+ if ((acpi_dsdt = preload_search_by_type("acpi_dsdt")) == NULL)
+ {
+ return(AE_OK);
+ }
+
+ if ((p = preload_search_info(acpi_dsdt, MODINFO_ADDR)) == NULL)
+ {
+ return(AE_OK);
+ }
+
+ (*NewTable) = *(void **)p;
+
+ printf("ACPI: DSDT was overridden.\n");
+
+ return(AE_OK);
+}
+
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
new file mode 100644
index 0000000..c5f920e
--- /dev/null
+++ b/sys/dev/acpica/acpi.c
@@ -0,0 +1,2191 @@
+/*-
+ * Copyright (c) 2000 Takanori Watanabe <takawata@jp.freebsd.org>
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/ioccom.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/ctype.h>
+#include <sys/linker.h>
+#include <sys/power.h>
+
+#include <machine/clock.h>
+#include <machine/resource.h>
+
+#include <isa/isavar.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpica_support.h>
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices");
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("ACPI")
+
+/*
+ * Character device
+ */
+
+static d_open_t acpiopen;
+static d_close_t acpiclose;
+static d_ioctl_t acpiioctl;
+
+#define CDEV_MAJOR 152
+static struct cdevsw acpi_cdevsw = {
+ .d_open = acpiopen,
+ .d_close = acpiclose,
+ .d_ioctl = acpiioctl,
+ .d_name = "acpi",
+ .d_maj = CDEV_MAJOR,
+};
+
+static const char* sleep_state_names[] = {
+ "S0", "S1", "S2", "S3", "S4", "S5", "NONE"};
+
+/* this has to be static, as the softc is gone when we need it */
+static int acpi_off_state = ACPI_STATE_S5;
+
+#if __FreeBSD_version >= 500000
+struct mtx acpi_mutex;
+#endif
+
+static int acpi_modevent(struct module *mod, int event, void *junk);
+static void acpi_identify(driver_t *driver, device_t parent);
+static int acpi_probe(device_t dev);
+static int acpi_attach(device_t dev);
+static device_t acpi_add_child(device_t bus, int order, const char *name, int unit);
+static int acpi_print_child(device_t bus, device_t child);
+static int acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result);
+static int acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value);
+static int acpi_set_resource(device_t dev, device_t child, int type, int rid, u_long start,
+ u_long count);
+static int acpi_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp,
+ u_long *countp);
+static struct resource *acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags);
+static int acpi_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r);
+static u_int32_t acpi_isa_get_logicalid(device_t dev);
+static u_int32_t acpi_isa_get_compatid(device_t dev);
+static int acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids);
+
+static void acpi_probe_children(device_t bus);
+static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status);
+
+static void acpi_shutdown_pre_sync(void *arg, int howto);
+static void acpi_shutdown_final(void *arg, int howto);
+
+static void acpi_enable_fixed_events(struct acpi_softc *sc);
+
+static void acpi_system_eventhandler_sleep(void *arg, int state);
+static void acpi_system_eventhandler_wakeup(void *arg, int state);
+static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
+
+static int acpi_pm_func(u_long cmd, void *arg, ...);
+
+static device_method_t acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, acpi_identify),
+ DEVMETHOD(device_probe, acpi_probe),
+ DEVMETHOD(device_attach, acpi_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, acpi_add_child),
+ DEVMETHOD(bus_print_child, acpi_print_child),
+ DEVMETHOD(bus_read_ivar, acpi_read_ivar),
+ DEVMETHOD(bus_write_ivar, acpi_write_ivar),
+ DEVMETHOD(bus_set_resource, acpi_set_resource),
+ DEVMETHOD(bus_get_resource, acpi_get_resource),
+ DEVMETHOD(bus_alloc_resource, acpi_alloc_resource),
+ DEVMETHOD(bus_release_resource, acpi_release_resource),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* ISA emulation */
+ DEVMETHOD(isa_pnp_probe, acpi_isa_pnp_probe),
+
+ {0, 0}
+};
+
+static driver_t acpi_driver = {
+ "acpi",
+ acpi_methods,
+ sizeof(struct acpi_softc),
+};
+
+static devclass_t acpi_devclass;
+DRIVER_MODULE(acpi, nexus, acpi_driver, acpi_devclass, acpi_modevent, 0);
+MODULE_VERSION(acpi, 100);
+
+SYSCTL_INT(_debug, OID_AUTO, acpi_debug_layer, CTLFLAG_RW, &AcpiDbgLayer, 0, "");
+SYSCTL_INT(_debug, OID_AUTO, acpi_debug_level, CTLFLAG_RW, &AcpiDbgLevel, 0, "");
+static int acpi_ca_version = ACPI_CA_VERSION;
+SYSCTL_INT(_debug, OID_AUTO, acpi_ca_version, CTLFLAG_RD, &acpi_ca_version, 0, "");
+
+/*
+ * ACPI can only be loaded as a module by the loader; activating it after
+ * system bootstrap time is not useful, and can be fatal to the system.
+ * It also cannot be unloaded, since the entire system bus heirarchy hangs off it.
+ */
+static int
+acpi_modevent(struct module *mod, int event, void *junk)
+{
+ switch(event) {
+ case MOD_LOAD:
+ if (!cold) {
+ printf("The ACPI driver cannot be loaded after boot.\n");
+ return(EPERM);
+ }
+ break;
+ case MOD_UNLOAD:
+ if (!cold && power_pm_get_type() == POWER_PM_TYPE_ACPI)
+ return(EBUSY);
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+
+/*
+ * Detect ACPI, perform early initialisation
+ */
+static void
+acpi_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+ int error;
+#ifdef ACPI_DEBUGGER
+ char *debugpoint;
+#endif
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (!cold)
+ return_VOID;
+
+ /*
+ * Check that we haven't been disabled with a hint.
+ */
+ if (!resource_int_value("acpi", 0, "disabled", &error) &&
+ (error != 0))
+ return_VOID;
+
+ /*
+ * Make sure we're not being doubly invoked.
+ */
+ if (device_find_child(parent, "acpi", 0) != NULL)
+ return_VOID;
+
+#if __FreeBSD_version >= 500000
+ /* initialise the ACPI mutex */
+ mtx_init(&acpi_mutex, "ACPI global lock", NULL, MTX_DEF);
+#endif
+
+ /*
+ * Start up the ACPI CA subsystem.
+ */
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "init"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+ if (ACPI_FAILURE(error = AcpiInitializeSubsystem())) {
+ printf("ACPI: initialisation failed: %s\n", AcpiFormatException(error));
+ return_VOID;
+ }
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "tables"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+
+ if (ACPI_FAILURE(error = AcpiLoadTables())) {
+ printf("ACPI: table load failed: %s\n", AcpiFormatException(error));
+ return_VOID;
+ }
+
+ /*
+ * Attach the actual ACPI device.
+ */
+ if ((child = BUS_ADD_CHILD(parent, 0, "acpi", 0)) == NULL) {
+ device_printf(parent, "ACPI: could not attach\n");
+ return_VOID;
+ }
+}
+
+/*
+ * Fetch some descriptive data from ACPI to put in our attach message
+ */
+static int
+acpi_probe(device_t dev)
+{
+ ACPI_TABLE_HEADER th;
+ char buf[20];
+ ACPI_STATUS status;
+ int error;
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (power_pm_get_type() != POWER_PM_TYPE_NONE &&
+ power_pm_get_type() != POWER_PM_TYPE_ACPI) {
+ device_printf(dev, "Other PM system enabled.\n");
+ return_VALUE(ENXIO);
+ }
+
+ ACPI_LOCK;
+
+ if (ACPI_FAILURE(status = AcpiGetTableHeader(ACPI_TABLE_XSDT, 1, &th))) {
+ device_printf(dev, "couldn't get XSDT header: %s\n", AcpiFormatException(status));
+ error = ENXIO;
+ } else {
+ sprintf(buf, "%.6s %.8s", th.OemId, th.OemTableId);
+ device_set_desc_copy(dev, buf);
+ error = 0;
+ }
+ ACPI_UNLOCK;
+ return_VALUE(error);
+}
+
+static int
+acpi_attach(device_t dev)
+{
+ struct acpi_softc *sc;
+ ACPI_STATUS status;
+ int error;
+ UINT32 flags;
+ char *env;
+#ifdef ACPI_DEBUGGER
+ char *debugpoint;
+#endif
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_LOCK;
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(*sc));
+ sc->acpi_dev = dev;
+
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "spaces"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+
+ /*
+ * Install the default address space handlers.
+ */
+ error = ENXIO;
+ if (ACPI_FAILURE(status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_SYSTEM_MEMORY,
+ ACPI_DEFAULT_HANDLER,
+ NULL, NULL))) {
+ device_printf(dev, "could not initialise SystemMemory handler: %s\n", AcpiFormatException(status));
+ goto out;
+ }
+ if (ACPI_FAILURE(status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ ACPI_DEFAULT_HANDLER,
+ NULL, NULL))) {
+ device_printf(dev, "could not initialise SystemIO handler: %s\n", AcpiFormatException(status));
+ goto out;
+ }
+ if (ACPI_FAILURE(status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ ACPI_DEFAULT_HANDLER,
+ NULL, NULL))) {
+ device_printf(dev, "could not initialise PciConfig handler: %s\n", AcpiFormatException(status));
+ goto out;
+ }
+
+ /*
+ * Bring ACPI fully online.
+ *
+ * Note that some systems (specifically, those with namespace evaluation issues
+ * that require the avoidance of parts of the namespace) must avoid running _INI
+ * and _STA on everything, as well as dodging the final object init pass.
+ *
+ * For these devices, we set ACPI_NO_DEVICE_INIT and ACPI_NO_OBJECT_INIT).
+ *
+ * XXX We should arrange for the object init pass after we have attached all our
+ * child devices, but on many systems it works here.
+ */
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "enable"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+ flags = 0;
+ if (testenv("debug.acpi.avoid"))
+ flags = ACPI_NO_DEVICE_INIT | ACPI_NO_OBJECT_INIT;
+ if (ACPI_FAILURE(status = AcpiEnableSubsystem(flags))) {
+ device_printf(dev, "could not enable ACPI: %s\n", AcpiFormatException(status));
+ goto out;
+ }
+
+ if (ACPI_FAILURE(status = AcpiInitializeObjects(flags))) {
+ device_printf(dev, "could not initialize ACPI objects: %s\n", AcpiFormatException(status));
+ goto out;
+ }
+
+ /*
+ * Setup our sysctl tree.
+ *
+ * XXX: This doesn't check to make sure that none of these fail.
+ */
+ sysctl_ctx_init(&sc->acpi_sysctl_ctx);
+ sc->acpi_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+ device_get_name(dev), CTLFLAG_RD, 0, "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "supported_sleep_state", CTLTYPE_STRING | CTLFLAG_RD,
+ 0, 0, acpi_supported_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "power_button_state", CTLTYPE_STRING | CTLFLAG_RW,
+ &sc->acpi_power_button_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "sleep_button_state", CTLTYPE_STRING | CTLFLAG_RW,
+ &sc->acpi_sleep_button_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "lid_switch_state", CTLTYPE_STRING | CTLFLAG_RW,
+ &sc->acpi_lid_switch_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "standby_state", CTLTYPE_STRING | CTLFLAG_RW,
+ &sc->acpi_standby_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "suspend_state", CTLTYPE_STRING | CTLFLAG_RW,
+ &sc->acpi_suspend_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "sleep_delay", CTLFLAG_RD | CTLFLAG_RW,
+ &sc->acpi_sleep_delay, 0, "sleep delay");
+ SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "s4bios", CTLFLAG_RD | CTLFLAG_RW,
+ &sc->acpi_s4bios, 0, "S4BIOS mode");
+ SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "verbose", CTLFLAG_RD | CTLFLAG_RW,
+ &sc->acpi_verbose, 0, "verbose mode");
+ SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "disable_on_poweroff", CTLFLAG_RD | CTLFLAG_RW,
+ &sc->acpi_disable_on_poweroff, 0, "ACPI subsystem disable on poweroff");
+ sc->acpi_disable_on_poweroff = 1;
+ sc->acpi_sleep_delay = 0;
+ sc->acpi_s4bios = 1;
+ if (bootverbose)
+ sc->acpi_verbose = 1;
+ if ((env = getenv("hw.acpi.verbose")) && strcmp(env, "0")) {
+ sc->acpi_verbose = 1;
+ freeenv(env);
+ }
+
+ /*
+ * Dispatch the default sleep state to devices.
+ * TBD: should be configured from userland policy manager.
+ */
+ sc->acpi_power_button_sx = ACPI_POWER_BUTTON_DEFAULT_SX;
+ sc->acpi_sleep_button_sx = ACPI_SLEEP_BUTTON_DEFAULT_SX;
+ sc->acpi_lid_switch_sx = ACPI_LID_SWITCH_DEFAULT_SX;
+ sc->acpi_standby_sx = ACPI_STATE_S1;
+ sc->acpi_suspend_sx = ACPI_STATE_S3;
+
+ acpi_enable_fixed_events(sc);
+
+ /*
+ * Scan the namespace and attach/initialise children.
+ */
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "probe"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+
+ /*
+ * Register our shutdown handlers
+ */
+ EVENTHANDLER_REGISTER(shutdown_pre_sync, acpi_shutdown_pre_sync, sc, SHUTDOWN_PRI_LAST);
+ EVENTHANDLER_REGISTER(shutdown_final, acpi_shutdown_final, sc, SHUTDOWN_PRI_LAST);
+
+ /*
+ * Register our acpi event handlers.
+ * XXX should be configurable eg. via userland policy manager.
+ */
+ EVENTHANDLER_REGISTER(acpi_sleep_event, acpi_system_eventhandler_sleep, sc, ACPI_EVENT_PRI_LAST);
+ EVENTHANDLER_REGISTER(acpi_wakeup_event, acpi_system_eventhandler_wakeup, sc, ACPI_EVENT_PRI_LAST);
+
+ /*
+ * Flag our initial states.
+ */
+ sc->acpi_enabled = 1;
+ sc->acpi_sstate = ACPI_STATE_S0;
+ sc->acpi_sleep_disabled = 0;
+
+ /*
+ * Create the control device
+ */
+ sc->acpi_dev_t = make_dev(&acpi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644,
+ "acpi");
+ sc->acpi_dev_t->si_drv1 = sc;
+
+#ifdef ACPI_DEBUGGER
+ debugpoint = getenv("debug.acpi.debugger");
+ if (debugpoint) {
+ if (!strcmp(debugpoint, "running"))
+ acpi_EnterDebugger();
+ freeenv(debugpoint);
+ }
+#endif
+
+#ifdef ACPI_USE_THREADS
+ if ((error = acpi_task_thread_init())) {
+ goto out;
+ }
+#endif
+
+ if ((error = acpi_machdep_init(dev))) {
+ goto out;
+ }
+
+ /* Register ACPI again to pass the correct argument of pm_func. */
+ power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, sc);
+
+ if (!acpi_disabled("bus"))
+ acpi_probe_children(dev);
+
+ error = 0;
+
+ out:
+ ACPI_UNLOCK;
+ return_VALUE(error);
+}
+
+/*
+ * Handle a new device being added
+ */
+static device_t
+acpi_add_child(device_t bus, int order, const char *name, int unit)
+{
+ struct acpi_device *ad;
+ device_t child;
+
+ if ((ad = malloc(sizeof(*ad), M_ACPIDEV, M_NOWAIT)) == NULL)
+ return(NULL);
+ bzero(ad, sizeof(*ad));
+
+ resource_list_init(&ad->ad_rl);
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child != NULL)
+ device_set_ivars(child, ad);
+ return(child);
+}
+
+static int
+acpi_print_child(device_t bus, device_t child)
+{
+ struct acpi_device *adev = device_get_ivars(child);
+ struct resource_list *rl = &adev->ad_rl;
+ int retval = 0;
+
+ retval += bus_print_child_header(bus, child);
+ retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx");
+ retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx");
+ retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
+ retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%ld");
+ retval += bus_print_child_footer(bus, child);
+
+ return(retval);
+}
+
+
+/*
+ * Handle per-device ivars
+ */
+static int
+acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
+{
+ struct acpi_device *ad;
+
+ if ((ad = device_get_ivars(child)) == NULL) {
+ printf("device has no ivars\n");
+ return(ENOENT);
+ }
+
+ switch(index) {
+ /* ACPI ivars */
+ case ACPI_IVAR_HANDLE:
+ *(ACPI_HANDLE *)result = ad->ad_handle;
+ break;
+ case ACPI_IVAR_MAGIC:
+ *(int *)result = ad->ad_magic;
+ break;
+ case ACPI_IVAR_PRIVATE:
+ *(void **)result = ad->ad_private;
+ break;
+
+ /* ISA compatibility */
+ case ISA_IVAR_VENDORID:
+ case ISA_IVAR_SERIAL:
+ case ISA_IVAR_COMPATID:
+ *(int *)result = -1;
+ break;
+
+ case ISA_IVAR_LOGICALID:
+ *(int *)result = acpi_isa_get_logicalid(child);
+ break;
+
+ default:
+ return(ENOENT);
+ }
+ return(0);
+}
+
+static int
+acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
+{
+ struct acpi_device *ad;
+
+ if ((ad = device_get_ivars(child)) == NULL) {
+ printf("device has no ivars\n");
+ return(ENOENT);
+ }
+
+ switch(index) {
+ /* ACPI ivars */
+ case ACPI_IVAR_HANDLE:
+ ad->ad_handle = (ACPI_HANDLE)value;
+ break;
+ case ACPI_IVAR_MAGIC:
+ ad->ad_magic = (int )value;
+ break;
+ case ACPI_IVAR_PRIVATE:
+ ad->ad_private = (void *)value;
+ break;
+
+ default:
+ panic("bad ivar write request (%d)", index);
+ return(ENOENT);
+ }
+ return(0);
+}
+
+/*
+ * Handle child resource allocation/removal
+ */
+static int
+acpi_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count)
+{
+ struct acpi_device *ad = device_get_ivars(child);
+ struct resource_list *rl = &ad->ad_rl;
+
+ resource_list_add(rl, type, rid, start, start + count -1, count);
+
+ return(0);
+}
+
+static int
+acpi_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp)
+{
+ struct acpi_device *ad = device_get_ivars(child);
+ struct resource_list *rl = &ad->ad_rl;
+ struct resource_list_entry *rle;
+
+ rle = resource_list_find(rl, type, rid);
+ if (!rle)
+ return(ENOENT);
+
+ if (startp)
+ *startp = rle->start;
+ if (countp)
+ *countp = rle->count;
+
+ return(0);
+}
+
+static struct resource *
+acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct acpi_device *ad = device_get_ivars(child);
+ struct resource_list *rl = &ad->ad_rl;
+
+ return(resource_list_alloc(rl, bus, child, type, rid, start, end, count, flags));
+}
+
+static int
+acpi_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r)
+{
+ struct acpi_device *ad = device_get_ivars(child);
+ struct resource_list *rl = &ad->ad_rl;
+
+ return(resource_list_release(rl, bus, child, type, rid, r));
+}
+
+/*
+ * Handle ISA-like devices probing for a PnP ID to match.
+ */
+#define PNP_EISAID(s) \
+ ((((s[0] - '@') & 0x1f) << 2) \
+ | (((s[1] - '@') & 0x18) >> 3) \
+ | (((s[1] - '@') & 0x07) << 13) \
+ | (((s[2] - '@') & 0x1f) << 8) \
+ | (PNP_HEXTONUM(s[4]) << 16) \
+ | (PNP_HEXTONUM(s[3]) << 20) \
+ | (PNP_HEXTONUM(s[6]) << 24) \
+ | (PNP_HEXTONUM(s[5]) << 28))
+
+static u_int32_t
+acpi_isa_get_logicalid(device_t dev)
+{
+ ACPI_HANDLE h;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+ u_int32_t pnpid;
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ pnpid = 0;
+ ACPI_LOCK;
+
+ /* fetch and validate the HID */
+ if ((h = acpi_get_handle(dev)) == NULL)
+ goto out;
+ if (ACPI_FAILURE(error = AcpiGetObjectInfo(h, &devinfo)))
+ goto out;
+ if (!(devinfo.Valid & ACPI_VALID_HID))
+ goto out;
+
+ pnpid = PNP_EISAID(devinfo.HardwareId);
+out:
+ ACPI_UNLOCK;
+ return_VALUE(pnpid);
+}
+
+static u_int32_t
+acpi_isa_get_compatid(device_t dev)
+{
+ ACPI_HANDLE h;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+ u_int32_t pnpid;
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ pnpid = 0;
+ ACPI_LOCK;
+
+ /* fetch and validate the HID */
+ if ((h = acpi_get_handle(dev)) == NULL)
+ goto out;
+ if (ACPI_FAILURE(error = AcpiGetObjectInfo(h, &devinfo)))
+ goto out;
+ if (ACPI_FAILURE(error = acpi_EvaluateInteger(h, "_CID", &pnpid)))
+ goto out;
+
+out:
+ ACPI_UNLOCK;
+ return_VALUE(pnpid);
+}
+
+
+static int
+acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids)
+{
+ int result;
+ u_int32_t lid, cid;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * ISA-style drivers attached to ACPI may persist and
+ * probe manually if we return ENOENT. We never want
+ * that to happen, so don't ever return it.
+ */
+ result = ENXIO;
+
+ /* scan the supplied IDs for a match */
+ lid = acpi_isa_get_logicalid(child);
+ cid = acpi_isa_get_compatid(child);
+ while (ids && ids->ip_id) {
+ if (lid == ids->ip_id || cid == ids->ip_id) {
+ result = 0;
+ goto out;
+ }
+ ids++;
+ }
+ out:
+ return_VALUE(result);
+}
+
+/*
+ * Scan relevant portions of the ACPI namespace and attach child devices.
+ *
+ * Note that we only expect to find devices in the \_PR_, \_TZ_, \_SI_ and \_SB_ scopes,
+ * and \_PR_ and \_TZ_ become obsolete in the ACPI 2.0 spec.
+ */
+static void
+acpi_probe_children(device_t bus)
+{
+ ACPI_HANDLE parent;
+ static char *scopes[] = {"\\_PR_", "\\_TZ_", "\\_SI", "\\_SB_", NULL};
+ int i;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_ASSERTLOCK;
+
+ /*
+ * Create any static children by calling device identify methods.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "device identify routines\n"));
+ bus_generic_probe(bus);
+
+ /*
+ * Scan the namespace and insert placeholders for all the devices that
+ * we find.
+ *
+ * Note that we use AcpiWalkNamespace rather than AcpiGetDevices because
+ * we want to create nodes for all devices, not just those that are currently
+ * present. (This assumes that we don't want to create/remove devices as they
+ * appear, which might be smarter.)
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "namespace scan\n"));
+ for (i = 0; scopes[i] != NULL; i++)
+ if (ACPI_SUCCESS(AcpiGetHandle(ACPI_ROOT_OBJECT, scopes[i], &parent)))
+ AcpiWalkNamespace(ACPI_TYPE_ANY, parent, 100, acpi_probe_child, bus, NULL);
+
+ /*
+ * Scan all of the child devices we have created and let them probe/attach.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "first bus_generic_attach\n"));
+ bus_generic_attach(bus);
+
+ /*
+ * Some of these children may have attached others as part of their attach
+ * process (eg. the root PCI bus driver), so rescan.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "second bus_generic_attach\n"));
+ bus_generic_attach(bus);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "done attaching children\n"));
+ return_VOID;
+}
+
+/*
+ * Evaluate a child device and determine whether we might attach a device to
+ * it.
+ */
+static ACPI_STATUS
+acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
+{
+ ACPI_OBJECT_TYPE type;
+ device_t child, bus = (device_t)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Skip this device if we think we'll have trouble with it.
+ */
+ if (acpi_avoid(handle))
+ return_ACPI_STATUS(AE_OK);
+
+ if (ACPI_SUCCESS(AcpiGetType(handle, &type))) {
+ switch(type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_POWER:
+ if (acpi_disabled("children"))
+ break;
+ /*
+ * Create a placeholder device for this node. Sort the placeholder
+ * so that the probe/attach passes will run breadth-first.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "scanning '%s'\n", acpi_name(handle)));
+ child = BUS_ADD_CHILD(bus, level * 10, NULL, -1);
+ if (child == NULL)
+ break;
+ acpi_set_handle(child, handle);
+
+ /*
+ * Check that the device is present. If it's not present,
+ * leave it disabled (so that we have a device_t attached to
+ * the handle, but we don't probe it).
+ */
+ if ((type == ACPI_TYPE_DEVICE) && (!acpi_DeviceIsPresent(child))) {
+ device_disable(child);
+ break;
+ }
+
+ /*
+ * Get the device's resource settings and attach them.
+ * Note that if the device has _PRS but no _CRS, we need
+ * to decide when it's appropriate to try to configure the
+ * device. Ignore the return value here; it's OK for the
+ * device not to have any resources.
+ */
+ acpi_parse_resources(child, handle, &acpi_res_parse_set);
+
+ /* if we're debugging, probe/attach now rather than later */
+ ACPI_DEBUG_EXEC(device_probe_and_attach(child));
+ break;
+ }
+ }
+ return_ACPI_STATUS(AE_OK);
+}
+
+static void
+acpi_shutdown_pre_sync(void *arg, int howto)
+{
+
+ struct acpi_softc *sc = arg;
+
+ ACPI_ASSERTLOCK;
+
+ /*
+ * Disable all ACPI events before soft off, otherwise the system
+ * will be turned on again on some laptops.
+ *
+ * XXX this should probably be restricted to masking some events just
+ * before powering down, since we may still need ACPI during the
+ * shutdown process.
+ */
+ if (sc->acpi_disable_on_poweroff)
+ acpi_Disable(sc);
+}
+
+static void
+acpi_shutdown_final(void *arg, int howto)
+{
+ ACPI_STATUS status;
+
+ ACPI_ASSERTLOCK;
+
+ if (howto & RB_POWEROFF) {
+ printf("Power system off using ACPI...\n");
+ if (ACPI_FAILURE(status = AcpiEnterSleepStatePrep(acpi_off_state))) {
+ printf("AcpiEnterSleepStatePrep failed - %s\n",
+ AcpiFormatException(status));
+ return;
+ }
+ if (ACPI_FAILURE(status = AcpiEnterSleepState(acpi_off_state))) {
+ printf("ACPI power-off failed - %s\n", AcpiFormatException(status));
+ } else {
+ DELAY(1000000);
+ printf("ACPI power-off failed - timeout\n");
+ }
+ } else {
+ printf("Terminate ACPI\n");
+ AcpiTerminate();
+ }
+}
+
+static void
+acpi_enable_fixed_events(struct acpi_softc *sc)
+{
+ static int first_time = 1;
+#define MSGFORMAT "%s button is handled as a fixed feature programming model.\n"
+
+ ACPI_ASSERTLOCK;
+
+ /* Enable and clear fixed events and install handlers. */
+ if ((AcpiGbl_FADT != NULL) && (AcpiGbl_FADT->PwrButton == 0)) {
+ AcpiEnableEvent(ACPI_EVENT_POWER_BUTTON, ACPI_EVENT_FIXED, 0);
+ AcpiClearEvent(ACPI_EVENT_POWER_BUTTON, ACPI_EVENT_FIXED);
+ AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
+ acpi_eventhandler_power_button_for_sleep, sc);
+ if (first_time) {
+ device_printf(sc->acpi_dev, MSGFORMAT, "power");
+ }
+ }
+ if ((AcpiGbl_FADT != NULL) && (AcpiGbl_FADT->SleepButton == 0)) {
+ AcpiEnableEvent(ACPI_EVENT_SLEEP_BUTTON, ACPI_EVENT_FIXED, 0);
+ AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON, ACPI_EVENT_FIXED);
+ AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON,
+ acpi_eventhandler_sleep_button_for_sleep, sc);
+ if (first_time) {
+ device_printf(sc->acpi_dev, MSGFORMAT, "sleep");
+ }
+ }
+
+ first_time = 0;
+}
+
+/*
+ * Returns true if the device is actually present and should
+ * be attached to. This requires the present, enabled, UI-visible
+ * and diagnostics-passed bits to be set.
+ */
+BOOLEAN
+acpi_DeviceIsPresent(device_t dev)
+{
+ ACPI_HANDLE h;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+
+ ACPI_ASSERTLOCK;
+
+ if ((h = acpi_get_handle(dev)) == NULL)
+ return(FALSE);
+ if (ACPI_FAILURE(error = AcpiGetObjectInfo(h, &devinfo)))
+ return(FALSE);
+ /* if no _STA method, must be present */
+ if (!(devinfo.Valid & ACPI_VALID_STA))
+ return(TRUE);
+ /* return true for 'present' and 'functioning' */
+ if ((devinfo.CurrentStatus & 0x9) == 0x9)
+ return(TRUE);
+ return(FALSE);
+}
+
+/*
+ * Returns true if the battery is actually present and inserted.
+ */
+BOOLEAN
+acpi_BatteryIsPresent(device_t dev)
+{
+ ACPI_HANDLE h;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+
+ ACPI_ASSERTLOCK;
+
+ if ((h = acpi_get_handle(dev)) == NULL)
+ return(FALSE);
+ if (ACPI_FAILURE(error = AcpiGetObjectInfo(h, &devinfo)))
+ return(FALSE);
+ /* if no _STA method, must be present */
+ if (!(devinfo.Valid & ACPI_VALID_STA))
+ return(TRUE);
+ /* return true for 'present' and 'functioning' */
+ if ((devinfo.CurrentStatus & 0x19) == 0x19)
+ return(TRUE);
+ return(FALSE);
+}
+
+/*
+ * Match a HID string against a device
+ */
+BOOLEAN
+acpi_MatchHid(device_t dev, char *hid)
+{
+ ACPI_HANDLE h;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+ int cid;
+
+ ACPI_ASSERTLOCK;
+
+ if (hid == NULL)
+ return(FALSE);
+ if ((h = acpi_get_handle(dev)) == NULL)
+ return(FALSE);
+ if (ACPI_FAILURE(error = AcpiGetObjectInfo(h, &devinfo)))
+ return(FALSE);
+ if ((devinfo.Valid & ACPI_VALID_HID) && !strcmp(hid, devinfo.HardwareId))
+ return(TRUE);
+ if (ACPI_FAILURE(error = acpi_EvaluateInteger(h, "_CID", &cid)))
+ return(FALSE);
+ if (cid == PNP_EISAID(hid))
+ return(TRUE);
+ return(FALSE);
+}
+
+/*
+ * Return the handle of a named object within our scope, ie. that of (parent)
+ * or one if its parents.
+ */
+ACPI_STATUS
+acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result)
+{
+ ACPI_HANDLE r;
+ ACPI_STATUS status;
+
+ ACPI_ASSERTLOCK;
+
+ /* walk back up the tree to the root */
+ for (;;) {
+ if (ACPI_SUCCESS(status = AcpiGetHandle(parent, path, &r))) {
+ *result = r;
+ return(AE_OK);
+ }
+ if (status != AE_NOT_FOUND)
+ return(AE_OK);
+ if (ACPI_FAILURE(AcpiGetParent(parent, &r)))
+ return(AE_NOT_FOUND);
+ parent = r;
+ }
+}
+
+/*
+ * Allocate a buffer with a preset data size.
+ */
+ACPI_BUFFER *
+acpi_AllocBuffer(int size)
+{
+ ACPI_BUFFER *buf;
+
+ if ((buf = malloc(size + sizeof(*buf), M_ACPIDEV, M_NOWAIT)) == NULL)
+ return(NULL);
+ buf->Length = size;
+ buf->Pointer = (void *)(buf + 1);
+ return(buf);
+}
+
+/*
+ * Evaluate a path that should return an integer.
+ */
+ACPI_STATUS
+acpi_EvaluateInteger(ACPI_HANDLE handle, char *path, int *number)
+{
+ ACPI_STATUS error;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT param;
+
+ ACPI_ASSERTLOCK;
+
+ if (handle == NULL)
+ handle = ACPI_ROOT_OBJECT;
+
+ /*
+ * Assume that what we've been pointed at is an Integer object, or
+ * a method that will return an Integer.
+ */
+ buf.Pointer = &param;
+ buf.Length = sizeof(param);
+ if (ACPI_SUCCESS(error = AcpiEvaluateObject(handle, path, NULL, &buf))) {
+ if (param.Type == ACPI_TYPE_INTEGER) {
+ *number = param.Integer.Value;
+ } else {
+ error = AE_TYPE;
+ }
+ }
+
+ /*
+ * In some applications, a method that's expected to return an Integer
+ * may instead return a Buffer (probably to simplify some internal
+ * arithmetic). We'll try to fetch whatever it is, and if it's a Buffer,
+ * convert it into an Integer as best we can.
+ *
+ * This is a hack.
+ */
+ if (error == AE_BUFFER_OVERFLOW) {
+ if ((buf.Pointer = AcpiOsAllocate(buf.Length)) == NULL) {
+ error = AE_NO_MEMORY;
+ } else {
+ if (ACPI_SUCCESS(error = AcpiEvaluateObject(handle, path, NULL, &buf))) {
+ error = acpi_ConvertBufferToInteger(&buf, number);
+ }
+ }
+ AcpiOsFree(buf.Pointer);
+ }
+ return(error);
+}
+
+ACPI_STATUS
+acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp, int *number)
+{
+ ACPI_OBJECT *p;
+ int i;
+
+ p = (ACPI_OBJECT *)bufp->Pointer;
+ if (p->Type == ACPI_TYPE_INTEGER) {
+ *number = p->Integer.Value;
+ return(AE_OK);
+ }
+ if (p->Type != ACPI_TYPE_BUFFER)
+ return(AE_TYPE);
+ if (p->Buffer.Length > sizeof(int))
+ return(AE_BAD_DATA);
+ *number = 0;
+ for (i = 0; i < p->Buffer.Length; i++)
+ *number += (*(p->Buffer.Pointer + i) << (i * 8));
+ return(AE_OK);
+}
+
+/*
+ * Iterate over the elements of an a package object, calling the supplied
+ * function for each element.
+ *
+ * XXX possible enhancement might be to abort traversal on error.
+ */
+ACPI_STATUS
+acpi_ForeachPackageObject(ACPI_OBJECT *pkg, void (* func)(ACPI_OBJECT *comp, void *arg), void *arg)
+{
+ ACPI_OBJECT *comp;
+ int i;
+
+ if ((pkg == NULL) || (pkg->Type != ACPI_TYPE_PACKAGE))
+ return(AE_BAD_PARAMETER);
+
+ /* iterate over components */
+ for (i = 0, comp = pkg->Package.Elements; i < pkg->Package.Count; i++, comp++)
+ func(comp, arg);
+
+ return(AE_OK);
+}
+
+/*
+ * Find the (index)th resource object in a set.
+ */
+ACPI_STATUS
+acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp)
+{
+ ACPI_RESOURCE *rp;
+ int i;
+
+ rp = (ACPI_RESOURCE *)buf->Pointer;
+ i = index;
+ while (i-- > 0) {
+ /* range check */
+ if (rp > (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + buf->Length))
+ return(AE_BAD_PARAMETER);
+ /* check for terminator */
+ if ((rp->Id == ACPI_RSTYPE_END_TAG) ||
+ (rp->Length == 0))
+ return(AE_NOT_FOUND);
+ rp = ACPI_RESOURCE_NEXT(rp);
+ }
+ if (resp != NULL)
+ *resp = rp;
+ return(AE_OK);
+}
+
+/*
+ * Append an ACPI_RESOURCE to an ACPI_BUFFER.
+ *
+ * Given a pointer to an ACPI_RESOURCE structure, expand the ACPI_BUFFER
+ * provided to contain it. If the ACPI_BUFFER is empty, allocate a sensible
+ * backing block. If the ACPI_RESOURCE is NULL, return an empty set of
+ * resources.
+ */
+#define ACPI_INITIAL_RESOURCE_BUFFER_SIZE 512
+
+ACPI_STATUS
+acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res)
+{
+ ACPI_RESOURCE *rp;
+ void *newp;
+
+ /*
+ * Initialise the buffer if necessary.
+ */
+ if (buf->Pointer == NULL) {
+ buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE;
+ if ((buf->Pointer = AcpiOsAllocate(buf->Length)) == NULL)
+ return(AE_NO_MEMORY);
+ rp = (ACPI_RESOURCE *)buf->Pointer;
+ rp->Id = ACPI_RSTYPE_END_TAG;
+ rp->Length = 0;
+ }
+ if (res == NULL)
+ return(AE_OK);
+
+ /*
+ * Scan the current buffer looking for the terminator.
+ * This will either find the terminator or hit the end
+ * of the buffer and return an error.
+ */
+ rp = (ACPI_RESOURCE *)buf->Pointer;
+ for (;;) {
+ /* range check, don't go outside the buffer */
+ if (rp >= (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + buf->Length))
+ return(AE_BAD_PARAMETER);
+ if ((rp->Id == ACPI_RSTYPE_END_TAG) ||
+ (rp->Length == 0)) {
+ break;
+ }
+ rp = ACPI_RESOURCE_NEXT(rp);
+ }
+
+ /*
+ * Check the size of the buffer and expand if required.
+ *
+ * Required size is:
+ * size of existing resources before terminator +
+ * size of new resource and header +
+ * size of terminator.
+ *
+ * Note that this loop should really only run once, unless
+ * for some reason we are stuffing a *really* huge resource.
+ */
+ while ((((u_int8_t *)rp - (u_int8_t *)buf->Pointer) +
+ res->Length + ACPI_RESOURCE_LENGTH_NO_DATA +
+ ACPI_RESOURCE_LENGTH) >= buf->Length) {
+ if ((newp = AcpiOsAllocate(buf->Length * 2)) == NULL)
+ return(AE_NO_MEMORY);
+ bcopy(buf->Pointer, newp, buf->Length);
+ rp = (ACPI_RESOURCE *)((u_int8_t *)newp +
+ ((u_int8_t *)rp - (u_int8_t *)buf->Pointer));
+ AcpiOsFree(buf->Pointer);
+ buf->Pointer = newp;
+ buf->Length += buf->Length;
+ }
+
+ /*
+ * Insert the new resource.
+ */
+ bcopy(res, rp, res->Length + ACPI_RESOURCE_LENGTH_NO_DATA);
+
+ /*
+ * And add the terminator.
+ */
+ rp = ACPI_RESOURCE_NEXT(rp);
+ rp->Id = ACPI_RSTYPE_END_TAG;
+ rp->Length = 0;
+
+ return(AE_OK);
+}
+
+/*
+ * Set interrupt model.
+ */
+ACPI_STATUS
+acpi_SetIntrModel(int model)
+{
+ ACPI_OBJECT_LIST ArgList;
+ ACPI_OBJECT Arg;
+
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = model;
+ ArgList.Count = 1;
+ ArgList.Pointer = &Arg;
+ return (AcpiEvaluateObject(ACPI_ROOT_OBJECT, "_PIC", &ArgList, NULL));
+}
+
+#define ACPI_MINIMUM_AWAKETIME 5
+
+static void
+acpi_sleep_enable(void *arg)
+{
+ ((struct acpi_softc *)arg)->acpi_sleep_disabled = 0;
+}
+
+/*
+ * Set the system sleep state
+ *
+ * Currently we only support S1 and S5
+ */
+ACPI_STATUS
+acpi_SetSleepState(struct acpi_softc *sc, int state)
+{
+ ACPI_STATUS status = AE_OK;
+ UINT8 TypeA;
+ UINT8 TypeB;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+ ACPI_ASSERTLOCK;
+
+ if (sc->acpi_sstate != ACPI_STATE_S0)
+ return_ACPI_STATUS(AE_BAD_PARAMETER); /* avoid reentry */
+
+ if (sc->acpi_sleep_disabled)
+ return_ACPI_STATUS(AE_OK);
+
+ switch (state) {
+ case ACPI_STATE_S0: /* XXX only for testing */
+ if (ACPI_FAILURE(status = AcpiEnterSleepState((UINT8)state))) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", AcpiFormatException(status));
+ }
+ break;
+
+ case ACPI_STATE_S1:
+ case ACPI_STATE_S2:
+ case ACPI_STATE_S3:
+ case ACPI_STATE_S4:
+ if (ACPI_FAILURE(status = AcpiGetSleepTypeData((UINT8)state, &TypeA, &TypeB))) {
+ device_printf(sc->acpi_dev, "AcpiGetSleepTypeData failed - %s\n", AcpiFormatException(status));
+ break;
+ }
+
+ sc->acpi_sstate = state;
+ sc->acpi_sleep_disabled = 1;
+
+ /*
+ * Inform all devices that we are going to sleep.
+ */
+ if (DEVICE_SUSPEND(root_bus) != 0) {
+ /*
+ * Re-wake the system.
+ *
+ * XXX note that a better two-pass approach with a 'veto' pass
+ * followed by a "real thing" pass would be better, but the
+ * current bus interface does not provide for this.
+ */
+ DEVICE_RESUME(root_bus);
+ return_ACPI_STATUS(AE_ERROR);
+ }
+
+ if (ACPI_FAILURE(status = AcpiEnterSleepStatePrep(state))) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
+ AcpiFormatException(status));
+ break;
+ }
+
+ if (sc->acpi_sleep_delay > 0) {
+ DELAY(sc->acpi_sleep_delay * 1000000);
+ }
+
+ if (state != ACPI_STATE_S1) {
+ acpi_sleep_machdep(sc, state);
+
+ /* AcpiEnterSleepState() maybe incompleted, unlock here if locked. */
+ if (AcpiGbl_AcpiMutexInfo[ACPI_MTX_HARDWARE].OwnerId != ACPI_MUTEX_NOT_ACQUIRED) {
+ AcpiUtReleaseMutex(ACPI_MTX_HARDWARE);
+ }
+
+ /* Re-enable ACPI hardware on wakeup from sleep state 4. */
+ if (state == ACPI_STATE_S4) {
+ AcpiEnable();
+ }
+ } else {
+ if (ACPI_FAILURE(status = AcpiEnterSleepState((UINT8)state))) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", AcpiFormatException(status));
+ break;
+ }
+ }
+ AcpiLeaveSleepState((UINT8)state);
+ DEVICE_RESUME(root_bus);
+ sc->acpi_sstate = ACPI_STATE_S0;
+ acpi_enable_fixed_events(sc);
+ break;
+
+ case ACPI_STATE_S5:
+ /*
+ * Shut down cleanly and power off. This will call us back through the
+ * shutdown handlers.
+ */
+ shutdown_nice(RB_POWEROFF);
+ break;
+
+ default:
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+ if (sc->acpi_sleep_disabled)
+ timeout(acpi_sleep_enable, (caddr_t)sc, hz * ACPI_MINIMUM_AWAKETIME);
+
+ return_ACPI_STATUS(status);
+}
+
+/*
+ * Enable/Disable ACPI
+ */
+ACPI_STATUS
+acpi_Enable(struct acpi_softc *sc)
+{
+ ACPI_STATUS status;
+ u_int32_t flags;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_ASSERTLOCK;
+
+ flags = ACPI_NO_ADDRESS_SPACE_INIT | ACPI_NO_HARDWARE_INIT |
+ ACPI_NO_DEVICE_INIT | ACPI_NO_OBJECT_INIT;
+ if (!sc->acpi_enabled) {
+ status = AcpiEnableSubsystem(flags);
+ } else {
+ status = AE_OK;
+ }
+ if (status == AE_OK)
+ sc->acpi_enabled = 1;
+ return_ACPI_STATUS(status);
+}
+
+ACPI_STATUS
+acpi_Disable(struct acpi_softc *sc)
+{
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_ASSERTLOCK;
+
+ if (sc->acpi_enabled) {
+ status = AcpiDisable();
+ } else {
+ status = AE_OK;
+ }
+ if (status == AE_OK)
+ sc->acpi_enabled = 0;
+ return_ACPI_STATUS(status);
+}
+
+/*
+ * ACPI Event Handlers
+ */
+
+/* System Event Handlers (registered by EVENTHANDLER_REGISTER) */
+
+static void
+acpi_system_eventhandler_sleep(void *arg, int state)
+{
+ ACPI_LOCK_DECL;
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+
+ ACPI_LOCK;
+ if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX)
+ acpi_SetSleepState((struct acpi_softc *)arg, state);
+ ACPI_UNLOCK;
+ return_VOID;
+}
+
+static void
+acpi_system_eventhandler_wakeup(void *arg, int state)
+{
+ ACPI_LOCK_DECL;
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+
+ /* Well, what to do? :-) */
+
+ ACPI_LOCK;
+ ACPI_UNLOCK;
+
+ return_VOID;
+}
+
+/*
+ * ACPICA Event Handlers (FixedEvent, also called from button notify handler)
+ */
+UINT32
+acpi_eventhandler_power_button_for_sleep(void *context)
+{
+ struct acpi_softc *sc = (struct acpi_softc *)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ EVENTHANDLER_INVOKE(acpi_sleep_event, sc->acpi_power_button_sx);
+
+ return_VALUE(ACPI_INTERRUPT_HANDLED);
+}
+
+UINT32
+acpi_eventhandler_power_button_for_wakeup(void *context)
+{
+ struct acpi_softc *sc = (struct acpi_softc *)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ EVENTHANDLER_INVOKE(acpi_wakeup_event, sc->acpi_power_button_sx);
+
+ return_VALUE(ACPI_INTERRUPT_HANDLED);
+}
+
+UINT32
+acpi_eventhandler_sleep_button_for_sleep(void *context)
+{
+ struct acpi_softc *sc = (struct acpi_softc *)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ EVENTHANDLER_INVOKE(acpi_sleep_event, sc->acpi_sleep_button_sx);
+
+ return_VALUE(ACPI_INTERRUPT_HANDLED);
+}
+
+UINT32
+acpi_eventhandler_sleep_button_for_wakeup(void *context)
+{
+ struct acpi_softc *sc = (struct acpi_softc *)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ EVENTHANDLER_INVOKE(acpi_wakeup_event, sc->acpi_sleep_button_sx);
+
+ return_VALUE(ACPI_INTERRUPT_HANDLED);
+}
+
+/*
+ * XXX This is kinda ugly, and should not be here.
+ */
+struct acpi_staticbuf {
+ ACPI_BUFFER buffer;
+ char data[512];
+};
+
+char *
+acpi_name(ACPI_HANDLE handle)
+{
+ static struct acpi_staticbuf buf;
+
+ ACPI_ASSERTLOCK;
+
+ buf.buffer.Length = 512;
+ buf.buffer.Pointer = &buf.data[0];
+
+ if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf.buffer)))
+ return(buf.buffer.Pointer);
+ return("(unknown path)");
+}
+
+/*
+ * Debugging/bug-avoidance. Avoid trying to fetch info on various
+ * parts of the namespace.
+ */
+int
+acpi_avoid(ACPI_HANDLE handle)
+{
+ char *cp, *env, *np;
+ int len;
+
+ np = acpi_name(handle);
+ if (*np == '\\')
+ np++;
+ if ((env = getenv("debug.acpi.avoid")) == NULL)
+ return(0);
+
+ /* scan the avoid list checking for a match */
+ cp = env;
+ for (;;) {
+ while ((*cp != 0) && isspace(*cp))
+ cp++;
+ if (*cp == 0)
+ break;
+ len = 0;
+ while ((cp[len] != 0) && !isspace(cp[len]))
+ len++;
+ if (!strncmp(cp, np, len)) {
+ freeenv(env);
+ return(1);
+ }
+ cp += len;
+ }
+ freeenv(env);
+ return(0);
+}
+
+/*
+ * Debugging/bug-avoidance. Disable ACPI subsystem components.
+ */
+int
+acpi_disabled(char *subsys)
+{
+ char *cp, *env;
+ int len;
+
+ if ((env = getenv("debug.acpi.disable")) == NULL)
+ return(0);
+ if (!strcmp(env, "all")) {
+ freeenv(env);
+ return(1);
+ }
+
+ /* scan the disable list checking for a match */
+ cp = env;
+ for (;;) {
+ while ((*cp != 0) && isspace(*cp))
+ cp++;
+ if (*cp == 0)
+ break;
+ len = 0;
+ while ((cp[len] != 0) && !isspace(cp[len]))
+ len++;
+ if (!strncmp(cp, subsys, len)) {
+ freeenv(env);
+ return(1);
+ }
+ cp += len;
+ }
+ freeenv(env);
+ return(0);
+}
+
+/*
+ * Device wake capability enable/disable.
+ */
+void
+acpi_device_enable_wake_capability(ACPI_HANDLE h, int enable)
+{
+ ACPI_OBJECT_LIST ArgList;
+ ACPI_OBJECT Arg;
+
+ /*
+ * TBD: All Power Resources referenced by elements 2 through N
+ * of the _PRW object are put into the ON state.
+ */
+
+ /*
+ * enable/disable device wake function.
+ */
+
+ ArgList.Count = 1;
+ ArgList.Pointer = &Arg;
+
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = enable;
+
+ (void)AcpiEvaluateObject(h, "_PSW", &ArgList, NULL);
+}
+
+void
+acpi_device_enable_wake_event(ACPI_HANDLE h)
+{
+ struct acpi_softc *sc;
+ ACPI_STATUS status;
+ ACPI_BUFFER prw_buffer;
+ ACPI_OBJECT *res;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if ((sc = devclass_get_softc(acpi_devclass, 0)) == NULL) {
+ return;
+ }
+
+ /*
+ * _PRW object is only required for devices that have the ability
+ * to wake the system from a system sleeping state.
+ */
+ prw_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiEvaluateObject(h, "_PRW", NULL, &prw_buffer);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ res = (ACPI_OBJECT *)prw_buffer.Pointer;
+ if (res == NULL) {
+ return;
+ }
+
+ if ((res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count < 2)) {
+ goto out;
+ }
+
+ /*
+ * The element 1 of the _PRW object:
+ * The lowest power system sleeping state that can be entered
+ * while still providing wake functionality.
+ * The sleeping state being entered must be greater or equal to
+ * the power state declared in element 1 of the _PRW object.
+ */
+ if (res->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
+ goto out;
+ }
+
+ if (sc->acpi_sstate > res->Package.Elements[1].Integer.Value) {
+ goto out;
+ }
+
+ /*
+ * The element 0 of the _PRW object:
+ */
+ switch(res->Package.Elements[0].Type) {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * If the data type of this package element is numeric, then this
+ * _PRW package element is the bit index in the GPEx_EN, in the
+ * GPE blocks described in the FADT, of the enable bit that is
+ * enabled for the wake event.
+ */
+
+ status = AcpiEnableEvent(res->Package.Elements[0].Integer.Value,
+ ACPI_EVENT_GPE, ACPI_EVENT_WAKE_ENABLE);
+ if (ACPI_FAILURE(status))
+ printf("%s: EnableEvent Failed\n", __func__);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /* XXX TBD */
+
+ /*
+ * If the data type of this package element is a package, then this
+ * _PRW package element is itself a package containing two
+ * elements. The first is an object reference to the GPE Block
+ * device that contains the GPE that will be triggered by the wake
+ * event. The second element is numeric and it contains the bit
+ * index in the GPEx_EN, in the GPE Block referenced by the
+ * first element in the package, of the enable bit that is enabled for
+ * the wake event.
+ * For example, if this field is a package then it is of the form:
+ * Package() {\_SB.PCI0.ISA.GPE, 2}
+ */
+
+ break;
+
+ default:
+ break;
+ }
+
+out:
+ if (prw_buffer.Pointer != NULL)
+ AcpiOsFree(prw_buffer.Pointer);
+ return;
+}
+
+/*
+ * Control interface.
+ *
+ * We multiplex ioctls for all participating ACPI devices here. Individual
+ * drivers wanting to be accessible via /dev/acpi should use the register/deregister
+ * interface to make their handlers visible.
+ */
+struct acpi_ioctl_hook
+{
+ TAILQ_ENTRY(acpi_ioctl_hook) link;
+ u_long cmd;
+ int (* fn)(u_long cmd, caddr_t addr, void *arg);
+ void *arg;
+};
+
+static TAILQ_HEAD(,acpi_ioctl_hook) acpi_ioctl_hooks;
+static int acpi_ioctl_hooks_initted;
+
+/*
+ * Register an ioctl handler.
+ */
+int
+acpi_register_ioctl(u_long cmd, int (* fn)(u_long cmd, caddr_t addr, void *arg), void *arg)
+{
+ struct acpi_ioctl_hook *hp;
+
+ if ((hp = malloc(sizeof(*hp), M_ACPIDEV, M_NOWAIT)) == NULL)
+ return(ENOMEM);
+ hp->cmd = cmd;
+ hp->fn = fn;
+ hp->arg = arg;
+ if (acpi_ioctl_hooks_initted == 0) {
+ TAILQ_INIT(&acpi_ioctl_hooks);
+ acpi_ioctl_hooks_initted = 1;
+ }
+ TAILQ_INSERT_TAIL(&acpi_ioctl_hooks, hp, link);
+ return(0);
+}
+
+/*
+ * Deregister an ioctl handler.
+ */
+void
+acpi_deregister_ioctl(u_long cmd, int (* fn)(u_long cmd, caddr_t addr, void *arg))
+{
+ struct acpi_ioctl_hook *hp;
+
+ TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link)
+ if ((hp->cmd == cmd) && (hp->fn == fn))
+ break;
+
+ if (hp != NULL) {
+ TAILQ_REMOVE(&acpi_ioctl_hooks, hp, link);
+ free(hp, M_ACPIDEV);
+ }
+}
+
+static int
+acpiopen(dev_t dev, int flag, int fmt, d_thread_t *td)
+{
+ return(0);
+}
+
+static int
+acpiclose(dev_t dev, int flag, int fmt, d_thread_t *td)
+{
+ return(0);
+}
+
+static int
+acpiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
+{
+ struct acpi_softc *sc;
+ struct acpi_ioctl_hook *hp;
+ int error, xerror, state;
+ ACPI_LOCK_DECL;
+
+ ACPI_LOCK;
+
+ error = state = 0;
+ sc = dev->si_drv1;
+
+ /*
+ * Scan the list of registered ioctls, looking for handlers.
+ */
+ if (acpi_ioctl_hooks_initted) {
+ TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link) {
+ if (hp->cmd == cmd) {
+ xerror = hp->fn(cmd, addr, hp->arg);
+ if (xerror != 0)
+ error = xerror;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * Core ioctls are not permitted for non-writable user.
+ * Currently, other ioctls just fetch information.
+ * Not changing system behavior.
+ */
+ if(!(flag & FWRITE)){
+ return EPERM;
+ }
+
+ /*
+ * Core system ioctls.
+ */
+ switch (cmd) {
+ case ACPIIO_ENABLE:
+ if (ACPI_FAILURE(acpi_Enable(sc)))
+ error = ENXIO;
+ break;
+
+ case ACPIIO_DISABLE:
+ if (ACPI_FAILURE(acpi_Disable(sc)))
+ error = ENXIO;
+ break;
+
+ case ACPIIO_SETSLPSTATE:
+ if (!sc->acpi_enabled) {
+ error = ENXIO;
+ break;
+ }
+ state = *(int *)addr;
+ if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX) {
+ acpi_SetSleepState(sc, state);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ default:
+ if (error == 0)
+ error = EINVAL;
+ break;
+ }
+
+out:
+ ACPI_UNLOCK;
+ return(error);
+}
+
+static int
+acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ char sleep_state[4];
+ char buf[16];
+ int error;
+ UINT8 state, TypeA, TypeB;
+
+ buf[0] = '\0';
+ for (state = ACPI_STATE_S1; state < ACPI_S_STATES_MAX+1; state++) {
+ if (ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) {
+ sprintf(sleep_state, "S%d ", state);
+ strcat(buf, sleep_state);
+ }
+ }
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ return(error);
+}
+
+static int
+acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ char sleep_state[10];
+ int error;
+ u_int new_state, old_state;
+
+ old_state = *(u_int *)oidp->oid_arg1;
+ if (old_state > ACPI_S_STATES_MAX+1) {
+ strcpy(sleep_state, "unknown");
+ } else {
+ bzero(sleep_state, sizeof(sleep_state));
+ strncpy(sleep_state, sleep_state_names[old_state],
+ sizeof(sleep_state_names[old_state]));
+ }
+ error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req);
+ if (error == 0 && req->newptr != NULL) {
+ for (new_state = ACPI_STATE_S0; new_state <= ACPI_S_STATES_MAX+1; new_state++) {
+ if (strncmp(sleep_state, sleep_state_names[new_state],
+ sizeof(sleep_state)) == 0)
+ break;
+ }
+ if (new_state <= ACPI_S_STATES_MAX+1) {
+ if (new_state != old_state) {
+ *(u_int *)oidp->oid_arg1 = new_state;
+ }
+ } else {
+ error = EINVAL;
+ }
+ }
+ return(error);
+}
+
+#ifdef ACPI_DEBUG
+/*
+ * Support for parsing debug options from the kernel environment.
+ *
+ * Bits may be set in the AcpiDbgLayer and AcpiDbgLevel debug registers
+ * by specifying the names of the bits in the debug.acpi.layer and
+ * debug.acpi.level environment variables. Bits may be unset by
+ * prefixing the bit name with !.
+ */
+struct debugtag
+{
+ char *name;
+ UINT32 value;
+};
+
+static struct debugtag dbg_layer[] = {
+ {"ACPI_UTILITIES", ACPI_UTILITIES},
+ {"ACPI_HARDWARE", ACPI_HARDWARE},
+ {"ACPI_EVENTS", ACPI_EVENTS},
+ {"ACPI_TABLES", ACPI_TABLES},
+ {"ACPI_NAMESPACE", ACPI_NAMESPACE},
+ {"ACPI_PARSER", ACPI_PARSER},
+ {"ACPI_DISPATCHER", ACPI_DISPATCHER},
+ {"ACPI_EXECUTER", ACPI_EXECUTER},
+ {"ACPI_RESOURCES", ACPI_RESOURCES},
+ {"ACPI_CA_DEBUGGER", ACPI_CA_DEBUGGER},
+ {"ACPI_OS_SERVICES", ACPI_OS_SERVICES},
+ {"ACPI_CA_DISASSEMBLER", ACPI_CA_DISASSEMBLER},
+
+ {"ACPI_BUS", ACPI_BUS},
+ {"ACPI_SYSTEM", ACPI_SYSTEM},
+ {"ACPI_POWER", ACPI_POWER},
+ {"ACPI_EC", ACPI_EC},
+ {"ACPI_AC_ADAPTER", ACPI_AC_ADAPTER},
+ {"ACPI_BATTERY", ACPI_BATTERY},
+ {"ACPI_BUTTON", ACPI_BUTTON},
+ {"ACPI_PROCESSOR", ACPI_PROCESSOR},
+ {"ACPI_THERMAL", ACPI_THERMAL},
+ {"ACPI_FAN", ACPI_FAN},
+
+ {"ACPI_ALL_DRIVERS", ACPI_ALL_DRIVERS},
+ {"ACPI_ALL_COMPONENTS", ACPI_ALL_COMPONENTS},
+ {NULL, 0}
+};
+
+static struct debugtag dbg_level[] = {
+ {"ACPI_LV_ERROR", ACPI_LV_ERROR},
+ {"ACPI_LV_WARN", ACPI_LV_WARN},
+ {"ACPI_LV_INIT", ACPI_LV_INIT},
+ {"ACPI_LV_DEBUG_OBJECT", ACPI_LV_DEBUG_OBJECT},
+ {"ACPI_LV_INFO", ACPI_LV_INFO},
+ {"ACPI_LV_ALL_EXCEPTIONS", ACPI_LV_ALL_EXCEPTIONS},
+
+ /* Trace verbosity level 1 [Standard Trace Level] */
+ {"ACPI_LV_PARSE", ACPI_LV_PARSE},
+ {"ACPI_LV_LOAD", ACPI_LV_LOAD},
+ {"ACPI_LV_DISPATCH", ACPI_LV_DISPATCH},
+ {"ACPI_LV_EXEC", ACPI_LV_EXEC},
+ {"ACPI_LV_NAMES", ACPI_LV_NAMES},
+ {"ACPI_LV_OPREGION", ACPI_LV_OPREGION},
+ {"ACPI_LV_BFIELD", ACPI_LV_BFIELD},
+ {"ACPI_LV_TABLES", ACPI_LV_TABLES},
+ {"ACPI_LV_VALUES", ACPI_LV_VALUES},
+ {"ACPI_LV_OBJECTS", ACPI_LV_OBJECTS},
+ {"ACPI_LV_RESOURCES", ACPI_LV_RESOURCES},
+ {"ACPI_LV_USER_REQUESTS", ACPI_LV_USER_REQUESTS},
+ {"ACPI_LV_PACKAGE", ACPI_LV_PACKAGE},
+ {"ACPI_LV_INIT_NAMES", ACPI_LV_INIT_NAMES},
+ {"ACPI_LV_VERBOSITY1", ACPI_LV_VERBOSITY1},
+
+ /* Trace verbosity level 2 [Function tracing and memory allocation] */
+ {"ACPI_LV_ALLOCATIONS", ACPI_LV_ALLOCATIONS},
+ {"ACPI_LV_FUNCTIONS", ACPI_LV_FUNCTIONS},
+ {"ACPI_LV_OPTIMIZATIONS", ACPI_LV_OPTIMIZATIONS},
+ {"ACPI_LV_VERBOSITY2", ACPI_LV_VERBOSITY2},
+ {"ACPI_LV_ALL", ACPI_LV_ALL},
+
+ /* Trace verbosity level 3 [Threading, I/O, and Interrupts] */
+ {"ACPI_LV_MUTEX", ACPI_LV_MUTEX},
+ {"ACPI_LV_THREADS", ACPI_LV_THREADS},
+ {"ACPI_LV_IO", ACPI_LV_IO},
+ {"ACPI_LV_INTERRUPTS", ACPI_LV_INTERRUPTS},
+ {"ACPI_LV_VERBOSITY3", ACPI_LV_VERBOSITY3},
+
+ /* Exceptionally verbose output -- also used in the global "DebugLevel" */
+ {"ACPI_LV_AML_DISASSEMBLE", ACPI_LV_AML_DISASSEMBLE},
+ {"ACPI_LV_VERBOSE_INFO", ACPI_LV_VERBOSE_INFO},
+ {"ACPI_LV_FULL_TABLES", ACPI_LV_FULL_TABLES},
+ {"ACPI_LV_EVENTS", ACPI_LV_EVENTS},
+ {"ACPI_LV_VERBOSE", ACPI_LV_VERBOSE},
+ {NULL, 0}
+};
+
+static void
+acpi_parse_debug(char *cp, struct debugtag *tag, UINT32 *flag)
+{
+ char *ep;
+ int i, l;
+ int set;
+
+ while (*cp) {
+ if (isspace(*cp)) {
+ cp++;
+ continue;
+ }
+ ep = cp;
+ while (*ep && !isspace(*ep))
+ ep++;
+ if (*cp == '!') {
+ set = 0;
+ cp++;
+ if (cp == ep)
+ continue;
+ } else {
+ set = 1;
+ }
+ l = ep - cp;
+ for (i = 0; tag[i].name != NULL; i++) {
+ if (!strncmp(cp, tag[i].name, l)) {
+ if (set) {
+ *flag |= tag[i].value;
+ } else {
+ *flag &= ~tag[i].value;
+ }
+ printf("ACPI_DEBUG: set '%s'\n", tag[i].name);
+ }
+ }
+ cp = ep;
+ }
+}
+
+static void
+acpi_set_debugging(void *junk)
+{
+ char *cp;
+
+ if (!cold)
+ return;
+
+ AcpiDbgLayer = 0;
+ AcpiDbgLevel = 0;
+ if ((cp = getenv("debug.acpi.layer")) != NULL) {
+ acpi_parse_debug(cp, &dbg_layer[0], &AcpiDbgLayer);
+ freeenv(cp);
+ }
+ if ((cp = getenv("debug.acpi.level")) != NULL) {
+ acpi_parse_debug(cp, &dbg_level[0], &AcpiDbgLevel);
+ freeenv(cp);
+ }
+
+ printf("ACPI debug layer 0x%x debug level 0x%x\n", AcpiDbgLayer, AcpiDbgLevel);
+}
+SYSINIT(acpi_debugging, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_set_debugging, NULL);
+#endif
+
+static int
+acpi_pm_func(u_long cmd, void *arg, ...)
+{
+ int state, acpi_state;
+ int error;
+ struct acpi_softc *sc;
+ va_list ap;
+
+ error = 0;
+ switch (cmd) {
+ case POWER_CMD_SUSPEND:
+ sc = (struct acpi_softc *)arg;
+ if (sc == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ va_start(ap, arg);
+ state = va_arg(ap, int);
+ va_end(ap);
+
+ switch (state) {
+ case POWER_SLEEP_STATE_STANDBY:
+ acpi_state = sc->acpi_standby_sx;
+ break;
+ case POWER_SLEEP_STATE_SUSPEND:
+ acpi_state = sc->acpi_suspend_sx;
+ break;
+ case POWER_SLEEP_STATE_HIBERNATE:
+ acpi_state = ACPI_STATE_S4;
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+
+ acpi_SetSleepState(sc, acpi_state);
+ break;
+
+ default:
+ error = EINVAL;
+ goto out;
+ }
+
+out:
+ return (error);
+}
+
+static void
+acpi_pm_register(void *arg)
+{
+ int error;
+
+ if (!cold)
+ return;
+
+ if (!resource_int_value("acpi", 0, "disabled", &error) &&
+ (error != 0))
+ return;
+
+ power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, NULL);
+}
+
+SYSINIT(power, SI_SUB_KLD, SI_ORDER_ANY, acpi_pm_register, 0);
+
diff --git a/sys/dev/acpica/acpi_acad.c b/sys/dev/acpica/acpi_acad.c
new file mode 100644
index 0000000..09bb149
--- /dev/null
+++ b/sys/dev/acpica/acpi_acad.c
@@ -0,0 +1,282 @@
+/*-
+ * Copyright (c) 2000 Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/power.h>
+
+#include "acpi.h"
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_AC_ADAPTER
+ACPI_MODULE_NAME("AC_ADAPTER")
+
+#define ACPI_DEVICE_CHECK_PNP 0x00
+#define ACPI_DEVICE_CHECK_EXISTENCE 0x01
+#define ACPI_POWERSOURCE_STAT_CHANGE 0x80
+
+static void acpi_acad_get_status(void * );
+static void acpi_acad_notify_handler(ACPI_HANDLE , UINT32 ,void *);
+static int acpi_acad_probe(device_t);
+static int acpi_acad_attach(device_t);
+static int acpi_acad_ioctl(u_long, caddr_t, void *);
+static int acpi_acad_sysctl(SYSCTL_HANDLER_ARGS);
+static void acpi_acad_init_acline(void *arg);
+
+struct acpi_acad_softc {
+ int status;
+
+ int initializing;
+};
+
+static void
+acpi_acad_get_status(void *context)
+{
+ int newstatus;
+ device_t dev = context;
+ struct acpi_acad_softc *sc = device_get_softc(dev);
+ ACPI_HANDLE h = acpi_get_handle(dev);
+
+ if (ACPI_FAILURE(acpi_EvaluateInteger(h, "_PSR", &newstatus))) {
+ sc->status = -1;
+ return;
+ }
+
+ if (sc->status != newstatus) {
+ sc->status = newstatus;
+ /* set system power profile based on AC adapter status */
+ power_profile_set_state(sc->status ? POWER_PROFILE_PERFORMANCE : POWER_PROFILE_ECONOMY);
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "%s Line\n",(sc->status) ? "On" : "Off");
+ }
+}
+
+static void
+acpi_acad_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ device_t dev = context;
+
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "Notify %d\n", notify);
+
+ switch (notify) {
+ case ACPI_DEVICE_CHECK_PNP:
+ case ACPI_DEVICE_CHECK_EXISTENCE:
+ case ACPI_POWERSOURCE_STAT_CHANGE:
+ /*Temporally. It is better to notify policy manager*/
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO,
+ acpi_acad_get_status,context);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+acpi_acad_probe(device_t dev)
+{
+
+ if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
+ acpi_MatchHid(dev, "ACPI0003")) {
+
+ /*
+ * Set device description
+ */
+ device_set_desc(dev, "AC adapter");
+ return(0);
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_acad_attach(device_t dev)
+{
+ int error;
+ struct acpi_acad_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_HANDLE handle = acpi_get_handle(dev);
+ AcpiInstallNotifyHandler(handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_acad_notify_handler, dev);
+ /*Installing system notify is not so good*/
+ AcpiInstallNotifyHandler(handle,
+ ACPI_SYSTEM_NOTIFY,
+ acpi_acad_notify_handler, dev);
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return (ENXIO);
+ }
+ if ((error = acpi_register_ioctl(ACPIIO_ACAD_GET_STATUS,
+ acpi_acad_ioctl, dev)) != 0) {
+ return (error);
+ }
+
+ if (device_get_unit(dev) == 0) {
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ SYSCTL_ADD_PROC(&acpi_sc->acpi_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
+ OID_AUTO, "acline", CTLTYPE_INT | CTLFLAG_RD,
+ &sc->status, 0, acpi_acad_sysctl, "I", "");
+ }
+
+ /* Get initial status after whole system is up. */
+ sc->status = -1;
+ sc->initializing = 0;
+
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_acad_init_acline, dev);
+
+ return(0);
+}
+
+static device_method_t acpi_acad_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_acad_probe),
+ DEVMETHOD(device_attach, acpi_acad_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_acad_driver = {
+ "acpi_acad",
+ acpi_acad_methods,
+ sizeof(struct acpi_acad_softc),
+};
+
+static devclass_t acpi_acad_devclass;
+DRIVER_MODULE(acpi_acad,acpi,acpi_acad_driver,acpi_acad_devclass,0,0);
+
+static int
+acpi_acad_ioctl(u_long cmd, caddr_t addr, void *arg)
+{
+ device_t dev;
+ struct acpi_acad_softc *sc;
+
+ dev = (device_t)arg;
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return(ENXIO);
+ }
+
+ /*
+ * No security check required: information retrieval only. If
+ * new functions are added here, a check might be required.
+ */
+
+ switch (cmd) {
+ case ACPIIO_ACAD_GET_STATUS:
+ acpi_acad_get_status(dev);
+ *(int *)addr = sc->status;
+ break;
+ }
+
+ return(0);
+}
+
+static int
+acpi_acad_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int val;
+ int error;
+
+ if (acpi_acad_get_acline(&val)) {
+ return (ENXIO);
+ }
+
+ val = *(u_int *)oidp->oid_arg1;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ return (error);
+}
+
+static void
+acpi_acad_init_acline(void *arg)
+{
+ int retry;
+ int status;
+ device_t dev = (device_t)arg;
+ struct acpi_acad_softc *sc = device_get_softc(dev);
+#define ACPI_ACAD_RETRY_MAX 6
+
+ if (sc->initializing) {
+ return;
+ }
+
+ sc->initializing = 1;
+
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "acline initialization start\n");
+
+ status = 0;
+ for (retry = 0; retry < ACPI_ACAD_RETRY_MAX; retry++, AcpiOsSleep(10, 0)) {
+ acpi_acad_get_status(dev);
+ if (status != sc->status)
+ break;
+ }
+
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "acline initialization done, tried %d times\n", retry+1);
+
+ sc->initializing = 0;
+}
+
+/*
+ * Public interfaces.
+ */
+
+int
+acpi_acad_get_acline(int *status)
+{
+ device_t dev;
+ struct acpi_acad_softc *sc;
+
+ if ((dev = devclass_get_device(acpi_acad_devclass, 0)) == NULL) {
+ return (ENXIO);
+ }
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return (ENXIO);
+ }
+
+ acpi_acad_get_status(dev);
+ *status = sc->status;
+
+ return (0);
+}
+
diff --git a/sys/dev/acpica/acpi_battery.c b/sys/dev/acpica/acpi_battery.c
new file mode 100644
index 0000000..e26aee1
--- /dev/null
+++ b/sys/dev/acpica/acpi_battery.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h" /* XXX trim includes */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/ioccom.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/ctype.h>
+
+#include <machine/clock.h>
+
+#include <machine/resource.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+MALLOC_DEFINE(M_ACPIBATT, "acpibatt", "ACPI generic battery data");
+
+/*
+ * ACPI Battery Abstruction Layer.
+ */
+
+struct acpi_batteries {
+ TAILQ_ENTRY(acpi_batteries) link;
+ struct acpi_battdesc battdesc;
+};
+
+static TAILQ_HEAD(,acpi_batteries) acpi_batteries;
+static int acpi_batteries_initted = 0;
+static int acpi_batteries_units = 0;
+static int acpi_battery_info_expire = 5;
+static struct acpi_battinfo acpi_battery_battinfo;
+
+int
+acpi_battery_get_units(void)
+{
+
+ return (acpi_batteries_units);
+}
+
+int
+acpi_battery_get_battdesc(int logical_unit, struct acpi_battdesc *battdesc)
+{
+ int i;
+ struct acpi_batteries *bp;
+
+ if (logical_unit < 0 || logical_unit >= acpi_batteries_units) {
+ return (ENXIO);
+ }
+
+ i = 0;
+ TAILQ_FOREACH(bp, &acpi_batteries, link) {
+ if (logical_unit == i) {
+ battdesc->type = bp->battdesc.type;
+ battdesc->phys_unit = bp->battdesc.phys_unit;
+ return (0);
+ }
+ i++;
+ }
+
+ return (ENXIO);
+}
+
+int
+acpi_battery_get_battinfo(int unit, struct acpi_battinfo *battinfo)
+{
+ int error;
+ struct acpi_battdesc battdesc;
+
+ error = 0;
+ if (unit == -1) {
+ error = acpi_cmbat_get_battinfo(-1, battinfo);
+ goto out;
+ } else {
+ if ((error = acpi_battery_get_battdesc(unit, &battdesc)) != 0) {
+ goto out;
+ }
+ switch (battdesc.type) {
+ case ACPI_BATT_TYPE_CMBAT:
+ error = acpi_cmbat_get_battinfo(battdesc.phys_unit,
+ battinfo);
+ break;
+ default:
+ error = ENXIO;
+ break;
+ }
+ }
+out:
+ return (error);
+}
+
+int
+acpi_battery_get_info_expire(void)
+{
+
+ return (acpi_battery_info_expire);
+}
+
+static int
+acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
+{
+ int error;
+ int logical_unit;
+ union acpi_battery_ioctl_arg *ioctl_arg;
+
+ ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
+ error = 0;
+
+ /*
+ * No security check required: information retrieval only. If
+ * new functions are added here, a check might be required.
+ */
+
+ switch (cmd) {
+ case ACPIIO_BATT_GET_UNITS:
+ *(int *)addr = acpi_battery_get_units();
+ break;
+
+ case ACPIIO_BATT_GET_BATTDESC:
+ logical_unit = ioctl_arg->unit;
+ error = acpi_battery_get_battdesc(logical_unit, &ioctl_arg->battdesc);
+ break;
+
+ case ACPIIO_BATT_GET_BATTINFO:
+ logical_unit = ioctl_arg->unit;
+ error = acpi_battery_get_battinfo(logical_unit,
+ &ioctl_arg->battinfo);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+acpi_battery_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int val;
+ int error;
+
+ acpi_battery_get_battinfo(-1, &acpi_battery_battinfo);
+ val = *(u_int *)oidp->oid_arg1;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ return (error);
+}
+
+static int
+acpi_battery_init(void)
+{
+ device_t dev;
+ struct acpi_softc *sc;
+ int error;
+
+ if ((dev = devclass_get_device(devclass_find("acpi"), 0)) == NULL) {
+ return (ENXIO);
+ }
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return (ENXIO);
+ }
+
+ error = 0;
+
+ TAILQ_INIT(&acpi_batteries);
+ acpi_batteries_initted = 1;
+
+ if ((error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS,
+ acpi_battery_ioctl, NULL)) != 0) {
+ return (error);
+ }
+ if ((error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTDESC,
+ acpi_battery_ioctl, NULL)) != 0) {
+ return (error);
+ }
+ if ((error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO,
+ acpi_battery_ioctl, NULL)) != 0) {
+ return (error);
+ }
+
+ sysctl_ctx_init(&sc->acpi_battery_sysctl_ctx);
+ sc->acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "battery", CTLFLAG_RD, 0, "");
+ SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+ OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD,
+ &acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I", "");
+ SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+ OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD,
+ &acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I", "");
+ SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+ OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RD,
+ &acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I", "");
+ SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+ OID_AUTO, "units", CTLFLAG_RD, &acpi_batteries_units, 0, "");
+ SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+ OID_AUTO, "info_expire", CTLFLAG_RD | CTLFLAG_RW,
+ &acpi_battery_info_expire, 0, "");
+
+ return (error);
+}
+
+int
+acpi_battery_register(int type, int phys_unit)
+{
+ int error;
+ struct acpi_batteries *bp;
+
+ error = 0;
+ if ((bp = malloc(sizeof(*bp), M_ACPIBATT, M_NOWAIT)) == NULL) {
+ return(ENOMEM);
+ }
+
+ bp->battdesc.type = type;
+ bp->battdesc.phys_unit = phys_unit;
+ if (acpi_batteries_initted == 0) {
+ if ((error = acpi_battery_init()) != 0) {
+ free(bp, M_ACPIBATT);
+ return(error);
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&acpi_batteries, bp, link);
+ acpi_batteries_units++;
+
+ return(0);
+}
diff --git a/sys/dev/acpica/acpi_button.c b/sys/dev/acpica/acpi_button.c
new file mode 100644
index 0000000..a4bebc2
--- /dev/null
+++ b/sys/dev/acpica/acpi_button.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2000 Mitsaru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUTTON
+ACPI_MODULE_NAME("BUTTON")
+
+struct acpi_button_softc {
+ device_t button_dev;
+ ACPI_HANDLE button_handle;
+#define ACPI_POWER_BUTTON 0
+#define ACPI_SLEEP_BUTTON 1
+ boolean_t button_type; /* Power or Sleep Button */
+};
+
+static int acpi_button_probe(device_t dev);
+static int acpi_button_attach(device_t dev);
+static int acpi_button_suspend(device_t dev);
+static int acpi_button_resume(device_t dev);
+static void acpi_button_notify_handler(ACPI_HANDLE h,UINT32 notify, void *context);
+static void acpi_button_notify_pressed_for_sleep(void *arg);
+static void acpi_button_notify_pressed_for_wakeup(void *arg);
+
+static device_method_t acpi_button_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_button_probe),
+ DEVMETHOD(device_attach, acpi_button_attach),
+ DEVMETHOD(device_suspend, acpi_button_suspend),
+ DEVMETHOD(device_resume, acpi_button_resume),
+
+ {0, 0}
+};
+
+static driver_t acpi_button_driver = {
+ "acpi_button",
+ acpi_button_methods,
+ sizeof(struct acpi_button_softc),
+};
+
+static devclass_t acpi_button_devclass;
+DRIVER_MODULE(acpi_button, acpi, acpi_button_driver, acpi_button_devclass, 0, 0);
+
+static int
+acpi_button_probe(device_t dev)
+{
+ struct acpi_button_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (acpi_get_type(dev) == ACPI_TYPE_DEVICE) {
+ if (!acpi_disabled("button")) {
+ if (acpi_MatchHid(dev, "PNP0C0C")) {
+ device_set_desc(dev, "Power Button");
+ sc->button_type = ACPI_POWER_BUTTON;
+ return(0);
+ }
+ if (acpi_MatchHid(dev, "PNP0C0E")) {
+ device_set_desc(dev, "Sleep Button");
+ sc->button_type = ACPI_SLEEP_BUTTON;
+ return(0);
+ }
+ }
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_button_attach(device_t dev)
+{
+ struct acpi_button_softc *sc;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ sc->button_dev = dev;
+ sc->button_handle = acpi_get_handle(dev);
+
+ if (ACPI_FAILURE(status = AcpiInstallNotifyHandler(sc->button_handle, ACPI_DEVICE_NOTIFY,
+ acpi_button_notify_handler, sc))) {
+ device_printf(sc->button_dev, "couldn't install Notify handler - %s\n", AcpiFormatException(status));
+ return_VALUE(ENXIO);
+ }
+ acpi_device_enable_wake_capability(sc->button_handle, 1);
+ return_VALUE(0);
+}
+
+static int
+acpi_button_suspend(device_t dev)
+{
+ struct acpi_button_softc *sc;
+
+ sc = device_get_softc(dev);
+ acpi_device_enable_wake_event(sc->button_handle);
+ return (0);
+}
+
+static int
+acpi_button_resume(device_t dev)
+{
+
+ return (0);
+}
+
+static void
+acpi_button_notify_pressed_for_sleep(void *arg)
+{
+ struct acpi_button_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_button_softc *)arg;
+ acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
+ if (acpi_sc == NULL) {
+ return_VOID;
+ }
+
+ switch (sc->button_type) {
+ case ACPI_POWER_BUTTON:
+ ACPI_VPRINT(sc->button_dev, acpi_sc, "power button pressed\n");
+ acpi_eventhandler_power_button_for_sleep((void *)acpi_sc);
+ break;
+ case ACPI_SLEEP_BUTTON:
+ ACPI_VPRINT(sc->button_dev, acpi_sc, "sleep button pressed\n");
+ acpi_eventhandler_sleep_button_for_sleep((void *)acpi_sc);
+ break;
+ default:
+ break; /* unknown button type */
+ }
+ return_VOID;
+}
+
+static void
+acpi_button_notify_pressed_for_wakeup(void *arg)
+{
+ struct acpi_button_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_button_softc *)arg;
+ acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
+ if (acpi_sc == NULL) {
+ return_VOID;
+ }
+
+ switch (sc->button_type) {
+ case ACPI_POWER_BUTTON:
+ ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by power button\n");
+ acpi_eventhandler_power_button_for_wakeup((void *)acpi_sc);
+ break;
+ case ACPI_SLEEP_BUTTON:
+ ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by sleep button\n");
+ acpi_eventhandler_sleep_button_for_wakeup((void *)acpi_sc);
+ break;
+ default:
+ break; /* unknown button type */
+ }
+ return_VOID;
+}
+
+/* XXX maybe not here */
+#define ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP 0x80
+#define ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP 0x02
+
+static void
+acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_button_softc *sc = (struct acpi_button_softc *)context;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+ switch (notify) {
+ case ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP:
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_button_notify_pressed_for_sleep, sc);
+ break;
+ case ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP:
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_button_notify_pressed_for_wakeup, sc);
+ break;
+ default:
+ break; /* unknown notification value */
+ }
+ return_VOID;
+}
+
+
diff --git a/sys/dev/acpica/acpi_cmbat.c b/sys/dev/acpica/acpi_cmbat.c
new file mode 100644
index 0000000..1cfd584
--- /dev/null
+++ b/sys/dev/acpica/acpi_cmbat.c
@@ -0,0 +1,715 @@
+/*-
+ * Copyright (c) 2000 Munehiro Matsuda
+ * Copyright (c) 2000 Takanori Watanabe
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data");
+
+#define CMBAT_POLLRATE (60 * hz)
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BATTERY
+ACPI_MODULE_NAME("BATTERY")
+
+#define ACPI_BATTERY_BST_CHANGE 0x80
+#define ACPI_BATTERY_BIF_CHANGE 0x81
+
+#define PKG_GETINT(res, tmp, idx, dest, label) do { \
+ tmp = &res->Package.Elements[idx]; \
+ if (tmp == NULL) { \
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
+ "%s: PKG_GETINT error, idx = %d\n.", __func__, idx); \
+ goto label; \
+ } \
+ if (tmp->Type != ACPI_TYPE_INTEGER) \
+ goto label; \
+ dest = tmp->Integer.Value; \
+} while (0)
+
+#define PKG_GETSTR(res, tmp, idx, dest, size, label) do { \
+ size_t length; \
+ length = size; \
+ tmp = &res->Package.Elements[idx]; \
+ if (tmp == NULL) { \
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
+ "%s: PKG_GETSTR error, idx = %d\n.", __func__, idx); \
+ goto label; \
+ } \
+ bzero(dest, sizeof(dest)); \
+ switch (tmp->Type) { \
+ case ACPI_TYPE_STRING: \
+ if (tmp->String.Length < length) { \
+ length = tmp->String.Length; \
+ } \
+ strncpy(dest, tmp->String.Pointer, length); \
+ break; \
+ case ACPI_TYPE_BUFFER: \
+ if (tmp->Buffer.Length < length) { \
+ length = tmp->Buffer.Length; \
+ } \
+ strncpy(dest, tmp->Buffer.Pointer, length); \
+ break; \
+ default: \
+ goto label; \
+ } \
+ dest[sizeof(dest)-1] = '\0'; \
+} while (0)
+
+struct acpi_cmbat_softc {
+ device_t dev;
+
+ struct acpi_bif bif;
+ struct acpi_bst bst;
+ struct timespec bif_lastupdated;
+ struct timespec bst_lastupdated;
+ int bif_updating;
+ int bst_updating;
+
+ int present;
+ int cap;
+ int min;
+ int full_charge_time;
+ int initializing;
+};
+
+static struct timespec acpi_cmbat_info_lastupdated;
+
+/* XXX: devclass_get_maxunit() don't give us the current allocated units... */
+static int acpi_cmbat_units = 0;
+
+static int acpi_cmbat_info_expired(struct timespec *);
+static void acpi_cmbat_info_updated(struct timespec *);
+static void acpi_cmbat_get_bst(void *);
+static void acpi_cmbat_get_bif(void *);
+static void acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *);
+static int acpi_cmbat_probe(device_t);
+static int acpi_cmbat_attach(device_t);
+static int acpi_cmbat_resume(device_t);
+static int acpi_cmbat_ioctl(u_long, caddr_t, void *);
+static int acpi_cmbat_is_bst_valid(struct acpi_bst*);
+static int acpi_cmbat_is_bif_valid(struct acpi_bif*);
+static int acpi_cmbat_get_total_battinfo(struct acpi_battinfo *);
+static void acpi_cmbat_init_battery(void *);
+
+static __inline int
+acpi_cmbat_info_expired(struct timespec *lastupdated)
+{
+ struct timespec curtime;
+
+ if (lastupdated == NULL) {
+ return (1);
+ }
+
+ if (!timespecisset(lastupdated)) {
+ return (1);
+ }
+
+ getnanotime(&curtime);
+ timespecsub(&curtime, lastupdated);
+ return ((curtime.tv_sec < 0 || curtime.tv_sec > acpi_battery_get_info_expire()));
+}
+
+
+static __inline void
+acpi_cmbat_info_updated(struct timespec *lastupdated)
+{
+
+ if (lastupdated != NULL) {
+ getnanotime(lastupdated);
+ }
+}
+
+static void
+acpi_cmbat_get_bst(void *context)
+{
+ device_t dev;
+ struct acpi_cmbat_softc *sc;
+ ACPI_STATUS as;
+ ACPI_OBJECT *res, *tmp;
+ ACPI_HANDLE h;
+ ACPI_BUFFER bst_buffer;
+
+ dev = context;
+ sc = device_get_softc(dev);
+ h = acpi_get_handle(dev);
+ bst_buffer.Pointer = NULL;
+
+ if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) {
+ return;
+ }
+
+ if (sc->bst_updating) {
+ return;
+ }
+ sc->bst_updating = 1;
+
+ bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer))) {
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "error fetching current battery status -- %s\n",
+ AcpiFormatException(as));
+ goto end;
+ }
+
+ res = (ACPI_OBJECT *)bst_buffer.Pointer;
+
+ if ((res == NULL) || (res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 4)) {
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "battery status corrupted\n");
+ goto end;
+ }
+
+ PKG_GETINT(res, tmp, 0, sc->bst.state, end);
+ PKG_GETINT(res, tmp, 1, sc->bst.rate, end);
+ PKG_GETINT(res, tmp, 2, sc->bst.cap, end);
+ PKG_GETINT(res, tmp, 3, sc->bst.volt, end);
+ acpi_cmbat_info_updated(&sc->bst_lastupdated);
+end:
+ if (bst_buffer.Pointer != NULL)
+ AcpiOsFree(bst_buffer.Pointer);
+ sc->bst_updating = 0;
+}
+
+static void
+acpi_cmbat_get_bif(void *context)
+{
+ device_t dev;
+ struct acpi_cmbat_softc *sc;
+ ACPI_STATUS as;
+ ACPI_OBJECT *res, *tmp;
+ ACPI_HANDLE h;
+ ACPI_BUFFER bif_buffer;
+
+ dev = context;
+ sc = device_get_softc(dev);
+ h = acpi_get_handle(dev);
+ bif_buffer.Pointer = NULL;
+
+ if (!acpi_cmbat_info_expired(&sc->bif_lastupdated)) {
+ return;
+ }
+
+ if (sc->bif_updating) {
+ return;
+ }
+ sc->bif_updating = 1;
+
+ bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer))) {
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "error fetching current battery info -- %s\n",
+ AcpiFormatException(as));
+ goto end;
+ }
+
+ res = (ACPI_OBJECT *)bif_buffer.Pointer;
+
+ if ((res == NULL) || (res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 13)) {
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "battery info corrupted\n");
+ goto end;
+ }
+
+ PKG_GETINT(res, tmp, 0, sc->bif.unit, end);
+ PKG_GETINT(res, tmp, 1, sc->bif.dcap, end);
+ PKG_GETINT(res, tmp, 2, sc->bif.lfcap, end);
+ PKG_GETINT(res, tmp, 3, sc->bif.btech, end);
+ PKG_GETINT(res, tmp, 4, sc->bif.dvol, end);
+ PKG_GETINT(res, tmp, 5, sc->bif.wcap, end);
+ PKG_GETINT(res, tmp, 6, sc->bif.lcap, end);
+ PKG_GETINT(res, tmp, 7, sc->bif.gra1, end);
+ PKG_GETINT(res, tmp, 8, sc->bif.gra2, end);
+ PKG_GETSTR(res, tmp, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN, end);
+ PKG_GETSTR(res, tmp, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN, end);
+ PKG_GETSTR(res, tmp, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN, end);
+ PKG_GETSTR(res, tmp, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN, end);
+ acpi_cmbat_info_updated(&sc->bif_lastupdated);
+end:
+ if (bif_buffer.Pointer != NULL)
+ AcpiOsFree(bif_buffer.Pointer);
+ sc->bif_updating = 0;
+}
+
+static void
+acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ device_t dev;
+ struct acpi_cmbat_softc *sc;
+
+ dev = (device_t)context;
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return;
+ }
+
+ switch (notify) {
+ case ACPI_BATTERY_BST_CHANGE:
+ timespecclear(&sc->bst_lastupdated);
+ break;
+ case ACPI_BATTERY_BIF_CHANGE:
+ timespecclear(&sc->bif_lastupdated);
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+acpi_cmbat_probe(device_t dev)
+{
+
+ if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
+ !acpi_disabled("cmbat") &&
+ acpi_MatchHid(dev, "PNP0C0A")) {
+ /*
+ * Set device description.
+ */
+ device_set_desc(dev, "Control method Battery");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+acpi_cmbat_attach(device_t dev)
+{
+ int error;
+ ACPI_HANDLE handle;
+ struct acpi_cmbat_softc *sc;
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return (ENXIO);
+ }
+
+ handle = acpi_get_handle(dev);
+
+ AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
+ acpi_cmbat_notify_handler, dev);
+
+ sc->bif_updating = sc->bst_updating = 0;
+ sc->dev = dev;
+
+ timespecclear(&sc->bif_lastupdated);
+ timespecclear(&sc->bst_lastupdated);
+
+ if (acpi_cmbat_units == 0) {
+ if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF,
+ acpi_cmbat_ioctl, NULL)) != 0) {
+ return (error);
+ }
+ if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST,
+ acpi_cmbat_ioctl, NULL)) != 0) {
+ return (error);
+ }
+ }
+
+ if ((error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT,
+ acpi_cmbat_units)) != 0) {
+ return (error);
+ }
+
+ acpi_cmbat_units++;
+ timespecclear(&acpi_cmbat_info_lastupdated);
+ sc->initializing = 0;
+
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
+ return (0);
+}
+
+static int
+acpi_cmbat_resume(device_t dev)
+{
+
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
+ return (0);
+}
+
+static device_method_t acpi_cmbat_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_cmbat_probe),
+ DEVMETHOD(device_attach, acpi_cmbat_attach),
+ DEVMETHOD(device_resume, acpi_cmbat_resume),
+
+ {0, 0}
+};
+
+static driver_t acpi_cmbat_driver = {
+ "acpi_cmbat",
+ acpi_cmbat_methods,
+ sizeof(struct acpi_cmbat_softc),
+};
+
+static devclass_t acpi_cmbat_devclass;
+DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
+
+static int
+acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg)
+{
+ device_t dev;
+ union acpi_battery_ioctl_arg *ioctl_arg;
+ struct acpi_cmbat_softc *sc;
+ struct acpi_bif *bifp;
+ struct acpi_bst *bstp;
+
+ ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
+ if ((dev = devclass_get_device(acpi_cmbat_devclass,
+ ioctl_arg->unit)) == NULL) {
+ return (ENXIO);
+ }
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ return (ENXIO);
+ }
+
+ /*
+ * No security check required: information retrieval only. If
+ * new functions are added here, a check might be required.
+ */
+
+ switch (cmd) {
+ case ACPIIO_CMBAT_GET_BIF:
+ acpi_cmbat_get_bif(dev);
+ bifp = &ioctl_arg->bif;
+ bifp->unit = sc->bif.unit;
+ bifp->dcap = sc->bif.dcap;
+ bifp->lfcap = sc->bif.lfcap;
+ bifp->btech = sc->bif.btech;
+ bifp->dvol = sc->bif.dvol;
+ bifp->wcap = sc->bif.wcap;
+ bifp->lcap = sc->bif.lcap;
+ bifp->gra1 = sc->bif.gra1;
+ bifp->gra2 = sc->bif.gra2;
+ strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
+ strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
+ strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
+ strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
+ break;
+
+ case ACPIIO_CMBAT_GET_BST:
+ bstp = &ioctl_arg->bst;
+ if (acpi_BatteryIsPresent(dev)) {
+ acpi_cmbat_get_bst(dev);
+ bstp->state = sc->bst.state;
+ bstp->rate = sc->bst.rate;
+ bstp->cap = sc->bst.cap;
+ bstp->volt = sc->bst.volt;
+ } else
+ bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
+ break;
+ }
+
+ return (0);
+}
+
+static __inline int
+acpi_cmbat_is_bst_valid(struct acpi_bst *bst)
+{
+ if (bst->state >= ACPI_BATT_STAT_MAX ||
+ bst->cap == 0xffffffff ||
+ bst->volt == 0xffffffff) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static __inline int
+acpi_cmbat_is_bif_valid(struct acpi_bif *bif)
+{
+ if (bif->lfcap == 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo)
+{
+ int i;
+ int error;
+ int batt_stat;
+ int valid_rate, valid_units;
+ int cap, min;
+ int total_cap, total_min, total_full;
+ device_t dev;
+ struct acpi_cmbat_softc *sc;
+ static int bat_units = 0;
+ static struct acpi_cmbat_softc **bat = NULL;
+
+ cap = min = -1;
+ batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
+ error = 0;
+
+ /* Allocate array of softc pointers */
+ if (bat_units != acpi_cmbat_units) {
+ if (bat != NULL) {
+ free(bat, M_ACPICMBAT);
+ bat = NULL;
+ }
+ bat_units = 0;
+ }
+ if (bat == NULL) {
+ bat_units = acpi_cmbat_units;
+ bat = malloc(sizeof(struct acpi_cmbat_softc *) * bat_units,
+ M_ACPICMBAT, M_NOWAIT);
+ if (bat == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Collect softc pointers */
+ for (i = 0; i < acpi_cmbat_units; i++) {
+ if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ bat[i] = sc;
+ }
+ }
+
+ /* Get battery status, valid rate and valid units */
+ batt_stat = valid_rate = valid_units = 0;
+ for (i = 0; i < acpi_cmbat_units; i++) {
+ bat[i]->present = acpi_BatteryIsPresent(bat[i]->dev);
+ if (!bat[i]->present)
+ continue;
+
+ acpi_cmbat_get_bst(bat[i]->dev);
+
+ /* If battey not installed, we get strange values */
+ if (!acpi_cmbat_is_bst_valid(&(bat[i]->bst)) ||
+ !acpi_cmbat_is_bif_valid(&(bat[i]->bif))) {
+ bat[i]->present = 0;
+ continue;
+ }
+
+ valid_units++;
+
+ bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap;
+
+ batt_stat |= bat[i]->bst.state;
+
+ if (bat[i]->bst.rate > 0) {
+ /*
+ * XXX Hack to calculate total battery time.
+ * Systems with 2 or more battries, they may get used
+ * one by one, thus bst.rate is set only to the one
+ * in use. For remaining batteries bst.rate = 0, which
+ * makes it impossible to calculate remaining time.
+ * Some other systems may need sum of bst.rate in
+ * dis-charging state.
+ * There for we sum up the bst.rate that is valid
+ * (in dis-charging state), and use the sum to
+ * calcutate remaining batteries' time.
+ */
+ if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG) {
+ valid_rate += bat[i]->bst.rate;
+ }
+ }
+ }
+
+ /* Calculate total battery capacity and time */
+ total_cap = total_min = total_full = 0;
+ for (i = 0; i < acpi_cmbat_units; i++) {
+ if (!bat[i]->present) {
+ continue;
+ }
+
+ if (valid_rate > 0) {
+ /* Use the sum of bst.rate */
+ bat[i]->min = 60 * bat[i]->bst.cap / valid_rate;
+ } else if (bat[i]->full_charge_time > 0) {
+ bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100;
+ } else {
+ /* Couldn't find valid rate and full battery time */
+ bat[i]->min = 0;
+ }
+ total_min += bat[i]->min;
+ total_cap += bat[i]->cap;
+ total_full += bat[i]->full_charge_time;
+ }
+
+ /* Battery life */
+ if (valid_units == 0) {
+ cap = -1;
+ batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
+ } else {
+ cap = total_cap / valid_units;
+ }
+
+ /* Battery time */
+ if (valid_units == 0) {
+ min = -1;
+ } else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) {
+ if (total_full == 0) {
+ min = -1;
+ } else {
+ min = (total_full * cap) / 100;
+ }
+ } else {
+ min = total_min;
+ }
+
+ acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated);
+out:
+ battinfo->cap = cap;
+ battinfo->min = min;
+ battinfo->state = batt_stat;
+
+ return (error);
+}
+
+static void
+acpi_cmbat_init_battery(void *arg)
+{
+ int retry;
+ device_t dev = (device_t)arg;
+ struct acpi_cmbat_softc *sc = device_get_softc(dev);
+#define ACPI_CMBAT_RETRY_MAX 6
+
+ if (sc->initializing) {
+ return;
+ }
+
+ sc->initializing = 1;
+
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "battery initialization start\n");
+
+ for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10, 0)) {
+ sc->present = acpi_BatteryIsPresent(dev);
+ if (!sc->present) {
+ continue;
+ }
+
+ timespecclear(&sc->bst_lastupdated);
+ timespecclear(&sc->bif_lastupdated);
+
+ acpi_cmbat_get_bst(dev);
+
+ if (!acpi_cmbat_is_bst_valid(&sc->bst)) {
+ continue;
+ }
+
+ acpi_cmbat_get_bif(dev);
+
+ if (!acpi_cmbat_is_bif_valid(&sc->bif)) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (retry == ACPI_CMBAT_RETRY_MAX)
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "battery initialization failed, giving up\n");
+ else
+ ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
+ "battery initialization done, tried %d times\n",
+ retry+1);
+
+ sc->initializing = 0;
+}
+
+/*
+ * Public interfaces.
+ */
+
+int
+acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo)
+{
+ int error;
+ device_t dev;
+ struct acpi_cmbat_softc *sc;
+
+ if (unit == -1) {
+ return (acpi_cmbat_get_total_battinfo(battinfo));
+ }
+
+ if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) {
+ error = acpi_cmbat_get_total_battinfo(battinfo);
+ if (error) {
+ goto out;
+ }
+ }
+
+ error = 0;
+ if (unit >= acpi_cmbat_units) {
+ error = ENXIO;
+ goto out;
+ }
+
+ if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ if ((sc = device_get_softc(dev)) == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ if (!sc->present) {
+ battinfo->cap = -1;
+ battinfo->min = -1;
+ battinfo->state = ACPI_BATT_STAT_NOT_PRESENT;
+ } else {
+ battinfo->cap = sc->cap;
+ battinfo->min = sc->min;
+ battinfo->state = sc->bst.state;
+ }
+out:
+ return (error);
+}
+
diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c
new file mode 100644
index 0000000..b5b04fd
--- /dev/null
+++ b/sys/dev/acpica/acpi_cpu.c
@@ -0,0 +1,415 @@
+/*-
+ * Copyright (c) 2001 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/power.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Support for ACPI Processor devices.
+ *
+ * Note that this only provides ACPI 1.0 support (with the exception of the
+ * PSTATE_CNT field). 2.0 support will involve implementing _PTC, _PCT,
+ * _PSS and _PPC.
+ */
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_PROCESSOR
+ACPI_MODULE_NAME("PROCESSOR")
+
+struct acpi_cpu_softc {
+ device_t cpu_dev;
+ ACPI_HANDLE cpu_handle;
+
+ u_int32_t cpu_id;
+
+ /* CPU throttling control register */
+ struct resource *cpu_p_blk;
+#define CPU_GET_P_CNT(sc) (bus_space_read_4(rman_get_bustag((sc)->cpu_p_blk), \
+ rman_get_bushandle((sc)->cpu_p_blk), \
+ 0))
+#define CPU_SET_P_CNT(sc, val) (bus_space_write_4(rman_get_bustag((sc)->cpu_p_blk), \
+ rman_get_bushandle((sc)->cpu_p_blk), \
+ 0, (val)))
+#define CPU_P_CNT_THT_EN (1<<4)
+};
+
+/*
+ * Speeds are stored in counts, from 1 - CPU_MAX_SPEED, and
+ * reported to the user in tenths of a percent.
+ */
+static u_int32_t cpu_duty_offset;
+static u_int32_t cpu_duty_width;
+#define CPU_MAX_SPEED (1 << cpu_duty_width)
+#define CPU_SPEED_PERCENT(x) ((1000 * (x)) / CPU_MAX_SPEED)
+#define CPU_SPEED_PRINTABLE(x) (CPU_SPEED_PERCENT(x) / 10),(CPU_SPEED_PERCENT(x) % 10)
+
+static u_int32_t cpu_smi_cmd; /* should be a generic way to do this */
+static u_int8_t cpu_pstate_cnt;
+
+static u_int32_t cpu_current_state;
+static u_int32_t cpu_performance_state;
+static u_int32_t cpu_economy_state;
+static u_int32_t cpu_max_state;
+
+static device_t *cpu_devices;
+static int cpu_ndevices;
+
+static struct sysctl_ctx_list acpi_cpu_sysctl_ctx;
+static struct sysctl_oid *acpi_cpu_sysctl_tree;
+
+static int acpi_cpu_probe(device_t dev);
+static int acpi_cpu_attach(device_t dev);
+static void acpi_cpu_init_throttling(void *arg);
+static void acpi_cpu_set_speed(u_int32_t speed);
+static void acpi_cpu_power_profile(void *arg);
+static int acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS);
+
+static device_method_t acpi_cpu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_cpu_probe),
+ DEVMETHOD(device_attach, acpi_cpu_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_cpu_driver = {
+ "acpi_cpu",
+ acpi_cpu_methods,
+ sizeof(struct acpi_cpu_softc),
+};
+
+static devclass_t acpi_cpu_devclass;
+DRIVER_MODULE(acpi_cpu, acpi, acpi_cpu_driver, acpi_cpu_devclass, 0, 0);
+
+static int
+acpi_cpu_probe(device_t dev)
+{
+ if (!acpi_disabled("cpu") &&
+ (acpi_get_type(dev) == ACPI_TYPE_PROCESSOR)) {
+ device_set_desc(dev, "CPU"); /* XXX get more verbose description? */
+ return(0);
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_cpu_attach(device_t dev)
+{
+ struct acpi_cpu_softc *sc;
+ struct acpi_softc *acpi_sc;
+ ACPI_OBJECT processor;
+ ACPI_BUFFER buf;
+ ACPI_STATUS status;
+ u_int32_t p_blk;
+ u_int32_t p_blk_length;
+ u_int32_t duty_end;
+ int rid;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ sc = device_get_softc(dev);
+ sc->cpu_dev = dev;
+ sc->cpu_handle = acpi_get_handle(dev);
+
+ /*
+ * Get global parameters from the FADT.
+ */
+ if (device_get_unit(sc->cpu_dev) == 0) {
+ cpu_duty_offset = AcpiGbl_FADT->DutyOffset;
+ cpu_duty_width = AcpiGbl_FADT->DutyWidth;
+ cpu_smi_cmd = AcpiGbl_FADT->SmiCmd;
+ cpu_pstate_cnt = AcpiGbl_FADT->PstateCnt;
+
+ /* validate the offset/width */
+ if (cpu_duty_width > 0) {
+ duty_end = cpu_duty_offset + cpu_duty_width - 1;
+ /* check that it fits */
+ if (duty_end > 31) {
+ printf("acpi_cpu: CLK_VAL field overflows P_CNT register\n");
+ cpu_duty_width = 0;
+ }
+ /* check for overlap with the THT_EN bit */
+ if ((cpu_duty_offset <= 4) && (duty_end >= 4)) {
+ printf("acpi_cpu: CLK_VAL field overlaps THT_EN bit\n");
+ cpu_duty_width = 0;
+ }
+ }
+
+ /*
+ * Start the throttling process once the probe phase completes, if we think that
+ * it's going to be useful. If the duty width value is zero, there are no significant
+ * bits in the register and thus no throttled states.
+ */
+ if (cpu_duty_width > 0) {
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_init_throttling, NULL);
+
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ sysctl_ctx_init(&acpi_cpu_sysctl_ctx);
+ acpi_cpu_sysctl_tree = SYSCTL_ADD_NODE(&acpi_cpu_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
+ OID_AUTO, "cpu", CTLFLAG_RD, 0, "");
+
+ SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
+ OID_AUTO, "max_speed", CTLFLAG_RD,
+ &cpu_max_state, 0, "maximum CPU speed");
+ SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
+ OID_AUTO, "current_speed", CTLFLAG_RD,
+ &cpu_current_state, 0, "current CPU speed");
+ SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
+ OID_AUTO, "performance_speed", CTLTYPE_INT | CTLFLAG_RW,
+ &cpu_performance_state, 0, acpi_cpu_speed_sysctl, "I", "");
+ SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
+ OID_AUTO, "economy_speed", CTLTYPE_INT | CTLFLAG_RW,
+ &cpu_economy_state, 0, acpi_cpu_speed_sysctl, "I", "");
+ }
+ }
+
+ /*
+ * Get the processor object.
+ */
+ buf.Pointer = &processor;
+ buf.Length = sizeof(processor);
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf))) {
+ device_printf(sc->cpu_dev, "couldn't get Processor object - %s\n", AcpiFormatException(status));
+ return_VALUE(ENXIO);
+ }
+ if (processor.Type != ACPI_TYPE_PROCESSOR) {
+ device_printf(sc->cpu_dev, "Processor object has bad type %d\n", processor.Type);
+ return_VALUE(ENXIO);
+ }
+ sc->cpu_id = processor.Processor.ProcId;
+
+ /*
+ * If it looks like we support throttling, find this CPU's P_BLK.
+ *
+ * Note that some systems seem to duplicate the P_BLK pointer across
+ * multiple CPUs, so not getting the resource is not fatal.
+ *
+ * XXX should support _PTC here as well, once we work out how to parse it.
+ *
+ * XXX is it valid to assume that the P_BLK must be 6 bytes long?
+ */
+ if (cpu_duty_width > 0) {
+ p_blk = processor.Processor.PblkAddress;
+ p_blk_length = processor.Processor.PblkLength;
+
+ /* allocate bus space if possible */
+ if ((p_blk > 0) && (p_blk_length == 6)) {
+ rid = 0;
+ bus_set_resource(sc->cpu_dev, SYS_RES_IOPORT, rid, p_blk, p_blk_length);
+ sc->cpu_p_blk = bus_alloc_resource(sc->cpu_dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO, "acpi_cpu%d: throttling with P_BLK at 0x%x/%d%s\n",
+ device_get_unit(sc->cpu_dev), p_blk, p_blk_length,
+ sc->cpu_p_blk ? "" : " (shadowed)"));
+ }
+ }
+ return_VALUE(0);
+}
+
+/*
+ * Call this *after* all CPUs have been attached.
+ *
+ * Takes the ACPI lock to avoid fighting anyone over the SMI command
+ * port. Could probably lock less code.
+ */
+static void
+acpi_cpu_init_throttling(void *arg)
+{
+ int cpu_temp_speed;
+ ACPI_LOCK_DECL;
+
+ ACPI_LOCK;
+
+ /* get set of CPU devices */
+ devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices);
+
+ /* initialise throttling states */
+ cpu_max_state = CPU_MAX_SPEED;
+ cpu_performance_state = cpu_max_state;
+ cpu_economy_state = cpu_performance_state / 2;
+ if (cpu_economy_state == 0) /* 0 is 'reserved' */
+ cpu_economy_state++;
+ if (TUNABLE_INT_FETCH("hw.acpi.cpu.performance_speed",
+ &cpu_temp_speed) && cpu_temp_speed > 0 &&
+ cpu_temp_speed <= cpu_max_state)
+ cpu_performance_state = cpu_temp_speed;
+ if (TUNABLE_INT_FETCH("hw.acpi.cpu.economy_speed",
+ &cpu_temp_speed) && cpu_temp_speed > 0 &&
+ cpu_temp_speed <= cpu_max_state)
+ cpu_economy_state = cpu_temp_speed;
+
+ /* register performance profile change handler */
+ EVENTHANDLER_REGISTER(power_profile_change, acpi_cpu_power_profile, NULL, 0);
+
+ /* if ACPI 2.0+, signal platform that we are taking over throttling */
+ if (cpu_pstate_cnt != 0) {
+ /* XXX should be a generic interface for this */
+ AcpiOsWritePort(cpu_smi_cmd, cpu_pstate_cnt, 8);
+ }
+
+ ACPI_UNLOCK;
+
+ /* set initial speed */
+ acpi_cpu_power_profile(NULL);
+
+ printf("acpi_cpu: throttling enabled, %d steps (100%% to %d.%d%%), "
+ "currently %d.%d%%\n", CPU_MAX_SPEED, CPU_SPEED_PRINTABLE(1),
+ CPU_SPEED_PRINTABLE(cpu_current_state));
+}
+
+/*
+ * Set CPUs to the new state.
+ *
+ * Must be called with the ACPI lock held.
+ */
+static void
+acpi_cpu_set_speed(u_int32_t speed)
+{
+ struct acpi_cpu_softc *sc;
+ int i;
+ u_int32_t p_cnt, clk_val;
+
+ ACPI_ASSERTLOCK;
+
+ /* iterate over processors */
+ for (i = 0; i < cpu_ndevices; i++) {
+ sc = device_get_softc(cpu_devices[i]);
+ if (sc->cpu_p_blk == NULL)
+ continue;
+
+ /* get the current P_CNT value and disable throttling */
+ p_cnt = CPU_GET_P_CNT(sc);
+ p_cnt &= ~CPU_P_CNT_THT_EN;
+ CPU_SET_P_CNT(sc, p_cnt);
+
+ /* if we're at maximum speed, that's all */
+ if (speed < CPU_MAX_SPEED) {
+
+ /* mask the old CLK_VAL off and or-in the new value */
+ clk_val = CPU_MAX_SPEED << cpu_duty_offset;
+ p_cnt &= ~clk_val;
+ p_cnt |= (speed << cpu_duty_offset);
+
+ /* write the new P_CNT value and then enable throttling */
+ CPU_SET_P_CNT(sc, p_cnt);
+ p_cnt |= CPU_P_CNT_THT_EN;
+ CPU_SET_P_CNT(sc, p_cnt);
+ }
+ ACPI_VPRINT(sc->cpu_dev, acpi_device_get_parent_softc(sc->cpu_dev),
+ "set speed to %d.%d%%\n", CPU_SPEED_PRINTABLE(speed));
+ }
+ cpu_current_state = speed;
+}
+
+/*
+ * Power profile change hook.
+ *
+ * Uses the ACPI lock to avoid reentrancy.
+ */
+static void
+acpi_cpu_power_profile(void *arg)
+{
+ int state;
+ u_int32_t new;
+ ACPI_LOCK_DECL;
+
+ state = power_profile_get_state();
+ if (state != POWER_PROFILE_PERFORMANCE &&
+ state != POWER_PROFILE_ECONOMY) {
+ return;
+ }
+
+ ACPI_LOCK;
+
+ switch (state) {
+ case POWER_PROFILE_PERFORMANCE:
+ new = cpu_performance_state;
+ break;
+ case POWER_PROFILE_ECONOMY:
+ new = cpu_economy_state;
+ break;
+ default:
+ new = cpu_current_state;
+ break;
+ }
+
+ if (cpu_current_state != new)
+ acpi_cpu_set_speed(new);
+
+ ACPI_UNLOCK;
+}
+
+/*
+ * Handle changes in the performance/ecomony CPU settings.
+ *
+ * Does not need the ACPI lock (although setting *argp should
+ * probably be atomic).
+ */
+static int
+acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ u_int32_t *argp;
+ u_int32_t arg;
+ int error;
+
+ argp = (u_int32_t *)oidp->oid_arg1;
+ arg = *argp;
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ /* error or no new value */
+ if ((error != 0) || (req->newptr == NULL))
+ return(error);
+
+ /* range check */
+ if ((arg < 1) || (arg > cpu_max_state))
+ return(EINVAL);
+
+ /* set new value and possibly switch */
+ *argp = arg;
+ acpi_cpu_power_profile(NULL);
+
+ return(0);
+}
diff --git a/sys/dev/acpica/acpi_ec.c b/sys/dev/acpica/acpi_ec.c
new file mode 100644
index 0000000..680d3f4
--- /dev/null
+++ b/sys/dev/acpica/acpi_ec.c
@@ -0,0 +1,848 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/******************************************************************************
+ *
+ * 1. Copyright Notice
+ *
+ * Some or all of this work - Copyright (c) 1999, Intel Corp. All rights
+ * reserved.
+ *
+ * 2. License
+ *
+ * 2.1. This is your license from Intel Corp. under its intellectual property
+ * rights. You may have additional license terms from the party that provided
+ * you this software, covering your right to use that party's intellectual
+ * property rights.
+ *
+ * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
+ * copy of the source code appearing in this file ("Covered Code") an
+ * irrevocable, perpetual, worldwide license under Intel's copyrights in the
+ * base code distributed originally by Intel ("Original Intel Code") to copy,
+ * make derivatives, distribute, use and display any portion of the Covered
+ * Code in any form, with the right to sublicense such rights; and
+ *
+ * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
+ * license (with the right to sublicense), under only those claims of Intel
+ * patents that are infringed by the Original Intel Code, to make, use, sell,
+ * offer to sell, and import the Covered Code and derivative works thereof
+ * solely to the minimum extent necessary to exercise the above copyright
+ * license, and in no event shall the patent license extend to any additions
+ * to or modifications of the Original Intel Code. No other license or right
+ * is granted directly or by implication, estoppel or otherwise;
+ *
+ * The above copyright and patent license is granted only if the following
+ * conditions are met:
+ *
+ * 3. Conditions
+ *
+ * 3.1. Redistribution of Source with Rights to Further Distribute Source.
+ * Redistribution of source code of any substantial portion of the Covered
+ * Code or modification with rights to further distribute source must include
+ * the above Copyright Notice, the above License, this list of Conditions,
+ * and the following Disclaimer and Export Compliance provision. In addition,
+ * Licensee must cause all Covered Code to which Licensee contributes to
+ * contain a file documenting the changes Licensee made to create that Covered
+ * Code and the date of any change. Licensee must include in that file the
+ * documentation of any changes made by any predecessor Licensee. Licensee
+ * must include a prominent statement that the modification is derived,
+ * directly or indirectly, from Original Intel Code.
+ *
+ * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
+ * Redistribution of source code of any substantial portion of the Covered
+ * Code or modification without rights to further distribute source must
+ * include the following Disclaimer and Export Compliance provision in the
+ * documentation and/or other materials provided with distribution. In
+ * addition, Licensee may not authorize further sublicense of source of any
+ * portion of the Covered Code, and must include terms to the effect that the
+ * license from Licensee to its licensee is limited to the intellectual
+ * property embodied in the software Licensee provides to its licensee, and
+ * not to intellectual property embodied in modifications its licensee may
+ * make.
+ *
+ * 3.3. Redistribution of Executable. Redistribution in executable form of any
+ * substantial portion of the Covered Code or modification must reproduce the
+ * above Copyright Notice, and the following Disclaimer and Export Compliance
+ * provision in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3.4. Intel retains all right, title, and interest in and to the Original
+ * Intel Code.
+ *
+ * 3.5. Neither the name Intel nor any other trademark owned or controlled by
+ * Intel shall be used in advertising or otherwise to promote the sale, use or
+ * other dealings in products derived from or relating to the Covered Code
+ * without prior written authorization from Intel.
+ *
+ * 4. Disclaimer and Export Compliance
+ *
+ * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
+ * HERE. ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
+ * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT, ASSISTANCE,
+ * INSTALLATION, TRAINING OR OTHER SERVICES. INTEL WILL NOT PROVIDE ANY
+ * UPDATES, ENHANCEMENTS OR EXTENSIONS. INTEL SPECIFICALLY DISCLAIMS ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
+ * PARTICULAR PURPOSE.
+ *
+ * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
+ * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
+ * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
+ * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
+ * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
+ * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS
+ * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
+ * LIMITED REMEDY.
+ *
+ * 4.3. Licensee shall not export, either directly or indirectly, any of this
+ * software or system incorporating such software without first obtaining any
+ * required license or other approval from the U. S. Department of Commerce or
+ * any other agency or department of the United States Government. In the
+ * event Licensee exports any such software from the United States or
+ * re-exports any such software from a foreign destination, Licensee shall
+ * ensure that the distribution and export/re-export of the software is in
+ * compliance with all laws, regulations, orders, or other restrictions of the
+ * U.S. Export Administration Regulations. Licensee agrees that neither it nor
+ * any of its subsidiaries will export/re-export any technical data, process,
+ * software, or service, directly or indirectly, to any country for which the
+ * United States government or any agency thereof requires an export license,
+ * other governmental approval, or letter of assurance, without first obtaining
+ * such license, approval or letter.
+ *
+ *****************************************************************************/
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_EC
+ACPI_MODULE_NAME("EC")
+
+/*
+ * EC_COMMAND:
+ * -----------
+ */
+typedef UINT8 EC_COMMAND;
+
+#define EC_COMMAND_UNKNOWN ((EC_COMMAND) 0x00)
+#define EC_COMMAND_READ ((EC_COMMAND) 0x80)
+#define EC_COMMAND_WRITE ((EC_COMMAND) 0x81)
+#define EC_COMMAND_BURST_ENABLE ((EC_COMMAND) 0x82)
+#define EC_COMMAND_BURST_DISABLE ((EC_COMMAND) 0x83)
+#define EC_COMMAND_QUERY ((EC_COMMAND) 0x84)
+
+/*
+ * EC_STATUS:
+ * ----------
+ * The encoding of the EC status register is illustrated below.
+ * Note that a set bit (1) indicates the property is TRUE
+ * (e.g. if bit 0 is set then the output buffer is full).
+ * +-+-+-+-+-+-+-+-+
+ * |7|6|5|4|3|2|1|0|
+ * +-+-+-+-+-+-+-+-+
+ * | | | | | | | |
+ * | | | | | | | +- Output Buffer Full?
+ * | | | | | | +--- Input Buffer Full?
+ * | | | | | +----- <reserved>
+ * | | | | +------- Data Register is Command Byte?
+ * | | | +--------- Burst Mode Enabled?
+ * | | +----------- SCI Event?
+ * | +------------- SMI Event?
+ * +--------------- <Reserved>
+ *
+ */
+typedef UINT8 EC_STATUS;
+
+#define EC_FLAG_OUTPUT_BUFFER ((EC_STATUS) 0x01)
+#define EC_FLAG_INPUT_BUFFER ((EC_STATUS) 0x02)
+#define EC_FLAG_BURST_MODE ((EC_STATUS) 0x10)
+#define EC_FLAG_SCI ((EC_STATUS) 0x20)
+
+/*
+ * EC_EVENT:
+ * ---------
+ */
+typedef UINT8 EC_EVENT;
+
+#define EC_EVENT_UNKNOWN ((EC_EVENT) 0x00)
+#define EC_EVENT_OUTPUT_BUFFER_FULL ((EC_EVENT) 0x01)
+#define EC_EVENT_INPUT_BUFFER_EMPTY ((EC_EVENT) 0x02)
+#define EC_EVENT_SCI ((EC_EVENT) 0x20)
+
+/*
+ * Register access primitives
+ */
+#define EC_GET_DATA(sc) \
+ bus_space_read_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0)
+
+#define EC_SET_DATA(sc, v) \
+ bus_space_write_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0, (v))
+
+#define EC_GET_CSR(sc) \
+ bus_space_read_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0)
+
+#define EC_SET_CSR(sc, v) \
+ bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v))
+
+/*
+ * Driver softc.
+ */
+struct acpi_ec_softc {
+ device_t ec_dev;
+ ACPI_HANDLE ec_handle;
+ UINT32 ec_gpebit;
+
+ int ec_data_rid;
+ struct resource *ec_data_res;
+ bus_space_tag_t ec_data_tag;
+ bus_space_handle_t ec_data_handle;
+
+ int ec_csr_rid;
+ struct resource *ec_csr_res;
+ bus_space_tag_t ec_csr_tag;
+ bus_space_handle_t ec_csr_handle;
+
+ int ec_locked;
+ int ec_lockhandle;
+ int ec_pendquery;
+ int ec_csrvalue;
+};
+
+static int acpi_ec_event_driven = 0;
+TUNABLE_INT("hw.acpi.ec.event_driven", &acpi_ec_event_driven);
+
+#define EC_LOCK_TIMEOUT 1000 /* 1ms */
+
+static __inline ACPI_STATUS
+EcLock(struct acpi_ec_softc *sc)
+{
+ ACPI_STATUS status;
+
+ /* XXX ACPI_WAIT_FOREVER is probably a bad idea, what is a better time? */
+ if (ACPI_SUCCESS(status = AcpiAcquireGlobalLock(ACPI_WAIT_FOREVER, &sc->ec_lockhandle)))
+ (sc)->ec_locked = 1;
+
+ return(status);
+}
+
+static __inline void
+EcUnlock(struct acpi_ec_softc *sc)
+{
+ (sc)->ec_locked = 0;
+ AcpiReleaseGlobalLock(sc->ec_lockhandle);
+}
+
+static __inline int
+EcIsLocked(struct acpi_ec_softc *sc)
+{
+ return((sc)->ec_locked != 0);
+}
+
+typedef struct
+{
+ EC_COMMAND Command;
+ UINT8 Address;
+ UINT8 Data;
+} EC_REQUEST;
+
+static void EcGpeHandler(void *Context);
+static ACPI_STATUS EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
+ void *Context, void **return_Context);
+static ACPI_STATUS EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width, ACPI_INTEGER *Value,
+ void *Context, void *RegionContext);
+
+static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event);
+static ACPI_STATUS EcQuery(struct acpi_ec_softc *sc, UINT8 *Data);
+static ACPI_STATUS EcTransaction(struct acpi_ec_softc *sc, EC_REQUEST *EcRequest);
+static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data);
+static ACPI_STATUS EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data);
+
+static void acpi_ec_identify(driver_t driver, device_t bus);
+static int acpi_ec_probe(device_t dev);
+static int acpi_ec_attach(device_t dev);
+
+static device_method_t acpi_ec_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, acpi_ec_identify),
+ DEVMETHOD(device_probe, acpi_ec_probe),
+ DEVMETHOD(device_attach, acpi_ec_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_ec_driver = {
+ "acpi_ec",
+ acpi_ec_methods,
+ sizeof(struct acpi_ec_softc),
+};
+
+static devclass_t acpi_ec_devclass;
+DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0);
+
+/*
+ * Look for an ECDT table and if we find one, set up a default EC
+ * space handler to catch possible attempts to access EC space before
+ * we have a real driver instance in place.
+ * We're not really an identify routine, but because we get called
+ * before most other things, this works out OK.
+ */
+static void
+acpi_ec_identify(driver_t driver, device_t bus)
+{
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /* XXX implement - need an ACPI 2.0 system to test this */
+
+ return_VOID;
+}
+
+/*
+ * We could setup resources in the probe routine in order to have them printed
+ * when the device is attached.
+ */
+static int
+acpi_ec_probe(device_t dev)
+{
+
+ if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
+ !acpi_disabled("ec") &&
+ acpi_MatchHid(dev, "PNP0C09")) {
+
+ /*
+ * Set device description
+ */
+ device_set_desc(dev, "embedded controller");
+
+ return(0);
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_ec_attach(device_t dev)
+{
+ struct acpi_ec_softc *sc;
+ ACPI_STATUS Status;
+ int errval = 0;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Fetch/initialise softc
+ */
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(*sc));
+ sc->ec_dev = dev;
+ sc->ec_handle = acpi_get_handle(dev);
+
+ /*
+ * Attach bus resources
+ */
+ sc->ec_data_rid = 0;
+ if ((sc->ec_data_res = bus_alloc_resource(sc->ec_dev, SYS_RES_IOPORT, &sc->ec_data_rid,
+ 0, ~0, 1, RF_ACTIVE)) == NULL) {
+ device_printf(dev, "can't allocate data port\n");
+ errval = ENXIO;
+ goto out;
+ }
+ sc->ec_data_tag = rman_get_bustag(sc->ec_data_res);
+ sc->ec_data_handle = rman_get_bushandle(sc->ec_data_res);
+
+ sc->ec_csr_rid = 1;
+ if ((sc->ec_csr_res = bus_alloc_resource(sc->ec_dev, SYS_RES_IOPORT, &sc->ec_csr_rid,
+ 0, ~0, 1, RF_ACTIVE)) == NULL) {
+ device_printf(dev, "can't allocate command/status port\n");
+ errval = ENXIO;
+ goto out;
+ }
+ sc->ec_csr_tag = rman_get_bustag(sc->ec_csr_res);
+ sc->ec_csr_handle = rman_get_bushandle(sc->ec_csr_res);
+
+ /*
+ * Install GPE handler
+ *
+ * Evaluate the _GPE method to find the GPE bit used by the EC to signal
+ * status (SCI).
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching GPE\n"));
+ if (ACPI_FAILURE(Status = acpi_EvaluateInteger(sc->ec_handle, "_GPE", &sc->ec_gpebit))) {
+ device_printf(dev, "can't evaluate _GPE - %s\n", AcpiFormatException(Status));
+ errval =ENXIO;
+ goto out;
+ }
+
+ /*
+ * Install a handler for this EC's GPE bit. Note that EC SCIs are
+ * treated as both edge- and level-triggered interrupts; in other words
+ * we clear the status bit immediately after getting an EC-SCI, then
+ * again after we're done processing the event. This guarantees that
+ * events we cause while performing a transaction (e.g. IBE/OBF) get
+ * cleared before re-enabling the GPE.
+ */
+ if (ACPI_FAILURE(Status = AcpiInstallGpeHandler(sc->ec_gpebit,
+ ACPI_EVENT_LEVEL_TRIGGERED |
+ ACPI_EVENT_EDGE_TRIGGERED,
+ EcGpeHandler, sc))) {
+ device_printf(dev, "can't install GPE handler for %s - %s\n",
+ acpi_name(sc->ec_handle), AcpiFormatException(Status));
+ errval = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Install address space handler
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n"));
+ if (ACPI_FAILURE(Status = AcpiInstallAddressSpaceHandler(sc->ec_handle,
+ ACPI_ADR_SPACE_EC,
+ EcSpaceHandler,
+ EcSpaceSetup,
+ sc))) {
+ device_printf(dev, "can't install address space handler for %s - %s\n",
+ acpi_name(sc->ec_handle), AcpiFormatException(Status));
+ panic("very suck");
+ errval = ENXIO;
+ goto out;
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attach complete\n"));
+ return_VALUE(0);
+ out:
+ if(sc->ec_csr_res)
+ bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid,
+ sc->ec_csr_res);
+ if(sc->ec_data_res)
+ bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid,
+ sc->ec_data_res);
+ return_VALUE(errval);
+}
+
+static void
+EcGpeQueryHandler(void *Context)
+{
+ struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context;
+ UINT8 Data;
+ ACPI_STATUS Status;
+ char qxx[5];
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ for (;;) {
+
+ /*
+ * Check EC_SCI.
+ *
+ * Bail out if the EC_SCI bit of the status register is not set.
+ * Note that this function should only be called when
+ * this bit is set (polling is used to detect IBE/OBF events).
+ *
+ * It is safe to do this without locking the controller, as it's
+ * OK to call EcQuery when there's no data ready; in the worst
+ * case we should just find nothing waiting for us and bail.
+ */
+ if (!(EC_GET_CSR(sc) & EC_EVENT_SCI))
+ break;
+
+ /*
+ * Find out why the EC is signalling us
+ */
+ Status = EcQuery(sc, &Data);
+
+ /*
+ * If we failed to get anything from the EC, give up
+ */
+ if (ACPI_FAILURE(Status)) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "GPE query failed - %s\n", AcpiFormatException(Status));
+ break;
+ }
+
+ /*
+ * Evaluate _Qxx to respond to the controller.
+ */
+ sprintf(qxx, "_Q%02x", Data);
+ strupr(qxx);
+ Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
+ /*
+ * Ignore spurious query requests.
+ */
+ if (ACPI_FAILURE(Status) && (Data != 0 || Status != AE_NOT_FOUND)) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "evaluation of GPE query method %s failed - %s\n",
+ qxx, AcpiFormatException(Status));
+ }
+ }
+ /* I know I request Level trigger cleanup */
+ if (ACPI_FAILURE(AcpiClearEvent(sc->ec_gpebit, ACPI_EVENT_GPE)))
+ printf("EcGpeQueryHandler:ClearEvent Failed\n");
+ if (ACPI_FAILURE(AcpiEnableEvent(sc->ec_gpebit, ACPI_EVENT_GPE, 0)))
+ printf("EcGpeQueryHandler:EnableEvent Failed\n");
+ return_VOID;
+}
+
+/*
+ * Handle a GPE sent to us.
+ */
+static void
+EcGpeHandler(void *Context)
+{
+ struct acpi_ec_softc *sc = Context;
+ int csrvalue;
+
+ /*
+ * If EC is locked, the intr must process EcRead/Write wait only.
+ * Query request must be pending.
+ */
+ if (EcIsLocked(sc)){
+ csrvalue = EC_GET_CSR(sc);
+ if (csrvalue & EC_EVENT_SCI)
+ sc->ec_pendquery = 1;
+ if ((csrvalue & EC_FLAG_OUTPUT_BUFFER)
+ || !(csrvalue & EC_FLAG_INPUT_BUFFER)) {
+ sc->ec_csrvalue = csrvalue;
+ wakeup((void *)&sc->ec_csrvalue);
+ }
+ }else{
+ /* Queue GpeQuery Handler */
+ if (ACPI_FAILURE(AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
+ EcGpeQueryHandler,Context))) {
+ printf("QueryHandler Queuing Failed\n");
+ }
+ }
+ return;
+}
+
+static ACPI_STATUS
+EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, void *Context, void **RegionContext)
+{
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Just pass the context through, there's nothing to do here.
+ */
+ *RegionContext = Context;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+static ACPI_STATUS
+EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width, ACPI_INTEGER *Value,
+ void *Context, void *RegionContext)
+{
+ struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context;
+ ACPI_STATUS Status = AE_OK;
+ EC_REQUEST EcRequest;
+ int i;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address);
+
+ if ((Address > 0xFF) || (width % 8 != 0) || (Value == NULL) || (Context == NULL))
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ switch (Function) {
+ case ACPI_READ:
+ EcRequest.Command = EC_COMMAND_READ;
+ EcRequest.Address = Address;
+ (*Value) = 0;
+ break;
+
+ case ACPI_WRITE:
+ EcRequest.Command = EC_COMMAND_WRITE;
+ EcRequest.Address = Address;
+ break;
+
+ default:
+ device_printf(sc->ec_dev, "invalid Address Space function %d\n", Function);
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Perform the transaction.
+ */
+ for (i = 0; i < width; i += 8) {
+ if (Function == ACPI_READ)
+ EcRequest.Data = 0;
+ else
+ EcRequest.Data = (UINT8)((*Value) >> i);
+ if (ACPI_FAILURE(Status = EcTransaction(sc, &EcRequest)))
+ break;
+ (*Value) |= (ACPI_INTEGER)EcRequest.Data << i;
+ if (++EcRequest.Address == 0)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ return_ACPI_STATUS(Status);
+}
+
+/*
+ * Wait for an event interrupt for a specific condition.
+ */
+static ACPI_STATUS
+EcWaitEventIntr(struct acpi_ec_softc *sc, EC_EVENT Event)
+{
+ EC_STATUS EcStatus;
+ int i;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Event);
+
+ /* XXX this should test whether interrupts are available some other way */
+ if (cold || acpi_ec_event_driven)
+ return_ACPI_STATUS(EcWaitEvent(sc, Event));
+
+ if (!EcIsLocked(sc))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcWaitEventIntr called without EC lock!\n");
+
+ EcStatus = EC_GET_CSR(sc);
+
+ /* XXX waiting too long? */
+ for(i = 0; i < 10; i++){
+ /*
+ * Check EC status against the desired event.
+ */
+ if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) &&
+ (EcStatus & EC_FLAG_OUTPUT_BUFFER))
+ return_ACPI_STATUS(AE_OK);
+
+ if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) &&
+ !(EcStatus & EC_FLAG_INPUT_BUFFER))
+ return_ACPI_STATUS(AE_OK);
+
+ sc->ec_csrvalue = 0;
+ if (ACPI_MSLEEP(&sc->ec_csrvalue, &acpi_mutex, PZERO, "EcWait", 1) != EWOULDBLOCK){
+ EcStatus = sc->ec_csrvalue;
+ }else{
+ EcStatus = EC_GET_CSR(sc);
+ }
+ }
+ return_ACPI_STATUS(AE_ERROR);
+}
+
+static ACPI_STATUS
+EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
+{
+ EC_STATUS EcStatus;
+ UINT32 i = 0;
+
+ if (!EcIsLocked(sc))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcWaitEvent called without EC lock!\n");
+
+ /*
+ * Stall 1us:
+ * ----------
+ * Stall for 1 microsecond before reading the status register
+ * for the first time. This allows the EC to set the IBF/OBF
+ * bit to its proper state.
+ *
+ * XXX it is not clear why we read the CSR twice.
+ */
+ AcpiOsStall(1);
+ EcStatus = EC_GET_CSR(sc);
+
+ /*
+ * Wait For Event:
+ * ---------------
+ * Poll the EC status register to detect completion of the last
+ * command. Wait up to 10ms (in 10us chunks) for this to occur.
+ */
+ for (i = 0; i < 1000; i++) {
+ EcStatus = EC_GET_CSR(sc);
+
+ if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) &&
+ (EcStatus & EC_FLAG_OUTPUT_BUFFER))
+ return(AE_OK);
+
+ if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) &&
+ !(EcStatus & EC_FLAG_INPUT_BUFFER))
+ return(AE_OK);
+
+ AcpiOsStall(10);
+ }
+
+ return(AE_ERROR);
+}
+
+static ACPI_STATUS
+EcQuery(struct acpi_ec_softc *sc, UINT8 *Data)
+{
+ ACPI_STATUS Status;
+
+ if (ACPI_FAILURE(Status = EcLock(sc)))
+ return(Status);
+
+ EC_SET_CSR(sc, EC_COMMAND_QUERY);
+ if (ACPI_SUCCESS(Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL)))
+ *Data = EC_GET_DATA(sc);
+
+ EcUnlock(sc);
+
+ if (ACPI_FAILURE(Status))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "timeout waiting for EC to respond to EC_COMMAND_QUERY\n");
+ return(Status);
+}
+
+static ACPI_STATUS
+EcTransaction(struct acpi_ec_softc *sc, EC_REQUEST *EcRequest)
+{
+ ACPI_STATUS Status;
+
+ /*
+ * Lock the EC
+ */
+ if (ACPI_FAILURE(Status = EcLock(sc)))
+ return(Status);
+
+ /*
+ * Perform the transaction.
+ */
+ switch (EcRequest->Command) {
+ case EC_COMMAND_READ:
+ Status = EcRead(sc, EcRequest->Address, &(EcRequest->Data));
+ break;
+
+ case EC_COMMAND_WRITE:
+ Status = EcWrite(sc, EcRequest->Address, &(EcRequest->Data));
+ break;
+
+ default:
+ Status = AE_SUPPORT;
+ break;
+ }
+
+ /*
+ * Unlock the EC
+ */
+ EcUnlock(sc);
+
+ /*
+ * Clear & Re-Enable the EC GPE:
+ * -----------------------------
+ * 'Consume' any EC GPE events that we generated while performing
+ * the transaction (e.g. IBF/OBF). Clearing the GPE here shouldn't
+ * have an adverse affect on outstanding EC-SCI's, as the source
+ * (EC-SCI) will still be high and thus should trigger the GPE
+ * immediately after we re-enabling it.
+ */
+ if (sc->ec_pendquery){
+ if (ACPI_FAILURE(AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
+ EcGpeQueryHandler, sc)))
+ printf("Pend Query Queuing Failed\n");
+ sc->ec_pendquery = 0;
+ }
+
+ if (ACPI_FAILURE(AcpiClearEvent(sc->ec_gpebit, ACPI_EVENT_GPE)))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRequest: Unable to clear the EC GPE.\n");
+ if (ACPI_FAILURE(AcpiEnableEvent(sc->ec_gpebit, ACPI_EVENT_GPE, 0)))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRequest: Unable to re-enable the EC GPE.\n");
+
+ return(Status);
+}
+
+
+static ACPI_STATUS
+EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
+{
+ ACPI_STATUS Status;
+
+ if (!EcIsLocked(sc))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRead called without EC lock!\n");
+
+ /*EcBurstEnable(EmbeddedController);*/
+
+ EC_SET_CSR(sc, EC_COMMAND_READ);
+ if (ACPI_FAILURE(Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY))) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRead: Failed waiting for EC to process read command.\n");
+ return(Status);
+ }
+
+ EC_SET_DATA(sc, Address);
+ if (ACPI_FAILURE(Status = EcWaitEventIntr(sc, EC_EVENT_OUTPUT_BUFFER_FULL))) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRead: Failed waiting for EC to send data.\n");
+ return(Status);
+ }
+
+ (*Data) = EC_GET_DATA(sc);
+
+ /*EcBurstDisable(EmbeddedController);*/
+
+ return(AE_OK);
+}
+
+static ACPI_STATUS
+EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
+{
+ ACPI_STATUS Status;
+
+ if (!EcIsLocked(sc))
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcWrite called without EC lock!\n");
+
+ /*EcBurstEnable(EmbeddedController);*/
+
+ EC_SET_CSR(sc, EC_COMMAND_WRITE);
+ if (ACPI_FAILURE(Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY))) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcWrite: Failed waiting for EC to process write command.\n");
+ return(Status);
+ }
+
+ EC_SET_DATA(sc, Address);
+ if (ACPI_FAILURE(Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY))) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcRead: Failed waiting for EC to process address.\n");
+ return(Status);
+ }
+
+ EC_SET_DATA(sc, *Data);
+ if (ACPI_FAILURE(Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY))) {
+ ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
+ "EcWrite: Failed waiting for EC to process data.\n");
+ return(Status);
+ }
+
+ /*EcBurstDisable(EmbeddedController);*/
+
+ return(AE_OK);
+}
diff --git a/sys/dev/acpica/acpi_lid.c b/sys/dev/acpica/acpi_lid.c
new file mode 100644
index 0000000..5a4b8cc
--- /dev/null
+++ b/sys/dev/acpica/acpi_lid.c
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 2000 Takanori Watanabe <takawata@jp.freebsd.org>
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2000 Michael Smith <msmith@freebd.org>
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUTTON
+ACPI_MODULE_NAME("LID")
+
+struct acpi_lid_softc {
+ device_t lid_dev;
+ ACPI_HANDLE lid_handle;
+ int lid_status; /* open or closed */
+};
+
+static int acpi_lid_probe(device_t dev);
+static int acpi_lid_attach(device_t dev);
+static int acpi_lid_suspend(device_t dev);
+static int acpi_lid_resume(device_t dev);
+static void acpi_lid_notify_status_changed(void *arg);
+static void acpi_lid_notify_handler(ACPI_HANDLE h,UINT32 notify, void *context);
+
+static device_method_t acpi_lid_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_lid_probe),
+ DEVMETHOD(device_attach, acpi_lid_attach),
+ DEVMETHOD(device_suspend, acpi_lid_suspend),
+ DEVMETHOD(device_resume, acpi_lid_resume),
+
+ {0, 0}
+};
+
+static driver_t acpi_lid_driver = {
+ "acpi_lid",
+ acpi_lid_methods,
+ sizeof(struct acpi_lid_softc),
+};
+
+static devclass_t acpi_lid_devclass;
+DRIVER_MODULE(acpi_lid, acpi, acpi_lid_driver, acpi_lid_devclass, 0, 0);
+
+static int
+acpi_lid_probe(device_t dev)
+{
+ if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
+ !acpi_disabled("lid") &&
+ acpi_MatchHid(dev, "PNP0C0D")) {
+ device_set_desc(dev, "Control Method Lid Switch");
+ return(0);
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_lid_attach(device_t dev)
+{
+ struct acpi_lid_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ sc->lid_dev = dev;
+ sc->lid_handle = acpi_get_handle(dev);
+
+ /*
+ * Install notification handler
+ */
+ AcpiInstallNotifyHandler(sc->lid_handle, ACPI_DEVICE_NOTIFY, acpi_lid_notify_handler, sc);
+ acpi_device_enable_wake_capability(sc->lid_handle, 1);
+ return_VALUE(0);
+}
+
+static int
+acpi_lid_suspend(device_t dev)
+{
+ struct acpi_lid_softc *sc;
+
+ sc = device_get_softc(dev);
+ acpi_device_enable_wake_event(sc->lid_handle);
+ return (0);
+}
+
+static int
+acpi_lid_resume(device_t dev)
+{
+ return (0);
+}
+
+static void
+acpi_lid_notify_status_changed(void *arg)
+{
+ struct acpi_lid_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_lid_softc *)arg;
+
+ /*
+ * Evaluate _LID and check the return value, update lid status.
+ * Zero: The lid is closed
+ * Non-zero: The lid is open
+ */
+ if (ACPI_FAILURE(acpi_EvaluateInteger(sc->lid_handle, "_LID", &sc->lid_status)))
+ return_VOID;
+
+ acpi_sc = acpi_device_get_parent_softc(sc->lid_dev);
+ if (acpi_sc == NULL) {
+ return_VOID;
+ }
+
+ ACPI_VPRINT(sc->lid_dev, acpi_sc,
+ "Lid %s\n", sc->lid_status ? "opened" : "closed");
+
+ if (sc->lid_status == 0) {
+ EVENTHANDLER_INVOKE(acpi_sleep_event, acpi_sc->acpi_lid_switch_sx);
+ } else {
+ EVENTHANDLER_INVOKE(acpi_wakeup_event, acpi_sc->acpi_lid_switch_sx);
+ }
+
+ return_VOID;
+}
+
+/* XXX maybe not here */
+#define ACPI_NOTIFY_STATUS_CHANGED 0x80
+
+static void
+acpi_lid_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_lid_softc *sc = (struct acpi_lid_softc *)context;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+ switch (notify) {
+ case ACPI_NOTIFY_STATUS_CHANGED:
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_lid_notify_status_changed, sc);
+ break;
+ default:
+ break; /* unknown notification value */
+ }
+ return_VOID;
+}
+
diff --git a/sys/dev/acpica/acpi_pci.c b/sys/dev/acpica/acpi_pci.c
new file mode 100644
index 0000000..2448b62
--- /dev/null
+++ b/sys/dev/acpica/acpi_pci.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000, BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+#include <sys/pciio.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
+
+#include "pcib_if.h"
+#include "pci_if.h"
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("PCI")
+
+struct acpi_pci_devinfo {
+ struct pci_devinfo ap_dinfo;
+ ACPI_HANDLE ap_handle;
+};
+
+static int acpi_pci_probe(device_t dev);
+static int acpi_pci_attach(device_t dev);
+static int acpi_pci_read_ivar(device_t dev, device_t child, int which,
+ uintptr_t *result);
+#if 0
+static int acpi_pci_set_powerstate_method(device_t dev, device_t child,
+ int state);
+static int acpi_pci_get_powerstate_method(device_t dev, device_t child);
+#endif
+static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level,
+ void *context, void **status);
+
+static device_method_t acpi_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_pci_probe),
+ DEVMETHOD(device_attach, acpi_pci_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, pci_print_child),
+ DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch),
+ DEVMETHOD(bus_read_ivar, acpi_pci_read_ivar),
+ DEVMETHOD(bus_write_ivar, pci_write_ivar),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_delete_resource, pci_delete_resource),
+ DEVMETHOD(bus_alloc_resource, pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
+ DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
+
+ /* PCI interface */
+ DEVMETHOD(pci_read_config, pci_read_config_method),
+ DEVMETHOD(pci_write_config, pci_write_config_method),
+ DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method),
+ DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
+ DEVMETHOD(pci_enable_io, pci_enable_io_method),
+ DEVMETHOD(pci_disable_io, pci_disable_io_method),
+ /* XXX: We should override these two. */
+ DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
+ DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method),
+
+ { 0, 0 }
+};
+
+static driver_t acpi_pci_driver = {
+ "pci",
+ acpi_pci_methods,
+ 0, /* no softc */
+};
+
+DRIVER_MODULE(acpi_pci, pcib, acpi_pci_driver, pci_devclass, 0, 0);
+MODULE_VERSION(acpi_pci, 1);
+MODULE_DEPEND(acpi_pci, pci, 1, 1, 1);
+
+static int
+acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct acpi_pci_devinfo *dinfo;
+
+ switch (which) {
+ case ACPI_IVAR_HANDLE:
+ dinfo = device_get_ivars(child);
+ *result = (uintptr_t)dinfo->ap_handle;
+ return(0);
+ }
+ return(pci_read_ivar(dev, child, which, result));
+}
+
+#if 0
+/*
+ * PCI power manangement
+ */
+static int
+acpi_pci_set_powerstate_method(device_t dev, device_t child, int state)
+{
+ /* XXX: TODO */
+ return (ENXIO);
+}
+
+static int
+acpi_pci_get_powerstate_method(device_t dev, device_t child)
+{
+ /* XXX: TODO */
+ return (ENXIO);
+}
+#endif
+
+static ACPI_STATUS
+acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context,
+ void **status)
+{
+ struct acpi_pci_devinfo *dinfo;
+ device_t *devlist;
+ int devcount, i, func, slot;
+ UINT32 address;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (ACPI_FAILURE(acpi_EvaluateInteger(handle, "_ADR", &address)))
+ return_ACPI_STATUS(AE_OK);
+ slot = address >> 16;
+ func = address & 0xffff;
+ if (device_get_children((device_t)context, &devlist, &devcount) != 0)
+ return_ACPI_STATUS(AE_OK);
+ for (i = 0; i < devcount; i++) {
+ dinfo = device_get_ivars(devlist[i]);
+ if (dinfo->ap_dinfo.cfg.func == func &&
+ dinfo->ap_dinfo.cfg.slot == slot) {
+ dinfo->ap_handle = handle;
+ free(devlist, M_TEMP);
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+ free(devlist, M_TEMP);
+ return_ACPI_STATUS(AE_OK);
+}
+
+static int
+acpi_pci_probe(device_t dev)
+{
+
+ if (pcib_get_bus(dev) < 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI PCI bus");
+
+ if (acpi_get_handle(dev) == NULL)
+ return (ENXIO);
+ return (0);
+}
+
+static int
+acpi_pci_attach(device_t dev)
+{
+ int busno;
+
+ /*
+ * Since there can be multiple independantly numbered PCI
+ * busses on some large alpha systems, we can't use the unit
+ * number to decide what bus we are probing. We ask the parent
+ * pcib what our bus number is.
+ */
+ busno = pcib_get_bus(dev);
+ if (bootverbose)
+ device_printf(dev, "physical bus=%d\n", busno);
+
+ /*
+ * First, PCI devices are added as in the normal PCI bus driver.
+ * Afterwards, the ACPI namespace under the bridge driver is
+ * walked to save ACPI handles to all the devices that appear in
+ * the ACPI namespace as immediate descendants of the bridge.
+ *
+ * XXX: Sometimes PCI devices show up in the ACPI namespace that
+ * pci_add_children() doesn't find. We currently just ignore
+ * these devices.
+ */
+ pci_add_children(dev, busno, sizeof(struct acpi_pci_devinfo));
+ (void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 1,
+ acpi_pci_save_handle, dev, NULL);
+
+ return (bus_generic_attach(dev));
+}
diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c
new file mode 100644
index 0000000..7027cc7
--- /dev/null
+++ b/sys/dev/acpica/acpi_pci_link.c
@@ -0,0 +1,1077 @@
+/*-
+ * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpi_pcibvar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("PCI_LINK")
+
+#define MAX_POSSIBLE_INTERRUPTS 16
+#define MAX_ISA_INTERRUPTS 16
+#define MAX_ACPI_INTERRUPTS 255
+
+struct acpi_pci_link_entry {
+ TAILQ_ENTRY(acpi_pci_link_entry) links;
+ ACPI_HANDLE handle;
+ UINT8 current_irq;
+ UINT8 initial_irq;
+ ACPI_RESOURCE possible_resources;
+ UINT8 number_of_interrupts;
+ UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];
+
+ UINT8 sorted_irq[MAX_POSSIBLE_INTERRUPTS];
+ int references;
+ int priority;
+};
+
+TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
+static struct acpi_pci_link_entries acpi_pci_link_entries;
+
+struct acpi_prt_entry {
+ TAILQ_ENTRY(acpi_prt_entry) links;
+ device_t pcidev;
+ int busno;
+ ACPI_PCI_ROUTING_TABLE prt;
+ struct acpi_pci_link_entry *pci_link;
+};
+
+TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
+static struct acpi_prt_entries acpi_prt_entries;
+
+static int irq_penalty[MAX_ACPI_INTERRUPTS];
+
+#define ACPI_STA_PRESENT 0x00000001
+#define ACPI_STA_ENABLE 0x00000002
+#define ACPI_STA_SHOWINUI 0x00000004
+#define ACPI_STA_FUNCTIONAL 0x00000008
+
+/*
+ * PCI link object management
+ */
+
+static void
+acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
+{
+ UINT8 i;
+ ACPI_RESOURCE_IRQ *Irq;
+
+ if (entry == NULL || entry->pci_link == NULL) {
+ return;
+ }
+
+ printf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
+ entry->pci_link->current_irq);
+
+ printf("[");
+ for (i = 0; i < entry->pci_link->number_of_interrupts; i++) {
+ printf("%3d", entry->pci_link->interrupts[i]);
+ }
+ printf("] ");
+
+ Irq = NULL;
+ switch (entry->pci_link->possible_resources.Id) {
+ case ACPI_RSTYPE_IRQ:
+ Irq = &entry->pci_link->possible_resources.Data.Irq;
+
+ switch (Irq->ActiveHighLow) {
+ case ACPI_ACTIVE_HIGH:
+ printf("high,");
+ break;
+
+ case ACPI_ACTIVE_LOW:
+ printf("low,");
+ break;
+
+ default:
+ printf("unkown,");
+ break;
+ }
+
+ switch (Irq->EdgeLevel) {
+ case ACPI_EDGE_SENSITIVE:
+ printf("edge,");
+ break;
+
+ case ACPI_LEVEL_SENSITIVE:
+ printf("level,");
+ break;
+
+ default:
+ printf("unkown,");
+ break;
+ }
+
+ switch (Irq->SharedExclusive) {
+ case ACPI_EXCLUSIVE:
+ printf("exclusive");
+ break;
+
+ case ACPI_SHARED:
+ printf("sharable");
+ break;
+
+ default:
+ printf("unkown");
+ break;
+ }
+
+ break;
+
+ case ACPI_RSTYPE_EXT_IRQ:
+ /* TBD */
+ break;
+ }
+
+ printf(" %d.%d.%d", entry->busno,
+ (int)((entry->prt.Address & 0xffff0000) >> 16),
+ (int)entry->prt.Pin);
+
+ printf("\n");
+}
+
+static ACPI_STATUS
+acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
+{
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS error;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (handle == NULL || sta == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "invalid argument\n"));
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ error = AcpiGetObjectInfo(handle, &devinfo);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't get object info %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ if ((devinfo.Valid & ACPI_VALID_HID) == 0 ||
+ strcmp(devinfo.HardwareId, "PNP0C0F") != 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid hardware ID - %s\n",
+ acpi_name(handle)));
+ return_ACPI_STATUS (AE_TYPE);
+ }
+
+ if (devinfo.Valid & ACPI_VALID_STA) {
+ *sta = devinfo.CurrentStatus;
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid status - %s\n",
+ acpi_name(handle)));
+ *sta = 0;
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
+ UINT8 *number_of_interrupts, UINT8 interrupts[])
+{
+ UINT8 count;
+ UINT8 i;
+ UINT32 NumberOfInterrupts;
+ UINT32 *Interrupts;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (resources == NULL || number_of_interrupts == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ *number_of_interrupts = 0;
+ NumberOfInterrupts = 0;
+ Interrupts = NULL;
+
+ if (resources->Id == ACPI_RSTYPE_START_DPF)
+ resources = ACPI_NEXT_RESOURCE(resources);
+
+ if (resources->Id != ACPI_RSTYPE_IRQ &&
+ resources->Id != ACPI_RSTYPE_EXT_IRQ) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Resource is not an IRQ entry - %d\n", resources->Id));
+ return_ACPI_STATUS (AE_TYPE);
+ }
+
+ switch (resources->Id) {
+ case ACPI_RSTYPE_IRQ:
+ NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts;
+ Interrupts = resources->Data.Irq.Interrupts;
+ break;
+
+ case ACPI_RSTYPE_EXT_IRQ:
+ NumberOfInterrupts = resources->Data.ExtendedIrq.NumberOfInterrupts;
+ Interrupts = resources->Data.ExtendedIrq.Interrupts;
+ break;
+ }
+
+ if (NumberOfInterrupts == 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n"));
+ return_ACPI_STATUS (AE_NULL_ENTRY);
+ }
+
+ count = 0;
+ for (i = 0; i < NumberOfInterrupts; i++) {
+ if (i >= MAX_POSSIBLE_INTERRUPTS) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs %d\n", i));
+ break;
+ }
+
+ if (Interrupts[i] == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n",
+ Interrupts[i]));
+ continue;
+ }
+ interrupts[count] = Interrupts[i];
+ count++;
+ }
+ *number_of_interrupts = count;
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
+{
+ ACPI_STATUS error;
+ ACPI_BUFFER buf;
+ ACPI_RESOURCE *resources;
+ UINT8 number_of_interrupts;
+ UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (link == NULL || irq == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ *irq = 0;
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ error = AcpiGetCurrentResources(link->handle, &buf);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't get PCI interrupt link device _CRS %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+ if (buf.Pointer == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't allocate memory - %s\n",
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ resources = (ACPI_RESOURCE *) buf.Pointer;
+
+ number_of_interrupts = 0;
+ bzero(interrupts, sizeof(interrupts));
+ error = acpi_pci_link_get_irq_resources(resources,
+ &number_of_interrupts, interrupts);
+ AcpiOsFree(buf.Pointer);
+
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get current IRQ from PCI interrupt link %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ if (number_of_interrupts == 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "PCI interrupt link device _CRS data is corrupted - %s\n",
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (AE_NULL_ENTRY);
+ }
+
+ *irq = interrupts[0];
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
+{
+ ACPI_STATUS error;
+ ACPI_BUFFER buf;
+ ACPI_RESOURCE *resources;
+ struct acpi_pci_link_entry *link;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ entry->pci_link = NULL;
+ TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
+ if (link->handle == handle) {
+ entry->pci_link = link;
+ link->references++;
+ return_ACPI_STATUS (AE_OK);
+ }
+ }
+
+ link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
+ if (link == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't allocate memory - %s\n", acpi_name(handle)));
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+
+ bzero(link, sizeof(struct acpi_pci_link_entry));
+
+ link->handle = handle;
+
+ error = acpi_pci_link_get_current_irq(link, &link->current_irq);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get current IRQ from PCI interrupt link %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ }
+
+ link->initial_irq = link->current_irq;
+
+ error = AcpiGetPossibleResources(handle, &buf);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get PCI interrupt link device _PRS data %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ goto out;
+ }
+
+ if (buf.Pointer == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "_PRS nuffer is empty - %s\n", acpi_name(handle)));
+ error = AE_NO_MEMORY;
+ goto out;
+ }
+
+ resources = (ACPI_RESOURCE *) buf.Pointer;
+ bcopy(resources, &link->possible_resources,
+ sizeof(link->possible_resources));
+
+ error = acpi_pci_link_get_irq_resources(resources,
+ &link->number_of_interrupts, link->interrupts);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get possible IRQs from PCI interrupt link %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ goto out;
+ }
+
+ if (link->number_of_interrupts == 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "PCI interrupt link device _PRS data is corrupted - %s\n",
+ acpi_name(handle)));
+ error = AE_NULL_ENTRY;
+ goto out;
+ }
+
+ link->references++;
+
+ TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
+ entry->pci_link = link;
+
+ error = AE_OK;
+out:
+ if (buf.Pointer != NULL) {
+ AcpiOsFree(buf.Pointer);
+ }
+
+ if (error != AE_OK && link != NULL) {
+ AcpiOsFree(link);
+ }
+
+ return_ACPI_STATUS (error);
+}
+
+static ACPI_STATUS
+acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
+{
+ ACPI_HANDLE handle;
+ ACPI_STATUS error;
+ UINT32 sta;
+ struct acpi_prt_entry *entry;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if ((prt == NULL) || (prt->Source == NULL) || (prt->Source[0] == '\0')) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't handle this routing table - hardwired\n"));
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't get acpi handle - %s\n",
+ AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ error = acpi_pci_link_get_object_status(handle, &sta);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't get object status %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ if (!(sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "PCI interrupt link is not functional - %s\n",
+ acpi_name(handle)));
+ return_ACPI_STATUS (AE_ERROR);
+ }
+
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ if (entry->busno == busno &&
+ entry->prt.Address == prt->Address &&
+ entry->prt.Pin == prt->Pin) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "PCI interrupt link entry already exists - %s\n",
+ acpi_name(handle)));
+ return_ACPI_STATUS (AE_ALREADY_EXISTS);
+ }
+ }
+
+ entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
+ if (entry == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't allocate memory - %s\n", acpi_name(handle)));
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ bzero(entry, sizeof(struct acpi_prt_entry));
+
+ entry->pcidev = pcidev;
+ entry->busno = busno;
+ bcopy(prt, &entry->prt, sizeof(entry->prt));
+
+ error = acpi_pci_link_add_link(handle, entry);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't add prt entry to pci link %s - %s\n",
+ acpi_name(handle), AcpiFormatException(error)));
+ goto out;
+ }
+
+ TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
+ error = AE_OK;
+
+out:
+ if (error != AE_OK && entry != NULL) {
+ AcpiOsFree(entry);
+ }
+
+ return_ACPI_STATUS (error);
+}
+
+static int
+acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+{
+ UINT8 i;
+
+ if (irq == 0) {
+ return (0);
+ }
+
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ if (link->interrupts[i] == irq) {
+ return (1);
+ }
+ }
+
+ /* allow initial IRQ as valid one. */
+ if (link->initial_irq == irq) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static ACPI_STATUS
+acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+{
+ ACPI_STATUS error;
+ ACPI_RESOURCE resbuf;
+ ACPI_BUFFER crsbuf;
+ UINT32 sta;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (!acpi_pci_link_is_valid_irq(link, irq)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't set invalid IRQ %d - %s\n", irq,
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ error = acpi_pci_link_get_current_irq(link, &link->current_irq);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get current IRQ from PCI interrupt link %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ }
+
+ if (link->current_irq == irq) {
+ return_ACPI_STATUS (AE_OK);
+ }
+
+ bzero(&resbuf, sizeof(resbuf));
+ crsbuf.Pointer = NULL;
+ resbuf.Id = ACPI_RSTYPE_IRQ;
+ resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
+
+ if (link->possible_resources.Id != ACPI_RSTYPE_IRQ &&
+ link->possible_resources.Id != ACPI_RSTYPE_EXT_IRQ) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Resource is not an IRQ entry %s - %d\n",
+ acpi_name(link->handle), link->possible_resources.Id));
+ return_ACPI_STATUS (AE_TYPE);
+ }
+
+ switch (link->possible_resources.Id) {
+ case ACPI_RSTYPE_IRQ:
+ /* structure copy other fields */
+ resbuf.Data.Irq = link->possible_resources.Data.Irq;
+ break;
+
+ case ACPI_RSTYPE_EXT_IRQ:
+ /* XXX */
+ resbuf.Data.Irq.EdgeLevel = ACPI_LEVEL_SENSITIVE;
+ resbuf.Data.Irq.ActiveHighLow = ACPI_ACTIVE_LOW;
+ resbuf.Data.Irq.SharedExclusive = ACPI_SHARED;
+ break;
+ }
+
+ resbuf.Data.Irq.NumberOfInterrupts = 1;
+ resbuf.Data.Irq.Interrupts[0] = irq;
+
+ error = acpi_AppendBufferResource(&crsbuf, &resbuf);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "couldn't setup buffer by acpi_AppendBufferResource - %s\n",
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (error);
+ }
+
+ if (crsbuf.Pointer == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "buffer setup by acpi_AppendBufferResource is corrupted - %s\n",
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ error = AcpiSetCurrentResources(link->handle, &crsbuf);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't set PCI interrupt link device _SRS %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ AcpiOsFree(crsbuf.Pointer);
+ link->current_irq = 0;
+
+ error = acpi_pci_link_get_object_status(link->handle, &sta);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get object status %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ if (!(sta & ACPI_STA_ENABLE)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "PCI interrupt link is disabled - %s\n",
+ acpi_name(link->handle)));
+ return_ACPI_STATUS (AE_ERROR);
+ }
+
+ error = acpi_pci_link_get_current_irq(link, &link->current_irq);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't get current IRQ from PCI interrupt link %s - %s\n",
+ acpi_name(link->handle), AcpiFormatException(error)));
+ return_ACPI_STATUS (error);
+ }
+
+ if (link->current_irq == irq) {
+ error = AE_OK;
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't set IRQ %d to PCI interrupt link %d - %s\n",
+ irq, link->current_irq, acpi_name(link->handle)));
+
+ link->current_irq = 0;
+ error = AE_ERROR;
+ }
+
+ return_ACPI_STATUS (error);
+}
+
+/*
+ * Auto arbitration for boot-disabled devices
+ */
+
+static void
+acpi_pci_link_bootdisabled_dump(void)
+
+{
+ int i;
+ int irq;
+ struct acpi_pci_link_entry *link;
+
+ TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
+ /* boot-disabled link only. */
+ if (link->current_irq != 0) {
+ continue;
+ }
+
+ printf("%s:\n", acpi_name(link->handle));
+ printf(" interrupts: ");
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ irq = link->sorted_irq[i];
+ printf("%6d", irq);
+ }
+ printf("\n");
+ printf(" penalty: ");
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ irq = link->sorted_irq[i];
+ printf("%6d", irq_penalty[irq]);
+ }
+ printf("\n");
+ printf(" references: %d\n", link->references);
+ printf(" priority: %d\n", link->priority);
+ }
+}
+
+static void
+acpi_pci_link_init_irq_penalty(void)
+{
+ int irq;
+
+ bzero(irq_penalty, sizeof(irq_penalty));
+ for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
+ /* 0, 1, 2, 8: timer, keyboard, cascade */
+ if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
+ irq_penalty[irq] = 100000;
+ continue;
+ }
+
+ /* 13, 14, 15: npx, ATA controllers */
+ if (irq == 13 || irq == 14 || irq == 15) {
+ irq_penalty[irq] = 10000;
+ continue;
+ }
+
+ /* 3,4,6,7,12: typicially used by legacy hardware */
+ if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
+ irq_penalty[irq] = 1000;
+ continue;
+ }
+ }
+}
+
+static int
+acpi_pci_link_is_irq_exclusive(ACPI_RESOURCE *res)
+{
+ if (res == NULL) {
+ return (0);
+ }
+
+ if (res->Id != ACPI_RSTYPE_IRQ &&
+ res->Id != ACPI_RSTYPE_EXT_IRQ) {
+ return (0);
+ }
+
+ if (res->Id == ACPI_RSTYPE_IRQ &&
+ res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) {
+ return (1);
+ }
+
+ if (res->Id == ACPI_RSTYPE_EXT_IRQ &&
+ res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+acpi_pci_link_update_irq_penalty(device_t dev, int busno)
+{
+ int i;
+ int irq;
+ int rid;
+ struct resource *res;
+ struct acpi_prt_entry *entry;
+ struct acpi_pci_link_entry *link;
+
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ if (entry->busno != busno) {
+ continue;
+ }
+
+ link = entry->pci_link;
+ if (link == NULL) {
+ continue; /* impossible... */
+ }
+
+ if (link->current_irq != 0) {
+ /* not boot-disabled link, we will use this IRQ. */
+ irq_penalty[link->current_irq] += 100;
+ continue;
+ }
+
+ /* boot-disabled link */
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ /* give 10 for each possible IRQs. */
+ irq = link->interrupts[i];
+ irq_penalty[irq] += 10;
+
+ /* higher penalty if exclusive. */
+ if (acpi_pci_link_is_irq_exclusive(&link->possible_resources)) {
+ irq_penalty[irq] += 100;
+ }
+
+ /* XXX try to get this IRQ in non-sharable mode. */
+ rid = 0;
+ res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &rid, irq, irq, 1, 0);
+ if (res != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rid, res);
+ } else {
+ /* this is in use, give 100. */
+ irq_penalty[irq] += 100;
+ }
+ }
+
+ /* initialize `sorted' possible IRQs. */
+ bcopy(link->interrupts, link->sorted_irq,
+ sizeof(link->sorted_irq));
+ }
+}
+
+static void
+acpi_pci_link_set_bootdisabled_priority(void)
+{
+ int sum_penalty;
+ int i;
+ int irq;
+ struct acpi_pci_link_entry *link, *link_pri;
+ TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
+
+ if (bootverbose) {
+ printf("---- before setting priority for links ------------\n");
+ acpi_pci_link_bootdisabled_dump();
+ }
+
+ /* reset priority for all links. */
+ TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
+ link->priority = 0;
+ }
+
+ TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
+ /* not boot-disabled link, give no chance to be arbitrated. */
+ if (link->current_irq != 0) {
+ link->priority = 0;
+ continue;
+ }
+
+ /*
+ * Calculate the priority for each boot-disabled links.
+ * o IRQ penalty indicates difficulty to use.
+ * o #references for devices indicates importance of the link.
+ * o #interrupts indicates flexibility of the link.
+ */
+ sum_penalty = 0;
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ irq = link->interrupts[i];
+ sum_penalty += irq_penalty[irq];
+ }
+
+ link->priority = (sum_penalty * link->references) / link->number_of_interrupts;
+ }
+
+ /*
+ * Sort PCI links based on the priority.
+ * XXX Any other better ways rather than using work list?
+ */
+ TAILQ_INIT(&sorted_list);
+ while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
+ link = TAILQ_FIRST(&acpi_pci_link_entries);
+ /* find an entry which has the highest priority. */
+ TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links) {
+ if (link->priority < link_pri->priority) {
+ link = link_pri;
+ }
+ }
+ /* move to work list. */
+ TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
+ TAILQ_INSERT_TAIL(&sorted_list, link, links);
+ }
+
+ while (!TAILQ_EMPTY(&sorted_list)) {
+ /* move them back to the list, one by one... */
+ link = TAILQ_FIRST(&sorted_list);
+ TAILQ_REMOVE(&sorted_list, link, links);
+ TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
+ }
+}
+
+static void
+acpi_pci_link_fixup_bootdisabled_link(void)
+{
+ int i, j;
+ int irq1, irq2;
+ struct acpi_pci_link_entry *link;
+ ACPI_STATUS error;
+
+ if (bootverbose) {
+ printf("---- before fixup boot-disabled links -------------\n");
+ acpi_pci_link_bootdisabled_dump();
+ }
+
+ TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
+ /* ignore non boot-disabled links. */
+ if (link->current_irq != 0) {
+ continue;
+ }
+
+ /* sort IRQs based on their penalty descending. */
+ for (i = 0; i < link->number_of_interrupts; i++) {
+ irq1 = link->sorted_irq[i];
+ for (j = i + 1; j < link->number_of_interrupts; j++) {
+ irq2 = link->sorted_irq[j];
+ if (irq_penalty[irq1] < irq_penalty[irq2]) {
+ continue;
+ }
+ link->sorted_irq[i] = irq2;
+ link->sorted_irq[j] = irq1;
+ irq1 = irq2;
+ }
+ }
+
+ /* try with lower penalty IRQ. */
+ for (i = 0; i < link->number_of_interrupts - 1; i++) {
+ irq1 = link->sorted_irq[i];
+ error = acpi_pci_link_set_irq(link, irq1);
+ if (error == AE_OK) {
+ /* OK, we use this. give another penalty. */
+ irq_penalty[irq1] += 100 * link->references;
+ break;
+ }
+ /* NG, try next IRQ... */
+ }
+ }
+
+ if (bootverbose) {
+ printf("---- after fixup boot-disabled links --------------\n");
+ acpi_pci_link_bootdisabled_dump();
+ }
+}
+
+/*
+ * Public interface
+ */
+
+int
+acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
+{
+ struct acpi_prt_entry *entry;
+ ACPI_PCI_ROUTING_TABLE *prt;
+ u_int8_t *prtp;
+ ACPI_STATUS error;
+ static int first_time =1;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (acpi_disabled("pci_link")) {
+ return (0);
+ }
+
+ if (first_time) {
+ TAILQ_INIT(&acpi_prt_entries);
+ TAILQ_INIT(&acpi_pci_link_entries);
+ acpi_pci_link_init_irq_penalty();
+ first_time = 0;
+ }
+
+ if (prtbuf == NULL) {
+ return (-1);
+ }
+
+ prtp = prtbuf->Pointer;
+ if (prtp == NULL) { /* didn't get routing table */
+ return (-1);
+ }
+
+ /* scan the PCI Routing Table */
+ for (;;) {
+ prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
+
+ if (prt->Length == 0) /* end of table */
+ break;
+
+ error = acpi_pci_link_add_prt(dev, prt, busno);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't add PCI interrupt link entry - %s\n",
+ AcpiFormatException(error)));
+ }
+
+ /* skip to next entry */
+ prtp += prt->Length;
+ }
+
+ if (bootverbose) {
+ printf("---- initial configuration ------------------------\n");
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ if (entry->busno != busno) {
+ continue;
+ }
+
+ acpi_pci_link_entry_dump(entry);
+ }
+ }
+
+ /* manual configuration. */
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ UINT8 irq;
+ char *irqstr, *op;
+ char prthint[32];
+
+ if (entry->busno != busno) {
+ continue;
+ }
+
+ snprintf(prthint, sizeof(prthint),
+ "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
+ (int)((entry->prt.Address & 0xffff0000) >> 16),
+ (int)entry->prt.Pin);
+
+ irqstr = getenv(prthint);
+ if (irqstr == NULL) {
+ continue;
+ }
+
+ irq = strtoul(irqstr, &op, 0);
+ if (*op != '\0') {
+ continue;
+ }
+
+ if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
+ error = acpi_pci_link_set_irq(entry->pci_link, irq);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't set IRQ to PCI interrupt link entry %s - %s\n",
+ acpi_name(entry->pci_link->handle),
+ AcpiFormatException(error)));
+ }
+ continue;
+ }
+
+ /*
+ * Do auto arbitration for this device's PCI link
+ * if hint value 0 is specified.
+ */
+ if (irq == 0) {
+ entry->pci_link->current_irq = 0;
+ }
+ }
+
+ /* auto arbitration */
+ acpi_pci_link_update_irq_penalty(dev, busno);
+ acpi_pci_link_set_bootdisabled_priority();
+ acpi_pci_link_fixup_bootdisabled_link();
+
+ if (bootverbose) {
+ printf("---- arbitrated configuration ---------------------\n");
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ if (entry->busno != busno) {
+ continue;
+ }
+
+ acpi_pci_link_entry_dump(entry);
+ }
+ }
+
+ return (0);
+}
+
+int
+acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
+{
+ struct acpi_prt_entry *entry;
+ ACPI_STATUS error;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (acpi_disabled("pci_link")) {
+ return (0);
+ }
+
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ if (entry->pcidev != dev) {
+ continue;
+ }
+
+ error = acpi_pci_link_set_irq(entry->pci_link,
+ entry->pci_link->current_irq);
+ if (ACPI_FAILURE(error)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "couldn't set IRQ to PCI interrupt link entry %s - %s\n",
+ acpi_name(entry->pci_link->handle),
+ AcpiFormatException(error)));
+ }
+ }
+
+ return (0);
+}
+
diff --git a/sys/dev/acpica/acpi_pcib.c b/sys/dev/acpica/acpi_pcib.c
new file mode 100644
index 0000000..3677966
--- /dev/null
+++ b/sys/dev/acpica/acpi_pcib.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpi_pcibvar.h>
+
+#include <machine/pci_cfgreg.h>
+#include <pci/pcivar.h>
+#include <pci/pcib_private.h>
+#include "pcib_if.h"
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("PCI")
+
+int
+acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
+{
+ device_t child;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Don't attach if we're not really there.
+ *
+ * XXX: This isn't entirely correct since we may be a PCI bus
+ * on a hot-plug docking station, etc.
+ */
+ if (!acpi_DeviceIsPresent(dev))
+ return_VALUE(ENXIO);
+
+ /*
+ * Get the PCI interrupt routing table for this bus.
+ */
+ prt->Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
+ if (ACPI_FAILURE(status))
+ /* This is not an error, but it may reduce functionality. */
+ device_printf(dev,
+ "could not get PCI interrupt routing table for %s - %s\n",
+ acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
+
+ /*
+ * Attach the PCI bus proper.
+ */
+ if ((child = device_add_child(dev, "pci", busno)) == NULL) {
+ device_printf(device_get_parent(dev), "couldn't attach pci bus\n");
+ return_VALUE(ENXIO);
+ }
+
+ /*
+ * Now go scan the bus.
+ */
+ acpi_pci_link_config(dev, prt, busno);
+ return_VALUE(bus_generic_attach(dev));
+}
+
+int
+acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno)
+{
+ acpi_pci_link_resume(dev, prt, busno);
+ return (bus_generic_resume(dev));
+}
+
+/*
+ * Route an interrupt for a child of the bridge.
+ *
+ * XXX clean up error messages
+ *
+ * XXX this function is somewhat bulky
+ */
+int
+acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
+ ACPI_BUFFER *prtbuf)
+{
+ ACPI_PCI_ROUTING_TABLE *prt;
+ ACPI_HANDLE lnkdev;
+ ACPI_BUFFER crsbuf, prsbuf;
+ ACPI_RESOURCE *crsres, *prsres, resbuf;
+ ACPI_DEVICE_INFO devinfo;
+ ACPI_STATUS status;
+ u_int8_t *prtp;
+ int interrupt;
+ int i;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ crsbuf.Pointer = NULL;
+ prsbuf.Pointer = NULL;
+ interrupt = 255;
+
+ /* ACPI numbers pins 0-3, not 1-4 like the BIOS */
+ pin--;
+
+ prtp = prtbuf->Pointer;
+ if (prtp == NULL) /* didn't get routing table */
+ goto out;
+
+ /* scan the table looking for this device */
+ for (;;) {
+ prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
+
+ if (prt->Length == 0) /* end of table */
+ goto out;
+
+ /*
+ * Compare the slot number (high word of Address) and pin number
+ * (note that ACPI uses 0 for INTA) to check for a match.
+ *
+ * Note that the low word of the Address field (function number)
+ * is required by the specification to be 0xffff. We don't risk
+ * checking it here.
+ */
+ if ((((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev)) &&
+ (prt->Pin == pin)) {
+ if (bootverbose)
+ device_printf(pcib, "matched entry for %d.%d.INT%c (source %s)\n",
+ pci_get_bus(dev), pci_get_slot(dev), 'A' + pin, prt->Source);
+ break;
+ }
+
+ /* skip to next entry */
+ prtp += prt->Length;
+ }
+
+ /*
+ * If source is empty/NULL, the source index is the global IRQ number.
+ */
+ if ((prt->Source == NULL) || (prt->Source[0] == '\0')) {
+ if (bootverbose)
+ device_printf(pcib, "device is hardwired to IRQ %d\n",
+ prt->SourceIndex);
+ interrupt = prt->SourceIndex;
+ goto out;
+ }
+
+ /*
+ * We have to find the source device (PCI interrupt link device)
+ */
+ if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
+ device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
+ prt->Source);
+ goto out;
+ }
+
+ /*
+ * Verify that this is a PCI link device, and that it's present.
+ */
+ if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &devinfo))) {
+ device_printf(pcib, "couldn't validate PCI interrupt link device %s\n",
+ prt->Source);
+ goto out;
+ }
+ if (!(devinfo.Valid & ACPI_VALID_HID) || strcmp("PNP0C0F", devinfo.HardwareId)) {
+ device_printf(pcib, "PCI interrupt link device %s has wrong _HID (%s)\n",
+ prt->Source, devinfo.HardwareId);
+ goto out;
+ }
+ if (devinfo.Valid & ACPI_VALID_STA && (devinfo.CurrentStatus & 0x9) != 0x9) {
+ device_printf(pcib, "PCI interrupt link device %s not present\n",
+ prt->Source);
+ goto out;
+ }
+
+ /*
+ * Get the current and possible resources for the interrupt link device.
+ */
+ crsbuf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
+ device_printf(pcib, "couldn't get PCI interrupt link device _CRS data - %s\n",
+ AcpiFormatException(status));
+ goto out; /* this is fatal */
+ }
+ prsbuf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
+ device_printf(pcib, "couldn't get PCI interrupt link device _PRS data - %s\n",
+ AcpiFormatException(status));
+ /* this is not fatal, since it may be hardwired */
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n",
+ (long)crsbuf.Length, acpi_name(lnkdev)));
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n",
+ (long)prsbuf.Length, acpi_name(lnkdev)));
+
+ /*
+ * The interrupt may already be routed, so check _CRS first. We don't check the
+ * 'decoding' bit in the _STA result, since there's nothing in the spec that
+ * mandates it be set, however some BIOS' will set it if the decode is active.
+ *
+ * The Source Index points to the particular resource entry we're interested in.
+ */
+ if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex, &crsres))) {
+ device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n");
+ goto out;
+ }
+
+ /* type-check the resource we've got */
+ if (crsres->Id != ACPI_RSTYPE_IRQ) { /* XXX ACPI_RSTYPE_EXT_IRQ */
+ device_printf(pcib, "_CRS resource entry has unsupported type %d\n",
+ crsres->Id);
+ goto out;
+ }
+
+ /* if there's more than one interrupt, we are confused */
+ if (crsres->Data.Irq.NumberOfInterrupts > 1) {
+ device_printf(pcib, "device has too many interrupts (%d)\n",
+ crsres->Data.Irq.NumberOfInterrupts);
+ goto out;
+ }
+
+ /*
+ * If there's only one interrupt, and it's not zero, then we're already routed.
+ *
+ * Note that we could also check the 'decoding' bit in _STA, but can't depend on
+ * it since it's not part of the spec.
+ *
+ * XXX check ASL examples to see if this is an acceptable set of tests
+ */
+ if ((crsres->Data.Irq.NumberOfInterrupts == 1) && (crsres->Data.Irq.Interrupts[0] != 0)) {
+ device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
+ pci_get_slot(dev), 'A' + pin, crsres->Data.Irq.Interrupts[0]);
+ interrupt = crsres->Data.Irq.Interrupts[0];
+ goto out;
+ }
+
+ /*
+ * There isn't an interrupt, so we have to look at _PRS to get one.
+ * Get the set of allowed interrupts from the _PRS resource indexed by SourceIndex.
+ */
+ if (prsbuf.Pointer == NULL) {
+ device_printf(pcib, "device has no routed interrupt and no _PRS on PCI interrupt link device\n");
+ goto out;
+ }
+ if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, prt->SourceIndex, &prsres))) {
+ device_printf(pcib, "_PRS buffer corrupt, cannot route interrupt\n");
+ goto out;
+ }
+
+ /* type-check the resource we've got */
+ if (prsres->Id != ACPI_RSTYPE_IRQ) { /* XXX ACPI_RSTYPE_EXT_IRQ */
+ device_printf(pcib, "_PRS resource entry has unsupported type %d\n",
+ prsres->Id);
+ goto out;
+ }
+
+ /* there has to be at least one interrupt available */
+ if (prsres->Data.Irq.NumberOfInterrupts < 1) {
+ device_printf(pcib, "device has no interrupts\n");
+ goto out;
+ }
+
+ /*
+ * Pick an interrupt to use. Note that a more scientific approach than just
+ * taking the first one available would be desirable.
+ *
+ * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI doesn't
+ * seem to offer a similar mechanism, so picking a "good" interrupt here is a
+ * difficult task.
+ *
+ * Build a resource buffer and pass it to AcpiSetCurrentResources to route the
+ * new interrupt.
+ */
+ device_printf(pcib, "possible interrupts:");
+ for (i = 0; i < prsres->Data.Irq.NumberOfInterrupts; i++)
+ printf(" %d", prsres->Data.Irq.Interrupts[i]);
+ printf("\n");
+
+ if (crsbuf.Pointer != NULL) /* should never happen */
+ AcpiOsFree(crsbuf.Pointer);
+ crsbuf.Pointer = NULL;
+ resbuf.Id = ACPI_RSTYPE_IRQ;
+ resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
+ resbuf.Data.Irq = prsres->Data.Irq; /* structure copy other fields */
+ resbuf.Data.Irq.NumberOfInterrupts = 1;
+ resbuf.Data.Irq.Interrupts[0] = prsres->Data.Irq.Interrupts[0]; /* just take first... */
+ if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
+ device_printf(pcib, "couldn't route interrupt %d via %s, interrupt resource build failed - %s\n",
+ prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
+ goto out;
+ }
+ if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
+ device_printf(pcib, "couldn't route interrupt %d via %s - %s\n",
+ prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
+ goto out;
+ }
+
+ /* successful, return the interrupt we just routed */
+ device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
+ pci_get_slot(dev), 'A' + pin, prsres->Data.Irq.Interrupts[0],
+ acpi_name(lnkdev));
+ interrupt = prsres->Data.Irq.Interrupts[0];
+
+ out:
+ if (crsbuf.Pointer != NULL)
+ AcpiOsFree(crsbuf.Pointer);
+ if (prsbuf.Pointer != NULL)
+ AcpiOsFree(prsbuf.Pointer);
+
+ /* XXX APIC_IO interrupt mapping? */
+ return_VALUE(interrupt);
+}
+
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
new file mode 100644
index 0000000..7fca8be
--- /dev/null
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -0,0 +1,291 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+#include <machine/pci_cfgreg.h>
+#include <pci/pcivar.h>
+#include <pci/pcib_private.h>
+#include "pcib_if.h"
+
+#include <dev/acpica/acpi_pcibvar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("PCI_ACPI")
+
+struct acpi_hpcib_softc {
+ device_t ap_dev;
+ ACPI_HANDLE ap_handle;
+
+ int ap_segment; /* analagous to Alpha 'hose' */
+ int ap_bus; /* bios-assigned bus number */
+
+ ACPI_BUFFER ap_prt; /* interrupt routing table */
+};
+
+
+static int acpi_pcib_acpi_probe(device_t bus);
+static int acpi_pcib_acpi_attach(device_t bus);
+static int acpi_pcib_acpi_resume(device_t bus);
+static int acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result);
+static int acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value);
+static u_int32_t acpi_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes);
+static void acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg,
+ u_int32_t data, int bytes);
+static int acpi_pcib_acpi_route_interrupt(device_t pcib,
+ device_t dev, int pin);
+
+static device_method_t acpi_pcib_acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_pcib_acpi_probe),
+ DEVMETHOD(device_attach, acpi_pcib_acpi_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, acpi_pcib_acpi_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, acpi_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, acpi_pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_read_config, acpi_pcib_read_config),
+ DEVMETHOD(pcib_write_config, acpi_pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, acpi_pcib_acpi_route_interrupt),
+
+ {0, 0}
+};
+
+static driver_t acpi_pcib_acpi_driver = {
+ "pcib",
+ acpi_pcib_acpi_methods,
+ sizeof(struct acpi_hpcib_softc),
+};
+
+DRIVER_MODULE(acpi_pcib, acpi, acpi_pcib_acpi_driver, pcib_devclass, 0, 0);
+
+static int
+acpi_pcib_acpi_probe(device_t dev)
+{
+
+ if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
+ !acpi_disabled("pci") &&
+ acpi_MatchHid(dev, "PNP0A03")) {
+
+ if (!pci_cfgregopen())
+ return(ENXIO);
+
+ /*
+ * Set device description
+ */
+ device_set_desc(dev, "ACPI Host-PCI bridge");
+ return(0);
+ }
+ return(ENXIO);
+}
+
+static int
+acpi_pcib_acpi_attach(device_t dev)
+{
+ struct acpi_hpcib_softc *sc;
+ ACPI_STATUS status;
+ uint addr, slot, func, busok;
+ uint8_t busno;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ sc->ap_dev = dev;
+ sc->ap_handle = acpi_get_handle(dev);
+
+ /*
+ * Get our base bus number by evaluating _BBN.
+ * If this doesn't work, we assume we're bus number 0.
+ *
+ * XXX note that it may also not exist in the case where we are
+ * meant to use a private configuration space mechanism for this bus,
+ * so we should dig out our resources and check to see if we have
+ * anything like that. How do we do this?
+ * XXX If we have the requisite information, and if we don't think the
+ * default PCI configuration space handlers can deal with this bus,
+ * we should attach our own handler.
+ * XXX invoke _REG on this for the PCI config space address space?
+ * XXX It seems many BIOS's with multiple Host-PCI bridges do not set
+ * _BBN correctly. They set _BBN to zero for all bridges. Thus,
+ * if _BBN is zero and pcib0 already exists, we try to read our
+ * bus number from the configuration registers at address _ADR.
+ */
+ status = acpi_EvaluateInteger(sc->ap_handle, "_BBN", &sc->ap_bus);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ device_printf(dev, "could not evaluate _BBN - %s\n",
+ AcpiFormatException(status));
+ return_VALUE(ENXIO);
+ } else {
+ /* if it's not found, assume 0 */
+ sc->ap_bus = 0;
+ }
+ }
+
+ /*
+ * If the bus is zero and pcib0 already exists, read the bus number
+ * via PCI config space.
+ */
+ busok = 1;
+ if (sc->ap_bus == 0 && devclass_get_device(pcib_devclass, 0) != dev) {
+ busok = 0;
+ status = acpi_EvaluateInteger(sc->ap_handle, "_ADR", &addr);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ device_printf(dev, "could not evaluate _ADR - %s\n",
+ AcpiFormatException(status));
+ return_VALUE(ENXIO);
+ } else
+ device_printf(dev, "could not determine config space address\n");
+ } else {
+ /* XXX: We assume bus 0. */
+ slot = addr >> 16;
+ func = addr & 0xffff;
+ if (bootverbose)
+ device_printf(dev, "reading config registers from 0:%d:%d\n",
+ slot, func);
+ if (host_pcib_get_busno(pci_cfgregread, 0, slot, func, &busno) == 0)
+ device_printf(dev, "could not read bus number from config space\n");
+ else {
+ sc->ap_bus = busno;
+ busok = 1;
+ }
+ }
+ }
+
+ /*
+ * If nothing else worked, hope that ACPI at least lays out the
+ * host-PCI bridges in order and that as a result our unit number
+ * is actually our bus number. There are several reasons this
+ * might not be true.
+ */
+ if (busok == 0) {
+ sc->ap_bus = device_get_unit(dev);
+ device_printf(dev, "trying bus number %d\n", sc->ap_bus);
+ }
+
+ /*
+ * Get our segment number by evaluating _SEG
+ * It's OK for this to not exist.
+ */
+ if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->ap_handle, "_SEG", &sc->ap_segment))) {
+ if (status != AE_NOT_FOUND) {
+ device_printf(dev, "could not evaluate _SEG - %s\n", AcpiFormatException(status));
+ return_VALUE(ENXIO);
+ }
+ /* if it's not found, assume 0 */
+ sc->ap_segment = 0;
+ }
+
+ return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_bus));
+}
+
+static int
+acpi_pcib_acpi_resume(device_t dev)
+{
+ struct acpi_hpcib_softc *sc = device_get_softc(dev);
+
+ return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_bus));
+}
+
+/*
+ * Support for standard PCI bridge ivars.
+ */
+static int
+acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct acpi_hpcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ *result = sc->ap_bus;
+ return(0);
+ case ACPI_IVAR_HANDLE:
+ *result = (uintptr_t)sc->ap_handle;
+ return(0);
+ }
+ return(ENOENT);
+}
+
+static int
+acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct acpi_hpcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->ap_bus = value;
+ return(0);
+ }
+ return(ENOENT);
+}
+
+static u_int32_t
+acpi_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes)
+{
+ return(pci_cfgregread(bus, slot, func, reg, bytes));
+}
+
+static void
+acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, u_int32_t data, int bytes)
+{
+ pci_cfgregwrite(bus, slot, func, reg, data, bytes);
+}
+
+static int
+acpi_pcib_acpi_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+ struct acpi_hpcib_softc *sc;
+
+ /* find the bridge softc */
+ sc = device_get_softc(pcib);
+ return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
+}
diff --git a/sys/dev/acpica/acpi_pcib_pci.c b/sys/dev/acpica/acpi_pcib_pci.c
new file mode 100644
index 0000000..5909e65
--- /dev/null
+++ b/sys/dev/acpica/acpi_pcib_pci.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "opt_acpi.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpi_pcibvar.h>
+
+#include <machine/pci_cfgreg.h>
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <pci/pcib_private.h>
+
+#include "pcib_if.h"
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("PCI_PCI")
+
+struct acpi_pcib_softc {
+ struct pcib_softc ap_pcibsc;
+ ACPI_HANDLE ap_handle;
+ ACPI_BUFFER ap_prt; /* interrupt routing table */
+};
+
+struct acpi_pcib_lookup_info {
+ UINT32 address;
+ ACPI_HANDLE handle;
+};
+
+static int acpi_pcib_pci_probe(device_t bus);
+static int acpi_pcib_pci_attach(device_t bus);
+static int acpi_pcib_pci_resume(device_t bus);
+static int acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result);
+static int acpi_pcib_pci_route_interrupt(device_t pcib,
+ device_t dev, int pin);
+
+static device_method_t acpi_pcib_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_pcib_pci_probe),
+ DEVMETHOD(device_attach, acpi_pcib_pci_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, acpi_pcib_pci_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, acpi_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, pcib_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_read_config, pcib_read_config),
+ DEVMETHOD(pcib_write_config, pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, acpi_pcib_pci_route_interrupt),
+
+ {0, 0}
+};
+
+static driver_t acpi_pcib_pci_driver = {
+ "pcib",
+ acpi_pcib_pci_methods,
+ sizeof(struct acpi_pcib_softc),
+};
+
+DRIVER_MODULE(acpi_pcib, pci, acpi_pcib_pci_driver, pcib_devclass, 0, 0);
+
+static int
+acpi_pcib_pci_probe(device_t dev)
+{
+
+ if ((pci_get_class(dev) != PCIC_BRIDGE) ||
+ (pci_get_subclass(dev) != PCIS_BRIDGE_PCI) ||
+ acpi_disabled("pci"))
+ return (ENXIO);
+ if (acpi_get_handle(dev) == NULL)
+ return (ENXIO);
+ if (!pci_cfgregopen())
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI PCI-PCI bridge");
+ return (-1000);
+}
+
+static int
+acpi_pcib_pci_attach(device_t dev)
+{
+ struct acpi_pcib_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ pcib_attach_common(dev);
+ sc = device_get_softc(dev);
+ sc->ap_handle = acpi_get_handle(dev);
+ return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_pcibsc.secbus));
+}
+
+static int
+acpi_pcib_pci_resume(device_t dev)
+{
+ struct acpi_pcib_softc *sc = device_get_softc(dev);
+
+ return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_pcibsc.secbus));
+}
+
+static int
+acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct acpi_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case ACPI_IVAR_HANDLE:
+ *result = (uintptr_t)sc->ap_handle;
+ return(0);
+ }
+ return(pcib_read_ivar(dev, child, which, result));
+}
+
+static int
+acpi_pcib_pci_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+ struct acpi_pcib_softc *sc;
+
+ sc = device_get_softc(pcib);
+ return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
+}
diff --git a/sys/dev/acpica/acpi_pcibvar.h b/sys/dev/acpica/acpi_pcibvar.h
new file mode 100644
index 0000000..a7a5f20
--- /dev/null
+++ b/sys/dev/acpica/acpi_pcibvar.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ACPI_PCIBVAR_H_
+#define _ACPI_PCIBVAR_H_
+
+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 *ptrbuf);
+int acpi_pcib_resume(device_t bus, ACPI_BUFFER *prt, int busno);
+
+int acpi_pci_link_config(device_t pcib, ACPI_BUFFER *prt, int busno);
+int acpi_pci_link_resume(device_t pcib, ACPI_BUFFER *prt, int busno);
+
+#endif
diff --git a/sys/dev/acpica/acpi_powerres.c b/sys/dev/acpica/acpi_powerres.c
new file mode 100644
index 0000000..fc42695
--- /dev/null
+++ b/sys/dev/acpica/acpi_powerres.c
@@ -0,0 +1,654 @@
+/*-
+ * Copyright (c) 2001 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h" /* XXX trim includes */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/ioccom.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+
+#include <machine/clock.h>
+
+#include <machine/resource.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+/*
+ * ACPI power resource management.
+ *
+ * Power resource behaviour is slightly complicated by the fact that
+ * a single power resource may provide power for more than one device.
+ * Thus, we must track the device(s) being powered by a given power
+ * resource, and only deactivate it when there are no powered devices.
+ *
+ * Note that this only manages resources for known devices. There is an
+ * ugly case where we may turn of power to a device which is in use because
+ * we don't know that it depends on a given resource. We should perhaps
+ * try to be smarter about this, but a more complete solution would involve
+ * scanning all of the ACPI namespace to find devices we're not currently
+ * aware of, and this raises questions about whether they should be left
+ * on, turned off, etc.
+ *
+ * XXX locking
+ */
+
+MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_POWER
+ACPI_MODULE_NAME("POWERRES")
+
+/* return values from _STA on a power resource */
+#define ACPI_PWR_OFF 0
+#define ACPI_PWR_ON 1
+
+/*
+ * A relationship between a power resource and a consumer.
+ */
+struct acpi_powerreference {
+ struct acpi_powerconsumer *ar_consumer;
+ struct acpi_powerresource *ar_resource;
+ TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */
+ TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */
+};
+
+/*
+ * A power-managed device.
+ */
+struct acpi_powerconsumer {
+ ACPI_HANDLE ac_consumer; /* device which is powered */
+ int ac_state;
+ TAILQ_ENTRY(acpi_powerconsumer) ac_link;
+ TAILQ_HEAD(,acpi_powerreference) ac_references;
+};
+
+/*
+ * A power resource.
+ */
+struct acpi_powerresource {
+ TAILQ_ENTRY(acpi_powerresource) ap_link;
+ TAILQ_HEAD(,acpi_powerreference) ap_references;
+ ACPI_HANDLE ap_resource; /* the resource's handle */
+ ACPI_INTEGER ap_systemlevel;
+ ACPI_INTEGER ap_order;
+};
+
+static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources;
+static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers;
+
+static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer);
+static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
+static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res);
+static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res);
+static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg);
+static ACPI_STATUS acpi_pwr_switch_power(void);
+static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res);
+static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer);
+
+/*
+ * Initialise our lists.
+ */
+static void
+acpi_pwr_init(void *junk)
+{
+ TAILQ_INIT(&acpi_powerresources);
+ TAILQ_INIT(&acpi_powerconsumers);
+}
+SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL);
+
+/*
+ * Register a power resource.
+ *
+ * It's OK to call this if we already know about the resource.
+ */
+static ACPI_STATUS
+acpi_pwr_register_resource(ACPI_HANDLE res)
+{
+ ACPI_STATUS status;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *obj;
+ struct acpi_powerresource *rp, *srp;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ rp = NULL;
+ buf.Pointer = NULL;
+
+ /* look to see if we know about this resource */
+ if (acpi_pwr_find_resource(res) != NULL)
+ return_ACPI_STATUS(AE_OK); /* already know about it */
+
+ /* allocate a new resource */
+ if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
+ status = AE_NO_MEMORY;
+ goto out;
+ }
+ TAILQ_INIT(&rp->ap_references);
+ rp->ap_resource = res;
+
+ /* get the Power Resource object */
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
+ goto out;
+ }
+ obj = buf.Pointer;
+ if (obj->Type != ACPI_TYPE_POWER) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res)));
+ status = AE_TYPE;
+ goto out;
+ }
+ rp->ap_systemlevel = obj->PowerResource.SystemLevel;
+ rp->ap_order = obj->PowerResource.ResourceOrder;
+
+ /* sort the resource into the list */
+ status = AE_OK;
+ srp = TAILQ_FIRST(&acpi_powerresources);
+ if ((srp == NULL) || (rp->ap_order < srp->ap_order)) {
+ TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
+ goto done;
+ }
+ TAILQ_FOREACH(srp, &acpi_powerresources, ap_link)
+ if (rp->ap_order < srp->ap_order) {
+ TAILQ_INSERT_BEFORE(srp, rp, ap_link);
+ goto done;
+ }
+ TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
+
+ done:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res)));
+ out:
+ if (buf.Pointer != NULL)
+ AcpiOsFree(buf.Pointer);
+ if (ACPI_FAILURE(status) && (rp != NULL))
+ free(rp, M_ACPIPWR);
+ return_ACPI_STATUS(status);
+}
+
+/*
+ * Deregister a power resource.
+ */
+static ACPI_STATUS
+acpi_pwr_deregister_resource(ACPI_HANDLE res)
+{
+ struct acpi_powerresource *rp;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ rp = NULL;
+
+ /* find the resource */
+ if ((rp = acpi_pwr_find_resource(res)) == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ /* check that there are no consumers referencing this resource */
+ if (TAILQ_FIRST(&rp->ap_references) != NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ /* pull it off the list and free it */
+ TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
+ free(rp, M_ACPIPWR);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res)));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Register a power consumer.
+ *
+ * It's OK to call this if we already know about the consumer.
+ */
+static ACPI_STATUS
+acpi_pwr_register_consumer(ACPI_HANDLE consumer)
+{
+ struct acpi_powerconsumer *pc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /* check to see whether we know about this consumer already */
+ if ((pc = acpi_pwr_find_consumer(consumer)) != NULL)
+ return_ACPI_STATUS(AE_OK);
+
+ /* allocate a new power consumer */
+ if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL)
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
+ TAILQ_INIT(&pc->ac_references);
+ pc->ac_consumer = consumer;
+
+ pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer)));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Deregister a power consumer.
+ *
+ * This should only be done once the consumer has been powered off.
+ * (XXX is this correct? Check once implemented)
+ */
+static ACPI_STATUS
+acpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
+{
+ struct acpi_powerconsumer *pc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /* find the consumer */
+ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ /* make sure the consumer's not referencing anything right now */
+ if (TAILQ_FIRST(&pc->ac_references) != NULL)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+ /* pull the consumer off the list and free it */
+ TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer)));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Set a power consumer to a particular power state.
+ */
+ACPI_STATUS
+acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
+{
+ struct acpi_powerconsumer *pc;
+ struct acpi_powerreference *pr;
+ ACPI_HANDLE method_handle, reslist_handle, pr0_handle;
+ ACPI_BUFFER reslist_buffer;
+ ACPI_OBJECT *reslist_object;
+ ACPI_STATUS status;
+ char *method_name, *reslist_name;
+ int res_changed;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /* find the consumer */
+ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
+ if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
+ return_ACPI_STATUS(status);
+ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
+ return_ACPI_STATUS(AE_ERROR); /* something very wrong */
+ }
+ }
+
+ /* check for valid transitions */
+ if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0))
+ return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */
+
+ /* find transition mechanism(s) */
+ switch(state) {
+ case ACPI_STATE_D0:
+ method_name = "_PS0";
+ reslist_name = "_PR0";
+ break;
+ case ACPI_STATE_D1:
+ method_name = "_PS1";
+ reslist_name = "_PR1";
+ break;
+ case ACPI_STATE_D2:
+ method_name = "_PS2";
+ reslist_name = "_PR2";
+ break;
+ case ACPI_STATE_D3:
+ method_name = "_PS3";
+ reslist_name = "_PR3";
+ break;
+ default:
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
+ acpi_name(consumer), pc->ac_state, state));
+
+ /*
+ * Verify that this state is supported, ie. one of method or
+ * reslist must be present. We need to do this before we go
+ * dereferencing resources (since we might be trying to go to
+ * a state we don't support).
+ *
+ * Note that if any states are supported, the device has to
+ * support D0 and D3. It's never an error to try to go to
+ * D0.
+ */
+ reslist_buffer.Pointer = NULL;
+ reslist_object = NULL;
+ if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
+ method_handle = NULL;
+ if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
+ reslist_handle = NULL;
+ if ((reslist_handle == NULL) && (method_handle == NULL)) {
+ if (state == ACPI_STATE_D0) {
+ pc->ac_state = ACPI_STATE_D0;
+ return_ACPI_STATUS(AE_OK);
+ }
+ if (state != ACPI_STATE_D3) {
+ goto bad;
+ }
+
+ /* turn off the resources listed in _PR0 to go to D3. */
+ if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
+ goto bad;
+ }
+ reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) {
+ goto bad;
+ }
+ reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
+ if ((reslist_object->Type != ACPI_TYPE_PACKAGE) ||
+ (reslist_object->Package.Count == 0)) {
+ goto bad;
+ }
+ AcpiOsFree(reslist_buffer.Pointer);
+ reslist_buffer.Pointer = NULL;
+ reslist_object = NULL;
+ }
+
+ /*
+ * Check that we can actually fetch the list of power resources
+ */
+ if (reslist_handle != NULL) {
+ reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n",
+ acpi_name(reslist_handle)));
+ goto out;
+ }
+ reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
+ if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
+ reslist_object->Type));
+ status = AE_TYPE;
+ goto out;
+ }
+ }
+
+ /*
+ * Now we are ready to switch, so kill off any current power resource references.
+ */
+ res_changed = 0;
+ while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
+ res_changed = 1;
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource)));
+ TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
+ TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
+ free(pr, M_ACPIPWR);
+ }
+
+ /*
+ * Add new power resource references, if we have any. Traverse the
+ * package that we got from evaluating reslist_handle, and look up each
+ * of the resources that are referenced.
+ */
+ if (reslist_object != NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
+ reslist_object->Package.Count));
+ acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc);
+ res_changed = 1;
+ }
+
+ /*
+ * If we changed anything in the resource list, we need to run a switch
+ * pass now.
+ */
+ if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n",
+ acpi_name(consumer), state));
+ goto out; /* XXX is this appropriate? Should we return to previous state? */
+ }
+
+ /* invoke power state switch method (if present) */
+ if (method_handle != NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n",
+ acpi_name(method_handle)));
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
+ AcpiFormatException(status)));
+ pc->ac_state = ACPI_STATE_UNKNOWN;
+ goto out; /* XXX Should we return to previous state? */
+ }
+ }
+
+ /* transition was successful */
+ pc->ac_state = state;
+ return_ACPI_STATUS(AE_OK);
+
+ bad:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n",
+ state));
+ status = AE_BAD_PARAMETER;
+
+ out:
+ if (reslist_buffer.Pointer != NULL)
+ AcpiOsFree(reslist_buffer.Pointer);
+ return_ACPI_STATUS(status);
+}
+
+/*
+ * Called to create a reference between a power consumer and a power resource
+ * identified in the object.
+ */
+static void
+acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
+{
+ struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg;
+ struct acpi_powerreference *pr;
+ struct acpi_powerresource *rp;
+ ACPI_HANDLE res;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /* check the object type */
+ switch (obj->Type) {
+ case ACPI_TYPE_ANY:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
+ acpi_name(pc->ac_consumer), acpi_name(obj->Reference.Handle)));
+
+ res = obj->Reference.Handle;
+ break;
+
+ case ACPI_TYPE_STRING:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
+ acpi_name(pc->ac_consumer), obj->String.Pointer));
+
+ /* get the handle of the resource */
+ if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n",
+ obj->String.Pointer));
+ return_VOID;
+ }
+ break;
+
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n",
+ obj->Type));
+ return_VOID;
+ }
+
+ /* create/look up the resource */
+ if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n",
+ obj->String.Pointer, AcpiFormatException(status)));
+ return_VOID;
+ }
+ if ((rp = acpi_pwr_find_resource(res)) == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
+ return_VOID;
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource)));
+
+ /* create a reference between the consumer and resource */
+ if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n"));
+ return_VOID;
+ }
+ pr->ar_consumer = pc;
+ pr->ar_resource = rp;
+ TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
+ TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
+
+ return_VOID;
+}
+
+
+/*
+ * Switch power resources to conform to the desired state.
+ *
+ * Consumers may have modified the power resource list in an arbitrary
+ * fashion; we sweep it in sequence order.
+ */
+static ACPI_STATUS
+acpi_pwr_switch_power(void)
+{
+ struct acpi_powerresource *rp;
+ ACPI_STATUS status;
+ int cur;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Sweep the list forwards turning things on.
+ */
+ TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
+ if (TAILQ_FIRST(&rp->ap_references) == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n",
+ acpi_name(rp->ap_resource)));
+ continue;
+ }
+
+ /* we could cache this if we trusted it not to change under us */
+ if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
+ acpi_name(rp->ap_resource), status));
+ continue; /* XXX is this correct? Always switch if in doubt? */
+ }
+
+ /*
+ * Switch if required. Note that we ignore the result of the switch
+ * effort; we don't know what to do if it fails, so checking wouldn't
+ * help much.
+ */
+ if (cur != ACPI_PWR_ON) {
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n",
+ acpi_name(rp->ap_resource), AcpiFormatException(status)));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource)));
+ }
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource)));
+ }
+ }
+
+ /*
+ * Sweep the list backwards turning things off.
+ */
+ TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) {
+ if (TAILQ_FIRST(&rp->ap_references) != NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n",
+ acpi_name(rp->ap_resource)));
+ continue;
+ }
+
+ /* we could cache this if we trusted it not to change under us */
+ if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
+ acpi_name(rp->ap_resource), status));
+ continue; /* XXX is this correct? Always switch if in doubt? */
+ }
+
+ /*
+ * Switch if required. Note that we ignore the result of the switch
+ * effort; we don't know what to do if it fails, so checking wouldn't
+ * help much.
+ */
+ if (cur != ACPI_PWR_OFF) {
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n",
+ acpi_name(rp->ap_resource), AcpiFormatException(status)));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource)));
+ }
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource)));
+ }
+ }
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Find a power resource's control structure.
+ */
+static struct acpi_powerresource *
+acpi_pwr_find_resource(ACPI_HANDLE res)
+{
+ struct acpi_powerresource *rp;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ TAILQ_FOREACH(rp, &acpi_powerresources, ap_link)
+ if (rp->ap_resource == res)
+ break;
+ return_PTR(rp);
+}
+
+/*
+ * Find a power consumer's control structure.
+ */
+static struct acpi_powerconsumer *
+acpi_pwr_find_consumer(ACPI_HANDLE consumer)
+{
+ struct acpi_powerconsumer *pc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link)
+ if (pc->ac_consumer == consumer)
+ break;
+ return_PTR(pc);
+}
+
diff --git a/sys/dev/acpica/acpi_resource.c b/sys/dev/acpica/acpi_resource.c
new file mode 100644
index 0000000..9dc0e3c
--- /dev/null
+++ b/sys/dev/acpica/acpi_resource.c
@@ -0,0 +1,592 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("RESOURCE")
+
+/*
+ * Fetch a device's resources and associate them with the device.
+ *
+ * Note that it might be nice to also locate ACPI-specific resource items, such
+ * as GPE bits.
+ *
+ * We really need to split the resource-fetching code out from the
+ * resource-parsing code, since we may want to use the parsing
+ * code for _PRS someday.
+ */
+ACPI_STATUS
+acpi_parse_resources(device_t dev, ACPI_HANDLE handle, struct acpi_parse_resource_set *set)
+{
+ ACPI_BUFFER buf;
+ ACPI_RESOURCE *res;
+ char *curr, *last;
+ ACPI_STATUS status;
+ void *context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ /*
+ * Special-case some devices that abuse _PRS/_CRS to mean
+ * something other than "I consume this resource".
+ *
+ * XXX do we really need this? It's only relevant once
+ * we start always-allocating these resources, and even
+ * then, the only special-cased device is likely to be
+ * the PCI interrupt link.
+ */
+
+ /*
+ * Fetch the device's current resources.
+ */
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE((status = AcpiGetCurrentResources(handle, &buf)))) {
+ if (status != AE_NOT_FOUND)
+ printf("can't fetch resources for %s - %s\n",
+ acpi_name(handle), AcpiFormatException(status));
+ return_ACPI_STATUS(status);
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "%s - got %ld bytes of resources\n",
+ acpi_name(handle), (long)buf.Length));
+ set->set_init(dev, &context);
+
+ /*
+ * Iterate through the resources
+ */
+ curr = buf.Pointer;
+ last = (char *)buf.Pointer + buf.Length;
+ while (curr < last) {
+ res = (ACPI_RESOURCE *)curr;
+ curr += res->Length;
+
+ /*
+ * Handle the individual resource types
+ */
+ switch(res->Id) {
+ case ACPI_RSTYPE_END_TAG:
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "EndTag\n"));
+ curr = last;
+ break;
+
+ case ACPI_RSTYPE_FIXED_IO:
+ if (res->Data.FixedIo.RangeLength <= 0)
+ break;
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedIo 0x%x/%d\n",
+ res->Data.FixedIo.BaseAddress,
+ res->Data.FixedIo.RangeLength));
+ set->set_ioport(dev, context,
+ res->Data.FixedIo.BaseAddress,
+ res->Data.FixedIo.RangeLength);
+ break;
+
+ case ACPI_RSTYPE_IO:
+ if (res->Data.Io.RangeLength <= 0)
+ break;
+ if (res->Data.Io.MinBaseAddress == res->Data.Io.MaxBaseAddress) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x/%d\n",
+ res->Data.Io.MinBaseAddress,
+ res->Data.Io.RangeLength));
+ set->set_ioport(dev, context,
+ res->Data.Io.MinBaseAddress,
+ res->Data.Io.RangeLength);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x-0x%x/%d\n",
+ res->Data.Io.MinBaseAddress,
+ res->Data.Io.MaxBaseAddress,
+ res->Data.Io.RangeLength));
+ set->set_iorange(dev, context,
+ res->Data.Io.MinBaseAddress,
+ res->Data.Io.MaxBaseAddress,
+ res->Data.Io.RangeLength, res->Data.Io.Alignment);
+ }
+ break;
+
+ case ACPI_RSTYPE_FIXED_MEM32:
+ if (res->Data.FixedMemory32.RangeLength <= 0)
+ break;
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedMemory32 0x%x/%d\n",
+ res->Data.FixedMemory32.RangeBaseAddress,
+ res->Data.FixedMemory32.RangeLength));
+ set->set_memory(dev, context, res->Data.FixedMemory32.RangeBaseAddress,
+ res->Data.FixedMemory32.RangeLength);
+ break;
+
+ case ACPI_RSTYPE_MEM32:
+ if (res->Data.Memory32.RangeLength <= 0)
+ break;
+ if (res->Data.Memory32.MinBaseAddress == res->Data.Memory32.MaxBaseAddress) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x/%d\n",
+ res->Data.Memory32.MinBaseAddress,
+ res->Data.Memory32.RangeLength));
+ set->set_memory(dev, context,
+ res->Data.Memory32.MinBaseAddress,
+ res->Data.Memory32.RangeLength);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x-0x%x/%d\n",
+ res->Data.Memory32.MinBaseAddress,
+ res->Data.Memory32.MaxBaseAddress,
+ res->Data.Memory32.RangeLength));
+ set->set_memoryrange(dev, context,
+ res->Data.Memory32.MinBaseAddress,
+ res->Data.Memory32.MaxBaseAddress,
+ res->Data.Memory32.RangeLength,
+ res->Data.Memory32.Alignment);
+ }
+ break;
+
+ case ACPI_RSTYPE_MEM24:
+ if (res->Data.Memory24.RangeLength <= 0)
+ break;
+ if (res->Data.Memory24.MinBaseAddress == res->Data.Memory24.MaxBaseAddress) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x/%d\n",
+ res->Data.Memory24.MinBaseAddress,
+ res->Data.Memory24.RangeLength));
+ set->set_memory(dev, context, res->Data.Memory24.MinBaseAddress,
+ res->Data.Memory24.RangeLength);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x-0x%x/%d\n",
+ res->Data.Memory24.MinBaseAddress,
+ res->Data.Memory24.MaxBaseAddress,
+ res->Data.Memory24.RangeLength));
+ set->set_memoryrange(dev, context,
+ res->Data.Memory24.MinBaseAddress,
+ res->Data.Memory24.MaxBaseAddress,
+ res->Data.Memory24.RangeLength,
+ res->Data.Memory24.Alignment);
+ }
+ break;
+
+ case ACPI_RSTYPE_IRQ:
+ /*
+ * from 1.0b 6.4.2
+ * "This structure is repeated for each separate interrupt
+ * required"
+ */
+ set->set_irq(dev, context, res->Data.Irq.Interrupts,
+ res->Data.Irq.NumberOfInterrupts);
+ break;
+
+ case ACPI_RSTYPE_DMA:
+ /*
+ * from 1.0b 6.4.3
+ * "This structure is repeated for each separate dma channel
+ * required"
+ */
+
+ set->set_drq(dev, context, res->Data.Dma.Channels,
+ res->Data.Dma.NumberOfChannels);
+ break;
+
+ case ACPI_RSTYPE_START_DPF:
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "start dependant functions\n"));
+ set->set_start_dependant(dev, context,
+ res->Data.StartDpf.CompatibilityPriority);
+ break;
+
+ case ACPI_RSTYPE_END_DPF:
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "end dependant functions\n"));
+ set->set_end_dependant(dev, context);
+ break;
+
+ case ACPI_RSTYPE_ADDRESS32:
+ if (res->Data.Address32.AddressLength <= 0)
+ break;
+ if (res->Data.Address32.ProducerConsumer != ACPI_CONSUMER) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address32 %s producer\n",
+ (res->Data.Address32.ResourceType == ACPI_IO_RANGE) ?
+ "IO" : "Memory"));
+ break;
+ }
+ if (res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE &&
+ res->Data.Address32.ResourceType != ACPI_IO_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
+ "ignored Address32 for non-memory, non-I/O\n"));
+ break;
+ }
+
+ if ((res->Data.Address32.MinAddressFixed == ACPI_ADDRESS_FIXED) &&
+ (res->Data.Address32.MaxAddressFixed == ACPI_ADDRESS_FIXED)) {
+ if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x/%d\n",
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.AddressLength));
+ set->set_memory(dev, context,
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.AddressLength);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x/%d\n",
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.AddressLength));
+ set->set_ioport(dev, context,
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.AddressLength);
+ }
+ } else {
+ if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x-0x%x/%d\n",
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.MaxAddressRange,
+ res->Data.Address32.AddressLength));
+ set->set_memoryrange(dev, context,
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.MaxAddressRange,
+ res->Data.Address32.AddressLength,
+ res->Data.Address32.Granularity);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x-0x%x/%d\n",
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.MaxAddressRange,
+ res->Data.Address32.AddressLength));
+ set->set_iorange(dev, context,
+ res->Data.Address32.MinAddressRange,
+ res->Data.Address32.MaxAddressRange,
+ res->Data.Address32.AddressLength,
+ res->Data.Address32.Granularity);
+ }
+ }
+ break;
+
+ case ACPI_RSTYPE_ADDRESS16:
+ if (res->Data.Address16.AddressLength <= 0)
+ break;
+ if (res->Data.Address16.ProducerConsumer != ACPI_CONSUMER) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address16 %s producer\n",
+ (res->Data.Address16.ResourceType == ACPI_IO_RANGE) ?
+ "IO" : "Memory"));
+ break;
+ }
+ if (res->Data.Address16.ResourceType != ACPI_MEMORY_RANGE &&
+ res->Data.Address16.ResourceType != ACPI_IO_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
+ "ignored Address16 for non-memory, non-I/O\n"));
+ break;
+ }
+
+ if ((res->Data.Address16.MinAddressFixed == ACPI_ADDRESS_FIXED) &&
+ (res->Data.Address16.MaxAddressFixed == ACPI_ADDRESS_FIXED)) {
+ if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x/%d\n",
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.AddressLength));
+ set->set_memory(dev, context,
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.AddressLength);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x/%d\n",
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.AddressLength));
+ set->set_ioport(dev, context,
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.AddressLength);
+ }
+ } else {
+ if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x-0x%x/%d\n",
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.MaxAddressRange,
+ res->Data.Address16.AddressLength));
+ set->set_memoryrange(dev, context,
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.MaxAddressRange,
+ res->Data.Address16.AddressLength,
+ res->Data.Address16.Granularity);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x-0x%x/%d\n",
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.MaxAddressRange,
+ res->Data.Address16.AddressLength));
+ set->set_iorange(dev, context,
+ res->Data.Address16.MinAddressRange,
+ res->Data.Address16.MaxAddressRange,
+ res->Data.Address16.AddressLength,
+ res->Data.Address16.Granularity);
+ }
+ }
+ break;
+
+ case ACPI_RSTYPE_ADDRESS64:
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented Address64 resource\n"));
+ break;
+
+ case ACPI_RSTYPE_EXT_IRQ:
+ /* XXX special handling? */
+ set->set_irq(dev, context,res->Data.ExtendedIrq.Interrupts,
+ res->Data.ExtendedIrq.NumberOfInterrupts);
+ break;
+
+ case ACPI_RSTYPE_VENDOR:
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented VendorSpecific resource\n"));
+ break;
+ default:
+ break;
+ }
+ }
+ AcpiOsFree(buf.Pointer);
+ set->set_done(dev, context);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Resource-set vectors used to attach _CRS-derived resources
+ * to an ACPI device.
+ */
+static void acpi_res_set_init(device_t dev, void **context);
+static void acpi_res_set_done(device_t dev, void *context);
+static void acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length);
+static void acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high,
+ u_int32_t length, u_int32_t align);
+static void acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length);
+static void acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high,
+ u_int32_t length, u_int32_t align);
+static void acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq,
+ int count);
+static void acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq,
+ int count);
+static void acpi_res_set_start_dependant(device_t dev, void *context, int preference);
+static void acpi_res_set_end_dependant(device_t dev, void *context);
+
+struct acpi_parse_resource_set acpi_res_parse_set = {
+ acpi_res_set_init,
+ acpi_res_set_done,
+ acpi_res_set_ioport,
+ acpi_res_set_iorange,
+ acpi_res_set_memory,
+ acpi_res_set_memoryrange,
+ acpi_res_set_irq,
+ acpi_res_set_drq,
+ acpi_res_set_start_dependant,
+ acpi_res_set_end_dependant
+};
+
+struct acpi_res_context {
+ int ar_nio;
+ int ar_nmem;
+ int ar_nirq;
+ int ar_ndrq;
+};
+
+static void
+acpi_res_set_init(device_t dev, void **context)
+{
+ struct acpi_res_context *cp;
+
+ if ((cp = AcpiOsAllocate(sizeof(*cp))) != NULL) {
+ bzero(cp, sizeof(*cp));
+ *context = cp;
+ }
+}
+
+static void
+acpi_res_set_done(device_t dev, void *context)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ AcpiOsFree(cp);
+}
+
+static void
+acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ bus_set_resource(dev, SYS_RES_IOPORT, cp->ar_nio++, base, length);
+}
+
+static void
+acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ device_printf(dev, "I/O range not supported\n");
+}
+
+static void
+acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+
+ bus_set_resource(dev, SYS_RES_MEMORY, cp->ar_nmem++, base, length);
+}
+
+static void
+acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ device_printf(dev, "memory range not supported\n");
+}
+
+static void
+acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, int count)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ if (irq == NULL)
+ return;
+
+ /*This implements no resource relocation.*/
+ if(count != 1)
+ return;
+
+ bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1);
+}
+
+static void
+acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, int count)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ if (drq == NULL)
+ return;
+
+ /*This implements no resource relocation.*/
+ if(count != 1)
+ return;
+
+ bus_set_resource(dev, SYS_RES_DRQ, cp->ar_ndrq++, *drq, 1);
+}
+
+static void
+acpi_res_set_start_dependant(device_t dev, void *context, int preference)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+ device_printf(dev, "dependant functions not supported\n");
+}
+
+static void
+acpi_res_set_end_dependant(device_t dev, void *context)
+{
+ struct acpi_res_context *cp = (struct acpi_res_context *)context;
+
+ if (cp == NULL)
+ return;
+}
+
+/*
+ * Resource-owning placeholders.
+ *
+ * This code "owns" system resource objects that aren't
+ * otherwise useful to devices, and which shouldn't be
+ * considered "free".
+ *
+ * Note that some systems claim *all* of the physical address space
+ * with a PNP0C01 device, so we cannot correctly "own" system memory
+ * here (must be done in the SMAP handler on x86 systems, for
+ * example).
+ */
+
+static int acpi_sysresource_probe(device_t dev);
+static int acpi_sysresource_attach(device_t dev);
+
+static device_method_t acpi_sysresource_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_sysresource_probe),
+ DEVMETHOD(device_attach, acpi_sysresource_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_sysresource_driver = {
+ "acpi_sysresource",
+ acpi_sysresource_methods,
+ 0,
+};
+
+static devclass_t acpi_sysresource_devclass;
+DRIVER_MODULE(acpi_sysresource, acpi, acpi_sysresource_driver, acpi_sysresource_devclass, 0, 0);
+
+static int
+acpi_sysresource_probe(device_t dev)
+{
+ if (acpi_disabled("sysresource"))
+ return(ENXIO);
+ if (acpi_MatchHid(dev, "PNP0C02")) {
+ device_set_desc(dev, "system resource");
+ } else {
+ return(ENXIO);
+ }
+ device_quiet(dev);
+ return(-100);
+}
+
+static int
+acpi_sysresource_attach(device_t dev)
+{
+ struct resource *res;
+ int i, rid;
+
+ /*
+ * Suck up all the resources that might have been assigned to us.
+ * Note that it's impossible to tell the difference between a
+ * resource that someone else has claimed, and one that doesn't
+ * exist.
+ */
+ for (i = 0; i < 100; i++) {
+ rid = i;
+ res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, 0);
+ rid = i;
+ res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 0);
+ rid = i;
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE);
+ }
+ return(0);
+}
diff --git a/sys/dev/acpica/acpi_thermal.c b/sys/dev/acpica/acpi_thermal.c
new file mode 100644
index 0000000..7806180
--- /dev/null
+++ b/sys/dev/acpica/acpi_thermal.c
@@ -0,0 +1,828 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/power.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_THERMAL
+ACPI_MODULE_NAME("THERMAL")
+
+#define TZ_ZEROC 2732
+#define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
+
+#define TZ_NOTIFY_TEMPERATURE 0x80
+#define TZ_NOTIFY_DEVICES 0x81
+#define TZ_NOTIFY_LEVELS 0x82
+
+#define TZ_POLLRATE 30 /* every 30 seconds by default */
+
+#define TZ_NUMLEVELS 10 /* defined by ACPI spec */
+struct acpi_tz_zone {
+ int ac[TZ_NUMLEVELS];
+ ACPI_BUFFER al[TZ_NUMLEVELS];
+ int crt;
+ int hot;
+ ACPI_BUFFER psl;
+ int psv;
+ int tc1;
+ int tc2;
+ int tsp;
+ int tzp;
+};
+
+
+struct acpi_tz_softc {
+ device_t tz_dev; /* device handle */
+ ACPI_HANDLE tz_handle; /* thermal zone handle */
+ int tz_temperature; /* current temperature */
+ int tz_active; /* current active cooling */
+#define TZ_ACTIVE_NONE -1
+ int tz_requested; /* user-requested minimum active cooling */
+ int tz_thflags; /* current temperature-related flags */
+#define TZ_THFLAG_NONE 0
+#define TZ_THFLAG_PSV (1<<0)
+#define TZ_THFLAG_HOT (1<<2)
+#define TZ_THFLAG_CRT (1<<3)
+ int tz_flags;
+#define TZ_FLAG_NO_SCP (1<<0) /* no _SCP method */
+#define TZ_FLAG_GETPROFILE (1<<1) /* fetch power_profile in timeout */
+ struct timespec tz_cooling_started; /* current cooling starting time */
+
+ struct sysctl_ctx_list tz_sysctl_ctx; /* sysctl tree */
+ struct sysctl_oid *tz_sysctl_tree;
+
+ struct acpi_tz_zone tz_zone; /* thermal zone parameters */
+ int tz_tmp_updating;
+};
+
+static int acpi_tz_probe(device_t dev);
+static int acpi_tz_attach(device_t dev);
+static int acpi_tz_establish(struct acpi_tz_softc *sc);
+static void acpi_tz_monitor(struct acpi_tz_softc *sc);
+static void acpi_tz_all_off(struct acpi_tz_softc *sc);
+static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
+static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
+static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
+static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
+static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
+static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
+static void acpi_tz_timeout(struct acpi_tz_softc *sc);
+static void acpi_tz_power_profile(void *arg);
+
+static void acpi_tz_thread(void *arg);
+static struct proc *acpi_tz_proc;
+
+static device_method_t acpi_tz_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_tz_probe),
+ DEVMETHOD(device_attach, acpi_tz_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_tz_driver = {
+ "acpi_tz",
+ acpi_tz_methods,
+ sizeof(struct acpi_tz_softc),
+};
+
+static devclass_t acpi_tz_devclass;
+DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
+
+static struct sysctl_ctx_list acpi_tz_sysctl_ctx;
+static struct sysctl_oid *acpi_tz_sysctl_tree;
+
+static int acpi_tz_min_runtime = 0;/* minimum cooling run time */
+static int acpi_tz_polling_rate = TZ_POLLRATE;
+
+/*
+ * Match an ACPI thermal zone.
+ */
+static int
+acpi_tz_probe(device_t dev)
+{
+ int result;
+ ACPI_LOCK_DECL;
+
+ ACPI_LOCK;
+
+ /* no FUNCTION_TRACE - too noisy */
+
+ if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
+ !acpi_disabled("thermal")) {
+ device_set_desc(dev, "thermal zone");
+ result = -10;
+ } else {
+ result = ENXIO;
+ }
+ ACPI_UNLOCK;
+ return(result);
+}
+
+/*
+ * Attach to an ACPI thermal zone.
+ */
+static int
+acpi_tz_attach(device_t dev)
+{
+ struct acpi_tz_softc *sc;
+ struct acpi_softc *acpi_sc;
+ int error;
+ char oidname[8];
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_LOCK;
+
+ sc = device_get_softc(dev);
+ sc->tz_dev = dev;
+ sc->tz_handle = acpi_get_handle(dev);
+ sc->tz_requested = TZ_ACTIVE_NONE;
+ sc->tz_tmp_updating = 0;
+
+ /*
+ * Parse the current state of the thermal zone and build control
+ * structures.
+ */
+ if ((error = acpi_tz_establish(sc)) != 0)
+ goto out;
+
+ /*
+ * Register for any Notify events sent to this zone.
+ */
+ AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
+ acpi_tz_notify_handler, sc);
+
+ /*
+ * Create our sysctl nodes.
+ *
+ * XXX we need a mechanism for adding nodes under ACPI.
+ */
+ if (device_get_unit(dev) == 0) {
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ sysctl_ctx_init(&acpi_tz_sysctl_ctx);
+ acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
+ OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
+ SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
+ OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
+ &acpi_tz_min_runtime, 0, "minimum cooling run time in sec");
+ SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
+ OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
+ &acpi_tz_polling_rate, 0, "monitor polling rate");
+ }
+ sysctl_ctx_init(&sc->tz_sysctl_ctx);
+ sprintf(oidname, "tz%d", device_get_unit(dev));
+ sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
+ oidname, CTLFLAG_RD, 0, "");
+ SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "temperature", CTLFLAG_RD,
+ &sc->tz_temperature, 0, "current thermal zone temperature");
+ SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acpi_tz_active_sysctl, "I", "");
+
+ SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "thermal_flags", CTLFLAG_RD,
+ &sc->tz_thflags, 0, "thermal zone flags");
+ SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "_PSV", CTLFLAG_RD,
+ &sc->tz_zone.psv, 0, "");
+ SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "_HOT", CTLFLAG_RD,
+ &sc->tz_zone.hot, 0, "");
+ SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "_CRT", CTLFLAG_RD,
+ &sc->tz_zone.crt, 0, "");
+ SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
+ OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
+ sizeof(sc->tz_zone.ac), "I", "");
+
+
+ /*
+ * Register our power profile event handler, and flag it for a manual
+ * invocation by our timeout. We defer it like this so that the rest
+ * of the subsystem has time to come up.
+ */
+ EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0);
+ sc->tz_flags |= TZ_FLAG_GETPROFILE;
+
+ /*
+ * Don't bother evaluating/printing the temperature at this point;
+ * on many systems it'll be bogus until the EC is running.
+ */
+
+ /*
+ * Create our thread; we only need one, it will service all of the
+ * thermal zones.
+ */
+ if (acpi_tz_proc == NULL) {
+ error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
+ RFHIGHPID, 0, "acpi_thermal");
+ if (error != 0) {
+ device_printf(sc->tz_dev, "could not create thread - %d", error);
+ goto out;
+ }
+ }
+
+ out:
+ ACPI_UNLOCK;
+
+ return_VALUE(error);
+}
+
+/*
+ * Parse the current state of this thermal zone and set up to use it.
+ *
+ * Note that we may have previous state, which will have to be discarded.
+ */
+static int
+acpi_tz_establish(struct acpi_tz_softc *sc)
+{
+ ACPI_OBJECT *obj;
+ int i;
+ char nbuf[8];
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ /*
+ * Power everything off and erase any existing state.
+ */
+ acpi_tz_all_off(sc);
+ for (i = 0; i < TZ_NUMLEVELS; i++)
+ if (sc->tz_zone.al[i].Pointer != NULL)
+ AcpiOsFree(sc->tz_zone.al[i].Pointer);
+ if (sc->tz_zone.psl.Pointer != NULL)
+ AcpiOsFree(sc->tz_zone.psl.Pointer);
+ bzero(&sc->tz_zone, sizeof(sc->tz_zone));
+
+ /*
+ * Evaluate thermal zone parameters.
+ */
+ for (i = 0; i < TZ_NUMLEVELS; i++) {
+ sprintf(nbuf, "_AC%d", i);
+ acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
+ sprintf(nbuf, "_AL%d", i);
+ sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
+ sc->tz_zone.al[i].Pointer = NULL;
+ AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
+ obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
+ if (obj != NULL) {
+ /* should be a package containing a list of power objects */
+ if (obj->Type != ACPI_TYPE_PACKAGE) {
+ device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
+ nbuf, obj->Type);
+ return_VALUE(ENXIO);
+ }
+ }
+ }
+ acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
+ acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
+ sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
+ sc->tz_zone.psl.Pointer = NULL;
+ AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
+ acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
+ acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
+ acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
+ acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
+ acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
+
+ /*
+ * Sanity-check the values we've been given.
+ *
+ * XXX what do we do about systems that give us the same value for
+ * more than one of these setpoints?
+ */
+ acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
+ acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
+ acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
+ for (i = 0; i < TZ_NUMLEVELS; i++)
+ acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
+
+ /*
+ * Power off everything that we've just been given.
+ */
+ acpi_tz_all_off(sc);
+
+ return_VALUE(0);
+}
+
+static char *aclevel_string[] = {
+ "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
+ "_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
+
+static __inline const char *
+acpi_tz_aclevel_string(int active)
+{
+ if (active < -1 || active >= TZ_NUMLEVELS) {
+ return (aclevel_string[0]);
+ }
+
+ return (aclevel_string[active+1]);
+}
+
+/*
+ * Evaluate the condition of a thermal zone, take appropriate actions.
+ */
+static void
+acpi_tz_monitor(struct acpi_tz_softc *sc)
+{
+ int temp;
+ int i;
+ int newactive, newflags;
+ struct timespec curtime;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ if (sc->tz_tmp_updating) {
+ goto out;
+ }
+ sc->tz_tmp_updating = 1;
+
+ /*
+ * Get the current temperature.
+ */
+ if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp))) {
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "error fetching current temperature -- %s\n",
+ AcpiFormatException(status));
+ /* XXX disable zone? go to max cooling? */
+ goto out;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
+ sc->tz_temperature = temp;
+
+ /*
+ * Work out what we ought to be doing right now.
+ *
+ * Note that the _ACx levels sort from hot to cold.
+ */
+ newactive = TZ_ACTIVE_NONE;
+ for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
+ if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
+ newactive = i;
+ if (sc->tz_active != newactive) {
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
+ TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
+ getnanotime(&sc->tz_cooling_started);
+ }
+ }
+ }
+
+ /*
+ * We are going to get _ACx level down (colder side), but give a guaranteed
+ * minimum cooling run time if requested.
+ */
+ if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
+ (newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
+ getnanotime(&curtime);
+ timespecsub(&curtime, &sc->tz_cooling_started);
+ if (curtime.tv_sec < acpi_tz_min_runtime) {
+ newactive = sc->tz_active;
+ }
+ }
+
+ /* handle user override of active mode */
+ if (sc->tz_requested > newactive)
+ newactive = sc->tz_requested;
+
+ /* update temperature-related flags */
+ newflags = TZ_THFLAG_NONE;
+ if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
+ newflags |= TZ_THFLAG_PSV;
+ if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
+ newflags |= TZ_THFLAG_HOT;
+ if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
+ newflags |= TZ_THFLAG_CRT;
+
+ /*
+ * If the active cooling state has changed, we have to switch things.
+ */
+ if (newactive != sc->tz_active) {
+
+ /* turn off the cooling devices that are on, if any are */
+ if (sc->tz_active != TZ_ACTIVE_NONE)
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
+ acpi_tz_switch_cooler_off, sc);
+
+ /* turn on cooling devices that are required, if any are */
+ if (newactive != TZ_ACTIVE_NONE)
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
+ acpi_tz_switch_cooler_on, sc);
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "switched from %s to %s: %d.%dC\n",
+ acpi_tz_aclevel_string(sc->tz_active),
+ acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
+ sc->tz_active = newactive;
+ }
+
+ /*
+ * XXX (de)activate any passive cooling that may be required.
+ */
+
+ /*
+ * If we have just become _HOT or _CRT, warn the user.
+ *
+ * We should actually shut down at this point, but it's not clear
+ * that some systems don't actually map _CRT to the same value as _AC0.
+ */
+ if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
+ !(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
+ device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
+ TZ_KELVTOC(sc->tz_temperature));
+ /* shutdown_nice(RB_POWEROFF);*/
+ }
+ sc->tz_thflags = newflags;
+
+out:
+ sc->tz_tmp_updating = 0;
+ return_VOID;
+}
+
+/*
+ * Turn off all the cooling devices.
+ */
+static void
+acpi_tz_all_off(struct acpi_tz_softc *sc)
+{
+ int i;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ /*
+ * Scan all the _ALx objects, and turn them all off.
+ */
+ for (i = 0; i < TZ_NUMLEVELS; i++) {
+ if (sc->tz_zone.al[i].Pointer == NULL)
+ continue;
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
+ acpi_tz_switch_cooler_off, sc);
+ }
+
+ /*
+ * XXX revert any passive-cooling options.
+ */
+
+ sc->tz_active = TZ_ACTIVE_NONE;
+ sc->tz_thflags = TZ_THFLAG_NONE;
+ return_VOID;
+}
+
+/*
+ * Given an object, verify that it's a reference to a device of some sort,
+ * and try to switch it off.
+ */
+static void
+acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
+{
+ ACPI_HANDLE cooler;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ switch(obj->Type) {
+ case ACPI_TYPE_ANY:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", acpi_name(obj->Reference.Handle)));
+
+ acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D3);
+ break;
+
+ case ACPI_TYPE_STRING:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer));
+
+ /*
+ * Find the handle for the device and turn it off.
+ * The String object here seems to contain a fully-qualified path, so we
+ * don't have to search for it in our parents.
+ *
+ * XXX This may not always be the case.
+ */
+ if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler)))
+ acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
+ break;
+
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
+ obj->Type));
+ break;
+ }
+ return_VOID;
+}
+
+/*
+ * Given an object, verify that it's a reference to a device of some sort,
+ * and try to switch it on.
+ *
+ * XXX replication of off/on function code is bad, mmmkay?
+ */
+static void
+acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
+{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
+ ACPI_HANDLE cooler;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ switch(obj->Type) {
+ case ACPI_TYPE_ANY:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", acpi_name(obj->Reference.Handle)));
+
+ if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D0))) {
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "failed to activate %s - %s\n", acpi_name(obj->Reference.Handle),
+ AcpiFormatException(status));
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer));
+
+ /*
+ * Find the handle for the device and turn it off.
+ * The String object here seems to contain a fully-qualified path, so we
+ * don't have to search for it in our parents.
+ *
+ * XXX This may not always be the case.
+ */
+ if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler))) {
+ if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "failed to activate %s - %s\n",
+ obj->String.Pointer, AcpiFormatException(status));
+ }
+ } else {
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "couldn't find %s\n", obj->String.Pointer);
+ }
+ break;
+
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
+ obj->Type));
+ break;
+ }
+ return_VOID;
+}
+
+/*
+ * Read/debug-print a parameter, default it to -1.
+ */
+static void
+acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
+{
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ if (ACPI_FAILURE(acpi_EvaluateInteger(sc->tz_handle, node, data))) {
+ *data = -1;
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle),
+ node, *data));
+ }
+ return_VOID;
+}
+
+/*
+ * Sanity-check a temperature value. Assume that setpoints
+ * should be between 0C and 150C.
+ */
+static void
+acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
+{
+ if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
+ device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
+ what, TZ_KELVTOC(*val));
+ *val = -1;
+ }
+}
+
+/*
+ * Respond to a sysctl on the active state node.
+ */
+static int
+acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_tz_softc *sc;
+ int active;
+ int error;
+ ACPI_LOCK_DECL;
+
+ ACPI_LOCK;
+
+ sc = (struct acpi_tz_softc *)oidp->oid_arg1;
+ active = sc->tz_active;
+ error = sysctl_handle_int(oidp, &active, 0, req);
+
+ /* error or no new value */
+ if ((error != 0) || (req->newptr == NULL))
+ goto out;
+
+ /* range check */
+ if ((active < -1) || (active >= TZ_NUMLEVELS)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* set new preferred level and re-switch */
+ sc->tz_requested = active;
+ acpi_tz_monitor(sc);
+
+ out:
+ ACPI_UNLOCK;
+ return(error);
+}
+
+/*
+ * Respond to a Notify event sent to the zone.
+ */
+static void
+acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_ASSERTLOCK;
+
+ switch(notify) {
+ case TZ_NOTIFY_TEMPERATURE:
+ /* temperature change occurred */
+ AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
+ break;
+ case TZ_NOTIFY_DEVICES:
+ case TZ_NOTIFY_LEVELS:
+ /* zone devices/setpoints changed */
+ AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
+ break;
+ default:
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "unknown Notify event 0x%x\n", notify);
+ break;
+ }
+ return_VOID;
+}
+
+/*
+ * Poll the thermal zone.
+ */
+static void
+acpi_tz_timeout(struct acpi_tz_softc *sc)
+{
+
+ /* do we need to get the power profile settings? */
+ if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
+ acpi_tz_power_profile((void *)sc);
+ sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
+ }
+
+ ACPI_ASSERTLOCK;
+
+ /* check the current temperature and take action based on it */
+ acpi_tz_monitor(sc);
+
+ /* XXX passive cooling actions? */
+}
+
+/*
+ * System power profile may have changed; fetch and notify the
+ * thermal zone accordingly.
+ *
+ * Since this can be called from an arbitrary eventhandler, it needs
+ * to get the ACPI lock itself.
+ */
+static void
+acpi_tz_power_profile(void *arg)
+{
+ ACPI_OBJECT_LIST args;
+ ACPI_OBJECT obj;
+ ACPI_STATUS status;
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
+ int state;
+ ACPI_LOCK_DECL;
+
+ state = power_profile_get_state();
+ if (state != POWER_PROFILE_PERFORMANCE &&
+ state != POWER_PROFILE_ECONOMY) {
+ return;
+ }
+
+ ACPI_LOCK;
+
+ /* check that we haven't decided there's no _SCP method */
+ if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
+
+ /* call _SCP to set the new profile */
+ obj.Type = ACPI_TYPE_INTEGER;
+ obj.Integer.Value = (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1;
+ args.Count = 1;
+ args.Pointer = &obj;
+ if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
+ if (status != AE_NOT_FOUND)
+ ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
+ "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
+ AcpiFormatException(status));
+ sc->tz_flags |= TZ_FLAG_NO_SCP;
+ } else {
+ /* we have to re-evaluate the entire zone now */
+ AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
+ }
+ }
+ ACPI_UNLOCK;
+}
+
+/*
+ * Thermal zone monitor thread.
+ */
+static void
+acpi_tz_thread(void *arg)
+{
+ device_t *devs;
+ int devcount, i;
+ ACPI_LOCK_DECL;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+
+ devs = NULL;
+ devcount = 0;
+
+ for (;;) {
+ tsleep(&acpi_tz_proc, PZERO, "nothing", hz * acpi_tz_polling_rate);
+
+#if __FreeBSD_version >= 500000
+ mtx_lock(&Giant);
+#endif
+
+ if (devcount == 0)
+ devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
+
+ ACPI_LOCK;
+ for (i = 0; i < devcount; i++)
+ acpi_tz_timeout(device_get_softc(devs[i]));
+ ACPI_UNLOCK;
+
+#if __FreeBSD_version >= 500000
+ mtx_unlock(&Giant);
+#endif
+ }
+}
diff --git a/sys/dev/acpica/acpi_timer.c b/sys/dev/acpica/acpi_timer.c
new file mode 100644
index 0000000..67e308a
--- /dev/null
+++ b/sys/dev/acpica/acpi_timer.c
@@ -0,0 +1,389 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#if __FreeBSD_version >= 500000
+#include <sys/timetc.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+#include <pci/pcivar.h>
+
+/*
+ * A timecounter based on the free-running ACPI timer.
+ *
+ * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
+ */
+
+/*
+ * Hooks for the ACPI CA debugging infrastructure
+ */
+#define _COMPONENT ACPI_SYSTEM
+ACPI_MODULE_NAME("TIMER")
+
+static device_t acpi_timer_dev;
+struct resource *acpi_timer_reg;
+
+static u_int acpi_timer_frequency = 14318182/4;
+
+static void acpi_timer_identify(driver_t *driver, device_t parent);
+static int acpi_timer_probe(device_t dev);
+static int acpi_timer_attach(device_t dev);
+static unsigned acpi_timer_get_timecount(struct timecounter *tc);
+static unsigned acpi_timer_get_timecount_safe(struct timecounter *tc);
+static int acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
+static void acpi_timer_test(void);
+
+static u_int32_t read_counter(void);
+static int test_counter(void);
+
+/*
+ * Driver hung off ACPI.
+ */
+static device_method_t acpi_timer_methods[] = {
+ DEVMETHOD(device_identify, acpi_timer_identify),
+ DEVMETHOD(device_probe, acpi_timer_probe),
+ DEVMETHOD(device_attach, acpi_timer_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_timer_driver = {
+ "acpi_timer",
+ acpi_timer_methods,
+ 0,
+};
+
+static devclass_t acpi_timer_devclass;
+DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
+
+/*
+ * Timecounter.
+ */
+static struct timecounter acpi_timer_timecounter = {
+ acpi_timer_get_timecount_safe,
+ 0,
+ 0xffffff,
+ 0,
+ "ACPI"
+};
+
+
+static u_int32_t
+read_counter()
+{
+ bus_space_handle_t bsh;
+ bus_space_tag_t bst;
+ u_int32_t tv;
+
+ bsh = rman_get_bushandle(acpi_timer_reg);
+ bst = rman_get_bustag(acpi_timer_reg);
+ tv = bus_space_read_4(bst, bsh, 0);
+ bus_space_barrier(bst, bsh, 0, 4, BUS_SPACE_BARRIER_READ);
+ return (tv);
+}
+
+#define N 2000
+static int
+test_counter()
+{
+ int min, max, n, delta;
+ unsigned last, this;
+
+ min = 10000000;
+ max = 0;
+ last = read_counter();
+ for (n = 0; n < N; n++) {
+ this = read_counter();
+ delta = (this - last) & 0xffffff;
+ if (delta > max)
+ max = delta;
+ else if (delta < min)
+ min = delta;
+ last = this;
+ }
+ if (max - min > 2)
+ n = 0;
+ else if (min < 0 || max == 0)
+ n = 0;
+ else
+ n = 1;
+ if (bootverbose)
+ printf("ACPI timer looks %s min = %d, max = %d, width = %d\n",
+ n ? "GOOD" : "BAD ",
+ min, max, max - min);
+ return (n);
+}
+
+/*
+ * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
+ * we will be using.
+ */
+static void
+acpi_timer_identify(driver_t *driver, device_t parent)
+{
+ device_t dev;
+ char desc[40];
+ u_long rlen, rstart;
+ int i, j, rid, rtype;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (acpi_disabled("timer"))
+ return_VOID;
+
+ if (AcpiGbl_FADT == NULL)
+ return_VOID;
+
+ if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
+ device_printf(parent, "could not add acpi_timer0\n");
+ return_VOID;
+ }
+ acpi_timer_dev = dev;
+
+ rid = 0;
+ rlen = AcpiGbl_FADT->PmTmLen;
+ rtype = (AcpiGbl_FADT->XPmTmrBlk.AddressSpaceId)
+ ? SYS_RES_IOPORT : SYS_RES_MEMORY;
+ rstart = AcpiGbl_FADT->XPmTmrBlk.Address;
+ bus_set_resource(dev, rtype, rid, rstart, rlen);
+ acpi_timer_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE);
+ if (acpi_timer_reg == NULL) {
+ device_printf(dev, "couldn't allocate I/O resource (%s 0x%lx)\n",
+ (rtype == SYS_RES_IOPORT) ? "port" : "mem", rstart);
+ return_VOID;
+ }
+ if (testenv("debug.acpi.timer_test"))
+ acpi_timer_test();
+
+ acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
+ j = 0;
+ for(i = 0; i < 10; i++)
+ j += test_counter();
+ if (j == 10) {
+ acpi_timer_timecounter.tc_name = "ACPI-fast";
+ acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
+ } else {
+ acpi_timer_timecounter.tc_name = "ACPI-safe";
+ acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount_safe;
+ }
+ tc_init(&acpi_timer_timecounter);
+
+ sprintf(desc, "%d-bit timer at 3.579545MHz", (AcpiGbl_FADT->TmrValExt)
+ ? 32 : 24);
+ device_set_desc_copy(dev, desc);
+
+ return_VOID;
+}
+
+static int
+acpi_timer_probe(device_t dev)
+{
+ if (dev == acpi_timer_dev)
+ return(0);
+ return(ENXIO);
+}
+
+static int
+acpi_timer_attach(device_t dev)
+{
+ return(0);
+}
+
+/*
+ * Fetch current time value from reliable hardware.
+ */
+static unsigned
+acpi_timer_get_timecount(struct timecounter *tc)
+{
+ return (read_counter());
+}
+
+/*
+ * Fetch current time value from hardware that may not correctly
+ * latch the counter.
+ */
+static unsigned
+acpi_timer_get_timecount_safe(struct timecounter *tc)
+{
+ unsigned u1, u2, u3;
+
+ u2 = read_counter();
+ u3 = read_counter();
+ do {
+ u1 = u2;
+ u2 = u3;
+ u3 = read_counter();
+ } while (u1 > u2 || u2 > u3 || (u3 - u1) > 15);
+ return (u2);
+}
+
+/*
+ * Timecounter freqency adjustment interface.
+ */
+static int
+acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ u_int freq;
+
+ if (acpi_timer_timecounter.tc_frequency == 0)
+ return (EOPNOTSUPP);
+ freq = acpi_timer_frequency;
+ error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
+ if (error == 0 && req->newptr != NULL) {
+ acpi_timer_frequency = freq;
+ acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
+
+/*
+ * Test harness for verifying ACPI timer behaviour.
+ * Boot with debug.acpi.timer_test set to invoke this.
+ */
+static void
+acpi_timer_test(void)
+{
+ u_int32_t u1, u2, u3;
+
+ u1 = read_counter();
+ u2 = read_counter();
+ u3 = read_counter();
+
+ device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
+ for (;;) {
+ /*
+ * The failure case is where u3 > u1, but u2 does not fall between the two,
+ * ie. it contains garbage.
+ */
+ if (u3 > u1) {
+ if ((u2 < u1) || (u2 > u3))
+ device_printf(acpi_timer_dev, "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
+ u1, u2, u3);
+ }
+ u1 = u2;
+ u2 = u3;
+ u3 = read_counter();
+ }
+}
+
+/*
+ * Chipset workaround driver hung off PCI.
+ *
+ * Some ACPI timers are known or believed to suffer from implementation
+ * problems which can lead to erroneous values being read from the timer.
+ *
+ * Since we can't trust unknown chipsets, we default to a timer-read
+ * routine which compensates for the most common problem (as detailed
+ * in the excerpt from the Intel PIIX4 datasheet below).
+ *
+ * When we detect a known-functional chipset, we disable the workaround
+ * to improve speed.
+ *
+ * ] 20. ACPI Timer Errata
+ * ]
+ * ] Problem: The power management timer may return improper result when
+ * ] read. Although the timer value settles properly after incrementing,
+ * ] while incrementing there is a 3nS window every 69.8nS where the
+ * ] timer value is indeterminate (a 4.2% chance that the data will be
+ * ] incorrect when read). As a result, the ACPI free running count up
+ * ] timer specification is violated due to erroneous reads. Implication:
+ * ] System hangs due to the "inaccuracy" of the timer when used by
+ * ] software for time critical events and delays.
+ * ]
+ * ] Workaround: Read the register twice and compare.
+ * ] Status: This will not be fixed in the PIIX4 or PIIX4E, it is fixed
+ * ] in the PIIX4M.
+ *
+ * The counter is in other words not latched to the PCI bus clock when
+ * read. Notice the workaround isn't: We need to read until we have
+ * three monotonic samples and then use the middle one, otherwise we are
+ * not protected against the fact that the bits can be wrong in two
+ * directions. If we only cared about monosity two reads would be enough.
+ */
+
+#if 0
+static int acpi_timer_pci_probe(device_t dev);
+
+static device_method_t acpi_timer_pci_methods[] = {
+ DEVMETHOD(device_probe, acpi_timer_pci_probe),
+ {0, 0}
+};
+
+static driver_t acpi_timer_pci_driver = {
+ "acpi_timer_pci",
+ acpi_timer_pci_methods,
+ 0,
+};
+
+devclass_t acpi_timer_pci_devclass;
+DRIVER_MODULE(acpi_timer_pci, pci, acpi_timer_pci_driver, acpi_timer_pci_devclass, 0, 0);
+
+/*
+ * Look at PCI devices going past; if we detect one we know contains
+ * a functional ACPI timer device, enable the faster timecounter read
+ * routine.
+ */
+static int
+acpi_timer_pci_probe(device_t dev)
+{
+ int vendor, device, revid;
+
+ vendor = pci_get_vendor(dev);
+ device = pci_get_device(dev);
+ revid = pci_get_revid(dev);
+
+ if (((vendor == 0x8086) && (device == 0x7113) && (revid >= 0x03)) || /* PIIX4M */
+ ((vendor == 0x8086) && (device == 0x719b)) || /* i440MX */
+ 0) {
+
+ acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
+ acpi_timer_timecounter.tc_name = "ACPI-fast";
+ if (bootverbose)
+ device_printf(acpi_timer_dev, "functional ACPI timer detected, enabling fast timecount interface\n");
+ }
+
+ return(ENXIO); /* we never match anything */
+}
+#endif
diff --git a/sys/dev/acpica/acpica_support.c b/sys/dev/acpica/acpica_support.c
new file mode 100644
index 0000000..7dfde3b
--- /dev/null
+++ b/sys/dev/acpica/acpica_support.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2001 Mitsuru IWASAKI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "acpi.h"
+#include <machine/cpufunc.h>
+#include <dev/acpica/acpica_support.h>
+
+ACPI_MODULE_NAME("SUPPORT")
+
+/*
+ * Implement support code temporary here until officially merged into
+ * Intel ACPI CA release.
+ */
+
+#undef _COMPONENT
+#define _COMPONENT ACPI_HARDWARE
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiEnterSleepStateS4Bios
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state S4BIOS
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnterSleepStateS4Bios (
+ void)
+{
+ ACPI_OBJECT_LIST ArgList;
+ ACPI_OBJECT Arg;
+ UINT32 Value;
+
+
+ ACPI_FUNCTION_TRACE ("AcpiEnterSleepStateS4Bios");
+
+ /* run the _PTS and _GTS methods */
+
+ ACPI_MEMSET(&ArgList, 0, sizeof(ArgList));
+ ArgList.Count = 1;
+ ArgList.Pointer = &Arg;
+
+ ACPI_MEMSET(&Arg, 0, sizeof(Arg));
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = ACPI_STATE_S4;
+
+ AcpiEvaluateObject (NULL, "\\_PTS", &ArgList, NULL);
+ AcpiEvaluateObject (NULL, "\\_GTS", &ArgList, NULL);
+
+ /* clear wake status */
+
+ AcpiSetRegister (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_LOCK);
+
+ ACPI_DISABLE_IRQS ();
+
+ AcpiHwDisableNonWakeupGpes();
+
+ /* flush caches */
+
+ ACPI_FLUSH_CPU_CACHE ();
+
+ /* write the value to command port and wait until we enter sleep state */
+ do
+ {
+ AcpiOsStall(1000000);
+ AcpiOsWritePort (AcpiGbl_FADT->SmiCmd, AcpiGbl_FADT->S4BiosReq, 8);
+ AcpiGetRegister (ACPI_BITREG_WAKE_STATUS, &Value, ACPI_MTX_LOCK);
+ }
+ while (!Value);
+
+ AcpiHwEnableNonWakeupGpes();
+
+ ACPI_ENABLE_IRQS ();
+
+ return_ACPI_STATUS (AE_OK);
+}
+
diff --git a/sys/dev/acpica/acpica_support.h b/sys/dev/acpica/acpica_support.h
new file mode 100644
index 0000000..359d0d0
--- /dev/null
+++ b/sys/dev/acpica/acpica_support.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2001 Mitsuru IWASAKI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "acpi.h"
+
+ACPI_STATUS
+AcpiEnterSleepStateS4Bios (
+ void);
+
+ACPI_STATUS
+AcpiSetDsdtTablePtr(
+ ACPI_TABLE_HEADER *Ptr);
+
diff --git a/sys/dev/acpica/acpiio.h b/sys/dev/acpica/acpiio.h
new file mode 100644
index 0000000..002233b
--- /dev/null
+++ b/sys/dev/acpica/acpiio.h
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe <takawata@shidahara1.planet.sci.kobe-u.ac.jp>
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Core ACPI subsystem ioctls
+ */
+#define ACPIIO_ENABLE _IO('P', 1)
+#define ACPIIO_DISABLE _IO('P', 2)
+#define ACPIIO_SETSLPSTATE _IOW('P', 3, int)
+
+struct acpi_battdesc {
+ int type; /* battery type: e.g. CMBAT */
+ int phys_unit; /* physical unit of devclass */
+};
+
+#define ACPI_BATT_TYPE_CMBAT 0x0000
+#define ACPI_BATT_TYPE_SMBAT 0x0001
+
+struct acpi_battinfo {
+ int cap; /* percent */
+ int min; /* remianing time */
+ int state; /* battery state */
+};
+
+#define ACPI_CMBAT_MAXSTRLEN 32
+struct acpi_bif {
+ u_int32_t unit; /* 0 for mWh, 1 for mAh */
+ u_int32_t dcap; /* Design Capacity */
+ u_int32_t lfcap; /* Last Full capacity */
+ u_int32_t btech; /* Battery Technorogy */
+ u_int32_t dvol; /* Design voltage (mV) */
+ u_int32_t wcap; /* WARN capacity */
+ u_int32_t lcap; /* Low capacity */
+ u_int32_t gra1; /* Granulity 1(Warn to Low) */
+ u_int32_t gra2; /* Granulity 2(Full to Warn) */
+ char model[ACPI_CMBAT_MAXSTRLEN]; /* model identifier */
+ char serial[ACPI_CMBAT_MAXSTRLEN]; /* Serial number */
+ char type[ACPI_CMBAT_MAXSTRLEN]; /* Type */
+ char oeminfo[ACPI_CMBAT_MAXSTRLEN]; /* OEM infomation */
+};
+
+struct acpi_bst {
+ u_int32_t state; /* Battery State */
+ u_int32_t rate; /* Present Rate */
+ u_int32_t cap; /* Remaining Capacity */
+ u_int32_t volt; /* Present Voltage */
+};
+
+#define ACPI_BATT_STAT_DISCHARG 0x0001
+#define ACPI_BATT_STAT_CHARGING 0x0002
+#define ACPI_BATT_STAT_CRITICAL 0x0004
+#define ACPI_BATT_STAT_NOT_PRESENT 0x0007
+#define ACPI_BATT_STAT_MAX 0x0007
+
+union acpi_battery_ioctl_arg {
+ int unit; /* argument: logical unit (-1 = overall) */
+
+ struct acpi_battdesc battdesc;
+ struct acpi_battinfo battinfo;
+
+ struct acpi_bif bif; /* for acpi_cmbat */
+ struct acpi_bst bst; /* for acpi_cmbat */
+};
+
+/* Common battery ioctl */
+#define ACPIIO_BATT_GET_UNITS _IOR('B', 0x01, int)
+#define ACPIIO_BATT_GET_TYPE _IOR('B', 0x02, union acpi_battery_ioctl_arg)
+#define ACPIIO_BATT_GET_BATTINFO _IOWR('B', 0x03, union acpi_battery_ioctl_arg)
+#define ACPIIO_BATT_GET_BATTDESC _IOWR('B', 0x04, union acpi_battery_ioctl_arg)
+
+/* Cotrol Method battery ioctl */
+#define ACPIIO_CMBAT_GET_BIF _IOWR('B', 0x10, union acpi_battery_ioctl_arg)
+#define ACPIIO_CMBAT_GET_BST _IOWR('B', 0x11, union acpi_battery_ioctl_arg)
+
+#define ACPIIO_ACAD_GET_STATUS _IOR('A', 1, int)
+
+#ifdef _KERNEL
+extern int acpi_register_ioctl(u_long cmd, int (* fn)(u_long cmd, caddr_t addr, void *arg), void *arg);
+extern void acpi_deregister_ioctl(u_long cmd, int (* fn)(u_long cmd, caddr_t addr, void *arg));
+#endif
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
new file mode 100644
index 0000000..cd254ac
--- /dev/null
+++ b/sys/dev/acpica/acpivar.h
@@ -0,0 +1,411 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "bus_if.h"
+#include <sys/eventhandler.h>
+#include <sys/sysctl.h>
+#if __FreeBSD_version >= 500000
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#if __FreeBSD_version < 500000
+typedef vm_offset_t vm_paddr_t;
+#endif
+
+struct acpi_softc {
+ device_t acpi_dev;
+ dev_t acpi_dev_t;
+
+ struct resource *acpi_irq;
+ int acpi_irq_rid;
+ void *acpi_irq_handle;
+
+ int acpi_enabled;
+ int acpi_sstate;
+ int acpi_sleep_disabled;
+
+ struct sysctl_ctx_list acpi_sysctl_ctx;
+ struct sysctl_oid *acpi_sysctl_tree;
+#define ACPI_POWER_BUTTON_DEFAULT_SX ACPI_STATE_S5;
+#define ACPI_SLEEP_BUTTON_DEFAULT_SX ACPI_STATE_S1;
+#define ACPI_LID_SWITCH_DEFAULT_SX ACPI_STATE_S1;
+ int acpi_power_button_sx;
+ int acpi_sleep_button_sx;
+ int acpi_lid_switch_sx;
+
+ int acpi_standby_sx;
+ int acpi_suspend_sx;
+
+ int acpi_sleep_delay;
+ int acpi_s4bios;
+ int acpi_disable_on_poweroff;
+
+ int acpi_verbose;
+
+ bus_dma_tag_t acpi_waketag;
+ bus_dmamap_t acpi_wakemap;
+ vm_offset_t acpi_wakeaddr;
+ vm_paddr_t acpi_wakephys;
+
+ struct sysctl_ctx_list acpi_battery_sysctl_ctx;
+ struct sysctl_oid *acpi_battery_sysctl_tree;
+};
+
+struct acpi_device {
+ /* ACPI ivars */
+ ACPI_HANDLE ad_handle;
+ int ad_magic;
+ void *ad_private;
+
+ /* resources */
+ struct resource_list ad_rl;
+
+};
+
+#if __FreeBSD_version < 500000
+/*
+ * In 4.x, ACPI is protected by splhigh().
+ */
+# define ACPI_LOCK s = splhigh()
+# define ACPI_UNLOCK splx(s)
+# define ACPI_ASSERTLOCK
+# define ACPI_MSLEEP(a, b, c, d, e) tsleep(a, c, d, e)
+# define ACPI_LOCK_DECL int s
+# define kthread_create(a, b, c, d, e, f) kthread_create(a, b, c, f)
+# define tc_init(a) init_timecounter(a)
+#elif 0
+/*
+ * The ACPI subsystem lives under a single mutex. You *must*
+ * acquire this mutex before calling any of the acpi_ or Acpi* functions.
+ */
+extern struct mtx acpi_mutex;
+# define ACPI_LOCK mtx_lock(&acpi_mutex)
+# define ACPI_UNLOCK mtx_unlock(&acpi_mutex)
+# define ACPI_ASSERTLOCK mtx_assert(&acpi_mutex, MA_OWNED)
+# define ACPI_MSLEEP(a, b, c, d, e) msleep(a, b, c, d, e)
+# define ACPI_LOCK_DECL
+#else
+# define ACPI_LOCK
+# define ACPI_UNLOCK
+# define ACPI_ASSERTLOCK
+# define ACPI_MSLEEP(a, b, c, d, e) tsleep(a, c, d, e)
+# define ACPI_LOCK_DECL
+#endif
+
+/*
+ * ACPI CA does not define layers for non-ACPI CA drivers.
+ * We define some here within the range provided.
+ */
+#define ACPI_BUS 0x00010000
+#define ACPI_SYSTEM 0x00020000
+#define ACPI_POWER 0x00040000
+#define ACPI_EC 0x00080000
+#define ACPI_AC_ADAPTER 0x00100000
+#define ACPI_BATTERY 0x00110000
+#define ACPI_BUTTON 0x00120000
+#define ACPI_PROCESSOR 0x00140000
+#define ACPI_THERMAL 0x00180000
+#define ACPI_FAN 0x00200000
+
+/*
+ * Constants for different interrupt models used with acpi_SetIntrModel().
+ */
+#define ACPI_INTR_PIC 0
+#define ACPI_INTR_APIC 1
+#define ACPI_INTR_SAPIC 2
+
+/* XXX this is no longer referenced anywhere, remove? */
+#if 0
+/*
+ * This is a cheap and nasty way to get around the horrid counted list
+ * argument format that AcpiEvalateObject uses.
+ */
+#define ACPI_OBJECTLIST_MAX 16
+struct acpi_object_list {
+ UINT32 count;
+ ACPI_OBJECT *pointer[ACPI_OBJECTLIST_MAX];
+ ACPI_OBJECT object[ACPI_OBJECTLIST_MAX];
+};
+
+static __inline struct acpi_object_list *
+acpi_AllocObjectList(int nobj)
+{
+ struct acpi_object_list *l;
+ int i;
+
+ if (nobj > ACPI_OBJECTLIST_MAX)
+ return(NULL);
+ if ((l = AcpiOsAllocate(sizeof(*l))) == NULL)
+ return(NULL);
+ bzero(l, sizeof(*l));
+ for (i = 0; i < ACPI_OBJECTLIST_MAX; i++)
+ l->pointer[i] = &l->object[i];
+ l->count = nobj;
+ return(l);
+}
+#endif /* unused */
+
+/*
+ * Note that the low ivar values are reserved to provide
+ * interface compatibility with ISA drivers which can also
+ * attach to ACPI.
+ */
+#define ACPI_IVAR_HANDLE 0x100
+#define ACPI_IVAR_MAGIC 0x101
+#define ACPI_IVAR_PRIVATE 0x102
+
+static __inline ACPI_HANDLE
+acpi_get_handle(device_t dev)
+{
+ uintptr_t up;
+ ACPI_HANDLE h;
+
+ if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_HANDLE, &up))
+ return(NULL);
+ h = (ACPI_HANDLE)up;
+ return(h);
+}
+
+static __inline int
+acpi_set_handle(device_t dev, ACPI_HANDLE h)
+{
+ uintptr_t up;
+
+ up = (uintptr_t)h;
+ return(BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_HANDLE, up));
+}
+
+static __inline int
+acpi_get_magic(device_t dev)
+{
+ uintptr_t up;
+ int m;
+
+ if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_MAGIC, &up))
+ return(0);
+ m = (int)up;
+ return(m);
+}
+
+static __inline int
+acpi_set_magic(device_t dev, int m)
+{
+ uintptr_t up;
+
+ up = (uintptr_t)m;
+ return(BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_MAGIC, up));
+}
+
+static __inline void *
+acpi_get_private(device_t dev)
+{
+ uintptr_t up;
+ void *p;
+
+ if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_PRIVATE, &up))
+ return(NULL);
+ p = (void *)up;
+ return(p);
+}
+
+static __inline int
+acpi_set_private(device_t dev, void *p)
+{
+ uintptr_t up;
+
+ up = (uintptr_t)p;
+ return(BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_PRIVATE, up));
+}
+
+static __inline ACPI_OBJECT_TYPE
+acpi_get_type(device_t dev)
+{
+ ACPI_HANDLE h;
+ ACPI_OBJECT_TYPE t;
+
+ if ((h = acpi_get_handle(dev)) == NULL)
+ return(ACPI_TYPE_NOT_FOUND);
+ if (AcpiGetType(h, &t) != AE_OK)
+ return(ACPI_TYPE_NOT_FOUND);
+ return(t);
+}
+
+#ifdef ACPI_DEBUGGER
+extern void acpi_EnterDebugger(void);
+#endif
+
+#ifdef ACPI_DEBUG
+#include <sys/cons.h>
+#define STEP(x) do {printf x, printf("\n"); cngetc();} while (0)
+#else
+#define STEP(x)
+#endif
+
+#define ACPI_VPRINT(dev, acpi_sc, x...) do { \
+ if (acpi_get_verbose(acpi_sc)) \
+ device_printf(dev, x); \
+} while (0)
+
+#define ACPI_DEVINFO_PRESENT(x) (((x) & 0x9) == 9)
+extern BOOLEAN acpi_DeviceIsPresent(device_t dev);
+extern BOOLEAN acpi_BatteryIsPresent(device_t dev);
+extern BOOLEAN acpi_MatchHid(device_t dev, char *hid);
+extern ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result);
+extern ACPI_BUFFER *acpi_AllocBuffer(int size);
+extern ACPI_STATUS acpi_EvaluateInteger(ACPI_HANDLE handle, char *path, int *number);
+extern ACPI_STATUS acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp, int *number);
+extern ACPI_STATUS acpi_ForeachPackageObject(ACPI_OBJECT *obj,
+ void (* func)(ACPI_OBJECT *comp, void *arg),
+ void *arg);
+extern ACPI_STATUS acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp);
+extern ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res);
+extern ACPI_STATUS acpi_SetIntrModel(int model);
+
+extern ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state);
+extern ACPI_STATUS acpi_Enable(struct acpi_softc *sc);
+extern ACPI_STATUS acpi_Disable(struct acpi_softc *sc);
+
+struct acpi_parse_resource_set {
+ void (* set_init)(device_t dev, void **context);
+ void (* set_done)(device_t dev, void *context);
+ void (* set_ioport)(device_t dev, void *context, u_int32_t base, u_int32_t length);
+ void (* set_iorange)(device_t dev, void *context, u_int32_t low, u_int32_t high,
+ u_int32_t length, u_int32_t align);
+ void (* set_memory)(device_t dev, void *context, u_int32_t base, u_int32_t length);
+ void (* set_memoryrange)(device_t dev, void *context, u_int32_t low, u_int32_t high,
+ u_int32_t length, u_int32_t align);
+ void (* set_irq)(device_t dev, void *context, u_int32_t *irq, int cout);
+ void (* set_drq)(device_t dev, void *context, u_int32_t *drq, int count);
+ void (* set_start_dependant)(device_t dev, void *context, int preference);
+ void (* set_end_dependant)(device_t dev, void *context);
+};
+
+extern struct acpi_parse_resource_set acpi_res_parse_set;
+extern ACPI_STATUS acpi_parse_resources(device_t dev, ACPI_HANDLE handle,
+ struct acpi_parse_resource_set *set);
+/* XXX until Intel fix this in their headers, based on NEXT_RESOURCE */
+#define ACPI_RESOURCE_NEXT(Res) (ACPI_RESOURCE *)((UINT8 *) Res + Res->Length)
+
+/*
+ * ACPI event handling
+ */
+extern UINT32 acpi_eventhandler_power_button_for_sleep(void *context);
+extern UINT32 acpi_eventhandler_power_button_for_wakeup(void *context);
+extern UINT32 acpi_eventhandler_sleep_button_for_sleep(void *context);
+extern UINT32 acpi_eventhandler_sleep_button_for_wakeup(void *context);
+
+#define ACPI_EVENT_PRI_FIRST 0
+#define ACPI_EVENT_PRI_DEFAULT 10000
+#define ACPI_EVENT_PRI_LAST 20000
+
+typedef void (*acpi_event_handler_t)(void *, int);
+
+EVENTHANDLER_DECLARE(acpi_sleep_event, acpi_event_handler_t);
+EVENTHANDLER_DECLARE(acpi_wakeup_event, acpi_event_handler_t);
+
+/*
+ * Device power control.
+ */
+extern ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state);
+
+/*
+ * Misc.
+ */
+static __inline struct acpi_softc *
+acpi_device_get_parent_softc(device_t child)
+{
+ device_t parent;
+
+ parent = device_get_parent(child);
+ if (parent == NULL) {
+ return(NULL);
+ }
+ return(device_get_softc(parent));
+}
+
+static __inline int
+acpi_get_verbose(struct acpi_softc *sc)
+{
+ if (sc)
+ return(sc->acpi_verbose);
+ return(0);
+}
+
+extern char *acpi_name(ACPI_HANDLE handle);
+extern int acpi_avoid(ACPI_HANDLE handle);
+extern int acpi_disabled(char *subsys);
+
+extern void acpi_device_enable_wake_capability(ACPI_HANDLE h, int enable);
+extern void acpi_device_enable_wake_event(ACPI_HANDLE h);
+
+extern int acpi_machdep_init(device_t dev);
+extern void acpi_install_wakeup_handler(struct acpi_softc *sc);
+extern int acpi_sleep_machdep(struct acpi_softc *sc, int state);
+
+/*
+ * Battery Abstraction.
+ */
+struct acpi_battinfo;
+struct acpi_battdesc;
+
+extern int acpi_battery_register(int, int);
+extern int acpi_battery_get_battinfo(int, struct acpi_battinfo *);
+extern int acpi_battery_get_units(void);
+extern int acpi_battery_get_info_expire(void);
+extern int acpi_battery_get_battdesc(int, struct acpi_battdesc *);
+
+extern int acpi_cmbat_get_battinfo(int, struct acpi_battinfo *);
+
+/*
+ * AC adapter interface.
+ */
+
+extern int acpi_acad_get_acline(int *);
+
+#if __FreeBSD_version >= 500000
+#ifndef ACPI_MAX_THREADS
+#define ACPI_MAX_THREADS 3
+#endif
+#if ACPI_MAX_THREADS > 0
+#define ACPI_USE_THREADS
+#endif
+#endif
+
+#ifdef ACPI_USE_THREADS
+/*
+ * ACPI task kernel thread initialization.
+ */
+extern int acpi_task_thread_init(void);
+#endif
+
OpenPOWER on IntegriCloud