xref: /openbsd-src/sys/dev/acpi/acpipwrres.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 /* $OpenBSD: acpipwrres.c,v 1.4 2010/07/21 19:35:15 deraadt Exp $ */
2 /*
3  * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/param.h>
18 #include <sys/proc.h>
19 #include <sys/signalvar.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 
24 #include <machine/bus.h>
25 
26 #include <dev/acpi/acpireg.h>
27 #include <dev/acpi/acpivar.h>
28 #include <dev/acpi/acpidev.h>
29 #include <dev/acpi/amltypes.h>
30 #include <dev/acpi/dsdt.h>
31 
32 int	acpipwrres_match(struct device *, void *, void *);
33 void	acpipwrres_attach(struct device *, struct device *, void *);
34 int	acpipwrres_notify(struct aml_node *, int, void *);
35 
36 #define	NOTIFY_PWRRES_OFF	0
37 #define NOTIFY_PWRRES_ON	1
38 
39 #ifdef ACPIPWRRES_DEBUG
40 #define DPRINTF(x)	printf x
41 #else
42 #define DPRINTF(x)
43 #endif
44 
45 struct acpipwrres_softc {
46 	struct device		sc_dev;
47 
48 	bus_space_tag_t		sc_iot;
49 	bus_space_handle_t	sc_ioh;
50 
51 	struct acpi_softc	*sc_acpi;
52 	struct aml_node		*sc_devnode;
53 
54 	TAILQ_HEAD(acpipwrres_cons_h, acpipwrres_consumer)	sc_cons;
55 
56 	int	sc_level;
57 	int	sc_order;
58 	int	sc_ncons;
59 	int	sc_state;
60 #define ACPIPWRRES_OFF		0
61 #define ACPIPWRRES_ON		1
62 #define ACPIPWRRES_UNK		-1
63 };
64 
65 struct acpipwrres_consumer {
66 	struct aml_node				*cs_node;
67 	TAILQ_ENTRY(acpipwrres_consumer)	cs_link;
68 };
69 
70 struct cfattach acpipwrres_ca = {
71 	sizeof(struct acpipwrres_softc), acpipwrres_match, acpipwrres_attach
72 };
73 
74 struct cfdriver acpipwrres_cd = {
75 	NULL, "acpipwrres", DV_DULL
76 };
77 
78 int	acpipwrres_foundcons(struct aml_node *, void *);
79 
80 int
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
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 
100 	extern struct aml_node	aml_root;
101 
102 	sc->sc_acpi = (struct acpi_softc *)parent;
103 	sc->sc_devnode = aaa->aaa_node;
104 	memset(&res, 0, sizeof res);
105 
106 	printf(": %s\n", sc->sc_devnode->name);
107 
108 	aml_register_notify(sc->sc_devnode, aaa->aaa_dev,
109 	    acpipwrres_notify, sc, ACPIDEV_NOPOLL);
110 
111 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
112 		sc->sc_state = (int)aml_val2int(&res);
113 		if (sc->sc_state != ACPIPWRRES_ON &&
114 		    sc->sc_state != ACPIPWRRES_OFF)
115 			sc->sc_state = ACPIPWRRES_UNK;
116 	} else
117 		sc->sc_state = ACPIPWRRES_UNK;
118 	DPRINTF(("%s: state = %d\n", DEVNAME(sc), sc->sc_state));
119 	if (aml_evalnode(sc->sc_acpi, aaa->aaa_node, 0, NULL, &res) == 0) {
120 		sc->sc_level = res.v_powerrsrc.pwr_level;
121 		sc->sc_order = res.v_powerrsrc.pwr_order;
122 		DPRINTF(("%s: level = %d, order %d\n", DEVNAME(sc),
123 		    sc->sc_level, sc->sc_order));
124 		aml_freevalue(&res);
125 	}
126 
127 	/* Get the list of consumers */
128 	TAILQ_INIT(&sc->sc_cons);
129 	aml_find_node(&aml_root, "_PRW", acpipwrres_foundcons, sc);
130 	aml_find_node(&aml_root, "_PR0", acpipwrres_foundcons, sc);
131 	aml_find_node(&aml_root, "_PR1", acpipwrres_foundcons, sc);
132 	aml_find_node(&aml_root, "_PR2", acpipwrres_foundcons, sc);
133 }
134 
135 int
136 acpipwrres_notify(struct aml_node *node, int notify, void *arg)
137 {
138 	int				fmatch = 0;
139 	struct acpipwrres_consumer	*cons;
140 	struct acpipwrres_softc		*sc = arg;
141 	struct aml_value		res;
142 
143 	memset(&res, 0, sizeof res);
144 
145 	TAILQ_FOREACH(cons, &sc->sc_cons, cs_link)
146 		if (cons->cs_node == node) {
147 			fmatch = 1;
148 			break;
149 		}
150 	if (!fmatch)
151 		return (0);
152 
153 	switch (notify) {
154 	case NOTIFY_PWRRES_ON:
155 		DPRINTF(("pwr: on devs %d\n", sc->sc_ncons));
156 		if (sc->sc_ncons++ == 0)
157 			aml_evalname(sc->sc_acpi, sc->sc_devnode, "_ON", 0, NULL,
158 			    &res);
159 		aml_freevalue(&res);
160 		break;
161 	case NOTIFY_PWRRES_OFF:
162 		DPRINTF(("pwr: off devs %d\n", sc->sc_ncons));
163 		if (--sc->sc_ncons == 0)
164 			aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0, NULL,
165 			    &res);
166 		aml_freevalue(&res);
167 		break;
168 	default:
169 		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
170 		break;
171 	}
172 
173 	return (0);
174 }
175 
176 int
177 acpipwrres_foundcons(struct aml_node *node, void *arg)
178 {
179 	int				i = 0;
180 	struct acpipwrres_consumer	*cons;
181 	struct aml_node			*pnode;
182 	struct acpipwrres_softc		*sc = (struct acpipwrres_softc *)arg;
183 	struct aml_value		res;
184 
185 	extern struct aml_node	aml_root;
186 
187 	memset(&res, 0, sizeof res);
188 
189 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
190 		DPRINTF(("pwr: consumer not found\n"));
191 		return (-1);
192 	} else {
193 		DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node)));
194 		if (!strcmp(node->name, "_PRW"))
195 			i = 2;          /* _PRW first two values are ints */
196 		for (; i < res.length; i++) {
197 			pnode = aml_searchname(&aml_root,
198 			    res.v_package[i]->v_string);
199 			if (pnode == sc->sc_devnode) {
200 				DPRINTF(("%s: consumer match\n", DEVNAME(sc)));
201 				cons = malloc(sizeof *cons, M_DEVBUF,
202 				    M_WAITOK | M_ZERO);
203 				cons->cs_node = pnode;
204 				TAILQ_INSERT_HEAD(&sc->sc_cons, cons, cs_link);
205 			}
206 		}
207 	}
208 
209 	return (0);
210 }
211