Subject: Re: it(4) for NetBSD
To: =?iso-8859-1?q?Bartosz_Ku=BCma?= <bartosz@atom.eu.org>
From: Johan Danielsson <joda@pdc.kth.se>
List: current-users
Date: 06/23/2004 13:42:46
--=-=-=
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit

Bartosz Kužma <bartosz@atom.eu.org> writes:

> I just ported the it(4) driver [from OpenBSD].

Looks reasonable, but it should probably be called iteenv or similar,
and the manpage is missing.

Why does it use 103/20 and 83/20 when 36/7 and 29/7 would be more
accurate (the datasheet says 4 1/7 for resistor quotient for negative
twelve)? Not that it matters much.

For reference, the half-finished driver I wrote is appended.

/Johan


--=-=-=
Content-Disposition: attachment; filename=iteenv.c

/* $NetBSD$ */

/*
 * Copyright (c) 2002 Johan Danielsson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 *
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 *
 * 3. Neither the name of author nor the names of any contributors may
 *    be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* A driver for the Environment Controller found on ITE 8705/8712. */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/device.h>
#include <sys/syslog.h>

#include <machine/bus.h>
#include <machine/intr.h>

#include <dev/isa/isavar.h>

#include <dev/sysmon/sysmonvar.h>

#define DPRINTF(X) do { if(1) printf X ; } while(0)


#define ITEENV_FANDIV		0x0b
#define ITEENV_FAN0SHIFT(v)	((v) & 7)
#define ITEENV_FAN1SHIFT(v)	(((v) >> 3) & 7)
#define ITEENV_FAN2SHIFT(v)	(((v) & 0x40) ? 3 : 1)
#define ITEENV_FAN0		0x0d
#define ITEENV_FAN1		0x0e
#define ITEENV_FAN2		0x0f
#define ITEENV_VSENSE0		0x20
#define ITEENV_VSENSE1		0x21
#define ITEENV_VSENSE2		0x22
#define ITEENV_VSENSE3		0x23
#define ITEENV_VSENSE4		0x24
#define ITEENV_VSENSE5		0x25
#define ITEENV_VSENSE6		0x26
#define ITEENV_VSENSE7		0x27
#define ITEENV_VBAT		0x28
#define ITEENV_TSENS0		0x29
#define ITEENV_TSENS1		0x2a
#define ITEENV_TSENS2		0x2b
#define ITEENV_VENDORREG	0x58
#define ITEENV_VENDORID			0x90



#define ITEENVNUMSENSORS 15

struct iteenv_softc {
    struct device sc_dev;

    bus_space_tag_t sc_iot;
    bus_space_handle_t sc_ioh;

    struct envsys_tre_data sc_data[ITEENVNUMSENSORS];
    struct envsys_basic_info sc_info[ITEENVNUMSENSORS];
    unsigned int sc_negative_input[ITEENVNUMSENSORS];
    
    struct simplelock sc_slock;
    struct timeval sc_lastread;

    struct sysmon_envsys sc_sysmon;
};

const struct envsys_range iteenv_ranges[] = {
	{ 0, 2,		ENVSYS_STEMP },
	{ 3, 5,		ENVSYS_SFANRPM },
	{ 1, 0,		ENVSYS_SVOLTS_AC },	/* none */
	{ 6, 15,	ENVSYS_SVOLTS_DC },
	{ 1, 0,		ENVSYS_SOHMS },		/* none */
	{ 1, 0,		ENVSYS_SWATTS },	/* none */
	{ 1, 0,		ENVSYS_SAMPS },		/* none */
};

int iteenv_probe (struct device *, struct cfdata *, void *);
void iteenv_attach (struct device *, struct device *, void *);

static void iteenv_refresh_sensor_data(struct iteenv_softc*);
static int iteenv_gtredata(struct sysmon_envsys*, struct envsys_tre_data*);
static int iteenv_streinfo(struct sysmon_envsys*, struct envsys_basic_info*);


CFATTACH_DECL(iteenv, sizeof(struct iteenv_softc),
	      iteenv_probe, iteenv_attach, NULL, NULL);

static inline uint8_t
READREG(struct iteenv_softc *sc, uint8_t reg)
{
    bus_space_write_1(sc->sc_iot, sc->sc_ioh, 5, reg);
    return bus_space_read_1(sc->sc_iot, sc->sc_ioh, 6);
}

static inline void
WRITEREG(struct iteenv_softc *sc, uint8_t reg, uint8_t value)
{
    bus_space_write_1(sc->sc_iot, sc->sc_ioh, 5, reg);
    bus_space_write_1(sc->sc_iot, sc->sc_ioh, 6, value);
}

#define ITEENV_IOSIZE 8

int
iteenv_probe(struct device *parent,
	   struct cfdata *match,
	   void *aux)
{
    struct isa_attach_args *ia = aux;
    bus_space_tag_t iot;
    bus_space_handle_t ioh;
    u_long base;
    int ret;
    uint8_t v;

    if (ia->ia_nio < 1)
	return (0);

    if (ISA_DIRECT_CONFIG(ia))
	return (0);
    
    /* Disallow wildcarded i/o address. */
    if (ia->ia_io[0].ir_addr == ISACF_PORT_DEFAULT)
	return (0);


    iot = ia->ia_iot;
    base = ia->ia_io[0].ir_addr;
    if (bus_space_map(iot, base, ITEENV_IOSIZE, 0, &ioh))
	return 0;

    bus_space_write_1(iot, ioh, 5, ITEENV_VENDORREG);
    v = bus_space_read_1(iot, ioh, 6);
    
    bus_space_unmap(iot, ioh, ITEENV_IOSIZE);
    
    ret = (v == ITEENV_VENDORID) ? 1 : 0;
    if(ret != 0) {
	ia->ia_nio = 1;
	ia->ia_io[0].ir_size = ITEENV_IOSIZE;

	ia->ia_niomem = 0;
	ia->ia_nirq = 0;
	ia->ia_ndrq = 0;
    }
	
    return ret;
}

void
iteenv_attach(struct device *parent,
	    struct device *self,
	    void *aux)
{
    struct isa_attach_args *ia = aux;
    struct iteenv_softc *sc = (struct iteenv_softc *) self;
    u_long base;
    int i;

    sc->sc_iot = ia->ia_iot;
    base = ia->ia_io[0].ir_addr;
    if (bus_space_map(sc->sc_iot, base, ITEENV_IOSIZE, 0, &sc->sc_ioh)) {
	printf(": failed to map i/o\n");
	return;
    }

    printf(": IT8705/8712\n");

    simple_lock_init(&sc->sc_slock);

    /* Initialize sensors */
    for (i = 0; i < ITEENVNUMSENSORS; ++i) {
	sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
	sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID);
	sc->sc_info[i].validflags = ENVSYS_FVALID;
	sc->sc_data[i].warnflags = ENVSYS_WARN_OK;
    }

    for (i = 0; i <= 2; i++) {
	sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP;
	snprintf(sc->sc_info[i].desc, sizeof(sc->sc_info[i].desc),
		 "TSENS%d", i + 1);
    }

    for (i = 3; i <= 5; i++) {
	sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM;
	snprintf(sc->sc_info[i].desc, sizeof(sc->sc_info[i].desc), 
		 "FAN%d", i - 2);
    }

    for (i = 6; i <= 14; ++i) {
	sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SVOLTS_DC;
	switch(i) {
	case 9:
	case 13:
	    sc->sc_info[i].rfact = 6800; /* +5V */
	    break;
	case 10:
	    sc->sc_info[i].rfact = 30000; /* +12V */
	    break;
	case 11:
	    sc->sc_info[i].rfact = 41428; /* -12V */
	    sc->sc_negative_input[i] = 1;
	    break;
	default:
	    sc->sc_info[i].rfact = 0; /* <4V */
	    break;
	}
	snprintf(sc->sc_info[i].desc, sizeof(sc->sc_info[i].desc), 
		 "VSENS%d", i - 5);
    }
    /* change name of this */
    strcpy(sc->sc_info[14].desc, "VBAT");

    /* Get initial set of sensor values. */
    iteenv_refresh_sensor_data(sc);

    /*
     * Hook into the System Monitor.
     */
    sc->sc_sysmon.sme_ranges = iteenv_ranges;
    sc->sc_sysmon.sme_sensor_info = sc->sc_info;
    sc->sc_sysmon.sme_sensor_data = sc->sc_data;
    sc->sc_sysmon.sme_cookie = sc;

    sc->sc_sysmon.sme_gtredata = iteenv_gtredata;
    sc->sc_sysmon.sme_streinfo = iteenv_streinfo;

    sc->sc_sysmon.sme_nsensors = ITEENVNUMSENSORS;
    sc->sc_sysmon.sme_envsys_version = 1000;

    if (sysmon_envsys_register(&sc->sc_sysmon))
	printf("%s: unable to register with sysmon\n",
	       sc->sc_dev.dv_xname);
}

/* convert Celsius to uKelvin */
#define C2MK(T) (273150000 + (T) * 1000000)
/* convert counter and shift to fan rpms */
#define FSPEED(R, S) (((R) == 255) ? 0 : ((1350000 / (R)) >> (S)))

static void
iteenv_refresh_sensor_data(struct iteenv_softc *sc)
{
    static const struct timeval onepointfive =  { 1, 500000 };
    struct timeval t;
    u_int8_t v, v2;
    int i, s;

    /* Read new values at most once every 1.5 seconds. */
    timeradd(&sc->sc_lastread, &onepointfive, &t);
    s = splclock();
    i = timercmp(&mono_time, &t, >);
    if (i)
	sc->sc_lastread = mono_time;
    splx(s);

    if (i == 0)
	return;

    /* temperature */
    v = READREG(sc, ITEENV_TSENS0);
    DPRINTF(("TSENS0 = %d\n", v));
    sc->sc_data[0].cur.data_us = C2MK(v);
    sc->sc_data[0].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    v = READREG(sc, ITEENV_TSENS1);
    DPRINTF(("TSENS1 = %d\n", v));
    sc->sc_data[1].cur.data_us = C2MK(v);
    sc->sc_data[1].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    v = READREG(sc, ITEENV_TSENS2);
    DPRINTF(("TSENS2 = %d\n", v));
    sc->sc_data[2].cur.data_us = C2MK(v);
    sc->sc_data[2].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    /* fan */
    v2 = READREG(sc, ITEENV_FANDIV);

    v = READREG(sc, ITEENV_FAN0);
    DPRINTF(("FAN0 = %d\n", v));
    sc->sc_data[3].cur.data_us = FSPEED(v, ITEENV_FAN0SHIFT(v2));
    sc->sc_data[3].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    v = READREG(sc, ITEENV_FAN1);
    DPRINTF(("FAN1 = %d\n", v));
    sc->sc_data[4].cur.data_us = FSPEED(v, ITEENV_FAN1SHIFT(v2));
    sc->sc_data[4].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    v = READREG(sc, ITEENV_FAN2);
    DPRINTF(("FAN2 = %d\n", v));
    sc->sc_data[5].cur.data_us = FSPEED(v, ITEENV_FAN2SHIFT(v2));
    sc->sc_data[5].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;

    for(i = 0; i < 9; i++) {
	uint Vin = READREG(sc, ITEENV_VSENSE0 + i);
	uint Rquote = sc->sc_info[6 + i].rfact;
	
	DPRINTF(("VSENSE%d = %d\n", i, Vin));
	Vin *= 16;

	/* Vin is a bytes measured in 16mV steps, so possible inputs
	   (Vs) range from 0 to 4.080V.
	   
	   To measure other ranges, external resistors (Ra and Rb) are
	   needed. Positive voltages connect via them to ground, and
	   negative connect to the internal Vref (4.096V).
	   
	   What we need to calculate Vs is to know if it's a negative
	   input, and the ratio between Ra and Rb.

	   The sample table included suggests these ratios:

	   Voltage   Ra/Rb
	   +5V       0.68
	   +12V      3
	   -5V       2 1/7
	   -12V      4 1/7

	   One seventh seems strange, but it matches what the BIOS
	   reports on the Shuttle FS51 MB (for -12V, there is no -5V).
	*/

	sc->sc_data[6 + i].cur.data_s = Vin * (10000 + Rquote);
	if(sc->sc_negative_input[6 + i]) 
	    sc->sc_data[6 + i].cur.data_s -= 4096 * Rquote;
	sc->sc_data[6 + i].cur.data_s /= 10; /* get us to uV */
	sc->sc_data[6 + i].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
    }
}

static int
iteenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
{
	struct iteenv_softc *sc = sme->sme_cookie;

	simple_lock(&sc->sc_slock);

	iteenv_refresh_sensor_data(sc);
	*tred = sc->sc_data[tred->sensor];

	simple_unlock(&sc->sc_slock);

	return (0);
}

static int
iteenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
{
    struct iteenv_softc *sc = sme->sme_cookie;

    if (sc->sc_info[binfo->sensor].units == ENVSYS_SVOLTS_DC)
	sc->sc_info[binfo->sensor].rfact = binfo->rfact;
    binfo->validflags = ENVSYS_FVALID;
	
    return 0;
}

--=-=-=--