xref: /netbsd-src/sys/dev/pci/viaenv.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: viaenv.c,v 1.14 2006/06/07 22:33:37 kardel 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.14 2006/06/07 22:33:37 kardel 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 static 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 static int	viaenv_gtredata(struct sysmon_envsys *,
92 				struct envsys_tre_data *);
93 static int 	viaenv_streinfo(struct sysmon_envsys *,
94 				struct envsys_basic_info *);
95 
96 static int
97 viaenv_match(struct device * parent, struct cfdata * match, void *aux)
98 {
99 	struct viapm_attach_args *va = aux;
100 
101 	if (va->va_type == VIAPM_HWMON)
102 		return 1;
103 	return 0;
104 }
105 
106 /*
107  * XXX there doesn't seem to exist much hard documentation on how to
108  * convert the raw values to usable units, this code is more or less
109  * stolen from the Linux driver, but changed to suit our conditions
110  */
111 
112 /*
113  * lookup-table to translate raw values to uK, this is the same table
114  * used by the Linux driver (modulo units); there is a fifth degree
115  * polynomial that supposedly been used to generate this table, but I
116  * haven't been able to figure out how -- it doesn't give the same values
117  */
118 
119 static const long val_to_temp[] = {
120 	20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
121 	22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
122 	23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
123 	24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
124 	25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
125 	26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
126 	26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
127 	27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
128 	27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
129 	28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
130 	28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
131 	28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
132 	29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
133 	29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
134 	30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
135 	30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
136 	31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
137 	31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
138 	32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
139 	32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
140 	33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
141 	34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
142 	35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
143 	36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
144 	38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
145 	40575, 40835, 41095, 41375, 41655, 41935,
146 };
147 
148 /* use above table to convert values to temperatures in micro-Kelvins */
149 static int
150 val_to_uK(unsigned int val)
151 {
152 	int     i = val / 4;
153 	int     j = val % 4;
154 
155 	assert(i >= 0 && i <= 255);
156 
157 	if (j == 0 || i == 255)
158 		return val_to_temp[i] * 10000;
159 
160 	/* is linear interpolation ok? */
161 	return (val_to_temp[i] * (4 - j) +
162 	    val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
163 }
164 
165 static int
166 val_to_rpm(unsigned int val, int div)
167 {
168 
169 	if (val == 0)
170 		return 0;
171 
172 	return 1350000 / val / div;
173 }
174 
175 static long
176 val_to_uV(unsigned int val, int index)
177 {
178 	static const long mult[] =
179 	    {1250000, 1250000, 1670000, 2600000, 6300000};
180 
181 	assert(index >= 0 && index <= 4);
182 
183 	return (25LL * val + 133) * mult[index] / 2628;
184 }
185 
186 #define VIAENV_TSENS3	0x1f
187 #define VIAENV_TSENS1	0x20
188 #define VIAENV_TSENS2	0x21
189 #define VIAENV_VSENS1	0x22
190 #define VIAENV_VSENS2	0x23
191 #define VIAENV_VCORE	0x24
192 #define VIAENV_VSENS3	0x25
193 #define VIAENV_VSENS4	0x26
194 #define VIAENV_FAN1	0x29
195 #define VIAENV_FAN2	0x2a
196 #define VIAENV_FANCONF	0x47	/* fan configuration */
197 #define VIAENV_TLOW	0x49	/* temperature low order value */
198 #define VIAENV_TIRQ	0x4b	/* temperature interrupt configuration */
199 
200 
201 static void
202 viaenv_refresh_sensor_data(struct viaenv_softc *sc)
203 {
204 	static const struct timeval onepointfive =  { 1, 500000 };
205 	struct timeval t, utv;
206 	u_int8_t v, v2;
207 	int i;
208 
209 	/* Read new values at most once every 1.5 seconds. */
210 	timeradd(&sc->sc_lastread, &onepointfive, &t);
211 	getmicrouptime(&utv);
212 	i = timercmp(&utv, &t, >);
213 	if (i)
214 		sc->sc_lastread = utv;
215 
216 	if (i == 0)
217 		return;
218 
219 	/* temperature */
220 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
221 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
222 	DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
223 	sc->sc_data[0].cur.data_us = val_to_uK((v2 << 2) | (v >> 6));
224 	sc->sc_data[0].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
225 
226 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
227 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
228 	DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
229 	sc->sc_data[1].cur.data_us =
230 	    val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
231 	sc->sc_data[1].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
232 
233 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
234 	DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
235 	sc->sc_data[2].cur.data_us = val_to_uK((v2 << 2) | (v >> 6));
236 	sc->sc_data[2].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
237 
238 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
239 
240 	sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
241 	sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
242 
243 	/* fan */
244 	for (i = 3; i <= 4; i++) {
245 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
246 		    VIAENV_FAN1 + i - 3);
247 		DPRINTF(("FAN%d = %d / %d\n", i - 3, v,
248 		    sc->sc_fan_div[i - 3]));
249 		sc->sc_data[i].cur.data_us = val_to_rpm(v,
250 		    sc->sc_fan_div[i - 3]);
251 		sc->sc_data[i].validflags =
252 		    ENVSYS_FVALID | ENVSYS_FCURVALID;
253 	}
254 
255 	/* voltage */
256 	for (i = 5; i <= 9; i++) {
257 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
258 		    VIAENV_VSENS1 + i - 5);
259 		DPRINTF(("V%d = %d\n", i - 5, v));
260 		sc->sc_data[i].cur.data_us = val_to_uV(v, i - 5);
261 		sc->sc_data[i].validflags =
262 		    ENVSYS_FVALID | ENVSYS_FCURVALID;
263 	}
264 }
265 
266 static void
267 viaenv_attach(struct device * parent, struct device * self, void *aux)
268 {
269 	struct viapm_attach_args *va = aux;
270 	struct viaenv_softc *sc = (struct viaenv_softc *) self;
271 	pcireg_t iobase, control;
272 	int i;
273 
274 	iobase = pci_conf_read(va->va_pc, va->va_tag, va->va_offset);
275 	if ((iobase & 0xff80) == 0) {
276 		printf(": disabled\n");
277 		return;
278 	}
279 	control = pci_conf_read(va->va_pc, va->va_tag, va->va_offset + 4);
280 	/* If the device is disabled, turn it on */
281 	if ((control & 1) == 0)
282 		pci_conf_write(va->va_pc, va->va_tag, va->va_offset + 4,
283 		    control | 1);
284 
285 	sc->sc_iot = va->va_iot;
286 	if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) {
287 		printf(": failed to map i/o\n");
288 		return;
289 	}
290 	printf("\n");
291 
292 	simple_lock_init(&sc->sc_slock);
293 
294 	/* Initialize sensors */
295 	for (i = 0; i < VIANUMSENSORS; ++i) {
296 		sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
297 		sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID);
298 		sc->sc_info[i].validflags = ENVSYS_FVALID;
299 		sc->sc_data[i].warnflags = ENVSYS_WARN_OK;
300 	}
301 
302 	for (i = 0; i <= 2; i++) {
303 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP;
304 	}
305 	strcpy(sc->sc_info[0].desc, "TSENS1");
306 	strcpy(sc->sc_info[1].desc, "TSENS2");
307 	strcpy(sc->sc_info[2].desc, "TSENS3");
308 
309 	for (i = 3; i <= 4; i++) {
310 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM;
311 	}
312 	strcpy(sc->sc_info[3].desc, "FAN1");
313 	strcpy(sc->sc_info[4].desc, "FAN2");
314 
315 	for (i = 5; i <= 9; ++i) {
316 		sc->sc_data[i].units = sc->sc_info[i].units =
317 		    ENVSYS_SVOLTS_DC;
318 		sc->sc_info[i].rfact = 1;	/* what is this used for? */
319 	}
320 	strcpy(sc->sc_info[5].desc, "VSENS1");	/* CPU core (2V) */
321 	strcpy(sc->sc_info[6].desc, "VSENS2");	/* NB core? (2.5V) */
322 	strcpy(sc->sc_info[7].desc, "Vcore");	/* Vcore (3.3V) */
323 	strcpy(sc->sc_info[8].desc, "VSENS3");	/* VSENS3 (5V) */
324 	strcpy(sc->sc_info[9].desc, "VSENS4");	/* VSENS4 (12V) */
325 
326 	/* Get initial set of sensor values. */
327 	viaenv_refresh_sensor_data(sc);
328 
329 	/*
330 	 * Hook into the System Monitor.
331 	 */
332 	sc->sc_sysmon.sme_ranges = viaenv_ranges;
333 	sc->sc_sysmon.sme_sensor_info = sc->sc_info;
334 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
335 	sc->sc_sysmon.sme_cookie = sc;
336 
337 	sc->sc_sysmon.sme_gtredata = viaenv_gtredata;
338 	sc->sc_sysmon.sme_streinfo = viaenv_streinfo;
339 
340 	sc->sc_sysmon.sme_nsensors = VIANUMSENSORS;
341 	sc->sc_sysmon.sme_envsys_version = 1000;
342 
343 	if (sysmon_envsys_register(&sc->sc_sysmon))
344 		printf("%s: unable to register with sysmon\n",
345 		    sc->sc_dev.dv_xname);
346 }
347 
348 CFATTACH_DECL(viaenv, sizeof(struct viaenv_softc),
349     viaenv_match, viaenv_attach, NULL, NULL);
350 
351 static 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 static 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