Subject: [Fwd: [PATCH][RFC] ppc32: TEMAC driver for ML403]

From: "David H. Lynch Jr." <[email-masked]>
Date: Thu, 03 Aug 2006 12:39:59 -0400

Edward Bockhoefer wrote:
> Thank you for the reply David,
> I have started the montavista 10/100 enet port from the xilinx edk
> generated 2.4 driver to the 2.6 driver. Unfortunately, I can't post
> the driver port publicly because it's not GLP'd, as far as I know. We
> only needed a 10/100 mac, so we didn't bother with a TEMAC. I will
> work on this more today. If you want to send me your TEMAC code,
> please do so. I can always use the reference.

    The original message for the GB TEMAC 2.6 patch is below. It is from
Andrei Konovalov.
   
      I think it is just a newer version of the 2.4 Driver. Regardless
it just worked for me. I had to make a very minor change - I added an
"u32 spd" to the private data structure, initialized it to 10 (I am on a
10Mhz Net), and found every reference to 1000Mhz replaced it with
lp->spd; There was one place I needed a switch statement to load the
appropriate value. I think there were only 3 really trivial changes. I
would post my changes but I am not setup to post them as a patch right
now. If you really want them I will just send my adaptor.c and you can
use everything else from the patch referenced below.

    I have also attached my Local Link TEMAC code. That is basically a
single file driver. Again I am not able to provide it as a patch at the
moment. It (much like the PLB TEMAC driver) requires a platform data
entry for the LL TEMAC in your Board support code. Though it could be
fairly trivially changed to work from defines. I beleive there are only
2 critical values - the DCR base address, the TEMAC memory address.
There is an IRQ value, but I can only get the LL TEMAC to generate PHY
interupts, so the driver is polled. It does work albeit with lots of
dropped packets, but that is probably debugging code and optimizing the
poll rate.
    The driver is solely my own work based loosely on several other
drivers, and is GPL'd. According to my client LL TEMAC performance has
tested abysmally in comparision to the PLB TEMAC, however the kicker was
the inability to generate rx interupts. We are working on other OS
ports, and some do not have strictly polled options, so for  now the LL
TEMAC is on the back burner.
   
    Of course I have learned an incredible amount about Linux Network
Drivers as well as now having a reasonable understanding of the
differences between the assorted Xilinx TEMAC's

-------- Original Message --------
Return-Path: 	<linuxppc-embedded-bounces+dhlii=[email-masked]>
Received: 	from colinux.fixit1.net (colinux.fixit1.com [216.37.233.220])
by xm.dlasys.net (Cyrus v2.2.12-Debian-2.2.12-4) with LMTPA; Tue, 14 Mar
2006 00:34:38 -0500
X-Sieve: 	CMU Sieve 2.2
Received: 	from ozlabs.org ([203.10.76.45]:45331) by colinux.fixit1.net
with esmtp (Exim 4.50 #1 (Debian)) id 1FIny3-0002KK-J4 for
<[email-masked]>; Mon, 13 Mar 2006 09:24:16 -0500
Received: 	from ozlabs.org (localhost [127.0.0.1]) by ozlabs.org
(Postfix) with ESMTP id 628F067B23 for <[email-masked]>; Tue, 14 Mar
2006 01:17:33 +1100 (EST)
(Postfix) with SMTP id A94B7679E2 for <[email-masked]>;
Tue, 14 Mar 2006 01:17:21 +1100 (EST)
Received: 	(qmail 31931 invoked from network); 13 Mar 2006 14:17:16 -0000
Received: 	from unknown (HELO ?192.168.1.108?) (192.168.1.108) by
mail.dev.rtsoft.ru with SMTP; 13 Mar 2006 14:17:16 -0000
Message-ID: 	<[email-masked]>
Date: 	Mon, 13 Mar 2006 17:17:19 +0300
From: 	Andrei Konovalov <[email-masked]>
User-Agent: 	Mozilla Thunderbird 1.0.2-6 (X11/20050513)
X-Accept-Language: 	en-us, en
MIME-Version: 	1.0
To: 	linuxppc-embedded list <[email-masked]>
Subject: 	[PATCH][RFC] ppc32: TEMAC driver for ML403
X-BeenThere: 	[email-masked]
X-Mailman-Version: 	2.1.7
Precedence: 	list
List-Id: 	Linux on Embedded PowerPC Developers Mail List
<linuxppc-embedded.ozlabs.org>
List-Unsubscribe:
<https://ozlabs.org/mailman/listinfo/linuxppc-embedded>,
<mailto:[email-masked]?subject=unsubscribe>
List-Archive: 	<http://ozlabs.org/pipermail/linuxppc-embedded>
List-Post: 	<mailto:[email-masked]>
List-Help: 	<mailto:[email-masked]?subject=help>
List-Subscribe:
<https://ozlabs.org/mailman/listinfo/linuxppc-embedded>,
<mailto:[email-masked]?subject=subscribe>
Content-Type: 	text/plain; charset="us-ascii"
Content-Transfer-Encoding: 	7bit
Sender: 	linuxppc-embedded-bounces+dhlii=[email-masked]
Errors-To: 	linuxppc-embedded-bounces+dhlii=[email-masked]

Here:
   http://source.mvista.com/~ank/paulus-powerpc/20060309/
is an StGIT patchset against
   516450179454de9e689e0a53ed8f34b896e8651c
   branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc

virtex-ppc_sys_devices-fix.patch - is the same fix posted by Grant Likely few days ago (only formatting is different),
ppc32_xilinx_edk_temac.patch - the low level code from EDK-7.1i used by the this driver
ppc32_xilinx_edk_temac_mods.patch - few modifications to EDK-7.1i code to get rid of compiling the xparameters.h
   #defines into the driver. These mods should not be needed after EDK-8.1 SP2 is released.
ppc32_xilinx_temac.patch - the linux driver itself
ppc32_xilinx_ml403_temac.patch - modifications to the ML403 platform code to add TEMAC.
temac.paulus-powerpc.20060309.tgz - all the patches put together into a tarball (just in case).
temac.config - .config used.

This is not the final version yet (no MII support; SGDMA is operational, but seems to need some additional work).
And I would probably wait for EDK-8.1 SP2 (up to one month or so) where both the TEMAC IP and the EDK code
should be updated.

But it would be nice to know if there is anything to rework in the current code to get the driver
accepted into linux/kernel/git/paulus/powerpc.git or linux/kernel/git/torvalds/linux-2.6.git tree.

And probably someone could start playing with the current driver now. At least I was able to mount
the root over NFS and to run several netperf tests.

Thanks,
Andrei
_______________________________________________
Linuxppc-embedded mailing list
[email-masked]
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

-- 
Dave Lynch 					  	    DLA Systems
Software Development:  				         Embedded Linux
717.627.3770 	       [email-masked] 	  http://www.dlasys.net
fax: 1.253.369.9244 			           Cell: 1.717.587.7774
Over 25 years' experience in platforms, languages, and technologies too numerous to list.

"Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction."
Albert Einstein

/*
 linux/drivers/net/temac.c
 *
 Driver for Xilinx hard temac ethernet NIC's
 *
 Author: David H. Lynch Jr. <[email-masked]>
 Copyright (C) 2005 DLA Systems
  The author may be reached as [email-masked], or C/O
 *	DLA Systems
 *	354 Rudy Dam rd.
 *	Lititz PA 17543
 *
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
 *
 $Id: temac.c,v 0.10 2005/12/14 10:03:27 dhlii Exp $
 *
 */
#define DRV_NAME	"temac"
#define DRV_CARDNAME    "Xilinx hard temac"
#define DRV_DESCRIPTION	"Xilinx hard Tri-Mode Eth MAC driver"
#define DRV_VERSION	"0.10a"
#define DRV_RELDATE	"07/10/2006"

static const char version[] = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  David H. Lynch Jr. ([email-masked])\n";

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>

#include <linux/mii.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/ethtool.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

// #define XGP_CON_REG_PERIPH_RESET          	(1 << 31)
#define XGP_CON_REG_PERIPH_RESET          	(1 << 31)
#define XGP_CON_REG_TEMAC_RESET          	(1 << 30)
#define XGP_CON_REG_PHY_RESET 	         	(1 << 29)

#define MII_ANI					0x10
// B0 Interrupt Enabled
// B1 Interrupt Asserted

#define MII_SSR 				0x11
/* MARVELL 88E1111 PHY status registers masks*/
#define MII_SSR_SPDMASK          		0xC000
#define MII_SSR_FD         			(1 << 13)
#define MII_SSR_LINK           			(1 << 10)

/* MARVELL 88E1111 PHY status registers value masks*/
#define MII_SSR_SPD1000         		(1 << 15)
#define MII_SSR_SPD100          		(1 << 14)
#define MII_SSR_SPD10           		0
#define MII_SSR_FD    				(1 << 13)
#define MII_ISR 	    			0x13

/* Emac Config word masks */
#define XGP_E0_MODECONFIG_SPD1000     		(1 << 31)
#define XGP_E0_MODECONFIG_SPD100      		(1 << 30)
#define XGP_E0_MODECONFIG_SPD10       		0
#define PHY_NUM 0

#define XGP_HIF_BASEADDR   			0x10C	/* DCR Host Interface address */
#define XGP_HIFMSW     				0	/* data most significant word */
#define XGP_HIFLSW     				1	/* data least significant word */
#define XGP_HIFCON         			2	/* control */
#define XGP_HIFCON_DCR_WRITE 			(1 << 15)	/* Write enable */
#define XGP_HIFRDY       			3	/* rdy status */
#define XGP_HIFRDY_DCR_SRR 			(1 << 0)
#define XGP_HIFRDY_DCR_MRR 			(1 << 1)
#define XGP_HIFRDY_DCR_MWR 			(1 << 2)
#define XGP_HIFRDY_DCR_ARR 			(1 << 3)
#define XGP_HIFRDY_DCR_AWR 			(1 << 4)
#define XGP_HIFRDY_DCR_GRR 			(1 << 5)
#define XGP_HIFRDY_DCR_GWR 			(1 << 6)
#define XGP_CON					4	/* unimplimented */
#define XGP_STAT				5	/* unimplimented */
#define XGP_ROV					6	/* read errors and overflows */
#define XGP_PTIME				7	/* Pause Time */

#define XGP_MDIO_ENABLE_MASK  			(1 << 6)	/* enable MII bit */
#define XGP_MDIO_CLOCK_DIV_100MHz  		0x28	/* 100 MHz host clock */

#define XGP_E0_RxCW0 				0x200	/* Receiver configuration word 0 - pause fram MAC address */
#define XGP_E0_RxCW1 				0x240	/* Receiver configuration word 1 */
// Why is Rx_RESET unused ?
#define XGP_E0_RxCW1_RESET               	(1 << 31)	/* B31 */
//#define XGP_E0_RxCW1_JUMBO               	(1 << 30)	/* B30 */
//#define XGP_E0_RxCW1_INB_FCS                  (1 << 29)	/* B29 */
#define XGP_E0_RxCW1_ENABLE  			(1 << 28)	/* B28 Receiver enable */
//#define XGP_E0_RxCW1_VLAN                	(1 << 27)
#define XGP_E0_RxCW1_HD        			(1 << 26)

#define XGP_E0_TxCW 				0x280	/* Transmitter configuration */
#define XGP_E0_TxCW_RESET               	(1 << 31)	/* B31 */
// #define XGP_E0_TxCW_JUMBO               	(1 << 30)
//#define XGP_E0_TxCW_INB_FCS                   (1 << 29)	/* B29 */
#define XGP_E0_TxCW_ENABLE 			(1 << 28)
// #define XGP_E0_TxCW_VLAN                	(1 << 27)
#define XGP_E0_TxCW_HD       			(1 << 26)

#define XGP_E0_FLOW 				0x2c0	/* Flow */
// B30 Tx enable
// B29 Rx enable
#define XGP_E0_MODECONFIG 			0x300	/* Emac configuration */
#define XGP_E0_ECFG_SPD				0xc000000 /* 10 = 1000 01 = 100 00 = 10
// B24 RX16
// B25 TX16
// B26 HOST_EN
// B27 1000BASE-X
// B28 SGMII_ENABLE
// B29 RGMII_ENABLE
// B30:31 SPEED
// 10 => 1000
// 01 => 100
// 00 => 10
// 11 => NA
//
// #define XGP_E0_RGMIISGMII                    0x320   /* RGMII/SGMII status */
// B0 RGMII Link
// B1 RGMII HD
// B2:3 RGMII_SPD
// B30:31 SGMII_SPD
#define XGP_E0_MGMTCONFIG 			0x340	/* Management configuration */
// B0:5 clock divisor
// B6 MDIO enable
#define XGP_E0_UNICAST0 			0x380	/* Unicast Address [31:0] */
#define XGP_E0_UNICAST1 			0x384	/* Unicast Address [47:32] */
#define XGP_E0_ATCW0 				0x388	/* Address Table Address [31:0] */
#define XGP_E0_ATCW1 				0x38c	/* Address Table Address [47:32] */
// B16:17 are the address index being read/written
// B23 RNW 1= READ 0 = WRITE
#define XGP_E0_ATCAF 				0x390	/* Address Table AF */
// *B31 promiscuous
#define XGP_E0_ISR 				0x3a0	
// B0 STATRST0
// B1 MIIMRST0
// B2 MIIMWST0
// B3 AFRST0
// B4 AFWST0
// B5 CFGRST0
// B6 CGFWST0
#define XGP_E0_IER 				0x3a4	
// B0 STATREN0
// B1 MIIMREN0
// B2 MIIMWEN0
// B3 AFREN0
// B4 AFWEN0
// B5 CFGREN0
// B6 CGFWEN0
#define XGP_E0_MIIM_WR 				0x3b0	/* MIIM write data */
#define XGP_E0_MIIM_ADDR 			0x3b4	/* Decode address for MIIM address output */

#define LL_REM_MASK         			0x0000000F
#define LL_EOP_MASK         			(1 << 5)
#define LL_SOP_MASK         			(1 << 6)
#define LL_EOF_MASK         			(1 << 7)
#define LL_SOF_MASK         			(1 << 8)

#define LL_TX_DATA				0x00
#define LL_TX_CTRL				0x08
#define LL_TX_RDY				0x10
#define LL_RX_DATA				0x20
#define LL_RX_CTRL				0x28
#define LL_RX_RDY				0x30

#define LL_RX_SRC_RDY_MASK  			(1 << 4)

// ISR RO 0x0
// B25 Tx
// B26 Rx
// B27 DMA/SG Tx
// B28 DMA/SG Rx
// B29 IPISC Interupts
// IPISC ISR 0x20
// B10 RAE		Recieve alignment error
// B11 RLE		receive long error 
// B12 RSE		receive short error
// B13 RLFE		receive length field error
// B14 RFCS		receive FCS error
// B15 RCE		receive collision error
// B16 RMFE		receive missed frame error
// B17 RDFO		receive data fifo overrun 
// B18 TPPR		tx pause packet received
// B19 TLFU		tx length FIFO underrun
// B20 TLFO		tx length FIFO overrun
// B21 TFSU		tx status FIFO underrun
// B22 TSFO		tx status FIFO overrun
// B23 RSVD		
// B24 RSVD
// B25 TLFF		tx length FIFO full
// B26 RLFE		rx length FIFO empty
// B27 TSFE		tx Status FIFO empty
// B28 RERR		rx error
// B29 TERR		tx error
// B30 RXCP		rx complete
// B31 TXCP		tx complete
// IPISC IER 0x28
// IER RW 0x8
// IPR RO 0x4
// IID RO 0x18
// Global Interrupt enable  0x1c
//
// IPIF registers
// TxDMA_Sg_Reset 	W	Base+0x2300
// TxDMA_Sg_MID ID 	R	Base+0x2300
// TxDMA_Sg_CTRL 	RW	Base+0x2304
// TxDMA_Sg_SRC 	RW	Base+0x2308
// TxDMA_Sg_DST 	RW	Base+0x230c
// TxDMA_Sg_Start_len 	RW	Base+0x2310
// TxDMA_Sg_Status 	R	Base+0x2314
// TxDMA_Sg_BDA 	RW	Base+0x2318
// TxDMA_Software CTRL 	RW	Base+0x231c
// TxDMA_Sg_UThesh 	RW	Base+0x2320
// TxDMA_Sg_PCThesh 	RW	Base+0x2324
// TxDMA_Sg_PWThesh 	RW	Base+0x2328
// TxDMA_Sg_ISR 	RW	Base+0x232c
// TxDMA_Sg_IER 	RW	Base+0x2330
// RxDMA_Sg_RESET 	W	Base+0x2340
// RxDMA_Sg_MID 	R	Base+0x2340
// RxDMA_Sg_CTRL 	RW	Base+0x2344
// RxDMA_Sg_SRC 	RW	Base+0x2348
// RxDMA_Sg_DST 	RW	Base+0x234c
// RxDMA_Sg_Start_len 	RW	Base+0x2350
// RxDMA_Sg_Status 	RW	Base+0x2354
// RxDMA_Sg_BDA 	RW	Base+0x2358
// RxDMA_Software CTRL 	RW	Base+0x235c
// RxDMA_Sg_UPC 	RW	Base+0x2360
// TxDMA_Sg_PCThesh 	RW	Base+0x2364
// TxDMA_Sg_PWThesh 	RW	Base+0x2368
// TxDMA_Sg_ISR 	RW	Base+0x236c
// TxDMA_Sg_IER 	RW	Base+0x2370
// WP_FIFO_RESET 	W	Base+0x2000
// WP_FIFO_MID 		R	Base+0x2000
// WP_FIFO_Vacancy	R	Base+0x2004
// WP_FIFO_Push		W	Base+0x2008
// WP_FIFO_Data		W	Base+0x2100-28ff
// RP_FIFO_RESET 	W	Base+0x2010
// RP_FIFO_MID 		R	Base+0x2010
// RP_FIFO_Vacancy	R	Base+0x2014
// RP_FIFO_Push		W	Base+0x2018
// RP_FIFO_Data		W	Base+0x2200-29ff
// DEVICE_ISR		R	Base+0x0
// DEVICE_IPR		R	Base+0x4
// DEVICE_IER		RW	Base+0x8
// DEVICE_IIR		R	Base+0x18
// DEVICE_GLB_IE	W	Base+0x1c
// IP_ISR		RW	Base+0x20
// IP_IER		RW	Base+0x28
// DEVICE_RESET		W	Base+0x40
// DEVICE_ID		W	Base+0x40
// EMAC registers
// EMAC_MID		R	Base+0x1100
// [0-3]MJRV[4-10]MNRV[11-15]REVL[16-23]BLID[24-31]BTYPE
// EMAC_CTRL		RW	Base+0x1104
// B0 FD		fULL dUPLEX
// B1 RSTTX		Reset Transmitter
// B2 ENTX		Enable Transmitter
// B3 RSTRX		reset receiver
// B4 ENRX		enable receiver
// B5 ENPHY		enable PHY
// B6 TXPAD		enable TX pad insertion
// B7 TXFCS		enable TX FCS insertion
// B8 TXSA		enable TX source insertion
// B9 TXERR		enable TX error insertion
// B10 SAOE		enable source address overwrite
// B11 ILBE		internal loopback enable
// B12 STRP		enabble RX FCS/Pad strip
// B13 RSVD
// B14 UA		enable Unicast address 
// B15 MA		enable multicast address
// M16 BA		enable broadcast address
// B17 PA		enable promiscuous
// B18 REO		receive error override
// B19 RXJB		enable receive jumbo frams
// B20 CAME		enable receive CAM
// B21 IPPE		enable Pause Packets
// B22 RSVD	
// B23 CAMW		Receive CAM write
// EMAC_IFGP		RW	Base+0x1108
// EMAC_SAH		RW	Base+0x110c	// Mac Address
// EMAC_SAL		RW	Base+0x1110
// EMAC_MII_MGTCR	RW	Base+0x1114
// B0 SB			Start/Busy
// B1 RWN			0=Write 1=Read
// 2-6 PHYAD Phy Address
// 7-11 REGAD Register address
// B12 IE 			enable MII
// B13 MIIRE	Reade error
// EMAC_MII_MGTDR	RW	Base+0x1118
// B16-31 MII Data register
// EMAC_RxPLR		R	Base+0x111c	// fifo
// EMAC_TxPLR		RW	Base+0x1120	// fifo
// EMAC_TxSTAT		R	Base+0x1124	// fifo
// B0 TXED Transmit Excess Deferral Error
// B1 PFIFOU	Packet FIFO underrun
// B2-6 TxA     # of attempts
// B7 	TXLC    Late collision error
// B31 TPCF
// EMAC_RxMFC		R	Base+0x1128
// EMAC_RxCC		R	Base+0x112c
// EMAC_RxFCS_EC	R	Base+0x1130
// EMAC_RxRAEC		R	Base+0x1134
// EMAC_TxEDC		R	Base+0x1138
// B31 Received Packet Complete
// EMAC_RxSTAT		R	Base+0x113c	// fifo
// EMAC_RxCAMH		R	Base+0x1140
// B10-15 CAM address 0-63
// B16-31 MAC Address High
// EMAC_RxCAML		R	Base+0x1144
//
//
// TeMAC Lite
// Txdata starts at Base+0x0
//
// Base+7f8 GIE Glocal interupt enable
// Base+7f4 Length of Tx Data
// Base+7fc
// B28 Transmit Interrupt enable
// B30 P
// B31 S Send enable - 1 to Send 0 to load data
// IF Ping Pong separate Tx at 0x800
// MAC defaults to 00-00-5e-00-fa-ce
// receive at 0x1000
// base+17fc B31 Data ready
// B28 Rx Interrupt Enable
//
// Receive PingPong 0x1800
//
#define XTE_MTU             			1500	/* max MTU size of Ethernet frame */
#define XTE_HDR_SIZE        			14	/* size of Ethernet header */
#define XTE_TRL_SIZE        			4	/* size of Ethernet trailer (FCS) */
#define XTE_MAX_FRAME_SIZE       		(XTE_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE)

#define SRC_RDY_TIMEOUT_ERROR         		-2
#define SOF_TIMEOUT_ERROR             		-3

/* Board/System/Debug information/definition ---------------- */

#define TRUE					1
#define FALSE					0

#define PFX DRV_CARDNAME ": "

#define IS_DBG 1		/* enable debugging */

#if IS_DBG
#define DEBUG_FUNC   printk("\n" DRV_CARDNAME ": %s()",__FUNCTION__);
#else
#define DEBUG_FUNC   do { } while(0);
#endif

#if IS_DBG
#define DEBUG_PRINTL(args...)   printk("\n" DRV_CARDNAME ": " args)
#else
#define DEBUG_PRINTL(args...)  do { } while(0)
#endif

#if IS_DBG
#define DEBUG_PRINTK(args...)   printk( args)
#else
#define DEBUG_PRINTK(args...)  do { } while(0)
#endif
/* use 0 for production, 1 for verification, >1 for debug */
#if IS_DBG
static unsigned int temac_debug = IS_DBG;
#else
static unsigned int temac_debug = 1;
#endif

#define TEMAC_RX_TIMEOUT 			(jiffies + ((1 * HZ)/5))
#define TEMAC_TX_TIMEOUT 			(jiffies + (2 * HZ))
#define TEMAC_MII_TIMEOUT  			(jiffies + (2 * HZ))	/* timer wakeup time : 2 second */

/*
Transmit timeout, default 5 seconds.
 */
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");

static struct platform_device *temac_device;

/* for exclusion of all program flows (processes, ISRs and BHs) */
DEFINE_SPINLOCK(XTE_spinlock);

/* Structure/enum declaration ------------------------------- */
// ndev->mem_start should be the FIFO address
//	unsigned char		perm_addr[MAX_ADDR_LEN]; /* permanent hw address */
//	struct dev_mc_list	*mc_list;	/* Multicast mac addresses	*/
//	int			mc_count;	/* Number of installed mcasts	*/
//	int			promiscuity;
//	unsigned char		dev_addr[MAX_ADDR_LEN];	/* hw address, (before bcast because most packets are unicast) */
typedef struct temac_local {
	struct list_head rcv;
	struct list_head xmit;
	spinlock_t lock;
	struct net_device_stats stats;

	void __iomem 		*nic_vaddr;		/* Register I/O base address */
	u16 			phy_addr;
	u32 			phy_status;
	u8  			regshift;
	u32 			msg_enable;		/* debug message level */

	struct resource 	*nic_addr_res;		/* resources found */
	struct resource 	*phy_addr_res;
	struct resource 	*nic_addr_req;		/* resources requested */
	struct resource 	*phy_addr_req;
	// struct resource 	*irq_res;

	unsigned int 		poll:1;
	unsigned int 		mii:1;			/* mii port available */
	struct mii_if_info 	mii_if;

	struct timer_list 	rx_timer;
	struct timer_list 	mii_timer;
} temac_local_t;

static void dump_skb(struct sk_buff *skb, char *str)
{
	int ii;
	u8 *cp = skb->data ;

	DEBUG_PRINTL("%s %d bytes", str, skb->len);
	for (ii = 0; ii < skb->len; ii++) {
		if (( ii % 16) == 0) DEBUG_PRINTK("\n");
		if (( ii % 8) == 0) DEBUG_PRINTK(" ");
		DEBUG_PRINTK("%02x ", cp[ii]);
	}
	DEBUG_PRINTK("\n");
}
static u32 
ior(struct net_device *ndev, int offset)
{
	u32 value;
	// struct temac_local *lp = ndev->priv;
	// offset <<= lp->regshift;

	value = (*(volatile u32 *) ( ndev->base_addr + offset)); 
	__asm__ __volatile__("eieio");
	return value;
}

static void
iow(struct net_device *ndev, int offset, u32 value)
{
	// struct temac_local *lp = ndev->priv;
	// offset <<= lp->regshift;

	(*(volatile u32 *)( ndev->base_addr + offset) = value); 
 	__asm__ __volatile__("eieio");
}
/*
  Read a word from phyxcer
 */
static u32 mdio_read(struct net_device *ndev, int phy_id, int reg_num)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	u32 ret = 0;
	// DEBUG_PRINTL("%s(%d,%d)",__FUNCTION__,phy_id, reg_num);

	if (lp->mii) {
		// DEBUG_PRINTK(" phy_addr=%x",lp->phy_addr);
		spin_lock_irqsave(&lp->lock, flags);
		mtdcr(lp->phy_addr + XGP_HIFLSW, ((phy_id << 5) | (reg_num)));
		mtdcr(lp->phy_addr + XGP_HIFCON, XGP_E0_MIIM_ADDR);
		while (!(mfdcr(lp->phy_addr + XGP_HIFRDY) & XGP_HIFRDY_DCR_MRR));

		ret = mfdcr(lp->phy_addr + XGP_HIFLSW);
		spin_unlock_irqrestore(&lp->lock, flags);
	}
	// DEBUG_PRINTK("=%x",ret);

	return ret;
}

/*
  Write a word to phyxcer
 */
static void mdio_write(struct net_device *ndev, int phy_id, int reg_num, u32 val)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	int EmacNum = 0;
	// DEBUG_FUNC;

	if (lp->mii) {
		spin_lock_irqsave(&lp->lock, flags);
		mtdcr(lp->phy_addr + XGP_HIFLSW, (val));
		mtdcr(lp->phy_addr + XGP_HIFCON, XGP_HIFCON_DCR_WRITE | XGP_E0_MIIM_WR);
		mtdcr(lp->phy_addr + XGP_HIFLSW, ((phy_id << 5) | (reg_num)));
		mtdcr(lp->phy_addr + XGP_HIFCON, XGP_HIFCON_DCR_WRITE | ((EmacNum) << 10) | XGP_E0_MIIM_ADDR);
		while (!(mfdcr(lp->phy_addr + XGP_HIFRDY) & XGP_HIFRDY_DCR_MWR));
		spin_unlock_irqrestore(&lp->lock, flags);
	}
}

static u32 emac_cfg_read(struct net_device *ndev, int phy_id, int reg_num)
{
	struct temac_local *lp = ndev->priv;
	int EmacNum = 0;
	u32 ret = 0;

	if (lp->mii) {
		mtdcr(lp->phy_addr + XGP_HIFCON, (EmacNum << 10) | (reg_num));
		while (!(mfdcr(lp->phy_addr + XGP_HIFRDY) & (XGP_HIFRDY_DCR_GRR << (8 * (EmacNum)))));
		ret = mfdcr(lp->phy_addr + XGP_HIFLSW);
	}

	return ret;
}

static u32 emac_AF_read(struct net_device *ndev, int reg_num)
{
	struct temac_local *lp = ndev->priv;
	int EmacNum = 0;
        mtdcr((lp->phy_addr) + XGP_HIFCON, (EmacNum << 10) | (reg_num)); 
        while ( !(mfdcr(lp->phy_addr + XGP_HIFRDY )  & (XGP_HIFRDY_DCR_ARR << (8*(EmacNum))))); 
        mfdcr((lp->phy_addr) + XGP_HIFLSW) ; 
}

static void emac_AF_write(struct net_device *ndev, int reg_num, u32 val)
{
	struct temac_local *lp = ndev->priv;
	int EmacNum = 0;

	if (lp->mii) {
		mtdcr(lp->phy_addr + XGP_HIFLSW, (val));
		mtdcr(lp->phy_addr + XGP_HIFCON, XGP_HIFCON_DCR_WRITE | ((EmacNum) << 10) | (reg_num));
		while (!(mfdcr(lp->phy_addr + XGP_HIFRDY) & XGP_HIFRDY_DCR_AWR));
	}
}

u32 emac_AFM_read(struct net_device *ndev, int reg_num, int MultAddrReg, u32 *mult_addr_msw, u32 *mult_addr_lsw)  
{
	struct temac_local *lp = ndev->priv;
	int EmacNum = 0;
        mtdcr((lp->phy_addr) + XGP_HIFLSW, MultAddrReg ); 
        mtdcr((lp->phy_addr) + XGP_HIFCON, XGP_HIFCON_DCR_WRITE | (EmacNum << 10) | reg_num); 
        while ( !(mfdcr(lp->phy_addr + XGP_HIFRDY )  & (XGP_HIFRDY_DCR_ARR << (8*EmacNum)))); 
        *mult_addr_lsw = mfdcr((lp->phy_addr) + XGP_HIFLSW); 
        *mult_addr_msw = mfdcr((lp->phy_addr) + XGP_HIFMSW); 
}

static void emac_AFM_write(struct net_device *ndev, int reg_num, u32 mult_addr_msw, u32 mult_addr_lsw)  
{
        emac_AF_write(ndev, XGP_E0_ATCW0, (((mult_addr_msw << 24) & 0xff000000) | ((mult_addr_msw << 8) & 0x00ff0000) | ((mult_addr_msw >> 8) &  0x0000ff00) | ((mult_addr_msw >> 24) & 0x000000ff))); 
        emac_AF_write(ndev, XGP_E0_ATCW1, (reg_num << 16) | ((mult_addr_lsw<<8) & 0xff00) | ((mult_addr_lsw >>8 ) & 0x00ff)) ;

}

static void emac_cfg_write(struct net_device *ndev, int phy_id, int reg_num, u32 val)
{
	struct temac_local *lp = ndev->priv;
	int EmacNum = 0;

	if (lp->mii) {
		mtdcr(lp->phy_addr + XGP_HIFLSW, (val));
		mtdcr(lp->phy_addr + XGP_HIFCON, XGP_HIFCON_DCR_WRITE | (EmacNum << 10) | (reg_num));
		while (!(mfdcr(lp->phy_addr + XGP_HIFRDY) & XGP_HIFRDY_DCR_GWR));
	}
}

void disp_emac_cfg(struct net_device *ndev, char *rname, int  regnum, int idx)
{
	u32 ret;
	int ii;

	if ((idx % 4) == 0) DEBUG_PRINTK("\n\t");
	ret = emac_cfg_read(ndev, 0, regnum); 		
	if (rname) {
		DEBUG_PRINTK("%s:", rname);
		for(ii=strlen(rname);ii<10;ii++) DEBUG_PRINTK(" ");
		DEBUG_PRINTK("0x%08x  ", ret);
	} else 
		DEBUG_PRINTK("R%03d:      0x%08x  ",  regnum, ret);
}

void disp_mii(struct net_device *ndev, char *rname, int  regnum)
{
	u32 ret;
	int ii;
	if ((regnum % 4) == 0) DEBUG_PRINTK("\n\t");
	ret =  mdio_read(ndev, PHY_NUM, regnum);
	if (rname) {
		DEBUG_PRINTK("%s:", rname);
		for(ii=strlen(rname);ii<10;ii++) DEBUG_PRINTK(" ");
		DEBUG_PRINTK("0x%08x  ", ret);
	} else 
		DEBUG_PRINTK("R%02d:       0x%08x  ",  regnum, ret);
}

#define res_size(_r) (((_r)->end - (_r)->start) + 1)
static void temac_dump(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	u32 ret;
	char * name;
	int ii, jj;

	DEBUG_PRINTK("\nresources:\n\tirq=%d", ndev->irq);
	DEBUG_PRINTK("\n\tnic  physical address =%x ", lp->nic_addr_res->start);
	DEBUG_PRINTK("size %x ", res_size(lp->nic_addr_res));
	DEBUG_PRINTK("virtual address %lx", ndev->base_addr);
	DEBUG_PRINTK("\n\t irq=%d mem=%lx phy=%x", ndev->irq, ndev->base_addr, lp->phy_addr);

	DEBUG_PRINTK("\n\t");
	ret = mdio_read(ndev, PHY_NUM, MII_SSR) ;
	switch (ret & MII_SSR_SPDMASK) {
		case MII_SSR_SPD1000:
			DEBUG_PRINTK("1000");
			break;
		case MII_SSR_SPD100:
			DEBUG_PRINTK("100");
			break;
		case MII_SSR_SPD10:
			DEBUG_PRINTK("10");
			break;
		default:
			DEBUG_PRINTK("Error : invalid PHY mode/speed");
	};
	DEBUG_PRINTK("BASE-T, ");
	if ((ret & MII_SSR_FD) == MII_SSR_FD) {
		DEBUG_PRINTK("FD");
	} else {
		DEBUG_PRINTK("HD");
	}
	if ((ret & MII_SSR_LINK)  == 0) {
		DEBUG_PRINTK(" Ethernet Link Down");
	}
	/* read mii phy registers */
	DEBUG_PRINTK("\nReading PHY Regs through DCR:\n\t");
	for (ii = 0; ii < 32; ii++) {
		switch (ii) {
			case MII_ANI:
				disp_mii(ndev, "ANI", ii);
				break;
			case MII_SSR:
				disp_mii(ndev, "SSR", ii);
				break;
			case MII_ISR:
				disp_mii(ndev, "ISR", ii);
				break;
			case MII_BMCR:
				disp_mii(ndev, "MCR", ii);
				break;
			case MII_BMSR:
				disp_mii(ndev, "MSR", ii);
				break;
			case MII_PHYSID1:
				disp_mii(ndev,"PHYSID1",ii);
				break;
			case MII_PHYSID2:
				disp_mii(ndev,"PHYSID2",ii);
				break;
			case MII_ADVERTISE:
				disp_mii(ndev,"ADV", ii);
				break;
			case MII_LPA:
				disp_mii(ndev,"LPA", ii);
				break;
			case MII_EXPANSION:
				disp_mii(ndev,"EXPANSION", ii);
				break;
			case MII_CTRL1000:
				disp_mii(ndev,"CTRL1000", ii);
				break;
			case MII_STAT1000:
				disp_mii(ndev,"STAT1000", ii);
				break;
			case MII_ESTATUS:
				disp_mii(ndev,"ESTATUS", ii);
				break;
			case MII_DCOUNTER:
				disp_mii(ndev,"DCOUNTER", ii);
				break;
			case MII_NWAYTEST:
				disp_mii(ndev,"NWAYTEST", ii);
				break;
			case MII_RERRCOUNTER:
				disp_mii(ndev,"RERRCOUNT", ii);
				break;
			case MII_SREVISION:
				disp_mii(ndev,"SREVISION",ii);
				break;
			case MII_RESV1:
				disp_mii(ndev,"RESV1",ii);
				break;
			case MII_LBRERROR:
				disp_mii(ndev,"LBERROR",ii);
				break;
			case MII_PHYADDR:
				disp_mii(ndev,"PHYADDR",ii);
				break;
			case MII_RESV2:
				disp_mii(ndev,"RESV2",ii);
				break;
			case MII_TPISTATUS:
				disp_mii(ndev,"TPISTATUS",ii);
				break;
			case MII_NCONFIG:
				disp_mii(ndev,"NCONFIG",ii);
				break;
#if 0
			case MII_FCSCOUNTER:
				disp_mii(ndev,"FCSCOUNTER",ii);
				break;
#endif
			default:
				disp_mii(ndev,0, ii);
				break;
		}
	}
	/*
	Print TEMAC Receiver and Transmitter configuration 
	*/
	DEBUG_PRINTK("\nReading TEMAC Regs:\n");

	for (ii = 0x200,jj = 0; ii <= 0x3a4; ii += 4) {
		switch (ii) {
			case XGP_E0_RxCW0:
				disp_emac_cfg(ndev, "RxCW0", ii, jj++);
				break;
			case XGP_E0_RxCW1:
				disp_emac_cfg(ndev, "RxCW1", ii, jj++);
				break;
			case XGP_E0_TxCW:
				disp_emac_cfg(ndev, "TxCW", ii, jj++);
				break;
			case XGP_E0_FLOW:
				disp_emac_cfg(ndev, "Flow", ii, jj++);
				break;
			case XGP_E0_MODECONFIG:
				disp_emac_cfg(ndev, "ModeCfg", ii, jj++);
				break;
			case XGP_E0_MGMTCONFIG:
				disp_emac_cfg(ndev, "MgmtCfg", ii, jj++);
				break;
			case XGP_E0_UNICAST0:
				disp_emac_cfg(ndev, "MacAddr0", ii, jj++);
				break;
			case XGP_E0_UNICAST1:
				disp_emac_cfg(ndev, "MacAddr1", ii, jj++);
				break;
			case XGP_E0_ATCW0:
				disp_emac_cfg(ndev, "AtAddr0", ii, jj++);
				break;
			case XGP_E0_ATCW1:
				disp_emac_cfg(ndev, "AtAddr1", ii, jj++);
				break;
			case XGP_E0_ATCAF:
				disp_emac_cfg(ndev, "AtCAF", ii, jj++);
				break;
			case XGP_E0_ISR:
				disp_emac_cfg(ndev, "ISR", ii, jj++);
				break;
			case XGP_E0_IER:
				disp_emac_cfg(ndev, "IER", ii, jj++);
				break;
			default:
				break;
		}
	}
}

/*
OPTIONAL
void (*set_multicast_list)(struct net_device *dev); 
Method called when the multicast list for the device changes and when the 
flags change. See the section Multicast for further details and a sample 
implementation. 
*/
static void temac_set_multicast_list(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	struct dev_mc_list *mcptr = ndev->mc_list;
	int mc_cnt = ndev->mc_count;
	u32 hash_val;
	u16 i, oft, hash_table[4];
	unsigned long flags;

	// DEBUG_FUNC;

	spin_lock_irqsave(&lp->lock, flags);
#if 0
	for (i = 0, oft = 0x10; i < 6; i++, oft++)
		iow(lp, oft, ndev->dev_addr[i]);

	/* Clear Hash Table */
	for (i = 0; i < 4; i++)
		hash_table[i] = 0x0;

	/* broadcast address */
	hash_table[3] = (1 << 15);

	/* the multicast address in Hash Table : 64 bits */
	for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
		hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
		hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
	}

	/* Write the hash table to MAC MD table */
	for (i = 0, oft = 0x16; i < 4; i++) {
		iow(lp, oft++, hash_table[i] & 0xff);
		iow(lp, oft++, (hash_table[i] >> 8) & 0xff);
	}
#endif
	spin_unlock_irqrestore(&lp->lock, flags);
}
/* 
Changes the mac address if the controller is not running. 

int (*set_mac_address)(struct net_device *dev, void *addr); 
Function that can be implemented if the interface supports the ability to change its 
hardware address. Many interfaces don't support this ability at all. Others use the 
default eth_mac_addr implementation (from drivers/net/net_init.c). eth_mac_addr 
only copies the new address into dev->dev_addr, and it does so only if the interface 
is not running. Drivers that use eth_mac_addr should set the hardware MAC 
address from dev->dev_addr in their open method. 

*/
u32 temac_set_mac_address(struct net_device *ndev, void *addr)
{
	u32 ret;

	// DEBUG_FUNC;
	if (addr) 
		memcpy(ndev->dev_addr, addr, ETH_ALEN);

	if (!is_valid_ether_addr(ndev->dev_addr)) {
		printk("%s: Invalid ethernet MAC address.  Please set using ifconfig\n", ndev->name);
		return -EIO;
	}

#if 0
	{
	int ii;
	DEBUG_PRINTK(" MAC: ");
		for (ii = 0; ii < 5; ii++)
			printk("%02x:", ndev->dev_addr[ii]);
		printk("%02x", ndev->dev_addr[5]);
	}
#endif

	/* set up unicast MAC address filter */
	ret = ((ndev->dev_addr[3] << 24) | (ndev->dev_addr[2] << 16) | (ndev->dev_addr[1] << 8) | (ndev->dev_addr[0]));
	// printk("%08x", ret);
	emac_AF_write(ndev, XGP_E0_UNICAST0, ret);

	ret = (ndev->dev_addr[5] << 8) | ndev->dev_addr[4];
	// printk("%08x", ret);
	emac_AF_write(ndev, XGP_E0_UNICAST1, ret);

	/* enable address filter */
	emac_AF_write(ndev, XGP_E0_ATCAF, 0);
	return 0;
}
/*
Initilize temac board
 */
static void
temac_reset(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	u32 ret;
	int i;

	// DEBUG_FUNC;
	/* 
	   Hold the PHY in reset 
	 */
	mtdcr(XPAR_LL_TEMAC_0_DCR_BASEADDR, XGP_CON_REG_PHY_RESET);
	mdelay(40);
	/* 
	   reset the peripheral and the emac
	   hold the temac in reset since this bit is not self-clearing
	 */
	mtdcr(XPAR_LL_TEMAC_0_DCR_BASEADDR, XGP_CON_REG_TEMAC_RESET);
	mdelay(40);

	/* 
	   reset the peripheral. This is self-clearing. This will also kick 
	   the temac and PHY out of reset.  
	   Note that this may enable the Tx/Rx of the 
	   temac if tied-off that way in hardware, but that should be OK. 
	 */
	mtdcr(XPAR_LL_TEMAC_0_DCR_BASEADDR, XGP_CON_REG_PERIPH_RESET);
	mdelay(40);

	mtdcr(XPAR_LL_TEMAC_0_DCR_BASEADDR, 0);
	mdelay(40);

	/* disable the Tx/Rx first */
	ret = emac_cfg_read(ndev, 0, XGP_E0_TxCW);
	emac_cfg_write(ndev, 0, XGP_E0_TxCW, (ret & (~XGP_E0_TxCW_ENABLE)));
	ret = emac_cfg_read(ndev, 0, XGP_E0_RxCW1);
	emac_cfg_write(ndev, 0, XGP_E0_RxCW1, (ret & (~XGP_E0_RxCW1_ENABLE)));

	/* Set up MII management registers to write to PHY  */
	emac_cfg_write(ndev, 0, XGP_E0_MGMTCONFIG, XGP_MDIO_ENABLE_MASK | XGP_MDIO_CLOCK_DIV_100MHz);
	/* 
	Set A-N Advertisement Regs for Full Duplex modes ONLY
	address 4 = Autonegotiate Advertise Register
	Disable 1000 Mbps for negotiation if not built for GEth
	*/
	ret = mdio_read(ndev, PHY_NUM, MII_ADVERTISE);
	// mdio_write(ndev, PHY_NUM, MII_ADVERTISE, ((ret & (~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF)) | (ADVERTISE_100FULL | ADVERTISE_10FULL))));
	mdio_write(ndev, PHY_NUM, MII_ADVERTISE, (ret | ADVERTISE_CSMA | ADVERTISE_100FULL));
	mdio_write(ndev, PHY_NUM, MII_CTRL1000, 0);
	// ret = mdio_read(ndev, PHY_NUM, MII_CTRL1000); 
	// mdio_write(ndev, PHY_NUM, MII_CTRL1000, ((ret & 0x0000FEFF) | ADVERTISE_1000FULL));
	/*
	Soft reset the PHY
	address 0 = Basic Mode Control Register
	*/
	ret = mdio_read(ndev, PHY_NUM, MII_BMCR);
	mdio_write(ndev, PHY_NUM, MII_BMCR, (ret | BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART));

	if (lp->poll == 0) {
		/* 
		uncomment these lines if you need to set up interrupt from the PHY
		to notify the CPU of link status changes
		*/
		/* 
		set up interrupt from the PHY
		Speed change                 	(14)
		Duplex                       	(13)
		Autoneg Complete     		(11)
		Link Status          		(10)
		*/
		mdio_write(ndev, PHY_NUM, MII_DCOUNTER, 0x6c00);
		/* 
		at this point, the PHY interrupts the CPU when link is established. The Phy interrupt
		handler will have to call ltemac_Reset to set the MAC appropriately
		*/
	}

	/*
	Wait for a PHY Link (auto-negotiation to complete)...
	*/
	// DEBUG_PRINTK("\nWaiting for ethernet link..");
	ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
	i = 64;
	while (((ret & BMSR_LSTATUS) != BMSR_LSTATUS) && i--) {
		DEBUG_PRINTK(".");
		mdelay(500);
		ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
	}

	ret = mdio_read(ndev, PHY_NUM, MII_SSR) ;
	switch (ret & MII_SSR_SPDMASK) {
		case MII_SSR_SPD1000: 				/* 1000Base-T */
			emac_cfg_write(ndev, 0, XGP_E0_MODECONFIG, XGP_E0_MODECONFIG_SPD1000);
			break;
		case MII_SSR_SPD100: 				/* 100Base-T */
			emac_cfg_write(ndev, 0, XGP_E0_MODECONFIG, XGP_E0_MODECONFIG_SPD100);
			break;
		case MII_SSR_SPD10: 				/* 10Base-T */
			emac_cfg_write(ndev, 0, XGP_E0_MODECONFIG, XGP_E0_MODECONFIG_SPD10);
			break;
	};
	if ((ret & MII_SSR_FD) != (MII_SSR_FD)) {
			/* set up Tx/Rx config reg for half duplex */
			ret = emac_cfg_read(ndev, 0, XGP_E0_TxCW);
			emac_cfg_write(ndev, 0, XGP_E0_TxCW, ret | XGP_E0_TxCW_HD);
			ret = emac_cfg_read(ndev, 0, XGP_E0_RxCW1);
			emac_cfg_write(ndev, 0, XGP_E0_RxCW1, ret | XGP_E0_RxCW1_HD);
	}
	temac_set_mac_address(ndev,0);
	/* Set address filter table */
	temac_set_multicast_list(ndev);
	/*
	 Enable EMAC TX and RX
	*/
	ret = emac_cfg_read(ndev, 0, XGP_E0_TxCW);
	emac_cfg_write(ndev, 0, XGP_E0_TxCW, ret | XGP_E0_TxCW_ENABLE);

	ret = emac_cfg_read(ndev, 0, XGP_E0_RxCW1);
	emac_cfg_write(ndev, 0, XGP_E0_RxCW1, ret | XGP_E0_RxCW1_ENABLE);

#define XTE_IPISR_OFFSET 0x00000020  /**< IP interrupt status */
#define XTE_IPIER_OFFSET 0x00000028  /**< IP interrupt enable */
#define XTE_IPXR_XMIT_DONE_MASK         0x00000001 /**< Tx complete */
#define XTE_IPXR_RECV_DONE_MASK         0x00000002 /**< Rx complete */

	/* Get contents of IPIER register */
	// ret = ior(ndev, XTE_IPIER_OFFSET);
	ret = emac_cfg_read(ndev, 0, XGP_E0_IER);

	DEBUG_PRINTK("\nIER: = %08x ", ret);
	ret |= XTE_IPXR_XMIT_DONE_MASK;
	ret |= XTE_IPXR_RECV_DONE_MASK;
	DEBUG_PRINTK("\t%08x", ret);
	/* Update IPIER with new setting */
	emac_cfg_write(ndev, 0, XGP_E0_IER, ret);
	// iow(ndev, XTE_IPIER_OFFSET, ret);
	// ret = ior(ndev, XTE_IPIER_OFFSET);
	ret = emac_cfg_read(ndev, 0, XGP_E0_IER);
	DEBUG_PRINTK("\t%08x", ret);

	/*
 	Print PHY regs so that we can see if they are configured correctly
 	*/
	// temac_dump(ndev);

	/* Init Driver variable */
	ndev->trans_start = 0;
	spin_lock_init(&lp->lock);
}

#define C_IDLE    (1)
#define C_HEADER  (2)
#define C_PAYLOAD (3)
#define C_FOOTER  (4)

int recvFrame(struct net_device *ndev, struct sk_buff *skb, u8 *bd)
{
	u32 cw;
	u32 dw;
	u32 src_rdy_counter = 0;
	u32 sof_counter = 0;
	enum { S_IDLE=1, S_HEADER, S_PAYLOAD, S_FOOTER } state = S_IDLE;
	const u32 src_rdy_counter_max = 10;
	const u32 sof_counter_max = 10;
	u8 *b ;
	// DEBUG_FUNC;
	// skb->data SHOULD == skb-->tail; 
	b = skb->tail ;
	skb->len = 0;
	while (1) {

		/*  read the control word to see if the source ready bit is active (low) */
		while (((cw = ior(ndev,LL_RX_CTRL)) & LL_RX_SRC_RDY_MASK) == LL_RX_SRC_RDY_MASK) {

			/* only hang around for some amount of time waiting for data to be present */
			// DEBUG_PRINTK("00 cw=%08x )", cw);
			if (++src_rdy_counter == src_rdy_counter_max) {
				skb->len = 0;
				//DEBUG_PRINTK(" SRC_RDY_TIMEOUT_ERROR");
				return SRC_RDY_TIMEOUT_ERROR;
			}
		}

		/* reset watchdog counter */
		src_rdy_counter = 0;
		/* 
		IDLE STATE
		*/
		if (state == S_IDLE) {
			// DEBUG_PRINTK("S_IDLE");

			/*
			haven't reached the SOF yet
			only hang around for so long awaiting an SOF before returning
			*/
			if ((cw & LL_SOF_MASK)) {
				if (++sof_counter == sof_counter_max) {
					skb->len = 0;
					// DEBUG_PRINTK(" SOF_TIMEOUT_ERROR");
					return SOF_TIMEOUT_ERROR;
				}
			}

			else {

				/*
				reset watchdog counter
				*/
				sof_counter = 0;
				/* 
				ensure we record the start of a frame
				*/
				state = S_HEADER;
			}

			/* ignore the LocalLink Header Words */
			dw = ior(ndev,LL_RX_DATA);
			// DEBUG_PRINTK(" %08x",dw);
		}
		/*
		HEADER STATE
		*/
		else if (state == S_HEADER) {
			// DEBUG_PRINTK("S_HEADER");
			dw = ior(ndev, LL_RX_DATA);
			if (!(cw & LL_SOP_MASK)) {
				state = S_PAYLOAD;
				// DEBUG_PRINTK(" %08x",dw);
				*((u32 *) b) = dw;
				b += 4;
				skb->len += 4;
				skb->tail += 4;
			}
		}
		/* PAYLOAD STATE */
		else if (state == S_PAYLOAD) {
			// DEBUG_PRINTK("S_PAYLOAD");
			*((u32 *) b) = ior(ndev,LL_RX_DATA);
			// DEBUG_PRINTK(" %08x",dw);
			b += 4;
			skb->len += 4;
			skb->tail += 4;
			if (!(cw & LL_EOP_MASK))
				state = S_FOOTER;
		}
		/* FOOTER STATE */
		else if (state == S_FOOTER) {
			// DEBUG_PRINTK("S_FOOTER");
			*((u32 *) bd) = ior(ndev,LL_RX_DATA);
			// DEBUG_PRINTK(" %08x",dw);
			bd += 4;
			if (!(cw & LL_EOF_MASK)) {

				/* we've got the last data word */
				cw = ~cw & LL_REM_MASK;
				/* update the payload count according to the (masked) rem value */
				while (cw) {
					if (cw & 1)
						skb->len++;
					cw >>= 1;
				}

				/* return success */
				// DEBUG_PRINTK("SUCCESS");
				return 0;
			}
		}
	}
}

u8 recvBd[] = {
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00
};
/*
 Received a packet and pass to upper layer
 */
static void temac_rx(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	struct sk_buff *skb;
	u32 ret = 0;

	// DEBUG_FUNC;
	skb = dev_alloc_skb(XTE_MAX_FRAME_SIZE);
	if (!skb) {
		if (printk_ratelimit())
			printk(KERN_NOTICE "temac: low on memory - packet dropped\n");
		lp->stats.rx_dropped++;
		return;
	}
	if ((ret = recvFrame(ndev, skb, recvBd)) == 0) {
		// dump_skb(skb, "Recv");
		/* Pass to upper layer */
		skb->dev = ndev;
		skb->protocol = eth_type_trans(skb, ndev);
		lp->stats.rx_bytes += skb->len;
		lp->stats.rx_packets++;
		netif_rx(skb);
	} else {
		//DEBUG_PRINTK("\nwhoops=%x",ret);
	}
}
static irqreturn_t temac_interrupt(int irq, void *ndev_id, struct pt_regs *regs)
{
	struct net_device *ndev = ndev_id;
	struct temac_local *lp = ndev->priv;
	int ret ;
	// DEBUG_FUNC;
	if (!ndev) {
		DEBUG_PRINTK("temac_interrupt() without DEVICE arg\n");
		return IRQ_HANDLED;
	}
	switch (irq) {
		case -1:
			DEBUG_PRINTK("temac_interrupt() NET_POLL\n");
			break;
		case -2:
			// DEBUG_PRINTK("temac_interrupt() temac_rx_timeout()\n");
			break;
		case 0:
			DEBUG_PRINTK("temac_interrupt() INT\n");
			break;
		default:
			DEBUG_PRINTK("temac_interrupt() IRQ=%d\n",irq);
			break;
	}
	disp_emac_cfg(ndev, "ISR", XGP_E0_ISR,0); 		
	disp_emac_cfg(ndev, "IER", XGP_E0_IER,0); 		

	/* A real interrupt coming */
	spin_lock(&lp->lock);
	/*  read the control word to see if the source ready bit is active (low) */
	while ((ior(ndev, LL_RX_CTRL) & LL_RX_SRC_RDY_MASK) != LL_RX_SRC_RDY_MASK) 
		temac_rx(ndev);

	/* read from PHY status register */
	ret = mdio_read(ndev, PHY_NUM, MII_FCSCOUNTER);
	if (lp->phy_status != ret) {
		lp->phy_status = ret;
		DEBUG_PRINTK("\nPHY interrupt status register %08x", ret);
		if ((ret & 0x00000c00) == (0x00000c00)) {
			DEBUG_PRINTK("\nautoneg complete , link up");
		} else if (ret & 0x00000400) {
			DEBUG_PRINTK("\nlink down");
		}
	}

	spin_unlock(&lp->lock);
	return IRQ_HANDLED;
}
/* 
OPTIONAL
void (*poll_controller)(struct net_device *dev); 
Function that asks the driver to check for events on the interface in situations 
where interrupts are disabled. It is used for specific in-kernel networking tasks, 
such as remote consoles and kernel debugging over the network. 

*/
#ifdef CONFIG_NET_POLL_CONTROLLER
static void temac_poll_controller(struct net_device *ndev)
{
	DEBUG_FUNC;
	disable_irq(ndev->irq);
	temac_interrupt(-1, ndev, NULL);
	enable_irq(ndev->irq);
}
#endif

/*
 * This function is used to handle ports that do not have an interrupt.
 */
static void 
temac_rx_timeout(unsigned long data)
{
	struct net_device *ndev = (struct net_device *) data;
	struct temac_local *lp = ndev->priv;

	// DEBUG_FUNC;
	disable_irq(ndev->irq);
	temac_interrupt(-2, ndev, NULL);
	enable_irq(ndev->irq);

	mod_timer(&lp->rx_timer, TEMAC_RX_TIMEOUT);
}
/*
 A periodic timer routine
 Dynamic media sense, allocated Rx buffer...
 */
static void temac_mii_timeout(unsigned long data)
{
	struct net_device *ndev = (struct net_device *) data;
	struct temac_local *lp = ndev->priv;

	// DEBUG_PRINTL("temac_mii_timer()");
	mii_check_media(&lp->mii_if, netif_msg_link(lp), 0);
	/* Set timer again */
	mod_timer(&lp->mii_timer, TEMAC_MII_TIMEOUT);
}

/*

Open the interface.

The interface is opened whenever "ifconfig" actives it.
The open method should register any system resource it needs (I/O ports, IRQ, 
DMA, etc.), turn on the hardware, and perform any other setup your device 
requires. 

First, the hardware (MAC) address needs to be copied from the hardware device to 
dev->dev_addr before the interface can communicate with the outside world. The 
hardware address can then be copied to the device at open time. The snull software 
interface assigns it from within open; it just fakes a hardware number using an ASCII 
string of length ETH_ALEN, the length of Ethernet hardware addresses. 

The open method should also start the interfaces transmit queue (allowing it to 
accept packets for transmission) once it is ready to start sending data. The kernel 
provides a function to start the queue: 

void netif_start_queue(struct net_device *dev); 

The open code for snull looks like the following: 

int snull_open(struct net_device *dev) 

{

request_region(), request_irq(), .... (like fops->open) 

Assign the hardware address of the board: use "\0SNULx", where 
x is 0 or 1. The first byte is '\0' to avoid being a multicast 
address (the first byte of multicast addrs is odd). 

; 
if (dev == snull_devs[1]
)

 */
static int
temac_open(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;

	// DEBUG_FUNC;

	/*
	 * Just to be safe, stop TX queue and the ndevice first. If the ndevice is
	 * already stopped, an error will be returned. In this case, we don't
	 * really care,.
	 */
	netif_stop_queue(ndev);
	spin_lock_irqsave(&XTE_spinlock, flags);

	if (ndev->mtu > XTE_MTU)
		ndev->mtu = XTE_MTU;

	/*
	Enable interrupts if not in polled mode 
	*/
	if (ndev->irq < 0) {
		lp->poll = 1;
	} else if (request_irq(ndev->irq, &temac_interrupt, SA_SHIRQ, ndev->name, ndev)) {
			printk(KERN_ERR "%s: XTemac could not allocate interrupt %d. reverting to polled IO\n", ndev->name, ndev->irq);
			lp->poll = 1;
			// spin_unlock_irqrestore(&XTE_spinlock, flags);
			// return -EAGAIN;
	}
	
	/* Initialize TEMAC board */
	temac_reset(ndev);

	/* set and active a timer process */
	lp->mii_timer.data     = (unsigned long) ndev;
	lp->mii_timer.function = &temac_mii_timeout;
	init_timer(&lp->mii_timer);
	mod_timer(&lp->mii_timer, TEMAC_MII_TIMEOUT);

	lp->rx_timer.data     = (unsigned long) ndev;
	lp->rx_timer.function = &temac_rx_timeout;
	init_timer(&lp->rx_timer);
	mod_timer(&lp->rx_timer, TEMAC_RX_TIMEOUT);

	mii_check_media(&lp->mii_if, netif_msg_link(lp), 1);
	// netif_carrier_on(ndev);
	/* We're ready to go. */
	netif_start_queue(ndev);
	spin_unlock_irqrestore(&XTE_spinlock, flags);

	return 0;
}

static int temac_ethtool_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	int r = -EOPNOTSUPP;

	if (lp->mii) {
		spin_lock_irqsave(&lp->lock, flags);
		mii_ethtool_gset(&lp->mii_if, cmd);
		spin_unlock_irqrestore(&lp->lock, flags);
		r = 0;
	}
	return r;
}

static int
temac_ethtool_set_settings(struct net_device *ndev,
			   struct ethtool_cmd *cmd)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	int r = -EOPNOTSUPP;

	if (lp->mii) {
		spin_lock_irqsave(&lp->lock, flags);
		r = mii_ethtool_sset(&lp->mii_if, cmd);
		spin_unlock_irqrestore(&lp->lock, flags);
	}
	return r;
}

static void
temac_ethtool_get_drvinfo(struct net_device *ndev,
			  struct ethtool_drvinfo *info)
{

	memset(info, 0, sizeof(struct ethtool_drvinfo));
	strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
	strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
	/* Also tell how much memory is neinfoinfo for dumping register values */
	info->regdump_len = 0;
}

static u32 temac_ethtool_get_link(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	int r;

	spin_lock_irqsave(&lp->lock, flags);
	r = mii_link_ok(&lp->mii_if);
	spin_unlock_irqrestore(&lp->lock, flags);

	return r;
}

static u32 temac_ethtool_get_msglevel(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	return lp->msg_enable;
}

static void temac_ethtool_set_msglevel(struct net_device *ndev, u32 value)
{
	struct temac_local *lp = ndev->priv;
	lp->msg_enable = value;
}

static int temac_ethtool_nway_reset(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	int r = -EOPNOTSUPP;

	if (lp->mii) {
		spin_lock_irqsave(&lp->lock, flags);
		r = mii_nway_restart(&lp->mii_if);
		spin_unlock_irqrestore(&lp->lock, flags);
	}
	return r;
}

static struct ethtool_ops temac_ethtool_ops = {
	.get_settings = temac_ethtool_get_settings,
	.set_settings = temac_ethtool_set_settings,
	.get_drvinfo = temac_ethtool_get_drvinfo,
	.get_msglevel = temac_ethtool_get_msglevel,
	.set_msglevel = temac_ethtool_set_msglevel,
	.nway_reset = temac_ethtool_nway_reset,
	.get_link = temac_ethtool_get_link,
	.get_tx_csum = ethtool_op_get_tx_csum,
	.get_sg = ethtool_op_get_sg,
	.get_tso = ethtool_op_get_tso,
	.get_perm_addr = ethtool_op_get_perm_addr,
};
/* TEMAC network board routine ---------------------------- */

/* 
OPTIONAL

int (*change_mtu)(struct net_device *dev, int new_mtu); 
Function that takes action if there is a change in the maximum transfer unit (MTU) 
for the interface. If the driver needs to do anything particular when the MTU is 
changed by the user, it should declare its own function; otherwise, the default does 
the right thing. snull has a template for the function if you are interested. 
*/
int temac_change_mtu(struct net_device *dev, int new_mtu) 
{
	return 0;
}

/* 
Our watchdog timed out. Called by the networking layer 
Method called by the networking code when a packet transmission fails to complete within a reasonable period, on the assumption that an interrupt has been 
missed or the interface has locked up. It should handle the problem and resume packet transmission. 
*/
static void 
temac_tx_timeout(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;

	DEBUG_FUNC;
	spin_lock_irqsave(&lp->lock, flags);

	netif_stop_queue(ndev);
	printk(KERN_ERR "%s: XTemac exceeded transmit timeout of %lu ms.  Resetting emac.\n", ndev->name, TEMAC_TX_TIMEOUT * 1000UL / HZ);
	lp->stats.tx_errors++;
	temac_reset(ndev);
	/* We can accept TX packets again */
	ndev->trans_start = jiffies;
	netif_wake_queue(ndev);

	spin_unlock_irqrestore(&lp->lock, flags);
}

/*
OPTIONAL
int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); 
Performs interface-specific ioctl commands. (Implementation of those commands is described in the section “Custom ioctl Commands.”) 
The corresponding field in structnet_device can be left as NULL if the interface doesn’t need any 
interface-specific commands. 
 */
static int temac_do_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
	struct temac_local *lp = ndev->priv;
	int rc;
	unsigned long flags;

	/* SIOC[GS]MIIxxx ioctls */
	if (lp->mii) {
		spin_lock_irqsave(&lp->lock, flags);
		rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);
		spin_unlock_irqrestore(&lp->lock, flags);
	} else {
		rc = -EOPNOTSUPP;
	}

	return rc;
}

u32 rem_table[] = {
	0x0000000F, 0x00000008, 0x0000000C, 0x0000000E,
};
int sendFrame(struct net_device *ndev, struct sk_buff *skb, u8 *bd)
{
	int i;
	u32 cw;
	u32 *bp = (u32 *) skb->data;
	u32 *bdesc = (u32 *) bd;

	// DEBUG_FUNC;
	/*
	Send Buffer Descriptor as LocalLink Header
	*/

	iow(ndev, LL_TX_DATA, bdesc[0]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & (LL_SOF_MASK | LL_REM_MASK)));
	iow(ndev, LL_TX_DATA, bdesc[1]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[2]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[3]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[4]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[5]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[6]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
	iow(ndev, LL_TX_DATA, bdesc[7]);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));

	/*
	Send Ethernet Frame in LocalLink payload
	*/
	for (i = 0; i < skb->len / 4; i++) {

		/* Assume caches are off */
		iow(ndev, LL_TX_DATA, bp[i]);
		cw = 0;
		/* Detect End of Packet */
		if (i == (skb->len / 4 - 1)) {
			if (skb->len % 4) {
				cw |= LL_REM_MASK;
			} else {
				cw |= LL_EOP_MASK | LL_REM_MASK;
			}
		}
		/* Detect Start of Packet */
		if (i == 0)
			cw |= LL_SOP_MASK | LL_REM_MASK;
		if (i > 0 && i < (skb->len / 4 - 1))
			cw |= LL_REM_MASK;
		iow(ndev, LL_TX_CTRL, ~(0xffffffff & cw));
	}

	/*  If skb->len is not a multiple of 4, then send last 1, 2, or 3 bytes... */
	if (skb->len % 4) {
		iow(ndev, LL_TX_DATA, bp[i]);
		cw = LL_EOP_MASK | rem_table[skb->len % 4];
		iow(ndev, LL_TX_CTRL, ~(0xffffffff & cw));
	}
	/*
	Send Dummy LocalLink Footer
	*/
	iow(ndev, LL_TX_DATA, 0);
	iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_EOF_MASK));
	return 0;
}
u8 bDescriptor[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/*
Hardware start transmission.
Send a packet to media from the upper layer.
Method that initiates the transmission of a packet. 
The full packet (protocol headers and all) is contained in a socket buffer (sk_buff) structure. 
Socket buffers are introduced later in this chapter. 
skb->data points to the packet
skb->len is its length
 */
static int temac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	// DEBUG_FUNC;
	netif_stop_queue(ndev);
	lp->stats.tx_bytes += skb->len;
	
	ndev->trans_start = jiffies;	/* save the time stamp */

	sendFrame(ndev,skb, bDescriptor);
	// dump_skb(skb, "Send");
	/* free this SKB */
	dev_kfree_skb(skb);
	netif_wake_queue(ndev);
	return 0;
}
static void temac_shutdown(struct net_device *ndev)
{
	// struct temac_local *lp = ndev->priv;
	DEBUG_FUNC;
	/* RESET device */
	mdio_write(ndev, 0, MII_BMCR, BMCR_RESET);	/* PHY RESET */
	/* Power-Down PHY */
	/* Disable all interrupt */
	/* Disable RX */
}
/*
Stop the interface.
Stops the interface. The interface is stopped when it is brought down. 
This function should reverse operations performed at open time. 
*/
static int temac_stop(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	unsigned long flags;
	DEBUG_PRINTL("temac_stop()");
	DEBUG_FUNC;
	spin_lock_irqsave(&XTE_spinlock, flags);

	del_timer(&lp->rx_timer);
	del_timer(&lp->mii_timer);

	/* Stop Send queue */
	netif_stop_queue(ndev);
	/* Now we could stop the ndevice */
	netif_carrier_off(ndev);
	/*
	 * If not in polled mode, free the interrupt.  Currently, there
	 * isn't any code to set polled mode, so this check is probably
	 * superfluous.
	 */
	free_irq(ndev->irq, ndev);
	temac_shutdown(ndev);
	return 0;
}

/* temac_release_board
 *
release a board, and any mapped resources
 */

static void temac_release_board(struct platform_device *pdev, struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	DEBUG_FUNC;
	/* unmap our resources */

	iounmap((void *) ndev->base_addr);

	/* release the resources */

	if (lp->phy_addr_req != NULL) {
		release_resource(lp->phy_addr_req);
		kfree(lp->phy_addr_req);
	}

	if (lp->nic_addr_res != NULL) {
		release_resource(lp->nic_addr_res);
		kfree(lp->nic_addr_req);
	}
}
/*
Whenever an application needs to get statistics for the interface, this method is 
called. This happens, for example, when ifconfig or netstat -i is run. A sample 
implementation for snull is introduced in the section “Statistical Information.” 
 */
static struct net_device_stats 
*temac_get_stats(struct net_device *ndev)
{
	struct temac_local *lp = ndev->priv;
	// DEBUG_PRINTL("temac_get_stats()");
	return &lp->stats;
}

/*
Search TEMAC board, allocate space and register it
 */
static int temac_device_probe(struct platform_device *pdev)
{
	struct net_device *ndev;
	struct xhtemac_platform_data *pdata = pdev->dev.platform_data;
	struct temac_local *lp;	/* Point a board information structure */
	int i;
	int ret = 0;
	int nic_size;
	// DEBUG_FUNC;
	printk(version);
	/* Init network device */
	ndev = alloc_etherdev(sizeof(struct temac_local));
	if (!ndev) {
		printk("%s: could not allocate device.\n", DRV_CARDNAME);
		return -ENOMEM;
	}

	SET_MODULE_OWNER(ndev);
	SET_NETDEV_DEV(ndev, &pdev->dev);
	/* setup board info structure */
	lp = (struct temac_local *) ndev->priv;
	// lp->einfo = einfo;
	/* Clear memory */
	memset(lp, 0, sizeof(*lp));
	spin_lock_init(&lp->lock);
/* 	get device configuration from platform Device */
	ndev->irq = platform_get_irq(pdev, 0);

	if (pdev->num_resources < 2) {
		printk("%s: insufficient resources %d.\n", DRV_CARDNAME, pdev->num_resources);
		ret = -ENODEV;
		goto out;
	}
	lp->phy_addr = XGP_HIF_BASEADDR;					/* set default address of PHY */
	ndev->base_addr  = XPAR_PLB_LL_IF_0_BASEADDR ;
	ndev->irq = XPAR_INTC_0_TEMAC_0_VEC_ID;
	// lp->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);	/*  IRQ */
	lp->nic_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	/* address of NIC */
	if (lp->nic_addr_res == NULL) {
		printk(KERN_ERR PFX "insufficient resources\n");
		ret = -ENOENT;
		goto out;
	}
	nic_size = res_size(lp->nic_addr_res);
	lp->nic_addr_req = request_mem_region(lp->nic_addr_res->start, nic_size, pdev->name);
	if (lp->nic_addr_req == NULL) {
		printk(KERN_ERR PFX "cannot claim address reg area\n");
		ret = -EIO;
		goto out;
	}

	ndev->base_addr = (u32) ioremap(lp->nic_addr_res->start, nic_size);
	if (ndev->base_addr == NULL) {
		printk(KERN_ERR "failed to ioremap address reg\n");
		ret = -EINVAL;
		goto out;
	}

	/* fill in parameters for net-dev structure */

	// ndev->irq = lp->irq_res->start;
	// lp->phy_addr    = lp->phy_addr_res->start;
/*      done getting platform_device parameters start initializine device */
	/* Set all handlers to stub values, let user configure this data */
	lp->mii = 1;		/*  really important can't read/write anyting until set */
	lp->regshift = 3;	

	lp->mii_if.full_duplex = 1;
	lp->mii_if.phy_id_mask = 0x1f;
	lp->mii_if.reg_num_mask = 0x1f;
	lp->mii = 1;
	lp->msg_enable = temac_debug;
	lp->mii_if.dev = ndev;
	lp->mii_if.mdio_read = mdio_read;
	lp->mii_if.mdio_write = mdio_write;
	/* Set the mii phy_id so that we can query the link state */
	//  if (lp->mii)
	//      lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f;
	memcpy(ndev->dev_addr, "\0\1\2345", ETH_ALEN);

	temac_set_mac_address(ndev,pdata->mac_addr);
	/* from this point we assume that we have found a TEMAC */
	/* driver system function */
	ether_setup(ndev);
	/* The TEMAC-specific entries in the device structure. */
	ndev->open = &temac_open;
	ndev->stop = &temac_stop;
	ndev->get_stats = &temac_get_stats;
	ndev->do_ioctl = &temac_do_ioctl;
	ndev->tx_timeout = &temac_tx_timeout;
	ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
	ndev->hard_start_xmit = &temac_hard_start_xmit;
	ndev->set_multicast_list = &temac_set_multicast_list;
	ndev->ethtool_ops = &temac_ethtool_ops;
#ifdef CONFIG_NET_POLL_CONTROLLER
	ndev->poll_controller = temac_poll_controller;
#endif
	ndev->flags &= ~IFF_MULTICAST;			/* clear multicast */
	ndev->change_mtu = temac_change_mtu;
	platform_set_drvdata(pdev, ndev);
	ret = register_netdev(ndev);
	if (ret == 0) {
		printk("%s: temac at %lx,%x IRQ %d MAC: ", ndev->name, ndev->base_addr, lp->phy_addr, ndev->irq);
		for (i = 0; i < 5; i++)
			printk("%02x:", ndev->dev_addr[i]);
		printk("%02x", ndev->dev_addr[5]);
#ifdef CONFIG_NET_POLL_CONTROLLER
		printk(" NET_POLL");
#endif
		printk("\n");

	}
	return 0;
//  release:
    out:
	printk("%s: not found (%d).\n", DRV_CARDNAME, ret);
	temac_release_board(pdev, ndev);
	kfree(ndev);
	return ret;
}

static int temac_device_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct net_device *ndev = platform_get_drvdata(pdev);
	DEBUG_PRINTL("temac_device_suspend()");
	if (ndev) {
		if (netif_running(ndev)) {
			netif_device_detach(ndev);
			temac_shutdown(ndev);
		}
	}
	return 0;
}

static int temac_device_resume(struct platform_device *pdev)
{
	struct net_device *ndev = platform_get_drvdata(pdev);
	DEBUG_FUNC;
	if (ndev) {

		if (netif_running(ndev)) {
			temac_reset(ndev);
			netif_device_attach(ndev);
		}
	}
	return 0;
}

static int __devexit temac_device_remove(struct platform_device *pdev)
{
	struct net_device *ndev = platform_get_drvdata(pdev);
	DEBUG_FUNC;
	platform_set_drvdata(pdev, NULL);
	unregister_netdev(ndev);
	temac_release_board(pdev, ndev);
	free_netdev(ndev);	/* free device structure */
	DEBUG_PRINTL("%s() exit", __FUNCTION__);
	return 0;
}

static struct platform_driver temac_driver = {
	.probe = temac_device_probe,
	.remove = temac_device_remove,
	.suspend = temac_device_suspend,
	.resume = temac_device_resume,.driver = {
		.name = DRV_NAME,
	},
};

static int __init temac_init_module(void)
{
	int err;
	if ((err = platform_driver_register(&temac_driver))) {	/* search board and register */
		printk(KERN_ERR "Driver registration failed\n");
		return err;
	}
#if 0
	temac_device = platform_device_alloc(DRV_NAME, 0);
	if (!temac_device) {
		goto out_unregister;
	}

	if (platform_device_add(temac_device)) {
		platform_device_put(temac_device);
		temac_device = NULL;
	}
#endif
	return 0;
      out_unregister:
	driver_unregister(&temac_driver);
	return -ENOMEM;
}

static void __exit temac_cleanup_module(void)
{
	platform_driver_unregister(&temac_driver);
	if (temac_device) {
		platform_device_unregister(temac_device);
		temac_device = NULL;
	}
}

module_init(temac_init_module);
module_exit(temac_cleanup_module);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
module_param(temac_debug, int, 0);
MODULE_PARM_DESC(temac_debug, "temac debug level (1-4)");
MODULE_AUTHOR("David H. Lynch Jr. <[email-masked]>");
MODULE_LICENSE("GPL");

/*

OPTIONAL functions

Function (called before hard_start_xmit) that builds the hardware header from 
the source and destination hardware addresses that were previously retrieved; its 
job is to organize the information passed to it as arguments into an appropriate, 
device-specific hardware header. eth_header is the default function for Ethernet-
like interfaces, and ether_setup assigns this field accordingly. 
int (*hard_header) (struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); 

Function used to rebuild the hardware header after ARP resolution completes 
but before a packet is transmitted. The default function used by Ethernet devices 
uses the ARP support code to fill the packet with missing information. 
int (*rebuild_header)(struct sk_buff *skb); 

Changes the interface configuration. This method is the entry point for configuring the driver. The I/O address for the device and its interrupt number can be 
changed at runtime using set_config. This capability can be used by the system 
administrator if the interface cannot be probed for. Drivers for modern hardware normally do not need to implement this method. 
int (*set_config)(struct net_device *dev, struct ifmap *map); 

Method provided by NAPI-compliant drivers to operate the interface in a polled 
mode, with interrupts disabled. NAPI (and the weight field) are covered in the 
section Receive Interrupt Mitigation. 
int (*poll)(struct net_device *dev; int *quota) L

header_cache is called to fill in the hh_cache structure with the results of an ARP 
query. Almost all Ethernet-like drivers can use the default eth_header_cache 
implementation. 
int (*header_cache) (struct neighbour *neigh, struct hh_cache *hh); 

Method that updates the destination address in the hh_cache structure in 
response to a change. Ethernet devices use eth_header_cache_update. 
int (*header_cache_update) (struct hh_cache *hh, struct net_device *dev, unsigned char *haddr); 

The hard_header_parse method extracts the source address from the packet contained in skb, 
copying it into the buffer at haddr. The return value from the function is the length of that address. Ethernet devices normally use eth_header_parse. 
int (*hard_header_parse) (struct sk_buff *skb, unsigned char *haddr); 

Utility Fields 

The remaining struct net_device data fields are used by the interface to hold useful 
status information. Some of the fields are used by ifconfig and netstat to provide the 
user with information about the current configuration. Therefore, an interface 
should assign values to these fields: 

unsigned long trans_start; 
unsigned long last_rx; 

Fields that hold a jiffies value. The driver is responsible for updating these values when transmission begins and when a packet is received, respectively. The 
trans_start value is used by the networking subsystem to detect transmitter 
lockups. last_rx is currently unused, but the driver should maintain this field 
anyway to be prepared for future use. 

int watchdog_timeo; 

The minimum time (in jiffies) that should pass before the networking layer 
decides that a transmission timeout has occurred and calls the drivers tx_timeout function. 

void *priv; 
The equivalent of filp->private_data. In modern drivers, this field is set by 
alloc_netdev and should not be accessed directly; use netdev_priv instead. 

struct dev_mc_list *mc_list; 

int mc_count; 
Fields that handle multicast transmission. mc_count is the count of items in mc_list. 
See the section Multicast for further details. 

spinlock_t xmit_lock; 

int xmit_lock_owner; 
The xmit_lock is used to avoid multiple simultaneous calls to the drivers 
hard_start_xmit function. xmit_lock_owner is the number of the CPU that has 
obtained xmit_lock. The driver should make no changes to these fields. 

*/
#if 0
	} else {		/* FIFO interrupt mode */
		xlltemac_IntrFifoEnable(&lp->Emac, XTE_RECV | XTE_SEND);
	}

----------------------------------

	/* Register interrupt handler */
	if ((Options & XTE_POLLED_OPTION) == 0) {
		int retval;
		/* Grab the IRQ */
		retval =
		    request_irq(dev->irq, &xtenet_interrupt, 0, dev->name, dev);
		if (retval) {
			printk(KERN_ERR
			       "%s: xlltemac could not allocate interrupt %d.\n",
			       dev->name, dev->irq);
			spin_unlock_irqrestore(&XTE_spinlock, flags);
			return retval;
		}
	}

	/* Enable interrupts if not in polled mode */
	if ((Options & XTE_POLLED_OPTION) == 0) {
		if (!xlltemac_mIsSgDma(&lp->Emac))	/*fifo direct interrupt driver mode */
			xlltemac_IntrFifoEnable(&lp->Emac, XTE_RECV | XTE_SEND);
		else		/* SG DMA mode */
			xlltemac_IntrSgEnable(&lp->Emac, XTE_SEND | XTE_RECV);
	}

static int xtenet_FifoSend(struct sk_buff *skb, struct net_device *dev)
{
	struct net_local *lp;
	unsigned int len;
	XStatus result;
	unsigned long flags, fifo_free_bytes;

	/* The following lock is used to protect GetFreeBytes, FifoWrite
	 * and FifoSend sequence which could happen from FifoSendHandler
	 * or other processor in SMP case.
	 */
	spin_lock_irqsave(&XTE_spinlock, flags);
	lp = (struct net_local *)dev->priv;
	len = skb->len;

	fifo_free_bytes = xlltemac_FifoGetFreeBytes(&lp->Emac, XTE_SEND);
	if (fifo_free_bytes < len) {
		netif_stop_queue(dev);	/* stop send queue */
		lp->deferred_skb = skb;	/* buffer the sk_buffer and will send
					   it in interrupt context */
		spin_unlock_irqrestore(&XTE_spinlock, flags);
		return 0;
	}

	/* Write frame data to FIFO */
	result =
	    xlltemac_FifoWrite(&lp->Emac, (void *)skb->data, len,
			     XTE_END_OF_PACKET);
	if (result != XST_SUCCESS) {
		reset(dev, __LINE__);
		lp->stats.tx_errors++;
		spin_unlock_irqrestore(&XTE_spinlock, flags);
		return -EIO;
	}

	/* Initiate transmit */
	if ((result = xlltemac_FifoSend(&lp->Emac, len)) != XST_SUCCESS) {
		reset(dev, __LINE__);
		lp->stats.tx_errors++;
		spin_unlock_irqrestore(&XTE_spinlock, flags);
		return -EIO;
	}
	lp->stats.tx_bytes += len;
	spin_unlock_irqrestore(&XTE_spinlock, flags);

	dev_kfree_skb(skb);	/* free skb */
	dev->trans_start = jiffies;
	return 0;
}

/* Callback function for completed frames sent in FIFO interrupt driven mode */
static void FifoSendHandler(void *CallbackRef)
{
	struct net_device *dev;
	struct net_local *lp;
	XStatus result;
	struct sk_buff *skb;

	spin_lock(&XTE_spinlock);
	dev = (struct net_device *)CallbackRef;
	lp = (struct net_local *)dev->priv;
	lp->stats.tx_packets++;

	/*Send out the deferred skb and wake up send queue if a deferred skb exists */
	if (lp->deferred_skb) {

		skb = lp->deferred_skb;
		/* If no room for the deferred packet, return */
		if (xlltemac_FifoGetFreeBytes(&lp->Emac, XTE_SEND) < skb->len) {
			spin_unlock(&XTE_spinlock);
			return;
		}

		/* Write frame data to FIFO */
		result = xlltemac_FifoWrite(&lp->Emac, (void *)skb->data,
					  skb->len, XTE_END_OF_PACKET);
		if (result != XST_SUCCESS) {
			reset(dev, __LINE__);
			lp->stats.tx_errors++;
			spin_unlock(&XTE_spinlock);
			return;
		}

		/* Initiate transmit */
		if ((result =
		     xlltemac_FifoSend(&lp->Emac, skb->len)) != XST_SUCCESS) {
			reset(dev, __LINE__);
			lp->stats.tx_errors++;
			spin_unlock(&XTE_spinlock);
			return;
		}

		dev_kfree_skb_irq(skb);
		lp->deferred_skb = NULL;
		lp->stats.tx_packets++;
		lp->stats.tx_bytes += skb->len;
		dev->trans_start = jiffies;
		netif_wake_queue(dev);	/* wake up send queue */
	}
	spin_unlock(&XTE_spinlock);
}
/* The callback function for frames received when in FIFO mode. */

#define XTE_RX_SINK_BUFFER_SIZE 1024

static void FifoRecvHandler(void *CallbackRef)
{
	struct net_device *ndev;
	struct net_local *lp;
	struct sk_buff *skb;
	u32 len;
	XStatus Result;
	static u32 rx_buffer_sink[XTE_RX_SINK_BUFFER_SIZE / sizeof(u32)];

	spin_lock(&XTE_spinlock);
	ndev = (struct net_device *)CallbackRef;
	lp = netdev_priv(ndev);

	Result = xlltemac_FifoRecv(&lp->Emac, &len);
	if (Result != XST_SUCCESS) {
		printk(KERN_ERR
		       "%s: xlltemac could not read received packet length, error=%d.\n",
		       ndev->name, Result);
		lp->stats.rx_errors++;
		reset(ndev, __LINE__);
		spin_unlock(&XTE_spinlock);
		return;
	}

	if (!(skb = dev_alloc_skb(len + ALIGNMENT_RECV))) {
		/* Couldn't get memory. */
		lp->stats.rx_dropped++;
		printk(KERN_ERR
		       "%s: xlltemac could not allocate receive buffer.\n",
		       ndev->name);

		/* consume data in Xilinx TEMAC RX data fifo so it is sync with RX length fifo */
		for (; len > XTE_RX_SINK_BUFFER_SIZE;
		     len -= XTE_RX_SINK_BUFFER_SIZE) {
			xlltemac_FifoRead(&lp->Emac, rx_buffer_sink,
					XTE_RX_SINK_BUFFER_SIZE,
					XTE_PARTIAL_PACKET);
		}
		xlltemac_FifoRead(&lp->Emac, rx_buffer_sink, len,
				XTE_END_OF_PACKET);

		spin_unlock(&XTE_spinlock);
		return;
	}

	/* Read the packet data */
	Result = xlltemac_FifoRead(&lp->Emac, skb->data, len, XTE_END_OF_PACKET);
	if (Result != XST_SUCCESS) {
		lp->stats.rx_errors++;
		dev_kfree_skb_irq(skb);
		printk(KERN_ERR "%s: xlltemac could not receive buffer, error=%d.\n", ndev->name, Result);
		reset(ndev, __LINE__);
		spin_unlock(&XTE_spinlock);
		return;
	}
	lp->stats.rx_packets++;
	lp->stats.rx_bytes += len;
	spin_unlock(&XTE_spinlock);

	skb_put(skb, len);	/* Tell the skb how much data we got. */
	skb->dev = ndev;	/* Fill out required meta-data. */
	skb->protocol = eth_type_trans(skb, ndev);
	/* skb->ip_summed = CHECKSUM_NONE; */
	netif_rx(skb);		/* Send the packet upstream. */
}

	lp->Config.RxPktFifoDepth = pdata->rx_pkt_fifo_depth;
	lp->Config.TxPktFifoDepth = pdata->tx_pkt_fifo_depth;
	lp->Config.MacFifoDepth = pdata->mac_fifo_depth;
	} else {
		DEBUG_PRINTK("xtenet_probe(07)\n");
		printk(KERN_INFO "%s: xlltemac using fifo direct interrupt driven mode.\n", ndev->name);
		xlltemac_SetHandler(&lp->Emac, XTE_HANDLER_FIFORECV, FifoRecvHandler, ndev);
		xlltemac_SetHandler(&lp->Emac, XTE_HANDLER_FIFOSEND, FifoSendHandler, ndev);
		ndev->hard_start_xmit = xtenet_FifoSend;
		lp->Isr = xlltemac_IntrFifoHandler;
	}

/*****************************************************************************/
/**
 *
 * Disable FIFO related interrupts for FIFO direct frame transfer mode. Dma
 * interrupts are not affected.
 *
 * Do not use this function when using SG DMA frame transfer mode.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param Direction specifies whether the transmit related (XTE_SEND) or
 *        receive related (XTE_RECV) interrupts should be affected, or
 *        both (XTE_SEND | XTE_RECV).
 *
 * @return None
 *
 * @note The state of the transmitter and receiver are not modified by this
 *       function.
 *
 * @note If the device is configured for SGDMA, then this function has no
 *       effect. Use XTemac_IntrSgDmaDisable() instead.
 *
 ******************************************************************************/
void XTemac_IntrFifoDisable(XTemac * InstancePtr, u32 Direction)
{
	u32 RegIPIER;

	XASSERT_VOID(InstancePtr != NULL);
	XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_VOID(!(Direction & ~(XTE_SEND | XTE_RECV)));

	/* Don't allow if device is configured for SGDMA */
	if (XTemac_mIsSgDma(InstancePtr)) {
		return;
	}

	/* Get contents of IPIER register */
	RegIPIER = GetIpIfReg(XTE_IPIER_OFFSET);

	/* Handle send direction */
	if (Direction & XTE_SEND) {
		RegIPIER &=
		    ~(XTE_IPXR_XMIT_ERROR_MASK | XTE_IPXR_XMIT_DONE_MASK);
		InstancePtr->Flags &= ~XTE_FLAGS_SEND_FIFO_INT_ENABLE;
	}

	/* Handle receive direction */
	if (Direction & XTE_RECV) {
		RegIPIER &=
		    ~(XTE_IPXR_RECV_ERROR_MASK | XTE_IPXR_RECV_DONE_MASK);
		InstancePtr->Flags &= ~XTE_FLAGS_RECV_FIFO_INT_ENABLE;
	}

	/* Update IPIER with new setting */
	XTemac_mSetIpifReg(XTE_IPIER_OFFSET, RegIPIER);
}
/**
 *
 * Master interrupt handler for FIFO direct frame transfer mode. This routine
 * will query the status of the device, bump statistics, and invoke user
 * callbacks in the following priority:
 *
 * This routine must be connected to an interrupt controller using OS/BSP
 * specific methods.
 *
 * @param InstancePtr is a pointer to the TEMAC instance that has caused the
 *        interrupt.
 *
 * @return None
 *
 ******************************************************************************/
void XTemac_IntrFifoHandler(void *TemacPtr)
{
	u32 RegDISR;
	u32 CorePending;
	u32 RegMisc;
	unsigned Cnt;
	XTemac *InstancePtr = (XTemac *) TemacPtr;

	XASSERT_VOID(InstancePtr != NULL);

	/* This ISR will try to handle as many interrupts as it can in a single
	 * call. However, in most of the places where the user's error handler is
	 * called, this ISR exits because it is expected that the user will reset
	 * the device most of the time.
	 */

	/* Log interrupt */
	XTemac_mBumpStats(Interrupts, 1);

	/* Get top level interrupt status. The status is self clearing when the
	 * interrupt source is cleared
	 */
	RegDISR = GetIpIfReg(XTE_DISR_OFFSET);

	/* Handle IPIF and packet FIFO errors */
	if (RegDISR & (XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK |
		       XTE_DXR_RECV_FIFO_MASK | XTE_DXR_SEND_FIFO_MASK)) {
		/* IPIF transaction or data phase error */
		if (RegDISR & (XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK)) {
			XTemac_mBumpStats(IpifErrors, 1);
			ERR_HANDLER(XST_IPIF_ERROR, RegDISR, 0);
			return;
		}

		/* Receive packet FIFO is deadlocked */
		if (RegDISR & XTE_DXR_RECV_FIFO_MASK) {
			XTemac_mBumpStats(RxPktFifoErrors, 1);
			ERR_HANDLER(XST_PFIFO_DEADLOCK, XTE_RECV, 0);
			return;
		}

		/* Transmit packet FIFO is deadlocked */
		if (RegDISR & XTE_DXR_SEND_FIFO_MASK) {
			XTemac_mBumpStats(TxPktFifoErrors, 1);
			ERR_HANDLER(XST_PFIFO_DEADLOCK, XTE_SEND, 0);
			return;
		}
	}

	/* Handle core interrupts */
	if (RegDISR & XTE_DXR_CORE_MASK) {
		/* Calculate which enabled interrupts have been asserted */
		CorePending = GetIpIfReg(XTE_IPIER_OFFSET) &
		    GetIpIfReg(XTE_IPISR_OFFSET);

		/* Check for fatal status/length FIFO errors. These errors can't be
		 * cleared
		 */
		if (CorePending & XTE_IPXR_FIFO_FATAL_ERROR_MASK) {
			XTemac_mBumpStats(FifoErrors, 1);
			ERR_HANDLER(XST_FIFO_ERROR, CorePending &
				    XTE_IPXR_FIFO_FATAL_ERROR_MASK, 0);
			return;
		}

		/* A receive packet has arrived. Call the receive handler.
		 *
		 * Acking this interrupt is not done here. The handler has a choice:
		 * 1) Call XTemac_FifoRecv() which will ack this interrupt source, or
		 * 2) Call XTemac_IntrFifoDisable() and defer XTEmac_FifoRecv() to a
		 * later time. Failure to do one of these actions will leave this
		 * interupt still pending resulting in an exception loop.
		 */
		if (CorePending & XTE_IPXR_RECV_DONE_MASK) {
			FIFORECV_HANDLER();
		}

		/* A transmit has completed. Pull off all statuses that are available.
		 * For each status that contains a non-fatal error, the error handler
		 * is invoked. For fatal errors, the error handler is invoked once and
		 * assumes the callback will reset the device.
		 *
		 * Unless there was a fatal error, then call the send handler since
		 * resources in the packet FIFO, transmit length FIFO, and transmit
		 * status FIFO have been freed up. This gives the handler a chance
		 * to enqueue new frame(s).
		 */
		if (CorePending & XTE_IPXR_XMIT_DONE_MASK) {
			Cnt = 0;

			/* While XMIT_DONE persists */
			do {
				/* Get TSR, try to clear XMIT_DONE */
				RegMisc =
				    GetIpIfReg(XTE_TSR_OFFSET);
				XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
						   XTE_IPXR_XMIT_DONE_MASK);

				Cnt++;

				/* Read IPISR and test XMIT_DONE again */
				RegMisc =
				    GetIpIfReg(XTE_IPISR_OFFSET);
			} while (RegMisc & XTE_IPXR_XMIT_DONE_MASK);

			FIFOSEND_HANDLER(Cnt);
		}

		/* Auto negotiation interrupt */
		if (CorePending & XTE_IPXR_AUTO_NEG_MASK) {
			ANEG_HANDLER();
		}

		/* Check for dropped receive frame. Ack the interupt then call the
		 * error handler
		 */
		if (CorePending & XTE_IPXR_RECV_DROPPED_MASK) {
			XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
					   CorePending &
					   XTE_IPXR_RECV_DROPPED_MASK);

			XTemac_mBumpStats(RxRejectErrors, 1);
			ERR_HANDLER(XST_RECV_ERROR,
				    CorePending &
				    XTE_IPXR_RECV_DROPPED_MASK, 0);

			/* no return here, nonfatal error */
		}
	}
}
/******************************************************************************
*
*     Author: Xilinx, Inc.
*
*
*     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.
*
*
*     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
*     COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
*     ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD,
*     XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE
*     FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING
*     ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
*     XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
*     THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY
*     WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM
*     CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND
*     FITNESS FOR A PARTICULAR PURPOSE.
*
*
*     Xilinx hardware products are not intended for use in life support
*     appliances, devices, or systems. Use in such applications is
*     expressly prohibited.
*
*
*     (c) Copyright 2005 Xilinx Inc.
*     All rights reserved.
*
*
*     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.,
*     675 Mass Ave, Cambridge, MA 02139, USA.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xtemac_fifo.c
*
* Functions in this file implement FIFO direct and Simple DMA frame transfer
* mode. See xtemac.h for a detailed description of the driver.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a rmm  06/01/05 First release
* 1.00b rmm  09/23/05 Fixed void* arithmetic usage, added XST_FIFO_ERROR 
*                     return code to send/recv query functions.
* 2.00a rmm  11/21/05 Removed XST_FAILURE return code for XTemac_FifoQuery-
*                     SendStatus, removed simple dma code
* </pre>
******************************************************************************/

/***************************** Include Files *********************************/

#include "xtemac.h"
#include "xtemac_i.h"
#include "xio.h"

/************************** Constant Definitions *****************************/

#define PFIFO_64BIT_WIDTH_BYTES 8

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/

/*******************************************************************************
 * Primitives that modify the hold structure for XTemac_PacketFifo. All F
 * parameters refer to a pointer to XTemac_PacketFifo.
 *
 * mHold_GetIndex(F) - Get the ByteIndex of Hold
 * mHold_SetIndex(F,D) - Set the ByteIndex of Hold to D
 * mHold_Advance(F,D) - Advance the ByteIndex of Hold by D bytes
 * mHold_CopyIn(F,I,D) - Set Hold[I] to D
 * mHold_CopyOut(F,I,D) - Set D to Hold[I]
 * mHoldS_IsFull(F) - Is a write channel Hold full of data
 * mHoldS_IsEmpty(F) - Is a write channel Hold empty
 * mHoldS_SetEmpty(F) - Set a write channel Hold empty
 * mHoldR_IsFull(F) - Is a read channel Hold full of data
 * mHoldR_IsEmpty(F) - Is a read channel Hold empty
 * mHoldR_SetEmpty(F) - Set a read channel Hold empty
 *
 * @param F - Address to a XTemac_PacketFifo structure
 * @param SrcPtr - Source data address aligned on 4 byte boundary
 *
 ******************************************************************************/
#define mHold_GetIndex(F)      ((F)->ByteIndex)
#define mHold_SetIndex(F, D)   ((F)->ByteIndex = (D))
#define mHold_Advance(F, D)    ((F)->ByteIndex += (D))
#define mHold_CopyIn(F, I, D)  (*(u8*)(((u8*)(&(F)->Hold[0])) + (I)) = (D))
#define mHold_CopyOut(F, I, D) ((D) = (*(u8*)(((u8*)(&(F)->Hold[0])) + (I))))

#define mHoldS_IsFull(F)    ((F)->ByteIndex >= (F)->Width)
#define mHoldS_IsEmpty(F)   ((F)->ByteIndex == 0)
#define mHoldS_SetEmpty(F)  ((F)->ByteIndex = 0)

#define mHoldR_IsFull(F)    ((F)->ByteIndex == 0)
#define mHoldR_IsEmpty(F)   ((F)->ByteIndex >= (F)->Width)
#define mHoldR_SetEmpty(F)  ((F)->ByteIndex = (F)->Width)

/*******************************************************************************
 * Primitive write to 64 bit FIFO. Use two 32-bit wide I/O accesses.
 *
 * @param F - Address to a XTemac_PacketFifo structure
 * @param SrcPtr - Source data address aligned on 4 byte boundary
 *
 ******************************************************************************/
#define mWriteFifo64(F, SrcPtr)                                \
    {                                                          \
        register u32 Faddr = F->Fifo.DataBaseAddress;      \
        XIo_Out32(Faddr, (SrcPtr)[0]);                         \
        XIo_Out32(Faddr + 4, (SrcPtr)[1]);                     \
    }

/*******************************************************************************
 * Primitive read from 64 bit FIFO. Use two 32-bit wide I/O accesses.
 *
 * @param F - Address to a XTemac_PacketFifo structure
 * @param DestPtr - Destination data address aligned on 4 byte boundary
 *
 ******************************************************************************/
#define mReadFifo64(F, DestPtr)                                \
    (DestPtr)[0] = XIo_In32(F->Fifo.DataBaseAddress);          \
    (DestPtr)[1] = XIo_In32(F->Fifo.DataBaseAddress + 4);

/*******************************************************************************
 * Primitive to transfer the holding data to the FIFO 64 bits at a time
 *
 * @param F - Address to a XTemac_PacketFifo structure
 *
 ******************************************************************************/
#define mPush64(F) mWriteFifo64(F, &F->Hold[0])

/*******************************************************************************
 * Primitive to tranfer FIFO contents into the holding data 64 bits at a time
 *
 * @param F - Address to a XTemac_PacketFifo structure
 *
 ******************************************************************************/
#define mPop64(F) mReadFifo64(F, &F->Hold[0])

/************************** Function Prototypes ******************************/

/* The following functions will be attached to the FifoRead and FifoWrite
 * attribute of an instance by XTemac_ConfigureFifoAccess
 */
static XStatus Write_64(XTemac_PacketFifo * Fptr, void *BufPtr,
			u32 ByteCount, int Eop);
static XStatus Read_64(XTemac_PacketFifo * Fptr, void *BufPtr,
		       u32 ByteCount, int Eop);

/* 64 bit wide FIFO support functions */
static void Write64_Unaligned(XTemac_PacketFifo * F, void *BufPtr,
			      u32 ByteCount);
static void Write64_Aligned(XTemac_PacketFifo * F, u32 * BufPtr,
			    u32 ByteCount);
static void Read64_Unaligned(XTemac_PacketFifo * F, void *BufPtr,
			     u32 ByteCount);
static void Read64_Aligned(XTemac_PacketFifo * F, u32 * BufPtr,
			   u32 ByteCount);

/*******************************************************************************
 * Select the best method for accessing the read and write FIFOs for FIFO direct
 * frame transfer mode. On the write (transmit) side, the choices are DRE or via
 * the holding structure. Both methods allow unaligned transfers. On the read
 * (receive) side, the only choice is the holding structure.
 *
 * This function should be called only from XTemac_Initialize().
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 *
 * @return XST_SUCCESS or XST_FAILURE if an error was detected
 *
 ******************************************************************************/
XStatus XTemac_ConfigureFifoAccess(XTemac * InstancePtr)
{
	XStatus Result;

	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(00)\n");
	/* Initialize the packet FIFOs */
	Result =
	    XPacketFifoV200a_Initialize(&InstancePtr->RecvFifo.Fifo,
					InstancePtr->BaseAddress +
					XTE_PFIFO_RXREG_OFFSET,
					InstancePtr->BaseAddress +
					XTE_PFIFO_RXDATA_OFFSET);
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(01)\n");
	if (Result != XST_SUCCESS) {
		return (XST_FAILURE);
	}
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(02)\n");

	Result =
	    XPacketFifoV200a_Initialize(&InstancePtr->SendFifo.Fifo,
					InstancePtr->BaseAddress +
					XTE_PFIFO_TXREG_OFFSET,
					InstancePtr->BaseAddress +
					XTE_PFIFO_TXDATA_OFFSET);

	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(03)\n");
	if (Result != XST_SUCCESS) {
		return (XST_FAILURE);
	}
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(04)\n");

	/* Choose an access algorithm.
	 * Note: 64-bit wide FIFO is the only width supported at this time
	 */
	InstancePtr->RecvFifo.Width = PFIFO_64BIT_WIDTH_BYTES;
	InstancePtr->RecvFifo.XferFn = Read_64;
	InstancePtr->SendFifo.Width = PFIFO_64BIT_WIDTH_BYTES;
	InstancePtr->SendFifo.XferFn = Write_64;
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(05)\n");

	/* Initialize the holds */
	mHoldS_SetEmpty(&InstancePtr->SendFifo);
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(06)\n");
	mHoldR_SetEmpty(&InstancePtr->RecvFifo);
	DEBUG_PRINTK("XTemac_ConfigureFifoAccess(07)\n");

	return (XST_SUCCESS);
}

/******************************************************************************/
/**
 * Copy data from a user buffer to the transmit packet FIFO. The data copied
 * may comprise of single, multiple, or partial packets. The data is not
 * transmitted until XTemac_FifoSend() is called.
 *
 * If the user buffer contains multiple packets, then extra care must be taken.
 * In this special situation, the end of one packet and the beginning of a new
 * packet is specified within the user buffer. The beginning of each NEW packet
 * must begin on a 4 byte alignment. The user is responsible for adding filler
 * data between packets to acheive this alignment. The amount of filler data
 * depends on what byte the end of the previous packet falls on. When calling
 * XTemac_FifoSend() to transmit the packets, DO NOT specify the filler bytes
 * in the TxByteCount parameter. For example, if a user buffer contains two
 * complete packets of 15 bytes each with 1 byte of filler between them, then
 * XTemac_FifoWrite() is called once to write all 31 bytes to the FIFO.
 * XTemac_FifoSend() is called twice specifying 15 bytes each time to transmit
 * the packets (the 1 byte of filler data is ignored by the TEMAC). Of course
 * you could also just call XTemac_FifoWrite() once for each packet. This way,
 * the driver will manage the filler data.
 *
 * If the user's buffer is not aligned on a 4 byte boundary, then the transfer
 * may take longer to complete.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param BufPtr is the buffer containing user data that will be transferred
 *        into the transmit FIFO. The buffer may be on any alignment.
 * @param ByteCount is the number of bytes to transfer from 1 to the number
 *        of bytes available in the FIFO at the time of invocation. See usage
 *        note for situations when a value of 0 is legal.
 * @param Eop specifies whether the last byte of BufPtr marks the End Of Packet.
 *        If set to XTE_END_OF_PACKET, then any partial bytes being buffered by
 *        the driver are flushed into the packet FIFO. If set to
 *        XTE_PARTIAL_PACKET, then more packet data is expected to be written
 *        through more calls to this function. Failure to use XTE_END_OF_PACKET
 *        prior to calling XTemac_FifoSend() may cause a packet FIFO underrun.
 *
 * @return
 * - XST_SUCCESS if the data was transferred to the FIFO.
 * - XST_DEVICE_IS_STOPPED if the device has not been started.
 * - XST_PFIFO_ERROR if there was a packet FIFO overflow during the transfer.
 *   This is a fatal condition. If this value is returned in polled mode, then
 *   the device must be reset. For interrupt driven modes, an interrupt will be
 *   asserted resulting in a call to the registered error handler which should
 *   handle reset of the device.
 * - XST_IPIF_ERROR if a data or bus error occurred within the TEMAC's IPIF.
 *   Like the PFIFO error, this is a fatal condition and should be handled
 *   in the same manner.
 *
 * @note
 * Calling this function with ByteCount = 0 will not result in the transfer of
 * data from BufPtr to the FIFO. However, if at the same time Eop is set to
 * XTE_END_OF_PACKET, then all data previously written with this function is
 * guaranteed to be flushed into the packet FIFO and available for transmission
 * with XTemac_FifoSend().
 ******************************************************************************/
XStatus XTemac_FifoWrite(XTemac * InstancePtr, void *BufPtr, u32 ByteCount,
			 int Eop)
{
	u32 RegDISR;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(!((Eop != XTE_END_OF_PACKET)
			 && (Eop != XTE_PARTIAL_PACKET)));

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* Transfer the data using the best/fastest method */
	InstancePtr->SendFifo.XferFn(&InstancePtr->SendFifo, BufPtr,
				     ByteCount, Eop);

	/* Make sure the packet FIFO didn't report an error */
	RegDISR = GetIpIfReg(XTE_DISR_OFFSET);
	if (RegDISR & XTE_DXR_SEND_FIFO_MASK) {
		/* Only bump stats in polled mode. For interrupt driven mode, this stat
		 * is bumped in XTemac_IntrFifoHandler()
		 */
		if (InstancePtr->Options & XTE_POLLED_OPTION) {
			XTemac_mBumpStats(TxPktFifoErrors, 1);
		}
		return (XST_PFIFO_ERROR);
	}

	/* Verify no IPIF errors */
	if (RegDISR & (XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK)) {
		/* Only bump stats in polled mode. For interrupt driven mode, this stat
		 * is bumped in XTemac_IntrFifoHandler()
		 */
		if (InstancePtr->Options & XTE_POLLED_OPTION) {
			XTemac_mBumpStats(IpifErrors, 1);
		}
		return (XST_IPIF_ERROR);
	}

	return (XST_SUCCESS);
}

/******************************************************************************/
/**
 * Initiate a transmit of one packet of data previously written with
 * XTemac_FifoWrite(). The given length in bytes is written to the transmit
 * length FIFO. There should be at least this many bytes in the packet FIFO
 * ready for transmit.
 *
 * If FIFO interrupts are enabled (see XTemac_IntrFifoEnable()), then upon
 * completion of the transmit, the registered XTemac_FifoSendHandler() is
 * invoked.
 *
 * If more bytes that are in the packet FIFO are specified in the TxByteCount
 * parameter, then a packet FIFO underrun error will result.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param TxByteCount is the number of bytes to transmit. Range is 1 to the
 *        total number of bytes available in the packet FIFO to be transmitted.
 *
 * @return
 * - XST_SUCCESS if transmit was initiated.
 * - XST_DEVICE_IS_STOPPED if the device has not been started.
 * - XST_FIFO_NO_ROOM if the transmit was not initiated because the transmit
 *   length FIFO was full. This is not a fatal condition. The user may need to
 *   wait for other packets to transmit before this condition clears itself.
 *
 ******************************************************************************/
XStatus XTemac_FifoSend(XTemac * InstancePtr, u32 TxByteCount)
{
	u32 RegIPISR;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(TxByteCount != 0);

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* See if transmit length FIFO is full. If it is, try to clear the
	 * status. If it the status remains, then return an error
	 */
	RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);
	if (RegIPISR & XTE_IPXR_XMIT_LFIFO_FULL_MASK) {
		XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
				   XTE_IPXR_XMIT_LFIFO_FULL_MASK);

		RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);
		if (RegIPISR & XTE_IPXR_XMIT_LFIFO_FULL_MASK) {
			XTemac_mBumpStats(FifoErrors, 1);
			return (XST_FIFO_NO_ROOM);
		}
	}

	/* Start transmit */
	XTemac_mSetIpifReg(XTE_TPLR_OFFSET, TxByteCount);

	/* Return sucess */
	return (XST_SUCCESS);
}

/******************************************************************************/
/**
 * Return the length of a received packet. If a packet is waiting in the
 * receive packet FIFO, then it may be copied to a user buffer with
 * XTemac_FifoRead().
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param ByteCountPtr is the length of the next received packet if the return
 *        status is XST_SUCCESS. 
 *
 * @return
 * - XST_SUCCESS if a packet has been received and a value has been written to
 *   ByteCountPtr.
 * - XST_DEVICE_IS_STOPPED if the device has been stopped.
 * - XST_NO_DATA if no packet length is available. ByteCountPtr is not modified.
 *
 ******************************************************************************/
XStatus XTemac_FifoRecv(XTemac * InstancePtr, u32 * ByteCountPtr)
{
	u32 RegIPISR;
	volatile u32 RegRSR;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(ByteCountPtr != NULL);

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* If the receive length FIFO is empty, then there's no packet waiting */
	RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);
	if (!(RegIPISR & XTE_IPXR_RECV_DONE_MASK)) {
		return (XST_NO_DATA);
	}

	/* Get the length */
	*ByteCountPtr = GetIpIfReg(XTE_RPLR_OFFSET);

	/* The IPXR_RECV_DONE_MASK status bit is tied to the RSR register. To clear
	 * this condition, read from the RSR (which has no information) then write
	 * to the IPISR register to ack the status.
	 */
	RegRSR = GetIpIfReg(XTE_RSR_OFFSET);
	XTemac_mSetIpifReg(XTE_IPISR_OFFSET, XTE_IPXR_RECV_DONE_MASK);

	/* Return sucess */
	return (XST_SUCCESS);
}

/******************************************************************************/
/**
 * Copy data from the receive packet FIFO into a user buffer. The number of
 * bytes to copy is derived from XTemac_FifoRecv(). The packet data may be
 * copied out of the FIFO all at once or with multiple calls to this function.
 * The latter method supports systems that keep packet data in non-contiguous
 * memory regions. For example:
 * <pre>
 *    if (XTemac_FifoRecv(Tptr, &PacketLength) == XST_SUCCESS)
 *    {
 *       if (PacketLength > 14)
 *       {
 *          HeaderLength = 14;
 *          PayloadLength = PacketLength - HeaderLength;
 *
 *          Status =  XTemac_FifoRead(Tptr, UserHeaderBuf, HeaderLength,
 *                                    XTE_PARTIAL_PACKET);
 *          Status |= XTemac_FifoRead(Tptr, UserPayloadBuf, PayloadLength,
 *                                    XTE_END_OF_PACKET);
 *
 *          if (Status != XST_SUCCESS)
 *          {
 *             // handle error
 *          }
 *       }
 *    }
 * </pre>
 *
 * If the user's buffer is not aligned on a 4 byte boundary, then the transfer
 * may take longer to complete.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param BufPtr is the user buffer that will recieve packet data from the FIFO.
 *        The buffer may be on any alignment.
 * @param ByteCount is the number of bytes to transfer
 * @param Eop specifies whether the last byte read is the last byte of a packet.
 *        If set to XTE_END_OF_PACKET, then any partial bytes being buffered by
 *        the driver at the end of the transfer are discarded. These discarded
 *        bytes are filler provided by the hardware and have no meaning. If set
 *        to XTE_PARTIAL_PACKET, then more packet data is expected to be read
 *        through more calls to this function. Failure to use this parameter
 *        properly will result in undefined filler bytes being copied into
 *        BufPtr.
 *
 * @return
 * - XST_SUCCESS if the data was transferred to the user buffer
 * - XST_DEVICE_IS_STOPPED if the device has not been started.
 * - XST_NO_DATA if there was not enough data in the packet FIFO to satisfy the
 *   request.
 *
 * @note
 * Do not attempt to read more than one packets worth of data at a time with
 * this function.
 ******************************************************************************/
XStatus XTemac_FifoRead(XTemac * InstancePtr, void *BufPtr, u32 ByteCount,
			int Eop)
{
	XStatus Status;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(!((Eop != XTE_END_OF_PACKET)
			 && (Eop != XTE_PARTIAL_PACKET)));

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* Transfer the data using the best/fastest method */
	Status =
	    InstancePtr->RecvFifo.XferFn(&InstancePtr->RecvFifo, BufPtr,
					 ByteCount, Eop);

	/* Return correct status */
	if (Status == XST_NO_DATA) {
		return (XST_NO_DATA);
	} else {
		return (XST_SUCCESS);
	}
}

/******************************************************************************/
/**
 * Retrieve the number of free bytes in the packet FIFOs.
 *
 * For the transmit packet FIFO, the number returned is the number of bytes
 * that can be written by XTemac_FifoWrite(). If a non-zero number is returned,
 * then at least 1 packet of that size can be transmitted.
 *
 * For the receive packet FIFO, the number returned is the number of bytes that
 * can arrive from an external Ethernet device. This number does not reflect
 * the state of the receive length FIFO. If this FIFO is full, then arriving
 * packets will get dropped by the HW if there is no place to store the length.
 * 
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param Direction selects which packet FIFO to examine. If XTE_SEND, then
 *        the transmit packet FIFO is selected. If XTE_RECV, then the receive
 *        packet FIFO is selected.
 *
 * @return
 * Number of bytes available in the selected packet FIFO.
 *
 ******************************************************************************/
u32 XTemac_FifoGetFreeBytes(XTemac * InstancePtr, u32 Direction)
{
	u32 RegIPISR;
	u32 Count;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(!(Direction & ~(XTE_SEND | XTE_RECV)));

	/* For the send direction, even though there may be room in the
	 * packet FIFO, the length FIFO may be full. When this is the case,
	 * another packet cannot be transmiited so return 0.
	 */
	if (Direction == XTE_SEND) {
		/* Check length FIFO */
		RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);
		if (RegIPISR & XTE_IPXR_XMIT_LFIFO_FULL_MASK) {
			return (0);
		}

		/* Get FIFO entries */
		Count = XPF_V200A_GET_COUNT(&InstancePtr->SendFifo.Fifo);
	}

	/* Handle receive direction */
	else {
		Count = XPF_V200A_COUNT_MASK -
		    XPF_V200A_GET_COUNT(&InstancePtr->RecvFifo.Fifo);
	}

	/* Multiply free entries by the width of the packet FIFO to arrive at
	 * bytes
	 */
	return (Count * InstancePtr->RecvFifo.Width);
}

/******************************************************************************/
/**
 * Query the device for the latest transmit status for FIFO direct frame
 * transfer mode. This function should be used for polled mode operation only.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 * @param SendStatusPtr is the contents of the XTE_TSR_OFFSET register when the
 *        return code is XST_FAILURE. Otherwise 0 is returned.
 *
 * @return
 * - XST_NO_DATA if a transmit status is not currently available.
 * - XST_DEVICE_IS_STOPPED if the device has not been started.
 * - XST_NOT_POLLED if the device has not been set to polled mode.
 * - XST_SUCCESS if a transmit status was found and indicates that there was
 *   no error.
 * - XST_FIFO_ERROR if the transmit length or transmit status FIFOs error has
 *   been detected. If this error is returned, then the device must be reset
 *   before this function will return a valid transmit status indication.
 * - XST_PFIFO_ERROR if the transmit packet FIFO is deadlocked. If this error
 *   is returned, then the device must be reset before this function will
 *   return a valid transmit status indication
 * - XST_IPIF_ERROR if there has been a data phase timeout or transaction error
 *   in the IPIF. This is a fatal error.
 *
 * @note
 * When XST_FAILURE is returned with the XTE_TSR_PFIFOU_MASK bit set in the
 * SendStatusPtr parameter, then an attempt was made to transmit more data than
 * was present in the packet FIFO. No reset is required in this situation.
 *
 ******************************************************************************/
XStatus XTemac_FifoQuerySendStatus(XTemac * InstancePtr,
				   u32 * SendStatusPtr)
{
	u32 RegDISR;
	u32 RegIPISR;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);
	XASSERT_NONVOID(SendStatusPtr != NULL);

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* Have to be in polled mode to use this function */
	if (!(InstancePtr->Options & XTE_POLLED_OPTION)) {
		return (XST_NOT_POLLED);
	}

	/* Make sure send packet FIFO isn't deadlocked */
	RegDISR = GetIpIfReg(XTE_DISR_OFFSET);
	if (RegDISR & XTE_DXR_SEND_FIFO_MASK) {
		XTemac_mBumpStats(TxPktFifoErrors, 1);
		return (XST_PFIFO_ERROR);
	}

	/* Make sure no IPIF errors are present */
	if (RegDISR & (XTE_DXR_TERR_MASK | XTE_DXR_DPTO_MASK)) {
		XTemac_mBumpStats(IpifErrors, 1);
		return (XST_IPIF_ERROR);
	}

	/* Read the IPISR
	 * If any errors are detetected, try to clear and return error
	 */
	RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);
	if (RegIPISR & XTE_IPXR_XMIT_ERROR_MASK) {
		XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
				   RegIPISR & XTE_IPXR_XMIT_ERROR_MASK);
		XTemac_mBumpStats(FifoErrors, 1);
		return (XST_FIFO_ERROR);
	}

	/* No FIFO errors, so see of a transmit has completed */
	if (!(RegIPISR & XTE_IPXR_XMIT_DONE_MASK)) {
		return (XST_NO_DATA);
	}

	/* Transmit has completed, get the status, ack the condition */
	*SendStatusPtr = GetIpIfReg(XTE_TSR_OFFSET);
	XTemac_mSetIpifReg(XTE_IPISR_OFFSET, XTE_IPXR_XMIT_DONE_MASK);

	/* no errors to report */
	return (XST_SUCCESS);
}

/******************************************************************************/
/**
 * Query the device for the latest receive status for FIFO direct frame
 * transfer mode. This function should be used for polled mode operation only.
 *
 * @param InstancePtr is a pointer to the instance to be worked on.
 *
 * @return
 * - XST_SUCCESS if a frame has been received and no receive error was detected.
 * - XST_DEVICE_IS_STOPPED if the device has not been started.
 * - XST_NO_DATA if no frame has been received and no receive related error has
 *   been detected.
 * - XST_NOT_POLLED if the device has not been set to polled mode.
 * - XST_DATA_LOST if the device reports that it dropped a receive frame. This
 *   is not a serious problem but may indicate that frames are arriving faster
 *   than the system can process them.
 * - XST_FIFO_ERROR if an error was detected with the receive length FIFO. If
 *   this error is returned, then the device must be reset before any new frame
 *   can be received.
 * - XST_PFIFO_ERROR if the receive packet FIFO is deadlocked. If this error is
 *   returned, then the device must be reset before any new frame can be
 *   received.
 * - XST_IPIF_ERROR if there has been a data phase timeout or transaction error
 *   in the IPIF. This is a fatal error.
 *
 * @note
 * In situations where simultaneously a frame has been received for which an
 * XST_SUCCESS can be returned and a dropped frame for which an XST_DATA_LOST
 * can be returned, then this function will give priority to XST_SUCCESS so the
 * user can receive the frame.
 ******************************************************************************/
XStatus XTemac_FifoQueryRecvStatus(XTemac * InstancePtr)
{
	u32 RegDISR;
	u32 RegIPISR;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/* Make sure device is ready for this operation */
	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return (XST_DEVICE_IS_STOPPED);
	}

	/* Have to be in polled mode to use this function */
	if (!(InstancePtr->Options & XTE_POLLED_OPTION)) {
		return (XST_NOT_POLLED);
	}

	/* Read the DISR */
	RegDISR = GetIpIfReg(XTE_DISR_OFFSET);

	/* Make sure recv packet FIFO isn't deadlocked */
	if (RegDISR & XTE_DXR_RECV_FIFO_MASK) {
		XTemac_mBumpStats(RxPktFifoErrors, 1);
		return (XST_PFIFO_ERROR);
	}

	/* Make sure no IPIF errors are present */
	if (RegDISR & (XTE_DXR_TERR_MASK | XTE_DXR_DPTO_MASK)) {
		XTemac_mBumpStats(IpifErrors, 1);
		return (XST_IPIF_ERROR);
	}

	/* Read the IPISR */
	RegIPISR = GetIpIfReg(XTE_IPISR_OFFSET);

	/* Check for other recv related FIFO errors */
	if (RegIPISR &
	    (XTE_IPXR_RECV_ERROR_MASK - XTE_IPXR_RECV_DROPPED_MASK)) {
		XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
				   RegIPISR & XTE_IPXR_RECV_ERROR_MASK);
		XTemac_mBumpStats(FifoErrors, 1);
		return (XST_FIFO_ERROR);
	}

	/* See if a frame has been received */
	if (RegIPISR & XTE_IPXR_RECV_DONE_MASK) {
		return (XST_SUCCESS);
	}

	/* If option to detect recv reject errors is set, check for rejected
	 * receive frames. If one is detected, clear it and return error.
	 */
	if (InstancePtr->Options & XTE_REPORT_RXERR_OPTION) {
		if (RegIPISR & XTE_IPXR_RECV_DROPPED_MASK) {
			XTemac_mSetIpifReg(XTE_IPISR_OFFSET,
					   RegIPISR &
					   XTE_IPXR_RECV_DROPPED_MASK);
			return (XST_DATA_LOST);
		}
	}

	/* No frame has been received and no errors detected */
	return (XST_NO_DATA);
}

/*******************************************************************************
* Algorithm to write to a 64 bit wide transmit packet FIFO through the holding
* buffer.
*
* @param FPtr is a pointer to a Temac FIFO instance to worked on.
* @param BufPtr is the source buffer address on any alignment
* @param ByteCount is the number of bytes to transfer
* @param Eop specifies whether the last byte written is the last byte of the
*        packet.
*
* @return XST_SUCCESS
*******************************************************************************/
static XStatus Write_64(XTemac_PacketFifo * Fptr, void *BufPtr,
			u32 ByteCount, int Eop)
{
	unsigned BufAlignment = (unsigned) BufPtr & 3;
	unsigned PartialBytes;
	int HoldAlignment = mHold_GetIndex(Fptr);

	/* Case 1: Buffer aligned on 4-byte boundary and Hold is empty
	 *
	 *   1. Write all bytes using the fastest transfer method
	 */
	if ((BufAlignment == 0) && (mHoldS_IsEmpty(Fptr))) {
		Write64_Aligned(Fptr, (u32 *) BufPtr, ByteCount);
	}

	/* Case 2: Buffer and Hold are byte aligned with each other
	 *
	 *   1. Transfer enough bytes from the buffer to the Hold to trigger a flush
	 *      to the FIFO.
	 *
	 *   2. The state of the buffer and Hold are as described by Case 1 so
	 *      write remaining bytes using the fastest transfer method
	 */
	else if (BufAlignment == (HoldAlignment % PFIFO_64BIT_WIDTH_BYTES)) {
		PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;

		if (ByteCount < PartialBytes) {
			PartialBytes = ByteCount;
		}

		Write64_Unaligned(Fptr, BufPtr, PartialBytes);
		Write64_Aligned(Fptr, (u32 *) ((u32) BufPtr + PartialBytes),
				ByteCount - PartialBytes);
	}

	/* Case 3: No alignment to take advantage of
	 *
	 *    1. Read FIFOs using the slower method.
	 */
	else {
		Write64_Unaligned(Fptr, BufPtr, ByteCount);
	}

	/* If TxBytes is non-zero then the caller wants to transmit data from the
	 * FIFO
	 */
	if (Eop == XTE_END_OF_PACKET) {
		/* Push the hold to the FIFO if data is present */
		if (!mHoldS_IsEmpty(Fptr)) {
			mPush64(Fptr);
			mHoldS_SetEmpty(Fptr);
		}
	}

	return (XST_SUCCESS);
}

/*******************************************************************************
* Algorithm to read from a 64 bit wide receive packet FIFO with through the
* holding buffer.
*
* @param Fptr is a pointer to a Temac FIFO instance to worked on.
* @param BufPtr is the destination address on any alignment
* @param ByteCount is the number of bytes to transfer
*
* @return XST_SUCCESS if transfer completed or XST_NO_DATA if the amount of
*         data being buffered by the driver plus the amount of data in the
*         packet FIFO is not enough to satisfy the number of bytes requested
*         by the ByteCount parameter.
*******************************************************************************/
static XStatus Read_64(XTemac_PacketFifo * Fptr, void *BufPtr,
		       u32 ByteCount, int Eop)
{
	unsigned BufAlignment = (unsigned) BufPtr & 3;
	unsigned PartialBytes;
	unsigned MaxBytes;
	int HoldAlignment = mHold_GetIndex(Fptr);

	/* Determine how many bytes can be read from the packet FIFO */
	MaxBytes = XPF_V200A_COUNT_MASK & XPF_V200A_GET_COUNT(&Fptr->Fifo);
	MaxBytes *= PFIFO_64BIT_WIDTH_BYTES;

	/* Case 1: Buffer aligned on 4-byte boundary and Hold is empty
	 *
	 *   1. Read all bytes using the fastest transfer method
	 */
	if ((BufAlignment == 0) && (mHoldR_IsEmpty(Fptr))) {
		/* Enough data in fifo? */
		if (ByteCount > MaxBytes) {
			return (XST_NO_DATA);
		}

		Read64_Aligned(Fptr, (u32 *) BufPtr, ByteCount);
	}

	/* Case 2: Buffer and Hold are byte aligned with each other
	 *
	 *   1. Transfer enough bytes from the Hold to the buffer to trigger a
	 *      read from the FIFO.
	 *
	 *   2. The state of the buffer and Hold are now as described by Case 1 so
	 *      read remaining bytes using the fastest transfer method
	 */
	else if (BufAlignment == (HoldAlignment % PFIFO_64BIT_WIDTH_BYTES)) {
		PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;

		if (ByteCount < PartialBytes) {
			PartialBytes = ByteCount;
		}

		/* Enough data in fifo? Must account for the number of bytes the driver
		 * is currently buffering
		 */
		if (ByteCount > (MaxBytes + PartialBytes)) {
			return (XST_NO_DATA);
		}

		Read64_Unaligned(Fptr, BufPtr, PartialBytes);
		Read64_Aligned(Fptr, (u32 *) ((u32) BufPtr + PartialBytes),
			       ByteCount - PartialBytes);
	}

	/* Case 3: No alignment to take advantage of
	 *
	 *    1. Read FIFOs using the slower method.
	 */
	else {
		/* Enough data in fifo? Must account for the number of bytes the driver
		 * is currently buffering
		 */
		PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;
		if (ByteCount > (MaxBytes + PartialBytes)) {
			return (XST_NO_DATA);
		}

		Read64_Unaligned(Fptr, BufPtr, ByteCount);
	}

	/* If this marks the end of packet, then dump any remaining data in the
	 * hold. The dumped data in this context is meaningless.
	 */
	if (Eop == XTE_END_OF_PACKET) {
		mHoldR_SetEmpty(Fptr);
	}

	return (XST_SUCCESS);
}

/*******************************************************************************
* Write to the 64 bit holding buffer. Each time it becomes full, then it is
* pushed to the transmit FIFO.
*
* @param F is a pointer to the packet FIFO instance to be worked on.
* @param BufPtr is the source buffer address on any alignment
* @param ByteCount is the number of bytes to transfer
*
*******************************************************************************/
static void Write64_Unaligned(XTemac_PacketFifo * F,
			      void *BufPtr, u32 ByteCount)
{
	u8 *SrcPtr = (u8 *) BufPtr;
	unsigned FifoTransfersLeft;
	unsigned PartialBytes;
	unsigned BytesLeft;
	int i;

	/* Stage 1: The hold may be partially full. Write enough bytes to it to
	 * cause a push to the FIFO
	 */

	/* Calculate the number of bytes needed to trigger a push, if not enough
	 * bytes have been specified to cause a push, then adjust accordingly
	 */
	i = mHold_GetIndex(F);
	PartialBytes = PFIFO_64BIT_WIDTH_BYTES - i;
	if (PartialBytes > ByteCount) {
		PartialBytes = ByteCount;
	}

	/* Calculate the number of bytes remaining after the first push */
	BytesLeft = ByteCount - PartialBytes;

	/* Write to the hold and advance its index */
	mHold_Advance(F, PartialBytes);

	while (PartialBytes--) {
		mHold_CopyIn(F, i, *SrcPtr);
		SrcPtr++;
		i++;
	}

	/* Push to fifo if needed */
	if (mHoldS_IsFull(F)) {
		mPush64(F);
		mHoldS_SetEmpty(F);
	}

	/* No more data to process */
	if (!BytesLeft) {
		return;
	}

	/* Stage 2: The hold is empty now, if any more bytes are left to process, then
	 * it will begin with nothing in the hold. Use the hold as a temporary storage
	 * area to contain the data.
	 *
	 * The hold is filled then pushed out to the FIFOs a number of times based on
	 * how many bytes are left to process.
	 */

	/* Calculate the number of times a push will need to occur */
	FifoTransfersLeft = BytesLeft / PFIFO_64BIT_WIDTH_BYTES;

	/* Calculate the number of partial bytes left after this stage */
	PartialBytes =
	    BytesLeft - (FifoTransfersLeft * PFIFO_64BIT_WIDTH_BYTES);

	/* Write to the hold and push data to the FIFO */
	while (FifoTransfersLeft--) {
		for (i = 0; i < PFIFO_64BIT_WIDTH_BYTES; i++) {
			mHold_CopyIn(F, i, *SrcPtr);
			SrcPtr++;
		}
		mPush64(F);
	}

	/* No more data to process
	 * HoldIndex was left at 0 by stage 1, at this point, that is
	 * still the correct value.
	 */
	if (!PartialBytes) {
		return;
	}

	/* Stage 3: All that is left is to fill the hold with the remaining data
	 * to be processed. There will be no push to the FIFO because there is not
	 * enough data left to cause one.
	 */

	/* Write to the hold and push data to the FIFO */
	for (i = 0; i < PartialBytes; i++) {
		mHold_CopyIn(F, i, *SrcPtr);
		SrcPtr++;
	}

	/* Set the hold's index to its final correct value */
	mHold_SetIndex(F, PartialBytes);
}

/*******************************************************************************
* Write directly to the 64 bit wide transmit FIFO from an aligned source
* buffer. Leftover bytes are written to the holding buffer.
*
* @param F is a pointer to the packet FIFO instance to be worked on.
* @param BufPtr is the source buffer address on 32-bit alignment
* @param ByteCount is the number of bytes to transfer
*
*******************************************************************************/
static void Write64_Aligned(XTemac_PacketFifo * F, u32 * BufPtr, u32 ByteCount)
{
	unsigned FifoTransfersLeft = ByteCount / PFIFO_64BIT_WIDTH_BYTES;
	unsigned PartialBytes = ByteCount & (PFIFO_64BIT_WIDTH_BYTES - 1);

	/* Direct transfer */
	while (FifoTransfersLeft--) {
		mWriteFifo64(F, BufPtr);
		BufPtr += 2;
	}

	/* Leftover bytes are left in the holding area */
	if (PartialBytes) {
		Write64_Unaligned(F, BufPtr, PartialBytes);
	}
}

/*******************************************************************************
* Read into the 64 bit holding buffer from the receive packet FIFO.
* Each time the holding buffer becomes full, then it is flushed to the
* provided buffer.
*
* @param F is a pointer to the packet FIFO instance to be worked on.
* @param BufPtr is the destination buffer address on any alignment
* @param ByteCount is the number of bytes to transfer
*
*******************************************************************************/
static void Read64_Unaligned(XTemac_PacketFifo * F, void *BufPtr, u32 ByteCount)
{
	u8 *DestPtr = (u8 *) BufPtr;
	unsigned FifoTransfersLeft;
	unsigned PartialBytes;
	unsigned BytesLeft;
	int i;

	/* Stage 1: The hold may have some residual bytes that must be flushed
	 * to the buffer before anything is read from the FIFO
	 */

	/* Calculate the number of bytes to flush to the buffer from the hold.
	 * If the number of bytes to flush is greater than the "Bytes" requested,
	 * then adjust accordingly.
	 */
	i = mHold_GetIndex(F);
	PartialBytes = PFIFO_64BIT_WIDTH_BYTES - i;

	if (PartialBytes > ByteCount) {
		PartialBytes = ByteCount;
	}

	/* Calculate the number of bytes remaining after flushing to the buffer */
	BytesLeft = ByteCount - PartialBytes;

	/* Move the hold's index forward */
	mHold_Advance(F, PartialBytes);

	/* Copy bytes */
	while (PartialBytes--) {
		mHold_CopyOut(F, i, *DestPtr);
		i++;
		DestPtr++;
	}

	/* No more data to process */
	if (!BytesLeft) {
		return;
	}

	/* Stage 2: The hold is empty now, if any more bytes are left to process, then
	 * it will begin with nothing in the hold. Use the hold as a temporary storage
	 * area to contain the data.
	 *
	 * The hold is filled with FIFO data, then that data is written to the buffer.
	 * Do this FifoTransfersLeft times
	 */

	/* Calculate the number of times a push will need to occur */
	FifoTransfersLeft = BytesLeft / PFIFO_64BIT_WIDTH_BYTES;

	/* Calculate the number of partial bytes left after this stage */
	PartialBytes =
	    BytesLeft - (FifoTransfersLeft * PFIFO_64BIT_WIDTH_BYTES);

	/* Write to the hold and push data to the FIFO */
	while (FifoTransfersLeft--) {
		/* Load the hold with the next data set from the FIFO */
		mPop64(F);

		/* Write hold to buffer */
		for (i = 0; i < PFIFO_64BIT_WIDTH_BYTES; i++) {
			mHold_CopyOut(F, i, *DestPtr);
			DestPtr++;
		}
	}

	/* No more data to process
	 * After processing full FIFO chunks of data, the hold is empty at this
	 * point
	 */
	if (!PartialBytes) {
		return;
	}

	/* Stage 3: All that is left is to fill the hold one more time with FIFO
	 * data, then write the remaining requested bytes to the buffer
	 */

	/* Get FIFO data */
	mPop64(F);

	/* Copy bytes from the hold to the buffer */
	for (i = 0; i < PartialBytes; i++) {
		mHold_CopyOut(F, i, *DestPtr);
		DestPtr++;
	}

	/* Set the hold's index to its final correct value */
	mHold_SetIndex(F, PartialBytes);
}

/*******************************************************************************
* Read directly from the 64 bit wide receive FIFO into an aligned destination
* buffer. Leftover bytes are written to the holding buffer.
*
* @param F is a pointer to the packet FIFO instance to be worked on.
* @param BufPtr is the destination buffer address on 32-bit alignment
* @param ByteCount is the number of bytes to transfer
*
*******************************************************************************/
static void Read64_Aligned(XTemac_PacketFifo * F, u32 * BufPtr, u32 ByteCount)
{
	unsigned FifoTransfersLeft = ByteCount / PFIFO_64BIT_WIDTH_BYTES;
	unsigned PartialBytes = ByteCount & (PFIFO_64BIT_WIDTH_BYTES - 1);

	/* Direct transfer */
	while (FifoTransfersLeft--) {
		mReadFifo64(F, BufPtr);
		BufPtr += 2;
	}

	/* Leftover bytes are left in the holding area */
	if (PartialBytes) {
		Read64_Unaligned(F, BufPtr, PartialBytes);
	}
}
/** @name IPIF interrupt and reset registers
 *  @{
 */
#define XTE_DISR_OFFSET  0x00000000  /**< Device interrupt status */
#define XTE_DIPR_OFFSET  0x00000004  /**< Device interrupt pending */
#define XTE_DIER_OFFSET  0x00000008  /**< Device interrupt enable */
#define XTE_DGIE_OFFSET  0x0000001C  /**< Device global interrupt enable */
#define XTE_IPISR_OFFSET 0x00000020  /**< IP interrupt status */
#define XTE_IPIER_OFFSET 0x00000028  /**< IP interrupt enable */
#define XTE_DSR_OFFSET   0x00000040  /**< Device software reset (write) */
#define XTE_MIR_OFFSET   0x00000040  /**< Device software reset (read) */
#define XTE_DXR_SEND_FIFO_MASK          0x00000040 /**< Send FIFO channel */
#define XTE_DXR_RECV_FIFO_MASK          0x00000020 /**< Receive FIFO channel */
#define XTE_DXR_CORE_MASK               0x00000004 /**< Core */
#define XTE_DXR_DPTO_MASK               0x00000002 /**< Data phase timeout */
#define XTE_DXR_TERR_MASK               0x00000001 /**< Transaction error */
#define XTE_IPXR_XMIT_DONE_MASK         0x00000001 /**< Tx complete */
#define XTE_IPXR_RECV_DONE_MASK         0x00000002 /**< Rx complete */
#define XTE_IPXR_AUTO_NEG_MASK          0x00000004 /**< Auto negotiation complete */
#define XTE_IPXR_RECV_REJECT_MASK       0x00000008 /**< Rx packet rejected */
#define XTE_IPXR_XMIT_SFIFO_EMPTY_MASK  0x00000010 /**< Tx status fifo empty */
#define XTE_IPXR_RECV_LFIFO_EMPTY_MASK  0x00000020 /**< Rx length fifo empty */
#define XTE_IPXR_XMIT_LFIFO_FULL_MASK   0x00000040 /**< Tx length fifo full */
#define XTE_IPXR_RECV_LFIFO_OVER_MASK   0x00000080 /**< Rx length fifo overrun
                                                        Note that this signal is
                                                        no longer asserted by HW
                                                        */
#define XTE_IPXR_RECV_LFIFO_UNDER_MASK  0x00000100 /**< Rx length fifo underrun */
#define XTE_IPXR_XMIT_SFIFO_OVER_MASK   0x00000200 /**< Tx status fifo overrun */
#define XTE_IPXR_XMIT_SFIFO_UNDER_MASK  0x00000400 /**< Tx status fifo underrun */
#define XTE_IPXR_XMIT_LFIFO_OVER_MASK   0x00000800 /**< Tx length fifo overrun */
#define XTE_IPXR_XMIT_LFIFO_UNDER_MASK  0x00001000 /**< Tx length fifo underrun */
#define XTE_IPXR_RECV_PFIFO_ABORT_MASK  0x00002000 /**< Rx packet rejected due to
                                                        full packet FIFO */
#define XTE_IPXR_RECV_LFIFO_ABORT_MASK  0x00004000 /**< Rx packet rejected due to
                                                        full length FIFO */
#define XTE_IPXR_MII_PEND_MASK          0x00008000 /**< Mii operation now
                                                        pending */
#define XTE_IPXR_MII_DONE_MASK          0x00010000 /**< Mii operation has
                                                        completed */
#define XTE_IPXR_XMIT_PFIFO_UNDER_MASK  0x00020000 /**< Tx packet FIFO
                                                        underrun */
#define XTE_IPXR_XMIT_DMA_MASK          0x00080000 /**< Rx dma channel */
#define XTE_IPXR_RECV_DMA_MASK          0x00100000 /**< Tx dma channel */
#define XTE_IPXR_RECV_FIFO_LOCK_MASK    0x00200000 /**< Rx FIFO deadlock */
#define XTE_IPXR_XMIT_FIFO_LOCK_MASK    0x00400000 /**< Tx FIFO deadlock */
#define XTE_DGIE_ENABLE_MASK    0x80000000  /**< Write this value to DGIE to
                                                 enable interrupts from this
                                                 device */
#define XTE_CR_BCREJ_MASK       0x00000004   /**< Disable broadcast address
                                                  filtering */
#define XTE_CR_MCREJ_MASK       0x00000002   /**< Disable multicast address
                                                  filtering */
#define XTE_CR_HRST_MASK        0x00000001   /**< Reset the hard TEMAC core */
#define XTE_UAW1_MASK          0x0000FFFF   /**< Station address bits [47:32]
                                                 Station address bits [31:0] 
                                                 are stored in register
                                                 UAW0 */
#define XTE_MAW1_CAMRNW_MASK   0x00800000   /**< CAM read/write control */
#define XTE_MAW1_CAMADDR_MASK  0x00030000   /**< CAM address mask */
#define XTE_MAW1_MASK          0x0000FFFF   /**< Multicast address bits [47:32]
                                                 Multicast address bits [31:0] 
                                                 are stored in register
                                                 MAW0 */
#define XTE_MAW1_CAMMADDR_SHIFT_MASK 16	    /**< Number of bits to shift right
                                                 to align with
                                                 XTE_MAW1_CAMADDR_MASK */
#define XTE_AFM_EPPRM_MASK     0x80000000   /**< Promiscuous mode enable */
#define XTemac_mReadReg(BaseAddress, RegOffset) \
    XIo_In32((BaseAddress) + (RegOffset))
#define XTemac_mWriteReg(BaseAddress, RegOffset, Data) \
    XIo_Out32((BaseAddress) + (RegOffset), (Data))
#define XTemac_mHostOffset(HostRegOffset) \
    ((u32)(HostRegOffset) + XTE_HOST_IPIF_OFFSET)
#define XTemac_mReadHostReg(BaseAddress, HostRegOffset) \
    XIo_In32((BaseAddress) + XTemac_mHostOffset(HostRegOffset))
#define XTemac_mWriteHostReg(BaseAddress, HostRegOffset, Data) \
    XIo_Out32((BaseAddress) + XTemac_mHostOffset(HostRegOffset), (Data))

#endif