#include <ppcboot.h>
#include <malloc.h>
#include <mpc824x.h>
#include <net.h>

#include "pci.h"

#undef DEBUG

#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(CONFIG_NET_MULTI) && \
	defined(CONFIG_EEPRO100)

	/* Ethernet chip registers.
	 */
#define SCBStatus       	0	/* Rx/Command Unit Status *Word* */
#define SCBIntAckByte   	1	/* Rx/Command Unit STAT/ACK byte */
#define SCBCmd          	2	/* Rx/Command Unit Command *Word* */
#define SCBIntrCtlByte  	3	/* Rx/Command Unit Intr.Control Byte */
#define SCBPointer      	4	/* General purpose pointer. */
#define SCBPort         	8	/* Misc. commands and operands. */
#define SCBflash        	12	/* Flash memory control. */
#define SCBeeprom       	14	/* EEPROM memory control. */
#define SCBCtrlMDI      	16	/* MDI interface control. */
#define SCBEarlyRx      	20	/* Early receive byte count. */
#define SCBGenControl   	28	/* 82559 General Control Register */
#define SCBGenStatus    	29	/* 82559 General Status register */

	/* 82559 SCB status word defnitions
	 */
#define SCB_STATUS_CX   	0x8000	/* CU finished command (transmit) */
#define SCB_STATUS_FR   	0x4000	/* frame received */
#define SCB_STATUS_CNA  	0x2000	/* CU left active state */
#define SCB_STATUS_RNR  	0x1000	/* receiver left ready state */
#define SCB_STATUS_MDI  	0x0800	/* MDI read/write cycle done */
#define SCB_STATUS_SWI  	0x0400	/* software generated interrupt */
#define SCB_STATUS_FCP  	0x0100	/* flow control pause interrupt */

#define SCB_INTACK_MASK 	0xFD00	/* all the above */

#define SCB_INTACK_TX 		(SCB_STATUS_CX | SCB_STATUS_CNA)
#define SCB_INTACK_RX 		(SCB_STATUS_FR | SCB_STATUS_RNR)

	/* System control block commands
	 */
/* CU Commands */
#define CU_NOP          	0x0000
#define	CU_START        	0x0010
#define	CU_RESUME       	0x0020
#define	CU_STATSADDR    	0x0040	/* Load Dump Statistics ctrs addr */
#define	CU_SHOWSTATS    	0x0050	/* Dump statistics counters. */
#define	CU_ADDR_LOAD    	0x0060	/* Base address to add to CU commands */
#define	CU_DUMPSTATS    	0x0070	/* Dump then reset stats counters. */

/* RUC Commands */
#define RUC_NOP         	0x0000
#define	RUC_START       	0x0001
#define	RUC_RESUME      	0x0002
#define RUC_ABORT       	0x0004
#define	RUC_ADDR_LOAD   	0x0006	/* (seems not to clear on acceptance) */
#define RUC_RESUMENR    	0x0007

#define CU_CMD_MASK     	0x00f0
#define RU_CMD_MASK     	0x0007

#define SCB_M	        	0x0100	/* 0 = enable interrupt, 1 = disable */
#define SCB_SWI         	0x0200	/* 1 - cause device to interrupt */

#define CU_STATUS_MASK  	0x00C0
#define RU_STATUS_MASK  	0x003C

#define RU_STATUS_IDLE  	(0<<2)
#define RU_STATUS_SUS   	(1<<2)
#define RU_STATUS_NORES 	(2<<2)
#define RU_STATUS_READY 	(4<<2)
#define RU_STATUS_NO_RBDS_SUS   ((1<<2)|(8<<2))
#define RU_STATUS_NO_RBDS_NORES ((2<<2)|(8<<2))
#define RU_STATUS_NO_RBDS_READY ((4<<2)|(8<<2))

	/* 82559 Port interface commands.
	 */
#define I82559_RESET		0x00000000	/* Software reset */
#define I82559_SELFTEST		0x00000001	/* 82559 Selftest command */
#define I82559_SELECTIVE_RESET	0x00000002
#define I82559_DUMP		0x00000003
#define I82559_DUMP_WAKEUP	0x00000007

	/* 82559 Eeprom interface.
	 */
#define EE_SHIFT_CLK		0x01	/* EEPROM shift clock. */
#define EE_CS			0x02	/* EEPROM chip select. */
#define EE_DATA_WRITE		0x04	/* EEPROM chip data in. */
#define EE_WRITE_0		0x01
#define EE_WRITE_1		0x05
#define EE_DATA_READ		0x08	/* EEPROM chip data out. */
#define EE_ENB			(0x4800 | EE_CS)

	/* The EEPROM commands include the alway-set leading bit.
	 */
#define EE_WRITE_CMD		(5 << addr_len)
#define EE_READ_CMD		(6 << addr_len)
#define EE_ERASE_CMD		(7 << addr_len)

	/* Receive frame descriptors.
	 */
struct RxFD {
	volatile u16 status;
	volatile u16 control;
	volatile u32 link;		/* struct RxFD * */
	volatile u32 rx_buf_addr;	/* void * */
	volatile u32 count;
	volatile u8 data[PKTSIZE_ALIGN];
};

#define RFD_STATUS_C		0x8000	/* completion of received frame */
#define RFD_STATUS_OK   	0x2000	/* frame received with no errors */

#define RFD_CONTROL_EL   	0x8000	/* 1=last RFD in RFA */
#define RFD_CONTROL_S    	0x4000	/* 1=suspend RU after receiving frame */
#define RFD_CONTROL_H    	0x0010	/* 1=RFD is a header RFD */
#define RFD_CONTROL_SF   	0x0008	/* 0=simplified, 1=flexible mode */

#define RFD_COUNT_MASK     	0x3fff
#define RFD_COUNT_F        	0x4000
#define RFD_COUNT_EOF      	0x8000

#define RFD_RX_CRC          	0x0800	/* crc error */
#define RFD_RX_ALIGNMENT    	0x0400	/* alignment error */
#define RFD_RX_RESOURCE     	0x0200	/* out of space, no resources */
#define RFD_RX_DMA_OVER     	0x0100	/* DMA overrun */
#define RFD_RX_SHORT        	0x0080	/* short frame error */
#define RFD_RX_LENGTH       	0x0020
#define RFD_RX_ERROR        	0x0010	/* receive error */
#define RFD_RX_NO_ADR_MATCH 	0x0004	/* no address match */
#define RFD_RX_IA_MATCH     	0x0002	/* individual address does not match */
#define RFD_RX_TCO          	0x0001	/* TCO indication */

	/* Transmit frame descriptors
	 */
struct TxFD {				/* Transmit frame descriptor set. */
	volatile u16 status;
	volatile u16 command;
	volatile u32 link;		/* void * */
	volatile u32 tx_desc_addr;	/* Always points to the tx_buf_addr element. */
	volatile s32 count;

	volatile u32 tx_buf_addr0;	/* void *, frame to be transmitted.  */
	volatile s32 tx_buf_size0;	/* Length of Tx frame. */
	volatile u32 tx_buf_addr1;	/* void *, frame to be transmitted.  */
	volatile s32 tx_buf_size1;	/* Length of Tx frame. */
};

#define TxCB_CMD_TRANSMIT	0x0004	/* transmit command */
#define TxCB_CMD_SF         	0x0008	/* 0=simplified, 1=flexible mode */
#define TxCB_CMD_NC         	0x0010	/* 0=CRC insert by controller */
#define TxCB_CMD_I          	0x2000	/* generate interrupt on completion */
#define TxCB_CMD_S          	0x4000	/* suspend on completion */
#define TxCB_CMD_EL         	0x8000	/* last command block in CBL */

#define TxCB_COUNT_MASK     	0x3fff
#define TxCB_COUNT_EOF      	0x8000

	/* The Speedo3 Rx and Tx frame/buffer descriptors.
	 */
struct descriptor {			/* A generic descriptor. */
	volatile u16 status;
	volatile u16 command;
	volatile u32 link;		/* struct descriptor *  */

	unsigned char params[0];
};

#define CFG_CMD_EL         	0x8000
#define CFG_CMD_SUSPEND    	0x4000
#define CFG_CMD_INT        	0x2000
#define CFG_CMD_IAS        	0x0001	/* individual address setup */
#define CFG_CMD_CONFIGURE  	0x0002	/* configure */

#define CFG_STATUS_C       	0x8000
#define CFG_STATUS_OK      	0x2000

	/* Misc.
	 */
#define NUM_RX_DESC 		PKTBUFSRX
#define NUM_TX_DESC 		1	/* Number of TX descriptors   */

#define TOUT_LOOP		1000000

#define ETH_ALEN		6

static struct RxFD rx_ring[NUM_RX_DESC];	/* RX descriptor ring         */
static struct TxFD tx_ring[NUM_TX_DESC];	/* TX descriptor ring         */
static int rx_next;				/* RX descriptor ring pointer */
static int tx_next;				/* TX descriptor ring pointer */
static int tx_threshold;

static void init_rx_ring (void);
static void purge_tx_ring (void);

static void read_hw_addr (struct eth_device* dev, bd_t * bis);

static int eepro100_init (struct eth_device* dev, bd_t * bis);
static int eepro100_send (struct eth_device *dev, volatile void *packet, int length);
static int eepro100_recv (struct eth_device *dev);
static void eepro100_halt (struct eth_device* dev);

static inline int INL (struct eth_device* dev, u_long addr)
{
	return le32_to_cpu (*(volatile u32 *) (addr + dev->iobase));
}

static inline int INW (struct eth_device* dev, u_long addr)
{
	return le16_to_cpu (*(volatile u16 *) (addr + dev->iobase));
}

static inline int INB (struct eth_device* dev, u_long addr)
{
	return *(volatile u8 *) (addr + dev->iobase);
}

static inline void OUTB (struct eth_device* dev, int command, u_long addr)
{
	*(volatile u8 *) (addr + dev->iobase) = command;
}

static inline void OUTW (struct eth_device* dev, int command, u_long addr)
{
	*(volatile u16 *) (addr + dev->iobase) = cpu_to_le16 (command);
}

static inline void OUTL (struct eth_device* dev, int command, u_long addr)
{
	*(volatile u32 *) (addr + dev->iobase) = cpu_to_le32 (command);
}

	/* Wait for the chip get the command.
	 */
static int wait_for_eepro100 (struct eth_device* dev)
{
	int i;

	for (i = 0; INW (dev, SCBCmd) & (CU_CMD_MASK | RU_CMD_MASK); i++) {
		if (i >= TOUT_LOOP) {
			return 0;
		}
	}

	return 1;
}

int eepro100_initialize (bd_t * bis)
{
	int card_number, i, status = 0;
	int bus, devno, func;
	struct eth_device *dev;
	u_long iobase;
	static int supported[] = { 
		PCI_DEVICE_ID_INTEL_82557, 
		PCI_DEVICE_ID_INTEL_82559, 
		-1 
	}, *devid;

	for (card_number = i = 0, devid = supported; *devid != -1; devid++)
	{
		for (; ; i++)
		{
			/* Find PCI device
			 */
			if (pci_dev_find (PCI_VENDOR_ID_INTEL,
					  *devid,
					  i, &bus, &devno, &func) != TRUE) {
				break;
			}

			pci_config_inl (bus, devno, func,
					PCI_CFG_BASE_ADDRESS_0, (uint32 *) & iobase);
			iobase &= PCI_MEMBASE_MASK;

#ifdef DEBUG
			printf ("eepro100: Intel i82559 PCI EtherExpressPro @0x%lX"
				"(bus=%d, device=%d, func=%d)\n",
				iobase, bus, devno, func);
#endif

			pci_config_outl (bus, devno, func,
					 PCI_CFG_COMMAND,
					 PCI_CMD_MEM_ENABLE | PCI_CMD_MASTER_ENABLE);

			/* Check if I/O accesses and Bus Mastering are enabled.
			 */
			pci_config_inl (bus, devno, func, PCI_CFG_COMMAND, &status);

			if (!(status & PCI_CMD_MEM_ENABLE)) {
				puts ("Error: Can not enable MEM access\n");
				continue;
			}

			if (!(status & PCI_CMD_MASTER_ENABLE)) {
				puts ("Error: Can not enable Bus Mastering\n");
				continue;
			}

			dev = (struct eth_device*) malloc(sizeof *dev);

			sprintf(dev->name, "i82559#%d", i);
			dev->iobase = iobase;
			dev->priv   = (void*) devno;
			dev->init   = eepro100_init;
			dev->halt   = eepro100_halt;
			dev->send   = eepro100_send;
			dev->recv   = eepro100_recv;

			card_number++;

			eth_register(dev);

			/* Set the latency timer for value.
			 */
			pci_config_outb (bus, devno, func,
					 PCI_CFG_LATENCY_TIMER, PCI_LATENCY_TIMER);

			udelay (10 * 1000);

			read_hw_addr (dev, bis);
		}
	}

	return card_number;
}

static int eepro100_init (struct eth_device* dev, bd_t * bis)
{
	int i, status = 0;
	int tx_cur;
	struct descriptor *ias_cmd;

	/* Reset the ethernet controller
	 */
	OUTL (dev, I82559_SELECTIVE_RESET, SCBPort);
	udelay (20);

	OUTL (dev, I82559_RESET, SCBPort);
	udelay (20);

	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return (status);
	}
	OUTL (dev, 0, SCBPointer);
	OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd);

	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return (status);
	}
	OUTL (dev, 0, SCBPointer);
	OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd);

	/* Initialize Rx and Tx rings.
	 */
	init_rx_ring ();
	purge_tx_ring ();

	/* Tell the adapter where the RX ring is located.
	 */
	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return (status);
	}

	OUTL (dev, (u32) & rx_ring[rx_next], SCBPointer);
	OUTW (dev, SCB_M | RUC_START, SCBCmd);

	/* Send the Individual Address Setup frame
	 */
	tx_cur = tx_next;
	tx_next = ((tx_next + 1) % NUM_TX_DESC);

	ias_cmd = (struct descriptor *) &tx_ring[tx_cur];
	ias_cmd->command = cpu_to_le16 ((CFG_CMD_SUSPEND | CFG_CMD_IAS));
	ias_cmd->status = 0;
	ias_cmd->link = cpu_to_le32 ((u32) & tx_ring[tx_next]);

	memcpy (ias_cmd->params, bis->bi_enetaddr, 6);

	/* Tell the adapter where the TX ring is located.
	 */
	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return (status);
	}

	OUTL (dev, (u32) & tx_ring[tx_cur], SCBPointer);
	OUTW (dev, SCB_M | CU_START, SCBCmd);

	for (i = 0;
	     !(le16_to_cpu (tx_ring[tx_cur].status) & CFG_STATUS_C);
	     i++) {
		if (i >= TOUT_LOOP) {
			printf ("%s: Tx error buffer not ready\n", dev->name);
			return (status);
		}
	}

	if (!(le16_to_cpu (tx_ring[tx_cur].status) & CFG_STATUS_OK)) {
		printf ("TX error status = 0x%08X\n",
			le16_to_cpu (tx_ring[tx_cur].status));
		return (status);
	}

	status = 1;

	return (status);
}

static int eepro100_send (struct eth_device *dev, volatile void *packet, int length)
{
	int i, status = -1;
	int tx_cur;

	if (length <= 0) {
		printf ("%s: bad packet size: %d\n", dev->name, length);
		return (status);
	}

	tx_cur = tx_next;
	tx_next = (tx_next + 1) % NUM_TX_DESC;

	tx_ring[tx_cur].command = cpu_to_le16 ( TxCB_CMD_TRANSMIT |
						TxCB_CMD_SF |
						TxCB_CMD_S |
						TxCB_CMD_EL);
	tx_ring[tx_cur].status = 0;
	tx_ring[tx_cur].count  = cpu_to_le32 (tx_threshold);
	tx_ring[tx_cur].link   = cpu_to_le32 ((u32) & tx_ring[tx_next]);
	tx_ring[tx_cur].tx_desc_addr = cpu_to_le32 ((u32) & tx_ring[tx_cur].tx_buf_addr0);
	tx_ring[tx_cur].tx_buf_addr0 = cpu_to_le32 ((u_long) packet);
	tx_ring[tx_cur].tx_buf_size0 = cpu_to_le32 (length);

	if (!wait_for_eepro100 (dev)) {
		printf ("%s: Tx error ethernet controller not ready\n", dev->name);
		return (status);
	}

	/* Send the packet.
	 */
	OUTL (dev, (u32) & tx_ring[tx_cur], SCBPointer);
	OUTW (dev, SCB_M | CU_START, SCBCmd);

	for (i = 0;
	     !(le16_to_cpu (tx_ring[tx_cur].status) & CFG_STATUS_C);
	     i++) {
		if (i >= TOUT_LOOP) {
			printf ("%s: Tx error buffer not ready\n", dev->name);
			return (status);
		}
	}

	if (!(le16_to_cpu (tx_ring[tx_cur].status) & CFG_STATUS_OK)) {
		printf ("TX error status = 0x%08X\n",
			le16_to_cpu (tx_ring[tx_cur].status));
		return (status);
	}

	status = length;

	return (status);
}

static int eepro100_recv (struct eth_device *dev)
{
	u16 status, stat;
	int rx_prev, length = 0;

	stat = INW (dev, SCBStatus);
	OUTW (dev, stat & SCB_STATUS_RNR, SCBStatus);

	for (;;) {
		status = le16_to_cpu (rx_ring[rx_next].status);

		if (!(status & RFD_STATUS_C)) {
			break;
		}

		/* Valid frame status.
		 */
		if ((status & RFD_STATUS_OK)) {
			/* A valid frame received.
			 */
			length = le32_to_cpu (rx_ring[rx_next].count) & 0x3fff;

			/* Pass the packet up to the protocol
			 * layers.
			 */
			NetReceive (rx_ring[rx_next].data, length);
		} else {
			/* There was an error.
			 */
			printf ("RX error status = 0x%08X\n", status);
		}

		rx_ring[rx_next].control = cpu_to_le16 (RFD_CONTROL_S);
		rx_ring[rx_next].status = 0;
		rx_ring[rx_next].count = cpu_to_le32 (PKTSIZE_ALIGN << 16);

		rx_prev = (rx_next + NUM_RX_DESC - 1) % NUM_RX_DESC;
		rx_ring[rx_prev].control = 0;

		/* Update entry information.
		 */
		rx_next = (rx_next + 1) % NUM_RX_DESC;
	}

	if (stat & SCB_STATUS_RNR) {

		printf ("%s: Receiver is not ready, restart it\n", dev->name);

		/* Reinitialize Rx ring.
		 */
		init_rx_ring ();

		if (!wait_for_eepro100 (dev)) {
			puts ("Error: Can not restart ethernet controller\n");
			return (length);
		}

		OUTL (dev, (u32) & rx_ring[rx_next], SCBPointer);
		OUTW (dev, SCB_M | RUC_START, SCBCmd);
	}

	return (length);
}

static void eepro100_halt (struct eth_device* dev)
{
	/* Reset the ethernet controller
	 */
	OUTL (dev, I82559_SELECTIVE_RESET, SCBPort);
	udelay (20);

	OUTL (dev, I82559_RESET, SCBPort);
	udelay (20);

	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return;
	}
	OUTL (dev, 0, SCBPointer);
	OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd);

	if (!wait_for_eepro100 (dev)) {
		puts ("Error: Can not reset ethernet controller\n");
		return;
	}
	OUTL (dev, 0, SCBPointer);
	OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd);

	return;
}

	/* SROM Read.
	 */
static int read_eeprom (struct eth_device* dev, int location, int addr_len)
{
	unsigned short retval = 0;
	int read_cmd = location | EE_READ_CMD;
	int i;

	OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom);
	OUTW (dev, EE_ENB, SCBeeprom);

	/* Shift the read command bits out. */
	for (i = 12; i >= 0; i--) {
		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;

		OUTW (dev, EE_ENB | dataval, SCBeeprom);
		udelay (1);
		OUTW (dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom);
		udelay (1);
	}
	OUTW (dev, EE_ENB, SCBeeprom);

	for (i = 15; i >= 0; i--) {
		OUTW (dev, EE_ENB | EE_SHIFT_CLK, SCBeeprom);
		udelay (1);
		retval = (retval << 1) |
				((INW (dev, SCBeeprom) & EE_DATA_READ) ? 1 : 0);
		OUTW (dev, EE_ENB, SCBeeprom);
		udelay (1);
	}

	/* Terminate the EEPROM access. */
	OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom);
	return retval;
}

static void init_rx_ring (void)
{
	int i;

	for (i = 0; i < NUM_RX_DESC; i++) {
		rx_ring[i].status = 0;
		rx_ring[i].control = (i == NUM_RX_DESC - 1) ?
					cpu_to_le16 (RFD_CONTROL_S) : 0;
		rx_ring[i].link = cpu_to_le32 ((u32) & rx_ring[(i + 1) % NUM_RX_DESC]);
		rx_ring[i].rx_buf_addr = 0xffffffff;
		rx_ring[i].count = cpu_to_le32 (PKTSIZE_ALIGN << 16);
	}

	rx_next = 0;
}

static void purge_tx_ring (void)
{
	int i;

	tx_next = 0;
	tx_threshold = 0x01208000;

	for (i = 0; i < NUM_TX_DESC; i++) {
		tx_ring[i].status = 0;
		tx_ring[i].command = 0;
		tx_ring[i].link = 0;
		tx_ring[i].tx_desc_addr = 0;
		tx_ring[i].count = 0;

		tx_ring[i].tx_buf_addr0 = 0;
		tx_ring[i].tx_buf_size0 = 0;
		tx_ring[i].tx_buf_addr1 = 0;
		tx_ring[i].tx_buf_size1 = 0;
	}
}

static void read_hw_addr (struct eth_device *dev, bd_t * bis)
{
	u16 eeprom[0x40];
	u16 sum = 0;
	int i, j;
	int addr_len = read_eeprom (dev, 0, 6) == 0xffff ? 8 : 6;

	for (j = 0, i = 0; i < 0x40; i++) {
		u16 value = read_eeprom (dev, i, addr_len);

		eeprom[i] = value;
		sum += value;
		if (i < 3) {
			dev->enetaddr[j++] = value;
			dev->enetaddr[j++] = value >> 8;
		}
	}

	if (sum != 0xBABA) {
		printf ("%s: Invalid EEPROM checksum %#4.4x, "
			"check settings before activating this device!\n",
			dev->name, sum);
	}

#ifdef DEBUG
	for (i = 0; i < ETH_ALEN; i++) {
		if (dev->enetaddr[i] != bis->bi_enetaddr[i]) {
			printf ("Warning: HW address don't match:\n");
			printf ("Address in SROM is         "
				"%02X:%02X:%02X:%02X:%02X:%02X\n",
				dev->enetaddr[0], dev->enetaddr[1], 
				dev->enetaddr[2], dev->enetaddr[3], 
				dev->enetaddr[4], dev->enetaddr[5]);
			printf ("Address used by ppcboot is "
				"%02X:%02X:%02X:%02X:%02X:%02X\n",
				bis->bi_enetaddr[0], bis->bi_enetaddr[1],
				bis->bi_enetaddr[2], bis->bi_enetaddr[3],
				bis->bi_enetaddr[4], bis->bi_enetaddr[5]);
			return;
		}
	}
#endif

	return;
}

#endif
