1/*	$NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $	*/
2/*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
3
4/*
5 * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.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/cdefs.h>
21__FBSDID("$FreeBSD: stable/11/sys/dev/acpi_support/atk0110.c 335471 2018-06-21 09:41:44Z dim $");
22
23#include <machine/_inttypes.h>
24#include <sys/param.h>
25#include <sys/systm.h>
26#include <sys/kernel.h>
27#include <sys/bus.h>
28#include <sys/module.h>
29#include <sys/malloc.h>
30#include <sys/sysctl.h>
31#include <sys/stdint.h>
32
33#include <contrib/dev/acpica/include/acpi.h>
34#include <dev/acpica/acpivar.h>
35
36/*
37 * ASUSTeK AI Booster (ACPI ASOC ATK0110).
38 *
39 * This code was originally written for OpenBSD after the techniques
40 * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
41 * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
42 * provided by Sam Fourman Jr.  It was subsequently ported from OpenBSD to
43 * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
44 *
45 *				  -- Constantine A. Murenin <http://cnst.su/>
46 */
47
48#define _COMPONENT	ACPI_OEM
49ACPI_MODULE_NAME("aibs");
50ACPI_SERIAL_DECL(aibs, "aibs");
51
52#define AIBS_MORE_SENSORS
53#define AIBS_VERBOSE
54
55#define	AIBS_GROUP_SENSORS	0x06
56
57#define AIBS_SENS_TYPE(x)	(((x) >> 16) & 0xff)
58#define AIBS_SENS_TYPE_VOLT	2
59#define AIBS_SENS_TYPE_TEMP	3
60#define AIBS_SENS_TYPE_FAN	4
61
62#define	AIBS_SENS_TYPE_VOLT_NAME		"volt"
63#define	AIBS_SENS_TYPE_VOLT_TEMP		"temp"
64#define	AIBS_SENS_TYPE_VOLT_FAN		"fan"
65
66struct aibs_sensor {
67	ACPI_INTEGER	v;
68	ACPI_INTEGER	i;
69	ACPI_INTEGER	l;
70	ACPI_INTEGER	h;
71	int		t;
72};
73
74struct aibs_softc {
75	device_t		sc_dev;
76	ACPI_HANDLE		sc_ah;
77
78	struct aibs_sensor	*sc_asens_volt;
79	struct aibs_sensor	*sc_asens_temp;
80	struct aibs_sensor	*sc_asens_fan;
81	struct aibs_sensor	*sc_asens_all;
82
83	struct sysctl_oid	*sc_volt_sysctl;
84	struct sysctl_oid	*sc_temp_sysctl;
85	struct sysctl_oid	*sc_fan_sysctl;
86
87	bool			sc_ggrp_method;
88};
89
90static int aibs_probe(device_t);
91static int aibs_attach(device_t);
92static int aibs_detach(device_t);
93static int aibs_sysctl(SYSCTL_HANDLER_ARGS);
94static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS);
95
96static int aibs_attach_ggrp(struct aibs_softc *);
97static int aibs_attach_sif(struct aibs_softc *, int);
98
99static device_method_t aibs_methods[] = {
100	DEVMETHOD(device_probe,		aibs_probe),
101	DEVMETHOD(device_attach,	aibs_attach),
102	DEVMETHOD(device_detach,	aibs_detach),
103	{ NULL, NULL }
104};
105
106static driver_t aibs_driver = {
107	"aibs",
108	aibs_methods,
109	sizeof(struct aibs_softc)
110};
111
112static devclass_t aibs_devclass;
113
114DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
115MODULE_DEPEND(aibs, acpi, 1, 1, 1);
116
117static char* aibs_hids[] = {
118	"ATK0110",
119	NULL
120};
121
122static int
123aibs_probe(device_t dev)
124{
125	if (acpi_disabled("aibs") ||
126	    ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
127		return (ENXIO);
128
129	device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
130	return (0);
131}
132
133static int
134aibs_attach(device_t dev)
135{
136	struct aibs_softc *sc = device_get_softc(dev);
137	int err;
138
139	sc->sc_dev = dev;
140	sc->sc_ah = acpi_get_handle(dev);
141
142	sc->sc_ggrp_method = false;
143	err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT);
144	if (err == 0)
145		err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP);
146	if (err == 0)
147		err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN);
148
149	if (err == 0)
150		return (0);
151
152	/* Clean up whatever was allocated earlier. */
153	if (sc->sc_volt_sysctl != NULL)
154		sysctl_remove_oid(sc->sc_volt_sysctl, true, true);
155	if (sc->sc_temp_sysctl != NULL)
156		sysctl_remove_oid(sc->sc_temp_sysctl, true, true);
157	if (sc->sc_fan_sysctl != NULL)
158		sysctl_remove_oid(sc->sc_fan_sysctl, true, true);
159	aibs_detach(dev);
160
161	sc->sc_ggrp_method = true;
162	err = aibs_attach_ggrp(sc);
163	return (err);
164}
165
166static int
167aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o,
168    struct aibs_sensor* sensor, const char ** descr)
169{
170	int		off;
171
172	/*
173	 * Packages for the old and new methods are quite
174	 * similar except that the new package has two
175	 * new (unknown / unused) fields after the name field.
176	 */
177	if (sc->sc_ggrp_method)
178		off = 4;
179	else
180		off = 2;
181
182	if (o->Type != ACPI_TYPE_PACKAGE) {
183		device_printf(sc->sc_dev,
184		    "sensor object is not a package: %i type\n",
185		     o->Type);
186		return (ENXIO);
187	}
188	if (o[0].Package.Count != (off + 3) ||
189	    o->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
190	    o->Package.Elements[1].Type != ACPI_TYPE_STRING ||
191	    o->Package.Elements[off].Type != ACPI_TYPE_INTEGER ||
192	    o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER ||
193	    o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) {
194		device_printf(sc->sc_dev, "unexpected package content\n");
195		return (ENXIO);
196	}
197
198	sensor->i = o->Package.Elements[0].Integer.Value;
199	*descr = o->Package.Elements[1].String.Pointer;
200	sensor->l = o->Package.Elements[off].Integer.Value;
201	sensor->h = o->Package.Elements[off + 1].Integer.Value;
202	/* For the new method the second value is a range size. */
203	if (sc->sc_ggrp_method)
204		sensor->h += sensor->l;
205	sensor->t = AIBS_SENS_TYPE(sensor->i);
206
207	switch (sensor->t) {
208	case AIBS_SENS_TYPE_VOLT:
209	case AIBS_SENS_TYPE_TEMP:
210	case AIBS_SENS_TYPE_FAN:
211		return (0);
212	default:
213		device_printf(sc->sc_dev, "unknown sensor type 0x%x",
214		    sensor->t);
215		return (ENXIO);
216	}
217}
218
219static void
220aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so,
221    const char *type_name, int idx, struct aibs_sensor *sensor,
222    const char *descr)
223{
224	char	sysctl_name[8];
225
226	snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx);
227#ifdef AIBS_VERBOSE
228	device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n",
229	    type_name[0], idx,
230	    (uintmax_t)sensor->i, descr, (intmax_t)sensor->l,
231	    (intmax_t)sensor->h);
232#endif
233	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
234	    SYSCTL_CHILDREN(so), idx, sysctl_name,
235	    CTLTYPE_INT | CTLFLAG_RD, sc, (uintptr_t)sensor,
236	    sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl,
237	    sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr);
238}
239
240static int
241aibs_attach_ggrp(struct aibs_softc *sc)
242{
243	ACPI_STATUS		s;
244	ACPI_BUFFER		buf;
245	ACPI_HANDLE		h;
246	ACPI_OBJECT		id;
247	ACPI_OBJECT		*bp;
248	ACPI_OBJECT_LIST	arg;
249	int			i;
250	int			t, v, f;
251	int			err;
252	int			*s_idx;
253	const char		*name;
254	const char		*descr;
255	struct aibs_sensor	*sensor;
256	struct sysctl_oid	**so;
257
258	/* First see if GITM is available. */
259	s = AcpiGetHandle(sc->sc_ah, "GITM", &h);
260	if (ACPI_FAILURE(s)) {
261		if (bootverbose)
262			device_printf(sc->sc_dev, "GITM not found\n");
263		return (ENXIO);
264	}
265
266	/*
267	 * Now call GGRP with the appropriate argument to list sensors.
268	 * The method lists different groups of entities depending on
269	 * the argument.
270	 */
271	id.Integer.Value = AIBS_GROUP_SENSORS;
272	id.Type = ACPI_TYPE_INTEGER;
273	arg.Count = 1;
274	arg.Pointer = &id;
275	buf.Length = ACPI_ALLOCATE_BUFFER;
276	buf.Pointer = NULL;
277	s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf,
278	    ACPI_TYPE_PACKAGE);
279	if (ACPI_FAILURE(s)) {
280		device_printf(sc->sc_dev, "GGRP not found\n");
281		return (ENXIO);
282	}
283
284	bp = buf.Pointer;
285	sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count,
286	    M_DEVBUF, M_WAITOK | M_ZERO);
287	v = t = f = 0;
288	for (i = 0; i < bp->Package.Count; i++) {
289		sensor = &sc->sc_asens_all[i];
290		err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor,
291		    &descr);
292		if (err != 0)
293			continue;
294
295		switch (sensor->t) {
296		case AIBS_SENS_TYPE_VOLT:
297			name = "volt";
298			so = &sc->sc_volt_sysctl;
299			s_idx = &v;
300			break;
301		case AIBS_SENS_TYPE_TEMP:
302			name = "temp";
303			so = &sc->sc_temp_sysctl;
304			s_idx = &t;
305			break;
306		case AIBS_SENS_TYPE_FAN:
307			name = "fan";
308			so = &sc->sc_fan_sysctl;
309			s_idx = &f;
310			break;
311		default:
312			panic("add_sensor succeeded for unknown sensor type %d",
313			    sensor->t);
314		}
315
316		if (*so == NULL) {
317			/* sysctl subtree for sensors of this type */
318			*so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
319			    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
320			    sensor->t, name, CTLFLAG_RD, NULL, NULL);
321		}
322		aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
323		*s_idx += 1;
324	}
325
326	AcpiOsFree(buf.Pointer);
327	return (0);
328}
329
330static int
331aibs_attach_sif(struct aibs_softc *sc, int st)
332{
333	char			name[] = "?SIF";
334	ACPI_STATUS		s;
335	ACPI_BUFFER		b;
336	ACPI_OBJECT		*bp, *o;
337	const char		*node;
338	struct aibs_sensor	*as;
339	struct sysctl_oid	**so;
340	int			i, n;
341	int err;
342
343	switch (st) {
344	case AIBS_SENS_TYPE_VOLT:
345		node = "volt";
346		name[0] = 'V';
347		so = &sc->sc_volt_sysctl;
348		break;
349	case AIBS_SENS_TYPE_TEMP:
350		node = "temp";
351		name[0] = 'T';
352		so = &sc->sc_temp_sysctl;
353		break;
354	case AIBS_SENS_TYPE_FAN:
355		node = "fan";
356		name[0] = 'F';
357		so = &sc->sc_fan_sysctl;
358		break;
359	default:
360		panic("Unsupported sensor type %d", st);
361	}
362
363	b.Length = ACPI_ALLOCATE_BUFFER;
364	s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
365	    ACPI_TYPE_PACKAGE);
366	if (ACPI_FAILURE(s)) {
367		device_printf(sc->sc_dev, "%s not found\n", name);
368		return (ENXIO);
369	}
370
371	bp = b.Pointer;
372	o = bp->Package.Elements;
373	if (o[0].Type != ACPI_TYPE_INTEGER) {
374		device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
375		AcpiOsFree(b.Pointer);
376		return (ENXIO);
377	}
378
379	n = o[0].Integer.Value;
380	if (bp->Package.Count - 1 < n) {
381		device_printf(sc->sc_dev, "%s: invalid package\n", name);
382		AcpiOsFree(b.Pointer);
383		return (ENXIO);
384	} else if (bp->Package.Count - 1 > n) {
385		int on = n;
386
387#ifdef AIBS_MORE_SENSORS
388		n = bp->Package.Count - 1;
389#endif
390		device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
391		    ", assume %i\n", name, on, bp->Package.Count - 1, n);
392	}
393	if (n < 1) {
394		device_printf(sc->sc_dev, "%s: no members in the package\n",
395		    name);
396		AcpiOsFree(b.Pointer);
397		return (ENXIO);
398	}
399
400	as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
401	switch (st) {
402	case AIBS_SENS_TYPE_VOLT:
403		sc->sc_asens_volt = as;
404		break;
405	case AIBS_SENS_TYPE_TEMP:
406		sc->sc_asens_temp = as;
407		break;
408	case AIBS_SENS_TYPE_FAN:
409		sc->sc_asens_fan = as;
410		break;
411	}
412
413	/* sysctl subtree for sensors of this type */
414	*so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
415	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
416	    node, CTLFLAG_RD, NULL, NULL);
417
418	for (i = 0, o++; i < n; i++, o++) {
419		const char	*descr;
420
421		err = aibs_add_sensor(sc, o, &as[i], &descr);
422		if (err == 0)
423			aibs_sensor_added(sc, *so, node, i, &as[i], descr);
424	}
425
426	AcpiOsFree(b.Pointer);
427	return (0);
428}
429
430static int
431aibs_detach(device_t dev)
432{
433	struct aibs_softc	*sc = device_get_softc(dev);
434
435	if (sc->sc_asens_volt != NULL)
436		free(sc->sc_asens_volt, M_DEVBUF);
437	if (sc->sc_asens_temp != NULL)
438		free(sc->sc_asens_temp, M_DEVBUF);
439	if (sc->sc_asens_fan != NULL)
440		free(sc->sc_asens_fan, M_DEVBUF);
441	if (sc->sc_asens_all != NULL)
442		free(sc->sc_asens_all, M_DEVBUF);
443	return (0);
444}
445
446#ifdef AIBS_VERBOSE
447#define ddevice_printf(x...) device_printf(x)
448#else
449#define ddevice_printf(x...)
450#endif
451
452static int
453aibs_sysctl(SYSCTL_HANDLER_ARGS)
454{
455	struct aibs_softc	*sc = arg1;
456	struct aibs_sensor	*sensor = (void *)(intptr_t)arg2;
457	int			i = oidp->oid_number;
458	ACPI_STATUS		rs;
459	ACPI_OBJECT		p, *bp;
460	ACPI_OBJECT_LIST	mp;
461	ACPI_BUFFER		b;
462	char			*name;
463	ACPI_INTEGER		v, l, h;
464	int			so[3];
465
466	switch (sensor->t) {
467	case AIBS_SENS_TYPE_VOLT:
468		name = "RVLT";
469		break;
470	case AIBS_SENS_TYPE_TEMP:
471		name = "RTMP";
472		break;
473	case AIBS_SENS_TYPE_FAN:
474		name = "RFAN";
475		break;
476	default:
477		return (ENOENT);
478	}
479	l = sensor->l;
480	h = sensor->h;
481	p.Type = ACPI_TYPE_INTEGER;
482	p.Integer.Value = sensor->i;
483	mp.Count = 1;
484	mp.Pointer = &p;
485	b.Length = ACPI_ALLOCATE_BUFFER;
486	ACPI_SERIAL_BEGIN(aibs);
487	rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
488	    ACPI_TYPE_INTEGER);
489	if (ACPI_FAILURE(rs)) {
490		ddevice_printf(sc->sc_dev,
491		    "%s: %i: evaluation failed\n",
492		    name, i);
493		ACPI_SERIAL_END(aibs);
494		return (EIO);
495	}
496	bp = b.Pointer;
497	v = bp->Integer.Value;
498	AcpiOsFree(b.Pointer);
499	ACPI_SERIAL_END(aibs);
500
501	switch (sensor->t) {
502	case AIBS_SENS_TYPE_VOLT:
503		break;
504	case AIBS_SENS_TYPE_TEMP:
505		v += 2731;
506		l += 2731;
507		h += 2731;
508		break;
509	case AIBS_SENS_TYPE_FAN:
510		break;
511	}
512	so[0] = v;
513	so[1] = l;
514	so[2] = h;
515	return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
516}
517
518static int
519aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
520{
521	struct aibs_softc	*sc = arg1;
522	struct aibs_sensor	*sensor = (void *)(intptr_t)arg2;
523	ACPI_STATUS		rs;
524	ACPI_OBJECT		p, *bp;
525	ACPI_OBJECT_LIST	arg;
526	ACPI_BUFFER		buf;
527	ACPI_INTEGER		v, l, h;
528	int			so[3];
529	uint32_t		*ret;
530	uint32_t		cmd[3];
531
532	cmd[0] = sensor->i;
533	cmd[1] = 0;
534	cmd[2] = 0;
535	p.Type = ACPI_TYPE_BUFFER;
536	p.Buffer.Pointer = (void *)cmd;
537	p.Buffer.Length = sizeof(cmd);
538	arg.Count = 1;
539	arg.Pointer = &p;
540	buf.Pointer = NULL;
541	buf.Length = ACPI_ALLOCATE_BUFFER;
542	ACPI_SERIAL_BEGIN(aibs);
543	rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
544	    ACPI_TYPE_BUFFER);
545	ACPI_SERIAL_END(aibs);
546	if (ACPI_FAILURE(rs)) {
547		device_printf(sc->sc_dev, "GITM evaluation failed\n");
548		return (EIO);
549	}
550	bp = buf.Pointer;
551	if (bp->Buffer.Length < 8) {
552		device_printf(sc->sc_dev, "GITM returned short buffer\n");
553		return (EIO);
554	}
555	ret = (uint32_t *)bp->Buffer.Pointer;
556	if (ret[0] == 0) {
557		device_printf(sc->sc_dev, "GITM returned error status\n");
558		return (EINVAL);
559	}
560	v = ret[1];
561	AcpiOsFree(buf.Pointer);
562
563	l = sensor->l;
564	h = sensor->h;
565
566	switch (sensor->t) {
567	case AIBS_SENS_TYPE_VOLT:
568		break;
569	case AIBS_SENS_TYPE_TEMP:
570		v += 2731;
571		l += 2731;
572		h += 2731;
573		break;
574	case AIBS_SENS_TYPE_FAN:
575		break;
576	}
577	so[0] = v;
578	so[1] = l;
579	so[2] = h;
580	return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
581}
582