xref: /netbsd-src/sys/dev/acpi/acpi_pmtr.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: acpi_pmtr.c,v 1.1 2011/01/05 20:08:12 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2011 Jukka Ruohonen <jruohonen@iki.fi>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: acpi_pmtr.c,v 1.1 2011/01/05 20:08:12 jruoho Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/module.h>
34 
35 #include <dev/acpi/acpireg.h>
36 #include <dev/acpi/acpivar.h>
37 
38 #include <dev/sysmon/sysmonvar.h>
39 
40 #define _COMPONENT			ACPI_RESOURCE_COMPONENT
41 ACPI_MODULE_NAME			("acpi_pmtr")
42 
43 #define ACPIPMTR_CAP_FLAGS		0
44 #define ACPIPMTR_CAP_UNIT		1
45 #define ACPIPMTR_CAP_TYPE		2
46 #define ACPIPMTR_CAP_ACCURACY		3
47 #define ACPIPMTR_CAP_SAMPLING		4
48 #define ACPIPMTR_CAP_IVAL_MIN		5
49 #define ACPIPMTR_CAP_IVAL_MAX		6
50 #define ACPIPMTR_CAP_HYSTERESIS		7
51 #define ACPIPMTR_CAP_HWLIMIT		8
52 #define ACPIPMTR_CAP_HWLIMIT_MIN	9
53 #define ACPIPMTR_CAP_HWLIMIT_MAX	10
54 #define ACPIPMTR_CAP_COUNT		11
55 /*      ACPIPMTR_CAP_MODEL		11 */
56 /*      ACPIPMTR_CAP_SERIAL		12 */
57 /*      ACPIPMTR_CAP_OEM		13 */
58 
59 #define ACPIPMTR_FLAGS_MEASURE		__BIT(0)
60 #define ACPIPMTR_FLAGS_TRIP		__BIT(1)
61 #define ACPIPMTR_FLAGS_HWLIMIT		__BIT(2)
62 #define ACPIPMTR_FLAGS_NOTIFY		__BIT(3)
63 #define ACPIPMTR_FLAGS_DISCHARGE	__BIT(8)
64 
65 #define ACPIPMTR_POWER_INPUT		0x00
66 #define ACPIPMTR_POWER_OUTPUT		0x01
67 
68 #define ACPIPMTR_NOTIFY_CAP		0x80
69 #define ACPIPMTR_NOTIFY_TRIP		0x81
70 #define ACPIPMTR_NOTIFY_HWLIMIT1	0x82
71 #define ACPIPMTR_NOTIFY_HWLIMIT2	0x83
72 #define ACPIPMTR_NOTIFY_INTERVAL	0x84
73 
74 struct acpipmtr_softc {
75 	device_t		 sc_dev;
76 	struct acpi_devnode	*sc_node;
77 	struct sysmon_envsys	*sc_sme;
78 	envsys_data_t		 sc_sensor_i;
79 	envsys_data_t		 sc_sensor_o;
80 	uint32_t		 sc_cap[ACPIPMTR_CAP_COUNT];
81 	int32_t			 sc_interval;
82 };
83 
84 const char * const acpi_pmtr_ids[] = {
85 	"ACPI000D",
86 	NULL
87 };
88 
89 static int	acpipmtr_match(device_t, cfdata_t, void *);
90 static void	acpipmtr_attach(device_t, device_t, void *);
91 static int	acpipmtr_detach(device_t, int);
92 static bool	acpipmtr_cap_get(device_t, bool);
93 static bool	acpipmtr_dev_print(device_t);
94 static bool	acpipmtr_sensor_init(device_t);
95 static void	acpipmtr_sensor_type(device_t);
96 static int32_t	acpipmtr_sensor_get(device_t, const char *);
97 static int32_t	acpipmtr_sensor_get_reading(device_t);
98 static int32_t	acpipmtr_sensor_get_interval(device_t);
99 static void	acpipmtr_sensor_refresh(struct sysmon_envsys*,envsys_data_t *);
100 static void	acpipmtr_notify(ACPI_HANDLE, uint32_t, void *);
101 
102 CFATTACH_DECL_NEW(acpipmtr, sizeof(struct acpipmtr_softc),
103     acpipmtr_match, acpipmtr_attach, acpipmtr_detach, NULL);
104 
105 static int
106 acpipmtr_match(device_t parent, cfdata_t match, void *aux)
107 {
108 	struct acpi_attach_args *aa = aux;
109 
110 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
111 		return 0;
112 
113 	return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_pmtr_ids);
114 }
115 
116 static void
117 acpipmtr_attach(device_t parent, device_t self, void *aux)
118 {
119 	struct acpipmtr_softc *sc = device_private(self);
120 	struct acpi_attach_args *aa = aux;
121 	uint32_t acc;
122 
123 	sc->sc_sme = NULL;
124 	sc->sc_dev = self;
125 	sc->sc_node = aa->aa_node;
126 
127 	aprint_naive("\n");
128 	aprint_normal(": ACPI Power Meter\n");
129 
130 	(void)pmf_device_register(self, NULL, NULL);
131 
132 	if (acpipmtr_cap_get(self, true) != true)
133 		return;
134 
135 	if (acpipmtr_sensor_init(self) != true)
136 		return;
137 
138 	(void)acpipmtr_dev_print(self);
139 	(void)acpi_register_notify(sc->sc_node, acpipmtr_notify);
140 
141 	if ((acc = sc->sc_cap[ACPIPMTR_CAP_ACCURACY]) == 0)
142 		acc = 100000;
143 
144 	aprint_verbose_dev(self,
145 	    "measuring %s power at %u.%u %% accuracy, %u ms sampling\n",
146 	    (sc->sc_cap[ACPIPMTR_CAP_TYPE] != 0) ? "output" : "input",
147 	    acc / 1000, acc % 1000, sc->sc_cap[ACPIPMTR_CAP_SAMPLING]);
148 
149 	aprint_debug_dev(self, "%s hw-limits, capabilities 0x%02x\n",
150 	    (sc->sc_cap[ACPIPMTR_CAP_HWLIMIT] != 0) ? "rw" : "ro",
151 	     sc->sc_cap[ACPIPMTR_CAP_FLAGS]);
152 }
153 
154 static int
155 acpipmtr_detach(device_t self, int flags)
156 {
157 	struct acpipmtr_softc *sc = device_private(self);
158 
159 	pmf_device_deregister(self);
160 	acpi_deregister_notify(sc->sc_node);
161 
162 	if (sc->sc_sme != NULL)
163 		sysmon_envsys_unregister(sc->sc_sme);
164 
165 	return 0;
166 }
167 
168 static bool
169 acpipmtr_cap_get(device_t self, bool print)
170 {
171 	struct acpipmtr_softc *sc = device_private(self);
172 	ACPI_OBJECT *elm, *obj;
173 	ACPI_BUFFER buf;
174 	ACPI_STATUS rv;
175 	uint32_t i;
176 
177 	for (i = 0; i < __arraycount(sc->sc_cap); i++)
178 		sc->sc_cap[i] = 0;
179 
180 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMC", &buf);
181 
182 	if (ACPI_FAILURE(rv))
183 		goto out;
184 
185 	obj = buf.Pointer;
186 
187 	if (obj->Type != ACPI_TYPE_PACKAGE) {
188 		rv = AE_TYPE;
189 		goto out;
190 	}
191 
192 	elm = obj->Package.Elements;
193 
194 	if (obj->Package.Count != 14) {
195 		rv = AE_LIMIT;
196 		goto out;
197 	}
198 
199 	CTASSERT(__arraycount(sc->sc_cap) == 11);
200 
201 	for (i = 0; i < __arraycount(sc->sc_cap); i++) {
202 
203 		if (elm[i].Type != ACPI_TYPE_INTEGER) {
204 			rv = AE_TYPE;
205 			goto out;
206 		}
207 
208 		if (elm[i].Integer.Value > UINT32_MAX) {
209 			rv = AE_AML_NUMERIC_OVERFLOW;
210 			goto out;
211 		}
212 
213 		sc->sc_cap[i] = elm[i].Integer.Value;
214 	}
215 
216 	if (print != true)
217 		goto out;
218 
219 	for (; i < 14; i++) {
220 
221 		if (elm[i].Type != ACPI_TYPE_STRING)
222 			goto out;
223 
224 		if (elm[i].String.Pointer == NULL)
225 			goto out;
226 
227 		if (elm[i].String.Pointer[0] == '\0')
228 			goto out;
229 	}
230 
231 	aprint_debug_dev(self, "%s, serial %s, "
232 	    "model %s\n", elm[13].String.Pointer,
233 	    elm[12].String.Pointer, elm[11].String.Pointer);
234 
235 out:
236 	if (ACPI_FAILURE(rv))
237 		aprint_error_dev(self, "failed to evaluate _PMC: %s\n",
238 		    AcpiFormatException(rv));
239 
240 	if (buf.Pointer != NULL)
241 		ACPI_FREE(buf.Pointer);
242 
243 	return (rv != AE_OK) ? false : true;
244 }
245 
246 static bool
247 acpipmtr_dev_print(device_t self)
248 {
249 	struct acpipmtr_softc *sc = device_private(self);
250 	struct acpi_devnode *ad;
251 	ACPI_OBJECT *elm, *obj;
252 	ACPI_BUFFER buf;
253 	ACPI_HANDLE hdl;
254 	ACPI_STATUS rv;
255 	uint32_t i, n;
256 
257 	/*
258 	 * The _PMD method returns a package of devices whose total power
259 	 * drawn should roughly correspond with the readings from the meter.
260 	 */
261 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMD", &buf);
262 
263 	if (ACPI_FAILURE(rv))
264 		goto out;
265 
266 	obj = buf.Pointer;
267 
268 	if (obj->Type != ACPI_TYPE_PACKAGE) {
269 		rv = AE_TYPE;
270 		goto out;
271 	}
272 
273 	n = obj->Package.Count;
274 
275 	if (n == 0) {
276 		rv = AE_LIMIT;
277 		goto out;
278 	}
279 
280 	aprint_debug_dev(self, "measured devices: ");
281 
282 	for (i = 0; i < n; i++) {
283 
284 		elm = &obj->Package.Elements[i];
285 		rv = acpi_eval_reference_handle(elm, &hdl);
286 
287 		if (ACPI_FAILURE(rv))
288 			continue;
289 
290 		ad = acpi_get_node(hdl);
291 
292 		if (ACPI_FAILURE(rv))
293 			continue;
294 
295 		aprint_debug("%s ", ad->ad_name);
296 	}
297 
298 	aprint_debug("\n");
299 
300 out:
301 	if (ACPI_FAILURE(rv))
302 		aprint_debug_dev(self, "failed to evaluate _PMD: %s\n",
303 		    AcpiFormatException(rv));
304 
305 	if (buf.Pointer != NULL)
306 		ACPI_FREE(buf.Pointer);
307 
308 	return (rv != AE_OK) ? false : true;
309 }
310 
311 static bool
312 acpipmtr_sensor_init(device_t self)
313 {
314 	struct acpipmtr_softc *sc = device_private(self);
315 	const size_t siz = sizeof(sc->sc_sensor_i.desc);
316 	int32_t val;
317 
318 	val = acpipmtr_sensor_get_reading(self);
319 	sc->sc_interval = acpipmtr_sensor_get_interval(self);
320 
321 	if (val < 0) {
322 		aprint_error_dev(self, "failed to get sensor reading\n");
323 		return false;
324 	}
325 
326 	/* Always mW in ACPI 4.0. */
327 	if (sc->sc_cap[ACPIPMTR_CAP_UNIT] != 0)
328 		aprint_error_dev(self, "invalid measurement unit\n");
329 
330 	sc->sc_sme = sysmon_envsys_create();
331 
332 	sc->sc_sensor_i.units = ENVSYS_SWATTS;
333 	sc->sc_sensor_o.units = ENVSYS_SWATTS;
334 	sc->sc_sensor_i.value_cur = val * 1000;
335 	sc->sc_sensor_o.value_cur = val * 1000;
336 
337 	acpipmtr_sensor_type(self);
338 
339 	(void)strlcpy(sc->sc_sensor_i.desc, "input power", siz);
340 	(void)strlcpy(sc->sc_sensor_o.desc, "output power", siz);
341 
342 	sc->sc_sme->sme_cookie = self;
343 	sc->sc_sme->sme_flags = SME_POLL_ONLY;
344 	sc->sc_sme->sme_name = device_xname(self);
345 	sc->sc_sme->sme_refresh = acpipmtr_sensor_refresh;
346 
347 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_i) != 0)
348 		goto fail;
349 
350 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_o) != 0)
351 		goto fail;
352 
353 	if (sysmon_envsys_register(sc->sc_sme) != 0)
354 		goto fail;
355 
356 	return true;
357 
358 fail:
359 	aprint_error_dev(self, "failed to initialize sysmon\n");
360 
361 	sysmon_envsys_destroy(sc->sc_sme);
362 	sc->sc_sme = NULL;
363 
364 	return false;
365 }
366 
367 static void
368 acpipmtr_sensor_type(device_t self)
369 {
370 	struct acpipmtr_softc *sc = device_private(self);
371 
372 	switch (sc->sc_cap[ACPIPMTR_CAP_TYPE]) {
373 
374 	case ACPIPMTR_POWER_INPUT:
375 		sc->sc_sensor_i.state = ENVSYS_SVALID;
376 		sc->sc_sensor_o.state = ENVSYS_SINVALID;
377 		break;
378 
379 	case ACPIPMTR_POWER_OUTPUT:
380 		sc->sc_sensor_i.state = ENVSYS_SINVALID;
381 		sc->sc_sensor_o.state = ENVSYS_SVALID;
382 		break;
383 
384 	default:
385 		sc->sc_sensor_i.state = ENVSYS_SINVALID;
386 		sc->sc_sensor_o.state = ENVSYS_SINVALID;
387 		break;
388 	}
389 }
390 
391 static int32_t
392 acpipmtr_sensor_get(device_t self, const char *path)
393 {
394 	struct acpipmtr_softc *sc = device_private(self);
395 	ACPI_INTEGER val = 0;
396 	ACPI_STATUS rv;
397 
398 	rv = acpi_eval_integer(sc->sc_node->ad_handle, path, &val);
399 
400 	if (ACPI_FAILURE(rv))
401 		goto fail;
402 
403 	if (val == 0 || val > INT32_MAX) {
404 		rv = AE_LIMIT;
405 		goto fail;
406 	}
407 
408 	return val;
409 
410 fail:
411 	aprint_debug_dev(self, "failed to evaluate "
412 	    "%s: %s\n", path, AcpiFormatException(rv));
413 
414 	return -1;
415 }
416 
417 static int32_t
418 acpipmtr_sensor_get_reading(device_t self)
419 {
420 	return acpipmtr_sensor_get(self, "_PMM");
421 }
422 
423 static int32_t
424 acpipmtr_sensor_get_interval(device_t self)
425 {
426 	return acpipmtr_sensor_get(self, "_GAI");
427 }
428 
429 static void
430 acpipmtr_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
431 {
432 	device_t self = sme->sme_cookie;
433 	struct acpipmtr_softc *sc;
434 	int32_t val;
435 
436 	sc = device_private(self);
437 
438 	sc->sc_sensor_i.state = ENVSYS_SINVALID;
439 	sc->sc_sensor_o.state = ENVSYS_SINVALID;
440 
441 	val = acpipmtr_sensor_get_reading(self) * 1000;
442 
443 	if (val < 0)
444 		return;
445 
446 	sc->sc_sensor_i.value_cur = val;
447 	sc->sc_sensor_o.value_cur = val;
448 
449 	acpipmtr_sensor_type(self);
450 }
451 
452 static void
453 acpipmtr_notify(ACPI_HANDLE hdl, uint32_t evt, void *aux)
454 {
455 	struct acpipmtr_softc *sc;
456 	device_t self = aux;
457 	int32_t val;
458 
459 	sc = device_private(self);
460 
461 	switch (evt) {
462 
463 	case ACPIPMTR_NOTIFY_CAP:
464 
465 		if (acpipmtr_cap_get(self, false) != true)
466 			break;
467 
468 		acpipmtr_sensor_type(self);
469 		break;
470 
471 	case ACPIPMTR_NOTIFY_INTERVAL:
472 		val = acpipmtr_sensor_get_interval(self);
473 
474 		if (val < 0 || val == sc->sc_interval)
475 			break;
476 
477 		aprint_debug_dev(self, "averaging interval changed "
478 		    "from %u ms to %u ms\n", sc->sc_interval, val);
479 
480 		sc->sc_interval = val;
481 		break;
482 
483 	case ACPIPMTR_NOTIFY_TRIP:	/* AE_SUPPORT */
484 	case ACPIPMTR_NOTIFY_HWLIMIT1:	/* AE_SUPPORT */
485 	case ACPIPMTR_NOTIFY_HWLIMIT2:	/* AE_SUPPORT */
486 		break;
487 
488 	default:
489 		aprint_debug_dev(self, "unknown notify 0x%02x\n", evt);
490 	}
491 }
492 
493 #ifdef _MODULE
494 
495 MODULE(MODULE_CLASS_DRIVER, acpipmtr, NULL);
496 
497 #include "ioconf.c"
498 
499 static int
500 acpipmtr_modcmd(modcmd_t cmd, void *context)
501 {
502 
503 	switch (cmd) {
504 
505 	case MODULE_CMD_INIT:
506 		return config_init_component(cfdriver_ioconf_acpipmtr,
507 		    cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr);
508 
509 	case MODULE_CMD_FINI:
510 		return config_fini_component(cfdriver_ioconf_acpipmtr,
511 		    cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr);
512 
513 	default:
514 		return ENOTTY;
515 	}
516 }
517 
518 #endif	/* _MODULE */
519