/* 
 * Copyright 2011 Stefan Lankes, Chair for Operating Systems,
 *                               RWTH Aachen University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This file is part of MetalSVM. 
 */

/*
 * This eMAC driver required at least sccKit 1.4.0 and based on
 * the eMAC Driver Description (section 9.6.5) in the 
 * SccKit 1.4.0 User’s Guide (Revision 0.92 Part 9).
 */

#include <metalsvm/stddef.h>
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/processor.h>
#include <metalsvm/mailbox.h>
#include <metalsvm/page.h>
#include <metalsvm/time.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/RCCE.h>
#include <asm/RCCE_lib.h>
#include <asm/SCC_API.h>
#if defined(CONFIG_LWIP) && defined(CONFIG_ROCKCREEK)
#include <lwip/sys.h>
#include <lwip/stats.h>
#include <lwip/netif.h>
#include <netif/etharp.h>
#include <net/rckemac.h>

/* Limits */
#define BUFFER_ORDER			9
#define BUFFER_NUM			(1 << BUFFER_ORDER)
#define BUFFER_SIZE			(BUFFER_NUM * PAGE_SIZE)

#define EMAC0				0x01
#define EMAC1				0x02
#define EMAC2				0x04
#define EMAC3				0x08

#define EMAC_IPCONF			0x3200
#define EMAC_RX_CONTROL			0x9000
#define EMAC_TX_CONTROL			0x9900

/* IP configuration - offsets */
#define CONFIG_FLOW_CONTROL_ADD		0xC0
#define TRANSMITTER_ADDRESS		0x80
#define RECEIVER1_ADDRESS		0x40
#define CONFIG_ADD			0x100
#define ADD_FILTER_MOD			0x190

/* EMAC RX */
#define EMAC_RX_BUFFER_START_ADDRESS		0x0000
#define EMAC_RX_BUFFER_READ_OFFSET		0x0100
#define EMAC_RX_BUFFER_WRITE_OFFSET		0x0200
#define EMAC_RX_BUFFER_SIZE			0x0300
#define EMAC_RX_BUFFER_THRESHOLD		0x0400
#define EMAC_RX_MODE				0x0500
#define EMAC_RX_NETWORK_PORT_MAC_ADDRESS_HI	0x0600
#define EMAC_RX_NETWORK_PORT_MAC_ADDRESS_LO	0x0700
#define EMAC_RX_NETWORK_PORT_ENABLE		0x0800

/* EMAC TX */
#define EMAC_TX_BUFFER_START_ADDRESS		0x0000
#define EMAC_TX_BUFFER_READ_OFFSET		0x0100
#define EMAC_TX_BUFFER_WRITE_OFFSET		0x0200
#define EMAC_TX_BUFFER_SIZE			0x0300
#define EMAC_TX_MODE				0x0400
#define EMAC_TX_NETWORK_PORT_ENABLE		0x0500

// Using of LVT1 as interrupt line
#define EMAC_IRQ_MASK				0x00000001
#define EMAC_IRQ_NR				3
#define EMAC_LVT				APIC_LVT1
#define EMAC_IRQ_CONFIG				1

#define IRQ_STATUS				0xD000
#define IRQ_MASK				0xD200
#define IRQ_RESET				0xD400
#define IRQ_CONFIG				0xD800

/* Cache line wrappers */
#define CLINE_SHIFT			5
#define CLINE_SIZE			(1UL << CLINE_SHIFT)
#define CLINE_MASK			(~(CLINE_SIZE - 1))
#define CLINE_ALIGN(_x)			(((_x) + CLINE_SIZE - 1) & CLINE_MASK)
#define CLINE_PACKETS(_x)		(CLINE_ALIGN(_x) >> CLINE_SHIFT)

/* Flush */
#define CL1FLUSH			__asm__ volatile (".byte 0x0F; .byte 0x0A;\n")

/* Read 16bit from buffer */
#define U16(_addr)			(256 * (*((uint8_t*) (_addr + 1))) + (*((uint8_t*)(_addr))))

#define MAC_ADDRESS			0x00454D414331ULL
#define MAC_HI(_x)			((((_x) >> 32)) & 0xFFFF)
#define MAC_LO(_x)			(((_x) ) & 0xFFFFFFFF)

static struct netif* mynetif;

static int read_emac(int num_emac, int offset, int core)
{
	int ret;

	ret = *((volatile int*) (FPGA_BASE + num_emac * 0x1000 + offset + core * 4));
	/* no error: read twice, as xilinx ip need some time... */
	ret = *((volatile int*) (FPGA_BASE + num_emac * 0x1000 + offset + core * 4));

	return ret;
}

static void write_emac(int num_emac, int offset, int core, int value)
{
	*((volatile int*) (FPGA_BASE + num_emac * 0x1000 + offset + core * 4)) = value;
}

/*
 * @return error code
 * - ERR_OK: packet transferred to hardware
 * - ERR_CONN: no link or link failure
 * - ERR_IF: could not transfer to link (hardware buffer full?)
 */
static err_t rckemacif_output(struct netif* netif, struct pbuf* p)
{
	rckemacif_t* rckemacif = netif->state;
	uint32_t i;
	struct pbuf *q;
	void *addr;
	uint16_t read_offset;
	int rest;
	int packets;
	int sum = 0;

	/* check for over/underflow */
	if (BUILTIN_EXPECT((p->tot_len < 20) || (p->tot_len > 1536), 0)) {
		LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_output: illegal packet length %d => drop\n", p->len));
		return ERR_IF;
	}

	rckemacif->tx_write_offset++;
	/* check if we need to wrap */
        if (rckemacif->tx_write_offset > rckemacif->tx_buffer_max)
		rckemacif->tx_write_offset = 1;

	packets = CLINE_PACKETS(p->tot_len + 2);

	read_offset = read_emac(rckemacif->num_emac, EMAC_TX_CONTROL+EMAC_TX_BUFFER_READ_OFFSET, rckemacif->core);
#if 1
again:
	if (read_offset < rckemacif->tx_write_offset) {
		sum = rckemacif->tx_buffer_max - rckemacif->tx_write_offset + read_offset - 1;
	} else if (read_offset > rckemacif->tx_write_offset) {
		sum = read_offset - rckemacif->tx_write_offset - 1;
	}

	if (sum < packets) {
		LWIP_DEBUGF(NETIF_DEBUG, ("Warning: not enough space available, retrying...\n"));
		goto again;
	}
#endif

	addr = rckemacif->tx_buffer + rckemacif->tx_write_offset * 32;

	/* Set frame length */
	((uint8_t*)addr)[0] = p->tot_len % 256;
	((uint8_t*)addr)[1] = p->tot_len / 256;

#if ETH_PAD_SIZE
	pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

	if (rckemacif->tx_write_offset + packets - 1 <= rckemacif->tx_buffer_max) {
		/*
		 * q traverses through linked list of pbuf's
		 * This list MUST consist of a single packet ONLY
		 */
		for (q=p, i=0; q!=0; q=q->next) {
			memcpy(((uint8_t*)addr) + 2 + i, q->payload, q->len);
			i += q->len;
		}

		/* increment write ptr */
		rckemacif->tx_write_offset += packets - 1;
	} else {
		/* wrap in offsets. first copy to the end, second at the starting
		 * point
		 */
		int bytes_left = p->tot_len;
		int bytes_to_copy = (rckemacif->tx_buffer_max - rckemacif->tx_write_offset + 1) * 32 - 2;
		int sz = 0;

		if (bytes_left < bytes_to_copy) 
			bytes_to_copy = bytes_left;
                
		LWIP_DEBUGF(NETIF_DEBUG, ("special case: copy last %d bytes\n", bytes_to_copy));

		q = p; i = 0;
		while ((q != 0) && (i < bytes_to_copy)) {
			sz = q->len > bytes_to_copy-i ? bytes_to_copy-i : q->len;
			memcpy(((uint8_t*) addr) + 2 + i, q->payload, sz);
			bytes_left -= sz;
			i += sz;
			if (i < bytes_to_copy)
				q = q->next;
		}

		if (bytes_left != 0) {
			rckemacif->tx_write_offset = 1;
			addr = rckemacif->tx_buffer + 32;
			LWIP_DEBUGF(NETIF_DEBUG, ("special case: copy remaining %d bytes\n", bytes_left));

			i = 0;
			if (sz < q->len) {
				memcpy((uint8_t*) addr, q->payload + sz, q->len - sz);
				bytes_left -= (q->len - sz);
				i = q->len - sz;
			}
			for(q=q->next; (q != 0); q = q->next) {
				memcpy(((uint8_t*) addr) + i, q->payload, q->len);
				i += q->len;
			}

			rest = bytes_left % 32;
			if (rest != 0)
				rest = 32 - rest;

			LWIP_DEBUGF(NETIF_DEBUG, ("Rest is %d\n", rest));
			rckemacif->tx_write_offset += CLINE_PACKETS(bytes_left + rest) - 1;
		}
	}

	*((volatile int*) rckemacif->tx_buffer) = 2;

	/* set new write offset */
        //LWIP_DEBUGF(NETIF_DEBUG, ("Update tx write offset: %d (read offset %d)\n", rckemacif->tx_write_offset, read_offset));
	write_emac(rckemacif->num_emac, EMAC_TX_CONTROL+EMAC_TX_BUFFER_WRITE_OFFSET, rckemacif->core, rckemacif->tx_write_offset);

#if ETH_PAD_SIZE
	pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

	LINK_STATS_INC(link.xmit);

	return ERR_OK;
}

static void rckemacif_rx_handler(struct netif* netif, unsigned int write_offset)
{
	rckemacif_t* rckemacif = netif->state;
	unsigned short read_offset;
	unsigned int counter;
	volatile void *addr;
	uint16_t i, length;
	struct pbuf *p;
	struct pbuf* q;

	if (write_offset > rckemacif->rx_buffer_max) {
		LWIP_DEBUGF(NETIF_DEBUG, ("Warning, write offset > buffer max!! (%d > %d)\n", write_offset, rckemacif->rx_buffer_max));
                read_offset = 1;
		write_emac(rckemacif->num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_READ_OFFSET, rckemacif->core, read_offset);
		rckemacif->rx_read_offset = read_offset;
                return;
        }
 
	while(1) {
		if ((write_offset != 0) && (rckemacif->rx_read_offset != write_offset)) {
			read_offset = rckemacif->rx_read_offset;
			read_offset++;
			if (read_offset < 1 || read_offset > rckemacif->rx_buffer_max) {
				read_offset = 1;
			}
			addr = rckemacif->rx_buffer + read_offset * 32;

			length = U16(addr);
			
			// Check for over/underflow 
			if ((length < 20) || (length > 1536)) {
				LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_rx_handler(): illegal packet length %d => drop\n", length));
				LWIP_DEBUGF(NETIF_DEBUG, ("start read at %d; write_offset at %d; addr: %p, packet len: %d\n", read_offset, write_offset, addr, length));

				read_offset = write_offset;
#if 1
				kprintf("Buffer:\n");
				for (i = 0; i < 32; i++) {
					kprintf("%2.2x ", ((char*)addr)[i] & 0xFF);
				}
				kprintf("\n");

				kprintf("Buffer0:\n");
				for (i = 0; i < 32; i++) {
					kprintf("%2.2x ", ((char*)rckemacif->rx_buffer)[i] & 0xFF);
				}
				kprintf("\n");
#endif

				LINK_STATS_INC(link.memerr);
				LINK_STATS_INC(link.drop);

				goto rxDone;
			}

#if ETH_PAD_SIZE
			length += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
			//LWIP_DEBUGF(NETIF_DEBUG, ("length %u, read_offset %u, write_offset %u\n", length, read_offset, write_offset));

			p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
			if (p) {
#if ETH_PAD_SIZE
				pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
				if (read_offset < write_offset) {
					for (q=p, counter=0; q!=NULL; q=q->next) {
						for(i=0; i<q->len; i++, counter++) {
							((uint8_t*) q->payload)[i] = ((uint8_t*)addr)[2 + counter];
						} 
					}

					read_offset += CLINE_PACKETS(p->len + 2) - 1;
				} else {
					int rest;
					int bytesLeft = length;
					int bytesToCopy = length;

					/* rest to the end of buffer - 2 bytes length information */
					rest = (rckemacif->rx_buffer_max - read_offset + 1) * 32 - 2;
					if (length > rest)
						bytesToCopy = rest;
					LWIP_DEBUGF(NETIF_DEBUG, ("bytes to copy: %d, bytesLeft: %d\n", bytesToCopy, bytesLeft));

					for (q=p, counter=0; q!=NULL; q=q->next) {
						for(i=0; i<q->len; i++, counter++) {
							if (counter < bytesToCopy)
								((uint8_t*) q->payload)[i] = ((uint8_t*)addr)[2 + counter];
							else
								goto out;
						}
					}
out:
					bytesLeft -= bytesToCopy;

					if (bytesLeft != 0) {
						addr = rckemacif->rx_buffer + 0x20;
						LWIP_DEBUGF(NETIF_DEBUG, ("copying from %p, left: %d (%x)\n", addr, bytesLeft, ((uint8_t*)addr)[0]));
						for(counter=0; (i<q->len) && (counter < bytesLeft); i++, counter++)
							((uint8_t*) q->payload)[i] = ((uint8_t*)addr)[counter];
						for(q=q->next; (q!=NULL) && (counter < bytesLeft); q=q->next) {
							for(i=0; (i<q->len) && (counter < bytesLeft); i++, counter++) {
								((uint8_t*) q->payload)[i] = ((uint8_t*)addr)[counter];
							}
						}						
						read_offset = CLINE_PACKETS(bytesLeft);
					}  else {
						read_offset += CLINE_PACKETS(p->len + 2) - 1;
					}
				}

#if ETH_PAD_SIZE
				pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
				mailbox_ptr_post(&rckemacif->mbox, (void*)p);
				LINK_STATS_INC(link.recv);
			} else {
				LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_rx_inthandler: not enough memory!\n"));
				LINK_STATS_INC(link.memerr);
				LINK_STATS_INC(link.drop);
			}

rxDone:
			/* set new read pointer */
			//LWIP_DEBUGF(NETIF_DEBUG, ("Update rx read offset: %d\n", read_offset));
			write_emac(rckemacif->num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_READ_OFFSET, rckemacif->core, read_offset);
			rckemacif->rx_read_offset = read_offset;
		} else break;
	}
}

static void rckemacif_handler(struct state* s)
{
	unsigned int status, tmp;
	unsigned int write_offset;
	rckemacif_t* rckemacif = mynetif->state;

	status = *((volatile int*) (FPGA_BASE + IRQ_STATUS + rckemacif->core * 2 * 4));
	// read twice to be sure
	status = *((volatile int*) (FPGA_BASE + IRQ_STATUS + rckemacif->core * 2 * 4));
	if (!(status & (1 << rckemacif->num_emac))) {
		LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_handler: no interrupt\n"));
		return;
	}

nexttry:
	/* check for updated write offset */
	CL1FLUSH;
	write_offset = *((volatile unsigned int*) (rckemacif->rx_buffer)) & 0xFFFF;
	//write_offset = read_emac(rckemacif->num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_WRITE_OFFSET, rckemacif->core);
	if ((write_offset != 0) && (rckemacif->rx_read_offset != write_offset)) {
		rckemacif_rx_handler(mynetif, write_offset);
		goto nexttry;
	}

	/* Set interrupt bit */
	tmp = *((volatile unsigned int*) rckemacif->irq_address);
	tmp &= ~(EMAC_IRQ_MASK);
 	*((volatile unsigned int*) rckemacif->irq_address) = tmp;

	/* Reset */
	*((volatile unsigned*) (FPGA_BASE + IRQ_RESET + rckemacif->core * 2 * 4)) = (1 << rckemacif->num_emac);
}

err_t rckemacif_wait(struct netif* netif, uint32_t poll)
{
	rckemacif_t* rckemacif = netif->state;
	struct eth_hdr *ethhdr;
	struct pbuf *p = NULL;
	err_t err = ERR_OK;

	if (poll) {
		if (mailbox_ptr_tryfetch(&(rckemacif->mbox), (void**) &p))
			return err;
	} else {
		mailbox_ptr_fetch(&(rckemacif->mbox), (void**) &p);
	}

	/* points to packet payload, which starts with an Ethernet header */
	ethhdr = p->payload;

	//LWIP_DEBUGF(NETIF_DEBUG, ("Got packet of type 0x%x!\n", htons(ethhdr->type)));

	switch (htons(ethhdr->type)) {
	/* IP or ARP packet? */
	case ETHTYPE_ARP:
	case ETHTYPE_IP:
#if PPPOE_SUPPORT
	/* PPPoE packet? */
	case ETHTYPE_PPPOEDISC:
	case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
		/* full packet send to tcpip_thread to process */
		if ((err = mynetif->input(p, mynetif)) != ERR_OK) {
			LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_poll: IP input error\n"));
			pbuf_free(p);
		}
		break;
	default:
		pbuf_free(p);
		break;
	}

	return err;
}

err_t rckemacif_init(struct netif* netif)
{
	rckemacif_t* rckemacif;
	int num_emac;
	int macPorts;
	int i, tmp, x, y, z, core;
	uint64_t tile_offset;
	uint16_t write_offset;
	uint16_t read_offset;
	int mode;
	int subdest;
	int route;

	LWIP_ASSERT("netif != NULL", (netif != NULL));

	// Find out who I am...
	tmp = ReadConfigReg(CRB_OWN+MYTILEID);
	x = (tmp>>3) & 0x0f; // bits 06:03
	y = (tmp>>7) & 0x0f; // bits 10:07
	z = (tmp   ) & 0x07; // bits 02:00
	core = 12 * y + 2 * x + z;
 
	rckemacif = kmalloc(sizeof(rckemacif_t));
	if (!rckemacif) {
		LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: out of memory\n"));
		return ERR_MEM;
	}
	memset(rckemacif, 0, sizeof(rckemacif_t));
	rckemacif->core = core;

	/* allocate the receive buffer */
	rckemacif->rx_buffer = mem_allocation(BUFFER_SIZE, MAP_KERNEL_SPACE|MAP_NO_CACHE);
	if (!(rckemacif->rx_buffer)) {
		LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: out of memory\n"));
		kfree(rckemacif, sizeof(rckemacif_t));
		return ERR_MEM;
	}
	memset(rckemacif->rx_buffer, 0x00, 0x20);
	memset(rckemacif->rx_buffer + 0x20, 0xDA, BUFFER_SIZE - 0x20);
	rckemacif->rx_buffer_max = CLINE_PACKETS(BUFFER_SIZE) - 1;

	/* allocate the send buffers */
	rckemacif->tx_buffer = mem_allocation(BUFFER_SIZE, MAP_KERNEL_SPACE|MAP_NO_CACHE);
	if (!(rckemacif->tx_buffer)) {
		LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: out of memory\n"));
		kfree(rckemacif->rx_buffer, BUFFER_SIZE);
		kfree(rckemacif, sizeof(rckemacif_t));
		return ERR_MEM;
	}
	memset(rckemacif->tx_buffer, 0x00, 0x20);
	memset(rckemacif->tx_buffer + 0x20, 0xDA, BUFFER_SIZE - 0x20);
	rckemacif->tx_buffer_max = CLINE_PACKETS(BUFFER_SIZE) - 1;

	mailbox_ptr_init(&rckemacif->mbox);
	netif->state = rckemacif;

	/* Depending on core location read own private data
	 * (offset, subdest, route)
	 */
	if (z == 0) {
		tmp = ReadConfigReg(CRB_OWN + LUT0);
		rckemacif->irq_address = (void*) (CRB_OWN + GLCFG0);
	} else {
		tmp = ReadConfigReg(CRB_OWN + LUT1);
		rckemacif->irq_address = (void*) (CRB_OWN + GLCFG1);
	}
	tile_offset = (uint64_t)((uint64_t) tmp & 0x3FF) << 24;
	subdest = (tmp >> 10) & 0x07;
	route = (tmp >> 13) & 0xFF;
	mode = (subdest << 8) + route;
	LWIP_DEBUGF(NETIF_DEBUG, ("tile_offset = 0x%llx\n", tile_offset));

	/* get fpga/sccKit port settings */
	tmp = *((volatile int*)(FPGA_BASE + 0x822C));
	tmp = *((volatile int*)(FPGA_BASE + 0x822C));
	macPorts = ((tmp >> 9 ) & 0xFF);

	LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: eMAC0: %s eMAC1: %s eMAC2: %s eMAC3: %s\n",
		(macPorts & EMAC0) != 0 ? "present" : "-",
		(macPorts & EMAC1) != 0 ? "present" : "-",
		(macPorts & EMAC2) != 0 ? "present" : "-",
		(macPorts & EMAC3) != 0 ? "present" : "-"));

	// determine device and emac number
	num_emac=0;
	while (((macPorts & (1 << num_emac)) == 0) && (num_emac < 4))
		num_emac++;
	if (num_emac >= 4)
		return ERR_ARG;
	mynetif = netif;
	rckemacif->num_emac = num_emac;

	LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: used eMAC device %d\n", num_emac));

	tmp = read_emac(num_emac, EMAC_IPCONF+TRANSMITTER_ADDRESS, 0);
        tmp = read_emac(num_emac, EMAC_IPCONF+RECEIVER1_ADDRESS, 0);

	if (core == 0) {
		/* Only core 0 initialize the xilinx port */
		int flow_control = 0;
		int transmitter_addr = 0;
		int receiver1_addr = 0;
		int config_add = 0;
		int add_filter_mod = 0;

		/* Disable tx and rx flow control of eMAC */
		LWIP_DEBUGF(NETIF_DEBUG, ("Disabling tx/rx flow control of eMAC%d\n", num_emac));
		flow_control = read_emac(num_emac, EMAC_IPCONF+CONFIG_FLOW_CONTROL_ADD, 0);

		/* Set top 3 bits of the flow control configuration to zero,
		 * therefore disabling tx and rx flow control
		 */
		flow_control &= 0x7FFFFFF;
		write_emac(num_emac, EMAC_IPCONF+CONFIG_FLOW_CONTROL_ADD, 0, flow_control);

		/* Sanity check */
		flow_control = read_emac(num_emac, EMAC_IPCONF+CONFIG_FLOW_CONTROL_ADD, 0);
		LWIP_DEBUGF(NETIF_DEBUG, ("  CONFIG_FLOW_CONTROL_ADD set: 0x%x\n", flow_control));

		/* Setting the tx configuration bit to enable the transmitter and
		 * set to full duplex mode.
		 */
		LWIP_DEBUGF(NETIF_DEBUG, ("Setting rx configuration of eMAC%d\n", num_emac));
		transmitter_addr = read_emac(num_emac, EMAC_IPCONF+TRANSMITTER_ADDRESS, 0);

		/* Now set the relevant bits and write back into the register:
		 * 26 (half duplex) = 0, 28 (transmit enable) = 1, 31 (reset) = 0
		 */
		transmitter_addr &= ~(1 << 31);
		transmitter_addr &= ~(1 << 26);
		transmitter_addr |= (1 << 28);
		write_emac(num_emac, EMAC_IPCONF+TRANSMITTER_ADDRESS, 0, transmitter_addr);

		transmitter_addr = read_emac(num_emac, EMAC_IPCONF+TRANSMITTER_ADDRESS, 0);
		LWIP_DEBUGF(NETIF_DEBUG, ("  TRANSMITTER_ADDRESS set: %x\n", transmitter_addr));

		/* Setting the rx configuration bit to enable the transmitter and
		 * set to full duplex mode.
		 */
		LWIP_DEBUGF(NETIF_DEBUG, ("Setting IP configuration of EMAC%d\n", num_emac));

		/* Read the current config value from the register */
		receiver1_addr = read_emac(num_emac, EMAC_IPCONF+RECEIVER1_ADDRESS, 0);

		/* Now set the relevant bits and write back into the register:
		 *  25 = 1, 26 = 0, 28 = 1, 31 = 0
		 */
		/* Length/Type Error Check Disable */
		receiver1_addr |= (1 << 25);
		/* Disable Half Duplex => Full Duplex */
		receiver1_addr &= ~(1 << 26);
		/* Receiver enable */
		receiver1_addr |= (1 << 28);
		/* Reset */
		receiver1_addr &= ~(1 << 31);
		write_emac(num_emac, EMAC_IPCONF+RECEIVER1_ADDRESS, 0, receiver1_addr);

		receiver1_addr = read_emac(num_emac, EMAC_IPCONF+RECEIVER1_ADDRESS, 0);
		LWIP_DEBUGF(NETIF_DEBUG, ("  RECEIVER1_ADDRESS set: %x\n", receiver1_addr));

		/* Setting the speed to eMAC to 1Gb/s */
		LWIP_DEBUGF(NETIF_DEBUG, ("Setting speed of EMAC%d to 1Gb/s\n", num_emac));

		/* Read the current config value from register */
		config_add = read_emac(num_emac, EMAC_IPCONF+CONFIG_ADD, 0);

		/* Now set the relevant bits and write back into the register:
		 * 31 = 1, 30 = 0
		 */
		/* MAC Speed Configuration: 00 - 10Mbps, 01 - 100Mbps, 10 - 1Gbps */
		config_add |= (1 << 31);
		config_add &= ~(1 << 30);
		write_emac(num_emac, EMAC_IPCONF+CONFIG_ADD, 0, config_add);

		config_add = read_emac(num_emac, EMAC_IPCONF+CONFIG_ADD, 0);
		LWIP_DEBUGF(NETIF_DEBUG, ("  CONFIG_ADD set: %x\n", config_add));

		/* Read the current config addr filter mode */
		add_filter_mod = read_emac(num_emac, EMAC_IPCONF+ADD_FILTER_MOD, 0);

		/* Not set the relevant bits and write back into the register:
		 * 31 (promiscuous mode) = 1 not working, but thats ok!
		 */
		add_filter_mod |= (1 << 31);
		write_emac(num_emac, EMAC_IPCONF+ADD_FILTER_MOD, 0, add_filter_mod);

		add_filter_mod = read_emac(num_emac, EMAC_IPCONF+ADD_FILTER_MOD, 0);
		LWIP_DEBUGF(NETIF_DEBUG, ("  ADD_FILTER_MOD set: %x\n", add_filter_mod));
	}

	sleep(3);

	/* Start address */
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Buffer %p (%lx phys)\n", rckemacif->rx_buffer, virt_to_phys((uint32_t)rckemacif->rx_buffer)));

	/**** Receiver configuration ****/

	uint64_t addr_offset = (tile_offset + (uint64_t) virt_to_phys((uint32_t) rckemacif->rx_buffer)) >> 5;
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_START_ADDRESS, core, (uint32_t) addr_offset);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Buffer set to @%x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_START_ADDRESS, core)));

	/* Get buffer write offset */
	write_offset = read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_WRITE_OFFSET, core);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Buffer write offset at: %d\n", write_offset));

	/* Set buffer read offset to write offset */
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_READ_OFFSET, core, write_offset);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Buffer read offset set to: %d\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_READ_OFFSET, core)));
	rckemacif->rx_read_offset = write_offset;

	/* Size */
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_SIZE, core, rckemacif->rx_buffer_max);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Size set to %d\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_SIZE, core)));

	/* Threshold */
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_THRESHOLD, core, 0x01);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Threshold set to %x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_BUFFER_THRESHOLD, core)));

	/* Route */
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_MODE, core, (z << 24) | (((y << 4) | x) << 16) | mode);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Mode set to %x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_MODE, core)));

	// determine mac address
	uint32_t mac1 = *((volatile uint32_t*)(FPGA_BASE+0x7E00));
	uint32_t mac2 = *((volatile uint32_t*)(FPGA_BASE+0x7E04));
	uint64_t mac = (((unsigned long long)mac1) << 32) + (unsigned long long) mac2;
	if (mac == 0x00)
		mac = MAC_ADDRESS;
	/* Calculate mac address of core depending on selected emac device */
	mac = mac + (1 << num_emac) * 0x100 + core;

	for (i=0; i<6; i++) 
		mynetif->hwaddr[5-i] = (mac >> (i*8)) & 0xFF;
	LWIP_DEBUGF(NETIF_DEBUG, ("rckemacif_init: MAC address "));
	for (i=0; i<6; i++)
		LWIP_DEBUGF(NETIF_DEBUG, ("%02x ", mynetif->hwaddr[i]));
	LWIP_DEBUGF(NETIF_DEBUG, ("\n"));

	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_MAC_ADDRESS_HI, core, MAC_HI(mac));
	LWIP_DEBUGF(NETIF_DEBUG, ("  MAC1 set to %x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_MAC_ADDRESS_HI, core)));
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_MAC_ADDRESS_LO, core, MAC_LO(mac));
	LWIP_DEBUGF(NETIF_DEBUG, ("  MAC2 set to %x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_MAC_ADDRESS_LO, core)));

	/* Activate network port by setting enable bit */
	write_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_ENABLE, core, 0x01);
	LWIP_DEBUGF(NETIF_DEBUG, ("  RX Port enable set to %x\n", read_emac(num_emac, EMAC_RX_CONTROL + EMAC_RX_NETWORK_PORT_ENABLE, core)));

	/**** Transfer configuration ****/

	/* Start address */
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Buffer %p (%lx phys)\n", rckemacif->tx_buffer, virt_to_phys((uint32_t)rckemacif->tx_buffer)));
	write_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_START_ADDRESS, core, (uint32_t) (((uint64_t) virt_to_phys((uint32_t)rckemacif->tx_buffer) + tile_offset) >> 5));
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Buffer set to @%x\n", read_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_START_ADDRESS, core)));

	/* Get buffer read offset */
	read_offset = read_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_READ_OFFSET, core);
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Buffer read offset at: %d\n", read_offset));

	/* Set buffer write offset to read offset */
	write_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_WRITE_OFFSET, core, read_offset);
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Buffer write offset set to: %d\n", read_emac(num_emac, EMAC_TX_CONTROL+ EMAC_TX_BUFFER_WRITE_OFFSET, core)));
	rckemacif->tx_write_offset = read_offset;

	/* Size */
	write_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_SIZE, core, rckemacif->tx_buffer_max);
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Size set to %d\n", read_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_BUFFER_SIZE, core)));

	/* Route */
 	write_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_MODE, core, mode);
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Mode set to %x\n", read_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_MODE, core)));

	/* Activate network port by setting enable bit */
	write_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_NETWORK_PORT_ENABLE, core, 0x01);
	LWIP_DEBUGF(NETIF_DEBUG, ("  TX Port enable set to %x\n", read_emac(num_emac, EMAC_TX_CONTROL + EMAC_TX_NETWORK_PORT_ENABLE, core)));

	// set interrupt handler (INTR/LINT0)
	irq_install_handler(125, rckemacif_handler);

	/* Enable interrupt */
	tmp = *((volatile int*) (FPGA_BASE + IRQ_MASK + core * 2 * 4));
	*((volatile int*) (FPGA_BASE + IRQ_MASK + core * 2 * 4)) = tmp & ~(1 << num_emac);
	*((volatile int*) (FPGA_BASE + IRQ_CONFIG + core * 4)) = EMAC_IRQ_CONFIG;

	/*
	 * Initialize the snmp variables and counters inside the struct netif.
	 * The last argument should be replaced with your link speed, in units
	 * of bits per second.
	 */
	NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 1000 /* speed */);

	/* administrative details */
	netif->name[0] = 'e';
	netif->name[1] = 'n';
	netif->num = 0;
	/* downward functions */
	netif->output = etharp_output;
	netif->linkoutput = rckemacif_output;
	/* maximum transfer unit */
	netif->mtu = 1500;
	/* broadcast capability */
	netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
	/* hardware address length */
	netif->hwaddr_len = 6;

	rckemacif->ethaddr = (struct eth_addr *)netif->hwaddr;

	return ERR_OK;
}
#endif