From owner-acpi-jp@jp.freebsd.org  Thu Aug 24 19:21:19 2000
Received: (from daemon@localhost)
	by castle.jp.freebsd.org (8.9.3+3.2W/8.7.3) id TAA81396;
	Thu, 24 Aug 2000 19:21:19 +0900 (JST)
	(envelope-from owner-acpi-jp@jp.FreeBSD.org)
Received: from tasogare.imasy.or.jp (daemon@tasogare.imasy.or.jp [202.227.24.5])
	by castle.jp.freebsd.org (8.9.3+3.2W/8.7.3) with ESMTP id TAA81391
	for <acpi-jp@jp.freebsd.org>; Thu, 24 Aug 2000 19:21:18 +0900 (JST)
	(envelope-from iwasaki@jp.FreeBSD.org)
Received: from localhost (iwasaki.imasy.or.jp [202.227.24.92])
	by tasogare.imasy.or.jp (8.10.2+3.3W/3.7W-tasogare/smtpfeed 1.07) with ESMTP id e7OALFr35277
	for <acpi-jp@jp.freebsd.org>; Thu, 24 Aug 2000 19:21:15 +0900 (JST)
	(envelope-from iwasaki@jp.FreeBSD.org)
To: acpi-jp@jp.freebsd.org
In-Reply-To: <20000814181553D.iwasaki@jp.FreeBSD.org>
References: <20000814181553D.iwasaki@jp.FreeBSD.org>
X-Mailer: Mew version 1.94.1 on Emacs 19.34 / Mule 2.3 (SUETSUMUHANA)
Mime-Version: 1.0
Content-Type: Text/Plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit
Message-Id: <20000824192111I.iwasaki@jp.FreeBSD.org>
Date: Thu, 24 Aug 2000 19:21:11 +0900
From: Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
X-Dispatcher: imput version 20000228(IM140)
Lines: 418
Reply-To: acpi-jp@jp.freebsd.org
Precedence: list
X-Distribute: distribute version 2.1 (Alpha) patchlevel 24e+000315
X-Sequence: acpi-jp 580
Subject: [acpi-jp 580] Re: PowerResource control
Errors-To: owner-acpi-jp@jp.freebsd.org
Sender: owner-acpi-jp@jp.freebsd.org
X-Originator: iwasaki@jp.freebsd.org

$B$H$j$"$($:0J2<$NItJ,$KAjEv$9$k%3!<%I$r$6$C$H=q$$$F$_$^$7$?!#(B

> 7.5.2.2 System \_S1 State (Sleeping with Processor Context Maintained)
> While the system is in the S1 sleeping state, its behavior is the following:
> [snip]
> - Power Resources are in a state compatible with the system S1
>   state. All Power Resources that supply a System Level reference of S0
>   are in the OFF state.
> - Devices states are compatible with the current Power Resource
>   states. only devices which solely reference Power Resources which are
>   in the ON state for a given device state can be in that device
>   state. In all other cases, the device is in the D3 (off) state(*10).
> [snip]
> *10
> Or is at least assumed to be in the D3 state by its device driver. For
> example, if the device doesn't explicitly describe how it can stay in
> some state non-off state while the system is in a sleeping state, the
> operating software must assume that the device can lose its power and
> state.

S1 $BA+0\A0$K(B PowerResource $B$H(B Device state $B$N@)8f$O=PMh$k$h$&$K$J$j$^$7(B
$B$?$,!"(BPORTEGE 3110CT $B$G(B S1 $BI|5"8e$KL5H?1~$K$J$k7o$N2r7h$K$O$J$j$^$;$s(B
$B$G$7$?(B (;_;)
$BJL$N860x(B (Processor State? timer $B$,;_$C$?$^$^(B?) $B$rC5$C$F$_$^$9!#(B

$BCfESH>C<$G$9$,(B PowerResource $B@)8f$N%3!<%I$b$;$C$+$/=q$$$?$N$G(B
$BN.$7$F$*$-$^$9!#;~4V$,$"$l$P(B Device state $B@)8f$NItJ,$OJL4X?t$K(B
$B$7$?$$$H$3$m$G$9!#(B

Index: acpi.c
===================================================================
RCS file: /home/cvs/ACPI/sys/dev/acpi/acpi.c,v
retrieving revision 1.26
diff -u -r1.26 acpi.c
--- acpi.c	2000/08/15 14:43:43	1.26
+++ acpi.c	2000/08/24 09:36:35
@@ -34,6 +34,7 @@
 #include <sys/kernel.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
+#include <sys/malloc.h>
 #include <sys/sysctl.h>
 
 #include <sys/eventhandler.h>		/* for EVENTHANDLER_REGISTER */
@@ -85,6 +86,26 @@
 static struct	ACPIaddr acpi_addr;
 struct		ACPIrsdp *acpi_rsdp;
 
+/* PowerResource control */
+struct acpi_powerres_device {
+	LIST_ENTRY(acpi_powerres_device) links;
+	struct	aml_name *name;
+	u_int8_t	state;		/* D0 to D3 */
+	u_int8_t	next_state;	/* initialized with D0 */
+};
+
+struct acpi_powerres_device_ref {
+	LIST_ENTRY(acpi_powerres_device_ref) links;
+	struct	acpi_powerres_device *device;
+};
+
+struct acpi_powerres_info {
+	LIST_ENTRY(acpi_powerres_info) links;
+	struct	aml_name *name;
+	u_int8_t	state;		/* OFF or ON */
+	LIST_HEAD(, acpi_powerres_device_ref) reflist[3]; /* for _PR[0-2] */
+};
+
 /* softc */
 typedef struct acpi_softc {
 	struct	ACPIsdt *rsdt;
@@ -95,6 +116,8 @@
 	int	system_state_initialized;
 	int	broken_wakeuplogic;
 	struct	acpi_system_state_package system_state_package;
+	LIST_HEAD(, acpi_powerres_info) acpi_powerres_inflist;
+	LIST_HEAD(, acpi_powerres_device) acpi_powerres_devlist;
 } acpi_softc_t;
 
 /* Character device stuff */
@@ -154,6 +177,10 @@
 static void acpi_set_sleeping_state(acpi_softc_t *sc, u_int8_t state);
 static void acpi_execute_pts(acpi_softc_t *sc, u_int8_t state);
 static void acpi_execute_wak(acpi_softc_t *sc, u_int8_t state);
+static void acpi_powerres_init(acpi_softc_t *sc);
+static int  acpi_powerres_register(struct aml_name *name, va_list ap);
+static int  acpi_powerres_add_device(struct aml_name *name, va_list ap);
+static void acpi_powerres_set_sleeping_state(acpi_softc_t *sc, u_int8_t state);
 
 /* ACPI event stuff */
 static void acpi_process_event(acpi_softc_t *sc,
@@ -892,6 +919,9 @@
 	/* Prepare to sleep */
 	acpi_execute_pts(sc, state);
 
+	/* PowerResource manipulation */
+	acpi_powerres_set_sleeping_state(sc, state);
+
 	if (!sc->system_state_initialized) {
 		return;
 	}
@@ -959,6 +989,316 @@
 	    aml_eval_name_simple);
 	aml_apply_foreach_found_objects(aml_get_rootname(), "_PSR",
 	    aml_eval_name_simple);
+}
+
+static void
+acpi_powerres_init(acpi_softc_t *sc)
+{
+	struct	acpi_powerres_info *powerres;
+	struct	acpi_powerres_device_ref *device_ref;
+	struct	acpi_powerres_device *device;
+	int	i;
+
+	while ((powerres = LIST_FIRST(&sc->acpi_powerres_inflist))) {
+#ifdef ACPI_DEBUG
+		printf("acpi_powerres_init:");
+		aml_print_curname(powerres->name);
+		printf("[%d]\n", powerres->state);
+#endif
+		for (i = 0; i < 3; i++) {
+#ifdef ACPI_DEBUG
+			printf("\t_PR%d:", i);
+#endif
+			while ((device_ref = LIST_FIRST(&powerres->reflist[i]))) {
+#ifdef ACPI_DEBUG
+				device = device_ref->device;
+				aml_print_curname(device->name);
+				printf("[%d] ", device->state);
+#endif
+				LIST_REMOVE(device_ref, links);
+				FREE(device_ref, M_TEMP);
+			}
+#ifdef ACPI_DEBUG
+			printf("\n");
+#endif
+			LIST_INIT(&powerres->reflist[i]);
+		}
+		LIST_REMOVE(powerres, links);
+		FREE(powerres, M_TEMP);
+	}
+	LIST_INIT(&sc->acpi_powerres_inflist);
+
+	while ((device = LIST_FIRST(&sc->acpi_powerres_devlist))) {
+		LIST_REMOVE(device, links);
+		FREE(device, M_TEMP);
+	}
+	LIST_INIT(&sc->acpi_powerres_devlist);
+}
+
+static int
+acpi_powerres_register(struct aml_name *name, va_list ap)
+{
+	int	i;
+	acpi_softc_t *sc;
+	struct	acpi_powerres_info *powerres;
+	struct	aml_name *method;
+	union	aml_object *ret;
+	struct	aml_environ env;
+
+	sc = va_arg(ap, acpi_softc_t *);
+
+	if (name->property == NULL ||
+	    name->property->type != aml_t_powerres) {
+		return (0);
+	}
+
+	MALLOC(powerres, struct acpi_powerres_info *,
+	    sizeof(*powerres), M_TEMP, M_NOWAIT);
+	if (powerres == NULL) {
+		return (1);
+	}
+
+	powerres->name = name;
+
+	/* get the current ON or OFF status for the power resource */
+	method = aml_find_from_namespace(name, "_STA");
+	if (method != NULL) {
+		bzero(&env, sizeof(env));
+		aml_local_stack_push(aml_local_stack_create());
+		ret = aml_eval_name(&env, method);
+		aml_local_stack_delete(aml_local_stack_pop());
+		powerres->state = ret->num.number;	/* OFF or ON */
+	}
+
+	/* XXX must be sorted by resource order of PowerResource */
+	LIST_INSERT_HEAD(&sc->acpi_powerres_inflist, powerres, links);
+
+	for (i = 0; i < 3; i++) {
+		LIST_INIT(&powerres->reflist[i]);
+	}
+
+	return (0);
+}
+
+static int
+acpi_powerres_add_device(struct aml_name *name, va_list ap)
+{
+	int	i;
+	int	prnum;
+	int	dev_found;
+	acpi_softc_t *sc;
+	struct	acpi_powerres_device *device;
+	struct	acpi_powerres_device_ref *device_ref;
+	struct	acpi_powerres_info *powerres;
+	struct	aml_name *powerres_name;
+	struct	aml_name *method;
+	union	aml_object *ret;
+	struct	aml_environ env;
+
+	sc = va_arg(ap, acpi_softc_t *);
+
+	/* should be _PR[0-2] */
+	prnum =  name->name[3] - '0';
+	if (!(prnum >= 0 && prnum <= 2)) {
+		return (0);
+	}
+
+	if (name->property == NULL ||
+	    name->property->type != aml_t_package) {
+		return (0);
+	}
+
+	if (name->property->package.elements == 0) {
+		return (0);
+	}
+
+	/* make the list of devices */
+	dev_found = 0;
+	LIST_FOREACH(device, &sc->acpi_powerres_devlist, links) {
+		if (device->name == name) {
+			dev_found = 1;
+			break;
+		}
+	}
+	if (!dev_found) {
+		MALLOC(device, struct acpi_powerres_device *,
+		   sizeof(*device), M_TEMP, M_NOWAIT);
+		if (device == NULL) {
+			return (1);
+		}
+
+		/* this is a _PR[0-2] object, we need get a parent of this. */
+		device->name = name->parent;
+		device->state = 0;	/* assume D0 */
+
+		/* get the current device state */
+		method = aml_find_from_namespace(device->name, "_PSC");
+		if (method != NULL) {
+			bzero(&env, sizeof(env));
+			aml_local_stack_push(aml_local_stack_create());
+			ret = aml_eval_name(&env, method);
+			aml_local_stack_delete(aml_local_stack_pop());
+			device->state = ret->num.number;	/* D0 - D3 */
+		}
+		LIST_INSERT_HEAD(&sc->acpi_powerres_devlist, device, links);
+	}
+
+	/* find PowerResource which the device reference to */
+	MALLOC(device_ref, struct acpi_powerres_device_ref *,
+	   sizeof(*device_ref), M_TEMP, M_NOWAIT);
+	if (device_ref == NULL) {
+		return (1);
+	}
+	device_ref->device = device;
+	env.curname = device->name;
+	for (i = 0; i < name->property->package.elements; i++) {
+		if (name->property->package.objects[i]->type != aml_t_namestr) {
+			printf("acpi_powerres_add_device: not name string\n");
+			continue;
+		}
+		powerres_name = aml_search_name(&env,
+		    name->property->package.objects[i]->nstr.dp);
+		if (powerres_name == NULL) {
+			printf("acpi_powerres_add_device: not found\n");
+			continue;
+		}
+
+		LIST_FOREACH(powerres, &sc->acpi_powerres_inflist, links) {
+			if (powerres->name == powerres_name) {
+				LIST_INSERT_HEAD(&powerres->reflist[prnum],
+				    device_ref, links);
+				break;
+			}
+		}
+	}
+
+	return (0);
+}
+
+static void
+acpi_powerres_set_sleeping_state(acpi_softc_t *sc, u_int8_t state)
+{
+	int	i;
+	struct	acpi_powerres_info *powerres;
+	struct	acpi_powerres_device *device;
+	struct	acpi_powerres_device_ref *device_ref;
+	struct	aml_name *method;
+	union	aml_object *ret;
+	struct	aml_environ env;
+
+	if (!(state >= 1 && state <= 4)) {
+		return;
+	}
+
+	acpi_powerres_init(sc);
+	aml_apply_foreach_found_objects(aml_get_rootname(), ".",
+	    acpi_powerres_register, sc);
+	aml_apply_foreach_found_objects(aml_get_rootname(), "_PR",
+	    acpi_powerres_add_device, sc);
+
+	/*
+	 * initialize with D0, then change to D3 later based on
+	 * PowerResource state change.
+	 */
+	LIST_FOREACH(device, &sc->acpi_powerres_devlist, links) {
+		device->next_state = 0;
+	}
+
+	/*
+	 * 7.5.2 System \_Sx state
+	 * Power Resources are in a state compatible with the system Sx
+	 * state.  All power Resources that supply a System Level reference
+	 * of Sn (where n < x) are in the OFF state.
+	 */
+	LIST_FOREACH(powerres, &sc->acpi_powerres_inflist, links) {
+		if (powerres->name->property->pres.level < state) {
+			/* if ON state then put it in the OFF state */
+			if (powerres->state == 1) {
+				method = aml_find_from_namespace(powerres->name,
+				    "_OFF");
+				if (method == NULL) {
+					continue;	/* just in case */
+				}
+
+				bzero(&env, sizeof(env));
+				aml_local_stack_push(aml_local_stack_create());
+				aml_eval_name(&env, method);
+				aml_local_stack_delete(aml_local_stack_pop());
+				powerres->state = 0;
+			}
+			/*
+			 * Device states are compatible with the current
+			 * Power Resource states.
+			 */
+			for (i = 0; i < 3; i++) {
+				LIST_FOREACH(device_ref, &powerres->reflist[i], links) {
+					/* D3 state */
+					device_ref->device->next_state = 3;
+				}
+			}
+		} else {
+			/* if OFF state then put it in the ON state */
+			if (powerres->state == 0) {
+				method = aml_find_from_namespace(powerres->name,
+				    "_ON");
+				if (method == NULL) {
+					continue;	/* just in case */
+				}
+
+				bzero(&env, sizeof(env));
+				aml_local_stack_push(aml_local_stack_create());
+				aml_eval_name(&env, method);
+				aml_local_stack_delete(aml_local_stack_pop());
+				powerres->state = 1;
+			}
+		}
+	}
+
+	/*
+	 * Devices states are compatible with the current Power Resource
+	 * states. only devices which solely reference Power Resources which
+	 * are in the ON state for a given device state can be in that device
+	 * state. In all other cases, the device is in the D3 (off) state.
+	 * Note:
+	 * Or is at least assumed to be in the D3 state by its device driver.
+	 * For example, if the device doesn't explicitly describe how it can
+	 * stay in some state non-off state while the system is in a sleeping
+	 * state, the operating software must assume that the device can lose
+	 * its power and state.
+	 */
+
+	LIST_FOREACH(device, &sc->acpi_powerres_devlist, links) {
+		if (device->next_state == 3 && device->state != 3) {
+			method = aml_find_from_namespace(device->name, "_PS3");
+			if (method != NULL) {
+				bzero(&env, sizeof(env));
+				aml_local_stack_push(aml_local_stack_create());
+				aml_eval_name(&env, method);
+				aml_local_stack_delete(aml_local_stack_pop());
+			}
+		}
+		if (device->next_state == 0 && device->state != 0) {
+			method = aml_find_from_namespace(device->name, "_PS0");
+			if (method != NULL) {
+				bzero(&env, sizeof(env));
+				aml_local_stack_push(aml_local_stack_create());
+				aml_eval_name(&env, method);
+				aml_local_stack_delete(aml_local_stack_pop());
+			}
+		}
+		/* get the current device state */
+		method = aml_find_from_namespace(device->name, "_PSC");
+		if (method != NULL) {
+			bzero(&env, sizeof(env));
+			aml_local_stack_push(aml_local_stack_create());
+			ret = aml_eval_name(&env, method);
+			aml_local_stack_delete(aml_local_stack_pop());
+			device->state = ret->num.number;	/* D0 - D3 */
+		}
+	}
+#if 1
+	acpi_powerres_init(sc);
+#endif
 }
 
 /*
