/*! \file cs8900.c \brief Crystal CS8900 Ethernet Interface Driver. */
//*****************************************************************************
//
// File Name	: 'cs8900.c'
// Title		: Crystal CS8900 Ethernet Interface Driver
// Author		: Pascal Stang
// Created		: 11/7/2004
// Revised		: 11/7/2004
// Version		: 0.1
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
//*****************************************************************************

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

#include "cs8900.h"

// include configuration
#include "cs8900conf.h"

void nicInit(void)
{
	cs8900Init();
}

void nicSend(unsigned int len, unsigned char* packet)
{
	u08 timeout = 15;

	// request space in CS8900's on-chip memory for storing an outgoing frame
	cs8900Write16(CS8900_IO_TXCMD, TX_START_ALL_BYTES);
	cs8900Write16(CS8900_IO_TXLENGTH, len);
	// check if CS8900 is ready to accept the frame we want to send
	// (timeout after 1.5ms since it should only take 1.25ms to
	//  finish sending previous frame.  If we timeout, it's probably
	//  because there's no link, no ethernet cable.)
	while(!(cs8900ReadReg(PP_BusST) & READY_FOR_TX_NOW) && timeout)
	{
		// wait 100us
		delay_us(100);
		timeout--;
	}
	// write packet data bytes
	cs8900CopyToFrame(packet, len);

	// packet is automatically sent upon completion of above write
}

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

	packetLength = cs8900BeginPacketRetreive();

	// 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 )
	{
		cs8900EndPacketRetreive();
		return 0;
	}
	
	// copy the packet data into the packet buffer
	cs8900RetreivePacketData( packet, packetLength );
	cs8900EndPacketRetreive();
		
	return packetLength;
}

void nicGetMacAddress(u08* macaddr)
{
	// read MAC address registers
	// TODO: check byte order here!
	*((unsigned short*)(macaddr+0)) = cs8900ReadReg(PP_IA+0);
	*((unsigned short*)(macaddr+2)) = cs8900ReadReg(PP_IA+2);
	*((unsigned short*)(macaddr+4)) = cs8900ReadReg(PP_IA+4);
}

void nicSetMacAddress(u08* macaddr)
{
	// write MAC address registers
	cs8900WriteReg(PP_IA+0, (macaddr[1]<<8) + macaddr[0] );
	cs8900WriteReg(PP_IA+2, (macaddr[3]<<8) + macaddr[2] );
	cs8900WriteReg(PP_IA+4, (macaddr[5]<<8) + macaddr[4] );
}

unsigned int cs8900BeginPacketRetreive(void)
{
	unsigned short status;

	// check RxEvent
	status = cs8900ReadReg(PP_RxEvent);

	if( !((status&RX_OK)||(status&RX_IA)||(status&RX_BROADCAST)) )
	{
		return 0;
	}

//	return cs8900ReadReg(PP_RxFrameByteCnt);
	// read RxStatus high-byte first
	status =  cs8900Read(CS8900_IO_RXTX_DATA_PORT0+1)<<8;
	status |= cs8900Read(CS8900_IO_RXTX_DATA_PORT0+0);
	// read packet length high-byte first
	status =  cs8900Read(CS8900_IO_RXTX_DATA_PORT0+1)<<8;
	status |= cs8900Read(CS8900_IO_RXTX_DATA_PORT0+0);

	return status;
}

void cs8900RetreivePacketData(u08* packet, unsigned int packetLength )
{
	cs8900CopyFromFrame(packet, packetLength);
}

void cs8900EndPacketRetreive(void)
{
	// dummy read first four bytes
	//cs8900CopyFromFrame(packet, 4);
}



void cs8900InitPorts(void)
{
#if MEMORY_MAPPED_NIC == 1
  	// enable external SRAM interface - no wait states
  	sbi(MCUSR, SRE);
#else
	// set address port to output
	outb(CS8900_ADDRESS_DDR, CS8900_ADDRESS_MASK);
    
	// set data port to input with pull-ups
	outb(CS8900_DATA_DDR, 0x00);
	outb(CS8900_DATA_PORT, 0xFF);

	// initialize the control port read and write pins to de-asserted
	sbi( CS8900_CONTROL_PORT, CS8900_CONTROL_READPIN );
	sbi( CS8900_CONTROL_PORT, CS8900_CONTROL_WRITEPIN );
	// set the read and write pins to output
	sbi( CS8900_CONTROL_DDR, CS8900_CONTROL_READPIN );
	sbi( CS8900_CONTROL_DDR, CS8900_CONTROL_WRITEPIN );
#endif
	// set reset pin to output
	sbi( CS8900_RESET_DDR, CS8900_RESET_PIN );
}

void cs8900Init(void)
{
	cs8900InitPorts();

	// assert hardware reset
	sbi( CS8900_RESET_PORT, CS8900_RESET_PIN );
	// wait
	delay_ms(10);
	// release hardware reset
	cbi( CS8900_RESET_PORT, CS8900_RESET_PIN );
	delay_ms(10);
	
	// Reset the Ethernet-Controller
	cs8900Write16(CS8900_IO_PP_PTR, PP_SelfCTL);
	cs8900Write16(CS8900_IO_PP_DATA_PORT0, POWER_ON_RESET);
	// wait until chip-reset is done
	cs8900Write16(CS8900_IO_PP_PTR, PP_SelfST);
	while(!(cs8900Read16(CS8900_IO_PP_DATA_PORT0) & INIT_DONE));

	// set our MAC as Individual Address
	cs8900WriteReg(PP_IA+0, (CS8900_MAC1<<8) + CS8900_MAC0 );
	cs8900WriteReg(PP_IA+2, (CS8900_MAC3<<8) + CS8900_MAC2 );
	cs8900WriteReg(PP_IA+4, (CS8900_MAC5<<8) + CS8900_MAC4 );
	// configure the Physical Interface
	cs8900WriteReg(PP_LineCTL, SERIAL_RX_ON | SERIAL_TX_ON);
	cs8900WriteReg(PP_RxCTL, RX_OK_ACCEPT | RX_IA_ACCEPT | RX_BROADCAST_ACCEPT );
}

void cs8900Write(unsigned char address, unsigned char data)
{
	// assert the address
	outb(CS8900_ADDRESS_PORT, address | (inb(CS8900_ADDRESS_PORT)&~CS8900_ADDRESS_MASK));
	// set data bus as output
	outb(CS8900_DATA_DDR, 0xFF);
	// place data on bus
	outb(CS8900_DATA_PORT, data);
	// clock write pin
	cbi(CS8900_CONTROL_PORT, CS8900_CONTROL_WRITEPIN);
	nop();
	nop();
	nop();
	nop();
	sbi(CS8900_CONTROL_PORT, CS8900_CONTROL_WRITEPIN);
	// set data bus back to input with pullups enabled
	outb(CS8900_DATA_DDR, 0x00);
	outb(CS8900_DATA_PORT, 0xFF);
}

unsigned char cs8900Read(unsigned char address)
{
	unsigned char data;
	// assert the address
	outb(CS8900_ADDRESS_PORT, address | (inb(CS8900_ADDRESS_PORT)&~CS8900_ADDRESS_MASK));
	// set data bus to input with pullups enabled
	outb(CS8900_DATA_DDR, 0x00);
	outb(CS8900_DATA_PORT, 0xFF);
	// assert read
	cbi(CS8900_CONTROL_PORT, CS8900_CONTROL_READPIN);
	nop();
	nop();
	nop();
	nop();
	// read in the data
	data = inb( CS8900_DATA_PIN );
	// negate read
	sbi(CS8900_CONTROL_PORT, CS8900_CONTROL_READPIN);
	// return data
	return data;
}

void cs8900Write16(unsigned char address, unsigned short data)
{
	cs8900Write(address+0, data);	
	cs8900Write(address+1, data>>8);	
}

unsigned short cs8900Read16(unsigned char address)
{
	unsigned short data;
	data =  cs8900Read(address+0);
	data |= cs8900Read(address+1)<<8;
	return data;
}

// writes a word in little-endian byte order to a specified PacketPage address
void cs8900WriteReg(unsigned short address, unsigned short data)
{
	cs8900Write16(CS8900_IO_PP_PTR, address);
	cs8900Write16(CS8900_IO_PP_DATA_PORT0, data);
}

// reads a word in little-endian byte order from a specified PacketPage address
unsigned short cs8900ReadReg(unsigned short address)
{
	cs8900Write16(CS8900_IO_PP_PTR, address);
	return cs8900Read16(CS8900_IO_PP_DATA_PORT0);
}

// copies bytes from MCU-memory to frame port
// NOTES: * an odd number of byte may only be transfered
//          if the frame is written to the end!
//        * MCU-memory MUST start at word-boundary

void cs8900CopyToFrame(unsigned char *source, unsigned short size)
{
	while(size>1)
	{
		cs8900Write16(CS8900_IO_RXTX_DATA_PORT0, *((unsigned short*)source));
		source += 2;
		size -= 2;
	}
	// if odd num. of bytes...
	// write leftover byte (the LAN-controller ignores the highbyte)
	if(size)
		cs8900Write16(CS8900_IO_RXTX_DATA_PORT0, *(unsigned char*)source);
}

// copies bytes from frame port to MCU-memory
// NOTES: * an odd number of byte may only be transfered
//          if the frame is read to the end!
//        * MCU-memory MUST start at word-boundary

void cs8900CopyFromFrame(unsigned char *dest, unsigned short size)
{
	while(size>1)
	{
		*((unsigned short *)dest) = cs8900Read16(CS8900_IO_RXTX_DATA_PORT0);
		dest += 2;
		size -= 2;
	}
  
	// check for leftover byte...
	// the LAN-Controller will return 0 for the highbyte
	if(size)	
		*(unsigned char *)dest = cs8900Read16(CS8900_IO_RXTX_DATA_PORT0);
}

void cs8900IORegDump(void)
{
	rprintfProgStrM("CS8900 I/O Registers\r\n");
	rprintfProgStrM(" FRAME   ISQ  ADDR DATA0 DATA1\r\n");
	rprintfProgStrM("-------------------------------\r\n");
	rprintfProgStrM("  ");
	rprintfu16(cs8900Read16(CS8900_IO_RXTX_DATA_PORT0));
	rprintfProgStrM("  ");
	rprintfu16(cs8900Read16(CS8900_IO_ISQ));
	rprintfProgStrM("  ");
	rprintfu16(cs8900Read16(CS8900_IO_PP_PTR));
	rprintfProgStrM("  ");
	rprintfu16(cs8900Read16(CS8900_IO_PP_DATA_PORT0));
	rprintfProgStrM("  ");
	rprintfu16(cs8900Read16(CS8900_IO_PP_DATA_PORT1));
	rprintfCRLF();
}

void cs8900RegDump(void)
{
	rprintfProgStrM("CS8900 PacketPage Registers\r\n");
	rprintfProgStrM("CHIP ID: ");
	rprintfu16(cs8900ReadReg(PP_ChipID));
	rprintfCRLF();

	rprintfProgStrM("PP_ISAIOB: ");
	rprintfu16(cs8900ReadReg(PP_ISAIOB));
	rprintfCRLF();

	rprintfProgStrM("MAC addr: ");
	rprintfu16(cs8900ReadReg(PP_IA+0));
	rprintfu16(cs8900ReadReg(PP_IA+2));
	rprintfu16(cs8900ReadReg(PP_IA+4));
	rprintfCRLF();
}

void nicRegDump(void)
{
	cs8900IORegDump();
	cs8900RegDump();
}


u08 cs8900LinkStatus(void)
{
	if(cs8900ReadReg(PP_LineST) & LINK_OK)
		return 1;
	else
		return 0;
}
