xref: /openbsd/sys/dev/acpi/atk0110.c (revision 9593dc34)
1 /*	$OpenBSD: atk0110.c,v 1.20 2024/09/04 07:54:52 mglocker Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/sensors.h>
24 
25 #include <dev/acpi/acpireg.h>
26 #include <dev/acpi/acpivar.h>
27 #include <dev/acpi/acpidev.h>
28 #include <dev/acpi/amltypes.h>
29 #include <dev/acpi/dsdt.h>
30 
31 /*
32  * ASUSTeK AI Booster (ACPI ATK0110).
33  *
34  * The driver was inspired by Takanori Watanabe's acpi_aiboost driver.
35  * http://cvsweb.freebsd.org/src/sys/dev/acpi_support/acpi_aiboost.c
36  *
37  * Special thanks goes to Sam Fourman Jr. for providing access to several
38  * ASUS boxes where the driver could be tested.
39  *
40  *							-- cnst.su.
41  */
42 
43 #define ATK_ID_MUX_HWMON	0x00000006
44 
45 #define ATK_CLASS(x)		(((x) >> 24) & 0xff)
46 #define ATK_CLASS_FREQ_CTL	3
47 #define ATK_CLASS_FAN_CTL	4
48 #define ATK_CLASS_HWMON		6
49 #define ATK_CLASS_MGMT		17
50 
51 #define ATK_TYPE(x)		(((x) >> 16) & 0xff)
52 #define ATK_TYPE_VOLT		2
53 #define ATK_TYPE_TEMP		3
54 #define ATK_TYPE_FAN		4
55 
56 #define AIBS_MORE_SENSORS
57 /* #define AIBS_VERBOSE */
58 
59 struct aibs_sensor {
60 	struct ksensor	s;
61 	int64_t		i;
62 	int64_t		l;
63 	int64_t		h;
64 	SIMPLEQ_ENTRY(aibs_sensor)	entry;
65 };
66 
67 struct aibs_softc {
68 	struct device		sc_dev;
69 
70 	struct acpi_softc	*sc_acpi;
71 	struct aml_node		*sc_devnode;
72 
73 	struct aml_node		*sc_ggrpnode;
74 	struct aml_node		*sc_gitmnode;
75 	struct aml_node		*sc_sitmnode;
76 	struct aml_node		*sc_rtmpnode;
77 	struct aml_node		*sc_rvltnode;
78 	struct aml_node		*sc_rfannode;
79 
80 	SIMPLEQ_HEAD(, aibs_sensor)	sc_sensorlist;
81 	struct ksensordev	sc_sensordev;
82 
83 	int			sc_mode;	/* 1 = new, 0 = old */
84 };
85 
86 /* Command buffer used for GITM and SITM methods */
87 struct aibs_cmd_buffer {
88 	uint32_t	id;
89 	uint32_t	param1;
90 	uint32_t	param2;
91 };
92 
93 /* Return buffer used by the GITM and SITM methods */
94 struct aibs_ret_buffer {
95 	uint32_t	flags;
96 	uint32_t	value;
97 	/* there is more stuff that is unknown */
98 };
99 
100 int	aibs_match(struct device *, void *, void *);
101 void	aibs_attach(struct device *, struct device *, void *);
102 int	aibs_notify(struct aml_node *, int, void *);
103 void	aibs_refresh(void *);
104 
105 void	aibs_attach_sif(struct aibs_softc *, enum sensor_type);
106 void	aibs_attach_new(struct aibs_softc *);
107 void	aibs_add_sensor(struct aibs_softc *, const char *);
108 void	aibs_refresh_r(struct aibs_softc *, struct aibs_sensor *);
109 int	aibs_getvalue(struct aibs_softc *, int64_t, int64_t *);
110 int	aibs_getpack(struct aibs_softc *, struct aml_node *, int64_t,
111 	    struct aml_value *);
112 void	aibs_probe(struct aibs_softc *);
113 int	aibs_find_cb(struct aml_node *, void *);
114 
115 
116 const struct cfattach aibs_ca = {
117 	sizeof(struct aibs_softc), aibs_match, aibs_attach
118 };
119 
120 struct cfdriver aibs_cd = {
121 	NULL, "aibs", DV_DULL
122 };
123 
124 static const char* aibs_hids[] = {
125 	"ATK0110",
126 	NULL
127 };
128 
129 int
aibs_match(struct device * parent,void * match,void * aux)130 aibs_match(struct device *parent, void *match, void *aux)
131 {
132 	struct acpi_attach_args	*aa = aux;
133 	struct cfdata		*cf = match;
134 
135 	return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
136 }
137 
138 void
aibs_attach(struct device * parent,struct device * self,void * aux)139 aibs_attach(struct device *parent, struct device *self, void *aux)
140 {
141 	struct aibs_softc	*sc = (struct aibs_softc *)self;
142 	struct acpi_attach_args	*aa = aux;
143 
144 	sc->sc_acpi = (struct acpi_softc *)parent;
145 	sc->sc_devnode = aa->aaa_node;
146 
147 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
148 	    sizeof(sc->sc_sensordev.xname));
149 	SIMPLEQ_INIT(&sc->sc_sensorlist);
150 
151 	aibs_probe(sc);
152 	printf("\n");
153 
154 	if (sc->sc_mode)
155 		aibs_attach_new(sc);
156 	else {
157 		aibs_attach_sif(sc, SENSOR_TEMP);
158 		aibs_attach_sif(sc, SENSOR_FANRPM);
159 		aibs_attach_sif(sc, SENSOR_VOLTS_DC);
160 	}
161 
162 	if (sc->sc_sensordev.sensors_count == 0) {
163 		printf("%s: no sensors found\n", DEVNAME(sc));
164 		return;
165 	}
166 
167 	sensordev_install(&sc->sc_sensordev);
168 
169 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
170 	    aibs_notify, sc, ACPIDEV_POLL);
171 }
172 
173 void
aibs_attach_sif(struct aibs_softc * sc,enum sensor_type st)174 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
175 {
176 	struct aml_value	res;
177 	struct aml_value	**v;
178 	int			i, n;
179 	char			name[] = "?SIF";
180 
181 	switch (st) {
182 	case SENSOR_TEMP:
183 		name[0] = 'T';
184 		break;
185 	case SENSOR_FANRPM:
186 		name[0] = 'F';
187 		break;
188 	case SENSOR_VOLTS_DC:
189 		name[0] = 'V';
190 		break;
191 	default:
192 		return;
193 	}
194 
195 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
196 		printf("%s: %s not found\n", DEVNAME(sc), name);
197 		aml_freevalue(&res);
198 		return;
199 	}
200 	if (res.type != AML_OBJTYPE_PACKAGE) {
201 		printf("%s: %s: not a package\n", DEVNAME(sc), name);
202 		aml_freevalue(&res);
203 		return;
204 	}
205 	v = res.v_package;
206 	if (v[0]->type != AML_OBJTYPE_INTEGER) {
207 		printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
208 		aml_freevalue(&res);
209 		return;
210 	}
211 
212 	n = v[0]->v_integer;
213 	if (res.length - 1 < n) {
214 		printf("%s: %s: invalid package\n", DEVNAME(sc), name);
215 		aml_freevalue(&res);
216 		return;
217 	} else if (res.length - 1 > n) {
218 		printf("%s: %s: malformed package: %i/%i",
219 		    DEVNAME(sc), name, n, res.length - 1);
220 #ifdef AIBS_MORE_SENSORS
221 		n = res.length - 1;
222 #endif
223 		printf(", assume %i\n", n);
224 	}
225 	if (n < 1) {
226 		printf("%s: %s: no members in the package\n",
227 		    DEVNAME(sc), name);
228 		aml_freevalue(&res);
229 		return;
230 	}
231 
232 	for (i = 0, v++; i < n; i++, v++) {
233 		if(v[0]->type != AML_OBJTYPE_NAMEREF) {
234 			printf("%s: %s: %i: not a nameref: %i type\n",
235 			    DEVNAME(sc), name, i, v[0]->type);
236 			continue;
237 		}
238 		aibs_add_sensor(sc, aml_getname(v[0]->v_nameref));
239 	}
240 
241 	aml_freevalue(&res);
242 }
243 
244 void
aibs_attach_new(struct aibs_softc * sc)245 aibs_attach_new(struct aibs_softc *sc)
246 {
247 	struct aml_value	res;
248 	int			i;
249 
250 	if (aibs_getpack(sc, sc->sc_ggrpnode, ATK_ID_MUX_HWMON, &res)) {
251 		printf("%s: GGRP: sensor enumeration failed\n", DEVNAME(sc));
252 		return;
253 	}
254 
255 	for (i = 0; i < res.length; i++) {
256 		struct aml_value	*r;
257 		r = res.v_package[i];
258 		if (r->type != AML_OBJTYPE_STRING) {
259 			printf("%s: %s: %i: not a string (type %i)\n",
260 			    DEVNAME(sc), "GGRP", i, r->type);
261 			continue;
262 		}
263 		aibs_add_sensor(sc, r->v_string);
264 	}
265 	aml_freevalue(&res);
266 }
267 
268 void
aibs_add_sensor(struct aibs_softc * sc,const char * name)269 aibs_add_sensor(struct aibs_softc *sc, const char *name)
270 {
271 	struct aml_value	 ri;
272 	struct aibs_sensor	*as;
273 	int			 len, lim1, lim2, ena;
274 
275 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
276 	    0, NULL, &ri)) {
277 		printf("%s: aibs_add_sensor: %s not found\n",
278 		    DEVNAME(sc), name);
279 		aml_freevalue(&ri);
280 		return;
281 	}
282 	if (ri.type != AML_OBJTYPE_PACKAGE) {
283 		printf("%s: aibs_add_sensor: %s: not a package\n",
284 		    DEVNAME(sc), name);
285 		aml_freevalue(&ri);
286 		return;
287 	}
288 	if (sc->sc_mode) {
289 		len = 7;
290 		lim1 = 4;
291 		lim2 = 5;
292 		ena = 6;
293 	} else {
294 		len = 5;
295 		lim1 = 2;
296 		lim2 = 3;
297 		ena = 4;
298 	}
299 
300 	if (ri.length != len ||
301 	    ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
302 	    ri.v_package[1]->type != AML_OBJTYPE_STRING ||
303 	    ri.v_package[lim1]->type != AML_OBJTYPE_INTEGER ||
304 	    ri.v_package[lim2]->type != AML_OBJTYPE_INTEGER ||
305 	    ri.v_package[ena]->type != AML_OBJTYPE_INTEGER) {
306 		printf("%s: aibs_add_sensor: %s: invalid package\n",
307 		    DEVNAME(sc), name);
308 		aml_freevalue(&ri);
309 		return;
310 	}
311 	as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO);
312 	if (!as) {
313 		printf("%s: aibs_add_sensor: %s: failed to allocate sensor\n",
314 		    DEVNAME(sc), name);
315 		aml_freevalue(&ri);
316 		return;
317 	}
318 	as->i = ri.v_package[0]->v_integer;
319 	switch (ATK_TYPE(as->i)) {
320 	case ATK_TYPE_VOLT:
321 		as->s.type = SENSOR_VOLTS_DC;
322 		break;
323 	case ATK_TYPE_TEMP:
324 		as->s.type = SENSOR_TEMP;
325 		break;
326 	case ATK_TYPE_FAN:
327 		as->s.type = SENSOR_FANRPM;
328 		break;
329 	default:
330 		printf("%s: aibs_add_sensor: %s: unknown sensor type %llx\n",
331 		    DEVNAME(sc), name, ri.v_package[0]->v_integer);
332 		aml_freevalue(&ri);
333 		free(as, M_DEVBUF, sizeof(*as));
334 		return;
335 	}
336 	strlcpy(as->s.desc, ri.v_package[1]->v_string,
337 	    sizeof(as->s.desc));
338 	as->l = ri.v_package[lim1]->v_integer;
339 	if (sc->sc_mode)
340 		/* the second limit is a actually a range */
341 		as->h = as->l + ri.v_package[lim2]->v_integer;
342 	else
343 		as->h = ri.v_package[lim2]->v_integer;
344 #ifdef AIBS_VERBOSE
345 	printf("%s: %4s: %s 0x%08llx %5lli / %5lli  0x%llx\n",
346 	    DEVNAME(sc), name, as->s.desc, as->i, as->l, as->h,
347 	    ri.v_package[ena]->v_integer);
348 #endif
349 	SIMPLEQ_INSERT_TAIL(&sc->sc_sensorlist, as, entry);
350 	sensor_attach(&sc->sc_sensordev, &as->s);
351 	aml_freevalue(&ri);
352 	return;
353 }
354 
355 void
aibs_refresh(void * arg)356 aibs_refresh(void *arg)
357 {
358 	struct aibs_softc	*sc = arg;
359 	struct aibs_sensor	*as;
360 
361 	SIMPLEQ_FOREACH(as, &sc->sc_sensorlist, entry)
362 		aibs_refresh_r(sc, as);
363 }
364 
365 void
aibs_refresh_r(struct aibs_softc * sc,struct aibs_sensor * as)366 aibs_refresh_r(struct aibs_softc *sc, struct aibs_sensor *as)
367 {
368 	struct ksensor		*s = &as->s;
369 	int64_t			v;
370 	const int64_t		l = as->l, h = as->h;
371 
372 	if (aibs_getvalue(sc, as->i, &v)) {
373 		s->flags |= SENSOR_FINVALID;
374 		return;
375 	}
376 	switch (s->type) {
377 	case SENSOR_TEMP:
378 		s->value = v * 100 * 1000 + 273150000;
379 		if (v == 0) {
380 			s->status = SENSOR_S_UNKNOWN;
381 			s->flags |= SENSOR_FINVALID;
382 		} else {
383 			if (v > h)
384 				s->status = SENSOR_S_CRIT;
385 			else if (v > l)
386 				s->status = SENSOR_S_WARN;
387 			else
388 				s->status = SENSOR_S_OK;
389 			s->flags &= ~SENSOR_FINVALID;
390 		}
391 		break;
392 	case SENSOR_FANRPM:
393 		s->value = v;
394 		/* some boards have strange limits for fans */
395 		if ((l != 0 && l < v && v < h) ||
396 		    (l == 0 && v > h))
397 			s->status = SENSOR_S_OK;
398 		else
399 			s->status = SENSOR_S_WARN;
400 		s->flags &= ~SENSOR_FINVALID;
401 		break;
402 	case SENSOR_VOLTS_DC:
403 		s->value = v * 1000;
404 		if (l < v && v < h)
405 			s->status = SENSOR_S_OK;
406 		else
407 			s->status = SENSOR_S_WARN;
408 		s->flags &= ~SENSOR_FINVALID;
409 		break;
410 	default:
411 		/* NOTREACHED */
412 		break;
413 	}
414 }
415 
416 int
aibs_getvalue(struct aibs_softc * sc,int64_t i,int64_t * v)417 aibs_getvalue(struct aibs_softc *sc, int64_t i, int64_t *v)
418 {
419 	struct aml_node		*n = sc->sc_gitmnode;
420 	struct aml_value	req, res;
421 	struct aibs_cmd_buffer	cmd;
422 	struct aibs_ret_buffer	ret;
423 	enum aml_objecttype	type;
424 
425 	if (sc->sc_mode) {
426 		cmd.id = i;
427 		cmd.param1 = 0;
428 		cmd.param2 = 0;
429 		type = req.type = AML_OBJTYPE_BUFFER;
430 		req.v_buffer = (uint8_t *)&cmd;
431 		req.length = sizeof(cmd);
432 	} else {
433 		switch (ATK_TYPE(i)) {
434 		case ATK_TYPE_TEMP:
435 			n = sc->sc_rtmpnode;
436 			break;
437 		case ATK_TYPE_FAN:
438 			n = sc->sc_rfannode;
439 			break;
440 		case ATK_TYPE_VOLT:
441 			n = sc->sc_rvltnode;
442 			break;
443 		default:
444 			return (-1);
445 		}
446 		type = req.type = AML_OBJTYPE_INTEGER;
447 		req.v_integer = i;
448 	}
449 
450 	if (aml_evalnode(sc->sc_acpi, n, 1, &req, &res)) {
451 		dprintf("%s: %s: %lld: evaluation failed\n",
452 		    DEVNAME(sc), n->name, i);
453 		aml_freevalue(&res);
454 		return (-1);
455 	}
456 	if (res.type != type) {
457 		dprintf("%s: %s: %lld: not an integer: type %i\n",
458 		    DEVNAME(sc), n->name, i, res.type);
459 		aml_freevalue(&res);
460 		return (-1);
461 	}
462 
463 	if (sc->sc_mode) {
464 		if (res.length < sizeof(ret)) {
465 			dprintf("%s: %s: %lld: result buffer too small\n",
466 			    DEVNAME(sc), n->name, i);
467 			aml_freevalue(&res);
468 			return (-1);
469 		}
470 		memcpy(&ret, res.v_buffer, sizeof(ret));
471 		if (ret.flags == 0) {
472 			dprintf("%s: %s: %lld: bad flags in result\n",
473 			    DEVNAME(sc), n->name, i);
474 			aml_freevalue(&res);
475 			return (-1);
476 		}
477 		*v = ret.value;
478 	} else {
479 		*v = res.v_integer;
480 	}
481 	aml_freevalue(&res);
482 
483 	return (0);
484 }
485 
486 int
aibs_getpack(struct aibs_softc * sc,struct aml_node * n,int64_t i,struct aml_value * res)487 aibs_getpack(struct aibs_softc *sc, struct aml_node *n, int64_t i,
488     struct aml_value *res)
489 {
490 	struct aml_value	req;
491 
492 	req.type = AML_OBJTYPE_INTEGER;
493 	req.v_integer = i;
494 
495 	if (aml_evalnode(sc->sc_acpi, n, 1, &req, res)) {
496 		dprintf("%s: %s: %lld: evaluation failed\n",
497 		    DEVNAME(sc), n->name, i);
498 		aml_freevalue(res);
499 		return (-1);
500 	}
501 	if (res->type != AML_OBJTYPE_PACKAGE) {
502 		dprintf("%s: %s: %lld: not a package: type %i\n",
503 		    DEVNAME(sc), n->name, i, res->type);
504 		aml_freevalue(res);
505 		return (-1);
506 	}
507 
508 	return (0);
509 }
510 
511 void
aibs_probe(struct aibs_softc * sc)512 aibs_probe(struct aibs_softc *sc)
513 {
514 	/*
515 	 * Old mode uses TSIF, VSIF, and FSIF to enumerate sensors and
516 	 * RTMP, RVLT, and RFAN are used to get the values.
517 	 * New mode uses GGRP for enumeration and GITM and SITM as accessor.
518 	 * If the new methods are available use them else default to old mode.
519 	 */
520 	aml_find_node(sc->sc_devnode, "RTMP", aibs_find_cb, &sc->sc_rtmpnode);
521 	aml_find_node(sc->sc_devnode, "RVLT", aibs_find_cb, &sc->sc_rvltnode);
522 	aml_find_node(sc->sc_devnode, "RFAN", aibs_find_cb, &sc->sc_rfannode);
523 
524 	aml_find_node(sc->sc_devnode, "GGRP", aibs_find_cb, &sc->sc_ggrpnode);
525 	aml_find_node(sc->sc_devnode, "GITM", aibs_find_cb, &sc->sc_gitmnode);
526 	aml_find_node(sc->sc_devnode, "SITM", aibs_find_cb, &sc->sc_sitmnode);
527 
528 	if (sc->sc_ggrpnode && sc->sc_gitmnode && sc->sc_sitmnode &&
529 	    !sc->sc_rtmpnode && !sc->sc_rvltnode && !sc->sc_rfannode)
530 		sc->sc_mode = 1;
531 }
532 
533 int
aibs_find_cb(struct aml_node * node,void * arg)534 aibs_find_cb(struct aml_node *node, void *arg)
535 {
536 	struct aml_node	**np = arg;
537 
538 	printf(" %s", node->name);
539 	*np = node;
540 	return (1);
541 }
542 
543 int
aibs_notify(struct aml_node * node,int notify_type,void * arg)544 aibs_notify(struct aml_node *node, int notify_type, void *arg)
545 {
546 	struct aibs_softc *sc = arg;
547 
548 	if (notify_type == 0x00) {
549 		/* Poll sensors */
550 		aibs_refresh(sc);
551 	}
552 	return (0);
553 }
554