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