code:
/*	$OpenBSD: adt7460.c,v 1.21 2007/12/12 16:56:59 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Mark Kettenis
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ADT7460 registers */
#define ADT7460_2_5V		0x20
#define ADT7460_VCCP		0x21
#define ADT7460_VCC		0x22
#define ADT7460_V5		0x23
#define ADT7460_V12		0x24
#define ADT7460_VTR		0x99
#define ADT7460_VBAT		0x9a
#define ADT7460_REM1_TEMP	0x25
#define ADT7460_LOCAL_TEMP	0x26
#define ADT7460_REM2_TEMP	0x27
#define ADT7460_TACH1L		0x28
#define ADT7460_TACH1H		0x29
#define ADT7460_TACH2L		0x2a
#define ADT7460_TACH2H		0x2b
#define ADT7460_TACH3L		0x2c
#define ADT7460_TACH3H		0x2d
#define ADT7460_TACH4L		0x2e
#define ADT7460_TACH4H		0x2f
#define ADT7460_TACH5L		0xa9
#define ADT7460_TACH5H		0xaa
#define ADT7460_TACH6L		0xab
#define ADT7460_TACH6H		0xac
#define ADT7460_REVISION	0x3f
#define ADT7460_CONFIG		0x40
#define ADT7460_CONFIG_Vcc	0x80

/* Sensors */
#define ADT_2_5V		0
#define ADT_VCCP		1
#define ADT_VCC			2
#define ADT_V5			3
#define ADT_V12			4
#define ADT_VTR			5
#define ADT_VBAT		6
#define ADT_REM1_TEMP		7
#define ADT_LOCAL_TEMP		8
#define ADT_REM2_TEMP		9
#define ADT_TACH1		10
#define ADT_TACH2		11
#define ADT_TACH3		12
#define ADT_TACH4		13
#define ADT_TACH5		14
#define ADT_TACH6		15
#define ADT_NUM_SENSORS		16

struct adt_chip {
	const char	*name;
	short		ratio[7];
	int		type;
	short		vcc;
} adt_chips[] = {
	/* register	0x20  0x21  0x22  0x23  0x24  0xa8  0xaa	type	*/
	/* 		2.5v  vccp   vcc    5v   12v   vtr  vbat		*/

	{ "adt7460",	{ 2500,    0, 3300,    0,     0,    0,    0 },	7460,	5000 },
	{ "adt7467",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	7467,	5000 },
	{ "adt7475",	{    0, 2250, 3300,    0,     0,    0,    0 },	7475,	   0 },
	{ "adt7476",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	7476,	   0 },
	{ "adm1027",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	1027,	5000 },
	{ "lm85",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	7467,	   0 },
	{ "emc6d100",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	6100,	   0 },
	{ "emc6w201",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	6201,	   0 },
	{ "lm96000",	{ 2500, 2250, 3300, 5000, 12000,    0,    0 },	96000,	   0 },
	{ "sch5017",	{ 5000, 2250, 3300, 5000, 12000,    0,    0 },	5017,	   0 },
	{ "sch5027",	{ 5000, 2250, 3300, 5000, 12000, 3300, 3300 },	5027,	   0 }
};

struct {
	char		sensor;
	u_int8_t	cmd;
	u_short		index;
} worklist[] = {
	{ ADT_2_5V, ADT7460_2_5V, 32768 + 0 },
	{ ADT_VCCP, ADT7460_VCCP, 32768 + 1 },
	{ ADT_VCC, ADT7460_VCC, 32768 + 2 },
	{ ADT_V5, ADT7460_V5, 32768 + 3 },
	{ ADT_V12, ADT7460_V12, 32768 + 4 },
	{ ADT_VTR, ADT7460_VTR, 32768 + 5 },
	{ ADT_VBAT, ADT7460_VBAT, 32768 + 6 },
	{ ADT_REM1_TEMP, ADT7460_REM1_TEMP },
	{ ADT_LOCAL_TEMP, ADT7460_LOCAL_TEMP },
	{ ADT_REM2_TEMP, ADT7460_REM2_TEMP },
	{ ADT_TACH1, ADT7460_TACH1L },
	{ ADT_TACH2, ADT7460_TACH2L },
	{ ADT_TACH3, ADT7460_TACH3L },
	{ ADT_TACH4, ADT7460_TACH4L },
	{ ADT_TACH5, ADT7460_TACH5L },
	{ ADT_TACH6, ADT7460_TACH6L },
};

struct adt_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	i2c_addr_t sc_addr;
	u_int8_t sc_conf;
	struct adt_chip *chip;

	struct ksensor sc_sensor[ADT_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	adt_match(struct device *, void *, void *);
void	adt_attach(struct device *, struct device *, void *);

void	adt_refresh(void *);

struct cfattach adt_ca = {
	sizeof(struct adt_softc), adt_match, adt_attach
};

struct cfdriver adt_cd = {
	NULL, "adt", DV_DULL
};

int
adt_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;
	int i;

	for (i = 0; i < sizeof(adt_chips) / sizeof(adt_chips[0]); i++)
		if (strcmp(ia->ia_name, adt_chips[i].name) == 0)
			return (1);
	return (0);
}

void
adt_attach(struct device *parent, struct device *self, void *aux)
{
	struct adt_softc *sc = (struct adt_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, rev, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i < sizeof(adt_chips) / sizeof(adt_chips[0]); i++) {
		if (strcmp(ia->ia_name, adt_chips[i].name) == 0) {
			sc->chip = &adt_chips[i];
			break;
		}
	}

	cmd = ADT7460_REVISION;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &rev, sizeof rev, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot read REV register\n");
		return;
	}

	cmd = ADT7460_CONFIG;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sc->sc_conf, sizeof sc->sc_conf, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot read config register\n");
		return;
	}

	if (sc->chip->type == 7460) {
		data = 1;
		cmd = ADT7460_CONFIG;
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(": cannot set control register\n");
			return;
		}
	}

	iic_release_bus(sc->sc_tag, 0);

	printf(": %s rev 0x%02x", ia->ia_name, rev);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADT_2_5V].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+2.5Vin",
	    sizeof(sc->sc_sensor[ADT_2_5V].desc));

	if (sc->chip->type == 5017)
		strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+5VTR",
		    sizeof(sc->sc_sensor[ADT_2_5V].desc));
	if (sc->chip->type == 5027)
		strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+5V",
		    sizeof(sc->sc_sensor[ADT_2_5V].desc));

	sc->sc_sensor[ADT_VCCP].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_VCCP].desc, "Vccp",
	    sizeof(sc->sc_sensor[ADT_VCCP].desc));

	sc->sc_sensor[ADT_VCC].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_VCC].desc, "Vcc",
	    sizeof(sc->sc_sensor[ADT_VCC].desc));

	sc->sc_sensor[ADT_V5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_V5].desc, "+5V",
	    sizeof(sc->sc_sensor[ADT_V5].desc));

	sc->sc_sensor[ADT_V12].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_V12].desc, "+12V",
	    sizeof(sc->sc_sensor[ADT_V12].desc));

	sc->sc_sensor[ADT_VTR].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_VTR].desc, "+Vtr",
	    sizeof(sc->sc_sensor[ADT_VTR].desc));

	sc->sc_sensor[ADT_VBAT].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADT_VBAT].desc, "+Vbat",
	    sizeof(sc->sc_sensor[ADT_VBAT].desc));

	sc->sc_sensor[ADT_REM1_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADT_REM1_TEMP].desc, "Remote",
	    sizeof(sc->sc_sensor[ADT_REM1_TEMP].desc));

	sc->sc_sensor[ADT_LOCAL_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADT_LOCAL_TEMP].desc, "Internal",
	    sizeof(sc->sc_sensor[ADT_LOCAL_TEMP].desc));

	sc->sc_sensor[ADT_REM2_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADT_REM2_TEMP].desc, "Remote",
	    sizeof(sc->sc_sensor[ADT_REM2_TEMP].desc));

	sc->sc_sensor[ADT_TACH1].type = SENSOR_FANRPM;
	sc->sc_sensor[ADT_TACH2].type = SENSOR_FANRPM;
	sc->sc_sensor[ADT_TACH3].type = SENSOR_FANRPM;
	sc->sc_sensor[ADT_TACH4].type = SENSOR_FANRPM;
	sc->sc_sensor[ADT_TACH5].type = SENSOR_FANRPM;
	sc->sc_sensor[ADT_TACH6].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, adt_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADT_NUM_SENSORS; i++) {
		if (worklist[i].index >= 32768 &&
		    sc->chip->ratio[worklist[i].index - 32768] == 0)
			continue;
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	}
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
adt_refresh(void *arg)
{
	struct adt_softc *sc = arg;
	u_int8_t cmd, data, data2;
	u_int16_t fan;
	int i, ratio;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i < sizeof worklist / sizeof(worklist[0]); i++) {

		if (worklist[i].index >= 32768) {
			ratio = sc->chip->ratio[worklist[i].index - 32768];
			if (ratio == 0)	/* do not read a dead register */
				continue;
		}
		cmd = worklist[i].cmd;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			continue;
		}

		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
		switch (worklist[i].sensor) {
		case ADT_VCC:
			if (sc->chip->vcc && (sc->sc_conf & ADT7460_CONFIG_Vcc))
				ratio = sc->chip->vcc;
			/* FALLTHROUGH */
		case ADT_2_5V:
		case ADT_VCCP:
		case ADT_V5:
		case ADT_V12:
		case ADT_VTR:
		case ADT_VBAT:
			sc->sc_sensor[i].value = ratio * 1000 * (u_int)data / 192;
			break;
		case ADT_LOCAL_TEMP:
		case ADT_REM1_TEMP:
		case ADT_REM2_TEMP:
			if (data == 0x80)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[i].value =
				    (int8_t)data * 1000000 + 273150000;
			break;
		case ADT_TACH1:
		case ADT_TACH2:
		case ADT_TACH3:
		case ADT_TACH4:
			cmd = worklist[i].cmd + 1; /* TACHnH follows TACHnL */
			if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
			    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
				continue;
			}

			fan = data + (data2 << 8);
			if (fan == 0 || fan == 0xffff)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[i].value = (90000 * 60) / fan;
			break;
		case ADT_TACH5:
		case ADT_TACH6:
			if (sc->chip->type != 5027) {
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
				break;	/* only 5027 has these fans? */
			}
			cmd = worklist[i].cmd + 1; /* TACHnH follows TACHnL */
			if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
			    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
				continue;
			}

			fan = data + (data2 << 8);
			if (fan == 0 || fan == 0xffff)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[i].value = fan * 60;
			break;
		default:
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			break;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adt7462.c,v 1.6 2008/04/23 11:11:14 deraadt Exp $	*/

/*
 * Copyright (c) 2008 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

#define ADT7462_TEMPL		0x88
#define ADT7462_TEMPH		0x89
#define ADT7462_TEMP1L		0x8a
#define ADT7462_TEMP1H		0x8b
#define ADT7462_TEMP2L		0x8c
#define ADT7462_TEMP2H		0x8d
#define ADT7462_TEMP3L		0x8e
#define ADT7462_TEMP3H		0x8f
#define ADT7262_TACH1L		0x98
#define ADT7262_TACH1H		0x99
#define ADT7262_TACH2L		0x9a
#define ADT7262_TACH2H		0x9b
#define ADT7262_TACH3L		0x9c
#define ADT7262_TACH3H		0x9d
#define ADT7262_TACH4L		0x9e
#define ADT7262_TACH4H		0x9f
#define ADT7262_TACH5L		0xa2
#define ADT7262_TACH5H		0xa3
#define ADT7262_TACH6L		0xa4
#define ADT7262_TACH6H		0xa5
#define ADT7262_TACH7L		0xa6
#define ADT7262_TACH7H		0xa7
#define ADT7262_TACH8L		0xa8
#define ADT7262_TACH8H		0xa9

/* Sensors */
#define ADTFSM_TEMP0		0
#define ADTFSM_TEMP1		1
#define ADTFSM_TEMP2		2
#define ADTFSM_TEMP3		3
#define ADTFSM_TACH1		4
#define ADTFSM_TACH2		5
#define ADTFSM_TACH3		6
#define ADTFSM_TACH4		7
#define ADTFSM_TACH5		8
#define ADTFSM_TACH6		9
#define ADTFSM_TACH7		10
#define ADTFSM_TACH8		11
#define ADTFSM_NUM_SENSORS	12

struct adtfsm_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
	int		sc_fanmul;

	struct ksensor	sc_sensor[ADTFSM_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	adtfsm_match(struct device *, void *, void *);
void	adtfsm_attach(struct device *, struct device *, void *);
void	adtfsm_refresh(void *);

struct cfattach adtfsm_ca = {
	sizeof(struct adtfsm_softc), adtfsm_match, adtfsm_attach
};

struct cfdriver adtfsm_cd = {
	NULL, "adtfsm", DV_DULL
};

int
adtfsm_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adt7462") == 0)
		return (1);
	return (0);
}

void
adtfsm_attach(struct device *parent, struct device *self, void *aux)
{
	struct adtfsm_softc *sc = (struct adtfsm_softc *)self;
	struct i2c_attach_args *ia = aux;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	for (i = ADTFSM_TEMP0; i <= ADTFSM_TEMP3; i++) {
		sc->sc_sensor[i].type = SENSOR_TEMP;
		sc->sc_sensor[i].flags |= SENSOR_FINVALID;
	}

	strlcpy(sc->sc_sensor[0].desc, "Internal",
	    sizeof(sc->sc_sensor[0].desc));
	for (i = 1; i < 4; i++)
		strlcpy(sc->sc_sensor[i].desc, "External",
		    sizeof(sc->sc_sensor[i].desc));

	for (i = ADTFSM_TACH1; i <= ADTFSM_TACH8; i++) {
		sc->sc_sensor[i].type = SENSOR_FANRPM;
		sc->sc_sensor[i].flags |= SENSOR_FINVALID;
	}

	if (sensor_task_register(sc, adtfsm_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADTFSM_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
adtfsm_refresh(void *arg)
{
	struct adtfsm_softc *sc = arg;
	u_int8_t cmdh, cmdl, datah = 0x01, datal = 0x02;
	struct ksensor *ks;
	u_short ut;
	short t;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i <= ADTFSM_TEMP3 - ADTFSM_TEMP0; i++) {
		cmdl = ADT7462_TEMPL + i * 2;
		cmdh = ADT7462_TEMPH + i * 2;
		ks = &sc->sc_sensor[ADTFSM_TEMP0 + i];
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmdl, sizeof cmdl, &datal,
		    sizeof datal, 0) == 0 &&
		    iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmdh, sizeof cmdh, &datah,
		    sizeof datah, 0) == 0) {
			t = (((datah << 8) | datal) >> 6) - (64 << 2);
			ks->value = 273150000 + t * 250000;
			ks->flags &= ~SENSOR_FINVALID;
		} else
			ks->flags |= SENSOR_FINVALID;
	}

	for (i = 0; i <= ADTFSM_TACH8 - ADTFSM_TACH1; i++) {
		cmdl = ADT7262_TACH1L + i * 2;
		cmdh = ADT7262_TACH1H + i * 2;
		ks = &sc->sc_sensor[ADTFSM_TACH1 + i];
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmdl, sizeof cmdl, &datal,
		    sizeof datal, 0) == 0 &&
		    iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmdh, sizeof cmdh, &datah,
		    sizeof datah, 0) == 0) {
			ut = ((datah << 8) | datal);
			if (ut == 0x0000 || ut == 0xffff)
				ks->flags |= SENSOR_FINVALID;
			else {
				ks->value = 90000 * 60 / ut;
				ks->flags &= ~SENSOR_FINVALID;
			}
		} else
			ks->flags |= SENSOR_FINVALID;
	}

	iic_release_bus(sc->sc_tag, 0);
}

license: unknown


code:
/*	$OpenBSD: asb100.c,v 1.11 2008/04/17 19:01:48 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Damien Miller <djm@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* Apparently the ASB100 always lives here */
#define ASB100_ADDR			0x2d

/* ASB100 registers */
#define ASB100_TEMP3			0x17
#define ASB100_TEMP3_MAX		0x18
#define ASB100_TEMP3_HYST		0x19
#define ASB100_VIN0			0x20
#define ASB100_VIN1			0x21
#define ASB100_VIN2			0x22
#define ASB100_VIN3			0x23
#define ASB100_VIN4			0x24
#define ASB100_VIN5			0x25
#define ASB100_VIN6			0x26
#define ASB100_TEMP0			0x27
#define ASB100_FAN0			0x28
#define ASB100_FAN1			0x29
#define ASB100_FAN2			0x30
#define ASB100_VIN0_MIN			0x2b
#define ASB100_VIN0_MAX			0x2c
#define ASB100_VIN1_MIN			0x2d
#define ASB100_VIN1_MAX			0x2e
#define ASB100_VIN2_MIN			0x2f
#define ASB100_VIN2_MAX			0x30
#define ASB100_VIN3_MIN			0x31
#define ASB100_VIN3_MAX			0x32
#define ASB100_VIN4_MIN			0x33
#define ASB100_VIN4_MAX			0x34
#define ASB100_VIN5_MIN			0x35
#define ASB100_VIN5_MAX			0x36
#define ASB100_VIN6_MIN			0x37
#define ASB100_VIN6_MAX			0x38
#define ASB100_TEMP0_MAX		0x39
#define ASB100_TEMP0_HYST		0x3a
#define ASB100_FAN0_MIN			0x3b
#define ASB100_FAN1_MIN			0x3c
#define ASB100_FAN2_MIN			0x3d
#define	ASB100_CONFIG			0x40
#define	ASB100_ALARM1			0x41
#define	ASB100_ALARM2			0x42
#define	ASB100_SMIM1			0x43
#define	ASB100_SMIM2			0x44
#define	ASB100_VID_FANDIV01		0x47 /* 0-3 vid, 4-5 fan0, 6-7 fan1 */
#define	ASB100_I2C_ADDR			0x48
#define	ASB100_CHIPID			0x49
#define	ASB100_I2C_SUBADDR		0x4a
#define	ASB100_PIN_FANDIV2		0x4b /* 6-7 fan2 */
#define	ASB100_IRQ			0x4c
#define	ASB100_BANK			0x4e
#define	ASB100_CHIPMAN			0x4f
#define ASB100_VID_CHIPID		0x58 /* 0 vid highbit, 1-7 chipid */
#define ASB100_PWM			0x59 /* 0-3 duty cycle, 7 enable */

/* TEMP1/2 sensors live on other chips, pointed to by the I2C_SUBADDR reg */
#define	ASB100_SUB1_TEMP1		0x50 /* LM75 format */
#define	ASB100_SUB1_TEMP1_HYST		0x53
#define	ASB100_SUB1_TEMP1_MAX		0x55
#define	ASB100_SUB2_TEMP2		0x50 /* LM75 format */
#define	ASB100_SUB2_TEMP2_HYST		0x53
#define	ASB100_SUB2_TEMP2_MAX		0x55

/* Sensors */
#define ASB100_SENSOR_VIN0	0
#define ASB100_SENSOR_VIN1	1
#define ASB100_SENSOR_VIN2	2
#define ASB100_SENSOR_VIN3	3
#define ASB100_SENSOR_VIN4	4
#define ASB100_SENSOR_VIN5	5
#define ASB100_SENSOR_VIN6	6
#define ASB100_SENSOR_FAN0	7
#define ASB100_SENSOR_FAN1	8
#define ASB100_SENSOR_FAN2	9
#define ASB100_SENSOR_TEMP0	10
#define ASB100_SENSOR_TEMP1	11
#define ASB100_SENSOR_TEMP2	12
#define ASB100_SENSOR_TEMP3	13
#define ASB100_NUM_SENSORS	14

struct asbtm_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[ASB100_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int		sc_fanmul[3];
	int		sc_satellite[2];
};

int	asbtm_banksel(struct asbtm_softc *, u_int8_t, u_int8_t *);
int	asbtm_match(struct device *, void *, void *);
void	asbtm_attach(struct device *, struct device *, void *);
void	asbtm_refresh(void *);

struct cfattach asbtm_ca = {
	sizeof(struct asbtm_softc), asbtm_match, asbtm_attach
};

struct cfdriver asbtm_cd = {
	NULL, "asbtm", DV_DULL
};

int
asbtm_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "asb100") == 0)
		return (1);

	return (0);
}

int
asbtm_banksel(struct asbtm_softc *sc, u_int8_t new_bank, u_int8_t *orig_bank)
{
	u_int8_t cmd, data;

	new_bank &= 0xf;

	cmd = ASB100_BANK;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0))
		return (-1);

	if (orig_bank != NULL)
		*orig_bank = data & 0x0f;

	if ((data & 0xf) != new_bank) {
		cmd = ASB100_BANK;
		data = new_bank | (data & 0xf0);
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
		    &cmd, sizeof cmd, &data, sizeof data, 0))
			return (-1);
	}

	return (0);
}

void
asbtm_attach(struct device *parent, struct device *self, void *aux)
{
	struct asbtm_softc *sc = (struct asbtm_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t orig_bank, cmd, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	if (asbtm_banksel(sc, 0, &orig_bank) == -1) {
		printf(": cannot get/set register bank\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}

	cmd = ASB100_VID_FANDIV01;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": cannot get fan01 register\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	sc->sc_fanmul[0] = (1 << (data >> 4) & 0x3);
	sc->sc_fanmul[1] = (1 << (data >> 6) & 0x3);

	cmd = ASB100_PIN_FANDIV2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": cannot get fan2 register\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	sc->sc_fanmul[0] = (1 << (data >> 6) & 0x3);

	cmd = ASB100_I2C_SUBADDR;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": cannot get satellite chip address register\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	/* Maybe a relative address of zero means "not present" here... */
	sc->sc_satellite[0] = 0x48 + (data & 0xf);
	sc->sc_satellite[1] = 0x48 + ((data >> 4) & 0xf);

	iic_ignore_addr(sc->sc_satellite[0]);
	iic_ignore_addr(sc->sc_satellite[1]);
	if (sc->sc_satellite[0] == sc->sc_satellite[1])
		sc->sc_satellite[1] = -1;

	if (asbtm_banksel(sc, orig_bank, NULL) == -1) {
		printf(": cannot restore saved bank %d\n", orig_bank);
		iic_release_bus(sc->sc_tag, 0);
		return;
	}

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ASB100_SENSOR_VIN0].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN1].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN2].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN3].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN4].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN5].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[ASB100_SENSOR_VIN6].type = SENSOR_VOLTS_DC;

	sc->sc_sensor[ASB100_SENSOR_FAN0].type = SENSOR_FANRPM;
	sc->sc_sensor[ASB100_SENSOR_FAN1].type = SENSOR_FANRPM;
	sc->sc_sensor[ASB100_SENSOR_FAN2].type = SENSOR_FANRPM;

	sc->sc_sensor[ASB100_SENSOR_TEMP0].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ASB100_SENSOR_TEMP0].desc, "External",
	    sizeof(sc->sc_sensor[ASB100_SENSOR_TEMP0].desc));

	sc->sc_sensor[ASB100_SENSOR_TEMP1].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ASB100_SENSOR_TEMP1].desc, "Internal",
	    sizeof(sc->sc_sensor[ASB100_SENSOR_TEMP1].desc));

	sc->sc_sensor[ASB100_SENSOR_TEMP2].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ASB100_SENSOR_TEMP2].desc, "Internal",
	    sizeof(sc->sc_sensor[ASB100_SENSOR_TEMP2].desc));
	if (sc->sc_satellite[1] == -1)
		sc->sc_sensor[ASB100_SENSOR_TEMP2].flags |= SENSOR_FINVALID;

	sc->sc_sensor[ASB100_SENSOR_TEMP3].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ASB100_SENSOR_TEMP3].desc, "External",
	    sizeof(sc->sc_sensor[ASB100_SENSOR_TEMP3].desc));

	if (sensor_task_register(sc, asbtm_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ASB100_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

static void
fanval(struct ksensor *sens, int mul, u_int8_t data)
{
	int tmp = data * mul;

	if (tmp == 0)
		sens->flags |= SENSOR_FINVALID;
	else {
		sens->value = 1350000 / tmp;
		sens->flags &= ~SENSOR_FINVALID;
	}
}

void
asbtm_refresh(void *arg)
{
	struct asbtm_softc *sc = arg;
	u_int8_t orig_bank, cmd, data;
	int8_t sdata;
	u_int16_t sdata2;

	iic_acquire_bus(sc->sc_tag, 0);

	if (asbtm_banksel(sc, 0, &orig_bank) == -1) {
		printf("%s: cannot get/set register bank\n",
		    sc->sc_dev.dv_xname);
		iic_release_bus(sc->sc_tag, 0);
		return;
	}

	cmd = ASB100_VIN0;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN0].value = (data * 1000000) / 16;

	cmd = ASB100_VIN1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN1].value = (data * 1000000) / 16;

	cmd = ASB100_VIN2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN2].value = (data * 1000000) / 16;

	cmd = ASB100_VIN3;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN3].value = (data * 1000000) / 16;

	cmd = ASB100_VIN4;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN4].value = (data * 1000000) / 16;

	cmd = ASB100_VIN5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN5].value = (data * 1000000) / 16;

	cmd = ASB100_VIN6;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_VIN6].value = (data * 1000000) / 16;

	cmd = ASB100_FAN0;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ASB100_SENSOR_FAN0],
		    sc->sc_fanmul[0], data);

	cmd = ASB100_FAN1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ASB100_SENSOR_FAN1],
		    sc->sc_fanmul[1], data);

	cmd = ASB100_FAN2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ASB100_SENSOR_FAN2],
		    sc->sc_fanmul[2], data);

	cmd = ASB100_TEMP0;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_TEMP0].value = 273150000 +
		    1000000 * sdata;

	cmd = ASB100_TEMP3;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof sdata, 0) == 0)
		sc->sc_sensor[ASB100_SENSOR_TEMP3].value = 273150000 +
		    1000000 * sdata;

	/* Read satellite chips for TEMP1/TEMP2 */
	cmd = ASB100_SUB1_TEMP1;
	if (sc->sc_satellite[0] != -1) {
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_satellite[0], &cmd, sizeof cmd, &sdata2,
		    sizeof sdata2, 0) == 0 && sdata2 != 0xffff) {
			sc->sc_sensor[ASB100_SENSOR_TEMP1].value = 273150000 +
			    500000 * (betoh16(sdata2) / 128);
			sc->sc_sensor[ASB100_SENSOR_TEMP2].flags &=
			    ~SENSOR_FINVALID;
		} else {
			sc->sc_sensor[ASB100_SENSOR_TEMP2].flags |=
			    SENSOR_FINVALID;
		}
	}

	cmd = ASB100_SUB2_TEMP2;
	if (sc->sc_satellite[1] != -1) {
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_satellite[1], &cmd, sizeof cmd, &sdata2,
		    sizeof sdata2, 0) == 0 && sdata2 != 0xffff) {
			sc->sc_sensor[ASB100_SENSOR_TEMP2].value = 273150000 +
			    500000 * (betoh16(sdata2) / 128);
			sc->sc_sensor[ASB100_SENSOR_TEMP2].flags &=
			    ~SENSOR_FINVALID;
		} else {
			sc->sc_sensor[ASB100_SENSOR_TEMP2].flags |=
			    SENSOR_FINVALID;
		}
	}

	asbtm_banksel(sc, orig_bank, NULL);

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: asc7611.c,v 1.2 2009/01/26 15:07:49 kettenis Exp $	*/

/*
 * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/*
 * Andigilog aSC7611
 *	Hardware Monitor with Integrated Fan Control
 * http://www.andigilog.com/downloads/aSC7611_70A05007.pdf
 * October 2006
 */

/* Temperature */
#define ANDL_NUM_TEMPS	3
static const struct {
	const char	*name;
	const uint8_t	mreg;
	const uint8_t	lreg;
} andl_temp[ANDL_NUM_TEMPS] = {
	{ "External", 0x25, 0x10 },
	{ "Internal", 0x26, 0x15 },
	{ "External", 0x27, 0x0e }
};

/* Voltage */
#define ANDL_NUM_VOLTS	5
static const struct {
	const char	*name;
	const short	nominal;
	const uint8_t	mreg;
} andl_volt[ANDL_NUM_VOLTS] = {
	{ "+2.5V", 2500, 0x20 },
	{ "Vccp", 2250, 0x21 },
	{ "+3.3V", 3300, 0x22 },
	{ "+5V", 5000, 0x23 },
	{ "+12V", 12000, 0x24 }
};

/* Fan */
#define ANDL_NUM_TACHS	4
#define ANDL_TACH_START	0x28

#define ANDL_NUM_TOTAL	(ANDL_NUM_TEMPS + ANDL_NUM_VOLTS + ANDL_NUM_TACHS)

struct andl_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensors[ANDL_NUM_TOTAL];
	struct ksensordev sc_sensordev;
};

int	andl_match(struct device *, void *, void *);
void	andl_attach(struct device *, struct device *, void *);
void	andl_refresh(void *);

int	andl_refresh_temps(struct andl_softc *, struct ksensor *);
int	andl_refresh_volts(struct andl_softc *, struct ksensor *);
int	andl_refresh_tachs(struct andl_softc *, struct ksensor *);

uint8_t	andl_readreg(struct andl_softc *, uint8_t);
void	andl_writereg(struct andl_softc *, uint8_t, uint8_t);

struct cfattach andl_ca = {
	sizeof(struct andl_softc), andl_match, andl_attach
};

struct cfdriver andl_cd = {
	NULL, "andl", DV_DULL
};

int
andl_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "asc7611") == 0)
		return 1;
	return 0;
}

void
andl_attach(struct device *parent, struct device *self, void *aux)
{
	struct andl_softc *sc = (struct andl_softc *)self;
	struct i2c_attach_args *ia = aux;
	int i, j;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	printf(": %s", ia->ia_name);

	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	for (i = 0; i < ANDL_NUM_TEMPS; i++) {
		strlcpy(sc->sc_sensors[i].desc, andl_temp[i].name,
		    sizeof(sc->sc_sensors[i].desc));
		sc->sc_sensors[i].type = SENSOR_TEMP;
	}

	for (j = i; i < j + ANDL_NUM_VOLTS; i++) {
		strlcpy(sc->sc_sensors[i].desc, andl_volt[i - j].name,
		    sizeof(sc->sc_sensors[i].desc));
		sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
	}

	for (j = i + ANDL_NUM_TACHS; i < j; i++)
		sc->sc_sensors[i].type = SENSOR_FANRPM;

	for (i = 0; i < j; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);

	if (sensor_task_register(sc, andl_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	sensordev_install(&sc->sc_sensordev);
	printf("\n");
}

void
andl_refresh(void *arg)
{
	struct andl_softc *sc = arg;
	struct ksensor *s = sc->sc_sensors;

	iic_acquire_bus(sc->sc_tag, 0);

	s += andl_refresh_temps(sc, s);
	s += andl_refresh_volts(sc, s);
	s += andl_refresh_tachs(sc, s);

	iic_release_bus(sc->sc_tag, 0);
}

int
andl_refresh_temps(struct andl_softc *sc, struct ksensor *s)
{
	int i;

	for (i = 0; i < ANDL_NUM_TEMPS; i++) {
		uint8_t m = andl_readreg(sc, andl_temp[i].mreg);
		uint8_t l = andl_readreg(sc, andl_temp[i].lreg);
		int32_t t = (m << 8 | l) >> (16 - 10);

		if (t & 0x200)
			t -= 0x400;
		t *= 250;
		if (t < -55000 || t > 125000) {
			s[i].flags |= SENSOR_FINVALID;
			s[i].value = 0;
		} else {
			s[i].value = t * 1000 + 273150000;
			s[i].flags &= ~SENSOR_FINVALID;
		}
	}
	return i;
}

int
andl_refresh_volts(struct andl_softc *sc, struct ksensor *s)
{
	int i;

	for (i = 0; i < ANDL_NUM_VOLTS; i++)
		s[i].value = 1000ll * andl_readreg(sc, andl_volt[i].mreg) *
		    andl_volt[i].nominal / 0xc0;
	return i;
}

int
andl_refresh_tachs(struct andl_softc *sc, struct ksensor *s)
{
	int i;

	for (i = 0; i < ANDL_NUM_TACHS; i++) {
		uint8_t l = andl_readreg(sc, ANDL_TACH_START + i * 2);
		uint8_t m = andl_readreg(sc, ANDL_TACH_START + i * 2 + 1);
		uint16_t b = m << 8 | l;

		if (b >= 0xfffc || b == 0) {
			s[i].flags |= SENSOR_FINVALID;
			s[i].value = 0;
		} else {
			s[i].value = (90000 * 60) / b;
			s[i].flags &= ~SENSOR_FINVALID;
		}
	}
	return i;
}

uint8_t
andl_readreg(struct andl_softc *sc, uint8_t reg)
{
	uint8_t data;

	iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);

	return data;
}

void
andl_writereg(struct andl_softc *sc, uint8_t reg, uint8_t data)
{
	iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);
}


code:
/*	$OpenBSD: asc7621.c,v 1.4 2007/10/31 20:46:17 cnst Exp $	*/

/*
 * Copyright (c) 2007 Mike Belopuhov
 * Copyright (c) 2007 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ASC7621 registers */

#define ASC7621_PECI		0x40	/* Check for PECI monitoring */
#define ASC7621_PECI_MASK	0x10	/* 00010000 */

#define ASC7621_LEGACY		0x36	/* Check for legacy mode */
#define ASC7621_LEGACY_MASK	0x10	/* 00010000 */

#define ASC7621_TEMP1H		0x25	/* Zone 1 Temperature (MS Byte) */
#define ASC7621_TEMP1L		0x10	/* Zone 1 Temperature (LS Byte) */
#define ASC7621_TEMP2H		0x26	/* Zone 2 Temperature (MS Byte) */
#define ASC7621_TEMP2L		0x15	/* Zone 2 Temperature (LS Byte) */
#define ASC7621_TEMP3H		0x27	/* Zone 3 Temperature (MS Byte) */
#define ASC7621_TEMP3L		0x16	/* Zone 3 Temperature (LS Byte) */
#define ASC7621_TEMP4H		0x33	/* Zone 4 Temperature (MS Byte) */
#define ASC7621_TEMP4L		0x17	/* Zone 4 Temperature (LS Byte) */
#define ASC7621_TEMP_NA		0x80	/* Not plugged */

#define ASC7621_IN1_VH		0x20	/* 2.5V (MS Byte) */
#define ASC7621_IN1_VL		0x13	/* 2.5V (LS Byte) */
#define ASC7621_IN2_VH		0x21	/* VCCP (MS Byte) */
#define ASC7621_IN2_VL		0x18	/* VCCP (LS Byte) */
#define ASC7621_IN3_VH		0x22	/* 3.3V (MS Byte) */
#define ASC7621_IN3_VL		0x11	/* 2.3V (LS Byte) */
#define ASC7621_IN4_VH		0x23	/* 5V   (MS Byte) */
#define ASC7621_IN4_VL		0x12	/* 5V   (LS Byte) */
#define ASC7621_IN5_VH		0x24	/* 12V  (MS Byte) */
#define ASC7621_IN5_VL		0x14	/* 12V  (LS Byte) */

#define ASC7621_TACH1H		0x29	/* Tachometer 1 (MS Byte) */
#define ASC7621_TACH1L		0x28	/* Tachometer 1 (LS Byte) */
#define ASC7621_TACH2H		0x2b	/* Tachometer 2 (MS Byte) */
#define ASC7621_TACH2L		0x2a	/* Tachometer 2 (LS Byte) */
#define ASC7621_TACH3H		0x2d	/* Tachometer 3 (MS Byte) */
#define ASC7621_TACH3L		0x2c	/* Tachometer 3 (LS Byte) */
#define ASC7621_TACH4H		0x2f	/* Tachometer 4 (MS Byte) */
#define ASC7621_TACH4L		0x2e	/* Tachometer 4 (LS Byte) */

/* Sensors */
#define ADL_TEMP1		0
#define ADL_TEMP2		1
#define ADL_TEMP3		2
#define ADL_TEMP4		3
#define ADL_IN1_V		4
#define ADL_IN2_V		5
#define ADL_IN3_V		6
#define ADL_IN4_V		7
#define ADL_IN5_V		8
#define ADL_TACH1		9
#define ADL_TACH2		10
#define ADL_TACH3		11
#define ADL_TACH4		12
#define ADL_NUM_SENSORS		13

struct {
	char		sensor;
	u_int8_t	hreg;			/* MS-byte register */
	u_int8_t	lreg;			/* LS-byte register */
	char		*name;
	u_short		mVscale;
	u_short		tempscale;		/* else a fan */
} adl_worklist[] = {
	{ ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 },
	{ ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 },
	{ ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 },
	{ ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 },

	{ ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 },
	{ ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp",  2250, 0 },
	{ ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 },
	{ ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V",   5000, 0 },
	{ ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 },

	{ ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 },
	{ ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 },
	{ ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 },
	{ ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 }
};

struct adl_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	i2c_addr_t sc_addr;
	u_int8_t sc_conf;

	struct ksensor sc_sensor[ADL_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

#if 0
static int peci_enabled;
static int legacy_mode;
#endif

int	adl_match(struct device *, void *, void *);
void	adl_attach(struct device *, struct device *, void *);

void	adl_refresh(void *);

struct cfattach adl_ca = {
	sizeof(struct adl_softc), adl_match, adl_attach
};

struct cfdriver adl_cd = {
	NULL, "adl", DV_DULL
};

int
adl_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "asc7621") == 0)
		return (1);
	return (0);
}

void
adl_attach(struct device *parent, struct device *self, void *aux)
{
	struct adl_softc *sc = (struct adl_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	printf(": %s", ia->ia_name);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	/* Check for PECI mode */
	cmd = ASC7621_PECI;
	(void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof(cmd), &data, sizeof(data), 0);
	if (data & ASC7621_PECI_MASK)
		printf(", PECI enabled\n");

#if 0
	/* Check for legacy mode */
	cmd = ASC7621_LEGACY;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof(cmd), &data, sizeof(data), 0)) {
		printf(", unable to read PECI configuration register");
	}
	if (data & ASC7621_LEGACY_MASK)
		legacy_mode = 1;
#endif

	if (sensor_task_register(sc, adl_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADL_NUM_SENSORS; i++) {
		if (adl_worklist[i].tempscale)
			sc->sc_sensor[i].type = SENSOR_TEMP;
		else if (adl_worklist[i].mVscale)
			sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
		else
			sc->sc_sensor[i].type = SENSOR_FANRPM;
		strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name,
		    sizeof(sc->sc_sensor[i].desc));

		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	}
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
adl_refresh(void *arg)
{
	struct adl_softc *sc = arg;
	int64_t temp, volt;
	u_int8_t hdata, ldata, hreg, lreg;
	u_int16_t fan;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) {
		hreg = adl_worklist[i].hreg;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) {
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			continue;
		}
		lreg = adl_worklist[i].lreg;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) {
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			continue;
		}

		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
		if (adl_worklist[i].tempscale) {
			if (hdata == ASC7621_TEMP_NA)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else {
				/*
				 * 10-bit two's complement integer in
				 * steps of 0.25
				 */
				temp = ((hdata << 8 | ldata)) >> (16 - 10);
				temp = temp * 250000 + 273150000;
				sc->sc_sensor[i].value = temp;
			}
		} else if (adl_worklist[i].mVscale) {
			volt = ((hdata << 8 | ldata)) >> (16 - 10);
			volt = volt * adl_worklist[i].mVscale / (192 << 2);
			sc->sc_sensor[i].value = volt * 1000;
		} else {
			/*
			 * Inversed to ensure that the LS byte will be read
			 * before MS byte.
			 */
			fan = hdata + (ldata << 8);
			if (fan == 0 || fan == 0xffff)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[i].value = (90000 * 60) / fan;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: ds1631.c,v 1.11 2009/08/12 17:13:30 kettenis Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* Maxim ds 1631 registers */
#define DS1631_START		0x51
#define DS1624_START		0xee
#define DS1631_TEMP		0xaa
#define DS1631_CONTROL		0xac
#define  DS1631_CONTROL_DONE	0x80
#define  DS1631_CONTROL_1SHOT	0x01

/* Sensors */
#define MAXDS_TEMP		0
#define MAXDS_NUM_SENSORS	1

struct maxds_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[MAXDS_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	maxds_match(struct device *, void *, void *);
void	maxds_attach(struct device *, struct device *, void *);
void	maxds_refresh(void *);

struct cfattach maxds_ca = {
	sizeof(struct maxds_softc), maxds_match, maxds_attach
};

struct cfdriver maxds_cd = {
	NULL, "maxds", DV_DULL
};

int
maxds_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "ds1631") == 0 ||
	    strcmp(ia->ia_name, "ds1624") == 0 ||
	    strcmp(ia->ia_name, "ds1721") == 0)
		return (1);
	return (0);
}

void
maxds_attach(struct device *parent, struct device *self, void *aux)
{
	struct maxds_softc *sc = (struct maxds_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int i;

	printf(": %s", ia->ia_name);

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = DS1631_CONTROL;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data & DS1631_CONTROL_1SHOT) {
			/*
			 * 1-Shot mode would require us to write every refresh
			 * which is stupid.  Put us into continuous mode.
			 */
			data &= ~DS1631_CONTROL_1SHOT;

			(void) iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
			    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0);
			//delay(10 * 1000);
			printf(", continuous");
			goto dostart;
		}
		if (data & DS1631_CONTROL_DONE) {
dostart:
			cmd = DS1631_START;
			if (strcmp(ia->ia_name, "ds1624") == 0)
				cmd = DS1624_START;
			(void) iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
			    sc->sc_addr, &cmd, sizeof cmd, NULL, 0, 0);
			printf(", starting");
		}
	}

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[MAXDS_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[MAXDS_TEMP].desc, "Internal",
	    sizeof(sc->sc_sensor[MAXDS_TEMP].desc));

	if (sensor_task_register(sc, maxds_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < MAXDS_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
maxds_refresh(void *arg)
{
	struct maxds_softc *sc = arg;
	u_int8_t cmd;
	u_int16_t data;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = DS1631_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		sc->sc_sensor[MAXDS_TEMP].value = 273150000 +
		    (int)(betoh16(data)) / 8 * 31250;
		sc->sc_sensor[MAXDS_TEMP].flags &= ~SENSOR_FINVALID;
	} else
		sc->sc_sensor[MAXDS_TEMP].flags |= SENSOR_FINVALID;

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: fcu.c,v 1.7 2007/06/24 05:34:35 dlg Exp $	*/

/*
 * Copyright (c) 2005 Mark Kettenis
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* FCU registers */
#define FCU_FAN_FAIL	0x0b		/* fans states in bits 0<1-6>7 */
#define FCU_FAN_ACTIVE	0x0d
#define FCU_FANREAD(x)	0x11 + (x)*2
#define FCU_FANSET(x)	0x10 + (x)*2
#define FCU_PWM_FAIL	0x2b
#define FCU_PWM_ACTIVE	0x2d
#define FCU_PWMREAD(x)	0x30 + (x)*2

/* Sensors */
#define FCU_RPM1		0
#define FCU_RPM2		1
#define FCU_RPM3		2
#define FCU_RPM4		3
#define FCU_RPM5		4
#define FCU_RPM6		5
#define  FCU_FANS		6
#define FCU_PWM1		6
#define FCU_PWM2		7
#define  FCU_PWMS		2
#define FCU_NUM_SENSORS		8

struct fcu_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	i2c_addr_t sc_addr;

	struct ksensor sc_sensor[FCU_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	fcu_match(struct device *, void *, void *);
void	fcu_attach(struct device *, struct device *, void *);

void	fcu_refresh(void *);

struct cfattach fcu_ca = {
	sizeof(struct fcu_softc), fcu_match, fcu_attach
};

struct cfdriver fcu_cd = {
	NULL, "fcu", DV_DULL
};

int
fcu_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "fcu") == 0)
		return (1);
	return (0);
}

void
fcu_attach(struct device *parent, struct device *self, void *aux)
{
	struct fcu_softc *sc = (struct fcu_softc *)self;
	struct i2c_attach_args *ia = aux;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	for (i = 0; i < FCU_FANS; i++)
		sc->sc_sensor[i].type = SENSOR_FANRPM;
	for (i = 0; i < FCU_PWMS; i++) {
		sc->sc_sensor[FCU_PWM1 + i].type = SENSOR_PERCENT;
		strlcpy(sc->sc_sensor[FCU_PWM1 + i].desc, "PWM",
		    sizeof(sc->sc_sensor[FCU_PWM1 + i].desc));
	}

	if (sensor_task_register(sc, fcu_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < FCU_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
fcu_refresh(void *arg)
{
	struct fcu_softc *sc = arg;
	u_int8_t cmd, fail, fan[2], active;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = FCU_FAN_FAIL;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
		goto abort;
	cmd = FCU_FAN_ACTIVE;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
		goto abort;
	fail &= active;

	for (i = 0; i < FCU_FANS; i++) {
		if (fail & (1 << (i + 1)))
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
	}

	cmd = FCU_PWM_FAIL;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
		goto abort;
	cmd = FCU_PWM_ACTIVE;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
		goto abort;
	fail &= active;

	for (i = 0; i < FCU_PWMS; i++) {
		if (fail & (1 << (i + 1)))
			sc->sc_sensor[FCU_PWMS + i].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[FCU_PWMS + i].flags &= ~SENSOR_FINVALID;
	}

	for (i = 0; i < FCU_FANS; i++) {
		cmd = FCU_FANREAD(i + 1);
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
			sc->sc_sensor[FCU_RPM1 + i].flags |= SENSOR_FINVALID;
			continue;
		}
		sc->sc_sensor[FCU_RPM1 + i].value = (fan[0] << 5) | (fan[1] >> 3);
	}

	for (i = 0; i < FCU_PWMS; i++) {
		cmd = FCU_PWMREAD(i + 1);
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
			sc->sc_sensor[FCU_PWM1 + i].flags |= SENSOR_FINVALID;
			continue;
		}
		sc->sc_sensor[FCU_PWM1 + i].value = (fan[0] * 100 * 1000) / 255;
	}

abort:
	iic_release_bus(sc->sc_tag, 0);
}


code:
# $OpenBSD: files.i2c,v 1.51 2013/03/31 13:30:24 kettenis Exp $
#	$NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $

define	i2c {[addr = -1], [size = -1]}
device	iic: i2c
attach	iic at i2cbus
file	dev/i2c/i2c.c				iic | i2cbus
file	dev/i2c/i2c_exec.c			iic | i2cbus
file	dev/i2c/i2c_scan.c			iic | i2cbus

file	dev/i2c/i2c_bitbang.c			i2c_bitbang

#
# I2C client devices
#

# National Semiconductor LM75/LM77 temperature sensor
device	lmtemp
attach	lmtemp at i2c
file	dev/i2c/lm75.c				lmtemp

# National Semiconductor LM93 sensor
device	lmn
attach	lmn at i2c
file	dev/i2c/lm93.c				lmn

# National Semiconductor LM87 sensor
device	lmenv
attach	lmenv at i2c
file	dev/i2c/lm87.c				lmenv

# Maxim MAX6690 Remote/Local Temperature sensor
device	maxtmp
attach	maxtmp at i2c
file	dev/i2c/maxim6690.c			maxtmp

# Analog Devices AD741x Temperature sensor + 4-channel ADC
device	adc
attach	adc at i2c
file	dev/i2c/ad741x.c			adc

# TAOS TSL2560/61 light-to-digital converter
device	tsl
attach	tsl at i2c
file	dev/i2c/tsl2560.c			tsl

# ADM1021
device	admtemp
attach	admtemp at i2c
file	dev/i2c/adm1021.c			admtemp

# ADM1024
device	admlc
attach	admlc at i2c
file	dev/i2c/adm1024.c			admlc

# ADM1025
device	admtm
attach	admtm at i2c
file	dev/i2c/adm1025.c			admtm

# ADM1030
device	admtmp
attach	admtmp at i2c
file	dev/i2c/adm1030.c			admtmp

# ADM1031
device	admtt
attach	admtt at i2c
file	dev/i2c/adm1031.c			admtt

# PCA9554
device	pcagpio: gpiobus
attach	pcagpio at i2c
file	dev/i2c/pca9554.c			pcagpio

# Maxim DS1631
device	maxds
attach	maxds at i2c
file	dev/i2c/ds1631.c			maxds

# Apple FCU
device	fcu
attach	fcu at i2c
file	dev/i2c/fcu.c				fcu

# Apple ADT
device	adt
attach	adt at i2c
file	dev/i2c/adt7460.c			adt

# National Semiconductor LM78/79/81 and compatible hardware monitors
attach lm at i2c with lm_i2c
file	dev/i2c/lm78_i2c.c			lm_i2c

# ADM1026
device	admcts
attach	admcts at i2c
file	dev/i2c/adm1026.c			admcts

# ASB100
device	asbtm
attach	asbtm at i2c
file	dev/i2c/asb100.c			asbtm

# Winbond W83L784R/W83L875R/W83L785TS-L
device	wbenv
attach	wbenv at i2c
file	dev/i2c/w83l784r.c			wbenv

# Winbond W83793G
device	wbng
attach	wbng at i2c
file	dev/i2c/w83793g.c			wbng

# Nuvoton W83795G
device	nvt
attach	nvt at i2c
file	dev/i2c/w83795g.c			nvt

# Genesys Logic GL518SM
device	glenv
attach	glenv at i2c
file	dev/i2c/gl518sm.c			glenv

# RICOH RS5C372[AB] Real Time Clock
device	ricohrtc
attach	ricohrtc at i2c
file	dev/i2c/rs5c372.c			ricohrtc

# Fintek F75375
device	fintek
attach	fintek at i2c
file	dev/i2c/fintek.c			fintek

# Philips Semiconductor PCA9532
device	pcaled: gpiobus
attach	pcaled at i2c
file	dev/i2c/pca9532.c			pcaled

# Andigilog aSC7621
device	adl
attach	adl at i2c
file	dev/i2c/asc7621.c			adl

# Andigilog aSC7611
device	andl
attach	andl at i2c
file	dev/i2c/asc7611.c			andl

# TI TMHC50 / Analog ADM1022
device	thmc
attach	thmc at i2c
file	dev/i2c/thmc50.c			thmc

# SPD Memory EEPROM
attach	spdmem at i2c with spdmem_iic
file	dev/i2c/spdmem_i2c.c			spdmem

# SO-DIMM (JC-42.4) temperature sensor
device	sdtemp
attach	sdtemp at i2c
file	dev/i2c/sdtemp.c			sdtemp

device	adtfsm
attach	adtfsm at i2c
file	dev/i2c/adt7462.c			adtfsm

# STMicroelectronics LIS331DL MEMS motion sensor
device	lisa
attach	lisa at i2c
file	dev/i2c/lis331dl.c			lisa


code:
/*	$OpenBSD: fintek.c,v 1.8 2014/10/12 19:40:22 miod Exp $ */
/*
 * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* Sensors */
#define F_VCC	0
#define F_V1	1
#define F_V2	2
#define F_V3	3
#define F_TEMP1	4
#define F_TEMP2	5
#define F_FAN1	6
#define F_FAN2	7
#define F_NUM_SENSORS	8

struct fintek_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	i2c_addr_t sc_addr;

#ifndef SMALL_KERNEL
	struct ksensor sc_sensor[F_NUM_SENSORS];
	struct ksensordev sc_sensordev;
#endif
};

int	fintek_match(struct device *, void *, void *);
void	fintek_attach(struct device *, struct device *, void *);

void	fintek_refresh(void *);
int	fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
	    size_t size);
int	fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
	    size_t size);
void	fintek_fullspeed(struct fintek_softc *sc);

struct cfattach fintek_ca = {
	sizeof(struct fintek_softc), fintek_match, fintek_attach
};

struct cfdriver fintek_cd = {
	NULL, "fintek", DV_DULL
};

#define FINTEK_CONFIG1		0x01
#define  FINTEK_FAN1_LINEAR_MODE	0x10
#define  FINTEK_FAN2_LINEAR_MODE	0x20
#define FINTEK_VOLT0		0x10
#define FINTEK_VOLT1		0x11
#define FINTEK_VOLT2		0x12
#define FINTEK_VOLT3		0x13
#define FINTEK_TEMP1		0x14
#define FINTEK_TEMP2		0x15
#define FINTEK_FAN1		0x16
#define FINTEK_FAN2		0x18
#define FINTEK_VERSION		0x5c
#define FINTEK_RSTCR		0x60
#define  FINTEK_FAN1_MODE_MANUAL	0x30
#define  FINTEK_FAN2_MODE_MANUAL	0xc0
#define FINTEK_PWM_DUTY1	0x76
#define FINTEK_PWM_DUTY2	0x86

/* Options passed via the 'flags' config keyword. */
#define FINTEK_OPTION_FULLSPEED	0x0001

int
fintek_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "f75375") == 0)
		return (1);
	return (0);
}

int
fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
    size_t size)
{
	return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
}

int
fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
    size_t size)
{
	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
}

void
fintek_attach(struct device *parent, struct device *self, void *aux)
{
	struct fintek_softc *sc = (struct fintek_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
#ifndef SMALL_KERNEL
	int i;
#endif

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = FINTEK_VERSION;
	if (fintek_read_reg(sc, cmd, &data, sizeof data))
		goto failread;

	printf(": F75375 rev %d.%d", data>> 4, data & 0xf);

	/*
	 * It seems the fan in the Thecus n2100 doesn't provide a
	 * reliable fan count.  As a result the automatic fan
	 * controlling mode that the chip comes up in after reset
	 * doesn't work reliably.  So we have a flag to drive the fan
	 * at maximum voltage such that the box doesn't overheat.
	 */
	if (sc->sc_dev.dv_cfdata->cf_flags & FINTEK_OPTION_FULLSPEED)
		fintek_fullspeed(sc);

	iic_release_bus(sc->sc_tag, 0);

#ifndef SMALL_KERNEL
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[F_VCC].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[F_VCC].desc, "Vcc",
	    sizeof(sc->sc_sensor[F_VCC].desc));

	sc->sc_sensor[F_V1].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[F_V2].type = SENSOR_VOLTS_DC;
	sc->sc_sensor[F_V3].type = SENSOR_VOLTS_DC;

	sc->sc_sensor[F_TEMP1].type = SENSOR_TEMP;
	sc->sc_sensor[F_TEMP2].type = SENSOR_TEMP;

	sc->sc_sensor[F_FAN1].type = SENSOR_FANRPM;
	sc->sc_sensor[F_FAN2].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, fintek_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < F_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);
#endif

	printf("\n");
	return;

failread:
	printf("unable to read reg %d\n", cmd);
	iic_release_bus(sc->sc_tag, 0);
	return;
}

#ifndef SMALL_KERNEL
struct {
	char		sensor;
	u_int8_t	cmd;
} fintek_worklist[] = {
	{ F_VCC, FINTEK_VOLT0 },
	{ F_V1, FINTEK_VOLT1 },
	{ F_V2, FINTEK_VOLT2 },
	{ F_V3, FINTEK_VOLT3 },
	{ F_TEMP1, FINTEK_TEMP1 },
	{ F_TEMP2, FINTEK_TEMP2 },
	{ F_FAN1, FINTEK_FAN1 },
	{ F_FAN2, FINTEK_FAN2 }
};
#define FINTEK_WORKLIST_SZ (sizeof(fintek_worklist) / sizeof(fintek_worklist[0]))

void
fintek_refresh(void *arg)
{
	struct fintek_softc *sc =  arg;
	u_int8_t cmd, data, data2;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i < FINTEK_WORKLIST_SZ; i++){
		cmd = fintek_worklist[i].cmd;
		if (fintek_read_reg(sc, cmd, &data, sizeof data)) {
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			continue;
		}
		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
		switch (fintek_worklist[i].sensor) {
		case  F_VCC:
			sc->sc_sensor[i].value = data * 16000;
			break;
		case  F_V1:
			/* FALLTHROUGH */
		case  F_V2:
			/* FALLTHROUGH */
		case  F_V3:
			sc->sc_sensor[i].value = data * 8000;
			break;
		case  F_TEMP1:
			/* FALLTHROUGH */
		case  F_TEMP2:
			sc->sc_sensor[i].value = 273150000 + data * 1000000;
			break;
		case  F_FAN1:
			/* FALLTHROUGH */
		case  F_FAN2:
			/* FANx LSB follows FANx MSB */
			cmd = fintek_worklist[i].cmd + 1;
			if (fintek_read_reg(sc, cmd, &data2, sizeof data2)) {
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
				continue;
			}
			if ((data == 0xff && data2 == 0xff) ||
			    (data == 0 && data2 == 0))
				sc->sc_sensor[i].value = 0;
			else
				sc->sc_sensor[i].value = 1500000 /
				    (data << 8 | data2);
			break;
		default:
			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			break;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}
#endif

void
fintek_fullspeed(struct fintek_softc *sc)
{
	u_int8_t data;

	data = FINTEK_FAN1_LINEAR_MODE | FINTEK_FAN2_LINEAR_MODE;
	fintek_write_reg(sc, FINTEK_CONFIG1, &data, sizeof data);

	data = FINTEK_FAN1_MODE_MANUAL | FINTEK_FAN2_MODE_MANUAL;
	fintek_write_reg(sc, FINTEK_RSTCR, &data, sizeof data);

	data = 0xff;		/* Maximum voltage */
	fintek_write_reg(sc, FINTEK_PWM_DUTY1, &data, sizeof data);
	fintek_write_reg(sc, FINTEK_PWM_DUTY2, &data, sizeof data);
}


code:
/*	$OpenBSD: gl518sm.c,v 1.6 2007/06/24 05:34:35 dlg Exp $	*/

/*
 * Copyright (c) 2006 Mark Kettenis
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* GL518SM registers */
#define GL518SM_CHIPID		0x00
#define GL518SM_REVISION	0x01
#define GL518SM_VENDORID	0x02
#define GL518SM_CONFIG		0x03
#define  GL518SM_CONFIG_START		0x40
#define  GL518SM_CONFIG_CLEARST		0x20
#define  GL518SM_CONFIG_NOFAN2		0x10
#define GL518SM_TEMP		0x04
#define GL518SM_TEMP_OVER	0x05
#define GL518SM_TEMP_HYST	0x06
#define GL518SM_FAN_COUNT	0x07
#define GL518SM_FAN_LIMIT	0x08
#define GL518SM_VIN1_LIMIT	0x09
#define GL518SM_VIN2_LIMIT	0x0a
#define GL518SM_VIN3_LIMIT	0x0b
#define GL518SM_VDD_LIMIT	0x0c
#define GL518SM_VOLTMETER	0x0d
#define GL518SM_MISC		0x0f
#define GL518SM_ALARM		0x10
#define GL518SM_MASK		0x11
#define GL518SM_INTSTAT		0x12

/* Sensors */
#define GLENV_VIN3		0
#define GLENV_TEMP		1
#define GLENV_FAN1		2
#define GLENV_FAN2		3
#define GLENV_NUM_SENSORS	4

struct glenv_softc {
	struct device sc_dev;

	i2c_tag_t sc_tag;
	i2c_addr_t sc_addr;

	struct ksensor sc_sensor[GLENV_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int	sc_fan1_div, sc_fan2_div;
};

int	glenv_match(struct device *, void *, void *);
void	glenv_attach(struct device *, struct device *, void *);

void	glenv_refresh(void *);

struct cfattach glenv_ca = {
	sizeof(struct glenv_softc), glenv_match, glenv_attach
};

struct cfdriver glenv_cd = {
	NULL, "glenv", DV_DULL
};

int
glenv_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "gl518sm") == 0)
		return (1);
	return (0);
}

void
glenv_attach(struct device *parent, struct device *self, void *aux)
{
	struct glenv_softc *sc = (struct glenv_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = GL518SM_REVISION;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot read revision register\n");
		return;
	}

	printf(": GL518SM rev 0x%02x", data);

	cmd = GL518SM_MISC;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(", cannot read misc register\n");
		return;
	}
	sc->sc_fan1_div = 1 << ((data >> 6) & 0x03);
	sc->sc_fan2_div = 1 << ((data >> 4) & 0x03);

	cmd = GL518SM_CONFIG;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(", cannot read configuration register\n");
		return;
	}
	if (data & GL518SM_CONFIG_NOFAN2)
		sc->sc_fan2_div = 0;

	/* Start monitoring and clear interrupt status. */
	data = (data | GL518SM_CONFIG_START | GL518SM_CONFIG_CLEARST);
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot write configuration register\n");
			return;
	}

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[GLENV_VIN3].type = SENSOR_VOLTS_DC;

	sc->sc_sensor[GLENV_TEMP].type = SENSOR_TEMP;

	sc->sc_sensor[GLENV_FAN1].type = SENSOR_FANRPM;

	sc->sc_sensor[GLENV_FAN2].type = SENSOR_FANRPM;
	if (sc->sc_fan2_div == -1)
		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;

	if (sensor_task_register(sc, glenv_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < GLENV_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
glenv_refresh(void *arg)
{
	struct glenv_softc *sc = arg;
	u_int8_t cmd, data, data2[2];
	u_int tmp;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = GL518SM_VOLTMETER;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		sc->sc_sensor[GLENV_VIN3].flags |= SENSOR_FINVALID;
	} else {
		sc->sc_sensor[GLENV_VIN3].flags &= ~SENSOR_FINVALID;
		sc->sc_sensor[GLENV_VIN3].value = data * 19000;
	}

	cmd = GL518SM_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		sc->sc_sensor[GLENV_TEMP].flags |= SENSOR_FINVALID;
	} else {
		sc->sc_sensor[GLENV_TEMP].flags &= ~SENSOR_FINVALID;
		sc->sc_sensor[GLENV_TEMP].value =
			(data - 119) * 1000000 + 273150000;
	}

	cmd = GL518SM_FAN_COUNT;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
		sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
	} else {
		sc->sc_sensor[GLENV_FAN1].flags &= ~SENSOR_FINVALID;
		tmp = data2[0] * sc->sc_fan1_div * 2;
		if (tmp == 0)
			sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[GLENV_FAN1].value = 960000 / tmp;

		sc->sc_sensor[GLENV_FAN2].flags &= ~SENSOR_FINVALID;
		tmp = data2[1] * sc->sc_fan2_div * 2;
		if (tmp == 0)
			sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[GLENV_FAN2].value = 960000 / tmp;
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: i2c.c,v 1.16 2015/03/14 03:38:47 jsg Exp $	*/
/*	$NetBSD: i2c.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/event.h>

#define _I2C_PRIVATE
#include <dev/i2c/i2cvar.h>

#define IICCF_ADDR	0
#define IICCF_SIZE	1

struct iic_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
};

int	iic_match(struct device *, void *, void *);
void	iic_attach(struct device *, struct device *, void *);
int	iic_search(struct device *, void *, void *);

struct cfattach iic_ca = {
	sizeof (struct iic_softc),
	iic_match,
	iic_attach
};

struct cfdriver iic_cd = {
	NULL, "iic", DV_DULL
};

int
iicbus_print(void *aux, const char *pnp)
{
	struct i2cbus_attach_args *iba = aux;

	if (pnp != NULL)
		printf("%s at %s", iba->iba_name, pnp);

	return (UNCONF);
}

int
iic_print(void *aux, const char *pnp)
{
	struct i2c_attach_args *ia = aux;

	if (pnp != NULL)
		printf("\"%s\" at %s", ia->ia_name, pnp);
	printf(" addr 0x%x", ia->ia_addr);

	return (UNCONF);
}

int
iic_search(struct device *parent, void *arg, void *aux)
{
	struct iic_softc *sc = (void *) parent;
	struct cfdata *cf = arg;
	struct i2c_attach_args ia;

	if (cf->cf_loc[IICCF_ADDR] != -1) {
		memset(&ia, 0, sizeof(ia));
		ia.ia_tag = sc->sc_tag;
		ia.ia_addr = cf->cf_loc[IICCF_ADDR];
		ia.ia_size = cf->cf_loc[IICCF_SIZE];
		ia.ia_name = "unknown";

		if (cf->cf_attach->ca_match(parent, cf, &ia) > 0)
			config_attach(parent, cf, &ia, iic_print);
	}
	return (0);
}

int
iic_match(struct device *parent, void *arg, void *aux)
{
	struct cfdata *cf = arg;
	struct i2cbus_attach_args *iba = aux;

	/* Just make sure we're looking for i2c. */
	return (strcmp(iba->iba_name, cf->cf_driver->cd_name) == 0);
}

void
iic_attach(struct device *parent, struct device *self, void *aux)
{
	struct iic_softc *sc = (void *) self;
	struct i2cbus_attach_args *iba = aux;

	sc->sc_tag = iba->iba_tag;

	printf("\n");

	/*
	 * Attach all i2c devices described in the kernel
	 * configuration file.
	 */
	config_search(iic_search, self, NULL);

	/*
	 * Scan for known device signatures.
	 */
	if (iba->iba_bus_scan)
		(iba->iba_bus_scan)(self, aux, iba->iba_bus_scan_arg);
	else
		iic_scan(self, aux);
}


code:
/*	$OpenBSD: i2c_bitbang.c,v 1.4 2013/04/20 14:27:09 kettenis Exp $	*/
/*	$NetBSD: i2c_bitbang.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

/*
 * Common module for bit-bang'ing an I2C bus.
 */

#include <sys/param.h>

#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>

#define	BB_SET(x)	ops->ibo_set_bits(v, (x))
#define	BB_DIR(x)	ops->ibo_set_dir(v, (x))
#define	BB_READ		ops->ibo_read_bits(v)

#define	SDA		ops->ibo_bits[I2C_BIT_SDA]	/* i2c signal */
#define	SCL		ops->ibo_bits[I2C_BIT_SCL]	/* i2c signal */
#define	OUTPUT		ops->ibo_bits[I2C_BIT_OUTPUT]	/* SDA is output */
#define	INPUT		ops->ibo_bits[I2C_BIT_INPUT]	/* SDA is input */

#define SCL_BAIL_COUNT	1000

int i2c_wait_for_scl(void *, i2c_bitbang_ops_t);

int
i2c_wait_for_scl(void *v, i2c_bitbang_ops_t ops)
{
	int bail = 0;

	while(((BB_READ & SCL) == 0) && bail < SCL_BAIL_COUNT) {
		delay(1);
		bail++;
	}
	if (bail == SCL_BAIL_COUNT) {
		i2c_bitbang_send_stop(v, 0, ops);
		return (EIO);
	}

	return (0);
}

/*ARGSUSED*/
int
i2c_bitbang_send_start(void *v, int flags, i2c_bitbang_ops_t ops)
{

	BB_DIR(OUTPUT);

	BB_SET(SDA | SCL);
	delay(5);		/* bus free time (4.7 uS) */
	BB_SET(      SCL);
	if (i2c_wait_for_scl(v, ops) != 0)
		return (EIO);
	delay(4);		/* start hold time (4.0 uS) */
	BB_SET(        0);
	delay(5);		/* clock low time (4.7 uS) */

	return (0);
}

/*ARGSUSED*/
int
i2c_bitbang_send_stop(void *v, int flags, i2c_bitbang_ops_t ops)
{

	BB_DIR(OUTPUT);

	BB_SET(      SCL);
	delay(4);		/* stop setup time (4.0 uS) */
	BB_SET(SDA | SCL);

	return (0);
}

int
i2c_bitbang_initiate_xfer(void *v, i2c_addr_t addr, int flags,
    i2c_bitbang_ops_t ops)
{
	int i2caddr;

	/* XXX Only support 7-bit addressing for now. */
	if ((addr & 0x78) == 0x78)
		return (EINVAL);

	i2caddr = (addr << 1) | ((flags & I2C_F_READ) ? 1 : 0);

	(void) i2c_bitbang_send_start(v, flags, ops);
	return (i2c_bitbang_write_byte(v, i2caddr, flags & ~I2C_F_STOP, ops));
}

int
i2c_bitbang_read_byte(void *v, uint8_t *valp, int flags,
    i2c_bitbang_ops_t ops)
{
	int i;
	uint8_t val = 0;
	uint32_t bit;

	BB_DIR(INPUT);
	BB_SET(SDA      );

	for (i = 0; i < 8; i++) {
		val <<= 1;
		BB_SET(SDA | SCL);
		if (i2c_wait_for_scl(v, ops) != 0)
			return (EIO);
		delay(4);	/* clock high time (4.0 uS) */
		if (BB_READ & SDA)
			val |= 1;
		BB_SET(SDA      );
		delay(5);	/* clock low time (4.7 uS) */
	}

	bit = (flags & I2C_F_LAST) ? SDA : 0;
	BB_DIR(OUTPUT);
	BB_SET(bit      );
	delay(1);	/* data setup time (250 nS) */
	BB_SET(bit | SCL);
	if (i2c_wait_for_scl(v, ops) != 0)
		return (EIO);
	delay(4);	/* clock high time (4.0 uS) */
	BB_SET(bit      );
	delay(5);	/* clock low time (4.7 uS) */

	BB_DIR(INPUT);
	BB_SET(SDA      );
	delay(5);

	if ((flags & (I2C_F_STOP | I2C_F_LAST)) == (I2C_F_STOP | I2C_F_LAST))
		(void) i2c_bitbang_send_stop(v, flags, ops);

	*valp = val;
	return (0);
}

int
i2c_bitbang_write_byte(void *v, uint8_t val, int flags,
    i2c_bitbang_ops_t ops)
{
	uint32_t bit;
	uint8_t mask;
	int error;

	BB_DIR(OUTPUT);

	for (mask = 0x80; mask != 0; mask >>= 1) {
		bit = (val & mask) ? SDA : 0;
		BB_SET(bit      );
		delay(1);	/* data setup time (250 nS) */
		BB_SET(bit | SCL);
		if (i2c_wait_for_scl(v, ops) != 0)
			return (EIO);
		delay(4);	/* clock high time (4.0 uS) */
		BB_SET(bit      );
		delay(5);	/* clock low time (4.7 uS) */
	}

	BB_DIR(INPUT);

	BB_SET(SDA      );
	delay(5);
	BB_SET(SDA | SCL);
	if (i2c_wait_for_scl(v, ops) != 0)
		return (EIO);
	delay(4);
	error = (BB_READ & SDA) ? EIO : 0;
	BB_SET(SDA      );
	delay(5);

	if (flags & I2C_F_STOP)
		(void) i2c_bitbang_send_stop(v, flags, ops);

	return (error);
}


code:
/*	$OpenBSD: i2c_bitbang.h,v 1.3 2006/01/13 23:56:46 grange Exp $	*/
/*	$NetBSD: i2c_bitbang.h,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

#ifndef _DEV_I2C_I2C_BITBANG_H_
#define	_DEV_I2C_I2C_BITBANG_H_

#define	I2C_BIT_SDA		0	/* SDA signal */
#define	I2C_BIT_SCL		1	/* SCL signal */
#define	I2C_BIT_OUTPUT		2	/* controller: SDA is output */
#define	I2C_BIT_INPUT		3	/* controller: SDA is input */
#define	I2C_NBITS		4

struct i2c_bitbang_ops {
	void		(*ibo_set_bits)(void *, uint32_t);
	void		(*ibo_set_dir)(void *, uint32_t);
	uint32_t	(*ibo_read_bits)(void *);
	uint32_t	ibo_bits[I2C_NBITS];
};

typedef const struct i2c_bitbang_ops *i2c_bitbang_ops_t;

int	i2c_bitbang_send_start(void *, int, i2c_bitbang_ops_t);
int	i2c_bitbang_send_stop(void *, int, i2c_bitbang_ops_t);
int	i2c_bitbang_initiate_xfer(void *, i2c_addr_t, int, i2c_bitbang_ops_t);
int	i2c_bitbang_read_byte(void *, uint8_t *, int, i2c_bitbang_ops_t);
int	i2c_bitbang_write_byte(void *, uint8_t, int, i2c_bitbang_ops_t);

#endif /* _DEV_I2C_I2C_BITBANG_H_ */


code:
/*	$OpenBSD: i2c_exec.c,v 1.3 2015/03/14 03:38:47 jsg Exp $	*/
/*	$NetBSD: i2c_exec.c,v 1.3 2003/10/29 00:34:58 mycroft Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/event.h>

#define	_I2C_PRIVATE
#include <dev/i2c/i2cvar.h>

/*
 * iic_exec:
 *
 *	Simplified I2C client interface engine.
 *
 *	This and the SMBus routines are the preferred interface for
 *	client access to I2C/SMBus, since many automated controllers
 *	do not provide access to the low-level primitives of the I2C
 *	bus protocol.
 */
int
iic_exec(i2c_tag_t tag, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
    size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
	const uint8_t *cmd = vcmd;
	uint8_t *buf = vbuf;
	int error;
	size_t len;

	/*
	 * Defer to the controller if it provides an exec function.  Use
	 * it if it does.
	 */
	if (tag->ic_exec != NULL)
		return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd,
					cmdlen, buf, buflen, flags));

	if ((len = cmdlen) != 0) {
		if ((error = iic_initiate_xfer(tag, addr, flags)) != 0)
			goto bad;
		while (len--) {
			if ((error = iic_write_byte(tag, *cmd++, flags)) != 0)
				goto bad;
		}
	}

	if (I2C_OP_READ_P(op))
		flags |= I2C_F_READ;

	len = buflen;
	while (len--) {
		if (len == 0 && I2C_OP_STOP_P(op))
			flags |= I2C_F_STOP;
		if (I2C_OP_READ_P(op)) {
			/* Send REPEATED START. */
			if ((len + 1) == buflen &&
			    (error = iic_initiate_xfer(tag, addr, flags)) != 0)
				goto bad;
			/* NACK on last byte. */
			if (len == 0)
				flags |= I2C_F_LAST;
			if ((error = iic_read_byte(tag, buf++, flags)) != 0)
				goto bad;
		} else  {
			/* Maybe send START. */
			if ((len + 1) == buflen && cmdlen == 0 &&
			    (error = iic_initiate_xfer(tag, addr, flags)) != 0)
				goto bad;
			if ((error = iic_write_byte(tag, *buf++, flags)) != 0)
				goto bad;
		}
	}

	return (0);
 bad:
	iic_send_stop(tag, flags);
	return (error);
}

/*
 * iic_smbus_write_byte:
 *
 *	Perform an SMBus "write byte" operation.
 */
int
iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    uint8_t val, int flags)
{

	return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr,
	    &cmd, sizeof cmd, &val, sizeof val, flags));
}

/*
 * iic_smbus_read_byte:
 *
 *	Perform an SMBus "read byte" operation.
 */
int
iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    uint8_t *valp, int flags)
{

	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr,
	    &cmd, sizeof cmd, valp, sizeof (*valp), flags));
}

/*
 * iic_smbus_receive_byte:
 *
 *	Perform an SMBus "receive byte" operation.
 */
int
iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp,
    int flags)
{

	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr,
	    NULL, 0, valp, sizeof (*valp), flags));
}


code:
/*	$OpenBSD: i2c_io.h,v 1.1 2004/05/23 17:33:43 grange Exp $	*/
/*	$NetBSD: i2c_io.h,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

#ifndef _DEV_I2C_I2C_IO_H_
#define	_DEV_I2C_I2C_IO_H_

#include <sys/ioccom.h>

/* I2C bus address. */
typedef uint16_t i2c_addr_t;

/* High-level I2C operations. */
typedef enum {
	I2C_OP_READ		= 0,
	I2C_OP_READ_WITH_STOP	= 1,
	I2C_OP_WRITE		= 2,
	I2C_OP_WRITE_WITH_STOP	= 3,
} i2c_op_t;

#define	I2C_OP_READ_P(x)	(((int)(x) & 2) == 0)
#define	I2C_OP_WRITE_P(x)	(! I2C_OP_READ_P(x))
#define	I2C_OP_STOP_P(x)	(((int)(x) & 1) != 0)

/*
 * This structure describes a single I2C control script fragment.
 *
 * Note that use of this scripted API allows for support of automated
 * SMBus controllers.  The following table maps SMBus operations to
 * script fragment configuration:
 *
 *	SMBus "write byte":		I2C_OP_WRITE_WITH_STOP
 *					cmdlen = 1
 *
 *	SMBus "read byte":		I2C_OP_READ_WITH_STOP
 *					cmdlen = 1
 *
 *	SMBus "receive byte":		I2C_OP_READ_WITH_STOP
 *					cmdlen = 0
 *
 * Note that while each of these 3 SMBus operations implies a STOP
 * (which an automated controller will likely perform automatically),
 * non-SMBus clients may continue to function even if they issue
 * I2C_OP_WRITE and I2C_OP_READ.
 */

#ifdef notyet
/*
 * I2C_IOCTL_EXEC:
 *
 *	User ioctl to execute an i2c operation.
 */
typedef struct i2c_ioctl_exec {
	i2c_op_t iie_op;		/* operation to perform */
	i2c_addr_t iie_addr;		/* address of device */
	const void *iie_cmd;		/* pointer to command */
	size_t iie_cmdlen;		/* length of command */
	void *iie_buf;			/* pointer to data buffer */
	size_t iie_buflen;		/* length of data buffer */
} i2c_ioctl_exec_t;
#define	I2C_EXEC_MAX_CMDLEN	32
#define	I2C_EXEC_MAX_BUFLEN	32

#define	I2C_IOCTL_EXEC		 _IOW('I', 0, i2c_ioctl_exec_t)
#endif

#endif /* _DEV_I2C_I2C_IO_H_ */