acpi_powerres.c revision 119418
178915Smsmith/*-
278915Smsmith * Copyright (c) 2001 Michael Smith
378915Smsmith * All rights reserved.
478915Smsmith *
578915Smsmith * Redistribution and use in source and binary forms, with or without
678915Smsmith * modification, are permitted provided that the following conditions
778915Smsmith * are met:
878915Smsmith * 1. Redistributions of source code must retain the above copyright
978915Smsmith *    notice, this list of conditions and the following disclaimer.
1078915Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1178915Smsmith *    notice, this list of conditions and the following disclaimer in the
1278915Smsmith *    documentation and/or other materials provided with the distribution.
1378915Smsmith *
1478915Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1578915Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1678915Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1778915Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1878915Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1978915Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2078915Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2178915Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2278915Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2378915Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2478915Smsmith * SUCH DAMAGE.
2578915Smsmith */
2678915Smsmith
27119418Sobrien#include <sys/cdefs.h>
28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_powerres.c 119418 2003-08-24 17:55:58Z obrien $");
29119418Sobrien
3078915Smsmith#include "opt_acpi.h"		/* XXX trim includes */
3178915Smsmith#include <sys/param.h>
3278915Smsmith#include <sys/kernel.h>
3378915Smsmith#include <sys/proc.h>
3478915Smsmith#include <sys/malloc.h>
3578915Smsmith#include <sys/bus.h>
3678915Smsmith#include <sys/conf.h>
3778915Smsmith#include <sys/ioccom.h>
3878915Smsmith#include <sys/reboot.h>
3978915Smsmith#include <sys/sysctl.h>
4078915Smsmith#include <sys/systm.h>
4178915Smsmith#include <sys/ctype.h>
4278915Smsmith
4378915Smsmith#include <machine/clock.h>
4478915Smsmith
4578915Smsmith#include <machine/resource.h>
4678915Smsmith
4778915Smsmith#include "acpi.h"
4878915Smsmith
4978915Smsmith#include <dev/acpica/acpivar.h>
5078915Smsmith#include <dev/acpica/acpiio.h>
5178915Smsmith
5278915Smsmith/*
5378915Smsmith * ACPI power resource management.
5478915Smsmith *
5578915Smsmith * Power resource behaviour is slightly complicated by the fact that
5678915Smsmith * a single power resource may provide power for more than one device.
5778915Smsmith * Thus, we must track the device(s) being powered by a given power
5878915Smsmith * resource, and only deactivate it when there are no powered devices.
5978915Smsmith *
6078915Smsmith * Note that this only manages resources for known devices.  There is an
6178915Smsmith * ugly case where we may turn of power to a device which is in use because
6278915Smsmith * we don't know that it depends on a given resource.  We should perhaps
6378915Smsmith * try to be smarter about this, but a more complete solution would involve
6478915Smsmith * scanning all of the ACPI namespace to find devices we're not currently
6578915Smsmith * aware of, and this raises questions about whether they should be left
6678915Smsmith * on, turned off, etc.
6778915Smsmith *
6878915Smsmith * XXX locking
6978915Smsmith */
7078915Smsmith
7178915SmsmithMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
7278915Smsmith
7378915Smsmith/*
7478915Smsmith * Hooks for the ACPI CA debugging infrastructure
7578915Smsmith */
7678993Smsmith#define _COMPONENT	ACPI_POWER
7791125SmsmithACPI_MODULE_NAME("POWERRES")
7878915Smsmith
7979357Smsmith/* return values from _STA on a power resource */
8079357Smsmith#define ACPI_PWR_OFF	0
8179357Smsmith#define ACPI_PWR_ON	1
8279357Smsmith
8378915Smsmith/*
8478915Smsmith * A relationship between a power resource and a consumer.
8578915Smsmith */
8678915Smsmithstruct acpi_powerreference {
8778915Smsmith    struct acpi_powerconsumer	*ar_consumer;
8878915Smsmith    struct acpi_powerresource	*ar_resource;
8978915Smsmith    TAILQ_ENTRY(acpi_powerreference) ar_rlink;	/* link on resource list */
9078915Smsmith    TAILQ_ENTRY(acpi_powerreference) ar_clink;	/* link on consumer */
9178915Smsmith};
9278915Smsmith
9378915Smsmith/*
9478915Smsmith * A power-managed device.
9578915Smsmith */
9678915Smsmithstruct acpi_powerconsumer {
9778915Smsmith    ACPI_HANDLE		ac_consumer;		/* device which is powered */
9878915Smsmith    int			ac_state;
9978915Smsmith    TAILQ_ENTRY(acpi_powerconsumer) ac_link;
10078915Smsmith    TAILQ_HEAD(,acpi_powerreference) ac_references;
10178915Smsmith};
10278915Smsmith
10378915Smsmith/*
10478915Smsmith * A power resource.
10578915Smsmith */
10678915Smsmithstruct acpi_powerresource {
10778915Smsmith    TAILQ_ENTRY(acpi_powerresource) ap_link;
10878915Smsmith    TAILQ_HEAD(,acpi_powerreference) ap_references;
10978915Smsmith    ACPI_HANDLE		ap_resource;		/* the resource's handle */
11078915Smsmith    ACPI_INTEGER	ap_systemlevel;
11178915Smsmith    ACPI_INTEGER	ap_order;
11278915Smsmith};
11378915Smsmith
11489054Smsmithstatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)	acpi_powerresources;
11589054Smsmithstatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer)	acpi_powerconsumers;
11678915Smsmith
11778915Smsmithstatic ACPI_STATUS		acpi_pwr_register_consumer(ACPI_HANDLE consumer);
11878915Smsmithstatic ACPI_STATUS		acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
11978915Smsmithstatic ACPI_STATUS		acpi_pwr_register_resource(ACPI_HANDLE res);
12078915Smsmithstatic ACPI_STATUS		acpi_pwr_deregister_resource(ACPI_HANDLE res);
12178915Smsmithstatic void			acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg);
12278915Smsmithstatic ACPI_STATUS		acpi_pwr_switch_power(void);
12378915Smsmithstatic struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res);
12478915Smsmithstatic struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer);
12578915Smsmith
12678915Smsmith/*
12778915Smsmith * Initialise our lists.
12878915Smsmith */
12978915Smsmithstatic void
13078915Smsmithacpi_pwr_init(void *junk)
13178915Smsmith{
13278915Smsmith    TAILQ_INIT(&acpi_powerresources);
13378915Smsmith    TAILQ_INIT(&acpi_powerconsumers);
13478915Smsmith}
13578915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL);
13678915Smsmith
13778915Smsmith/*
13878915Smsmith * Register a power resource.
13978915Smsmith *
14078915Smsmith * It's OK to call this if we already know about the resource.
14178915Smsmith */
14278915Smsmithstatic ACPI_STATUS
14378915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res)
14478915Smsmith{
14578915Smsmith    ACPI_STATUS			status;
14678915Smsmith    ACPI_BUFFER			buf;
14779493Smsmith    ACPI_OBJECT			*obj;
14878915Smsmith    struct acpi_powerresource	*rp, *srp;
14978915Smsmith
15096926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
15178915Smsmith
15278915Smsmith    rp = NULL;
15391125Smsmith    buf.Pointer = NULL;
15478915Smsmith
15578915Smsmith    /* look to see if we know about this resource */
15678915Smsmith    if (acpi_pwr_find_resource(res) != NULL)
15778915Smsmith	return_ACPI_STATUS(AE_OK);		/* already know about it */
15878915Smsmith
15978915Smsmith    /* allocate a new resource */
16078915Smsmith    if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
16178915Smsmith	status = AE_NO_MEMORY;
16278915Smsmith	goto out;
16378915Smsmith    }
16478915Smsmith    TAILQ_INIT(&rp->ap_references);
16578915Smsmith    rp->ap_resource = res;
16678915Smsmith
16778915Smsmith    /* get the Power Resource object */
16891125Smsmith    buf.Length = ACPI_ALLOCATE_BUFFER;
16991125Smsmith    if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
17082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
17178915Smsmith	goto out;
17278915Smsmith    }
17378915Smsmith    obj = buf.Pointer;
17479493Smsmith    if (obj->Type != ACPI_TYPE_POWER) {
17582372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res)));
17679493Smsmith	status = AE_TYPE;
17779493Smsmith	goto out;
17878915Smsmith    }
17979493Smsmith    rp->ap_systemlevel = obj->PowerResource.SystemLevel;
18079493Smsmith    rp->ap_order = obj->PowerResource.ResourceOrder;
18178915Smsmith
18278915Smsmith    /* sort the resource into the list */
18378915Smsmith    status = AE_OK;
18478915Smsmith    srp = TAILQ_FIRST(&acpi_powerresources);
18578915Smsmith    if ((srp == NULL) || (rp->ap_order < srp->ap_order)) {
18678915Smsmith	TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
18779357Smsmith	goto done;
18878915Smsmith    }
18978915Smsmith    TAILQ_FOREACH(srp, &acpi_powerresources, ap_link)
19078915Smsmith	if (rp->ap_order < srp->ap_order) {
19178915Smsmith	    TAILQ_INSERT_BEFORE(srp, rp, ap_link);
19279357Smsmith	    goto done;
19378915Smsmith	}
19478915Smsmith    TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
19579357Smsmith
19679357Smsmith done:
19782372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res)));
19878915Smsmith out:
19991125Smsmith    if (buf.Pointer != NULL)
20091125Smsmith	AcpiOsFree(buf.Pointer);
20191125Smsmith    if (ACPI_FAILURE(status) && (rp != NULL))
20278915Smsmith	free(rp, M_ACPIPWR);
20378915Smsmith    return_ACPI_STATUS(status);
20478915Smsmith}
20578915Smsmith
20678915Smsmith/*
20778915Smsmith * Deregister a power resource.
20878915Smsmith */
20978915Smsmithstatic ACPI_STATUS
21078915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res)
21178915Smsmith{
21278915Smsmith    struct acpi_powerresource	*rp;
21378915Smsmith
21496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
21578915Smsmith
21678915Smsmith    rp = NULL;
21778915Smsmith
21878915Smsmith    /* find the resource */
21978915Smsmith    if ((rp = acpi_pwr_find_resource(res)) == NULL)
22078915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
22178915Smsmith
22278915Smsmith    /* check that there are no consumers referencing this resource */
22378915Smsmith    if (TAILQ_FIRST(&rp->ap_references) != NULL)
22478915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
22578915Smsmith
22678915Smsmith    /* pull it off the list and free it */
22778915Smsmith    TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
22878915Smsmith    free(rp, M_ACPIPWR);
22978915Smsmith
23082372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res)));
23178915Smsmith
23278915Smsmith    return_ACPI_STATUS(AE_OK);
23378915Smsmith}
23478915Smsmith
23578915Smsmith/*
23678915Smsmith * Register a power consumer.
23778915Smsmith *
23878915Smsmith * It's OK to call this if we already know about the consumer.
23978915Smsmith */
24078915Smsmithstatic ACPI_STATUS
24178915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer)
24278915Smsmith{
24378915Smsmith    struct acpi_powerconsumer	*pc;
24478915Smsmith
24596926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
24678915Smsmith
24778915Smsmith    /* check to see whether we know about this consumer already */
24878915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) != NULL)
24978915Smsmith	return_ACPI_STATUS(AE_OK);
25078915Smsmith
25178915Smsmith    /* allocate a new power consumer */
25278915Smsmith    if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL)
25378915Smsmith	return_ACPI_STATUS(AE_NO_MEMORY);
25478915Smsmith    TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
25578915Smsmith    TAILQ_INIT(&pc->ac_references);
25678915Smsmith    pc->ac_consumer = consumer;
25778915Smsmith
25878915Smsmith    pc->ac_state = ACPI_STATE_UNKNOWN;	/* XXX we should try to find its current state */
25978915Smsmith
26082372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer)));
26178915Smsmith
26278915Smsmith    return_ACPI_STATUS(AE_OK);
26378915Smsmith}
26478915Smsmith
26578915Smsmith/*
26678915Smsmith * Deregister a power consumer.
26778915Smsmith *
26878915Smsmith * This should only be done once the consumer has been powered off.
26978915Smsmith * (XXX is this correct?  Check once implemented)
27078915Smsmith */
27178915Smsmithstatic ACPI_STATUS
27278915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
27378915Smsmith{
27478915Smsmith    struct acpi_powerconsumer	*pc;
27578915Smsmith
27696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
27778915Smsmith
27878915Smsmith    /* find the consumer */
27978915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
28078915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
28178915Smsmith
28278915Smsmith    /* make sure the consumer's not referencing anything right now */
28378915Smsmith    if (TAILQ_FIRST(&pc->ac_references) != NULL)
28478915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
28578915Smsmith
28678915Smsmith    /* pull the consumer off the list and free it */
28778915Smsmith    TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
28878915Smsmith
28982372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer)));
29078915Smsmith
29178915Smsmith    return_ACPI_STATUS(AE_OK);
29278915Smsmith}
29378915Smsmith
29478915Smsmith/*
29578915Smsmith * Set a power consumer to a particular power state.
29678915Smsmith */
29778915SmsmithACPI_STATUS
29878915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
29978915Smsmith{
30078915Smsmith    struct acpi_powerconsumer	*pc;
30178915Smsmith    struct acpi_powerreference	*pr;
30282084Siwasaki    ACPI_HANDLE			method_handle, reslist_handle, pr0_handle;
30378915Smsmith    ACPI_BUFFER			reslist_buffer;
30478915Smsmith    ACPI_OBJECT			*reslist_object;
30578915Smsmith    ACPI_STATUS			status;
30678915Smsmith    char			*method_name, *reslist_name;
30778915Smsmith    int				res_changed;
30878915Smsmith
30996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
31078915Smsmith
31178915Smsmith    /* find the consumer */
31278915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
31391125Smsmith	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
31478915Smsmith	    return_ACPI_STATUS(status);
31578915Smsmith	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
31678915Smsmith	    return_ACPI_STATUS(AE_ERROR);	/* something very wrong */
31778915Smsmith	}
31878915Smsmith    }
31978915Smsmith
32078915Smsmith    /* check for valid transitions */
32178915Smsmith    if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0))
32278915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);	/* can only go to D0 from D3 */
32378915Smsmith
32478915Smsmith    /* find transition mechanism(s) */
32578915Smsmith    switch(state) {
32678915Smsmith    case ACPI_STATE_D0:
32778915Smsmith	method_name = "_PS0";
32878915Smsmith	reslist_name = "_PR0";
32978915Smsmith	break;
33078915Smsmith    case ACPI_STATE_D1:
33178915Smsmith	method_name = "_PS1";
33278915Smsmith	reslist_name = "_PR1";
33378915Smsmith	break;
33478915Smsmith    case ACPI_STATE_D2:
33578915Smsmith	method_name = "_PS2";
33678915Smsmith	reslist_name = "_PR2";
33778915Smsmith	break;
33878915Smsmith    case ACPI_STATE_D3:
33978915Smsmith	method_name = "_PS3";
34078915Smsmith	reslist_name = "_PR3";
34178915Smsmith	break;
34278915Smsmith    default:
34378915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
34478915Smsmith    }
34582372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
34682372Smsmith		      acpi_name(consumer), pc->ac_state, state));
34778915Smsmith
34878915Smsmith    /*
34978915Smsmith     * Verify that this state is supported, ie. one of method or
35078915Smsmith     * reslist must be present.  We need to do this before we go
35178915Smsmith     * dereferencing resources (since we might be trying to go to
35278915Smsmith     * a state we don't support).
35378915Smsmith     *
35478915Smsmith     * Note that if any states are supported, the device has to
35578915Smsmith     * support D0 and D3.  It's never an error to try to go to
35678915Smsmith     * D0.
35778915Smsmith     */
35891125Smsmith    reslist_buffer.Pointer = NULL;
35982084Siwasaki    reslist_object = NULL;
36091125Smsmith    if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
36178915Smsmith	method_handle = NULL;
36291125Smsmith    if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
36378915Smsmith	reslist_handle = NULL;
36478915Smsmith    if ((reslist_handle == NULL) && (method_handle == NULL)) {
36578915Smsmith	if (state == ACPI_STATE_D0) {
36678915Smsmith	    pc->ac_state = ACPI_STATE_D0;
36778915Smsmith	    return_ACPI_STATUS(AE_OK);
36878915Smsmith	}
36982084Siwasaki	if (state != ACPI_STATE_D3) {
37082084Siwasaki	    goto bad;
37182084Siwasaki	}
37282084Siwasaki
37382084Siwasaki	/* turn off the resources listed in _PR0 to go to D3. */
37491125Smsmith	if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
37582084Siwasaki	    goto bad;
37682084Siwasaki	}
37791125Smsmith	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
37891125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) {
37982084Siwasaki	    goto bad;
38082084Siwasaki	}
38182084Siwasaki	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
38282084Siwasaki	if ((reslist_object->Type != ACPI_TYPE_PACKAGE) ||
38382084Siwasaki	    (reslist_object->Package.Count == 0)) {
38482084Siwasaki	    goto bad;
38582084Siwasaki	}
38691125Smsmith	AcpiOsFree(reslist_buffer.Pointer);
38791125Smsmith	reslist_buffer.Pointer = NULL;
38891125Smsmith	reslist_object = NULL;
38978915Smsmith    }
39078915Smsmith
39178915Smsmith    /*
39278915Smsmith     * Check that we can actually fetch the list of power resources
39378915Smsmith     */
39478915Smsmith    if (reslist_handle != NULL) {
39591125Smsmith	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
39691125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) {
39782372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n",
39882372Smsmith			      acpi_name(reslist_handle)));
39991125Smsmith	    goto out;
40078915Smsmith	}
40178915Smsmith	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
40278915Smsmith	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
40382372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
40482372Smsmith			      reslist_object->Type));
40591125Smsmith	    status = AE_TYPE;
40691125Smsmith	    goto out;
40778915Smsmith	}
40878915Smsmith    }
40978915Smsmith
41078915Smsmith    /*
41178915Smsmith     * Now we are ready to switch, so  kill off any current power resource references.
41278915Smsmith     */
41378915Smsmith    res_changed = 0;
41479357Smsmith    while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
41579357Smsmith	res_changed = 1;
41682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource)));
41778915Smsmith	TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
41879357Smsmith	TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
41979357Smsmith	free(pr, M_ACPIPWR);
42078915Smsmith    }
42178915Smsmith
42278915Smsmith    /*
42378915Smsmith     * Add new power resource references, if we have any.  Traverse the
42478915Smsmith     * package that we got from evaluating reslist_handle, and look up each
42578915Smsmith     * of the resources that are referenced.
42678915Smsmith     */
42778915Smsmith    if (reslist_object != NULL) {
42882372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
42982372Smsmith			  reslist_object->Package.Count));
43078915Smsmith	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc);
43178915Smsmith	res_changed = 1;
43278915Smsmith    }
43378915Smsmith
43478915Smsmith    /*
43578915Smsmith     * If we changed anything in the resource list, we need to run a switch
43678915Smsmith     * pass now.
43778915Smsmith     */
43891125Smsmith    if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
43982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n",
44082372Smsmith			  acpi_name(consumer), state));
44191125Smsmith	goto out;		/* XXX is this appropriate?  Should we return to previous state? */
44278915Smsmith    }
44378915Smsmith
44478915Smsmith    /* invoke power state switch method (if present) */
44578915Smsmith    if (method_handle != NULL) {
44682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n",
44782372Smsmith			  acpi_name(method_handle)));
44891125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) {
44991125Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
45091125Smsmith				  AcpiFormatException(status)));
45191125Smsmith		pc->ac_state = ACPI_STATE_UNKNOWN;
45291125Smsmith		goto out;	/* XXX Should we return to previous state? */
45391125Smsmith	}
45478915Smsmith    }
45591125Smsmith
45678915Smsmith    /* transition was successful */
45778915Smsmith    pc->ac_state = state;
45878915Smsmith    return_ACPI_STATUS(AE_OK);
45982084Siwasaki
46082084Siwasaki bad:
46182372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n",
46282372Smsmith		      state));
46391125Smsmith    status = AE_BAD_PARAMETER;
46491125Smsmith
46591125Smsmith out:
46691125Smsmith    if (reslist_buffer.Pointer != NULL)
46791125Smsmith	AcpiOsFree(reslist_buffer.Pointer);
46891125Smsmith    return_ACPI_STATUS(status);
46978915Smsmith}
47078915Smsmith
47178915Smsmith/*
47278915Smsmith * Called to create a reference between a power consumer and a power resource
47378915Smsmith * identified in the object.
47478915Smsmith */
47578915Smsmithstatic void
47678915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
47778915Smsmith{
47878915Smsmith    struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
47979357Smsmith    struct acpi_powerreference	*pr;
48079357Smsmith    struct acpi_powerresource	*rp;
48179357Smsmith    ACPI_HANDLE			res;
48279357Smsmith    ACPI_STATUS			status;
48378915Smsmith
48496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
48578915Smsmith
48679357Smsmith    /* check the object type */
487102470Siwasaki    switch (obj->Type) {
488102470Siwasaki    case ACPI_TYPE_ANY:
489102470Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
490102470Siwasaki			  acpi_name(pc->ac_consumer), acpi_name(obj->Reference.Handle)));
491102470Siwasaki
492102470Siwasaki	res = obj->Reference.Handle;
493102470Siwasaki	break;
494102470Siwasaki
495102470Siwasaki    case ACPI_TYPE_STRING:
496102470Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
497102470Siwasaki			  acpi_name(pc->ac_consumer), obj->String.Pointer));
498102470Siwasaki
499102470Siwasaki	/* get the handle of the resource */
500102470Siwasaki	if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) {
501102470Siwasaki	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n",
502102470Siwasaki			      obj->String.Pointer));
503102470Siwasaki	    return_VOID;
504102470Siwasaki	}
505102470Siwasaki	break;
506102470Siwasaki
507102470Siwasaki    default:
50882372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n",
50982372Smsmith			  obj->Type));
51079357Smsmith	return_VOID;
51179357Smsmith    }
51278915Smsmith
51379357Smsmith    /* create/look up the resource */
51479357Smsmith    if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
51582372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n",
51682372Smsmith			  obj->String.Pointer, AcpiFormatException(status)));
51779357Smsmith	return_VOID;
51879357Smsmith    }
51979357Smsmith    if ((rp = acpi_pwr_find_resource(res)) == NULL) {
52082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
52179357Smsmith	return_VOID;
52279357Smsmith    }
52382372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource)));
52479357Smsmith
52579357Smsmith    /* create a reference between the consumer and resource */
52679357Smsmith    if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
52782372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n"));
52879357Smsmith	return_VOID;
52979357Smsmith    }
53079357Smsmith    pr->ar_consumer = pc;
53179357Smsmith    pr->ar_resource = rp;
53279357Smsmith    TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
53379357Smsmith    TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
53479357Smsmith
53578915Smsmith    return_VOID;
53678915Smsmith}
53778915Smsmith
53878915Smsmith
53978915Smsmith/*
54078915Smsmith * Switch power resources to conform to the desired state.
54178915Smsmith *
54278915Smsmith * Consumers may have modified the power resource list in an arbitrary
54378915Smsmith * fashion; we sweep it in sequence order.
54478915Smsmith */
54578915Smsmithstatic ACPI_STATUS
54678915Smsmithacpi_pwr_switch_power(void)
54778915Smsmith{
54878915Smsmith    struct acpi_powerresource	*rp;
54978915Smsmith    ACPI_STATUS			status;
55078915Smsmith    int				cur;
55178915Smsmith
55296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
55378915Smsmith
55478915Smsmith    /*
55578915Smsmith     * Sweep the list forwards turning things on.
55678915Smsmith     */
55778915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
55879357Smsmith	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
55982372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n",
56082372Smsmith			      acpi_name(rp->ap_resource)));
56179357Smsmith	    continue;
56279357Smsmith	}
56378915Smsmith
56478915Smsmith	/* we could cache this if we trusted it not to change under us */
56591125Smsmith	if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
56682372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
56782372Smsmith			      acpi_name(rp->ap_resource), status));
56878915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
56978915Smsmith	}
57078915Smsmith
57178915Smsmith	/*
57278915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
57378915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
57478915Smsmith	 * help much.
57578915Smsmith	 */
57679357Smsmith	if (cur != ACPI_PWR_ON) {
57779357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) {
57882372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n",
57982372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
58079357Smsmith	    } else {
58182372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource)));
58279357Smsmith	    }
58379357Smsmith	} else {
58482372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource)));
58579357Smsmith	}
58678915Smsmith    }
58778915Smsmith
58878915Smsmith    /*
58978915Smsmith     * Sweep the list backwards turning things off.
59078915Smsmith     */
59178915Smsmith    TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) {
59279357Smsmith	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
59382372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n",
59482372Smsmith			      acpi_name(rp->ap_resource)));
59579357Smsmith	    continue;
59679357Smsmith	}
59778915Smsmith
59878915Smsmith	/* we could cache this if we trusted it not to change under us */
59991125Smsmith	if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
60082372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
60182372Smsmith			      acpi_name(rp->ap_resource), status));
60278915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
60378915Smsmith	}
60478915Smsmith
60578915Smsmith	/*
60678915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
60778915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
60878915Smsmith	 * help much.
60978915Smsmith	 */
61079357Smsmith	if (cur != ACPI_PWR_OFF) {
61179357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) {
61282372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n",
61382372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
61479357Smsmith	    } else {
61582372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource)));
61679357Smsmith	    }
61779357Smsmith	} else {
61882372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource)));
61979357Smsmith	}
62078915Smsmith    }
62178915Smsmith    return_ACPI_STATUS(AE_OK);
62278915Smsmith}
62378915Smsmith
62478915Smsmith/*
62578915Smsmith * Find a power resource's control structure.
62678915Smsmith */
62778915Smsmithstatic struct acpi_powerresource *
62878915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res)
62978915Smsmith{
63078915Smsmith    struct acpi_powerresource	*rp;
63178915Smsmith
63296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
63378915Smsmith
63478915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link)
63578915Smsmith	if (rp->ap_resource == res)
63678915Smsmith	    break;
63784445Sdfr    return_PTR(rp);
63878915Smsmith}
63978915Smsmith
64078915Smsmith/*
64178915Smsmith * Find a power consumer's control structure.
64278915Smsmith */
64378915Smsmithstatic struct acpi_powerconsumer *
64478915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer)
64578915Smsmith{
64678915Smsmith    struct acpi_powerconsumer	*pc;
64778915Smsmith
64896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
64978915Smsmith
65078915Smsmith    TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link)
65178915Smsmith	if (pc->ac_consumer == consumer)
65278915Smsmith	    break;
65384445Sdfr    return_PTR(pc);
65478915Smsmith}
65578915Smsmith
656