1 /*	$OpenBSD: viaenv.c,v 1.6 2004/10/05 19:03:58 grange Exp $	*/
2 /*	$NetBSD: viaenv.c,v 1.9 2002/10/02 16:51:59 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 2000 Johan Danielsson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of author nor the names of any contributors may
20  *    be used to endorse or promote products derived from this
21  *    software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /* driver for the hardware monitoring part of the VIA VT82C686A */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/device.h>
41 #include <sys/kernel.h>
42 #include <sys/queue.h>
43 #include <sys/sensors.h>
44 #include <sys/timeout.h>
45 #ifdef __HAVE_TIMECOUNTER
46 #include <sys/timetc.h>
47 #endif
48 
49 #include <machine/bus.h>
50 
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcidevs.h>
54 
55 #ifdef VIAENV_DEBUG
56 unsigned int viaenv_debug = 0;
57 #define DPRINTF(X) do { if(viaenv_debug) printf X ; } while(0)
58 #else
59 #define DPRINTF(X)
60 #endif
61 
62 #define VIANUMSENSORS 10	/* three temp, two fan, five voltage */
63 
64 struct viaenv_softc {
65 	struct device sc_dev;
66 	bus_space_tag_t sc_iot;
67 	bus_space_handle_t sc_ioh;
68 	bus_space_handle_t sc_pm_ioh;
69 
70 	int     sc_fan_div[2];	/* fan RPM divisor */
71 
72 	struct sensor sc_data[VIANUMSENSORS];
73 };
74 
75 int  viaenv_match(struct device *, void *, void *);
76 void viaenv_attach(struct device *, struct device *, void *);
77 
78 int  val_to_uK(unsigned int);
79 int  val_to_rpm(unsigned int, int);
80 long val_to_uV(unsigned int, int);
81 void viaenv_refresh_sensor_data(struct viaenv_softc *);
82 void viaenv_refresh(void *);
83 
84 #ifdef __HAVE_TIMECOUNTER
85 u_int viaenv_get_timecount(struct timecounter *tc);
86 
87 struct timecounter viaenv_timecounter = {
88 	viaenv_get_timecount,	/* get_timecount */
89 	0,			/* no poll_pps */
90 	0xffffff,		/* counter_mask */
91 	3579545,		/* frequency */
92 	"VIAPM",		/* name */
93 	1000			/* quality */
94 };
95 #endif	/* __HAVE_TIMECOUNTER */
96 
97 struct cfattach viaenv_ca = {
98 	sizeof(struct viaenv_softc),
99 	viaenv_match,
100 	viaenv_attach
101 };
102 
103 struct cfdriver viaenv_cd = {
104 	NULL, "viaenv", DV_DULL
105 };
106 
107 struct timeout viaenv_timeout;
108 
109 const struct pci_matchid viaenv_devices[] = {
110 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_SMB },
111 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8231_PWR }
112 };
113 
114 int
viaenv_match(struct device * parent,void * match,void * aux)115 viaenv_match(struct device *parent, void *match, void *aux)
116 {
117 	return (pci_matchbyid((struct pci_attach_args *)aux, viaenv_devices,
118 	    sizeof(viaenv_devices) / sizeof(viaenv_devices[0])));
119 }
120 
121 /*
122  * XXX there doesn't seem to exist much hard documentation on how to
123  * convert the raw values to usable units, this code is more or less
124  * stolen from the Linux driver, but changed to suit our conditions
125  */
126 
127 /*
128  * lookup-table to translate raw values to uK, this is the same table
129  * used by the Linux driver (modulo units); there is a fifth degree
130  * polynomial that supposedly been used to generate this table, but I
131  * haven't been able to figure out how -- it doesn't give the same values
132  */
133 
134 static const long val_to_temp[] = {
135 	20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
136 	22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
137 	23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
138 	24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
139 	25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
140 	26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
141 	26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
142 	27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
143 	27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
144 	28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
145 	28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
146 	28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
147 	29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
148 	29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
149 	30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
150 	30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
151 	31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
152 	31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
153 	32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
154 	32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
155 	33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
156 	34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
157 	35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
158 	36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
159 	38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
160 	40575, 40835, 41095, 41375, 41655, 41935,
161 };
162 
163 /* use above table to convert values to temperatures in micro-Kelvins */
164 int
val_to_uK(unsigned int val)165 val_to_uK(unsigned int val)
166 {
167 	int     i = val / 4;
168 	int     j = val % 4;
169 
170 	assert(i >= 0 && i <= 255);
171 
172 	if (j == 0 || i == 255)
173 		return val_to_temp[i] * 10000;
174 
175 	/* is linear interpolation ok? */
176 	return (val_to_temp[i] * (4 - j) +
177 	    val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
178 }
179 
180 int
val_to_rpm(unsigned int val,int div)181 val_to_rpm(unsigned int val, int div)
182 {
183 
184 	if (val == 0)
185 		return 0;
186 
187 	return 1350000 / val / div;
188 }
189 
190 long
val_to_uV(unsigned int val,int index)191 val_to_uV(unsigned int val, int index)
192 {
193 	static const long mult[] =
194 	    {1250000, 1250000, 1670000, 2600000, 6300000};
195 
196 	assert(index >= 0 && index <= 4);
197 
198 	return (25LL * val + 133) * mult[index] / 2628;
199 }
200 
201 #define VIAENV_TSENS3	0x1f
202 #define VIAENV_TSENS1	0x20
203 #define VIAENV_TSENS2	0x21
204 #define VIAENV_VSENS1	0x22
205 #define VIAENV_VSENS2	0x23
206 #define VIAENV_VCORE	0x24
207 #define VIAENV_VSENS3	0x25
208 #define VIAENV_VSENS4	0x26
209 #define VIAENV_FAN1	0x29
210 #define VIAENV_FAN2	0x2a
211 #define VIAENV_FANCONF	0x47	/* fan configuration */
212 #define VIAENV_TLOW	0x49	/* temperature low order value */
213 #define VIAENV_TIRQ	0x4b	/* temperature interrupt configuration */
214 
215 #define VIAENV_GENCFG	0x40	/* general configuration */
216 #define VIAENV_GENCFG_TMR32	(1 << 11)	/* 32-bit PM timer */
217 #define VIAENV_GENCFG_PMEN	(1 << 15)	/* enable PM I/O space */
218 #define VIAENV_PMBASE	0x48	/* power management I/O space base */
219 #define VIAENV_PMSIZE	128	/* power management I/O space size */
220 #define VIAENV_PM_TMR	0x08	/* PM timer */
221 
222 void
viaenv_refresh_sensor_data(struct viaenv_softc * sc)223 viaenv_refresh_sensor_data(struct viaenv_softc *sc)
224 {
225 	int i;
226 	u_int8_t v, v2;
227 
228 	/* temperature */
229 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
230 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
231 	DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
232 	sc->sc_data[0].value = val_to_uK((v2 << 2) | (v >> 6));
233 
234 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
235 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
236 	DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
237 	sc->sc_data[1].value = val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
238 
239 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
240 	DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
241 	sc->sc_data[2].value = val_to_uK((v2 << 2) | (v >> 6));
242 
243 	v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
244 
245 	sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
246 	sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
247 
248 	/* fan */
249 	for (i = 3; i <= 4; i++) {
250 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
251 		    VIAENV_FAN1 + i - 3);
252 		DPRINTF(("FAN%d = %d / %d\n", i - 3, v,
253 		    sc->sc_fan_div[i - 3]));
254 		sc->sc_data[i].value = val_to_rpm(v, sc->sc_fan_div[i - 3]);
255 	}
256 
257 	/* voltage */
258 	for (i = 5; i <= 9; i++) {
259 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
260 		    VIAENV_VSENS1 + i - 5);
261 		DPRINTF(("V%d = %d\n", i - 5, v));
262 		sc->sc_data[i].value = val_to_uV(v, i - 5);
263 	}
264 }
265 
266 void
viaenv_attach(struct device * parent,struct device * self,void * aux)267 viaenv_attach(struct device * parent, struct device * self, void *aux)
268 {
269 	struct viaenv_softc *sc = (struct viaenv_softc *) self;
270 	struct pci_attach_args *pa = aux;
271 	pcireg_t iobase, control;
272 	int i;
273 
274 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x70);
275 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x74);
276 	if ((iobase & 0xff80) == 0 || (control & 1) == 0) {
277 		printf(": HWM disabled");
278 		goto nohwm;
279 	}
280 	sc->sc_iot = pa->pa_iot;
281 	if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) {
282 		printf(": failed to map HWM I/O space");
283 		goto nohwm;
284 	}
285 
286 	/* Initialize sensors */
287 	for (i = 0; i < VIANUMSENSORS; ++i) {
288 		strlcpy(sc->sc_data[i].device, sc->sc_dev.dv_xname,
289 		    sizeof(sc->sc_data[i].device));
290 		SENSOR_ADD(&sc->sc_data[i]);
291 	}
292 
293 	for (i = 0; i <= 2; i++) {
294 		sc->sc_data[i].type = SENSOR_TEMP;
295 	}
296 	strlcpy(sc->sc_data[0].desc, "TSENS1", sizeof(sc->sc_data[0].desc));
297 	strlcpy(sc->sc_data[1].desc, "TSENS2", sizeof(sc->sc_data[1].desc));
298 	strlcpy(sc->sc_data[2].desc, "TSENS3", sizeof(sc->sc_data[2].desc));
299 
300 	for (i = 3; i <= 4; i++) {
301 		sc->sc_data[i].type = SENSOR_FANRPM;
302 	}
303 	strlcpy(sc->sc_data[3].desc, "FAN1", sizeof(sc->sc_data[3].desc));
304 	strlcpy(sc->sc_data[4].desc, "FAN2", sizeof(sc->sc_data[4].desc));
305 
306 	for (i = 5; i <= 9; ++i) {
307 		sc->sc_data[i].type = SENSOR_VOLTS_DC;
308 		sc->sc_data[i].rfact = 1;	/* what is this used for? */
309 	}
310 	strlcpy(sc->sc_data[5].desc, "VSENS1",
311 	    sizeof(sc->sc_data[5].desc));	/* CPU core (2V) */
312 	strlcpy(sc->sc_data[6].desc, "VSENS2",
313 	    sizeof(sc->sc_data[6].desc));	/* NB core? (2.5V) */
314 	strlcpy(sc->sc_data[7].desc, "Vcore",
315 	    sizeof(sc->sc_data[7].desc));	/* Vcore (3.3V) */
316 	strlcpy(sc->sc_data[8].desc, "VSENS3",
317 	    sizeof(sc->sc_data[8].desc));	/* VSENS3 (5V) */
318 	strlcpy(sc->sc_data[9].desc, "VSENS4",
319 	    sizeof(sc->sc_data[9].desc));	/* VSENS4 (12V) */
320 
321 	/* Get initial set of sensor values. */
322 	viaenv_refresh_sensor_data(sc);
323 
324 	/* Refresh sensors data every 1.5 seconds */
325 	timeout_set(&viaenv_timeout, viaenv_refresh, sc);
326 	timeout_add(&viaenv_timeout, (15 * hz) / 10);
327 
328 nohwm:
329 #ifdef __HAVE_TIMECOUNTER
330 	/* Check if power management I/O space is enabled */
331 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_GENCFG);
332 	if ((control & VIAENV_GENCFG_PMEN) == 0) {
333 		printf(": PM disabled");
334 		goto nopm;
335 	}
336 
337 	/* Map power management I/O space */
338 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_PMBASE);
339 	if (bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(iobase),
340 	    VIAENV_PMSIZE, 0, &sc->sc_pm_ioh)) {
341 		printf(": failed to map PM I/O space");
342 		goto nopm;
343 	}
344 
345 	/* Check for 32-bit PM timer */
346 	if (control & VIAENV_GENCFG_TMR32)
347 		viaenv_timecounter.tc_counter_mask = 0xffffffff;
348 
349 	/* Register new timecounter */
350 	viaenv_timecounter.tc_priv = sc;
351 	tc_init(&viaenv_timecounter);
352 
353 	printf(": %s-bit timer at %lluHz",
354 	    (viaenv_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
355 	    (unsigned long long)viaenv_timecounter.tc_frequency);
356 
357 nopm:
358 #endif	/* __HAVE_TIMECOUNTER */
359 	printf("\n");
360 }
361 
362 #if 0
363 int
364 viaenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
365 {
366 	struct viaenv_softc *sc = sme->sme_cookie;
367 
368 	simple_lock(&sc->sc_slock);
369 
370 	viaenv_refresh_sensor_data(sc);
371 	*tred = sc->sc_data[tred->sensor];
372 
373 	simple_unlock(&sc->sc_slock);
374 
375 	return (0);
376 }
377 
378 int
379 viaenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
380 {
381 
382 	/* XXX Not implemented */
383 	binfo->validflags = 0;
384 
385 	return (0);
386 }
387 #endif
388 
389 void
viaenv_refresh(void * arg)390 viaenv_refresh(void *arg)
391 {
392 	struct viaenv_softc *sc = (struct viaenv_softc *)arg;
393 
394 	viaenv_refresh_sensor_data(sc);
395 	timeout_add(&viaenv_timeout, (15 * hz) / 10);
396 }
397 
398 #ifdef __HAVE_TIMECOUNTER
399 u_int
viaenv_get_timecount(struct timecounter * tc)400 viaenv_get_timecount(struct timecounter *tc)
401 {
402 	struct viaenv_softc *sc = tc->tc_priv;
403 	u_int u1, u2, u3;
404 
405 	u2 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAENV_PM_TMR);
406 	u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAENV_PM_TMR);
407 	do {
408 		u1 = u2;
409 		u2 = u3;
410 		u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
411 		    VIAENV_PM_TMR);
412 	} while (u1 > u2 || u2 > u3);
413 
414 	return (u2);
415 }
416 #endif	/* __HAVE_TIMECOUNTER */
417