xref: /netbsd-src/sys/dev/pci/viaenv.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /*	$NetBSD: viaenv.c,v 1.13 2005/12/11 12:22:51 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.13 2005/12/11 12:22:51 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 	{ 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;
206 	u_int8_t v, v2;
207 	int i, s;
208 
209 	/* Read new values at most once every 1.5 seconds. */
210 	timeradd(&sc->sc_lastread, &onepointfive, &t);
211 	s = splclock();
212 	i = timercmp(&mono_time, &t, >);
213 	if (i)
214 		sc->sc_lastread = mono_time;
215 	splx(s);
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, 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, struct envsys_basic_info *binfo)
369 {
370 
371 	/* XXX Not implemented */
372 	binfo->validflags = 0;
373 
374 	return (0);
375 }
376