atk0110.c revision 256281
159191Skris/*	$NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $	*/
259191Skris/*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
355714Skris
4296341Sdelphij/*
5296341Sdelphij * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org>
6296341Sdelphij *
7296341Sdelphij * Permission to use, copy, modify, and distribute this software for any
8296341Sdelphij * purpose with or without fee is hereby granted, provided that the above
9296341Sdelphij * copyright notice and this permission notice appear in all copies.
10296341Sdelphij *
11296341Sdelphij * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12296341Sdelphij * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13296341Sdelphij * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14296341Sdelphij * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15296341Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16296341Sdelphij * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17296341Sdelphij * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18296341Sdelphij */
19296341Sdelphij
20296341Sdelphij#include <sys/cdefs.h>
21296341Sdelphij__FBSDID("$FreeBSD: stable/10/sys/dev/acpi_support/atk0110.c 252276 2013-06-26 23:52:10Z jkim $");
22296341Sdelphij
23296341Sdelphij#include <machine/_inttypes.h>
24296341Sdelphij#include <sys/param.h>
25296341Sdelphij#include <sys/systm.h>
26296341Sdelphij#include <sys/kernel.h>
27296341Sdelphij#include <sys/bus.h>
28296341Sdelphij#include <sys/module.h>
29296341Sdelphij#include <sys/malloc.h>
30296341Sdelphij#include <sys/sysctl.h>
31296341Sdelphij
32296341Sdelphij#include <contrib/dev/acpica/include/acpi.h>
33296341Sdelphij#include <dev/acpica/acpivar.h>
34296341Sdelphij
35296341Sdelphij/*
36296341Sdelphij * ASUSTeK AI Booster (ACPI ASOC ATK0110).
37296341Sdelphij *
38296341Sdelphij * This code was originally written for OpenBSD after the techniques
39296341Sdelphij * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
40296341Sdelphij * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
41296341Sdelphij * provided by Sam Fourman Jr.  It was subsequently ported from OpenBSD to
42296341Sdelphij * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
43296341Sdelphij *
44296341Sdelphij *				  -- Constantine A. Murenin <http://cnst.su/>
45296341Sdelphij */
46296341Sdelphij
47296341Sdelphij#define _COMPONENT	ACPI_OEM
48296341SdelphijACPI_MODULE_NAME("aibs");
49296341SdelphijACPI_SERIAL_DECL(aibs, "aibs");
5055714Skris
51296341Sdelphij#define AIBS_MORE_SENSORS
52296341Sdelphij#define AIBS_VERBOSE
53296341Sdelphij
54296341Sdelphijenum aibs_type {
55296341Sdelphij	AIBS_VOLT,
56296341Sdelphij	AIBS_TEMP,
5755714Skris	AIBS_FAN
5855714Skris};
59296341Sdelphij
60296341Sdelphijstruct aibs_sensor {
61296341Sdelphij	ACPI_INTEGER	v;
62296341Sdelphij	ACPI_INTEGER	i;
63238405Sjkim	ACPI_INTEGER	l;
6455714Skris	ACPI_INTEGER	h;
6555714Skris	enum aibs_type	t;
66296341Sdelphij};
67296341Sdelphij
68296341Sdelphijstruct aibs_softc {
69296341Sdelphij	struct device		*sc_dev;
70109998Smarkm	ACPI_HANDLE		sc_ah;
71296341Sdelphij
7255714Skris	struct aibs_sensor	*sc_asens_volt;
73109998Smarkm	struct aibs_sensor	*sc_asens_temp;
74296341Sdelphij	struct aibs_sensor	*sc_asens_fan;
7559191Skris};
76296341Sdelphij
77296341Sdelphijstatic int aibs_probe(device_t);
78109998Smarkmstatic int aibs_attach(device_t);
79296341Sdelphijstatic int aibs_detach(device_t);
8055714Skrisstatic int aibs_sysctl(SYSCTL_HANDLER_ARGS);
81296341Sdelphij
82296341Sdelphijstatic void aibs_attach_sif(struct aibs_softc *, enum aibs_type);
83296341Sdelphij
84109998Smarkmstatic device_method_t aibs_methods[] = {
85296341Sdelphij	DEVMETHOD(device_probe,		aibs_probe),
8655714Skris	DEVMETHOD(device_attach,	aibs_attach),
87109998Smarkm	DEVMETHOD(device_detach,	aibs_detach),
88296341Sdelphij	{ NULL, NULL }
8968651Skris};
90109998Smarkm
91296341Sdelphijstatic driver_t aibs_driver = {
9255714Skris	"aibs",
93109998Smarkm	aibs_methods,
94296341Sdelphij	sizeof(struct aibs_softc)
9555714Skris};
96160814Ssimon
97296341Sdelphijstatic devclass_t aibs_devclass;
98160814Ssimon
99160814SsimonDRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
100296341SdelphijMODULE_DEPEND(aibs, acpi, 1, 1, 1);
101160814Ssimon
102296341Sdelphijstatic char* aibs_hids[] = {
103109998Smarkm	"ATK0110",
104296341Sdelphij	NULL
10555714Skris};
106109998Smarkm
107296341Sdelphijstatic int
10855714Skrisaibs_probe(device_t dev)
109296341Sdelphij{
110269686Sjkim	if (acpi_disabled("aibs") ||
111296341Sdelphij	    ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
11255714Skris		return ENXIO;
113269686Sjkim
114296341Sdelphij	device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
11555714Skris	return 0;
116111147Snectar}
117296341Sdelphij
118111147Snectarstatic int
119269686Sjkimaibs_attach(device_t dev)
120296341Sdelphij{
12155714Skris	struct aibs_softc *sc = device_get_softc(dev);
122296341Sdelphij
123296341Sdelphij	sc->sc_dev = dev;
124194206Ssimon	sc->sc_ah = acpi_get_handle(dev);
125296341Sdelphij
126194206Ssimon	aibs_attach_sif(sc, AIBS_VOLT);
127296341Sdelphij	aibs_attach_sif(sc, AIBS_TEMP);
128296341Sdelphij	aibs_attach_sif(sc, AIBS_FAN);
129269686Sjkim
130296341Sdelphij	return 0;
13155714Skris}
132296341Sdelphij
133109998Smarkmstatic void
134296341Sdelphijaibs_attach_sif(struct aibs_softc *sc, enum aibs_type st)
13555714Skris{
136296341Sdelphij	ACPI_STATUS		s;
137296341Sdelphij	ACPI_BUFFER		b;
138296341Sdelphij	ACPI_OBJECT		*bp, *o;
139296341Sdelphij	int			i, n;
140296341Sdelphij	const char		*node;
141296341Sdelphij	char			name[] = "?SIF";
142296341Sdelphij	struct aibs_sensor	*as;
143111147Snectar	struct sysctl_oid	*so;
144296341Sdelphij
145111147Snectar	switch (st) {
146238405Sjkim	case AIBS_VOLT:
147296341Sdelphij		node = "volt";
148238405Sjkim		name[0] = 'V';
149296341Sdelphij		break;
150296341Sdelphij	case AIBS_TEMP:
151238405Sjkim		node = "temp";
152296341Sdelphij		name[0] = 'T';
153238405Sjkim		break;
154109998Smarkm	case AIBS_FAN:
155296341Sdelphij		node = "fan";
156109998Smarkm		name[0] = 'F';
157109998Smarkm		break;
158296341Sdelphij	default:
159109998Smarkm		return;
160109998Smarkm	}
161296341Sdelphij
162109998Smarkm	b.Length = ACPI_ALLOCATE_BUFFER;
163109998Smarkm	s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
164296341Sdelphij	    ACPI_TYPE_PACKAGE);
165109998Smarkm	if (ACPI_FAILURE(s)) {
166109998Smarkm		device_printf(sc->sc_dev, "%s not found\n", name);
167296341Sdelphij		return;
168109998Smarkm	}
169109998Smarkm
170296341Sdelphij	bp = b.Pointer;
171109998Smarkm	o = bp->Package.Elements;
172109998Smarkm	if (o[0].Type != ACPI_TYPE_INTEGER) {
173296341Sdelphij		device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
174109998Smarkm		AcpiOsFree(b.Pointer);
175109998Smarkm		return;
176296341Sdelphij	}
177109998Smarkm
178109998Smarkm	n = o[0].Integer.Value;
179296341Sdelphij	if (bp->Package.Count - 1 < n) {
180109998Smarkm		device_printf(sc->sc_dev, "%s: invalid package\n", name);
181109998Smarkm		AcpiOsFree(b.Pointer);
182296341Sdelphij		return;
183109998Smarkm	} else if (bp->Package.Count - 1 > n) {
184109998Smarkm		int on = n;
185296341Sdelphij
186109998Smarkm#ifdef AIBS_MORE_SENSORS
187109998Smarkm		n = bp->Package.Count - 1;
188296341Sdelphij#endif
189109998Smarkm		device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
190109998Smarkm		    ", assume %i\n", name, on, bp->Package.Count - 1, n);
191296341Sdelphij	}
192109998Smarkm	if (n < 1) {
193162911Ssimon		device_printf(sc->sc_dev, "%s: no members in the package\n",
194296341Sdelphij		    name);
195162911Ssimon		AcpiOsFree(b.Pointer);
196162911Ssimon		return;
197296341Sdelphij	}
198162911Ssimon
199162911Ssimon	as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
200296341Sdelphij	if (as == NULL) {
201162911Ssimon		device_printf(sc->sc_dev, "%s: malloc fail\n", name);
202162911Ssimon		AcpiOsFree(b.Pointer);
203296341Sdelphij		return;
204162911Ssimon	}
205162911Ssimon	switch (st) {
206296341Sdelphij	case AIBS_VOLT:
207162911Ssimon		sc->sc_asens_volt = as;
208162911Ssimon		break;
209296341Sdelphij	case AIBS_TEMP:
210162911Ssimon		sc->sc_asens_temp = as;
211296341Sdelphij		break;
212238405Sjkim	case AIBS_FAN:
213296341Sdelphij		sc->sc_asens_fan = as;
214238405Sjkim		break;
215109998Smarkm	}
216296341Sdelphij
21755714Skris	/* sysctl subtree for sensors of this type */
218109998Smarkm	so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
219296341Sdelphij	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
22055714Skris	    node, CTLFLAG_RD, NULL, NULL);
221109998Smarkm
222296341Sdelphij	for (i = 0, o++; i < n; i++, o++) {
22355714Skris		ACPI_OBJECT	*oi;
224109998Smarkm		char		si[3];
225296341Sdelphij		const char	*desc;
22655714Skris
227194206Ssimon		/* acpica5 automatically evaluates the referenced package */
228296341Sdelphij		if (o[0].Type != ACPI_TYPE_PACKAGE) {
229194206Ssimon			device_printf(sc->sc_dev,
230109998Smarkm			    "%s: %i: not a package: %i type\n",
231296341Sdelphij			    name, i, o[0].Type);
23255714Skris			continue;
233109998Smarkm		}
234296341Sdelphij		oi = o[0].Package.Elements;
23559191Skris		if (o[0].Package.Count != 5 ||
236109998Smarkm		    oi[0].Type != ACPI_TYPE_INTEGER ||
237296341Sdelphij		    oi[1].Type != ACPI_TYPE_STRING ||
23855714Skris		    oi[2].Type != ACPI_TYPE_INTEGER ||
239109998Smarkm		    oi[3].Type != ACPI_TYPE_INTEGER ||
240296341Sdelphij		    oi[4].Type != ACPI_TYPE_INTEGER) {
24155714Skris			device_printf(sc->sc_dev,
242109998Smarkm			    "%s: %i: invalid package\n",
243296341Sdelphij			    name, i);
24455714Skris			continue;
245109998Smarkm		}
246296341Sdelphij		as[i].i = oi[0].Integer.Value;
24755714Skris		desc = oi[1].String.Pointer;
248109998Smarkm		as[i].l = oi[2].Integer.Value;
249296341Sdelphij		as[i].h = oi[3].Integer.Value;
25055714Skris		as[i].t = st;
251109998Smarkm#ifdef AIBS_VERBOSE
252296341Sdelphij		device_printf(sc->sc_dev, "%c%i: "
25355714Skris		    "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64"  "
254109998Smarkm		    "0x%"PRIx64"\n",
255296341Sdelphij		    name[0], i,
25655714Skris		    (uint64_t)as[i].i, desc, (int64_t)as[i].l,
257109998Smarkm		    (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value);
258296341Sdelphij#endif
25955714Skris		snprintf(si, sizeof(si), "%i", i);
260109998Smarkm		SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
261296341Sdelphij		    SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD,
26255714Skris		    sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc);
263109998Smarkm	}
264296341Sdelphij
26555714Skris	AcpiOsFree(b.Pointer);
266109998Smarkm}
267296341Sdelphij
26855714Skrisstatic int
269109998Smarkmaibs_detach(device_t dev)
270296341Sdelphij{
27155714Skris	struct aibs_softc	*sc = device_get_softc(dev);
272109998Smarkm
273296341Sdelphij	if (sc->sc_asens_volt != NULL)
27455714Skris		free(sc->sc_asens_volt, M_DEVBUF);
275109998Smarkm	if (sc->sc_asens_temp != NULL)
276296341Sdelphij		free(sc->sc_asens_temp, M_DEVBUF);
27755714Skris	if (sc->sc_asens_fan != NULL)
278109998Smarkm		free(sc->sc_asens_fan, M_DEVBUF);
279296341Sdelphij	return 0;
28055714Skris}
281109998Smarkm
282296341Sdelphij#ifdef AIBS_VERBOSE
28355714Skris#define ddevice_printf(x...) device_printf(x)
284109998Smarkm#else
285296341Sdelphij#define ddevice_printf(x...)
28655714Skris#endif
287109998Smarkm
288296341Sdelphijstatic int
28955714Skrisaibs_sysctl(SYSCTL_HANDLER_ARGS)
290109998Smarkm{
291296341Sdelphij	struct aibs_softc	*sc = arg1;
29255714Skris	enum aibs_type		st = arg2;
293109998Smarkm	int			i = oidp->oid_number;
294296341Sdelphij	ACPI_STATUS		rs;
29555714Skris	ACPI_OBJECT		p, *bp;
296194206Ssimon	ACPI_OBJECT_LIST	mp;
297296341Sdelphij	ACPI_BUFFER		b;
298194206Ssimon	char			*name;
299194206Ssimon	struct aibs_sensor	*as;
300296341Sdelphij	ACPI_INTEGER		v, l, h;
301194206Ssimon	int			so[3];
302194206Ssimon
303296341Sdelphij	switch (st) {
304194206Ssimon	case AIBS_VOLT:
305194206Ssimon		name = "RVLT";
306296341Sdelphij		as = sc->sc_asens_volt;
307194206Ssimon		break;
308109998Smarkm	case AIBS_TEMP:
309296341Sdelphij		name = "RTMP";
31055714Skris		as = sc->sc_asens_temp;
311109998Smarkm		break;
312296341Sdelphij	case AIBS_FAN:
31355714Skris		name = "RFAN";
314109998Smarkm		as = sc->sc_asens_fan;
315296341Sdelphij		break;
31655714Skris	default:
317109998Smarkm		return ENOENT;
318296341Sdelphij	}
31955714Skris	if (as == NULL)
320109998Smarkm		return ENOENT;
321296341Sdelphij	l = as[i].l;
32259191Skris	h = as[i].h;
323109998Smarkm	p.Type = ACPI_TYPE_INTEGER;
324296341Sdelphij	p.Integer.Value = as[i].i;
32559191Skris	mp.Count = 1;
326109998Smarkm	mp.Pointer = &p;
327296341Sdelphij	b.Length = ACPI_ALLOCATE_BUFFER;
32855714Skris	ACPI_SERIAL_BEGIN(aibs);
329109998Smarkm	rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
330296341Sdelphij	    ACPI_TYPE_INTEGER);
33155714Skris	if (ACPI_FAILURE(rs)) {
332109998Smarkm		ddevice_printf(sc->sc_dev,
333296341Sdelphij		    "%s: %i: evaluation failed\n",
33455714Skris		    name, i);
335109998Smarkm		ACPI_SERIAL_END(aibs);
336296341Sdelphij		return EIO;
33755714Skris	}
338109998Smarkm	bp = b.Pointer;
339296341Sdelphij	v = bp->Integer.Value;
34055714Skris	AcpiOsFree(b.Pointer);
341109998Smarkm	ACPI_SERIAL_END(aibs);
342296341Sdelphij
34355714Skris	switch (st) {
344109998Smarkm	case AIBS_VOLT:
345296341Sdelphij		break;
34655714Skris	case AIBS_TEMP:
347109998Smarkm		v += 2732;
348296341Sdelphij		l += 2732;
34955714Skris		h += 2732;
350109998Smarkm		break;
351296341Sdelphij	case AIBS_FAN:
35255714Skris		break;
353109998Smarkm	}
354296341Sdelphij	so[0] = v;
35555714Skris	so[1] = l;
356109998Smarkm	so[2] = h;
357296341Sdelphij	return sysctl_handle_opaque(oidp, &so, sizeof(so), req);
35855714Skris}
359109998Smarkm