diff mbox

[U-Boot] net: ll_temac: Add LL TEMAC driver to u-boot

Message ID 1314617652-19292-1-git-send-email-monstr@monstr.eu
State Changes Requested
Headers show

Commit Message

Michal Simek Aug. 29, 2011, 11:34 a.m. UTC
LL Temac driver can be used by microblaze, xilinx ppc405/440
in sdma and fifo mode. DCR or XPS bus can be used.

The driver uses and requires PHYLIB.

Signed-off-by: Michal Simek <monstr@monstr.eu>
---
 drivers/net/Makefile          |    1 +
 drivers/net/xilinx_ll_temac.c |  686 +++++++++++++++++++++++++++++++++++++++++
 include/netdev.h              |    1 +
 3 files changed, 688 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/xilinx_ll_temac.c

Comments

Mike Frysinger Aug. 29, 2011, 7:33 p.m. UTC | #1
On Monday, August 29, 2011 07:34:12 Michal Simek wrote:
> --- /dev/null
> +++ b/drivers/net/xilinx_ll_temac.c
> 
> +#include <asm/processor.h>

what do you need from this header ?

> +#ifdef SDMA_MODE
> +static unsigned char tx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));
> +#endif
> +static unsigned char rx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));

no space before that "]"

> +static inline void temac_out_be32(u32 addr, u32 offset, u32 val)
> +{
> +	out_be32((u32 *)(addr + offset), val);
> +}
> +
> +static inline u32 temac_in_be32(u32 addr, u32 offset)
> +{
> +	return in_be32((u32 *)(addr + offset));
> +}

write a C struct describing the register layout, then your code can simply do:
	in_be32(&regs->lsw0)
and you don't need these two helpers

> +static int xps_ll_temac_addr_setup(struct eth_device *dev)
> +{
> +	int val;
> +
> +	/* set up unicast MAC address filter */
> +	val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) |
> +		(dev->enetaddr[1] << 8) | (dev->enetaddr[0]));
> +	xps_ll_temac_indirect_set(dev, 0, UAW0, val);
> +	val = (dev->enetaddr[5] << 8) | dev->enetaddr[4] ;
> +	xps_ll_temac_indirect_set(dev, 0, UAW1, val);
> +
> +	return 0;
> +}

this should be set to dev->write_hwaddr in the initialize func

> +int xilinx_ll_temac_initialize(bd_t *bis, int base_addr)

the register base address really should be "unsigned long" and not "int"
-mike
Michal Simek Aug. 31, 2011, 6:26 a.m. UTC | #2
Mike Frysinger wrote:
> On Monday, August 29, 2011 07:34:12 Michal Simek wrote:
>> --- /dev/null
>> +++ b/drivers/net/xilinx_ll_temac.c
>>
>> +#include <asm/processor.h>
> 
> what do you need from this header ?

dcr for ppc.

> 
>> +#ifdef SDMA_MODE
>> +static unsigned char tx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));
>> +#endif
>> +static unsigned char rx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));
> 
> no space before that "]"

done.

> 
>> +static inline void temac_out_be32(u32 addr, u32 offset, u32 val)
>> +{
>> +	out_be32((u32 *)(addr + offset), val);
>> +}
>> +
>> +static inline u32 temac_in_be32(u32 addr, u32 offset)
>> +{
>> +	return in_be32((u32 *)(addr + offset));
>> +}
> 
> write a C struct describing the register layout, then your code can simply do:
> 	in_be32(&regs->lsw0)
> and you don't need these two helpers

Agree that there is elegant way to fix it. Look at my v2.

> 
>> +static int xps_ll_temac_addr_setup(struct eth_device *dev)
>> +{
>> +	int val;
>> +
>> +	/* set up unicast MAC address filter */
>> +	val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) |
>> +		(dev->enetaddr[1] << 8) | (dev->enetaddr[0]));
>> +	xps_ll_temac_indirect_set(dev, 0, UAW0, val);
>> +	val = (dev->enetaddr[5] << 8) | dev->enetaddr[4] ;
>> +	xps_ll_temac_indirect_set(dev, 0, UAW1, val);
>> +
>> +	return 0;
>> +}
> 
> this should be set to dev->write_hwaddr in the initialize func

done. Have to look at axi_ethernet version if possible to use it.

> 
>> +int xilinx_ll_temac_initialize(bd_t *bis, int base_addr)
> 
> the register base address really should be "unsigned long" and not "int"

Agree. Probably good to fix all network devices too.

Have sent v2 for ll_temac and the first version for axi_emac.

Thanks,
Michal

P.S.: Forget to send it yesterday.
diff mbox

Patch

diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 819b197..4541eaf 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -84,6 +84,7 @@  COBJS-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 COBJS-$(CONFIG_ULI526X) += uli526x.o
 COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o
 COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
+COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o
 
 COBJS	:= $(sort $(COBJS-y))
 SRCS	:= $(COBJS:.o=.c)
diff --git a/drivers/net/xilinx_ll_temac.c b/drivers/net/xilinx_ll_temac.c
new file mode 100644
index 0000000..04b685d
--- /dev/null
+++ b/drivers/net/xilinx_ll_temac.c
@@ -0,0 +1,686 @@ 
+/*
+ * Xilinx xps_ll_temac ethernet driver for u-boot
+
+ * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu>
+ * June 2008 Microblaze optimalization, FIFO mode support
+ *
+ * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver
+ * Copyright (C) 2008 Nissin Systems Co.,Ltd.
+ * March 2008 created
+ *
+ * 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.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <net.h>
+#include <malloc.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <phy.h>
+#include <miiphy.h>
+
+#undef ETH_HALTING
+
+#define XTE_EMMC_LINKSPEED_MASK	0xC0000000 /* Link speed */
+/* XTE_EMCFG_LINKSPD_MASK */
+#define XTE_EMMC_LINKSPD_10	0x00000000 /* for 10 Mbit */
+#define XTE_EMMC_LINKSPD_100	0x40000000 /* for 100 Mbit */
+#define XTE_EMMC_LINKSPD_1000	0x80000000 /* forr 1000 Mbit */
+
+/* XPS_LL_TEMAC direct registers definition */
+#define TEMAC_LSW0		0x24
+#define TEMAC_CTL0		0x28
+#define TEMAC_RDY0		0x2c
+
+#define XTE_RSE_MIIM_RR_MASK	0x0002
+#define XTE_RSE_MIIM_WR_MASK	0x0004
+#define XTE_RSE_CFG_RR_MASK	0x0020
+#define XTE_RSE_CFG_WR_MASK	0x0040
+
+/* XPS_LL_TEMAC indirect registers offset definition */
+#define RCW1	0x240
+#define TC	0x280
+#define EMMC	0x300
+#define MC	0x340
+#define UAW0	0x380
+#define UAW1	0x384
+#define AFM	0x390
+#define MIIMWD	0x3b0
+#define MIIMAI	0x3b4
+
+#define CNTLREG_WRITE_ENABLE_MASK	0x8000
+
+#define MDIO_ENABLE_MASK	0x40
+#define MDIO_CLOCK_DIV_100MHz	0x28
+
+/* Backwards compatibility to older xparameters.h files */
+#ifndef XILINX_LLTEMAC_SDMA_USE_DCR
+#define XILINX_LLTEMAC_SDMA_USE_DCR 0
+#endif
+
+#ifdef XILINX_LLTEMAC_FIFO_BASEADDR
+# define FIFO_MODE	1
+#elif XILINX_LLTEMAC_SDMA_CTRL_BASEADDR
+# define SDMA_MODE	1
+# if XILINX_LLTEMAC_SDMA_USE_DCR == 1
+
+static void mtdcr_local(u32 reg, u32 val)
+{
+	mtdcr(0x00, reg);
+	mtdcr(0x01, val);
+}
+
+static u32 mfdcr_local(u32 reg)
+{
+	u32 val;
+	mtdcr(0x00, reg);
+	val = mfdcr(0x01);
+	return val;
+}
+
+#  define sdma_out_be32(addr, offset, val) \
+				mtdcr_local((u32) (addr + offset), (val))
+#  define sdma_in_be32(addr, offset)	mfdcr_local((u32) (addr + offset))
+
+# else
+#  define sdma_out_be32(addr, offset, val) \
+				out_be32((u32 *)(addr + offset * 4), (val))
+#  define sdma_in_be32(addr, offset)	in_be32((u32 *)(addr + offset * 4))
+# endif
+#else
+# error Xilinx LL Temac: Unsupported mode - Please setup SDMA or FIFO mode
+#endif
+
+#ifdef SDMA_MODE
+/* XPS_LL_TEMAC SDMA registers definition */
+# define TX_CURDESC_PTR		0x03
+# define TX_TAILDESC_PTR	0x04
+# define TX_CHNL_CTRL		0x05
+# define TX_IRQ_REG		0x06
+# define TX_CHNL_STS		0x07
+# define RX_NXTDESC_PTR		0x08
+# define RX_CURDESC_PTR		0x0b
+# define RX_TAILDESC_PTR	0x0c
+# define RX_CHNL_CTRL		0x0d
+# define RX_IRQ_REG		0x0e
+# define RX_CHNL_STS		0x0f
+# define DMA_CONTROL_REG	0x10
+
+/* CDMAC descriptor status bit definitions */
+# define BDSTAT_STOP_ON_END_MASK	0x20
+# define BDSTAT_COMPLETED_MASK		0x10
+# define BDSTAT_SOP_MASK		0x08
+# define BDSTAT_EOP_MASK		0x04
+# define CHNL_STS_ERROR_MASK		0x80
+
+/* All interrupt enable bits */
+#define XLLDMA_CR_IRQ_ALL_EN_MASK	0x00000087
+/* All interrupt bits */
+#define XLLDMA_IRQ_ALL_MASK		0x0000001F
+/* Disable error when 2 or 4 bit coalesce counter overflows */
+#define XLLDMA_DMACR_RX_OVERFLOW_ERR_DIS_MASK	0x00000010
+/* Disable error when 2 or 4 bit coalesce counter overflows */
+#define XLLDMA_DMACR_TX_OVERFLOW_ERR_DIS_MASK	0x00000008
+/* Enable use of tail pointer register */
+#define XLLDMA_DMACR_TAIL_PTR_EN_MASK	0x00000004
+
+/* SDMA Buffer Descriptor */
+typedef struct cdmac_bd_t {
+	struct cdmac_bd_t *next_p;
+	unsigned char *phys_buf_p;
+	unsigned long buf_len;
+	unsigned char stat;
+	unsigned char app1_1;
+	unsigned short app1_2;
+	unsigned long app2;
+	unsigned long app3;
+	unsigned long app4;
+	unsigned long app5;
+} cdmac_bd __attribute((aligned(32))) ;
+
+static cdmac_bd	tx_bd;
+static cdmac_bd	rx_bd;
+#endif
+
+#ifdef FIFO_MODE
+typedef struct ll_fifo_s {
+	int isr; /* Interrupt Status Register 0x0 */
+	int ier; /* Interrupt Enable Register 0x4 */
+	int tdfr; /* Transmit data FIFO reset 0x8 */
+	int tdfv; /* Transmit data FIFO Vacancy 0xC */
+	int tdfd; /* Transmit data FIFO 32bit wide data write port 0x10 */
+	int tlf; /* Write Transmit Length FIFO 0x14 */
+	int rdfr; /* Read Receive data FIFO reset 0x18 */
+	int rdfo; /* Receive data FIFO Occupancy 0x1C */
+	int rdfd; /* Read Receive data FIFO 32bit wide data read port 0x20 */
+	int rlf; /* Read Receive Length FIFO 0x24 */
+	int llr; /* Read LocalLink reset 0x28 */
+} ll_fifo_s;
+
+ll_fifo_s *ll_fifo = (ll_fifo_s *) (XILINX_LLTEMAC_FIFO_BASEADDR);
+#endif
+
+#ifdef SDMA_MODE
+static unsigned char tx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));
+#endif
+static unsigned char rx_buffer[PKTSIZE_ALIGN ] __attribute((aligned(32)));
+
+struct ll_priv {
+	unsigned int sdma;
+	unsigned int use_dcr;
+	int phyaddr;
+	unsigned int initialized;
+
+	struct phy_device *phydev;
+	struct mii_dev *bus;
+};
+
+static inline void temac_out_be32(u32 addr, u32 offset, u32 val)
+{
+	out_be32((u32 *)(addr + offset), val);
+}
+
+static inline u32 temac_in_be32(u32 addr, u32 offset)
+{
+	return in_be32((u32 *)(addr + offset));
+}
+
+#if defined(DEBUG) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+/* undirect hostif write to ll_temac */
+static void xps_ll_temac_hostif_set(struct eth_device *dev, int emac,
+			int phy_addr, int reg_addr, int phy_data)
+{
+	temac_out_be32(dev->iobase, TEMAC_LSW0, phy_data);
+	temac_out_be32(dev->iobase, TEMAC_CTL0,
+					CNTLREG_WRITE_ENABLE_MASK | MIIMWD);
+	temac_out_be32(dev->iobase, TEMAC_LSW0, (phy_addr << 5) | (reg_addr));
+	temac_out_be32(dev->iobase, TEMAC_CTL0,
+			CNTLREG_WRITE_ENABLE_MASK | MIIMAI | (emac << 10));
+	while (!(temac_in_be32(dev->iobase, TEMAC_RDY0) & XTE_RSE_MIIM_WR_MASK))
+		;
+}
+#endif
+
+/* undirect hostif read from ll_temac */
+static unsigned int xps_ll_temac_hostif_get(struct eth_device *dev,
+			int emac, int phy_addr, int reg_addr)
+{
+	temac_out_be32(dev->iobase, TEMAC_LSW0, (phy_addr << 5) | (reg_addr));
+	temac_out_be32(dev->iobase, TEMAC_CTL0, MIIMAI | (emac << 10));
+	while (!(temac_in_be32(dev->iobase, TEMAC_RDY0) & XTE_RSE_MIIM_RR_MASK))
+		;
+	return temac_in_be32(dev->iobase, TEMAC_LSW0);
+}
+
+/* undirect write to ll_temac */
+static void xps_ll_temac_indirect_set(struct eth_device *dev,
+				int emac, int reg_offset, int reg_data)
+{
+	temac_out_be32(dev->iobase, TEMAC_LSW0, reg_data);
+	temac_out_be32(dev->iobase, TEMAC_CTL0,
+			CNTLREG_WRITE_ENABLE_MASK | (emac << 10) | reg_offset);
+	while (!(temac_in_be32(dev->iobase, TEMAC_RDY0) & XTE_RSE_CFG_WR_MASK))
+		;
+}
+
+/* undirect read from ll_temac */
+static int xps_ll_temac_indirect_get(struct eth_device *dev,
+			int emac, int reg_offset)
+{
+	temac_out_be32(dev->iobase, TEMAC_CTL0, (emac << 10) | reg_offset);
+	while (!(temac_in_be32(dev->iobase, TEMAC_RDY0) & XTE_RSE_CFG_RR_MASK))
+		;
+	return temac_in_be32(dev->iobase, TEMAC_LSW0);
+}
+
+#ifdef DEBUG
+/* read from phy */
+static void read_phy_reg(struct eth_device *dev, int phy_addr)
+{
+	int j, result;
+	debug("phy%d ", phy_addr);
+	for (j = 0; j < 32; j++) {
+		result = xps_ll_temac_hostif_get(dev, 0, phy_addr, j);
+		debug("%d: 0x%x ", j, result);
+	}
+	debug("\n");
+}
+#endif
+
+/* setting ll_temac and phy to proper setting */
+static int xps_ll_temac_phy_ctrl(struct eth_device *dev)
+{
+#ifdef CONFIG_PHYLIB
+	int i;
+	unsigned int temp, speed;
+	struct ll_priv *priv = dev->priv;
+	struct phy_device *phydev;
+
+	u32 supported = (SUPPORTED_10baseT_Half |
+			SUPPORTED_10baseT_Full |
+			SUPPORTED_100baseT_Half |
+			SUPPORTED_100baseT_Full |
+			SUPPORTED_1000baseT_Half |
+			SUPPORTED_1000baseT_Full);
+
+	if (priv->phyaddr == -1) {
+		for (i = 31; i >= 0; i--) {
+			temp = xps_ll_temac_hostif_get(dev, 0, i, 1);
+			if ((temp & 0x0ffff) != 0x0ffff) {
+				debug("phy %x result %x\n", i, temp);
+				priv->phyaddr = i;
+				break;
+			}
+		}
+	}
+
+	/* interface - look at tsec */
+	phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0);
+
+	phydev->supported &= supported;
+	phydev->advertising = phydev->supported;
+	priv->phydev = phydev;
+	phy_config(phydev);
+	phy_startup(phydev);
+
+	switch (phydev->speed) {
+	case 1000:
+		speed = XTE_EMMC_LINKSPD_1000;
+		break;
+	case 100:
+		speed = XTE_EMMC_LINKSPD_100;
+		break;
+	case 10:
+		speed = XTE_EMMC_LINKSPD_10;
+		break;
+	default:
+		return 0;
+	}
+	temp = xps_ll_temac_indirect_get(dev, 0, EMMC) &
+						(~XTE_EMMC_LINKSPEED_MASK);
+	temp |= speed;
+	xps_ll_temac_indirect_set(dev, 0, EMMC, temp);
+
+	return 1;
+
+#else
+	puts("Enable PHYLIB support!\n");
+	return 0;
+#endif
+}
+
+#ifdef SDMA_MODE
+static inline int xps_ll_temac_dma_error(struct eth_device *dev)
+{
+	int err;
+	struct ll_priv *priv = dev->priv;
+
+	/* Check for TX and RX channel errrors.  */
+	err = sdma_in_be32(priv->sdma, TX_CHNL_STS) & CHNL_STS_ERROR_MASK;
+	err |= sdma_in_be32(priv->sdma, RX_CHNL_STS) & CHNL_STS_ERROR_MASK;
+	return err;
+}
+
+static void xps_ll_temac_reset_dma(struct eth_device *dev)
+{
+	u32 r;
+	struct ll_priv *priv = dev->priv;
+
+	/* Soft reset the DMA.  */
+	sdma_out_be32(priv->sdma, DMA_CONTROL_REG, 0x00000001);
+	while (sdma_in_be32(priv->sdma, DMA_CONTROL_REG) & 1)
+		;
+
+	/* Now clear the interrupts.  */
+	r = sdma_in_be32(priv->sdma, TX_CHNL_CTRL);
+	r &= ~XLLDMA_CR_IRQ_ALL_EN_MASK;
+	sdma_out_be32(priv->sdma,  TX_CHNL_CTRL, r);
+
+	r = sdma_in_be32(priv->sdma, RX_CHNL_CTRL);
+	r &= ~XLLDMA_CR_IRQ_ALL_EN_MASK;
+	sdma_out_be32(priv->sdma, RX_CHNL_CTRL, r);
+
+	/* Now ACK pending IRQs.  */
+	sdma_out_be32(priv->sdma, TX_IRQ_REG, XLLDMA_IRQ_ALL_MASK);
+	sdma_out_be32(priv->sdma, RX_IRQ_REG, XLLDMA_IRQ_ALL_MASK);
+
+	/* Set tail-ptr mode, disable errors for both channels.  */
+	sdma_out_be32(priv->sdma, DMA_CONTROL_REG,
+			XLLDMA_DMACR_TAIL_PTR_EN_MASK |
+			XLLDMA_DMACR_RX_OVERFLOW_ERR_DIS_MASK |
+			XLLDMA_DMACR_TX_OVERFLOW_ERR_DIS_MASK);
+}
+
+/* bd init */
+static void xps_ll_temac_bd_init(struct eth_device *dev)
+{
+	struct ll_priv *priv = dev->priv;
+	memset((void *)&tx_bd, 0, sizeof(cdmac_bd));
+	memset((void *)&rx_bd, 0, sizeof(cdmac_bd));
+
+	rx_bd.phys_buf_p = &rx_buffer[0];
+
+	rx_bd.next_p = &rx_bd;
+	rx_bd.buf_len = PKTSIZE_ALIGN ;
+	flush_cache((u32)&rx_bd, sizeof(cdmac_bd));
+	flush_cache((u32)rx_bd.phys_buf_p, PKTSIZE_ALIGN );
+
+	sdma_out_be32(priv->sdma, RX_CURDESC_PTR, (u32)&rx_bd);
+	sdma_out_be32(priv->sdma, RX_TAILDESC_PTR, (u32)&rx_bd);
+	/* setup first fd */
+	sdma_out_be32(priv->sdma, RX_NXTDESC_PTR, (u32)&rx_bd);
+
+	tx_bd.phys_buf_p = &tx_buffer[0];
+	tx_bd.next_p = &tx_bd;
+
+	flush_cache((u32)&tx_bd, sizeof(cdmac_bd));
+	sdma_out_be32(priv->sdma, TX_CURDESC_PTR, (u32)&tx_bd);
+}
+
+static int xps_ll_temac_send_sdma(struct eth_device *dev,
+				unsigned char *buffer, int length)
+{
+	struct ll_priv *priv = dev->priv;
+
+	if (xps_ll_temac_dma_error(dev)) {
+		xps_ll_temac_reset_dma(dev);
+		xps_ll_temac_bd_init(dev);
+	}
+
+	memcpy(tx_buffer, buffer, length);
+	flush_cache((u32)tx_buffer, length);
+
+	tx_bd.stat = BDSTAT_SOP_MASK | BDSTAT_EOP_MASK |
+			BDSTAT_STOP_ON_END_MASK;
+	tx_bd.buf_len = length;
+	flush_cache((u32)&tx_bd, sizeof(cdmac_bd));
+
+	sdma_out_be32(priv->sdma, TX_CURDESC_PTR, (u32)&tx_bd);
+	sdma_out_be32(priv->sdma, TX_TAILDESC_PTR, (u32)&tx_bd); /* DMA start */
+
+	do {
+		flush_cache((u32)&tx_bd, sizeof(cdmac_bd));
+	} while (!(((volatile int)tx_bd.stat) & BDSTAT_COMPLETED_MASK));
+
+	return 0;
+}
+
+static int xps_ll_temac_recv_sdma(struct eth_device *dev)
+{
+	int length;
+	struct ll_priv *priv = dev->priv;
+
+	if (xps_ll_temac_dma_error(dev)) {
+		xps_ll_temac_reset_dma(dev);
+		xps_ll_temac_bd_init(dev);
+	}
+
+	flush_cache((u32)&rx_bd, sizeof(cdmac_bd));
+
+	if (!(rx_bd.stat & BDSTAT_COMPLETED_MASK))
+		return 0;
+
+	/* Read out the packet info and start the DMA
+	   onto the second buffer to enable the ethernet rx
+	   path to run in parallel with sw processing
+	   packets.  */
+	length = rx_bd.app5 & 0x3FFF;
+	if (length > 0)
+		NetReceive(rx_bd.phys_buf_p, length);
+
+	/* flip the buffer and re-enable the DMA.  */
+	flush_cache((u32)rx_bd.phys_buf_p, length);
+
+	rx_bd.buf_len = PKTSIZE_ALIGN ;
+	rx_bd.stat = 0;
+	rx_bd.app5 = 0;
+
+	flush_cache((u32)&rx_bd, sizeof(cdmac_bd));
+	sdma_out_be32(priv->sdma, RX_TAILDESC_PTR, (u32)&rx_bd);
+
+	return length;
+}
+#endif
+
+#ifdef FIFO_MODE
+#ifdef DEBUG
+static void debugll(int count)
+{
+	printf("%d fifo isr 0x%08x, fifo_ier 0x%08x, fifo_rdfr 0x%08x, "
+		"fifo_rdfo 0x%08x fifo_rlr 0x%08x\n", count, ll_fifo->isr,
+		ll_fifo->ier, ll_fifo->rdfr, ll_fifo->rdfo, ll_fifo->rlf);
+}
+#endif
+
+static int xps_ll_temac_send_fifo(unsigned char *buffer, int length)
+{
+	u32 *buf = (u32 *)buffer;
+	u32 len, i, val;
+
+	len = (length / 4) + 1;
+
+	for (i = 0; i < len; i++) {
+		val = *buf++;
+		ll_fifo->tdfd = val;
+	}
+
+	ll_fifo->tlf = length;
+
+	return 0;
+}
+
+static int xps_ll_temac_recv_fifo(void)
+{
+	u32 len = 0;
+	u32 len2, i, val;
+	u32 *buf = (u32 *)&rx_buffer;
+
+	if (ll_fifo->isr & 0x04000000) {
+		ll_fifo->isr = 0xffffffff; /* reset isr */
+
+		/* while (ll_fifo->isr); */
+		len = ll_fifo->rlf & 0x7FF;
+		len2 = (len / 4) + 1;
+
+		for (i = 0; i < len2; i++) {
+			val = ll_fifo->rdfd;
+			*buf++ = val ;
+		}
+
+		/* debugll(1); */
+		NetReceive((uchar *)&rx_buffer, len);
+	}
+	return len;
+}
+#endif
+
+/* setup mac addr */
+static int xps_ll_temac_addr_setup(struct eth_device *dev)
+{
+	int val;
+
+	/* set up unicast MAC address filter */
+	val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) |
+		(dev->enetaddr[1] << 8) | (dev->enetaddr[0]));
+	xps_ll_temac_indirect_set(dev, 0, UAW0, val);
+	val = (dev->enetaddr[5] << 8) | dev->enetaddr[4] ;
+	xps_ll_temac_indirect_set(dev, 0, UAW1, val);
+
+	return 0;
+}
+
+static int xps_ll_temac_init(struct eth_device *dev, bd_t *bis)
+{
+#ifdef SDMA_MODE
+	xps_ll_temac_reset_dma(dev);
+	xps_ll_temac_bd_init(dev);
+#endif
+#ifdef FIFO_MODE
+	ll_fifo->tdfr = 0x000000a5; /* set fifo length */
+	ll_fifo->rdfr = 0x000000a5;
+
+	/* ll_fifo->isr = 0x0; */
+	/* ll_fifo->ier = 0x0; */
+#endif
+	xps_ll_temac_indirect_set(dev, 0, MC,
+				MDIO_ENABLE_MASK | MDIO_CLOCK_DIV_100MHz);
+
+	xps_ll_temac_addr_setup(dev);
+	/* Promiscuous mode disable */
+	xps_ll_temac_indirect_set(dev, 0, AFM, 0x00000000);
+	/* Enable Receiver */
+	xps_ll_temac_indirect_set(dev, 0, RCW1, 0x10000000);
+	/* Enable Transmitter */
+	xps_ll_temac_indirect_set(dev, 0, TC, 0x10000000);
+	return 0;
+}
+
+/* halt device */
+static void ll_temac_halt(struct eth_device *dev)
+{
+#ifdef ETH_HALTING
+	/* Disable Receiver */
+	xps_ll_temac_indirect_set(dev, 0, RCW1, 0x00000000);
+	/* Disable Transmitter */
+	xps_ll_temac_indirect_set(dev, 0, TC, 0x00000000);
+
+#ifdef SDMA_MODE
+	struct ll_priv *priv = dev->priv;
+	sdma_out_be32(priv->sdma, DMA_CONTROL_REG, 0x00000001);
+	while (sdma_in_be32(priv->sdma, DMA_CONTROL_REG) & 1)
+		;
+#endif
+#ifdef FIFO_MODE
+	/* reset fifos */
+#endif
+#endif
+}
+
+static int ll_temac_init(struct eth_device *dev, bd_t *bis)
+{
+	struct ll_priv *priv = dev->priv;
+#if DEBUG
+	int i;
+#endif
+	if (priv->initialized)
+		return 0;
+
+	xps_ll_temac_init(dev, bis);
+
+	printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n",
+		dev->name, 0, dev->iobase);
+
+#if DEBUG
+	for (i = 0; i < 32; i++)
+		read_phy_reg(dev, i);
+#endif
+
+	if (!xps_ll_temac_phy_ctrl(dev)) {
+		ll_temac_halt(dev);
+		return -1;
+	}
+
+	priv->initialized = 1;
+	return 0;
+}
+
+static int ll_temac_send(struct eth_device *dev, volatile void *packet,
+		int length)
+{
+#ifdef SDMA_MODE
+	return xps_ll_temac_send_sdma(dev, (unsigned char *)packet, length);
+#endif
+#ifdef FIFO_MODE
+	return xps_ll_temac_send_fifo((unsigned char *)packet, length);
+#endif
+}
+
+static int ll_temac_recv(struct eth_device *dev)
+{
+#ifdef SDMA_MODE
+	return xps_ll_temac_recv_sdma(dev);
+#endif
+#ifdef FIFO_MODE
+	return xps_ll_temac_recv_fifo();
+#endif
+}
+
+static int ll_temac_miiphy_read(const char *devname, uchar addr,
+							uchar reg, ushort *val)
+{
+	struct eth_device *dev = eth_get_dev();
+
+	*val = xps_ll_temac_hostif_get(dev, 0, addr, reg); /* emac = 0 */
+
+	debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, *val);
+	return 0;
+}
+
+static int ll_temac_miiphy_write(const char *devname, uchar addr,
+							uchar reg, ushort val)
+{
+	struct eth_device *dev = eth_get_dev();
+	debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, val);
+
+	xps_ll_temac_hostif_set(dev, 0, addr, reg, val);
+
+	return 0;
+}
+
+static int ll_temac_bus_reset(struct mii_dev *bus)
+{
+	debug("Just bus reset\n");
+	return 0;
+}
+
+
+int xilinx_ll_temac_initialize(bd_t *bis, int base_addr)
+{
+	struct eth_device *dev;
+	struct ll_priv *priv;
+
+	dev = calloc(1, sizeof(*dev));
+	if (dev == NULL)
+		return -1;
+
+	dev->priv = calloc(1, sizeof(struct ll_priv));
+	if (dev->priv == NULL) {
+		free(dev);
+		return -1;
+	}
+
+	priv = dev->priv;
+
+	sprintf(dev->name, "Xlltem.%x", base_addr);
+
+	dev->iobase = base_addr;
+#ifdef SDMA_MODE
+	priv->sdma = XILINX_LLTEMAC_SDMA_CTRL_BASEADDR;
+	priv->use_dcr = XILINX_LLTEMAC_SDMA_USE_DCR;
+#endif
+
+#ifdef CONFIG_PHY_ADDR
+	priv->phyaddr = CONFIG_PHY_ADDR;
+#else
+	priv->phyaddr = -1;
+#endif
+
+	dev->init = ll_temac_init;
+	dev->halt = ll_temac_halt;
+	dev->send = ll_temac_send;
+	dev->recv = ll_temac_recv;
+
+	eth_register(dev);
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
+	miiphy_register(dev->name, ll_temac_miiphy_read, ll_temac_miiphy_write);
+	priv->bus = miiphy_get_dev_by_name(dev->name);
+
+	priv->bus->reset = ll_temac_bus_reset;
+#endif
+	return 1;
+}
diff --git a/include/netdev.h b/include/netdev.h
index 5abafbc..a215e0f 100644
--- a/include/netdev.h
+++ b/include/netdev.h
@@ -91,6 +91,7 @@  int tsi108_eth_initialize(bd_t *bis);
 int uec_standard_init(bd_t *bis);
 int uli526x_initialize(bd_t *bis);
 int xilinx_emaclite_initialize (bd_t *bis, int base_addr, int txpp, int rxpp);
+int xilinx_ll_temac_initialize(bd_t *bis, int base_addr);
 int sh_eth_initialize(bd_t *bis);
 int dm9000_initialize(bd_t *bis);
 int fecmxc_initialize(bd_t *bis);