atk0110.c revision 300421
1209523Srpaulo/*	$NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $	*/
2209523Srpaulo/*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
3209523Srpaulo
4209523Srpaulo/*
5209523Srpaulo * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org>
6209523Srpaulo *
7209523Srpaulo * Permission to use, copy, modify, and distribute this software for any
8209523Srpaulo * purpose with or without fee is hereby granted, provided that the above
9209523Srpaulo * copyright notice and this permission notice appear in all copies.
10209523Srpaulo *
11209523Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12209523Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13209523Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14209523Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15209523Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16209523Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17209523Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18209523Srpaulo */
19209523Srpaulo
20209523Srpaulo#include <sys/cdefs.h>
21209523Srpaulo__FBSDID("$FreeBSD: head/sys/dev/acpi_support/atk0110.c 300421 2016-05-22 13:58:32Z loos $");
22209523Srpaulo
23209523Srpaulo#include <machine/_inttypes.h>
24209523Srpaulo#include <sys/param.h>
25209523Srpaulo#include <sys/systm.h>
26209523Srpaulo#include <sys/kernel.h>
27209523Srpaulo#include <sys/bus.h>
28209523Srpaulo#include <sys/module.h>
29209523Srpaulo#include <sys/malloc.h>
30209523Srpaulo#include <sys/sysctl.h>
31209523Srpaulo
32209523Srpaulo#include <contrib/dev/acpica/include/acpi.h>
33209523Srpaulo#include <dev/acpica/acpivar.h>
34209523Srpaulo
35209523Srpaulo/*
36209523Srpaulo * ASUSTeK AI Booster (ACPI ASOC ATK0110).
37209523Srpaulo *
38209523Srpaulo * This code was originally written for OpenBSD after the techniques
39209523Srpaulo * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
40209523Srpaulo * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
41209523Srpaulo * provided by Sam Fourman Jr.  It was subsequently ported from OpenBSD to
42209523Srpaulo * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
43209523Srpaulo *
44209523Srpaulo *				  -- Constantine A. Murenin <http://cnst.su/>
45209523Srpaulo */
46209523Srpaulo
47209523Srpaulo#define _COMPONENT	ACPI_OEM
48209523SrpauloACPI_MODULE_NAME("aibs");
49209523SrpauloACPI_SERIAL_DECL(aibs, "aibs");
50209523Srpaulo
51209523Srpaulo#define AIBS_MORE_SENSORS
52209523Srpaulo#define AIBS_VERBOSE
53209523Srpaulo
54209523Srpauloenum aibs_type {
55209523Srpaulo	AIBS_VOLT,
56209523Srpaulo	AIBS_TEMP,
57209523Srpaulo	AIBS_FAN
58209523Srpaulo};
59209523Srpaulo
60209523Srpaulostruct aibs_sensor {
61209523Srpaulo	ACPI_INTEGER	v;
62209523Srpaulo	ACPI_INTEGER	i;
63209523Srpaulo	ACPI_INTEGER	l;
64209523Srpaulo	ACPI_INTEGER	h;
65209523Srpaulo	enum aibs_type	t;
66209523Srpaulo};
67209523Srpaulo
68209523Srpaulostruct aibs_softc {
69299052Sadrian	device_t		sc_dev;
70209523Srpaulo	ACPI_HANDLE		sc_ah;
71209523Srpaulo
72209523Srpaulo	struct aibs_sensor	*sc_asens_volt;
73209523Srpaulo	struct aibs_sensor	*sc_asens_temp;
74209523Srpaulo	struct aibs_sensor	*sc_asens_fan;
75209523Srpaulo};
76209523Srpaulo
77209523Srpaulostatic int aibs_probe(device_t);
78209523Srpaulostatic int aibs_attach(device_t);
79209523Srpaulostatic int aibs_detach(device_t);
80209523Srpaulostatic int aibs_sysctl(SYSCTL_HANDLER_ARGS);
81209523Srpaulo
82209523Srpaulostatic void aibs_attach_sif(struct aibs_softc *, enum aibs_type);
83209523Srpaulo
84209523Srpaulostatic device_method_t aibs_methods[] = {
85209523Srpaulo	DEVMETHOD(device_probe,		aibs_probe),
86209523Srpaulo	DEVMETHOD(device_attach,	aibs_attach),
87209523Srpaulo	DEVMETHOD(device_detach,	aibs_detach),
88209523Srpaulo	{ NULL, NULL }
89209523Srpaulo};
90209523Srpaulo
91209523Srpaulostatic driver_t aibs_driver = {
92209523Srpaulo	"aibs",
93209523Srpaulo	aibs_methods,
94209523Srpaulo	sizeof(struct aibs_softc)
95209523Srpaulo};
96209523Srpaulo
97209523Srpaulostatic devclass_t aibs_devclass;
98209523Srpaulo
99209523SrpauloDRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
100232256SkevloMODULE_DEPEND(aibs, acpi, 1, 1, 1);
101209523Srpaulo
102209523Srpaulostatic char* aibs_hids[] = {
103209523Srpaulo	"ATK0110",
104209523Srpaulo	NULL
105209523Srpaulo};
106209523Srpaulo
107209523Srpaulostatic int
108209523Srpauloaibs_probe(device_t dev)
109209523Srpaulo{
110209523Srpaulo	if (acpi_disabled("aibs") ||
111209523Srpaulo	    ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
112209523Srpaulo		return ENXIO;
113209523Srpaulo
114209523Srpaulo	device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
115209523Srpaulo	return 0;
116209523Srpaulo}
117209523Srpaulo
118209523Srpaulostatic int
119209523Srpauloaibs_attach(device_t dev)
120209523Srpaulo{
121209523Srpaulo	struct aibs_softc *sc = device_get_softc(dev);
122209523Srpaulo
123209523Srpaulo	sc->sc_dev = dev;
124209523Srpaulo	sc->sc_ah = acpi_get_handle(dev);
125209523Srpaulo
126209523Srpaulo	aibs_attach_sif(sc, AIBS_VOLT);
127209523Srpaulo	aibs_attach_sif(sc, AIBS_TEMP);
128209523Srpaulo	aibs_attach_sif(sc, AIBS_FAN);
129209523Srpaulo
130209523Srpaulo	return 0;
131209523Srpaulo}
132209523Srpaulo
133209523Srpaulostatic void
134209523Srpauloaibs_attach_sif(struct aibs_softc *sc, enum aibs_type st)
135209523Srpaulo{
136209523Srpaulo	ACPI_STATUS		s;
137209523Srpaulo	ACPI_BUFFER		b;
138209523Srpaulo	ACPI_OBJECT		*bp, *o;
139209523Srpaulo	int			i, n;
140209523Srpaulo	const char		*node;
141209523Srpaulo	char			name[] = "?SIF";
142209523Srpaulo	struct aibs_sensor	*as;
143209523Srpaulo	struct sysctl_oid	*so;
144209523Srpaulo
145209523Srpaulo	switch (st) {
146209523Srpaulo	case AIBS_VOLT:
147209523Srpaulo		node = "volt";
148209523Srpaulo		name[0] = 'V';
149209523Srpaulo		break;
150209523Srpaulo	case AIBS_TEMP:
151209523Srpaulo		node = "temp";
152209523Srpaulo		name[0] = 'T';
153209523Srpaulo		break;
154209523Srpaulo	case AIBS_FAN:
155209523Srpaulo		node = "fan";
156209523Srpaulo		name[0] = 'F';
157209523Srpaulo		break;
158209523Srpaulo	default:
159209523Srpaulo		return;
160209523Srpaulo	}
161209523Srpaulo
162209523Srpaulo	b.Length = ACPI_ALLOCATE_BUFFER;
163209523Srpaulo	s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
164209523Srpaulo	    ACPI_TYPE_PACKAGE);
165209523Srpaulo	if (ACPI_FAILURE(s)) {
166209523Srpaulo		device_printf(sc->sc_dev, "%s not found\n", name);
167209523Srpaulo		return;
168209523Srpaulo	}
169209523Srpaulo
170209523Srpaulo	bp = b.Pointer;
171209523Srpaulo	o = bp->Package.Elements;
172209523Srpaulo	if (o[0].Type != ACPI_TYPE_INTEGER) {
173209523Srpaulo		device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
174209523Srpaulo		AcpiOsFree(b.Pointer);
175209523Srpaulo		return;
176209523Srpaulo	}
177209523Srpaulo
178209523Srpaulo	n = o[0].Integer.Value;
179209523Srpaulo	if (bp->Package.Count - 1 < n) {
180209523Srpaulo		device_printf(sc->sc_dev, "%s: invalid package\n", name);
181209523Srpaulo		AcpiOsFree(b.Pointer);
182209523Srpaulo		return;
183209523Srpaulo	} else if (bp->Package.Count - 1 > n) {
184209523Srpaulo		int on = n;
185209523Srpaulo
186209523Srpaulo#ifdef AIBS_MORE_SENSORS
187209523Srpaulo		n = bp->Package.Count - 1;
188209523Srpaulo#endif
189209523Srpaulo		device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
190209523Srpaulo		    ", assume %i\n", name, on, bp->Package.Count - 1, n);
191209523Srpaulo	}
192209523Srpaulo	if (n < 1) {
193209523Srpaulo		device_printf(sc->sc_dev, "%s: no members in the package\n",
194209523Srpaulo		    name);
195209523Srpaulo		AcpiOsFree(b.Pointer);
196209523Srpaulo		return;
197209523Srpaulo	}
198209523Srpaulo
199209523Srpaulo	as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
200209523Srpaulo	if (as == NULL) {
201209523Srpaulo		device_printf(sc->sc_dev, "%s: malloc fail\n", name);
202209523Srpaulo		AcpiOsFree(b.Pointer);
203209523Srpaulo		return;
204209523Srpaulo	}
205209523Srpaulo	switch (st) {
206209523Srpaulo	case AIBS_VOLT:
207209523Srpaulo		sc->sc_asens_volt = as;
208209523Srpaulo		break;
209209523Srpaulo	case AIBS_TEMP:
210209523Srpaulo		sc->sc_asens_temp = as;
211209523Srpaulo		break;
212209523Srpaulo	case AIBS_FAN:
213209523Srpaulo		sc->sc_asens_fan = as;
214209523Srpaulo		break;
215209523Srpaulo	}
216209523Srpaulo
217209523Srpaulo	/* sysctl subtree for sensors of this type */
218209523Srpaulo	so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
219209523Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
220209523Srpaulo	    node, CTLFLAG_RD, NULL, NULL);
221209523Srpaulo
222209523Srpaulo	for (i = 0, o++; i < n; i++, o++) {
223209523Srpaulo		ACPI_OBJECT	*oi;
224209523Srpaulo		char		si[3];
225209523Srpaulo		const char	*desc;
226209523Srpaulo
227209523Srpaulo		/* acpica5 automatically evaluates the referenced package */
228209523Srpaulo		if (o[0].Type != ACPI_TYPE_PACKAGE) {
229209523Srpaulo			device_printf(sc->sc_dev,
230209523Srpaulo			    "%s: %i: not a package: %i type\n",
231209523Srpaulo			    name, i, o[0].Type);
232209523Srpaulo			continue;
233209523Srpaulo		}
234209523Srpaulo		oi = o[0].Package.Elements;
235209523Srpaulo		if (o[0].Package.Count != 5 ||
236209523Srpaulo		    oi[0].Type != ACPI_TYPE_INTEGER ||
237209523Srpaulo		    oi[1].Type != ACPI_TYPE_STRING ||
238209523Srpaulo		    oi[2].Type != ACPI_TYPE_INTEGER ||
239209523Srpaulo		    oi[3].Type != ACPI_TYPE_INTEGER ||
240209523Srpaulo		    oi[4].Type != ACPI_TYPE_INTEGER) {
241209523Srpaulo			device_printf(sc->sc_dev,
242209523Srpaulo			    "%s: %i: invalid package\n",
243209523Srpaulo			    name, i);
244209523Srpaulo			continue;
245209523Srpaulo		}
246209523Srpaulo		as[i].i = oi[0].Integer.Value;
247209523Srpaulo		desc = oi[1].String.Pointer;
248209523Srpaulo		as[i].l = oi[2].Integer.Value;
249209523Srpaulo		as[i].h = oi[3].Integer.Value;
250209523Srpaulo		as[i].t = st;
251209523Srpaulo#ifdef AIBS_VERBOSE
252209523Srpaulo		device_printf(sc->sc_dev, "%c%i: "
253209523Srpaulo		    "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64"  "
254209523Srpaulo		    "0x%"PRIx64"\n",
255209523Srpaulo		    name[0], i,
256252276Sjkim		    (uint64_t)as[i].i, desc, (int64_t)as[i].l,
257252276Sjkim		    (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value);
258209523Srpaulo#endif
259209523Srpaulo		snprintf(si, sizeof(si), "%i", i);
260209523Srpaulo		SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
261217566Smdf		    SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD,
262209523Srpaulo		    sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc);
263209523Srpaulo	}
264209523Srpaulo
265209523Srpaulo	AcpiOsFree(b.Pointer);
266209523Srpaulo}
267209523Srpaulo
268209523Srpaulostatic int
269209523Srpauloaibs_detach(device_t dev)
270209523Srpaulo{
271209523Srpaulo	struct aibs_softc	*sc = device_get_softc(dev);
272209523Srpaulo
273209523Srpaulo	if (sc->sc_asens_volt != NULL)
274209523Srpaulo		free(sc->sc_asens_volt, M_DEVBUF);
275209523Srpaulo	if (sc->sc_asens_temp != NULL)
276209523Srpaulo		free(sc->sc_asens_temp, M_DEVBUF);
277209523Srpaulo	if (sc->sc_asens_fan != NULL)
278209523Srpaulo		free(sc->sc_asens_fan, M_DEVBUF);
279209523Srpaulo	return 0;
280209523Srpaulo}
281209523Srpaulo
282209523Srpaulo#ifdef AIBS_VERBOSE
283209523Srpaulo#define ddevice_printf(x...) device_printf(x)
284209523Srpaulo#else
285209523Srpaulo#define ddevice_printf(x...)
286209523Srpaulo#endif
287209523Srpaulo
288209523Srpaulostatic int
289209523Srpauloaibs_sysctl(SYSCTL_HANDLER_ARGS)
290209523Srpaulo{
291209523Srpaulo	struct aibs_softc	*sc = arg1;
292209523Srpaulo	enum aibs_type		st = arg2;
293209523Srpaulo	int			i = oidp->oid_number;
294209523Srpaulo	ACPI_STATUS		rs;
295209523Srpaulo	ACPI_OBJECT		p, *bp;
296209523Srpaulo	ACPI_OBJECT_LIST	mp;
297209523Srpaulo	ACPI_BUFFER		b;
298209523Srpaulo	char			*name;
299209523Srpaulo	struct aibs_sensor	*as;
300209523Srpaulo	ACPI_INTEGER		v, l, h;
301209523Srpaulo	int			so[3];
302209523Srpaulo
303209523Srpaulo	switch (st) {
304209523Srpaulo	case AIBS_VOLT:
305209523Srpaulo		name = "RVLT";
306209523Srpaulo		as = sc->sc_asens_volt;
307209523Srpaulo		break;
308209523Srpaulo	case AIBS_TEMP:
309209523Srpaulo		name = "RTMP";
310209523Srpaulo		as = sc->sc_asens_temp;
311209523Srpaulo		break;
312209523Srpaulo	case AIBS_FAN:
313209523Srpaulo		name = "RFAN";
314209523Srpaulo		as = sc->sc_asens_fan;
315209523Srpaulo		break;
316209523Srpaulo	default:
317209523Srpaulo		return ENOENT;
318209523Srpaulo	}
319209523Srpaulo	if (as == NULL)
320209523Srpaulo		return ENOENT;
321209523Srpaulo	l = as[i].l;
322209523Srpaulo	h = as[i].h;
323209523Srpaulo	p.Type = ACPI_TYPE_INTEGER;
324209523Srpaulo	p.Integer.Value = as[i].i;
325209523Srpaulo	mp.Count = 1;
326209523Srpaulo	mp.Pointer = &p;
327209523Srpaulo	b.Length = ACPI_ALLOCATE_BUFFER;
328209523Srpaulo	ACPI_SERIAL_BEGIN(aibs);
329209523Srpaulo	rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
330209523Srpaulo	    ACPI_TYPE_INTEGER);
331209523Srpaulo	if (ACPI_FAILURE(rs)) {
332209523Srpaulo		ddevice_printf(sc->sc_dev,
333209523Srpaulo		    "%s: %i: evaluation failed\n",
334209523Srpaulo		    name, i);
335209523Srpaulo		ACPI_SERIAL_END(aibs);
336209523Srpaulo		return EIO;
337209523Srpaulo	}
338209523Srpaulo	bp = b.Pointer;
339209523Srpaulo	v = bp->Integer.Value;
340209523Srpaulo	AcpiOsFree(b.Pointer);
341209523Srpaulo	ACPI_SERIAL_END(aibs);
342209523Srpaulo
343209523Srpaulo	switch (st) {
344209523Srpaulo	case AIBS_VOLT:
345209523Srpaulo		break;
346209523Srpaulo	case AIBS_TEMP:
347300421Sloos		v += 2731;
348300421Sloos		l += 2731;
349300421Sloos		h += 2731;
350209523Srpaulo		break;
351209523Srpaulo	case AIBS_FAN:
352209523Srpaulo		break;
353209523Srpaulo	}
354209523Srpaulo	so[0] = v;
355209523Srpaulo	so[1] = l;
356209523Srpaulo	so[2] = h;
357209523Srpaulo	return sysctl_handle_opaque(oidp, &so, sizeof(so), req);
358209523Srpaulo}
359