Line data Source code
1 : /* $OpenBSD: acpipwrres.c,v 1.7 2014/09/14 14:17:24 jsg Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
5 : * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org>
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 : 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
81 0 : acpipwrres_match(struct device *parent, void *match, void *aux)
82 : {
83 0 : struct acpi_attach_args *aaa = aux;
84 0 : struct cfdata *cf = match;
85 :
86 0 : if (aaa->aaa_name == NULL || strcmp(aaa->aaa_name,
87 0 : cf->cf_driver->cd_name) != 0 || aaa->aaa_table != NULL)
88 0 : return (0);
89 :
90 0 : return (1);
91 0 : }
92 :
93 : void
94 0 : acpipwrres_attach(struct device *parent, struct device *self, void *aux)
95 : {
96 0 : struct acpipwrres_softc *sc = (struct acpipwrres_softc *)self;
97 0 : struct acpi_attach_args *aaa = aux;
98 0 : struct aml_value res;
99 : struct acpipwrres_consumer *cons;
100 :
101 : extern struct aml_node aml_root;
102 :
103 0 : sc->sc_acpi = (struct acpi_softc *)parent;
104 0 : sc->sc_devnode = aaa->aaa_node;
105 0 : memset(&res, 0, sizeof res);
106 :
107 0 : printf(": %s", sc->sc_devnode->name);
108 :
109 0 : if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
110 0 : sc->sc_state = (int)aml_val2int(&res);
111 0 : if (sc->sc_state != ACPIPWRRES_ON &&
112 0 : sc->sc_state != ACPIPWRRES_OFF)
113 0 : sc->sc_state = ACPIPWRRES_UNK;
114 : } else
115 0 : sc->sc_state = ACPIPWRRES_UNK;
116 : DPRINTF(("\n%s: state = %d\n", DEVNAME(sc), sc->sc_state));
117 0 : if (aml_evalnode(sc->sc_acpi, aaa->aaa_node, 0, NULL, &res) == 0) {
118 0 : sc->sc_level = res.v_powerrsrc.pwr_level;
119 0 : sc->sc_order = res.v_powerrsrc.pwr_order;
120 : DPRINTF(("%s: level = %d, order %d\n", DEVNAME(sc),
121 : sc->sc_level, sc->sc_order));
122 0 : aml_freevalue(&res);
123 0 : }
124 :
125 : /* Get the list of consumers */
126 0 : SIMPLEQ_INIT(&sc->sc_cons);
127 : #if notyet
128 : aml_find_node(&aml_root, "_PRW", acpipwrres_foundcons, sc);
129 : #endif
130 0 : aml_find_node(&aml_root, "_PR0", acpipwrres_foundcons, sc);
131 0 : aml_find_node(&aml_root, "_PR1", acpipwrres_foundcons, sc);
132 0 : aml_find_node(&aml_root, "_PR2", acpipwrres_foundcons, sc);
133 0 : aml_find_node(&aml_root, "_PR3", acpipwrres_foundcons, sc);
134 :
135 : DPRINTF(("%s", DEVNAME(sc)));
136 0 : if (!SIMPLEQ_EMPTY(&sc->sc_cons)) {
137 0 : printf(", resource for");
138 0 : SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next)
139 0 : printf(" %s%s", cons->cs_node->name,
140 0 : (SIMPLEQ_NEXT(cons, cs_next) == NULL) ? "" : ",");
141 : }
142 0 : printf("\n");
143 0 : }
144 :
145 : int
146 0 : acpipwrres_ref_incr(struct acpipwrres_softc *sc, struct aml_node *node)
147 : {
148 0 : struct aml_value res;
149 :
150 0 : if (!acpipwrres_hascons(sc, node))
151 0 : return (1);
152 :
153 : DPRINTF(("%s: dev %s ON %d\n", DEVNAME(sc), node->name,
154 : sc->sc_cons_ref));
155 :
156 0 : if (sc->sc_cons_ref++ == 0) {
157 0 : memset(&res, 0, sizeof(res));
158 0 : aml_evalname(sc->sc_acpi, sc->sc_devnode, "_ON", 0,
159 : NULL, &res);
160 0 : aml_freevalue(&res);
161 0 : }
162 :
163 0 : return (0);
164 0 : }
165 :
166 : int
167 0 : acpipwrres_ref_decr(struct acpipwrres_softc *sc, struct aml_node *node)
168 : {
169 0 : struct aml_value res;
170 :
171 0 : if (!acpipwrres_hascons(sc, node))
172 0 : return (1);
173 :
174 : DPRINTF(("%s: dev %s OFF %d\n", DEVNAME(sc), node->name,
175 : sc->sc_cons_ref));
176 :
177 0 : if (--sc->sc_cons_ref == 0) {
178 0 : memset(&res, 0, sizeof(res));
179 0 : aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0,
180 : NULL, &res);
181 0 : aml_freevalue(&res);
182 0 : }
183 :
184 0 : return (0);
185 0 : }
186 :
187 : int
188 0 : acpipwrres_hascons(struct acpipwrres_softc *sc, struct aml_node *node)
189 : {
190 : struct acpipwrres_consumer *cons;
191 :
192 0 : SIMPLEQ_FOREACH(cons, &sc->sc_cons, cs_next) {
193 0 : if (cons->cs_node == node)
194 0 : return (1);
195 : }
196 :
197 0 : return (0);
198 0 : }
199 :
200 : int
201 0 : acpipwrres_addcons(struct acpipwrres_softc *sc, struct aml_node *node)
202 : {
203 : struct acpipwrres_consumer *cons;
204 : struct acpi_pwrres *pr;
205 : int state;
206 :
207 : /*
208 : * Add handlers to put the device into Dx states.
209 : *
210 : * XXX What about PRW?
211 : */
212 0 : if (strcmp(node->name, "_PR0") == 0) {
213 : state = ACPI_STATE_D0;
214 0 : } else if (strcmp(node->name, "_PR1") == 0) {
215 : state = ACPI_STATE_D1;
216 0 : } else if (strcmp(node->name, "_PR2") == 0) {
217 : state = ACPI_STATE_D2;
218 0 : } else if (strcmp(node->name, "_PR3") == 0) {
219 : state = ACPI_STATE_D3;
220 : } else {
221 0 : return (0);
222 : }
223 :
224 0 : if (!acpipwrres_hascons(sc, node->parent)) {
225 0 : cons = malloc(sizeof(*cons), M_DEVBUF, M_NOWAIT | M_ZERO);
226 0 : if (cons == NULL)
227 0 : return (ENOMEM);
228 :
229 0 : cons->cs_node = node->parent;
230 0 : SIMPLEQ_INSERT_TAIL(&sc->sc_cons, cons, cs_next);
231 0 : }
232 :
233 : DPRINTF(("%s: resource for %s (D%d) \n", DEVNAME(sc),
234 : node->parent->name, state));
235 :
236 : /*
237 : * Make sure we attach only once the same Power Resource for a
238 : * given state.
239 : */
240 0 : SIMPLEQ_FOREACH(pr, &sc->sc_acpi->sc_pwrresdevs, p_next) {
241 0 : if (pr->p_node == node->parent &&
242 0 : pr->p_res_state == state &&
243 0 : pr->p_res_sc == sc) {
244 : DPRINTF(("error: pr for %s already set\n",
245 : aml_nodename(pr->p_node)));
246 0 : return (EINVAL);
247 : }
248 : }
249 :
250 0 : pr = malloc(sizeof(struct acpi_pwrres), M_DEVBUF, M_NOWAIT | M_ZERO);
251 0 : if (pr == NULL)
252 0 : return (ENOMEM);
253 :
254 0 : pr->p_node = node->parent;
255 0 : pr->p_state = -1;
256 0 : pr->p_res_state = state;
257 0 : pr->p_res_sc = sc;
258 :
259 0 : SIMPLEQ_INSERT_TAIL(&sc->sc_acpi->sc_pwrresdevs, pr, p_next);
260 :
261 0 : return (0);
262 0 : }
263 :
264 : int
265 0 : acpipwrres_foundcons(struct aml_node *node, void *arg)
266 : {
267 0 : struct acpipwrres_softc *sc = (struct acpipwrres_softc *)arg;
268 0 : struct aml_value res, *ref;
269 : int i = 0;
270 :
271 : extern struct aml_node aml_root;
272 :
273 0 : memset(&res, 0, sizeof(res));
274 :
275 0 : if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
276 : DPRINTF(("pwr: consumer not found\n"));
277 0 : return (1);
278 : }
279 :
280 0 : if (res.type != AML_OBJTYPE_PACKAGE) {
281 : DPRINTF(("%s: %s is not a package\n", DEVNAME(sc),
282 : aml_nodename(node)));
283 0 : aml_freevalue(&res);
284 0 : return (1);
285 : }
286 :
287 : DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node)));
288 0 : if (!strcmp(node->name, "_PRW"))
289 0 : i = 2; /* _PRW first two values are ints */
290 :
291 0 : for (; i < res.length; i++) {
292 0 : ref = res.v_package[i];
293 :
294 0 : if (ref->type == AML_OBJTYPE_STRING) {
295 : struct aml_node *pnode;
296 :
297 0 : pnode = aml_searchrel(&aml_root, ref->v_string);
298 0 : if (pnode == NULL) {
299 : DPRINTF(("%s: device %s not found\n",
300 : DEVNAME(sc), ref->v_string));
301 0 : continue;
302 : }
303 0 : ref = pnode->value;
304 0 : }
305 :
306 0 : if (ref->type == AML_OBJTYPE_OBJREF)
307 0 : ref = ref->v_objref.ref;
308 :
309 0 : if (ref->type != AML_OBJTYPE_POWERRSRC) {
310 : DPRINTF(("%s: object reference has a wrong type (%d)\n",
311 : DEVNAME(sc), ref->type));
312 : continue;
313 : }
314 :
315 0 : if (ref->node == sc->sc_devnode)
316 0 : (void)acpipwrres_addcons(sc, node);
317 : }
318 0 : aml_freevalue(&res);
319 :
320 0 : return (0);
321 0 : }
|