/*
 * (C) Copyright 2000
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <exports.h>
#include "tsec_test.h"

#define NUM_TSEC 2
#define TX_BUF_CNT 1024
#define TX_LEN 60

#define TSEC_DEBUG
#ifdef TSEC_DEBUG
#define DBGPRINT(arg...) printf(arg)
#else
#define DBGPRINT(arg...)
#endif

#define TX_COUNT

typedef volatile struct rtxbd {
	txbd8_t txbd[TX_BUF_CNT];
	rxbd8_t rxbd[TX_BUF_CNT];
}  RTXBD;

int tsec_init(struct eth_device* dev, tsec_t *regs);
uint read_phy_reg(tsec_t *regbase, uint phyid, uint offset);
void init_phy(tsec_t *regs);
void init_registers(tsec_t *regs);
void tsec_halt(struct eth_device* dev, tsec_t *regs);


int tsec_test (int argc, char *argv[])
{
	int i,j, result =0, length;
	struct eth_device *dev[NUM_TSEC];
	tsec_t *regs[NUM_TSEC];	
	
	uint tsec1_rx_count=0, tsec2_rx_count=0;
	uint tsec1_tx_count=0, tsec2_tx_count=0;	
	uint tsec1_tx_result=0, tsec2_tx_result=0;	

	uint rxIdx[2];	/* index of the current RX buffer */
	uint txIdx[2];	/* index of the current TX buffer */
		
	uchar *NetRxPackets1[TX_BUF_CNT]; /* Receive packets */
	uchar *NetRxPackets2[TX_BUF_CNT]; /* Receive packets */

	RTXBD rtx1 __attribute__ ((aligned(8)));
	RTXBD rtx2 __attribute__ ((aligned(8)));

	/* Print the ABI version */
	app_startup(argv);

	DBGPRINT("run tsec_test()\n");
	regs[0] = (tsec_t *)(TSEC_BASE_ADDR);
	regs[1] = (tsec_t *)(TSEC2_BASE_ADDR);

	printf("TSEC_BASE_ADDR = 0x%x\n", (uint)regs[0]);
	printf("TSEC2_BASE_ADDR = 0x%x\n", (uint)regs[1]);
	DBGPRINT("-------------------------------------------------\n");
	
	for (i = 0; i < NUM_TSEC; i++)
	{
		dev[i] = (struct eth_device*) malloc(sizeof dev[i]);						
		if(dev[i] == NULL)
			return 0;
		dev[i]->iobase = 0;
		dev[i]->priv = 0;

		/* Tell project(u-boot) to get the addr from the env */
		for(j=0;j<6;j++)
	    		dev[i]->enetaddr[j] = 0;
		
		rxIdx[i]=0;
		txIdx[i]=0;
	
		/* Point to the buffer descriptors */
		if (i==0)
		{
			regs[0]->tbase = (uint)(&rtx1.txbd[0]);
			regs[0]->rbase = (uint)(&rtx1.rxbd[0]);			
		}
		else
		{
			regs[1]->tbase = (uint)(&rtx2.txbd[0]);
			regs[1]->rbase = (uint)(&rtx2.rxbd[0]);			
		}
			
		printf("regs[%d]->tbase = 0x%0x\n", i, regs[i]->tbase);
		printf("regs[%d]->rbase = 0x%0x\n", i, regs[i]->rbase);

		tsec_init(dev[i], regs[i]);

	}

	/* Initialize the Rx Buffer descriptors */
	for (j = 0; j < TX_BUF_CNT; j++) 
	{
		rtx1.rxbd[j].status = RXBD_EMPTY;
		rtx1.rxbd[j].length = 0;

		NetRxPackets1[j] = (uchar*) malloc(1518);		
		rtx1.rxbd[j].bufPtr = (uint)NetRxPackets1[j];
#ifdef TSEC_DEBUG
		printf("tsec1: rxbd[%d].status = 0x%0x\n", j, rtx1.rxbd[j].status);
		printf("tsec1: rxbd[%d].length = 0x%0x\n", j, rtx1.rxbd[j].length);
		printf("tsec1: rxbd[%d].bufPtr = 0x%0x\n", j, rtx1.rxbd[j].bufPtr);				
#endif
		rtx2.rxbd[j].status = RXBD_EMPTY;
		rtx2.rxbd[j].length = 0;
				
		NetRxPackets2[j] = (uchar*) malloc(1518);		
		rtx2.rxbd[j].bufPtr = (uint)NetRxPackets2[j];
#ifdef TSEC_DEBUG
		printf("tsec2: rxbd[%d].status = 0x%0x\n", j, rtx2.rxbd[j].status);
		printf("tsec2: rxbd[%d].length = 0x%0x\n", j, rtx2.rxbd[j].length);
		printf("tsec2: rxbd[%d].bufPtr = 0x%0x\n", j, rtx2.rxbd[j].bufPtr);		
#endif		
	}
	rtx1.rxbd[TX_BUF_CNT -1].status |= RXBD_WRAP;
#ifdef TSEC_DEBUG	
	printf("tsec1: rtx[%d].rxbd[TX_BUF_CNT -1].status = 0x%0x\n", i, rtx1.rxbd[TX_BUF_CNT -1].status);
#endif	

	rtx2.rxbd[TX_BUF_CNT -1].status |= RXBD_WRAP;			
#ifdef TSEC_DEBUG	
	printf("tsec2: rtx[%d].rxbd[TX_BUF_CNT -1].status = 0x%0x\n", i, rtx2.rxbd[TX_BUF_CNT -1].status);
#endif
				
	/* Initialize the TX Buffer Descriptors */
	for(j=0; j<TX_BUF_CNT; j++) 
	{
		rtx1.txbd[j].status = 0;
		rtx1.txbd[j].length = 0;
		rtx1.txbd[j].bufPtr = 0;

		rtx2.txbd[j].status = 0;
		rtx2.txbd[j].length = 0;
		rtx2.txbd[j].bufPtr = 0;

		rtx1.txbd[j].length = TX_LEN;	
		rtx1.txbd[j].bufPtr = (uint)rtx2.rxbd[rxIdx[1]].bufPtr;

		rtx2.txbd[j].length = TX_LEN;	
		rtx2.txbd[j].bufPtr = (uint)rtx1.rxbd[rxIdx[0]].bufPtr;
		
	}
	rtx1.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP;
	rtx2.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP;
	
	/* Enable Transmit and Receive */
	regs[0]->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);

	/* Tell the DMA it is clear to go */
	regs[0]->dmactrl |= DMACTRL_INIT_SETTINGS;
	regs[0]->tstat = TSTAT_CLEAR_THALT;
	regs[0]->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);				
				
	/* Enable Transmit and Receive */
	regs[1]->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);

	/* Tell the DMA it is clear to go */
	regs[1]->dmactrl |= DMACTRL_INIT_SETTINGS;
	regs[1]->tstat = TSTAT_CLEAR_THALT;
	regs[1]->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
																			
	printf("-------------------------------------------------\n");
	
//	while(1)
	while(!tstc())
	{

		rtx2.txbd[txIdx[1]].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);

		/* Tell the DMA to go */
		regs[1]->tstat = TSTAT_CLEAR_THALT;
		txIdx[1] = (txIdx[1] + 1) % TX_BUF_CNT;
		tsec2_tx_result = rtx2.txbd[txIdx[1]].status & TXBD_STATS;
		tsec2_tx_count++;


		rtx1.txbd[txIdx[0]].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);

		/* Tell the DMA to go */
		regs[0]->tstat = TSTAT_CLEAR_THALT;
		txIdx[0] = (txIdx[0] + 1) % TX_BUF_CNT;
		tsec1_tx_result = rtx1.txbd[txIdx[0]].status & TXBD_STATS;
		tsec1_tx_count++;
		
	}
	
	/* consume input */
	(void) getc();		
	
	printf ("\n\n");

	printf("tsec1 tx count = %d\n", tsec1_tx_count);
	printf("tsec2 tx count = %d\n", tsec2_tx_count);

	return (0);
}


uint read_phy_reg(tsec_t *regbase, uint phyid, uint offset)
{
	uint value;

	/* Put the address of the phy, and the register number into
	 * MIIMADD
	 */	 	
	regbase->miimadd = (phyid << 8) | offset;

	/* Clear the command register, and wait */
	regbase->miimcom = 0;
	asm("msync");

	/* Initiate a read command, and wait */
	regbase->miimcom = MIIM_READ_COMMAND;
	asm("msync");

	/* Wait for the the indication that the read is done */
	while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY)));

	/* Grab the value read from the PHY */
	value = regbase->miimstat;

	return value;
}

/* Setup the PHY */
void init_phy(tsec_t *target_regs)
{	
	uint testval;
	tsec_t *regs;
	
	unsigned int timeout = TSEC_TIMEOUT;

	DBGPRINT("init_phy()\n");
	
	regs = (tsec_t *)(TSEC_BASE_ADDR);

	/* Assign a Physical address to the TBI */
	target_regs->tbipa=TBIPA_VALUE;

	/* reset the management interface */
	regs->miimcfg=MIIMCFG_RESET;

	regs->miimcfg=MIIMCFG_INIT_VALUE;

	/* Wait until the bus is free */
	while(regs->miimind & MIIMIND_BUSY);

	/* Set the PHY to gigabit, full duplex, Auto-negotiate */
	if(regs == target_regs)
		write_phy_reg(regs, 0, MIIM_CONTROL, MIIM_CONTROL_INIT);	/* phyid = 0 */
	else
		write_phy_reg(regs, 1, MIIM_CONTROL, MIIM_CONTROL_INIT);	/* phyid = 1 */

	/* Wait until TBI_STATUS indicates AN is done */
	DBGPRINT("Waiting for Auto-negotiation to complete\n");
	if(regs == target_regs)
		testval=read_phy_reg(regs, 0, MIIM_TBI_STATUS);
	else
		testval=read_phy_reg(regs, 1, MIIM_TBI_STATUS);	

	DBGPRINT("While loop for MIIM_TBI_STATUS_AN_DONE\n");
	while((!(testval & MIIM_TBI_STATUS_AN_DONE))&& timeout--) {
		if(regs == target_regs)
			testval=read_phy_reg(regs, 0, MIIM_TBI_STATUS);
		else
			testval=read_phy_reg(regs, 1, MIIM_TBI_STATUS);
	}

	if(testval & MIIM_TBI_STATUS_AN_DONE)
		DBGPRINT("Auto-negotiation done\n");
	else
		DBGPRINT("Auto-negotiation timed-out.\n");

/* #if (CONFIG_PHY_M88E1011 | CONFIG_PHY_M88E1111) */

	/* Read the PHY to see what speed and duplex we are */
	if(regs == target_regs)
		testval=read_phy_reg(regs, 0, MIIM_PHY_STATUS);
	else
		testval=read_phy_reg(regs, 1, MIIM_PHY_STATUS);

	timeout = TSEC_TIMEOUT;
	while((!(testval & MIIM_PHYSTAT_SPDDONE)) && timeout--) {
		if(regs == target_regs)	
			testval = read_phy_reg(regs,0,MIIM_PHY_STATUS);
		else
			testval = read_phy_reg(regs,1,MIIM_PHY_STATUS);
	}

	if(!(testval & MIIM_PHYSTAT_SPDDONE))
		DBGPRINT("Enet: Speed not resolved\n");

	if(regs == target_regs)	
		testval=read_phy_reg(regs, 0, MIIM_PHY_STATUS);
	else
		testval=read_phy_reg(regs, 1, MIIM_PHY_STATUS);
	
	DBGPRINT("Enet starting in Full Duplex\n");
	target_regs->maccfg2 |= MACCFG2_FULL_DUPLEX;

	if(!((testval&MIIM_PHYSTAT_SPEED) == MIIM_PHYSTAT_GBIT)) {
		if((testval & MIIM_PHYSTAT_SPEED) == MIIM_PHYSTAT_100)
			DBGPRINT("Enet starting in 100BT\n");
		else
			DBGPRINT("Enet starting in 10BT\n");

		target_regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | MACCFG2_MII);
	} else {
		DBGPRINT("Enet starting in 1000BT\n");
	}
/*#endif*/

}


void init_registers(tsec_t *regs)
{
	/* Clear IEVENT */
   volatile immap_t *immap = (immap_t *)CFG_IMMR;
	volatile ccsr_l2cache_t *l2= &immap->im_l2cache;
	DBGPRINT("init_registers()\n");

/**************************************************************************************/
	/* L2 locking of TSEC TxBDs using L2 programmability */

	printf("regs = 0x%x\n",regs);
	if (regs == (tsec_t *)(TSEC_BASE_ADDR))
	{
		l2->l2cewcr0 = 0;
		asm("sync; isync; msync");
		l2->l2cewar0 = regs->tbase;
		l2->l2cewcr0 = ((0x00ffffff - (16 * TX_BUF_CNT) + 1) | 0xc0000000);
		asm("sync; isync; msync");
		printf("L2CEWAR0 = 0x%x\n",l2->l2cewar0);
		printf("L2CEWCR0 = 0x%x\n",l2->l2cewcr0);
		printf("TSEC1 TxBD at 0x%x size 0x%x bytes locked in L2 cache\n",regs->tbase, (16 * TX_BUF_CNT)); 
	} else {
		l2->l2cewcr1 = 0;
		asm("sync; isync; msync");
		l2->l2cewar1 = regs->tbase;
		l2->l2cewcr1 = ((0x00ffffff - (16 * TX_BUF_CNT) + 1) | 0xc0000000);
		asm("sync; isync; msync");
		printf("L2CEWAR1 = 0x%x\n",l2->l2cewar1);
		printf("L2CEWCR1 = 0x%x\n",l2->l2cewcr1);
		printf("TSEC2 TxBD at 0x%x size 0x%x bytes locked in L2 cache\n",regs->tbase, (16 * TX_BUF_CNT)); 
	}

#define FIFO_TX_THR					0x10
#define FIFO_TX_STARVE				0x8
#define FIFO_TX_STARVE_SHUTOFF	0x10

/* Attribute Register (ATTR) */
#define ATTR_ELCWT_NONE				0x00000000
#define ATTR_ELCWT_ALLOC_L2		0x00004000
#define ATTR_ELCWT_ALLOC_LL2		0x00006000
#define ATTR_BDLWT_NONE				0x00000000
#define ATTR_BDLWT_ALLOC_L2		0x00000800
#define ATTR_BDLWT_ALLOC_LL2		0x00000c00
#define ATTR_RDSEN					0x00000080
#define ATTR_RBDSEN					0x00000040

/* Attribute Extract Length and Extract Index Register (ATTRELI) */
#define ATTRELI_EL					0x3FFF0000
#define ATTRELI_ELs					(31-15)
#define ATTRELI_EI					0x00003FFF
#define ATTRELI_EIs					(31-31)


	regs->attr = (ATTR_ELCWT_ALLOC_L2 | ATTR_BDLWT_ALLOC_LL2 | ATTR_RDSEN | ATTR_RBDSEN);
	regs->attreli = (0 << ATTRELI_EIs) | (63 << ATTRELI_ELs);
	
	regs->fifo_tx_thr = FIFO_TX_THR;		/* 0x.08c - FIFO transmit threshold register */
	regs->fifo_tx_starve = FIFO_TX_STARVE;		/* 0x.098 - FIFO transmit starve register */
	regs->fifo_tx_starve_shutoff = FIFO_TX_STARVE_SHUTOFF;	/* 0x.09c - FIFO transmit starve shutoff register */
	/* rapncsg1 IFG setting */
	regs->ipgifg = 0x40605060;

	regs->ievent = IEVENT_INIT_CLEAR;
	regs->imask = IMASK_INIT_CLEAR;

	regs->hash.iaddr0 = 0;
	regs->hash.iaddr1 = 0;
	regs->hash.iaddr2 = 0;
	regs->hash.iaddr3 = 0;
	regs->hash.iaddr4 = 0;
	regs->hash.iaddr5 = 0;
	regs->hash.iaddr6 = 0;
	regs->hash.iaddr7 = 0;

	regs->hash.gaddr0 = 0;
	regs->hash.gaddr1 = 0;
	regs->hash.gaddr2 = 0;
	regs->hash.gaddr3 = 0;
	regs->hash.gaddr4 = 0;
	regs->hash.gaddr5 = 0;
	regs->hash.gaddr6 = 0;
	regs->hash.gaddr7 = 0;

	regs->rctrl = 0x00000000;

	/* Init RMON mib registers */
	/* memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));*/

	regs->rmon.cam1 = 0xffffffff;
	regs->rmon.cam2 = 0xffffffff;

	regs->mrblr = MRBLR_INIT_SETTINGS;

	regs->minflr = MINFLR_INIT_SETTINGS;


	printf("attr = 0x%x\n",regs->attr);
	printf("attreli = 0x%x\n",regs->attreli);
	printf("fifo_tx_thr = 0x%x\n",regs->fifo_tx_thr);
	printf("fifo_tx_starve = 0x%x\n",regs->fifo_tx_starve);
	printf("fifo_tx_starve_shutoff = 0x%x\n",regs->fifo_tx_starve_shutoff);
	printf("IPGIFG = 0x%x\n",regs->ipgifg); 

}


void tsec_halt(struct eth_device* dev, tsec_t *regs)
{
	/* tsec_t *regs = (tsec_t *)(TSEC_BASE_ADDR); */

	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
	regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);

	while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC)));

	regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);

}

/* Initializes data structures and registers for the controller,
 * and brings the interface up */
int tsec_init(struct eth_device* dev, tsec_t *regs)
{
	uint tempval;
	char tmpbuf[MAC_ADDR_LEN];
	int i;
		
	DBGPRINT("tsec_init()\n");
		
	/* Make sure the controller is stopped */
	tsec_halt(dev, regs);

	/* Reset the MAC */
	regs->maccfg1 |= MACCFG1_SOFT_RESET;

	/* Clear MACCFG1[Soft_Reset] */
	regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);

	/* Init MACCFG2.  Defaults to GMII/MII */
	regs->maccfg2 = MACCFG2_INIT_SETTINGS;

	/* Init ECNTRL */
	regs->ecntrl = ECNTRL_INIT_SETTINGS;

	/* Copy the station address into the address registers.
	 * Backwards, because little endian MACS are dumb */
	for(i=0;i<MAC_ADDR_LEN;i++) {
		tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];
	}
	(uint)(regs->macstnaddr1) = *((uint *)(tmpbuf));

	tempval = *((uint *)(tmpbuf +4));

	(uint)(regs->macstnaddr2) = tempval;

	/* Initialize the PHY */
	init_phy(regs);

	/* Clear out (for the most part) the other registers */
	init_registers(regs);
	return 1;
}

