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