xref: /netbsd-src/sys/dev/pci/viaenv.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: viaenv.c,v 1.29 2008/04/30 14:07:14 ad 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 /*
36  * Driver for the hardware monitoring and power management timer
37  * in the VIA VT82C686A and VT8231 South Bridges.
38  */
39 
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: viaenv.c,v 1.29 2008/04/30 14:07:14 ad Exp $");
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/device.h>
47 
48 #include <sys/bus.h>
49 #include <dev/ic/acpipmtimer.h>
50 
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcidevs.h>
54 
55 #include <dev/sysmon/sysmonvar.h>
56 
57 #ifdef VIAENV_DEBUG
58 unsigned int viaenv_debug = 0;
59 #define DPRINTF(X) do { if (viaenv_debug) printf X ; } while(0)
60 #else
61 #define DPRINTF(X)
62 #endif
63 
64 #define VIANUMSENSORS 10	/* three temp, two fan, five voltage */
65 
66 struct viaenv_softc {
67 	bus_space_tag_t sc_iot;
68 	bus_space_handle_t sc_ioh;
69 	bus_space_handle_t sc_pm_ioh;
70 
71 	int     sc_fan_div[2];	/* fan RPM divisor */
72 
73 	struct sysmon_envsys *sc_sme;
74 	envsys_data_t sc_sensor[VIANUMSENSORS];
75 
76 	struct timeval sc_lastread;
77 };
78 
79 /* autoconf(9) glue */
80 static int 	viaenv_match(device_t, cfdata_t, void *);
81 static void 	viaenv_attach(device_t, device_t, void *);
82 
83 CFATTACH_DECL_NEW(viaenv, sizeof(struct viaenv_softc),
84     viaenv_match, viaenv_attach, NULL, NULL);
85 
86 /* envsys(4) glue */
87 static void viaenv_refresh(struct sysmon_envsys *, envsys_data_t *);
88 
89 static int val_to_uK(unsigned int);
90 static int val_to_rpm(unsigned int, int);
91 static long val_to_uV(unsigned int, int);
92 
93 static int
94 viaenv_match(device_t parent, cfdata_t match, void *aux)
95 {
96 	struct pci_attach_args *pa = aux;
97 
98 	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_VIATECH)
99 		return 0;
100 
101 	switch (PCI_PRODUCT(pa->pa_id)) {
102 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
103 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
104 		return 1;
105 	default:
106 		return 0;
107 	}
108 }
109 
110 /*
111  * XXX there doesn't seem to exist much hard documentation on how to
112  * convert the raw values to usable units, this code is more or less
113  * stolen from the Linux driver, but changed to suit our conditions
114  */
115 
116 /*
117  * lookup-table to translate raw values to uK, this is the same table
118  * used by the Linux driver (modulo units); there is a fifth degree
119  * polynomial that supposedly been used to generate this table, but I
120  * haven't been able to figure out how -- it doesn't give the same values
121  */
122 
123 static const long val_to_temp[] = {
124 	20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
125 	22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
126 	23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
127 	24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
128 	25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
129 	26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
130 	26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
131 	27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
132 	27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
133 	28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
134 	28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
135 	28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
136 	29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
137 	29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
138 	30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
139 	30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
140 	31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
141 	31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
142 	32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
143 	32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
144 	33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
145 	34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
146 	35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
147 	36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
148 	38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
149 	40575, 40835, 41095, 41375, 41655, 41935,
150 };
151 
152 /* use above table to convert values to temperatures in micro-Kelvins */
153 static int
154 val_to_uK(unsigned int val)
155 {
156 	int     i = val / 4;
157 	int     j = val % 4;
158 
159 	assert(i >= 0 && i <= 255);
160 
161 	if (j == 0 || i == 255)
162 		return val_to_temp[i] * 10000;
163 
164 	/* is linear interpolation ok? */
165 	return (val_to_temp[i] * (4 - j) +
166 	    val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
167 }
168 
169 static int
170 val_to_rpm(unsigned int val, int div)
171 {
172 
173 	if (val == 0)
174 		return 0;
175 
176 	return 1350000 / val / div;
177 }
178 
179 static long
180 val_to_uV(unsigned int val, int index)
181 {
182 	static const long mult[] =
183 	    {1250000, 1250000, 1670000, 2600000, 6300000};
184 
185 	assert(index >= 0 && index <= 4);
186 
187 	return (25LL * val + 133) * mult[index] / 2628;
188 }
189 
190 #define VIAENV_TSENS3	0x1f
191 #define VIAENV_TSENS1	0x20
192 #define VIAENV_TSENS2	0x21
193 #define VIAENV_VSENS1	0x22
194 #define VIAENV_VSENS2	0x23
195 #define VIAENV_VCORE	0x24
196 #define VIAENV_VSENS3	0x25
197 #define VIAENV_VSENS4	0x26
198 #define VIAENV_FAN1	0x29
199 #define VIAENV_FAN2	0x2a
200 #define VIAENV_FANCONF	0x47	/* fan configuration */
201 #define VIAENV_TLOW	0x49	/* temperature low order value */
202 #define VIAENV_TIRQ	0x4b	/* temperature interrupt configuration */
203 
204 #define VIAENV_GENCFG	0x40	/* general configuration */
205 #define VIAENV_GENCFG_TMR32	(1 << 11)	/* 32-bit PM timer */
206 #define VIAENV_GENCFG_PMEN	(1 << 15)	/* enable PM I/O space */
207 #define VIAENV_PMBASE	0x48	/* power management I/O space base */
208 #define VIAENV_PMSIZE	128	/* HWM and power management I/O space size */
209 #define VIAENV_PM_TMR	0x08	/* PM timer */
210 #define VIAENV_HWMON_CONF	0x70	/* HWMon I/O base */
211 #define VIAENV_HWMON_CTL	0x74	/* HWMon control register */
212 
213 static void
214 viaenv_refresh_sensor_data(struct viaenv_softc *sc, envsys_data_t *edata)
215 {
216 	static const struct timeval onepointfive =  { 1, 500000 };
217 	static int old_sensor = -1;
218 	struct timeval t, utv;
219 	uint8_t v, v2;
220 	int i;
221 
222 	/* Read new values at most once every 1.5 seconds. */
223 	timeradd(&sc->sc_lastread, &onepointfive, &t);
224 	getmicrouptime(&utv);
225 	i = timercmp(&utv, &t, >);
226 	if (i)
227 		sc->sc_lastread = utv;
228 
229 	if (i == 0 && old_sensor == edata->sensor)
230 		return;
231 
232 	old_sensor = edata->sensor;
233 
234 	/* temperature */
235 	if (edata->sensor == 0) {
236 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
237 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
238 		DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
239 		edata->value_cur = val_to_uK((v2 << 2) | (v >> 6));
240 		edata->state = ENVSYS_SVALID;
241 	} else if (edata->sensor == 1) {
242 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
243 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
244 		DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
245 		edata->value_cur = val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
246 		edata->state = ENVSYS_SVALID;
247 	} else if (edata->sensor == 2) {
248 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
249 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
250 		DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
251 		edata->value_cur = val_to_uK((v2 << 2) | (v >> 6));
252 		edata->state = ENVSYS_SVALID;
253 	} else if (edata->sensor > 2 && edata->sensor < 5) {
254 		/* fans */
255 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
256 
257 		sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
258 		sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
259 
260 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
261 		    VIAENV_FAN1 + edata->sensor - 3);
262 		DPRINTF(("FAN%d = %d / %d\n", edata->sensor - 3, v,
263 		    sc->sc_fan_div[edata->sensor - 3]));
264 		edata->value_cur = val_to_rpm(v,
265 		    sc->sc_fan_div[edata->sensor - 3]);
266 		edata->state = ENVSYS_SVALID;
267 	} else {
268 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
269 		    VIAENV_VSENS1 + edata->sensor - 5);
270 		DPRINTF(("V%d = %d\n", edata->sensor - 5, v));
271 		edata->value_cur = val_to_uV(v, edata->sensor - 5);
272 		edata->state = ENVSYS_SVALID;
273 	}
274 }
275 
276 static void
277 viaenv_attach(device_t parent, device_t self, void *aux)
278 {
279 	struct viaenv_softc *sc = device_private(self);
280 	struct pci_attach_args *pa = aux;
281 	pcireg_t iobase, control;
282 	int i;
283 
284 	aprint_naive("\n");
285 	aprint_normal(": VIA Technologies ");
286 	switch (PCI_PRODUCT(pa->pa_id)) {
287 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
288 		aprint_normal("VT82C686A Hardware Monitor\n");
289 		break;
290 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
291 		aprint_normal("VT8231 Hardware Monitor\n");
292 		break;
293 	default:
294 		aprint_normal("Unknown Hardware Monitor\n");
295 		break;
296 	}
297 
298 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_HWMON_CONF);
299 	DPRINTF(("%s: iobase 0x%x\n", device_xname(self), iobase));
300 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_HWMON_CTL);
301 
302 	/* Check if the Hardware Monitor enable bit is set */
303 	if ((control & 1) == 0) {
304 		aprint_normal_dev(self, "Hardware Monitor disabled\n");
305 		goto nohwm;
306 	}
307 
308 	/* Map Hardware Monitor I/O space */
309 	sc->sc_iot = pa->pa_iot;
310 	if (bus_space_map(sc->sc_iot, iobase & 0xff80,
311 	    VIAENV_PMSIZE, 0, &sc->sc_ioh)) {
312 		aprint_error_dev(self, "failed to map I/O space\n");
313 		goto nohwm;
314 	}
315 
316 	for (i = 0; i < 3; i++)
317 		sc->sc_sensor[i].units = ENVSYS_STEMP;
318 
319 #define COPYDESCR(x, y) 				\
320 	do {						\
321 		strlcpy((x), (y), sizeof(x));		\
322 	} while (0)
323 
324 	COPYDESCR(sc->sc_sensor[0].desc, "TSENS1");
325 	COPYDESCR(sc->sc_sensor[1].desc, "TSENS2");
326 	COPYDESCR(sc->sc_sensor[2].desc, "TSENS3");
327 
328 	for (i = 3; i < 5; i++)
329 		sc->sc_sensor[i].units = ENVSYS_SFANRPM;
330 
331 	COPYDESCR(sc->sc_sensor[3].desc, "FAN1");
332 	COPYDESCR(sc->sc_sensor[4].desc, "FAN2");
333 
334 	for (i = 5; i < 10; i++)
335 		sc->sc_sensor[i].units = ENVSYS_SVOLTS_DC;
336 
337 	COPYDESCR(sc->sc_sensor[5].desc, "VSENS1");	/* CPU core (2V) */
338 	COPYDESCR(sc->sc_sensor[6].desc, "VSENS2");	/* NB core? (2.5V) */
339 	COPYDESCR(sc->sc_sensor[7].desc, "Vcore");	/* Vcore (3.3V) */
340 	COPYDESCR(sc->sc_sensor[8].desc, "VSENS3");	/* VSENS3 (5V) */
341 	COPYDESCR(sc->sc_sensor[9].desc, "VSENS4");	/* VSENS4 (12V) */
342 
343 #undef COPYDESCR
344 
345 	sc->sc_sme = sysmon_envsys_create();
346 
347 	/* Initialize sensors */
348 	for (i = 0; i < VIANUMSENSORS; i++) {
349 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
350 						&sc->sc_sensor[i])) {
351 			sysmon_envsys_destroy(sc->sc_sme);
352 			return;
353 		}
354 	}
355 
356 	/*
357 	 * Hook into the System Monitor.
358 	 */
359 	sc->sc_sme->sme_name = device_xname(self);
360 	sc->sc_sme->sme_cookie = sc;
361 	sc->sc_sme->sme_refresh = viaenv_refresh;
362 
363 	if (sysmon_envsys_register(sc->sc_sme)) {
364 		aprint_error_dev(self, "unable to register with sysmon\n");
365 		sysmon_envsys_destroy(sc->sc_sme);
366 		return;
367 	}
368 
369 nohwm:
370 	/* Check if power management I/O space is enabled */
371 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_GENCFG);
372 	if ((control & VIAENV_GENCFG_PMEN) == 0) {
373                 aprint_normal_dev(self,
374 		    "Power Managament controller disabled\n");
375                 goto nopm;
376         }
377 
378         /* Map power management I/O space */
379         iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_PMBASE);
380         if (bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(iobase),
381             VIAENV_PMSIZE, 0, &sc->sc_pm_ioh)) {
382                 aprint_error_dev(self, "failed to map PM I/O space\n");
383                 goto nopm;
384         }
385 
386 	/* Attach our PM timer with the generic acpipmtimer function */
387 	acpipmtimer_attach(self, sc->sc_iot, sc->sc_pm_ioh,
388 	    VIAENV_PM_TMR,
389 	    ((control & VIAENV_GENCFG_TMR32) ? ACPIPMT_32BIT : 0));
390 
391 nopm:
392 	return;
393 }
394 
395 static void
396 viaenv_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
397 {
398 	struct viaenv_softc *sc = sme->sme_cookie;
399 
400 	viaenv_refresh_sensor_data(sc, edata);
401 }
402