xref: /netbsd-src/sys/dev/pci/viaenv.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: viaenv.c,v 1.24 2007/10/19 12:00:56 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.24 2007/10/19 12:00:56 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 	struct device sc_dev;
68 	bus_space_tag_t sc_iot;
69 	bus_space_handle_t sc_ioh;
70 	bus_space_handle_t sc_pm_ioh;
71 
72 	int     sc_fan_div[2];	/* fan RPM divisor */
73 
74 	envsys_data_t sc_data[VIANUMSENSORS];
75 
76 	struct timeval sc_lastread;
77 
78 	struct sysmon_envsys sc_sysmon;
79 };
80 
81 /* autoconf(9) glue */
82 static int viaenv_match(struct device *, struct cfdata *, void *);
83 static void viaenv_attach(struct device *, struct device *, void *);
84 
85 CFATTACH_DECL(viaenv, sizeof(struct viaenv_softc),
86     viaenv_match, viaenv_attach, NULL, NULL);
87 
88 /* envsys(4) glue */
89 static int viaenv_gtredata(struct sysmon_envsys *, envsys_data_t *);
90 
91 static int val_to_uK(unsigned int);
92 static int val_to_rpm(unsigned int, int);
93 static long val_to_uV(unsigned int, int);
94 
95 static int
96 viaenv_match(struct device *parent, struct cfdata *match, void *aux)
97 {
98 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
99 
100 	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_VIATECH)
101 		return 0;
102 
103 	switch (PCI_PRODUCT(pa->pa_id)) {
104 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
105 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
106 		return 1;
107 	default:
108 		return 0;
109 	}
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 #define VIAENV_GENCFG	0x40	/* general configuration */
207 #define VIAENV_GENCFG_TMR32	(1 << 11)	/* 32-bit PM timer */
208 #define VIAENV_GENCFG_PMEN	(1 << 15)	/* enable PM I/O space */
209 #define VIAENV_PMBASE	0x48	/* power management I/O space base */
210 #define VIAENV_PMSIZE	128	/* HWM and power management I/O space size */
211 #define VIAENV_PM_TMR	0x08	/* PM timer */
212 #define VIAENV_HWMON_CONF	0x70	/* HWMon I/O base */
213 #define VIAENV_HWMON_CTL	0x74	/* HWMon control register */
214 
215 static void
216 viaenv_refresh_sensor_data(struct viaenv_softc *sc, envsys_data_t *edata)
217 {
218 	static const struct timeval onepointfive =  { 1, 500000 };
219 	static int old_sensor = -1;
220 	struct timeval t, utv;
221 	uint8_t v, v2;
222 	int i;
223 
224 	/* Read new values at most once every 1.5 seconds. */
225 	timeradd(&sc->sc_lastread, &onepointfive, &t);
226 	getmicrouptime(&utv);
227 	i = timercmp(&utv, &t, >);
228 	if (i)
229 		sc->sc_lastread = utv;
230 
231 	if (i == 0 && old_sensor == edata->sensor)
232 		return;
233 
234 	old_sensor = edata->sensor;
235 
236 	/* temperature */
237 	if (edata->sensor == 0) {
238 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
239 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
240 		DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
241 		edata->value_cur = val_to_uK((v2 << 2) | (v >> 6));
242 		edata->state = ENVSYS_SVALID;
243 	} else if (edata->sensor == 1) {
244 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
245 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
246 		DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
247 		edata->value_cur = val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
248 		edata->state = ENVSYS_SVALID;
249 	} else if (edata->sensor == 2) {
250 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
251 		v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
252 		DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
253 		edata->value_cur = val_to_uK((v2 << 2) | (v >> 6));
254 		edata->state = ENVSYS_SVALID;
255 	} else if (edata->sensor > 2 && edata->sensor < 5) {
256 		/* fans */
257 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
258 
259 		sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
260 		sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
261 
262 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
263 		    VIAENV_FAN1 + edata->sensor - 3);
264 		DPRINTF(("FAN%d = %d / %d\n", edata->sensor - 3, v,
265 		    sc->sc_fan_div[edata->sensor - 3]));
266 		edata->value_cur = val_to_rpm(v,
267 		    sc->sc_fan_div[edata->sensor - 3]);
268 		edata->state = ENVSYS_SVALID;
269 	} else {
270 		v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
271 		    VIAENV_VSENS1 + edata->sensor - 5);
272 		DPRINTF(("V%d = %d\n", edata->sensor - 5, v));
273 		edata->value_cur = val_to_uV(v, edata->sensor - 5);
274 		edata->state = ENVSYS_SVALID;
275 	}
276 }
277 
278 static void
279 viaenv_attach(struct device *parent, struct device *self, void *aux)
280 {
281 	struct viaenv_softc *sc = (struct viaenv_softc *)self;
282 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
283 	pcireg_t iobase, control;
284 	int i;
285 
286 	aprint_naive("\n");
287 	aprint_normal(": VIA Technologies ");
288 	switch (PCI_PRODUCT(pa->pa_id)) {
289 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
290 		aprint_normal("VT82C686A Hardware Monitor\n");
291 		break;
292 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
293 		aprint_normal("VT8231 Hardware Monitor\n");
294 		break;
295 	default:
296 		aprint_normal("Unknown Hardware Monitor\n");
297 		break;
298 	}
299 
300 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_HWMON_CONF);
301 	DPRINTF(("%s: iobase 0x%x\n", sc->sc_dev.dv_xname, iobase));
302 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_HWMON_CTL);
303 
304 	/* Check if the Hardware Monitor enable bit is set */
305 	if ((control & 1) == 0) {
306 		aprint_normal("%s : Hardware Monitor disabled\n",
307 		    sc->sc_dev.dv_xname);
308 		goto nohwm;
309 	}
310 
311 	/* Map Hardware Monitor I/O space */
312 	sc->sc_iot = pa->pa_iot;
313 	if (bus_space_map(sc->sc_iot, iobase & 0xff80,
314 	    VIAENV_PMSIZE, 0, &sc->sc_ioh)) {
315 		aprint_error("%s: failed to map I/O space\n",
316 		    sc->sc_dev.dv_xname);
317 		goto nohwm;
318 	}
319 
320 	/* Initialize sensors */
321 	for (i = 0; i < VIANUMSENSORS; i++) {
322 		sc->sc_data[i].sensor = i;
323 		sc->sc_data[i].state = ENVSYS_SVALID;
324 	}
325 
326 	for (i = 0; i < 3; i++)
327 		sc->sc_data[i].units = ENVSYS_STEMP;
328 
329 #define COPYDESCR(x, y) 				\
330 	do {						\
331 		strlcpy((x), (y), sizeof(x));		\
332 	} while (0)
333 
334 	COPYDESCR(sc->sc_data[0].desc, "TSENS1");
335 	COPYDESCR(sc->sc_data[1].desc, "TSENS2");
336 	COPYDESCR(sc->sc_data[2].desc, "TSENS3");
337 
338 	for (i = 3; i < 5; i++)
339 		sc->sc_data[i].units = ENVSYS_SFANRPM;
340 
341 	COPYDESCR(sc->sc_data[3].desc, "FAN1");
342 	COPYDESCR(sc->sc_data[4].desc, "FAN2");
343 
344 	for (i = 5; i < 10; i++)
345 		sc->sc_data[i].units = ENVSYS_SVOLTS_DC;
346 
347 	COPYDESCR(sc->sc_data[5].desc, "VSENS1");	/* CPU core (2V) */
348 	COPYDESCR(sc->sc_data[6].desc, "VSENS2");	/* NB core? (2.5V) */
349 	COPYDESCR(sc->sc_data[7].desc, "Vcore");	/* Vcore (3.3V) */
350 	COPYDESCR(sc->sc_data[8].desc, "VSENS3");	/* VSENS3 (5V) */
351 	COPYDESCR(sc->sc_data[9].desc, "VSENS4");	/* VSENS4 (12V) */
352 
353 #undef COPYDESCR
354 
355 	/*
356 	 * Hook into the System Monitor.
357 	 */
358 	sc->sc_sysmon.sme_name = sc->sc_dev.dv_xname;
359 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
360 	sc->sc_sysmon.sme_cookie = sc;
361 	sc->sc_sysmon.sme_gtredata = viaenv_gtredata;
362 	sc->sc_sysmon.sme_nsensors = VIANUMSENSORS;
363 
364 	if (sysmon_envsys_register(&sc->sc_sysmon))
365 		printf("%s: unable to register with sysmon\n",
366 		    sc->sc_dev.dv_xname);
367 
368 nohwm:
369 	/* Check if power management I/O space is enabled */
370 	control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_GENCFG);
371 	if ((control & VIAENV_GENCFG_PMEN) == 0) {
372                 aprint_normal("%s: Power Managament controller disabled\n",
373 		    sc->sc_dev.dv_xname);
374                 goto nopm;
375         }
376 
377         /* Map power management I/O space */
378         iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_PMBASE);
379         if (bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(iobase),
380             VIAENV_PMSIZE, 0, &sc->sc_pm_ioh)) {
381                 aprint_error("%s: failed to map PM I/O space\n",
382 		    sc->sc_dev.dv_xname);
383                 goto nopm;
384         }
385 
386 	/* Attach our PM timer with the generic acpipmtimer function */
387 	acpipmtimer_attach(&sc->sc_dev, 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 int
396 viaenv_gtredata(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 	return 0;
402 }
403