xref: /openbsd/sys/dev/acpi/acpipwrres.c (revision a4dd6042)
1 /* $OpenBSD: acpipwrres.c,v 1.13 2023/02/18 14:32:02 dv Exp $ */
2 
3 /*
4  * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
5  * Copyright (c) 2009 Paul Irofti <paul@irofti.net>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/signalvar.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 #include <sys/malloc.h>
25 
26 #include <machine/bus.h>
27 
28 #include <dev/acpi/acpireg.h>
29 #include <dev/acpi/acpivar.h>
30 #include <dev/acpi/acpidev.h>
31 #include <dev/acpi/amltypes.h>
32 #include <dev/acpi/dsdt.h>
33 
34 int	acpipwrres_match(struct device *, void *, void *);
35 void	acpipwrres_attach(struct device *, struct device *, void *);
36 
37 #ifdef ACPIPWRRES_DEBUG
38 #define DPRINTF(x)	printf x
39 #else
40 #define DPRINTF(x)
41 #endif
42 
43 struct acpipwrres_softc {
44 	struct device		sc_dev;
45 
46 	bus_space_tag_t		sc_iot;
47 	bus_space_handle_t	sc_ioh;
48 
49 	struct acpi_softc	*sc_acpi;
50 	struct aml_node		*sc_devnode;
51 
52 	SIMPLEQ_HEAD(, acpipwrres_consumer)	sc_cons;
53 	int					sc_cons_ref;
54 
55 	int	sc_level;
56 	int	sc_order;
57 	int	sc_state;
58 #define ACPIPWRRES_OFF		0
59 #define ACPIPWRRES_ON		1
60 #define ACPIPWRRES_UNK		-1
61 };
62 
63 struct acpipwrres_consumer {
64 	struct aml_node				*cs_node;
65 	SIMPLEQ_ENTRY(acpipwrres_consumer)	cs_next;
66 };
67 
68 const struct cfattach acpipwrres_ca = {
69 	sizeof(struct acpipwrres_softc), acpipwrres_match, acpipwrres_attach
70 };
71 
72 struct cfdriver acpipwrres_cd = {
73 	NULL, "acpipwrres", DV_DULL
74 };
75 
76 int	acpipwrres_hascons(struct acpipwrres_softc *, struct aml_node *);
77 int	acpipwrres_addcons(struct acpipwrres_softc *, struct aml_node *);
78 int	acpipwrres_foundcons(struct aml_node *, void *);
79 
80 int
acpipwrres_match(struct device * parent,void * match,void * aux)81 acpipwrres_match(struct device *parent, void *match, void *aux)
82 {
83 	struct acpi_attach_args *aaa = aux;
84 	struct cfdata		*cf = match;
85 
86 	if (aaa->aaa_name == NULL || strcmp(aaa->aaa_name,
87 	    cf->cf_driver->cd_name) != 0 || aaa->aaa_table != NULL)
88 		return (0);
89 
90 	return (1);
91 }
92 
93 void
acpipwrres_attach(struct device * parent,struct device * self,void * aux)94 acpipwrres_attach(struct device *parent, struct device *self, void *aux)
95 {
96 	struct acpipwrres_softc		*sc = (struct acpipwrres_softc *)self;
97 	struct acpi_attach_args		*aaa = aux;
98 	struct aml_value		res;
99 	struct acpipwrres_consumer	*cons;
100 
101 	sc->sc_acpi = (struct acpi_softc *)parent;
102 	sc->sc_devnode = aaa->aaa_node;
103 	memset(&res, 0, sizeof res);
104 
105 	printf(": %s", sc->sc_devnode->name);
106 
107 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
108 		sc->sc_state = (int)aml_val2int(&res);
109 		if (sc->sc_state != ACPIPWRRES_ON &&
110 		    sc->sc_state != ACPIPWRRES_OFF)
111 			sc->sc_state = ACPIPWRRES_UNK;
112 	} else
113 		sc->sc_state = ACPIPWRRES_UNK;
114 	DPRINTF(("\n%s: state = %d\n", DEVNAME(sc), sc->sc_state));
115 	if (aml_evalnode(sc->sc_acpi, aaa->aaa_node, 0, NULL, &res) == 0) {
116 		sc->sc_level = res.v_powerrsrc.pwr_level;
117 		sc->sc_order = res.v_powerrsrc.pwr_order;
118 		DPRINTF(("%s: level = %d, order %d\n", DEVNAME(sc),
119 		    sc->sc_level, sc->sc_order));
120 		aml_freevalue(&res);
121 	}
122 
123 	/* Get the list of consumers */
124 	SIMPLEQ_INIT(&sc->sc_cons);
125 #if notyet
126 	aml_find_node(sc->sc_acpi->sc_root, "_PRW", acpipwrres_foundcons, sc);
127 #endif
128 	aml_find_node(sc->sc_acpi->sc_root, "_PR0", acpipwrres_foundcons, sc);
129 	aml_find_node(sc->sc_acpi->sc_root, "_PR1", acpipwrres_foundcons, sc);
130 	aml_find_node(sc->sc_acpi->sc_root, "_PR2", acpipwrres_foundcons, sc);
131 	aml_find_node(sc->sc_acpi->sc_root, "_PR3", acpipwrres_foundcons, sc);
132 
133 	DPRINTF(("%s", DEVNAME(sc)));
134 	if (!SIMPLEQ_EMPTY(&sc->sc_cons)) {
135 		printf(", resource for");
136 		SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next)
137 			printf(" %s%s", cons->cs_node->name,
138 			   (SIMPLEQ_NEXT(cons, cs_next) == NULL) ? "" : ",");
139 	}
140 	printf("\n");
141 }
142 
143 int
acpipwrres_ref_incr(struct acpipwrres_softc * sc,struct aml_node * node)144 acpipwrres_ref_incr(struct acpipwrres_softc *sc, struct aml_node *node)
145 {
146 	if (!acpipwrres_hascons(sc, node))
147 		return (1);
148 
149 	DPRINTF(("%s: dev %s ON %d\n", DEVNAME(sc), node->name,
150 	    sc->sc_cons_ref));
151 
152 	if (sc->sc_cons_ref++ == 0)
153 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_ON", 0,
154 		    NULL, NULL);
155 
156 	return (0);
157 }
158 
159 int
acpipwrres_ref_decr(struct acpipwrres_softc * sc,struct aml_node * node)160 acpipwrres_ref_decr(struct acpipwrres_softc *sc, struct aml_node *node)
161 {
162 	if (!acpipwrres_hascons(sc, node))
163 		return (1);
164 
165 	DPRINTF(("%s: dev %s OFF %d\n", DEVNAME(sc), node->name,
166 	    sc->sc_cons_ref));
167 
168 	if (--sc->sc_cons_ref == 0)
169 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0,
170 		    NULL, NULL);
171 
172 	return (0);
173 }
174 
175 int
acpipwrres_hascons(struct acpipwrres_softc * sc,struct aml_node * node)176 acpipwrres_hascons(struct acpipwrres_softc *sc, struct aml_node *node)
177 {
178 	struct acpipwrres_consumer	*cons;
179 
180 	SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next) {
181 		if (cons->cs_node == node)
182 			return (1);
183 	}
184 
185 	return (0);
186 }
187 
188 int
acpipwrres_addcons(struct acpipwrres_softc * sc,struct aml_node * node)189 acpipwrres_addcons(struct acpipwrres_softc *sc, struct aml_node *node)
190 {
191 	struct acpipwrres_consumer	*cons;
192 	struct acpi_pwrres		*pr;
193 	int				state;
194 
195 	/*
196 	 * Add handlers to put the device into Dx states.
197 	 *
198 	 * XXX What about PRW?
199 	 */
200 	if (strcmp(node->name, "_PR0") == 0) {
201 		state = ACPI_STATE_D0;
202 	} else if (strcmp(node->name, "_PR1") == 0) {
203 		state = ACPI_STATE_D1;
204 	} else if (strcmp(node->name, "_PR2") == 0) {
205 		state = ACPI_STATE_D2;
206 	} else if (strcmp(node->name, "_PR3") == 0) {
207 		state = ACPI_STATE_D3;
208 	} else {
209 		return (0);
210 	}
211 
212 	if (!acpipwrres_hascons(sc, node->parent)) {
213 		cons = malloc(sizeof(*cons), M_DEVBUF, M_NOWAIT | M_ZERO);
214 		if (cons == NULL)
215 			return (ENOMEM);
216 
217 		cons->cs_node = node->parent;
218 		SIMPLEQ_INSERT_TAIL(&sc->sc_cons, cons, cs_next);
219 	}
220 
221 	DPRINTF(("%s: resource for %s (D%d) \n", DEVNAME(sc),
222 	    node->parent->name, state));
223 
224 	/*
225 	 * Make sure we attach only once the same Power Resource for a
226 	 * given state.
227 	 */
228 	SIMPLEQ_FOREACH(pr, &sc->sc_acpi->sc_pwrresdevs, p_next) {
229 		if (pr->p_node == node->parent &&
230 		    pr->p_res_state == state &&
231 		    pr->p_res_sc == sc) {
232 			DPRINTF(("error: pr for %s already set\n",
233 			   aml_nodename(pr->p_node)));
234 			return (EINVAL);
235 		}
236 	}
237 
238 	pr = malloc(sizeof(struct acpi_pwrres), M_DEVBUF, M_NOWAIT | M_ZERO);
239 	if (pr == NULL)
240 		return (ENOMEM);
241 
242 	pr->p_node = node->parent;
243 	pr->p_state = -1;
244 	pr->p_res_state = state;
245 	pr->p_res_sc = sc;
246 
247 	SIMPLEQ_INSERT_TAIL(&sc->sc_acpi->sc_pwrresdevs, pr, p_next);
248 
249 	return (0);
250 }
251 
252 int
acpipwrres_foundcons(struct aml_node * node,void * arg)253 acpipwrres_foundcons(struct aml_node *node, void *arg)
254 {
255 	struct acpipwrres_softc		*sc = (struct acpipwrres_softc *)arg;
256 	struct aml_value		res, *ref;
257 	int				i = 0;
258 
259 	memset(&res, 0, sizeof(res));
260 
261 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
262 		DPRINTF(("pwr: consumer not found\n"));
263 		return (1);
264 	}
265 
266 	if (res.type != AML_OBJTYPE_PACKAGE) {
267 		DPRINTF(("%s: %s is not a package\n", DEVNAME(sc),
268 		    aml_nodename(node)));
269 		aml_freevalue(&res);
270 		return (1);
271 	}
272 
273 	DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node)));
274 	if (!strcmp(node->name, "_PRW"))
275 		i = 2;          /* _PRW first two values are ints */
276 
277 	for (; i < res.length; i++) {
278 		ref = res.v_package[i];
279 
280 		if (ref->type == AML_OBJTYPE_NAMEREF) {
281 			struct aml_node	*pnode;
282 
283 			pnode = aml_searchrel(sc->sc_acpi->sc_root,
284 			    aml_getname(ref->v_nameref));
285 			if (pnode == NULL) {
286 				DPRINTF(("%s: device %s not found\n",
287 				    DEVNAME(sc), ref->v_string));
288 				continue;
289 			}
290 			ref = pnode->value;
291 		}
292 
293 		if (ref->type == AML_OBJTYPE_OBJREF)
294 			ref = ref->v_objref.ref;
295 
296 		if (ref->type != AML_OBJTYPE_POWERRSRC) {
297 			DPRINTF(("%s: object reference has a wrong type (%d)\n",
298 			    DEVNAME(sc), ref->type));
299 			continue;
300 		}
301 
302 		if (ref->node == sc->sc_devnode)
303 			(void)acpipwrres_addcons(sc, node);
304 	}
305 	aml_freevalue(&res);
306 
307 	return (0);
308 }
309