static void
InterruptWrapper(void *arg)
{
- ACPI_LOCK_DECL;
-
- ACPI_LOCK;
+ crit_enter();
InterruptHandler(arg);
- ACPI_UNLOCK;
+ crit_exit();
}
/*-
- * Copyright (c) 2000 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2000 Takanori Watanabe <takawata@jp.kfreebsd.org>
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.kfreebsd.org>
* Copyright (c) 2000, 2001 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
* 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: src/sys/dev/acpica/acpi.c,v 1.160 2004/06/14 03:52:19 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.37 2008/10/03 00:26:21 hasso Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi.c,v 1.243.2.4.4.1 2009/04/15 03:14:26 kensmith Exp $");
*/
+#include <sys/cdefs.h>
+
#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/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
-#include <sys/device.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 <sys/sbuf.h>
-#include <sys/rman.h>
-
-#include <sys/thread2.h>
-#include <sys/lock.h>
+#include <sys/device.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
-#include <machine/clock.h>
-#include <machine/globaldata.h>
+#include <sys/rman.h>
#include <bus/isa/isavar.h>
+#include <bus/isa/pnpvar.h>
#include "acpi.h"
-#include "accommon.h"
#include <dev/acpica5/acpivar.h>
#include <dev/acpica5/acpiio.h>
-#include <acnamesp.h>
+#include "achware.h"
+#include "acnamesp.h"
+#include "acglobal.h"
+
+#include "pci_if.h"
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pci_private.h>
+
+#include <vm/vm_param.h>
MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices");
+#define GIANT_REQUIRED
+#define mtx_lock(a)
+#define mtx_unlock(a)
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_BUS
ACPI_MODULE_NAME("ACPI")
#define CDEV_MAJOR 152
static struct dev_ops acpi_ops = {
- { "acpi", CDEV_MAJOR, 0 },
- .d_open = acpiopen,
- .d_close = acpiclose,
- .d_ioctl = acpiioctl
-};
-
-#if __FreeBSD_version >= 500000
-struct mtx acpi_mutex;
-#endif
-
-/* Local pools for managing system resources for ACPI child devices. */
-struct rman acpi_rman_io, acpi_rman_mem;
-
-struct acpi_quirks {
- char *OemId;
- uint32_t OemRevision;
- char *value;
+ { "acpi", CDEV_MAJOR, 0 },
+ .d_open = acpiopen,
+ .d_close = acpiclose,
+ .d_ioctl = acpiioctl
};
-#define ACPI_OEM_REV_ANY 0
-
-static struct acpi_quirks acpi_quirks_table[] = {
-#ifdef notyet
- /* Bad PCI routing table. Used on some SuperMicro boards. */
- { "PTLTD ", 0x06040000, "pci_link" },
-#endif
-#ifdef ACPI_QUIRK_VMWARE
- /*
- * VMware's ACPI-fast24 timer runs roughly 65 times too fast, and not
- * predictably/monotonic either. This is observed at least under SMP
- * conditions.
- *
- * NOTE: this combination of OemId and OemRevision is NOT unique; it is
- * known or suspected that at least some SuperMicro boards (see above) and
- * the Compaq Presario 1952 use this combination. That's why this quirks
- * entry is guarded by an #ifdef, and associated config option.
- */
- { "PTLTD ", 0x06040000, "timer" },
-#endif /* ACPI_QUIRK_VMWARE */
- { NULL, 0, NULL }
-};
+/* Global mutex for locking access to the ACPI subsystem. */
+struct lock acpi_lock;
+/* Bitmap of device quirks. */
+int acpi_quirks;
static int acpi_modevent(struct module *mod, int event, void *junk);
-static int acpi_identify(driver_t *driver, device_t parent);
+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 int acpi_suspend(device_t dev);
+static int acpi_resume(device_t dev);
static int acpi_shutdown(device_t dev);
-static void acpi_quirks_set(void);
-static device_t acpi_add_child(device_t bus, device_t parent, int order,
- const char *name, int unit);
+static device_t acpi_add_child(device_t bus, device_t parent, int order, const char *name,
+ int unit);
static int acpi_print_child(device_t bus, device_t child);
+static void acpi_probe_nomatch(device_t bus, device_t child);
+static void acpi_driver_added(device_t dev, driver_t *driver);
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 struct resource_list *acpi_get_rlist(device_t dev, device_t child);
+static int acpi_sysres_alloc(device_t dev);
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 void acpi_delete_resource(device_t bus, device_t child, int type,
+ int rid);
static uint32_t acpi_isa_get_logicalid(device_t dev);
static int acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count);
static char *acpi_device_id_probe(device_t bus, device_t dev, char **ids);
+static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev,
+ ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters,
+ ACPI_BUFFER *ret);
+static int acpi_device_pwr_for_sleep(device_t bus, device_t dev,
+ int *dstate);
+static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level,
+ void *context, void **retval);
+static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev,
+ int max_depth, acpi_scan_cb_t user_fn, void *arg);
+static int acpi_set_powerstate_method(device_t bus, device_t child,
+ int state);
static int acpi_isa_pnp_probe(device_t bus, device_t child,
- struct isa_pnp_id *ids);
+ struct isa_pnp_id *ids);
static void acpi_probe_children(device_t bus);
+static void acpi_probe_order(ACPI_HANDLE handle, int *order);
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);
+ void *context, void **status);
+BOOLEAN acpi_MatchHid(ACPI_HANDLE h, const char *hid);
+static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state);
static void acpi_shutdown_final(void *arg, int howto);
-static void acpi_shutdown_poweroff(void *arg);
static void acpi_enable_fixed_events(struct acpi_softc *sc);
-static int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw);
-static ACPI_STATUS acpi_wake_limit(ACPI_HANDLE h, UINT32 level, void *context,
- void **status);
-static int acpi_wake_limit_walk(int sstate);
+static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate);
+static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate);
+static int acpi_wake_prep_walk(int sstate);
static int acpi_wake_sysctl_walk(device_t dev);
-#ifdef dfly_notyet
static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS);
-#endif
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);
DEVMETHOD(device_attach, acpi_attach),
DEVMETHOD(device_shutdown, acpi_shutdown),
DEVMETHOD(device_detach, bus_generic_detach),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, acpi_suspend),
+ DEVMETHOD(device_resume, acpi_resume),
/* Bus interface */
DEVMETHOD(bus_add_child, acpi_add_child),
DEVMETHOD(bus_print_child, acpi_print_child),
+ DEVMETHOD(bus_probe_nomatch, acpi_probe_nomatch),
+ DEVMETHOD(bus_driver_added, acpi_driver_added),
DEVMETHOD(bus_read_ivar, acpi_read_ivar),
DEVMETHOD(bus_write_ivar, acpi_write_ivar),
DEVMETHOD(bus_get_resource_list, acpi_get_rlist),
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
DEVMETHOD(bus_alloc_resource, acpi_alloc_resource),
DEVMETHOD(bus_release_resource, acpi_release_resource),
+ DEVMETHOD(bus_delete_resource, acpi_delete_resource),
DEVMETHOD(bus_child_pnpinfo_str, acpi_child_pnpinfo_str_method),
DEVMETHOD(bus_child_location_str, acpi_child_location_str_method),
- 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),
/* ACPI bus */
DEVMETHOD(acpi_id_probe, acpi_device_id_probe),
+ DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj),
+ DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep),
+ DEVMETHOD(acpi_scan_children, acpi_device_scan_children),
+
+ /* PCI emulation */
+ DEVMETHOD(pci_set_powerstate, acpi_set_powerstate_method),
/* ISA emulation */
DEVMETHOD(isa_pnp_probe, acpi_isa_pnp_probe),
DRIVER_MODULE(acpi, nexus, acpi_driver, acpi_devclass, acpi_modevent, 0);
MODULE_VERSION(acpi, 1);
+ACPI_SERIAL_DECL(acpi, "ACPI serializer")
+
+/* Local pools for managing system resources for ACPI child devices. */
+static struct rman acpi_rman_io, acpi_rman_mem;
+
+#define ACPI_MINIMUM_AWAKETIME 5
+
static const char* sleep_state_names[] = {
"S0", "S1", "S2", "S3", "S4", "S5", "NONE"};
-SYSCTL_NODE(_debug, OID_AUTO, acpi, CTLFLAG_RW, NULL, "ACPI debugging");
+SYSCTL_NODE(_debug, OID_AUTO, acpi, CTLFLAG_RD, NULL, "ACPI debugging");
static char acpi_ca_version[12];
SYSCTL_STRING(_debug_acpi, OID_AUTO, acpi_ca_version, CTLFLAG_RD,
acpi_ca_version, 0, "Version of Intel ACPI-CA");
static int acpi_serialize_methods;
TUNABLE_INT("hw.acpi.serialize_methods", &acpi_serialize_methods);
+/* Power devices off and on in suspend and resume. XXX Remove once tested. */
+static int acpi_do_powerstate = 1;
+TUNABLE_INT("debug.acpi.do_powerstate", &acpi_do_powerstate);
+SYSCTL_INT(_debug_acpi, OID_AUTO, do_powerstate, CTLFLAG_RW,
+ &acpi_do_powerstate, 1, "Turn off devices when suspending.");
+
+/* Allow users to override quirks. */
+TUNABLE_INT("debug.acpi.quirks", &acpi_quirks);
+
+static int acpi_susp_bounce;
+SYSCTL_INT(_debug_acpi, OID_AUTO, suspend_bounce, CTLFLAG_RW,
+ &acpi_susp_bounce, 0, "Don't actually suspend, just test devices.");
+
/*
* 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.
static int
acpi_modevent(struct module *mod, int event, void *junk)
{
- switch(event) {
+ switch (event) {
case MOD_LOAD:
if (!cold) {
kprintf("The ACPI driver cannot be loaded after boot.\n");
ACPI_STATUS
acpi_Startup(void)
{
-#ifdef ACPI_DEBUGGER
- char *debugpoint;
-#endif
- static int error, started = 0;
+ static int started = 0;
+ ACPI_STATUS status;
+ int val;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ /* Only run the startup code once. The MADT driver also calls this. */
if (started)
- return_VALUE (error);
+ return_VALUE (AE_OK);
started = 1;
- /* Start up the ACPI CA subsystem. */
-#ifdef ACPI_DEBUGGER
- debugpoint = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (!strcmp(debugpoint, "init"))
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
+ /*
+ * Pre-allocate space for RSDT/XSDT and DSDT tables and allow resizing
+ * if more tables exist.
+ */
+ if (ACPI_FAILURE(status = AcpiInitializeTables(NULL, 2, TRUE))) {
+ kprintf("ACPI: Table initialisation failed: %s\n",
+ AcpiFormatException(status));
+ return_VALUE (status);
}
+
+ /* Set up any quirks we have for this system. */
+#ifdef notyet
+ if (acpi_quirks == ACPI_Q_OK)
+ acpi_table_quirks(&acpi_quirks);
#endif
- error = AcpiInitializeTables(NULL, 16, TRUE);
- if (ACPI_FAILURE(error)) {
- kprintf("ACPI: table initialization failed:\n");
- return_VALUE (error);
- }
- /* Set up any quirks we have for this XSDT. */
- acpi_quirks_set();
- if (acpi_disabled("acpi"))
- return_VALUE (AE_ERROR);
+ /* If the user manually set the disabled hint to 0, force-enable ACPI. */
+ if (resource_int_value("acpi", 0, "disabled", &val) == 0 && val == 0)
+ acpi_quirks &= ~ACPI_Q_BROKEN;
+ if (acpi_quirks & ACPI_Q_BROKEN) {
+ kprintf("ACPI disabled by blacklist. Contact your BIOS vendor.\n");
+ status = AE_SUPPORT;
+ }
- return_VALUE (AE_OK);
+ return_VALUE (status);
}
/*
* Detect ACPI, perform early initialisation
*/
-static int
+static void
acpi_identify(driver_t *driver, device_t parent)
{
device_t child;
- /*
- * No sense rescanning an ACPI 'bus'.
- */
- if (device_get_state(parent) == DS_ATTACHED)
- return(0);
-
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
if (!cold)
- return (ENXIO);
+ return_VOID;
/* Check that we haven't been disabled with a hint. */
if (resource_disabled("acpi", 0))
- return (ENXIO);
+ return_VOID;
/* Make sure we're not being doubly invoked. */
if (device_find_child(parent, "acpi", 0) != NULL)
- return (ENXIO);
+ return_VOID;
- /* Initialize ACPI-CA. */
- if (ACPI_FAILURE(acpi_Startup()))
- return (ENXIO);
+ ksnprintf(acpi_ca_version, sizeof(acpi_ca_version), "%x", ACPI_CA_VERSION);
- ksnprintf(acpi_ca_version, sizeof(acpi_ca_version), "%#x", ACPI_CA_VERSION);
+ /* Initialize root tables. */
+ if (ACPI_FAILURE(acpi_Startup())) {
+ kprintf("ACPI: Try disabling either ACPI or apic support.\n");
+ return_VOID;
+ }
/* Attach the actual ACPI device. */
- if ((child = BUS_ADD_CHILD(parent, parent, 0, "acpi", 0)) == NULL) {
- device_printf(parent, "ACPI: could not attach\n");
- return (ENXIO);
+ if ((child = BUS_ADD_CHILD(parent, parent, 10, "acpi", 0)) == NULL) {
+ device_printf(parent, "device_identify failed\n");
+ return_VOID;
}
- return (0);
-}
-
-/*
- * Get a mapping of the root table header, as ACPICA code no longer
- * keeps local copy of RSDT/XSDT
- *
- * return value: if non-NULL, mapped physical address of root table header.
- * caller is supposed to unmap the region by AcpiOsUnmapMemory()
- */
-static ACPI_TABLE_HEADER *
-acpi_map_rsdt_header(void)
-{
- ACPI_PHYSICAL_ADDRESS rsdp_addr, addr;
- ACPI_TABLE_RSDP *rsdp;
-
- if ((rsdp_addr = AcpiOsGetRootPointer()) == 0)
- return(NULL);
- if ((rsdp = AcpiOsMapMemory(rsdp_addr, sizeof(*rsdp))) == NULL)
- return(NULL);
- if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress)
- addr = (ACPI_PHYSICAL_ADDRESS)rsdp->XsdtPhysicalAddress;
- else
- addr = (ACPI_PHYSICAL_ADDRESS)rsdp->RsdtPhysicalAddress;
- AcpiOsUnmapMemory(rsdp, sizeof(*rsdp));
-
- return AcpiOsMapMemory(addr, sizeof(ACPI_TABLE_HEADER));
}
/*
- * Fetch some descriptive data from ACPI to put in our attach message
+ * 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];
- int error;
+ ACPI_TABLE_RSDP *rsdp;
+ ACPI_TABLE_HEADER *rsdt;
+ ACPI_PHYSICAL_ADDRESS paddr;
+ char buf[ACPI_OEM_ID_SIZE + ACPI_OEM_TABLE_ID_SIZE + 2];
struct sbuf sb;
- 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);
+ device_printf(dev, "probe failed, other PM system enabled.\n");
+ return_VALUE (ENXIO);
}
- ACPI_LOCK;
-
- th = acpi_map_rsdt_header();
- if (th == NULL) {
- device_printf(dev, "couldn't get RSDT header\n");
- error = ENXIO;
- goto unlock;
- }
+ if ((paddr = AcpiOsGetRootPointer()) == 0 ||
+ (rsdp = AcpiOsMapMemory(paddr, sizeof(ACPI_TABLE_RSDP))) == NULL)
+ return_VALUE (ENXIO);
+ if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress != 0)
+ paddr = (ACPI_PHYSICAL_ADDRESS)rsdp->XsdtPhysicalAddress;
+ else
+ paddr = (ACPI_PHYSICAL_ADDRESS)rsdp->RsdtPhysicalAddress;
+ AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP));
+ if ((rsdt = AcpiOsMapMemory(paddr, sizeof(ACPI_TABLE_HEADER))) == NULL)
+ return_VALUE (ENXIO);
sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
- sbuf_bcat(&sb, th->OemId, 6);
+ sbuf_bcat(&sb, rsdt->OemId, ACPI_OEM_ID_SIZE);
sbuf_trim(&sb);
sbuf_putc(&sb, ' ');
- sbuf_bcat(&sb, th->OemTableId, 8);
+ sbuf_bcat(&sb, rsdt->OemTableId, ACPI_OEM_TABLE_ID_SIZE);
sbuf_trim(&sb);
sbuf_finish(&sb);
device_set_desc_copy(dev, sbuf_data(&sb));
sbuf_delete(&sb);
- AcpiOsUnmapMemory(th, sizeof(*th));
- error = 0;
-unlock:
- ACPI_UNLOCK;
- return_VALUE(error);
+ AcpiOsUnmapMemory(rsdt, sizeof(ACPI_TABLE_HEADER));
+
+ return_VALUE (0);
}
static int
acpi_attach(device_t dev)
{
struct acpi_softc *sc;
+ ACPI_TABLE_FACS *facs;
ACPI_STATUS status;
int error, state;
UINT32 flags;
UINT8 TypeA, TypeB;
char *env;
- ACPI_TABLE_FACS *facsp;
-#ifdef ACPI_DEBUGGER
- char *debugpoint;
-#endif
- ACPI_LOCK_DECL;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-#if __FreeBSD_version >= 500000
- /* Initialise the ACPI mutex */
- mtx_init(&acpi_mutex, "ACPI global lock", NULL, MTX_DEF);
-#endif
- ACPI_LOCK;
+ //lwkt_serialize_init(&acpi_sxlock);
sc = device_get_softc(dev);
- bzero(sc, sizeof(*sc));
sc->acpi_dev = dev;
- callout_init(&sc->acpi_sleep_timer);
+ callout_init(&sc->susp_force_to);
if ((error = acpi_task_thread_init())) {
- device_printf(dev, "Could not start task thread.\n");
- goto out;
+ device_printf(dev, "Could not start task thread.\n");
+ goto out;
}
- /*
- * Set the globals from our tunables. This is needed because ACPI-CA
- * uses UINT8 for some values and we have no tunable_byte.
- */
- AcpiGbl_AllMethodsSerialized = (UINT8)acpi_serialize_methods;
- AcpiGbl_EnableInterpreterSlack = TRUE;
-
error = ENXIO;
-#ifdef ACPI_DEBUGGER
- debugpoint = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (!strcmp(debugpoint, "tables"))
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
- }
-#endif
-
- if (ACPI_FAILURE(status = AcpiInitializeSubsystem())) {
- kprintf("ACPI: initialisation failed: %s\n",
- AcpiFormatException(status));
- goto out;
- }
- if (ACPI_FAILURE(status = AcpiLoadTables())) {
- kprintf("ACPI: table load failed: %s\n", AcpiFormatException(status));
- goto out;
- }
/* Initialize resource manager. */
acpi_rman_io.rm_type = RMAN_ARRAY;
acpi_rman_io.rm_start = 0;
acpi_rman_io.rm_end = 0xffff;
- acpi_rman_io.rm_descr = "I/O ports";
+ acpi_rman_io.rm_descr = "ACPI I/O ports";
if (rman_init(&acpi_rman_io) != 0)
panic("acpi rman_init IO ports failed");
acpi_rman_mem.rm_type = RMAN_ARRAY;
acpi_rman_mem.rm_start = 0;
acpi_rman_mem.rm_end = ~0ul;
- acpi_rman_mem.rm_descr = "I/O memory addresses";
+ acpi_rman_mem.rm_descr = "ACPI I/O memory addresses";
if (rman_init(&acpi_rman_mem) != 0)
panic("acpi rman_init memory failed");
-#ifdef ACPI_DEBUGGER
- debugpoint = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (!strcmp(debugpoint, "spaces"))
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
- }
+ /* Initialise the ACPI mutex */
+#ifdef notyet
+ spin_init(&acpi_mutex);
#endif
+
+ /*
+ * Set the globals from our tunables. This is needed because ACPI-CA
+ * uses UINT8 for some values and we have no tunable_byte.
+ */
+ AcpiGbl_AllMethodsSerialized = acpi_serialize_methods;
+ AcpiGbl_EnableInterpreterSlack = TRUE;
+
+ /* Start up the ACPI CA subsystem. */
+ status = AcpiInitializeSubsystem();
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Could not initialize Subsystem: %s\n",
+ AcpiFormatException(status));
+ goto out;
+ }
+
+ /* Load ACPI name space. */
+ status = AcpiLoadTables();
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Could not load Namespace: %s\n",
+ AcpiFormatException(status));
+ goto out;
+ }
+
/* Install the default address space handlers. */
status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_SYSTEM_MEMORY, ACPI_DEFAULT_HANDLER, NULL, NULL);
}
/*
- * 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).
- * For avoiding portions of the namespace without totally disabling _INI
- * and _STA, use "debug.acpi.avoid.paths".
*
* 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 = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (!strcmp(debugpoint, "enable"))
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
- }
-#endif
flags = 0;
if (ktestenv("debug.acpi.avoid"))
flags = ACPI_NO_DEVICE_INIT | ACPI_NO_OBJECT_INIT;
+
+ /* Bring the hardware and basic handlers online. */
if (ACPI_FAILURE(status = AcpiEnableSubsystem(flags))) {
device_printf(dev, "Could not enable ACPI: %s\n",
AcpiFormatException(status));
/*
* Call the ECDT probe function to provide EC functionality before
* the namespace has been evaluated.
+ *
+ * XXX This happens before the sysresource devices have been probed and
+ * attached so its resources come from nexus0. In practice, this isn't
+ * a problem but should be addressed eventually.
*/
acpi_ec_ecdt_probe(dev);
+ /* Bring device objects and regions online. */
if (ACPI_FAILURE(status = AcpiInitializeObjects(flags))) {
device_printf(dev, "Could not initialize ACPI objects: %s\n",
AcpiFormatException(status));
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");
+ OID_AUTO, "sleep_delay", 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");
+ OID_AUTO, "s4bios", 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");
+ OID_AUTO, "verbose", 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");
+ OID_AUTO, "disable_on_reboot", CTLFLAG_RW,
+ &sc->acpi_do_disable, 0, "Disable ACPI when rebooting/halting system");
+ SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
+ OID_AUTO, "handle_reboot", CTLFLAG_RW,
+ &sc->acpi_handle_reboot, 0, "Use ACPI Reset Register to reboot");
/*
* Default to 1 second before sleeping to give some machines time to
* stabilize.
*/
sc->acpi_sleep_delay = 1;
- sc->acpi_disable_on_poweroff = 0;
if (bootverbose)
sc->acpi_verbose = 1;
- if ((env = kgetenv("hw.acpi.verbose")) && strcmp(env, "0")) {
- sc->acpi_verbose = 1;
+ if ((env = kgetenv("hw.acpi.verbose")) != NULL) {
+ if (strcmp(env, "0") != 0)
+ sc->acpi_verbose = 1;
kfreeenv(env);
}
/* Only enable S4BIOS by default if the FACS says it is available. */
- status = AcpiGetTableByIndex(ACPI_TABLE_INDEX_FACS,
- (ACPI_TABLE_HEADER **)&facsp);
- if (ACPI_SUCCESS(status)) {
- if ((facsp->Flags & ACPI_FACS_S4_BIOS_PRESENT) != 0)
- sc->acpi_s4bios = 1;
+ status = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "couldn't get FACS: %s\n",
+ AcpiFormatException(status));
+ error = ENXIO;
+ goto out;
}
+ if (facs->Flags & ACPI_FACS_S4_BIOS_PRESENT)
+ sc->acpi_s4bios = 1;
/*
* Dispatch the default sleep state to devices. The lid switch is set
/* Pick the first valid sleep state for the sleep button default. */
sc->acpi_sleep_button_sx = ACPI_S_STATES_MAX + 1;
- for (state = ACPI_STATE_S1; state < ACPI_STATE_S5; state++)
+ for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++)
if (ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) {
sc->acpi_sleep_button_sx = state;
break;
/*
* Scan the namespace and attach/initialise children.
*/
-#ifdef ACPI_DEBUGGER
- debugpoint = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (!strcmp(debugpoint, "probe"))
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
- }
-#endif
- /* Register our shutdown handlers */
- EVENTHANDLER_REGISTER(shutdown_pre_sync, acpi_shutdown_pre_sync, sc,
- SHUTDOWN_PRI_LAST);
+ /* Register our shutdown handler. */
EVENTHANDLER_REGISTER(shutdown_final, acpi_shutdown_final, sc,
SHUTDOWN_PRI_LAST);
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_ops, 0, UID_ROOT, GID_WHEEL, 0644,
"acpi");
sc->acpi_dev_t->si_drv1 = sc;
-#ifdef ACPI_DEBUGGER
- debugpoint = kgetenv("debug.acpi.debugger");
- if (debugpoint) {
- if (strcmp(debugpoint, "running") == 0)
- acpi_EnterDebugger();
- kfreeenv(debugpoint);
- }
-#endif
-
if ((error = acpi_machdep_init(dev)))
goto out;
error = 0;
out:
- ACPI_UNLOCK;
-
cputimer_intr_pmfixup();
return_VALUE (error);
}
static int
-acpi_shutdown(device_t dev)
+acpi_suspend(device_t dev)
{
- /* Allow children to shutdown first. */
- bus_generic_shutdown(dev);
+ device_t child, *devlist;
+ int error, i, numdevs, pstate;
- /* Disable all wake GPEs not appropriate for reboot/poweroff. */
- acpi_wake_limit_walk(ACPI_STATE_S5);
- return (0);
+ GIANT_REQUIRED;
+
+ /* First give child devices a chance to suspend. */
+ error = bus_generic_suspend(dev);
+ if (error)
+ return (error);
+
+ /*
+ * Now, set them into the appropriate power state, usually D3. If the
+ * device has an _SxD method for the next sleep state, use that power
+ * state instead.
+ */
+ device_get_children(dev, &devlist, &numdevs);
+ for (i = 0; i < numdevs; i++) {
+ /* If the device is not attached, we've powered it down elsewhere. */
+ child = devlist[i];
+ if (!device_is_attached(child))
+ continue;
+
+ /*
+ * Default to D3 for all sleep states. The _SxD method is optional
+ * so set the powerstate even if it's absent.
+ */
+ pstate = PCI_POWERSTATE_D3;
+ error = acpi_device_pwr_for_sleep(device_get_parent(child),
+ child, &pstate);
+ if ((error == 0 || error == ESRCH) && acpi_do_powerstate)
+ pci_set_powerstate(child, pstate);
+ }
+ kfree(devlist, M_TEMP);
+ error = 0;
+
+ return (error);
}
-static void
-acpi_quirks_set(void)
+static int
+acpi_resume(device_t dev)
{
- ACPI_TABLE_HEADER *rsdt;
- struct acpi_quirks *quirk;
- char *env, *tmp;
- int len;
+ ACPI_HANDLE handle;
+ int i, numdevs;
+ device_t child, *devlist;
+
+ GIANT_REQUIRED;
/*
- * If the user loaded a custom table or disabled "quirks", leave
- * the settings alone.
+ * Put all devices in D0 before resuming them. Call _S0D on each one
+ * since some systems expect this.
*/
- len = 0;
- if ((env = kgetenv("acpi_dsdt_load")) != NULL) {
- /* XXX No strcasecmp but this is good enough. */
- if (*env == 'Y' || *env == 'y')
- goto out;
- kfreeenv(env);
- }
- if ((env = kgetenv("debug.acpi.disabled")) != NULL) {
- if (strstr("quirks", env) != NULL)
- goto out;
- len = strlen(env);
+ device_get_children(dev, &devlist, &numdevs);
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ handle = acpi_get_handle(child);
+ if (handle)
+ AcpiEvaluateObject(handle, "_S0D", NULL, NULL);
+ if (device_is_attached(child) && acpi_do_powerstate)
+ pci_set_powerstate(child, PCI_POWERSTATE_D0);
}
+ kfree(devlist, M_TEMP);
+
+ return (bus_generic_resume(dev));
+}
+
+static int
+acpi_shutdown(device_t dev)
+{
+
+ GIANT_REQUIRED;
+
+ /* Allow children to shutdown first. */
+ bus_generic_shutdown(dev);
/*
- * Search through our quirk table and concatenate the disabled
- * values with whatever we find.
+ * Enable any GPEs that are able to power-on the system (i.e., RTC).
+ * Also, disable any that are not valid for this state (most).
*/
- if ((rsdt = acpi_map_rsdt_header()) == NULL)
- goto out;
- for (quirk = acpi_quirks_table; quirk->OemId; quirk++) {
- if (!strncmp(rsdt->OemId, quirk->OemId, strlen(quirk->OemId)) &&
- (rsdt->OemRevision == quirk->OemRevision ||
- quirk->OemRevision == ACPI_OEM_REV_ANY)) {
- len += strlen(quirk->value) + 2;
- if ((tmp = kmalloc(len, M_TEMP, M_NOWAIT)) == NULL)
- goto out;
- ksprintf(tmp, "%s %s", env ? env : "", quirk->value);
- ksetenv("debug.acpi.disabled", tmp);
- kfree(tmp, M_TEMP);
- break;
- }
- }
- AcpiOsUnmapMemory(rsdt, sizeof(*rsdt));
+ acpi_wake_prep_walk(ACPI_STATE_S5);
-out:
- if (env)
- kfreeenv(env);
+ return (0);
}
/*
* Handle a new device being added
*/
static device_t
-acpi_add_child(device_t bus, device_t parent, int order,
- const char *name, int unit)
+acpi_add_child(device_t bus, device_t parent, int order, const char *name, int unit)
{
struct acpi_device *ad;
device_t child;
- ad = kmalloc(sizeof(*ad), M_ACPIDEV, M_INTWAIT | M_ZERO);
+ if ((ad = kmalloc(sizeof(*ad), M_ACPIDEV, M_NOWAIT | M_ZERO)) == NULL)
+ return (NULL);
resource_list_init(&ad->ad_rl);
-
child = device_add_child_ordered(parent, order, name, unit);
if (child != NULL)
device_set_ivars(child, ad);
+ else
+ kfree(ad, M_ACPIDEV);
return (child);
}
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");
+ if (device_get_flags(child))
+ retval += kprintf(" flags %#x", device_get_flags(child));
retval += bus_print_child_footer(bus, child);
return (retval);
}
+/*
+ * If this device is an ACPI child but no one claimed it, attempt
+ * to power it off. We'll power it back up when a driver is added.
+ *
+ * XXX Disabled for now since many necessary devices (like fdc and
+ * ATA) don't claim the devices we created for them but still expect
+ * them to be powered up.
+ */
+static void
+acpi_probe_nomatch(device_t bus, device_t child)
+{
+
+ /* pci_set_powerstate(child, PCI_POWERSTATE_D3); */
+}
+
+/*
+ * If a new driver has a chance to probe a child, first power it up.
+ *
+ * XXX Disabled for now (see acpi_probe_nomatch for details).
+ */
+static void
+acpi_driver_added(device_t dev, driver_t *driver)
+{
+ device_t child, *devlist;
+ int i, numdevs;
+
+ DEVICE_IDENTIFY(driver, dev);
+ device_get_children(dev, &devlist, &numdevs);
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ if (device_get_state(child) == DS_NOTPRESENT) {
+ /* pci_set_powerstate(child, PCI_POWERSTATE_D0); */
+ if (device_probe_and_attach(child) != 0)
+ ; /* pci_set_powerstate(child, PCI_POWERSTATE_D3); */
+ }
+ }
+ kfree(devlist, M_TEMP);
+}
+
/* Location hint for devctl(8) */
static int
acpi_child_location_str_method(device_t cbdev, device_t child, char *buf,
struct acpi_device *dinfo = device_get_ivars(child);
if (dinfo->ad_handle)
- ksnprintf(buf, buflen, "path=%s", acpi_name(dinfo->ad_handle));
+ ksnprintf(buf, buflen, "handle=%s", acpi_name(dinfo->ad_handle));
else
- ksnprintf(buf, buflen, "magic=unknown");
+ ksnprintf(buf, buflen, "unknown");
return (0);
}
error = AcpiGetObjectInfo(dinfo->ad_handle, &adbuf);
adinfo = (ACPI_DEVICE_INFO *) adbuf.Pointer;
-
if (error)
- ksnprintf(buf, buflen, "Unknown");
+ ksnprintf(buf, buflen, "unknown");
else
ksnprintf(buf, buflen, "_HID=%s _UID=%lu",
- (adinfo->Valid & ACPI_VALID_HID) ?
- adinfo->HardwareId.Value : "UNKNOWN",
- (adinfo->Valid & ACPI_VALID_UID) ?
- strtoul(adinfo->UniqueId.Value, &end, 10) : 0);
-
+ (adinfo->Valid & ACPI_VALID_HID) ?
+ adinfo->HardwareId.Value : "none",
+ (adinfo->Valid & ACPI_VALID_UID) ?
+ strtoul(adinfo->UniqueId.Value, &end, 10) : 0);
if (adinfo)
AcpiOsFree(adinfo);
*(ACPI_HANDLE *)result = ad->ad_handle;
break;
case ACPI_IVAR_MAGIC:
- *(int *)result = ad->ad_magic;
+ *(uintptr_t *)result = ad->ad_magic;
break;
case ACPI_IVAR_PRIVATE:
*(void **)result = ad->ad_private;
break;
+ case ACPI_IVAR_FLAGS:
+ *(int *)result = ad->ad_flags;
+ break;
case ISA_IVAR_VENDORID:
case ISA_IVAR_SERIAL:
case ISA_IVAR_COMPATID:
ad->ad_handle = (ACPI_HANDLE)value;
break;
case ACPI_IVAR_MAGIC:
- ad->ad_magic = (int)value;
+ ad->ad_magic = (uintptr_t)value;
break;
case ACPI_IVAR_PRIVATE:
ad->ad_private = (void *)value;
break;
+ case ACPI_IVAR_FLAGS:
+ ad->ad_flags = (int)value;
+ break;
default:
panic("bad ivar write request (%d)", index);
return (ENOENT);
return (&ad->ad_rl);
}
+/*
+ * Pre-allocate/manage all memory and IO resources. Since rman can't handle
+ * duplicates, we merge any in the sysresource attach routine.
+ */
+static int
+acpi_sysres_alloc(device_t dev)
+{
+ struct resource *res;
+ struct resource_list *rl;
+ struct resource_list_entry *rle;
+ struct rman *rm;
+ char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL };
+ device_t *children;
+ int child_count, i;
+ /*
+ * Probe/attach any sysresource devices. This would be unnecessary if we
+ * had multi-pass probe/attach.
+ */
+ if (device_get_children(dev, &children, &child_count) != 0)
+ return (ENXIO);
+ for (i = 0; i < child_count; i++) {
+ if (ACPI_ID_PROBE(dev, children[i], sysres_ids) != NULL)
+ device_probe_and_attach(children[i]);
+ }
+ kfree(children, M_TEMP);
+
+ rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
+ if(!rl)
+ return 0;
+ SLIST_FOREACH(rle, rl, link) {
+ if (rle->res != NULL) {
+ device_printf(dev, "duplicate resource for %lx\n", rle->start);
+ continue;
+ }
+
+ /* Only memory and IO resources are valid here. */
+ switch (rle->type) {
+ case SYS_RES_IOPORT:
+ rm = &acpi_rman_io;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &acpi_rman_mem;
+ break;
+ default:
+ continue;
+ }
+
+ /* Pre-allocate resource and add to our rman pool. */
+ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, rle->type,
+ &rle->rid, rle->start, rle->start + rle->count - 1, rle->count, 0);
+ if (res != NULL) {
+ rman_manage_region(rm, rman_get_start(res), rman_get_end(res));
+ rle->res = res;
+ } else
+ device_printf(dev, "reservation of %lx, %lx (%d) failed\n",
+ rle->start, rle->count, rle->type);
+ }
+ 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)
{
+ ACPI_RESOURCE ares;
struct acpi_device *ad = device_get_ivars(child);
struct resource_list *rl = &ad->ad_rl;
struct resource_list_entry *rle;
struct resource *res;
struct rman *rm;
- int needactivate;
+
+ res = NULL;
+
+ /* We only handle memory and IO resources through rman. */
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &acpi_rman_io;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &acpi_rman_mem;
+ break;
+ default:
+ rm = NULL;
+ }
+
+ ACPI_SERIAL_BEGIN(acpi);
/*
* If this is an allocation of the "default" range for a given RID, and
* we know what the resources for this device are (i.e., they're on the
* child's resource list), use those start/end values.
*/
- if (start == 0UL && end == ~0UL) {
+ if (bus == device_get_parent(child) && start == 0UL && end == ~0UL) {
rle = resource_list_find(rl, type, *rid);
if (rle == NULL)
- return (NULL);
+ goto out;
start = rle->start;
end = rle->end;
count = rle->count;
}
- /* If we don't manage this address, pass the request up to the parent. */
- rle = acpi_sysres_find(type, start);
- if (rle == NULL) {
- return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid,
- start, end, count, flags));
- }
+ /*
+ * If this is an allocation of a specific range, see if we can satisfy
+ * the request from our system resource regions. If we can't, pass the
+ * request up to the parent.
+ */
+ if (start + count - 1 == end && rm != NULL)
+ res = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
+ child);
+ if (res == NULL) {
+ res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid,
+ start, end, count, flags);
+ } else {
+ rman_set_rid(res, *rid);
- /* We only handle memory and IO resources through rman. */
- switch (type) {
- case SYS_RES_IOPORT:
- rm = &acpi_rman_io;
- break;
- case SYS_RES_MEMORY:
- rm = &acpi_rman_mem;
- break;
- default:
- panic("acpi_alloc_resource: invalid res type %d", type);
+ /* If requested, activate the resource using the parent's method. */
+ if (flags & RF_ACTIVE)
+ if (bus_activate_resource(child, type, *rid, res) != 0) {
+ rman_release_resource(res);
+ res = NULL;
+ goto out;
+ }
}
- /* If we do know it, allocate it from the local pool. */
- needactivate = flags & RF_ACTIVE;
- flags &= ~RF_ACTIVE;
- res = rman_reserve_resource(rm, start, end, count, flags, child);
- if (res == NULL)
- return (NULL);
-
- /* Copy the bus tag from the pre-allocated resource. */
- rman_set_bustag(res, rman_get_bustag(rle->res));
- if (type == SYS_RES_IOPORT)
- rman_set_bushandle(res, res->r_start);
-
- /* If requested, activate the resource using the parent's method. */
- if (needactivate)
- if (bus_activate_resource(child, type, *rid, res) != 0) {
- rman_release_resource(res);
- return (NULL);
+ if (res != NULL && device_get_parent(child) == bus)
+ switch (type) {
+ case SYS_RES_IRQ:
+ /*
+ * Since bus_config_intr() takes immediate effect, we cannot
+ * configure the interrupt associated with a device when we
+ * parse the resources but have to defer it until a driver
+ * actually allocates the interrupt via bus_alloc_resource().
+ *
+ * XXX: Should we handle the lookup failing?
+ */
+kprintf("%s(): %d\n", __FUNCTION__, __LINE__);
+ if (ACPI_SUCCESS(acpi_lookup_irq_resource(child, *rid, res, &ares)))
+ acpi_config_intr(child, &ares);
+ else
+ kprintf("irq resource not found\n");
+ break;
}
+out:
+ ACPI_SERIAL_END(acpi);
return (res);
}
acpi_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
+ struct rman *rm;
int ret;
+ /* We only handle memory and IO resources through rman. */
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &acpi_rman_io;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &acpi_rman_mem;
+ break;
+ default:
+ rm = NULL;
+ }
+
+ ACPI_SERIAL_BEGIN(acpi);
+
/*
- * If we know about this address, deactivate it and release it to the
- * local pool. If we don't, pass this request up to the parent.
+ * If this resource belongs to one of our internal managers,
+ * deactivate it and release it to the local pool. If it doesn't,
+ * pass this request up to the parent.
*/
- if (acpi_sysres_find(type, rman_get_start(r)) == NULL) {
+ if (rm != NULL && rman_is_region_manager(r, rm)) {
if (rman_get_flags(r) & RF_ACTIVE) {
ret = bus_deactivate_resource(child, type, rid, r);
if (ret != 0)
- return (ret);
+ goto out;
}
ret = rman_release_resource(r);
} else
ret = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r);
+out:
+ ACPI_SERIAL_END(acpi);
return (ret);
}
+static void
+acpi_delete_resource(device_t bus, device_t child, int type, int rid)
+{
+ struct resource_list *rl;
+
+ rl = acpi_get_rlist(bus, child);
+ resource_list_delete(rl, type, rid);
+}
+
/* Allocate an IO port or memory resource, given its GAS. */
-struct resource *
-acpi_bus_alloc_gas(device_t dev, int *rid, const ACPI_GENERIC_ADDRESS *gas,
- u_int flags)
+int
+acpi_bus_alloc_gas(device_t dev, int *type, int *rid, ACPI_GENERIC_ADDRESS *gas,
+ struct resource **res, u_int flags)
{
- int type;
+ int error, res_type;
- if (gas == NULL || gas->BitWidth < 8)
- return (NULL);
+ error = ENOMEM;
+ if (type == NULL || rid == NULL || gas == NULL || res == NULL)
+ return (EINVAL);
+ /* We only support memory and IO spaces. */
switch (gas->SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- type = SYS_RES_MEMORY;
+ res_type = SYS_RES_MEMORY;
break;
case ACPI_ADR_SPACE_SYSTEM_IO:
- type = SYS_RES_IOPORT;
+ res_type = SYS_RES_IOPORT;
break;
default:
- return (NULL);
+ return (EOPNOTSUPP);
}
- bus_set_resource(dev, type, *rid, gas->Address, gas->BitWidth / 8);
- return (bus_alloc_resource_any(dev, type, rid, RF_ACTIVE | flags));
-}
+ /*
+ * If the register width is less than 8, assume the BIOS author means
+ * it is a bit field and just allocate a byte.
+ */
+ if (gas->BitWidth && gas->BitWidth < 8)
+ gas->BitWidth = 8;
-/*
- * 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))
+ /* Validate the address after we're sure we support the space. */
+ if (gas->Address == 0 || gas->BitWidth == 0)
+ return (EINVAL);
+
+ bus_set_resource(dev, res_type, *rid, gas->Address,
+ gas->BitWidth / 8);
+ *res = bus_alloc_resource_any(dev, res_type, rid, RF_ACTIVE | flags);
+ if (*res != NULL) {
+ *type = res_type;
+ error = 0;
+ } else
+ bus_delete_resource(dev, res_type, *rid);
+ return (error);
+}
+
+/* Probe _HID and _CID for compatible ISA PNP ids. */
static uint32_t
acpi_isa_get_logicalid(device_t dev)
{
ACPI_HANDLE h;
ACPI_STATUS error;
u_int32_t pnpid;
- ACPI_LOCK_DECL;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
- ACPI_LOCK;
-
/* Fetch and validate the HID. */
if ((h = acpi_get_handle(dev)) == NULL)
goto out;
out:
if (buf.Pointer != NULL)
AcpiOsFree(buf.Pointer);
- ACPI_UNLOCK;
return_VALUE (pnpid);
}
ACPI_STATUS error;
uint32_t *pnpid;
int valid, i;
- ACPI_LOCK_DECL;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
- ACPI_LOCK;
-
/* Fetch and validate the CID */
if ((h = acpi_get_handle(dev)) == NULL)
goto out;
out:
if (buf.Pointer != NULL)
AcpiOsFree(buf.Pointer);
- ACPI_UNLOCK;
return_VALUE (valid);
}
return (NULL);
}
+static ACPI_STATUS
+acpi_device_eval_obj(device_t bus, device_t dev, ACPI_STRING pathname,
+ ACPI_OBJECT_LIST *parameters, ACPI_BUFFER *ret)
+{
+ ACPI_HANDLE h;
+
+ if (dev == NULL)
+ h = ACPI_ROOT_OBJECT;
+ else if ((h = acpi_get_handle(dev)) == NULL)
+ return (AE_BAD_PARAMETER);
+ return (AcpiEvaluateObject(h, pathname, parameters, ret));
+}
+
+static int
+acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate)
+{
+ struct acpi_softc *sc;
+ ACPI_HANDLE handle;
+ ACPI_STATUS status;
+ char sxd[8];
+ int error;
+
+ sc = device_get_softc(bus);
+ handle = acpi_get_handle(dev);
+
+ /*
+ * XXX If we find these devices, don't try to power them down.
+ * The serial and IRDA ports on my T23 hang the system when
+ * set to D3 and it appears that such legacy devices may
+ * need special handling in their drivers.
+ */
+ if (handle == NULL ||
+ acpi_MatchHid(handle, "PNP0500") ||
+ acpi_MatchHid(handle, "PNP0501") ||
+ acpi_MatchHid(handle, "PNP0502") ||
+ acpi_MatchHid(handle, "PNP0510") ||
+ acpi_MatchHid(handle, "PNP0511"))
+ return (ENXIO);
+
+ /*
+ * Override next state with the value from _SxD, if present. If no
+ * dstate argument was provided, don't fetch the return value.
+ */
+ ksnprintf(sxd, sizeof(sxd), "_S%dD", sc->acpi_sstate);
+ if (dstate)
+ status = acpi_GetInteger(handle, sxd, dstate);
+ else
+ status = AcpiEvaluateObject(handle, sxd, NULL, NULL);
+
+ switch (status) {
+ case AE_OK:
+ error = 0;
+ break;
+ case AE_NOT_FOUND:
+ error = ESRCH;
+ break;
+ default:
+ error = ENXIO;
+ break;
+ }
+
+ return (error);
+}
+
+/* Callback arg for our implementation of walking the namespace. */
+struct acpi_device_scan_ctx {
+ acpi_scan_cb_t user_fn;
+ void *arg;
+ ACPI_HANDLE parent;
+};
+
+static ACPI_STATUS
+acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level, void *arg, void **retval)
+{
+ struct acpi_device_scan_ctx *ctx;
+ device_t dev, old_dev;
+ ACPI_STATUS status;
+ ACPI_OBJECT_TYPE type;
+
+ /*
+ * Skip this device if we think we'll have trouble with it or it is
+ * the parent where the scan began.
+ */
+ ctx = (struct acpi_device_scan_ctx *)arg;
+ if (acpi_avoid(h) || h == ctx->parent)
+ return (AE_OK);
+
+ /* If this is not a valid device type (e.g., a method), skip it. */
+ if (ACPI_FAILURE(AcpiGetType(h, &type)))
+ return (AE_OK);
+ if (type != ACPI_TYPE_DEVICE && type != ACPI_TYPE_PROCESSOR &&
+ type != ACPI_TYPE_THERMAL && type != ACPI_TYPE_POWER)
+ return (AE_OK);
+
+ /*
+ * Call the user function with the current device. If it is unchanged
+ * afterwards, return. Otherwise, we update the handle to the new dev.
+ */
+ old_dev = acpi_get_device(h);
+ dev = old_dev;
+ status = ctx->user_fn(h, &dev, level, ctx->arg);
+ if (ACPI_FAILURE(status) || old_dev == dev)
+ return (status);
+
+ /* Remove the old child and its connection to the handle. */
+ if (old_dev != NULL) {
+ device_delete_child(device_get_parent(old_dev), old_dev);
+ AcpiDetachData(h, acpi_fake_objhandler);
+ }
+
+ /* Recreate the handle association if the user created a device. */
+ if (dev != NULL)
+ AcpiAttachData(h, acpi_fake_objhandler, dev);
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_device_scan_children(device_t bus, device_t dev, int max_depth,
+ acpi_scan_cb_t user_fn, void *arg)
+{
+ ACPI_HANDLE h;
+ struct acpi_device_scan_ctx ctx;
+
+ if (acpi_disabled("children"))
+ return (AE_OK);
+
+ if (dev == NULL)
+ h = ACPI_ROOT_OBJECT;
+ else if ((h = acpi_get_handle(dev)) == NULL)
+ return (AE_BAD_PARAMETER);
+ ctx.user_fn = user_fn;
+ ctx.arg = arg;
+ ctx.parent = h;
+ return (AcpiWalkNamespace(ACPI_TYPE_ANY, h, max_depth,
+ acpi_device_scan_cb, &ctx, NULL));
+}
+
+/*
+ * Even though ACPI devices are not PCI, we use the PCI approach for setting
+ * device power states since it's close enough to ACPI.
+ */
+static int
+acpi_set_powerstate_method(device_t bus, device_t child, int state)
+{
+ ACPI_HANDLE h;
+ ACPI_STATUS status;
+ int error;
+
+ error = 0;
+ h = acpi_get_handle(child);
+ if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3)
+ return (EINVAL);
+ if (h == NULL)
+ return (0);
+
+ /* Ignore errors if the power methods aren't present. */
+ status = acpi_pwr_switch_consumer(h, state);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND
+ && status != AE_BAD_PARAMETER)
+ device_printf(bus, "failed to set ACPI power state D%d on %s: %s\n",
+ state, acpi_name(h), AcpiFormatException(status));
+
+ return (error);
+}
+
static int
acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids)
{
}
out:
+ if (result == 0 && ids->ip_desc)
+ device_set_desc(child, ids->ip_desc);
+
return_VALUE (result);
}
/*
- * Scan relevant portions of the ACPI namespace and attach child devices.
+ * Scan all 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.
+ * We should only expect to find devices in the \_PR, \_TZ, \_SI, and
+ * \_SB scopes, and \_PR and \_TZ became obsolete in the ACPI 2.0 spec.
+ * However, in violation of the spec, some systems place their PCI link
+ * devices in \, so we have to walk the whole namespace. We check the
+ * type of namespace nodes, so this should be ok.
*/
static void
acpi_probe_children(device_t bus)
{
- ACPI_HANDLE parent;
- ACPI_STATUS status;
- int i;
- static char *scopes[] = {"\\_PR_", "\\_TZ_", "\\_SI", "\\_SB_", NULL};
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
- ACPI_ASSERTLOCK;
/*
* Scan the namespace and insert placeholders for all the devices that
* devices as they appear, which might be smarter.)
*/
ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "namespace scan\n"));
- for (i = 0; scopes[i] != NULL; i++) {
- status = AcpiGetHandle(ACPI_ROOT_OBJECT, scopes[i], &parent);
- if (ACPI_SUCCESS(status)) {
- AcpiWalkNamespace(ACPI_TYPE_ANY, parent, 100, acpi_probe_child,
- bus, NULL);
- }
- }
+ AcpiWalkNamespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, 100, acpi_probe_child,
+ bus, NULL);
+ /* Pre-allocate resources for our rman from any sysresource devices. */
+ acpi_sysres_alloc(bus);
/* Create any static children by calling device identify methods. */
ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "device identify routines\n"));
bus_generic_probe(bus);
- /*
- * Scan all of the child devices we have created and let them probe/attach.
- */
+ /* Probe/attach all children, created staticly and from the namespace. */
ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "first bus_generic_attach\n"));
bus_generic_attach(bus);
return_VOID;
}
-static int
-acpi_probe_order(ACPI_HANDLE handle, int level, int *order)
+/*
+ * Determine the probe order for a given device.
+ */
+static void
+acpi_probe_order(ACPI_HANDLE handle, int *order)
{
- int ret;
+ ACPI_OBJECT_TYPE type;
- ret = 0;
- /* IO port and memory system resource holders are first. */
- if (acpi_MatchHid(handle, "PNP0C01") || acpi_MatchHid(handle, "PNP0C02")) {
+ /*
+ * 1. I/O port and memory system resource holders
+ * 2. Embedded controllers (to handle early accesses)
+ * 3. PCI Link Devices
+ * 100000. CPUs
+ */
+ AcpiGetType(handle, &type);
+ if (acpi_MatchHid(handle, "PNP0C01") || acpi_MatchHid(handle, "PNP0C02"))
*order = 1;
- ret = 1;
- }
-
- /* The embedded controller is needed to handle accesses early. */
- if (acpi_MatchHid(handle, "PNP0C09")) {
+ else if (acpi_MatchHid(handle, "PNP0C09"))
*order = 2;
- ret = 1;
- }
-
- *order = (level + 1) * 10;
- return (ret);
+ else if (acpi_MatchHid(handle, "PNP0C0F"))
+ *order = 3;
+ else if (type == ACPI_TYPE_PROCESSOR)
+ *order = 100000;
}
/*
static ACPI_STATUS
acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
{
- ACPI_OBJECT_TYPE type;
- device_t child, bus;
- int order, probe_now;
+ ACPI_OBJECT_TYPE type;
+ ACPI_HANDLE h;
+ device_t bus, child;
+ int order;
+ char *handle_str, **search;
+ static char *scopes[] = {"\\_PR_", "\\_TZ_", "\\_SI_", "\\_SB_", NULL};
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
/* Skip this device if we think we'll have trouble with it. */
- if (acpi_avoid(handle)) {
- ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "not scanning '%s'\n",
- acpi_name(handle)));
+ if (acpi_avoid(handle))
return_ACPI_STATUS (AE_OK);
- }
bus = (device_t)context;
if (ACPI_SUCCESS(AcpiGetType(handle, &type))) {
- switch(type) {
+ switch (type) {
case ACPI_TYPE_DEVICE:
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_THERMAL:
if (acpi_disabled("children"))
break;
+ /*
+ * Since we scan from \, be sure to skip system scope objects.
+ * At least \_SB and \_TZ are detected as devices (ACPI-CA bug?)
+ */
+ handle_str = acpi_name(handle);
+ for (search = scopes; *search != NULL; search++) {
+ if (strcmp(handle_str, *search) == 0)
+ break;
+ }
+ if (*search != NULL)
+ break;
+
/*
- * Create a placeholder device for this node. Sort the placeholder
- * so that the probe/attach passes will run breadth-first.
+ * Create a placeholder device for this node. Sort the
+ * placeholder so that the probe/attach passes will run
+ * breadth-first. Orders less than ACPI_DEV_BASE_ORDER
+ * are reserved for special objects (i.e., system
+ * resources). CPU devices have a very high order to
+ * ensure they are probed after other devices.
*/
- ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "scanning '%s'\n",
- acpi_name(handle)));
- probe_now = acpi_probe_order(handle, level, &order);
+ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "scanning '%s'\n", handle_str));
+ order = level * 10 + 100;
+ acpi_probe_order(handle, &order);
child = BUS_ADD_CHILD(bus, bus, order, NULL, -1);
if (child == NULL)
break;
acpi_set_handle(child, handle);
AcpiAttachData(handle, acpi_fake_objhandler, child);
- /* Check if the device can generate wake events. */
- if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_PRW", NULL, NULL)))
- device_set_flags(child, ACPI_FLAG_WAKE_CAPABLE);
-
/*
* 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).
+ *
+ * XXX PCI link devices sometimes report "present" but not
+ * "functional" (i.e. if disabled). Go ahead and probe them
+ * anyway since we may enable them later.
*/
if (type == ACPI_TYPE_DEVICE && !acpi_DeviceIsPresent(child)) {
+ /* Never disable PCI link devices. */
+ if (acpi_MatchHid(handle, "PNP0C0F"))
+ break;
+ /*
+ * Docking stations should remain enabled since the system
+ * may be undocked at boot.
+ */
+ if (ACPI_SUCCESS(AcpiGetHandle(handle, "_DCK", &h)))
+ break;
+
device_disable(child);
break;
}
* device not to have any resources.
*/
acpi_parse_resources(child, handle, &acpi_res_parse_set, NULL);
-
- /* If order was overridden, probe/attach now rather than later. */
- if (probe_now)
- 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);
-}
-
/*
* AcpiAttachData() requires an object handler but never uses it. This is a
* placeholder object handler so we can store a device_t in an ACPI_HANDLE.
static void
acpi_shutdown_final(void *arg, int howto)
{
- ACPI_STATUS status;
- ACPI_ASSERTLOCK;
+ struct acpi_softc *sc;
+ ACPI_STATUS status;
/*
- * If powering off, run the actual shutdown code on each processor.
- * It will only perform the shutdown on the BSP. Some chipsets do
- * not power off the system correctly if called from an AP.
+ * XXX Shutdown code should only run on the BSP (cpuid 0).
+ * Some chipsets do not power off the system correctly if called from
+ * an AP.
*/
+ sc = arg;
if ((howto & RB_POWEROFF) != 0) {
status = AcpiEnterSleepStatePrep(ACPI_STATE_S5);
if (ACPI_FAILURE(status)) {
return;
}
kprintf("Powering system off using ACPI\n");
- acpi_shutdown_poweroff(NULL);
- } else {
+ ACPI_DISABLE_IRQS();
+ status = AcpiEnterSleepState(ACPI_STATE_S5);
+ if (ACPI_FAILURE(status)) {
+ kprintf("ACPI power-off failed - %s\n", AcpiFormatException(status));
+ } else {
+ DELAY(1000000);
+ kprintf("ACPI power-off failed - timeout\n");
+ }
+ } else if ((howto & RB_HALT) == 0 &&
+ (AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER) &&
+ sc->acpi_handle_reboot) {
+ /* Reboot using the reset register. */
+ status = AcpiWrite(
+ AcpiGbl_FADT.ResetValue, &AcpiGbl_FADT.ResetRegister);
+ if (ACPI_FAILURE(status)) {
+ kprintf("ACPI reset failed - %s\n", AcpiFormatException(status));
+ } else {
+ DELAY(1000000);
+ kprintf("ACPI reset failed - timeout\n");
+ }
+ } else if (sc->acpi_do_disable && panicstr == NULL) {
+ /*
+ * Only disable ACPI if the user requested. On some systems, writing
+ * the disable value to SMI_CMD hangs the system.
+ */
kprintf("Shutting down ACPI\n");
AcpiTerminate();
}
}
-/*
- * Since this function may be called with locks held or in an unknown
- * context, it cannot allocate memory, acquire locks, sleep, etc.
- */
-static void
-acpi_shutdown_poweroff(void *arg)
-{
- ACPI_STATUS status;
-
- ACPI_ASSERTLOCK;
-
- /* Only attempt to power off if this is the BSP (cpuid 0). */
- if (mdcpu->mi.gd_cpuid != 0)
- return;
-
- ACPI_DISABLE_IRQS();
- status = AcpiEnterSleepState(ACPI_STATE_S5);
- if (ACPI_FAILURE(status)) {
- kprintf("ACPI power-off failed - %s\n", AcpiFormatException(status));
- } else {
- DELAY(1000000);
- kprintf("ACPI power-off failed - timeout\n");
- }
-}
-
static void
acpi_enable_fixed_events(struct acpi_softc *sc)
{
static int first_time = 1;
- ACPI_ASSERTLOCK;
-
/* Enable and clear fixed events and install handlers. */
if ((AcpiGbl_FADT.Flags & ACPI_FADT_POWER_BUTTON) == 0) {
AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
ACPI_STATUS error;
int ret;
- ACPI_ASSERTLOCK;
-
ret = FALSE;
if ((h = acpi_get_handle(dev)) == NULL)
return (FALSE);
ret = TRUE;
/* Return true for 'present' and 'functioning' */
- if ((devinfo->CurrentStatus & 0x9) == 0x9)
+ if (ACPI_DEVICE_PRESENT(devinfo->CurrentStatus))
ret = TRUE;
AcpiOsFree(buf.Pointer);
ACPI_STATUS error;
int ret;
- ACPI_ASSERTLOCK;
-
ret = FALSE;
if ((h = acpi_get_handle(dev)) == NULL)
return (FALSE);
if ((devinfo->Valid & ACPI_VALID_STA) == 0)
ret = TRUE;
- /* Return true for 'present' and 'functioning' */
- if ((devinfo->CurrentStatus & 0x19) == 0x19)
+ /* Return true for 'present', 'battery present', and 'functioning' */
+ if (ACPI_BATTERY_PRESENT(devinfo->CurrentStatus))
ret = TRUE;
AcpiOsFree(buf.Pointer);
* Match a HID string against a handle
*/
BOOLEAN
-acpi_MatchHid(ACPI_HANDLE h, char *hid)
+acpi_MatchHid(ACPI_HANDLE h, const char *hid)
{
ACPI_DEVICE_INFO *devinfo;
ACPI_BUFFER buf;
ACPI_STATUS error;
int ret, i;
- ACPI_ASSERTLOCK;
-
ret = FALSE;
if (hid == NULL || h == NULL)
return (ret);
ACPI_HANDLE r;
ACPI_STATUS status;
- ACPI_ASSERTLOCK;
-
/* Walk back up the tree to the root */
for (;;) {
status = AcpiGetHandle(parent, path, &r);
*result = r;
return (AE_OK);
}
+ /* XXX Return error here? */
if (status != AE_NOT_FOUND)
return (AE_OK);
if (ACPI_FAILURE(AcpiGetParent(parent, &r)))
if (end >= start)
delta = end - start;
- else if ((AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER) == 0)
- delta = ((0x00FFFFFF - start) + end + 1) & 0x00FFFFFF;
- else
+ else if (AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER)
delta = ((0xFFFFFFFF - start) + end + 1);
+ else
+ delta = ((0x00FFFFFF - start) + end + 1) & 0x00FFFFFF;
return (delta);
}
{
ACPI_BUFFER *buf;
- buf = kmalloc(size + sizeof(*buf), M_ACPIDEV, M_INTWAIT);
+ if ((buf = kmalloc(size + sizeof(*buf), M_ACPIDEV, M_NOWAIT)) == NULL)
+ return (NULL);
buf->Length = size;
buf->Pointer = (void *)(buf + 1);
return (buf);
ACPI_OBJECT arg1;
ACPI_OBJECT_LIST args;
- ACPI_ASSERTLOCK;
-
arg1.Type = ACPI_TYPE_INTEGER;
arg1.Integer.Value = number;
args.Count = 1;
ACPI_BUFFER buf;
ACPI_OBJECT param;
- ACPI_ASSERTLOCK;
-
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
{
ACPI_OBJECT *comp;
int i;
-
+
if (pkg == NULL || pkg->Type != ACPI_TYPE_PACKAGE)
return (AE_BAD_PARAMETER);
rp = (ACPI_RESOURCE *)buf->Pointer;
i = index;
while (i-- > 0) {
- /* Range check */
+ /* Range check */
if (rp > (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + buf->Length))
return (AE_BAD_PARAMETER);
{
ACPI_RESOURCE *rp;
void *newp;
-
+
/* Initialise the buffer if necessary. */
if (buf->Pointer == NULL) {
buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE;
}
if (res == NULL)
return (AE_OK);
-
+
/*
* Scan the current buffer looking for the terminator.
* This will either find the terminator or hit the end
buf->Pointer = newp;
buf->Length += buf->Length;
}
-
- /* Insert the new resource. */
- bcopy(res, rp, res->Length + ACPI_RS_SIZE_NO_DATA);
-
- /* And add the terminator. */
- rp = ACPI_NEXT_RESOURCE(rp);
- rp->Type = ACPI_RESOURCE_TYPE_END_TAG;
- rp->Length = 0;
- return (AE_OK);
+ /* Insert the new resource. */
+ bcopy(res, rp, res->Length + ACPI_RS_SIZE_NO_DATA);
+
+ /* And add the terminator. */
+ rp = ACPI_NEXT_RESOURCE(rp);
+ rp->Type = ACPI_RESOURCE_TYPE_END_TAG;
+ rp->Length = 0;
+
+ return (AE_OK);
+}
+
+/*
+ * Set interrupt model.
+ */
+ACPI_STATUS
+acpi_SetIntrModel(int model)
+{
+
+ return (acpi_SetInteger(ACPI_ROOT_OBJECT, "_PIC", model));
+}
+
+/*
+ * DEPRECATED. This interface has serious deficiencies and will be
+ * removed.
+ *
+ * Immediately enter the sleep state. In the old model, acpiconf(8) ran
+ * rc.suspend and rc.resume so we don't have to notify devd(8) to do this.
+ */
+ACPI_STATUS
+acpi_SetSleepState(struct acpi_softc *sc, int state)
+{
+ static int once;
+
+ if (!once) {
+ kprintf(
+"warning: acpi_SetSleepState() deprecated, need to update your software\n");
+ once = 1;
+ }
+ return (acpi_EnterSleepState(sc, state));
+}
+
+static void
+acpi_sleep_force(void *arg)
+{
+ struct acpi_softc *sc;
+
+ kprintf("acpi: suspend request timed out, forcing sleep now\n");
+ sc = arg;
+ if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate)))
+ kprintf("acpi: force sleep state S%d failed\n", sc->acpi_next_sstate);
+}
+
+/*
+ * Request that the system enter the given suspend state. All /dev/apm
+ * devices and devd(8) will be notified. Userland then has a chance to
+ * save state and acknowledge the request. The system sleeps once all
+ * acks are in.
+ */
+int
+acpi_ReqSleepState(struct acpi_softc *sc, int state)
+{
+ struct apm_clone_data *clone;
+
+ if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
+ return (EINVAL);
+
+ /* S5 (soft-off) should be entered directly with no waiting. */
+ if (state == ACPI_STATE_S5) {
+ if (ACPI_SUCCESS(acpi_EnterSleepState(sc, state)))
+ return (0);
+ else
+ return (ENXIO);
+ }
+
+#if !defined(__i386__)
+ /* This platform does not support acpi suspend/resume. */
+ return (EOPNOTSUPP);
+#endif
+
+ /* If a suspend request is already in progress, just return. */
+ ACPI_LOCK(acpi);
+ if (sc->acpi_next_sstate != 0) {
+ ACPI_UNLOCK(acpi);
+ return (0);
+ }
+
+ /* Record the pending state and notify all apm devices. */
+ sc->acpi_next_sstate = state;
+#if 0
+ STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) {
+ clone->notify_status = APM_EV_NONE;
+ if ((clone->flags & ACPI_EVF_DEVD) == 0) {
+ selwakeuppri(&clone->sel_read, PZERO);
+ KNOTE_UNLOCKED(&clone->sel_read.si_note, 0);
+ }
+ }
+#endif
+
+ /* If devd(8) is not running, immediately enter the sleep state. */
+ if (devctl_process_running() == FALSE) {
+ ACPI_UNLOCK(acpi);
+ if (ACPI_SUCCESS(acpi_EnterSleepState(sc, sc->acpi_next_sstate))) {
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+ }
+
+ /* Now notify devd(8) also. */
+ acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, state);
+
+ /*
+ * Set a timeout to fire if userland doesn't ack the suspend request
+ * in time. This way we still eventually go to sleep if we were
+ * overheating or running low on battery, even if userland is hung.
+ * We cancel this timeout once all userland acks are in or the
+ * suspend request is aborted.
+ */
+ callout_reset(&sc->susp_force_to, 10 * hz, acpi_sleep_force, sc);
+ ACPI_UNLOCK(acpi);
+ return (0);
}
/*
- * Set interrupt model.
+ * Acknowledge (or reject) a pending sleep state. The caller has
+ * prepared for suspend and is now ready for it to proceed. If the
+ * error argument is non-zero, it indicates suspend should be cancelled
+ * and gives an errno value describing why. Once all votes are in,
+ * we suspend the system.
*/
-ACPI_STATUS
-acpi_SetIntrModel(int model)
+int
+acpi_AckSleepState(struct apm_clone_data *clone, int error)
{
- return (acpi_SetInteger(ACPI_ROOT_OBJECT, "_PIC", model));
-}
+ struct acpi_softc *sc;
+ int ret, sleeping;
-#define ACPI_MINIMUM_AWAKETIME 5
+#if !defined(__i386__)
+ /* This platform does not support acpi suspend/resume. */
+ return (EOPNOTSUPP);
+#endif
+
+ /* If no pending sleep state, return an error. */
+ ACPI_LOCK(acpi);
+ sc = clone->acpi_sc;
+ if (sc->acpi_next_sstate == 0) {
+ ACPI_UNLOCK(acpi);
+ return (ENXIO);
+ }
+
+ /* Caller wants to abort suspend process. */
+ if (error) {
+ sc->acpi_next_sstate = 0;
+ callout_stop(&sc->susp_force_to);
+ kprintf("acpi: listener on %s cancelled the pending suspend\n",
+ devtoname(clone->cdev));
+ ACPI_UNLOCK(acpi);
+ return (0);
+ }
+
+ /*
+ * Mark this device as acking the suspend request. Then, walk through
+ * all devices, seeing if they agree yet. We only count devices that
+ * are writable since read-only devices couldn't ack the request.
+ */
+ clone->notify_status = APM_EV_ACKED;
+ sleeping = TRUE;
+ STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) {
+ if ((clone->flags & ACPI_EVF_WRITE) != 0 &&
+ clone->notify_status != APM_EV_ACKED) {
+ sleeping = FALSE;
+ break;
+ }
+ }
+
+ /* If all devices have voted "yes", we will suspend now. */
+ if (sleeping)
+ callout_stop(&sc->susp_force_to);
+ ACPI_UNLOCK(acpi);
+ ret = 0;
+ if (sleeping) {
+ if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate)))
+ ret = ENODEV;
+ }
+
+ return (ret);
+}
static void
acpi_sleep_enable(void *arg)
{
+
((struct acpi_softc *)arg)->acpi_sleep_disabled = 0;
}
+enum acpi_sleep_state {
+ ACPI_SS_NONE,
+ ACPI_SS_GPE_SET,
+ ACPI_SS_DEV_SUSPEND,
+ ACPI_SS_SLP_PREP,
+ ACPI_SS_SLEPT,
+};
+
/*
- * Set the system sleep state
+ * Enter the desired system sleep state.
*
* Currently we support S1-S5 but S4 is only S4BIOS
*/
-ACPI_STATUS
-acpi_SetSleepState(struct acpi_softc *sc, int state)
+static ACPI_STATUS
+acpi_EnterSleepState(struct acpi_softc *sc, int state)
{
- ACPI_STATUS status = AE_OK;
+ ACPI_STATUS status;
UINT8 TypeA;
UINT8 TypeB;
+ enum acpi_sleep_state slp_state;
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
- ACPI_ASSERTLOCK;
-
- /* Avoid reentry if already attempting to suspend. */
- if (sc->acpi_sstate != ACPI_STATE_S0)
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- /* We recently woke up so don't suspend again for a while. */
- if (sc->acpi_sleep_disabled)
- return_ACPI_STATUS (AE_OK);
+ /* Re-entry once we're suspending is not allowed. */
+ status = AE_OK;
+ ACPI_LOCK(acpi);
+ if (sc->acpi_sleep_disabled) {
+ ACPI_UNLOCK(acpi);
+ kprintf("acpi: suspend request ignored (not ready yet)\n");
+ return (AE_ERROR);
+ }
+ sc->acpi_sleep_disabled = 1;
+ ACPI_UNLOCK(acpi);
+ /*
+ * Be sure to hold Giant across DEVICE_SUSPEND/RESUME since non-MPSAFE
+ * drivers need this.
+ */
+ //get_mplock();
+ slp_state = ACPI_SS_NONE;
switch (state) {
case ACPI_STATE_S1:
case ACPI_STATE_S2:
case ACPI_STATE_S3:
case ACPI_STATE_S4:
- status = AcpiGetSleepTypeData((UINT8)state, &TypeA, &TypeB);
+ status = AcpiGetSleepTypeData(state, &TypeA, &TypeB);
if (status == AE_NOT_FOUND) {
device_printf(sc->acpi_dev,
"Sleep state S%d not supported by BIOS\n", state);
}
sc->acpi_sstate = state;
- sc->acpi_sleep_disabled = 1;
- /* Disable all wake GPEs not appropriate for this state. */
- acpi_wake_limit_walk(state);
+ /* Enable any GPEs as appropriate and requested by the user. */
+ acpi_wake_prep_walk(state);
+ slp_state = ACPI_SS_GPE_SET;
- /* Inform all devices that we are going to sleep. */
+ /*
+ * Inform all devices that we are going to sleep. If at least one
+ * device fails, DEVICE_SUSPEND() automatically resumes the tree.
+ *
+ * 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.
+ */
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);
+ device_printf(sc->acpi_dev, "device_suspend failed\n");
+ break;
}
+ slp_state = ACPI_SS_DEV_SUSPEND;
+
+ /* If testing device suspend only, back out of everything here. */
+ if (acpi_susp_bounce)
+ break;
status = AcpiEnterSleepStatePrep(state);
if (ACPI_FAILURE(status)) {
AcpiFormatException(status));
break;
}
+ slp_state = ACPI_SS_SLP_PREP;
if (sc->acpi_sleep_delay > 0)
DELAY(sc->acpi_sleep_delay * 1000000);
if (state != ACPI_STATE_S1) {
acpi_sleep_machdep(sc, state);
-#if 0
- /* AcpiEnterSleepState() may be incomplete, unlock if locked. */
- AcpiOsReleaseLock(AcpiGbl_HardwareLock, 1);
-#endif
+
/* Re-enable ACPI hardware on wakeup from sleep state 4. */
if (state == ACPI_STATE_S4)
AcpiEnable();
} else {
- status = AcpiEnterSleepState((UINT8)state);
+ ACPI_DISABLE_IRQS();
+ status = AcpiEnterSleepState(state);
if (ACPI_FAILURE(status)) {
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);
+ slp_state = ACPI_SS_SLEPT;
break;
case ACPI_STATE_S5:
/*
break;
}
- /* Disable a second sleep request for a short period */
- if (sc->acpi_sleep_disabled)
- callout_reset(&sc->acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME,
- acpi_sleep_enable, sc);
+ /*
+ * Back out state according to how far along we got in the suspend
+ * process. This handles both the error and success cases.
+ */
+ sc->acpi_next_sstate = 0;
+ if (slp_state >= ACPI_SS_GPE_SET) {
+ acpi_wake_prep_walk(state);
+ sc->acpi_sstate = ACPI_STATE_S0;
+ }
+ if (slp_state >= ACPI_SS_SLP_PREP)
+ AcpiLeaveSleepState(state);
+ if (slp_state >= ACPI_SS_DEV_SUSPEND)
+ DEVICE_RESUME(root_bus);
+ if (slp_state >= ACPI_SS_SLEPT)
+ acpi_enable_fixed_events(sc);
+
+ /* Allow another sleep request after a while. */
+ /* XXX: needs timeout */
+ if (state != ACPI_STATE_S5)
+ acpi_sleep_enable(sc);
+ /* Run /etc/rc.resume after we are back. */
+ acpi_UserNotify("Resume", ACPI_ROOT_OBJECT, state);
+
+ //rel_mplock();
return_ACPI_STATUS (status);
}
{
struct acpi_prw_data prw;
- /* Check that the device can wake the system. */
- if ((device_get_flags(dev) & ACPI_FLAG_WAKE_CAPABLE) == 0)
- return (ENXIO);
-
/* Evaluate _PRW to find the GPE. */
if (acpi_parse_prw(acpi_get_handle(dev), &prw) != 0)
return (ENXIO);
acpi_wake_set_enable(device_t dev, int enable)
{
struct acpi_prw_data prw;
- ACPI_HANDLE handle;
ACPI_STATUS status;
int flags;
- /* Make sure the device supports waking the system. */
- flags = device_get_flags(dev);
- handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
-
- /* Evaluate _PRW to find the GPE. */
- if (acpi_parse_prw(handle, &prw) != 0)
+ /* Make sure the device supports waking the system and get the GPE. */
+ if (acpi_parse_prw(acpi_get_handle(dev), &prw) != 0)
return (ENXIO);
+ flags = acpi_get_flags(dev);
if (enable) {
status = AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (ACPI_FAILURE(status)) {
device_printf(dev, "enable wake failed\n");
return (ENXIO);
}
- device_set_flags(dev, flags | ACPI_FLAG_WAKE_ENABLED);
+ acpi_set_flags(dev, flags | ACPI_FLAG_WAKE_ENABLED);
} else {
status = AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (ACPI_FAILURE(status)) {
device_printf(dev, "disable wake failed\n");
return (ENXIO);
}
- device_set_flags(dev, flags & ~ACPI_FLAG_WAKE_ENABLED);
+ acpi_set_flags(dev, flags & ~ACPI_FLAG_WAKE_ENABLED);
}
return (0);
}
-/* Configure a device's GPE appropriately for the new sleep state. */
-int
-acpi_wake_sleep_prep(device_t dev, int sstate)
+static int
+acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate)
{
struct acpi_prw_data prw;
- ACPI_HANDLE handle;
- int flags;
-
- /* Check that this is an ACPI device and get its GPE. */
- flags = device_get_flags(dev);
- handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
+ device_t dev;
- /* Evaluate _PRW to find the GPE. */
+ /* Check that this is a wake-capable device and get its GPE. */
if (acpi_parse_prw(handle, &prw) != 0)
return (ENXIO);
+ dev = acpi_get_device(handle);
/*
- * TBD: All Power Resources referenced by elements 2 through N
- * of the _PRW object are put into the ON state.
- */
-
- /*
- * If the user requested that this device wake the system and the next
- * sleep state is valid for this GPE, enable it and the device's wake
- * capability. The sleep state must be less than (i.e., higher power)
- * or equal to the value specified by _PRW. Return early, leaving
- * the appropriate power resources enabled.
+ * The destination sleep state must be less than (i.e., higher power)
+ * or equal to the value specified by _PRW. If this GPE cannot be
+ * enabled for the next sleep state, then disable it. If it can and
+ * the user requested it be enabled, turn on any required power resources
+ * and set _PSW.
*/
- if ((flags & ACPI_FLAG_WAKE_ENABLED) != 0 &&
- sstate <= prw.lowest_wake) {
+ if (sstate > prw.lowest_wake) {
+ AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (bootverbose)
- device_printf(dev, "wake_prep enabled gpe %#x for state %d\n",
- prw.gpe_bit, sstate);
- AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ device_printf(dev, "wake_prep disabled wake for %s (S%d)\n",
+ acpi_name(handle), sstate);
+ } else if (dev && (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) != 0) {
+ acpi_pwr_wake_enable(handle, 1);
acpi_SetInteger(handle, "_PSW", 1);
- return (0);
+ if (bootverbose)
+ device_printf(dev, "wake_prep enabled for %s (S%d)\n",
+ acpi_name(handle), sstate);
}
- /*
- * If the device wake was disabled or this sleep state is too low for
- * this device, disable its wake capability and GPE.
- */
- AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
- acpi_SetInteger(handle, "_PSW", 0);
- if (bootverbose)
- device_printf(dev, "wake_prep disabled gpe %#x for state %d\n",
- prw.gpe_bit, sstate);
-
- /*
- * TBD: All Power Resources referenced by elements 2 through N
- * of the _PRW object are put into the OFF state.
- */
-
return (0);
}
-/* Re-enable GPEs after wake. */
-int
-acpi_wake_run_prep(device_t dev)
+static int
+acpi_wake_run_prep(ACPI_HANDLE handle, int sstate)
{
struct acpi_prw_data prw;
- ACPI_HANDLE handle;
- int flags;
-
- /* Check that this is an ACPI device and get its GPE. */
- flags = device_get_flags(dev);
- handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
+ device_t dev;
- /* Evaluate _PRW to find the GPE. */
+ /*
+ * Check that this is a wake-capable device and get its GPE. Return
+ * now if the user didn't enable this device for wake.
+ */
if (acpi_parse_prw(handle, &prw) != 0)
return (ENXIO);
+ dev = acpi_get_device(handle);
+ if (dev == NULL || (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) == 0)
+ return (0);
/*
- * TBD: Be sure all Power Resources referenced by elements 2 through N
- * of the _PRW object are in the ON state.
+ * If this GPE couldn't be enabled for the previous sleep state, it was
+ * disabled before going to sleep so re-enable it. If it was enabled,
+ * clear _PSW and turn off any power resources it used.
*/
-
- /* Disable wake capability and if the user requested, enable the GPE. */
- acpi_SetInteger(handle, "_PSW", 0);
- if ((flags & ACPI_FLAG_WAKE_ENABLED) != 0)
+ if (sstate > prw.lowest_wake) {
AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ if (bootverbose)
+ device_printf(dev, "run_prep re-enabled %s\n", acpi_name(handle));
+ } else {
+ acpi_SetInteger(handle, "_PSW", 0);
+ acpi_pwr_wake_enable(handle, 0);
+ if (bootverbose)
+ device_printf(dev, "run_prep cleaned up for %s\n",
+ acpi_name(handle));
+ }
+
return (0);
}
static ACPI_STATUS
-acpi_wake_limit(ACPI_HANDLE h, UINT32 level, void *context, void **status)
+acpi_wake_prep(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
{
- struct acpi_prw_data prw;
- int *sstate;
-
- /* It's ok not to have _PRW if the device can't wake the system. */
- if (acpi_parse_prw(h, &prw) != 0)
- return (AE_OK);
-
- sstate = (int *)context;
- if (*sstate > prw.lowest_wake)
- AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ int sstate;
+ /* If suspending, run the sleep prep function, otherwise wake. */
+ sstate = *(int *)context;
+ if (AcpiGbl_SystemAwakeAndRunning)
+ acpi_wake_sleep_prep(handle, sstate);
+ else
+ acpi_wake_run_prep(handle, sstate);
return (AE_OK);
}
-/* Walk all system devices, disabling them if necessary for sstate. */
+/* Walk the tree rooted at acpi0 to prep devices for suspend/resume. */
static int
-acpi_wake_limit_walk(int sstate)
+acpi_wake_prep_walk(int sstate)
{
ACPI_HANDLE sb_handle;
if (ACPI_SUCCESS(AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle)))
- AcpiWalkNamespace(ACPI_TYPE_ANY, sb_handle, 100,
- acpi_wake_limit, &sstate, NULL);
+ AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 100,
+ acpi_wake_prep, &sstate, NULL);
return (0);
}
int error, i, numdevs;
device_t *devlist;
device_t child;
+ ACPI_STATUS status;
error = device_get_children(dev, &devlist, &numdevs);
- if (error != 0 || numdevs == 0)
+ if (error != 0 || numdevs == 0) {
+ if (numdevs == 0)
+ kfree(devlist, M_TEMP);
return (error);
+ }
+#ifdef notye
for (i = 0; i < numdevs; i++) {
child = devlist[i];
+ acpi_wake_sysctl_walk(child);
if (!device_is_attached(child))
continue;
- if (device_get_flags(child) & ACPI_FLAG_WAKE_CAPABLE) {
-#ifdef dfly_notyet
+ status = AcpiEvaluateObject(acpi_get_handle(child), "_PRW", NULL, NULL);
+ if (ACPI_SUCCESS(status)) {
SYSCTL_ADD_PROC(device_get_sysctl_ctx(child),
SYSCTL_CHILDREN(device_get_sysctl_tree(child)), OID_AUTO,
"wake", CTLTYPE_INT | CTLFLAG_RW, child, 0,
acpi_wake_set_sysctl, "I", "Device set to wake the system");
-#endif /* dfly_notyet */
}
- acpi_wake_sysctl_walk(child);
}
+#endif
kfree(devlist, M_TEMP);
return (0);
}
-#ifdef dfly_notyet
/* Enable or disable wake from userland. */
static int
acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS)
device_t dev;
dev = (device_t)arg1;
- enable = (device_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) ? 1 : 0;
+ enable = (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) ? 1 : 0;
error = sysctl_handle_int(oidp, &enable, 0, req);
if (error != 0 || req->newptr == NULL)
return (acpi_wake_set_enable(dev, enable));
}
-#endif /* dfly_notyet */
/* Parse a device's _PRW into a structure. */
-static int
+int
acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw)
{
ACPI_STATUS status;
ACPI_BUFFER prw_buffer;
ACPI_OBJECT *res, *res2;
- int error;
+ int error, i, power_count;
if (h == NULL || prw == NULL)
return (EINVAL);
goto out;
}
- /* XXX No power resource handling yet. */
- prw->power_res = NULL;
+ /* Elements 2 to N of the _PRW object are power resources. */
+ power_count = res->Package.Count - 2;
+ if (power_count > ACPI_PRW_MAX_POWERRES) {
+ kprintf("ACPI device %s has too many power resources\n", acpi_name(h));
+ power_count = 0;
+ }
+ prw->power_res_count = power_count;
+ for (i = 0; i < power_count; i++)
+ prw->power_res[i] = res->Package.Elements[i];
out:
if (prw_buffer.Pointer != NULL)
}
/*
- * 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
*/
static void
acpi_system_eventhandler_sleep(void *arg, int state)
{
- ACPI_LOCK_DECL;
+ int ret;
+
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;
+ /* Check if button action is disabled. */
+ if (state == ACPI_S_STATES_MAX + 1)
+ return;
+
+ /* Request that the system prepare to enter the given suspend state. */
+ ret = acpi_ReqSleepState((struct acpi_softc *)arg, state);
+ if (ret != 0)
+ kprintf("acpi: request to enter state S%d failed (err %d)\n",
+ state, ret);
+
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_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
- ACPI_LOCK;
- ACPI_UNLOCK;
+ /* Currently, nothing to do for wakeup. */
return_VOID;
}
}
/*
- * XXX This is kinda ugly, and should not be here.
+ * XXX This static buffer is suboptimal. There is no locking so only
+ * use this for single-threaded callers.
*/
-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];
+ ACPI_BUFFER buf;
+ static char data[256];
- if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf.buffer)))
- return (buf.buffer.Pointer);
+ buf.Length = sizeof(data);
+ buf.Pointer = data;
- return ("(unknown path)");
+ if (handle && ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf)))
+ return (data);
+ return ("(unknown)");
}
/*
np = acpi_name(handle);
if (*np == '\\')
np++;
- if ((env = kgetenv("debug.acpi.avoid.paths")) == NULL &&
- (env = kgetenv("debug.acpi.avoid")) == NULL)
+ if ((env = kgetenv("debug.acpi.avoid")) == NULL)
return (0);
/* Scan the avoid list checking for a match */
cp = env;
for (;;) {
- while ((*cp != 0) && isspace(*cp))
+ while (*cp != 0 && isspace(*cp))
cp++;
if (*cp == 0)
break;
len = 0;
- while ((cp[len] != 0) && !isspace(cp[len]))
+ while (cp[len] != 0 && !isspace(cp[len]))
len++;
if (!strncmp(cp, np, len)) {
kfreeenv(env);
}
/*
- * Debugging/bug-avoidance. Disable ACPI subsystem components. Note that
- * some components may be disabled by default and can only be enabled
- * via acpi_enabled() (debug.acpi.enabled).
+ * Debugging/bug-avoidance. Disable ACPI subsystem components.
*/
int
acpi_disabled(char *subsys)
int
acpi_enabled(char *subsys)
{
- char *cp, *env;
- int len;
+ char *cp, *env;
+ int len;
if ((env = kgetenv("debug.acpi.enabled")) == NULL)
- return (0);
+ return (0);
if (strcmp(env, "all") == 0) {
- kfreeenv(env);
- return (1);
+ kfreeenv(env);
+ return (1);
}
/* Scan the enable 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) == 0) {
- kfreeenv(env);
- return (1);
- }
- cp += len;
+ 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) == 0) {
+ kfreeenv(env);
+ return (1);
+ }
+ cp += len;
}
kfreeenv(env);
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, acpi_ioctl_fn fn, void *arg)
{
struct acpi_ioctl_hook *hp;
- hp = kmalloc(sizeof(*hp), M_ACPIDEV, M_INTWAIT);
+ if ((hp = kmalloc(sizeof(*hp), M_ACPIDEV, M_NOWAIT)) == NULL)
+ return (ENOMEM);
hp->cmd = cmd;
hp->fn = fn;
hp->arg = arg;
+
+ ACPI_LOCK(acpi);
if (acpi_ioctl_hooks_initted == 0) {
TAILQ_INIT(&acpi_ioctl_hooks);
acpi_ioctl_hooks_initted = 1;
}
TAILQ_INSERT_TAIL(&acpi_ioctl_hooks, hp, link);
+ ACPI_UNLOCK(acpi);
+
return (0);
}
-/*
- * Deregister an ioctl handler.
- */
-void
+void
acpi_deregister_ioctl(u_long cmd, acpi_ioctl_fn fn)
{
struct acpi_ioctl_hook *hp;
+ ACPI_LOCK(acpi);
TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link)
- if ((hp->cmd == cmd) && (hp->fn == fn))
+ if (hp->cmd == cmd && hp->fn == fn)
break;
if (hp != NULL) {
TAILQ_REMOVE(&acpi_ioctl_hooks, hp, link);
kfree(hp, M_ACPIDEV);
}
+ ACPI_UNLOCK(acpi);
}
static int
{
struct acpi_softc *sc;
struct acpi_ioctl_hook *hp;
- int error, xerror, state;
- ACPI_LOCK_DECL;
-
- ACPI_LOCK;
+ int error, state;
- error = state = 0;
+ error = 0;
+ hp = NULL;
sc = ap->a_head.a_dev->si_drv1;
/*
* Scan the list of registered ioctls, looking for handlers.
*/
- if (acpi_ioctl_hooks_initted) {
+ ACPI_LOCK(acpi);
+ if (acpi_ioctl_hooks_initted)
TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link) {
- if (hp->cmd == ap->a_cmd) {
- xerror = hp->fn(ap->a_cmd, ap->a_data, hp->arg);
- if (xerror != 0)
- error = xerror;
- goto out;
- }
+ if (hp->cmd == ap->a_cmd)
+ break;
}
- }
+ ACPI_UNLOCK(acpi);
+ if (hp)
+ return (hp->fn(ap->a_cmd, ap->a_data, hp->arg));
/*
* Core ioctls are not permitted for non-writable user.
* Currently, other ioctls just fetch information.
* Not changing system behavior.
*/
- if((ap->a_fflag & FWRITE) == 0) {
- error = EPERM;
- goto out;
- }
+ if ((ap->a_fflag & FWRITE) == 0)
+ return (EPERM);
/* Core system ioctls. */
switch (ap->a_cmd) {
- case ACPIIO_ENABLE:
- if (ACPI_FAILURE(acpi_Enable(sc)))
+ case ACPIIO_REQSLPSTATE:
+ state = *(int *)ap->a_data;
+ if (state != ACPI_STATE_S5)
+ error = acpi_ReqSleepState(sc, state);
+ else {
+ kprintf("power off via acpi ioctl not supported\n");
error = ENXIO;
+ }
break;
- case ACPIIO_DISABLE:
- if (ACPI_FAILURE(acpi_Disable(sc)))
- error = ENXIO;
+ case ACPIIO_ACKSLPSTATE:
+ error = *(int *)ap->a_data;
+ error = acpi_AckSleepState(sc->acpi_clone, error);
break;
- case ACPIIO_SETSLPSTATE:
- if (!sc->acpi_enabled) {
- error = ENXIO;
- break;
- }
+ case ACPIIO_SETSLPSTATE: /* DEPRECATED */
+ error = EINVAL;
state = *(int *)ap->a_data;
- if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX) {
- if (ACPI_FAILURE(acpi_SetSleepState(sc, state)))
- error = EINVAL;
- } else {
- error = EINVAL;
- }
+ if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX)
+ if (ACPI_SUCCESS(acpi_SetSleepState(sc, state)))
+ error = 0;
break;
default:
- if (error == 0)
- error = EINVAL;
+ error = ENXIO;
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;
+ struct sbuf sb;
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))) {
- ksprintf(sleep_state, "S%d ", state);
- strcat(buf, sleep_state);
- }
- }
- error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ sbuf_new(&sb, NULL, 32, SBUF_AUTOEXTEND);
+ for (state = ACPI_STATE_S1; state < ACPI_S_STATES_MAX + 1; state++)
+ if (ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB)))
+ sbuf_printf(&sb, "S%d ", state);
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+ sbuf_delete(&sb);
return (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]));
- }
+ if (old_state > ACPI_S_STATES_MAX + 1)
+ strlcpy(sleep_state, "unknown", sizeof(sleep_state));
+ else
+ strlcpy(sleep_state, sleep_state_names[old_state], sizeof(sleep_state));
error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req);
if (error == 0 && req->newptr != NULL) {
new_state = ACPI_STATE_S0;
- for (; new_state <= ACPI_S_STATES_MAX + 1; new_state++) {
- if (strncmp(sleep_state, sleep_state_names[new_state],
- sizeof(sleep_state)) == 0)
+ for (; new_state <= ACPI_S_STATES_MAX + 1; new_state++)
+ if (strcmp(sleep_state, sleep_state_names[new_state]) == 0)
break;
- }
if (new_state <= ACPI_S_STATES_MAX + 1) {
if (new_state != old_state)
*(u_int *)oidp->oid_arg1 = new_state;
- } else {
+ } else
error = EINVAL;
- }
}
return (error);
};
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},
}
}
-/*
- * Warning: also called in early boot, before any allocators
- * are working.
- */
static void
acpi_set_debugging(void *junk)
{
- char *layer, *level;
+ char *layer, *level;
if (cold) {
AcpiDbgLayer = 0;
}
kprintf("\n");
}
-SYSINIT(acpi_debugging, SI_BOOT1_TUNABLES, SI_ORDER_ANY,
- acpi_set_debugging, NULL);
+
+SYSINIT(acpi_debugging, SI_BOOT1_TUNABLES, SI_ORDER_ANY, acpi_set_debugging,
+ NULL);
static int
acpi_debug_sysctl(SYSCTL_HANDLER_ARGS)
}
/* Get old values if this is a get request. */
+ ACPI_SERIAL_BEGIN(acpi);
if (*dbg == 0) {
sbuf_cpy(&sb, "NONE");
} else if (req->newptr == NULL) {
/* If the user is setting a string, parse it. */
if (error == 0 && req->newptr != NULL) {
*dbg = 0;
- ksetenv(oidp->oid_arg1, req->newptr);
+ setenv((char *)oidp->oid_arg1, (char *)req->newptr);
acpi_set_debugging(NULL);
}
+ ACPI_SERIAL_END(acpi);
return (error);
}
+
SYSCTL_PROC(_debug_acpi, OID_AUTO, layer, CTLFLAG_RW | CTLTYPE_STRING,
"debug.acpi.layer", 0, acpi_debug_sysctl, "A", "");
SYSCTL_PROC(_debug_acpi, OID_AUTO, level, CTLFLAG_RW | CTLTYPE_STRING,
"debug.acpi.level", 0, acpi_debug_sysctl, "A", "");
-#endif
+#endif /* ACPI_DEBUG */
static int
acpi_pm_func(u_long cmd, void *arg, ...)
va_start(ap, arg);
state = va_arg(ap, int);
- va_end(ap);
+ va_end(ap);
switch (state) {
case POWER_SLEEP_STATE_STANDBY:
goto out;
}
- acpi_SetSleepState(sc, acpi_state);
+ if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state)))
+ error = ENXIO;
break;
default:
error = EINVAL;
* 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: src/sys/dev/acpica/acpi_acad.c,v 1.27 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_acad.c,v 1.8 2007/10/23 03:04:48 y0netan1 Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_acad.c,v 1.39 2009/06/05 18:44:36 jkim Exp
*/
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
+
#include <sys/rman.h>
+#include <sys/ioccom.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/power.h>
#include "acpi.h"
+
#include <dev/acpica5/acpivar.h>
#include <dev/acpica5/acpiio.h>
+#include <bus/isa/isavar.h>
+#include <bus/isa/pnpvar.h>
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_AC_ADAPTER
/* Number of times to retry initialization before giving up. */
#define ACPI_ACAD_RETRY_MAX 6
-#define ACPI_DEVICE_CHECK_PNP 0x00
-#define ACPI_DEVICE_CHECK_EXISTENCE 0x01
#define ACPI_POWERSOURCE_STAT_CHANGE 0x80
struct acpi_acad_softc {
int status;
- int initializing;
};
static void acpi_acad_get_status(void *);
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);
+static void acpi_acad_ac_only(void *arg);
static device_method_t acpi_acad_methods[] = {
/* Device interface */
DRIVER_MODULE(acpi_acad, acpi, acpi_acad_driver, acpi_acad_devclass, 0, 0);
MODULE_DEPEND(acpi_acad, acpi, 1, 1, 1);
+ACPI_SERIAL_DECL(acad, "ACPI AC adapter");
+
+SYSINIT(acad, SI_SUB_KTHREAD_IDLE, SI_ORDER_FIRST, acpi_acad_ac_only, NULL);
+
static void
acpi_acad_get_status(void *context)
{
dev = context;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
- if (ACPI_FAILURE(acpi_GetInteger(h, "_PSR", &newstatus))) {
- sc->status = -1;
- return;
- }
+ newstatus = -1;
+ acpi_GetInteger(h, "_PSR", &newstatus);
- if (sc->status != newstatus) {
+ /* If status is valid and has changed, notify the system. */
+ ACPI_SERIAL_BEGIN(acad);
+ if (newstatus != -1 && 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);
+ power_profile_set_state(newstatus ? POWER_PROFILE_PERFORMANCE :
+ POWER_PROFILE_ECONOMY);
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
- "%s Line\n", sc->status ? "On" : "Off");
-
- acpi_UserNotify("ACAD", h, sc->status);
+ "%s Line\n", newstatus ? "On" : "Off");
+ acpi_UserNotify("ACAD", h, newstatus);
}
+ ACPI_SERIAL_END(acad);
}
static void
dev = (device_t)context;
switch (notify) {
- case ACPI_DEVICE_CHECK_PNP:
- case ACPI_DEVICE_CHECK_EXISTENCE:
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
case ACPI_POWERSOURCE_STAT_CHANGE:
/* Temporarily. It is better to notify policy manager */
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_acad_get_status, context);
static int
acpi_acad_probe(device_t dev)
{
- if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("acad") &&
- acpi_MatchHid(acpi_get_handle(dev), "ACPI0003")) {
- device_set_desc(dev, "AC Adapter");
- return (0);
- }
- return (ENXIO);
+ static char *acad_ids[] = { "ACPI0003", NULL };
+
+ if (acpi_disabled("acad") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, acad_ids) == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, "AC Adapter");
+ return (0);
}
static int
int error;
sc = device_get_softc(dev);
- if (sc == NULL)
- return (ENXIO);
handle = acpi_get_handle(dev);
error = acpi_register_ioctl(ACPIIO_ACAD_GET_STATUS, acpi_acad_ioctl, dev);
/* Get initial status after whole system is up. */
sc->status = -1;
- sc->initializing = 0;
/*
- * Also install a system notify handler even though this is not
- * required by the specification. The Casio FIVA needs this.
+ * Install both system and device notify handlers since the Casio
+ * FIVA needs them.
*/
- AcpiInstallNotifyHandler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_acad_notify_handler, dev);
- AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
+ AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
acpi_acad_notify_handler, dev);
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_acad_init_acline, dev);
dev = (device_t)arg;
sc = device_get_softc(dev);
- if (sc == NULL)
- return (ENXIO);
/*
* No security check required: information retrieval only. If
{
struct acpi_acad_softc *sc;
device_t dev;
- int retry, status;
+ int retry;
dev = (device_t)arg;
sc = device_get_softc(dev);
- 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++) {
acpi_acad_get_status(dev);
- if (status != sc->status)
+ if (sc->status != -1)
break;
- AcpiOsSleep(10);
+ AcpiOsSleep(10000);
}
- sc->initializing = 0;
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"acline initialization done, tried %d times\n", retry + 1);
}
/*
+ * If no AC line devices detected after boot, create an "online" event
+ * so that userland code can adjust power settings accordingly. The default
+ * power profile is "performance" so we don't need to repeat that here.
+ */
+static void
+acpi_acad_ac_only(void __unused *arg)
+{
+
+ if (devclass_get_count(acpi_acad_devclass) == 0)
+ acpi_UserNotify("ACAD", ACPI_ROOT_OBJECT, 1);
+}
+
+/*
* Public interfaces.
*/
int
if (dev == NULL)
return (ENXIO);
sc = device_get_softc(dev);
- if (sc == NULL)
- return (ENXIO);
acpi_acad_get_status(dev);
*status = sc->status;
* 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: src/sys/dev/acpi_support/acpi_asus.c,v 1.24.2.4.2.1 2008/10/02 02:57:24 kensmith Exp $
+ * $FreeBSD: src/sys/dev/acpi_support/acpi_asus.c,v 1.42 2009/06/05 18:44:36 jkim Exp
*/
#include <sys/cdefs.h>
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
-#include <sys/bus.h>
-#include <machine/cpufunc.h>
#include <sys/module.h>
-#include <sys/sensors.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/lock.h>
+#include <sys/bus.h>
#include <sys/sbuf.h>
-#include <sys/thread2.h>
-#include <machine/clock.h>
#include "acpi.h"
#include "accommon.h"
#include "acpivar.h"
-#include "acpi_if.h"
+
+#if defined(__FreeBSD__)
+#include <dev/led/led.h>
+#endif
/* Methods */
#define ACPI_ASUS_METHOD_BRN 1
#define ACPI_ASUS_METHOD_DISP 2
#define ACPI_ASUS_METHOD_LCD 3
+#define ACPI_ASUS_METHOD_CAMERA 4
+#define ACPI_ASUS_METHOD_CARDRD 5
+#define ACPI_ASUS_METHOD_WLAN 6
#define _COMPONENT ACPI_OEM
ACPI_MODULE_NAME("ASUS")
struct acpi_asus_model {
char *name;
-
char *bled_set;
+ char *dled_set;
+ char *gled_set;
char *mled_set;
char *tled_set;
char *wled_set;
char *disp_get;
char *disp_set;
+
+ char *cam_get;
+ char *cam_set;
+
+ char *crd_get;
+ char *crd_set;
+
+ char *wlan_get;
+ char *wlan_set;
+
+ void (*n_func)(ACPI_HANDLE, UINT32, void *);
+
+ char *lcdd;
+ void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *);
};
struct acpi_asus_led {
int state;
enum {
ACPI_ASUS_LED_BLED,
+ ACPI_ASUS_LED_DLED,
+ ACPI_ASUS_LED_GLED,
ACPI_ASUS_LED_MLED,
ACPI_ASUS_LED_TLED,
ACPI_ASUS_LED_WLED,
struct acpi_asus_softc {
device_t dev;
ACPI_HANDLE handle;
+ ACPI_HANDLE lcdd_handle;
struct acpi_asus_model *model;
struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
-
+#if defined(__FreeBSD__)
struct acpi_asus_led s_bled;
+ struct acpi_asus_led s_dled;
+ struct acpi_asus_led s_gled;
struct acpi_asus_led s_mled;
struct acpi_asus_led s_tled;
struct acpi_asus_led s_wled;
+#endif
int s_brn;
int s_disp;
int s_lcd;
+ int s_cam;
+ int s_crd;
+ int s_wlan;
};
+static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
+ void *context);
+
/*
* We can identify Asus laptops from the string they return
* as a result of calling the ATK0100 'INIT' method.
.disp_set = "SDSP"
},
{
+ .name = "A3E",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A3F",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .bled_set = "BLED",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\SSTE",
+ .disp_set = "SDSP"
+ },
+ {
.name = "A3N",
.mled_set = "MLED",
.bled_set = "BLED",
.wled_set = "WLED",
- .lcd_get = NULL,
+ .lcd_get = "\\BKLT",
.lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
.brn_set = "SPLV",
- .brn_get = "SDSP",
- .disp_set = "SDSP",
- .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD"
+ .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD",
+ .disp_set = "SDSP"
},
{
.name = "A4D",
.disp_set = "SDSP"
},
{
+ .name = "A8SR",
+ .bled_set = "BLED",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = NULL,
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD",
+ .disp_set = "SDSP",
+ .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD",
+ .lcdd_n_func = acpi_asus_lcdd_notify
+ },
+ {
.name = "D1x",
.mled_set = "MLED",
.lcd_get = "\\GP11",
.disp_set = "SDSP"
},
{
+ .name = "G2K",
+ .bled_set = "BLED",
+ .dled_set = "DLED",
+ .gled_set = "GLED",
+ .mled_set = "MLED",
+ .tled_set = "TLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD",
+ .disp_set = "SDSP",
+ },
+ {
.name = "L2D",
.mled_set = "MLED",
.wled_set = "WLED",
},
{
.name = "L8L"
- /* Only has hotkeys, apparantly */
+ /* Only has hotkeys, apparently */
},
{
.name = "M1A",
{ .name = NULL }
};
+static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
+
/*
* EeePC have an Asus ASUS010 gadget interface,
* but they can't be probed quite the same way as Asus laptops.
{
.name = "EEE",
.brn_get = "\\_SB.ATKD.PBLG",
- .brn_set = "\\_SB.ATKD.PBLS"
+ .brn_set = "\\_SB.ATKD.PBLS",
+ .cam_get = "\\_SB.ATKD.CAMG",
+ .cam_set = "\\_SB.ATKD.CAMS",
+ .crd_set = "\\_SB.ATKD.CRDS",
+ .crd_get = "\\_SB.ATKD.CRDG",
+ .wlan_get = "\\_SB.ATKD.WLDG",
+ .wlan_set = "\\_SB.ATKD.WLDS",
+ .n_func = acpi_asus_eeepc_notify
},
{ .name = NULL }
char *name;
char *description;
int method;
+ int flags;
} acpi_asus_sysctls[] = {
{
.name = "lcd_backlight",
.method = ACPI_ASUS_METHOD_LCD,
- .description = "state of the lcd backlight"
+ .description = "state of the lcd backlight",
+ .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
},
{
.name = "lcd_brightness",
.method = ACPI_ASUS_METHOD_BRN,
- .description = "brightness of the lcd panel"
+ .description = "brightness of the lcd panel",
+ .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
},
{
.name = "video_output",
.method = ACPI_ASUS_METHOD_DISP,
- .description = "display output state"
+ .description = "display output state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "camera",
+ .method = ACPI_ASUS_METHOD_CAMERA,
+ .description = "internal camera state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "cardreader",
+ .method = ACPI_ASUS_METHOD_CARDRD,
+ .description = "internal card reader state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "wlan",
+ .method = ACPI_ASUS_METHOD_WLAN,
+ .description = "wireless lan state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
},
{ .name = NULL }
};
+ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
+
+/* Function prototypes */
static int acpi_asus_probe(device_t dev);
static int acpi_asus_attach(device_t dev);
static int acpi_asus_detach(device_t dev);
-static struct lock asuslock;
+#if defined(__FreeBSD__)
+static void acpi_asus_led(struct acpi_asus_led *led, int state);
+static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
+#endif
static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
static device_method_t acpi_asus_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, acpi_asus_probe),
+ DEVMETHOD(device_probe, acpi_asus_probe),
DEVMETHOD(device_attach, acpi_asus_attach),
DEVMETHOD(device_detach, acpi_asus_detach),
- {0, 0}
+
+ { 0, 0 }
};
-static driver_t acpi_asus_driver = {
+static driver_t acpi_asus_driver = {
"acpi_asus",
acpi_asus_methods,
- sizeof(struct acpi_asus_softc),
+ sizeof(struct acpi_asus_softc)
};
static devclass_t acpi_asus_devclass;
-DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver,
- acpi_asus_devclass, 0, 0);
+DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
-static char *asus_ids[] = { "ATK0100", "ASUS010", NULL };
-
static int
acpi_asus_probe(device_t dev)
{
ACPI_BUFFER Buf;
ACPI_OBJECT Arg, *Obj;
ACPI_OBJECT_LIST Args;
+ static char *asus_ids[] = { "ATK0100", "ASUS010", NULL };
char *rstr;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
- if (acpi_disabled("asus")) return (ENXIO);
+ if (acpi_disabled("asus"))
+ return (ENXIO);
+ ACPI_SERIAL_INIT(asus);
rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
if (rstr == NULL) {
return (ENXIO);
}
-
sc = device_get_softc(dev);
sc->dev = dev;
sc->handle = acpi_get_handle(dev);
-
Arg.Type = ACPI_TYPE_INTEGER;
Arg.Integer.Value = 0;
ACPI_STATUS status;
ACPI_TABLE_HEADER th;
- status = AcpiGetTableHeader(ACPI_SIG_DSDT, 1, &th);
+ status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
if (ACPI_FAILURE(status)) {
device_printf(dev, "Unsupported (Samsung?) laptop\n");
AcpiOsFree(Buf.Pointer);
return (0);
}
- /* if EeePC */
+ /* EeePC */
if (strncmp("ASUS010", rstr, 7) == 0) {
sc->model = &acpi_eeepc_models[0];
device_set_desc(dev, "ASUS EeePC");
}
}
- sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
+ sb = sbuf_new_auto();
if (sb == NULL)
return (ENOMEM);
AcpiOsFree(Buf.Pointer);
return (0);
}
-
+
/*
* Some models look exactly the same as other models, but have
* their own ids. If we spot these, set them up with the same
else if (strncmp(model->name, "A2x", 3) == 0 &&
strncmp(Obj->String.Pointer, "A2", 2) == 0)
goto good;
+ else if (strncmp(model->name, "A3F", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "A6F", 3) == 0)
+ goto good;
else if (strncmp(model->name, "D1x", 3) == 0 &&
strncmp(Obj->String.Pointer, "D1", 2) == 0)
goto good;
}
static int
-acpi_asus_detach(device_t dev)
-{
- struct acpi_asus_softc *sc;
-
- ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- sc = device_get_softc(dev);
-
-
- /* Turn the lights off */
- /* We don't have LED support, sadly... :( */
-/* if (sc->model->bled_set)
- led_destroy(sc->s_bled.cdev);
-
- if (sc->model->mled_set)
- led_destroy(sc->s_mled.cdev);
-
- if (sc->model->tled_set)
- led_destroy(sc->s_tled.cdev);
-
- if (sc->model->wled_set)
- led_destroy(sc->s_wled.cdev);
-*/
- /* Remove notify handler */
- AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
- acpi_asus_notify);
-
- /* Free sysctl tree */
- sysctl_ctx_free(&sc->sysctl_ctx);
-
- return (0);
-}
-
-static int
acpi_asus_attach(device_t dev)
{
struct acpi_asus_softc *sc;
SYSCTL_ADD_PROC(&sc->sysctl_ctx,
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
acpi_asus_sysctls[i].name,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
+ acpi_asus_sysctls[i].flags,
sc, i, acpi_asus_sysctl, "I",
acpi_asus_sysctls[i].description);
}
+#if defined(__FreeBSD__)
/* Attach leds */
-
- /*Currently we don't have LEDs control. Just comment this for now...*/
-/*
if (sc->model->bled_set) {
sc->s_bled.busy = 0;
sc->s_bled.sc = sc;
sc->s_bled.type = ACPI_ASUS_LED_BLED;
sc->s_bled.cdev =
- led_create((led_t *)acpi_asus_led, &sc->s_bled, "bled");
+ led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
+ "bled", 1);
+ }
+
+ if (sc->model->dled_set) {
+ sc->s_dled.busy = 0;
+ sc->s_dled.sc = sc;
+ sc->s_dled.type = ACPI_ASUS_LED_DLED;
+ sc->s_dled.cdev =
+ led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
+ }
+
+ if (sc->model->gled_set) {
+ sc->s_gled.busy = 0;
+ sc->s_gled.sc = sc;
+ sc->s_gled.type = ACPI_ASUS_LED_GLED;
+ sc->s_gled.cdev =
+ led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
}
if (sc->model->mled_set) {
sc->s_tled.sc = sc;
sc->s_tled.type = ACPI_ASUS_LED_TLED;
sc->s_tled.cdev =
- led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled");
+ led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
+ "tled", 1);
}
if (sc->model->wled_set) {
sc->s_wled.sc = sc;
sc->s_wled.type = ACPI_ASUS_LED_WLED;
sc->s_wled.cdev =
- led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled");
+ led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
+ "wled", 1);
}
-*/
+#endif
/* Activate hotkeys */
AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
/* Handle notifies */
+ if (sc->model->n_func == NULL)
+ sc->model->n_func = acpi_asus_notify;
+
AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
- acpi_asus_notify, dev);
+ sc->model->n_func, dev);
+
+ /* Find and hook the 'LCDD' object */
+ if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
+ ACPI_STATUS res;
+
+ sc->lcdd_handle = NULL;
+ res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
+ NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
+ if (ACPI_SUCCESS(res)) {
+ AcpiInstallNotifyHandler((sc->lcdd_handle),
+ ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
+ } else {
+ kprintf("%s: unable to find LCD device '%s'\n",
+ __func__, sc->model->lcdd);
+ }
+ }
+
+ return (0);
+}
+
+static int
+acpi_asus_detach(device_t dev)
+{
+ struct acpi_asus_softc *sc;
- lockinit(&asuslock, "asus", 0, 0);
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+#if defined(__FreeBSD)
+ /* Turn the lights off */
+ if (sc->model->bled_set)
+ led_destroy(sc->s_bled.cdev);
+
+ if (sc->model->dled_set)
+ led_destroy(sc->s_dled.cdev);
+
+ if (sc->model->gled_set)
+ led_destroy(sc->s_gled.cdev);
+
+ if (sc->model->mled_set)
+ led_destroy(sc->s_mled.cdev);
+
+ if (sc->model->tled_set)
+ led_destroy(sc->s_tled.cdev);
+
+ if (sc->model->wled_set)
+ led_destroy(sc->s_wled.cdev);
+#endif
+
+ /* Remove notify handler */
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
+ acpi_asus_notify);
+
+ if (sc->lcdd_handle) {
+ KASSERT(sc->model->lcdd_n_func != NULL,
+ ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
+ AcpiRemoveNotifyHandler((sc->lcdd_handle),
+ ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
+ }
+
+ /* Free sysctl tree */
+ sysctl_ctx_free(&sc->sysctl_ctx);
return (0);
}
+#if defined(__FreeBSD__)
+static void
+acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
+{
+ struct acpi_asus_softc *sc;
+ char *method;
+ int state;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = led->sc;
+
+ switch (led->type) {
+ case ACPI_ASUS_LED_BLED:
+ method = sc->model->bled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_DLED:
+ method = sc->model->dled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_GLED:
+ method = sc->model->gled_set;
+ state = led->state + 1; /* 1: off, 2: on */
+ break;
+ case ACPI_ASUS_LED_MLED:
+ method = sc->model->mled_set;
+ state = !led->state; /* inverted */
+ break;
+ case ACPI_ASUS_LED_TLED:
+ method = sc->model->tled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_WLED:
+ method = sc->model->wled_set;
+ state = led->state;
+ break;
+ default:
+ kprintf("acpi_asus_led: invalid LED type %d\n",
+ (int)led->type);
+ return;
+ }
+
+ acpi_SetInteger(sc->handle, method, state);
+ led->busy = 0;
+}
+
+static void
+acpi_asus_led(struct acpi_asus_led *led, int state)
+{
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (led->busy)
+ return;
+
+ led->busy = 1;
+ led->state = state;
+
+ AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
+}
+#endif
+
static int
acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
{
int error = 0;
int function;
int method;
-
+
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
sc = (struct acpi_asus_softc *)oidp->oid_arg1;
function = oidp->oid_arg2;
method = acpi_asus_sysctls[function].method;
- lockmgr(&asuslock, LK_EXCLUSIVE);
+ ACPI_SERIAL_BEGIN(asus);
arg = acpi_asus_sysctl_get(sc, method);
error = sysctl_handle_int(oidp, &arg, 0, req);
error = acpi_asus_sysctl_set(sc, method, arg);
out:
- lockmgr(&asuslock, LK_RELEASE);
+ ACPI_SERIAL_END(asus);
return (error);
}
int val = 0;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
- KKASSERT(lockstatus(&asuslock, NULL)!=0); /* was ACPI_SERIAL_ASSERT(asus); may be I lost something? */
+ ACPI_SERIAL_ASSERT(asus);
switch (method) {
case ACPI_ASUS_METHOD_BRN:
case ACPI_ASUS_METHOD_LCD:
val = sc->s_lcd;
break;
+ case ACPI_ASUS_METHOD_CAMERA:
+ val = sc->s_cam;
+ break;
+ case ACPI_ASUS_METHOD_CARDRD:
+ val = sc->s_crd;
+ break;
+ case ACPI_ASUS_METHOD_WLAN:
+ val = sc->s_wlan;
+ break;
}
return (val);
static int
acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
{
- ACPI_STATUS status = AE_OK;
+ ACPI_STATUS status = AE_OK;
+ ACPI_OBJECT_LIST acpiargs;
+ ACPI_OBJECT acpiarg[1];
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(asus);
-
- KKASSERT(lockstatus(&asuslock, NULL)!=0); /* was ACPI_SERIAL_ASSERT(asus); another miss? */
+ acpiargs.Count = 1;
+ acpiargs.Pointer = acpiarg;
+ acpiarg[0].Type = ACPI_TYPE_INTEGER;
+ acpiarg[0].Integer.Value = arg;
switch (method) {
case ACPI_ASUS_METHOD_BRN:
sc->s_lcd = arg;
break;
+ case ACPI_ASUS_METHOD_CAMERA:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->cam_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_cam = arg;
+ break;
+ case ACPI_ASUS_METHOD_CARDRD:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->crd_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_crd = arg;
+ break;
+ case ACPI_ASUS_METHOD_WLAN:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->wlan_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_wlan = arg;
+ break;
}
return (0);
}
return (FALSE);
case ACPI_ASUS_METHOD_LCD:
- if (sc->model->lcd_get &&
- strncmp(sc->model->name, "L3H", 3) != 0) {
+ if (sc->model->lcd_get) {
+ if (strncmp(sc->model->name, "G2K", 3) == 0) {
+ ACPI_BUFFER Buf;
+ ACPI_OBJECT Arg, Obj;
+ ACPI_OBJECT_LIST Args;
+
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = 0x11;
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Buf.Length = sizeof(Obj);
+ Buf.Pointer = &Obj;
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->lcd_get, &Args, &Buf);
+ if (ACPI_SUCCESS(status) &&
+ Obj.Type == ACPI_TYPE_INTEGER) {
+ sc->s_lcd = Obj.Integer.Value;
+ return (TRUE);
+ }
+ } else if (strncmp(sc->model->name, "L3H", 3) == 0) {
+ ACPI_BUFFER Buf;
+ ACPI_OBJECT Arg[2], Obj;
+ ACPI_OBJECT_LIST Args;
+
+ /* L3H is a bit special */
+ Arg[0].Type = ACPI_TYPE_INTEGER;
+ Arg[0].Integer.Value = 0x02;
+ Arg[1].Type = ACPI_TYPE_INTEGER;
+ Arg[1].Integer.Value = 0x03;
+
+ Args.Count = 2;
+ Args.Pointer = Arg;
+
+ Buf.Length = sizeof(Obj);
+ Buf.Pointer = &Obj;
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->lcd_get, &Args, &Buf);
+ if (ACPI_SUCCESS(status) &&
+ Obj.Type == ACPI_TYPE_INTEGER) {
+ sc->s_lcd = Obj.Integer.Value >> 8;
+ return (TRUE);
+ }
+ } else {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->lcd_get, &sc->s_lcd);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_CAMERA:
+ if (sc->model->cam_get) {
status = acpi_GetInteger(sc->handle,
- sc->model->lcd_get, &sc->s_lcd);
+ sc->model->cam_get, &sc->s_cam);
if (ACPI_SUCCESS(status))
return (TRUE);
}
- else if (sc->model->lcd_get) {
- ACPI_BUFFER Buf;
- ACPI_OBJECT Arg[2], Obj;
- ACPI_OBJECT_LIST Args;
-
- /* L3H is a bit special */
- Arg[0].Type = ACPI_TYPE_INTEGER;
- Arg[0].Integer.Value = 0x02;
- Arg[1].Type = ACPI_TYPE_INTEGER;
- Arg[1].Integer.Value = 0x03;
-
- Args.Count = 2;
- Args.Pointer = Arg;
-
- Buf.Length = sizeof(Obj);
- Buf.Pointer = &Obj;
-
- status = AcpiEvaluateObject(sc->handle,
- sc->model->lcd_get, &Args, &Buf);
- if (ACPI_SUCCESS(status) &&
- Obj.Type == ACPI_TYPE_INTEGER) {
- sc->s_lcd = Obj.Integer.Value >> 8;
+ return (FALSE);
+ case ACPI_ASUS_METHOD_CARDRD:
+ if (sc->model->crd_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->crd_get, &sc->s_crd);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_WLAN:
+ if (sc->model->wlan_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->wlan_get, &sc->s_wlan);
+ if (ACPI_SUCCESS(status))
return (TRUE);
- }
}
return (FALSE);
}
sc = device_get_softc((device_t)context);
acpi_sc = acpi_device_get_parent_softc(sc->dev);
- lockmgr(&asuslock, LK_EXCLUSIVE);
+ ACPI_SERIAL_BEGIN(asus);
if ((notify & ~0x10) <= 15) {
sc->s_brn = notify & ~0x10;
ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
} else if (notify == 0x34) {
sc->s_lcd = 0;
ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
+ } else if (notify == 0x86) {
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
+ } else if (notify == 0x87) {
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
} else {
/* Notify devd(8) */
acpi_UserNotify("ASUS", h, notify);
}
- lockmgr(&asuslock, LK_RELEASE);
+ ACPI_SERIAL_END(asus);
+}
+
+static void
+acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc((device_t)context);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_BEGIN(asus);
+ switch (notify) {
+ case 0x87:
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
+ break;
+ case 0x86:
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
+ break;
+ }
+ ACPI_SERIAL_END(asus);
+}
+
+static void
+acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc((device_t)context);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_BEGIN(asus);
+ if ((notify & ~0x20) <= 15) {
+ sc->s_brn = notify & ~0x20;
+ ACPI_VPRINT(sc->dev, acpi_sc,
+ "Brightness increased/decreased\n");
+ } else {
+ /* Notify devd(8) */
+ acpi_UserNotify("ASUS-Eee", h, notify);
+ }
+ ACPI_SERIAL_END(asus);
}
* 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: src/sys/dev/acpica/acpi_battery.c,v 1.26 2007/11/20 18:35:36 jkim Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_battery.c,v 1.4 2008/09/29 06:59:45 hasso Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_battery.c,v 1.30 2009/08/20 19:17:53 jhb
*/
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/bus.h>
+#include <sys/ioccom.h>
#include <sys/sysctl.h>
-#include <sys/thread2.h>
#include "acpi.h"
+
#include <dev/acpica5/acpivar.h>
#include <dev/acpica5/acpiio.h>
static struct sysctl_ctx_list acpi_battery_sysctl_ctx;
static struct sysctl_oid *acpi_battery_sysctl_tree;
+ACPI_SERIAL_DECL(battery, "ACPI generic battery");
+
static void acpi_reset_battinfo(struct acpi_battinfo *info);
static void acpi_battery_clean_str(char *str, int len);
static device_t acpi_battery_find_dev(u_int logical_unit);
int error;
error = 0;
- crit_enter();
+ ACPI_SERIAL_BEGIN(battery);
if (!acpi_batteries_initted)
error = acpi_battery_init();
- crit_exit();
+ ACPI_SERIAL_END(battery);
return (error);
}
int
acpi_battery_remove(device_t dev)
{
+
return (0);
}
int
acpi_battery_get_info_expire(void)
{
+
return (acpi_battery_info_expire);
}
* is 0 (due to some error reading the battery), skip this
* conversion.
*/
- if (bif->units == ACPI_BIF_UNITS_MA && bif->dvol != 0) {
+ if (bif->units == ACPI_BIF_UNITS_MA && bif->dvol != 0 && dev == NULL) {
bst[i].rate = (bst[i].rate * bif->dvol) / 1000;
bst[i].cap = (bst[i].cap * bif->dvol) / 1000;
bif->lfcap = (bif->lfcap * bif->dvol) / 1000;
device_t dev;
int error;
+ ACPI_SERIAL_ASSERT(battery);
+
error = ENXIO;
dev = devclass_get_device(devclass_find("acpi"), 0);
if (dev == NULL)
* 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: src/sys/dev/acpica/acpi_button.c,v 1.27 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_button.c,v 1.5 2007/10/23 03:04:48 y0netan1 Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_button.c,v 1.33 2009/06/05 18:44:36 jkim Exp
*/
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include "acpi.h"
#include "accommon.h"
+
#include <dev/acpica5/acpivar.h>
/* Hooks for the ACPI CA debugging infrastructure */
static void acpi_button_notify_sleep(void *arg);
static void acpi_button_notify_wakeup(void *arg);
+static char *btn_ids[] = {
+ "PNP0C0C", "ACPI_FPB", "PNP0C0E", "ACPI_FSB",
+ NULL
+};
+
static device_method_t acpi_button_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, acpi_button_probe),
static int
acpi_button_probe(device_t dev)
{
- struct acpi_button_softc *sc;
- ACPI_HANDLE h;
- int ret = ENXIO;
+ struct acpi_button_softc *sc;
+ char *str;
+
+ if (acpi_disabled("button") ||
+ (str = ACPI_ID_PROBE(device_get_parent(dev), dev, btn_ids)) == NULL)
+ return (ENXIO);
- h = acpi_get_handle(dev);
sc = device_get_softc(dev);
- if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("button")) {
- if (acpi_MatchHid(h, "PNP0C0C")) {
- device_set_desc(dev, "Power Button");
- sc->button_type = ACPI_POWER_BUTTON;
- ret = 0;
- } else if (acpi_MatchHid(h, "ACPI_FPB")) {
- device_set_desc(dev, "Power Button (fixed)");
- sc->button_type = ACPI_POWER_BUTTON;
- sc->fixed = 1;
- ret = 0;
- } else if (acpi_MatchHid(h, "PNP0C0E")) {
- device_set_desc(dev, "Sleep Button");
- sc->button_type = ACPI_SLEEP_BUTTON;
- ret = 0;
- } else if (acpi_MatchHid(h, "ACPI_FSB")) {
- device_set_desc(dev, "Sleep Button (fixed)");
- sc->button_type = ACPI_SLEEP_BUTTON;
- sc->fixed = 1;
- ret = 0;
- }
+ if (strcmp(str, "PNP0C0C") == 0) {
+ device_set_desc(dev, "Power Button");
+ sc->button_type = ACPI_POWER_BUTTON;
+ } else if (strcmp(str, "ACPI_FPB") == 0) {
+ device_set_desc(dev, "Power Button (fixed)");
+ sc->button_type = ACPI_POWER_BUTTON;
+ sc->fixed = 1;
+ } else if (strcmp(str, "PNP0C0E") == 0) {
+ device_set_desc(dev, "Sleep Button");
+ sc->button_type = ACPI_SLEEP_BUTTON;
+ } else if (strcmp(str, "ACPI_FSB") == 0) {
+ device_set_desc(dev, "Sleep Button (fixed)");
+ sc->button_type = ACPI_SLEEP_BUTTON;
+ sc->fixed = 1;
}
- return (ret);
+
+ return (0);
}
static int
static int
acpi_button_suspend(device_t dev)
{
- struct acpi_softc *acpi_sc;
-
- acpi_sc = acpi_device_get_parent_softc(dev);
- acpi_wake_sleep_prep(dev, acpi_sc->acpi_sstate);
return (0);
}
static int
acpi_button_resume(device_t dev)
{
- acpi_wake_run_prep(dev);
return (0);
}
* 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: src/sys/dev/acpica/acpi_cmbat.c,v 1.46 2007/03/22 18:16:40 jkim Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_cmbat.c,v 1.12 2008/09/29 06:59:45 hasso Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_cmbat.c,v 1.46.8.1 2009/04/15 03:14:26 kensmith Exp $");
*/
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
+#include <sys/ioccom.h>
+
#include <sys/rman.h>
#include <sys/malloc.h>
-#include <sys/thread2.h>
#include "acpi.h"
#include <dev/acpica5/acpivar.h>
struct timespec bst_lastupdated;
};
+ACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
+
static int acpi_cmbat_probe(device_t dev);
static int acpi_cmbat_attach(device_t dev);
static int acpi_cmbat_detach(device_t dev);
{
struct timespec curtime;
+ ACPI_SERIAL_ASSERT(cmbat);
+
if (lastupdated == NULL)
return (TRUE);
if (!timespecisset(lastupdated))
static void
acpi_cmbat_info_updated(struct timespec *lastupdated)
{
+
+ ACPI_SERIAL_ASSERT(cmbat);
+
if (lastupdated != NULL)
getnanotime(lastupdated);
}
ACPI_BUFFER bst_buffer;
device_t dev;
+ ACPI_SERIAL_ASSERT(cmbat);
+
dev = arg;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
static void
acpi_cmbat_get_bif_task(void *arg)
{
- crit_enter();
+
+ ACPI_SERIAL_BEGIN(cmbat);
acpi_cmbat_get_bif(arg);
- crit_exit();
+ ACPI_SERIAL_END(cmbat);
}
static void
ACPI_BUFFER bif_buffer;
device_t dev;
+ ACPI_SERIAL_ASSERT(cmbat);
+
dev = arg;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
* the info has changed. Many systems apparently take a long time to
* process a _BIF call so we avoid it if possible.
*/
- crit_enter();
+ ACPI_SERIAL_BEGIN(cmbat);
bifp->units = sc->bif.units;
bifp->dcap = sc->bif.dcap;
bifp->lfcap = sc->bif.lfcap;
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));
- crit_exit();
+ ACPI_SERIAL_END(cmbat);
return (0);
}
sc = device_get_softc(dev);
- crit_enter();
+ ACPI_SERIAL_BEGIN(cmbat);
if (acpi_BatteryIsPresent(dev)) {
acpi_cmbat_get_bst(dev);
bstp->state = sc->bst.state;
bstp->volt = sc->bst.volt;
} else
bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
- crit_exit();
+ ACPI_SERIAL_END(cmbat);
return (0);
}
* embedded controller isn't always ready just after boot, we may have
* to wait a while.
*/
- for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10)) {
+ for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
/* batteries on DOCK can be ejected w/ DOCK during retrying */
if (!device_is_attached(dev))
return;
* Only query the battery if this is the first try or the specific
* type of info is still invalid.
*/
- crit_enter();
+ ACPI_SERIAL_BEGIN(cmbat);
if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
timespecclear(&sc->bst_lastupdated);
acpi_cmbat_get_bst(dev);
valid = acpi_battery_bst_valid(&sc->bst) &&
acpi_battery_bif_valid(&sc->bif);
- crit_exit();
+ ACPI_SERIAL_END(cmbat);
if (valid)
break;
#include <sys/rman.h>
#include "acpi.h"
-#include "accommon.h"
#include "acpivar.h"
#include "acpi_cpu.h"
gas.Address = sc->cpu_p_blk + 4;
cx_ptr->rid = sc->cpu_parent->cpux_next_rid;
- cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &cx_ptr->rid, &gas,
+ acpi_bus_alloc_gas(sc->cpu_dev, &cx_ptr->type, &cx_ptr->rid, &gas, &cx_ptr->p_lvlx,
RF_SHAREABLE);
if (cx_ptr->p_lvlx != NULL) {
sc->cpu_parent->cpux_next_rid++;
gas.Address = sc->cpu_p_blk + 5;
cx_ptr->rid = sc->cpu_parent->cpux_next_rid;
- cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &cx_ptr->rid, &gas,
- RF_SHAREABLE);
+ acpi_bus_alloc_gas(sc->cpu_dev, &cx_ptr->type, &cx_ptr->rid, &gas,
+ &cx_ptr->p_lvlx, RF_SHAREABLE);
if (cx_ptr->p_lvlx != NULL) {
sc->cpu_parent->cpux_next_rid++;
cx_ptr->type = ACPI_STATE_C3;
/* Allocate the control register for C2 or C3. */
cx_ptr->rid = sc->cpu_parent->cpux_next_rid;
- acpi_PkgGas(sc->cpu_dev, pkg, 0, &cx_ptr->rid, &cx_ptr->p_lvlx,
+ acpi_PkgGas(sc->cpu_dev, pkg, 0, &cx_ptr->type, &cx_ptr->rid, &cx_ptr->p_lvlx,
RF_SHAREABLE);
if (cx_ptr->p_lvlx) {
sc->cpu_parent->cpux_next_rid++;
if (val) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"acpi_cpu: PIIX4: reset BRLD_EN_BM\n"));
- AcpiWriteBitRegister(ACPI_BITREG_BUS_MASTER_RLD, 0);
+ AcpiReadBitRegister(ACPI_BITREG_BUS_MASTER_RLD, 0);
}
break;
default:
return error;
}
if (bootverbose) {
- device_printf(dev, "control reg %jd %jx\n",
- (intmax_t)sc->pst_creg.pr_gas.SpaceId,
- (intmax_t)sc->pst_creg.pr_gas.Address);
+ device_printf(dev, "control reg %d %llx\n",
+ sc->pst_creg.pr_gas.SpaceId,
+ sc->pst_creg.pr_gas.Address);
}
/* Save and try allocating status register */
struct acpi_pst_res *res)
{
struct acpi_pst_softc *sc = device_get_softc(dev);
- int error;
+ int error, type;
/* Save GAS */
error = acpi_PkgRawGas(obj, idx, &res->pr_gas);
/* Allocate resource, if possible */
res->pr_rid = sc->pst_parent->cpux_next_rid;
- res->pr_res = acpi_bus_alloc_gas(dev, &res->pr_rid, &res->pr_gas, 0);
+ acpi_bus_alloc_gas(dev, &type, &res->pr_rid, &res->pr_gas, &res->pr_res, 0);
if (res->pr_res != NULL) {
sc->pst_parent->cpux_next_rid++;
res->pr_bt = rman_get_bustag(res->pr_res);
/*-
- * Copyright (c) 2003 Nate Lawson
+ * Copyright (c) 2003-2007 Nate Lawson
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
* 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: src/sys/dev/acpica/acpi_ec.c,v 1.52 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_ec.c,v 1.14 2008/08/27 16:35:19 hasso Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_ec.c,v 1.76.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
*/
-/******************************************************************************
- *
- * 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.
- *
- *****************************************************************************/
- /*
- * $FreeBSD: src/sys/dev/acpica/acpi_ec.c,v 1.52 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_ec.c,v 1.14 2008/08/27 16:35:19 hasso Exp $
- *
- */
+
+#include <sys/cdefs.h>
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
-#include <sys/thread.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/lock.h>
#include <sys/rman.h>
#include "acpi.h"
-#include "accommon.h"
#include <dev/acpica5/acpivar.h>
+#include "acutils.h"
-/*
- * Hooks for the ACPI CA debugging infrastructure
- */
+/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_EC
ACPI_MODULE_NAME("EC")
+#define rebooting 0
+#define PZERO 0
/*
* EC_COMMAND:
* -----------
#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|
+ * |7|6|5|4|3|2|1|0|
* +-+-+-+-+-+-+-+-+
* | | | | | | | |
* | | | | | | | +- Output Buffer Full?
* | | | +--------- Burst Mode Enabled?
* | | +----------- SCI Event?
* | +------------- SMI Event?
- * +--------------- <Reserved>
+ * +--------------- <reserved>
*
*/
typedef UINT8 EC_STATUS;
#define EC_FLAG_OUTPUT_BUFFER ((EC_STATUS) 0x01)
#define EC_FLAG_INPUT_BUFFER ((EC_STATUS) 0x02)
+#define EC_FLAG_DATA_IS_CMD ((EC_STATUS) 0x08)
#define EC_FLAG_BURST_MODE ((EC_STATUS) 0x10)
-#define EC_FLAG_SCI ((EC_STATUS) 0x20)
/*
* EC_EVENT:
#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)
+#define EC_EVENT_SMI ((EC_EVENT) 0x40)
+
+/* Data byte returned after burst enable indicating it was successful. */
+#define EC_BURST_ACK 0x90
/*
* Register access primitives
};
/* Indicate that this device has already been probed via ECDT. */
-#define DEV_ECDT(x) (acpi_get_magic(x) == (int)&acpi_ec_devclass)
+#define DEV_ECDT(x) (acpi_get_magic(x) == (uintptr_t)&acpi_ec_devclass)
/*
* Driver softc.
int ec_uid;
ACPI_HANDLE ec_gpehandle;
UINT8 ec_gpebit;
- UINT8 ec_csrvalue;
-
+
int ec_data_rid;
struct resource *ec_data_res;
bus_space_tag_t ec_data_tag;
int ec_glk;
int ec_glkhandle;
- struct lock ec_lock;
+ int ec_burstactive;
+ int ec_sci_pend;
+ u_int ec_gencount;
+ int ec_suspending;
};
/*
- * XXX
+ * XXX njl
* I couldn't find it in the spec but other implementations also use a
* value of 1 ms for the time to acquire global lock.
*/
#define EC_LOCK_TIMEOUT 1000
-/* Default interval in microseconds for the status polling loop. */
-#define EC_POLL_DELAY 10
+/* Default delay in microseconds between each run of the status polling loop. */
+#define EC_POLL_DELAY 5
-/* Total time in ms spent in the poll loop waiting for a response. */
-#define EC_POLL_TIMEOUT 100
+/* Total time in ms spent waiting for a response from EC. */
+#define EC_TIMEOUT 750
#define EVENT_READY(event, status) \
(((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \
((event) == EC_EVENT_INPUT_BUFFER_EMPTY && \
((status) & EC_FLAG_INPUT_BUFFER) == 0))
-static int ec_poll_timeout = EC_POLL_TIMEOUT;
-TUNABLE_INT("hw.acpi.ec.poll_timeout", &ec_poll_timeout);
+ACPI_SERIAL_DECL(ec, "ACPI embedded controller");
+
+SYSCTL_DECL(_debug_acpi);
+SYSCTL_NODE(_debug_acpi, OID_AUTO, ec, CTLFLAG_RD, NULL, "EC debugging");
-static __inline ACPI_STATUS
+static int ec_burst_mode;
+TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0,
+ "Enable use of burst mode (faster for nearly all systems)");
+static int ec_polled_mode;
+TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0,
+ "Force use of polled mode (only if interrupt mode doesn't work)");
+static int ec_timeout = EC_TIMEOUT;
+TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout,
+ EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)");
+
+static ACPI_STATUS
EcLock(struct acpi_ec_softc *sc)
{
- ACPI_STATUS status = AE_OK;
-
- /* Always acquire this EC's mutex. */
- lockmgr(&sc->ec_lock, LK_EXCLUSIVE|LK_RETRY);
+ ACPI_STATUS status;
- /* If _GLK is non-zero, also acquire the global lock. */
+ /* If _GLK is non-zero, acquire the global lock. */
+ status = AE_OK;
if (sc->ec_glk) {
status = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->ec_glkhandle);
if (ACPI_FAILURE(status))
- lockmgr(&sc->ec_lock, LK_RELEASE);
+ return (status);
}
-
+ ACPI_SERIAL_BEGIN(ec);
return (status);
}
-static __inline void
+static void
EcUnlock(struct acpi_ec_softc *sc)
{
+ ACPI_SERIAL_END(ec);
if (sc->ec_glk)
AcpiReleaseGlobalLock(sc->ec_glkhandle);
- lockmgr(&sc->ec_lock, LK_RELEASE);
}
static uint32_t EcGpeHandler(void *Context);
-static ACPI_STATUS EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
+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 EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event,
+ u_int gen_count);
static ACPI_STATUS EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address,
UINT8 *Data);
UINT8 *Data);
static int acpi_ec_probe(device_t dev);
static int acpi_ec_attach(device_t dev);
+static int acpi_ec_suspend(device_t dev);
+static int acpi_ec_resume(device_t dev);
+static int acpi_ec_shutdown(device_t dev);
static int acpi_ec_read_method(device_t dev, u_int addr,
ACPI_INTEGER *val, int width);
static int acpi_ec_write_method(device_t dev, u_int addr,
/* Device interface */
DEVMETHOD(device_probe, acpi_ec_probe),
DEVMETHOD(device_attach, acpi_ec_attach),
+ DEVMETHOD(device_suspend, acpi_ec_suspend),
+ DEVMETHOD(device_resume, acpi_ec_resume),
+ DEVMETHOD(device_shutdown, acpi_ec_shutdown),
/* Embedded controller interface */
- DEVMETHOD(acpi_ec_read, acpi_ec_read_method),
- DEVMETHOD(acpi_ec_write, acpi_ec_write_method),
+ DEVMETHOD(acpi_ec_read, acpi_ec_read_method),
+ DEVMETHOD(acpi_ec_write, acpi_ec_write_method),
{0, 0}
};
MODULE_DEPEND(acpi_ec, acpi, 1, 1, 1);
/*
- * Look for an ECDT and if we find one, set up default GPE and
+ * Look for an ECDT and if we find one, set up default GPE and
* space handlers to catch attempts to access EC space before
* we have a real driver instance in place.
- * TODO: if people report invalid ECDTs, add a tunable to disable them.
+ *
+ * TODO: Some old Gateway laptops need us to fake up an ECDT or
+ * otherwise attach early so that _REG methods can run.
*/
void
acpi_ec_ecdt_probe(device_t parent)
/* Find and validate the ECDT. */
status = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
if (ACPI_FAILURE(status) ||
- ecdt->Control.BitWidth != 8 || ecdt->Data.BitWidth != 8) {
+ ecdt->Control.BitWidth != 8 ||
+ ecdt->Data.BitWidth != 8) {
return;
}
params->uid = ecdt->Uid;
acpi_GetInteger(h, "_GLK", ¶ms->glk);
acpi_set_private(child, params);
- acpi_set_magic(child, (int)&acpi_ec_devclass);
+ acpi_set_magic(child, (uintptr_t)&acpi_ec_devclass);
/* Finish the attach process. */
if (device_probe_and_attach(child) != 0)
char desc[64];
int ret;
struct acpi_ec_params *params;
+ static char *ec_ids[] = { "PNP0C09", NULL };
/* Check that this is a device and that EC is not disabled. */
if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("ec"))
if (DEV_ECDT(dev)) {
params = acpi_get_private(dev);
ret = 0;
- } else if (acpi_MatchHid(acpi_get_handle(dev), "PNP0C09")) {
+ } else if (!acpi_disabled("ec") &&
+ ACPI_ID_PROBE(device_get_parent(dev), dev, ec_ids)) {
params = kmalloc(sizeof(struct acpi_ec_params), M_TEMP,
M_WAITOK | M_ZERO);
h = acpi_get_handle(dev);
if (ACPI_FAILURE(status)) {
device_printf(dev, "can't evaluate _GPE - %s\n",
AcpiFormatException(status));
- return (ENXIO);
+ goto out;
}
obj = (ACPI_OBJECT *)buf.Pointer;
if (obj == NULL)
- return (ENXIO);
+ goto out;
switch (obj->Type) {
case ACPI_TYPE_INTEGER:
params = acpi_get_private(dev);
sc->ec_dev = dev;
sc->ec_handle = acpi_get_handle(dev);
- lockinit(&sc->ec_lock, "eclock", 0, 0);
/* Retrieve previously probed values via device ivars. */
sc->ec_glk = params->glk;
sc->ec_gpebit = params->gpe_bit;
sc->ec_gpehandle = params->gpe_handle;
sc->ec_uid = params->uid;
+ sc->ec_suspending = FALSE;
kfree(params, M_TEMP);
/* Attach bus resources for data and command/status ports. */
goto error;
}
- /*
+ /*
* Install address space handler
*/
ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n"));
AcpiRemoveAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC,
EcSpaceHandler);
if (sc->ec_csr_res)
- bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid,
+ 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);
- /* mtx_destroy(&sc->ec_mtx); */
return (ENXIO);
}
+static int
+acpi_ec_suspend(device_t dev)
+{
+ struct acpi_ec_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->ec_suspending = TRUE;
+ return (0);
+}
+
+static int
+acpi_ec_resume(device_t dev)
+{
+ struct acpi_ec_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->ec_suspending = FALSE;
+ return (0);
+}
+
+static int
+acpi_ec_shutdown(device_t dev)
+{
+ struct acpi_ec_softc *sc;
+
+ /* Disable the GPE so we don't get EC events during shutdown. */
+ sc = device_get_softc(dev);
+ AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR);
+ return (0);
+}
+
/* Methods to allow other devices (e.g., smbat) to read/write EC space. */
static int
acpi_ec_read_method(device_t dev, u_int addr, ACPI_INTEGER *val, int width)
sc = device_get_softc(dev);
status = EcSpaceHandler(ACPI_READ, addr, width * 8, val, sc, NULL);
if (ACPI_FAILURE(status))
- return (ENXIO);
+ return (ENXIO);
return (0);
}
sc = device_get_softc(dev);
status = EcSpaceHandler(ACPI_WRITE, addr, width * 8, &val, sc, NULL);
if (ACPI_FAILURE(status))
- return (ENXIO);
+ return (ENXIO);
return (0);
}
struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context;
UINT8 Data;
ACPI_STATUS Status;
- EC_STATUS EcStatus;
char qxx[5];
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
+ /* Serialize user access with EcSpaceHandler(). */
Status = EcLock(sc);
if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GpeQuery lock error: %s\n", AcpiFormatException(Status));
+ device_printf(sc->ec_dev, "GpeQuery lock error: %s\n",
+ AcpiFormatException(Status));
return;
}
/*
- * If the EC_SCI bit of the status register is not set, then pass
- * it along to any potential waiters as it may be an IBE/OBF event.
- */
- EcStatus = EC_GET_CSR(sc);
- if ((EcStatus & EC_EVENT_SCI) == 0) {
- sc->ec_csrvalue = EcStatus;
- wakeup(&sc->ec_csrvalue);
- EcUnlock(sc);
- goto re_enable;
- }
-
- /*
* Send a query command to the EC to find out which _Qxx call it
* wants to make. This command clears the SCI bit and also the
- * interrupt source since we are edge-triggered.
+ * interrupt source since we are edge-triggered. To prevent the GPE
+ * that may arise from running the query from causing another query
+ * to be queued, we clear the pending flag only after running it.
*/
Status = EcCommand(sc, EC_COMMAND_QUERY);
+ sc->ec_sci_pend = FALSE;
if (ACPI_FAILURE(Status)) {
EcUnlock(sc);
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GPE query failed - %s\n", AcpiFormatException(Status));
- goto re_enable;
+ device_printf(sc->ec_dev, "GPE query failed: %s\n",
+ AcpiFormatException(Status));
+ return;
}
Data = EC_GET_DATA(sc);
+
+ /*
+ * We have to unlock before running the _Qxx method below since that
+ * method may attempt to read/write from EC address space, causing
+ * recursive acquisition of the lock.
+ */
EcUnlock(sc);
/* Ignore the value for "no outstanding event". (13.3.5) */
+#if 0
+ CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data);
+#endif
if (Data == 0)
- goto re_enable;
+ return;
/* Evaluate _Qxx to respond to the controller. */
- ksprintf(qxx, "_Q%02x", Data);
- strupr(qxx);
+ ksnprintf(qxx, sizeof(qxx), "_Q%02X", Data);
+ AcpiUtStrupr(qxx);
Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
if (ACPI_FAILURE(Status) && 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));
+ device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n",
+ qxx, AcpiFormatException(Status));
}
-
-re_enable:
- /* Re-enable the GPE event so we'll get future requests. */
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR);
- if (ACPI_FAILURE(Status))
- kprintf("EcGpeQueryHandler: AcpiEnableEvent failed\n");
}
/*
- * Handle a GPE. Currently we only handle SCI events as others must
- * be handled by polling in EcWaitEvent(). This is because some ECs
- * treat events as level when they should be edge-triggered.
+ * The GPE handler is called when IBE/OBF or SCI events occur. We are
+ * called from an unknown lock context.
*/
static uint32_t
EcGpeHandler(void *Context)
{
struct acpi_ec_softc *sc = Context;
ACPI_STATUS Status;
+ EC_STATUS EcStatus;
KASSERT(Context != NULL, ("EcGpeHandler called with NULL"));
+#if 0
+ CTR0(KTR_ACPI, "ec gpe handler start");
+#endif
+ /*
+ * Notify EcWaitEvent() that the status register is now fresh. If we
+ * didn't do this, it wouldn't be possible to distinguish an old IBE
+ * from a new one, for example when doing a write transaction (writing
+ * address and then data values.)
+ */
+ atomic_add_int(&sc->ec_gencount, 1);
+ wakeup(&sc->ec_gencount);
- /* Disable further GPEs while we handle this one. */
- AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
-
- /* Schedule the GPE query handler. */
- Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler,
- Context);
- if (ACPI_FAILURE(Status)) {
- kprintf("Queuing GPE query handler failed.\n");
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
- if (ACPI_FAILURE(Status))
- kprintf("EcGpeHandler: AcpiEnableEvent failed\n");
+ /*
+ * If the EC_SCI bit of the status register is set, queue a query handler.
+ * It will run the query and _Qxx method later, under the lock.
+ */
+ EcStatus = EC_GET_CSR(sc);
+ if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
+#if 0
+ CTR0(KTR_ACPI, "ec gpe queueing query handler");
+#endif
+ Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context);
+ if (ACPI_SUCCESS(Status))
+ sc->ec_sci_pend = TRUE;
+ else
+ kprintf("EcGpeHandler: queuing GPE query handler failed\n");
}
-
return (0);
}
EcAddr = Address;
Status = AE_ERROR;
+ /*
+ * If booting, check if we need to run the query handler. If so, we
+ * we call it directly here since our thread taskq is not active yet.
+ */
+ if (cold || rebooting) {
+ if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
+#if 0
+ CTR0(KTR_ACPI, "ec running gpe handler directly");
+#endif
+ EcGpeQueryHandler(sc);
+ }
+ }
+
+ /* Serialize with EcGpeQueryHandler() at transaction granularity. */
+ Status = EcLock(sc);
+ if (ACPI_FAILURE(Status))
+ return_ACPI_STATUS (Status);
+
/* Perform the transaction(s), based on width. */
for (i = 0; i < width; i += 8, EcAddr++) {
- Status = EcLock(sc);
- if (ACPI_FAILURE(Status))
- break;
-
switch (Function) {
case ACPI_READ:
Status = EcRead(sc, EcAddr, &EcData);
Status = AE_BAD_PARAMETER;
break;
}
- EcUnlock(sc);
if (ACPI_FAILURE(Status))
break;
}
+ EcUnlock(sc);
return_ACPI_STATUS (Status);
}
static ACPI_STATUS
-EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
+EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event)
{
- EC_STATUS EcStatus;
- ACPI_STATUS Status;
- int count, i, period, retval, slp_ival;
- static int EcDbgMaxDelay;
+ ACPI_STATUS status;
+ EC_STATUS ec_status;
- /* mtx_assert(&sc->ec_mtx, MA_OWNED); */
- Status = AE_NO_HARDWARE_RESPONSE;
+ status = AE_NO_HARDWARE_RESPONSE;
+ ec_status = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) {
+#if 0
+ CTR1(KTR_ACPI, "ec burst disabled in waitevent (%s)", msg);
+#endif
+ sc->ec_burstactive = FALSE;
+ }
+ if (EVENT_READY(event, ec_status)) {
+#if 0
+ CTR2(KTR_ACPI, "ec %s wait ready, status %#x", msg, ec_status);
+#endif
+ status = AE_OK;
+ }
+ return (status);
+}
- /*
- * Wait for 1 us before checking the CSR. Testing shows about
- * 50% of requests complete in 1 us and 90% of them complete
- * in 5 us or less.
- */
- AcpiOsStall(1);
+static ACPI_STATUS
+EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count)
+{
+ ACPI_STATUS Status;
+ int count, i, slp_ival;
+ ACPI_SERIAL_ASSERT(ec);
+ Status = AE_NO_HARDWARE_RESPONSE;
+ int need_poll = cold || rebooting || ec_polled_mode || sc->ec_suspending;
/*
- * Poll the EC status register for up to 1 ms in chunks of 10 us
- * to detect completion of the last command.
+ * The main CPU should be much faster than the EC. So the status should
+ * be "not ready" when we start waiting. But if the main CPU is really
+ * slow, it's possible we see the current "ready" response. Since that
+ * can't be distinguished from the previous response in polled mode,
+ * this is a potential issue. We really should have interrupts enabled
+ * during boot so there is no ambiguity in polled mode.
+ *
+ * If this occurs, we add an additional delay before actually entering
+ * the status checking loop, hopefully to allow the EC to go to work
+ * and produce a non-stale status.
*/
- for (i = 0; i < 1000 / EC_POLL_DELAY; i++) {
- EcStatus = EC_GET_CSR(sc);
- if (EVENT_READY(Event, EcStatus)) {
- Status = AE_OK;
- break;
+ if (need_poll) {
+ static int once;
+
+ if (EcCheckStatus(sc, "pre-check", Event) == AE_OK) {
+ if (!once) {
+ device_printf(sc->ec_dev,
+ "warning: EC done before starting event wait\n");
+ once = 1;
+ }
+ AcpiOsStall(10);
}
- AcpiOsStall(EC_POLL_DELAY);
}
- period = i * EC_POLL_DELAY;
- /*
- * If we still don't have a response and we're up and running, wait up
- * to ec_poll_timeout ms for completion, sleeping for chunks of 10 ms.
- */
- slp_ival = 0;
- if (Status != AE_OK) {
- retval = ENXIO;
- count = ec_poll_timeout / 10;
+ /* Wait for event by polling or GPE (interrupt). */
+ if (need_poll) {
+ count = (ec_timeout * 1000) / EC_POLL_DELAY;
if (count == 0)
count = 1;
- slp_ival = hz / 100;
- if (slp_ival == 0)
- slp_ival = 1;
for (i = 0; i < count; i++) {
- if (retval != 0)
- EcStatus = EC_GET_CSR(sc);
- else
- EcStatus = sc->ec_csrvalue;
- if (EVENT_READY(Event, EcStatus)) {
- Status = AE_OK;
+ Status = EcCheckStatus(sc, "poll", Event);
+ if (Status == AE_OK)
break;
+ AcpiOsStall(EC_POLL_DELAY);
+ }
+ } else {
+ slp_ival = hz / 1000;
+ if (slp_ival != 0) {
+ count = ec_timeout;
+ } else {
+ /* hz has less than 1 ms resolution so scale timeout. */
+ slp_ival = 1;
+ count = ec_timeout / (1000 / hz);
+ }
+
+ /*
+ * Wait for the GPE to signal the status changed, checking the
+ * status register each time we get one. It's possible to get a
+ * GPE for an event we're not interested in here (i.e., SCI for
+ * EC query).
+ */
+ for (i = 0; i < count; i++) {
+ if (gen_count != sc->ec_gencount) {
+ /*
+ * Record new generation count. It's possible the GPE was
+ * just to notify us that a query is needed and we need to
+ * wait for a second GPE to signal the completion of the
+ * event we are actually waiting for.
+ */
+ gen_count = sc->ec_gencount;
+ Status = EcCheckStatus(sc, "sleep", Event);
+ if (Status == AE_OK)
+ break;
}
- if (!cold)
- retval = tsleep(&sc->ec_csrvalue, 0, "ecpoll", slp_ival);
- else
- AcpiOsStall(10000);
+ tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival);
}
- }
- /* Calculate new delay and print it if it exceeds the max. */
- if (slp_ival > 0)
- period += i * 10000;
- if (period > EcDbgMaxDelay) {
- EcDbgMaxDelay = period;
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "info: new max delay is %d us\n", period);
+ /*
+ * We finished waiting for the GPE and it never arrived. Try to
+ * read the register once and trust whatever value we got. This is
+ * the best we can do at this point. Then, force polled mode on
+ * since this system doesn't appear to generate GPEs.
+ */
+ if (Status != AE_OK) {
+ Status = EcCheckStatus(sc, "sleep_end", Event);
+ device_printf(sc->ec_dev,
+ "wait timed out (%sresponse), forcing polled mode\n",
+ Status == AE_OK ? "" : "no ");
+ ec_polled_mode = TRUE;
+ }
}
-
+#if 0
+ if (Status != AE_OK)
+ CTR0(KTR_ACPI, "error: ec wait timed out");
+#endif
return (Status);
-}
+}
static ACPI_STATUS
EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
{
- ACPI_STATUS Status;
- EC_EVENT Event;
+ ACPI_STATUS status;
+ EC_EVENT event;
+ EC_STATUS ec_status;
+ u_int gen_count;
- /*mtx_assert(&sc->ec_mtx, MA_OWNED);*/
+ ACPI_SERIAL_ASSERT(ec);
+
+ /* Don't use burst mode if user disabled it. */
+ if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
+ return (AE_ERROR);
/* Decide what to wait for based on command type. */
switch (cmd) {
case EC_COMMAND_READ:
case EC_COMMAND_WRITE:
case EC_COMMAND_BURST_DISABLE:
- Event = EC_EVENT_INPUT_BUFFER_EMPTY;
+ event = EC_EVENT_INPUT_BUFFER_EMPTY;
break;
case EC_COMMAND_QUERY:
case EC_COMMAND_BURST_ENABLE:
- Event = EC_EVENT_OUTPUT_BUFFER_FULL;
+ event = EC_EVENT_OUTPUT_BUFFER_FULL;
break;
default:
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: Invalid command %#x\n", cmd);
+ device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd);
return (AE_BAD_PARAMETER);
}
/* Run the command and wait for the chosen event. */
+#if 0
+ CTR1(KTR_ACPI, "ec running command %#x", cmd);
+#endif
+ gen_count = sc->ec_gencount;
EC_SET_CSR(sc, cmd);
- Status = EcWaitEvent(sc, Event);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: no response to %#x\n", cmd);
- }
-
- return (Status);
+ status = EcWaitEvent(sc, event, gen_count);
+ if (ACPI_SUCCESS(status)) {
+ /* If we succeeded, burst flag should now be present. */
+ if (cmd == EC_COMMAND_BURST_ENABLE) {
+ ec_status = EC_GET_CSR(sc);
+ if ((ec_status & EC_FLAG_BURST_MODE) == 0)
+ status = AE_ERROR;
+ }
+ } else
+ device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd);
+ return (status);
}
static ACPI_STATUS
EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
{
- ACPI_STATUS Status;
-
- /*mtx_assert(&sc->ec_mtx, MA_OWNED);*/
+ ACPI_STATUS status;
+ UINT8 data;
+ u_int gen_count;
-#ifdef notyet
+ ACPI_SERIAL_ASSERT(ec);
+#if 0
+ CTR1(KTR_ACPI, "ec read from %#x", Address);
+#endif
/* If we can't start burst mode, continue anyway. */
- EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ if (status == AE_OK) {
+ data = EC_GET_DATA(sc);
+ if (data == EC_BURST_ACK) {
+#if 0
+ CTR0(KTR_ACPI, "ec burst enabled");
#endif
+ sc->ec_burstactive = TRUE;
+ }
+ }
- Status = EcCommand(sc, EC_COMMAND_READ);
- if (ACPI_FAILURE(Status))
- return (Status);
+ status = EcCommand(sc, EC_COMMAND_READ);
+ if (ACPI_FAILURE(status))
+ return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to send data.\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n");
+ return (status);
}
-
*Data = EC_GET_DATA(sc);
-#ifdef notyet
if (sc->ec_burstactive) {
- Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
- if (ACPI_FAILURE(Status))
- return (Status);
- }
+ sc->ec_burstactive = FALSE;
+ status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+ if (ACPI_FAILURE(status))
+ return (status);
+#if 0
+ CTR0(KTR_ACPI, "ec disabled burst ok");
#endif
+ }
return (AE_OK);
-}
+}
static ACPI_STATUS
EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
{
- ACPI_STATUS Status;
+ ACPI_STATUS status;
+ UINT8 data;
+ u_int gen_count;
- /*mtx_assert(&sc->ec_mtx, MA_OWNED);*/
+ ACPI_SERIAL_ASSERT(ec);
+#if 0
+ CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, *Data);
+#endif
-#ifdef notyet
/* If we can't start burst mode, continue anyway. */
- EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ if (status == AE_OK) {
+ data = EC_GET_DATA(sc);
+ if (data == EC_BURST_ACK) {
+#if 0
+ CTR0(KTR_ACPI, "ec burst enabled");
#endif
+ sc->ec_burstactive = TRUE;
+ }
+ }
- Status = EcCommand(sc, EC_COMMAND_WRITE);
- if (ACPI_FAILURE(Status))
- return (Status);
+ status = EcCommand(sc, EC_COMMAND_WRITE);
+ if (ACPI_FAILURE(status))
+ return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to process address\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n");
+ return (status);
}
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, *Data);
- Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcWrite: Failed waiting for EC to process data\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n");
+ return (status);
}
-#ifdef notyet
if (sc->ec_burstactive) {
- Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
- if (ACPI_FAILURE(Status))
- return (Status);
- }
+ sc->ec_burstactive = FALSE;
+ status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+ if (ACPI_FAILURE(status))
+ return (status);
+#if 0
+ CTR0(KTR_ACPI, "ec disabled burst ok");
#endif
+ }
return (AE_OK);
}
{
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
- if (!acpi_enabled("hpet"))
+ if (acpi_disabled("hpet"))
return ENXIO;
if (!DEV_HPET(dev) &&
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
-# $DragonFly: src/sys/dev/acpica5/acpi_if.m,v 1.3 2008/09/29 06:59:45 hasso Exp $
+# $FreeBSD: src/sys/dev/acpica/acpi_if.m,v 1.8.8.1 2009/04/15 03:14:26 kensmith Exp $
#
#include <sys/bus.h>
} DEFAULT acpi_generic_id_probe;
#
+# Evaluate an ACPI method or object, given its path.
+#
+# device_t bus: parent bus for the device
+#
+# device_t dev: evaluate the object relative to this device's handle.
+# Specify NULL to begin the search at the ACPI root.
+#
+# ACPI_STRING pathname: absolute or relative path to this object
+#
+# ACPI_OBJECT_LIST *parameters: array of arguments to pass to the object.
+# Specify NULL if there are none.
+#
+# ACPI_BUFFER *ret: the result (if any) of the evaluation
+# Specify NULL if there is none.
+#
+# Returns: AE_OK or an error value
+#
+METHOD ACPI_STATUS evaluate_object {
+ device_t bus;
+ device_t dev;
+ ACPI_STRING pathname;
+ ACPI_OBJECT_LIST *parameters;
+ ACPI_BUFFER *ret;
+};
+
+#
+# Get the highest power state (D0-D3) that is usable for a device when
+# suspending/resuming. If a bus calls this when suspending a device, it
+# must also call it when resuming.
+#
+# device_t bus: parent bus for the device
+#
+# device_t dev: check this device's appropriate power state
+#
+# int *dstate: if successful, contains the highest valid sleep state
+#
+# Returns: 0 on success, ESRCH if device has no special state, or
+# some other error value.
+#
+METHOD int pwr_for_sleep {
+ device_t bus;
+ device_t dev;
+ int *dstate;
+};
+
+#
+# Rescan a subtree and optionally reattach devices to handles. Users
+# specify a callback that is called for each ACPI_HANDLE of type Device
+# that is a child of "dev".
+#
+# device_t bus: parent bus for the device
+#
+# device_t dev: begin the scan starting with this device's handle.
+# Specify NULL to begin the scan at the ACPI root.
+#
+# int max_depth: number of levels to traverse (i.e., 1 means just the
+# immediate children.
+#
+# acpi_scan_cb_t user_fn: called for each child handle
+#
+# void *arg: argument to pass to the callback function
+#
+# Returns: AE_OK or an error value, based on the callback return value
+#
+METHOD ACPI_STATUS scan_children {
+ device_t bus;
+ device_t dev;
+ int max_depth;
+ acpi_scan_cb_t user_fn;
+ void *arg;
+};
+
+#
# Query a given driver for its supported feature(s). This should be
# called by the parent bus before the driver is probed.
#
device_t dev;
struct acpi_bst *bst;
};
-
* 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: src/sys/dev/acpica/acpi_isab.c,v 1.8 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_isab.c,v 1.4 2007/10/23 03:04:48 y0netan1 Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_isab.c,v 1.11 2009/06/05 18:44:36 jkim
*/
+#include <sys/cdefs.h>
+
/*
* ISA Bridge driver for Generic ISA Bus Devices. See section 10.7 of the
* ACPI 2.0a specification for details on this device.
#include "acpi.h"
#include "accommon.h"
-#include "acpivar.h"
+
+#include <dev/acpica5/acpivar.h>
#include <bus/isa/isavar.h>
/* Hooks for the ACPI CA debugging infrastructure. */
static int
acpi_isab_probe(device_t dev)
{
- ACPI_HANDLE h;
-
- h = acpi_get_handle(dev);
- if (acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
- !acpi_disabled("isa") &&
- devclass_get_device(isab_devclass, 0) == dev &&
- (acpi_MatchHid(h, "PNP0A05") || acpi_MatchHid(h, "PNP0A06"))) {
- device_set_desc(dev, "ACPI Generic ISA bridge");
- return (0);
- }
- return (ENXIO);
+ static char *isa_ids[] = { "PNP0A05", "PNP0A06", NULL };
+
+ if (acpi_disabled("isab") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, isa_ids) == NULL ||
+ devclass_get_device(isab_devclass, 0) != dev)
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI Generic ISA bridge");
+ return (0);
}
static int
* 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: src/sys/dev/acpica/acpi_lid.c,v 1.23 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_lid.c,v 1.5 2007/10/23 03:04:48 y0netan1 Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_lid.c,v 1.30 2009/06/05 18:44:36 jkim Exp $
*/
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include "acpi.h"
#include "accommon.h"
+
#include <dev/acpica5/acpivar.h>
/* Hooks for the ACPI CA debugging infrastructure */
int lid_status; /* open or closed */
};
+ACPI_SERIAL_DECL(lid, "ACPI lid");
+
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_probe(device_t dev)
{
- if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("lid") &&
- acpi_MatchHid(acpi_get_handle(dev), "PNP0C0D")) {
+ static char *lid_ids[] = { "PNP0C0D", NULL };
- device_set_desc(dev, "Control Method Lid Switch");
- return (0);
- }
- return (ENXIO);
+ ACPI_SERIAL_INIT(lid);
+
+ if (acpi_disabled("lid") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, lid_ids) == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, "Control Method Lid Switch");
+ return (0);
}
static int
acpi_wake_init(dev, ACPI_GPE_TYPE_WAKE_RUN);
acpi_wake_set_enable(dev, 1);
- return_VALUE (0);
+ return (0);
}
static int
acpi_lid_suspend(device_t dev)
{
- struct acpi_softc *acpi_sc;
-
- acpi_sc = acpi_device_get_parent_softc(dev);
- acpi_wake_sleep_prep(dev, acpi_sc->acpi_sstate);
return (0);
}
static int
acpi_lid_resume(device_t dev)
{
- acpi_wake_run_prep(dev);
return (0);
}
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
sc = (struct acpi_lid_softc *)arg;
+ ACPI_SERIAL_BEGIN(lid);
/*
* Evaluate _LID and check the return value, update lid status.
*/
status = acpi_GetInteger(sc->lid_handle, "_LID", &sc->lid_status);
if (ACPI_FAILURE(status))
- return_VOID;
+ goto out;
acpi_sc = acpi_device_get_parent_softc(sc->lid_dev);
if (acpi_sc == NULL)
- return_VOID;
+ goto out;
ACPI_VPRINT(sc->lid_dev, acpi_sc, "Lid %s\n",
sc->lid_status ? "opened" : "closed");
else
EVENTHANDLER_INVOKE(acpi_wakeup_event, acpi_sc->acpi_lid_switch_sx);
+out:
+ ACPI_SERIAL_END(lid);
return_VOID;
}
sc = (struct acpi_lid_softc *)context;
switch (notify) {
case ACPI_NOTIFY_STATUS_CHANGED:
- AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_lid_notify_status_changed, sc);
+ AcpiOsExecute(OSL_NOTIFY_HANDLER,
+ acpi_lid_notify_status_changed, sc);
break;
default:
device_printf(sc->lid_dev, "unknown notify %#x\n", notify);
* 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: src/sys/dev/acpica/acpi_package.c,v 1.3 2004/04/09 06:40:03 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_package.c,v 1.4 2008/09/05 10:28:35 hasso Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_package.c,v 1.9.8.1 2009/04/15 03:14:26 kensmith Exp $");
*/
+#include <sys/cdefs.h>
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/sbuf.h>
+
#include <sys/rman.h>
#include "acpi.h"
}
int
-acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid,
- struct resource **dst, u_int flags)
+acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type, int *rid,
+ struct resource **dst, u_int flags)
{
ACPI_GENERIC_ADDRESS gas;
- int error;
+ ACPI_OBJECT *obj;
- error = acpi_PkgRawGas(res, idx, &gas);
- if (error)
- return (error);
+ obj = &res->Package.Elements[idx];
+ if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER ||
+ obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
+ return (EINVAL);
- *dst = acpi_bus_alloc_gas(dev, rid, &gas, flags);
- if (*dst == NULL)
- return (ENXIO);
+ memcpy(&gas, obj->Buffer.Pointer + 3, sizeof(gas));
- return (0);
+ return (acpi_bus_alloc_gas(dev, type, rid, &gas, dst, flags));
}
int
acpi_PkgRawGas(ACPI_OBJECT *res, int idx, ACPI_GENERIC_ADDRESS *gas)
{
- ACPI_OBJECT *obj;
-
+ ACPI_OBJECT *obj;
obj = &res->Package.Elements[idx];
if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER ||
- obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
- return (EINVAL);
-
+ obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
+ return (EINVAL);
memcpy(gas, obj->Buffer.Pointer + 3, sizeof(*gas));
return (0);
}
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org>
* Copyright (c) 2000, BSDi
* All rights reserved.
*
* 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: src/sys/dev/acpica/acpi_pci.c,v 1.16 2004/05/29 04:32:50 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pci.c,v 1.5 2006/09/05 00:55:36 dillon Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pci.c,v 1.31.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
*/
-#include "opt_bus.h"
+#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include "acpi.h"
-#include "accommon.h"
#include "acpivar.h"
#include <sys/pciio.h>
struct acpi_pci_devinfo {
struct pci_devinfo ap_dinfo;
ACPI_HANDLE ap_handle;
+ int ap_flags;
};
-static int acpi_pci_probe(device_t dev);
+ACPI_SERIAL_DECL(pci_powerstate, "ACPI PCI power methods");
+
+/* Be sure that ACPI and PCI power states are equivalent. */
+CTASSERT(ACPI_STATE_D0 == PCI_POWERSTATE_D0);
+CTASSERT(ACPI_STATE_D1 == PCI_POWERSTATE_D1);
+CTASSERT(ACPI_STATE_D2 == PCI_POWERSTATE_D2);
+CTASSERT(ACPI_STATE_D3 == PCI_POWERSTATE_D3);
+
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);
+static int acpi_pci_suspend(device_t dev);
+static int acpi_pci_resume(device_t dev);
static int acpi_pci_child_location_str_method(device_t cbdev,
device_t child, char *buf, size_t buflen);
-
-
-static int acpi_pci_set_powerstate_method(device_t dev, device_t child,
- int state);
+static int acpi_pci_probe(device_t dev);
+static int acpi_pci_read_ivar(device_t dev, device_t child, int which,
+ uintptr_t *result);
+static int acpi_pci_write_ivar(device_t dev, device_t child, int which,
+ uintptr_t value);
static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level,
void *context, void **status);
+static int acpi_pci_set_powerstate_method(device_t dev, device_t child,
+ int state);
+static void acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child);
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),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, acpi_pci_suspend),
+ DEVMETHOD(device_resume, acpi_pci_resume),
/* Bus interface */
- DEVMETHOD(bus_print_child, pci_print_child),
- DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch),
+ DEVMETHOD(bus_print_child, pci_print_child),
+ 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_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_write_ivar, acpi_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_child_location_str, acpi_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),
- DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method),
- DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
+ 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),
+ DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
+ DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method),
+ DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
{ 0, 0 }
};
static driver_t acpi_pci_driver = {
- "pci",
- acpi_pci_methods,
- 0, /* no softc */
+ "pci",
+ acpi_pci_methods,
+ 0, /* no softc */
};
static devclass_t pci_devclass;
DRIVER_MODULE(acpi_pci, pcib, acpi_pci_driver, pci_devclass, 0, 0);
MODULE_DEPEND(acpi_pci, acpi, 1, 1, 1);
+MODULE_DEPEND(acpi_pci, pci, 1, 1, 1);
MODULE_VERSION(acpi_pci, 1);
static int
{
struct acpi_pci_devinfo *dinfo;
+ dinfo = device_get_ivars(child);
switch (which) {
case ACPI_IVAR_HANDLE:
- dinfo = device_get_ivars(child);
*result = (uintptr_t)dinfo->ap_handle;
return (0);
+ case ACPI_IVAR_FLAGS:
+ *result = (uintptr_t)dinfo->ap_flags;
+ return (0);
}
return (pci_read_ivar(dev, child, which, result));
}
static int
+acpi_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct acpi_pci_devinfo *dinfo;
+
+ dinfo = device_get_ivars(child);
+ switch (which) {
+ case ACPI_IVAR_HANDLE:
+ dinfo->ap_handle = (ACPI_HANDLE)value;
+ return (0);
+ case ACPI_IVAR_FLAGS:
+ dinfo->ap_flags = (int)value;
+ return (0);
+ }
+ return (pci_write_ivar(dev, child, which, value));
+}
+
+static int
acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf,
size_t buflen)
{
pci_child_location_str_method(cbdev, child, buf, buflen);
if (dinfo->ap_handle) {
- strlcat(buf, " path=", buflen);
+ strlcat(buf, " handle=", buflen);
strlcat(buf, acpi_name(dinfo->ap_handle), buflen);
}
return (0);
{
ACPI_HANDLE h;
ACPI_STATUS status;
- int acpi_state, old_state, error;
-
- switch (state) {
- case PCI_POWERSTATE_D0:
- acpi_state = ACPI_STATE_D0;
- break;
- case PCI_POWERSTATE_D1:
- acpi_state = ACPI_STATE_D1;
- break;
- case PCI_POWERSTATE_D2:
- acpi_state = ACPI_STATE_D2;
- break;
- case PCI_POWERSTATE_D3:
- acpi_state = ACPI_STATE_D3;
- break;
- default:
+ int old_state, error;
+
+ error = 0;
+ if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3)
return (EINVAL);
- }
/*
* We set the state using PCI Power Management outside of setting
* it can enable any needed Power Resources before changing the PCI
* power state.
*/
+ ACPI_SERIAL_BEGIN(pci_powerstate);
old_state = pci_get_powerstate(child);
if (old_state < state) {
error = pci_set_powerstate_method(dev, child, state);
if (error)
- return (error);
+ goto out;
}
h = acpi_get_handle(child);
- if (h != NULL) {
- status = acpi_pwr_switch_consumer(h, acpi_state);
- if (ACPI_FAILURE(status))
- device_printf(dev,
- "Failed to set ACPI power state D%d on %s: %s\n",
- acpi_state, device_get_nameunit(child),
- AcpiFormatException(status));
- }
+ status = acpi_pwr_switch_consumer(h, state);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ device_printf(dev,
+ "Failed to set ACPI power state D%d on %s: %s\n",
+ state, acpi_name(h), AcpiFormatException(status));
if (old_state > state)
- return (pci_set_powerstate_method(dev, child, state));
- else
- return (0);
+ error = pci_set_powerstate_method(dev, child, state);
+
+out:
+ ACPI_SERIAL_END(pci_powerstate);
+ return (error);
+}
+
+static void
+acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child)
+{
+ ACPI_STATUS status;
+ device_t child;
+
+ /*
+ * Lookup and remove the unused device that acpi0 creates when it walks
+ * the namespace creating devices.
+ */
+ child = acpi_get_device(handle);
+ if (child != NULL) {
+ if (device_is_alive(child)) {
+ /*
+ * The TabletPC TC1000 has a second PCI-ISA bridge
+ * that has a _HID for an acpi_sysresource device.
+ * In that case, leave ACPI-CA's device data pointing
+ * at the ACPI-enumerated device.
+ */
+ device_printf(child,
+ "Conflicts with PCI device %d:%d:%d\n",
+ pci_get_bus(pci_child), pci_get_slot(pci_child),
+ pci_get_function(pci_child));
+ return;
+ }
+ KASSERT(device_get_parent(child) ==
+ devclass_get_device(devclass_find("acpi"), 0),
+ ("%s: child (%s)'s parent is not acpi0", __func__,
+ acpi_name(handle)));
+ device_delete_child(device_get_parent(child), child);
+ }
+
+ /*
+ * Update ACPI-CA to use the PCI enumerated device_t for this handle.
+ */
+ status = AcpiDetachData(handle, acpi_fake_objhandler);
+ if (ACPI_FAILURE(status))
+ kprintf("WARNING: Unable to detach object data from %s - %s\n",
+ acpi_name(handle), AcpiFormatException(status));
+ status = AcpiAttachData(handle, acpi_fake_objhandler, pci_child);
+ if (ACPI_FAILURE(status))
+ kprintf("WARNING: Unable to attach object data to %s - %s\n",
+ acpi_name(handle), AcpiFormatException(status));
}
static ACPI_STATUS
if (ACPI_FAILURE(acpi_GetInteger(handle, "_ADR", &address)))
return_ACPI_STATUS (AE_OK);
- slot = address >> 16;
- func = address & 0xffff;
+ slot = ACPI_ADR_PCI_SLOT(address);
+ func = ACPI_ADR_PCI_FUNC(address);
if (device_get_children((device_t)context, &devlist, &devcount) != 0)
return_ACPI_STATUS (AE_OK);
for (i = 0; i < devcount; i++) {
if (dinfo->ap_dinfo.cfg.func == func &&
dinfo->ap_dinfo.cfg.slot == slot) {
dinfo->ap_handle = handle;
- kfree(devlist, M_TEMP);
- return_ACPI_STATUS (AE_OK);
+ acpi_pci_update_device(handle, devlist[i]);
+ break;
}
}
kfree(devlist, M_TEMP);
static int
acpi_pci_probe(device_t dev)
{
- if (pcib_get_bus(device_get_parent(dev)) < 0)
+
+ if (pcib_get_bus(dev) < 0)
return (ENXIO);
if (acpi_get_handle(dev) == NULL)
return (ENXIO);
/*
* 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.
+ * busses on systems with multiple PCI domains, we can't use
+ * the unit number to decide which bus we are probing. We ask
+ * the parent pcib what our domain and bus numbers are.
*/
- busno = pcib_get_bus(device_get_parent(dev));
+ busno = pcib_get_bus(dev);
+ domain = pcib_get_domain(dev);
if (bootverbose)
- device_printf(dev, "physical bus=%d\n", busno);
-
- domain = pcib_get_domain(device_get_parent(dev));
+ device_printf(dev, "domain=%d, physical bus=%d\n",
+ domain, busno);
/*
* First, PCI devices are added as in the normal PCI bus driver.
return (bus_generic_attach(dev));
}
+
+int
+acpi_pci_suspend(device_t dev)
+{
+ int dstate, error, i, numdevs;
+ device_t acpi_dev, child, *devlist;
+ struct pci_devinfo *dinfo;
+ /*
+ * Save the PCI configuration space for each child and set the
+ * device in the appropriate power state for this sleep state.
+ */
+ acpi_dev = NULL;
+ acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
+ device_get_children(dev, &devlist, &numdevs);
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ dinfo = (struct pci_devinfo *) device_get_ivars(child);
+ pci_cfg_save(child, dinfo, 0);
+ }
+ /* Suspend devices before potentially powering them down. */
+ error = bus_generic_suspend(dev);
+ if (error) {
+ kfree(devlist, M_TEMP);
+ return (error);
+ }
+ /*
+ * Always set the device to D3. If ACPI suggests a different
+ * power state, use it instead. If ACPI is not present, the
+ * firmware is responsible for managing device power. Skip
+ * children who aren't attached since they are powered down
+ * separately. Only manage type 0 devices for now.
+ */
+ for (i = 0; acpi_dev && i < numdevs; i++) {
+ child = devlist[i];
+ dinfo = (struct pci_devinfo *) device_get_ivars(child);
+ if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) {
+ dstate = PCI_POWERSTATE_D3;
+ ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate);
+ pci_set_powerstate(child, dstate);
+ }
+ }
+ kfree(devlist, M_TEMP);
+ return (0);
+}
+
+int
+acpi_pci_resume(device_t dev)
+{
+ int i, numdevs;
+ device_t acpi_dev, child, *devlist;
+ struct pci_devinfo *dinfo;
+ /*
+ * Set each child to D0 and restore its PCI configuration space.
+ */
+ acpi_dev = NULL;
+ acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
+ device_get_children(dev, &devlist, &numdevs);
+ for (i = 0; i < numdevs; i++) {
+ /*
+ * Notify ACPI we're going to D0 but ignore the result. If
+ * ACPI is not present, the firmware is responsible for
+ * managing device power. Only manage type 0 devices for now.
+ */
+ child = devlist[i];
+ dinfo = (struct pci_devinfo *) device_get_ivars(child);
+ if (acpi_dev && device_is_attached(child) &&
+ dinfo->cfg.hdrtype == 0) {
+ ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL);
+ pci_set_powerstate(child, PCI_POWERSTATE_D0);
+ }
+ /* Now the device is powered up, restore its config space. */
+ pci_cfg_restore(child, dinfo);
+ }
+ kfree(devlist, M_TEMP);
+ return (bus_generic_resume(dev));
+}
-/*-
- * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+/*
+ * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.kfreebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 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: src/sys/dev/acpica/acpi_pci_link.c,v 1.16 2004/06/14 18:54:14 jhb Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pci_link.c,v 1.7 2006/12/22 23:26:14 swildner Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.56.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
*/
+#define MPASS(ex) MPASS4(ex, #ex, __FILE__, __LINE__)
+#define MPASS4(ex, what, file, line) \
+ KASSERT((ex), ("Assertion %s failed at %s:%d", what, file, line))
+
+#include <sys/cdefs.h>
+
#include "opt_acpi.h"
#include <sys/param.h>
-#include <sys/kernel.h>
#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
#include "acpi.h"
-#include "accommon.h"
#include <dev/acpica5/acpivar.h>
#include <dev/acpica5/acpi_pcibvar.h>
+#include <bus/pci/i386/pci_cfgreg.h>
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include "pcib_if.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;
+ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
+
+#define NUM_ISA_INTERRUPTS 16
+#define NUM_ACPI_INTERRUPTS 256
+
+/*
+ * An ACPI PCI link device may contain multiple links. Each link has its
+ * own ACPI resource. _PRT entries specify which link is being used via
+ * the Source Index.
+ *
+ * XXX: A note about Source Indices and DPFs: Currently we assume that
+ * the DPF start and end tags are not counted towards the index that
+ * Source Index corresponds to. Also, we assume that when DPFs are in use
+ * they various sets overlap in terms of Indices. Here's an example
+ * resource list indicating these assumptions:
+ *
+ * Resource Index
+ * -------- -----
+ * I/O Port 0
+ * Start DPF -
+ * IRQ 1
+ * MemIO 2
+ * Start DPF -
+ * IRQ 1
+ * MemIO 2
+ * End DPF -
+ * DMA Channel 3
+ *
+ * The XXX is because I'm not sure if this is a valid assumption to make.
+ */
+
+/* States during DPF processing. */
+#define DPF_OUTSIDE 0
+#define DPF_FIRST 1
+#define DPF_IGNORE 2
+
+struct link;
+
+struct acpi_pci_link_softc {
+ int pl_num_links;
+ int pl_crs_bad;
+ struct link *pl_links;
+ device_t pl_dev;
};
-TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
-static struct acpi_pci_link_entries acpi_pci_link_entries;
+struct link {
+ struct acpi_pci_link_softc *l_sc;
+ uint8_t l_bios_irq;
+ uint8_t l_irq;
+ uint8_t l_initial_irq;
+ int l_res_index;
+ int l_num_irqs;
+ int *l_irqs;
+ int l_references;
+ int l_routed:1;
+ int l_isa_irq:1;
+ ACPI_RESOURCE l_prs_template;
+};
-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;
+struct link_count_request {
+ int in_dpf;
+ int count;
};
-TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
-static struct acpi_prt_entries acpi_prt_entries;
+struct link_res_request {
+ struct acpi_pci_link_softc *sc;
+ int in_dpf;
+ int res_index;
+ int link_index;
+};
+
+MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
-static int irq_penalty[MAX_ACPI_INTERRUPTS];
+static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
+static int pci_link_bios_isa_irqs;
-#define ACPI_STA_PRESENT 0x00000001
-#define ACPI_STA_ENABLE 0x00000002
-#define ACPI_STA_SHOWINUI 0x00000004
-#define ACPI_STA_FUNCTIONAL 0x00000008
+static char *pci_link_ids[] = { "PNP0C0F", NULL };
/*
- * PCI link object management
+ * Fetch the short name associated with an ACPI handle and save it in the
+ * passed in buffer.
*/
-
-static void
-acpi_pci_link_dump_polarity(UINT32 Polarity)
+static ACPI_STATUS
+acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
{
+ ACPI_BUFFER buf;
- switch (Polarity) {
- case ACPI_ACTIVE_HIGH:
- kprintf("high,");
- break;
- case ACPI_ACTIVE_LOW:
- kprintf("low,");
- break;
- default:
- kprintf("unknown,");
- break;
- }
+ buf.Length = buflen;
+ buf.Pointer = buffer;
+ return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
}
-static void
-acpi_pci_link_dump_trigger(UINT32 Triggering)
+static int
+acpi_pci_link_probe(device_t dev)
{
+ char descr[28], name[12];
- switch (Triggering) {
- case ACPI_EDGE_SENSITIVE:
- kprintf("edge,");
- break;
- case ACPI_LEVEL_SENSITIVE:
- kprintf("level,");
- break;
- default:
- kprintf("unknown,");
- break;
- }
+ /*
+ * We explicitly do not check _STA since not all systems set it to
+ * sensible values.
+ */
+ if (acpi_disabled("pci_link") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL)
+ return (ENXIO);
+
+ if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
+ sizeof(name)))) {
+ ksnprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
+ device_set_desc_copy(dev, descr);
+ } else
+ device_set_desc(dev, "ACPI PCI Link");
+ device_quiet(dev);
+ return (0);
}
-static void
-acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
+static ACPI_STATUS
+acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
{
-
- switch (SharedExclusive) {
- case ACPI_EXCLUSIVE:
- kprintf("exclusive");
+ struct link_count_request *req;
+
+ req = (struct link_count_request *)context;
+ switch (res->Type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ req->in_dpf = DPF_IGNORE;
+ break;
+ }
break;
- case ACPI_SHARED:
- kprintf("sharable");
+ case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
break;
- default:
- kprintf("unknown");
- break;
- }
-}
-
-static void
-acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
-{
- UINT8 i;
- ACPI_RESOURCE_IRQ *Irq;
- ACPI_RESOURCE_EXTENDED_IRQ *ExtIrq;
-
- if (entry == NULL || entry->pci_link == NULL)
- return;
-
- kprintf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
- entry->pci_link->current_irq);
-
- kprintf("[");
- for (i = 0; i < entry->pci_link->number_of_interrupts; i++)
- kprintf("%3d", entry->pci_link->interrupts[i]);
- kprintf("] ");
-
- switch (entry->pci_link->possible_resources.Type) {
case ACPI_RESOURCE_TYPE_IRQ:
- Irq = &entry->pci_link->possible_resources.Data.Irq;
- acpi_pci_link_dump_polarity(Irq->Polarity);
- acpi_pci_link_dump_trigger(Irq->Triggering);
- acpi_pci_link_dump_sharemode(Irq->Sharable);
- break;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
- ExtIrq = &entry->pci_link->possible_resources.Data.ExtendedIrq;
- acpi_pci_link_dump_polarity(ExtIrq->Polarity);
- acpi_pci_link_dump_trigger(ExtIrq->Triggering);
- acpi_pci_link_dump_sharemode(ExtIrq->Sharable);
- break;
+ /*
+ * Don't count resources if we are in a DPF set that we are
+ * ignoring.
+ */
+ if (req->in_dpf != DPF_IGNORE)
+ req->count++;
}
-
- kprintf(" %d.%d.%d\n", entry->busno,
- (int)((entry->prt.Address & 0xffff0000) >> 16),
- (int)entry->prt.Pin);
+ return (AE_OK);
}
static ACPI_STATUS
-acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
+link_add_crs(ACPI_RESOURCE *res, void *context)
{
- ACPI_DEVICE_INFO *devinfo;
- ACPI_BUFFER buf;
- ACPI_STATUS error;
-
- ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- if (handle == NULL || sta == NULL) {
- kprintf("invalid argument\n");
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
-
- buf.Pointer = NULL;
- buf.Length = ACPI_ALLOCATE_BUFFER;
- error = AcpiGetObjectInfo(handle, &buf);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get object info %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
- }
+ struct link_res_request *req;
+ struct link *link;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ req = (struct link_res_request *)context;
+ switch (res->Type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ panic(
+ "%s: Multiple dependent functions within a current resource",
+ __func__);
+ break;
+ }
+ break;
+ case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
+ break;
+ case ACPI_RESOURCE_TYPE_IRQ:
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ KASSERT(req->link_index < req->sc->pl_num_links,
+ ("%s: array boundary violation", __func__));
+ link = &req->sc->pl_links[req->link_index];
+ link->l_res_index = req->res_index;
+ req->link_index++;
+ req->res_index++;
- devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
- if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
- strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
- kprintf("invalid hardware ID - %s\n", acpi_name(handle));
- AcpiOsFree(buf.Pointer);
- return_ACPI_STATUS (AE_TYPE);
- }
+ /*
+ * Only use the current value if there's one IRQ. Some
+ * systems return multiple IRQs (which is nonsense for _CRS)
+ * when the link hasn't been programmed.
+ */
+ if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
+ if (res->Data.Irq.InterruptCount == 1)
+ link->l_irq = res->Data.Irq.Interrupts[0];
+ } else if (res->Data.ExtendedIrq.InterruptCount == 1)
+ link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
- if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
- *sta = devinfo->CurrentStatus;
- } else {
- kprintf("invalid status - %s\n", acpi_name(handle));
- *sta = 0;
+ /*
+ * An IRQ of zero means that the link isn't routed.
+ */
+ if (link->l_irq == 0)
+ link->l_irq = PCI_INVALID_IRQ;
+ break;
+ default:
+ req->res_index++;
}
-
- AcpiOsFree(buf.Pointer);
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
+/*
+ * Populate the set of possible IRQs for each device.
+ */
static ACPI_STATUS
-acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
- UINT8 *number_of_interrupts, UINT8 interrupts[])
+link_add_prs(ACPI_RESOURCE *res, void *context)
{
- UINT8 count;
- UINT8 i;
- UINT32 InterruptCount;
- UINT32 *Interrupts32;
- UINT8 *Interrupts8;
-
- ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- if (resources == NULL || number_of_interrupts == NULL) {
- kprintf("invalid argument\n");
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
-
- *number_of_interrupts = 0;
- InterruptCount = 0;
- Interrupts8 = NULL;
- Interrupts32 = NULL;
-
- if (resources->Type == ACPI_RESOURCE_TYPE_START_DEPENDENT)
- resources = ACPI_NEXT_RESOURCE(resources);
-
- if (resources->Type != ACPI_RESOURCE_TYPE_IRQ &&
- resources->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
- kprintf("Resource is not an IRQ entry - %d\n", resources->Type);
- return_ACPI_STATUS (AE_TYPE);
- }
-
- switch (resources->Type) {
- case ACPI_RESOURCE_TYPE_IRQ:
- InterruptCount = resources->Data.Irq.InterruptCount;
- Interrupts8 = resources->Data.Irq.Interrupts;
+ struct link_res_request *req;
+ struct link *link;
+ UINT8 *irqs = NULL;
+ UINT32 *ext_irqs = NULL;
+ int i, is_ext_irq = 1;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ req = (struct link_res_request *)context;
+ switch (res->Type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ req->in_dpf = DPF_IGNORE;
+ break;
+ }
break;
- case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
- InterruptCount =
- resources->Data.ExtendedIrq.InterruptCount;
- Interrupts32 = resources->Data.ExtendedIrq.Interrupts;
+ case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
break;
- }
-
- if (InterruptCount == 0) {
- kprintf("Blank IRQ resource\n");
- return_ACPI_STATUS (AE_NULL_ENTRY);
- }
-
- count = 0;
- for (i = 0; i < InterruptCount; i++) {
- UINT32 intr;
-
- if (i >= MAX_POSSIBLE_INTERRUPTS) {
- kprintf("too many IRQs (%d)\n", i);
+ case ACPI_RESOURCE_TYPE_IRQ:
+ is_ext_irq = 0;
+ /* fall through */
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ /*
+ * Don't parse resources if we are in a DPF set that we are
+ * ignoring.
+ */
+ if (req->in_dpf == DPF_IGNORE)
break;
+
+ KASSERT(req->link_index < req->sc->pl_num_links,
+ ("%s: array boundary violation", __func__));
+ link = &req->sc->pl_links[req->link_index];
+ if (link->l_res_index == -1) {
+ KASSERT(req->sc->pl_crs_bad,
+ ("res_index should be set"));
+ link->l_res_index = req->res_index;
}
+ req->link_index++;
+ req->res_index++;
- KKASSERT(Interrupts8 != NULL || Interrupts32 != NULL);
- if (Interrupts8 != NULL)
- intr = Interrupts8[i];
- else
- intr = Interrupts32[i];
+ /*
+ * Stash a copy of the resource for later use when doing
+ * _SRS.
+ */
+ bcopy(res, &link->l_prs_template, sizeof(ACPI_RESOURCE));
+ if (is_ext_irq) {
+ link->l_num_irqs =
+ res->Data.ExtendedIrq.InterruptCount;
+ ext_irqs = res->Data.ExtendedIrq.Interrupts;
+ } else {
+ link->l_num_irqs = res->Data.Irq.InterruptCount;
+ irqs = res->Data.Irq.Interrupts;
+ }
+ if (link->l_num_irqs == 0)
+ break;
- if (intr == 0) {
- kprintf("invalid IRQ %d\n", intr);
- continue;
+ /*
+ * Save a list of the valid IRQs. Also, if all of the
+ * valid IRQs are ISA IRQs, then mark this link as
+ * routed via an ISA interrupt.
+ */
+ link->l_isa_irq = TRUE;
+
+ link->l_irqs = kmalloc(sizeof(int) * link->l_num_irqs,
+ M_PCI_LINK, M_WAITOK | M_ZERO);
+ for (i = 0; i < link->l_num_irqs; i++) {
+ if (is_ext_irq) {
+ link->l_irqs[i] = ext_irqs[i];
+ if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
+ link->l_isa_irq = FALSE;
+ } else {
+ link->l_irqs[i] = irqs[i];
+ if (irqs[i] >= NUM_ISA_INTERRUPTS)
+ link->l_isa_irq = FALSE;
+ }
}
- interrupts[count] = intr;
- count++;
+ break;
+ default:
+ if (req->in_dpf == DPF_IGNORE)
+ break;
+ if (req->sc->pl_crs_bad)
+ device_printf(req->sc->pl_dev,
+ "Warning: possible resource %d will be lost during _SRS\n",
+ req->res_index);
+ req->res_index++;
}
- *number_of_interrupts = count;
-
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
-static ACPI_STATUS
-acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
+static int
+link_valid_irq(struct link *link, int 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) {
- kprintf("invalid argument\n");
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
+ int i;
- *irq = 0;
- buf.Pointer = NULL;
- buf.Length = ACPI_ALLOCATE_BUFFER;
- error = AcpiGetCurrentResources(link->handle, &buf);
- if (ACPI_FAILURE(error)) {
- kprintf("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) {
- kprintf("couldn't allocate memory - %s\n",
- acpi_name(link->handle));
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
+ ACPI_SERIAL_ASSERT(pci_link);
- 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)) {
- kprintf("couldn't get current IRQ from "
- "interrupt link %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
- }
+ /* Invalid interrupts are never valid. */
+ if (!PCI_INTERRUPT_VALID(irq))
+ return (FALSE);
- if (number_of_interrupts == 0) {
- kprintf("PCI interrupt link device _CRS data is corrupted - "
- "%s\n", acpi_name(link->handle));
- return_ACPI_STATUS (AE_NULL_ENTRY);
- }
+ /* Any interrupt in the list of possible interrupts is valid. */
+ for (i = 0; i < link->l_num_irqs; i++)
+ if (link->l_irqs[i] == irq)
+ return (TRUE);
- *irq = interrupts[0];
+ /*
+ * For links routed via an ISA interrupt, if the SCI is routed via
+ * an ISA interrupt, the SCI is always treated as a valid IRQ.
+ */
+ if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
+ irq < NUM_ISA_INTERRUPTS)
+ return (TRUE);
- return_ACPI_STATUS (AE_OK);
+ /* If the interrupt wasn't found in the list it is not valid. */
+ return (FALSE);
}
-static ACPI_STATUS
-acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
+static void
+acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
{
- 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);
- }
+ struct link *link;
+ char buf[16];
+ int i, j;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ if (header) {
+ ksnprintf(buf, sizeof(buf), "%s:",
+ device_get_nameunit(sc->pl_dev));
+ kprintf("%-16.16s Index IRQ Rtd Ref IRQs\n", buf);
}
-
- link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
- if (link == NULL) {
- kprintf("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)) {
- kprintf("couldn't get current IRQ from "
- "interrupt link %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- }
-
- link->initial_irq = link->current_irq;
-
- error = AcpiGetPossibleResources(handle, &buf);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get interrupt link device _PRS "
- "data %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- goto out;
- }
- if (buf.Pointer == NULL) {
- kprintf("_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)) {
- kprintf("couldn't get possible IRQs from "
- "interrupt link %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- goto out;
- }
-
- if (link->number_of_interrupts == 0) {
- kprintf("interrupt link device _PRS data is corrupted - %s\n",
- acpi_name(handle));
- error = AE_NULL_ENTRY;
- goto out;
+ for (i = 0; i < sc->pl_num_links; i++) {
+ link = &sc->pl_links[i];
+ kprintf(" %-14.14s %5d %3d %c %3d ", i == 0 ? tag : "", i,
+ link->l_irq, link->l_routed ? 'Y' : 'N',
+ link->l_references);
+ if (link->l_num_irqs == 0)
+ kprintf(" none");
+ else for (j = 0; j < link->l_num_irqs; j++)
+ kprintf(" %d", link->l_irqs[j]);
+ kprintf("\n");
}
-
- 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)
+static int
+acpi_pci_link_attach(device_t dev)
{
- ACPI_HANDLE handle;
- ACPI_STATUS error;
- UINT32 sta;
- struct acpi_prt_entry *entry;
+ struct acpi_pci_link_softc *sc;
+ struct link_count_request creq;
+ struct link_res_request rreq;
+ ACPI_STATUS status;
+ int i;
- ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- if (prt == NULL || prt->Source == NULL || prt->Source[0] == '\0') {
- kprintf("couldn't handle this routing table - hardwired\n");
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
+ sc = device_get_softc(dev);
+ sc->pl_dev = dev;
+ ACPI_SERIAL_BEGIN(pci_link);
- error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get handle - %s\n",
- AcpiFormatException(error));
- return_ACPI_STATUS (error);
+ /*
+ * Count the number of current resources so we know how big of
+ * a link array to allocate. On some systems, _CRS is broken,
+ * so for those systems try to derive the count from _PRS instead.
+ */
+ creq.in_dpf = DPF_OUTSIDE;
+ creq.count = 0;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
+ acpi_count_irq_resources, &creq);
+ sc->pl_crs_bad = ACPI_FAILURE(status);
+ if (sc->pl_crs_bad) {
+ creq.in_dpf = DPF_OUTSIDE;
+ creq.count = 0;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
+ acpi_count_irq_resources, &creq);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev,
+ "Unable to parse _CRS or _PRS: %s\n",
+ AcpiFormatException(status));
+ ACPI_SERIAL_END(pci_link);
+ return (ENXIO);
+ }
}
-
- error = acpi_pci_link_get_object_status(handle, &sta);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get object status %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
+ sc->pl_num_links = creq.count;
+ if (creq.count == 0) {
+ ACPI_SERIAL_END(pci_link);
+ return (0);
}
-
- if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0) {
- kprintf("interrupt link is not functional - %s\n",
- acpi_name(handle));
- return_ACPI_STATUS (AE_ERROR);
+ sc->pl_links = kmalloc(sizeof(struct link) * sc->pl_num_links,
+ M_PCI_LINK, M_WAITOK | M_ZERO);
+
+ /* Initialize the child links. */
+ for (i = 0; i < sc->pl_num_links; i++) {
+ sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+ sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
+ sc->pl_links[i].l_sc = sc;
+ sc->pl_links[i].l_isa_irq = FALSE;
+ sc->pl_links[i].l_res_index = -1;
}
- TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
- if (entry->busno == busno &&
- entry->prt.Address == prt->Address &&
- entry->prt.Pin == prt->Pin) {
- kprintf("interrupt link entry already exists - %s\n",
- acpi_name(handle));
- return_ACPI_STATUS (AE_ALREADY_EXISTS);
+ /* Try to read the current settings from _CRS if it is valid. */
+ if (!sc->pl_crs_bad) {
+ rreq.in_dpf = DPF_OUTSIDE;
+ rreq.link_index = 0;
+ rreq.res_index = 0;
+ rreq.sc = sc;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
+ link_add_crs, &rreq);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Unable to parse _CRS: %s\n",
+ AcpiFormatException(status));
+ goto fail;
}
}
- entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
- if (entry == NULL) {
- kprintf("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)) {
- kprintf("couldn't add _PRT entry to link %s - %s\n",
- acpi_name(handle), AcpiFormatException(error));
- goto out;
+ /*
+ * Try to read the possible settings from _PRS. Note that if the
+ * _CRS is toast, we depend on having a working _PRS. However, if
+ * _CRS works, then it is ok for _PRS to be missing.
+ */
+ rreq.in_dpf = DPF_OUTSIDE;
+ rreq.link_index = 0;
+ rreq.res_index = 0;
+ rreq.sc = sc;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
+ link_add_prs, &rreq);
+ if (ACPI_FAILURE(status) &&
+ (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
+ device_printf(dev, "Unable to parse _PRS: %s\n",
+ AcpiFormatException(status));
+ goto fail;
}
+ if (bootverbose)
+ acpi_pci_link_dump(sc, 1, "Initial Probe");
+
+ /* Verify initial IRQs if we have _PRS. */
+ if (status != AE_NOT_FOUND)
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (!link_valid_irq(&sc->pl_links[i],
+ sc->pl_links[i].l_irq))
+ sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+ if (bootverbose)
+ acpi_pci_link_dump(sc, 0, "Validation");
+
+ /* Save initial IRQs. */
+ for (i = 0; i < sc->pl_num_links; i++)
+ sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
- TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
- error = AE_OK;
-
-out:
- if (error != AE_OK && entry != NULL)
- AcpiOsFree(entry);
-
- return_ACPI_STATUS (error);
+ /*
+ * Try to disable this link. If successful, set the current IRQ to
+ * zero and flags to indicate this link is not routed. If we can't
+ * run _DIS (i.e., the method doesn't exist), assume the initial
+ * IRQ was routed by the BIOS.
+ */
+ if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
+ NULL)))
+ for (i = 0; i < sc->pl_num_links; i++)
+ sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+ else
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
+ sc->pl_links[i].l_routed = TRUE;
+ if (bootverbose)
+ acpi_pci_link_dump(sc, 0, "After Disable");
+ ACPI_SERIAL_END(pci_link);
+ return (0);
+fail:
+ ACPI_SERIAL_END(pci_link);
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (sc->pl_links[i].l_irqs != NULL)
+ kfree(sc->pl_links[i].l_irqs, M_PCI_LINK);
+ kfree(sc->pl_links, M_PCI_LINK);
+ return (ENXIO);
}
-static int
-acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+/* XXX: Note that this is identical to pci_pir_search_irq(). */
+static uint8_t
+acpi_pci_link_search_irq(int bus, int device, int pin)
{
- UINT8 i;
-
- if (irq == 0)
- return (0);
+ uint32_t value;
+ uint8_t func, maxfunc;
+
+ /* See if we have a valid device at function 0. */
+ value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
+ if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ return (PCI_INVALID_IRQ);
+ if (value & PCIM_MFDEV)
+ maxfunc = PCI_FUNCMAX;
+ else
+ maxfunc = 0;
+
+ /* Scan all possible functions at this device. */
+ for (func = 0; func <= maxfunc; func++) {
+ value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
+ if (value == 0xffffffff)
+ continue;
+ value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
- for (i = 0; i < link->number_of_interrupts; i++) {
- if (link->interrupts[i] == irq)
- return (1);
+ /*
+ * See if it uses the pin in question. Note that the passed
+ * in pin uses 0 for A, .. 3 for D whereas the intpin
+ * register uses 0 for no interrupt, 1 for A, .. 4 for D.
+ */
+ if (value != pin + 1)
+ continue;
+ value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
+ if (bootverbose)
+ kprintf(
+ "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
+ bus, device, pin + 'A', func, value);
+ if (value != PCI_INVALID_IRQ)
+ return (value);
}
-
- /* allow initial IRQ as valid one. */
- if (link->initial_irq == irq)
- return (1);
-
- return (0);
+ return (PCI_INVALID_IRQ);
}
-static ACPI_STATUS
-acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+/*
+ * Find the link structure that corresponds to the resource index passed in
+ * via 'source_index'.
+ */
+static struct link *
+acpi_pci_link_lookup(device_t dev, int source_index)
{
- 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)) {
- kprintf("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)) {
- kprintf("couldn't get current IRQ from "
- "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;
-
- switch (link->possible_resources.Type) {
- case ACPI_RESOURCE_TYPE_IRQ:
- resbuf.Type = ACPI_RESOURCE_TYPE_IRQ;
- resbuf.Length = sizeof(ACPI_RESOURCE_IRQ);
-
- /* structure copy other fields */
- resbuf.Data.Irq = link->possible_resources.Data.Irq;
- resbuf.Data.Irq.InterruptCount = 1;
- resbuf.Data.Irq.Interrupts[0] = irq;
- break;
- case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
- resbuf.Type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
- resbuf.Length = sizeof(ACPI_RESOURCE_EXTENDED_IRQ);
-
- /* structure copy other fields */
- resbuf.Data.ExtendedIrq =
- link->possible_resources.Data.ExtendedIrq;
- resbuf.Data.ExtendedIrq.InterruptCount = 1;
- resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
- break;
- default:
- kprintf("Resource is not an IRQ entry %s - %d\n",
- acpi_name(link->handle), link->possible_resources.Type);
- return_ACPI_STATUS (AE_TYPE);
- }
-
- error = acpi_AppendBufferResource(&crsbuf, &resbuf);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't setup buffer by "
- "acpi_AppendBufferResource - %s\n",
- acpi_name(link->handle));
- return_ACPI_STATUS (error);
- }
- if (crsbuf.Pointer == NULL) {
- kprintf("appended buffer for %s is corrupted\n",
- acpi_name(link->handle));
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
-
- error = AcpiSetCurrentResources(link->handle, &crsbuf);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't set link device _SRS %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
- }
+ struct acpi_pci_link_softc *sc;
+ int i;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (sc->pl_links[i].l_res_index == source_index)
+ return (&sc->pl_links[i]);
+ return (NULL);
+}
- AcpiOsFree(crsbuf.Pointer);
- link->current_irq = 0;
+void
+acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
+ int pin)
+{
+ struct link *link;
+ uint8_t bios_irq;
+ uintptr_t bus;
- error = acpi_pci_link_get_object_status(link->handle, &sta);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get object status %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
+ /*
+ * Look up the PCI bus for the specified PCI bridge device. Note
+ * that the PCI bridge device might not have any children yet.
+ * However, looking up its bus number doesn't require a valid child
+ * device, so we just pass NULL.
+ */
+ if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
+ device_printf(pcib, "Unable to read PCI bus number");
+ panic("PCI bridge without a bus number");
}
- if ((sta & ACPI_STA_ENABLE) == 0) {
- kprintf("interrupt link %s is disabled\n",
- acpi_name(link->handle));
- return_ACPI_STATUS (AE_ERROR);
+ /* Bump the reference count. */
+ ACPI_SERIAL_BEGIN(pci_link);
+ link = acpi_pci_link_lookup(dev, index);
+ if (link == NULL) {
+ device_printf(dev, "apparently invalid index %d\n", index);
+ ACPI_SERIAL_END(pci_link);
+ return;
}
+ link->l_references++;
+ if (link->l_routed)
+ pci_link_interrupt_weights[link->l_irq]++;
- error = acpi_pci_link_get_current_irq(link, &link->current_irq);
- if (ACPI_FAILURE(error)) {
- kprintf("couldn't get current IRQ from "
- "interrupt link %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error));
- return_ACPI_STATUS (error);
+ /*
+ * The BIOS only routes interrupts via ISA IRQs using the ATPICs
+ * (8259As). Thus, if this link is routed via an ISA IRQ, go
+ * look to see if the BIOS routed an IRQ for this link at the
+ * indicated (bus, slot, pin). If so, we prefer that IRQ for
+ * this link and add that IRQ to our list of known-good IRQs.
+ * This provides a good work-around for link devices whose _CRS
+ * method is either broken or bogus. We only use the value
+ * returned by _CRS if we can't find a valid IRQ via this method
+ * in fact.
+ *
+ * If this link is not routed via an ISA IRQ (because we are using
+ * APIC for example), then don't bother looking up the BIOS IRQ
+ * as if we find one it won't be valid anyway.
+ */
+ if (!link->l_isa_irq) {
+ ACPI_SERIAL_END(pci_link);
+ return;
}
- if (link->current_irq == irq) {
- error = AE_OK;
- } else {
- kprintf("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;
+ /* Try to find a BIOS IRQ setting from any matching devices. */
+ bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
+ if (!PCI_INTERRUPT_VALID(bios_irq)) {
+ ACPI_SERIAL_END(pci_link);
+ return;
}
- return_ACPI_STATUS (error);
+ /* Validate the BIOS IRQ. */
+ if (!link_valid_irq(link, bios_irq)) {
+ device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
+ bios_irq, (int)bus, slot, pin + 'A');
+ } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
+ link->l_bios_irq = bios_irq;
+ if (bios_irq < NUM_ISA_INTERRUPTS)
+ pci_link_bios_isa_irqs |= (1 << bios_irq);
+ if (bios_irq != link->l_initial_irq &&
+ PCI_INTERRUPT_VALID(link->l_initial_irq))
+ device_printf(dev,
+ "BIOS IRQ %u does not match initial IRQ %u\n",
+ bios_irq, link->l_initial_irq);
+ } else if (bios_irq != link->l_bios_irq)
+ device_printf(dev,
+ "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
+ bios_irq, (int)bus, slot, pin + 'A',
+ link->l_bios_irq);
+ ACPI_SERIAL_END(pci_link);
}
-/*
- * Auto arbitration for boot-disabled devices
- */
-
-static void
-acpi_pci_link_bootdisabled_dump(void)
+static ACPI_STATUS
+acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
{
- 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;
+ ACPI_RESOURCE *resource, *end, newres, *resptr;
+ ACPI_BUFFER crsbuf;
+ ACPI_STATUS status;
+ struct link *link;
+ int i, in_dpf;
+
+ /* Fetch the _CRS. */
+ ACPI_SERIAL_ASSERT(pci_link);
+ crsbuf.Pointer = NULL;
+ crsbuf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf);
+ if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL)
+ status = AE_NO_MEMORY;
+ if (ACPI_FAILURE(status)) {
+ if (bootverbose)
+ device_printf(sc->pl_dev,
+ "Unable to fetch current resources: %s\n",
+ AcpiFormatException(status));
+ return (status);
+ }
- kprintf("%s:\n", acpi_name(link->handle));
- kprintf(" interrupts: ");
- for (i = 0; i < link->number_of_interrupts; i++) {
- irq = link->sorted_irq[i];
- kprintf("%6d", irq);
+ /* Fill in IRQ resources via link structures. */
+ srsbuf->Pointer = NULL;
+ link = sc->pl_links;
+ i = 0;
+ in_dpf = DPF_OUTSIDE;
+ resource = (ACPI_RESOURCE *)crsbuf.Pointer;
+ end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length);
+ for (;;) {
+ switch (resource->Type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ switch (in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ panic(
+ "%s: Multiple dependent functions within a current resource",
+ __func__);
+ break;
+ }
+ resptr = NULL;
+ break;
+ case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+ /* We are finished with DPF parsing. */
+ KASSERT(in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ in_dpf = DPF_OUTSIDE;
+ resptr = NULL;
+ break;
+ case ACPI_RESOURCE_TYPE_IRQ:
+ MPASS(i < sc->pl_num_links);
+ MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ);
+ newres = link->l_prs_template;
+ resptr = &newres;
+ resptr->Data.Irq.InterruptCount = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq)) {
+ KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
+ ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
+ __func__, link->l_irq));
+ resptr->Data.Irq.Interrupts[0] = link->l_irq;
+ } else
+ resptr->Data.Irq.Interrupts[0] = 0;
+ link++;
+ i++;
+ break;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ MPASS(i < sc->pl_num_links);
+ MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ);
+ newres = link->l_prs_template;
+ resptr = &newres;
+ resptr->Data.ExtendedIrq.InterruptCount = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq))
+ resptr->Data.ExtendedIrq.Interrupts[0] =
+ link->l_irq;
+ else
+ resptr->Data.ExtendedIrq.Interrupts[0] = 0;
+ link++;
+ i++;
+ break;
+ default:
+ resptr = resource;
}
- kprintf("\n");
- kprintf(" penalty: ");
- for (i = 0; i < link->number_of_interrupts; i++) {
- irq = link->sorted_irq[i];
- kprintf("%6d", irq_penalty[irq]);
+ if (resptr != NULL) {
+ status = acpi_AppendBufferResource(srsbuf, resptr);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->pl_dev,
+ "Unable to build resources: %s\n",
+ AcpiFormatException(status));
+ if (srsbuf->Pointer != NULL)
+ AcpiOsFree(srsbuf->Pointer);
+ AcpiOsFree(crsbuf.Pointer);
+ return (status);
+ }
}
- kprintf("\n");
- kprintf(" references: %d\n", link->references);
- kprintf(" priority: %d\n", link->priority);
+ if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
+ break;
+ resource = ACPI_NEXT_RESOURCE(resource);
+ if (resource >= end)
+ break;
}
+ AcpiOsFree(crsbuf.Pointer);
+ return (AE_OK);
}
-static void
-acpi_pci_link_init_irq_penalty(void)
+static ACPI_STATUS
+acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
+ ACPI_BUFFER *srsbuf)
{
- 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;
+ ACPI_RESOURCE newres;
+ ACPI_STATUS status;
+ struct link *link;
+ int i;
+
+ /* Start off with an empty buffer. */
+ srsbuf->Pointer = NULL;
+ link = sc->pl_links;
+ for (i = 0; i < sc->pl_num_links; i++) {
+
+ /* Add a new IRQ resource from each link. */
+ link = &sc->pl_links[i];
+ newres = link->l_prs_template;
+ if (newres.Type == ACPI_RESOURCE_TYPE_IRQ) {
+
+ /* Build an IRQ resource. */
+ newres.Data.Irq.InterruptCount = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq)) {
+ KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
+ ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
+ __func__, link->l_irq));
+ newres.Data.Irq.Interrupts[0] = link->l_irq;
+ } else
+ newres.Data.Irq.Interrupts[0] = 0;
+ } else {
+
+ /* Build an ExtIRQ resuorce. */
+ newres.Data.ExtendedIrq.InterruptCount = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq))
+ newres.Data.ExtendedIrq.Interrupts[0] =
+ link->l_irq;
+ else
+ newres.Data.ExtendedIrq.Interrupts[0] = 0;
}
- /* 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;
+ /* Add the new resource to the end of the _SRS buffer. */
+ status = acpi_AppendBufferResource(srsbuf, &newres);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->pl_dev,
+ "Unable to build resources: %s\n",
+ AcpiFormatException(status));
+ if (srsbuf->Pointer != NULL)
+ AcpiOsFree(srsbuf->Pointer);
+ return (status);
}
}
+ return (AE_OK);
}
-static int
-link_exclusive(ACPI_RESOURCE *res)
-{
- if (res == NULL ||
- (res->Type != ACPI_RESOURCE_TYPE_IRQ &&
- res->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ))
- return (0);
-
- if ((res->Type == ACPI_RESOURCE_TYPE_IRQ &&
- res->Data.Irq.Sharable == ACPI_EXCLUSIVE) ||
- (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ &&
- res->Data.ExtendedIrq.Sharable == ACPI_EXCLUSIVE))
- return (1);
-
- return (0);
-}
-
-static void
-acpi_pci_link_update_irq_penalty(device_t dev, int busno)
+static ACPI_STATUS
+acpi_pci_link_route_irqs(device_t dev)
{
- 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;
-
- /* Impossible? */
- link = entry->pci_link;
- if (link == NULL)
- continue;
-
- if (link->current_irq != 0) {
- /* not boot-disabled link, we will use this IRQ. */
- irq_penalty[link->current_irq] += 100;
- continue;
- }
+ struct acpi_pci_link_softc *sc;
+ ACPI_RESOURCE *resource, *end;
+ ACPI_BUFFER srsbuf;
+ ACPI_STATUS status;
+ struct link *link;
+ int i;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ sc = device_get_softc(dev);
+ if (sc->pl_crs_bad)
+ status = acpi_pci_link_srs_from_links(sc, &srsbuf);
+ else
+ status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
+
+ /* Write out new resources via _SRS. */
+ status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Unable to route IRQs: %s\n",
+ AcpiFormatException(status));
+ AcpiOsFree(srsbuf.Pointer);
+ return (status);
+ }
- /* 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 (link_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;
+ /*
+ * Perform acpi_config_intr() on each IRQ resource if it was just
+ * routed for the first time.
+ */
+ link = sc->pl_links;
+ i = 0;
+ resource = (ACPI_RESOURCE *)srsbuf.Pointer;
+ end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
+ for (;;) {
+ if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
+ break;
+ switch (resource->Type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ MPASS(i < sc->pl_num_links);
+
+ /*
+ * Only configure the interrupt and update the
+ * weights if this link has a valid IRQ and was
+ * previously unrouted.
+ */
+ if (!link->l_routed &&
+ PCI_INTERRUPT_VALID(link->l_irq)) {
+ link->l_routed = TRUE;
+ acpi_config_intr(dev, resource);
+ pci_link_interrupt_weights[link->l_irq] +=
+ link->l_references;
}
+ link++;
+ i++;
+ break;
}
-
- /* initialize `sorted' possible IRQs. */
- bcopy(link->interrupts, link->sorted_irq,
- sizeof(link->sorted_irq));
+ resource = ACPI_NEXT_RESOURCE(resource);
+ if (resource >= end)
+ break;
}
+ AcpiOsFree(srsbuf.Pointer);
+ return (AE_OK);
}
-static void
-acpi_pci_link_set_bootdisabled_priority(void)
+static int
+acpi_pci_link_resume(device_t dev)
{
- 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) {
- kprintf("ACPI PCI link before setting link priority:\n");
- acpi_pci_link_bootdisabled_dump();
- }
+ struct acpi_pci_link_softc *sc;
+ ACPI_STATUS status;
+ int i, routed;
- /* reset priority for all links. */
- TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
- link->priority = 0;
+ /*
+ * If all of our links are routed, then restore the link via _SRS,
+ * otherwise, disable the link via _DIS.
+ */
+ ACPI_SERIAL_BEGIN(pci_link);
+ sc = device_get_softc(dev);
+ routed = 0;
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (sc->pl_links[i].l_routed)
+ routed++;
+ if (routed == sc->pl_num_links)
+ status = acpi_pci_link_route_irqs(dev);
+ else {
+ AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
+ status = AE_OK;
+ }
+ ACPI_SERIAL_END(pci_link);
+ if (ACPI_FAILURE(status))
+ return (ENXIO);
+ else
+ return (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;
+/*
+ * Pick an IRQ to use for this unrouted link.
+ */
+static uint8_t
+acpi_pci_link_choose_irq(device_t dev, struct link *link)
+{
+ char tunable_buffer[64], link_name[5];
+ u_int8_t best_irq, pos_irq;
+ int best_weight, pos_weight, i;
+
+ KASSERT(!link->l_routed, ("%s: link already routed", __func__));
+ KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
+ ("%s: link already has an IRQ", __func__));
+
+ /* Check for a tunable override. */
+ if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
+ sizeof(link_name)))) {
+ ksnprintf(tunable_buffer, sizeof(tunable_buffer),
+ "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
+ if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
+ if (!link_valid_irq(link, i))
+ device_printf(dev,
+ "Warning, IRQ %d is not listed as valid\n",
+ i);
+ return (i);
}
-
- /*
- * 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];
+ ksnprintf(tunable_buffer, sizeof(tunable_buffer),
+ "hw.pci.link.%s.irq", link_name);
+ if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
+ if (!link_valid_irq(link, i))
+ device_printf(dev,
+ "Warning, IRQ %d is not listed as valid\n",
+ i);
+ return (i);
}
-
- 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?
+ * If we have a valid BIOS IRQ, use that. We trust what the BIOS
+ * says it routed over what _CRS says the link thinks is routed.
*/
- 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);
- }
+ if (PCI_INTERRUPT_VALID(link->l_bios_irq))
+ return (link->l_bios_irq);
- 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) {
- kprintf("ACPI PCI link before fixup for boot-disabled links:\n");
- acpi_pci_link_bootdisabled_dump();
- }
+ /*
+ * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
+ * then use that.
+ */
+ if (PCI_INTERRUPT_VALID(link->l_initial_irq))
+ return (link->l_initial_irq);
- TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
- /* ignore non boot-disabled links. */
- if (link->current_irq != 0)
+ /*
+ * Ok, we have no useful hints, so we have to pick from the
+ * possible IRQs. For ISA IRQs we only use interrupts that
+ * have already been used by the BIOS.
+ */
+