xref: /netbsd-src/sys/dev/pci/viaenv.c (revision 69b6d498973bb4d7230c2d3c12bd9a032738ec8e)
1 /*	$NetBSD: viaenv.c,v 1.11 2005/06/28 00:28:42 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.11 2005/06/28 00:28:42 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 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 	control = pci_conf_read(va->va_pc, va->va_tag, va->va_offset + 4);
277 	if ((iobase & 0xff80) == 0 || (control & 1) == 0) {
278 		printf(": disabled\n");
279 		return;
280 	}
281 	sc->sc_iot = va->va_iot;
282 	if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) {
283 		printf(": failed to map i/o\n");
284 		return;
285 	}
286 	printf("\n");
287 
288 	simple_lock_init(&sc->sc_slock);
289 
290 	/* Initialize sensors */
291 	for (i = 0; i < VIANUMSENSORS; ++i) {
292 		sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
293 		sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID);
294 		sc->sc_info[i].validflags = ENVSYS_FVALID;
295 		sc->sc_data[i].warnflags = ENVSYS_WARN_OK;
296 	}
297 
298 	for (i = 0; i <= 2; i++) {
299 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP;
300 	}
301 	strcpy(sc->sc_info[0].desc, "TSENS1");
302 	strcpy(sc->sc_info[1].desc, "TSENS2");
303 	strcpy(sc->sc_info[2].desc, "TSENS3");
304 
305 	for (i = 3; i <= 4; i++) {
306 		sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM;
307 	}
308 	strcpy(sc->sc_info[3].desc, "FAN1");
309 	strcpy(sc->sc_info[4].desc, "FAN2");
310 
311 	for (i = 5; i <= 9; ++i) {
312 		sc->sc_data[i].units = sc->sc_info[i].units =
313 		    ENVSYS_SVOLTS_DC;
314 		sc->sc_info[i].rfact = 1;	/* what is this used for? */
315 	}
316 	strcpy(sc->sc_info[5].desc, "VSENS1");	/* CPU core (2V) */
317 	strcpy(sc->sc_info[6].desc, "VSENS2");	/* NB core? (2.5V) */
318 	strcpy(sc->sc_info[7].desc, "Vcore");	/* Vcore (3.3V) */
319 	strcpy(sc->sc_info[8].desc, "VSENS3");	/* VSENS3 (5V) */
320 	strcpy(sc->sc_info[9].desc, "VSENS4");	/* VSENS4 (12V) */
321 
322 	/* Get initial set of sensor values. */
323 	viaenv_refresh_sensor_data(sc);
324 
325 	/*
326 	 * Hook into the System Monitor.
327 	 */
328 	sc->sc_sysmon.sme_ranges = viaenv_ranges;
329 	sc->sc_sysmon.sme_sensor_info = sc->sc_info;
330 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
331 	sc->sc_sysmon.sme_cookie = sc;
332 
333 	sc->sc_sysmon.sme_gtredata = viaenv_gtredata;
334 	sc->sc_sysmon.sme_streinfo = viaenv_streinfo;
335 
336 	sc->sc_sysmon.sme_nsensors = VIANUMSENSORS;
337 	sc->sc_sysmon.sme_envsys_version = 1000;
338 
339 	if (sysmon_envsys_register(&sc->sc_sysmon))
340 		printf("%s: unable to register with sysmon\n",
341 		    sc->sc_dev.dv_xname);
342 }
343 
344 CFATTACH_DECL(viaenv, sizeof(struct viaenv_softc),
345     viaenv_match, viaenv_attach, NULL, NULL);
346 
347 static int
348 viaenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
349 {
350 	struct viaenv_softc *sc = sme->sme_cookie;
351 
352 	simple_lock(&sc->sc_slock);
353 
354 	viaenv_refresh_sensor_data(sc);
355 	*tred = sc->sc_data[tred->sensor];
356 
357 	simple_unlock(&sc->sc_slock);
358 
359 	return (0);
360 }
361 
362 static int
363 viaenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
364 {
365 
366 	/* XXX Not implemented */
367 	binfo->validflags = 0;
368 
369 	return (0);
370 }
371