root/dev/pci/viaenv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. viaenv_match
  2. val_to_uK
  3. val_to_rpm
  4. val_to_uV
  5. viaenv_refresh_sensor_data
  6. viaenv_attach
  7. viaenv_refresh
  8. viaenv_get_timecount

    1 /*      $OpenBSD: viaenv.c,v 1.11 2007/05/14 00:37:18 jsg 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 ksensor sc_data[VIANUMSENSORS];
   73         struct ksensordev sc_sensordev;
   74 };
   75 
   76 int  viaenv_match(struct device *, void *, void *);
   77 void viaenv_attach(struct device *, struct device *, void *);
   78 
   79 int  val_to_uK(unsigned int);
   80 int  val_to_rpm(unsigned int, int);
   81 long val_to_uV(unsigned int, int);
   82 void viaenv_refresh_sensor_data(struct viaenv_softc *);
   83 void viaenv_refresh(void *);
   84 
   85 #ifdef __HAVE_TIMECOUNTER
   86 u_int viaenv_get_timecount(struct timecounter *tc);
   87 
   88 struct timecounter viaenv_timecounter = {
   89         viaenv_get_timecount,   /* get_timecount */
   90         0,                      /* no poll_pps */
   91         0xffffff,               /* counter_mask */
   92         3579545,                /* frequency */
   93         "VIAPM",                /* name */
   94         1000                    /* quality */
   95 };
   96 #endif  /* __HAVE_TIMECOUNTER */
   97 
   98 struct cfattach viaenv_ca = {
   99         sizeof(struct viaenv_softc),
  100         viaenv_match,
  101         viaenv_attach
  102 };
  103 
  104 struct cfdriver viaenv_cd = {
  105         NULL, "viaenv", DV_DULL
  106 };
  107 
  108 struct timeout viaenv_timeout;
  109 
  110 const struct pci_matchid viaenv_devices[] = {
  111         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_SMB },
  112         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8231_PWR }
  113 };
  114 
  115 int
  116 viaenv_match(struct device *parent, void *match, void *aux)
  117 {
  118         return (pci_matchbyid((struct pci_attach_args *)aux, viaenv_devices,
  119             sizeof(viaenv_devices) / sizeof(viaenv_devices[0])));
  120 }
  121 
  122 /*
  123  * XXX there doesn't seem to exist much hard documentation on how to
  124  * convert the raw values to usable units, this code is more or less
  125  * stolen from the Linux driver, but changed to suit our conditions
  126  */
  127 
  128 /*
  129  * lookup-table to translate raw values to uK, this is the same table
  130  * used by the Linux driver (modulo units); there is a fifth degree
  131  * polynomial that supposedly been used to generate this table, but I
  132  * haven't been able to figure out how -- it doesn't give the same values
  133  */
  134 
  135 static const long val_to_temp[] = {
  136         20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
  137         22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
  138         23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
  139         24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
  140         25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
  141         26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
  142         26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
  143         27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
  144         27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
  145         28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
  146         28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
  147         28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
  148         29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
  149         29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
  150         30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
  151         30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
  152         31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
  153         31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
  154         32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
  155         32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
  156         33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
  157         34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
  158         35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
  159         36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
  160         38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
  161         40575, 40835, 41095, 41375, 41655, 41935,
  162 };
  163 
  164 /* use above table to convert values to temperatures in micro-Kelvins */
  165 int
  166 val_to_uK(unsigned int val)
  167 {
  168         int     i = val / 4;
  169         int     j = val % 4;
  170 
  171         assert(i >= 0 && i <= 255);
  172 
  173         if (j == 0 || i == 255)
  174                 return val_to_temp[i] * 10000;
  175 
  176         /* is linear interpolation ok? */
  177         return (val_to_temp[i] * (4 - j) +
  178             val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
  179 }
  180 
  181 int
  182 val_to_rpm(unsigned int val, int div)
  183 {
  184 
  185         if (val == 0)
  186                 return 0;
  187 
  188         return 1350000 / val / div;
  189 }
  190 
  191 long
  192 val_to_uV(unsigned int val, int index)
  193 {
  194         static const long mult[] =
  195             {1250000, 1250000, 1670000, 2600000, 6300000};
  196 
  197         assert(index >= 0 && index <= 4);
  198 
  199         return (25LL * val + 133) * mult[index] / 2628;
  200 }
  201 
  202 #define VIAENV_TSENS3   0x1f
  203 #define VIAENV_TSENS1   0x20
  204 #define VIAENV_TSENS2   0x21
  205 #define VIAENV_VSENS1   0x22
  206 #define VIAENV_VSENS2   0x23
  207 #define VIAENV_VCORE    0x24
  208 #define VIAENV_VSENS3   0x25
  209 #define VIAENV_VSENS4   0x26
  210 #define VIAENV_FAN1     0x29
  211 #define VIAENV_FAN2     0x2a
  212 #define VIAENV_FANCONF  0x47    /* fan configuration */
  213 #define VIAENV_TLOW     0x49    /* temperature low order value */
  214 #define VIAENV_TIRQ     0x4b    /* temperature interrupt configuration */
  215 
  216 #define VIAENV_GENCFG   0x40    /* general configuration */
  217 #define VIAENV_GENCFG_TMR32     (1 << 11)       /* 32-bit PM timer */
  218 #define VIAENV_GENCFG_PMEN      (1 << 15)       /* enable PM I/O space */
  219 #define VIAENV_PMBASE   0x48    /* power management I/O space base */
  220 #define VIAENV_PMSIZE   128     /* power management I/O space size */
  221 #define VIAENV_PM_TMR   0x08    /* PM timer */
  222 
  223 void
  224 viaenv_refresh_sensor_data(struct viaenv_softc *sc)
  225 {
  226         int i;
  227         u_int8_t v, v2;
  228 
  229         /* temperature */
  230         v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ);
  231         v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1);
  232         DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6)));
  233         sc->sc_data[0].value = val_to_uK((v2 << 2) | (v >> 6));
  234 
  235         v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW);
  236         v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2);
  237         DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3)));
  238         sc->sc_data[1].value = val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
  239 
  240         v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3);
  241         DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6)));
  242         sc->sc_data[2].value = val_to_uK((v2 << 2) | (v >> 6));
  243 
  244         v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF);
  245 
  246         sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
  247         sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
  248 
  249         /* fan */
  250         for (i = 3; i <= 4; i++) {
  251                 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  252                     VIAENV_FAN1 + i - 3);
  253                 DPRINTF(("FAN%d = %d / %d\n", i - 3, v,
  254                     sc->sc_fan_div[i - 3]));
  255                 sc->sc_data[i].value = val_to_rpm(v, sc->sc_fan_div[i - 3]);
  256         }
  257 
  258         /* voltage */
  259         for (i = 5; i <= 9; i++) {
  260                 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  261                     VIAENV_VSENS1 + i - 5);
  262                 DPRINTF(("V%d = %d\n", i - 5, v));
  263                 sc->sc_data[i].value = val_to_uV(v, i - 5);
  264         }
  265 }
  266 
  267 void
  268 viaenv_attach(struct device * parent, struct device * self, void *aux)
  269 {
  270         struct viaenv_softc *sc = (struct viaenv_softc *) self;
  271         struct pci_attach_args *pa = aux;
  272         pcireg_t iobase, control;
  273         int i;
  274 
  275         iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x70);
  276         control = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x74);
  277         if ((iobase & 0xff80) == 0 || (control & 1) == 0) {
  278                 printf(": HWM disabled");
  279                 goto nohwm;
  280         }
  281         sc->sc_iot = pa->pa_iot;
  282         if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) {
  283                 printf(": failed to map HWM I/O space");
  284                 goto nohwm;
  285         }
  286 
  287         for (i = 0; i <= 2; i++) {
  288                 sc->sc_data[i].type = SENSOR_TEMP;
  289         }
  290 
  291         for (i = 3; i <= 4; i++) {
  292                 sc->sc_data[i].type = SENSOR_FANRPM;
  293         }
  294 
  295         for (i = 5; i <= 9; ++i) {
  296                 sc->sc_data[i].type = SENSOR_VOLTS_DC;
  297         }
  298         strlcpy(sc->sc_data[5].desc, "VSENS1",
  299             sizeof(sc->sc_data[5].desc));       /* CPU core (2V) */
  300         strlcpy(sc->sc_data[6].desc, "VSENS2",
  301             sizeof(sc->sc_data[6].desc));       /* NB core? (2.5V) */
  302         strlcpy(sc->sc_data[7].desc, "Vcore",
  303             sizeof(sc->sc_data[7].desc));       /* Vcore (3.3V) */
  304         strlcpy(sc->sc_data[8].desc, "VSENS3",
  305             sizeof(sc->sc_data[8].desc));       /* VSENS3 (5V) */
  306         strlcpy(sc->sc_data[9].desc, "VSENS4",
  307             sizeof(sc->sc_data[9].desc));       /* VSENS4 (12V) */
  308 
  309         /* Get initial set of sensor values. */
  310         viaenv_refresh_sensor_data(sc);
  311 
  312         /* Register sensors with sysctl */
  313         strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  314             sizeof(sc->sc_sensordev.xname));
  315         for (i = 0; i < VIANUMSENSORS; ++i)
  316                 sensor_attach(&sc->sc_sensordev, &sc->sc_data[i]);
  317         sensordev_install(&sc->sc_sensordev);
  318 
  319         /* Refresh sensors data every 1.5 seconds */
  320         timeout_set(&viaenv_timeout, viaenv_refresh, sc);
  321         timeout_add(&viaenv_timeout, (15 * hz) / 10);
  322 
  323 nohwm:
  324 #ifdef __HAVE_TIMECOUNTER
  325         /* Check if power management I/O space is enabled */
  326         control = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_GENCFG);
  327         if ((control & VIAENV_GENCFG_PMEN) == 0) {
  328                 printf(": PM disabled");
  329                 goto nopm;
  330         }
  331 
  332         /* Map power management I/O space */
  333         iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAENV_PMBASE);
  334         if (bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(iobase),
  335             VIAENV_PMSIZE, 0, &sc->sc_pm_ioh)) {
  336                 printf(": failed to map PM I/O space");
  337                 goto nopm;
  338         }
  339 
  340         /* Check for 32-bit PM timer */
  341         if (control & VIAENV_GENCFG_TMR32)
  342                 viaenv_timecounter.tc_counter_mask = 0xffffffff;
  343 
  344         /* Register new timecounter */
  345         viaenv_timecounter.tc_priv = sc;
  346         tc_init(&viaenv_timecounter);
  347 
  348         printf(": %s-bit timer at %lluHz",
  349             (viaenv_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
  350             (unsigned long long)viaenv_timecounter.tc_frequency);
  351 
  352 nopm:
  353 #endif  /* __HAVE_TIMECOUNTER */
  354         printf("\n");
  355 }
  356 
  357 void
  358 viaenv_refresh(void *arg)
  359 {
  360         struct viaenv_softc *sc = (struct viaenv_softc *)arg;
  361 
  362         viaenv_refresh_sensor_data(sc);
  363         timeout_add(&viaenv_timeout, (15 * hz) / 10);
  364 }
  365 
  366 #ifdef __HAVE_TIMECOUNTER
  367 u_int
  368 viaenv_get_timecount(struct timecounter *tc)
  369 {
  370         struct viaenv_softc *sc = tc->tc_priv;
  371         u_int u1, u2, u3;
  372 
  373         u2 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAENV_PM_TMR);
  374         u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAENV_PM_TMR);
  375         do {
  376                 u1 = u2;
  377                 u2 = u3;
  378                 u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
  379                     VIAENV_PM_TMR);
  380         } while (u1 > u2 || u2 > u3);
  381 
  382         return (u2);
  383 }
  384 #endif  /* __HAVE_TIMECOUNTER */

/* [<][>][^][v][top][bottom][index][help] */