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