acpi_powerres.c revision 82372
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 82372 2001-08-26 22:50:15Z 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
7878915SmsmithMODULE_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
11578915SmsmithTAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)	acpi_powerresources;
11678915SmsmithTAILQ_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
15178915Smsmith    FUNCTION_TRACE(__func__);
15278915Smsmith
15378915Smsmith    rp = NULL;
15478915Smsmith    obj = 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 */
16978915Smsmith    if ((status = acpi_EvaluateIntoBuffer(res, NULL, NULL, &buf)) != AE_OK) {
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:
19978915Smsmith    if (obj != NULL)
20078915Smsmith	AcpiOsFree(obj);
20178915Smsmith    if ((status != AE_OK) && (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
21478915Smsmith    FUNCTION_TRACE(__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
24578915Smsmith    FUNCTION_TRACE(__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
27678915Smsmith    FUNCTION_TRACE(__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
30978915Smsmith    FUNCTION_TRACE(__func__);
31078915Smsmith
31178915Smsmith    /* find the consumer */
31278915Smsmith    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
31378915Smsmith	if ((status = acpi_pwr_register_consumer(consumer)) != AE_OK)
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     */
35882084Siwasaki    reslist_object = NULL;
35978915Smsmith    if (AcpiGetHandle(consumer, method_name, &method_handle) != AE_OK)
36078915Smsmith	method_handle = NULL;
36178915Smsmith    if (AcpiGetHandle(consumer, reslist_name, &reslist_handle) != AE_OK)
36278915Smsmith	reslist_handle = NULL;
36378915Smsmith    if ((reslist_handle == NULL) && (method_handle == NULL)) {
36478915Smsmith	if (state == ACPI_STATE_D0) {
36578915Smsmith	    pc->ac_state = ACPI_STATE_D0;
36678915Smsmith	    return_ACPI_STATUS(AE_OK);
36778915Smsmith	}
36882084Siwasaki	if (state != ACPI_STATE_D3) {
36982084Siwasaki	    goto bad;
37082084Siwasaki	}
37182084Siwasaki
37282084Siwasaki	/* turn off the resources listed in _PR0 to go to D3. */
37382084Siwasaki	if (AcpiGetHandle(consumer, "_PR0", &pr0_handle) != AE_OK) {
37482084Siwasaki	    goto bad;
37582084Siwasaki	}
37682084Siwasaki	status = acpi_EvaluateIntoBuffer(pr0_handle, NULL, NULL, &reslist_buffer);
37782084Siwasaki	if (status != AE_OK) {
37882084Siwasaki	    goto bad;
37982084Siwasaki	}
38082084Siwasaki	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
38182084Siwasaki	if ((reslist_object->Type != ACPI_TYPE_PACKAGE) ||
38282084Siwasaki	    (reslist_object->Package.Count == 0)) {
38382084Siwasaki	    goto bad;
38482084Siwasaki	}
38582084Siwasaki	AcpiOsFree(reslist_object);
38678915Smsmith    }
38778915Smsmith
38878915Smsmith    /*
38978915Smsmith     * Check that we can actually fetch the list of power resources
39078915Smsmith     */
39178915Smsmith    if (reslist_handle != NULL) {
39278915Smsmith	if ((status = acpi_EvaluateIntoBuffer(reslist_handle, NULL, NULL, &reslist_buffer)) != AE_OK) {
39382372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n",
39482372Smsmith			      acpi_name(reslist_handle)));
39578915Smsmith	    return_ACPI_STATUS(status);
39678915Smsmith	}
39778915Smsmith	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
39878915Smsmith	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
39982372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
40082372Smsmith			      reslist_object->Type));
40178915Smsmith	    return_ACPI_STATUS(AE_TYPE);
40278915Smsmith	}
40378915Smsmith    } else {
40478915Smsmith	reslist_object = NULL;
40578915Smsmith    }
40678915Smsmith
40778915Smsmith    /*
40878915Smsmith     * Now we are ready to switch, so  kill off any current power resource references.
40978915Smsmith     */
41078915Smsmith    res_changed = 0;
41179357Smsmith    while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
41279357Smsmith	res_changed = 1;
41382372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource)));
41478915Smsmith	TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
41579357Smsmith	TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
41679357Smsmith	free(pr, M_ACPIPWR);
41778915Smsmith    }
41878915Smsmith
41978915Smsmith    /*
42078915Smsmith     * Add new power resource references, if we have any.  Traverse the
42178915Smsmith     * package that we got from evaluating reslist_handle, and look up each
42278915Smsmith     * of the resources that are referenced.
42378915Smsmith     */
42478915Smsmith    if (reslist_object != NULL) {
42582372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
42682372Smsmith			  reslist_object->Package.Count));
42778915Smsmith	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc);
42878915Smsmith	res_changed = 1;
42978915Smsmith    }
43078915Smsmith
43178915Smsmith    /*
43278915Smsmith     * If we changed anything in the resource list, we need to run a switch
43378915Smsmith     * pass now.
43478915Smsmith     */
43578915Smsmith    if ((status = acpi_pwr_switch_power()) != AE_OK) {
43682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n",
43782372Smsmith			  acpi_name(consumer), state));
43878915Smsmith	return_ACPI_STATUS(status);	/* XXX is this appropriate?  Should we return to previous state? */
43978915Smsmith    }
44078915Smsmith
44178915Smsmith    /* invoke power state switch method (if present) */
44278915Smsmith    if (method_handle != NULL) {
44382372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n",
44482372Smsmith			  acpi_name(method_handle)));
44578915Smsmith	if ((status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL)) != AE_OK)
44678915Smsmith	    pc->ac_state = ACPI_STATE_UNKNOWN;
44778915Smsmith	    return_ACPI_STATUS(status);	/* XXX is this appropriate?  Should we return to previous state? */
44878915Smsmith    }
44978915Smsmith
45078915Smsmith    /* transition was successful */
45178915Smsmith    pc->ac_state = state;
45278915Smsmith    return_ACPI_STATUS(AE_OK);
45382084Siwasaki
45482084Siwasaki bad:
45582372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n",
45682372Smsmith		      state));
45782084Siwasaki    if (reslist_object)
45882084Siwasaki	AcpiOsFree(reslist_object);
45982084Siwasaki    return_ACPI_STATUS(AE_BAD_PARAMETER);
46078915Smsmith}
46178915Smsmith
46278915Smsmith/*
46378915Smsmith * Called to create a reference between a power consumer and a power resource
46478915Smsmith * identified in the object.
46578915Smsmith */
46678915Smsmithstatic void
46778915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
46878915Smsmith{
46978915Smsmith    struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
47079357Smsmith    struct acpi_powerreference	*pr;
47179357Smsmith    struct acpi_powerresource	*rp;
47279357Smsmith    ACPI_HANDLE			res;
47379357Smsmith    ACPI_STATUS			status;
47478915Smsmith
47578915Smsmith    FUNCTION_TRACE(__func__);
47678915Smsmith
47779357Smsmith    /* check the object type */
47879357Smsmith    if (obj->Type != ACPI_TYPE_STRING) {
47982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n",
48082372Smsmith			  obj->Type));
48179357Smsmith	return_VOID;
48279357Smsmith    }
48378915Smsmith
48482372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
48582372Smsmith		      acpi_name(pc->ac_consumer), obj->String.Pointer));
48679357Smsmith
48779357Smsmith    /* get the handle of the resource */
48879357Smsmith    if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) {
48982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n",
49082372Smsmith			  obj->String.Pointer));
49179357Smsmith	return_VOID;
49279357Smsmith    }
49379357Smsmith
49479357Smsmith    /* create/look up the resource */
49579357Smsmith    if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
49682372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n",
49782372Smsmith			  obj->String.Pointer, AcpiFormatException(status)));
49879357Smsmith	return_VOID;
49979357Smsmith    }
50079357Smsmith    if ((rp = acpi_pwr_find_resource(res)) == NULL) {
50182372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
50279357Smsmith	return_VOID;
50379357Smsmith    }
50482372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource)));
50579357Smsmith
50679357Smsmith    /* create a reference between the consumer and resource */
50779357Smsmith    if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
50882372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n"));
50979357Smsmith	return_VOID;
51079357Smsmith    }
51179357Smsmith    pr->ar_consumer = pc;
51279357Smsmith    pr->ar_resource = rp;
51379357Smsmith    TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
51479357Smsmith    TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
51579357Smsmith
51678915Smsmith    return_VOID;
51778915Smsmith}
51878915Smsmith
51978915Smsmith
52078915Smsmith/*
52178915Smsmith * Switch power resources to conform to the desired state.
52278915Smsmith *
52378915Smsmith * Consumers may have modified the power resource list in an arbitrary
52478915Smsmith * fashion; we sweep it in sequence order.
52578915Smsmith */
52678915Smsmithstatic ACPI_STATUS
52778915Smsmithacpi_pwr_switch_power(void)
52878915Smsmith{
52978915Smsmith    struct acpi_powerresource	*rp;
53078915Smsmith    ACPI_STATUS			status;
53178915Smsmith    int				cur;
53278915Smsmith
53378915Smsmith    FUNCTION_TRACE(__func__);
53478915Smsmith
53578915Smsmith    /*
53678915Smsmith     * Sweep the list forwards turning things on.
53778915Smsmith     */
53878915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
53979357Smsmith	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
54082372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n",
54182372Smsmith			      acpi_name(rp->ap_resource)));
54279357Smsmith	    continue;
54379357Smsmith	}
54478915Smsmith
54578915Smsmith	/* we could cache this if we trusted it not to change under us */
54678915Smsmith	if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) {
54782372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
54882372Smsmith			      acpi_name(rp->ap_resource), status));
54978915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
55078915Smsmith	}
55178915Smsmith
55278915Smsmith	/*
55378915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
55478915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
55578915Smsmith	 * help much.
55678915Smsmith	 */
55779357Smsmith	if (cur != ACPI_PWR_ON) {
55879357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) {
55982372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n",
56082372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
56179357Smsmith	    } else {
56282372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource)));
56379357Smsmith	    }
56479357Smsmith	} else {
56582372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource)));
56679357Smsmith	}
56778915Smsmith    }
56878915Smsmith
56978915Smsmith    /*
57078915Smsmith     * Sweep the list backwards turning things off.
57178915Smsmith     */
57278915Smsmith    TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) {
57379357Smsmith	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
57482372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n",
57582372Smsmith			      acpi_name(rp->ap_resource)));
57679357Smsmith	    continue;
57779357Smsmith	}
57878915Smsmith
57978915Smsmith	/* we could cache this if we trusted it not to change under us */
58078915Smsmith	if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) {
58182372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
58282372Smsmith			      acpi_name(rp->ap_resource), status));
58378915Smsmith	    continue;	/* XXX is this correct?  Always switch if in doubt? */
58478915Smsmith	}
58578915Smsmith
58678915Smsmith	/*
58778915Smsmith	 * Switch if required.  Note that we ignore the result of the switch
58878915Smsmith	 * effort; we don't know what to do if it fails, so checking wouldn't
58978915Smsmith	 * help much.
59078915Smsmith	 */
59179357Smsmith	if (cur != ACPI_PWR_OFF) {
59279357Smsmith	    if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) {
59382372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n",
59482372Smsmith				  acpi_name(rp->ap_resource), AcpiFormatException(status)));
59579357Smsmith	    } else {
59682372Smsmith		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource)));
59779357Smsmith	    }
59879357Smsmith	} else {
59982372Smsmith	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource)));
60079357Smsmith	}
60178915Smsmith    }
60278915Smsmith    return_ACPI_STATUS(AE_OK);
60378915Smsmith}
60478915Smsmith
60578915Smsmith/*
60678915Smsmith * Find a power resource's control structure.
60778915Smsmith */
60878915Smsmithstatic struct acpi_powerresource *
60978915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res)
61078915Smsmith{
61178915Smsmith    struct acpi_powerresource	*rp;
61278915Smsmith
61378915Smsmith    FUNCTION_TRACE(__func__);
61478915Smsmith
61578915Smsmith    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link)
61678915Smsmith	if (rp->ap_resource == res)
61778915Smsmith	    break;
61878915Smsmith    return_VALUE(rp);
61978915Smsmith}
62078915Smsmith
62178915Smsmith/*
62278915Smsmith * Find a power consumer's control structure.
62378915Smsmith */
62478915Smsmithstatic struct acpi_powerconsumer *
62578915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer)
62678915Smsmith{
62778915Smsmith    struct acpi_powerconsumer	*pc;
62878915Smsmith
62978915Smsmith    FUNCTION_TRACE(__func__);
63078915Smsmith
63178915Smsmith    TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link)
63278915Smsmith	if (pc->ac_consumer == consumer)
63378915Smsmith	    break;
63478915Smsmith    return_VALUE(pc);
63578915Smsmith}
63678915Smsmith
637