/*-------------------------------------------------------------------------
   Realtek RTL8019AS function copy right information

   Copyright (C) 2006-2008. Lin, Rongrong <woody@palmmicro.com>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-------------------------------------------------------------------------*/

#include "version.h"
#include "type.h"

#ifdef RTL8019AS

#include "sfr_gpio.h"
#include "ne2000.h"
#include "apps.h"
#include "core.h"
#include "ar168.h"
#include "tcpip.h"
#include "bank2.h"

//#define DEBUG_OVERFLOW

UCHAR _iCurrentPacket;

BOOLEAN _bAdapterOK;

extern USHORT _sLayer2Qos;
extern BOOLEAN _bVLan;
extern USHORT _sCurPacketAddr;

void _CardWaitStatus(UCHAR iStatus)
{
	PCHAR p;

	p = (PCHAR)BANK_BASE;
	while (!(p[NIC_INTR_STATUS] & iStatus));
}

void Ne2000Read(USHORT sAddr, PCHAR pBuf, USHORT sLen)
{
//	UCHAR iDummy;
	PCHAR p;

	p = (PCHAR)BANK_BASE;

	EnableCE1();
    // Read the Command Register, to make sure it is ready for a write
//    iDummy = p[NIC_COMMAND];

    // Set iCount and destination address)
	p[NIC_COMMAND] = CR_PAGE0;
	USHORT2PCHAR_L(sAddr, (PCHAR)(BANK_BASE + NIC_RMT_ADDR_LSB));
//	p[NIC_RMT_ADDR_LSB] = (UCHAR)sAddr;
//	p[NIC_RMT_ADDR_MSB] = (UCHAR)(sAddr >> 8);
	USHORT2PCHAR_L(sLen, (PCHAR)(BANK_BASE + NIC_RMT_COUNT_LSB));
//	p[NIC_RMT_COUNT_LSB] = (UCHAR)sLen;
//	p[NIC_RMT_COUNT_MSB] = (UCHAR)(sLen >> 8);

    // Set direction (Read)
    p[NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_READ;
	fixed_input(pBuf, sLen);

    // Wait for DMA to complete
    _CardWaitStatus(ISR_DMA_DONE);
	DisableCEx();
}

UCHAR _CardGetCurrent()
{
	UCHAR iCurrent;
	PCHAR p;

	p = (PCHAR)BANK_BASE;

	EnableCE1();
   	p[NIC_COMMAND] = CR_START + CR_NO_DMA + CR_PAGE1;	// Have to go to page 1 to read NIC_CURRENT register
	iCurrent = p[NIC_CURRENT];
	p[NIC_COMMAND] =  CR_START + CR_NO_DMA + CR_PAGE0;
	DisableCEx();

	return iCurrent;
}

void _CardSetBoundary(UCHAR iCur)
{
	PCHAR p;

	p = (PCHAR)BANK_BASE;

	// Sets the value of the "boundary" NIC register to one behind _iCurrentPacket, 
	// to prevent packets from being received on top of un-indicated ones.
	// Have to be careful with "one behind NicNextPacket" when NicNextPacket is the first buffer in receive area.
	EnableCE1();
	p[NIC_BOUNDARY] = (iCur == NE2000_NIC_PAGE_START) ? (NE2000_NIC_PAGE_STOP - 1) : (iCur - 1);
	DisableCEx();
}

#ifdef DEBUG_OVERFLOW
void _CardCheckInterrupt()
{
	PCHAR p;
	UCHAR iStatus;

	p = (PCHAR)BANK_BASE;

	EnableCE1();
	iStatus = p[NIC_INTR_STATUS];
	if (iStatus != ISR_EMPTY)
	{	// Acknowledge the interrupt
		p[NIC_INTR_STATUS] = iStatus;
		DisableCEx();
		if (iStatus & IMR_OVERFLOW)
		{
			UdpDebugString("NE2000 overflow");
		}
		if (iStatus & IMR_RCV_ERR)
		{
			UdpDebugString("NE2000 receive error");
		}
		if (iStatus & IMR_COUNTER)
		{
			UdpDebugString("NE2000 counter");
		}
	}
	else
	{
		DisableCEx();
	}
}
#endif

void Ne2000Init()
{
	PCHAR p;

	p = (PCHAR)BANK_BASE;

	EnableCE1();
    // Check that Command register reflects this last command
    if (!(p[NIC_COMMAND] & CR_STOP))
    {
		goto Error;
    }

    p[NIC_RMT_COUNT_LSB] = 55;				// Do initialization errata
    p[NIC_COMMAND] = CR_DMA_READ + CR_START;	// Setup for a read
    p[NIC_INTR_MASK] = 0x0;					// Mask Interrupts
    p[NIC_DATA_CONFIG] = DCR_FIFO_8_BYTE + DCR_NORMAL + DCR_BYTE_WIDE;    // Setup the Adapter for reading ram
    p[NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;	    // Clear transmit configuration.
    p[NIC_RCV_CONFIG] = 0;				    // Clear receive configuration.
    p[NIC_INTR_STATUS] = 0xff;			    // Clear any interrupts
    p[NIC_COMMAND] = CR_NO_DMA + CR_STOP;	    // Stop the chip
    p[NIC_RMT_COUNT_LSB] = 0;					// Clear any DMA values
    p[NIC_RMT_COUNT_MSB] = 0;				    // Clear any DMA values

    // Wait for the reset to complete.
	_CardWaitStatus(ISR_RESET);

    p[NIC_XMIT_CONFIG] = TCR_LOOPBACK;	    // Put card in loopback mode
    p[NIC_COMMAND] = CR_NO_DMA + CR_START;    // Start the chip.
    p[NIC_COMMAND] = CR_STOP + CR_NO_DMA;	    // Stop the chip

    // Setup the card based on the initialization information
    p[NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE0;	    // Write to and read from CR to make sure it is there.
    if ((p[NIC_COMMAND] & (CR_STOP + CR_NO_DMA + CR_PAGE0)) != (CR_STOP + CR_NO_DMA + CR_PAGE0))
    {
		goto Error;
    }

    // Set up the registers in the correct sequence, as defined by the 8390 specification.
    p[NIC_DATA_CONFIG] = DCR_BYTE_WIDE + DCR_NORMAL + DCR_FIFO_8_BYTE;
    p[NIC_RMT_COUNT_MSB] = 0;
    p[NIC_RMT_COUNT_LSB] = 0;
    p[NIC_RCV_CONFIG] = RCR_REJECT_ERR + RCR_BROADCAST;
    p[NIC_XMIT_CONFIG] = TCR_LOOPBACK;
    p[NIC_BOUNDARY] = NE2000_NIC_PAGE_START;
    p[NIC_PAGE_START] = NE2000_NIC_PAGE_START;
    p[NIC_PAGE_STOP] = NE2000_NIC_PAGE_STOP;

    p[NIC_INTR_STATUS] = 0xff;	    
#ifdef DEBUG_OVERFLOW
	p[NIC_INTR_MASK] = IMR_OVERFLOW + IMR_RCV_ERR + IMR_COUNTER;   // Enable overflow interrupt.
#else
    p[NIC_INTR_MASK] = 0;
#endif

/*  93c46 
	// Setup to read the ethernet address
    p[NIC_RMT_COUNT_LSB] = 12;
    p[NIC_RMT_COUNT_MSB] = 0;
    p[NIC_RMT_ADDR_LSB] = 0;
    p[NIC_RMT_ADDR_MSB] = 0;
    p[NIC_COMMAND] = CR_START | CR_DMA_READ;

    // Read in the station address. (We have to read words -- 2 * 6 -- bytes)
    for (i = 0; i < HW_ALEN; i ++)
    {
        Sys_pMacAddress[i] = p[NIC_RACK_NIC];
        Sys_pMacAddress[i] = p[NIC_RACK_NIC];
    }
*/

    // Move to page 1 to write the station address
	p[NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE1;
	memcpy((PCHAR)(BANK_BASE+NIC_PHYS_ADDR), Sys_pMacAddress, HW_ALEN);		// memcpy MUST be in common bank

    // Write out the current receive buffer to receive into
    _iCurrentPacket = NE2000_NIC_PAGE_START + 1;
    p[NIC_CURRENT] = _iCurrentPacket;

    // move back to page 0 and start the card...
    p[NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE0;
    p[NIC_COMMAND] = CR_START + CR_NO_DMA + CR_PAGE0;

    // ... but it is still in loopback mode.
    // Start up the adapter.
    p[NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;

	DisableCEx();
	_bAdapterOK = TRUE;

    return;

Error:
	DisableCEx();
	_bAdapterOK = FALSE;
	LcdDisplay("Ne2000 error", 1);
}

#ifdef CALL_NONE

void _Delay5ms()
{
	UCHAR i;
	
	for (i = 0; i < 5; i ++)
	{
		delay1ms();
	}
}

// Ne2000 functions
void Ne2000InitStep1()
{
	PCHAR p;

	p = (PCHAR)BANK_BASE;

#ifndef SYS_CHIP_PLUS
	// GPIO G3 as ethernet RESET
	rGPIO_G_Enable = (rGPIO_G_Enable & 0x7f) | 0x08;	// 01111111, 00001000
	rGPIO_G &= 0xf7;			// 11110111

	rGPIO_G |= 0x08;			// 00001000
	_Delay5ms();
	rGPIO_G &= 0xf7;			// 11110111
#elif defined VER_AR168I || defined VER_AR168MS
	// GPIO A2 as ethernet RESET
	rGPIO_A &= 0xfb;			// 11111011

	rGPIO_A |= 0x04;			// 00000100
	_Delay5ms();
	rGPIO_A &= 0xfb;			// 11111011
#elif defined VER_AR168L || defined VER_AR168LS || defined VER_AR168M
	// GPIO G0 as ethernet RESET
	rGPIO_G_Enable |= 0x01;

	rGPIO_G &= 0xfe;

	rGPIO_G |= 0x01;
	_Delay5ms();
	rGPIO_G &= 0xfe;
#endif

#ifdef SYS_PROGRAMMER
	// set GPIO D4 (UART RX) as input
	rGPIO_D_Input_Enable |= 0x10;
	rGPIO_D &= 0xef;
#else
	// set GPIO C1 as input, IRQ
	rGPIO_C_Enable = (rGPIO_C_Enable & 0xfd) | 0x20;		// 11111101, 00100000
	rGPIO_C &= 0xfd;
#endif

	EnableCE1();

	// Initializes the card into a running state.
    p[NIC_INTR_MASK] = 0;						// Turn off interrupts first.
    p[NIC_COMMAND] = CR_STOP + CR_NO_DMA;		// Stop the card.
    p[NIC_DATA_CONFIG] = DCR_AUTO_INIT + DCR_FIFO_8_BYTE;	    // Initialize the Data Configuration register.
    p[NIC_XMIT_START] = 0xa0;					// Set Xmit start location
    p[NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;		// Set Xmit configuration
    p[NIC_RCV_CONFIG] = RCR_MONITOR;			// Set Receive configuration
    p[NIC_PAGE_START] = 0x4;					// Set Receive start
    p[NIC_PAGE_STOP] = 0xFF;					// Set Receive end
    p[NIC_BOUNDARY] = 0x4;					// Set Receive boundary
    p[NIC_XMIT_COUNT_LSB] = 0x3C;				// Set Xmit bytes
    p[NIC_XMIT_COUNT_MSB] = 0x0;
	DisableCEx();
}

void Ne2000InitStep2()
{
	PCHAR p;

	p = (PCHAR)BANK_BASE;

	EnableCE1();
    p[NIC_INTR_STATUS] = 0xff;				// Ack all interrupts that we might have produced
    p[NIC_COMMAND] = CR_PAGE1 + CR_STOP;		// Change to page 1
    p[NIC_CURRENT] = 0x4;						// Set current
    p[NIC_COMMAND] = CR_PAGE0 + CR_STOP;		// Back to page 0
	DisableCEx();
}
#else

void Ne2000MiniRun()
{
	UCHAR iCurrent, iNext;

#ifdef DEBUG_OVERFLOW
	_CardCheckInterrupt();
#endif

	Ne2000Read(_sCurPacketAddr + HEADER_NEXT_PACKET, &iNext, 1);
	while (1)
	{
		iCurrent = _CardGetCurrent();
		if (iCurrent != iNext)
		{
			iNext = ReadPacket(iNext, TRUE);
			if (!iNext)
			{
				break;
			}
		}
		else
		{
			break;
		}
	}
}

#endif

void Ne2000Run()
{
	UCHAR iCurrent, iNum;

	if (!_bAdapterOK)	return;

#ifdef DEBUG_OVERFLOW
	_CardCheckInterrupt();
#endif

	iNum = 0;
	do
	{
		iCurrent = _CardGetCurrent();
		if (iCurrent != _iCurrentPacket)
		{
			_iCurrentPacket = ReadPacket(_iCurrentPacket, FALSE);
			if (!_iCurrentPacket)	        // Read packet
			{
				_iCurrentPacket = iCurrent;
			}
			_CardSetBoundary(_iCurrentPacket);
		}
		else
		{
			break;
		}
		iNum ++;
	} while (iNum < ETHERNET_MAX_READ_PACKET);
}

void Ne2000Write(USHORT sAddr, PCHAR pBuf, USHORT sLen)
{
//	UCHAR iDummy;
	PCHAR p;

//	if (!_bAdapterOK)	return;

	p = (PCHAR)BANK_BASE;

	EnableCE1();

    // Read the Command Register, to make sure it is ready for a write
//    iDummy = p[NIC_COMMAND];
/*
    // Do Write errata as described on pages 1-143 and 1-144 of the 1992 LAN databook
	p[NIC_COMMAND] = CR_PAGE0;
	USHORT2PCHAR_L(sAddr, (PCHAR)(BANK_BASE+NIC_RMT_ADDR_LSB));
    p[NIC_RMT_COUNT_LSB] = 0x2;
    p[NIC_RMT_COUNT_MSB] = 0x0;

    // Set direction (Read)
    p[NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_READ;

    // Read from port
	iDummy = p[NIC_RACK_NIC];
    iDummy = p[NIC_RACK_NIC];

    // Wait for addr to change
	while ((p[NIC_CRDA_LSB] == (UCHAR)sAddr) && (p[NIC_CRDA_MSB] == (UCHAR)(sAddr >> 8)));
*/
    // Set sCount and destination address
	p[NIC_COMMAND] = CR_PAGE0;
	USHORT2PCHAR_L(sAddr, (PCHAR)(BANK_BASE + NIC_RMT_ADDR_LSB));
//	p[NIC_RMT_ADDR_LSB] = (UCHAR)sAddr;
//	p[NIC_RMT_ADDR_MSB] = (UCHAR)(sAddr >> 8);
	USHORT2PCHAR_L(sLen, (PCHAR)(BANK_BASE + NIC_RMT_COUNT_LSB));
//	p[NIC_RMT_COUNT_LSB] = (UCHAR)sLen;
//	p[NIC_RMT_COUNT_MSB] = (UCHAR)(sLen >> 8);

    // Set direction (Write)
    p[NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_WRITE;
	fixed_output(pBuf, sLen);

    // Wait for DMA to complete
	_CardWaitStatus(ISR_DMA_DONE);
	DisableCEx();
}

void Ne2000Send(USHORT sLen, UCHAR iType)
{
	UCHAR iDummy;
	PCHAR p;
#ifndef CALL_NONE
	UCHAR pHead[EP_DATA + VLAN_HEADER_SIZE];
#endif

	if (!_bAdapterOK)	return;

	p = (PCHAR)BANK_BASE;

#ifndef CALL_NONE
	if (_bVLan)
	{
		memcpy(pHead, Adapter_pPacketBuf, EP_TYPE);
		USHORT2PCHAR(EPT_VLAN, (PCHAR)(pHead + EP_TYPE));
		USHORT2PCHAR(_sLayer2Qos, (PCHAR)(pHead + EP_TYPE + VLAN_TCI));
		memcpy((PCHAR)(pHead + EP_TYPE + VLAN_HEADER_SIZE), (PCHAR)(Adapter_pPacketBuf + EP_TYPE), 2);
		Ne2000Write(NE2000_RAM_BASE, pHead, EP_DATA + VLAN_HEADER_SIZE);
	}
#endif

	if (iType == NET_SEND_NORMAL)
	{
		if (_bVLan)
		{
			Ne2000Write(NE2000_RAM_BASE + EP_DATA + VLAN_HEADER_SIZE, (PCHAR)(Adapter_pPacketBuf + EP_DATA), sLen - EP_DATA);
			sLen += VLAN_HEADER_SIZE;
		}
		else
		{
			Ne2000Write(NE2000_RAM_BASE, Adapter_pPacketBuf, sLen);
		}
	}
#ifndef CALL_NONE
	else if (iType == NET_SEND_PPPOE)
	{
		if (_bVLan)
		{
			Ne2000Write(NE2000_RAM_BASE + EP_DATA + VLAN_HEADER_SIZE, PPPoE_pSendHead, PPPOE_INFO);
			Ne2000Write(NE2000_RAM_BASE + EP_DATA + VLAN_HEADER_SIZE + PPPOE_INFO, (PCHAR)(Adapter_pPacketBuf + EP_DATA), sLen - EP_DATA);
			sLen += VLAN_HEADER_SIZE + PPPOE_INFO;
		}
		else
		{
			Ne2000Write(NE2000_RAM_BASE, Adapter_pPacketBuf, EP_DATA);
			Ne2000Write(NE2000_RAM_BASE + EP_DATA, PPPoE_pSendHead, PPPOE_INFO);
			Ne2000Write(NE2000_RAM_BASE + EP_DATA + PPPOE_INFO, (PCHAR)(Adapter_pPacketBuf + EP_DATA), sLen - EP_DATA);
			sLen += PPPOE_INFO;
		}
	}
#endif

	EnableCE1();

#ifdef DEBUG_OVERFLOW
	p[NIC_INTR_MASK] |= IMR_XMIT + IMR_XMIT_ERR;   // Enable transmit interrupts.
#else
	p[NIC_INTR_MASK] = IMR_XMIT + IMR_XMIT_ERR;   // Enable transmit interrupts.
#endif

    // Prepare the NIC registers for transmission.
    p[NIC_XMIT_START] = NE2000_NIC_XMIT_START;
	USHORT2PCHAR_L((sLen < ETHERNET_MIN_SIZE) ? ETHERNET_MIN_SIZE : sLen, (PCHAR)(BANK_BASE+NIC_XMIT_COUNT_LSB));
/*	if (sLen < ETHERNET_MIN_SIZE)		sLen = ETHERNET_MIN_SIZE;
	p[NIC_XMIT_COUNT_LSB] = (UCHAR)sLen;
	p[NIC_XMIT_COUNT_MSB] = (UCHAR)(sLen >> 8);
*/
    // Start transmission, check for power failure first.
//    iDummy = p[NIC_COMMAND];
    p[NIC_COMMAND] = CR_START + CR_XMIT + CR_NO_DMA;

	do
	{
#ifdef SYS_PROGRAMMER
		if (rGPIO_D & 0x10)			// Wait for the current send to complete, GPIO D4 to high
#else
		if (rGPIO_C & 0x02)			// Wait for the current send to complete, GPIO C1 to high
#endif
		{
		    // Get any new interrupts
			iDummy = p[NIC_INTR_STATUS];
//		    if (iDummy != ISR_EMPTY)
		    if (iDummy)
			{
				// Acknowledge the interrupt
				p[NIC_INTR_STATUS] = iDummy;
		        if (iDummy & (ISR_XMIT + ISR_XMIT_ERR))
			    {
					break;
				}
			}
		}
	} while (1);
#ifdef DEBUG_OVERFLOW
	p[NIC_INTR_MASK] &= ~(IMR_XMIT + IMR_XMIT_ERR);
#else
    p[NIC_INTR_MASK] = 0;
#endif

	DisableCEx();
}

#endif
