diff --git a/sys/dev/acpica/acpi_powerres.c b/sys/dev/acpica/acpi_powerres.c index 0a8b67a5fa84..0baa5c595470 100644 --- a/sys/dev/acpica/acpi_powerres.c +++ b/sys/dev/acpica/acpi_powerres.c @@ -1,1018 +1,864 @@ /*- * Copyright (c) 2001 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_acpi.h" #include #include #include #include #include #include #include /* * ACPI power resource management. * * Power resource behaviour is slightly complicated by the fact that * a single power resource may provide power for more than one device. * Thus, we must track the device(s) being powered by a given power * resource, and only deactivate it when there are no powered devices. * * Note that this only manages resources for known devices. There is an * ugly case where we may turn off power to a device which is in use because * we don't know that it depends on a given resource. We should perhaps * try to be smarter about this, but a more complete solution would involve * scanning all of the ACPI namespace to find devices we're not currently * aware of, and this raises questions about whether they should be left * on, turned off, etc. */ static MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_POWERRES ACPI_MODULE_NAME("POWERRES") /* Return values from _STA on a power resource */ #define ACPI_PWR_OFF 0 #define ACPI_PWR_ON 1 /* A relationship between a power resource and a consumer. */ struct acpi_powerreference { struct acpi_powerconsumer *ar_consumer; struct acpi_powerresource *ar_resource; TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ }; /* A power-managed device. */ struct acpi_powerconsumer { /* Device which is powered */ ACPI_HANDLE ac_consumer; int ac_state; struct { bool prx_has; size_t prx_count; ACPI_HANDLE *prx_deps; } ac_prx[ACPI_D_STATE_COUNT]; TAILQ_ENTRY(acpi_powerconsumer) ac_link; TAILQ_HEAD(,acpi_powerreference) ac_references; }; /* A power resource. */ struct acpi_powerresource { TAILQ_ENTRY(acpi_powerresource) ap_link; TAILQ_HEAD(,acpi_powerreference) ap_references; ACPI_HANDLE ap_resource; UINT64 ap_systemlevel; UINT64 ap_order; }; static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources = TAILQ_HEAD_INITIALIZER(acpi_powerresources); static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers = TAILQ_HEAD_INITIALIZER(acpi_powerconsumers); ACPI_SERIAL_DECL(powerres, "ACPI power resources"); static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); #ifdef notyet 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); static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); -static ACPI_STATUS acpi_pwr_infer_state(struct acpi_powerconsumer *pc); -static ACPI_STATUS acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state); /* * Register a power resource. * * It's OK to call this if we already know about the resource. */ static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res) { ACPI_STATUS status; ACPI_BUFFER buf; ACPI_OBJECT *obj; struct acpi_powerresource *rp, *srp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); rp = NULL; buf.Pointer = NULL; /* Look to see if we know about this resource */ if (acpi_pwr_find_resource(res) != NULL) return_ACPI_STATUS (AE_OK); /* already know about it */ /* Allocate a new resource */ if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { status = AE_NO_MEMORY; goto out; } TAILQ_INIT(&rp->ap_references); rp->ap_resource = res; /* Get the Power Resource object */ buf.Length = ACPI_ALLOCATE_BUFFER; if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); goto out; } obj = buf.Pointer; if (obj->Type != ACPI_TYPE_POWER) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); status = AE_TYPE; goto out; } rp->ap_systemlevel = obj->PowerResource.SystemLevel; rp->ap_order = obj->PowerResource.ResourceOrder; /* Sort the resource into the list */ status = AE_OK; srp = TAILQ_FIRST(&acpi_powerresources); if (srp == NULL || rp->ap_order < srp->ap_order) { TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); goto done; } TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { if (rp->ap_order < srp->ap_order) { TAILQ_INSERT_BEFORE(srp, rp, ap_link); goto done; } } TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); done: ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); out: if (buf.Pointer != NULL) AcpiOsFree(buf.Pointer); if (ACPI_FAILURE(status) && rp != NULL) free(rp, M_ACPIPWR); return_ACPI_STATUS (status); } #ifdef notyet /* * Deregister a power resource. */ static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res) { struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); rp = NULL; /* Find the resource */ if ((rp = acpi_pwr_find_resource(res)) == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Check that there are no consumers referencing this resource */ if (TAILQ_FIRST(&rp->ap_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Pull it off the list and free it */ TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); free(rp, M_ACPIPWR); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); return_ACPI_STATUS (AE_OK); } #endif /* notyet */ /* * Evaluate the _PRx (power resources each D-state depends on). This also * populates the acpi_powerresources queue with the power resources discovered * during this step. * * ACPI 7.3.8 - 7.3.11 guarantee that _PRx will return the same data each * time they are evaluated. * * If this function fails, acpi_pwr_deregister_consumer() must be called on the * power consumer to free already allocated memory. */ static ACPI_STATUS acpi_pwr_get_power_resources(ACPI_HANDLE consumer, struct acpi_powerconsumer *pc) { ACPI_INTEGER status; ACPI_STRING reslist_name; ACPI_HANDLE reslist_handle; ACPI_STRING reslist_names[] = {"_PR0", "_PR1", "_PR2", "_PR3"}; ACPI_BUFFER reslist; ACPI_OBJECT *reslist_object; ACPI_OBJECT *dep; ACPI_HANDLE *res; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); MPASS(consumer != NULL); for (int state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { pc->ac_prx[state].prx_has = false; pc->ac_prx[state].prx_count = 0; pc->ac_prx[state].prx_deps = NULL; reslist_name = reslist_names[state - ACPI_STATE_D0]; if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) continue; reslist.Pointer = NULL; reslist.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObjectTyped(reslist_handle, NULL, NULL, &reslist, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(status) || reslist.Pointer == NULL) /* * ACPI_ALLOCATE_BUFFER entails everything will be freed on error * by AcpiEvaluateObjectTyped. */ continue; reslist_object = (ACPI_OBJECT *)reslist.Pointer; pc->ac_prx[state].prx_has = true; pc->ac_prx[state].prx_count = reslist_object->Package.Count; if (reslist_object->Package.Count == 0) { AcpiOsFree(reslist_object); continue; } pc->ac_prx[state].prx_deps = mallocarray(pc->ac_prx[state].prx_count, sizeof(*pc->ac_prx[state].prx_deps), M_ACPIPWR, M_NOWAIT); if (pc->ac_prx[state].prx_deps == NULL) { AcpiOsFree(reslist_object); return_ACPI_STATUS (AE_NO_MEMORY); } for (size_t i = 0; i < reslist_object->Package.Count; i++) { dep = &reslist_object->Package.Elements[i]; res = dep->Reference.Handle; pc->ac_prx[state].prx_deps[i] = res; /* It's fine to attempt to register the same resource twice. */ acpi_pwr_register_resource(res); } AcpiOsFree(reslist_object); } return_ACPI_STATUS (AE_OK); } /* * Register a power consumer. * * It's OK to call this if we already know about the consumer. */ static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer) { ACPI_INTEGER status; 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 (acpi_pwr_find_consumer(consumer) != NULL) return_ACPI_STATUS (AE_OK); /* Allocate a new power consumer */ if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) return_ACPI_STATUS (AE_NO_MEMORY); TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); TAILQ_INIT(&pc->ac_references); pc->ac_consumer = consumer; /* * Get all its power resource dependencies, if it has _PRx. We do this now * as an opportunity to populate the acpi_powerresources queue. * * If this fails, immediately deregister it. */ status = acpi_pwr_get_power_resources(consumer, pc); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to get power resources for %s\n", acpi_name(consumer))); acpi_pwr_deregister_consumer(consumer); return_ACPI_STATUS (status); } - /* Find its initial state. */ - if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &pc->ac_state))) - pc->ac_state = ACPI_STATE_UNKNOWN; + /* XXX we should try to find its current state */ + pc->ac_state = ACPI_STATE_UNKNOWN; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); return_ACPI_STATUS (AE_OK); } /* * Deregister a power consumer. * * This should only be done once the consumer has been powered off. * (XXX is this correct? Check once implemented) */ static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) { struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); /* Find the consumer */ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Make sure the consumer's not referencing anything right now */ if (TAILQ_FIRST(&pc->ac_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Pull the consumer off the list and free it */ TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); for (size_t i = 0; i < sizeof(pc->ac_prx) / sizeof(*pc->ac_prx); i++) if (pc->ac_prx[i].prx_deps != NULL) free(pc->ac_prx[i].prx_deps, M_ACPIPWR); free(pc, M_ACPIPWR); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); return_ACPI_STATUS (AE_OK); } /* - * The _PSC control method isn't required if it's possible to infer the D-state - * from the _PRx control methods. (See 7.3.6.) - * We can infer that a given D-state has been achieved when all the dependencies - * are in the ON state. - */ -static ACPI_STATUS -acpi_pwr_infer_state(struct acpi_powerconsumer *pc) -{ - ACPI_HANDLE *res; - uint32_t on; - bool all_on = false; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_SERIAL_ASSERT(powerres); - - /* It is important we go from the hottest to the coldest state. */ - for ( - pc->ac_state = ACPI_STATE_D0; - pc->ac_state <= ACPI_STATE_D3_HOT && !all_on; - pc->ac_state++ - ) { - MPASS(pc->ac_state <= sizeof(pc->ac_prx) / sizeof(*pc->ac_prx)); - - if (!pc->ac_prx[pc->ac_state].prx_has) - continue; - - all_on = true; - - for (size_t i = 0; i < pc->ac_prx[pc->ac_state].prx_count; i++) { - res = pc->ac_prx[pc->ac_state].prx_deps[i]; - /* If failure, better to assume D-state is hotter than colder. */ - if (ACPI_FAILURE(acpi_GetInteger(res, "_STA", &on))) - continue; - if (on == 0) { - all_on = false; - break; - } - } - } - - MPASS(pc->ac_state != ACPI_STATE_D0); - - /* - * If none of the power resources required for the shallower D-states are - * on, then we can assume it is unpowered (i.e. D3cold). A device is not - * required to support D3cold however; in that case, _PR3 is not explicitly - * provided. Those devices should default to D3hot instead. - * - * See comments of first row of table 7.1 in ACPI spec. - */ - if (!all_on) - pc->ac_state = pc->ac_prx[ACPI_STATE_D3_HOT].prx_has ? - ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT; - else - pc->ac_state--; - - return_ACPI_STATUS (AE_OK); -} - -static ACPI_STATUS -acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state) -{ - struct acpi_powerconsumer *pc; - ACPI_HANDLE method_handle; - ACPI_STATUS status; - ACPI_BUFFER result; - ACPI_OBJECT *object = NULL; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - ACPI_SERIAL_ASSERT(powerres); - - if (consumer == NULL) - return_ACPI_STATUS (AE_NOT_FOUND); - - 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 added power consumer but can't find it"); - } - - status = AcpiGetHandle(consumer, "_PSC", &method_handle); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no _PSC object - %s\n", - AcpiFormatException(status))); - status = acpi_pwr_infer_state(pc); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't infer D-state - %s\n", - AcpiFormatException(status))); - pc->ac_state = ACPI_STATE_UNKNOWN; - } - goto out; - } - - result.Pointer = NULL; - result.Length = ACPI_ALLOCATE_BUFFER; - status = AcpiEvaluateObjectTyped(method_handle, NULL, NULL, &result, ACPI_TYPE_INTEGER); - if (ACPI_FAILURE(status) || result.Pointer == NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to get state with _PSC - %s\n", - AcpiFormatException(status))); - pc->ac_state = ACPI_STATE_UNKNOWN; - goto out; - } - - object = (ACPI_OBJECT *)result.Pointer; - pc->ac_state = ACPI_STATE_D0 + object->Integer.Value; - status = AE_OK; - -out: - if (object != NULL) - AcpiOsFree(object); - *state = pc->ac_state; - return_ACPI_STATUS (status); -} - -/* - * Get a power consumer's D-state. - */ -ACPI_STATUS -acpi_pwr_get_state(ACPI_HANDLE consumer, int *state) -{ - ACPI_STATUS res; - - ACPI_SERIAL_BEGIN(powerres); - res = acpi_pwr_get_state_locked(consumer, state); - ACPI_SERIAL_END(powerres); - return (res); -} - -/* - * Set a power consumer to a particular D-state. + * Set a power consumer to a particular power state. */ ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) { struct acpi_powerconsumer *pc; ACPI_HANDLE method_handle, reslist_handle, pr0_handle; ACPI_BUFFER reslist_buffer; ACPI_OBJECT *reslist_object; ACPI_STATUS status; char *method_name, *reslist_name = NULL; - int new_state; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* 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))) goto out; if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) panic("acpi added power consumer but can't find it"); } /* Stop here if we're already at the target D-state. */ if (pc->ac_state == state) { status = AE_OK; goto out; } /* * Check for valid transitions. From D3hot or D3cold, we can only go to D0. * The exception to this is going from D3hot to D3cold or the other way * around. This is because they both use _PS3, so the only difference when * doing these transitions is whether or not the power resources for _PR3 * are on for devices which support D3cold, and turning these power * resources on/off is always perfectly fine (ACPI 7.3.11). */ status = AE_BAD_PARAMETER; if (pc->ac_state == ACPI_STATE_D3_HOT && state != ACPI_STATE_D0 && state != ACPI_STATE_D3_COLD) goto out; if (pc->ac_state == ACPI_STATE_D3_COLD && state != ACPI_STATE_D0 && state != ACPI_STATE_D3_HOT) goto out; /* Find transition mechanism(s) */ switch (state) { case ACPI_STATE_D0: method_name = "_PS0"; reslist_name = "_PR0"; break; case ACPI_STATE_D1: method_name = "_PS1"; reslist_name = "_PR1"; break; case ACPI_STATE_D2: method_name = "_PS2"; reslist_name = "_PR2"; break; case ACPI_STATE_D3_HOT: method_name = "_PS3"; reslist_name = "_PR3"; break; case ACPI_STATE_D3_COLD: method_name = "_PS3"; reslist_name = NULL; break; default: goto out; } ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s %s -> %s\n", acpi_name(consumer), acpi_d_state_to_str(pc->ac_state), acpi_d_state_to_str(state))); /* * Verify that this state is supported, ie. one of method or * reslist must be present. We need to do this before we go * dereferencing resources (since we might be trying to go to * a state we don't support). * * Note that if any states are supported, the device has to * support D0 and D3. It's never an error to try to go to * D0. */ if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) method_handle = NULL; if (reslist_name == NULL || ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) reslist_handle = NULL; if (reslist_handle == NULL && method_handle == NULL) { if (state == ACPI_STATE_D0) { pc->ac_state = ACPI_STATE_D0; status = AE_OK; goto out; } if (state == ACPI_STATE_D3_COLD) state = ACPI_STATE_D3_HOT; if (state != ACPI_STATE_D3_HOT) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state %s\n", acpi_d_state_to_str(state))); goto out; } /* * 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 %s)\n", acpi_d_state_to_str(state))); goto out; } reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate _PR0 for device %s, state %s\n", acpi_name(consumer), acpi_d_state_to_str(state))); goto out; } reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; if (!ACPI_PKG_VALID(reslist_object, 1)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invalid package object for state %s\n", acpi_d_state_to_str(state))); status = AE_TYPE; goto out; } AcpiOsFree(reslist_buffer.Pointer); reslist_buffer.Pointer = NULL; reslist_object = NULL; } /* * Check that we can actually fetch the list of power resources */ if (reslist_handle != NULL) { reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", acpi_name(reslist_handle))); goto out; } reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; if (reslist_object->Type != ACPI_TYPE_PACKAGE) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", reslist_object->Type)); status = AE_TYPE; goto out; } } /* * Now we are ready to switch, so kill off any current power * resource references. */ acpi_pwr_dereference_resource(pc); /* * Add new power resource references, if we have any. Traverse the * package that we got from evaluating reslist_handle, and look up each * of the resources that are referenced. */ if (reslist_object != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", reslist_object->Package.Count)); acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); } /* * If we changed anything in the resource list, we need to run a switch * pass now. */ if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch resources from %s to %s\n", acpi_name(consumer), acpi_d_state_to_str(state))); /* XXX is this appropriate? Should we return to previous state? */ goto out; } /* Invoke power state switch method (if present) */ if (method_handle != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", acpi_name(method_handle))); status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", AcpiFormatException(status))); pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX Should we return to previous state? */ goto out; } } - /* - * Make sure the transition succeeded. If getting new state failed, - * just assume the new state is what we wanted. This was the behaviour - * before we were checking D-states. - */ - if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &new_state))) { - printf("%s: failed to get new D-state\n", __func__); - pc->ac_state = state; - } else { - if (new_state != state) - printf("%s: new power state %s is not the one requested %s\n", - __func__, acpi_d_state_to_str(new_state), - acpi_d_state_to_str(state)); - pc->ac_state = new_state; - } - - /* - * We consider the transition successful even if the state we got doesn't - * reflect what we set it to. This is because we weren't previously - * checking the new state at all, so there might exist buggy platforms on - * which suspend would otherwise succeed if we failed here. - */ + /* Transition was successful */ + pc->ac_state = state; status = AE_OK; 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. */ static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) { struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; struct acpi_powerreference *pr; struct acpi_powerresource *rp; ACPI_HANDLE res; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); res = acpi_GetReference(NULL, obj); if (res == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't create a power reference for object type %d\n", obj->Type)); return_VOID; } /* Create/look up the resource */ if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", obj->String.Pointer, AcpiFormatException(status))); return_VOID; } if ((rp = acpi_pwr_find_resource(res)) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); return_VOID; } ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); /* Create a reference between the consumer and resource */ if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "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); free(pr, M_ACPIPWR); changed = 1; } return (changed); } /* * Switch power resources to conform to the desired state. * * Consumers may have modified the power resource list in an arbitrary * fashion; we sweep it in sequence order. */ static ACPI_STATUS acpi_pwr_switch_power(void) { struct acpi_powerresource *rp; ACPI_STATUS status; int cur; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); /* * Sweep the list forwards turning things on. */ TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { if (TAILQ_FIRST(&rp->ap_references) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", acpi_name(rp->ap_resource))); continue; } status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 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; } /* * 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) { status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); } } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); } } /* Sweep the list backwards turning things off. */ TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { if (TAILQ_FIRST(&rp->ap_references) != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", acpi_name(rp->ap_resource))); continue; } status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 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; } /* * 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) { status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); } } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); } } return_ACPI_STATUS (AE_OK); } /* * Find a power resource's control structure. */ static struct acpi_powerresource * acpi_pwr_find_resource(ACPI_HANDLE res) { struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { if (rp->ap_resource == res) break; } return_PTR (rp); } /* * Find a power consumer's control structure. */ static struct acpi_powerconsumer * acpi_pwr_find_consumer(ACPI_HANDLE consumer) { struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(powerres); TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { if (pc->ac_consumer == consumer) break; } return_PTR (pc); } diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index 4c789dd3e9f2..71d8e46ab310 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -1,637 +1,636 @@ /*- * Copyright (c) 2000 Mitsuru IWASAKI * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _ACPIVAR_H_ #define _ACPIVAR_H_ #ifdef _KERNEL #include "acpi_if.h" #include "bus_if.h" #include #ifdef INTRNG #include #endif #include #include #include #include #include #include #include #include #include struct apm_clone_data; struct acpi_softc { device_t acpi_dev; struct cdev *acpi_dev_t; int acpi_enabled; enum power_stype acpi_stype; int acpi_sleep_disabled; struct sysctl_ctx_list acpi_sysctl_ctx; struct sysctl_oid *acpi_sysctl_tree; enum power_stype acpi_power_button_stype; enum power_stype acpi_sleep_button_stype; enum power_stype acpi_lid_switch_stype; int acpi_standby_sx; int acpi_s4bios; int acpi_sleep_delay; int acpi_do_disable; int acpi_verbose; int acpi_handle_reboot; vm_offset_t acpi_wakeaddr; vm_paddr_t acpi_wakephys; enum power_stype acpi_next_stype; /* Next suspend sleep type. */ 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. */ /* System Resources */ struct resource_list sysres_rl; }; struct acpi_device { /* ACPI ivars */ ACPI_HANDLE ad_handle; void *ad_private; int ad_flags; int ad_cls_class; int ad_domain; ACPI_BUFFER dsd; /* Device Specific Data */ const ACPI_OBJECT *dsd_pkg; /* Resources */ struct resource_list ad_rl; }; #ifdef INTRNG struct intr_map_data_acpi { struct intr_map_data hdr; u_int irq; u_int pol; u_int trig; }; #endif /* 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; struct selinfo sel_read; }; #define ACPI_PRW_MAX_POWERRES 8 struct acpi_prw_data { ACPI_HANDLE gpe_handle; int gpe_bit; int lowest_wake; 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_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) /* * 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_mutex). * * 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 */ extern struct mtx acpi_mutex; #define ACPI_LOCK(sys) mtx_lock(&sys##_mutex) #define ACPI_UNLOCK(sys) mtx_unlock(&sys##_mutex) #define ACPI_LOCK_ASSERT(sys) mtx_assert(&sys##_mutex, MA_OWNED); #define ACPI_LOCK_DECL(sys, name) \ static struct mtx sys##_mutex; \ MTX_SYSINIT(sys##_mutex, &sys##_mutex, name, MTX_DEF) #define ACPI_SERIAL_BEGIN(sys) sx_xlock(&sys##_sxlock) #define ACPI_SERIAL_END(sys) sx_xunlock(&sys##_sxlock) #define ACPI_SERIAL_ASSERT(sys) sx_assert(&sys##_sxlock, SX_XLOCKED); #define ACPI_SERIAL_DECL(sys, name) \ static struct sx sys##_sxlock; \ SX_SYSINIT(sys##_sxlock, &sys##_sxlock, name) /* * ACPI CA does not define layers for non-ACPI CA drivers. * We define some here within the range provided. */ #define ACPI_AC_ADAPTER 0x00010000 #define ACPI_BATTERY 0x00020000 #define ACPI_BUS 0x00040000 #define ACPI_BUTTON 0x00080000 #define ACPI_EC 0x00100000 #define ACPI_FAN 0x00200000 #define ACPI_POWERRES 0x00400000 #define ACPI_PROCESSOR 0x00800000 #define ACPI_THERMAL 0x01000000 #define ACPI_TIMER 0x02000000 #define ACPI_OEM 0x04000000 /* * Constants for different interrupt models used with acpi_SetIntrModel(). */ #define ACPI_INTR_PIC 0 #define ACPI_INTR_APIC 1 #define ACPI_INTR_SAPIC 2 /* * 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 "Intel Processor Vendor-Specific ACPI", * number 302223-007. */ #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 */ #define ACPI_CAP_THR_MSRS (1 << 2) /* Intel OnDemand throttling MSRs */ #define ACPI_CAP_SMP_SAME (1 << 3) /* MP C1, Px, and Tx (all the same) */ #define ACPI_CAP_SMP_SAME_C3 (1 << 4) /* MP C2 and C3 (all the same) */ #define ACPI_CAP_SMP_DIFF_PX (1 << 5) /* MP Px (different, using _PSD) */ #define ACPI_CAP_SMP_DIFF_CX (1 << 6) /* MP Cx (different, using _CSD) */ #define ACPI_CAP_SMP_DIFF_TX (1 << 7) /* MP Tx (different, using _TSD) */ #define ACPI_CAP_SMP_C1_NATIVE (1 << 8) /* MP C1 support other than halt */ #define ACPI_CAP_SMP_C3_NATIVE (1 << 9) /* MP C2 and C3 support */ #define ACPI_CAP_PX_HW_COORD (1 << 11) /* Intel P-state HW coordination */ #define ACPI_CAP_INTR_CPPC (1 << 12) /* Native Interrupt Handling for Collaborative Processor Performance Control notifications */ #define ACPI_CAP_HW_DUTY_C (1 << 13) /* Hardware Duty Cycling */ /* * 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. * ACPI_Q_AEI_NOPULL: Specifies that _AEI objects incorrectly designate pins * as "PullUp" and they should be treated as "NoPull" instead. * ACPI_Q_CLEAR_PME_ON_DETACH: Specifies that PCIM_PSTAT_(PME & ~PMEENABLE) * should be written to the power status register as part of ACPI Eject. * ACPI_Q_DELAY_BEFORE_EJECT_RESCAN: Specifies that we need a short (10ms) * delay after _EJ0 returns before rescanning the PCI bus. */ 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) #define ACPI_Q_AEI_NOPULL (1 << 3) #define ACPI_Q_CLEAR_PME_ON_DETACH (1 << 4) #define ACPI_Q_DELAY_BEFORE_EJECT_RESCAN (1 << 5) #if defined(__amd64__) || defined(__i386__) /* * Certain Intel BIOSes have buggy AML that specify an IRQ that is * edge-sensitive and active-lo. Normally, edge-sensitive IRQs should * be active-hi. If this value is non-zero, edge-sensitive ISA IRQs * are forced to be active-hi instead. At least some AMD systems use * active-lo edge-sensitive ISA IRQs, so this setting is only enabled * by default on systems with Intel CPUs. */ extern int acpi_override_isa_irq_polarity; #endif /* * Plug and play information for device matching. Matching table format * is compatible with ids parameter of ACPI_ID_PROBE bus method. * * XXX: While ACPI_ID_PROBE matches against _HID and all _CIDs, current * acpi_pnpinfo() exports only _HID and first _CID. That means second * and further _CIDs should be added to both acpi_pnpinfo() and * ACPICOMPAT_PNP_INFO if device matching against them is required. */ #define ACPICOMPAT_PNP_INFO(t, busname) \ MODULE_PNP_INFO("Z:_HID", busname, t##hid, t, nitems(t)-1); \ MODULE_PNP_INFO("Z:_CID", busname, t##cid, t, nitems(t)-1); #define ACPI_PNP_INFO(t) ACPICOMPAT_PNP_INFO(t, acpi) /* * Note that the low ivar values are reserved to provide * interface compatibility with ISA drivers which can also * attach to ACPI. */ #define ACPI_IVAR_HANDLE 0x100 #define ACPI_IVAR_UNUSED 0x101 /* Unused/reserved. */ #define ACPI_IVAR_PRIVATE 0x102 #define ACPI_IVAR_FLAGS 0x103 #define ACPI_IVAR_DOMAIN 0x104 /* * ad_domain NUMA domain special value. */ #define ACPI_DEV_DOMAIN_UNKNOWN (-1) /* * Accessor functions for our ivars. Default value for BUS_READ_IVAR is * (type) 0. The accessor functions don't check return values. */ #define __ACPI_BUS_ACCESSOR(varp, var, ivarp, ivar, type) \ \ static __inline type varp ## _get_ ## var(device_t dev) \ { \ uintptr_t v = 0; \ BUS_READ_IVAR(device_get_parent(dev), dev, \ ivarp ## _IVAR_ ## ivar, &v); \ return ((type) v); \ } \ \ static __inline void varp ## _set_ ## var(device_t dev, type t) \ { \ uintptr_t v = (uintptr_t) t; \ BUS_WRITE_IVAR(device_get_parent(dev), dev, \ ivarp ## _IVAR_ ## ivar, v); \ } __ACPI_BUS_ACCESSOR(acpi, handle, ACPI, HANDLE, ACPI_HANDLE) __ACPI_BUS_ACCESSOR(acpi, private, ACPI, PRIVATE, void *) __ACPI_BUS_ACCESSOR(acpi, flags, ACPI, FLAGS, int) __ACPI_BUS_ACCESSOR(acpi, domain, ACPI, DOMAIN, int) void acpi_fake_objhandler(ACPI_HANDLE h, void *data); static __inline device_t acpi_get_device(ACPI_HANDLE handle) { void *dev = NULL; AcpiGetData(handle, acpi_fake_objhandler, &dev); return ((device_t)dev); } static __inline ACPI_OBJECT_TYPE acpi_get_type(device_t dev) { ACPI_HANDLE h; ACPI_OBJECT_TYPE t; if ((h = acpi_get_handle(dev)) == NULL) return (ACPI_TYPE_NOT_FOUND); if (ACPI_FAILURE(AcpiGetType(h, &t))) return (ACPI_TYPE_NOT_FOUND); return (t); } /* Find the difference between two PM tick counts. */ static __inline uint32_t acpi_TimerDelta(uint32_t end, uint32_t start) { if (end < start && (AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER) == 0) end |= 0x01000000; return (end - start); } #ifdef ACPI_DEBUGGER void acpi_EnterDebugger(void); #endif #ifdef ACPI_DEBUG #include #define STEP(x) do {printf x, printf("\n"); cngetc();} while (0) #else #define STEP(x) #endif #define ACPI_VPRINT(dev, acpi_sc, x...) do { \ if (acpi_get_verbose(acpi_sc)) \ device_printf(dev, x); \ } while (0) /* Values for the first status word returned by _OSC. */ #define ACPI_OSC_FAILURE (1 << 1) #define ACPI_OSC_BAD_UUID (1 << 2) #define ACPI_OSC_BAD_REVISION (1 << 3) #define ACPI_OSC_CAPS_MASKED (1 << 4) #define ACPI_DEVINFO_PRESENT(x, flags) \ (((x) & (flags)) == (flags)) #define ACPI_DEVICE_PRESENT(x) \ ACPI_DEVINFO_PRESENT(x, ACPI_STA_DEVICE_PRESENT | \ ACPI_STA_DEVICE_FUNCTIONING) #define ACPI_BATTERY_PRESENT(x) \ ACPI_DEVINFO_PRESENT(x, ACPI_STA_DEVICE_PRESENT | \ ACPI_STA_DEVICE_FUNCTIONING | ACPI_STA_BATTERY_PRESENT) /* Callback function type for walking subtables within a table. */ typedef void acpi_subtable_handler(ACPI_SUBTABLE_HEADER *, void *); BOOLEAN acpi_DeviceIsPresent(device_t dev); BOOLEAN acpi_BatteryIsPresent(device_t dev); ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result); ACPI_STATUS acpi_GetProperty(device_t dev, ACPI_STRING propname, const ACPI_OBJECT **value); ACPI_BUFFER *acpi_AllocBuffer(int size); ACPI_STATUS acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp, UINT32 *number); ACPI_STATUS acpi_GetInteger(ACPI_HANDLE handle, char *path, UINT32 *number); ACPI_STATUS acpi_SetInteger(ACPI_HANDLE handle, char *path, UINT32 number); ACPI_STATUS acpi_ForeachPackageObject(ACPI_OBJECT *obj, void (*func)(ACPI_OBJECT *comp, void *arg), void *arg); ACPI_STATUS acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp); ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res); UINT64 acpi_DSMQuery(ACPI_HANDLE h, const uint8_t *uuid, int revision); ACPI_STATUS acpi_EvaluateDSM(ACPI_HANDLE handle, const uint8_t *uuid, int revision, UINT64 function, ACPI_OBJECT *package, ACPI_BUFFER *out_buf); ACPI_STATUS acpi_EvaluateDSMTyped(ACPI_HANDLE handle, const uint8_t *uuid, int revision, UINT64 function, ACPI_OBJECT *package, ACPI_BUFFER *out_buf, ACPI_OBJECT_TYPE type); ACPI_STATUS acpi_EvaluateOSC(ACPI_HANDLE handle, uint8_t *uuid, int revision, int count, uint32_t *caps_in, uint32_t *caps_out, bool query); ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber); ACPI_STATUS acpi_SetIntrModel(int model); int acpi_ReqSleepState(struct acpi_softc *sc, enum power_stype stype); int acpi_AckSleepState(struct apm_clone_data *clone, int error); ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state); int acpi_wake_set_enable(device_t dev, int enable); int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw); ACPI_STATUS acpi_Startup(void); void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify); int acpi_bus_alloc_gas(device_t dev, int *type, int *rid, ACPI_GENERIC_ADDRESS *gas, struct resource **res, u_int flags); void acpi_walk_subtables(void *first, void *end, acpi_subtable_handler *handler, void *arg); BOOLEAN acpi_has_hid(ACPI_HANDLE handle); int acpi_MatchHid(ACPI_HANDLE h, const char *hid); #define ACPI_MATCHHID_NOMATCH 0 #define ACPI_MATCHHID_HID 1 #define ACPI_MATCHHID_CID 2 static __inline bool acpi_HasProperty(device_t dev, ACPI_STRING propname) { return ACPI_SUCCESS(acpi_GetProperty(dev, propname, NULL)); } struct acpi_parse_resource_set { void (*set_init)(device_t dev, void *arg, void **context); void (*set_done)(device_t dev, void *context); void (*set_ioport)(device_t dev, void *context, uint64_t base, uint64_t length); void (*set_iorange)(device_t dev, void *context, uint64_t low, uint64_t high, uint64_t length, uint64_t align); void (*set_memory)(device_t dev, void *context, uint64_t base, uint64_t length); void (*set_memoryrange)(device_t dev, void *context, uint64_t low, uint64_t high, uint64_t length, uint64_t align); void (*set_irq)(device_t dev, void *context, uint8_t *irq, int count, int trig, int pol); void (*set_ext_irq)(device_t dev, void *context, uint32_t *irq, int count, int trig, int pol); void (*set_drq)(device_t dev, void *context, uint8_t *drq, int count); void (*set_start_dependent)(device_t dev, void *context, int preference); void (*set_end_dependent)(device_t dev, void *context); }; extern struct acpi_parse_resource_set acpi_res_parse_set; int acpi_identify(void); void acpi_config_intr(device_t dev, ACPI_RESOURCE *res); #ifdef INTRNG int acpi_map_intr(device_t dev, u_int irq, ACPI_HANDLE handle); #endif 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); /* ACPI event handling */ UINT32 acpi_event_power_button_sleep(void *context); UINT32 acpi_event_power_button_wake(void *context); UINT32 acpi_event_sleep_button_sleep(void *context); UINT32 acpi_event_sleep_button_wake(void *context); #define ACPI_EVENT_PRI_FIRST 0 #define ACPI_EVENT_PRI_DEFAULT 10000 #define ACPI_EVENT_PRI_LAST 20000 typedef void (*acpi_event_handler_t)(void *, int); EVENTHANDLER_DECLARE(acpi_sleep_event, acpi_event_handler_t); EVENTHANDLER_DECLARE(acpi_wakeup_event, acpi_event_handler_t); EVENTHANDLER_DECLARE(acpi_acad_event, acpi_event_handler_t); EVENTHANDLER_DECLARE(acpi_video_event, acpi_event_handler_t); /* Device power control. */ ACPI_STATUS acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable); -ACPI_STATUS acpi_pwr_get_state(ACPI_HANDLE consumer, int *state); ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state); acpi_pwr_for_sleep_t acpi_device_pwr_for_sleep; int acpi_set_powerstate(device_t child, int state); /* APM emulation */ void acpi_apm_init(struct acpi_softc *); /* Misc. */ static __inline struct acpi_softc * acpi_device_get_parent_softc(device_t child) { device_t parent; parent = device_get_parent(child); if (parent == NULL) return (NULL); return (device_get_softc(parent)); } static __inline int acpi_get_verbose(struct acpi_softc *sc) { if (sc) return (sc->acpi_verbose); return (0); } static __inline const char * acpi_d_state_to_str(int state) { const char *strs[ACPI_D_STATE_COUNT] = {"D0", "D1", "D2", "D3hot", "D3cold"}; MPASS(state >= ACPI_STATE_D0 && state <= ACPI_D_STATES_MAX); return (strs[state]); } char *acpi_name(ACPI_HANDLE handle); int acpi_avoid(ACPI_HANDLE handle); int acpi_disabled(char *subsys); int acpi_get_acpi_device_path(device_t bus, device_t child, const char *locator, struct sbuf *sb); 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_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, int intr_enabled); int acpi_table_quirks(int *quirks); int acpi_machdep_quirks(int *quirks); int acpi_pnpinfo(ACPI_HANDLE handle, struct sbuf *sb); uint32_t hpet_get_uid(device_t dev); /* Battery Abstraction. */ struct acpi_battinfo; int acpi_battery_register(device_t dev); int acpi_battery_remove(device_t dev); int acpi_battery_get_units(void); int acpi_battery_get_info_expire(void); int acpi_battery_bst_valid(struct acpi_bst *bst); int acpi_battery_bix_valid(struct acpi_bix *bix); int acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *info); /* Embedded controller. */ void acpi_ec_ecdt_probe(device_t); /* AC adapter interface. */ int acpi_acad_get_acline(int *); /* Package manipulation convenience functions. */ #define ACPI_PKG_VALID(pkg, size) \ ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE && \ (pkg)->Package.Count >= (size)) #define ACPI_PKG_VALID_EQ(pkg, size) \ ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE && \ (pkg)->Package.Count == (size)) int acpi_PkgInt(ACPI_OBJECT *res, int idx, UINT64 *dst); int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst); int acpi_PkgInt16(ACPI_OBJECT *res, int idx, uint16_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 *type, int *rid, struct resource **dst, u_int flags); int acpi_PkgFFH_IntelCpu(ACPI_OBJECT *res, int idx, int *vendor, int *class, uint64_t *address, int *accsize); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); /* * 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 100 /* Default maximum number of tasks to enqueue. */ #ifndef ACPI_MAX_TASKS #define ACPI_MAX_TASKS MAX(32, MAXCPU * 4) #endif /* 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 SYSCTL_DECL(_debug_acpi); /* * Parse and use proximity information in SRAT and SLIT. */ int acpi_pxm_init(int ncpus, vm_paddr_t maxphys); void acpi_pxm_parse_tables(void); void acpi_pxm_set_mem_locality(void); void acpi_pxm_set_cpu_locality(void); int acpi_pxm_get_cpu_locality(int apic_id); int acpi_pxm_parse(device_t dev); /* * Map a PXM to a VM domain. * * Returns the VM domain ID if found, or -1 if not found / invalid. */ int acpi_map_pxm_to_vm_domainid(int pxm); bus_get_cpus_t acpi_get_cpus; #ifdef __aarch64__ /* * ARM specific ACPI interfaces, relating to IORT table. */ int acpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid); int acpi_iort_map_pci_smmuv3(u_int seg, u_int rid, uint64_t *xref, u_int *devid); int acpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm); int acpi_iort_map_named_msi(const char *devname, u_int rid, u_int *xref, u_int *devid); int acpi_iort_map_named_smmuv3(const char *devname, u_int rid, uint64_t *xref, u_int *devid); #endif #endif /* _KERNEL */ #endif /* !_ACPIVAR_H_ */