/**************************************************************************
Etherboot -  BOOTP/TFTP Bootstrap Program
Skeleton NIC driver for Etherboot
***************************************************************************/

/*
 * 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, or (at
 * your option) any later version.
 */

/*
 * This file is a modified version from the Galileo polled mode
 * network driver for the ethernet contained within the GT64260
 * chip. It has been modified to fit into the ppcboot framework, from
 * the original (etherboot) setup.  Also, additional cleanup and features
 * were added.
 *
 * - Josh Huber <huber@mclx.com>
 */

#include <ppcboot.h>
#include <malloc.h>
#include <galileo/gt64260R.h>
#include <galileo/core.h>
#include "eth.h"

/* globals */
eth0_tx_desc_single *eth0_tx_desc;
eth0_rx_desc_single *eth0_rx_desc;
char *eth0_tx_buffer;
char *eth0_rx_buffer[NR];

unsigned long pci_dram_offset = 0;

char *eth_data;

/* if you like verbose output, turn this on! */
#undef DEBUG

/* calculate the offset to the desired ethernet registers */
/* the registers are offset by 0x400 for each port -- see the headers */
#define REG_OFF (CONFIG_ETHER_PORT * 0x400)

/**************************************************************************
RESET - Reset adapter
***************************************************************************/
void
evb64260_eth0_reset(bd_t *bis)
{
	printf ("RESET\n");
	/* put the card in its initial state */
}

/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
int
evb64260_eth0_poll(void)
{
	unsigned int eth_len;

	if (eth0_rx_desc[(RDN_ETH0)].command_status & 0x80000000) {
		return 0; // No packet received
	}

	eth_len = (unsigned int)
		(eth0_rx_desc[(RDN_ETH0)].buff_size_byte_count) & 0x0000ffff;
	eth_data = (char *) eth0_rx_buffer[RDN_ETH0];

	/* 
	 * packet is now in:
	 * eth0_rx_buffer[RDN_ETH0];
	 */

	eth0_rx_desc[(RDN_ETH0)].buff_size_byte_count = 1536<<16;

	/* GT96100 Owner */
	eth0_rx_desc[(RDN_ETH0)].command_status = 0x80000000;

	RDN_ETH0 ++;
	if (RDN_ETH0 == NR) {RDN_ETH0 = 0;}

	/* Start Rx*/
	GT_REG_WRITE (ETHERNET0_SDMA_COMMAND_REGISTER + REG_OFF, 0x00000080);

	return eth_len;
}

/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
int
evb64260_eth0_transmit(unsigned int s, volatile char *p)
{
	/* wait for tx to be ready */
	while (eth0_tx_desc[(TDN_ETH0)].command_status & 0x80000000) ;
  
	GT_REG_WRITE (ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER0 + REG_OFF,
		      (unsigned int)eth0_tx_desc);

#if 0 /* this is already done */
	/* Build Ethernet Header */
	/* Destination MAC is broadcast */
	eth0_tx_buffer[0 ] = d[0];
	eth0_tx_buffer[1 ] = d[1];
	eth0_tx_buffer[2 ] = d[2];
	eth0_tx_buffer[3 ] = d[3];
	eth0_tx_buffer[4 ] = d[4];
	eth0_tx_buffer[5 ] = d[5];
	/* Source MAC is as defined in eeprom parameters */
	eth0_tx_buffer[6 ] =  evb64260_eth0_mac_addr[0];
	eth0_tx_buffer[7 ] =  evb64260_eth0_mac_addr[1];
	eth0_tx_buffer[8 ] =  evb64260_eth0_mac_addr[2];
	eth0_tx_buffer[9 ] =  evb64260_eth0_mac_addr[3];
	eth0_tx_buffer[10] =  evb64260_eth0_mac_addr[4];
	eth0_tx_buffer[11] =  evb64260_eth0_mac_addr[5];
	/* Next is IP packet */
	eth0_tx_buffer[12] = (t & 0xff00) >> 8;
	eth0_tx_buffer[13] = t & 0xff;
#endif

	memcpy(eth0_tx_buffer, (char *) p, s);

	eth0_tx_desc[(TDN_ETH0)].buff_pointer = eth0_tx_buffer;
	eth0_tx_desc[(TDN_ETH0)].bytecount_reserved = ((__u16)s) << 16;
	if (s<60) {
		eth0_tx_desc[(TDN_ETH0)].bytecount_reserved =  60<<16;
	}

	eth0_tx_desc[(TDN_ETH0)].command_status = 0x80470000;
	eth0_tx_desc[(TDN_ETH0)].next_desc =
		(struct eth0_tx_desc_struct *)
		&eth0_tx_desc[(TDN_ETH0+1)%NT].bytecount_reserved;

	eth0_tx_desc[(TDN_ETH0+1)%NT].command_status = 0x0003ffff;

	/*
	{
		unsigned long tempo;
		for (tempo=0;tempo<10;tempo++) {}
	}
	*/

	GT_REG_WRITE(ETHERNET0_SDMA_COMMAND_REGISTER + REG_OFF, 0x01000000);

	return 0;
}

/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
void
evb64260_eth0_disable(void)
{
	GT_REG_WRITE(ETHERNET0_SDMA_COMMAND_REGISTER + REG_OFF, 0x80008000);
}

/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
int
evb64260_eth0_probe(bd_t *bis)
{
	unsigned long temp;

#ifdef DEBUG
	printf ("\nCOMM_CONTROL = %08x , COMM_CONF = %08x\n",
		GTREGREAD(COMM_UNIT_ARBITER_CONTROL),
		GTREGREAD(COMM_UNIT_ARBITER_CONFIGURATION_REGISTER));
#endif
	GT_REG_WRITE(MAIN_ROUTING_REGISTER, 0x7ffe38);
	GT_REG_WRITE(SERIAL_PORT_MULTIPLEX, 0x1102);
	GT_REG_WRITE(ETHERNET0_SDMA_CONFIGURATION_REGISTER + REG_OFF,
		     0x0000303c); 
	GT_REG_READ(ETHERNET0_PORT_CONFIGURATION_REGISTER + REG_OFF, &temp);

	/* turn on autonegotiation */
	temp = temp | 0x00000081;

	GT_REG_WRITE(ETHERNET0_PORT_CONFIGURATION_REGISTER + REG_OFF, temp);
	GT_REG_WRITE(ETHERNET0_PORT_CONFIGURATION_EXTEND_REGISTER + REG_OFF,
		     0x00104d00);

	/* we're dealing with the first ethernet only for this stripped down
	 * driver */
	TDN_ETH0=0;
	RDN_ETH0=0;

	eth0_tx_desc = 
		(eth0_tx_desc_single *) 
		(((unsigned int) malloc(sizeof (eth0_tx_desc_single) *
					(NT+1)) & 0xfffffff0) + 0x10);
	eth0_rx_desc = 
		(eth0_rx_desc_single *) 
		(((unsigned int) malloc(sizeof (eth0_rx_desc_single) * 
					(NR+1)) & 0xfffffff0) + 0x10);
	eth0_tx_buffer = 
		(char *) (((unsigned int) malloc(1536) & 0xfffffff0) + 0x10);

	for (temp = 0 ; temp < NR ; temp ++) {
		eth0_rx_buffer[temp] = 
			(char *)
			(((unsigned int) malloc(1536) & 0xfffffff0) + 0x10);
	}

	eth0_tx_desc[(TDN_ETH0)].command_status = 0;

	// Initialize Rx Side
	for (temp = 0; temp < NR; temp++) {
		eth0_rx_desc[temp].buff_pointer = eth0_rx_buffer[temp];
		eth0_rx_desc[temp].buff_size_byte_count = 1536<<16;

		/* GT96100 Owner */
		eth0_rx_desc[temp].command_status = 0x80000000;
		eth0_rx_desc[temp].next_desc = 
			(struct eth0_rx_desc_struct *)
			&eth0_rx_desc[(temp+1)%NR].buff_size_byte_count;
	}
  
	GT_REG_WRITE(ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER0 + REG_OFF,
		      (unsigned int) eth0_tx_desc);
	GT_REG_WRITE(ETHERNET0_FIRST_RX_DESCRIPTOR_POINTER0 + REG_OFF,
		      (unsigned int) eth0_rx_desc);
	GT_REG_WRITE(ETHERNET0_CURRENT_RX_DESCRIPTOR_POINTER0 + REG_OFF,
		      (unsigned int) eth0_rx_desc);

#ifdef DEBUG
	printf ("\nRx descriptor pointer is %08x %08x\n",
		GTREGREAD(ETHERNET0_FIRST_RX_DESCRIPTOR_POINTER0 + REG_OFF),
		GTREGREAD(ETHERNET0_CURRENT_RX_DESCRIPTOR_POINTER0 + REG_OFF));
	printf ("\n\n%08x %08x\n",
		(unsigned int)eth0_rx_desc,eth0_rx_desc[0].command_status);

	printf ("Descriptor dump:\n");
	printf ("%08x\n",eth0_rx_desc[0].command_status);
	printf ("%08x\n",eth0_rx_desc[0].buff_size_byte_count);
	printf ("%08x\n",(unsigned int)eth0_rx_desc[0].buff_pointer);
	printf ("%08x\n\n",(unsigned int)eth0_rx_desc[0].next_desc);
	printf ("%08x\n",*(unsigned int *) ((unsigned int)eth0_rx_desc + 0x0));
	printf ("%08x\n",*(unsigned int *) ((unsigned int)eth0_rx_desc + 0x4));
	printf ("%08x\n",*(unsigned int *) ((unsigned int)eth0_rx_desc + 0x8));
	printf ("%08x\n\n",
		*(unsigned int *) ((unsigned int)eth0_rx_desc + 0xc));
#endif

	/* Start Rx*/
	GT_REG_WRITE(ETHERNET0_SDMA_COMMAND_REGISTER + REG_OFF, 0x00000080);

	return 0;
}
