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