/*
 * Integrated Technology Express IT8705F/IT8712F hardware monitor chip
 *
 ***************************************************************
 * Before calling these routines, one must call method->Open() *
 * After calling these routines, one must call method->Close() *
 ***************************************************************
 *

Integrated Technology Express
         Chip         Temp    Volt    Fan     SMBus   IOport
        it8705         3       8       3       yes     yes
        it8712         3       8       3       yes     yes

SiS
         Chip         Temp    Volt    Fan     SMBus   IOport
        sis950         3       8       3       yes     yes

 *
 * by YRS
 */


#include	<stdio.h>
#include	"sensors.h"

/* external (global) data */
extern int smb_slave;
extern LM_METHODS method_isa, method_smb;
extern int numSMBSlave, canSMBSlave[128];


#define	IT87_ADDR_START		0x50	/*0x50-0x5E*/
#define	IT87_ADDR_END		0x5E

#define	IT87_SMBADDR	0x48
#define	IT87_REGCHIP	0x58
#define	IT87_CHIPID		0x90

/* temp nr=0,1,2; volt nr=0,1,...6; fan nr=0,1,2 */
#define	IT87_TEMP(nr)	(0x29 + (nr))
#define	IT87_VOLT(nr)	(0x20 + (nr))
#define	IT87_FAN(nr)	(0x0D + (nr))
#define	IT87_FANDIV		0x0B

static	int		it87_probe(LM_METHODS *);
static	float	it87_temp(LM_METHODS *, int);
static	int		it87_fanrpm(LM_METHODS *, int);
static	float	it87_volt(LM_METHODS *, int);

SENSOR it87 = {
	"Int.Tec.Exp. Chip IT8705F/IT8712F or SIS950",
	it87_probe,
	it87_temp,
	it87_volt,
	it87_fanrpm
};


#define IT87_chkRegNum 8

/* Register checked for probing */
static int chkReg[] = {
	0x00, 0x01, 0x02, 0x03,
	0x0A, 0x48, 0x50, 0x51,
	0x20, 0x21, 0x22, 0x23,
	0x24, 0x25, 0x26, 0x27,
	0x28, 0x29, 0x2A, 0x2B,
	0x0B, 0x0D, 0x0E, 0x0F,
	-1 };


/*
 *  return 0 if not probed
 */
static	int     it87_probe(LM_METHODS *method)
{
	int n, save;

	if (method != &method_isa  && method != &method_smb)
		return 0;

	save = smb_slave;

	if (method == &method_smb) {
		for (n = IT87_ADDR_START; n <= IT87_ADDR_END;) {
			if (!(smb_slave = get_smb_slave(n, IT87_ADDR_END)))
				goto ret0;
			else if (smb_slave != 2 * method->Read(IT87_SMBADDR))
				goto ret0;
			else {
				if (method->Read(IT87_REGCHIP) == IT87_CHIPID
					&& chkReg_Probe(smb_slave,
						"Probing ITE7805/7812/SIS950 chip:\n", chkReg, method)
							>= IT87_chkRegNum)
					goto ret1;
				else
					n = smb_slave + 2;
			}
		}
		goto ret0;
	} else {
		if (method->Read(IT87_REGCHIP) == IT87_CHIPID
			&& chkReg_Probe(0, "Probing ITE7805/7812/SIS950 chip:\n",
				chkReg, method) >= IT87_chkRegNum)
			goto ret1;
	}

ret0:
	smb_slave = save;
	return 0;
ret1:
	if (method == &method_smb)
		kill_smb_slave(smb_slave);
	return 1;
}


/*
 *	\retval	0xFFFF	no sensor
 *	\retval	other	temperature
 *  no = 0,1,2,...
 */
static	float	it87_temp( LM_METHODS *method, int no )
{
	if (no < 0 || 2 < no)
		return 0xFFFF;

	return (float) method->Read(IT87_TEMP(no));
}


/*
 *	\retval	0x0000FFFF	no sensor
 *  no = 0,1,2,...
 */
static	float	it87_volt(LM_METHODS *method, int no)
{
	float fac;

	if (no < 0 || 6 < no)
		return 0xFFFF;

	switch (no) {
		case 0:
		case 1:
		case 2:
			fac = 0.016;
			break;
		case 3:
			fac = 0.016 * 1.68;
			break;
		case 4:
			fac = 0.016 * 3.80;
			break;
		case 5:
			fac = - 0.016 * 3.477;
			break;
		case 6:
			fac = - 0.016 * 1.505;
	}

	return (float) method->Read(IT87_VOLT(no)) * fac;
}


/*
	Controlling Fan Divisor for 1st/2nd fans: CR = 0x0B.
	lowest three bits for fan1, next three bits for fan2

         7       3     0
        +-+-+-+-+-+-+-+-+     xxx = 000,..,111  div1fac = 1,..,128
        |   |y y y|x x x|     yyy = 000,..,111  div2fac = 1,..,128
        +-+-+-+-+-+-+-+-+    initial values: xx=001, yy=001

	No divisor available for fan3.

 */

/*
 *	\retval	0x0000FFFF	no sensor
 *  no = 0,1,2,...
 *
 *  Clock is 22.5kHz (22,500 x 60 = 1350000 counts/minute)
 */
static	int		it87_fanrpm(LM_METHODS *method, int no)
{
	int	r, n;
	static int div[3] = {1,1,1};

	if (no < 0 || 2 < no)
		return 0xFFFF;

	n = method->Read(IT87_FANDIV);
	div[0] =  n & 0x07;
	div[1] = (n >> 3) & 0x07;

	r = method->Read(IT87_FAN(no));
	if (r == 0xFF) {
		/* change divisor for the sake of next call ! */
		if (no != 2) {
			if (div[no] < 7)
				++(div[no]);
			else
				div[no] = 0;
			r = (n & 0x3F) | div[0] | (div[1] << 3);
			method->Write(IT87_FANDIV, r);
		}
		return 0xFFFF;
	} else if (r == 0) {
		return 0xFFFF;
	}

	return 1350000 / (r * (1 << div[no]));
}
