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