code:
/*	$OpenBSD: i2c_scan.c,v 1.145 2015/05/29 00:37:10 uebayasi Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt <deraadt@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.
 */

/*
 * I2C bus scanning.  We apologize in advance for the massive overuse of 0x.
 */

#include "ipmi.h"

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

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

#undef I2C_DEBUG
#define I2C_VERBOSE

#define MAX_IGNORE 8
u_int8_t ignore_addrs[MAX_IGNORE];

struct iicprobelist {
	u_int8_t start, end;
};

/*
 * Addresses at which to probe for sensors.  Skip address 0x4f, since
 * probing it seems to crash at least one Sony VAIO laptop.  Only a
 * few chips can actually sit at that address, and vendors seem to
 * place those at other addresses, so this isn't a big loss.
 */
struct iicprobelist probe_addrs_sensor[] = {
	{ 0x18, 0x1f },
	{ 0x20, 0x2f },
	{ 0x48, 0x4e },
	{ 0, 0 }
};

/*
 * Addresses at which to probe for eeprom devices.
 */
struct iicprobelist probe_addrs_eeprom[] = {
	{ 0x50, 0x57 },
	{ 0, 0 }
};

char 	*iic_probe_sensor(struct device *, u_int8_t);
char	*iic_probe_eeprom(struct device *, u_int8_t);

#define PFLAG_SENSOR	1
static struct {
	struct iicprobelist *pl;
	char	*(*probe)(struct device *, u_int8_t);
	int	flags;
} probes[] = {
	{ probe_addrs_sensor, iic_probe_sensor, PFLAG_SENSOR },
	{ probe_addrs_eeprom, iic_probe_eeprom, 0 },
	{ NULL, NULL }
};

/*
 * Some Maxim 1617 clones MAY NOT even read cmd 0xfc!  When it is
 * read, they will power-on-reset.  Their default condition
 * (control register bit 0x80) therefore will be that they assert
 * /ALERT for the 5 potential errors that may occur.  One of those
 * errors is that the external temperature diode is missing.  This
 * is unfortunately a common choice of system designers, except
 * suddenly now we get a /ALERT, which may on some chipsets cause
 * us to receive an entirely unexpected SMI .. and then an NMI.
 *
 * As we probe each device, if we hit something which looks suspiciously
 * like it may potentially be a 1617 or clone, we immediately set this
 * variable to avoid reading that register offset.
 */
int	skip_fc;

static i2c_tag_t probe_ic;
static u_int8_t probe_addr;
static u_int8_t probe_val[256];

void		iicprobeinit(struct i2cbus_attach_args *, u_int8_t);
u_int8_t	iicprobenc(u_int8_t);
u_int8_t	iicprobe(u_int8_t);
u_int16_t	iicprobew(u_int8_t);
char		*lm75probe(void);
char		*adm1032cloneprobe(u_int8_t);
void		iic_dump(struct device *, u_int8_t, char *);

void
iicprobeinit(struct i2cbus_attach_args *iba, u_int8_t addr)
{
	probe_ic = iba->iba_tag;
	probe_addr = addr;
	memset(probe_val, 0xff, sizeof probe_val);
}

u_int8_t
iicprobenc(u_int8_t cmd)
{
	u_int8_t data;

	/*
	 * If we think we are talking to an evil Maxim 1617 or clone,
	 * avoid accessing this register because it is death.
	 */
	if (skip_fc && cmd == 0xfc)
		return (0xff);
	iic_acquire_bus(probe_ic, 0);
	if (iic_exec(probe_ic, I2C_OP_READ_WITH_STOP,
	    probe_addr, &cmd, sizeof cmd, &data, sizeof data, 0) != 0)
		data = 0xff;
	iic_release_bus(probe_ic, 0);
	return (data);
}

u_int16_t
iicprobew(u_int8_t cmd)
{
	u_int16_t data;

	/*
	 * If we think we are talking to an evil Maxim 1617 or clone,
	 * avoid accessing this register because it is death.
	 */
	if (skip_fc && cmd == 0xfc)
		return (0xffff);
	iic_acquire_bus(probe_ic, 0);
	if (iic_exec(probe_ic, I2C_OP_READ_WITH_STOP,
	    probe_addr, &cmd, sizeof cmd, &data, sizeof data, 0) != 0)
		data = 0xffff;
	iic_release_bus(probe_ic, 0);
	return betoh16(data);
}

u_int8_t
iicprobe(u_int8_t cmd)
{
	if (probe_val[cmd] != 0xff)
		return probe_val[cmd];
	probe_val[cmd] = iicprobenc(cmd);
	return (probe_val[cmd]);
}

#define LM75TEMP	0x00
#define LM75CONF	0x01
#define LM75Thyst	0x02
#define LM75Tos		0x03
#define LM77Tlow	0x04
#define LM77Thigh	0x05
#define LM75TMASK	0xff80	/* 9 bits in temperature registers */
#define LM77TMASK	0xfff8	/* 13 bits in temperature registers */

/*
 * The LM75/LM75A/LM77 family are very hard to detect.  Thus, we check
 * for all other possible chips first.  These chips do not have an
 * ID register.  They do have a few quirks though:
 * -  on the LM75 and LM77, registers 0x06 and 0x07 return whatever
 *    value was read before
 * -  the LM75 lacks registers 0x04 and 0x05, so those act as above
 * -  the LM75A returns 0xffff for registers 0x04, 0x05, 0x06 and 0x07
 * -  the chip registers loop every 8 registers
 * The downside is that we must read almost every register to guess
 * if this is an LM75, LM75A or LM77.
 */
char *
lm75probe(void)
{
	u_int16_t temp, thyst, tos, tlow, thigh, mask = LM75TMASK;
	u_int8_t conf;
	int i, echocount, ffffcount, score;
	int echoreg67, echoreg45, ffffreg67, ffffreg45;

	temp = iicprobew(LM75TEMP);

	/*
	 * Sometimes the other probes can upset the chip, if we get 0xffff
	 * the first time, try it once more.
	 */
	if (temp == 0xffff)
		temp = iicprobew(LM75TEMP);

	conf = iicprobenc(LM75CONF);
	thyst = iicprobew(LM75Thyst);
	tos = iicprobew(LM75Tos);

	/* totally bogus data */
	if (conf == 0xff && temp == 0xffff && thyst == 0xffff)
		return (NULL);

	temp &= mask;
	thyst &= mask;
	tos &= mask;

	/* All values the same?  Very unlikely */
	if (temp == thyst && thyst == tos)
		return (NULL);

#if notsure
	/* more register aliasing effects that indicate not a lm75 */
	if ((temp >> 8) == conf)
		return (NULL);
#endif

	/*
	 * LM77/LM75 registers 6, 7
	 * echo whatever was read just before them from reg 0, 1, or 2
	 *
	 * LM75A doesn't appear to do this, but does appear to reliably
	 * return 0xffff
	 */
	for (i = 6, echocount = 2, ffffcount = 0; i <= 7; i++) {
		if ((iicprobew(LM75TEMP) & mask) != (iicprobew(i) & mask) ||
		    (iicprobew(LM75Thyst) & mask) != (iicprobew(i) & mask) ||
		    (iicprobew(LM75Tos) & mask) != (iicprobew(i) & mask))
			echocount--;
		if (iicprobew(i) == 0xffff)
			ffffcount++;
	}

	/* Make sure either both registers echo, or neither does */
	if (echocount == 1 || ffffcount == 1)
		return (NULL);

	echoreg67 = (echocount == 0) ? 0 : 1;
	ffffreg67 = (ffffcount == 0) ? 0 : 1;

	/*
	 * LM75 has no registers 4 or 5, and they will act as echos too
	 *
	 * LM75A doesn't appear to do this either, but does appear to
	 * reliably return 0xffff
	 */
	for (i = 4, echocount = 2, ffffcount = 0; i <= 5; i++) {
		if ((iicprobew(LM75TEMP) & mask) != (iicprobew(i) & mask) ||
		    (iicprobew(LM75Thyst) & mask) != (iicprobew(i) & mask) ||
		    (iicprobew(LM75Tos) & mask) != (iicprobew(i) & mask))
			echocount--;
		if (iicprobew(i) == 0xffff)
			ffffcount++;
	}

	/* Make sure either both registers echo, or neither does */
	if (echocount == 1 || ffffcount == 1)
		return (NULL);

	echoreg45 = (echocount == 0) ? 0 : 1;
	ffffreg45 = (ffffcount == 0) ? 0 : 1;

	/*
	 * If we find that 4 and 5 are not echos, and don't return 0xffff
	 * then based on whether the echo test of registers 6 and 7
	 * succeeded or not, we may have an LM77
	 */
	if (echoreg45 == 0 && ffffreg45 == 0 && echoreg67 == 1) {
		mask = LM77TMASK;

		/* mask size changed, must re-read for the next checks */
		thyst = iicprobew(LM75Thyst) & mask;
		tos = iicprobew(LM75Tos) & mask;
		tlow = iicprobew(LM77Tlow) & mask;
		thigh = iicprobew(LM77Thigh) & mask;
	}

	/* a real LM75/LM75A/LM77 repeats its registers.... */
	for (i = 0x08; i <= 0xf8; i += 8) {
		if (conf != iicprobenc(LM75CONF + i) ||
		    thyst != (iicprobew(LM75Thyst + i) & mask) ||
		    tos != (iicprobew(LM75Tos + i) & mask))
			return (NULL);

		/*
		 * Check that the repeated registers 0x06 and 0x07 still
		 * either echo or return 0xffff
		 */
		if (echoreg67 == 1) {
			tos = iicprobew(LM75Tos) & mask;
			if (tos != (iicprobew(0x06 + i) & mask) ||
			    tos != (iicprobew(0x07 + i) & mask))
				return (NULL);
		} else if (ffffreg67 == 1)
			if (iicprobew(0x06 + i) != 0xffff ||
			    iicprobew(0x07 + i) != 0xffff)
				return (NULL);

		/*
		 * Check that the repeated registers 0x04 and 0x05 still
		 * either echo or return 0xffff. If they do neither, and
		 * registers 0x06 and 0x07 echo, then we will be probing
		 * for an LM77, so make sure those still repeat
		 */
		if (echoreg45 == 1) {
			tos = iicprobew(LM75Tos) & mask;
			if (tos != (iicprobew(LM77Tlow + i) & mask) ||
			    tos != (iicprobew(LM77Thigh + i) & mask))
				return (NULL);
		} else if (ffffreg45 == 1) {
			if (iicprobew(LM77Tlow + i) != 0xffff ||
			    iicprobew(LM77Thigh + i) != 0xffff)
				return (NULL);
		} else if (echoreg67 == 1)
			if (tlow != (iicprobew(LM77Tlow + i) & mask) ||
			    thigh != (iicprobew(LM77Thigh + i) & mask))
				return (NULL);
	}

	/*
	 * Given that we now know how the first eight registers behave and
	 * that this behaviour is consistently repeated, we can now use
	 * the following table:
	 *
	 * echoreg67 | echoreg45 | ffffreg67 | ffffreg45 | chip
	 * ----------+-----------+-----------+-----------+------
	 *     1     |     1     |     0     |     0     | LM75
	 *     1     |     0     |     0     |     0     | LM77
	 *     0     |     0     |     1     |     1     | LM75A
	 */

	/* Convert the various flags into a single score */
	score = (echoreg67 << 3) + (echoreg45 << 2) + (ffffreg67 << 1) +
	    ffffreg45;

	switch (score) {
	case 12:
		return ("lm75");
	case 8:
		return ("lm77");
	case 3:
		return ("lm75a");
	default:
#if defined(I2C_DEBUG)
		printf("lm75probe: unknown chip, scored %d\n", score);
#endif /* defined(I2C_DEBUG) */
		return (NULL);
	}
}

char *
adm1032cloneprobe(u_int8_t addr)
{
	if (addr == 0x18 || addr == 0x1a || addr == 0x29 ||
	    addr == 0x2b || addr == 0x4c || addr == 0x4e) {
		u_int8_t reg, val;
		int zero = 0, copy = 0;

		val = iicprobe(0x00);
		for (reg = 0x00; reg < 0x09; reg++) {
			if (iicprobe(reg) == 0xff)
				return (NULL);
			if (iicprobe(reg) == 0x00)
				zero++;
			if (val == iicprobe(reg))
				copy++;
		}
		if (zero > 6 || copy > 6)
			return (NULL);
		val = iicprobe(0x09);
		for (reg = 0x0a; reg < 0xfc; reg++) {
			if (iicprobe(reg) != val)
				return (NULL);
		}
		/* 0xfe may be Maxim, or some other vendor */
		if (iicprobe(0xfe) == 0x4d)
			return ("max1617");
		/*
		 * "xeontemp" is the name we choose for clone chips
		 * which have all sorts of buggy bus interactions, such
		 * as those we just probed.  Why?
		 * Intel is partly to blame for this situation.
		 */
		return ("xeontemp");
	}
	return (NULL);
}

void
iic_ignore_addr(u_int8_t addr)
{
	int i;

	for (i = 0; i < sizeof(ignore_addrs); i++)
		if (ignore_addrs[i] == 0) {
			ignore_addrs[i] = addr;
			return;
		}
}

#ifdef I2C_VERBOSE
void
iic_dump(struct device *dv, u_int8_t addr, char *name)
{
	static u_int8_t iicvalcnt[256];
	u_int8_t val, val2, max;
	int i, cnt = 0;

	/*
	 * Don't bother printing the most often repeated register
	 * value, since it is often weird devices that respond
	 * incorrectly, busted controller driver, or in the worst
	 * case, it in mosts cases, the value 0xff.
	 */
	bzero(iicvalcnt, sizeof iicvalcnt);
	val = iicprobe(0);
	iicvalcnt[val]++;
	for (i = 1; i <= 0xff; i++) {
		val2 = iicprobe(i);
		iicvalcnt[val2]++;
		if (val == val2)
			cnt++;
	}

	for (val = max = i = 0; i <= 0xff; i++)
		if (max < iicvalcnt[i]) {
			max = iicvalcnt[i];
			val = i;
		}

	if (cnt == 255)
		return;

	printf("%s: addr 0x%x", dv->dv_xname, addr);
	for (i = 0; i <= 0xff; i++) {
		if (iicprobe(i) != val)
			printf(" %02x=%02x", i, iicprobe(i));
	}
	printf(" words");
	for (i = 0; i < 8; i++)
		printf(" %02x=%04x", i, iicprobew(i));
	if (name)
		printf(": %s", name);
	printf("\n");
}
#endif /* I2C_VERBOSE */

char *
iic_probe_sensor(struct device *self, u_int8_t addr)
{
	char *name = NULL;

	skip_fc = 0;

	/*
	 * Many I2C/SMBus devices use register 0x3e as a vendor ID
	 * register.
	 */
	switch (iicprobe(0x3e)) {
	case 0x01:		/* National Semiconductor */
		/*
		 * Some newer National products use a vendor code at
		 * 0x3e of 0x01, and then 0x3f contains a product code
		 * But some older products are missing a product code,
		 * and contain who knows what in that register.  We assume
		 * that some employee was smart enough to keep the numbers
		 * unique.
		 */
		if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) == 0x73 || iicprobe(0x3f) == 0x72) &&
		    iicprobe(0x00) == 0x00)
			name = "lm93";	/* product 0x72 is the prototype */
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3f) == 0x68)
			name = "lm96000";	/* adt7460 compat? */
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) == 0x60 || iicprobe(0x3f) == 0x62))
			name = "lm85";		/* lm85C/B == adt7460 compat */
		else if ((addr & 0x7c) == 0x2c &&	/* addr 0b01011xx */
		    iicprobe(0x48) == addr &&
		    (iicprobe(0x3f) == 0x03 || iicprobe(0x3f) == 0x04) &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "lm81";
		break;
	case 0x02:		/* National Semiconductor? */
		if ((iicprobe(0x3f) & 0xfc) == 0x04)
			name = "lm87";		/* complete check */
		break;
	case 0x23:		/* Analog Devices? */
		if (iicprobe(0x48) == addr &&
		    (iicprobe(0x40) & 0x80) == 0x00 &&
		    (addr & 0x7c) == 0x2c)
			name = "adm9240";	/* lm87 clone */
		break;
	case 0x41:		/* Analog Devices */
		/*
		 * Newer chips have a valid 0x3d product number, while
		 * older ones sometimes encoded the product into the
		 * upper half of the "step register" at 0x3f.
		 */
		if ((addr == 0x2c || addr == 0x2e || addr == 0x2f) &&
		    iicprobe(0x3d) == 0x70)
			name = "adt7470";
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3d) == 0x76)
			name = "adt7476"; /* or adt7476a */
		else if (addr == 0x2e && iicprobe(0x3d) == 0x75)
			name = "adt7475";
		else if (iicprobe(0x3d) == 0x27 &&
		    (iicprobe(0x3f) == 0x60 || iicprobe(0x3f) == 0x6a))
			name = "adm1027";	/* or adt7463 */
		else if (iicprobe(0x3d) == 0x27 &&
		    (iicprobe(0x3f) == 0x62 || iicprobe(0x3f) == 0x6a))
			name = "adt7460";	/* complete check */
		else if ((addr == 0x2c || addr == 0x2e) &&
		    iicprobe(0x3d) == 0x62 && iicprobe(0x3f) == 0x04)
			name = "adt7462";
		else if (addr == 0x4c &&
		    iicprobe(0x3d) == 0x66 && iicprobe(0x3f) == 0x02)
			name = "adt7466";
		else if (addr == 0x2e &&
		    iicprobe(0x3d) == 0x68 && (iicprobe(0x3f) & 0xf0) == 0x70)
			name = "adt7467"; /* or adt7468 */
		else if (iicprobe(0x3d) == 0x33 && iicprobe(0x3f) == 0x02)
			name = "adm1033";
		else if (iicprobe(0x3d) == 0x34 && iicprobe(0x3f) == 0x02)
			name = "adm1034";
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3d) == 0x30 &&
		    (iicprobe(0x01) & 0x80) == 0x00 &&
		    (iicprobe(0x0d) & 0x70) == 0x00 &&
		    (iicprobe(0x0e) & 0x70) == 0x00)
			/*
			 * Revision 3 seems to be an adm1031 with
			 * remote diode 2 shorted.  Therefore we
			 * cannot assume the reserved/unused bits of
			 * register 0x03 and 0x06 are set to zero.
			 */
			name = "adm1030";	/* complete check */
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3d) == 0x31 &&
		    (iicprobe(0x01) & 0x80) == 0x00 &&
		    (iicprobe(0x0d) & 0x70) == 0x00 &&
		    (iicprobe(0x0e) & 0x70) == 0x00 &&
		    (iicprobe(0x0f) & 0x70) == 0x00)
			name = "adm1031";	/* complete check */
		else if ((addr & 0x7c) == 0x2c &&	/* addr 0b01011xx */
		    (iicprobe(0x3f) & 0xf0) == 0x20 &&
		    (iicprobe(0x40) & 0x80) == 0x00 &&
		    (iicprobe(0x41) & 0xc0) == 0x00 &&
		    (iicprobe(0x42) & 0xbc) == 0x00)
			name = "adm1025";	/* complete check */
		else if ((addr & 0x7c) == 0x2c &&	/* addr 0b01011xx */
		    (iicprobe(0x3f) & 0xf0) == 0x10 &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "adm1024";	/* complete check */
		else if ((iicprobe(0xff) & 0xf0) == 0x30)
			name = "adm1023";
		else if (addr == 0x2e &&
		    (iicprobe(0x3f) & 0xf0) == 0xd0 &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "adm1028";	/* adm1022 clone? */
		else if ((addr == 0x2c || addr == 0x2e || addr == 0x2f) &&
		    (iicprobe(0x3f) & 0xf0) == 0xc0 &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "adm1022";
		break;
	case 0x49:		/* Texas Instruments */
		if ((addr == 0x2c || addr == 0x2e || addr == 0x2f) &&
		    (iicprobe(0x3f) & 0xf0) == 0xc0 &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "thmc50";	/* adm1022 clone */
		break;
	case 0x55:		/* SMSC */
		if ((addr & 0x7c) == 0x2c &&		/* addr 0b01011xx */
		    iicprobe(0x3f) == 0x20 &&
		    (iicprobe(0x47) & 0x70) == 0x00 &&
		    (iicprobe(0x49) & 0xfe) == 0x80)
			name = "47m192";	/* adm1025 compat */
		break;
	case 0x5c:		/* SMSC */
		if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) == 0x69))
			name = "sch5027";
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) & 0xf0) == 0x60)
			name = "emc6d100";   /* emc6d101, emc6d102, emc6d103 */
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) & 0xf0) == 0x80)
			name = "sch5017";
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    (iicprobe(0x3f) & 0xf0) == 0xb0)
			name = "emc6w201";
		break;
	case 0x61:		/* Andigilog */
		if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3f) == 0x69 &&
		    iicprobe(0x22) >= 0xaf &&		/* Vdd */
		    (iicprobe(0x09) & 0xbf) == 0x00 && iicprobe(0x0f) == 0x00 &&
		    (iicprobe(0x40) & 0xf0) == 0x00)
			name = "asc7611";
		else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
		    iicprobe(0x3f) == 0x6c &&
		    iicprobe(0x22) >= 0xae)		/* Vdd */
			name = "asc7621";
		break;
	case 0xa1:		/* Philips */
		if ((iicprobe(0x3f) & 0xf0) == 0x20 &&
		    (iicprobe(0x40) & 0x80) == 0x00 &&
		    (iicprobe(0x41) & 0xc0) == 0x00 &&
		    (iicprobe(0x42) & 0xbc) == 0x00)
			name = "ne1619";	/* adm1025 compat */
		break;
	case 0xda:		/* Dallas Semiconductor */
		if (iicprobe(0x3f) == 0x01 && iicprobe(0x48) == addr &&
		    (iicprobe(0x40) & 0x80) == 0x00)
			name = "ds1780";	/* lm87 clones */
		break;
	}

	switch (iicprobe(0x4e)) {
	case 0x41:		/* Analog Devices */
		if ((addr == 0x48 || addr == 0x4a || addr == 0x4b) &&
		    (iicprobe(0x4d) == 0x03 || iicprobe(0x4d) == 0x08 ||
		    iicprobe(0x4d) == 0x07))
			name = "adt7516";	/* adt7517, adt7519 */
		break;
	}

	switch (iicprobe(0xfe)) {
	case 0x01:		/* National Semiconductor */
		if (addr == 0x4c &&
		    iicprobe(0xff) == 0x41 && (iicprobe(0x03) & 0x18) == 0 &&
		    iicprobe(0x04) <= 0x0f && (iicprobe(0xbf) & 0xf8) == 0)
			name = "lm63";
		else if (addr == 0x4c &&
		    iicprobe(0xff) == 0x11 && (iicprobe(0x03) & 0x2a) == 0 &&
		    iicprobe(0x04) <= 0x09 && (iicprobe(0xbf) & 0xf8) == 0)
			name = "lm86";
		else if (addr == 0x4c &&
		    iicprobe(0xff) == 0x31 && (iicprobe(0x03) & 0x2a) == 0 &&
		    iicprobe(0x04) <= 0x09 && (iicprobe(0xbf) & 0xf8) == 0)
			name = "lm89";		/* or lm99 */
		else if (addr == 0x4d &&
		    iicprobe(0xff) == 0x34 && (iicprobe(0x03) & 0x2a) == 0 &&
		    iicprobe(0x04) <= 0x09 && (iicprobe(0xbf) & 0xf8) == 0)
			name = "lm89-1";	/* or lm99-1 */
		else if (addr == 0x4c &&
		    iicprobe(0xff) == 0x21 && (iicprobe(0x03) & 0x2a) == 0 &&
		    iicprobe(0x04) <= 0x09 && (iicprobe(0xbf) & 0xf8) == 0)
			name = "lm90";
		break;
	case 0x23:		/* Genesys Logic? */
		if ((addr == 0x4c) &&
		    (iicprobe(0x03) & 0x3f) == 0x00 && iicprobe(0x04) <= 0x08)
			/*
			 * Genesys Logic doesn't make the datasheet
			 * for the GL523SM publically available, so
			 * the checks above are nothing more than a
			 * (conservative) educated guess.
			 */
			name = "gl523sm";
		break;
	case 0x41:		/* Analog Devices */
		if ((addr == 0x4c || addr == 0x4d) &&
		    iicprobe(0xff) == 0x51 &&
		    (iicprobe(0x03) & 0x1f) == 0x04 &&
		    iicprobe(0x04) <= 0x0a) {
			/* If not in adm1032 compatibility mode. */
			name = "adt7461";
		} else if ((addr == 0x18 || addr == 0x19 || addr == 0x1a ||
		    addr == 0x29 || addr == 0x2a || addr == 0x2b ||
		    addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    (iicprobe(0xff) & 0xf0) == 0x00 &&
		    (iicprobe(0x03) & 0x3f) == 0x00 &&
		    iicprobe(0x04) <= 0x07) {
			name = "adm1021";
			skip_fc = 1;
		} else if ((addr == 0x18 || addr == 0x19 || addr == 0x1a ||
		    addr == 0x29 || addr == 0x2a || addr == 0x2b ||
		    addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    (iicprobe(0xff) & 0xf0) == 0x30 &&
		    (iicprobe(0x03) & 0x3f) == 0x00 &&
		    iicprobe(0x04) <= 0x07) {
			name = "adm1023";	/* or adm1021a */
			skip_fc = 1;
		} else if ((addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    (iicprobe(0x03) & 0x3f) == 0x00 &&
		    iicprobe(0x04) <= 0x0a) {
			name = "adm1032";	/* or adm1020 */
			skip_fc = 1;
		}
		break;
	case 0x47:		/* Global Mixed-mode Technology */
		if (addr == 0x4c && iicprobe(0xff) == 0x01 &&
		    (iicprobe(0x03) & 0x3f) == 0x00 && iicprobe(0x04) <= 0x08)
			name = "g781";
		if (addr == 0x4d && iicprobe(0xff) == 0x03 &&
		    (iicprobe(0x03) & 0x3f) == 0x00 && iicprobe(0x04) <= 0x08)
			name = "g781-1";
		break;
	case 0x4d:		/* Maxim */
		if ((addr == 0x18 || addr == 0x19 || addr == 0x1a ||
		     addr == 0x29 || addr == 0x2a || addr == 0x2b ||
		     addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    iicprobe(0xff) == 0x08 && (iicprobe(0x02) & 0x03) == 0 &&
		    (iicprobe(0x03) & 0x07) == 0 && iicprobe(0x04) <= 0x08)
			name = "max6690";
		else if ((addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    iicprobe(0xff) == 0x59 && (iicprobe(0x03) & 0x1f) == 0 &&
		    iicprobe(0x04) <= 0x07)
			name = "max6646";	/* max6647/8/9, max6692 */
		else if ((addr == 0x4c || addr == 0x4d || addr == 0x4e) &&
		    (iicprobe(0x02) & 0x2b) == 0 &&
		    (iicprobe(0x03) & 0x0f) == 0 && iicprobe(0x04) <= 0x09) {
			name = "max6657";	/* max6658, max6659 */
			skip_fc = 1;
		} else if ((addr >= 0x48 && addr <= 0x4f) &&
		    (iicprobe(0x02) & 0x2b) == 0 &&
		    (iicprobe(0x03) & 0x0f) == 0)
			name = "max6642";
		break;
	case 0x55:		/* Texas Instruments */
		if (addr == 0x4c && iicprobe(0xff) == 0x11 &&
		    (iicprobe(0x03) & 0x1b) == 0x00 &&
		    (iicprobe(0x04) & 0xf0) == 0x00 &&
		    (iicprobe(0x10) & 0x0f) == 0x00 &&
		    (iicprobe(0x13) & 0x0f) == 0x00 &&
		    (iicprobe(0x14) & 0x0f) == 0x00 &&
		    (iicprobe(0x15) & 0x0f) == 0x00 &&
		    (iicprobe(0x16) & 0x0f) == 0x00 &&
		    (iicprobe(0x17) & 0x0f) == 0x00)
			name = "tmp401";
		break;
	case 0xa1:
		if ((addr >= 0x48 && addr <= 0x4f) &&
		    iicprobe(0xff) == 0x00 &&
		    (iicprobe(0x03) & 0xf8) == 0x00 &&
		    iicprobe(0x04) <= 0x09) {
			name = "sa56004x";	/* NXP sa56004x */
			skip_fc = 1;
		}
		break;
	}

	if (addr == iicprobe(0x48) &&
	    ((iicprobe(0x4f) == 0x5c && (iicprobe(0x4e) & 0x80)) ||
	    (iicprobe(0x4f) == 0xa3 && !(iicprobe(0x4e) & 0x80)))) {
		/*
		 * We could toggle 0x4e bit 0x80, then re-read 0x4f to
		 * see if the value changes to 0xa3 (indicating Winbond).
		 * But we are trying to avoid writes.
		 */
		if ((iicprobe(0x4e) & 0x07) == 0) {
			switch (iicprobe(0x58)) {
			case 0x10:
			case 0x11:			/* rev 2? */
				name = "w83781d";
				break;
			case 0x21:
				name = "w83627hf";
				break;
			case 0x30:
				name = "w83782d";
				break;
			case 0x31:
				name = "as99127f";	/* rev 2 */
				break;
			case 0x40:
				name = "w83783s";
				break;
			case 0x71:
				name = "w83791d";
				break;
			case 0x72:
				name = "w83791sd";
				break;
			case 0x7a:
				name = "w83792d";
				break;
			case 0xc1:
				name = "w83627dhg";
				break;
			}
		} else {
			/*
			 * The BIOS left the chip in a non-zero
			 * register bank.  Assume it's a W83781D and
			 * let lm(4) sort out the real model.
			 */
			name = "w83781d";
		}
	} else if (addr == (iicprobe(0xfc) & 0x7f) &&
	    iicprobe(0xfe) == 0x79 && iicprobe(0xfb) == 0x51 &&
	    ((iicprobe(0xfd) == 0x5c && (iicprobe(0x00) & 0x80)) ||
	    (iicprobe(0xfd) == 0xa3 && !(iicprobe(0x00) & 0x80)))) {
		/*
		 * We could toggle 0x00 bit 0x80, then re-read 0xfd to
		 * see if the value changes to 0xa3 (indicating Nuvoton).
		 * But we are trying to avoid writes.
		 */
		name = "w83795g";
	} else if (addr == iicprobe(0x4a) && iicprobe(0x4e) == 0x50 &&
	    iicprobe(0x4c) == 0xa3 && iicprobe(0x4d) == 0x5c) {
		name = "w83l784r";
	} else if (addr == 0x2d && iicprobe(0x4e) == 0x60 &&
	    iicprobe(0x4c) == 0xa3 && iicprobe(0x4d) == 0x5c) {
		name = "w83l785r";
	} else if (addr == 0x2e && iicprobe(0x4e) == 0x70 &&
	    iicprobe(0x4c) == 0xa3 && iicprobe(0x4d) == 0x5c) {
		name = "w83l785ts-l";
	} else if (addr >= 0x2c && addr <= 0x2f &&
	    ((iicprobe(0x00) & 0x07) != 0x0 ||
	    ((iicprobe(0x00) & 0x07) == 0x0 && addr * 2 == iicprobe(0x0b) &&
	    (iicprobe(0x0c) & 0x40) && !(iicprobe(0x0c) & 0x04))) &&
	    iicprobe(0x0e) == 0x7b &&
	    (iicprobe(0x0f) & 0xf0) == 0x10 &&
	    ((iicprobe(0x0d) == 0x5c && (iicprobe(0x00) & 0x80)) ||
	    (iicprobe(0x0d) == 0xa3 && !(iicprobe(0x00) & 0x80)))) {
		name = "w83793g";
	} else if (addr >= 0x28 && addr <= 0x2f &&
	    iicprobe(0x4f) == 0x12 && (iicprobe(0x4e) & 0x80)) {
		/*
		 * We could toggle 0x4e bit 0x80, then re-read 0x4f to
		 * see if the value changes to 0xc3 (indicating ASUS).
		 * But we are trying to avoid writes.
		 */
		if (iicprobe(0x58) == 0x31)
			name = "as99127f";	/* rev 1 */
	} else if ((addr == 0x2d || addr == 0x2e) &&
	    addr * 2 == iicprobe(0x04) &&
	    iicprobe(0x5d) == 0x19 && iicprobe(0x5e) == 0x34 &&
	    iicprobe(0x5a) == 0x03 && iicprobe(0x5b) == 0x06) {
		name = "f75375";	/* Fintek */
	} else if (addr == 0x2d &&
	    ((iicprobe(0x4f) == 0x06 && (iicprobe(0x4e) & 0x80)) ||
	    (iicprobe(0x4f) == 0x94 && !(iicprobe(0x4e) & 0x80)))) {
		/*
		 * We could toggle 0x4e bit 0x80, then re-read 0x4f to
		 * see if the value changes to 0x94 (indicating ASUS).
		 * But we are trying to avoid writes.
		 *
		 * NB. we won't match if the BIOS has selected a non-zero
		 * register bank (set via 0x4e). We could select bank 0 so
		 * we see the right registers, but that would require a
		 * write.  In general though, we bet no BIOS would leave us
		 * in the wrong state.
		 */
		if ((iicprobe(0x58) & 0x7f) == 0x31 &&
		    (iicprobe(0x4e) & 0xf) == 0x00)
			name = "asb100";
	} else if ((addr == 0x2c || addr == 0x2d) &&
	    iicprobe(0x00) == 0x80 &&
	    (iicprobe(0x01) == 0x00 || iicprobe(0x01) == 0x80) &&
	    iicprobe(0x02) == 0x00 && (iicprobe(0x03) & 0x83) == 0x00 &&
	    (iicprobe(0x0f) & 0x07) == 0x00 &&
	    (iicprobe(0x11) & 0x80) == 0x00 &&
	    (iicprobe(0x12) & 0x80) == 0x00) {
		/*
		 * The GL518SM is really crappy.  It has both byte and
		 * word registers, and reading a word register with a
		 * byte read command will make the device crap out and
		 * hang the bus.  This has nasty consequences on some
		 * machines, like preventing warm reboots.  The word
		 * registers are 0x07 through 0x0c, so make sure the
		 * checks above don't access those registers.  We
		 * don't want to do this check right up front though
		 * since this chip is somewhat hard to detect (which
		 * is why we check for every single fixed bit it has).
		 */
		name = "gl518sm";
	} else if ((addr == 0x2c || addr == 0x2d || addr == 0x2e) &&
	    iicprobe(0x16) == 0x41 && ((iicprobe(0x17) & 0xf0) == 0x40)) {
		name = "adm1026";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1131 &&
	    (iicprobew(0x07) & 0xfffc) == 0xa200) {
		name = "se97";		/* or se97b */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1131 &&
	    (iicprobew(0x07) & 0xfffc) == 0xa100 &&
	    (iicprobew(0x00) & 0xfff0) == 0x0010) {
		name = "se98";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x004d &&
	    iicprobew(0x07) == 0x3e00 &&
	    (iicprobew(0x00) & 0xffe0) == 0x0000) {
		name = "max6604";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x0054 &&
	    (iicprobew(0x07) & 0xfffc) == 0x0200 &&
	    (iicprobew(0x00) & 0xffe0) == 0x0000) {
		name = "mcp9804";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x0054 &&
	    (iicprobew(0x07) & 0xff00) == 0x0000 &&
	    (iicprobew(0x00) & 0xffe0) == 0x0000) {
		name = "mcp9805";		/* or mcp9843 */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x0054 &&
	    (iicprobew(0x07) & 0xfffc) == 0x2000 &&
	    (iicprobew(0x00) & 0xffe0) == 0x0000) {
		name = "mcp98242";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x0054 &&
	    (iicprobew(0x07) & 0xff00) == 0x2100 &&
	    (iicprobew(0x00) & 0xff00) == 0x0000) {
		name = "mcp98243";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x0054 &&
	    (iicprobew(0x07) & 0xfffc) == 0x2200 &&
	    (iicprobew(0x00) & 0xff00) == 0x0000) {
		name = "mcp98244";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x11d4 &&
	    iicprobew(0x07) == 0x0800 &&
	    iicprobew(0x00) == 0x001d) {
		name = "adt7408";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x104a &&
	    (iicprobew(0x07) & 0xfffe) == 0x0000 &&
	    (iicprobew(0x00) == 0x002d || iicprobew(0x00) == 0x002f)) {
		name = "stts424e02";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x104a &&
	    (iicprobew(0x07) & 0xfffe) == 0x0300 &&
	    (iicprobew(0x00) == 0x006f)) {
		name = "stts2002";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x104a &&
	    (iicprobew(0x07) & 0xffff) == 0x2201 &&
	    (iicprobew(0x00) == 0x00ef)) {
		name = "stts2004";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x104a &&
	    (iicprobew(0x07) & 0xffff) == 0x0200 &&
	    (iicprobew(0x00) == 0x006f)) {
		name = "stts3000";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x104a &&
	    (iicprobew(0x07) & 0xffff) == 0x0101 &&
	    (iicprobew(0x00) == 0x002d || iicprobew(0x00) == 0x002f)) {
		name = "stts424";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1b09 &&
	    (iicprobew(0x07) & 0xffe0) == 0x0800 &&
	    (iicprobew(0x00) & 0x001f) == 0x001f) {
		name = "cat34ts02";		/* or cat6095, prod 0x0813 */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1b09 &&
	    (iicprobew(0x07) & 0xffff) == 0x0a00 &&
	    (iicprobew(0x00) & 0x001f) == 0x001f) {
		name = "cat34ts02c";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1b09 &&
	    (iicprobew(0x07) & 0xffff) == 0x2200 &&
	    (iicprobew(0x00) == 0x007f)) {
		name = "cat34ts04";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x00b3 &&
	    (iicprobew(0x07) & 0xffff) == 0x2903 &&
	    (iicprobew(0x00) == 0x004f)) {
		name = "ts3000b3";		/* or tse2002b3 */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x00b3 &&
	    (iicprobew(0x07) & 0xffff) == 0x2912 &&
	    (iicprobew(0x00) == 0x006f)) {
		name = "ts3000gb2";		/* or tse2002gb2 */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x00b3 &&
	    (iicprobew(0x07) & 0xffff) == 0x2913 &&
	    (iicprobew(0x00) == 0x0077)) {
		name = "ts3000gb0";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x00b3 &&
	    (iicprobew(0x07) & 0xffff) == 0x3001 &&
	    (iicprobew(0x00) == 0x006f)) {
		name = "ts3001gb2";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x00b3 &&
	    (iicprobew(0x07) & 0xffff) == 0x2214 &&
	    (iicprobew(0x00) == 0x00ff)) {
		name = "tse2004gb2";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x001f &&
	    (iicprobew(0x07) & 0xffff) == 0x8201 &&
	    (iicprobew(0x00) & 0xff00) == 0x0000) {
		name = "at30ts00";		/* or at30tse002 */
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1114 &&
	    (iicprobew(0x07) & 0xffff) == 0x2200 &&
	    (iicprobew(0x00) & 0xff00) == 0x0000) {
		name = "at30tse004";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x1c68 &&
	    (iicprobew(0x07) & 0xffff) == 0x2201 &&
	    (iicprobew(0x00) & 0xff00) == 0x0000) {
		name = "gt30ts00";
	} else if ((addr & 0x78) == 0x18 && iicprobew(0x06) == 0x132d &&
	    (iicprobew(0x07) & 0xffff) == 0x3300 &&
	    (iicprobew(0x00) & 0x001f) == 0x001f) {
		name = "gt34ts02";
	} else if ((addr & 0x7e) == 0x1c && iicprobe(0x0f) == 0x3b &&
	    (iicprobe(0x21) & 0x60) == 0x00 &&
	    iicprobe(0x0f) == iicprobe(0x8f) &&	/* registers address is 7 bits */
	    iicprobe(0x20) == iicprobe(0xa0) &&
	    iicprobe(0x21) == iicprobe(0xa1) &&
	    iicprobe(0x22) == iicprobe(0xa2) &&
	    iicprobe(0x07) == 0x00) {		/* 0x00 to 0x0e are reserved */
		name = "lis331dl";
	} else if (name == NULL &&
	    (addr & 0x78) == 0x48) {		/* addr 0b1001xxx */
		name = lm75probe();
	}
#if 0
	/*
	 * XXX This probe needs to be improved; the driver does some
	 * dangerous writes.
	 */
	if (name == NULL && (addr & 0x7c) == 0x48 &&	/* addr 0b1001xxx */
	    (iicprobew(0xaa) & 0x0007) == 0x0000 &&
	    (iicprobew(0xa1) & 0x0007) == 0x0000 &&
	    (iicprobew(0xa2) & 0x0007) == 0x0000 &&
	    (iicprobe(0xac) & 0x10) == 0x00) {
		if ((iicprobe(0xac) & 0x7e) == 0x0a &&
		    iicprobe(0xab) == 0x00 && iicprobe(0xa8) == 0x00)
			name = "ds1624";
		else if ((iicprobe(0xac) & 0x7e) == 0x0c)
			name = "ds1631";	/* terrible probe */
		else if ((iicprobe(0xac) & 0x2e) == 0x2e)
			name = "ds1721";	/* terrible probe */
	}
#endif
	if (name == NULL && (addr & 0xf8) == 0x28 && iicprobe(0x48) == addr &&
	    (iicprobe(0x00) & 0x90) == 0x10 && iicprobe(0x58) == 0x90) {
		if (iicprobe(0x5b) == 0x12)
			name = "it8712";
		else if (iicprobe(0x5b) == 0x00)
			name = "it8712f-a";		/* sis950 too */
	}

	if (name == NULL && iicprobe(0x48) == addr &&
	    (iicprobe(0x40) & 0x80) == 0x00 && iicprobe(0x58) == 0xac)
		name = "mtp008";

	if (name == NULL) {
		name = adm1032cloneprobe(addr);
		if (name)
			skip_fc = 1;
	}

	return (name);
}

char *
iic_probe_eeprom(struct device *self, u_int8_t addr)
{
	u_int8_t type;
	char *name = NULL;

	type = iicprobe(0x02);
	/* limit to SPD types seen in the wild */
	if (type < 4 || type > 11)
		return (name);

	/* more matching in driver(s) */
	name = "eeprom";

	return (name);
}

void
iic_scan(struct device *self, struct i2cbus_attach_args *iba)
{
	i2c_tag_t ic = iba->iba_tag;
	struct i2c_attach_args ia;
	struct iicprobelist *pl;
	u_int8_t cmd = 0, addr;
	char *name;
	int i, j, k;

	bzero(ignore_addrs, sizeof(ignore_addrs));

	for (i = 0; probes[i].probe; i++) {
#if NIPMI > 0
		extern int ipmi_enabled;

		if ((probes[i].flags & PFLAG_SENSOR) && ipmi_enabled) {
			printf("%s: skipping sensors to avoid ipmi0 interactions\n",
			    self->dv_xname);
			continue;
		}
#endif
		pl = probes[i].pl;
		for (j = 0; pl[j].start && pl[j].end; j++) {
			for (addr = pl[j].start; addr <= pl[j].end; addr++) {
				for (k = 0; k < sizeof(ignore_addrs); k++)
					if (ignore_addrs[k] == addr)
						break;
				if (k < sizeof(ignore_addrs))
					continue;

				/* Perform RECEIVE BYTE command */
				iic_acquire_bus(ic, 0);
				if (iic_exec(ic, I2C_OP_READ_WITH_STOP, addr,
				    &cmd, sizeof cmd, NULL, 0, 0) == 0) {
					iic_release_bus(ic, 0);

					/* Some device exists */
					iicprobeinit(iba, addr);
					name = (*probes[i].probe)(self, addr);
#ifndef I2C_VERBOSE
					if (name == NULL)
						name = "unknown";
#endif /* !I2C_VERBOSE */
					if (name) {
						memset(&ia, 0, sizeof(ia));
						ia.ia_tag = iba->iba_tag;
						ia.ia_addr = addr;
						ia.ia_size = 1;
						ia.ia_name = name;
						if (config_found(self,
						    &ia, iic_print))
							continue;
					}
#ifdef I2C_VERBOSE
					if ((probes[i].flags & PFLAG_SENSOR))
						iic_dump(self, addr, name);
#endif /* I2C_VERBOSE */
				} else
					iic_release_bus(ic, 0);
			}
		}
	}
}


code:
/*	$OpenBSD: i2cvar.h,v 1.11 2013/07/05 09:32:14 kettenis Exp $	*/
/*	$NetBSD: i2cvar.h,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/

/*
 * Copyright (c) 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Steve C. Woodford and 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_I2CVAR_H_
#define	_DEV_I2C_I2CVAR_H_

#include <dev/i2c/i2c_io.h>

struct device;

/* Flags passed to i2c routines. */
#define	I2C_F_WRITE		0x00	/* new transfer is a write */
#define	I2C_F_READ		0x01	/* new transfer is a read */
#define	I2C_F_LAST		0x02	/* last byte of read */
#define	I2C_F_STOP		0x04	/* send stop after byte */
#define	I2C_F_POLL		0x08	/* poll, don't sleep */

/*
 * This structure provides the interface between the i2c framework
 * and the underlying i2c controller.
 *
 * Note that this structure is designed specifically to allow us
 * to either use the autoconfiguration framework or not.  This
 * allows a driver for a board with a private i2c bus use generic
 * i2c client drivers for chips that might be on that board.
 */
typedef struct i2c_controller {
	void	*ic_cookie;		/* controller private */

	/*
	 * These provide synchronization in the presence of
	 * multiple users of the i2c bus.  When a device
	 * driver wishes to perform transfers on the i2c
	 * bus, the driver should acquire the bus.  When
	 * the driver is finished, it should release the
	 * bus.
	 *
	 * This is provided by the back-end since a single
	 * controller may present e.g. i2c and smbus views
	 * of the same set of i2c wires.
	 */
	int	(*ic_acquire_bus)(void *, int);
	void	(*ic_release_bus)(void *, int);

	/*
	 * The preferred API for clients of the i2c interface
	 * is the scripted API.  This handles i2c controllers
	 * that do not provide raw access to the i2c signals.
	 */
	int	(*ic_exec)(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
		    void *, size_t, int);

	int	(*ic_send_start)(void *, int);
	int	(*ic_send_stop)(void *, int);
	int	(*ic_initiate_xfer)(void *, i2c_addr_t, int);
	int	(*ic_read_byte)(void *, uint8_t *, int);
	int	(*ic_write_byte)(void *, uint8_t, int);
} *i2c_tag_t;

/* Used to attach the i2c framework to the controller. */
struct i2cbus_attach_args {
	const char *iba_name;		/* bus name ("iic") */
	i2c_tag_t iba_tag;		/* the controller */
	void	(*iba_bus_scan)(struct device *, struct i2cbus_attach_args *,
		    void *);
	void	*iba_bus_scan_arg;
};

/* Used to attach devices on the i2c bus. */
struct i2c_attach_args {
	i2c_tag_t	ia_tag;		/* our controller */
	i2c_addr_t	ia_addr;	/* address of device */
	int		ia_size;	/* size (for EEPROMs) */
	char		*ia_name;	/* chip name */
	void		*ia_cookie;	/* pass extra info from bus to dev */
};

/*
 * API presented to i2c controllers.
 */
int	iicbus_print(void *, const char *);

#ifdef _I2C_PRIVATE
/*
 * Macros used internally by the i2c framework.
 */
#define	iic_send_start(ic, flags)					\
	(*(ic)->ic_send_start)((ic)->ic_cookie, (flags))
#define	iic_send_stop(ic, flags)					\
	(*(ic)->ic_send_stop)((ic)->ic_cookie, (flags))
#define	iic_initiate_xfer(ic, addr, flags)				\
	(*(ic)->ic_initiate_xfer)((ic)->ic_cookie, (addr), (flags))

#define	iic_read_byte(ic, bytep, flags)					\
	(*(ic)->ic_read_byte)((ic)->ic_cookie, (bytep), (flags))
#define	iic_write_byte(ic, byte, flags)					\
	(*(ic)->ic_write_byte)((ic)->ic_cookie, (byte), (flags))

void	iic_scan(struct device *, struct i2cbus_attach_args *);
int	iic_print(void *, const char *);
#endif /* _I2C_PRIVATE */

/*
 * Simplified API for clients of the i2c framework.  Definitions
 * in <dev/i2c/i2c_io.h>.
 */
#define	iic_acquire_bus(ic, flags)					\
	(*(ic)->ic_acquire_bus)((ic)->ic_cookie, (flags))
#define	iic_release_bus(ic, flags)					\
	(*(ic)->ic_release_bus)((ic)->ic_cookie, (flags))

int	iic_exec(i2c_tag_t, i2c_op_t, i2c_addr_t, const void *,
	    size_t, void *, size_t, int);

int	iic_smbus_write_byte(i2c_tag_t, i2c_addr_t, uint8_t, uint8_t, int);
int	iic_smbus_read_byte(i2c_tag_t, i2c_addr_t, uint8_t, uint8_t *, int);
int	iic_smbus_receive_byte(i2c_tag_t, i2c_addr_t, uint8_t *, int);

void	iic_ignore_addr(u_int8_t addr);

#endif /* _DEV_I2C_I2CVAR_H_ */

license: unknown


code:
/*	$OpenBSD: lis331dl.c,v 1.1 2009/08/12 14:51:20 cnst Exp $	*/

/*
 * Copyright (c) 2009 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>

/*
 * STMicroelectronics LIS331DL
 *	MEMS motion sensor
 * http://www.stm.com/stonline/products/literature/ds/13951.pdf
 * April 2008
 */

/* 3-axis accelerometer */
#define LISA_NUM_AXIS	3
static const struct {
	const char	*name;
	const uint8_t	reg;
} lisa_axis[LISA_NUM_AXIS] = {
	{ "OUT_X", 0x29 },
	{ "OUT_Y", 0x2b },
	{ "OUT_Z", 0x2d }
};

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

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

int	lisa_match(struct device *, void *, void *);
void	lisa_attach(struct device *, struct device *, void *);
void	lisa_refresh(void *);

uint8_t	lisa_readreg(struct lisa_softc *, uint8_t);
void	lisa_writereg(struct lisa_softc *, uint8_t, uint8_t);

struct cfattach lisa_ca = {
	sizeof(struct lisa_softc), lisa_match, lisa_attach
};

struct cfdriver lisa_cd = {
	NULL, "lisa", DV_DULL
};

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

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

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

	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 < LISA_NUM_AXIS; i++) {
		strlcpy(sc->sc_sensors[i].desc, lisa_axis[i].name,
		    sizeof(sc->sc_sensors[i].desc));
		sc->sc_sensors[i].type = SENSOR_INTEGER;
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
	}

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

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

void
lisa_refresh(void *arg)
{
	struct lisa_softc *sc = arg;
	struct ksensor *s = sc->sc_sensors;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);
	for (i = 0; i < LISA_NUM_AXIS; i++)
		s[i].value = (int8_t)lisa_readreg(sc, lisa_axis[i].reg);
	iic_release_bus(sc->sc_tag, 0);
}

uint8_t
lisa_readreg(struct lisa_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
lisa_writereg(struct lisa_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: lm75.c,v 1.20 2015/05/30 08:39:05 kettenis Exp $	*/
/*	$NetBSD: lm75.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/
/*
 * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org>
 * Copyright (c) 2004 Alexander Yurchenko <grange@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.
 */

/*
 * National Semiconductor LM75/LM76/LM77 temperature sensor.
 */

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

#include <dev/i2c/i2cvar.h>

#define	LM_MODEL_LM75	1
#define	LM_MODEL_LM77	2
#define	LM_MODEL_DS1775	3
#define	LM_MODEL_LM75A	4
#define	LM_MODEL_LM76	5

#define LM_POLLTIME	3	/* 3s */

#define	LM75_REG_TEMP			0x00
#define	LM75_REG_CONFIG			0x01
#define  LM75_CONFIG_SHUTDOWN		0x01
#define  LM75_CONFIG_CMPINT		0x02
#define  LM75_CONFIG_OSPOLARITY		0x04
#define  LM75_CONFIG_FAULT_QUEUE_MASK	0x18
#define  LM75_CONFIG_FAULT_QUEUE_1	(0 << 3)
#define  LM75_CONFIG_FAULT_QUEUE_2	(1 << 3)
#define  LM75_CONFIG_FAULT_QUEUE_4	(2 << 3)
#define  LM75_CONFIG_FAULT_QUEUE_6	(3 << 3)
#define  LM77_CONFIG_INTPOLARITY	0x08
#define  LM77_CONFIG_FAULT_QUEUE_4	0x10
#define  DS1755_CONFIG_RESOLUTION(i)	(9 + (((i) >> 5) & 3))
#define	LM75_REG_THYST_SET_POINT	0x02
#define	LM75_REG_TOS_SET_POINT		0x03
#define	LM77_REG_TLOW			0x04
#define	LM77_REG_THIGH			0x05

struct lmtemp_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	int	sc_addr;
	int	sc_model;
	int	sc_bits;
	int	sc_ratio;

	struct ksensor sc_sensor;
	struct ksensordev sc_sensordev;
};

int  lmtemp_match(struct device *, void *, void *);
void lmtemp_attach(struct device *, struct device *, void *);

struct cfattach lmtemp_ca = {
	sizeof(struct lmtemp_softc),
	lmtemp_match,
	lmtemp_attach
};

struct cfdriver lmtemp_cd = {
	NULL, "lmtemp", DV_DULL
};

/*
 * Temperature on the LM75 is represented by a 9-bit two's complement
 * integer in steps of 0.5C.  The following examples are taken from
 * the LM75 data sheet:
 *
 *	+125C	0 1111 1010	0x0fa
 *	+25C	0 0011 0010	0x032
 *	+0.5C	0 0000 0001	0x001
 *	0C	0 0000 0000	0x000
 *	-0.5C	1 1111 1111	0x1ff
 *	-25C	1 1100 1110	0x1ce
 *	-55C	1 1001 0010	0x192
 *
 * Temperature on the LM75A is represented by an 11-bit two's complement
 * integer in steps of 0.125C.  The LM75A can be treated like an LM75 if
 * the extra precision is not required.  The following examples are
 * taken from the LM75A data sheet:
 *
 *	+127.000C	011 1111 1000	0x3f8
 *	+126.875C	011 1111 0111	0x3f7
 *	+126.125C	011 1111 0001	0x3f1
 *	+125.000C	011 1110 1000	0x3e8
 *	+25.000C	000 1100 1000	0x0c8
 *	+0.125C		000 0000 0001	0x001
 *	0C		000 0000 0000	0x000
 *	-0.125C		111 1111 1111	0x7ff
 *	-25.000C	111 0011 1000	0x738
 *	-54.875C	110 0100 1001	0x649
 *	-55.000C	110 0100 1000	0x648
 *
 * Temperature on the LM77 is represented by a 13-bit two's complement
 * integer in steps of 0.5C.  The LM76 is similar, but the integer is
 * in steps of 0.065C
 *
 * LM75 temperature word:
 *
 * MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X X X
 * 15  14   13   12   11   10   9    8    7    6 5 4 3 2 1 0
 *
 *
 * LM75A temperature word:
 *
 * MSB Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X
 * 15  14   13   12   11   10   9    8    7    6    5    4 3 2 1 0
 *
 *
 * LM77 temperature word:
 *
 * Sign Sign Sign Sign MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Status bits
 * 15   14   13   12   11  10   9    8    7    6    5    4    3    2 1 0
 */

int  lmtemp_temp_read(struct lmtemp_softc *, uint8_t, int *);
void lmtemp_refresh_sensor_data(void *);

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

	if (strcmp(ia->ia_name, "lm75") == 0 ||
	    strcmp(ia->ia_name, "lm76") == 0 ||
	    strcmp(ia->ia_name, "lm77") == 0 ||
	    strcmp(ia->ia_name, "ds1775") == 0 ||
	    strcmp(ia->ia_name, "lm75a") == 0)
		return (1);
	return (0);
}

void
lmtemp_attach(struct device *parent, struct device *self, void *aux)
{
	struct lmtemp_softc *sc = (struct lmtemp_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;

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

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

	/* If in SHUTDOWN mode, wake it up */
	iic_acquire_bus(sc->sc_tag, 0);
	cmd = LM75_REG_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(", fails to respond\n");
		return;
	}
	if (data & LM75_CONFIG_SHUTDOWN) {
		data &= ~LM75_CONFIG_SHUTDOWN;
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			printf(", cannot wake up\n");
			iic_release_bus(sc->sc_tag, 0);
			return;
		}
		printf(", woken up");
	}
	iic_release_bus(sc->sc_tag, 0);

	sc->sc_model = LM_MODEL_LM75;
	sc->sc_bits = 9;
	sc->sc_ratio = 500000;		/* 0.5 degC for LSB */
	if (strcmp(ia->ia_name, "lm77") == 0) {
		sc->sc_model = LM_MODEL_LM77;
		sc->sc_bits = 13;
	} else if (strcmp(ia->ia_name, "lm76") == 0) {
		sc->sc_model = LM_MODEL_LM76;
		sc->sc_bits = 13;
		sc->sc_ratio = 62500;	/* 0.0625 degC for LSB */
	} else if (strcmp(ia->ia_name, "ds1775") == 0) {
		sc->sc_model = LM_MODEL_DS1775;
		//sc->sc_bits = DS1755_CONFIG_RESOLUTION(data);
	} else if (strcmp(ia->ia_name, "lm75a") == 0) {
		/* For simplicity's sake, treat the LM75A as an LM75 */
		sc->sc_model = LM_MODEL_LM75A;
	}

	printf("\n");

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

	/* Hook into the hw.sensors sysctl */
	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
	sensordev_install(&sc->sc_sensordev);

	sensor_task_register(sc, lmtemp_refresh_sensor_data, LM_POLLTIME);
}

int
lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, int *valp)
{
	u_int8_t cmd = which;
	u_int16_t data = 0x0000;
	int error;

	iic_acquire_bus(sc->sc_tag, 0);
	error = 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);
	if (error)
		return (error);

	/* Some chips return transient 0's.. we try next time */
	if (data == 0x0000)
		return (1);

	/* convert to half-degrees C */
	*valp = betoh16(data) / (1 << (16 - sc->sc_bits));
	return (0);
}

void
lmtemp_refresh_sensor_data(void *aux)
{
	struct lmtemp_softc *sc = aux;
	int val;
	int error;

	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val);
	if (error) {
#if 0
		printf("%s: unable to read temperature, error = %d\n",
		    sc->sc_dev.dv_xname, error);
#endif
		sc->sc_sensor.flags |= SENSOR_FINVALID;
		return;
	}

	sc->sc_sensor.value = val * sc->sc_ratio + 273150000;
	sc->sc_sensor.flags &= ~SENSOR_FINVALID;
}


code:
/*	$OpenBSD: lm78_i2c.c,v 1.4 2015/03/14 03:38:47 jsg 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>
#include <dev/ic/lm78var.h>

struct lm_i2c_softc {
	struct lm_softc sc_lmsc;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
};

int lm_i2c_match(struct device *, void *, void *);
void lm_i2c_attach(struct device *, struct device *, void *);
u_int8_t lm_i2c_readreg(struct lm_softc *, int);
void lm_i2c_writereg(struct lm_softc *, int, int);

struct cfattach lm_i2c_ca = {
	sizeof(struct lm_i2c_softc), lm_i2c_match, lm_i2c_attach
};

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

	if (strcmp(ia->ia_name, "as99127f") == 0 ||
	    strcmp(ia->ia_name, "w83627dhg") == 0 ||
	    strcmp(ia->ia_name, "w83627hf") == 0 ||
	    strcmp(ia->ia_name, "w83781d") == 0 ||
	    strcmp(ia->ia_name, "w83782d") == 0 ||
	    strcmp(ia->ia_name, "w83783s") == 0 ||
	    strcmp(ia->ia_name, "w83791d") == 0 ||
	    strcmp(ia->ia_name, "w83792d") == 0) {
		return (1);
	}
	/*
	 * XXX This chip doesn't have any real sensors, but we match
	 * it for now, just to knock out its satellites.
	 */
	if (strcmp(ia->ia_name, "w83791sd") == 0) {
		return (1);
	}
	return (0);
}

void
lm_i2c_attach(struct device *parent, struct device *self, void *aux)
{
	struct lm_i2c_softc *sc = (struct lm_i2c_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;

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

	/* Bus-independent attachment. */
	sc->sc_lmsc.lm_writereg = lm_i2c_writereg;
	sc->sc_lmsc.lm_readreg = lm_i2c_readreg;
	lm_attach(&sc->sc_lmsc);

	/* Remember we attached to iic(4). */
	sc->sc_lmsc.sbusaddr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = 0x4a;
	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);

	/* Make the bus scan ignore the satellites. */
	iic_ignore_addr(0x48 + (data & 0x7));
	iic_ignore_addr(0x48 + ((data >> 4) & 0x7));
}

u_int8_t
lm_i2c_readreg(struct lm_softc *lmsc, int reg)
{
	struct lm_i2c_softc *sc = (struct lm_i2c_softc *)lmsc;
	u_int8_t cmd, data;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = reg;
	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);

	return data;
}

void
lm_i2c_writereg(struct lm_softc *lmsc, int reg, int val)
{
	struct lm_i2c_softc *sc = (struct lm_i2c_softc *)lmsc;
	u_int8_t cmd, data;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = reg;
	data = val;
	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);
}


code:
/*	$OpenBSD: lm87.c,v 1.20 2008/11/10 05:19:48 cnst 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>

/* LM87 registers */
#define LM87_2_5V	0x20
#define LM87_VCCP1	0x21
#define LM87_VCC	0x22
#define LM87_5V		0x23
#define LM87_12V	0x24
#define LM87_VCCP2	0x25
#define LM87_EXT_TEMP	0x26
#define LM87_INT_TEMP	0x27
#define LM87_FAN1	0x28
#define LM87_FAN2	0x29
#define LM87_REVISION	0x3f
#define LM87_CONFIG1	0x40
#define  LM87_CONFIG1_START	0x01
#define  LM87_CONFIG1_INTCLR	0x08
#define LM87_CHANNEL	0x16
#define  LM87_CHANNEL_AIN1	0x01
#define  LM87_CHANNEL_AIN2	0x02
#define LM87_FANDIV	0x47

/* Sensors */
#define LMENV_2_5V		0
#define LMENV_VCCP1		1
#define LMENV_VCC		2
#define LMENV_5V		3
#define LMENV_12V		4
#define LMENV_VCCP2		5
#define LMENV_EXT_TEMP		6
#define LMENV_INT_TEMP		7
#define LMENV_FAN1		8
#define LMENV_FAN2		9
#define LMENV_NUM_SENSORS	10

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

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

int	lmenv_match(struct device *, void *, void *);
void	lmenv_attach(struct device *, struct device *, void *);

void	lmenv_refresh(void *);

struct cfattach lmenv_ca = {
	sizeof(struct lmenv_softc), lmenv_match, lmenv_attach
};

struct cfdriver lmenv_cd = {
	NULL, "lmenv", DV_DULL
};

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

	if (strcmp(ia->ia_name, "lm87") == 0 ||
	    strcmp(ia->ia_name, "lm87cimt") == 0 ||
	    strcmp(ia->ia_name, "adm9240") == 0 ||
	    strcmp(ia->ia_name, "lm81") == 0 ||
	    strcmp(ia->ia_name, "ds1780") == 0)
		return (1);
	return (0);
}

void
lmenv_attach(struct device *parent, struct device *self, void *aux)
{
	struct lmenv_softc *sc = (struct lmenv_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data, data2, channel;
	int i;

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

	sc->sc_family = 87;
	if (strcmp(ia->ia_name, "lm81") == 0 ||
	    strcmp(ia->ia_name, "adm9240") == 0 ||
	    strcmp(ia->ia_name, "ds1780") == 0)
		sc->sc_family = 81;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = LM87_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 ID register\n");
		return;
	}
	printf(": %s rev %x", ia->ia_name, data);

	cmd = LM87_FANDIV;
	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 Fan Divisor register\n");
		return;
	}
	sc->sc_fan1_div = 1 << ((data >> 4) & 0x03);
	sc->sc_fan2_div = 1 << ((data >> 6) & 0x03);

	if (sc->sc_family == 87) {
		cmd = LM87_CHANNEL;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &channel,
		    sizeof channel, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot read Channel register\n");
			return;
		}
	} else
		channel = 0;

	cmd = LM87_CONFIG1;
	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 1\n");
		return;
	}

	/*
	 * if chip is not running, try to start it.
	 * if it is stalled doing an interrupt, unstall it
	 */
	data2 = (data | LM87_CONFIG1_START);
	data2 = data2 & ~LM87_CONFIG1_INTCLR;

	if (data != data2) {
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot write Configuration Register 1\n");
			return;
		}
		printf(", starting scan");
	}
	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[LMENV_2_5V].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "+2.5Vin",
	    sizeof(sc->sc_sensor[LMENV_2_5V].desc));

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

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

	sc->sc_sensor[LMENV_5V].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[LMENV_5V].desc, "+5Vin/Vcc",
	    sizeof(sc->sc_sensor[LMENV_5V].desc));

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

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

	sc->sc_sensor[LMENV_EXT_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External",
	    sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc));
	if (sc->sc_family == 81)
		sc->sc_sensor[LMENV_EXT_TEMP].flags |= SENSOR_FINVALID;

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

	if (channel & LM87_CHANNEL_AIN1) {
		sc->sc_sensor[LMENV_FAN1].type = SENSOR_VOLTS_DC;
		strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "AIN1",
		    sizeof(sc->sc_sensor[LMENV_FAN1].desc));
	} else {
		sc->sc_sensor[LMENV_FAN1].type = SENSOR_FANRPM;
	}

	if (channel & LM87_CHANNEL_AIN2) {
		sc->sc_sensor[LMENV_FAN2].type = SENSOR_VOLTS_DC;
		strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "AIN2",
		    sizeof(sc->sc_sensor[LMENV_FAN2].desc));
	} else {
		sc->sc_sensor[LMENV_FAN2].type = SENSOR_FANRPM;
	}

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

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

	printf("\n");
}

void
lmenv_refresh(void *arg)
{
	struct lmenv_softc *sc = arg;
	u_int8_t cmd, data;
	u_int tmp;
	int sensor;

	iic_acquire_bus(sc->sc_tag, 0);

	for (sensor = 0; sensor < LMENV_NUM_SENSORS; sensor++) {
		cmd = LM87_2_5V + sensor;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
			continue;
		}

		sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID;
		switch (sensor) {
		case LMENV_2_5V:
			sc->sc_sensor[sensor].value = 2500000 * data / 192;
			break;
		case LMENV_5V:
			sc->sc_sensor[sensor].value = 5000000 * data / 192;
			break;
		case LMENV_12V:
			sc->sc_sensor[sensor].value = 12000000 * data / 192;
			break;
		case LMENV_VCCP1:
		case LMENV_VCCP2:
			sc->sc_sensor[sensor].value = 2700000 * data / 192;
			break;
		case LMENV_VCC:
			sc->sc_sensor[sensor].value = 3300000 * data / 192;
			break;
		case LMENV_EXT_TEMP:
			if (sc->sc_family == 81) {
				sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
				break;		/* missing on LM81 */
			}
			/* FALLTHROUGH */
		case LMENV_INT_TEMP:
			if (data == 0x80)
				sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[sensor].value =
				    (int8_t)data * 1000000 + 273150000;
			break;
		case LMENV_FAN1:
			if (sc->sc_sensor[sensor].type == SENSOR_VOLTS_DC) {
				sc->sc_sensor[sensor].value =
				    1870000 * data / 192;
				break;
			}
			tmp = data * sc->sc_fan1_div;
			if (tmp == 0)
				sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[sensor].value = 1350000 / tmp;
			break;
		case LMENV_FAN2:
			if (sc->sc_sensor[sensor].type == SENSOR_VOLTS_DC) {
				sc->sc_sensor[sensor].value =
				    1870000 * data / 192;
				break;
			}
			tmp = data * sc->sc_fan2_div;
			if (tmp == 0)
				sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[sensor].value = 1350000 / tmp;
			break;
		default:
			sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
			break;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


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

/*
 * 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>

/* LM93 registers */
#define LM93_CPU1_TEMP		0x50
#define LM93_CPU2_TEMP		0x51
#define LM93_INT_TEMP		0x52
#define LM93_EXT_TEMP		0x53
#define LM93_IN1_V		0x56
#define LM93_IN2_V		0x57
#define LM93_IN3_V		0x58
#define LM93_IN4_V		0x59
#define LM93_IN5_V		0x5a
#define LM93_IN6_V		0x5b
#define LM93_IN7_V		0x5c
#define LM93_IN8_V		0x5d
#define LM93_IN9_V		0x5e
#define LM93_IN10_V		0x5f
#define LM93_IN11_V		0x60
#define LM93_IN12_V		0x61
#define LM93_IN13_V		0x62
#define LM93_IN14_V		0x63
#define LM93_IN15_V		0x64
#define LM93_IN16_V		0x65
#define LM93_TACH1L		0x6e
#define LM93_TACH1H		0x6f
#define LM93_TACH2L		0x70
#define LM93_TACH2H		0x71
#define LM93_TACH3L		0x72
#define LM93_TACH3H		0x73
#define LM93_TACH4L		0x74
#define LM93_TACH4H		0x75
#define LM93_REVISION		0x3f

/* Sensors */
#define LMN_CPU1_TEMP		0
#define LMN_CPU2_TEMP		1
#define LMN_INT_TEMP		2
#define LMN_EXT_TEMP		3
#define LMN_IN1_V		4
#define LMN_IN2_V		5
#define LMN_IN3_V		6
#define LMN_IN4_V		7
#define LMN_IN5_V		8
#define LMN_IN6_V		9
#define LMN_IN7_V		10
#define LMN_IN8_V		11
#define LMN_IN9_V		12
#define LMN_IN10_V		13
#define LMN_IN11_V		14
#define LMN_IN12_V		15
#define LMN_IN13_V		16
#define LMN_IN14_V		17
#define LMN_IN15_V		18
#define LMN_IN16_V		19
#define LMN_TACH1		20
#define LMN_TACH2		21
#define LMN_TACH3		22
#define LMN_TACH4		23
#define LMN_NUM_SENSORS		24

struct {
	char		sensor;
	u_int8_t	cmd;
	char		*name;
	u_short		mVscale;
	u_short		tempscale;		/* else a fan */
} lmn_worklist[] = {
	{ LMN_CPU1_TEMP, LM93_CPU1_TEMP, "CPU", 0, 1 },
	{ LMN_CPU2_TEMP, LM93_CPU2_TEMP, "CPU", 0, 1 },
	{ LMN_INT_TEMP, LM93_INT_TEMP, "Internal", 0, 1 },
	{ LMN_EXT_TEMP, LM93_EXT_TEMP, "External", 0, 1 },

	{ LMN_IN1_V, LM93_IN1_V, "+12V", 1236*10, 0 },
	{ LMN_IN2_V, LM93_IN2_V, "+12V", 1236*10, 0 },
	{ LMN_IN3_V, LM93_IN3_V, "+12V", 1236*10, 0 },
	{ LMN_IN4_V, LM93_IN4_V, "FSB_Vtt 1.6V", 1600, 0 },
	{ LMN_IN5_V, LM93_IN5_V, "3GIO 2V ", 2000, 0 },
	{ LMN_IN6_V, LM93_IN6_V, "ICH_Core 2V", 2000, 0 },
	{ LMN_IN7_V, LM93_IN7_V, "Vccp 1.6V", 1600, 0 },
	{ LMN_IN8_V, LM93_IN8_V, "Vccp 1.6V", 1600, 0 },
	{ LMN_IN9_V, LM93_IN9_V, "+3.3V", 4400, 0 },
	{ LMN_IN10_V, LM93_IN10_V, "+5V", 6667, 0 },
	{ LMN_IN11_V, LM93_IN11_V, "SCSI_Core 3.3V", 3333, 0 },
	{ LMN_IN12_V, LM93_IN12_V, "Mem_Core 2.6V", 2625, 0 },
	{ LMN_IN13_V, LM93_IN13_V, "Mem_Vtt 1.3V", 1312, 0 },
	{ LMN_IN14_V, LM93_IN14_V, "Gbit_Core 1.3V", 1312, 0 },
	{ LMN_IN15_V, LM93_IN15_V, "-12V", -1236*10, 0 },
	{ LMN_IN16_V, LM93_IN16_V, "+3.3V S/B", 3600, 0 },

	{ LMN_TACH1, LM93_TACH1L, "", 0, 0 },
	{ LMN_TACH2, LM93_TACH2L, "", 0, 0 },
	{ LMN_TACH3, LM93_TACH3L, "", 0, 0 },
	{ LMN_TACH4, LM93_TACH4L, "", 0, 0 }
};

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

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

int	lmn_match(struct device *, void *, void *);
void	lmn_attach(struct device *, struct device *, void *);

void	lmn_refresh(void *);

struct cfattach lmn_ca = {
	sizeof(struct lmn_softc), lmn_match, lmn_attach
};

struct cfdriver lmn_cd = {
	NULL, "lmn", DV_DULL
};

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

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

void
lmn_attach(struct device *parent, struct device *self, void *aux)
{
	struct lmn_softc *sc = (struct lmn_softc *)self;
	struct i2c_attach_args *ia = aux;
	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));

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

	for (i = 0; i < LMN_NUM_SENSORS; i++) {
		if (lmn_worklist[i].tempscale)
			sc->sc_sensor[i].type = SENSOR_TEMP;
		else if (lmn_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, lmn_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
lmn_refresh(void *arg)
{
	struct lmn_softc *sc = arg;
	u_int8_t cmd, data, data2;
	u_int16_t fan;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

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

		cmd = lmn_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;
		if (lmn_worklist[i].tempscale) {
			if (data == 0x80)
				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
			else
				sc->sc_sensor[i].value =
				    (int8_t)data * 1000000 + 273150000;
		} else if (lmn_worklist[i].mVscale) {
			sc->sc_sensor[i].value = lmn_worklist[i].mVscale *
			    1000 * (u_int)data / 192;
		} else {
			cmd = lmn_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;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: maxim6690.c,v 1.16 2007/10/20 22:06:43 cnst 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 MAX6642/90 registers */
#define MAX6690_INT_TEMP	0x00
#define MAX6690_EXT_TEMP	0x01
#define MAX6690_INT_TEMP2	0x11
#define MAX6690_EXT_TEMP2	0x10
#define MAX6690_STATUS		0x02
#define MAX6690_DEVID		0xfe
#define MAX6690_REVISION	0xff	/* absent on MAX6642 */

#define MAX6642_TEMP_INVALID	0xff	/* sensor disconnected */
#define MAX6690_TEMP_INVALID	0x80	/* sensor disconnected */
#define MAX6690_TEMP_INVALID2	0x7f	/* open-circuit without pull-up */
#define LM90_TEMP_INVALID	0x7f	/* sensor disconnected */

#define MAX6642_TEMP2_MASK	0xc0	/* significant bits */
#define MAX6690_TEMP2_MASK	0xe0	/* significant bits */
#define LM90_TEMP2_MASK		0xe0	/* significant bits */

/* Sensors */
#define MAXTMP_INT		0
#define MAXTMP_EXT		1
#define MAXTMP_NUM_SENSORS	2

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

	u_int8_t sc_temp_invalid[2];
	u_int8_t sc_temp2_mask;

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

int	maxtmp_match(struct device *, void *, void *);
void	maxtmp_attach(struct device *, struct device *, void *);
void	maxtmp_refresh(void *);

struct cfattach maxtmp_ca = {
	sizeof(struct maxtmp_softc), maxtmp_match, maxtmp_attach
};

struct cfdriver maxtmp_cd = {
	NULL, "maxtmp", DV_DULL
};

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

	if (strcmp(ia->ia_name, "max6642") == 0 ||
	    strcmp(ia->ia_name, "max6690") == 0 ||
	    strcmp(ia->ia_name, "max6657") == 0 ||
	    strcmp(ia->ia_name, "max6658") == 0 ||
	    strcmp(ia->ia_name, "max6659") == 0 ||
	    strcmp(ia->ia_name, "lm63") == 0 ||
	    strcmp(ia->ia_name, "lm86") == 0 ||
	    strcmp(ia->ia_name, "lm89") == 0 ||
	    strcmp(ia->ia_name, "lm89-1") == 0 ||
	    strcmp(ia->ia_name, "lm90") == 0 ||
	    strcmp(ia->ia_name, "lm99") == 0 ||
	    strcmp(ia->ia_name, "lm99-1") == 0)
		return (1);
	return (0);
}

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

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

	if (strcmp(ia->ia_name, "max6642") == 0) {
		sc->sc_temp_invalid[0] = MAX6642_TEMP_INVALID;
		sc->sc_temp_invalid[1] = MAX6642_TEMP_INVALID;
		sc->sc_temp2_mask = MAX6642_TEMP2_MASK;
	} else if (strcmp(ia->ia_name, "max6690") == 0 ||
	    strcmp(ia->ia_name, "max6657") == 0 ||
	    strcmp(ia->ia_name, "max6658") == 0 ||
	    strcmp(ia->ia_name, "max6659") == 0) {
		sc->sc_temp_invalid[0] = MAX6690_TEMP_INVALID;
		sc->sc_temp_invalid[1] = MAX6690_TEMP_INVALID2;
		sc->sc_temp2_mask = MAX6690_TEMP2_MASK;
	} else {
		sc->sc_temp_invalid[0] = LM90_TEMP_INVALID;
		sc->sc_temp_invalid[1] = LM90_TEMP_INVALID;
		sc->sc_temp2_mask = LM90_TEMP2_MASK;
	}
	printf(": %s", ia->ia_name);

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

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

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

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

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

	printf("\n");
}

void	maxtmp_readport(struct maxtmp_softc *, u_int8_t, u_int8_t, int);

void
maxtmp_readport(struct maxtmp_softc *sc, u_int8_t cmd1, u_int8_t cmd2,
    int index)
{
	u_int8_t data, data2;

	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd1, sizeof cmd1, &data, sizeof data, 0))
		goto invalid;
	if (data == sc->sc_temp_invalid[0] || data == sc->sc_temp_invalid[1])
		goto invalid;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd2, sizeof cmd2, &data2, sizeof data2, 0))
		goto invalid;

	/* Set any meaningless bits to zero. */
	data2 &= sc->sc_temp2_mask;

	sc->sc_sensor[index].value = 273150000 +
	    1000000 * data + (data2 >> 5) * 1000000 / 8;
	return;

invalid:
	sc->sc_sensor[index].flags |= SENSOR_FINVALID;
}

void
maxtmp_refresh(void *arg)
{
	struct maxtmp_softc *sc = arg;

	iic_acquire_bus(sc->sc_tag, 0);

	maxtmp_readport(sc, MAX6690_INT_TEMP, MAX6690_INT_TEMP2, MAXTMP_INT);
	maxtmp_readport(sc, MAX6690_EXT_TEMP, MAX6690_EXT_TEMP2, MAXTMP_EXT);

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: pca9532.c,v 1.3 2008/04/17 16:50:17 deraadt 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/kernel.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/gpio.h>

#include <dev/i2c/i2cvar.h>
#include <dev/gpio/gpiovar.h>

#include "gpio.h"

/* driver for PCA 9532 */

#define PCALED_ADDR 0x60

#define PCALED_GPIO_NPINS 16

struct pcaled_softc {
	struct device sc_dev;
	i2c_tag_t sc_tag;
	int sc_addr;
	struct gpio_chipset_tag sc_gpio_gc;
	struct gpio_pin sc_gpio_pin[PCALED_GPIO_NPINS];

};

int pcaled_match(struct device *, void *, void *);
void pcaled_attach(struct device *, struct device *, void *);
int pcaled_gpio_pin_read(void *arg, int pin);
void pcaled_gpio_pin_write (void *arg, int pin, int value);
void pcaled_gpio_pin_ctl (void *arg, int pin, int flags);

struct cfattach pcaled_ca = {
	sizeof(struct pcaled_softc), pcaled_match, pcaled_attach
};

struct cfdriver pcaled_cd = {
	NULL, "pcaled", DV_DULL
};

int
pcaled_match(struct device *parent, void *v, void *arg)
{
	struct i2c_attach_args *ia = arg;
	int ok = 0;
	uint8_t cmd, data;

	if (ia->ia_addr != PCALED_ADDR)
		return (0);
	/* attempt to read input register 0 */
	iic_acquire_bus(ia->ia_tag, I2C_F_POLL);
	cmd = 0;
	if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr,
	    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
		goto fail;
	cmd = 9;
	if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr,
	    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
		goto fail;
	ok = 1;
fail:
	iic_release_bus(ia->ia_tag, I2C_F_POLL);
	return (ok);
}

void
pcaled_attach(struct device *parent, struct device *self, void *arg)
{
	struct pcaled_softc *sc = (void *)self;
	struct i2c_attach_args *ia = arg;
	struct gpiobus_attach_args gba;
	int i;
	uint8_t cmd, data;

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

	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);

	for (i = 0; i < PCALED_GPIO_NPINS; i++) {
		sc->sc_gpio_pin[i].pin_num = i;
		sc->sc_gpio_pin[i].pin_caps = GPIO_PIN_INOUT;
		if (i < 8)
			cmd = 0;
		else
			cmd = 1;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
		    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
			goto fail; /* XXX */
		sc->sc_gpio_pin[i].pin_state = (data >> (i & 3)) & 1;
	}
	sc->sc_gpio_gc.gp_cookie = sc;
	sc->sc_gpio_gc.gp_pin_read = pcaled_gpio_pin_read;
	sc->sc_gpio_gc.gp_pin_write = pcaled_gpio_pin_write;
	sc->sc_gpio_gc.gp_pin_ctl = pcaled_gpio_pin_ctl;

	printf(": PCA9532 LED controller\n");

	gba.gba_name = "gpio";
	gba.gba_gc = &sc->sc_gpio_gc;
	gba.gba_pins = sc->sc_gpio_pin;
	gba.gba_npins = PCALED_GPIO_NPINS;
#if NGPIO > 0
	config_found(&sc->sc_dev, &gba, gpiobus_print);
#endif

fail:
	iic_release_bus(sc->sc_tag, I2C_F_POLL);
}

int
pcaled_gpio_pin_read(void *arg, int pin)
{
	struct pcaled_softc *sc = arg;
	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
	uint8_t cmd, data;

	if (pin < 8)
		cmd = 0;
	else
		cmd = 1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
		goto fail; /* XXX */

fail:
	iic_release_bus(sc->sc_tag, I2C_F_POLL);
	return (data >> (pin & 3)) & 1;
}

void
pcaled_gpio_pin_write (void *arg, int pin, int value)
{
	struct pcaled_softc *sc = arg;
	uint8_t cmd, data;
	if (pin < 4)
		cmd = 6;
	else if (pin < 8)
		cmd = 7;
	else if (pin < 12)
		cmd = 8;
	else
		cmd = 9;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
		goto fail; /* XXX */
	data &= ~(0x3 << (2*(pin & 3)));
	data |= (value << (2*(pin & 3)));

	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &data, sizeof data, I2C_F_POLL))
		goto fail; /* XXX */

fail:
	iic_release_bus(sc->sc_tag, I2C_F_POLL);
}

void
pcaled_gpio_pin_ctl (void *arg, int pin, int flags)
{
	/* XXX all pins are inout */
}


code:
/*	$OpenBSD: pca9554.c,v 1.17 2008/09/10 16:13:43 reyk 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/gpio.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

#include <dev/gpio/gpiovar.h>

/* Philips 9554/6/7 registers */
#define PCA9554_IN		0x00
#define PCA9554_OUT		0x01
#define PCA9554_POLARITY	0x02
#define PCA9554_CONFIG		0x03

/* Philips 9555 registers */
#define PCA9555_IN0		0x00
#define PCA9555_IN1		0x01
#define PCA9555_OUT0		0x02
#define PCA9555_OUT1		0x03
#define PCA9555_POLARITY0	0x04
#define PCA9555_POLARITY1	0x05
#define PCA9555_CONFIG0		0x06
#define PCA9555_CONFIG1		0x07

/* Sensors */
#define PCAGPIO_NPINS	16

#define PCAGPIO_NPORTS	2
#define PCAGPIO_PORT(_pin)	((_pin) > 7 ? 1 : 0)
#define PCAGPIO_BIT(_pin)	(1 << ((_pin) % 8))

/* Register mapping index */
enum pcigpio_cmd {
	PCAGPIO_IN		= 0,
	PCAGPIO_OUT,
	PCAGPIO_POLARITY,
	PCAGPIO_CONFIG,
	PCAGPIO_MAX
};

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

	u_int8_t	sc_npins;
	u_int8_t	sc_control[PCAGPIO_NPORTS];
	u_int8_t	sc_polarity[PCAGPIO_NPORTS];
	u_int8_t	sc_regs[PCAGPIO_NPORTS][PCAGPIO_MAX];

	struct gpio_chipset_tag sc_gpio_gc;
        gpio_pin_t sc_gpio_pins[PCAGPIO_NPINS];

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

int	pcagpio_match(struct device *, void *, void *);
void	pcagpio_attach(struct device *, struct device *, void *);
int	pcagpio_init(struct pcagpio_softc *, int, u_int8_t *);
void	pcagpio_refresh(void *);

int     pcagpio_gpio_pin_read(void *, int);
void    pcagpio_gpio_pin_write(void *, int, int);
void    pcagpio_gpio_pin_ctl(void *, int, int);

struct cfattach pcagpio_ca = {
	sizeof(struct pcagpio_softc), pcagpio_match, pcagpio_attach
};

struct cfdriver pcagpio_cd = {
	NULL, "pcagpio", DV_DULL
};

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

	if (strcmp(ia->ia_name, "PCA9554") == 0 ||
	    strcmp(ia->ia_name, "PCA9554M") == 0 ||
	    strcmp(ia->ia_name, "pca9555") == 0 ||
	    strcmp(ia->ia_name, "pca9556") == 0 ||
	    strcmp(ia->ia_name, "pca9557") == 0)
		return (1);
	return (0);
}

void
pcagpio_attach(struct device *parent, struct device *self, void *aux)
{
	struct pcagpio_softc *sc = (struct pcagpio_softc *)self;
	struct i2c_attach_args *ia = aux;
	struct gpiobus_attach_args gba;
	int outputs = 0, i, port, bit;
	u_int8_t data[PCAGPIO_NPORTS];

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

	if (strcmp(ia->ia_name, "pca9555") == 0) {
		/* The pca9555 has two 8 bit ports */
		sc->sc_regs[0][PCAGPIO_IN] = PCA9555_IN0;
		sc->sc_regs[0][PCAGPIO_OUT] = PCA9555_OUT0;
		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9555_POLARITY0;
		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9555_CONFIG0;
		sc->sc_regs[1][PCAGPIO_IN] = PCA9555_IN1;
		sc->sc_regs[1][PCAGPIO_OUT] = PCA9555_OUT1;
		sc->sc_regs[1][PCAGPIO_POLARITY] = PCA9555_POLARITY1;
		sc->sc_regs[1][PCAGPIO_CONFIG] = PCA9555_CONFIG1;
		sc->sc_npins = 16;
	} else {
		/* All other supported devices have one 8 bit port */
		sc->sc_regs[0][PCAGPIO_IN] = PCA9554_IN;
		sc->sc_regs[0][PCAGPIO_OUT] = PCA9554_OUT;
		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9554_POLARITY;
		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9554_CONFIG;
		sc->sc_npins = 8;
	}
	if (pcagpio_init(sc, 0, &data[0]) != 0)
		return;
	if (sc->sc_npins > 8 && pcagpio_init(sc, 1, &data[1]) != 0)
		return;

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

	for (i = 0; i < sc->sc_npins; i++) {
		port = PCAGPIO_PORT(i);
		bit = PCAGPIO_BIT(i);
		sc->sc_sensor[i].type = SENSOR_INDICATOR;
		if ((sc->sc_control[port] & bit) == 0) {
			strlcpy(sc->sc_sensor[i].desc, "out",
			    sizeof(sc->sc_sensor[i].desc));
			outputs++;
		} else
			strlcpy(sc->sc_sensor[i].desc, "in",
			    sizeof(sc->sc_sensor[i].desc));
	}

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

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

	printf(":");
	if (sc->sc_npins - outputs)
		printf(" %d inputs", sc->sc_npins - outputs);
	if (outputs)
		printf(" %d outputs", outputs);
	printf("\n");

	for (i = 0; i < sc->sc_npins; i++) {
		port = PCAGPIO_PORT(i);
		bit = PCAGPIO_BIT(i);

		sc->sc_gpio_pins[i].pin_num = i;
		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;

		if ((sc->sc_control[port] & bit) == 0) {
			sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_OUTPUT;
			sc->sc_gpio_pins[i].pin_state = data[port] &
			    bit ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
		}
	}

	/* Create controller tag */
	sc->sc_gpio_gc.gp_cookie = sc;
	sc->sc_gpio_gc.gp_pin_read = pcagpio_gpio_pin_read;
	sc->sc_gpio_gc.gp_pin_write = pcagpio_gpio_pin_write;
	sc->sc_gpio_gc.gp_pin_ctl = pcagpio_gpio_pin_ctl;

	gba.gba_name = "gpio";
	gba.gba_gc = &sc->sc_gpio_gc;
	gba.gba_pins = sc->sc_gpio_pins;
	gba.gba_npins = sc->sc_npins;

	config_found(&sc->sc_dev, &gba, gpiobus_print);

}

int
pcagpio_init(struct pcagpio_softc *sc, int port, u_int8_t *datap)
{
	u_int8_t cmd, data;

	cmd = sc->sc_regs[port][PCAGPIO_CONFIG];
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": failed to initialize\n");
		return (-1);
	}
	sc->sc_control[port] = data;
	cmd = sc->sc_regs[port][PCAGPIO_POLARITY];
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": failed to initialize\n");
		return (-1);
	}
	sc->sc_polarity[port] = data;
	cmd = sc->sc_regs[port][PCAGPIO_OUT];
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": failed to initialize\n");
		return (-1);
	}

	*datap = data;
	return (0);
}

void
pcagpio_refresh(void *arg)
{
	struct pcagpio_softc *sc = arg;
	u_int8_t cmd, bit, in[PCAGPIO_NPORTS], out[PCAGPIO_NPORTS];
	int i, port;

	iic_acquire_bus(sc->sc_tag, 0);

	for (i = 0; i < PCAGPIO_NPORTS; i++) {
		cmd = sc->sc_regs[i][PCAGPIO_IN];
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &in[i], sizeof in[i], 0))
			goto invalid;

		cmd = sc->sc_regs[i][PCAGPIO_OUT];
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &out[i], sizeof out[i], 0))
			goto invalid;
	}

	for (i = 0; i < sc->sc_npins; i++) {
		port = PCAGPIO_PORT(i);
		bit = PCAGPIO_BIT(i);
		if ((sc->sc_control[port] & bit))
			sc->sc_sensor[i].value = (in[port] & bit) ? 1 : 0;
		else
			sc->sc_sensor[i].value = (out[port] & bit) ? 1 : 0;
	}

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

int
pcagpio_gpio_pin_read(void *arg, int pin)
{
	struct pcagpio_softc *sc = arg;
	u_int8_t cmd, in;
	int port, bit;

	port = PCAGPIO_PORT(pin);
	bit = PCAGPIO_BIT(pin);

	cmd = sc->sc_regs[port][PCAGPIO_IN];
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &in, sizeof in, 0))
		return 0;
	return ((in ^ sc->sc_polarity[port]) & bit) ? 1 : 0;
}

void
pcagpio_gpio_pin_write(void *arg, int pin, int value)
{
	struct pcagpio_softc *sc = arg;
	u_int8_t cmd, out, mask;
	int port, bit;

	port = PCAGPIO_PORT(pin);
	bit = PCAGPIO_BIT(pin);

	mask = 0xff ^ bit;
	cmd = sc->sc_regs[port][PCAGPIO_OUT];
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &out, sizeof out, 0))
		return;
	out = (out & mask) | (value ? bit : 0);

	cmd = sc->sc_regs[port][PCAGPIO_OUT];
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &out, sizeof out, 0))
		return;
}

void
pcagpio_gpio_pin_ctl(void *arg, int pin, int flags)
{
#if 0
	struct pcagpio_softc *sc = arg;
	u_int32_t conf;

	pcagpio_gpio_pin_select(sc, pin);
	conf = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
	    GSCGPIO_CONF);

	conf &= ~(GSCGPIO_CONF_OUTPUTEN | GSCGPIO_CONF_PUSHPULL |
	    GSCGPIO_CONF_PULLUP);
	if ((flags & GPIO_PIN_TRISTATE) == 0)
		conf |= GSCGPIO_CONF_OUTPUTEN;
	if (flags & GPIO_PIN_PUSHPULL)
		conf |= GSCGPIO_CONF_PUSHPULL;
	if (flags & GPIO_PIN_PULLUP)
		conf |= GSCGPIO_CONF_PULLUP;
	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
	    GSCGPIO_CONF, conf);
#endif
}