#include "palmtype.h"
#include "palm1lib.h"
#include "palmchip.h"

/*	Realtek 8305SC	PA1688		
	SLC_MDC			PIO6		Used as the clock pin, PA1688 generate it, 0 to 25MHz
	SDA_MOID		CamIO2		Used as the data pin, PA1688 should write this pin with special format
	
		Preamble	Start		OP code		PHYAD		REGAD	Turn Around	Data		Idle
		(32bits)	(2bits)		(2bits)		(5bits)		(5bits)	(2 bits)	(16bits)
Read	11...11		01			10			A4~A0		R4~R0		Z0		D15~D0		high-impedance
Write	11...11		01			01			A4~A0		R4~R0		10		D15~D0		high-impedance

	In current hardware, the relationship of 8305SC's PHY ports and VoIP equipment ports is
	RTL8305SC		port0		port2		port3
	VOIP Terminal	8019AS		LAN			WAN
	And in the most environment we only need 2 VLANs, one for voice and the other for data. So we hope that
port3 and port0 could be the VLAN for voice, and port3 and port2 could be the VLAN for the data. We use VLAN C
for voice and VLAN D for data.
*/

#define SCL_MDC		6		// PIO6
#define SDA_MDIO	2		// CamIO2

/*	Assume VLAN C ID is 0x002, VLAN D ID is 0x003; port3 and port0 is VLAN C, port3 and port2 is VLAN D. Then the
	value in the register should be
*/
#define GLOBAL_CTL0_REG			0x03fa	// iPhy=0, iReg=16, bit[10]=0(ena tag-VLAN), other default
#define GLOBAL_CTL2_REG			0xfeff	// iPhy=0, iReg=18, bit[8]=0(ena VLAN), other default
#define PORT0_CTL1_VLAN_A_REG	0x2000	// iPhy=0, iReg=24, bit[15:12]=0010, bit[4:0]=00000
#define PORT0_CTL2_VLAN_A_REG	0x0000	// iPhy=0, iReg=25, bit[11:0]=0x000 is VLAN A ID
#define PORT2_CTL1_VLAN_C_REG	0x3009	// iPhy=2, iReg=24, bit[15:12]=0011, bit[4:0]=01001
#define PORT2_CTL2_VLAN_C_REG	0x0002	// iPhy=2, iReg=25, bit[11:0]=0x002 is VLAN C ID
#define PORT3_CTL1_VLAN_D_REG	0x300c	// iPhy=3, iReg=24, bit[15:12]=0011, bit[4:0]=01100
#define PORT3_CTL2_VLAN_D_REG	0x0003	// iPhy=3, iReg=25, bit[11:0]=0x003 is VLAN D ID

#define PORT3_CTL0_REG			0x877e	// iPhy=3, iReg=22, bit[1:0]=10, add VID on the packet.

void _GenerateClock(UCHAR iClockNum)
{
	DCHAR i, j, iNum;

	iNum = iClockNum + iClockNum;
	for (i = 0; i < iNum; i ++)
	{
		Led_Blink(SCL_MDC);
		for (j = 0; j < 20; j ++)
		{
		}
	}
}

// iData is the data, and iBit is the valued bit number in the data.
void _WriteBits(UCHAR iData, UCHAR iBits)
{
	DCHAR i, iVal, iLeft, iRight1, iRight2;

	if (!iBits)
	{
		XBYTE[CAM_IO_EN] &= 0xfb;
		_GenerateClock(1);
		return;
	}

	iLeft = 9 - iBits;
	iRight1 = iBits - 1;
	iRight2 = iLeft - 1;

	for (i = 0; i < iBits; i ++)
	{
		iVal = iData >> iRight1;
		iData <<= iLeft;
		iData >>= iRight2;
		if (iVal)
		{
			Cam_LedOn(SDA_MDIO);
		}
		else
		{
			Cam_LedOff(SDA_MDIO);
		}
		_GenerateClock(1);
	}
}

USHORT _ReadBits(UCHAR iBits)
{
	DSHORT iData;
	DCHAR i, j;

	iData = 0;
	for (i = 0; i < iBits; i ++)
	{
		Led_Blink(SCL_MDC);
		for (j = 0; j < 20; j ++)
		{
		}

		XBYTE[CAM_IO_EN] &= 0xfb;
		if (XBYTE[CAM_IO_DATA] | 0x40)
		{
			iData ++;
		}
		iData <<= 1;

		Led_Blink(SCL_MDC);
		for (j = 0; j < 20; j ++)
		{
		}
	}

	return iData;
}

void VLANRegWrite(UCHAR iPhy, UCHAR iReg, USHORT iData)
{
	DCHAR iDataHi, iDataLo;

	iDataHi = P_HIBYTE(iData);
	iDataLo = P_LOBYTE(iData);

	Cam_LedOn(SDA_MDIO);		// Preamble
	_GenerateClock(32);

	_WriteBits(1, 2);		// Start : 01
	_WriteBits(1, 2);		// OP code, Write : 01
	_WriteBits(iPhy, 5);		// PHYAD
	_WriteBits(iReg, 5);		// REGAD
	_WriteBits(2, 2);		// Turn Around : 10
	_WriteBits(iDataHi, 8);	// Data High Byte
	_WriteBits(iDataLo, 8);	// Data Low Byte
}

USHORT VLANRegRead(UCHAR iPhy, UCHAR iReg)
{
	DSHORT iData;

	Cam_LedOn(SDA_MDIO);		// Preamble
	_GenerateClock(32);

	_WriteBits(1, 2);		// Start : 01
	_WriteBits(2, 2);		// OP code, Read : 10
	_WriteBits(iPhy, 5);		// PHYAD
	_WriteBits(iReg, 5);		// REGAD
	_WriteBits(0, 0);
	_WriteBits(0, 1);		// Turn Around : Z0
	iData = _ReadBits(16);

	return iData;
}

 void VLANStartup()
{
	Led_On(SCL_MDC);

	VLANRegWrite(0, 16, GLOBAL_CTL0_REG);
	VLANRegWrite(0, 18, GLOBAL_CTL2_REG);
	VLANRegWrite(0, 24, PORT0_CTL1_VLAN_A_REG);
	VLANRegWrite(0, 25, PORT0_CTL2_VLAN_A_REG);
	VLANRegWrite(2, 24, PORT2_CTL1_VLAN_C_REG);
	VLANRegWrite(2, 25, PORT2_CTL2_VLAN_C_REG);
	VLANRegWrite(3, 24, PORT3_CTL1_VLAN_D_REG);
	VLANRegWrite(3, 25, PORT3_CTL2_VLAN_D_REG);

	VLANRegWrite(3, 22, PORT3_CTL0_REG);
}

 void VLANDisable()
{
	XBYTE[IO_LO_ENABLE] &= 0xbf;
	XBYTE[CAM_IO_EN] &= 0x0b;
}