/************************************************
	Subroutines to get Mother Board Information
	assuming WinBond chips

	Information related to WinBond W83781D Chip
		and National Semiconductor LM78/LM79/LM75 Chips
	----------------------------------------------
	http://www.euronet.nl/users/darkside/mbmonitor
	----------------------------------------------
	by Alex van Kaam
 ************************************************/

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

#include <stdio.h>

extern struct lm_methods method_isa;
#ifdef HAVE_SMBUS
extern struct lm_methods method_smb;
#endif
static struct lm_methods *this_method;

#define OpenIO() (this_method->Open());
#define CloseIO() (this_method->Close());
#define ReadByte(addr) (this_method->Read((addr)))
#define WriteByte(addr,val) (this_method->Write((addr),(val)))
#define ReadTemp1() (this_method->ReadTemp1())
#define ReadTemp2() (this_method->ReadTemp2())

/* function declarations */
int InitMBinfo();
int FiniMBinfo();
int RstChip(void);
int getTemp(float *, float *, float *);
int getVolt(float *, float *, float *, float *, float *, float *, float *);
int getFanSp(int *, int *, int *);

/* debug flag used in InitMBInfo() and OpenIO() */
extern int debug_flag;

/* monitor chip flags */
int lm_flag = 0;
int winbond_flag = 0;
int asus_flag = 0;

/* temp1/2 flags */
int temp1_flag = 0;
int temp2_flag = 0;


/*----------------------
	Restarting Chip
  ----------------------*/
InitMBInfo(char type)
{
  int n, n_d, n_v, n_c, adrs, adrt1, adrt2;

  if(type=='I'){
	this_method=&method_isa;
  if(debug_flag)
	fprintf(stderr, "Using ISA access method !!\n");
  }
#ifdef HAVE_SMBUS
  else if(type=='S'){
	this_method=&method_smb;
  if(debug_flag)
	fprintf(stderr, "Using SMB access method !!\n");
  }
#endif
  else {
    return -1;
  }
  n = OpenIO();
  if (n != 0)
    return -1;

/* debug information */

	n=ReadByte(0x48)&0x7F;
	adrs = 2*n;
	if(debug_flag) printf("  MainSMBusADDR(CR48)  = %2X \n", adrs);

	n=ReadByte(0x4A)&0xFF;
	temp1_flag = (n & 0x08)>>3;
	temp2_flag = (n & 0x80)>>7;
	adrt1 = 2*(0x48 +   (n & 0x07) );
	adrt2 = 2*(0x48 + ( (n & 0x70)>>4) );
	if(debug_flag) printf("  SubSMBusADDRs(CR4A)  = %2X, %2X", adrt1, adrt2);
	if(debug_flag) {
		if(!temp1_flag)
			printf("  [Temp1 exists,");
		else
			printf("  [no Temp1,");
		if(!temp2_flag)
			printf(" Temp2 exists]\n");
		else
			printf(" no Temp2]\n");
	}

	n_d=ReadByte(0x49)&0xFF;
	n=n_d&0xFE;
	if(debug_flag) printf("  DeviceID(CR49&0xFE)  = %2X\n", n);

	WriteByte(0x4E, 0x00);
	n=n_v=ReadByte(0x4F)&0xFF;
	if(debug_flag) printf("  VenderID(CR4E=0,CR4F)= %2X\n", n);

	n=n_c=ReadByte(0x58)&0xFF;
	if(debug_flag) printf("  ChipID(CR58)         = %2X\n", n);

	if (n_d == 0xFF && n_v == 0xFF && n_c == 0xFF) {
		fprintf(stderr, "* No hardware monitor chip found, stop !!\n");
		return -1;
	} else if (n_v == 0xA3 ) {
		if (n_c < 0x30) {
			winbond_flag = 1;
			if(debug_flag) printf("* WinBond Chip, W83781D");
		} else {
			winbond_flag = 2;
			if(debug_flag) printf("* WinBond Chip, W83782D or W83783S");
		}
	} else if (n_v == 0xC3) {
		asus_flag = 1;
		if(debug_flag) printf("* Asus Chip, AS99127F");
	} else {
		lm_flag = 1;
		if(debug_flag) printf("* Unknown chip (assume LM78/79)");
	}
		if(debug_flag) printf(" found\n");

	if ( adrs != LM_ADDR) {
		if(debug_flag) printf("  SMBus address is wrong ??\n");
	}
	if ( !temp1_flag && adrt1 != WBtemp1_ADDR) {
		if(debug_flag) printf("  Temp1 address is wrong ??\n");
	}
	if ( !temp2_flag && adrt2 != WBtemp2_ADDR) {
		if(debug_flag) printf("  Temp2 address is wrong ??\n");
	}

	CloseIO();
	return 0;

}

FiniMBInfo()
{
  return CloseIO();
}

int RstChip(void)
{
  return WriteByte(0x40,0x01);
}


/*-------------------------------------------
	Getting Temperatures (assuming WinBond)
  -------------------------------------------*/

int getTemp(float *t1, float *t2, float *t3)
{
	int iofl, n;

	n = OpenIO();
	if (n != 0)
		return -1;

/* start from main temp */

	n=ReadByte(0x27);
	*t1 = n;
	if (*t1 > 100.0) *t1 = 0.0;

/* second and/or third temp */

	*t2 = *t3 = 0.0;
	if (!lm_flag) {
	  if(!temp1_flag) {
		n=ReadTemp1();
		*t2 = (n & 0xFF) + ((n & 0xFF00) >> 15)/2.;
		if (*t2 > 100.0) *t2 = 0.0;
	  }
	  if(!temp2_flag) {
		n=ReadTemp2();
		*t3 = (n & 0xFF) + ((n & 0xFF00) >> 15)/2.;
		if (*t3 > 100.0) *t3 = 0.0;
	  }
	}
	CloseIO();
	return 0;
}

/*-------------------------------------------
	Getting Voltages (assuming WinBond)
  -------------------------------------------*/

int getVolt(float *vc0, float *vc1, float *v33,\
			float *v50p, float *v50n,\
			float *v12p, float *v12n)
{
	unsigned char n;

	n = OpenIO();
	if (n != 0)
		return -1;

	n=ReadByte(0x20);
	*vc0 = n* 0.016;

	n=ReadByte(0x21);
	*vc1 = n * 0.016;

	n=ReadByte(0x22) ;
	*v33 = n* 0.016;

	n=ReadByte(0x23);
	*v50p = n * 0.016 * 1.68;

	n=ReadByte(0x24);
	if (asus_flag) {
		*v12p = n * 0.016 * 4.028;
	} else {
		*v12p =	 n * 0.016 * 3.80;
	}

	n=ReadByte(0x25) ;
	if (asus_flag) {
		*v12n = - n * 0.016 * 4.033;
	} else if (winbond_flag == 2) {
		*v12n =  (n * 0.016 - 3.6 * 0.8112) / 0.1888;
	} else {
		*v12n = - n * 0.016 * 3.477;
	}

	n=ReadByte(0x26);
	if (winbond_flag == 2) {
		*v50n =  (n * 0.016 - 3.6 * 0.6818) / 0.3182;
	} else {
		*v50n = - n * 0.016 * 1.505;
	}

	CloseIO();
	return 0;
}

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

int getFanSp(int *r1, int *r2, int *r3)
{
/*
	FanType = 1 for a normal fan
	FanType = 2 for a fan which gives 2 pulses per rotation
*/
	int FanType1 = 1, FanType2 = 1, FanType3 = 1;
	int iofl, div1, div2, div3 = 2;
	unsigned char n;

	n = OpenIO();
	if (n != 0)
		return -1;

	n=ReadByte(0x47);
	div1 = 1 << ((n & 0x30) >> 4);
	div2 = 1 << ((n & 0xC0) >> 6);
	n=ReadByte(0x28);
	if (n == 255)
		*r1 = 0;
	else
		*r1 = 1350000 / (n * div1 * FanType1);
	n=ReadByte(0x29);
	if (n == 255)
		*r2 = 0;
	else
		*r2 = 1350000 / (n * div2 * FanType2);
	n=ReadByte(0x2A);
	if (n == 255)
		*r3 = 0;
	else
		*r3 = 1350000 / (n * div3 * FanType3);
	
	CloseIO();
	return 0;
}

