/************************************************
	Subroutines to get Mother Board Information

	Information related to Winbond W83781D Chip
		and National Semiconductor LM78/LM79 Chips
	by Alex van Kaam

	Information for VIA VT82C686A/B
	by ":p araffin.(Yoneya)", MANY THANKS!!

	Information for SMBus access
	by Linux lm_sensor homepage, MANY THANKS!!
		http://www.netroedge.com/~lm78/

 ************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <stdio.h>
#include "pci_pm.h"
#include "smbuses.h"
#include "methods.h"
#include "sensors.h"
#include "smb_extemp.h"

#ifdef TEMP_LIMIT
#define TEMP_HIGH 100.0
#define TEMP_LOW  -10.0
#endif

/* external (global) data */
extern int debug_flag, fahrn_flag;
extern int TyanTigerMP_flag;
extern int isa_port_base;
extern int viahwm_base, smb_base;
extern int pm_smb_detected;
extern int smb_slave;
extern char *probe_request;
extern SENSOR *HWM_module[];
extern char *HWM_name[];
extern int HWM_VIA, HWM_SMB, HWM_ISA;
extern int HWM_SMBchip[];
extern int HWM_smbslave[];
extern int HWM_ISAchip[];
extern int numSMBSlave;
extern int canSMBSlave[];
extern int num_extemp_chip;
extern int smb_extemp_chip[];
extern int smb_extemp_slave[];
extern int extra_tempNO;

/* Access method functions, global */
extern LM_METHODS method_isa, method_smb, method_via;


static LM_METHODS *this_method = NULL;
static SENSOR *this_sensor = NULL;
static int probe_flag = SEARCH;
static int HWM_firstSMB_flag = 0;
static char method;

/* function declarations */
extern void TyanTigerMPinit(void);
int Probe_method(void);
int HWM_detection(int);
void HWM_set_firstSMB(SENSOR *, int);
int probe_HWMChip(LM_METHODS *, int);
int via_set(void);
int smb_set(int, int);
int isa_set(int);
int InitMBInfo(char);
int getTemp(float *, float *, float *);
int getVolt(float *, float *, float *, float *, float *, float *, float *);
int getFanSp(int *, int *, int *);


/*----------------------
	Detecting HWM Chip
  ----------------------*/

int Probe_method(void)
{
	int n;

	if (method != 'I') {
		pm_smb_detected = set_smbus_io(&viahwm_base, &smb_base);
	}
	if (method == 'V') {
  		if ((n = pm_smb_detected) == VIA686HWM) {
		/* VIA VT82C686 HWM is available */
			return via_set();
		} else if (n != -1) {
			fprintf(stderr, "No VIA686 HWM available!!\n");
			return 1;
		}
	} else if (method == 'S') {
  		if ((n = pm_smb_detected) > 0) {
		/* SMBus PowerManagement, hardware monitor exist ? */
			return smb_set(probe_flag, n);
		} else if (n != -1) {
			fprintf(stderr, "No SMBus HWM available!!\n");
			return 1;
		}
	} else if (method == 'I') {
		/* Just try ISA-IO method */
		if ((n = isa_set(probe_flag)) == 0) {
			return 0;
		} else if (n != -1) {
			fprintf(stderr, "No ISA-IO HWM available!!\n");
			return 1;
		}
	} else {
	/* No input method option: Try probing each HWM type */
	  	if ((probe_flag == SEARCH || probe_flag == c_via686)
					&& (n = pm_smb_detected) == VIA686HWM) {
			if (via_set() == 0 && method != 'A')
				return 0;
			else if (n > 0)
				goto smb_chk;
		} else if ((n = pm_smb_detected) > 0) {
smb_chk:	if (smb_set(probe_flag, n) == 0 && method != 'A')
				return 0;
			goto isa_chk;
		} else {
isa_chk:	if ((n = isa_set(probe_flag)) == 0 && method != 'A')
				return 0;
			else if (n != -1 && method != 'A') {
				fprintf(stderr, "No Hardware Monitor found!!\n");
				return 1;
			}
		}
	}

	if (method == 'A') {
		return HWM_detection(pm_smb_detected);
	}

	return -1;
}

int HWM_detection(int chip)
{
	int n, j, k, ext;

	if (debug_flag)
		fprintf(stderr, "Summary of Detection:\n");

	if (HWM_VIA + HWM_SMB + HWM_ISA <= 0) {
		if (debug_flag)
			fprintf(stderr, " * No monitors found.\n");
		return -1;
	}

	if (HWM_VIA > 0) {
		this_method = &method_via;
		if (debug_flag)
			fprintf(stderr, " * VIA686A/B monitor found.\n");
	}
	for (n = 0, k = 0; HWM_module[n] != NULL; n++) {
		if (HWM_SMBchip[n] != 0) {
			if (debug_flag) {
				if (!k)
#if !defined(LINUX) && defined(HAVE_SMBUS) && defined(SMBUS_IOCTL)
					fprintf(stderr, " * SMB monitor(s)[ioctl:%s]:\n",
#else		/* SMBus direct access routines */
					fprintf(stderr, " * SMB monitor(s)[%s]:\n",
#endif
						chk_smb_chip(chip));
				fprintf(stderr, "  ** %s found at slave address: 0x%02X.\n",
					HWM_module[n]->Name, HWM_smbslave[n]);
			}
			k++;
			ext = 0;
			j = num_extemp_chip;
			if (!strcmp(HWM_name[n], "lm75")) {
				smb_extemp_chip[j] = ex_lm75;
				ext = 1;
			} else if (!strcmp(HWM_name[n], "lm90")) {
				smb_extemp_chip[j] = ex_lm90;
				ext = 1;
			} else if (!strcmp(HWM_name[n], "wl784")
						&& HWM_SMBchip[n] == W83L785TS) {
				smb_extemp_chip[j] = ex_wl785ts;
				ext = 1;
			}
			if (ext) {
				smb_extemp_slave[j] = HWM_smbslave[n];
				num_extemp_chip++;
			}
			/* set the first SMB monitor found */
			if (!ext && !HWM_firstSMB_flag && HWM_SMB > 1)
				HWM_set_firstSMB(HWM_module[n], HWM_smbslave[n]);
		}
	}
	for (n = 0, k = 0; HWM_module[n] != NULL; n++) {
		if (HWM_ISAchip[n] != 0) {
			if (debug_flag) {
				if (!k)
					fprintf(stderr, " * ISA monitor(s):\n");
				fprintf(stderr, "  ** %s found.\n", HWM_module[n]->Name);
			}
			k++;
		}
	}
#ifdef DEBUG
printf("HWM_VIA=%d, HWM_SMB=%d, HWM_ISA=%d\n", HWM_VIA, HWM_SMB, HWM_ISA);
#endif
	if (HWM_VIA + HWM_SMB + HWM_ISA == num_extemp_chip)
		num_extemp_chip = 0;

	return 0;
}

void HWM_set_firstSMB(SENSOR *module, int slave)
{
	int n;

	for (n = 0; n < numSMBSlave; n++) {
		if (canSMBSlave[n] == 0xFF) {
			canSMBSlave[n] = slave;
			break;
		}
	}

	this_method = &method_smb;
	this_sensor = module;

	n = debug_flag;
	debug_flag = 0;
	{
		this_method->Open();
		this_sensor->Probe(this_method);
		this_method->Close();
	}
	debug_flag = n;
	HWM_firstSMB_flag = 1;
}

int probe_HWMChip(LM_METHODS *methodp, int probe)
{
	int i, n, n0 = 0, num = 0;

	if (methodp->Open() != 0)
		return -1;

	/* module order is important !! */
	for (n = 0; HWM_module[n] != NULL; n++) {
		if ((probe == SEARCH || probe == n)
				&& (i = HWM_module[n]->Probe(methodp)) != 0) {
			if (methodp == &method_smb) {
				HWM_SMBchip[n] = i;
				HWM_smbslave[n] = smb_slave;
				HWM_SMB++;
			} else if (methodp == &method_isa) {
				HWM_ISAchip[n] = i;
				HWM_ISA++;
			} else {
				HWM_VIA++;
			}
			num++;
			if (n0 == 0)
				n0 = n;
			if (method != 'A')
				break;
		}
	}
	if (num)
		this_sensor = HWM_module[n0];	/* set HWM found first */

	methodp->Close();
	return num;
}

int via_set(void)
{
	if (debug_flag > 1)
		fprintf(stderr, ">>> Testing Reg's at VIA686 HWM <<<\n");

	if (probe_HWMChip(&method_via, c_via686) > 0) {
		this_method = &method_via;
		if (debug_flag && method != 'A')
			fprintf(stderr, "Using VIA686 HWM directly!!\n");
		return 0;
	} else {
		fprintf(stderr, "Something Wrong in detected VIA686 HWM!!\n");
		return 1;
	}
}

int smb_set(int probe, int chip)
{
	if (debug_flag > 1) {
#if !defined(LINUX) && defined(HAVE_SMBUS) && defined(SMBUS_IOCTL)
		fprintf(stderr, ">>> Testing Reg's at SMBus <<<\n");
#else		/* SMBus direct access routines */
		fprintf(stderr, ">>> Testing Reg's at SMBus <<<\n"\
			"[%s, IO-Base:0x%0X]\n", chk_smb_chip(chip), smb_base);
#endif
	}
	if (find_smb_dev() <= 0)
		goto ret1;

	if (probe_HWMChip(&method_smb, probe) > 0) {
		this_method = &method_smb;
		if (debug_flag && method != 'A') {
#if !defined(LINUX) && defined(HAVE_SMBUS) && defined(SMBUS_IOCTL)
			fprintf(stderr, "Using SMBus-ioctl access method[%s]!!\n",
#else		/* SMBus direct access routines */
			fprintf(stderr, "Using SMBus access method[%s]!!\n",
#endif
				chk_smb_chip(chip));
		}
		return 0;
	} else {
ret1:	if (debug_flag) {
			fprintf(stderr, "SMBus[%s] found, but No HWM available on it!!\n",
				chk_smb_chip(chip));
		}
		return 1;
	}
}

int isa_set(int probe)
{
	int n;

	if (debug_flag > 1)
		fprintf(stderr, ">>> Testing Reg's at ISA-IO <<<\n"\
			"[ISA Port IO-Base:0x%0X]\n", isa_port_base);

	if ((n = probe_HWMChip(&method_isa, probe)) > 0) {
		this_method = &method_isa;
		if (debug_flag && method != 'A')
			fprintf(stderr, "Using ISA-IO access method!!\n");
		return 0;
	} else if (n == 0)
		n = 1;

	return n;
}

int InitMBInfo(char method_inp)
{
	int n;

 	/* this is TyanTigerMP specific treatment */
	if (TyanTigerMP_flag)
	 	TyanTigerMPinit();

	if (debug_flag > 1)
		fprintf(stderr, "Probe Request: %s\n", probe_request);

	for (n = 0; HWM_name[n] != NULL; n++) {
		if (strcmp(probe_request, HWM_name[n]) == 0) {
			probe_flag = n;
			break;
		}
	}

	method = method_inp;
	if ((n = Probe_method()) != 0) {
		return n;
	}

	if (this_method && this_sensor) {
		if (debug_flag && method != 'A')
			fprintf(stderr, "* %s found.\n", this_sensor->Name);
		return 0;
	} else {
		return -1;
	}
}


/*-------------------------
	Getting Temperatures
  -------------------------*/

#define traFahrn(x)	((x) * 1.8 + 32.0)

int getTemp(float *t1, float *t2, float *t3)
{
	int n;
	float f, t[3] = {0.0, 0.0, 0.0};

	if (this_method->Open() != 0)
		return -1;

	if (this_sensor) {
		for (n = 0; n < 3; n++) {
			if ((f = this_sensor->Temp(this_method, n)) != 0xFFFF) {
#ifdef TEMP_LIMIT
			if (f <= TEMP_HIGH || f >= TEMP_LOW)
#endif
				t[n] = f;
			}
		}
  	}
	if (this_sensor == &it87 || this_sensor == &via686) {
	/* special treatment of IT86 and VIA868 */
		f = t[0];
		t[0] = t[1];
		t[1] = t[2];
		t[2] = f;
	} else if (this_sensor == &lm85) {
	/* special treatment of LM85 */
		f = t[0];
		t[0] = t[1];
		t[1] = f;
	}

	if (num_extemp_chip > 0) {
		if ((f = smb_ExtraTemp()) != 0xFFFF)
			t[extra_tempNO] = f;
	}

	if (fahrn_flag) {
		for (n = 0; n <= 2; n++) {
			if (t[n] != 0.0)
				t[n] = traFahrn(t[n]);
		}
	}

	*t1 = t[0]; *t2 = t[1]; *t3 = t[2];

	this_method->Close();
	return 0;
}

/*--------------------
	Getting Voltages
  --------------------*/

int getVolt(float *vc0, float *vc1,\
			float *v33, float *v50p, float *v50n,\
			float *v12p, float *v12n)
{
	int n;
	float f, v[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

	if (this_method->Open() != 0)
		return -1;

	if (this_sensor) {
		for (n = 0; n < 7; n++) {
			if ((f = this_sensor->Volt(this_method, n)) != 0xFFFF)
				v[n] = f;
		}
	}
	if (this_sensor == &lm85) {
	/* special treatment of LM85 */
		f = v[0];
		v[0] = v[1];
		v[1] = f;
	}

	*vc0 = v[0], *vc1 = v[1];
	*v33 = v[2], *v50p = v[3], *v12p = v[4];
	*v12n = v[5], *v50n = v[6];

	this_method->Close();
	return 0;
}

/*----------------------
	Getting Fan Speed
  ----------------------*/

int getFanSp(int *r1, int *r2, int *r3)
{
	int n;
	int i, r[3] = {0,0,0};

	if (this_method->Open() != 0)
		return -1;

	if (this_sensor) {
		for (n = 0; n < 3; n++) {
			if ((i = this_sensor->FanRPM(this_method, n)) != 0xFFFF)
				r[n] = i;
		}
	}
	if (this_sensor == &lm85) {
	/* special treatment of LM85 */
		i = r[0];
		r[0] = r[1];
		r[1] = i;
	}
	*r1 = r[0]; *r2 = r[1]; *r3 = r[2];

	this_method->Close();
	return 0;
}
