1 /* $OpenBSD: acpipwrres.c,v 1.16 2024/08/12 17:24:58 kettenis 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 sc->sc_state = ACPIPWRRES_ON;
156 }
157
158 return (0);
159 }
160
161 int
acpipwrres_ref_decr(struct acpipwrres_softc * sc,struct aml_node * node)162 acpipwrres_ref_decr(struct acpipwrres_softc *sc, struct aml_node *node)
163 {
164 if (!acpipwrres_hascons(sc, node))
165 return (1);
166
167 DPRINTF(("%s: dev %s OFF %d\n", DEVNAME(sc), node->name,
168 sc->sc_cons_ref));
169
170 if (--sc->sc_cons_ref == 0) {
171 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0,
172 NULL, NULL);
173 sc->sc_state = ACPIPWRRES_OFF;
174 }
175
176 return (0);
177 }
178
179 int
acpipwrres_hascons(struct acpipwrres_softc * sc,struct aml_node * node)180 acpipwrres_hascons(struct acpipwrres_softc *sc, struct aml_node *node)
181 {
182 struct acpipwrres_consumer *cons;
183
184 SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next) {
185 if (cons->cs_node == node)
186 return (1);
187 }
188
189 return (0);
190 }
191
192 int
acpipwrres_addcons(struct acpipwrres_softc * sc,struct aml_node * node)193 acpipwrres_addcons(struct acpipwrres_softc *sc, struct aml_node *node)
194 {
195 struct acpipwrres_consumer *cons;
196 struct acpi_pwrres *pr;
197 int state;
198
199 /*
200 * Add handlers to put the device into Dx states.
201 *
202 * XXX What about PRW?
203 */
204 if (strcmp(node->name, "_PR0") == 0) {
205 state = ACPI_STATE_D0;
206 } else if (strcmp(node->name, "_PR1") == 0) {
207 state = ACPI_STATE_D1;
208 } else if (strcmp(node->name, "_PR2") == 0) {
209 state = ACPI_STATE_D2;
210 } else if (strcmp(node->name, "_PR3") == 0) {
211 state = ACPI_STATE_D3;
212 } else {
213 return (0);
214 }
215
216 if (!acpipwrres_hascons(sc, node->parent)) {
217 cons = malloc(sizeof(*cons), M_DEVBUF, M_NOWAIT | M_ZERO);
218 if (cons == NULL)
219 return (ENOMEM);
220
221 cons->cs_node = node->parent;
222 SIMPLEQ_INSERT_TAIL(&sc->sc_cons, cons, cs_next);
223 }
224
225 DPRINTF(("%s: resource for %s (D%d) \n", DEVNAME(sc),
226 node->parent->name, state));
227
228 /*
229 * Make sure we attach only once the same Power Resource for a
230 * given state.
231 */
232 SIMPLEQ_FOREACH(pr, &sc->sc_acpi->sc_pwrresdevs, p_next) {
233 if (pr->p_node == node->parent &&
234 pr->p_res_state == state &&
235 pr->p_res_sc == sc) {
236 DPRINTF(("error: pr for %s already set\n",
237 aml_nodename(pr->p_node)));
238 return (EINVAL);
239 }
240 }
241
242 pr = malloc(sizeof(struct acpi_pwrres), M_DEVBUF, M_NOWAIT | M_ZERO);
243 if (pr == NULL)
244 return (ENOMEM);
245
246 pr->p_node = node->parent;
247 pr->p_state = -1;
248 pr->p_res_state = state;
249 pr->p_res_sc = sc;
250
251 SIMPLEQ_INSERT_TAIL(&sc->sc_acpi->sc_pwrresdevs, pr, p_next);
252
253 return (0);
254 }
255
256 int
acpipwrres_foundcons(struct aml_node * node,void * arg)257 acpipwrres_foundcons(struct aml_node *node, void *arg)
258 {
259 struct acpipwrres_softc *sc = (struct acpipwrres_softc *)arg;
260 struct aml_value res, *ref;
261 int i = 0;
262
263 memset(&res, 0, sizeof(res));
264
265 if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
266 DPRINTF(("pwr: consumer not found\n"));
267 return (1);
268 }
269
270 if (res.type != AML_OBJTYPE_PACKAGE) {
271 DPRINTF(("%s: %s is not a package\n", DEVNAME(sc),
272 aml_nodename(node)));
273 aml_freevalue(&res);
274 return (1);
275 }
276
277 DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node)));
278 if (!strcmp(node->name, "_PRW"))
279 i = 2; /* _PRW first two values are ints */
280
281 for (; i < res.length; i++) {
282 ref = res.v_package[i];
283
284 if (ref->type == AML_OBJTYPE_NAMEREF) {
285 struct aml_node *pnode;
286
287 pnode = aml_searchrel(sc->sc_acpi->sc_root,
288 aml_getname(ref->v_nameref));
289 if (pnode == NULL) {
290 DPRINTF(("%s: device %s not found\n",
291 DEVNAME(sc), ref->v_string));
292 continue;
293 }
294 ref = pnode->value;
295 }
296
297 if (ref->type == AML_OBJTYPE_OBJREF)
298 ref = ref->v_objref.ref;
299
300 if (ref->type != AML_OBJTYPE_POWERRSRC) {
301 DPRINTF(("%s: object reference has a wrong type (%d)\n",
302 DEVNAME(sc), ref->type));
303 continue;
304 }
305
306 if (ref->node == sc->sc_devnode)
307 (void)acpipwrres_addcons(sc, node);
308 }
309 aml_freevalue(&res);
310
311 return (0);
312 }
313