xref: /netbsd-src/sys/dev/pci/viaenv.c (revision 08c81a9c2dc8c7300e893321eb65c0925d60871c)
1 /*	$NetBSD: viaenv.c,v 1.6 2002/03/25 21:29:58 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2000 Johan Danielsson
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  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of author nor the names of any contributors may
19  *    be used to endorse or promote products derived from this
20  *    software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /* driver for the hardware monitoring part of the VIA VT82C686A */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: viaenv.c,v 1.6 2002/03/25 21:29:58 thorpej Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/ioctl.h>
44 #include <sys/kthread.h>
45 #include <sys/lock.h>
46 #include <sys/errno.h>
47 #include <sys/device.h>
48 
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/pcireg.h>
51 
52 #include <dev/pci/viapmvar.h>
53 
54 #include <dev/sysmon/sysmonvar.h>
55 
56 #ifdef VIAENV_DEBUG
57 unsigned int viaenv_debug = 0;
58 #define DPRINTF(X) do { if(viaenv_debug) printf X ; } while(0)
59 #else
60 #define DPRINTF(X)
61 #endif
62 
63 #define VIANUMSENSORS 10	/* three temp, two fan, five voltage */
64 
65 struct viaenv_softc {
66 	struct device sc_dev;
67 	bus_space_tag_t sc_iot;
68 	bus_space_handle_t sc_ioh;
69 
70 	int     sc_fan_div[2];	/* fan RPM divisor */
71 
72 	struct envsys_tre_data sc_data[VIANUMSENSORS];
73 	struct envsys_basic_info sc_info[VIANUMSENSORS];
74 
75 	struct simplelock sc_slock;
76 	struct timeval sc_lastread;
77 
78 	struct sysmon_envsys sc_sysmon;
79 };
80 
81 const struct envsys_range viaenv_ranges[] = {
82 	{ 0, 2,		ENVSYS_STEMP },
83 	{ 3, 4,		ENVSYS_SFANRPM },
84 	{ 0, 1,		ENVSYS_SVOLTS_AC },	/* none */
85 	{ 5, 11,	ENVSYS_SVOLTS_DC },
86 	{ 1, 0,		ENVSYS_SOHMS },		/* none */
87 	{ 1, 0,		ENVSYS_SWATTS },	/* none */
88 	{ 1, 0,		ENVSYS_SAMPS },		/* none */
89 };
90 
91 int	viaenv_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
92 int	viaenv_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
93 
94 static int
95         viaenv_match(struct device * parent, struct cfdata * match, void *aux);
96 static void
97         viaenv_attach(struct device * parent, struct device * self, void *aux);
98 
99 struct cfattach viaenv_ca = {
100 	sizeof(struct viaenv_softc), viaenv_match, viaenv_attach
101 };
102 
103 static int
104 viaenv_match(struct device * parent, struct cfdata * match, void *aux)
105 {
106 	struct viapm_attach_args *va = aux;
107 
108 	if (va->va_type == VIAPM_HWMON)
109 		return 1;
110 	return 0;
111 }
112 
113 /*
114  * XXX there doesn't seem to exist much hard documentation on how to
115  * convert the raw values to usable units, this code is more or less
116  * stolen from the Linux driver, but changed to suit our conditions
117  */
118 
119 /*
120  * lookup-table to translate raw values to uK, this is the same table
121  * used by the Linux driver (modulo units); there is a fifth degree
122  * polynomial that supposedly been used to generate this table, but I
123  * haven't been able to figure out how -- it doesn't give the same values
124  */
125 
126 static const long val_to_temp[] = {
127 	20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
128 	22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
129 	23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
130 	24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
131 	25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
132 	26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
133 	26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
134 	27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
135 	27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
136 	28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
137 	28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
138 	28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
139 	29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
140 	29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
141 	30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
142 	30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
143 	31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
144 	31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
145 	32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
146 	32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
147 	33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
148 	34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
149 	35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
150 	36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
151 	38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
152 	40575, 40835, 41095, 41375, 41655, 41935,
153 };
154 
155 /* use above table to convert values to temperatures in micro-Kelvins */
156 static int
157 val_to_uK(unsigned int val)
158 {
159 	int     i = val / 4;
160 	int     j = val % 4;
161 
162 	assert(i >= 0 && i <= 255);
163 
164 	if (j == 0 || i == 255)
165 		return val_to_temp[i] * 10000;
166 
167 	/* is linear interpolation ok? */
168 	return (val_to_temp[i] * (4 - j) +
169 	    val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
170 }
171 
172 static int
173 val_to_rpm(unsigned int val, int div)
174 {
175 
176 	if (val == 0)
177 		return 0;
178 
179 	return 1350000 / val / div;
180 }
181 
182 static long
183 val_to_uV(unsigned int val, int index)
184 {
185 	static const long mult[] =
186 	    {1250000, 1250000, 1670000, 2600000, 6300000};
187 
188 	assert(index >= 0 && index <= 4);
189 
190 	return (25LL * val + 133) * mult[index] / 2628;
191 }
192 
193 #define VIAENV_TSENS3	0x1f
194 #define VIAENV_TSENS1	0x20
195 #define VIAENV_TSENS2	0x21
196 #define VIAENV_VSENS1	0x22
197 #define VIAENV_VSENS2	0x23
198 #define VIAENV_VCORE	0x24
199 #define VIAENV_VSENS3	0x25
200 #define VIAENV_VSENS4	0x26
201 #define VIAENV_FAN1	0x29
202 #define VIAENV_FAN2	0x2a
203 #define VIAENV_FANCONF	0x47	/* fan configuration */
204 #define VIAENV_TLOW	0x49	/* temperature low order value */
205 #define VIAENV_TIRQ	0x4b	/* temperature interrupt configuration */
206 
207 
208 static void
209 viaenv_refresh_sensor_data(struct viaenv_softc *sc)
210 {
211 	static const struct timeval onepointfive =  { 1, 500000 };
212 	struct timeval t;
213 	u_int8_t v, v2;
214 	int i, s;
215 
216 	/* Read new values at most once every 1.5 seconds. */
217 	timeradd(&sc->sc_lastread, &onepointfive, &t);
218 	s = splclock();
219 	i = timercmp(&mono_time, &t, >);
220 	if (i)
221 		sc->sc_lastread = mono_time;
222 	splx(s);
223 
224 	if (i == 0)
225 		return;
226 
227 	/* temperature */
228 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
229 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
230 	DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
231 	sc->sc_data[0].cur.data_us = val_to_uK((v2 << 2) | (v >> 6));
232 	sc->sc_data[0].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
233 
234 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
235 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
236 	DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
237 	sc->sc_data[1].cur.data_us =
238 	    val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
239 	sc->sc_data[1].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
240 
241 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
242 	DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
243 	sc->sc_data[2].cur.data_us = val_to_uK((v2 << 2) | (v >> 6));
244 	sc->sc_data[2].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
245 
246 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
247 
248 	sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
249 	sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
250 
251 	/* fan */
252 	for (i = 3; i <= 4; i++) {
253 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
254 		    VIAENV_FAN1 + i - 3);
255 		DPRINTF(("FAN%d = %d / %d\n", i - 3, v,
256 		    sc->sc_fan_div[i - 3]));
257 		sc->sc_data[i].cur.data_us = val_to_rpm(v,
258 		    sc->sc_fan_div[i - 3]);
259 		sc->sc_data[i].validflags =
260 		    ENVSYS_FVALID | ENVSYS_FCURVALID;
261 	}
262 
263 	/* voltage */
264 	for (i = 5; i <= 9; i++) {
265 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
266 		    VIAENV_VSENS1 + i - 5);
267 		DPRINTF(("V%d = %d\n", i - 5, v));
268 		sc->sc_data[i].cur.data_us = val_to_uV(v, i - 5);
269 		sc->sc_data[i].validflags =
270 		    ENVSYS_FVALID | ENVSYS_FCURVALID;
271 	}
272 }
273 
274 static void
275 viaenv_attach(struct device * parent, struct device * self, void *aux)
276 {
277 	struct viapm_attach_args *va = aux;
278 	struct viaenv_softc *sc = (struct viaenv_softc *) self;
279 	pcireg_t iobase, control;
280 	int i;
281 
282 	iobase = pci_conf_read(va->va_pc, va->va_tag, va->va_offset);
283 	control = pci_conf_read(va->va_pc, va->va_tag, va->va_offset + 4);
284 	if ((iobase & 0xff80) == 0 || (control & 1) == 0) {
285 		printf(": disabled\n");
286 		return;
287 	}
288 	sc->sc_iot = va->va_iot;
289 	if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) {
290 		printf(": failed to map i/o\n");
291 		return;
292 	}
293 	printf("\n");
294 
295 	simple_lock_init(&sc->sc_slock);
296 
297 	/* Initialize sensors */
298 	for (i = 0; i < VIANUMSENSORS; ++i) {
299 		sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
300 		sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID);
301 		sc->sc_info[i].validflags = ENVSYS_FVALID;
302 		sc->sc_data[i].warnflags = ENVSYS_WARN_OK;
303 	}
304 
305 	for (i = 0; i <= 2; i++) {
306 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP;
307 	}
308 	strcpy(sc->sc_info[0].desc, "TSENS1");
309 	strcpy(sc->sc_info[1].desc, "TSENS2");
310 	strcpy(sc->sc_info[2].desc, "TSENS3");
311 
312 	for (i = 3; i <= 4; i++) {
313 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM;
314 	}
315 	strcpy(sc->sc_info[3].desc, "FAN1");
316 	strcpy(sc->sc_info[4].desc, "FAN2");
317 
318 	for (i = 5; i <= 9; ++i) {
319 		sc->sc_data[i].units = sc->sc_info[i].units =
320 		    ENVSYS_SVOLTS_DC;
321 		sc->sc_info[i].rfact = 1;	/* what is this used for? */
322 	}
323 	strcpy(sc->sc_info[5].desc, "VSENS1");	/* CPU core (2V) */
324 	strcpy(sc->sc_info[6].desc, "VSENS2");	/* NB core? (2.5V) */
325 	strcpy(sc->sc_info[7].desc, "Vcore");	/* Vcore (3.3V) */
326 	strcpy(sc->sc_info[8].desc, "VSENS3");	/* VSENS3 (5V) */
327 	strcpy(sc->sc_info[9].desc, "VSENS4");	/* VSENS4 (12V) */
328 
329 	/* Get initial set of sensor values. */
330 	viaenv_refresh_sensor_data(sc);
331 
332 	/*
333 	 * Hook into the System Monitor.
334 	 */
335 	sc->sc_sysmon.sme_ranges = viaenv_ranges;
336 	sc->sc_sysmon.sme_sensor_info = sc->sc_info;
337 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
338 	sc->sc_sysmon.sme_cookie = sc;
339 
340 	sc->sc_sysmon.sme_gtredata = viaenv_gtredata;
341 	sc->sc_sysmon.sme_streinfo = viaenv_streinfo;
342 
343 	sc->sc_sysmon.sme_nsensors = VIANUMSENSORS;
344 	sc->sc_sysmon.sme_envsys_version = 1000;
345 
346 	if (sysmon_envsys_register(&sc->sc_sysmon))
347 		printf("%s: unable to register with sysmon\n",
348 		    sc->sc_dev.dv_xname);
349 }
350 
351 int
352 viaenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
353 {
354 	struct viaenv_softc *sc = sme->sme_cookie;
355 
356 	simple_lock(&sc->sc_slock);
357 
358 	viaenv_refresh_sensor_data(sc);
359 	*tred = sc->sc_data[tred->sensor];
360 
361 	simple_unlock(&sc->sc_slock);
362 
363 	return (0);
364 }
365 
366 int
367 viaenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
368 {
369 
370 	/* XXX Not implemented */
371 	binfo->validflags = 0;
372 
373 	return (0);
374 }
375