/*! \file rtl8019.c \brief Realtek RTL8019AS Ethernet Interface Driver. */
//*****************************************************************************
//
// File Name	: 'rtl8019.c'
// Title		: Realtek RTL8019AS Ethernet Interface Driver
// Author		: Pascal Stang
// Created		: 7/6/2004
// Revised		: 10/1/2005
// Version		: 0.1
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// Based in part on code by Louis Beaudoin (www.embedded-creations.com).
// Thanks to Adam Dunkels and Louis Beaudoin for providing the initial
// structure in which to write this driver.
//*****************************************************************************

#include "global.h"
#include "timer.h"
#include "rprintf.h"

#include "rtl8019.h"

// include configuration
#include "rtl8019conf.h"

// pointers to locations in the RTL8019 receive buffer
static unsigned char NextPage;				// page pointer to next Rx packet
static unsigned int CurrentRetreiveAddress;	// DMA address for read Rx packet location


void nicInit(void)
{
	rtl8019Init();
}

void nicSend(unsigned int len, unsigned char* packet)
{
	rtl8019BeginPacketSend(len);
	rtl8019SendPacketData(packet, len);
	rtl8019EndPacketSend();
}

unsigned int nicPoll(unsigned int maxlen, unsigned char* packet)
{
	unsigned int packetLength;
	
	packetLength = rtl8019BeginPacketRetreive();

	// if there's no packet or an error - exit without ending the operation
	if( !packetLength )
		return 0;

	// drop anything too big for the buffer
	if( packetLength > maxlen )
	{
		rtl8019EndPacketRetreive();
		return 0;
	}
	
	// copy the packet data into the packet buffer
	rtl8019RetreivePacketData( packet, packetLength );
	rtl8019EndPacketRetreive();
		
	return packetLength;
}

void nicGetMacAddress(u08* macaddr)
{
	u08 tempCR;
	// switch register pages
	tempCR = rtl8019Read(CR);
	rtl8019Write(CR,tempCR|PS0);
	// read MAC address registers
	*macaddr++ = rtl8019Read(PAR0);
	*macaddr++ = rtl8019Read(PAR1);
	*macaddr++ = rtl8019Read(PAR2);
	*macaddr++ = rtl8019Read(PAR3);
	*macaddr++ = rtl8019Read(PAR4);
	*macaddr++ = rtl8019Read(PAR5);
	// switch register pages back
	rtl8019Write(CR,tempCR);
}

void nicSetMacAddress(u08* macaddr)
{
	u08 tempCR;
	// switch register pages
	tempCR = rtl8019Read(CR);
	rtl8019Write(CR,tempCR|PS0);
	// write MAC address registers
	rtl8019Write(PAR0, *macaddr++);
	rtl8019Write(PAR1, *macaddr++);
	rtl8019Write(PAR2, *macaddr++);
	rtl8019Write(PAR3, *macaddr++);
	rtl8019Write(PAR4, *macaddr++);
	rtl8019Write(PAR5, *macaddr++);
	// switch register pages back
	rtl8019Write(CR,tempCR);
}

void nicRegDump(void)
{
	rtl8019RegDump();
}


void rtl8019SetupPorts(void)
{
#if NIC_CONNECTION == MEMORY_MAPPED
	// enable external SRAM interface - no wait states
	sbi(MCUCR, SRE);
//	sbi(MCUCR, SRW10);
//	sbi(XMCRA, SRW00);
//	sbi(XMCRA, SRW01);
//	sbi(XMCRA, SRW11);
#else
    // make the address port output
	RTL8019_ADDRESS_DDR |= RTL8019_ADDRESS_MASK;
    // make the data port input with pull-ups
    RTL8019_DATA_PORT = 0xFF;

	// initialize the control port read and write pins to de-asserted
	RTL8019_CONTROL_DDR |= (1<<RTL8019_CONTROL_READPIN);
    RTL8019_CONTROL_DDR |= (1<<RTL8019_CONTROL_WRITEPIN);
	// set the read and write pins to output
	RTL8019_CONTROL_PORT |= (1<<RTL8019_CONTROL_READPIN);
	RTL8019_CONTROL_PORT |= (1<<RTL8019_CONTROL_WRITEPIN);
#endif
	// set reset pin to output
	sbi(RTL8019_RESET_DDR, RTL8019_RESET_PIN);
}


#if NIC_CONNECTION == MEMORY_MAPPED
inline void rtl8019Write(u08 address, u08 data)
{
	*(volatile u08*)(RTL8019_MEMORY_MAPPED_OFFSET + address) = data;
}
#else
void rtl8019Write(unsigned char address, unsigned char data)
{
	// assert the address
	RTL8019_ADDRESS_PORT = address | (RTL8019_ADDRESS_PORT&~RTL8019_ADDRESS_MASK);
	// set data bus as output and place data on bus
	RTL8019_DATA_DDR = 0xFF;
	RTL8019_DATA_PORT = data;
	// clock write pin
	cbi(RTL8019_CONTROL_PORT, RTL8019_CONTROL_WRITEPIN);
	nop();
	nop();
	sbi(RTL8019_CONTROL_PORT, RTL8019_CONTROL_WRITEPIN);
	// set data port back to input with pullups enabled
    RTL8019_DATA_DDR = 0x00;
    RTL8019_DATA_PORT = 0xFF;
}
#endif


#if NIC_CONNECTION == MEMORY_MAPPED
inline u08 ax88796Read(u08 address)
{
	return *(volatile u08*)(RTL8019_MEMORY_MAPPED_OFFSET + address);
}
#else
unsigned char rtl8019Read(unsigned char address)
{
	unsigned char data;
   
	// assert the address
	RTL8019_ADDRESS_PORT = address | (RTL8019_ADDRESS_PORT&~RTL8019_ADDRESS_MASK);
	// assert read
	cbi(RTL8019_CONTROL_PORT, RTL8019_CONTROL_READPIN);
	nop();
	nop();
	// read in the data
	data = RTL8019_DATA_PIN;
	// negate read
	sbi(RTL8019_CONTROL_PORT, RTL8019_CONTROL_READPIN);
	// return data
	return data;
}
#endif                       


void rtl8019Init(void)
{
	// setup I/O ports
	rtl8019SetupPorts();
	
	// do a hard reset
	sbi(RTL8019_RESET_PORT, RTL8019_RESET_PIN);
	delay_ms(10);
	cbi(RTL8019_RESET_PORT, RTL8019_RESET_PIN);

	// clear interrupt state
	rtl8019Write( ISR, rtl8019Read(ISR) );
	delay_ms(50);

	// switch to page 3 to load config registers
	rtl8019Write(CR, (PS0|PS1|RD2|STOP));

	// disable EEPROM write protect of config registers
	rtl8019Write(RTL_EECR, (EEM1|EEM0));

    // set network type to 10 Base-T link test
	rtl8019Write(CONFIG2, 0x20);

    // disable powerdown and sleep
	rtl8019Write(CONFIG3, 0);
	delay_ms(255);

    // reenable EEPROM write protect
	rtl8019Write(RTL_EECR, 0);

    // go back to page 0, stop NIC, abort DMA
	rtl8019Write(CR, (RD2|STOP));
	delay_ms(2);					// wait for traffic to complete
	rtl8019Write(DCR, DCR_INIT);
	rtl8019Write(RBCR0,0x00);
	rtl8019Write(RBCR1,0x00);
	rtl8019Write(RCR, AB);
	rtl8019Write(TPSR, TXSTART_INIT);
	rtl8019Write(TCR, LB0);
	rtl8019Write(PSTART, RXSTART_INIT);
	rtl8019Write(BNRY, RXSTART_INIT);
	rtl8019Write(PSTOP, RXSTOP_INIT);
	rtl8019Write(CR, (PS0|RD2|STOP));	// switch to page 1
	delay_ms(2);
	rtl8019Write(CPR, RXSTART_INIT);
	
	// set MAC address
	rtl8019Write(PAR0, RTL8019_MAC0);
	rtl8019Write(PAR1, RTL8019_MAC1);
	rtl8019Write(PAR2, RTL8019_MAC2);
	rtl8019Write(PAR3, RTL8019_MAC3);
	rtl8019Write(PAR4, RTL8019_MAC4);
	rtl8019Write(PAR5, RTL8019_MAC5);
    
	// initialize sequence per NE2000 spec
	rtl8019Write(CR, (RD2|STOP));
	rtl8019Write(DCR, DCR_INIT);
	rtl8019Write(CR, (RD2|START));
	rtl8019Write(ISR,0xFF);			// clear all interrupts
	rtl8019Write(IMR, IMR_INIT);
	rtl8019Write(TCR, TCR_INIT);
	
	rtl8019Write(CR, (RD2|START));	// start the NIC
}


void rtl8019BeginPacketSend(unsigned int packetLength)
{
	unsigned int sendPacketLength;
	sendPacketLength = (packetLength>=ETHERNET_MIN_PACKET_LENGTH)?
						(packetLength):ETHERNET_MIN_PACKET_LENGTH;
	
	//start the NIC
	rtl8019Write(CR, (RD2|START));
	
	// still transmitting a packet - wait for it to finish
	while( rtl8019Read(CR) & TXP );

	// load beginning page for transmit buffer
	rtl8019Write(TPSR,TXSTART_INIT);
	
	// set start address for remote DMA operation
	rtl8019Write(RSAR0,0x00);
	rtl8019Write(RSAR1,0x40);
	
	// clear the packet stored interrupt
	rtl8019Write(ISR,PTX);

	// load data byte count for remote DMA
	rtl8019Write(RBCR0, (unsigned char)(packetLength));
	rtl8019Write(RBCR1, (unsigned char)(packetLength>>8));

	rtl8019Write(TBCR0, (unsigned char)(sendPacketLength));
	rtl8019Write(TBCR1, (unsigned char)((sendPacketLength)>>8));
	
	// do remote write operation
	rtl8019Write(CR,(RD1|START));
}


void rtl8019SendPacketData(unsigned char *localBuffer, unsigned int length)
{
	unsigned int i;
	
	// write data to DMA port
	for(i=0;i<length;i++)
		rtl8019Write(RDMAPORT, localBuffer[i]);
}


void rtl8019EndPacketSend(void)
{
	//send the contents of the transmit buffer onto the network
	rtl8019Write(CR,(RD2|TXP));
	// clear the remote DMA interrupt
	rtl8019Write(ISR, RDC);
}


unsigned int rtl8019BeginPacketRetreive(void)
{
	unsigned char i;
	unsigned char bnry;
	
	unsigned char pageheader[4];
	unsigned int rxlen;
	
	// check for and handle an overflow
	rtl8019ProcessInterrupt();
	
	// read CPR from page 1
	rtl8019Write(CR,(PS0|RD2|START));
	i = rtl8019Read(CPR);
	
	// return to page 0
	rtl8019Write(CR,(RD2|START));
	
	// read the boundary register - pointing to the beginning of the packet
	bnry = rtl8019Read(BNRY) ;
	
	// return if there is no packet in the buffer
	if( bnry == i )
		return 0;

	// clear the packet received interrupt flag
	rtl8019Write(ISR, PRX);
	
	// if boundary pointer is invalid
	if( (bnry >= RXSTOP_INIT) || (bnry < RXSTART_INIT) )
	{
		// reset the contents of the buffer and exit
		rtl8019Write(BNRY, RXSTART_INIT);
		rtl8019Write(CR, (PS0|RD2|START));
		rtl8019Write(CPR, RXSTART_INIT);
		rtl8019Write(CR, (RD2|START));
		return 0;
	}

	// initiate DMA to transfer the RTL8019 packet header
	rtl8019Write(RBCR0, 4);
	rtl8019Write(RBCR1, 0);
	rtl8019Write(RSAR0, 0);
	rtl8019Write(RSAR1, bnry);
	rtl8019Write(CR, (RD0|START));
	// transfer packet header
	for(i=0;i<4;i++)
		pageheader[i] = rtl8019Read(RDMAPORT);
	// end the DMA operation
	rtl8019Write(CR, (RD2|START));
	// wait for remote DMA complete
	for(i = 0; i < 20; i++)
		if(rtl8019Read(ISR) & RDC)
			break;
	rtl8019Write(ISR, RDC);

	rxlen = (pageheader[PKTHEADER_PKTLENH]<<8) + pageheader[PKTHEADER_PKTLENL];
	NextPage = pageheader[PKTHEADER_NEXTPAGE];
	
	CurrentRetreiveAddress = (bnry<<8) + 4;
	
	// if the NextPage pointer is invalid, the packet is not ready yet - exit
	if( (NextPage >= RXSTOP_INIT) || (NextPage < RXSTART_INIT) )
		return 0;
    
	return rxlen-4;
}


void rtl8019RetreivePacketData(unsigned char * localBuffer, unsigned int length)
{
	unsigned int i;
	
	// initiate DMA to transfer the data
	rtl8019Write(RBCR0, (unsigned char)length);
	rtl8019Write(RBCR1, (unsigned char)(length>>8));
	rtl8019Write(RSAR0, (unsigned char)CurrentRetreiveAddress);
	rtl8019Write(RSAR1, (unsigned char)(CurrentRetreiveAddress>>8));
	rtl8019Write(CR, (RD0|START));
	// transfer packet data
	for(i=0;i<length;i++)
		localBuffer[i] = rtl8019Read(RDMAPORT);
	// end the DMA operation
	rtl8019Write(CR, (RD2|START));
	// wait for remote DMA complete
	for(i=0; i<20; i++)
		if(rtl8019Read(ISR) & RDC)
			break;
	rtl8019Write(ISR, RDC);
	// keep track of current address
    CurrentRetreiveAddress += length;
    if( CurrentRetreiveAddress >= 0x6000 )
    	CurrentRetreiveAddress = CurrentRetreiveAddress - (0x6000-0x4600) ;
}


void rtl8019EndPacketRetreive(void)
{
	unsigned char i;

	// end the DMA operation
	rtl8019Write(CR, (RD2|START));
	// wait for remote DMA complete
	for(i=0; i<20; i++)
		if(rtl8019Read(ISR) & RDC)
			break;
	rtl8019Write(ISR, RDC);

	// set the boundary register to point to the start of the next packet
	rtl8019Write(BNRY, NextPage);
}


void rtl8019ProcessInterrupt(void)
{
	unsigned char byte = rtl8019Read(ISR);
	
	if( byte & OVW )
		rtl8019ReceiveOverflowRecover();
}

void rtl8019ReceiveOverflowRecover(void)
{
	unsigned char data_L, resend;	

	data_L = rtl8019Read(CR);
	rtl8019Write(CR, 0x21);
	delay_ms(2);
	rtl8019Write(RBCR0, 0x00);
	rtl8019Write(RBCR1, 0x00);
	if(!(data_L & 0x04))
		resend = 0;
	else if(data_L & 0x04)
	{
		data_L = rtl8019Read(ISR);
		if((data_L & 0x02) || (data_L & 0x08))
	    	resend = 0;
	    else
	    	resend = 1;
	}
	
	rtl8019Write(TCR, 0x02);
	rtl8019Write(CR, 0x22);
	rtl8019Write(BNRY, RXSTART_INIT);
	rtl8019Write(CR, 0x62);
	rtl8019Write(CPR, RXSTART_INIT);
	rtl8019Write(CR, 0x22);
	rtl8019Write(ISR, 0x10);
	rtl8019Write(TCR, TCR_INIT);
	
    if(resend)
        rtl8019Write(CR, 0x26);

    rtl8019Write(ISR, 0xFF);
}


void rtl8019RegDump(void)
{
//	unsigned char result;
//	result = rtl8019Read(TR);
	
//	rprintf("Media State: ");
//	if(!(result & AUTOD))
//   		rprintf("Autonegotiation\r\n");
//	else if(result & RST_B)
//   		rprintf("PHY in Reset   \r\n");
//	else if(!(result & RST_10B))
//		rprintf("10BASE-T       \r\n");
//	else if(!(result & RST_TXB))
//		rprintf("100BASE-T      \r\n");
				
	//rprintf("TR regsiter      : %x\r\n",result);
	//result = read_mii(0x10,0);
	//rprintf("MII regsiter 0x10: %x\r\n",result);

	rprintf("Page0: CR  BNRY PSR PST ISR TSR RSR MMR TR  GPI\r\n");
	rprintfProgStrM("       ");
	rprintfu08(rtl8019Read(CR));
	rprintfProgStrM("  ");
	rprintfu08(rtl8019Read(BNRY));
	rprintfProgStrM("   ");
	rprintfu08(rtl8019Read(PSTART));
	rprintfProgStrM("  ");
	rprintfu08(rtl8019Read(PSTOP));
	rprintfProgStrM("  ");
	rprintfu08(rtl8019Read(ISR));
	rprintfProgStrM("  ");
	rprintfu08(rtl8019Read(TSR));
	rprintfProgStrM("  ");
	rprintfu08(rtl8019Read(RSR));
	rprintfProgStrM("  ");
//	rprintfu08(rtl8019Read(MEMR));
	rprintfProgStrM("  ");
//	rprintfu08(rtl8019Read(TR));
	rprintfProgStrM("  ");
//	rprintfu08(rtl8019Read(GPI));
	rprintfCRLF();

	rtl8019Write(CR,rtl8019Read(CR)|PS0);

	rprintf("Page1: CR  PAR    CPR\r\n");
	rprintfProgStrM("       ");
	rprintfu08(rtl8019Read(CR));
	rprintfProgStrM("  ");
	rprintfChar(rtl8019Read(PAR0));
	rprintfChar(rtl8019Read(PAR1));
	rprintfChar(rtl8019Read(PAR2));
	rprintfChar(rtl8019Read(PAR3));
	rprintfChar(rtl8019Read(PAR4));
	rprintfChar(rtl8019Read(PAR5));
	rprintfProgStrM(" ");
	rprintfu08(rtl8019Read(CPR));
	
	rtl8019Write(CR,rtl8019Read(CR)&~PS0);

	delay_ms(25);
}

