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