From 10f976749fd9ad2e8642ea80ce533f7416910a65 Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Sun, 8 Nov 2009 23:26:01 +0300 Subject: [PATCH] Sync ACPI with FreeBSD 7.2 --- sys/dev/acpica5/Osd/OsdInterrupt.c | 6 +- sys/dev/acpica5/acpi.c | 2127 ++++++++++++++---------- sys/dev/acpica5/acpi_acad.c | 99 +- sys/dev/acpica5/acpi_asus/acpi_asus.c | 601 +++++-- sys/dev/acpica5/acpi_battery.c | 21 +- sys/dev/acpica5/acpi_button.c | 64 +- sys/dev/acpica5/acpi_cmbat.c | 39 +- sys/dev/acpica5/acpi_cpu_cstate.c | 11 +- sys/dev/acpica5/acpi_cpu_pstate.c | 10 +- sys/dev/acpica5/acpi_ec.c | 704 ++++---- sys/dev/acpica5/acpi_hpet.c | 2 +- sys/dev/acpica5/acpi_if.m | 76 +- sys/dev/acpica5/acpi_isab.c | 29 +- sys/dev/acpica5/acpi_lid.c | 41 +- sys/dev/acpica5/acpi_package.c | 34 +- sys/dev/acpica5/acpi_pci.c | 313 +++- sys/dev/acpica5/acpi_pci_link.c | 2150 +++++++++++++------------ sys/dev/acpica5/acpi_pcib.c | 715 ++++---- sys/dev/acpica5/acpi_pcib_acpi.c | 133 +- sys/dev/acpica5/acpi_pcib_pci.c | 49 +- sys/dev/acpica5/acpi_pcibvar.h | 17 +- sys/dev/acpica5/acpi_powerres.c | 208 ++- sys/dev/acpica5/acpi_resource.c | 272 ++-- sys/dev/acpica5/acpi_thermal.c | 799 ++++++--- sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c | 812 +++++----- sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c | 75 +- sys/dev/acpica5/acpiio.h | 46 +- sys/dev/acpica5/acpivar.h | 229 ++- 28 files changed, 5746 insertions(+), 3936 deletions(-) rewrite sys/dev/acpica5/acpi_pci_link.c (88%) rewrite sys/dev/acpica5/acpi_pcib.c (72%) diff --git a/sys/dev/acpica5/Osd/OsdInterrupt.c b/sys/dev/acpica5/Osd/OsdInterrupt.c index 06aa6859a6..78a76feef9 100644 --- a/sys/dev/acpica5/Osd/OsdInterrupt.c +++ b/sys/dev/acpica5/Osd/OsdInterrupt.c @@ -157,9 +157,7 @@ acpi_OverrideInterruptLevel(UINT32 InterruptNumber) static void InterruptWrapper(void *arg) { - ACPI_LOCK_DECL; - - ACPI_LOCK; + crit_enter(); InterruptHandler(arg); - ACPI_UNLOCK; + crit_exit(); } diff --git a/sys/dev/acpica5/acpi.c b/sys/dev/acpica5/acpi.c index c5adcf2174..fd22ed0373 100644 --- a/sys/dev/acpica5/acpi.c +++ b/sys/dev/acpica5/acpi.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 2000 Takanori Watanabe - * Copyright (c) 2000 Mitsuru IWASAKI + * Copyright (c) 2000 Takanori Watanabe + * Copyright (c) 2000 Mitsuru IWASAKI * Copyright (c) 2000, 2001 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. @@ -25,43 +25,53 @@ * 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 + #include "opt_acpi.h" #include #include #include #include #include +#include #include #include -#include +#include #include #include #include #include #include #include -#include - -#include -#include +#include +#include +#include -#include -#include +#include #include +#include #include "acpi.h" -#include "accommon.h" #include #include -#include +#include "achware.h" +#include "acnamesp.h" +#include "acglobal.h" + +#include "pci_if.h" +#include +#include + +#include 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") @@ -72,87 +82,71 @@ static d_ioctl_t acpiioctl; #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); @@ -170,12 +164,14 @@ static device_method_t acpi_methods[] = { 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), @@ -183,9 +179,9 @@ static device_method_t acpi_methods[] = { 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), @@ -193,6 +189,12 @@ static device_method_t acpi_methods[] = { /* 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), @@ -210,10 +212,17 @@ static devclass_t acpi_devclass; 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"); @@ -228,6 +237,19 @@ SYSCTL_STRING(_debug_acpi, OID_AUTO, acpi_ca_version, CTLFLAG_RD, 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. @@ -237,7 +259,7 @@ TUNABLE_INT("hw.acpi.serialize_methods", &acpi_serialize_methods); 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"); @@ -260,234 +282,192 @@ acpi_modevent(struct module *mod, int event, void *junk) 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); @@ -512,31 +492,21 @@ acpi_attach(device_t dev) } /* - * 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)); @@ -546,9 +516,14 @@ acpi_attach(device_t dev) /* * 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)); @@ -583,38 +558,42 @@ acpi_attach(device_t dev) 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 @@ -627,7 +606,7 @@ acpi_attach(device_t dev) /* 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; @@ -638,18 +617,8 @@ acpi_attach(device_t dev) /* * 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); @@ -666,21 +635,11 @@ acpi_attach(device_t dev) 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; @@ -693,91 +652,114 @@ acpi_attach(device_t dev) 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); } @@ -793,11 +775,52 @@ acpi_print_child(device_t bus, device_t 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, @@ -806,9 +829,9 @@ 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); } @@ -825,16 +848,14 @@ acpi_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, 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); @@ -860,11 +881,14 @@ acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) *(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: @@ -895,11 +919,14 @@ acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 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); @@ -920,69 +947,151 @@ acpi_get_rlist(device_t dev, device_t child) 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); } @@ -990,63 +1099,99 @@ static int 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) { @@ -1055,7 +1200,6 @@ 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__); @@ -1063,8 +1207,6 @@ acpi_isa_get_logicalid(device_t dev) buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; - ACPI_LOCK; - /* Fetch and validate the HID. */ if ((h = acpi_get_handle(dev)) == NULL) goto out; @@ -1079,7 +1221,6 @@ acpi_isa_get_logicalid(device_t dev) out: if (buf.Pointer != NULL) AcpiOsFree(buf.Pointer); - ACPI_UNLOCK; return_VALUE (pnpid); } @@ -1092,7 +1233,6 @@ acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count) ACPI_STATUS error; uint32_t *pnpid; int valid, i; - ACPI_LOCK_DECL; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); @@ -1101,8 +1241,6 @@ acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count) buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; - ACPI_LOCK; - /* Fetch and validate the CID */ if ((h = acpi_get_handle(dev)) == NULL) goto out; @@ -1125,7 +1263,6 @@ acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count) out: if (buf.Pointer != NULL) AcpiOsFree(buf.Pointer); - ACPI_UNLOCK; return_VALUE (valid); } @@ -1147,6 +1284,172 @@ acpi_device_id_probe(device_t bus, device_t dev, char **ids) 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) { @@ -1180,25 +1483,26 @@ 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 @@ -1210,21 +1514,16 @@ acpi_probe_children(device_t bus) * 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); @@ -1242,26 +1541,29 @@ acpi_probe_children(device_t 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; } /* @@ -1271,22 +1573,22 @@ acpi_probe_order(ACPI_HANDLE handle, int level, int *order) 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: @@ -1294,13 +1596,29 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) 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; @@ -1309,16 +1627,26 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) 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; } @@ -1331,10 +1659,6 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) * 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; } } @@ -1342,25 +1666,6 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) 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. @@ -1373,14 +1678,15 @@ acpi_fake_objhandler(ACPI_HANDLE h, UINT32 fn, void *data) 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)) { @@ -1389,45 +1695,41 @@ acpi_shutdown_final(void *arg, int howto) 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); @@ -1461,8 +1763,6 @@ acpi_DeviceIsPresent(device_t dev) ACPI_STATUS error; int ret; - ACPI_ASSERTLOCK; - ret = FALSE; if ((h = acpi_get_handle(dev)) == NULL) return (FALSE); @@ -1478,7 +1778,7 @@ acpi_DeviceIsPresent(device_t dev) ret = TRUE; /* Return true for 'present' and 'functioning' */ - if ((devinfo->CurrentStatus & 0x9) == 0x9) + if (ACPI_DEVICE_PRESENT(devinfo->CurrentStatus)) ret = TRUE; AcpiOsFree(buf.Pointer); @@ -1497,8 +1797,6 @@ acpi_BatteryIsPresent(device_t dev) ACPI_STATUS error; int ret; - ACPI_ASSERTLOCK; - ret = FALSE; if ((h = acpi_get_handle(dev)) == NULL) return (FALSE); @@ -1513,8 +1811,8 @@ acpi_BatteryIsPresent(device_t dev) 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); @@ -1525,15 +1823,13 @@ acpi_BatteryIsPresent(device_t dev) * 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); @@ -1570,8 +1866,6 @@ acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result) ACPI_HANDLE r; ACPI_STATUS status; - ACPI_ASSERTLOCK; - /* Walk back up the tree to the root */ for (;;) { status = AcpiGetHandle(parent, path, &r); @@ -1579,6 +1873,7 @@ acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result) *result = r; return (AE_OK); } + /* XXX Return error here? */ if (status != AE_NOT_FOUND) return (AE_OK); if (ACPI_FAILURE(AcpiGetParent(parent, &r))) @@ -1595,10 +1890,10 @@ acpi_TimerDelta(uint32_t end, uint32_t start) 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); } @@ -1610,7 +1905,8 @@ acpi_AllocBuffer(int size) { 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); @@ -1622,8 +1918,6 @@ acpi_SetInteger(ACPI_HANDLE handle, char *path, UINT32 number) ACPI_OBJECT arg1; ACPI_OBJECT_LIST args; - ACPI_ASSERTLOCK; - arg1.Type = ACPI_TYPE_INTEGER; arg1.Integer.Value = number; args.Count = 1; @@ -1642,8 +1936,6 @@ acpi_GetInteger(ACPI_HANDLE handle, char *path, UINT32 *number) ACPI_BUFFER buf; ACPI_OBJECT param; - ACPI_ASSERTLOCK; - if (handle == NULL) handle = ACPI_ROOT_OBJECT; @@ -1718,7 +2010,7 @@ acpi_ForeachPackageObject(ACPI_OBJECT *pkg, { ACPI_OBJECT *comp; int i; - + if (pkg == NULL || pkg->Type != ACPI_TYPE_PACKAGE) return (AE_BAD_PARAMETER); @@ -1743,7 +2035,7 @@ acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp) 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); @@ -1773,7 +2065,7 @@ acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res) { ACPI_RESOURCE *rp; void *newp; - + /* Initialise the buffer if necessary. */ if (buf->Pointer == NULL) { buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE; @@ -1785,7 +2077,7 @@ acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res) } if (res == NULL) return (AE_OK); - + /* * Scan the current buffer looking for the terminator. * This will either find the terminator or hit the end @@ -1824,64 +2116,247 @@ acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res) 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); @@ -1893,23 +2368,28 @@ acpi_SetSleepState(struct acpi_softc *sc, int 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)) { @@ -1917,31 +2397,27 @@ acpi_SetSleepState(struct acpi_softc *sc, int state) 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: /* @@ -1956,11 +2432,31 @@ acpi_SetSleepState(struct acpi_softc *sc, int state) 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); } @@ -1970,10 +2466,6 @@ acpi_wake_init(device_t dev, int type) { 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); @@ -1992,153 +2484,126 @@ int 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); } @@ -2149,30 +2614,34 @@ acpi_wake_sysctl_walk(device_t dev) 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) @@ -2181,7 +2650,7 @@ 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) @@ -2191,16 +2660,15 @@ acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS) 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); @@ -2273,8 +2741,15 @@ acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw) 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) @@ -2283,50 +2758,6 @@ out: } /* - * 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 */ @@ -2335,26 +2766,30 @@ acpi_Disable(struct acpi_softc *sc) 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; } @@ -2411,27 +2846,21 @@ acpi_event_sleep_button_wake(void *context) } /* - * 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)"); } /* @@ -2447,19 +2876,18 @@ acpi_avoid(ACPI_HANDLE handle) 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); @@ -2473,9 +2901,7 @@ acpi_avoid(ACPI_HANDLE handle) } /* - * 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) @@ -2519,31 +2945,31 @@ 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); @@ -2568,42 +2994,43 @@ struct acpi_ioctl_hook 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 @@ -2623,88 +3050,77 @@ acpiioctl(struct dev_ioctl_args *ap) { 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); } @@ -2716,27 +3132,21 @@ acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) 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); @@ -2808,6 +3218,8 @@ static struct debugtag dbg_layer[] = { }; 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}, @@ -2889,14 +3301,10 @@ acpi_parse_debug(char *cp, struct debugtag *tag, UINT32 *flag) } } -/* - * 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; @@ -2923,8 +3331,9 @@ acpi_set_debugging(void *junk) } 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) @@ -2944,6 +3353,7 @@ 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) { @@ -2962,17 +3372,19 @@ acpi_debug_sysctl(SYSCTL_HANDLER_ARGS) /* 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, ...) @@ -2993,7 +3405,7 @@ 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: @@ -3010,7 +3422,8 @@ acpi_pm_func(u_long cmd, void *arg, ...) goto out; } - acpi_SetSleepState(sc, acpi_state); + if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state))) + error = ENXIO; break; default: error = EINVAL; diff --git a/sys/dev/acpica5/acpi_acad.c b/sys/dev/acpica5/acpi_acad.c index 7327309cf9..6fc7b7b9a1 100644 --- a/sys/dev/acpica5/acpi_acad.c +++ b/sys/dev/acpica5/acpi_acad.c @@ -22,24 +22,29 @@ * 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 + #include "opt_acpi.h" #include #include #include + #include +#include #include #include #include #include #include "acpi.h" + #include #include +#include +#include /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_AC_ADAPTER @@ -48,13 +53,10 @@ ACPI_MODULE_NAME("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 *); @@ -64,6 +66,7 @@ static int acpi_acad_attach(device_t); static int acpi_acad_ioctl(u_long, caddr_t, void *); static int acpi_acad_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_acad_init_acline(void *arg); +static void acpi_acad_ac_only(void *arg); static device_method_t acpi_acad_methods[] = { /* Device interface */ @@ -83,6 +86,10 @@ static devclass_t acpi_acad_devclass; 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) { @@ -94,22 +101,20 @@ 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 @@ -119,8 +124,8 @@ acpi_acad_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 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); @@ -134,12 +139,14 @@ acpi_acad_notify_handler(ACPI_HANDLE h, UINT32 notify, void *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 @@ -151,8 +158,6 @@ acpi_acad_attach(device_t dev) 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); @@ -169,15 +174,12 @@ acpi_acad_attach(device_t 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); @@ -192,8 +194,6 @@ acpi_acad_ioctl(u_long cmd, caddr_t addr, void *arg) dev = (device_t)arg; sc = device_get_softc(dev); - if (sc == NULL) - return (ENXIO); /* * No security check required: information retrieval only. If @@ -229,31 +229,38 @@ acpi_acad_init_acline(void *arg) { 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 @@ -266,8 +273,6 @@ acpi_acad_get_acline(int *status) if (dev == NULL) return (ENXIO); sc = device_get_softc(dev); - if (sc == NULL) - return (ENXIO); acpi_acad_get_status(dev); *status = sc->status; diff --git a/sys/dev/acpica5/acpi_asus/acpi_asus.c b/sys/dev/acpica5/acpi_asus/acpi_asus.c index 1778dc09bd..2878421b18 100644 --- a/sys/dev/acpica5/acpi_asus/acpi_asus.c +++ b/sys/dev/acpica5/acpi_asus/acpi_asus.c @@ -22,8 +22,7 @@ * 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 @@ -44,34 +43,34 @@ #include "opt_acpi.h" #include #include -#include -#include #include -#include -#include -#include -#include +#include #include -#include -#include #include "acpi.h" #include "accommon.h" #include "acpivar.h" -#include "acpi_if.h" + +#if defined(__FreeBSD__) +#include +#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; @@ -86,6 +85,20 @@ struct acpi_asus_model { 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 { @@ -95,6 +108,8 @@ 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, @@ -104,21 +119,31 @@ struct acpi_asus_led { 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. @@ -155,16 +180,39 @@ static struct acpi_asus_model acpi_asus_models[] = { .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", @@ -191,6 +239,20 @@ static struct acpi_asus_model acpi_asus_models[] = { .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", @@ -201,6 +263,21 @@ static struct acpi_asus_model acpi_asus_models[] = { .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", @@ -262,7 +339,7 @@ static struct acpi_asus_model acpi_asus_models[] = { }, { .name = "L8L" - /* Only has hotkeys, apparantly */ + /* Only has hotkeys, apparently */ }, { .name = "M1A", @@ -363,6 +440,8 @@ static struct acpi_asus_model acpi_samsung_models[] = { { .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. @@ -371,7 +450,14 @@ static struct acpi_asus_model acpi_eeepc_models[] = { { .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 } @@ -381,31 +467,59 @@ static struct { 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); @@ -415,27 +529,24 @@ static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val) 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) { @@ -445,22 +556,23 @@ 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; @@ -481,7 +593,7 @@ acpi_asus_probe(device_t dev) 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); @@ -495,7 +607,7 @@ acpi_asus_probe(device_t dev) return (0); } - /* if EeePC */ + /* EeePC */ if (strncmp("ASUS010", rstr, 7) == 0) { sc->model = &acpi_eeepc_models[0]; device_set_desc(dev, "ASUS EeePC"); @@ -504,7 +616,7 @@ acpi_asus_probe(device_t dev) } } - sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + sb = sbuf_new_auto(); if (sb == NULL) return (ENOMEM); @@ -526,7 +638,7 @@ good: 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 @@ -545,6 +657,9 @@ good: 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; @@ -610,40 +725,6 @@ 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; @@ -668,21 +749,36 @@ acpi_asus_attach(device_t dev) 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) { @@ -698,7 +794,8 @@ acpi_asus_attach(device_t dev) 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) { @@ -706,22 +803,149 @@ acpi_asus_attach(device_t dev) 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) { @@ -730,14 +954,14 @@ 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); @@ -749,7 +973,7 @@ acpi_asus_sysctl(SYSCTL_HANDLER_ARGS) error = acpi_asus_sysctl_set(sc, method, arg); out: - lockmgr(&asuslock, LK_RELEASE); + ACPI_SERIAL_END(asus); return (error); } @@ -759,7 +983,7 @@ acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 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: @@ -771,6 +995,15 @@ acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 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); @@ -779,12 +1012,17 @@ acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 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: @@ -833,6 +1071,36 @@ acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 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); @@ -875,37 +1143,80 @@ acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) } 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); } @@ -923,7 +1234,7 @@ acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 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"); @@ -936,9 +1247,63 @@ acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) } 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); } diff --git a/sys/dev/acpica5/acpi_battery.c b/sys/dev/acpica5/acpi_battery.c index 0122c81a1a..93a58a9993 100644 --- a/sys/dev/acpica5/acpi_battery.c +++ b/sys/dev/acpica5/acpi_battery.c @@ -23,20 +23,21 @@ * 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 + #include "opt_acpi.h" #include #include #include #include +#include #include -#include #include "acpi.h" + #include #include @@ -49,6 +50,8 @@ static struct acpi_battinfo acpi_battery_battinfo; 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); @@ -63,16 +66,17 @@ acpi_battery_register(device_t dev) 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); } @@ -90,6 +94,7 @@ acpi_battery_get_units(void) int acpi_battery_get_info_expire(void) { + return (acpi_battery_info_expire); } @@ -193,7 +198,7 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo) * 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; @@ -434,6 +439,8 @@ acpi_battery_init(void) device_t dev; int error; + ACPI_SERIAL_ASSERT(battery); + error = ENXIO; dev = devclass_get_device(devclass_find("acpi"), 0); if (dev == NULL) diff --git a/sys/dev/acpica5/acpi_button.c b/sys/dev/acpica5/acpi_button.c index 567b76bb93..1ccc1e498a 100644 --- a/sys/dev/acpica5/acpi_button.c +++ b/sys/dev/acpica5/acpi_button.c @@ -24,11 +24,11 @@ * 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 + #include "opt_acpi.h" #include #include @@ -37,6 +37,7 @@ #include "acpi.h" #include "accommon.h" + #include /* Hooks for the ACPI CA debugging infrastructure */ @@ -66,6 +67,11 @@ static ACPI_STATUS 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), @@ -91,34 +97,31 @@ MODULE_DEPEND(acpi_button, acpi, 1, 1, 1); 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 @@ -170,17 +173,12 @@ acpi_button_attach(device_t dev) 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); } diff --git a/sys/dev/acpica5/acpi_cmbat.c b/sys/dev/acpica5/acpi_cmbat.c index cf2ffce2e9..a69b4bcb80 100644 --- a/sys/dev/acpica5/acpi_cmbat.c +++ b/sys/dev/acpica5/acpi_cmbat.c @@ -25,19 +25,20 @@ * 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 + #include "opt_acpi.h" #include #include #include #include +#include + #include #include -#include #include "acpi.h" #include @@ -67,6 +68,8 @@ struct acpi_cmbat_softc { 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); @@ -205,6 +208,8 @@ acpi_cmbat_info_expired(struct timespec *lastupdated) { struct timespec curtime; + ACPI_SERIAL_ASSERT(cmbat); + if (lastupdated == NULL) return (TRUE); if (!timespecisset(lastupdated)) @@ -219,6 +224,9 @@ acpi_cmbat_info_expired(struct timespec *lastupdated) static void acpi_cmbat_info_updated(struct timespec *lastupdated) { + + ACPI_SERIAL_ASSERT(cmbat); + if (lastupdated != NULL) getnanotime(lastupdated); } @@ -233,6 +241,8 @@ acpi_cmbat_get_bst(void *arg) ACPI_BUFFER bst_buffer; device_t dev; + ACPI_SERIAL_ASSERT(cmbat); + dev = arg; sc = device_get_softc(dev); h = acpi_get_handle(dev); @@ -285,9 +295,10 @@ end: 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 @@ -300,6 +311,8 @@ acpi_cmbat_get_bif(void *arg) ACPI_BUFFER bif_buffer; device_t dev; + ACPI_SERIAL_ASSERT(cmbat); + dev = arg; sc = device_get_softc(dev); h = acpi_get_handle(dev); @@ -366,7 +379,7 @@ acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) * 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; @@ -380,7 +393,7 @@ acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) 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); } @@ -392,7 +405,7 @@ acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) 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; @@ -401,7 +414,7 @@ acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) bstp->volt = sc->bst.volt; } else bstp->state = ACPI_BATT_STAT_NOT_PRESENT; - crit_exit(); + ACPI_SERIAL_END(cmbat); return (0); } @@ -423,7 +436,7 @@ acpi_cmbat_init_battery(void *arg) * 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; @@ -435,7 +448,7 @@ acpi_cmbat_init_battery(void *arg) * 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); @@ -445,7 +458,7 @@ acpi_cmbat_init_battery(void *arg) valid = acpi_battery_bst_valid(&sc->bst) && acpi_battery_bif_valid(&sc->bif); - crit_exit(); + ACPI_SERIAL_END(cmbat); if (valid) break; diff --git a/sys/dev/acpica5/acpi_cpu_cstate.c b/sys/dev/acpica5/acpi_cpu_cstate.c index feb8df96dc..21b9b92384 100644 --- a/sys/dev/acpica5/acpi_cpu_cstate.c +++ b/sys/dev/acpica5/acpi_cpu_cstate.c @@ -47,7 +47,6 @@ #include #include "acpi.h" -#include "accommon.h" #include "acpivar.h" #include "acpi_cpu.h" @@ -518,7 +517,7 @@ acpi_cpu_generic_cx_probe(struct acpi_cpu_softc *sc) 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++; @@ -536,8 +535,8 @@ acpi_cpu_generic_cx_probe(struct acpi_cpu_softc *sc) 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; @@ -642,7 +641,7 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) /* 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++; @@ -1017,7 +1016,7 @@ acpi_cpu_quirks(void) 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: diff --git a/sys/dev/acpica5/acpi_cpu_pstate.c b/sys/dev/acpica5/acpi_cpu_pstate.c index d1252f4a5f..5e436c8eda 100644 --- a/sys/dev/acpica5/acpi_cpu_pstate.c +++ b/sys/dev/acpica5/acpi_cpu_pstate.c @@ -351,9 +351,9 @@ acpi_pst_attach(device_t dev) 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 */ @@ -1065,7 +1065,7 @@ acpi_pst_alloc_resource(device_t dev, ACPI_OBJECT *obj, int idx, 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); @@ -1074,7 +1074,7 @@ acpi_pst_alloc_resource(device_t dev, ACPI_OBJECT *obj, int idx, /* 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); diff --git a/sys/dev/acpica5/acpi_ec.c b/sys/dev/acpica5/acpi_ec.c index 6fd40966e8..d5a7c86870 100644 --- a/sys/dev/acpica5/acpi_ec.c +++ b/sys/dev/acpica5/acpi_ec.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003 Nate Lawson + * Copyright (c) 2003-2007 Nate Lawson * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. @@ -24,144 +24,30 @@ * 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 #include "opt_acpi.h" #include #include #include -#include +#include #include #include -#include #include #include "acpi.h" -#include "accommon.h" #include +#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: * ----------- @@ -175,14 +61,14 @@ typedef UINT8 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? @@ -192,15 +78,15 @@ typedef UINT8 EC_COMMAND; * | | | +--------- Burst Mode Enabled? * | | +----------- SCI Event? * | +------------- SMI Event? - * +--------------- + * +--------------- * */ 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: @@ -212,6 +98,10 @@ typedef UINT8 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 @@ -237,7 +127,7 @@ struct acpi_ec_params { }; /* 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. @@ -248,8 +138,7 @@ struct acpi_ec_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; @@ -262,21 +151,24 @@ struct acpi_ec_softc { 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 && \ @@ -284,43 +176,57 @@ struct acpi_ec_softc { ((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); @@ -328,6 +234,9 @@ static ACPI_STATUS EcWrite(struct acpi_ec_softc *sc, UINT8 Address, 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, @@ -337,10 +246,13 @@ static device_method_t acpi_ec_methods[] = { /* 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} }; @@ -356,10 +268,12 @@ DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 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) @@ -375,7 +289,8 @@ 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; } @@ -414,7 +329,7 @@ acpi_ec_ecdt_probe(device_t parent) 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) @@ -432,6 +347,7 @@ acpi_ec_probe(device_t dev) 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")) @@ -449,7 +365,8 @@ acpi_ec_probe(device_t dev) 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); @@ -475,11 +392,11 @@ acpi_ec_probe(device_t 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: @@ -544,13 +461,13 @@ acpi_ec_attach(device_t dev) 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. */ @@ -587,7 +504,7 @@ acpi_ec_attach(device_t dev) goto error; } - /* + /* * Install address space handler */ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n")); @@ -622,15 +539,45 @@ error: 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) @@ -641,7 +588,7 @@ 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); } @@ -654,7 +601,7 @@ acpi_ec_write_method(device_t dev, u_int addr, ACPI_INTEGER val, int width) 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); } @@ -664,93 +611,99 @@ EcGpeQueryHandler(void *Context) 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); } @@ -794,12 +747,26 @@ EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width, 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); @@ -816,199 +783,286 @@ EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width, 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); } diff --git a/sys/dev/acpica5/acpi_hpet.c b/sys/dev/acpica5/acpi_hpet.c index 9889a932b5..9ed7e8c915 100644 --- a/sys/dev/acpica5/acpi_hpet.c +++ b/sys/dev/acpica5/acpi_hpet.c @@ -177,7 +177,7 @@ acpi_hpet_probe(device_t dev) { ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); - if (!acpi_enabled("hpet")) + if (acpi_disabled("hpet")) return ENXIO; if (!DEV_HPET(dev) && diff --git a/sys/dev/acpica5/acpi_if.m b/sys/dev/acpica5/acpi_if.m index 9eedf3f5a5..1062287320 100644 --- a/sys/dev/acpica5/acpi_if.m +++ b/sys/dev/acpica5/acpi_if.m @@ -23,7 +23,7 @@ # 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 @@ -86,6 +86,79 @@ METHOD char * id_probe { } 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. # @@ -149,4 +222,3 @@ METHOD int batt_get_status { device_t dev; struct acpi_bst *bst; }; - diff --git a/sys/dev/acpica5/acpi_isab.c b/sys/dev/acpica5/acpi_isab.c index d1fde8c3f6..27a51928ea 100644 --- a/sys/dev/acpica5/acpi_isab.c +++ b/sys/dev/acpica5/acpi_isab.c @@ -22,11 +22,11 @@ * 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 + /* * ISA Bridge driver for Generic ISA Bus Devices. See section 10.7 of the * ACPI 2.0a specification for details on this device. @@ -41,7 +41,8 @@ #include "acpi.h" #include "accommon.h" -#include "acpivar.h" + +#include #include /* Hooks for the ACPI CA debugging infrastructure. */ @@ -91,17 +92,15 @@ MODULE_DEPEND(acpi_isab, acpi, 1, 1, 1); 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 diff --git a/sys/dev/acpica5/acpi_lid.c b/sys/dev/acpica5/acpi_lid.c index 9cfea9962f..9633839871 100644 --- a/sys/dev/acpica5/acpi_lid.c +++ b/sys/dev/acpica5/acpi_lid.c @@ -25,11 +25,11 @@ * 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 + #include "opt_acpi.h" #include #include @@ -39,6 +39,7 @@ #include "acpi.h" #include "accommon.h" + #include /* Hooks for the ACPI CA debugging infrastructure */ @@ -51,6 +52,8 @@ struct acpi_lid_softc { 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); @@ -82,13 +85,16 @@ MODULE_DEPEND(acpi_lid, acpi, 1, 1, 1); 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 @@ -114,23 +120,18 @@ acpi_lid_attach(device_t dev) 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); } @@ -144,6 +145,7 @@ acpi_lid_notify_status_changed(void *arg) 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. @@ -152,11 +154,11 @@ acpi_lid_notify_status_changed(void *arg) */ 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"); @@ -168,6 +170,8 @@ acpi_lid_notify_status_changed(void *arg) else EVENTHANDLER_INVOKE(acpi_wakeup_event, acpi_sc->acpi_lid_switch_sx); +out: + ACPI_SERIAL_END(lid); return_VOID; } @@ -184,7 +188,8 @@ acpi_lid_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 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); diff --git a/sys/dev/acpica5/acpi_package.c b/sys/dev/acpica5/acpi_package.c index 1b12384cad..4123c4477c 100644 --- a/sys/dev/acpica5/acpi_package.c +++ b/sys/dev/acpica5/acpi_package.c @@ -22,15 +22,16 @@ * 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 + #include #include #include #include + #include #include "acpi.h" @@ -100,33 +101,30 @@ acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size) } 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); } diff --git a/sys/dev/acpica5/acpi_pci.c b/sys/dev/acpica5/acpi_pci.c index 5f8e16269b..895d6e05c8 100644 --- a/sys/dev/acpica5/acpi_pci.c +++ b/sys/dev/acpica5/acpi_pci.c @@ -1,6 +1,6 @@ -/* - * Copyright (c) 1997, Stefan Esser - * Copyright (c) 2000, Michael Smith +/*- + * Copyright (c) 1997, Stefan Esser + * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * All rights reserved. * @@ -24,12 +24,10 @@ * 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 #include #include @@ -39,7 +37,6 @@ #include #include "acpi.h" -#include "accommon.h" #include "acpivar.h" #include @@ -57,73 +54,84 @@ ACPI_MODULE_NAME("PCI") 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 @@ -131,16 +139,36 @@ acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { 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) { @@ -149,7 +177,7 @@ acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf, 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); @@ -163,24 +191,11 @@ acpi_pci_set_powerstate_method(device_t dev, device_t child, int state) { 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 @@ -191,25 +206,70 @@ acpi_pci_set_powerstate_method(device_t dev, device_t child, int state) * 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 @@ -225,8 +285,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, 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++) { @@ -234,8 +294,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, 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); @@ -245,7 +305,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, 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); @@ -260,15 +321,15 @@ acpi_pci_attach(device_t dev) /* * 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. @@ -286,3 +347,79 @@ acpi_pci_attach(device_t dev) 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)); +} diff --git a/sys/dev/acpica5/acpi_pci_link.c b/sys/dev/acpica5/acpi_pci_link.c dissimilarity index 88% index 42e0ca800f..24c9d120e9 100644 --- a/sys/dev/acpica5/acpi_pci_link.c +++ b/sys/dev/acpica5/acpi_pci_link.c @@ -1,1024 +1,1126 @@ -/*- - * Copyright (c) 2002 Mitsuru IWASAKI - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: 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 $ - */ - -#include "opt_acpi.h" -#include -#include -#include - -#include "acpi.h" -#include "accommon.h" -#include -#include - -/* Hooks for the ACPI CA debugging infrastructure. */ -#define _COMPONENT ACPI_BUS -ACPI_MODULE_NAME("PCI_LINK") - -#define MAX_POSSIBLE_INTERRUPTS 16 -#define MAX_ISA_INTERRUPTS 16 -#define MAX_ACPI_INTERRUPTS 255 - -struct acpi_pci_link_entry { - TAILQ_ENTRY(acpi_pci_link_entry) links; - ACPI_HANDLE handle; - UINT8 current_irq; - UINT8 initial_irq; - ACPI_RESOURCE possible_resources; - UINT8 number_of_interrupts; - UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS]; - UINT8 sorted_irq[MAX_POSSIBLE_INTERRUPTS]; - int references; - int priority; -}; - -TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry); -static struct acpi_pci_link_entries acpi_pci_link_entries; - -struct acpi_prt_entry { - TAILQ_ENTRY(acpi_prt_entry) links; - device_t pcidev; - int busno; - ACPI_PCI_ROUTING_TABLE prt; - struct acpi_pci_link_entry *pci_link; -}; - -TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry); -static struct acpi_prt_entries acpi_prt_entries; - -static int irq_penalty[MAX_ACPI_INTERRUPTS]; - -#define ACPI_STA_PRESENT 0x00000001 -#define ACPI_STA_ENABLE 0x00000002 -#define ACPI_STA_SHOWINUI 0x00000004 -#define ACPI_STA_FUNCTIONAL 0x00000008 - -/* - * PCI link object management - */ - -static void -acpi_pci_link_dump_polarity(UINT32 Polarity) -{ - - switch (Polarity) { - case ACPI_ACTIVE_HIGH: - kprintf("high,"); - break; - case ACPI_ACTIVE_LOW: - kprintf("low,"); - break; - default: - kprintf("unknown,"); - break; - } -} - -static void -acpi_pci_link_dump_trigger(UINT32 Triggering) -{ - - switch (Triggering) { - case ACPI_EDGE_SENSITIVE: - kprintf("edge,"); - break; - case ACPI_LEVEL_SENSITIVE: - kprintf("level,"); - break; - default: - kprintf("unknown,"); - break; - } -} - -static void -acpi_pci_link_dump_sharemode(UINT32 SharedExclusive) -{ - - switch (SharedExclusive) { - case ACPI_EXCLUSIVE: - kprintf("exclusive"); - break; - case ACPI_SHARED: - kprintf("sharable"); - 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; - } - - kprintf(" %d.%d.%d\n", entry->busno, - (int)((entry->prt.Address & 0xffff0000) >> 16), - (int)entry->prt.Pin); -} - -static ACPI_STATUS -acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta) -{ - 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); - } - - 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); - } - - if ((devinfo->Valid & ACPI_VALID_STA) != 0) { - *sta = devinfo->CurrentStatus; - } else { - kprintf("invalid status - %s\n", acpi_name(handle)); - *sta = 0; - } - - AcpiOsFree(buf.Pointer); - return_ACPI_STATUS (AE_OK); -} - -static ACPI_STATUS -acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources, - UINT8 *number_of_interrupts, UINT8 interrupts[]) -{ - UINT8 count; - UINT8 i; - UINT32 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; - break; - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - InterruptCount = - resources->Data.ExtendedIrq.InterruptCount; - Interrupts32 = resources->Data.ExtendedIrq.Interrupts; - 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); - break; - } - - KKASSERT(Interrupts8 != NULL || Interrupts32 != NULL); - if (Interrupts8 != NULL) - intr = Interrupts8[i]; - else - intr = Interrupts32[i]; - - if (intr == 0) { - kprintf("invalid IRQ %d\n", intr); - continue; - } - interrupts[count] = intr; - count++; - } - *number_of_interrupts = count; - - return_ACPI_STATUS (AE_OK); -} - -static ACPI_STATUS -acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq) -{ - ACPI_STATUS error; - ACPI_BUFFER buf; - ACPI_RESOURCE *resources; - UINT8 number_of_interrupts; - UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS]; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - if (link == NULL || irq == NULL) { - kprintf("invalid argument\n"); - return_ACPI_STATUS (AE_BAD_PARAMETER); - } - - *irq = 0; - buf.Pointer = NULL; - buf.Length = ACPI_ALLOCATE_BUFFER; - error = AcpiGetCurrentResources(link->handle, &buf); - if (ACPI_FAILURE(error)) { - 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); - } - - 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); - } - - 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); - } - - *irq = interrupts[0]; - - return_ACPI_STATUS (AE_OK); -} - -static ACPI_STATUS -acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry) -{ - ACPI_STATUS error; - ACPI_BUFFER buf; - ACPI_RESOURCE *resources; - struct acpi_pci_link_entry *link; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - entry->pci_link = NULL; - TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { - if (link->handle == handle) { - entry->pci_link = link; - link->references++; - return_ACPI_STATUS (AE_OK); - } - } - - link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry)); - if (link == NULL) { - 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; - } - - link->references++; - - TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links); - entry->pci_link = link; - - error = AE_OK; -out: - if (buf.Pointer != NULL) - AcpiOsFree(buf.Pointer); - if (error != AE_OK && link != NULL) - AcpiOsFree(link); - - return_ACPI_STATUS (error); -} - -static ACPI_STATUS -acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno) -{ - ACPI_HANDLE handle; - ACPI_STATUS error; - UINT32 sta; - struct acpi_prt_entry *entry; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - if (prt == NULL || prt->Source == NULL || prt->Source[0] == '\0') { - kprintf("couldn't handle this routing table - hardwired\n"); - return_ACPI_STATUS (AE_BAD_PARAMETER); - } - - error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle); - if (ACPI_FAILURE(error)) { - kprintf("couldn't get handle - %s\n", - AcpiFormatException(error)); - return_ACPI_STATUS (error); - } - - 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); - } - - 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); - } - - 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); - } - } - - 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; - } - - TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links); - error = AE_OK; - -out: - if (error != AE_OK && entry != NULL) - AcpiOsFree(entry); - - return_ACPI_STATUS (error); -} - -static int -acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq) -{ - UINT8 i; - - if (irq == 0) - return (0); - - for (i = 0; i < link->number_of_interrupts; i++) { - if (link->interrupts[i] == irq) - return (1); - } - - /* allow initial IRQ as valid one. */ - if (link->initial_irq == irq) - return (1); - - return (0); -} - -static ACPI_STATUS -acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq) -{ - ACPI_STATUS error; - ACPI_RESOURCE resbuf; - ACPI_BUFFER crsbuf; - UINT32 sta; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - if (!acpi_pci_link_is_valid_irq(link, irq)) { - 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); - } - - AcpiOsFree(crsbuf.Pointer); - link->current_irq = 0; - - 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); - } - - if ((sta & ACPI_STA_ENABLE) == 0) { - kprintf("interrupt link %s is disabled\n", - acpi_name(link->handle)); - return_ACPI_STATUS (AE_ERROR); - } - - error = acpi_pci_link_get_current_irq(link, &link->current_irq); - if (ACPI_FAILURE(error)) { - kprintf("couldn't get current IRQ from " - "interrupt link %s - %s\n", - acpi_name(link->handle), AcpiFormatException(error)); - return_ACPI_STATUS (error); - } - - 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; - } - - return_ACPI_STATUS (error); -} - -/* - * Auto arbitration for boot-disabled devices - */ - -static void -acpi_pci_link_bootdisabled_dump(void) -{ - int i; - int irq; - struct acpi_pci_link_entry *link; - - TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { - /* boot-disabled link only. */ - if (link->current_irq != 0) - continue; - - 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); - } - kprintf("\n"); - kprintf(" penalty: "); - for (i = 0; i < link->number_of_interrupts; i++) { - irq = link->sorted_irq[i]; - kprintf("%6d", irq_penalty[irq]); - } - kprintf("\n"); - kprintf(" references: %d\n", link->references); - kprintf(" priority: %d\n", link->priority); - } -} - -static void -acpi_pci_link_init_irq_penalty(void) -{ - int irq; - - bzero(irq_penalty, sizeof(irq_penalty)); - for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) { - /* 0, 1, 2, 8: timer, keyboard, cascade */ - if (irq == 0 || irq == 1 || irq == 2 || irq == 8) { - irq_penalty[irq] = 100000; - continue; - } - - /* 13, 14, 15: npx, ATA controllers */ - if (irq == 13 || irq == 14 || irq == 15) { - irq_penalty[irq] = 10000; - continue; - } - - /* 3,4,6,7,12: typicially used by legacy hardware */ - if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) { - irq_penalty[irq] = 1000; - continue; - } - } -} - -static int -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) -{ - 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; - } - - /* 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; - } - } - - /* initialize `sorted' possible IRQs. */ - bcopy(link->interrupts, link->sorted_irq, - sizeof(link->sorted_irq)); - } -} - -static void -acpi_pci_link_set_bootdisabled_priority(void) -{ - int sum_penalty; - int i; - int irq; - struct acpi_pci_link_entry *link, *link_pri; - TAILQ_HEAD(, acpi_pci_link_entry) sorted_list; - - if (bootverbose) { - kprintf("ACPI PCI link before setting link priority:\n"); - acpi_pci_link_bootdisabled_dump(); - } - - /* reset priority for all links. */ - TAILQ_FOREACH(link, &acpi_pci_link_entries, links) - link->priority = 0; - - TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { - /* not boot-disabled link, give no chance to be arbitrated. */ - if (link->current_irq != 0) { - link->priority = 0; - continue; - } - - /* - * Calculate the priority for each boot-disabled links. - * o IRQ penalty indicates difficulty to use. - * o #references for devices indicates importance of the link. - * o #interrupts indicates flexibility of the link. - */ - sum_penalty = 0; - for (i = 0; i < link->number_of_interrupts; i++) { - irq = link->interrupts[i]; - sum_penalty += irq_penalty[irq]; - } - - link->priority = (sum_penalty * link->references) / - link->number_of_interrupts; - } - - /* - * Sort PCI links based on the priority. - * XXX Any other better ways rather than using work list? - */ - TAILQ_INIT(&sorted_list); - while (!TAILQ_EMPTY(&acpi_pci_link_entries)) { - link = TAILQ_FIRST(&acpi_pci_link_entries); - /* find an entry which has the highest priority. */ - TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links) - if (link->priority < link_pri->priority) - link = link_pri; - - /* move to work list. */ - TAILQ_REMOVE(&acpi_pci_link_entries, link, links); - TAILQ_INSERT_TAIL(&sorted_list, link, links); - } - - while (!TAILQ_EMPTY(&sorted_list)) { - /* move them back to the list, one by one... */ - link = TAILQ_FIRST(&sorted_list); - TAILQ_REMOVE(&sorted_list, link, links); - TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links); - } -} - -static void -acpi_pci_link_fixup_bootdisabled_link(void) -{ - int i, j; - int irq1, irq2; - struct acpi_pci_link_entry *link; - ACPI_STATUS error; - - if (bootverbose) { - kprintf("ACPI PCI link before fixup for boot-disabled links:\n"); - acpi_pci_link_bootdisabled_dump(); - } - - TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { - /* ignore non boot-disabled links. */ - if (link->current_irq != 0) - continue; - - /* sort IRQs based on their penalty descending. */ - for (i = 0; i < link->number_of_interrupts; i++) { - irq1 = link->sorted_irq[i]; - for (j = i + 1; j < link->number_of_interrupts; j++) { - irq2 = link->sorted_irq[j]; - if (irq_penalty[irq1] < irq_penalty[irq2]) { - continue; - } - link->sorted_irq[i] = irq2; - link->sorted_irq[j] = irq1; - irq1 = irq2; - } - } - - /* try with lower penalty IRQ. */ - for (i = 0; i < link->number_of_interrupts; i++) { - irq1 = link->sorted_irq[i]; - error = acpi_pci_link_set_irq(link, irq1); - if (error == AE_OK) { - /* OK, we use this. give another penalty. */ - irq_penalty[irq1] += 100 * link->references; - break; - } - } - } - - if (bootverbose) { - kprintf("ACPI PCI link after fixup for boot-disabled links:\n"); - acpi_pci_link_bootdisabled_dump(); - } -} - -/* - * Public interface - */ - -int -acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno) -{ - struct acpi_prt_entry *entry; - ACPI_PCI_ROUTING_TABLE *prt; - u_int8_t *prtp; - ACPI_STATUS error; - static int first_time =1; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - if (acpi_disabled("pci_link")) - return (0); - - if (first_time) { - TAILQ_INIT(&acpi_prt_entries); - TAILQ_INIT(&acpi_pci_link_entries); - acpi_pci_link_init_irq_penalty(); - first_time = 0; - } - - if (prtbuf == NULL) - return (-1); - - prtp = prtbuf->Pointer; - if (prtp == NULL) /* didn't get routing table */ - return (-1); - - /* scan the PCI Routing Table */ - for (;;) { - prt = (ACPI_PCI_ROUTING_TABLE *)prtp; - - if (prt->Length == 0) /* end of table */ - break; - - error = acpi_pci_link_add_prt(dev, prt, busno); - if (ACPI_FAILURE(error)) { - kprintf("couldn't add PCI interrupt link entry - %s\n", - AcpiFormatException(error)); - } - - /* skip to next entry */ - prtp += prt->Length; - } - - if (bootverbose) { - kprintf("ACPI PCI link initial configuration:\n"); - TAILQ_FOREACH(entry, &acpi_prt_entries, links) { - if (entry->busno != busno) - continue; - acpi_pci_link_entry_dump(entry); - } - } - - /* manual configuration. */ - TAILQ_FOREACH(entry, &acpi_prt_entries, links) { - int irq; - char prthint[32]; - - if (entry->busno != busno) - continue; - - ksnprintf(prthint, sizeof(prthint), - "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno, - (int)((entry->prt.Address & 0xffff0000) >> 16), - (int)entry->prt.Pin); - - if (kgetenv_int(prthint, &irq) == 0) - continue; - - if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) { - error = acpi_pci_link_set_irq(entry->pci_link, irq); - if (ACPI_FAILURE(error)) { - kprintf("couldn't set IRQ to " - "link entry %s - %s\n", - acpi_name(entry->pci_link->handle), - AcpiFormatException(error)); - } - continue; - } - - /* - * Do auto arbitration for this device's PCI link - * if hint value 0 is specified. - */ - if (irq == 0) - entry->pci_link->current_irq = 0; - } - - /* auto arbitration */ - acpi_pci_link_update_irq_penalty(dev, busno); - acpi_pci_link_set_bootdisabled_priority(); - acpi_pci_link_fixup_bootdisabled_link(); - - if (bootverbose) { - kprintf("ACPI PCI link arbitrated configuration:\n"); - TAILQ_FOREACH(entry, &acpi_prt_entries, links) { - if (entry->busno != busno) - continue; - acpi_pci_link_entry_dump(entry); - } - } - - return (0); -} - -int -acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno) -{ - struct acpi_prt_entry *entry; - ACPI_STATUS error; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - if (acpi_disabled("pci_link")) - return (0); - - TAILQ_FOREACH(entry, &acpi_prt_entries, links) { - if (entry->pcidev != dev) - continue; - - error = acpi_pci_link_set_irq(entry->pci_link, - entry->pci_link->current_irq); - if (ACPI_FAILURE(error)) { - kprintf("couldn't set IRQ to link entry %s - %s\n", - acpi_name(entry->pci_link->handle), - AcpiFormatException(error)); - } - } - - return (0); -} +/* + * Copyright (c) 2002 Mitsuru IWASAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * __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 + +#include "opt_acpi.h" +#include +#include +#include +#include +#include +#include + +#include "acpi.h" +#include +#include + +#include +#include +#include +#include "pcib_if.h" + +/* Hooks for the ACPI CA debugging infrastructure. */ +#define _COMPONENT ACPI_BUS +ACPI_MODULE_NAME("PCI_LINK") + +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; +}; + +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 link_count_request { + int in_dpf; + int count; +}; + +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 pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS]; +static int pci_link_bios_isa_irqs; + +static char *pci_link_ids[] = { "PNP0C0F", NULL }; + +/* + * Fetch the short name associated with an ACPI handle and save it in the + * passed in buffer. + */ +static ACPI_STATUS +acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen) +{ + ACPI_BUFFER buf; + + buf.Length = buflen; + buf.Pointer = buffer; + return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf)); +} + +static int +acpi_pci_link_probe(device_t dev) +{ + char descr[28], name[12]; + + /* + * 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 ACPI_STATUS +acpi_count_irq_resources(ACPI_RESOURCE *res, void *context) +{ + 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_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: + /* + * Don't count resources if we are in a DPF set that we are + * ignoring. + */ + if (req->in_dpf != DPF_IGNORE) + req->count++; + } + return (AE_OK); +} + +static ACPI_STATUS +link_add_crs(ACPI_RESOURCE *res, void *context) +{ + 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++; + + /* + * 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]; + + /* + * 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++; + } + return (AE_OK); +} + +/* + * Populate the set of possible IRQs for each device. + */ +static ACPI_STATUS +link_add_prs(ACPI_RESOURCE *res, void *context) +{ + 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_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: + 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++; + + /* + * 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; + + /* + * 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; + } + } + 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++; + } + return (AE_OK); +} + +static int +link_valid_irq(struct link *link, int irq) +{ + int i; + + ACPI_SERIAL_ASSERT(pci_link); + + /* Invalid interrupts are never valid. */ + if (!PCI_INTERRUPT_VALID(irq)) + return (FALSE); + + /* 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); + + /* + * 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); + + /* If the interrupt wasn't found in the list it is not valid. */ + return (FALSE); +} + +static void +acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag) +{ + 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); + } + 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"); + } +} + +static int +acpi_pci_link_attach(device_t dev) +{ + struct acpi_pci_link_softc *sc; + struct link_count_request creq; + struct link_res_request rreq; + ACPI_STATUS status; + int i; + + sc = device_get_softc(dev); + sc->pl_dev = dev; + ACPI_SERIAL_BEGIN(pci_link); + + /* + * 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); + } + } + sc->pl_num_links = creq.count; + if (creq.count == 0) { + ACPI_SERIAL_END(pci_link); + return (0); + } + 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; + } + + /* 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; + } + } + + /* + * 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; + + /* + * 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); +} + +/* 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) +{ + 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); + + /* + * 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); + } + return (PCI_INVALID_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) +{ + 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); +} + +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; + + /* + * 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"); + } + + /* 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]++; + + /* + * 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; + } + + /* 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; + } + + /* 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); +} + +static ACPI_STATUS +acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf) +{ + 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); + } + + /* 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; + } + 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); + } + } + 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 ACPI_STATUS +acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc, + ACPI_BUFFER *srsbuf) +{ + 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; + } + + /* 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 ACPI_STATUS +acpi_pci_link_route_irqs(device_t dev) +{ + 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); + } + + /* + * 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; + } + resource = ACPI_NEXT_RESOURCE(resource); + if (resource >= end) + break; + } + AcpiOsFree(srsbuf.Pointer); + return (AE_OK); +} + +static int +acpi_pci_link_resume(device_t dev) +{ + struct acpi_pci_link_softc *sc; + ACPI_STATUS status; + int i, routed; + + /* + * 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); +} + +/* + * 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); + } + 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); + } + } + + /* + * 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. + */ + if (PCI_INTERRUPT_VALID(link->l_bios_irq)) + return (link->l_bios_irq); + + /* + * 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); + + /* + * 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. + */ + best_irq = PCI_INVALID_IRQ; + best_weight = INT_MAX; + for (i = 0; i < link->l_num_irqs; i++) { + pos_irq = link->l_irqs[i]; + if (pos_irq < NUM_ISA_INTERRUPTS && + (pci_link_bios_isa_irqs & 1 << pos_irq) == 0) + continue; + pos_weight = pci_link_interrupt_weights[pos_irq]; + if (pos_weight < best_weight) { + best_weight = pos_weight; + best_irq = pos_irq; + } + } + + /* + * If this is an ISA IRQ, try using the SCI if it is also an ISA + * interrupt as a fallback. + */ + if (link->l_isa_irq) { + pos_irq = AcpiGbl_FADT.SciInterrupt; + pos_weight = pci_link_interrupt_weights[pos_irq]; + if (pos_weight < best_weight) { + best_weight = pos_weight; + best_irq = pos_irq; + } + } + + if (PCI_INTERRUPT_VALID(best_irq)) { + if (bootverbose) + device_printf(dev, "Picked IRQ %u with weight %d\n", + best_irq, best_weight); + } else + device_printf(dev, "Unable to choose an IRQ\n"); + return (best_irq); +} + +int +acpi_pci_link_route_interrupt(device_t dev, int index) +{ + struct link *link; + + if (acpi_disabled("pci_link")) + return (PCI_INVALID_IRQ); + + ACPI_SERIAL_BEGIN(pci_link); + link = acpi_pci_link_lookup(dev, index); + if (link == NULL) + panic("%s: apparently invalid index %d", __func__, index); + + /* + * If this link device is already routed to an interrupt, just return + * the interrupt it is routed to. + */ + if (link->l_routed) { + KASSERT(PCI_INTERRUPT_VALID(link->l_irq), + ("%s: link is routed but has an invalid IRQ", __func__)); + ACPI_SERIAL_END(pci_link); + return (link->l_irq); + } + + /* Choose an IRQ if we need one. */ + if (!PCI_INTERRUPT_VALID(link->l_irq)) { + link->l_irq = acpi_pci_link_choose_irq(dev, link); + + /* + * Try to route the interrupt we picked. If it fails, then + * assume the interrupt is not routed. + */ + if (PCI_INTERRUPT_VALID(link->l_irq)) { + acpi_pci_link_route_irqs(dev); + if (!link->l_routed) + link->l_irq = PCI_INVALID_IRQ; + } + } + ACPI_SERIAL_END(pci_link); + return (link->l_irq); +} + +/* + * This is gross, but we abuse the identify routine to perform one-time + * SYSINIT() style initialization for the driver. + */ +static void +acpi_pci_link_identify(driver_t *driver, device_t parent) +{ + + /* + * If the SCI is an ISA IRQ, add it to the bitmask of known good + * ISA IRQs. + * + * XXX: If we are using the APIC, the SCI might have been + * rerouted to an APIC pin in which case this is invalid. However, + * if we are using the APIC, we also shouldn't be having any PCI + * interrupts routed via ISA IRQs, so this is probably ok. + */ + if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS) + pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt); +} + +static device_method_t acpi_pci_link_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, acpi_pci_link_identify), + DEVMETHOD(device_probe, acpi_pci_link_probe), + DEVMETHOD(device_attach, acpi_pci_link_attach), + DEVMETHOD(device_resume, acpi_pci_link_resume), + + {0, 0} +}; + +static driver_t acpi_pci_link_driver = { + "pci_link", + acpi_pci_link_methods, + sizeof(struct acpi_pci_link_softc), +}; + +static devclass_t pci_link_devclass; + +DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass, 0, + 0); +MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1); diff --git a/sys/dev/acpica5/acpi_pcib.c b/sys/dev/acpica5/acpi_pcib.c dissimilarity index 72% index e5305d676f..acab302186 100644 --- a/sys/dev/acpica5/acpi_pcib.c +++ b/sys/dev/acpica5/acpi_pcib.c @@ -1,402 +1,313 @@ -/*- - * Copyright (c) 2000 Michael Smith - * Copyright (c) 2000 BSDi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: src/sys/dev/acpica/acpi_pcib.c,v 1.43 2004/05/06 02:18:58 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_pcib.c,v 1.3 2006/12/22 23:26:14 swildner Exp $ - */ - -#include "opt_acpi.h" -#include -#include -#include -#include - -#include "acpi.h" -#include "accommon.h" -#include "acpivar.h" -#include "acpi_pcibvar.h" - -#include -#include "pcib_if.h" - -/* Hooks for the ACPI CA debugging infrastructure. */ -#define _COMPONENT ACPI_BUS -ACPI_MODULE_NAME("PCI") - -int -acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno) -{ - device_t child; - ACPI_STATUS status; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - /* - * Don't attach if we're not really there. - * - * XXX: This isn't entirely correct since we may be a PCI bus - * on a hot-plug docking station, etc. - */ - if (!acpi_DeviceIsPresent(dev)) - return_VALUE(ENXIO); - - /* - * Get the PCI interrupt routing table for this bus. If we can't - * get it, this is not an error but may reduce functionality. - */ - prt->Length = ACPI_ALLOCATE_BUFFER; - status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt); - if (ACPI_FAILURE(status)) - device_printf(dev, - "could not get PCI interrupt routing table for %s - %s\n", - acpi_name(acpi_get_handle(dev)), AcpiFormatException(status)); - - /* - * Attach the PCI bus proper. - */ - if ((child = device_add_child(dev, "pci", busno)) == NULL) { - device_printf(device_get_parent(dev), "couldn't attach pci bus\n"); - return_VALUE(ENXIO); - } - - /* - * Now go scan the bus. - */ - acpi_pci_link_config(dev, prt, busno); - - return_VALUE (bus_generic_attach(dev)); -} - -int -acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno) -{ - acpi_pci_link_resume(dev, prt, busno); - return (bus_generic_resume(dev)); -} - -/* - * Route an interrupt for a child of the bridge. - * - * XXX clean up error messages - * - * XXX this function is somewhat bulky - */ -int -acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin, - ACPI_BUFFER *prtbuf) -{ - ACPI_PCI_ROUTING_TABLE *prt; - ACPI_HANDLE lnkdev; - ACPI_BUFFER crsbuf, prsbuf, buf; - ACPI_RESOURCE *crsres, *prsres, resbuf; - ACPI_DEVICE_INFO *devinfo; - ACPI_STATUS status; - UINT32 InterruptCount, intr; - u_int8_t *prtp; - int interrupt; - int i; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - buf.Pointer = NULL; - crsbuf.Pointer = NULL; - prsbuf.Pointer = NULL; - interrupt = 255; - intr = 0; - - /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */ - pin--; - - /* We failed to retrieve the routing table. */ - prtp = prtbuf->Pointer; - if (prtp == NULL) - goto out; - - /* Scan the table to look for this device. */ - for (;;) { - prt = (ACPI_PCI_ROUTING_TABLE *)prtp; - - /* We hit the end of the table. */ - if (prt->Length == 0) - goto out; - - /* - * Compare the slot number (high word of Address) and pin number - * (note that ACPI uses 0 for INTA) to check for a match. - * - * Note that the low word of the Address field (function number) - * is required by the specification to be 0xffff. We don't risk - * checking it here. - */ - if (((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev) && - prt->Pin == pin) { - if (bootverbose) - device_printf(pcib, "matched entry for %d.%d.INT%c (src %s)\n", - pci_get_bus(dev), pci_get_slot(dev), 'A' + pin, - prt->Source); - break; - } - - /* Skip to the next entry. */ - prtp += prt->Length; - } - - /* - * If source is empty/NULL, the source index is the global IRQ number. - */ - if (prt->Source == NULL || prt->Source[0] == '\0') { - if (bootverbose) - device_printf(pcib, "device is hardwired to IRQ %d\n", - prt->SourceIndex); - interrupt = prt->SourceIndex; - goto out; - } - - /* - * We have to find the source device (PCI interrupt link device). - */ - if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) { - device_printf(pcib, "couldn't find PCI interrupt link device %s\n", - prt->Source); - goto out; - } - - /* - * Verify that this is a PCI link device and that it's present. - */ - buf.Length = ACPI_ALLOCATE_BUFFER; - if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &buf))) { - device_printf(pcib, "couldn't validate PCI interrupt link device %s\n", - prt->Source); - goto out; - } - devinfo = (ACPI_DEVICE_INFO *)buf.Pointer; - if ((devinfo->Valid & ACPI_VALID_HID) == 0 || - strcmp("PNP0C0F", devinfo->HardwareId.Value) != 0) { - device_printf(pcib, "PCI interrupt link %s has invalid _HID (%s)\n", - prt->Source, devinfo->HardwareId.Value); - goto out; - } - if ((devinfo->Valid & ACPI_VALID_STA) != 0 && - (devinfo->CurrentStatus & 0x9) != 0x9) { - device_printf(pcib, "PCI interrupt link device %s not present\n", - prt->Source); - goto out; - } - - /* - * Get the current and possible resources for the interrupt link device. - * If we fail to get the current resources, this is a fatal error. - */ - crsbuf.Length = ACPI_ALLOCATE_BUFFER; - if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) { - device_printf(pcib, "PCI interrupt link device _CRS failed - %s\n", - AcpiFormatException(status)); - goto out; - } - prsbuf.Length = ACPI_ALLOCATE_BUFFER; - if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) { - device_printf(pcib, "PCI interrupt link device _PRS failed - %s\n", - AcpiFormatException(status)); - } - ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n", - (long)crsbuf.Length, acpi_name(lnkdev))); - ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n", - (long)prsbuf.Length, acpi_name(lnkdev))); - - /* - * The interrupt may already be routed, so check _CRS first. We don't - * check the 'decoding' bit in the _STA result, since there's nothing in - * the spec that mandates it be set, however some BIOS' will set it if - * the decode is active. - * - * The Source Index points to the particular resource entry we're - * interested in. - */ - if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex, - &crsres))) { - device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n"); - goto out; - } - - /* Type-check the resource we've found. */ - if (crsres->Type != ACPI_RESOURCE_TYPE_IRQ && crsres->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { - device_printf(pcib, "_CRS resource entry has unsupported type %d\n", - crsres->Type); - goto out; - } - - /* Set variables based on resource type. */ - if (crsres->Type == ACPI_RESOURCE_TYPE_IRQ) { - InterruptCount = crsres->Data.Irq.InterruptCount; - if (InterruptCount >= 1) - intr = crsres->Data.Irq.Interrupts[0]; - } else { - InterruptCount = crsres->Data.ExtendedIrq.InterruptCount; - if (InterruptCount >= 1) - intr = crsres->Data.ExtendedIrq.Interrupts[0]; - } - - /* If there's more than one interrupt, this is an error. */ - if (InterruptCount > 1) { - device_printf(pcib, "device has too many interrupts (%d)\n", - InterruptCount); - goto out; - } - - /* - * If there's only one interrupt, and it's not zero, then it's already - * routed. - * - * Note that we could also check the 'decoding' bit in _STA, but can't - * depend on it since it's not part of the spec. - * - * XXX check ASL examples to see if this is an acceptable set of tests - */ - if (InterruptCount == 1 && intr != 0) { - device_printf(pcib, "slot %d INT%c is routed to irq %d\n", - pci_get_slot(dev), 'A' + pin, intr); - interrupt = intr; - goto out; - } - - /* - * There isn't an interrupt, so we have to look at _PRS to get one. - * Get the set of allowed interrupts from the _PRS resource indexed - * by SourceIndex. - */ - if (prsbuf.Pointer == NULL) { - device_printf(pcib, "no routed irq and no _PRS on irq link device\n"); - goto out; - } - - /* - * Search through the _PRS resources, looking for an IRQ or extended - * IRQ resource. Skip dependent function resources for now. In the - * future, we might use these for priority but this is good enough for - * now until BIOS vendors actually mean something by using them. - */ - prsres = NULL; - for (i = prt->SourceIndex; prsres == NULL; i++) { - if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, i, &prsres))) { - device_printf(pcib, "_PRS lacks IRQ resource, routing failed\n"); - goto out; - } - switch (prsres->Type) { - case ACPI_RESOURCE_TYPE_IRQ: - InterruptCount = prsres->Data.Irq.InterruptCount; - device_printf(pcib, "possible interrupts:"); - for (i = 0; i < InterruptCount; i++) - kprintf(" %d", prsres->Data.Irq.Interrupts[i]); - kprintf("\n"); - intr = prsres->Data.Irq.Interrupts[0]; - break; - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - InterruptCount = prsres->Data.ExtendedIrq.InterruptCount; - device_printf(pcib, "possible interrupts:"); - for (i = 0; i < InterruptCount; i++) - kprintf(" %d", prsres->Data.ExtendedIrq.Interrupts[i]); - kprintf("\n"); - intr = prsres->Data.ExtendedIrq.Interrupts[0]; - break; - case ACPI_RESOURCE_TYPE_START_DEPENDENT: - prsres = NULL; - continue; - default: - device_printf(pcib, "_PRS has invalid type %d\n", prsres->Type); - goto out; - } - } - - /* There has to be at least one interrupt available. */ - if (InterruptCount < 1) { - device_printf(pcib, "device has no interrupts\n"); - goto out; - } - - /* - * Pick an interrupt to use. Note that a more scientific approach than - * just taking the first one available would be desirable. - * - * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI - * doesn't seem to offer a similar mechanism, so picking a "good" - * interrupt here is a difficult task. - * - * Build a resource buffer and pass it to AcpiSetCurrentResources to - * route the new interrupt. - */ - - /* This should never happen. */ - if (crsbuf.Pointer != NULL) - AcpiOsFree(crsbuf.Pointer); - - /* XXX Data.Irq and Data.ExtendedIrq are implicitly structure-copied. */ - crsbuf.Pointer = NULL; - if (prsres->Type == ACPI_RESOURCE_TYPE_IRQ) { - resbuf.Type = ACPI_RESOURCE_TYPE_IRQ; - resbuf.Length = sizeof(ACPI_RESOURCE_IRQ); - resbuf.Data.Irq = prsres->Data.Irq; - resbuf.Data.Irq.InterruptCount = 1; - resbuf.Data.Irq.Interrupts[0] = intr; - } else { - resbuf.Type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ; - resbuf.Length = sizeof(ACPI_RESOURCE_EXTENDED_IRQ); - resbuf.Data.ExtendedIrq = prsres->Data.ExtendedIrq; - resbuf.Data.ExtendedIrq.InterruptCount = 1; - resbuf.Data.ExtendedIrq.Interrupts[0] = intr; - } - if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) { - device_printf(pcib, "buf append failed for interrupt %d via %s - %s\n", - intr, acpi_name(lnkdev), AcpiFormatException(status)); - goto out; - } - /* XXX Figure out how this is happening when the append succeeds. */ - if (crsbuf.Pointer == NULL) { - device_printf(pcib, "_CRS buf NULL after append?\n"); - goto out; - } - if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) { - device_printf(pcib, "_SRS failed for interrupt %d via %s - %s\n", - intr, acpi_name(lnkdev), AcpiFormatException(status)); - goto out; - } - - /* Return the interrupt we just routed. */ - device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n", - pci_get_slot(dev), 'A' + pin, intr, acpi_name(lnkdev)); - interrupt = intr; - -out: - if (crsbuf.Pointer != NULL) - AcpiOsFree(crsbuf.Pointer); - if (prsbuf.Pointer != NULL) - AcpiOsFree(prsbuf.Pointer); - if (buf.Pointer != NULL) - AcpiOsFree(buf.Pointer); - - /* XXX APIC_IO interrupt mapping? */ - return_VALUE (interrupt); -} +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib.c,v 1.60.8.1 2009/04/15 03:14:26 kensmith Exp $"); + */ + +#include + +#include "opt_acpi.h" +#include +#include +#include +#include +#include + +#include "acpi.h" +#include +#include + +#include +#include +#include "pcib_if.h" + +/* Hooks for the ACPI CA debugging infrastructure. */ +#define _COMPONENT ACPI_BUS +ACPI_MODULE_NAME("PCI") + +ACPI_SERIAL_DECL(pcib, "ACPI PCI bus methods"); + +/* + * For locking, we assume the caller is not concurrent since this is + * triggered by newbus methods. + */ + +struct prt_lookup_request { + ACPI_PCI_ROUTING_TABLE *pr_entry; + u_int pr_pin; + u_int pr_slot; +}; + +typedef void prt_entry_handler(ACPI_PCI_ROUTING_TABLE *entry, void *arg); + +static void prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg); +static void prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg); +static void prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler, + void *arg); + +#define PCI_INVALID_IRQ 255 + +static void +prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler, void *arg) +{ + ACPI_PCI_ROUTING_TABLE *entry; + char *prtptr; + + /* First check to see if there is a table to walk. */ + if (prt == NULL || prt->Pointer == NULL) + return; + + /* Walk the table executing the handler function for each entry. */ + prtptr = prt->Pointer; + entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; + while (entry->Length != 0) { + handler(entry, arg); + prtptr += entry->Length; + entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; + } +} + +static void +prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg) +{ + ACPI_HANDLE handle; + device_t child, pcib; + int error; + + /* We only care about entries that reference a link device. */ + if (entry->Source == NULL || entry->Source[0] == '\0') + return; + + /* + * In practice, we only see SourceIndex's of 0 out in the wild. + * When indices != 0 have been found, they've been bugs in the ASL. + */ + if (entry->SourceIndex != 0) + return; + + /* Lookup the associated handle and device. */ + pcib = (device_t)arg; + if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, entry->Source, &handle))) + return; + child = acpi_get_device(handle); + if (child == NULL) + return; + + /* If the device hasn't been probed yet, force it to do so. */ + error = device_probe_and_attach(child); + if (error != 0) { + device_printf(pcib, "failed to force attach of %s\n", + acpi_name(handle)); + return; + } + + /* Add a reference for a specific bus/device/pin tuple. */ + acpi_pci_link_add_reference(child, entry->SourceIndex, pcib, + ACPI_ADR_PCI_SLOT(entry->Address), entry->Pin); +} + +int +acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno) +{ + device_t child; + ACPI_STATUS status; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + /* + * Don't attach if we're not really there. + * + * XXX: This isn't entirely correct since we may be a PCI bus + * on a hot-plug docking station, etc. + */ + + if (!acpi_DeviceIsPresent(dev)) + return_VALUE(ENXIO); + + /* + * Get the PCI interrupt routing table for this bus. If we can't + * get it, this is not an error but may reduce functionality. There + * are several valid bridges in the field that do not have a _PRT, so + * only warn about missing tables if bootverbose is set. + */ + prt->Length = ACPI_ALLOCATE_BUFFER; + status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt); + if (ACPI_FAILURE(status) && (bootverbose || status != AE_NOT_FOUND)) + device_printf(dev, + "could not get PCI interrupt routing table for %s - %s\n", + acpi_name(acpi_get_handle(dev)), AcpiFormatException(status)); + + /* + * Attach the PCI bus proper. + */ + if ((child = device_add_child(dev, "pci", busno)) == NULL) { + device_printf(device_get_parent(dev), "couldn't attach pci bus\n"); + return_VALUE(ENXIO); + } + + /* + * Now go scan the bus. + */ + prt_walk_table(prt, prt_attach_devices, dev); + + return_VALUE (bus_generic_attach(dev)); +} + +int +acpi_pcib_resume(device_t dev) +{ + + return (bus_generic_resume(dev)); +} + +static void +prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg) +{ + struct prt_lookup_request *pr; + + pr = (struct prt_lookup_request *)arg; + if (pr->pr_entry != NULL) + return; + + /* + * Compare the slot number (high word of Address) and pin number + * (note that ACPI uses 0 for INTA) to check for a match. + * + * Note that the low word of the Address field (function number) + * is required by the specification to be 0xffff. We don't risk + * checking it here. + */ + if (ACPI_ADR_PCI_SLOT(entry->Address) == pr->pr_slot && + entry->Pin == pr->pr_pin) + pr->pr_entry = entry; +} + +/* + * Route an interrupt for a child of the bridge. + */ +int +acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin, + ACPI_BUFFER *prtbuf) +{ + ACPI_PCI_ROUTING_TABLE *prt; + struct prt_lookup_request pr; + ACPI_HANDLE lnkdev; + int interrupt; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + interrupt = PCI_INVALID_IRQ; + + /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */ + pin--; + + ACPI_SERIAL_BEGIN(pcib); + + /* Search for a matching entry in the routing table. */ + pr.pr_entry = NULL; + pr.pr_pin = pin; + pr.pr_slot = pci_get_slot(dev); + prt_walk_table(prtbuf, prt_lookup_device, &pr); + if (pr.pr_entry == NULL) { + device_printf(pcib, "no PRT entry for %d.%d.INT%c\n", pci_get_bus(dev), + pci_get_slot(dev), 'A' + pin); + goto out; + } + prt = pr.pr_entry; + + if (bootverbose) { + device_printf(pcib, "matched entry for %d.%d.INT%c", + pci_get_bus(dev), pci_get_slot(dev), 'A' + pin); + if (prt->Source != NULL && prt->Source[0] != '\0') + kprintf(" (src %s:%u)", prt->Source, prt->SourceIndex); + kprintf("\n"); + } + + /* + * If source is empty/NULL, the source index is a global IRQ number + * and it's hard-wired so we're done. + * + * XXX: If the source index is non-zero, ignore the source device and + * assume that this is a hard-wired entry. + */ + if (prt->Source == NULL || prt->Source[0] == '\0' || + prt->SourceIndex != 0) { + if (bootverbose) + device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n", + pci_get_slot(dev), 'A' + pin, prt->SourceIndex); + if (prt->SourceIndex) { + interrupt = prt->SourceIndex; +#if defined(APIC_IO) + int line; + line = pci_apic_irq(pci_get_bus(dev), pci_get_slot(dev), pin+1, interrupt); + if (line >= 0) { +kprintf("apic: try line %d\n", line); + return line; + } else { + int irq = pci_get_irq(dev); + + /* + * PCI interrupts might be redirected to the + * ISA bus according to some MP tables. Use the + * same methods as used by the ISA devices + * devices to find the proper IOAPIC int pin. + */ + kprintf("ACPI: Try routing through ISA bus for " + "bus %d slot %d INT%c irq %d\n", + pci_get_bus(dev), pci_get_slot(dev), 'A' + pin+1, irq); + line = isa_apic_irq(irq); + } + interrupt = line; +#endif + pci_write_config(dev, PCIR_INTLINE, interrupt, 1); + BUS_CONFIG_INTR(dev, interrupt, INTR_TRIGGER_LEVEL, + INTR_POLARITY_LOW); + } else + device_printf(pcib, "error: invalid hard-wired IRQ of 0\n"); + goto out; + } + + /* + * We have to find the source device (PCI interrupt link device). + */ + if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) { + device_printf(pcib, "couldn't find PCI interrupt link device %s\n", + prt->Source); + goto out; + } + interrupt = acpi_pci_link_route_interrupt(acpi_get_device(lnkdev), + prt->SourceIndex); + + if (bootverbose && PCI_INTERRUPT_VALID(interrupt)) { + if (PCI_INTERRUPT_VALID(interrupt)) + device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n", + pci_get_slot(dev), 'A' + pin, interrupt, acpi_name(lnkdev)); + } + +out: + ACPI_SERIAL_END(pcib); + + return_VALUE (interrupt); +} diff --git a/sys/dev/acpica5/acpi_pcib_acpi.c b/sys/dev/acpica5/acpi_pcib_acpi.c index 387257d9af..a7b707b76f 100644 --- a/sys/dev/acpica5/acpi_pcib_acpi.c +++ b/sys/dev/acpica5/acpi_pcib_acpi.c @@ -23,27 +23,28 @@ * 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_pcib_acpi.c,v 1.34 2004/05/30 20:08:23 phk Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_pcib_acpi.c,v 1.5 2008/08/02 01:14:41 dillon Exp $ + * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib_acpi.c,v 1.55.8.1 2009/04/15 03:14:26 kensmith Exp $"); */ + +#include + #include "opt_acpi.h" #include #include #include #include #include +#include #include "acpi.h" -#include "accommon.h" -#include "acpivar.h" +#include -#include +#include #include #include #include "pcib_if.h" -#include "acpi_pcibvar.h" +#include /* Hooks for the ACPI CA debugging infrastructure. */ #define _COMPONENT ACPI_BUS @@ -52,6 +53,7 @@ ACPI_MODULE_NAME("PCI_ACPI") struct acpi_hpcib_softc { device_t ap_dev; ACPI_HANDLE ap_handle; + int ap_flags; int ap_segment; /* analagous to Alpha 'hose' */ int ap_bus; /* bios-assigned bus number */ @@ -72,6 +74,14 @@ static void acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, uint32_t data, int bytes); static int acpi_pcib_acpi_route_interrupt(device_t pcib, device_t dev, int pin); +#ifdef MSI +static int acpi_pcib_alloc_msi(device_t pcib, device_t dev, + int count, int maxcount, int *irqs); +static int acpi_pcib_map_msi(device_t pcib, device_t dev, + int irq, uint64_t *addr, uint32_t *data); +static int acpi_pcib_alloc_msix(device_t pcib, device_t dev, + int *irq); +#endif static struct resource *acpi_pcib_acpi_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, @@ -92,7 +102,7 @@ static device_method_t acpi_pcib_acpi_methods[] = { DEVMETHOD(bus_alloc_resource, acpi_pcib_acpi_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), - DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), @@ -101,34 +111,36 @@ static device_method_t acpi_pcib_acpi_methods[] = { DEVMETHOD(pcib_read_config, acpi_pcib_read_config), DEVMETHOD(pcib_write_config, acpi_pcib_write_config), DEVMETHOD(pcib_route_interrupt, acpi_pcib_acpi_route_interrupt), - +#ifdef MSI + DEVMETHOD(pcib_alloc_msi, acpi_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, pcib_release_msi), + DEVMETHOD(pcib_alloc_msix, acpi_pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, pcib_release_msix), + DEVMETHOD(pcib_map_msi, acpi_pcib_map_msi), +#endif {0, 0} }; -static driver_t acpi_pcib_acpi_driver = { - "pcib", - acpi_pcib_acpi_methods, - sizeof(struct acpi_hpcib_softc), -}; - static devclass_t pcib_devclass; +DEFINE_CLASS_0(pcib, acpi_pcib_acpi_driver, acpi_pcib_acpi_methods, + sizeof(struct acpi_hpcib_softc)); DRIVER_MODULE(acpi_pcib, acpi, acpi_pcib_acpi_driver, pcib_devclass, 0, 0); MODULE_DEPEND(acpi_pcib, acpi, 1, 1, 1); static int acpi_pcib_acpi_probe(device_t dev) { + static char *pcib_ids[] = { "PNP0A03", NULL }; - if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && - acpi_enabled("pci") && - acpi_MatchHid(acpi_get_handle(dev), "PNP0A03")) { - if (pci_cfgregopen() == 0) - return (ENXIO); - device_set_desc(dev, "ACPI Host-PCI bridge"); - return (0); - } - return (ENXIO); + if (acpi_disabled("pcib") || + ACPI_ID_PROBE(device_get_parent(dev), dev, pcib_ids) == NULL) + return (ENXIO); + + if (pci_cfgregopen() == 0) + return (ENXIO); + device_set_desc(dev, "ACPI Host-PCI bridge"); + return (0); } static int @@ -149,7 +161,7 @@ acpi_pcib_acpi_attach(device_t dev) * Get our base bus number by evaluating _BBN. * If this doesn't work, we assume we're bus number 0. * - * XXX note that it may also not exist in the case where we are + * XXX note that it may also not exist in the case where we are * meant to use a private configuration space mechanism for this bus, * so we should dig out our resources and check to see if we have * anything like that. How do we do this? @@ -191,8 +203,8 @@ acpi_pcib_acpi_attach(device_t dev) device_printf(dev, "couldn't find _ADR\n"); } else { /* XXX: We assume bus 0. */ - slot = addr >> 16; - func = addr & 0xffff; + slot = ACPI_ADR_PCI_SLOT(addr); + func = ACPI_ADR_PCI_FUNC(addr); if (bootverbose) device_printf(dev, "reading config registers from 0:%d:%d\n", slot, func); @@ -230,16 +242,14 @@ acpi_pcib_acpi_attach(device_t dev) /* If it's not found, assume 0. */ sc->ap_segment = 0; } - return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_bus)); } static int acpi_pcib_acpi_resume(device_t dev) { - struct acpi_hpcib_softc *sc = device_get_softc(dev); - return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_bus)); + return (acpi_pcib_resume(dev)); } /* @@ -251,12 +261,18 @@ acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) struct acpi_hpcib_softc *sc = device_get_softc(dev); switch (which) { + case PCIB_IVAR_DOMAIN: + *result = 0; + return (0); case PCIB_IVAR_BUS: *result = sc->ap_bus; return (0); case ACPI_IVAR_HANDLE: *result = (uintptr_t)sc->ap_handle; return (0); + case ACPI_IVAR_FLAGS: + *result = (uintptr_t)sc->ap_flags; + return (0); } return (ENOENT); } @@ -264,12 +280,20 @@ acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) static int acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { - struct acpi_hpcib_softc *sc = device_get_softc(dev); + struct acpi_hpcib_softc *sc = device_get_softc(dev); switch (which) { + case PCIB_IVAR_DOMAIN: + return (EINVAL); case PCIB_IVAR_BUS: sc->ap_bus = value; return (0); + case ACPI_IVAR_HANDLE: + sc->ap_handle = (ACPI_HANDLE)value; + return (0); + case ACPI_IVAR_FLAGS: + sc->ap_flags = (int)value; + return (0); } return (ENOENT); } @@ -291,26 +315,61 @@ acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, static int acpi_pcib_acpi_route_interrupt(device_t pcib, device_t dev, int pin) { - struct acpi_hpcib_softc *sc; + struct acpi_hpcib_softc *sc = device_get_softc(pcib); - /* Find the bridge softc. */ - sc = device_get_softc(pcib); return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt)); } +#ifdef MSI +static int +acpi_pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, + int *irqs) +{ + device_t bus; + + bus = device_get_parent(pcib); + return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount, + irqs)); +} + +static int +acpi_pcib_alloc_msix(device_t pcib, device_t dev, int *irq) +{ + device_t bus; + + bus = device_get_parent(pcib); + return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq)); +} + +static int +acpi_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, + uint32_t *data) +{ + device_t bus; + + bus = device_get_parent(pcib); + return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data)); +} +#endif +static u_long acpi_host_mem_start = 0x80000000; +TUNABLE_INT("hw.acpi.host_mem_start", &acpi_host_mem_start); struct resource * acpi_pcib_acpi_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { /* - * If no memory preference is given, use upper 256MB slot most + * If no memory preference is given, use upper 32MB slot most * bioses use for their memory window. Typically other bridges * before us get in the way to assert their preferences on memory. * Hardcoding like this sucks, so a more MD/MI way needs to be - * found to do it. + * found to do it. This is typically only used on older laptops + * that don't have pci busses behind pci bridge, so assuming > 32MB + * is liekly OK. */ if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL) - start = 0xf0000000; + start = acpi_host_mem_start; + if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL) + start = 0x1000; return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } diff --git a/sys/dev/acpica5/acpi_pcib_pci.c b/sys/dev/acpica5/acpi_pcib_pci.c index 4a6007a5ac..deaac6e2a3 100644 --- a/sys/dev/acpica5/acpi_pcib_pci.c +++ b/sys/dev/acpica5/acpi_pcib_pci.c @@ -23,11 +23,11 @@ * 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_pcib_pci.c,v 1.9 2004/05/30 20:08:23 phk Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_pcib_pci.c,v 1.5 2008/08/02 01:14:41 dillon Exp $ + * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib_pci.c,v 1.17.8.1 2009/04/15 03:14:26 kensmith Exp $"); */ +#include + #include "opt_acpi.h" #include @@ -37,11 +37,10 @@ #include #include "acpi.h" -#include "accommon.h" -#include "acpivar.h" -#include "acpi_pcibvar.h" +#include +#include -#include +#include #include #include #include @@ -62,8 +61,6 @@ struct acpi_pcib_lookup_info { ACPI_HANDLE handle; }; -static devclass_t pcib_devclass; - static int acpi_pcib_pci_probe(device_t bus); static int acpi_pcib_pci_attach(device_t bus); static int acpi_pcib_pci_resume(device_t bus); @@ -84,7 +81,8 @@ static device_method_t acpi_pcib_pci_methods[] = { DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, acpi_pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), - DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), + // DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), @@ -96,25 +94,29 @@ static device_method_t acpi_pcib_pci_methods[] = { DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, acpi_pcib_pci_route_interrupt), - +#ifdef MSI + DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, pcib_release_msi), + DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, pcib_release_msix), + DEVMETHOD(pcib_map_msi, pcib_map_msi), +#endif {0, 0} }; -static driver_t acpi_pcib_pci_driver = { - "pcib", - acpi_pcib_pci_methods, - sizeof(struct acpi_pcib_softc), -}; - +static devclass_t pcib_devclass; +DEFINE_CLASS_0(pcib, acpi_pcib_pci_driver, acpi_pcib_pci_methods, + sizeof(struct acpi_pcib_softc)); DRIVER_MODULE(acpi_pcib, pci, acpi_pcib_pci_driver, pcib_devclass, 0, 0); MODULE_DEPEND(acpi_pcib, acpi, 1, 1, 1); static int acpi_pcib_pci_probe(device_t dev) { + if (pci_get_class(dev) != PCIC_BRIDGE || pci_get_subclass(dev) != PCIS_BRIDGE_PCI || - !acpi_enabled("pci")) + acpi_disabled("pci")) return (ENXIO); if (acpi_get_handle(dev) == NULL) return (ENXIO); @@ -122,16 +124,14 @@ acpi_pcib_pci_probe(device_t dev) return (ENXIO); device_set_desc(dev, "ACPI PCI-PCI bridge"); - return (-1000); + return (-100); } static int acpi_pcib_pci_attach(device_t dev) { struct acpi_pcib_softc *sc; - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - pcib_attach_common(dev); sc = device_get_softc(dev); sc->ap_handle = acpi_get_handle(dev); @@ -141,9 +141,8 @@ acpi_pcib_pci_attach(device_t dev) static int acpi_pcib_pci_resume(device_t dev) { - struct acpi_pcib_softc *sc = device_get_softc(dev); - return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_pcibsc.secbus)); + return (acpi_pcib_resume(dev)); } static int @@ -170,8 +169,10 @@ acpi_pcib_pci_route_interrupt(device_t pcib, device_t dev, int pin) * If we don't have a _PRT, fall back to the swizzle method * for routing interrupts. */ - if (sc->ap_prt.Pointer == NULL) + if (sc->ap_prt.Pointer == NULL) { +device_printf(pcib, "No _PRT found, routing with pci\n"); return (pcib_route_interrupt(pcib, dev, pin)); +} else return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt)); } diff --git a/sys/dev/acpica5/acpi_pcibvar.h b/sys/dev/acpica5/acpi_pcibvar.h index add69471db..bbbacd596b 100644 --- a/sys/dev/acpica5/acpi_pcibvar.h +++ b/sys/dev/acpica5/acpi_pcibvar.h @@ -24,19 +24,20 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/acpica/acpi_pcibvar.h,v 1.2 2002/10/05 02:01:02 iwasaki Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_pcibvar.h,v 1.1 2004/02/21 06:48:08 dillon Exp $ + * $FreeBSD: src/sys/dev/acpica/acpi_pcibvar.h,v 1.6.20.1 2009/04/15 03:14:26 kensmith Exp $ */ #ifndef _ACPI_PCIBVAR_H_ #define _ACPI_PCIBVAR_H_ +#ifdef _KERNEL +void acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, + int slot, int pin); +int acpi_pci_link_route_interrupt(device_t dev, int index); int acpi_pcib_attach(device_t bus, ACPI_BUFFER *prt, int busno); int acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin, - ACPI_BUFFER *ptrbuf); -int acpi_pcib_resume(device_t bus, ACPI_BUFFER *prt, int busno); + ACPI_BUFFER *prtbuf); +int acpi_pcib_resume(device_t dev); +#endif /* _KERNEL */ -int acpi_pci_link_config(device_t pcib, ACPI_BUFFER *prt, int busno); -int acpi_pci_link_resume(device_t pcib, ACPI_BUFFER *prt, int busno); - -#endif +#endif /* !_ACPI_PCIBVAR_H_ */ diff --git a/sys/dev/acpica5/acpi_powerres.c b/sys/dev/acpica5/acpi_powerres.c index 6d9bd071ff..d5fee40bd6 100644 --- a/sys/dev/acpica5/acpi_powerres.c +++ b/sys/dev/acpica5/acpi_powerres.c @@ -22,11 +22,11 @@ * 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_powerres.c,v 1.22 2004/04/14 17:58:19 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_powerres.c,v 1.5 2007/04/30 07:18:47 dillon Exp $ + * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_powerres.c,v 1.30.8.1 2009/04/15 03:14:26 kensmith Exp $"); */ +#include + #include "opt_acpi.h" #include #include @@ -34,7 +34,6 @@ #include #include "acpi.h" -#include "accommon.h" #include /* @@ -52,8 +51,6 @@ * scanning all of the ACPI namespace to find devices we're not currently * aware of, and this raises questions about whether they should be left * on, turned off, etc. - * - * XXX locking */ MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); @@ -65,6 +62,7 @@ ACPI_MODULE_NAME("POWERRES") /* Return values from _STA on a power resource */ #define ACPI_PWR_OFF 0 #define ACPI_PWR_ON 1 +#define ACPI_PWR_UNK (-1) /* A relationship between a power resource and a consumer. */ struct acpi_powerreference { @@ -90,12 +88,14 @@ struct acpi_powerresource { ACPI_HANDLE ap_resource; ACPI_INTEGER ap_systemlevel; ACPI_INTEGER ap_order; + int ap_state; }; static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; +ACPI_SERIAL_DECL(powerres, "ACPI power resources"); static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); #ifdef notyet @@ -107,6 +107,8 @@ static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); #endif /* notyet */ static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); +static int acpi_pwr_dereference_resource(struct acpi_powerconsumer + *pc); static ACPI_STATUS acpi_pwr_switch_power(void); static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); @@ -120,8 +122,7 @@ acpi_pwr_init(void *junk) TAILQ_INIT(&acpi_powerresources); TAILQ_INIT(&acpi_powerconsumers); } -SYSINIT(acpi_powerresource, SI_BOOT1_LOCK, SI_ORDER_ANY, - acpi_pwr_init, NULL); +// SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); /* * Register a power resource. @@ -137,6 +138,7 @@ acpi_pwr_register_resource(ACPI_HANDLE res) struct acpi_powerresource *rp, *srp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); rp = NULL; buf.Pointer = NULL; @@ -146,7 +148,10 @@ acpi_pwr_register_resource(ACPI_HANDLE res) return_ACPI_STATUS (AE_OK); /* already know about it */ /* Allocate a new resource */ - rp = kmalloc(sizeof(*rp), M_ACPIPWR, M_INTWAIT | M_ZERO); + if ((rp = kmalloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { + status = AE_NO_MEMORY; + goto out; + } TAILQ_INIT(&rp->ap_references); rp->ap_resource = res; @@ -166,6 +171,7 @@ acpi_pwr_register_resource(ACPI_HANDLE res) } rp->ap_systemlevel = obj->PowerResource.SystemLevel; rp->ap_order = obj->PowerResource.ResourceOrder; + rp->ap_state = ACPI_PWR_UNK; /* Sort the resource into the list */ status = AE_OK; @@ -204,6 +210,7 @@ acpi_pwr_deregister_resource(ACPI_HANDLE res) struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); rp = NULL; @@ -215,7 +222,7 @@ acpi_pwr_deregister_resource(ACPI_HANDLE res) if (TAILQ_FIRST(&rp->ap_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); - /* Pull it off the list and free it */ + /* Pull it off the list and kfree it */ TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); kfree(rp, M_ACPIPWR); @@ -237,13 +244,15 @@ acpi_pwr_register_consumer(ACPI_HANDLE consumer) struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); /* Check to see whether we know about this consumer already */ - if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) + if (acpi_pwr_find_consumer(consumer) != NULL) return_ACPI_STATUS (AE_OK); /* Allocate a new power consumer */ - pc = kmalloc(sizeof(*pc), M_ACPIPWR, M_INTWAIT); + if ((pc = kmalloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) + return_ACPI_STATUS (AE_NO_MEMORY); TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); TAILQ_INIT(&pc->ac_references); pc->ac_consumer = consumer; @@ -270,6 +279,7 @@ acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); /* Find the consumer */ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) @@ -279,8 +289,9 @@ acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) if (TAILQ_FIRST(&pc->ac_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); - /* Pull the consumer off the list and free it */ + /* Pull the consumer off the list and kfree it */ TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); + kfree(pc, M_ACPIPWR); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); @@ -296,7 +307,6 @@ ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) { struct acpi_powerconsumer *pc; - struct acpi_powerreference *pr; ACPI_HANDLE method_handle, reslist_handle, pr0_handle; ACPI_BUFFER reslist_buffer; ACPI_OBJECT *reslist_object; @@ -309,22 +319,25 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) /* It's never ok to switch a non-existent consumer. */ if (consumer == NULL) return_ACPI_STATUS (AE_NOT_FOUND); + reslist_buffer.Pointer = NULL; + reslist_object = NULL; + ACPI_SERIAL_BEGIN(powerres); /* Find the consumer */ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) - return_ACPI_STATUS (status); - if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { - return_ACPI_STATUS (AE_ERROR); /* something very wrong */ - } + goto out; + if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) + panic("acpi added power consumer but can't find it"); } - /* Check for valid transitions */ + /* Check for valid transitions. We can only go to D0 from D3. */ + status = AE_BAD_PARAMETER; if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) - return_ACPI_STATUS (AE_BAD_PARAMETER); /* can only go to D0 from D3 */ + goto out; /* Find transition mechanism(s) */ - switch(state) { + switch (state) { case ACPI_STATE_D0: method_name = "_PS0"; reslist_name = "_PR0"; @@ -342,7 +355,7 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) reslist_name = "_PR3"; break; default: - return_ACPI_STATUS (AE_BAD_PARAMETER); + goto out; } ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", acpi_name(consumer), pc->ac_state, state)); @@ -357,8 +370,6 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) * support D0 and D3. It's never an error to try to go to * D0. */ - reslist_buffer.Pointer = NULL; - reslist_object = NULL; if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) method_handle = NULL; if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) @@ -366,23 +377,39 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) if (reslist_handle == NULL && method_handle == NULL) { if (state == ACPI_STATE_D0) { pc->ac_state = ACPI_STATE_D0; - return_ACPI_STATUS (AE_OK); + status = AE_OK; + goto out; + } + if (state != ACPI_STATE_D3) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "attempt to set unsupported state D%d\n", state)); + goto out; } - if (state != ACPI_STATE_D3) - goto bad; - /* Turn off the resources listed in _PR0 to go to D3. */ - if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) - goto bad; + /* + * Turn off the resources listed in _PR0 to go to D3. If there is + * no _PR0 method, this object doesn't support ACPI power states. + */ + if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { + status = AE_NOT_FOUND; + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "device missing _PR0 (desired state was D%d)\n", state)); + goto out; + } reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); - if (ACPI_FAILURE(status)) - goto bad; + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "can't evaluate _PR0 for device %s, state D%d\n", + acpi_name(consumer), state)); + goto out; + } reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; - if (reslist_object->Type != ACPI_TYPE_PACKAGE || - reslist_object->Package.Count == 0) { - - goto bad; + if (!ACPI_PKG_VALID(reslist_object, 1)) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "invalid package object for state D%d\n", state)); + status = AE_TYPE; + goto out; } AcpiOsFree(reslist_buffer.Pointer); reslist_buffer.Pointer = NULL; @@ -416,15 +443,7 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) * Now we are ready to switch, so kill off any current power * resource references. */ - res_changed = 0; - while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { - res_changed = 1; - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", - acpi_name(pr->ar_resource->ap_resource))); - TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); - TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); - kfree(pr, M_ACPIPWR); - } + res_changed = acpi_pwr_dereference_resource(pc); /* * Add new power resource references, if we have any. Traverse the @@ -449,7 +468,7 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) acpi_name(consumer), state)); /* XXX is this appropriate? Should we return to previous state? */ - goto out; + goto out; } /* Invoke power state switch method (if present) */ @@ -467,22 +486,57 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) goto out; } } - + /* Transition was successful */ pc->ac_state = state; - return_ACPI_STATUS (AE_OK); - - bad: - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "attempt to set unsupported state D%d\n", state)); - status = AE_BAD_PARAMETER; + status = AE_OK; - out: +out: + ACPI_SERIAL_END(powerres); if (reslist_buffer.Pointer != NULL) AcpiOsFree(reslist_buffer.Pointer); return_ACPI_STATUS (status); } +/* Enable or disable a power resource for wake */ +ACPI_STATUS +acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) +{ + ACPI_STATUS status; + struct acpi_powerconsumer *pc; + struct acpi_prw_data prw; + int i; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (consumer == NULL) + return (AE_BAD_PARAMETER); + + ACPI_SERIAL_BEGIN(powerres); + if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { + if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) + goto out; + if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) + panic("acpi wake added power consumer but can't find it"); + } + + status = AE_OK; + if (acpi_parse_prw(consumer, &prw) != 0) + goto out; + for (i = 0; i < prw.power_res_count; i++) + if (enable) + acpi_pwr_reference_resource(&prw.power_res[i], pc); + else + acpi_pwr_dereference_resource(pc); + + if (prw.power_res_count > 0) + acpi_pwr_switch_power(); + +out: + ACPI_SERIAL_END(powerres); + return (status); +} + /* * Called to create a reference between a power consumer and a power resource * identified in the object. @@ -497,6 +551,7 @@ acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); res = acpi_GetReference(NULL, obj); if (res == NULL) { @@ -521,15 +576,40 @@ acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) acpi_name(rp->ap_resource))); /* Create a reference between the consumer and resource */ - pr = kmalloc(sizeof(*pr), M_ACPIPWR, M_INTWAIT | M_ZERO); + if ((pr = kmalloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "allocation failed for a power consumer reference\n")); + return_VOID; + } pr->ar_consumer = pc; pr->ar_resource = rp; TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); - + return_VOID; } +static int +acpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) +{ + struct acpi_powerreference *pr; + int changed; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); + + changed = 0; + while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", + acpi_name(pr->ar_resource->ap_resource))); + TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); + TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); + kfree(pr, M_ACPIPWR); + changed = 1; + } + + return (changed); +} /* * Switch power resources to conform to the desired state. @@ -545,6 +625,7 @@ acpi_pwr_switch_power(void) int cur; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); /* * Sweep the list forwards turning things on. @@ -562,17 +643,17 @@ acpi_pwr_switch_power(void) if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", acpi_name(rp->ap_resource), status)); - /* XXX is this correct? Always switch if in doubt? */ continue; - } + } else if (rp->ap_state == ACPI_PWR_UNK) + rp->ap_state = cur; /* * Switch if required. Note that we ignore the result of the switch * effort; we don't know what to do if it fails, so checking wouldn't * help much. */ - if (cur != ACPI_PWR_ON) { + if (rp->ap_state != ACPI_PWR_ON) { status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, @@ -580,6 +661,7 @@ acpi_pwr_switch_power(void) acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { + rp->ap_state = ACPI_PWR_ON; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); } @@ -607,14 +689,15 @@ acpi_pwr_switch_power(void) acpi_name(rp->ap_resource), status)); /* XXX is this correct? Always switch if in doubt? */ continue; - } + } else if (rp->ap_state == ACPI_PWR_UNK) + rp->ap_state = cur; /* * Switch if required. Note that we ignore the result of the switch * effort; we don't know what to do if it fails, so checking wouldn't * help much. */ - if (cur != ACPI_PWR_OFF) { + if (rp->ap_state != ACPI_PWR_OFF) { status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, @@ -622,6 +705,7 @@ acpi_pwr_switch_power(void) acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { + rp->ap_state = ACPI_PWR_OFF; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); } @@ -643,6 +727,7 @@ acpi_pwr_find_resource(ACPI_HANDLE res) struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { if (rp->ap_resource == res) @@ -661,6 +746,7 @@ acpi_pwr_find_consumer(ACPI_HANDLE consumer) struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { if (pc->ac_consumer == consumer) diff --git a/sys/dev/acpica5/acpi_resource.c b/sys/dev/acpica5/acpi_resource.c index 5005198a3e..aacc8057d5 100644 --- a/sys/dev/acpica5/acpi_resource.c +++ b/sys/dev/acpica5/acpi_resource.c @@ -23,27 +23,115 @@ * 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_resource.c,v 1.25 2004/06/13 22:52:30 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_resource.c,v 1.9 2007/10/23 03:04:48 y0netan1 Exp $ + * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_resource.c,v 1.40.8.1 2009/04/15 03:14:26 kensmith Exp $"); */ +#include + #include "opt_acpi.h" #include #include #include #include #include +#include + #include #include "acpi.h" -#include "accommon.h" #include /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("RESOURCE") +struct lookup_irq_request { + ACPI_RESOURCE *acpi_res; + struct resource *res; + int counter; + int rid; + int found; +}; + +static ACPI_STATUS +acpi_lookup_irq_handler(ACPI_RESOURCE *res, void *context) +{ + struct lookup_irq_request *req; + u_int irqnum, irq; + + switch (res->Type) { + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + if (res->Type == ACPI_RESOURCE_TYPE_IRQ) { + irqnum = res->Data.Irq.InterruptCount; + irq = res->Data.Irq.Interrupts[0]; + } else { + irqnum = res->Data.ExtendedIrq.InterruptCount; + irq = res->Data.ExtendedIrq.Interrupts[0]; + } + if (irqnum != 1) + break; + req = (struct lookup_irq_request *)context; + if (req->counter != req->rid) { + req->counter++; + break; + } + req->found = 1; + KASSERT(irq == rman_get_start(req->res), + ("IRQ resources do not match")); + bcopy(res, req->acpi_res, sizeof(ACPI_RESOURCE)); + return (AE_CTRL_TERMINATE); + } + return (AE_OK); +} + +ACPI_STATUS +acpi_lookup_irq_resource(device_t dev, int rid, struct resource *res, + ACPI_RESOURCE *acpi_res) +{ + struct lookup_irq_request req; + ACPI_STATUS status; + + req.acpi_res = acpi_res; + req.res = res; + req.counter = 0; + req.rid = rid; + req.found = 0; + status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", + acpi_lookup_irq_handler, &req); + if (ACPI_SUCCESS(status) && req.found == 0) + status = AE_NOT_FOUND; + return (status); +} + +void +acpi_config_intr(device_t dev, ACPI_RESOURCE *res) +{ + u_int irq; + int pol, trig; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_IRQ: + KASSERT(res->Data.Irq.InterruptCount == 1, + ("%s: multiple interrupts", __func__)); + irq = res->Data.Irq.Interrupts[0]; + trig = res->Data.Irq.Triggering; + pol = res->Data.Irq.Polarity; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + KASSERT(res->Data.ExtendedIrq.InterruptCount == 1, + ("%s: multiple interrupts", __func__)); + irq = res->Data.ExtendedIrq.Interrupts[0]; + trig = res->Data.ExtendedIrq.Triggering; + pol = res->Data.ExtendedIrq.Polarity; + break; + default: + panic("%s: bad resource type %u", __func__, res->Type); + } + BUS_CONFIG_INTR(dev, irq, (trig == ACPI_EDGE_SENSITIVE) ? + INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL, (pol == ACPI_ACTIVE_HIGH) ? + INTR_POLARITY_HIGH : INTR_POLARITY_LOW); +} + /* * Fetch a device's resources and associate them with the device. * @@ -211,12 +299,12 @@ acpi_parse_resources(device_t dev, ACPI_HANDLE handle, break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "start dependent functions\n")); - set->set_start_dependant(dev, context, + set->set_start_dependent(dev, context, res->Data.StartDpf.CompatibilityPriority); break; case ACPI_RESOURCE_TYPE_END_DEPENDENT: ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "end dependent functions\n")); - set->set_end_dependant(dev, context); + set->set_end_dependent(dev, context); break; case ACPI_RESOURCE_TYPE_ADDRESS32: if (res->Data.Address32.AddressLength <= 0) @@ -235,8 +323,8 @@ acpi_parse_resources(device_t dev, ACPI_HANDLE handle, break; } - if (res->Data.Address32.Minimum == ACPI_ADDRESS_FIXED && - res->Data.Address32.Maximum == ACPI_ADDRESS_FIXED) { + if (res->Data.Address32.MinAddressFixed == ACPI_ADDRESS_FIXED && + res->Data.Address32.MaxAddressFixed == ACPI_ADDRESS_FIXED) { if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) { ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, @@ -298,8 +386,8 @@ acpi_parse_resources(device_t dev, ACPI_HANDLE handle, break; } - if (res->Data.Address16.Minimum == ACPI_ADDRESS_FIXED && - res->Data.Address16.Maximum == ACPI_ADDRESS_FIXED) { + if (res->Data.Address16.MinAddressFixed == ACPI_ADDRESS_FIXED && + res->Data.Address16.MaxAddressFixed == ACPI_ADDRESS_FIXED) { if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) { ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, @@ -349,7 +437,11 @@ acpi_parse_resources(device_t dev, ACPI_HANDLE handle, "unimplemented Address64 resource\n")); break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - /* XXX special handling? */ + if (res->Data.ExtendedIrq.ProducerConsumer != ACPI_CONSUMER) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, + "ignored ExtIRQ producer\n")); + break; + } set->set_ext_irq(dev, context, res->Data.ExtendedIrq.Interrupts, res->Data.ExtendedIrq.InterruptCount, res->Data.ExtendedIrq.Triggering, @@ -388,13 +480,12 @@ static void acpi_res_set_memoryrange(device_t dev, void *context, static void acpi_res_set_irq(device_t dev, void *context, u_int8_t *irq, int count, int trig, int pol); static void acpi_res_set_ext_irq(device_t dev, void *context, - u_int32_t *irq, int count, int trig, - int pol); + u_int32_t *irq, int count, int trig, int pol); static void acpi_res_set_drq(device_t dev, void *context, u_int8_t *drq, int count); -static void acpi_res_set_start_dependant(device_t dev, void *context, +static void acpi_res_set_start_dependent(device_t dev, void *context, int preference); -static void acpi_res_set_end_dependant(device_t dev, void *context); +static void acpi_res_set_end_dependent(device_t dev, void *context); struct acpi_parse_resource_set acpi_res_parse_set = { acpi_res_set_init, @@ -406,8 +497,8 @@ struct acpi_parse_resource_set acpi_res_parse_set = { acpi_res_set_irq, acpi_res_set_ext_irq, acpi_res_set_drq, - acpi_res_set_start_dependant, - acpi_res_set_end_dependant + acpi_res_set_start_dependent, + acpi_res_set_end_dependent }; struct acpi_res_context { @@ -415,7 +506,7 @@ struct acpi_res_context { int ar_nmem; int ar_nirq; int ar_ndrq; - void *ar_parent; + void *ar_parent; }; static void @@ -499,11 +590,6 @@ acpi_res_set_irq(device_t dev, void *context, u_int8_t *irq, int count, return; bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1); -#if 0 /* From FreeBSD-5 XXX */ - BUS_CONFIG_INTR(dev, *irq, (trig == ACPI_EDGE_SENSITIVE) ? - INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL, (pol == ACPI_ACTIVE_HIGH) ? - INTR_POLARITY_HIGH : INTR_POLARITY_LOW); -#endif } static void @@ -520,11 +606,6 @@ acpi_res_set_ext_irq(device_t dev, void *context, u_int32_t *irq, int count, return; bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1); -#if 0 /* From FreeBSD-5 XXX */ - BUS_CONFIG_INTR(dev, *irq, (trig == ACPI_EDGE_SENSITIVE) ? - INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL, (pol == ACPI_ACTIVE_HIGH) ? - INTR_POLARITY_HIGH : INTR_POLARITY_LOW); -#endif } static void @@ -543,7 +624,7 @@ acpi_res_set_drq(device_t dev, void *context, u_int8_t *drq, int count) } static void -acpi_res_set_start_dependant(device_t dev, void *context, int preference) +acpi_res_set_start_dependent(device_t dev, void *context, int preference) { struct acpi_res_context *cp = (struct acpi_res_context *)context; @@ -553,7 +634,7 @@ acpi_res_set_start_dependant(device_t dev, void *context, int preference) } static void -acpi_res_set_end_dependant(device_t dev, void *context) +acpi_res_set_end_dependent(device_t dev, void *context) { struct acpi_res_context *cp = (struct acpi_res_context *)context; @@ -565,11 +646,13 @@ acpi_res_set_end_dependant(device_t dev, void *context) /* * Resource-owning placeholders for IO and memory pseudo-devices. * - * This code allocates system resource objects that will be owned by ACPI - * child devices. Really, the acpi parent device should have the resources - * but this would significantly affect the device probe code. + * This code allocates system resources that will be used by ACPI + * child devices. The acpi parent manages these resources through a + * private rman. */ +static int acpi_sysres_rid = 100; + static int acpi_sysres_probe(device_t dev); static int acpi_sysres_attach(device_t dev); @@ -595,93 +678,84 @@ MODULE_DEPEND(acpi_sysresource, acpi, 1, 1, 1); static int acpi_sysres_probe(device_t dev) { - ACPI_HANDLE h; + static char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL }; - h = acpi_get_handle(dev); if (acpi_disabled("sysresource") || - (!acpi_MatchHid(h, "PNP0C01") && !acpi_MatchHid(h, "PNP0C02"))) + ACPI_ID_PROBE(device_get_parent(dev), dev, sysres_ids) == NULL) return (ENXIO); device_set_desc(dev, "System Resource"); device_quiet(dev); - return (-100); + return (BUS_PROBE_DEFAULT); } static int acpi_sysres_attach(device_t dev) { + device_t bus; device_t gparent; - struct resource *res; - struct rman *rm; - struct resource_list_entry *rle; - struct resource_list *rl; - + struct resource_list_entry *bus_rle, *dev_rle; + struct resource_list *bus_rl, *dev_rl; + int done, type; + u_long start, end, count; /* - * Pre-allocate/manage all memory and IO resources. We detect duplicates - * by setting rle->res to the resource we got from the parent. We can't - * ignore them since rman can't handle duplicates. + * Loop through all current resources to see if the new one overlaps + * any existing ones. If so, grow the old one up and/or down + * accordingly. Discard any that are wholly contained in the old. If + * the resource is unique, add it to the parent. It will later go into + * the rman pool. */ - rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); - SLIST_FOREACH(rle, rl, link) { - if (rle->res != NULL) { - device_printf(dev, "duplicate resource for %lx\n", rle->start); + bus = device_get_parent(dev); +gparent = device_get_parent(bus); + dev_rl = BUS_GET_RESOURCE_LIST(bus, dev); + bus_rl = BUS_GET_RESOURCE_LIST(device_get_parent(bus), bus); +if(bus_rl) +kprintf("busrl is not null!\n"); + SLIST_FOREACH(dev_rle, dev_rl, link) { + if (dev_rle->type != SYS_RES_IOPORT && dev_rle->type != SYS_RES_MEMORY) 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; - } + start = dev_rle->start; + end = dev_rle->end; + count = dev_rle->count; + type = dev_rle->type; + done = FALSE; + if(bus_rl) { + SLIST_FOREACH(bus_rle, bus_rl, link) { + if (bus_rle->type != type) + continue; + + /* New resource wholly contained in old, discard. */ + if (start >= bus_rle->start && end <= bus_rle->end) + break; - /* Pre-allocate resource and add to our rman pool. */ - gparent = device_get_parent(device_get_parent(dev)); - res = BUS_ALLOC_RESOURCE(gparent, 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; - } - } + /* New tail overlaps old head, grow existing resource downward. */ + if (start < bus_rle->start && end >= bus_rle->start) { + bus_rle->count += bus_rle->start - start; + bus_rle->start = start; + done = TRUE; + } - return (0); -} + /* New head overlaps old tail, grow existing resource upward. */ + if (start <= bus_rle->end && end > bus_rle->end) { + bus_rle->count += end - bus_rle->end; + bus_rle->end = end; + done = TRUE; + } -struct resource_list_entry * -acpi_sysres_find(int type, u_long addr) -{ - device_t *devs; - int i, numdevs; - struct resource_list *rl; - struct resource_list_entry *rle; - - /* We only consider IO and memory resources for our pool. */ - rle = NULL; - if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) - return (rle); - - /* Find all the sysresource devices. */ - if (devclass_get_devices(acpi_sysres_devclass, &devs, &numdevs) != 0) - return (rle); - - /* Check each device for a resource that contains "addr". */ - for (i = 0; i < numdevs && rle == NULL; i++) { - rl = BUS_GET_RESOURCE_LIST(device_get_parent(devs[i]), devs[i]); - if (rl == NULL) - continue; - SLIST_FOREACH(rle, rl, link) { - if (type == rle->type && addr >= rle->start && - addr < rle->start + rle->count) + /* If we adjusted the old resource, we're finished. */ + if (done) break; } + } else bus_rle = NULL; + /* If we didn't merge with anything, add this resource. */ + if (bus_rle == NULL) { + bus_set_resource(bus, type, acpi_sysres_rid++, start, count); + } } - kfree(devs, M_TEMP); - return (rle); + /* After merging/moving resources to the parent, free the list. */ + resource_list_free(dev_rl); + + return (0); } diff --git a/sys/dev/acpica5/acpi_thermal.c b/sys/dev/acpica5/acpi_thermal.c index d77cded4ed..2664193071 100644 --- a/sys/dev/acpica5/acpi_thermal.c +++ b/sys/dev/acpica5/acpi_thermal.c @@ -23,15 +23,17 @@ * 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_thermal.c,v 1.47 2004/05/30 20:08:23 phk Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_thermal.c,v 1.7 2007/01/17 17:31:19 y0netan1 Exp $ + * $FreeBSD: src/sys/dev/acpica/acpi_thermal.c,v 1.73 2009/08/20 19:17:53 jhb */ +#include + #include "opt_acpi.h" #include #include +#include #include +#include #include #include #include @@ -39,11 +41,11 @@ #include #include #include -#include #include #include "acpi.h" #include "accommon.h" + #include /* Hooks for the ACPI CA debugging infrastructure */ @@ -51,7 +53,7 @@ ACPI_MODULE_NAME("THERMAL") #define TZ_ZEROC 2732 -#define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10) +#define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10) #define TZ_NOTIFY_TEMPERATURE 0x80 /* Temperature changed. */ #define TZ_NOTIFY_LEVELS 0x81 /* Cooling levels changed. */ @@ -67,6 +69,8 @@ ACPI_MODULE_NAME("THERMAL") /* Notify the user we will be shutting down in one more poll cycle. */ #define TZ_NOTIFYCOUNT (TZ_VALIDCHECKS - 1) +#define abs(x) ( x < 0 ? -x : x ) + /* ACPI spec defines this */ #define TZ_NUMLEVELS 10 struct acpi_tz_zone { @@ -88,45 +92,62 @@ struct acpi_tz_softc { int tz_temperature; /*Current temperature*/ int tz_active; /*Current active cooling*/ #define TZ_ACTIVE_NONE -1 +#define TZ_ACTIVE_UNKNOWN -2 int tz_requested; /*Minimum active cooling*/ int tz_thflags; /*Current temp-related flags*/ #define TZ_THFLAG_NONE 0 #define TZ_THFLAG_PSV (1<<0) #define TZ_THFLAG_HOT (1<<2) -#define TZ_THFLAG_CRT (1<<3) +#define TZ_THFLAG_CRT (1<<3) int tz_flags; #define TZ_FLAG_NO_SCP (1<<0) /*No _SCP method*/ #define TZ_FLAG_GETPROFILE (1<<1) /*Get power_profile in timeout*/ +#define TZ_FLAG_GETSETTINGS (1<<2) /*Get devs/setpoints*/ struct timespec tz_cooling_started; /*Current cooling starting time*/ struct sysctl_ctx_list tz_sysctl_ctx; struct sysctl_oid *tz_sysctl_tree; - + eventhandler_tag tz_event; + struct acpi_tz_zone tz_zone; /*Thermal zone parameters*/ - int tz_tmp_updating; int tz_validchecks; + + /* passive cooling */ + struct thread *tz_cooling_proc; + int tz_cooling_proc_running; + int tz_cooling_enabled; + int tz_cooling_active; + int tz_cooling_updated; + int tz_cooling_saved_freq; /* sensors(9) related */ struct ksensordev sensordev; struct ksensor sensor; }; +#define CPUFREQ_MAX_LEVELS 64 /* XXX cpufreq should export this */ + static int acpi_tz_probe(device_t dev); static int acpi_tz_attach(device_t dev); static int acpi_tz_establish(struct acpi_tz_softc *sc); static void acpi_tz_monitor(void *Context); -static void acpi_tz_all_off(struct acpi_tz_softc *sc); static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg); static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg); static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data); static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what); static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); -static void acpi_tz_timeout(struct acpi_tz_softc *sc); +static void acpi_tz_signal(struct acpi_tz_softc *sc, int flags); +static void acpi_tz_timeout(struct acpi_tz_softc *sc, int flags); static void acpi_tz_power_profile(void *arg); static void acpi_tz_thread(void *arg); +static int acpi_tz_cooling_is_available(struct acpi_tz_softc *sc); +static int acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc); static device_method_t acpi_tz_methods[] = { /* Device interface */ @@ -150,38 +171,29 @@ static struct sysctl_ctx_list acpi_tz_sysctl_ctx; static struct sysctl_oid *acpi_tz_sysctl_tree; /* Minimum cooling run time */ -static int acpi_tz_min_runtime = 0; +static int acpi_tz_min_runtime; static int acpi_tz_polling_rate = TZ_POLLRATE; +static int acpi_tz_override; /* Timezone polling thread */ static struct thread *acpi_tz_td; +ACPI_LOCK_DECL(thermal, "ACPI thermal zone"); + +static int acpi_tz_cooling_unit = -1; -/* - * Match an ACPI thermal zone. - */ static int acpi_tz_probe(device_t dev) { int result; - ACPI_LOCK_DECL; - - ACPI_LOCK; - - /* No FUNCTION_TRACE - too noisy */ if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) { device_set_desc(dev, "Thermal Zone"); result = -10; - } else { + } else result = ENXIO; - } - ACPI_UNLOCK; return (result); } -/* - * Attach to an ACPI thermal zone. - */ static int acpi_tz_attach(device_t dev) { @@ -189,29 +201,33 @@ acpi_tz_attach(device_t dev) struct acpi_softc *acpi_sc; int error; char oidname[8]; - ACPI_LOCK_DECL; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_LOCK; - sc = device_get_softc(dev); sc->tz_dev = dev; sc->tz_handle = acpi_get_handle(dev); sc->tz_requested = TZ_ACTIVE_NONE; - sc->tz_tmp_updating = 0; + sc->tz_active = TZ_ACTIVE_UNKNOWN; + sc->tz_thflags = TZ_THFLAG_NONE; + sc->tz_cooling_proc = NULL; + sc->tz_cooling_proc_running = FALSE; + sc->tz_cooling_active = FALSE; + sc->tz_cooling_updated = FALSE; + sc->tz_cooling_enabled = FALSE; /* * Parse the current state of the thermal zone and build control - * structures. + * structures. We don't need to worry about interference with the + * control thread since we haven't fully attached this device yet. */ if ((error = acpi_tz_establish(sc)) != 0) - goto out; + return (error); /* * Register for any Notify events sent to this zone. */ - AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, + AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, acpi_tz_notify_handler, sc); /* @@ -227,68 +243,115 @@ acpi_tz_attach(device_t dev) OID_AUTO, "thermal", CTLFLAG_RD, 0, ""); SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx, SYSCTL_CHILDREN(acpi_tz_sysctl_tree), - OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW, + OID_AUTO, "min_runtime", CTLFLAG_RW, &acpi_tz_min_runtime, 0, "minimum cooling run time in sec"); SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx, SYSCTL_CHILDREN(acpi_tz_sysctl_tree), - OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW, + OID_AUTO, "polling_rate", CTLFLAG_RW, &acpi_tz_polling_rate, 0, "monitor polling rate"); + SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx, + SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO, + "user_override", CTLFLAG_RW, &acpi_tz_override, 0, + "allow override of thermal settings"); } sysctl_ctx_init(&sc->tz_sysctl_ctx); ksprintf(oidname, "tz%d", device_get_unit(dev)); sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO, oidname, CTLFLAG_RD, 0, ""); - SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), - OID_AUTO, "temperature", CTLFLAG_RD, - &sc->tz_temperature, 0, "current thermal zone temperature"); + SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "temperature", CTLFLAG_RD, &sc->tz_temperature, + sizeof(sc->tz_temperature), "IK", + "current thermal zone temperature"); SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW, - sc, 0, acpi_tz_active_sysctl, "I", ""); - + sc, 0, acpi_tz_active_sysctl, "I", "cooling is active"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, acpi_tz_cooling_sysctl, "I", + "enable passive (speed reduction) cooling"); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "thermal_flags", CTLFLAG_RD, &sc->tz_thflags, 0, "thermal zone flags"); - SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), - OID_AUTO, "_PSV", CTLFLAG_RD, - &sc->tz_zone.psv, 0, ""); - SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), - OID_AUTO, "_HOT", CTLFLAG_RD, - &sc->tz_zone.hot, 0, ""); - SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), - OID_AUTO, "_CRT", CTLFLAG_RD, - &sc->tz_zone.crt, 0, ""); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.psv), + acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.hot), + acpi_tz_temp_sysctl, "IK", + "too hot temp setpoint (suspend now)"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_CRT", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.crt), + acpi_tz_temp_sysctl, "IK", + "critical temp setpoint (shutdown now)"); SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac, - sizeof(sc->tz_zone.ac), "I", ""); + sizeof(sc->tz_zone.ac), "IK", ""); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TC1", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.tc1), + acpi_tz_passive_sysctl, "I", + "thermal constant 1 for passive cooling"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TC2", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.tc2), + acpi_tz_passive_sysctl, "I", + "thermal constant 2 for passive cooling"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TSP", CTLTYPE_INT | CTLFLAG_RW, + sc, offsetof(struct acpi_tz_softc, tz_zone.tsp), + acpi_tz_passive_sysctl, "I", + "thermal sampling period for passive cooling"); /* - * Register our power profile event handler, and flag it for a manual - * invocation by our timeout. We defer it like this so that the rest - * of the subsystem has time to come up. + * Create thread to service all of the thermal zones. Register + * our power profile event handler. */ - EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0); - sc->tz_flags |= TZ_FLAG_GETPROFILE; + sc->tz_event = EVENTHANDLER_REGISTER(power_profile_change, + acpi_tz_power_profile, sc, 0); + if (acpi_tz_td == NULL) { + error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_td, + RFHIGHPID, 0, "acpi_thermal"); + if (error != 0) { + device_printf(sc->tz_dev, "could not create thread - %d", error); + goto out; + } + } /* - * Don't bother evaluating/printing the temperature at this point; - * on many systems it'll be bogus until the EC is running. + * Create a thread to handle passive cooling for 1st zone which + * has _PSV, _TSP, _TC1 and _TC2. Users can enable it for other + * zones manually for now. + * + * XXX We enable only one zone to avoid multiple zones conflict + * with each other since cpufreq currently sets all CPUs to the + * given frequency whereas it's possible for different thermal + * zones to specify independent settings for multiple CPUs. */ + if (acpi_tz_cooling_unit < 0 && acpi_tz_cooling_is_available(sc)) + sc->tz_cooling_enabled = TRUE; + if (sc->tz_cooling_enabled) { + error = acpi_tz_cooling_thread_start(sc); + if (error != 0) { + sc->tz_cooling_enabled = FALSE; + goto out; + } + acpi_tz_cooling_unit = device_get_unit(dev); + } /* - * Create our thread; we only need one, it will service all of the - * thermal zones. + * Flag the event handler for a manual invocation by our timeout. + * We defer it like this so that the rest of the subsystem has time + * to come up. Don't bother evaluating/printing the temperature at + * this point; on many systems it'll be bogus until the EC is running. */ - if (acpi_tz_td == NULL) { - error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_td, - RFHIGHPID, 0, "acpi_thermal"); - if (error != 0) { - device_printf(sc->tz_dev, "could not create thread - %d", - error); - goto out; - } - } + sc->tz_flags |= TZ_FLAG_GETPROFILE; + /* Attach sensors(9). */ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->tz_dev), sizeof(sc->sensordev.xname)); @@ -299,8 +362,12 @@ acpi_tz_attach(device_t dev) sensordev_install(&sc->sensordev); out: - ACPI_UNLOCK; - + if (error != 0) { + EVENTHANDLER_DEREGISTER(power_profile_change, sc->tz_event); + AcpiRemoveNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, + acpi_tz_notify_handler); + sysctl_ctx_free(&sc->tz_sysctl_ctx); + } return_VALUE (error); } @@ -315,19 +382,23 @@ acpi_tz_establish(struct acpi_tz_softc *sc) ACPI_OBJECT *obj; int i; char nbuf[8]; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - /* Power everything off and erase any existing state. */ - acpi_tz_all_off(sc); + /* Erase any existing state. */ for (i = 0; i < TZ_NUMLEVELS; i++) if (sc->tz_zone.al[i].Pointer != NULL) AcpiOsFree(sc->tz_zone.al[i].Pointer); if (sc->tz_zone.psl.Pointer != NULL) AcpiOsFree(sc->tz_zone.psl.Pointer); - bzero(&sc->tz_zone, sizeof(sc->tz_zone)); + + /* + * XXX: We initialize only ACPI_BUFFER to avoid race condition + * with passive cooling thread which refers psv, tc1, tc2 and tsp. + */ + bzero(sc->tz_zone.ac, sizeof(sc->tz_zone.ac)); + bzero(sc->tz_zone.al, sizeof(sc->tz_zone.al)); + bzero(&sc->tz_zone.psl, sizeof(sc->tz_zone.psl)); /* Evaluate thermal zone parameters. */ for (i = 0; i < TZ_NUMLEVELS; i++) { @@ -370,25 +441,56 @@ acpi_tz_establish(struct acpi_tz_softc *sc) for (i = 0; i < TZ_NUMLEVELS; i++) acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx"); - /* - * Power off everything that we've just been given. - */ - acpi_tz_all_off(sc); - return_VALUE (0); } -static char *aclevel_string[] = { - "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4", - "_AC5", "_AC6", "_AC7", "_AC8", "_AC9" }; +static char *aclevel_string[] = { + "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4", + "_AC5", "_AC6", "_AC7", "_AC8", "_AC9" +}; static __inline const char * acpi_tz_aclevel_string(int active) { - if (active < -1 || active >= TZ_NUMLEVELS) - return (aclevel_string[0]); + if (active < -1 || active >= TZ_NUMLEVELS) + return (aclevel_string[0]); - return (aclevel_string[active+1]); + return (aclevel_string[active + 1]); +} + +/* + * Get the current temperature. + */ +static int +acpi_tz_get_temperature(struct acpi_tz_softc *sc) +{ + int temp; + ACPI_STATUS status; + static char *tmp_name = "_TMP"; + + ACPI_FUNCTION_NAME ("acpi_tz_get_temperature"); + + /* Evaluate the thermal zone's _TMP method. */ + status = acpi_GetInteger(sc->tz_handle, tmp_name, &temp); + if (ACPI_FAILURE(status)) { + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "error fetching current temperature -- %s\n", + AcpiFormatException(status)); + return (FALSE); + } + + /* Check it for validity. */ + acpi_tz_sanity(sc, &temp, tmp_name); + if (temp == -1) + return (FALSE); + + ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp))); + sc->tz_temperature = temp; + /* Update sensor */ + if(sc->tz_temperature == -1) + sc->sensor.flags &= ~SENSOR_FINVALID; + sc->sensor.value = sc->tz_temperature * 100000; + return (TRUE); } /* @@ -402,33 +504,17 @@ acpi_tz_monitor(void *Context) int temp; int i; int newactive, newflags; - ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; - sc = (struct acpi_tz_softc *)Context; - if (sc->tz_tmp_updating) - goto out; - sc->tz_tmp_updating = 1; /* Get the current temperature. */ - status = acpi_GetInteger(sc->tz_handle, "_TMP", &temp); - if (ACPI_FAILURE(status)) { - ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), - "error fetching current temperature -- %s\n", - AcpiFormatException(status)); + if (!acpi_tz_get_temperature(sc)) { /* XXX disable zone? go to max cooling? */ - goto out; + return_VOID; } - - ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp))); - sc->tz_temperature = temp; - /* Update sensor */ - if(sc->tz_temperature == -1) - sc->sensor.flags &= ~SENSOR_FINVALID; - sc->sensor.value = sc->tz_temperature * 100000; + temp = sc->tz_temperature; /* * Work out what we ought to be doing right now. @@ -437,14 +523,13 @@ acpi_tz_monitor(void *Context) */ newactive = TZ_ACTIVE_NONE; for (i = TZ_NUMLEVELS - 1; i >= 0; i--) { - if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) { + if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i]) { newactive = i; if (sc->tz_active != newactive) { ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i, TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i])); - getnanotime(&sc->tz_cooling_started); } } } @@ -454,6 +539,7 @@ acpi_tz_monitor(void *Context) * minimum cooling run time if requested. */ if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE && + sc->tz_active != TZ_ACTIVE_UNKNOWN && (newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) { getnanotime(&curtime); @@ -463,7 +549,8 @@ acpi_tz_monitor(void *Context) } /* Handle user override of active mode */ - if (sc->tz_requested != TZ_ACTIVE_NONE && sc->tz_requested < newactive) + if (sc->tz_requested != TZ_ACTIVE_NONE && (newactive == TZ_ACTIVE_NONE + || sc->tz_requested < newactive)) newactive = sc->tz_requested; /* update temperature-related flags */ @@ -476,6 +563,23 @@ acpi_tz_monitor(void *Context) newflags |= TZ_THFLAG_CRT; /* If the active cooling state has changed, we have to switch things. */ + if (sc->tz_active == TZ_ACTIVE_UNKNOWN) { + /* + * We don't know which cooling device is on or off, + * so stop them all, because we now know which + * should be on (if any). + */ + for (i = 0; i < TZ_NUMLEVELS; i++) { + if (sc->tz_zone.al[i].Pointer != NULL) { + acpi_ForeachPackageObject( + (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, + acpi_tz_switch_cooler_off, sc); + } + } + /* now we know that all devices are off */ + sc->tz_active = TZ_ACTIVE_NONE; + } + if (newactive != sc->tz_active) { /* Turn off the cooling devices that are on, if any are */ if (sc->tz_active != TZ_ACTIVE_NONE) @@ -494,6 +598,7 @@ acpi_tz_monitor(void *Context) acpi_tz_aclevel_string(sc->tz_active), acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp)); sc->tz_active = newactive; + getnanotime(&sc->tz_cooling_started); } /* XXX (de)activate any passive cooling that may be required. */ @@ -522,43 +627,11 @@ acpi_tz_monitor(void *Context) } sc->tz_thflags = newflags; -out: - sc->tz_tmp_updating = 0; - return_VOID; -} - -/* - * Turn off all the cooling devices. - */ -static void -acpi_tz_all_off(struct acpi_tz_softc *sc) -{ - int i; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - ACPI_ASSERTLOCK; - - /* Scan all the _ALx objects and turn them all off. */ - for (i = 0; i < TZ_NUMLEVELS; i++) { - if (sc->tz_zone.al[i].Pointer == NULL) - continue; - acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, - acpi_tz_switch_cooler_off, sc); - } - - /* - * XXX revert any passive-cooling options. - */ - - sc->tz_active = TZ_ACTIVE_NONE; - sc->tz_thflags = TZ_THFLAG_NONE; - return_VOID; } /* - * Given an object, verify that it's a reference to a device of some sort, + * Given an object, verify that it's a reference to a device of some sort, * and try to switch it off. */ static void @@ -568,8 +641,6 @@ acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg) ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; - cooler = acpi_GetReference(NULL, obj); if (cooler == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n")); @@ -584,7 +655,7 @@ acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg) } /* - * Given an object, verify that it's a reference to a device of some sort, + * Given an object, verify that it's a reference to a device of some sort, * and try to switch it on. * * XXX replication of off/on function code is bad. @@ -595,10 +666,8 @@ acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg) struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; ACPI_HANDLE cooler; ACPI_STATUS status; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); cooler = acpi_GetReference(NULL, obj); if (cooler == NULL) { @@ -627,8 +696,6 @@ acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data) ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; - if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) { *data = -1; } else { @@ -636,17 +703,17 @@ acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data) acpi_name(sc->tz_handle), node, *data)); } - return_VOID; + return_VOID; } /* * Sanity-check a temperature value. Assume that setpoints - * should be between 0C and 150C. + * should be between 0C and 200C. */ static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) { - if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 1500)) { + if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) { device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", what, TZ_KELVTOC(*val)); *val = -1; @@ -655,16 +722,13 @@ acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) /* * Respond to a sysctl on the active state node. - */ + */ static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_tz_softc *sc; int active; int error; - ACPI_LOCK_DECL; - - ACPI_LOCK; sc = (struct acpi_tz_softc *)oidp->oid_arg1; active = sc->tz_active; @@ -672,24 +736,97 @@ acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS) /* Error or no new value */ if (error != 0 || req->newptr == NULL) - goto out; - if (active < -1 || active >= TZ_NUMLEVELS) { - error = EINVAL; - goto out; - } + return (error); + if (active < -1 || active >= TZ_NUMLEVELS) + return (EINVAL); /* Set new preferred level and re-switch */ sc->tz_requested = active; - acpi_tz_monitor(sc); + acpi_tz_signal(sc, 0); + return (0); +} + +static int +acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_tz_softc *sc; + int enabled, error; + + sc = (struct acpi_tz_softc *)oidp->oid_arg1; + enabled = sc->tz_cooling_enabled; + error = sysctl_handle_int(oidp, &enabled, 0, req); - out: - ACPI_UNLOCK; + /* Error or no new value */ + if (error != 0 || req->newptr == NULL) + return (error); + if (enabled != TRUE && enabled != FALSE) + return (EINVAL); + + if (enabled) { + if (acpi_tz_cooling_is_available(sc)) + error = acpi_tz_cooling_thread_start(sc); + else + error = ENODEV; + if (error) + enabled = FALSE; + } + sc->tz_cooling_enabled = enabled; return (error); } -/* - * Respond to a Notify event sent to the zone. - */ +static int +acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_tz_softc *sc; + int temp, *temp_ptr; + int error; + + sc = oidp->oid_arg1; + temp_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2); + temp = *temp_ptr; + error = sysctl_handle_int(oidp, &temp, 0, req); + + /* Error or no new value */ + if (error != 0 || req->newptr == NULL) + return (error); + + /* Only allow changing settings if override is set. */ + if (!acpi_tz_override) + return (EPERM); + + /* Check user-supplied value for sanity. */ + acpi_tz_sanity(sc, &temp, "user-supplied temp"); + if (temp == -1) + return (EINVAL); + + *temp_ptr = temp; + return (0); +} + +static int +acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_tz_softc *sc; + int val, *val_ptr; + int error; + + sc = oidp->oid_arg1; + val_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2); + val = *val_ptr; + error = sysctl_handle_int(oidp, &val, 0, req); + + /* Error or no new value */ + if (error != 0 || req->newptr == NULL) + return (error); + + /* Only allow changing settings if override is set. */ + if (!acpi_tz_override) + return (EPERM); + + *val_ptr = val; + return (0); +} + static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) { @@ -697,18 +834,15 @@ acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_ASSERTLOCK; - - switch(notify) { + switch (notify) { case TZ_NOTIFY_TEMPERATURE: /* Temperature change occurred */ - AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_tz_monitor, sc); + acpi_tz_signal(sc, 0); break; case TZ_NOTIFY_DEVICES: case TZ_NOTIFY_LEVELS: /* Zone devices/setpoints changed */ - AcpiOsExecute(OSL_NOTIFY_HANDLER, - (ACPI_OSD_EXEC_CALLBACK)acpi_tz_establish, sc); + acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS); break; default: ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), @@ -721,23 +855,43 @@ acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) return_VOID; } +static void +acpi_tz_signal(struct acpi_tz_softc *sc, int flags) +{ + ACPI_LOCK(thermal); + sc->tz_flags |= flags; + ACPI_UNLOCK(thermal); + wakeup(&acpi_tz_td); +} + /* - * Poll the thermal zone. + * Notifies can be generated asynchronously but have also been seen to be + * triggered by other thermal methods. One system generates a notify of + * 0x81 when the fan is turned on or off. Another generates it when _SCP + * is called. To handle these situations, we check the zone via + * acpi_tz_monitor() before evaluating changes to setpoints or the cooling + * policy. */ static void -acpi_tz_timeout(struct acpi_tz_softc *sc) +acpi_tz_timeout(struct acpi_tz_softc *sc, int flags) { - /* Do we need to get the power profile settings? */ - if (sc->tz_flags & TZ_FLAG_GETPROFILE) { - acpi_tz_power_profile((void *)sc); - sc->tz_flags &= ~TZ_FLAG_GETPROFILE; - } - - ACPI_ASSERTLOCK; /* Check the current temperature and take action based on it */ acpi_tz_monitor(sc); + /* If requested, get the power profile settings. */ + if (flags & TZ_FLAG_GETPROFILE) + acpi_tz_power_profile(sc); + + /* + * If requested, check for new devices/setpoints. After finding them, + * check if we need to switch fans based on the new values. + */ + if (flags & TZ_FLAG_GETSETTINGS) { + acpi_tz_establish(sc); + acpi_tz_monitor(sc); + } + /* XXX passive cooling actions? */ } @@ -754,14 +908,11 @@ acpi_tz_power_profile(void *arg) ACPI_STATUS status; struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; int state; - ACPI_LOCK_DECL; state = power_profile_get_state(); if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY) return; - ACPI_LOCK; - /* check that we haven't decided there's no _SCP method */ if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) { @@ -778,13 +929,9 @@ acpi_tz_power_profile(void *arg) sc->tz_flags |= TZ_FLAG_NO_SCP; } else { /* We have to re-evaluate the entire zone now */ - AcpiOsExecute(OSL_NOTIFY_HANDLER, - (ACPI_OSD_EXEC_CALLBACK)acpi_tz_establish, - sc); + acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS); } } - - ACPI_UNLOCK; } /* @@ -795,30 +942,278 @@ acpi_tz_thread(void *arg) { device_t *devs; int devcount, i; - ACPI_LOCK_DECL; + int flags; + struct acpi_tz_softc **sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); devs = NULL; devcount = 0; + sc = NULL; for (;;) { - tsleep(&acpi_tz_td, 0, "tzpoll", hz * acpi_tz_polling_rate); + /* If the number of devices has changed, re-evaluate. */ + if (devclass_get_count(acpi_tz_devclass) != devcount) { + if (devs != NULL) { + kfree(devs, M_TEMP); + kfree(sc, M_TEMP); + } + devclass_get_devices(acpi_tz_devclass, &devs, &devcount); + sc = kmalloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP, + M_WAITOK | M_ZERO); + for (i = 0; i < devcount; i++) + sc[i] = device_get_softc(devs[i]); + } + + /* Check for temperature events and act on them. */ + for (i = 0; i < devcount; i++) { + ACPI_LOCK(thermal); + flags = sc[i]->tz_flags; + sc[i]->tz_flags &= TZ_FLAG_NO_SCP; + ACPI_UNLOCK(thermal); + acpi_tz_timeout(sc[i], flags); + } + + /* If more work to do, don't go to sleep yet. */ + ACPI_LOCK(thermal); + for (i = 0; i < devcount; i++) { + if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP) + break; + } + + /* + * If we have no more work, sleep for a while, setting PDROP so that + * the mutex will not be reacquired. Otherwise, drop the mutex and + * loop to handle more events. + */ + if (i == devcount) + tsleep(&acpi_tz_td, 0, "tzpoll", + hz * acpi_tz_polling_rate); + else + ACPI_UNLOCK(thermal); + } +} + +#ifdef __FreeBSD__ +static int +acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc) +{ + device_t dev; + int error; + + if (!sc->tz_cooling_updated) + return (0); + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) + return (ENXIO); + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: resuming previous clock speed (%d MHz)\n", + TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq); + error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN); + if (error == 0) + sc->tz_cooling_updated = FALSE; + return (error); +} + +static int +acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) +{ + device_t dev; + struct cf_level *levels; + int num_levels, error, freq, desired_freq, perf, i; -#if __FreeBSD_version >= 500000 - mtx_lock(&Giant); + levels = kmalloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + + /* + * Find the main device, cpufreq0. We don't yet support independent + * CPU frequency control on SMP. + */ + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) { + error = ENXIO; + goto out; + } + + /* Get the current frequency. */ + error = CPUFREQ_GET(dev, &levels[0]); + if (error) + goto out; + freq = levels[0].total_set.freq; + + /* Get the current available frequency levels. */ + num_levels = CPUFREQ_MAX_LEVELS; + error = CPUFREQ_LEVELS(dev, levels, &num_levels); + if (error) { + if (error == E2BIG) + printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); + goto out; + } + + /* Calculate the desired frequency as a percent of the max frequency. */ + perf = 100 * freq / levels[0].total_set.freq - req; + if (perf < 0) + perf = 0; + else if (perf > 100) + perf = 100; + desired_freq = levels[0].total_set.freq * perf / 100; + + if (desired_freq < freq) { + /* Find the closest available frequency, rounding down. */ + for (i = 0; i < num_levels; i++) + if (levels[i].total_set.freq <= desired_freq) + break; + + /* If we didn't find a relevant setting, use the lowest. */ + if (i == num_levels) + i--; + } else { + /* If we didn't decrease frequency yet, don't increase it. */ + if (!sc->tz_cooling_updated) { + sc->tz_cooling_active = FALSE; + goto out; + } + + /* Use saved cpu frequency as maximum value. */ + if (desired_freq > sc->tz_cooling_saved_freq) + desired_freq = sc->tz_cooling_saved_freq; + + /* Find the closest available frequency, rounding up. */ + for (i = num_levels - 1; i >= 0; i--) + if (levels[i].total_set.freq >= desired_freq) + break; + + /* If we didn't find a relevant setting, use the highest. */ + if (i == -1) + i++; + + /* If we're going to the highest frequency, restore the old setting. */ + if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) { + error = acpi_tz_cpufreq_restore(sc); + if (error == 0) + sc->tz_cooling_active = FALSE; + goto out; + } + } + + /* If we are going to a new frequency, activate it. */ + if (levels[i].total_set.freq != freq) { + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: %screasing clock speed " + "from %d MHz to %d MHz\n", + TZ_KELVTOC(sc->tz_temperature), + (freq > levels[i].total_set.freq) ? "de" : "in", + freq, levels[i].total_set.freq); + error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN); + if (error == 0 && !sc->tz_cooling_updated) { + sc->tz_cooling_saved_freq = freq; + sc->tz_cooling_updated = TRUE; + } + } + +out: + if (levels) + free(levels, M_TEMP); + return (error); +} #endif - if (devcount == 0) - devclass_get_devices(acpi_tz_devclass, &devs, &devcount); +/* + * Passive cooling thread; monitors current temperature according to the + * cooling interval and calculates whether to scale back CPU frequency. + */ +static void +acpi_tz_cooling_thread(void *arg) +{ + struct acpi_tz_softc *sc; + int error, perf, curr_temp, prev_temp; - ACPI_LOCK; - for (i = 0; i < devcount; i++) - acpi_tz_timeout(device_get_softc(devs[i])); - ACPI_UNLOCK; + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); -#if __FreeBSD_version >= 500000 - mtx_unlock(&Giant); + sc = (struct acpi_tz_softc *)arg; + + prev_temp = sc->tz_temperature; + while (sc->tz_cooling_enabled) { + if (sc->tz_cooling_active) + (void)acpi_tz_get_temperature(sc); + curr_temp = sc->tz_temperature; + if (curr_temp >= sc->tz_zone.psv) + sc->tz_cooling_active = TRUE; + if (sc->tz_cooling_active) { + perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) + + sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv); + perf /= 10; + + if (perf != 0) { +#ifdef __FreeBSD__ + error = acpi_tz_cpufreq_update(sc, perf); + + /* + * If error and not simply a higher priority setting was + * active, disable cooling. + */ + if (error != 0 && error != EPERM) { + device_printf(sc->tz_dev, + "failed to set new freq, disabling passive cooling\n"); + sc->tz_cooling_enabled = FALSE; + } +#endif + } + } + prev_temp = curr_temp; + tsleep(&sc->tz_cooling_proc, 0, "cooling", + hz * sc->tz_zone.tsp / 10); + } + if (sc->tz_cooling_active) { +#ifdef __FreeBSD__ + acpi_tz_cpufreq_restore(sc); #endif + sc->tz_cooling_active = FALSE; } + sc->tz_cooling_proc = NULL; + ACPI_LOCK(thermal); + sc->tz_cooling_proc_running = FALSE; + ACPI_UNLOCK(thermal); + kthread_exit(); +} + +/* + * TODO: We ignore _PSL (list of cooling devices) since cpufreq enumerates + * all CPUs for us. However, it's possible in the future _PSL will + * reference non-CPU devices so we may want to support it then. + */ +static int +acpi_tz_cooling_is_available(struct acpi_tz_softc *sc) +{ + return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 && + sc->tz_zone.tsp != -1 && sc->tz_zone.tsp != 0 && + sc->tz_zone.psv != -1); +} + +static int +acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc) +{ + int error; + char name[16]; + + ACPI_LOCK(thermal); + if (sc->tz_cooling_proc_running) { + ACPI_UNLOCK(thermal); + return (0); + } + sc->tz_cooling_proc_running = TRUE; + ACPI_UNLOCK(thermal); + error = 0; + if (sc->tz_cooling_proc == NULL) { + ksnprintf(name, sizeof(name), "acpi_cooling%d", + device_get_unit(sc->tz_dev)); + error = kthread_create(acpi_tz_cooling_thread, sc, + &sc->tz_cooling_proc, RFHIGHPID, 0, name); + if (error != 0) { + device_printf(sc->tz_dev, "could not create thread - %d", error); + ACPI_LOCK(thermal); + sc->tz_cooling_proc_running = FALSE; + ACPI_UNLOCK(thermal); + } + } + return (error); } diff --git a/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c b/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c index 0327f6301a..03622fb56f 100644 --- a/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c +++ b/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2004 Takanori Watanabe * Copyright (c) 2005 Markus Brueffer * All rights reserved. @@ -23,15 +23,15 @@ * 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_ibm.c,v 1.15 2007/10/25 17:30:18 jhb Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c,v 1.2 2008/10/03 00:47:36 hasso Exp $ + * $FreeBSD: src/sys/dev/acpi_support/acpi_ibm.c,v 1.19 2009/06/05 18:44:36 jkim */ +#include + /* - * Driver for extra ACPI-controlled gadgets found on IBM and Lenovo ThinkPad - * laptops. Inspired by the ibm-acpi and tpb projects which implement these - * features on Linux. + * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. + * Inspired by the ibm-acpi and tpb projects which implement these features + * on Linux. * * acpi-ibm: * tpb: @@ -41,98 +41,101 @@ #include #include #include -#include -#include #include -#include -#include -#include +#include #include #include "acpi.h" #include "accommon.h" -#include "acpivar.h" + #include "acpi_if.h" +#include +#include +#if defined(__FreeBSD__) +#include +#endif +#include +#include #define _COMPONENT ACPI_OEM -ACPI_MODULE_NAME("THINKPAD") +ACPI_MODULE_NAME("IBM") /* Internal methods */ -#define ACPI_THINKPAD_METHOD_EVENTS 1 -#define ACPI_THINKPAD_METHOD_EVENTMASK 2 -#define ACPI_THINKPAD_METHOD_HOTKEY 3 -#define ACPI_THINKPAD_METHOD_BRIGHTNESS 4 -#define ACPI_THINKPAD_METHOD_VOLUME 5 -#define ACPI_THINKPAD_METHOD_MUTE 6 -#define ACPI_THINKPAD_METHOD_THINKLIGHT 7 -#define ACPI_THINKPAD_METHOD_BLUETOOTH 8 -#define ACPI_THINKPAD_METHOD_WLAN 9 -#define ACPI_THINKPAD_METHOD_FANSPEED 10 -#define ACPI_THINKPAD_METHOD_FANLEVEL 11 -#define ACPI_THINKPAD_METHOD_FANSTATUS 12 -#define ACPI_THINKPAD_METHOD_THERMAL 13 +#define ACPI_IBM_METHOD_EVENTS 1 +#define ACPI_IBM_METHOD_EVENTMASK 2 +#define ACPI_IBM_METHOD_HOTKEY 3 +#define ACPI_IBM_METHOD_BRIGHTNESS 4 +#define ACPI_IBM_METHOD_VOLUME 5 +#define ACPI_IBM_METHOD_MUTE 6 +#define ACPI_IBM_METHOD_THINKLIGHT 7 +#define ACPI_IBM_METHOD_BLUETOOTH 8 +#define ACPI_IBM_METHOD_WLAN 9 +#define ACPI_IBM_METHOD_FANSPEED 10 +#define ACPI_IBM_METHOD_FANLEVEL 11 +#define ACPI_IBM_METHOD_FANSTATUS 12 +#define ACPI_IBM_METHOD_THERMAL 13 /* Hotkeys/Buttons */ -#define THINKPAD_RTC_HOTKEY1 0x64 -#define THINKPAD_RTC_MASK_HOME (1 << 0) -#define THINKPAD_RTC_MASK_SEARCH (1 << 1) -#define THINKPAD_RTC_MASK_MAIL (1 << 2) -#define THINKPAD_RTC_MASK_WLAN (1 << 5) -#define THINKPAD_RTC_HOTKEY2 0x65 -#define THINKPAD_RTC_MASK_THINKPAD (1 << 3) -#define THINKPAD_RTC_MASK_ZOOM (1 << 5) -#define THINKPAD_RTC_MASK_VIDEO (1 << 6) -#define THINKPAD_RTC_MASK_HIBERNATE (1 << 7) -#define THINKPAD_RTC_THINKLIGHT 0x66 -#define THINKPAD_RTC_MASK_THINKLIGHT (1 << 4) -#define THINKPAD_RTC_SCREENEXPAND 0x67 -#define THINKPAD_RTC_MASK_SCREENEXPAND (1 << 5) -#define THINKPAD_RTC_BRIGHTNESS 0x6c -#define THINKPAD_RTC_MASK_BRIGHTNESS (1 << 5) -#define THINKPAD_RTC_VOLUME 0x6e -#define THINKPAD_RTC_MASK_VOLUME (1 << 7) +#define IBM_RTC_HOTKEY1 0x64 +#define IBM_RTC_MASK_HOME (1 << 0) +#define IBM_RTC_MASK_SEARCH (1 << 1) +#define IBM_RTC_MASK_MAIL (1 << 2) +#define IBM_RTC_MASK_WLAN (1 << 5) +#define IBM_RTC_HOTKEY2 0x65 +#define IBM_RTC_MASK_THINKPAD (1 << 3) +#define IBM_RTC_MASK_ZOOM (1 << 5) +#define IBM_RTC_MASK_VIDEO (1 << 6) +#define IBM_RTC_MASK_HIBERNATE (1 << 7) +#define IBM_RTC_THINKLIGHT 0x66 +#define IBM_RTC_MASK_THINKLIGHT (1 << 4) +#define IBM_RTC_SCREENEXPAND 0x67 +#define IBM_RTC_MASK_SCREENEXPAND (1 << 5) +#define IBM_RTC_BRIGHTNESS 0x6c +#define IBM_RTC_MASK_BRIGHTNESS (1 << 5) +#define IBM_RTC_VOLUME 0x6e +#define IBM_RTC_MASK_VOLUME (1 << 7) /* Embedded Controller registers */ -#define THINKPAD_EC_BRIGHTNESS 0x31 -#define THINKPAD_EC_MASK_BRI 0x7 -#define THINKPAD_EC_VOLUME 0x30 -#define THINKPAD_EC_MASK_VOL 0xf -#define THINKPAD_EC_MASK_MUTE (1 << 6) -#define THINKPAD_EC_FANSTATUS 0x2F -#define THINKPAD_EC_MASK_FANLEVEL 0x3f -#define THINKPAD_EC_MASK_FANDISENGAGED (1 << 6) -#define THINKPAD_EC_MASK_FANSTATUS (1 << 7) -#define THINKPAD_EC_FANSPEED 0x84 +#define IBM_EC_BRIGHTNESS 0x31 +#define IBM_EC_MASK_BRI 0x7 +#define IBM_EC_VOLUME 0x30 +#define IBM_EC_MASK_VOL 0xf +#define IBM_EC_MASK_MUTE (1 << 6) +#define IBM_EC_FANSTATUS 0x2F +#define IBM_EC_MASK_FANLEVEL 0x3f +#define IBM_EC_MASK_FANDISENGAGED (1 << 6) +#define IBM_EC_MASK_FANSTATUS (1 << 7) +#define IBM_EC_FANSPEED 0x84 /* CMOS Commands */ -#define THINKPAD_CMOS_VOLUME_DOWN 0 -#define THINKPAD_CMOS_VOLUME_UP 1 -#define THINKPAD_CMOS_VOLUME_MUTE 2 -#define THINKPAD_CMOS_BRIGHTNESS_UP 4 -#define THINKPAD_CMOS_BRIGHTNESS_DOWN 5 +#define IBM_CMOS_VOLUME_DOWN 0 +#define IBM_CMOS_VOLUME_UP 1 +#define IBM_CMOS_VOLUME_MUTE 2 +#define IBM_CMOS_BRIGHTNESS_UP 4 +#define IBM_CMOS_BRIGHTNESS_DOWN 5 /* ACPI methods */ -#define THINKPAD_NAME_KEYLIGHT "KBLT" -#define THINKPAD_NAME_WLAN_BT_GET "GBDC" -#define THINKPAD_NAME_WLAN_BT_SET "SBDC" -#define THINKPAD_NAME_MASK_BT (1 << 1) -#define THINKPAD_NAME_MASK_WLAN (1 << 2) -#define THINKPAD_NAME_THERMAL_GET "TMP7" -#define THINKPAD_NAME_THERMAL_UPDT "UPDT" - -#define THINKPAD_NAME_EVENTS_STATUS_GET "DHKC" -#define THINKPAD_NAME_EVENTS_MASK_GET "DHKN" -#define THINKPAD_NAME_EVENTS_STATUS_SET "MHKC" -#define THINKPAD_NAME_EVENTS_MASK_SET "MHKM" -#define THINKPAD_NAME_EVENTS_GET "MHKP" -#define THINKPAD_NAME_EVENTS_AVAILMASK "MHKA" - -#define THINKPAD_NUM_SENSORS 9 -#define THINKPAD_TEMP_SENSORS 8 +#define IBM_NAME_KEYLIGHT "KBLT" +#define IBM_NAME_WLAN_BT_GET "GBDC" +#define IBM_NAME_WLAN_BT_SET "SBDC" +#define IBM_NAME_MASK_BT (1 << 1) +#define IBM_NAME_MASK_WLAN (1 << 2) +#define IBM_NAME_THERMAL_GET "TMP7" +#define IBM_NAME_THERMAL_UPDT "UPDT" + +#define IBM_NAME_EVENTS_STATUS_GET "DHKC" +#define IBM_NAME_EVENTS_MASK_GET "DHKN" +#define IBM_NAME_EVENTS_STATUS_SET "MHKC" +#define IBM_NAME_EVENTS_MASK_SET "MHKM" +#define IBM_NAME_EVENTS_GET "MHKP" +#define IBM_NAME_EVENTS_AVAILMASK "MHKA" + +#define IBM_NUM_SENSORS 9 +#define IBM_THERMAL_SENSORS 8 #define ABS(x) (((x) < 0)? -(x) : (x)) -struct acpi_thinkpad_softc { +struct acpi_ibm_softc { device_t dev; ACPI_HANDLE handle; @@ -170,9 +173,9 @@ struct acpi_thinkpad_softc { /* sensors(9) related */ struct ksensordev sensordev; - struct ksensor sensors[THINKPAD_NUM_SENSORS]; + struct ksensor sensors[IBM_NUM_SENSORS]; - struct sysctl_ctx_list sysctl_ctx; + struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; }; @@ -181,70 +184,76 @@ static struct { int method; char *description; int access; -} acpi_thinkpad_sysctls[] = { +} acpi_ibm_sysctls[] = { { .name = "events", - .method = ACPI_THINKPAD_METHOD_EVENTS, + .method = ACPI_IBM_METHOD_EVENTS, .description = "ACPI events enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "eventmask", - .method = ACPI_THINKPAD_METHOD_EVENTMASK, + .method = ACPI_IBM_METHOD_EVENTMASK, .description = "ACPI eventmask", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "hotkey", - .method = ACPI_THINKPAD_METHOD_HOTKEY, + .method = ACPI_IBM_METHOD_HOTKEY, .description = "Key Status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "lcd_brightness", - .method = ACPI_THINKPAD_METHOD_BRIGHTNESS, + .method = ACPI_IBM_METHOD_BRIGHTNESS, .description = "LCD Brightness", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "volume", - .method = ACPI_THINKPAD_METHOD_VOLUME, + .method = ACPI_IBM_METHOD_VOLUME, .description = "Volume", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "mute", - .method = ACPI_THINKPAD_METHOD_MUTE, + .method = ACPI_IBM_METHOD_MUTE, .description = "Mute", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "thinklight", - .method = ACPI_THINKPAD_METHOD_THINKLIGHT, + .method = ACPI_IBM_METHOD_THINKLIGHT, .description = "Thinklight enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "bluetooth", - .method = ACPI_THINKPAD_METHOD_BLUETOOTH, + .method = ACPI_IBM_METHOD_BLUETOOTH, .description = "Bluetooth enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wlan", - .method = ACPI_THINKPAD_METHOD_WLAN, + .method = ACPI_IBM_METHOD_WLAN, .description = "WLAN enable", .access = CTLTYPE_INT | CTLFLAG_RD }, { + .name = "fan_speed", + .method = ACPI_IBM_METHOD_FANSPEED, + .description = "Fan speed", + .access = CTLTYPE_INT | CTLFLAG_RD + }, + { .name = "fan_level", - .method = ACPI_THINKPAD_METHOD_FANLEVEL, + .method = ACPI_IBM_METHOD_FANLEVEL, .description = "Fan level", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "fan", - .method = ACPI_THINKPAD_METHOD_FANSTATUS, + .method = ACPI_IBM_METHOD_FANSTATUS, .description = "Fan enable", .access = CTLTYPE_INT | CTLFLAG_RW }, @@ -252,66 +261,99 @@ static struct { { NULL, 0, NULL, 0 } }; -static struct lock tplock; +ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); + +static int acpi_ibm_probe(device_t dev); +static int acpi_ibm_attach(device_t dev); +static int acpi_ibm_detach(device_t dev); + +#if defined(__FreeBSD__) +static void ibm_led(void *softc, int onoff); +static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); +#endif -static int acpi_thinkpad_probe(device_t dev); -static int acpi_thinkpad_attach(device_t dev); -static int acpi_thinkpad_detach(device_t dev); +static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); +static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); +static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); -static int acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS); -static int acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, - int method); -static int acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, - int method); -static int acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, - int method, int val); +static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); +static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); -static int acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, - int val); -static void acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, - void *context); -static void acpi_thinkpad_refresh(void *); +static void acpi_ibm_refresh(void *); -static device_method_t acpi_thinkpad_methods[] = { +static device_method_t acpi_ibm_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, acpi_thinkpad_probe), - DEVMETHOD(device_attach, acpi_thinkpad_attach), - DEVMETHOD(device_detach, acpi_thinkpad_detach), + DEVMETHOD(device_probe, acpi_ibm_probe), + DEVMETHOD(device_attach, acpi_ibm_attach), + DEVMETHOD(device_detach, acpi_ibm_detach), + {0, 0} }; -static driver_t acpi_thinkpad_driver = { - "acpi_thinkpad", - acpi_thinkpad_methods, - sizeof(struct acpi_thinkpad_softc), +static driver_t acpi_ibm_driver = { + "acpi_ibm", + acpi_ibm_methods, + sizeof(struct acpi_ibm_softc), }; -static devclass_t acpi_thinkpad_devclass; +static devclass_t acpi_ibm_devclass; + +DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, + 0, 0); +MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); +static char *ibm_ids[] = {"IBM0057", "IBM0068", NULL}; + +#if defined(__FreeBSD__) +static void +ibm_led(void *softc, int onoff) +{ + struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (sc->led_busy) + return; + + sc->led_busy = 1; + sc->led_state = onoff; -DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver, - acpi_thinkpad_devclass, 0, 0); -MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1); -static char *thinkpad_ids[] = {"IBM0068", NULL}; + AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); +} + +static void +ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) +{ + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + ACPI_SERIAL_BEGIN(ibm); + acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); + ACPI_SERIAL_END(ibm); + + sc->led_busy = 0; +} +#endif static int -acpi_thinkpad_probe(device_t dev) +acpi_ibm_probe(device_t dev) { - if (acpi_disabled("thinkpad") || - ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL || - device_get_unit(dev) != 0) + if (acpi_disabled("ibm") || + ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || + device_get_unit(dev) != 0) return (ENXIO); - device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras"); + device_set_desc(dev, "IBM ThinkPad ACPI Extras"); + return (0); } static int -acpi_thinkpad_attach(device_t dev) +acpi_ibm_attach(device_t dev) { - struct acpi_thinkpad_softc *sc; - struct acpi_softc *acpi_sc; + struct acpi_ibm_softc *sc; devclass_t ec_devclass; - int i; + struct acpi_softc *acpi_sc; + int i; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); @@ -319,8 +361,6 @@ acpi_thinkpad_attach(device_t dev) sc->dev = dev; sc->handle = acpi_get_handle(dev); - acpi_sc = acpi_device_get_parent_softc(dev); - /* Look for the first embedded controller */ if (!(ec_devclass = devclass_find ("acpi_ec"))) { if (bootverbose) @@ -333,18 +373,19 @@ acpi_thinkpad_attach(device_t dev) return (EINVAL); } sc->ec_handle = acpi_get_handle(sc->ec_dev); - - lockinit(&tplock, "thinkpad", 0, 0); - lockmgr(&tplock, LK_EXCLUSIVE); + acpi_sc = acpi_device_get_parent_softc(dev); + + ACPI_SERIAL_BEGIN(ibm); + /* Get the sysctl tree */ sysctl_ctx_init(&sc->sysctl_ctx); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, - SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, - "thinkpad", CTLFLAG_RD, 0, ""); + SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, + "thinkpad", CTLFLAG_RD, 0, ""); /* Look for event mask and hook up the nodes */ sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, - THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); + IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); if (sc->events_mask_supported) { SYSCTL_ADD_INT(&sc->sysctl_ctx, @@ -354,7 +395,7 @@ acpi_thinkpad_attach(device_t dev) /* The availmask is the bitmask of supported events */ if (ACPI_FAILURE(acpi_GetInteger(sc->handle, - THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) + IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) sc->events_availmask = 0xffffffff; SYSCTL_ADD_INT(&sc->sysctl_ctx, @@ -364,98 +405,98 @@ acpi_thinkpad_attach(device_t dev) } /* Hook up proc nodes */ - for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) { - if (!acpi_thinkpad_sysctl_init(sc, - acpi_thinkpad_sysctls[i].method)) + for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { + if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) continue; SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, - acpi_thinkpad_sysctls[i].name, - acpi_thinkpad_sysctls[i].access, - sc, i, acpi_thinkpad_sysctl, "I", - acpi_thinkpad_sysctls[i].description); + acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, + sc, i, acpi_ibm_sysctl, "I", + acpi_ibm_sysctls[i].description); } - - lockmgr(&tplock, LK_RELEASE); + ACPI_SERIAL_END(ibm); /* Handle notifies */ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, - acpi_thinkpad_notify, dev); + acpi_ibm_notify, dev); - /* Attach sensors(9). */ - if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) { - device_printf(sc->dev, "unable to register update task\n"); - return 1; - } + /* Attach sensors(9). */ + if (sensor_task_register(sc, acpi_ibm_refresh, 5)) { + device_printf(sc->dev, "unable to register update task\n"); + return 1; + } + strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev), + sizeof(sc->sensordev.xname)); - strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev), - sizeof(sc->sensordev.xname)); + for (i = 0; i < IBM_THERMAL_SENSORS; i++) { + sc->sensors[i].type = SENSOR_TEMP; + sensor_attach(&sc->sensordev, &sc->sensors[i]); + } + + sc->sensors[i].type = SENSOR_FANRPM; + sensor_attach(&sc->sensordev, &sc->sensors[i]); - for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) { - sc->sensors[i].type = SENSOR_TEMP; - sensor_attach(&sc->sensordev, &sc->sensors[i]); - } - - sc->sensors[i].type = SENSOR_FANRPM; - sensor_attach(&sc->sensordev, &sc->sensors[i]); + sensordev_install(&sc->sensordev); - sensordev_install(&sc->sensordev); +#if defined(__FreeBSD__) + /* Hook up light to led(4) */ + if (sc->light_set_supported) + sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); +#endif return (0); } static int -acpi_thinkpad_detach(device_t dev) +acpi_ibm_detach(device_t dev) { int i; - ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); - struct acpi_thinkpad_softc *sc = device_get_softc(dev); + struct acpi_ibm_softc *sc = device_get_softc(dev); /* Disable events and restore eventmask */ - lockmgr(&tplock, LK_EXCLUSIVE); - acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0); - acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK, - sc->events_initialmask); - lockmgr(&tplock, LK_RELEASE); + ACPI_SERIAL_BEGIN(ibm); + acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); + acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); + ACPI_SERIAL_END(ibm); - AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, - acpi_thinkpad_notify); + AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); - if (sc->sysctl_tree != NULL) - sysctl_ctx_free(&sc->sysctl_ctx); + sensordev_deinstall(&sc->sensordev); + for (i = 0; i < IBM_THERMAL_SENSORS; i++) + sensor_detach(&sc->sensordev, &sc->sensors[i]); + sensor_task_unregister(sc); - sensordev_deinstall(&sc->sensordev); - for (i = 0; i < THINKPAD_NUM_SENSORS; i++) - sensor_detach(&sc->sensordev, &sc->sensors[i]); - sensor_task_unregister(sc); +#if defined(__FreeBSD__) + if (sc->led_dev != NULL) + led_destroy(sc->led_dev); +#endif return (0); } static int -acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val) +acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) { - int i; ACPI_OBJECT arg[2]; ACPI_OBJECT_LIST args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - KKASSERT(lockstatus(&tplock, curthread) != 0); + ACPI_SERIAL_ASSERT(ibm); args.Count = 2; args.Pointer = arg; arg[0].Type = ACPI_TYPE_INTEGER; arg[1].Type = ACPI_TYPE_INTEGER; - for (i = 0; i < 32; ++i) { + for (int i = 0; i < 32; ++i) { arg[0].Integer.Value = i+1; arg[1].Integer.Value = (((1 << i) & val) != 0); status = AcpiEvaluateObject(sc->handle, - THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL); + IBM_NAME_EVENTS_MASK_SET, &args, NULL); if (ACPI_FAILURE(status)) return (status); @@ -465,9 +506,9 @@ acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val) } static int -acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS) +acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) { - struct acpi_thinkpad_softc *sc; + struct acpi_ibm_softc *sc; int arg; int error = 0; int function; @@ -475,12 +516,12 @@ acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS) ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1; + sc = (struct acpi_ibm_softc *)oidp->oid_arg1; function = oidp->oid_arg2; - method = acpi_thinkpad_sysctls[function].method; + method = acpi_ibm_sysctls[function].method; - lockmgr(&tplock, LK_EXCLUSIVE); - arg = acpi_thinkpad_sysctl_get(sc, method); + ACPI_SERIAL_BEGIN(ibm); + arg = acpi_ibm_sysctl_get(sc, method); error = sysctl_handle_int(oidp, &arg, 0, req); /* Sanity check */ @@ -488,35 +529,33 @@ acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS) goto out; /* Update */ - error = acpi_thinkpad_sysctl_set(sc, method, arg); + error = acpi_ibm_sysctl_set(sc, method, arg); out: - lockmgr(&tplock, LK_RELEASE); + ACPI_SERIAL_END(ibm); return (error); } static int -acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method) +acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) { ACPI_INTEGER val_ec; int val = 0, key; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - KKASSERT(lockstatus(&tplock, curthread) != 0); + ACPI_SERIAL_ASSERT(ibm); switch (method) { - case ACPI_THINKPAD_METHOD_EVENTS: - acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET, - &val); + case ACPI_IBM_METHOD_EVENTS: + acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); break; - case ACPI_THINKPAD_METHOD_EVENTMASK: + case ACPI_IBM_METHOD_EVENTMASK: if (sc->events_mask_supported) - acpi_GetInteger(sc->handle, - THINKPAD_NAME_EVENTS_MASK_GET, &val); + acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); break; - case ACPI_THINKPAD_METHOD_HOTKEY: + case ACPI_IBM_METHOD_HOTKEY: /* * Construct the hotkey as a bitmask as illustrated below. * Note that whenever a key was pressed, the respecting bit @@ -538,75 +577,69 @@ acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method) * | +--------------------- Brightness * +------------------------ Volume/Mute */ - key = rtcin(THINKPAD_RTC_HOTKEY1); - val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH | - THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key; - key = rtcin(THINKPAD_RTC_HOTKEY2); - val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO | - THINKPAD_RTC_MASK_HIBERNATE) & key; - val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1; - key = rtcin(THINKPAD_RTC_THINKLIGHT); - val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4; - key = rtcin(THINKPAD_RTC_SCREENEXPAND); - val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4; - key = rtcin(THINKPAD_RTC_BRIGHTNESS); - val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5; - key = rtcin(THINKPAD_RTC_VOLUME); - val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4; + key = rtcin(IBM_RTC_HOTKEY1); + val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; + key = rtcin(IBM_RTC_HOTKEY2); + val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; + val |= (IBM_RTC_MASK_ZOOM & key) >> 1; + key = rtcin(IBM_RTC_THINKLIGHT); + val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; + key = rtcin(IBM_RTC_SCREENEXPAND); + val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; + key = rtcin(IBM_RTC_BRIGHTNESS); + val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; + key = rtcin(IBM_RTC_VOLUME); + val |= (IBM_RTC_MASK_VOLUME & key) << 4; break; - case ACPI_THINKPAD_METHOD_BRIGHTNESS: - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1); - val = val_ec & THINKPAD_EC_MASK_BRI; + case ACPI_IBM_METHOD_BRIGHTNESS: + ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); + val = val_ec & IBM_EC_MASK_BRI; break; - case ACPI_THINKPAD_METHOD_VOLUME: - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1); - val = val_ec & THINKPAD_EC_MASK_VOL; + case ACPI_IBM_METHOD_VOLUME: + ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); + val = val_ec & IBM_EC_MASK_VOL; break; - case ACPI_THINKPAD_METHOD_MUTE: - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1); - val = ((val_ec & THINKPAD_EC_MASK_MUTE) == - THINKPAD_EC_MASK_MUTE); + case ACPI_IBM_METHOD_MUTE: + ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); + val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); break; - case ACPI_THINKPAD_METHOD_THINKLIGHT: + case ACPI_IBM_METHOD_THINKLIGHT: if (sc->light_get_supported) - acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT, - &val); + acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); else val = sc->light_val; break; - case ACPI_THINKPAD_METHOD_BLUETOOTH: - acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val); + case ACPI_IBM_METHOD_BLUETOOTH: + acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); sc->wlan_bt_flags = val; - val = ((val & THINKPAD_NAME_MASK_BT) != 0); + val = ((val & IBM_NAME_MASK_BT) != 0); break; - case ACPI_THINKPAD_METHOD_WLAN: - acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val); + case ACPI_IBM_METHOD_WLAN: + acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); sc->wlan_bt_flags = val; - val = ((val & THINKPAD_NAME_MASK_WLAN) != 0); + val = ((val & IBM_NAME_MASK_WLAN) != 0); break; - case ACPI_THINKPAD_METHOD_FANSPEED: + case ACPI_IBM_METHOD_FANSPEED: if (sc->fan_handle) { - if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, - NULL, &val))) + if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) val = -1; } else { - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, - &val_ec, 2); + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); val = val_ec; } break; - case ACPI_THINKPAD_METHOD_FANLEVEL: + case ACPI_IBM_METHOD_FANLEVEL: /* - * The THINKPAD_EC_FANSTATUS register works as follows: + * The IBM_EC_FANSTATUS register works as follows: * Bit 0-5 indicate the level at which the fan operates. Only * values between 0 and 7 have an effect. Everything * above 7 is treated the same as level 7 @@ -615,18 +648,15 @@ acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method) * manual (0) or automatic (1) */ if (!sc->fan_handle) { - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, - &val_ec, 1); - val = val_ec & THINKPAD_EC_MASK_FANLEVEL; + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); + val = val_ec & IBM_EC_MASK_FANLEVEL; } break; - case ACPI_THINKPAD_METHOD_FANSTATUS: + case ACPI_IBM_METHOD_FANSTATUS: if (!sc->fan_handle) { - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, - &val_ec, 1); - val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) == - THINKPAD_EC_MASK_FANSTATUS; + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); + val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; } else val = -1; @@ -637,125 +667,110 @@ acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method) } static int -acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg) +acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) { - int val, step, i; + int val, step; ACPI_INTEGER val_ec; ACPI_OBJECT Arg; ACPI_OBJECT_LIST Args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - KKASSERT(lockstatus(&tplock, curthread) != 0); + ACPI_SERIAL_ASSERT(ibm); switch (method) { - case ACPI_THINKPAD_METHOD_EVENTS: + case ACPI_IBM_METHOD_EVENTS: if (arg < 0 || arg > 1) return (EINVAL); - status = acpi_SetInteger(sc->handle, - THINKPAD_NAME_EVENTS_STATUS_SET, arg); + status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); if (ACPI_FAILURE(status)) return (status); if (sc->events_mask_supported) - return acpi_thinkpad_eventmask_set(sc, - sc->events_availmask); + return acpi_ibm_eventmask_set(sc, sc->events_availmask); break; - case ACPI_THINKPAD_METHOD_EVENTMASK: + case ACPI_IBM_METHOD_EVENTMASK: if (sc->events_mask_supported) - return acpi_thinkpad_eventmask_set(sc, arg); + return acpi_ibm_eventmask_set(sc, arg); break; - case ACPI_THINKPAD_METHOD_BRIGHTNESS: + case ACPI_IBM_METHOD_BRIGHTNESS: if (arg < 0 || arg > 7) return (EINVAL); if (sc->cmos_handle) { /* Read the current brightness */ - status = ACPI_EC_READ(sc->ec_dev, - THINKPAD_EC_BRIGHTNESS, &val_ec, 1); + status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); - val = val_ec & THINKPAD_EC_MASK_BRI; + val = val_ec & IBM_EC_MASK_BRI; Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; - Arg.Integer.Value = (arg > val) ? - THINKPAD_CMOS_BRIGHTNESS_UP : - THINKPAD_CMOS_BRIGHTNESS_DOWN; + Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; step = (arg > val) ? 1 : -1; - for (i = val; i != arg; i += step) { - status = AcpiEvaluateObject(sc->cmos_handle, - NULL, &Args, NULL); + for (int i = val; i != arg; i += step) { + status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } } - return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, - arg, 1); + return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); break; - case ACPI_THINKPAD_METHOD_VOLUME: + case ACPI_IBM_METHOD_VOLUME: if (arg < 0 || arg > 14) return (EINVAL); - status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, - &val_ec, 1); + status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); if (sc->cmos_handle) { - val = val_ec & THINKPAD_EC_MASK_VOL; + val = val_ec & IBM_EC_MASK_VOL; Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; - Arg.Integer.Value = (arg > val) ? - THINKPAD_CMOS_VOLUME_UP : THINKPAD_CMOS_VOLUME_DOWN; + Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; step = (arg > val) ? 1 : -1; - for (i = val; i != arg; i += step) { - status = AcpiEvaluateObject(sc->cmos_handle, - NULL, &Args, NULL); + for (int i = val; i != arg; i += step) { + status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } } - return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, arg + - (val_ec & (~THINKPAD_EC_MASK_VOL)), 1); + return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); break; - case ACPI_THINKPAD_METHOD_MUTE: + case ACPI_IBM_METHOD_MUTE: if (arg < 0 || arg > 1) return (EINVAL); - status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, - &val_ec, 1); + status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); if (sc->cmos_handle) { - val = val_ec & THINKPAD_EC_MASK_VOL; + val = val_ec & IBM_EC_MASK_VOL; Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; - Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE; + Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; - status = AcpiEvaluateObject(sc->cmos_handle, NULL, - &Args, NULL); + status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } - return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, (arg==1) ? - val_ec | THINKPAD_EC_MASK_MUTE : - val_ec & (~THINKPAD_EC_MASK_MUTE), 1); + return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1); break; - case ACPI_THINKPAD_METHOD_THINKLIGHT: + case ACPI_IBM_METHOD_THINKLIGHT: if (arg < 0 || arg > 1) return (EINVAL); @@ -763,55 +778,46 @@ acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg) Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; - Arg.Integer.Value = arg ? - sc->light_cmd_on : sc->light_cmd_off; + Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; - status = AcpiEvaluateObject(sc->light_handle, NULL, - &Args, NULL); + status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); if (ACPI_SUCCESS(status)) sc->light_val = arg; return (status); } break; - case ACPI_THINKPAD_METHOD_BLUETOOTH: + case ACPI_IBM_METHOD_BLUETOOTH: if (arg < 0 || arg > 1) return (EINVAL); - val = (arg == 1) ? sc->wlan_bt_flags | - THINKPAD_NAME_MASK_BT : - sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT); - return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET, - val); + val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); + return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); break; - case ACPI_THINKPAD_METHOD_FANLEVEL: + case ACPI_IBM_METHOD_FANLEVEL: if (arg < 0 || arg > 7) return (EINVAL); if (!sc->fan_handle) { /* Read the current fanstatus */ - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, - &val_ec, 1); - val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL); + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); + val = val_ec & (~IBM_EC_MASK_FANLEVEL); - return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS, - val | arg, 1); + return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); } break; - case ACPI_THINKPAD_METHOD_FANSTATUS: + case ACPI_IBM_METHOD_FANSTATUS: if (arg < 0 || arg > 1) return (EINVAL); if (!sc->fan_handle) { /* Read the current fanstatus */ - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, - &val_ec, 1); + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); - return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS, - (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) : - (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1); + return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, + (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); } break; } @@ -820,46 +826,42 @@ acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg) } static int -acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) +acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) { int dummy; ACPI_OBJECT_TYPE cmos_t; ACPI_HANDLE ledb_handle; switch (method) { - case ACPI_THINKPAD_METHOD_EVENTS: + case ACPI_IBM_METHOD_EVENTS: /* Events are disabled by default */ return (TRUE); - case ACPI_THINKPAD_METHOD_EVENTMASK: + case ACPI_IBM_METHOD_EVENTMASK: return (sc->events_mask_supported); - case ACPI_THINKPAD_METHOD_HOTKEY: - case ACPI_THINKPAD_METHOD_BRIGHTNESS: - case ACPI_THINKPAD_METHOD_VOLUME: - case ACPI_THINKPAD_METHOD_MUTE: - /* EC is required here, which was already checked before */ + case ACPI_IBM_METHOD_HOTKEY: + case ACPI_IBM_METHOD_BRIGHTNESS: + case ACPI_IBM_METHOD_VOLUME: + case ACPI_IBM_METHOD_MUTE: + /* EC is required here, which was aready checked before */ return (TRUE); - case ACPI_THINKPAD_METHOD_THINKLIGHT: + case ACPI_IBM_METHOD_THINKLIGHT: sc->cmos_handle = NULL; sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( - sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val)); - - if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", - &sc->light_handle)) || - ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", - &sc->light_handle)) || - ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", - &sc->light_handle))) && - ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && - cmos_t == ACPI_TYPE_METHOD) { + sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); + + if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || + ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || + ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && + ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && + cmos_t == ACPI_TYPE_METHOD) { sc->light_cmd_on = 0x0c; sc->light_cmd_off = 0x0d; sc->cmos_handle = sc->light_handle; } - else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", - &sc->light_handle))) { + else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { sc->light_cmd_on = 1; sc->light_cmd_off = 0; } @@ -867,8 +869,7 @@ acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) sc->light_handle = NULL; sc->light_set_supported = (sc->light_handle && - ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", - &ledb_handle))); + ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); if (sc->light_get_supported) return (TRUE); @@ -880,26 +881,24 @@ acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) return (FALSE); - case ACPI_THINKPAD_METHOD_BLUETOOTH: - case ACPI_THINKPAD_METHOD_WLAN: - if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, - THINKPAD_NAME_WLAN_BT_GET, &dummy))) + case ACPI_IBM_METHOD_BLUETOOTH: + case ACPI_IBM_METHOD_WLAN: + if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) return (TRUE); return (FALSE); - case ACPI_THINKPAD_METHOD_FANSPEED: + case ACPI_IBM_METHOD_FANSPEED: /* * Some models report the fan speed in levels from 0-7 * Newer models report it contiguously */ - sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", - &sc->fan_handle)) || - ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", - &sc->fan_handle))); + sc->fan_levels = + (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || + ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); return (TRUE); - case ACPI_THINKPAD_METHOD_FANLEVEL: - case ACPI_THINKPAD_METHOD_FANSTATUS: + case ACPI_IBM_METHOD_FANLEVEL: + case ACPI_IBM_METHOD_FANSTATUS: /* * Fan status is only supported on those models, * which report fan RPM contiguously, not in levels @@ -908,12 +907,9 @@ acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) return (FALSE); return (TRUE); - case ACPI_THINKPAD_METHOD_THERMAL: - if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, - THINKPAD_NAME_THERMAL_GET, &dummy))) { - sc->thermal_updt_supported = - ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, - THINKPAD_NAME_THERMAL_UPDT, &dummy)); + case ACPI_IBM_METHOD_THERMAL: + if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { + sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); return (TRUE); } return (FALSE); @@ -921,12 +917,61 @@ acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) return (FALSE); } +void +acpi_ibm_refresh(void *arg) +{ + struct acpi_ibm_softc *sc; + char temp_cmd[] = "TMP0"; + int i, data, temp[8]; + ACPI_INTEGER speed; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + sc = (struct acpi_ibm_softc *)arg; + + ACPI_SERIAL_BEGIN(ibm); + + for (i = 0; i < IBM_THERMAL_SENSORS; ++i) { + temp_cmd[3] = '0' + i; + + /* + * The TMPx methods seem to return +/- 128 or 0 + * when the respecting sensor is not available + */ + if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, + &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) { + sc->sensors[i].flags |= SENSOR_FINVALID; + data = 0; + } + else if (sc->thermal_updt_supported) { + /* Temperature is reported in tenth of Kelvin */ + sc->sensors[i].value = data * 100000; + } + sc->sensors[i].value = data * 1000000 + 273150000; + } + sc->sensors[i].flags &= ~SENSOR_FINVALID; + if (sc->fan_handle) { + if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, + NULL, &data))) + sc->sensors[i].flags |= SENSOR_FINVALID; + data = -1; + } + else { + ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &speed, 2); + data = speed; + } + + sc->sensors[i].value = data; + + ACPI_SERIAL_END(ibm); +} + static void -acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context) +acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) { int event, arg, type; device_t dev = context; - struct acpi_thinkpad_softc *sc = device_get_softc(dev); + struct acpi_ibm_softc *sc = device_get_softc(dev); ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); @@ -934,12 +979,12 @@ acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context) device_printf(dev, "Unknown notify\n"); for (;;) { - acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET, - &event); + acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); if (event == 0) break; + type = (event >> 12) & 0xf; arg = event & 0xfff; switch (type) { @@ -950,53 +995,10 @@ acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context) } /* Notify devd(8) */ - acpi_UserNotify("THINKPAD", h, (arg & 0xff)); + acpi_UserNotify("IBM", h, (arg & 0xff)); break; default: break; } } } - -static void -acpi_thinkpad_refresh(void *arg) -{ - struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg; - char temp_cmd[] = "TMP0"; - int data, i; - ACPI_INTEGER speed; - - for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) { - temp_cmd[3] = '0' + i; - - /* - * The TMPx methods seem to return +/- 128 or 0 - * when the respecting sensor is not available - */ - sc->sensors[i].flags &= ~SENSOR_FINVALID; - if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, - &data)) || ABS(data) == 128 || data == 0) { - sc->sensors[i].flags |= SENSOR_FINVALID; - data = 0; - } - else if (sc->thermal_updt_supported) { - /* Temperature is reported in tenth of Kelvin */ - sc->sensors[i].value = data * 100000; - } - sc->sensors[i].value = data * 1000000 + 273150000; - } - - sc->sensors[i].flags &= ~SENSOR_FINVALID; - if (sc->fan_handle) { - if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, - NULL, &data))) - sc->sensors[i].flags |= SENSOR_FINVALID; - data = -1; - } - else { - ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2); - data = speed; - } - - sc->sensors[i].value = data; -} diff --git a/sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c b/sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c index 25df543adb..699273f93a 100644 --- a/sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c +++ b/sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c @@ -23,20 +23,23 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/acpi_support/acpi_toshiba.c,v 1.10 2004/06/15 02:17:23 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_toshiba/acpi_toshiba.c,v 1.1 2008/08/28 06:16:32 hasso Exp $ + * $FreeBSD: src/sys/dev/acpi_support/acpi_toshiba.c,v 1.18 2009/06/05 18:44:36 jkim */ +#include + #include "opt_acpi.h" #include -#include #include #include #include -#include #include "acpi.h" -#include "acpivar.h" + +#include + +#define _COMPONENT ACPI_OEM +ACPI_MODULE_NAME("Toshiba") /* * Toshiba HCI interface definitions @@ -148,6 +151,8 @@ static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, static int acpi_toshiba_video_probe(device_t dev); static int acpi_toshiba_video_attach(device_t dev); +ACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras"); + /* Table of sysctl names and HCI functions to call. */ static struct { char *name; @@ -209,23 +214,20 @@ TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys); * Dynabook Satellite 2455 * Dynabook SS 3500 * TOS6207 Dynabook SS2110 Series + * TOS6208 SPA40 */ static int acpi_toshiba_probe(device_t dev) { - ACPI_HANDLE h; - int ret = ENXIO; - - h = acpi_get_handle(dev); - if (!acpi_disabled("toshiba") && - acpi_get_type(dev) == ACPI_TYPE_DEVICE && - device_get_unit(dev) == 0 && - (acpi_MatchHid(h, "TOS6200") || acpi_MatchHid(h, "TOS6207"))) { - device_set_desc(dev, "Toshiba HCI Extras"); - ret = 0; - } + static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL }; - return (ret); + if (acpi_disabled("toshiba") || + ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids) == NULL || + device_get_unit(dev) != 0) + return (ENXIO); + + device_set_desc(dev, "Toshiba HCI Extras"); + return (0); } static int @@ -297,18 +299,21 @@ acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS) handler = sysctl_table[function].handler; /* Get the current value from the appropriate function. */ + ACPI_SERIAL_BEGIN(toshiba); error = handler(sc->handle, HCI_GET, &arg); if (error != 0) - return (error); + goto out; /* Send the current value to the user and return if no new value. */ error = sysctl_handle_int(oidp, &arg, 0, req); if (error != 0 || req->newptr == NULL) - return (error); + goto out; /* Set the new value via the appropriate function. */ error = handler(sc->handle, HCI_SET, &arg); +out: + ACPI_SERIAL_END(toshiba); return (error); } @@ -317,6 +322,7 @@ hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state) { int ret; + ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*state < 0 || *state > 1) return (EINVAL); @@ -334,6 +340,7 @@ hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output) int ret; ACPI_STATUS status; + ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*video_output < 1 || *video_output > 7) return (EINVAL); @@ -359,6 +366,7 @@ hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness) { int ret; + ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*brightness < 0 || *brightness > HCI_LCD_BRIGHTNESS_MAX) return (EINVAL); @@ -373,6 +381,8 @@ hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness) static int hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight) { + + ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*backlight < 0 || *backlight > 1) return (EINVAL); @@ -385,6 +395,7 @@ hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed) { int ret; + ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*speed < 0 || *speed > HCI_CPU_SPEED_MAX) return (EINVAL); @@ -405,6 +416,7 @@ hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg) ACPI_OBJECT *res; int status, i, ret; + ACPI_SERIAL_ASSERT(toshiba); status = ENXIO; for (i = 0; i < HCI_WORDS; i++) { @@ -438,6 +450,9 @@ hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg) /* * Sometimes system events are disabled without us requesting * it. This workaround attempts to re-enable them. + * + * XXX This call probably shouldn't be recursive. Queueing + * a task via AcpiOsQueueForExecution() might be better. */ i = 1; hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i); @@ -459,6 +474,7 @@ hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key) { UINT32 arg; + ACPI_SERIAL_ASSERT(toshiba); switch (key) { case FN_F6_RELEASE: /* Decrease LCD brightness. */ @@ -506,10 +522,12 @@ acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context) sc = (struct acpi_toshiba_softc *)context; if (notify == 0x80) { + ACPI_SERIAL_BEGIN(toshiba); while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) { hci_key_action(sc, h, key); acpi_UserNotify("TOSHIBA", h, (uint8_t)key); } + ACPI_SERIAL_END(toshiba); } else device_printf(sc->dev, "unknown notify: 0x%x\n", notify); } @@ -524,17 +542,16 @@ acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context) static int acpi_toshiba_video_probe(device_t dev) { - int ret = ENXIO; - - if (!acpi_disabled("toshiba") && - acpi_get_type(dev) == ACPI_TYPE_DEVICE && - acpi_MatchHid(acpi_get_handle(dev), "TOS6201")) { - device_quiet(dev); - device_set_desc(dev, "Toshiba Video"); - ret = 0; - } + static char *vid_ids[] = { "TOS6201", NULL }; - return (ret); + if (acpi_disabled("toshiba") || + ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids) == NULL || + device_get_unit(dev) != 0) + return (ENXIO); + + device_quiet(dev); + device_set_desc(dev, "Toshiba Video"); + return (0); } static int diff --git a/sys/dev/acpica5/acpiio.h b/sys/dev/acpica5/acpiio.h index a213c6a7a9..c665ddc75f 100644 --- a/sys/dev/acpica5/acpiio.h +++ b/sys/dev/acpica5/acpiio.h @@ -24,31 +24,35 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/acpica/acpiio.h,v 1.9 2003/09/13 20:13:01 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpiio.h,v 1.2 2008/09/29 06:59:45 hasso Exp $ + * $FreeBSD: src/sys/dev/acpica/acpiio.h,v 1.17.8.1 2009/04/15 03:14:26 kensmith Exp $ */ -#include +#ifndef _ACPIIO_H_ +#define _ACPIIO_H_ /* * Core ACPI subsystem ioctls */ -#define ACPIIO_ENABLE _IO('P', 1) -#define ACPIIO_DISABLE _IO('P', 2) -#define ACPIIO_SETSLPSTATE _IOW('P', 3, int) +#define ACPIIO_SETSLPSTATE _IOW('P', 3, int) /* DEPRECATED */ + +/* Request S1-5 sleep state. User is notified and then sleep proceeds. */ +#define ACPIIO_REQSLPSTATE _IOW('P', 4, int) + +/* Allow suspend to continue (0) or abort it (errno). */ +#define ACPIIO_ACKSLPSTATE _IOW('P', 5, int) struct acpi_battinfo { - int cap; /* percent */ - int min; /* remaining time (in minutes) */ - int state; /* battery state */ - int rate; /* emptying rate */ + int cap; /* percent */ + int min; /* remaining time (in minutes) */ + int state; /* battery state */ + int rate; /* emptying rate */ }; #define ACPI_CMBAT_MAXSTRLEN 32 struct acpi_bif { uint32_t units; /* Units (mW or mA). */ -#define ACPI_BIF_UNITS_MW 0 /* Capacity in mWh, rate in mW. */ -#define ACPI_BIF_UNITS_MA 1 /* Capacity in mAh, rate in mA. */ +#define ACPI_BIF_UNITS_MW 0 /* Capacity in mWh, rate in mW. */ +#define ACPI_BIF_UNITS_MA 1 /* Capacity in mAh, rate in mA. */ uint32_t dcap; /* Design Capacity */ uint32_t lfcap; /* Last Full capacity */ uint32_t btech; /* Battery Technology */ @@ -77,7 +81,7 @@ struct acpi_bst { #define ACPI_BATT_STAT_MAX 0x0007 union acpi_battery_ioctl_arg { - int unit; /* Device unit or ACPI_BATTERY_ALL_UNITS. */ + int unit; /* Device unit or ACPI_BATTERY_ALL_UNITS. */ struct acpi_battinfo battinfo; @@ -85,18 +89,18 @@ union acpi_battery_ioctl_arg { struct acpi_bst bst; }; -#define ACPI_BATTERY_ALL_UNITS (-1) -#define ACPI_BATT_UNKNOWN 0xffffffff /* _BST or _BIF value unknown. */ +#define ACPI_BATTERY_ALL_UNITS (-1) +#define ACPI_BATT_UNKNOWN 0xffffffff /* _BST or _BIF value unknown. */ /* Common battery ioctls */ -#define ACPIIO_BATT_GET_UNITS _IOR('B', 0x01, int) +#define ACPIIO_BATT_GET_UNITS _IOR('B', 0x01, int) #define ACPIIO_BATT_GET_BATTINFO _IOWR('B', 0x03, union acpi_battery_ioctl_arg) -#define ACPIIO_BATT_GET_BIF _IOWR('B', 0x10, union acpi_battery_ioctl_arg) -#define ACPIIO_BATT_GET_BST _IOWR('B', 0x11, union acpi_battery_ioctl_arg) +#define ACPIIO_BATT_GET_BIF _IOWR('B', 0x10, union acpi_battery_ioctl_arg) +#define ACPIIO_BATT_GET_BST _IOWR('B', 0x11, union acpi_battery_ioctl_arg) /* Control Method battery ioctls (deprecated) */ -#define ACPIIO_CMBAT_GET_BIF ACPIIO_BATT_GET_BIF -#define ACPIIO_CMBAT_GET_BST ACPIIO_BATT_GET_BST +#define ACPIIO_CMBAT_GET_BIF ACPIIO_BATT_GET_BIF +#define ACPIIO_CMBAT_GET_BST ACPIIO_BATT_GET_BST /* Get AC adapter status. */ #define ACPIIO_ACAD_GET_STATUS _IOR('A', 1, int) @@ -106,3 +110,5 @@ typedef int (*acpi_ioctl_fn)(u_long cmd, caddr_t addr, void *arg); extern int acpi_register_ioctl(u_long cmd, acpi_ioctl_fn fn, void *arg); extern void acpi_deregister_ioctl(u_long cmd, acpi_ioctl_fn fn); #endif + +#endif /* !_ACPIIO_H_ */ diff --git a/sys/dev/acpica5/acpivar.h b/sys/dev/acpica5/acpivar.h index ad66afc1de..5957d19f56 100644 --- a/sys/dev/acpica5/acpivar.h +++ b/sys/dev/acpica5/acpivar.h @@ -25,28 +25,38 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/acpica/acpivar.h,v 1.71 2004/06/13 22:52:30 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.16 2008/09/29 06:59:45 hasso Exp $ + * $FreeBSD: src/sys/dev/acpica/acpivar.h,v 1.108.8.1 2009/04/15 03:14:26 kensmith Exp $ */ +#ifndef _ACPIVAR_H_ +#define _ACPIVAR_H_ + +#ifdef _KERNEL + + #include "acpi_if.h" #include "bus_if.h" #include #include -#include -#include -#include #if __FreeBSD_version >= 500000 #include #include #endif #include +#include +#include +#include +#include "acmacros.h" +#include "acconfig.h" +#include "aclocal.h" +#include "acobject.h" +#include "acstruct.h" +struct apm_clone_data; struct acpi_softc { device_t acpi_dev; cdev_t acpi_dev_t; - struct callout acpi_sleep_timer; struct resource *acpi_irq; int acpi_irq_rid; void *acpi_irq_handle; @@ -66,72 +76,100 @@ struct acpi_softc { int acpi_sleep_delay; int acpi_s4bios; - int acpi_disable_on_poweroff; + int acpi_do_disable; int acpi_verbose; + int acpi_handle_reboot; bus_dma_tag_t acpi_waketag; bus_dmamap_t acpi_wakemap; vm_offset_t acpi_wakeaddr; vm_paddr_t acpi_wakephys; - struct sysctl_ctx_list acpi_battery_sysctl_ctx; - struct sysctl_oid *acpi_battery_sysctl_tree; + int acpi_next_sstate; /* Next suspend Sx state. */ + struct apm_clone_data *acpi_clone; /* Pseudo-dev for devd(8). */ + STAILQ_HEAD(,apm_clone_data) apm_cdevs; /* All apm/apmctl/acpi cdevs. */ + struct callout susp_force_to; /* Force suspend if no acks. */ }; struct acpi_device { /* ACPI ivars */ ACPI_HANDLE ad_handle; - int ad_magic; + uintptr_t ad_magic; void *ad_private; + int ad_flags; /* Resources */ struct resource_list ad_rl; }; +/* Track device (/dev/{apm,apmctl} and /dev/acpi) notification status. */ +struct apm_clone_data { + STAILQ_ENTRY(apm_clone_data) entries; + struct cdev *cdev; + int flags; +#define ACPI_EVF_NONE 0 /* /dev/apm semantics */ +#define ACPI_EVF_DEVD 1 /* /dev/acpi is handled via devd(8) */ +#define ACPI_EVF_WRITE 2 /* Device instance is opened writable. */ + int notify_status; +#define APM_EV_NONE 0 /* Device not yet aware of pending sleep. */ +#define APM_EV_NOTIFIED 1 /* Device saw next sleep state. */ +#define APM_EV_ACKED 2 /* Device agreed sleep can occur. */ + struct acpi_softc *acpi_sc; +}; + +#define ACPI_PRW_MAX_POWERRES 8 + struct acpi_prw_data { ACPI_HANDLE gpe_handle; int gpe_bit; int lowest_wake; - void *power_res; + ACPI_OBJECT power_res[ACPI_PRW_MAX_POWERRES]; + int power_res_count; }; /* Flags for each device defined in the AML namespace. */ -#define ACPI_FLAG_WAKE_CAPABLE 0x1 -#define ACPI_FLAG_WAKE_ENABLED 0x2 +#define ACPI_FLAG_WAKE_ENABLED 0x1 + +/* Macros for extracting parts of a PCI address from an _ADR value. */ +#define ACPI_ADR_PCI_SLOT(adr) (((adr) & 0xffff0000) >> 16) +#define ACPI_ADR_PCI_FUNC(adr) ((adr) & 0xffff) -#if defined(__DragonFly__) -/* - * In DragonFly, ACPI is protected by critical sections. - */ -# define ACPI_LOCK crit_enter() -# define ACPI_UNLOCK crit_exit() -# define ACPI_ASSERTLOCK -# define ACPI_MSLEEP(a, b, c, d, e) tsleep(a, c, d, e) -# define ACPI_LOCK_DECL -# define kthread_create(a, b, c, d, e, f) kthread_create(a, b, c, f) -#define ACPI_SERIAL_BEGIN(sys) lwkt_serialize_enter(&acpi_sys##_serializer) -#define ACPI_SERIAL_END(sys) lwkt_serialize_exit(&acpi_sys##_serializer) -#define ACPI_SERIAL_ASSERT(sys) ASSERT_SERIALIZED(&acpi_sys##_serializer) -#define ACPI_SERIAL_DECL(sys, name) static struct lwkt_serialize acpi_sys##_serializer; -#define ACPI_SERIAL_INIT(sys) lwkt_serialize_init(&acpi_sys##_serializer) -#elif __FreeBSD_version < 500000 /* - * In 4.x, ACPI is protected by splhigh(). + * Entry points to ACPI from above are global functions defined in this + * file, sysctls, and I/O on the control device. Entry points from below + * are interrupts (the SCI), notifies, task queue threads, and the thermal + * zone polling thread. + * + * ACPI tables and global shared data are protected by a global lock + * (acpi_lock). + * + * Each ACPI device can have its own driver-specific mutex for protecting + * shared access to local data. The ACPI_LOCK macros handle mutexes. + * + * Drivers that need to serialize access to functions (e.g., to route + * interrupts, get/set control paths, etc.) should use the sx lock macros + * (ACPI_SERIAL). + * + * ACPI-CA handles its own locking and should not be called with locks held. + * + * The most complicated path is: + * GPE -> EC runs _Qxx -> _Qxx reads EC space -> GPE */ -# define ACPI_LOCK s = splhigh() -# define ACPI_UNLOCK splx(s) -# define ACPI_ASSERTLOCK -# define ACPI_MSLEEP(a, b, c, d, e) tsleep(a, c, d, e) -# define ACPI_LOCK_DECL int s -# define kthread_create(a, b, c, d, e, f) kthread_create(a, b, c, f) -# define tc_init(a) init_timecounter(a) -#else -# define ACPI_LOCK -# define ACPI_UNLOCK -# define ACPI_ASSERTLOCK -# define ACPI_LOCK_DECL -#endif - +extern struct lock acpi_lock; +/* acpi_thermal does lock recurs on purpose */ +/* I bet I should use some other locks here */ +#define ACPI_LOCK(sys) lockmgr(&sys##_lock, LK_EXCLUSIVE|LK_RETRY|LK_CANRECURSE); +#define ACPI_UNLOCK(sys) lockmgr(&sys##_lock, LK_RELEASE); +#define ACPI_LOCK_ASSERT(sys) KKASSERT(lockstatus(&sys##_lock, curthread) == LK_EXCLUSIVE); +#define ACPI_ASSERTLOCK ACPI_LOCK_ASSERT +#define ACPI_LOCK_DECL(sys, name) static struct lock sys##_lock; +#define ACPI_LOCK_INIT(sys, name) lockinit(&sys##_lock, name, 0, 0); + +#define ACPI_SERIAL_INIT(sys) lwkt_serialize_init(&sys##_serializer); +#define ACPI_SERIAL_BEGIN(sys) lwkt_serialize_enter(&sys##_serializer); +#define ACPI_SERIAL_END(sys) lwkt_serialize_exit(&sys##_serializer); +#define ACPI_SERIAL_ASSERT(sys) ASSERT_SERIALIZED(&sys##_serializer); +#define ACPI_SERIAL_DECL(sys, name) static struct lwkt_serialize sys##_serializer; /* * ACPI CA does not define layers for non-ACPI CA drivers. * We define some here within the range provided. @@ -158,6 +196,8 @@ struct acpi_prw_data { /* * Various features and capabilities for the acpi_get_features() method. * In particular, these are used for the ACPI 3.0 _PDC and _OSC methods. + * See the Intel document titled "Processor Driver Capabilities Bit + * Definitions", number 302223-002. */ #define ACPI_CAP_PERF_MSRS (1 << 0) /* Intel SpeedStep PERF_CTL MSRs */ #define ACPI_CAP_C1_IO_HALT (1 << 1) /* Intel C1 "IO then halt" sequence */ @@ -170,6 +210,21 @@ struct acpi_prw_data { #define ACPI_CAP_SMP_C1_NATIVE (1 << 8) /* MP C1 support other than halt */ /* + * Quirk flags. + * + * ACPI_Q_BROKEN: Disables all ACPI support. + * ACPI_Q_TIMER: Disables support for the ACPI timer. + * ACPI_Q_MADT_IRQ0: Specifies that ISA IRQ 0 is wired up to pin 0 of the + * first APIC and that the MADT should force that by ignoring the PC-AT + * compatible flag and ignoring overrides that redirect IRQ 0 to pin 2. + */ +extern int acpi_quirks; +#define ACPI_Q_OK 0 +#define ACPI_Q_BROKEN (1 << 0) +#define ACPI_Q_TIMER (1 << 1) +#define ACPI_Q_MADT_IRQ0 (1 << 2) + +/* * Note that the low ivar values are reserved to provide * interface compatibility with ISA drivers which can also * attach to ACPI. @@ -177,6 +232,7 @@ struct acpi_prw_data { #define ACPI_IVAR_HANDLE 0x100 #define ACPI_IVAR_MAGIC 0x101 #define ACPI_IVAR_PRIVATE 0x102 +#define ACPI_IVAR_FLAGS 0x103 /* * Accessor functions for our ivars. Default value for BUS_READ_IVAR is @@ -200,8 +256,9 @@ static __inline void varp ## _set_ ## var(device_t dev, type t) \ } __ACPI_BUS_ACCESSOR(acpi, handle, ACPI, HANDLE, ACPI_HANDLE) -__ACPI_BUS_ACCESSOR(acpi, magic, ACPI, MAGIC, int) +__ACPI_BUS_ACCESSOR(acpi, magic, ACPI, MAGIC, uintptr_t) __ACPI_BUS_ACCESSOR(acpi, private, ACPI, PRIVATE, void *) +__ACPI_BUS_ACCESSOR(acpi, flags, ACPI, FLAGS, int) void acpi_fake_objhandler(ACPI_HANDLE h, UINT32 fn, void *data); static __inline device_t @@ -231,7 +288,7 @@ void acpi_EnterDebugger(void); #ifdef ACPI_DEBUG #include -#define STEP(x) do {kprintf x, kprintf("\n"); cngetc();} while (0) +#define STEP(x) do {printf x, printf("\n"); cngetc();} while (0) #else #define STEP(x) #endif @@ -241,10 +298,23 @@ void acpi_EnterDebugger(void); device_printf(dev, x); \ } while (0) -#define ACPI_DEVINFO_PRESENT(x) (((x) & 0x9) == 9) +/* Values for the device _STA (status) method. */ +#define ACPI_STA_PRESENT (1 << 0) +#define ACPI_STA_ENABLED (1 << 1) +#define ACPI_STA_SHOW_IN_UI (1 << 2) +#define ACPI_STA_FUNCTIONAL (1 << 3) +#define ACPI_STA_BATT_PRESENT (1 << 4) + +#define ACPI_DEVINFO_PRESENT(x, flags) \ + (((x) & (flags)) == (flags)) +#define ACPI_DEVICE_PRESENT(x) \ + ACPI_DEVINFO_PRESENT(x, ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL) +#define ACPI_BATTERY_PRESENT(x) \ + ACPI_DEVINFO_PRESENT(x, ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL | \ + ACPI_STA_BATT_PRESENT) + BOOLEAN acpi_DeviceIsPresent(device_t dev); BOOLEAN acpi_BatteryIsPresent(device_t dev); -BOOLEAN acpi_MatchHid(ACPI_HANDLE h, char *hid); ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result); uint32_t acpi_TimerDelta(uint32_t end, uint32_t start); @@ -263,18 +333,19 @@ ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res); ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber); ACPI_STATUS acpi_SetIntrModel(int model); +int acpi_ReqSleepState(struct acpi_softc *sc, int state); +int acpi_AckSleepState(struct apm_clone_data *clone, int error); ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state); int acpi_wake_init(device_t dev, int type); int acpi_wake_set_enable(device_t dev, int enable); -int acpi_wake_sleep_prep(device_t dev, int sstate); -int acpi_wake_run_prep(device_t dev); +int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw); ACPI_STATUS acpi_Startup(void); -ACPI_STATUS acpi_Enable(struct acpi_softc *sc); -ACPI_STATUS acpi_Disable(struct acpi_softc *sc); void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify); -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); +# define kthread_create(a, b, c, d, e, f) kthread_create(a, b, c, f) struct acpi_parse_resource_set { void (*set_init)(device_t dev, void *arg, void **context); @@ -293,16 +364,18 @@ struct acpi_parse_resource_set { int count, int trig, int pol); void (*set_drq)(device_t dev, void *context, u_int8_t *drq, int count); - void (*set_start_dependant)(device_t dev, void *context, + void (*set_start_dependent)(device_t dev, void *context, int preference); - void (*set_end_dependant)(device_t dev, void *context); + void (*set_end_dependent)(device_t dev, void *context); }; extern struct acpi_parse_resource_set acpi_res_parse_set; + +void acpi_config_intr(device_t dev, ACPI_RESOURCE *res); +ACPI_STATUS acpi_lookup_irq_resource(device_t dev, int rid, + struct resource *res, ACPI_RESOURCE *acpi_res); ACPI_STATUS acpi_parse_resources(device_t dev, ACPI_HANDLE handle, struct acpi_parse_resource_set *set, void *arg); -extern struct rman acpi_rman_io, acpi_rman_mem; -struct resource_list_entry *acpi_sysres_find(int type, u_long addr); /* ACPI event handling */ UINT32 acpi_event_power_button_sleep(void *context); @@ -320,6 +393,7 @@ EVENTHANDLER_DECLARE(acpi_sleep_event, acpi_event_handler_t); EVENTHANDLER_DECLARE(acpi_wakeup_event, acpi_event_handler_t); /* Device power control. */ +ACPI_STATUS acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable); ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state); /* Misc. */ @@ -349,6 +423,8 @@ int acpi_enabled(char *subsys); int acpi_machdep_init(device_t dev); void acpi_install_wakeup_handler(struct acpi_softc *sc); int acpi_sleep_machdep(struct acpi_softc *sc, int state); +int acpi_table_quirks(int *quirks); +int acpi_machdep_quirks(int *quirks); /* Battery Abstraction. */ struct acpi_battinfo; @@ -369,20 +445,37 @@ void acpi_ec_ecdt_probe(device_t); int acpi_acad_get_acline(int *); /* Package manipulation convenience functions. */ -#define _ACPI_PKG_VALID(pkg) \ - ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE) -#define ACPI_PKG_VALID(pkg, size) \ - (_ACPI_PKG_VALID((pkg)) && (pkg)->Package.Count >= (size)) -#define ACPI_PKG_VALID_EQ(pkg, size) \ - (_ACPI_PKG_VALID((pkg)) && (pkg)->Package.Count == (size)) +#define ACPI_PKG_VALID(pkg, size) \ + ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE && \ + (pkg)->Package.Count >= (size)) +#define ACPI_PKG_VALID_EQ(pkg, size) \ + (ACPI_PKG_VALID((pkg), (size)) && (pkg)->Package.Count == (size)) int acpi_PkgInt(ACPI_OBJECT *res, int idx, ACPI_INTEGER *dst); int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst); int acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size); -int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid, - struct resource **dst, u_int flags); +int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type, + int *rid, struct resource **dst, u_int flags); int acpi_PkgRawGas(ACPI_OBJECT *res, int idx, ACPI_GENERIC_ADDRESS *gas); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); - /* ACPI task kernel thread initialization. */ -int acpi_task_thread_init(void); +int acpi_task_thread_init(void); +extern BOOLEAN acpi_MatchHid(ACPI_HANDLE h, const char *hid); +/* + * Base level for BUS_ADD_CHILD. Special devices are added at orders less + * than this, and normal devices at or above this level. This keeps the + * probe order sorted so that things like sysresource are available before + * their children need them. + */ +#define ACPI_DEV_BASE_ORDER 10 + +/* Default number of task queue threads to start. */ +#ifndef ACPI_MAX_THREADS +#define ACPI_MAX_THREADS 3 +#endif + +/* Use the device logging level for ktr(4). */ +#define KTR_ACPI KTR_DEV + +#endif /* _KERNEL */ +#endif /* !_ACPIVAR_H_ */ -- 2.11.4.GIT