Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dev / pci / viaenv.c
blob439352a4472e0816f7bfedfeebd23b13970681f2
1 /* $NetBSD: viaenv.c,v 1.28 2008/04/10 19:13:38 cegger Exp $ */
3 /*
4 * Copyright (c) 2000 Johan Danielsson
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 * Driver for the hardware monitoring and power management timer
37 * in the VIA VT82C686A and VT8231 South Bridges.
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: viaenv.c,v 1.28 2008/04/10 19:13:38 cegger Exp $");
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/device.h>
48 #include <sys/bus.h>
49 #include <dev/ic/acpipmtimer.h>
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcidevs.h>
55 #include <dev/sysmon/sysmonvar.h>
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
64 #define VIANUMSENSORS 10 /* three temp, two fan, five voltage */
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;
71 int sc_fan_div[2]; /* fan RPM divisor */
73 struct sysmon_envsys *sc_sme;
74 envsys_data_t sc_sensor[VIANUMSENSORS];
76 struct timeval sc_lastread;
79 /* autoconf(9) glue */
80 static int viaenv_match(device_t, cfdata_t, void *);
81 static void viaenv_attach(device_t, device_t, void *);
83 CFATTACH_DECL_NEW(viaenv, sizeof(struct viaenv_softc),
84 viaenv_match, viaenv_attach, NULL, NULL);
86 /* envsys(4) glue */
87 static void viaenv_refresh(struct sysmon_envsys *, envsys_data_t *);
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);
93 static int
94 viaenv_match(device_t parent, cfdata_t match, void *aux)
96 struct pci_attach_args *pa = aux;
98 if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_VIATECH)
99 return 0;
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;
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
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
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,
152 /* use above table to convert values to temperatures in micro-Kelvins */
153 static int
154 val_to_uK(unsigned int val)
156 int i = val / 4;
157 int j = val % 4;
159 assert(i >= 0 && i <= 255);
161 if (j == 0 || i == 255)
162 return val_to_temp[i] * 10000;
164 /* is linear interpolation ok? */
165 return (val_to_temp[i] * (4 - j) +
166 val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
169 static int
170 val_to_rpm(unsigned int val, int div)
173 if (val == 0)
174 return 0;
176 return 1350000 / val / div;
179 static long
180 val_to_uV(unsigned int val, int index)
182 static const long mult[] =
183 {1250000, 1250000, 1670000, 2600000, 6300000};
185 assert(index >= 0 && index <= 4);
187 return (25LL * val + 133) * mult[index] / 2628;
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 */
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 */
213 static void
214 viaenv_refresh_sensor_data(struct viaenv_softc *sc, envsys_data_t *edata)
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;
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;
229 if (i == 0 && old_sensor == edata->sensor)
230 return;
232 old_sensor = edata->sensor;
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);
257 sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
258 sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
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;
276 static void
277 viaenv_attach(device_t parent, device_t self, void *aux)
279 struct viaenv_softc *sc = device_private(self);
280 struct pci_attach_args *pa = aux;
281 pcireg_t iobase, control;
282 int i;
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;
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);
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;
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;
316 for (i = 0; i < 3; i++)
317 sc->sc_sensor[i].units = ENVSYS_STEMP;
319 #define COPYDESCR(x, y) \
320 do { \
321 strlcpy((x), (y), sizeof(x)); \
322 } while (0)
324 COPYDESCR(sc->sc_sensor[0].desc, "TSENS1");
325 COPYDESCR(sc->sc_sensor[1].desc, "TSENS2");
326 COPYDESCR(sc->sc_sensor[2].desc, "TSENS3");
328 for (i = 3; i < 5; i++)
329 sc->sc_sensor[i].units = ENVSYS_SFANRPM;
331 COPYDESCR(sc->sc_sensor[3].desc, "FAN1");
332 COPYDESCR(sc->sc_sensor[4].desc, "FAN2");
334 for (i = 5; i < 10; i++)
335 sc->sc_sensor[i].units = ENVSYS_SVOLTS_DC;
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) */
343 #undef COPYDESCR
345 sc->sc_sme = sysmon_envsys_create();
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;
357 * Hook into the System Monitor.
359 sc->sc_sme->sme_name = device_xname(self);
360 sc->sc_sme->sme_cookie = sc;
361 sc->sc_sme->sme_refresh = viaenv_refresh;
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;
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;
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;
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));
391 nopm:
392 return;
395 static void
396 viaenv_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
398 struct viaenv_softc *sc = sme->sme_cookie;
400 viaenv_refresh_sensor_data(sc, edata);