xref: /openbsd/sys/dev/acpi/acpipwrres.c (revision a4dd6042)
1*a4dd6042Sdv /* $OpenBSD: acpipwrres.c,v 1.13 2023/02/18 14:32:02 dv Exp $ */
2b08441edSmpi 
37f2db1f2Spirofti /*
4b08441edSmpi  * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
58855e28cSpirofti  * Copyright (c) 2009 Paul Irofti <paul@irofti.net>
67f2db1f2Spirofti  *
77f2db1f2Spirofti  * Permission to use, copy, modify, and distribute this software for any
87f2db1f2Spirofti  * purpose with or without fee is hereby granted, provided that the above
97f2db1f2Spirofti  * copyright notice and this permission notice appear in all copies.
107f2db1f2Spirofti  *
117f2db1f2Spirofti  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
127f2db1f2Spirofti  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
137f2db1f2Spirofti  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
147f2db1f2Spirofti  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
157f2db1f2Spirofti  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
167f2db1f2Spirofti  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
177f2db1f2Spirofti  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
187f2db1f2Spirofti  */
19b08441edSmpi 
207f2db1f2Spirofti #include <sys/param.h>
217f2db1f2Spirofti #include <sys/signalvar.h>
227f2db1f2Spirofti #include <sys/systm.h>
237f2db1f2Spirofti #include <sys/device.h>
247f2db1f2Spirofti #include <sys/malloc.h>
257f2db1f2Spirofti 
267f2db1f2Spirofti #include <machine/bus.h>
277f2db1f2Spirofti 
287f2db1f2Spirofti #include <dev/acpi/acpireg.h>
297f2db1f2Spirofti #include <dev/acpi/acpivar.h>
307f2db1f2Spirofti #include <dev/acpi/acpidev.h>
317f2db1f2Spirofti #include <dev/acpi/amltypes.h>
327f2db1f2Spirofti #include <dev/acpi/dsdt.h>
337f2db1f2Spirofti 
347f2db1f2Spirofti int	acpipwrres_match(struct device *, void *, void *);
357f2db1f2Spirofti void	acpipwrres_attach(struct device *, struct device *, void *);
367f2db1f2Spirofti 
377f2db1f2Spirofti #ifdef ACPIPWRRES_DEBUG
387f2db1f2Spirofti #define DPRINTF(x)	printf x
397f2db1f2Spirofti #else
407f2db1f2Spirofti #define DPRINTF(x)
417f2db1f2Spirofti #endif
427f2db1f2Spirofti 
437f2db1f2Spirofti struct acpipwrres_softc {
447f2db1f2Spirofti 	struct device		sc_dev;
457f2db1f2Spirofti 
467f2db1f2Spirofti 	bus_space_tag_t		sc_iot;
477f2db1f2Spirofti 	bus_space_handle_t	sc_ioh;
487f2db1f2Spirofti 
497f2db1f2Spirofti 	struct acpi_softc	*sc_acpi;
507f2db1f2Spirofti 	struct aml_node		*sc_devnode;
517f2db1f2Spirofti 
52b08441edSmpi 	SIMPLEQ_HEAD(, acpipwrres_consumer)	sc_cons;
53b08441edSmpi 	int					sc_cons_ref;
547f2db1f2Spirofti 
557f2db1f2Spirofti 	int	sc_level;
567f2db1f2Spirofti 	int	sc_order;
577f2db1f2Spirofti 	int	sc_state;
587f2db1f2Spirofti #define ACPIPWRRES_OFF		0
597f2db1f2Spirofti #define ACPIPWRRES_ON		1
607f2db1f2Spirofti #define ACPIPWRRES_UNK		-1
617f2db1f2Spirofti };
627f2db1f2Spirofti 
637f2db1f2Spirofti struct acpipwrres_consumer {
647f2db1f2Spirofti 	struct aml_node				*cs_node;
65b08441edSmpi 	SIMPLEQ_ENTRY(acpipwrres_consumer)	cs_next;
667f2db1f2Spirofti };
677f2db1f2Spirofti 
68471aeecfSnaddy const struct cfattach acpipwrres_ca = {
697f2db1f2Spirofti 	sizeof(struct acpipwrres_softc), acpipwrres_match, acpipwrres_attach
707f2db1f2Spirofti };
717f2db1f2Spirofti 
727f2db1f2Spirofti struct cfdriver acpipwrres_cd = {
737f2db1f2Spirofti 	NULL, "acpipwrres", DV_DULL
747f2db1f2Spirofti };
757f2db1f2Spirofti 
76b08441edSmpi int	acpipwrres_hascons(struct acpipwrres_softc *, struct aml_node *);
77b08441edSmpi int	acpipwrres_addcons(struct acpipwrres_softc *, struct aml_node *);
787f2db1f2Spirofti int	acpipwrres_foundcons(struct aml_node *, void *);
797f2db1f2Spirofti 
807f2db1f2Spirofti int
acpipwrres_match(struct device * parent,void * match,void * aux)817f2db1f2Spirofti acpipwrres_match(struct device *parent, void *match, void *aux)
827f2db1f2Spirofti {
837f2db1f2Spirofti 	struct acpi_attach_args *aaa = aux;
847f2db1f2Spirofti 	struct cfdata		*cf = match;
857f2db1f2Spirofti 
867f2db1f2Spirofti 	if (aaa->aaa_name == NULL || strcmp(aaa->aaa_name,
877f2db1f2Spirofti 	    cf->cf_driver->cd_name) != 0 || aaa->aaa_table != NULL)
887f2db1f2Spirofti 		return (0);
897f2db1f2Spirofti 
907f2db1f2Spirofti 	return (1);
917f2db1f2Spirofti }
927f2db1f2Spirofti 
937f2db1f2Spirofti void
acpipwrres_attach(struct device * parent,struct device * self,void * aux)947f2db1f2Spirofti acpipwrres_attach(struct device *parent, struct device *self, void *aux)
957f2db1f2Spirofti {
967f2db1f2Spirofti 	struct acpipwrres_softc		*sc = (struct acpipwrres_softc *)self;
977f2db1f2Spirofti 	struct acpi_attach_args		*aaa = aux;
987f2db1f2Spirofti 	struct aml_value		res;
99b08441edSmpi 	struct acpipwrres_consumer	*cons;
1007f2db1f2Spirofti 
1017f2db1f2Spirofti 	sc->sc_acpi = (struct acpi_softc *)parent;
1027f2db1f2Spirofti 	sc->sc_devnode = aaa->aaa_node;
1037f2db1f2Spirofti 	memset(&res, 0, sizeof res);
1047f2db1f2Spirofti 
105b08441edSmpi 	printf(": %s", sc->sc_devnode->name);
1067f2db1f2Spirofti 
1077f2db1f2Spirofti 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
1087f2db1f2Spirofti 		sc->sc_state = (int)aml_val2int(&res);
1097f2db1f2Spirofti 		if (sc->sc_state != ACPIPWRRES_ON &&
1107f2db1f2Spirofti 		    sc->sc_state != ACPIPWRRES_OFF)
1117f2db1f2Spirofti 			sc->sc_state = ACPIPWRRES_UNK;
1127f2db1f2Spirofti 	} else
1137f2db1f2Spirofti 		sc->sc_state = ACPIPWRRES_UNK;
114b08441edSmpi 	DPRINTF(("\n%s: state = %d\n", DEVNAME(sc), sc->sc_state));
1157f2db1f2Spirofti 	if (aml_evalnode(sc->sc_acpi, aaa->aaa_node, 0, NULL, &res) == 0) {
1167f2db1f2Spirofti 		sc->sc_level = res.v_powerrsrc.pwr_level;
1177f2db1f2Spirofti 		sc->sc_order = res.v_powerrsrc.pwr_order;
1187f2db1f2Spirofti 		DPRINTF(("%s: level = %d, order %d\n", DEVNAME(sc),
1197f2db1f2Spirofti 		    sc->sc_level, sc->sc_order));
1207f2db1f2Spirofti 		aml_freevalue(&res);
1217f2db1f2Spirofti 	}
1227f2db1f2Spirofti 
1237f2db1f2Spirofti 	/* Get the list of consumers */
124b08441edSmpi 	SIMPLEQ_INIT(&sc->sc_cons);
125b08441edSmpi #if notyet
1269108e412Spatrick 	aml_find_node(sc->sc_acpi->sc_root, "_PRW", acpipwrres_foundcons, sc);
127b08441edSmpi #endif
1289108e412Spatrick 	aml_find_node(sc->sc_acpi->sc_root, "_PR0", acpipwrres_foundcons, sc);
1299108e412Spatrick 	aml_find_node(sc->sc_acpi->sc_root, "_PR1", acpipwrres_foundcons, sc);
1309108e412Spatrick 	aml_find_node(sc->sc_acpi->sc_root, "_PR2", acpipwrres_foundcons, sc);
1319108e412Spatrick 	aml_find_node(sc->sc_acpi->sc_root, "_PR3", acpipwrres_foundcons, sc);
132b08441edSmpi 
133b08441edSmpi 	DPRINTF(("%s", DEVNAME(sc)));
134b08441edSmpi 	if (!SIMPLEQ_EMPTY(&sc->sc_cons)) {
135a77309d2Sderaadt 		printf(", resource for");
136b08441edSmpi 		SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next)
137b08441edSmpi 			printf(" %s%s", cons->cs_node->name,
138b08441edSmpi 			   (SIMPLEQ_NEXT(cons, cs_next) == NULL) ? "" : ",");
139b08441edSmpi 	}
140b08441edSmpi 	printf("\n");
1417f2db1f2Spirofti }
1427f2db1f2Spirofti 
1437f2db1f2Spirofti int
acpipwrres_ref_incr(struct acpipwrres_softc * sc,struct aml_node * node)144b08441edSmpi acpipwrres_ref_incr(struct acpipwrres_softc *sc, struct aml_node *node)
1457f2db1f2Spirofti {
146b08441edSmpi 	if (!acpipwrres_hascons(sc, node))
147b08441edSmpi 		return (1);
1487f2db1f2Spirofti 
149b08441edSmpi 	DPRINTF(("%s: dev %s ON %d\n", DEVNAME(sc), node->name,
150b08441edSmpi 	    sc->sc_cons_ref));
151b08441edSmpi 
152*a4dd6042Sdv 	if (sc->sc_cons_ref++ == 0)
153b08441edSmpi 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_ON", 0,
154*a4dd6042Sdv 		    NULL, NULL);
155b08441edSmpi 
1567f2db1f2Spirofti 	return (0);
1577f2db1f2Spirofti }
1587f2db1f2Spirofti 
159b08441edSmpi int
acpipwrres_ref_decr(struct acpipwrres_softc * sc,struct aml_node * node)160b08441edSmpi acpipwrres_ref_decr(struct acpipwrres_softc *sc, struct aml_node *node)
161b08441edSmpi {
162b08441edSmpi 	if (!acpipwrres_hascons(sc, node))
163b08441edSmpi 		return (1);
164b08441edSmpi 
165b08441edSmpi 	DPRINTF(("%s: dev %s OFF %d\n", DEVNAME(sc), node->name,
166b08441edSmpi 	    sc->sc_cons_ref));
167b08441edSmpi 
168*a4dd6042Sdv 	if (--sc->sc_cons_ref == 0)
169b08441edSmpi 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0,
170*a4dd6042Sdv 		    NULL, NULL);
171b08441edSmpi 
172b08441edSmpi 	return (0);
173b08441edSmpi }
174b08441edSmpi 
175b08441edSmpi int
acpipwrres_hascons(struct acpipwrres_softc * sc,struct aml_node * node)176b08441edSmpi acpipwrres_hascons(struct acpipwrres_softc *sc, struct aml_node *node)
177b08441edSmpi {
178b08441edSmpi 	struct acpipwrres_consumer	*cons;
179b08441edSmpi 
180b08441edSmpi 	SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next) {
181b08441edSmpi 		if (cons->cs_node == node)
182b08441edSmpi 			return (1);
183b08441edSmpi 	}
184b08441edSmpi 
185b08441edSmpi 	return (0);
186b08441edSmpi }
187b08441edSmpi 
188b08441edSmpi int
acpipwrres_addcons(struct acpipwrres_softc * sc,struct aml_node * node)189b08441edSmpi acpipwrres_addcons(struct acpipwrres_softc *sc, struct aml_node *node)
190b08441edSmpi {
191b08441edSmpi 	struct acpipwrres_consumer	*cons;
192b08441edSmpi 	struct acpi_pwrres		*pr;
193b08441edSmpi 	int				state;
194b08441edSmpi 
195b08441edSmpi 	/*
196b08441edSmpi 	 * Add handlers to put the device into Dx states.
197b08441edSmpi 	 *
198b08441edSmpi 	 * XXX What about PRW?
199b08441edSmpi 	 */
200b08441edSmpi 	if (strcmp(node->name, "_PR0") == 0) {
201b08441edSmpi 		state = ACPI_STATE_D0;
202b08441edSmpi 	} else if (strcmp(node->name, "_PR1") == 0) {
203b08441edSmpi 		state = ACPI_STATE_D1;
204b08441edSmpi 	} else if (strcmp(node->name, "_PR2") == 0) {
205b08441edSmpi 		state = ACPI_STATE_D2;
206b08441edSmpi 	} else if (strcmp(node->name, "_PR3") == 0) {
207b08441edSmpi 		state = ACPI_STATE_D3;
208b08441edSmpi 	} else {
209b08441edSmpi 		return (0);
210b08441edSmpi 	}
211b08441edSmpi 
212b08441edSmpi 	if (!acpipwrres_hascons(sc, node->parent)) {
213b08441edSmpi 		cons = malloc(sizeof(*cons), M_DEVBUF, M_NOWAIT | M_ZERO);
214b08441edSmpi 		if (cons == NULL)
215b08441edSmpi 			return (ENOMEM);
216b08441edSmpi 
217b08441edSmpi 		cons->cs_node = node->parent;
218b08441edSmpi 		SIMPLEQ_INSERT_TAIL(&sc->sc_cons, cons, cs_next);
219b08441edSmpi 	}
220b08441edSmpi 
221b08441edSmpi 	DPRINTF(("%s: resource for %s (D%d) \n", DEVNAME(sc),
222b08441edSmpi 	    node->parent->name, state));
223b08441edSmpi 
224b08441edSmpi 	/*
225b08441edSmpi 	 * Make sure we attach only once the same Power Resource for a
226b08441edSmpi 	 * given state.
227b08441edSmpi 	 */
228b08441edSmpi 	SIMPLEQ_FOREACH(pr, &sc->sc_acpi->sc_pwrresdevs, p_next) {
229b08441edSmpi 		if (pr->p_node == node->parent &&
230b08441edSmpi 		    pr->p_res_state == state &&
231b08441edSmpi 		    pr->p_res_sc == sc) {
232b08441edSmpi 			DPRINTF(("error: pr for %s already set\n",
233b08441edSmpi 			   aml_nodename(pr->p_node)));
234b08441edSmpi 			return (EINVAL);
235b08441edSmpi 		}
236b08441edSmpi 	}
237b08441edSmpi 
238b08441edSmpi 	pr = malloc(sizeof(struct acpi_pwrres), M_DEVBUF, M_NOWAIT | M_ZERO);
239b08441edSmpi 	if (pr == NULL)
240b08441edSmpi 		return (ENOMEM);
241b08441edSmpi 
242b08441edSmpi 	pr->p_node = node->parent;
243b08441edSmpi 	pr->p_state = -1;
244b08441edSmpi 	pr->p_res_state = state;
245b08441edSmpi 	pr->p_res_sc = sc;
246b08441edSmpi 
247b08441edSmpi 	SIMPLEQ_INSERT_TAIL(&sc->sc_acpi->sc_pwrresdevs, pr, p_next);
248b08441edSmpi 
2497f2db1f2Spirofti 	return (0);
2507f2db1f2Spirofti }
2517f2db1f2Spirofti 
2527f2db1f2Spirofti int
acpipwrres_foundcons(struct aml_node * node,void * arg)2537f2db1f2Spirofti acpipwrres_foundcons(struct aml_node *node, void *arg)
2547f2db1f2Spirofti {
2557f2db1f2Spirofti 	struct acpipwrres_softc		*sc = (struct acpipwrres_softc *)arg;
256b08441edSmpi 	struct aml_value		res, *ref;
257b08441edSmpi 	int				i = 0;
2587f2db1f2Spirofti 
259b08441edSmpi 	memset(&res, 0, sizeof(res));
2607f2db1f2Spirofti 
2617f2db1f2Spirofti 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
2627f2db1f2Spirofti 		DPRINTF(("pwr: consumer not found\n"));
263b08441edSmpi 		return (1);
264b08441edSmpi 	}
265b08441edSmpi 
266b08441edSmpi 	if (res.type != AML_OBJTYPE_PACKAGE) {
267b08441edSmpi 		DPRINTF(("%s: %s is not a package\n", DEVNAME(sc),
268b08441edSmpi 		    aml_nodename(node)));
269b08441edSmpi 		aml_freevalue(&res);
270b08441edSmpi 		return (1);
271b08441edSmpi 	}
272b08441edSmpi 
2737f2db1f2Spirofti 	DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node)));
2747f2db1f2Spirofti 	if (!strcmp(node->name, "_PRW"))
2757f2db1f2Spirofti 		i = 2;          /* _PRW first two values are ints */
276b08441edSmpi 
2777f2db1f2Spirofti 	for (; i < res.length; i++) {
278b08441edSmpi 		ref = res.v_package[i];
279b08441edSmpi 
280d90a88ceSkettenis 		if (ref->type == AML_OBJTYPE_NAMEREF) {
281b08441edSmpi 			struct aml_node	*pnode;
282b08441edSmpi 
2839108e412Spatrick 			pnode = aml_searchrel(sc->sc_acpi->sc_root,
284abe2aaccSpatrick 			    aml_getname(ref->v_nameref));
285b08441edSmpi 			if (pnode == NULL) {
286b08441edSmpi 				DPRINTF(("%s: device %s not found\n",
287b08441edSmpi 				    DEVNAME(sc), ref->v_string));
288b08441edSmpi 				continue;
2897f2db1f2Spirofti 			}
290b08441edSmpi 			ref = pnode->value;
2917f2db1f2Spirofti 		}
292b08441edSmpi 
293b08441edSmpi 		if (ref->type == AML_OBJTYPE_OBJREF)
294b08441edSmpi 			ref = ref->v_objref.ref;
295b08441edSmpi 
296b08441edSmpi 		if (ref->type != AML_OBJTYPE_POWERRSRC) {
297b08441edSmpi 			DPRINTF(("%s: object reference has a wrong type (%d)\n",
298b08441edSmpi 			    DEVNAME(sc), ref->type));
299b08441edSmpi 			continue;
3007f2db1f2Spirofti 		}
3017f2db1f2Spirofti 
302b08441edSmpi 		if (ref->node == sc->sc_devnode)
303b08441edSmpi 			(void)acpipwrres_addcons(sc, node);
304b08441edSmpi 	}
305b08441edSmpi 	aml_freevalue(&res);
306b08441edSmpi 
3077f2db1f2Spirofti 	return (0);
3087f2db1f2Spirofti }
309