/*
 * VIA VT82C686A/B hardware monitor chip
 *
 ***************************************************************
 * Before calling these routines, one must call method->Open() *
 * After calling these routines, one must call method->Close() *
 ***************************************************************
 *

VIA
         Chip         Temp    Volt    Fan     SMBus   IOport
        via686a        3       5       2       yes     yes

 *
 * by YRS
 */


#include "pci_pm.h"
#include "sensors.h"

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


#define	VIA_ADDR_START		0x50	/*0x50-0x5E*/
#define	VIA_ADDR_END		0x5E

/* temp nr=0,1,2; volt nr=0,...4; fan nr=0,1 */
#define	VIA686_TEMP(nr)	(0x1F + (nr))
#define	VIA686_VOLT(nr)	(0x22 + (nr))
#define	VIA686_FAN(nr)	(0x29 + (nr))
#define	VIA686_FANDIV	0x47

static	int		via686_probe(LM_METHODS *methods);
static	float	via686_temp(LM_METHODS *methods, int no);
static	int		via686_fanrpm(LM_METHODS *methods, int no);
static	float	via686_volt(LM_METHODS *methods, int no);

 SENSOR via686 = {
	"VIA Chip VT82C686A/B",
	via686_probe,
	via686_temp,
	via686_volt,
	via686_fanrpm
};


#define VIA_chkRegNum 5

/* Register checked for probing */
static int chkReg[] = {
	0x40, 0x41, 0x42, 0x43,
	0x44, 0x47, 0x49, 0x4B,
	0x3F, 0x14, 0x1F, 0x20,
	0x21, 0x22, 0x23, 0x24,
	0x25, 0x26, 0x29, 0x2B,
	-1 };


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

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

  	if (pm_smb_detected != VIA686HWM && pm_smb_detected !=  VIA686SMB)
		return 0;

	save = smb_slave;

	if (method == &method_smb) {
		for (n = VIA_ADDR_START; n <= VIA_ADDR_END;) {
			if (!(smb_slave = get_smb_slave(n, VIA_ADDR_END)))
				goto ret0;
			else {
				if (chkReg_Probe(smb_slave, "Probing VIA686A/B chip:\n",
						chkReg, method) >= VIA_chkRegNum)
					goto ret1;
				else
				n = smb_slave + 2;
			}
		}
	} else {
		if (chkReg_Probe(0, "Probing VIA686A/B chip:\n",
					chkReg, method) >= VIA_chkRegNum)
			goto ret1;
	}

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


static const float via686temp_tab[256] = {\
   .00,   .00,   .00,   .00,   .00,   .00,   .00,   .00,
   .00,   .00,   .00,   .00,   .00,   .00,   .00,-50.00,
-48.00,-46.09,-44.26,-42.52,-40.86,-39.28,-37.78,-36.35,
-34.99,-33.70,-32.47,-31.31,-30.20,-29.15,-28.14,-27.18,
-26.27,-25.39,-24.55,-23.73,-22.95,-22.18,-21.44,-20.70,
-19.98,-19.27,-18.57,-17.87,-17.19,-16.51,-15.85,-15.20,
-14.56,-13.93,-13.32,-12.72,-12.14,-11.58,-11.03,-10.50,
 -9.99, -9.50, -9.03, -8.57, -8.11, -7.67, -7.22, -6.77,
 -6.31, -5.85, -5.37, -4.87, -4.36, -3.81, -3.24, -2.64,
 -2.00, -1.33,  -.65,   .00,   .59,  1.12,  1.60,  2.03,
  2.42,  2.79,  3.12,  3.44,  3.75,  4.05,  4.35,  4.67,
  4.99,  5.34,  5.71,  6.09,  6.49,  6.91,  7.34,  7.78,
  8.23,  8.69,  9.15,  9.62, 10.09, 10.57, 11.04, 11.51,
 11.98, 12.44, 12.90, 13.35, 13.80, 14.24, 14.68, 15.12,
 15.55, 15.99, 16.42, 16.85, 17.27, 17.70, 18.13, 18.56,
 18.99, 19.42, 19.85, 20.29, 20.72, 21.15, 21.59, 22.03,
 22.47, 22.90, 23.34, 23.78, 24.22, 24.66, 25.10, 25.54,
 25.99, 26.43, 26.87, 27.31, 27.75, 28.19, 28.63, 29.07,
 29.51, 29.95, 30.38, 30.82, 31.25, 31.68, 32.11, 32.53,
 32.96, 33.38, 33.80, 34.22, 34.63, 35.05, 35.48, 35.90,
 36.33, 36.76, 37.20, 37.64, 38.09, 38.55, 39.02, 39.50,
 39.99, 40.49, 41.00, 41.52, 42.06, 42.60, 43.15, 43.70,
 44.27, 44.84, 45.41, 45.99, 46.57, 47.16, 47.75, 48.35,
 48.94, 49.54, 50.13, 50.73, 51.33, 51.94, 52.55, 53.16,
 53.78, 54.41, 55.04, 55.68, 56.32, 56.98, 57.64, 58.32,
 59.00, 59.69, 60.40, 61.12, 61.85, 62.60, 63.36, 64.13,
 64.93, 65.73, 66.56, 67.40, 68.26, 69.14, 70.05, 70.97,
 71.91, 72.88, 73.86, 74.88, 75.92, 76.98, 78.08, 79.21,
 80.36, 81.55, 82.78, 84.03, 85.33, 86.66, 88.04, 89.45,
 90.90, 92.40, 93.94, 95.53, 97.16, 98.85,100.58,102.36,
104.19,106.07,108.01,110.00,   .00,   .00,   .00,   .00,
   .00,   .00,   .00,   .00,   .00,   .00,   .00,   .00
};

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

	return via686temp_tab[method->Read(VIA686_TEMP(no))];
}


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

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

	switch (no) {
		case 0:
		case 1:
			fac = 5.  / 10512.;
			break;
		case 2:
			fac = 5.  /  7884.;
			break;
		case 3:
			fac = 26. / 26280.;
			break;
		case 4:
			fac = 63. / 26280.;
	}

	if ((n = method->Read(VIA686_VOLT(no))) <= 0x40)
		n |= 0x100;

	return (float) (n * 25 + 133) * fac;
}


/*
	Controlling Fan Divisor: CR = 0x47.
	highest two bits for fan2, next two bits for fan1

         7       3     0
        +-+-+-+-+-+-+-+-+     xx = 00,01,10,11  div1fac = 1,2,4,8
        |y y|x x| VolID |     yy = 00,01,10,11  div2fac = 1,2,4,8
        +-+-+-+-+-+-+-+-+    initial values: xx=01, yy=01

 */

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

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

	n = method->Read(VIA686_FANDIV);
	div[0] = (n >> 4) & 0x03;
	div[1] =  n >> 6;

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

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


syntax highlighted by Code2HTML, v. 0.9.1