root/dev/isa/viasio.c

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

DEFINITIONS

This source file includes following definitions.
  1. viasio_conf_enable
  2. viasio_conf_disable
  3. viasio_conf_read
  4. viasio_conf_write
  5. viasio_raw2temp
  6. viasio_probe
  7. viasio_attach
  8. viasio_hm_init
  9. viasio_hm_refresh
  10. viasio_wdg_init
  11. viasio_wdg_cb

    1 /*      $OpenBSD: viasio.c,v 1.10 2007/06/14 19:13:37 grange Exp $      */
    2 /*
    3  * Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org>
    4  *
    5  * Permission to use, copy, modify, and distribute this software for any
    6  * purpose with or without fee is hereby granted, provided that the above
    7  * copyright notice and this permission notice appear in all copies.
    8  *
    9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   16  */
   17 
   18 /*
   19  * VIA VT1211 LPC Super I/O driver.
   20  */
   21 
   22 #include <sys/param.h>
   23 #include <sys/systm.h>
   24 #include <sys/device.h>
   25 #include <sys/kernel.h>
   26 #include <sys/sensors.h>
   27 #include <sys/timeout.h>
   28 
   29 #include <machine/bus.h>
   30 
   31 #include <dev/isa/isareg.h>
   32 #include <dev/isa/isavar.h>
   33 
   34 #include <dev/isa/viasioreg.h>
   35 
   36 #ifdef VIASIO_DEBUG
   37 #define DPRINTF(x) printf x
   38 #else
   39 #define DPRINTF(x)
   40 #endif
   41 
   42 /* autoconf flags */
   43 #define VIASIO_CFFLAGS_HM_ENABLE        0x0001  /* enable HM if disabled */
   44 #define VIASIO_CFFLAGS_WDG_ENABLE       0x0002  /* enable WDG if disabled */
   45 
   46 struct viasio_softc {
   47         struct device           sc_dev;
   48 
   49         bus_space_tag_t         sc_iot;
   50         bus_space_handle_t      sc_ioh;
   51 
   52         /* Hardware monitor */
   53         bus_space_handle_t      sc_hm_ioh;
   54         int                     sc_hm_clock;
   55         struct ksensor          sc_hm_sensors[VT1211_HM_NSENSORS];
   56         struct ksensordev       sc_sensordev;
   57         struct timeout          sc_hm_timo;
   58 
   59         /* Watchdog timer */
   60         bus_space_handle_t      sc_wdg_ioh;
   61 };
   62 
   63 int     viasio_probe(struct device *, void *, void *);
   64 void    viasio_attach(struct device *, struct device *, void *);
   65 
   66 void    viasio_hm_init(struct viasio_softc *);
   67 void    viasio_hm_refresh(void *);
   68 
   69 void    viasio_wdg_init(struct viasio_softc *);
   70 int     viasio_wdg_cb(void *, int);
   71 
   72 struct cfattach viasio_ca = {
   73         sizeof(struct viasio_softc),
   74         viasio_probe,
   75         viasio_attach
   76 };
   77 
   78 struct cfdriver viasio_cd = {
   79         NULL, "viasio", DV_DULL
   80 };
   81 
   82 static __inline void
   83 viasio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
   84 {
   85         bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC);
   86         bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC);
   87 }
   88 
   89 static __inline void
   90 viasio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
   91 {
   92         bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_DS_MAGIC);
   93 }
   94 
   95 static __inline u_int8_t
   96 viasio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
   97 {
   98         bus_space_write_1(iot, ioh, VT1211_INDEX, index);
   99         return (bus_space_read_1(iot, ioh, VT1211_DATA));
  100 }
  101 
  102 static __inline void
  103 viasio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
  104     u_int8_t data)
  105 {
  106         bus_space_write_1(iot, ioh, VT1211_INDEX, index);
  107         bus_space_write_1(iot, ioh, VT1211_DATA, data);
  108 }
  109 
  110 static __inline int64_t
  111 viasio_raw2temp(int raw)
  112 {
  113         int tblsize = sizeof(vt1211_hm_temptbl) / sizeof(vt1211_hm_temptbl[0]);
  114         int i;
  115         int raw1, raw2;
  116         int64_t temp = -1, temp1, temp2;
  117 
  118         if (raw < vt1211_hm_temptbl[0].raw ||
  119             raw > vt1211_hm_temptbl[tblsize - 1].raw)
  120                 return (-1);
  121 
  122         for (i = 0; i < tblsize - 1; i++) {
  123                 raw1 = vt1211_hm_temptbl[i].raw;
  124                 temp1 = vt1211_hm_temptbl[i].temp;
  125                 raw2 = vt1211_hm_temptbl[i + 1].raw;
  126                 temp2 = vt1211_hm_temptbl[i + 1].temp;
  127 
  128                 if (raw >= raw1 && raw <= raw2) {
  129                         /* linear interpolation */
  130                         temp = temp1 + ((raw - raw1) * (temp2 - temp1)) /
  131                             (raw2 - raw1);
  132                         break;
  133                 }
  134         }
  135 
  136         return (temp);
  137 }
  138 
  139 int
  140 viasio_probe(struct device *parent, void *match, void *aux)
  141 {
  142         struct isa_attach_args *ia = aux;
  143         bus_space_tag_t iot;
  144         bus_space_handle_t ioh;
  145         u_int8_t reg;
  146 
  147         /* Match by device ID */
  148         iot = ia->ia_iot;
  149         if (bus_space_map(iot, ia->ipa_io[0].base, VT1211_IOSIZE, 0, &ioh))
  150                 return (0);
  151         viasio_conf_enable(iot, ioh);
  152         reg = viasio_conf_read(iot, ioh, VT1211_ID);
  153         DPRINTF(("viasio_probe: id 0x%02x\n", reg));
  154         viasio_conf_disable(iot, ioh);
  155         bus_space_unmap(iot, ioh, VT1211_IOSIZE);
  156         if (reg == VT1211_ID_VT1211) {
  157                 ia->ipa_nio = 1;
  158                 ia->ipa_io[0].length = VT1211_IOSIZE;
  159                 ia->ipa_nmem = 0;
  160                 ia->ipa_nirq = 0;
  161                 ia->ipa_ndrq = 0;
  162                 return (1);
  163         }
  164 
  165         return (0);
  166 }
  167 
  168 void
  169 viasio_attach(struct device *parent, struct device *self, void *aux)
  170 {
  171         struct viasio_softc *sc = (void *)self;
  172         struct isa_attach_args *ia = aux;
  173         u_int8_t reg;
  174 
  175         /* Map ISA I/O space */
  176         sc->sc_iot = ia->ia_iot;
  177         if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base,
  178             VT1211_IOSIZE, 0, &sc->sc_ioh)) {
  179                 printf(": can't map I/O space\n");
  180                 return;
  181         }
  182 
  183         /* Enter configuration mode */
  184         viasio_conf_enable(sc->sc_iot, sc->sc_ioh);
  185 
  186         /* Read device revision */
  187         reg = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_REV);
  188         printf(": VT1211 rev 0x%02x", reg);
  189 
  190         /* Initialize logical devices */
  191         viasio_hm_init(sc);
  192         viasio_wdg_init(sc);
  193         printf("\n");
  194 
  195         /* Escape from configuration mode */
  196         viasio_conf_disable(sc->sc_iot, sc->sc_ioh);
  197 }
  198 
  199 void
  200 viasio_hm_init(struct viasio_softc *sc)
  201 {
  202         u_int8_t reg0, reg1;
  203         u_int16_t iobase;
  204         int i;
  205 
  206         printf(", HM");
  207 
  208         /* Select HM logical device */
  209         viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_HM);
  210 
  211         /*
  212          * Check if logical device is activated by firmware.  If not
  213          * try to activate it only if requested.
  214          */
  215         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ACT);
  216         DPRINTF((": ACT 0x%02x", reg0));
  217         if ((reg0 & VT1211_HM_ACT_EN) == 0) {
  218                 if ((sc->sc_dev.dv_cfdata->cf_flags &
  219                     VIASIO_CFFLAGS_HM_ENABLE) != 0) {
  220                         reg0 |= VT1211_HM_ACT_EN;
  221                         viasio_conf_write(sc->sc_iot, sc->sc_ioh,
  222                             VT1211_HM_ACT, reg0);
  223                         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh,
  224                             VT1211_HM_ACT);
  225                         DPRINTF((", new ACT 0x%02x", reg0));
  226                         if ((reg0 & VT1211_HM_ACT_EN) == 0) {
  227                                 printf(" failed to activate");
  228                                 return;
  229                         }
  230                 } else {
  231                         printf(" not activated");
  232                         return;
  233                 }
  234         }
  235 
  236         /* Read HM I/O space address */
  237         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_LSB);
  238         reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_MSB);
  239         iobase = (reg1 << 8) | reg0;
  240         DPRINTF((", addr 0x%04x", iobase));
  241 
  242         /* Map HM I/O space */
  243         if (bus_space_map(sc->sc_iot, iobase, VT1211_HM_IOSIZE, 0,
  244             &sc->sc_hm_ioh)) {
  245                 printf(" can't map I/O space");
  246                 return;
  247         }
  248 
  249         /*
  250          * Check if hardware monitoring is enabled by firmware.  If not
  251          * try to enable it only if requested.
  252          */
  253         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_CONF);
  254         DPRINTF((", CONF 0x%02x", reg0));
  255         if ((reg0 & VT1211_HM_CONF_START) == 0) {
  256                 if ((sc->sc_dev.dv_cfdata->cf_flags &
  257                     VIASIO_CFFLAGS_HM_ENABLE) != 0) {
  258                         reg0 |= VT1211_HM_CONF_START;
  259                         bus_space_write_1(sc->sc_iot, sc->sc_hm_ioh,
  260                             VT1211_HM_CONF, reg0);
  261                         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
  262                             VT1211_HM_CONF);
  263                         DPRINTF((", new CONF 0x%02x", reg0));
  264                         if ((reg0 & VT1211_HM_CONF_START) == 0) {
  265                                 printf(" failed to enable monitoring");
  266                                 return;
  267                         }
  268                 } else {
  269                         printf(" monitoring not enabled");
  270                         return;
  271                 }
  272         }
  273 
  274         /* Read PWM clock frequency */
  275         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_PWMCS);
  276         sc->sc_hm_clock = vt1211_hm_clock[reg0 & 0x07];
  277         DPRINTF((", PWMCS 0x%02x, %dHz", reg0, sc->sc_hm_clock));
  278 
  279         /* Temperature reading 1 */
  280         sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP;
  281 
  282         /* Universal channels (UCH) 1-5 */
  283         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_UCHCONF);
  284         DPRINTF((", UCHCONF 0x%02x", reg0));
  285         for (i = 1; i <= 5; i++) {
  286                 /* UCH can be configured either as thermal or voltage input */
  287                 if (VT1211_HM_UCHCONF_ISTEMP(reg0, i)) {
  288                         sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type =
  289                             SENSOR_TEMP;
  290                 } else {
  291                         sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type =
  292                             SENSOR_VOLTS_DC;
  293                 }
  294                 snprintf(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc,
  295                     sizeof(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc),
  296                     "UCH%d", i);
  297         }
  298 
  299         /* Internal +3.3V */
  300         sc->sc_hm_sensors[VT1211_HMS_33V].type = SENSOR_VOLTS_DC;
  301         strlcpy(sc->sc_hm_sensors[VT1211_HMS_33V].desc, "+3.3V",
  302             sizeof(sc->sc_hm_sensors[VT1211_HMS_33V].desc));
  303 
  304         /* FAN reading 1, 2 */
  305         sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM;
  306         sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM;
  307 
  308         /* Start sensors */
  309         strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  310             sizeof(sc->sc_sensordev.xname));
  311         for (i = 0; i < VT1211_HM_NSENSORS; i++)
  312                 sensor_attach(&sc->sc_sensordev, &sc->sc_hm_sensors[i]);
  313         sensordev_install(&sc->sc_sensordev);
  314         timeout_set(&sc->sc_hm_timo, viasio_hm_refresh, sc);
  315         timeout_add(&sc->sc_hm_timo, hz);
  316 }
  317 
  318 void
  319 viasio_hm_refresh(void *arg)
  320 {
  321         struct viasio_softc *sc = arg;
  322         u_int8_t reg0, reg1;
  323         int64_t val, rfact;
  324         int i;
  325 
  326         /* TEMP1 is a 10-bit thermal input */
  327         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TEMP1);
  328         reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TCONF1);
  329         reg1 = VT1211_HM_TCONF1_TEMP1(reg1);
  330         val = (reg0 << 2) | reg1;
  331 
  332         /* Convert to uK */
  333         /* XXX: conversion function is guessed */
  334         val = viasio_raw2temp(val);
  335         if (val == -1) {
  336                 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags |= SENSOR_FINVALID;
  337         } else {
  338                 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags &= ~SENSOR_FINVALID;
  339                 sc->sc_hm_sensors[VT1211_HMS_TEMP1].value = val;
  340         }
  341 
  342         /* Universal channels 1-5 */
  343         for (i = 1; i <= 5; i++) {
  344                 if (sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type ==
  345                     SENSOR_TEMP) {
  346                         /* UCH is a 10-bit thermal input */
  347                         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
  348                             VT1211_HM_UCH1 + i - 1);
  349                         if (i == 1) {
  350                                 reg1 = bus_space_read_1(sc->sc_iot,
  351                                     sc->sc_hm_ioh, VT1211_HM_VID4);
  352                                 reg1 = VT1211_HM_VID4_UCH1(reg1);
  353                         } else {
  354                                 reg1 = bus_space_read_1(sc->sc_iot,
  355                                     sc->sc_hm_ioh, VT1211_HM_ETR);
  356                                 reg1 = VT1211_HM_ETR_UCH(reg1, i);
  357                         }
  358                         val = (reg0 << 2) | reg1;
  359 
  360                         /* Convert to uK */
  361                         /* XXX: conversion function is guessed */
  362                         val = viasio_raw2temp(val);
  363                         if (val == -1) {
  364                                 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
  365                                     i - 1].flags |= SENSOR_FINVALID;
  366                         } else {
  367                                 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
  368                                     i - 1].flags &= ~SENSOR_FINVALID;
  369                                 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
  370                                     i - 1].value = val;
  371                         }
  372                 } else {
  373                         /* UCH is a voltage input */
  374                         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
  375                             VT1211_HM_UCH1 + i - 1);
  376                         val = reg0;
  377 
  378                         /* Convert to uV */
  379                         /* XXX: conversion function is guessed */
  380                         rfact = vt1211_hm_vrfact[i - 1];
  381                         sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].value =
  382                             ((val * 100000000000ULL) / (rfact * 958));
  383                 }
  384         }
  385 
  386         /* Read internal +3.3V */
  387         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_33V);
  388         val = reg0;
  389 
  390         /* Convert to uV */
  391         /* XXX: conversion function is guessed */
  392         rfact = vt1211_hm_vrfact[5];
  393         sc->sc_hm_sensors[VT1211_HMS_33V].value = ((val * 100000000000ULL) /
  394             (rfact * 958));
  395 
  396         /* Read FAN1 clock counter and divisor */
  397         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN1);
  398         reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL);
  399         reg1 = VT1211_HM_FSCTL_DIV1(reg1);
  400         val = reg0 << reg1;
  401 
  402         /* Convert to RPM */
  403         /* XXX: conversion function is guessed */
  404         if (val != 0) {         
  405                 sc->sc_hm_sensors[VT1211_HMS_FAN1].value =
  406                     (sc->sc_hm_clock * 60 / 2) / val;
  407                 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags &= ~SENSOR_FINVALID;
  408         } else {
  409                 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags |= SENSOR_FINVALID;
  410         }
  411 
  412         /* Read FAN2 clock counter and divisor */
  413         reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN2);
  414         reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL);
  415         reg1 = VT1211_HM_FSCTL_DIV2(reg1);
  416         val = reg0 << reg1;
  417 
  418         /* Convert to RPM */
  419         /* XXX: conversion function is guessed */
  420         if (val != 0) {         
  421                 sc->sc_hm_sensors[VT1211_HMS_FAN2].value =
  422                     (sc->sc_hm_clock * 60 / 2) / val;
  423                 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags &= ~SENSOR_FINVALID;
  424         } else {
  425                 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags |= SENSOR_FINVALID;
  426         }
  427 
  428         timeout_add(&sc->sc_hm_timo, hz);
  429 }
  430 
  431 void
  432 viasio_wdg_init(struct viasio_softc *sc)
  433 {
  434         u_int8_t reg0, reg1;
  435         u_int16_t iobase;
  436 
  437         printf(", WDG");
  438 
  439         /* Select WDG logical device */
  440         viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_WDG);
  441 
  442         /*
  443          * Check if logical device is activated by firmware.  If not
  444          * try to activate it only if requested.
  445          */
  446         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ACT);
  447         DPRINTF((": ACT 0x%02x", reg0));
  448         if ((reg0 & VT1211_WDG_ACT_EN) == 0) {
  449                 if ((sc->sc_dev.dv_cfdata->cf_flags &
  450                     VIASIO_CFFLAGS_WDG_ENABLE) != 0) {
  451                         reg0 |= VT1211_WDG_ACT_EN;
  452                         viasio_conf_write(sc->sc_iot, sc->sc_ioh,
  453                             VT1211_WDG_ACT, reg0);
  454                         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh,
  455                             VT1211_WDG_ACT);
  456                         DPRINTF((", new ACT 0x%02x", reg0));
  457                         if ((reg0 & VT1211_WDG_ACT_EN) == 0) {
  458                                 printf(" failed to activate");
  459                                 return;
  460                         }
  461                 } else {
  462                         printf(" not activated");
  463                         return;
  464                 }
  465         }
  466 
  467         /* Read WDG I/O space address */
  468         reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_LSB);
  469         reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_MSB);
  470         iobase = (reg1 << 8) | reg0;
  471         DPRINTF((", addr 0x%04x", iobase));
  472 
  473         /* Map WDG I/O space */
  474         if (bus_space_map(sc->sc_iot, iobase, VT1211_WDG_IOSIZE, 0,
  475             &sc->sc_wdg_ioh)) {
  476                 printf(" can't map I/O space");
  477                 return;
  478         }
  479 
  480         /* Register new watchdog */
  481         wdog_register(sc, viasio_wdg_cb);
  482 }
  483 
  484 int
  485 viasio_wdg_cb(void *arg, int period)
  486 {
  487         struct viasio_softc *sc = arg;
  488         int mins;
  489 
  490         mins = (period + 59) / 60;
  491         if (mins > 255)
  492                 mins = 255;
  493 
  494         bus_space_write_1(sc->sc_iot, sc->sc_wdg_ioh, VT1211_WDG_TIMEOUT, mins);
  495         DPRINTF(("viasio_wdg_cb: %d mins\n", mins));
  496 
  497         return (mins * 60);
  498 }

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