acpi_powerres.c revision 91125
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 *	$FreeBSD: head/sys/dev/acpica/acpi_powerres.c 91125 2002-02-23 05:28:22Z msmith $
2778915Smsmith */
2878915Smsmith
2978915Smsmith#include "opt_acpi.h"		/* XXX trim includes */
3078915Smsmith#include <sys/param.h>
3178915Smsmith#include <sys/kernel.h>
3278915Smsmith#include <sys/proc.h>
3378915Smsmith#include <sys/lock.h>
3478915Smsmith#include <sys/malloc.h>
3578915Smsmith#include <sys/mutex.h>
3678915Smsmith#include <sys/bus.h>
3778915Smsmith#include <sys/conf.h>
3878915Smsmith#include <sys/ioccom.h>
3978915Smsmith#include <sys/reboot.h>
4078915Smsmith#include <sys/sysctl.h>
4178915Smsmith#include <sys/systm.h>
4278915Smsmith#include <sys/ctype.h>
4378915Smsmith
4478915Smsmith#include <machine/clock.h>
4578915Smsmith
4678915Smsmith#include <machine/resource.h>
4778915Smsmith
4878915Smsmith#include "acpi.h"
4978915Smsmith
5078915Smsmith#include <dev/acpica/acpivar.h>
5178915Smsmith#include <dev/acpica/acpiio.h>
5278915Smsmith
5378915Smsmith/*
5478915Smsmith * ACPI power resource management.
5578915Smsmith *
5678915Smsmith * Power resource behaviour is slightly complicated by the fact that
5778915Smsmith * a single power resource may provide power for more than one device.
5878915Smsmith * Thus, we must track the device(s) being powered by a given power
5978915Smsmith * resource, and only deactivate it when there are no powered devices.
6078915Smsmith *
6178915Smsmith * Note that this only manages resources for known devices.  There is an
6278915Smsmith * ugly case where we may turn of power to a device which is in use because
6378915Smsmith * we don't know that it depends on a given resource.  We should perhaps
6478915Smsmith * try to be smarter about this, but a more complete solution would involve
6578915Smsmith * scanning all of the ACPI namespace to find devices we're not currently
6678915Smsmith * aware of, and this raises questions about whether they should be left
6778915Smsmith * on, turned off, etc.
6878915Smsmith *
6978915Smsmith * XXX locking
7078915Smsmith */
7178915Smsmith
7278915SmsmithMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
7378915Smsmith
7478915Smsmith/*
7578915Smsmith * Hooks for the ACPI CA debugging infrastructure
7678915Smsmith */
7778993Smsmith#define _COMPONENT	ACPI_POWER
7891125SmsmithACPI_MODULE_NAME("POWERRES")
7978915Smsmith
8079357Smsmith/* return values from _STA on a power resource */
8179357Smsmith#define ACPI_PWR_OFF	0
8279357Smsmith#define ACPI_PWR_ON	1
8379357Smsmith
8478915Smsmith/*
8578915Smsmith * A relationship between a power resource and a consumer.
8678915Smsmith */
8778915Smsmithstruct acpi_powerreference {
8878915Smsmith    struct acpi_powerconsumer	*ar_consumer;
8978915Smsmith    struct acpi_powerresource	*ar_resource;
9078915Smsmith    TAILQ_ENTRY(acpi_powerreference) ar_rlink;	/* link on resource list */
9178915Smsmith    TAILQ_ENTRY(acpi_powerreference) ar_clink;	/* link on consumer */
9278915Smsmith};
9378915Smsmith
9478915Smsmith/*
9578915Smsmith * A power-managed device.
9678915Smsmith */
9778915Smsmithstruct acpi_powerconsumer {
9878915Smsmith    ACPI_HANDLE		ac_consumer;		/* device which is powered */
9978915Smsmith    int			ac_state;
10078915Smsmith    TAILQ_ENTRY(acpi_powerconsumer) ac_link;
10178915Smsmith    TAILQ_HEAD(,acpi_powerreference) ac_references;
10278915Smsmith};
10378915Smsmith
10478915Smsmith/*
10578915Smsmith * A power resource.
10678915Smsmith */
10778915Smsmithstruct acpi_powerresource {
10878915Smsmith    TAILQ_ENTRY(acpi_powerresource) ap_link;
10978915Smsmith    TAILQ_HEAD(,acpi_powerreference) ap_references;
11078915Smsmith    ACPI_HANDLE		ap_resource;		/* the resource's handle */
11178915Smsmith    ACPI_INTEGER	ap_systemlevel;
11278915Smsmith    ACPI_INTEGER	ap_order;
11378915Smsmith};
11478915Smsmith
11589054Smsmithstatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)	acpi_powerresources;
11689054Smsmithstatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer)	acpi_powerconsumers;
11778915Smsmith
11878915Smsmithstatic ACPI_STATUS		acpi_pwr_register_consumer(ACPI_HANDLE consumer);
11978915Smsmithstatic ACPI_STATUS		acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
12078915Smsmithstatic ACPI_STATUS		acpi_pwr_register_resource(ACPI_HANDLE res);
12178915Smsmithstatic ACPI_STATUS		acpi_pwr_deregister_resource(ACPI_HANDLE res);
12278915Smsmithstatic void			acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg);
12378915Smsmithstatic ACPI_STATUS		acpi_pwr_switch_power(void);
12478915Smsmithstatic struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res);
12578915Smsmithstatic struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer);
12678915Smsmith
12778915Smsmith/*
12878915Smsmith * Initialise our lists.
12978915Smsmith */
13078915Smsmithstatic void
13178915Smsmithacpi_pwr_init(void *junk)
13278915Smsmith{
13378915Smsmith    TAILQ_INIT(&acpi_powerresources);
13478915Smsmith    TAILQ_INIT(&acpi_powerconsumers);
13578915Smsmith}
13678915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL);
13778915Smsmith
13878915Smsmith/*
13978915Smsmith * Register a power resource.
14078915Smsmith *
14178915Smsmith * It's OK to call this if we already know about the resource.
14278915Smsmith */
14378915Smsmithstatic ACPI_STATUS
14478915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res)
14578915Smsmith{
14678915Smsmith    ACPI_STATUS			status;
14778915Smsmith    ACPI_BUFFER			buf;
14879493Smsmith    ACPI_OBJECT			*obj;
14978915Smsmith    struct acpi_powerresource	*rp, *srp;
15078915Smsmith
15191125Smsmith    ACPI_FUNCTION_TRACE(__func__);
15278915Smsmith
15378915Smsmith    rp = NULL;
15491125Smsmith    buf.Pointer = NULL;
15578915Smsmith
15678915Smsmith    /* look to see if we know about this resource */
15778915Smsmith    if (acpi_pwr_find_resource(res) != NULL)
15878915Smsmith	return_ACPI_STATUS(AE_OK);		/* already know about it */
15978915Smsmith
16078915Smsmith    /* allocate a new resource */
16178915Smsmith    if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
16278915Smsmith	status = AE_NO_MEMORY;
16378915Smsmith	goto out;
16478915Smsmith    }
16578915Smsmith    TAILQ_INIT(&rp->ap_references);
16678915Smsmith    rp->ap_resource = res;
16778915Smsmith
16878915Smsmith    /* get the Power Resource object */
16991125Smsmith    buf.Length = ACPI_ALLOCATE_BUFFER;
17091125Smsmith    if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
17182372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
17278915Smsmith	goto out;
17378915Smsmith    }
17478915Smsmith    obj = buf.Pointer;
17579493Smsmith    if (obj->Type != ACPI_TYPE_POWER) {
17682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res)));
17779493Smsmith	status = AE_TYPE;
17879493Smsmith	goto out;
17978915Smsmith    }
18079493Smsmith    rp->ap_systemlevel = obj->PowerResource.SystemLevel;
18179493Smsmith    rp->ap_order = obj->PowerResource.ResourceOrder;
18278915Smsmith
18378915Smsmith    /* sort the resource into the list */
18478915Smsmith    status = AE_OK;
18578915Smsmith    srp = TAILQ_FIRST(&acpi_powerresources);
18678915Smsmith    if ((srp == NULL) || (rp->ap_order < srp->ap_order)) {
18778915Smsmith	TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
18879357Smsmith	goto done;
18978915Smsmith    }
19078915Smsmith    TAILQ_FOREACH(srp, &acpi_powerresources, ap_link)
19178915Smsmith	if (rp->ap_order < srp->ap_order) {
19278915Smsmith	    TAILQ_INSERT_BEFORE(srp, rp, ap_link);
19379357Smsmith	    goto done;
19478915Smsmith	}
19578915Smsmith    TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
19679357Smsmith
19779357Smsmith done:
19882372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res)));
19978915Smsmith out:
20091125Smsmith    if (buf.Pointer != NULL)
20191125Smsmith	AcpiOsFree(buf.Pointer);
20291125Smsmith    if (ACPI_FAILURE(status) && (rp != NULL))
20378915Smsmith	free(rp, M_ACPIPWR);
20478915Smsmith    return_ACPI_STATUS(status);
20578915Smsmith}
20678915Smsmith
20778915Smsmith/*
20878915Smsmith * Deregister a power resource.
20978915Smsmith */
21078915Smsmithstatic ACPI_STATUS
21178915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res)
21278915Smsmith{
21378915Smsmith    struct acpi_powerresource	*rp;
21478915Smsmith
21591125Smsmith    ACPI_FUNCTION_TRACE(__func__);
21678915Smsmith
21778915Smsmith    rp = NULL;
21878915Smsmith
21978915Smsmith    /* find the resource */
22078915Smsmith    if ((rp = acpi_pwr_find_resource(res)) == NULL)
22178915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
22278915Smsmith
22378915Smsmith    /* check that there are no consumers referencing this resource */
22478915Smsmith    if (TAILQ_FIRST(&rp->ap_references) != NULL)
22578915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
22678915Smsmith
22778915Smsmith    /* pull it off the list and free it */
22878915Smsmith    TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
22978915Smsmith    free(rp, M_ACPIPWR);
23078915Smsmith
23182372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res)));
23278915Smsmith
23378915Smsmith    return_ACPI_STATUS(AE_OK);
23478915Smsmith}
23578915Smsmith
23678915Smsmith/*
23778915Smsmith * Register a power consumer.
23878915Smsmith *
23978915Smsmith * It's OK to call this if we already know about the consumer.
24078915Smsmith */
24178915Smsmithstatic ACPI_STATUS
24278915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer)
24378915Smsmith{
24478915Smsmith    struct acpi_powerconsumer	*pc;
24578915Smsmith
24691125Smsmith    ACPI_FUNCTION_TRACE(__func__);
24778915Smsmith
24878915Smsmith    /* check to see whether we know about this consumer already */
24978915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) != NULL)
25078915Smsmith	return_ACPI_STATUS(AE_OK);
25178915Smsmith
25278915Smsmith    /* allocate a new power consumer */
25378915Smsmith    if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL)
25478915Smsmith	return_ACPI_STATUS(AE_NO_MEMORY);
25578915Smsmith    TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
25678915Smsmith    TAILQ_INIT(&pc->ac_references);
25778915Smsmith    pc->ac_consumer = consumer;
25878915Smsmith
25978915Smsmith    pc->ac_state = ACPI_STATE_UNKNOWN;	/* XXX we should try to find its current state */
26078915Smsmith
26182372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer)));
26278915Smsmith
26378915Smsmith    return_ACPI_STATUS(AE_OK);
26478915Smsmith}
26578915Smsmith
26678915Smsmith/*
26778915Smsmith * Deregister a power consumer.
26878915Smsmith *
26978915Smsmith * This should only be done once the consumer has been powered off.
27078915Smsmith * (XXX is this correct?  Check once implemented)
27178915Smsmith */
27278915Smsmithstatic ACPI_STATUS
27378915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
27478915Smsmith{
27578915Smsmith    struct acpi_powerconsumer	*pc;
27678915Smsmith
27791125Smsmith    ACPI_FUNCTION_TRACE(__func__);
27878915Smsmith
27978915Smsmith    /* find the consumer */
28078915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
28178915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
28278915Smsmith
28378915Smsmith    /* make sure the consumer's not referencing anything right now */
28478915Smsmith    if (TAILQ_FIRST(&pc->ac_references) != NULL)
28578915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
28678915Smsmith
28778915Smsmith    /* pull the consumer off the list and free it */
28878915Smsmith    TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
28978915Smsmith
29082372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer)));
29178915Smsmith
29278915Smsmith    return_ACPI_STATUS(AE_OK);
29378915Smsmith}
29478915Smsmith
29578915Smsmith/*
29678915Smsmith * Set a power consumer to a particular power state.
29778915Smsmith */
29878915SmsmithACPI_STATUS
29978915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
30078915Smsmith{
30178915Smsmith    struct acpi_powerconsumer	*pc;
30278915Smsmith    struct acpi_powerreference	*pr;
30382084Siwasaki    ACPI_HANDLE			method_handle, reslist_handle, pr0_handle;
30478915Smsmith    ACPI_BUFFER			reslist_buffer;
30578915Smsmith    ACPI_OBJECT			*reslist_object;
30678915Smsmith    ACPI_STATUS			status;
30778915Smsmith    char			*method_name, *reslist_name;
30878915Smsmith    int				res_changed;
30978915Smsmith
31091125Smsmith    ACPI_FUNCTION_TRACE(__func__);
31178915Smsmith
31278915Smsmith    /* find the consumer */
31378915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
31491125Smsmith	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
31578915Smsmith	    return_ACPI_STATUS(status);
31678915Smsmith	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
31778915Smsmith	    return_ACPI_STATUS(AE_ERROR);	/* something very wrong */
31878915Smsmith	}
31978915Smsmith    }
32078915Smsmith
32178915Smsmith    /* check for valid transitions */
32278915Smsmith    if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0))
32378915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);	/* can only go to D0 from D3 */
32478915Smsmith
32578915Smsmith    /* find transition mechanism(s) */
32678915Smsmith    switch(state) {
32778915Smsmith    case ACPI_STATE_D0:
32878915Smsmith	method_name = "_PS0";
32978915Smsmith	reslist_name = "_PR0";
33078915Smsmith	break;
33178915Smsmith    case ACPI_STATE_D1:
33278915Smsmith	method_name = "_PS1";
33378915Smsmith	reslist_name = "_PR1";
33478915Smsmith	break;
33578915Smsmith    case ACPI_STATE_D2:
33678915Smsmith	method_name = "_PS2";
33778915Smsmith	reslist_name = "_PR2";
33878915Smsmith	break;
33978915Smsmith    case ACPI_STATE_D3:
34078915Smsmith	method_name = "_PS3";
34178915Smsmith	reslist_name = "_PR3";
34278915Smsmith	break;
34378915Smsmith    default:
34478915Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
34578915Smsmith    }
34682372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
34782372Smsmith		      acpi_name(consumer), pc->ac_state, state));
34878915Smsmith
34978915Smsmith    /*
35078915Smsmith     * Verify that this state is supported, ie. one of method or
35178915Smsmith     * reslist must be present.  We need to do this before we go
35278915Smsmith     * dereferencing resources (since we might be trying to go to
35378915Smsmith     * a state we don't support).
35478915Smsmith     *
35578915Smsmith     * Note that if any states are supported, the device has to
35678915Smsmith     * support D0 and D3.  It's never an error to try to go to
35778915Smsmith     * D0.
35878915Smsmith     */
35991125Smsmith    reslist_buffer.Pointer = NULL;
36082084Siwasaki    reslist_object = NULL;
36191125Smsmith    if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
36278915Smsmith	method_handle = NULL;
36391125Smsmith    if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
36478915Smsmith	reslist_handle = NULL;
36578915Smsmith    if ((reslist_handle == NULL) && (method_handle == NULL)) {
36678915Smsmith	if (state == ACPI_STATE_D0) {
36778915Smsmith	    pc->ac_state = ACPI_STATE_D0;
36878915Smsmith	    return_ACPI_STATUS(AE_OK);
36978915Smsmith	}
37082084Siwasaki	if (state != ACPI_STATE_D3) {
37182084Siwasaki	    goto bad;
37282084Siwasaki	}
37382084Siwasaki
37482084Siwasaki	/* turn off the resources listed in _PR0 to go to D3. */
37591125Smsmith	if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
37682084Siwasaki	    goto bad;
37782084Siwasaki	}
37891125Smsmith	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
37991125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) {
38082084Siwasaki	    goto bad;
38182084Siwasaki	}
38282084Siwasaki	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
38382084Siwasaki	if ((reslist_object->Type != ACPI_TYPE_PACKAGE) ||
38482084Siwasaki	    (reslist_object->Package.Count == 0)) {
38582084Siwasaki	    goto bad;
38682084Siwasaki	}
38791125Smsmith	AcpiOsFree(reslist_buffer.Pointer);
38891125Smsmith	reslist_buffer.Pointer = NULL;
38991125Smsmith	reslist_object = NULL;
39078915Smsmith    }
39178915Smsmith
39278915Smsmith    /*
39378915Smsmith     * Check that we can actually fetch the list of power resources
39478915Smsmith     */
39578915Smsmith    if (reslist_handle != NULL) {
39691125Smsmith	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
39791125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) {
39882372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n",
39982372Smsmith			      acpi_name(reslist_handle)));
40091125Smsmith	    goto out;
40178915Smsmith	}
40278915Smsmith	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
40378915Smsmith	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
40482372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
40582372Smsmith			      reslist_object->Type));
40691125Smsmith	    status = AE_TYPE;
40791125Smsmith	    goto out;
40878915Smsmith	}
40978915Smsmith    }
41078915Smsmith
41178915Smsmith    /*
41278915Smsmith     * Now we are ready to switch, so  kill off any current power resource references.
41378915Smsmith     */
41478915Smsmith    res_changed = 0;
41579357Smsmith    while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
41679357Smsmith	res_changed = 1;
41782372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource)));
41878915Smsmith	TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
41979357Smsmith	TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
42079357Smsmith	free(pr, M_ACPIPWR);
42178915Smsmith    }
42278915Smsmith
42378915Smsmith    /*
42478915Smsmith     * Add new power resource references, if we have any.  Traverse the
42578915Smsmith     * package that we got from evaluating reslist_handle, and look up each
42678915Smsmith     * of the resources that are referenced.
42778915Smsmith     */
42878915Smsmith    if (reslist_object != NULL) {
42982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
43082372Smsmith			  reslist_object->Package.Count));
43178915Smsmith	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc);
43278915Smsmith	res_changed = 1;
43378915Smsmith    }
43478915Smsmith
43578915Smsmith    /*
43678915Smsmith     * If we changed anything in the resource list, we need to run a switch
43778915Smsmith     * pass now.
43878915Smsmith     */
43991125Smsmith    if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
44082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n",
44182372Smsmith			  acpi_name(consumer), state));
44291125Smsmith	goto out;		/* XXX is this appropriate?  Should we return to previous state? */
44378915Smsmith    }
44478915Smsmith
44578915Smsmith    /* invoke power state switch method (if present) */
44678915Smsmith    if (method_handle != NULL) {
44782372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n",
44882372Smsmith			  acpi_name(method_handle)));
44991125Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) {
45091125Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
45191125Smsmith				  AcpiFormatException(status)));
45291125Smsmith		pc->ac_state = ACPI_STATE_UNKNOWN;
45391125Smsmith		goto out;	/* XXX Should we return to previous state? */
45491125Smsmith	}
45578915Smsmith    }
45691125Smsmith
45778915Smsmith    /* transition was successful */
45878915Smsmith    pc->ac_state = state;
45978915Smsmith    return_ACPI_STATUS(AE_OK);
46082084Siwasaki
46182084Siwasaki bad:
46282372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n",
46382372Smsmith		      state));
46491125Smsmith    status = AE_BAD_PARAMETER;
46591125Smsmith
46691125Smsmith out:
46791125Smsmith    if (reslist_buffer.Pointer != NULL)
46891125Smsmith	AcpiOsFree(reslist_buffer.Pointer);
46991125Smsmith    return_ACPI_STATUS(status);
47078915Smsmith}
47178915Smsmith
47278915Smsmith/*
47378915Smsmith * Called to create a reference between a power consumer and a power resource
47478915Smsmith * identified in the object.
47578915Smsmith */
47678915Smsmithstatic void
47778915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
47878915Smsmith{
47978915Smsmith    struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
48079357Smsmith    struct acpi_powerreference	*pr;
48179357Smsmith    struct acpi_powerresource	*rp;
48279357Smsmith    ACPI_HANDLE			res;
48379357Smsmith    ACPI_STATUS			status;
48478915Smsmith
48591125Smsmith    ACPI_FUNCTION_TRACE(__func__);
48678915Smsmith
48779357Smsmith    /* check the object type */
48879357Smsmith    if (obj->Type != ACPI_TYPE_STRING) {
48982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n",
49082372Smsmith			  obj->Type));
49179357Smsmith	return_VOID;
49279357Smsmith    }
49378915Smsmith
49482372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
49582372Smsmith		      acpi_name(pc->ac_consumer), obj->String.Pointer));
49679357Smsmith
49779357Smsmith    /* get the handle of the resource */
49879357Smsmith    if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) {
49982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n",
50082372Smsmith			  obj->String.Pointer));
50179357Smsmith	return_VOID;
50279357Smsmith    }
50379357Smsmith
50479357Smsmith    /* create/look up the resource */
50579357Smsmith    if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
50682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n",
50782372Smsmith			  obj->String.Pointer, AcpiFormatException(status)));
50879357Smsmith	return_VOID;
50979357Smsmith    }
51079357Smsmith    if ((rp = acpi_pwr_find_resource(res)) == NULL) {
51182372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
51279357Smsmith	return_VOID;
51379357Smsmith    }
51482372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource)));
51579357Smsmith
51679357Smsmith    /* create a reference between the consumer and resource */
51779357Smsmith    if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
51882372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n"));
51979357Smsmith	return_VOID;
52079357Smsmith    }
52179357Smsmith    pr->ar_consumer = pc;
52279357Smsmith    pr->ar_resource = rp;
52379357Smsmith    TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
52479357Smsmith    TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
52579357Smsmith
52678915Smsmith    return_VOID;
52778915Smsmith}
52878915Smsmith
52978915Smsmith
53078915Smsmith/*
53178915Smsmith * Switch power resources to conform to the desired state.
53278915Smsmith *
53378915Smsmith * Consumers may have modified the power resource list in an arbitrary
53478915Smsmith * fashion; we sweep it in sequence order.
53578915Smsmith */
53678915Smsmithstatic ACPI_STATUS
53778915Smsmithacpi_pwr_switch_power(void)
53878915Smsmith{
53978915Smsmith    struct acpi_powerresource	*rp;
54078915Smsmith    ACPI_STATUS			status;
54178915Smsmith    int				cur;
54278915Smsmith
54391125Smsmith    ACPI_FUNCTION_TRACE(__func__);
54478915Smsmith
54578915Smsmith    /*
54678915Smsmith     * Sweep the list forwards turning things on.
54778915Smsmith     */
54878915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
54979357Smsmith	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
55082372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n",
55182372Smsmith			      acpi_name(rp->ap_resource)));
55279357Smsmith	    continue;
55379357Smsmith	}
55478915Smsmith
55578915Smsmith	/* we could cache this if we trusted it not to change under us */
55691125Smsmith	if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
55782372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
55882372Smsmith			      acpi_name(rp->ap_resource), status));
55978915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
56078915Smsmith	}
56178915Smsmith
56278915Smsmith	/*
56378915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
56478915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
56578915Smsmith	 * help much.
56678915Smsmith	 */
56779357Smsmith	if (cur != ACPI_PWR_ON) {
56879357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) {
56982372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n",
57082372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
57179357Smsmith	    } else {
57282372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource)));
57379357Smsmith	    }
57479357Smsmith	} else {
57582372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource)));
57679357Smsmith	}
57778915Smsmith    }
57878915Smsmith
57978915Smsmith    /*
58078915Smsmith     * Sweep the list backwards turning things off.
58178915Smsmith     */
58278915Smsmith    TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) {
58379357Smsmith	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
58482372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n",
58582372Smsmith			      acpi_name(rp->ap_resource)));
58679357Smsmith	    continue;
58779357Smsmith	}
58878915Smsmith
58978915Smsmith	/* we could cache this if we trusted it not to change under us */
59091125Smsmith	if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
59182372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
59282372Smsmith			      acpi_name(rp->ap_resource), status));
59378915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
59478915Smsmith	}
59578915Smsmith
59678915Smsmith	/*
59778915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
59878915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
59978915Smsmith	 * help much.
60078915Smsmith	 */
60179357Smsmith	if (cur != ACPI_PWR_OFF) {
60279357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) {
60382372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n",
60482372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
60579357Smsmith	    } else {
60682372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource)));
60779357Smsmith	    }
60879357Smsmith	} else {
60982372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource)));
61079357Smsmith	}
61178915Smsmith    }
61278915Smsmith    return_ACPI_STATUS(AE_OK);
61378915Smsmith}
61478915Smsmith
61578915Smsmith/*
61678915Smsmith * Find a power resource's control structure.
61778915Smsmith */
61878915Smsmithstatic struct acpi_powerresource *
61978915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res)
62078915Smsmith{
62178915Smsmith    struct acpi_powerresource	*rp;
62278915Smsmith
62391125Smsmith    ACPI_FUNCTION_TRACE(__func__);
62478915Smsmith
62578915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link)
62678915Smsmith	if (rp->ap_resource == res)
62778915Smsmith	    break;
62884445Sdfr    return_PTR(rp);
62978915Smsmith}
63078915Smsmith
63178915Smsmith/*
63278915Smsmith * Find a power consumer's control structure.
63378915Smsmith */
63478915Smsmithstatic struct acpi_powerconsumer *
63578915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer)
63678915Smsmith{
63778915Smsmith    struct acpi_powerconsumer	*pc;
63878915Smsmith
63991125Smsmith    ACPI_FUNCTION_TRACE(__func__);
64078915Smsmith
64178915Smsmith    TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link)
64278915Smsmith	if (pc->ac_consumer == consumer)
64378915Smsmith	    break;
64484445Sdfr    return_PTR(pc);
64578915Smsmith}
64678915Smsmith
647