From patchwork Thu Sep 1 11:39:13 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 112891 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id BDBE9B6F81 for ; Thu, 1 Sep 2011 21:39:25 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6AB9C28118; Thu, 1 Sep 2011 13:39:24 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UDIZ20IChp8x; Thu, 1 Sep 2011 13:39:24 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id A99BE2814E; Thu, 1 Sep 2011 13:39:21 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 05C3628165 for ; Thu, 1 Sep 2011 13:39:20 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1qITrsWcMmD3 for ; Thu, 1 Sep 2011 13:39:18 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-bw0-f44.google.com (mail-bw0-f44.google.com [209.85.214.44]) by theia.denx.de (Postfix) with ESMTPS id 124AD28118 for ; Thu, 1 Sep 2011 13:39:17 +0200 (CEST) Received: by bkar4 with SMTP id r4so1511039bka.3 for ; Thu, 01 Sep 2011 04:39:16 -0700 (PDT) Received: by 10.204.152.83 with SMTP id f19mr910313bkw.367.1314877156389; Thu, 01 Sep 2011 04:39:16 -0700 (PDT) Received: from localhost ([178.23.216.97]) by mx.google.com with ESMTPS id m18sm162579bkt.18.2011.09.01.04.39.15 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 01 Sep 2011 04:39:15 -0700 (PDT) From: Michal Simek To: u-boot@lists.denx.de Date: Thu, 1 Sep 2011 13:39:13 +0200 Message-Id: <1314877154-14536-1-git-send-email-monstr@monstr.eu> X-Mailer: git-send-email 1.5.5.1 Subject: [U-Boot] [PATCH v3] net: ll_temac: Add LL TEMAC driver to u-boot X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- v2: Remove helper function for access to temac Remove SDMA/FIFO/DCR macros and configure it in board Setup mac by write_hwaddr v3: Use helper functions for fifo mode Use helper functions for indirect accesses Code cleanup Add comments for MAGIC values Simplify code in fifo mode --- drivers/net/Makefile | 1 + drivers/net/xilinx_ll_temac.c | 666 +++++++++++++++++++++++++++++++++++++++++ include/netdev.h | 2 + 3 files changed, 669 insertions(+), 0 deletions(-) create mode 100644 drivers/net/xilinx_ll_temac.c 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..1639363 --- /dev/null +++ b/drivers/net/xilinx_ll_temac.c @@ -0,0 +1,666 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#undef ETH_HALTING + +#if !defined(CONFIG_PHYLIB) +# error LL_TEMAC requires PHYLIB +#endif + +#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 */ + +#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 + +/* 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 + +/* DMA control bit */ +#define DMA_CONTROL_RESET 0x1 + +/* 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 + +#define LL_FIFO_ISR_RC_COMPLETE 0x04000000 + +#define SDMA_BIT 1 +#define DCR_BIT 2 + +#define DMAALIGN 32 + +/* SDMA Buffer Descriptor */ +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; +}; + +static struct cdmac_bd_t tx_bd __attribute((aligned(DMAALIGN))); +static struct cdmac_bd_t rx_bd __attribute((aligned(DMAALIGN))); + +struct ll_fifo_s { + u32 isr; /* Interrupt Status Register 0x0 */ + u32 ier; /* Interrupt Enable Register 0x4 */ + u32 tdfr; /* Transmit data FIFO reset 0x8 */ + u32 tdfv; /* Transmit data FIFO Vacancy 0xC */ + u32 tdfd; /* Transmit data FIFO 32bit wide data write port 0x10 */ + u32 tlf; /* Write Transmit Length FIFO 0x14 */ + u32 rdfr; /* Read Receive data FIFO reset 0x18 */ + u32 rdfo; /* Receive data FIFO Occupancy 0x1C */ + u32 rdfd; /* Read Receive data FIFO 32bit wide data read port 0x20 */ + u32 rlf; /* Read Receive Length FIFO 0x24 */ + u32 llr; /* Read LocalLink reset 0x28 */ +}; + +static u8 tx_buffer[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN))); +static u8 rx_buffer[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN))); + +struct temac_reg { + u32 reserved[8]; + u32 msw; /* Hard TEMAC MSW Data Register */ + u32 lsw; /* Hard TEMAC LSW Data Register */ + u32 ctl; /* Hard TEMAC Control Register */ + u32 rdy; /* Hard TEMAC Ready Status */ +}; + +struct ll_priv { + u32 ctrl; + u32 mode; + int phyaddr; + + struct phy_device *phydev; + struct mii_dev *bus; +}; + +#define XILINX_INDIRECT_DCR_ADDRESS_REG 0 +#define XILINX_INDIRECT_DCR_ACCESS_REG 1 + +static void mtdcr_local(u32 reg, u32 val) +{ +#if defined(CONFIG_PPC) + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, reg); + mtdcr(XILINX_INDIRECT_DCR_ACCESS_REG, val); +#endif +} + +static u32 mfdcr_local(u32 reg) +{ + u32 val = 0; +#if defined(CONFIG_PPC) + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, reg); + val = mfdcr(XILINX_INDIRECT_DCR_ACCESS_REG); +#endif + return val; +} + +static void sdma_out_be32(struct ll_priv *priv, u32 offset, u32 val) +{ + if (priv->mode & DCR_BIT) + mtdcr_local(priv->ctrl + offset, val); + else + out_be32((u32 *)(priv->ctrl + offset * 4), val); +} + +static u32 sdma_in_be32(struct ll_priv *priv, u32 offset) +{ + if (priv->mode & DCR_BIT) + return mfdcr_local(priv->ctrl + offset); + + return in_be32((u32 *)(priv->ctrl + offset * 4)); +} + +static void xps_ll_temac_check_status(struct temac_reg *regs, int mask) +{ + u32 timeout = 2000; + while (timeout && (!(in_be32(®s->rdy) & mask))) + timeout--; + + if (!timeout) + printf("%s: Timeout\n", __func__); +} + +#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) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + out_be32(®s->lsw, phy_data); + out_be32(®s->ctl, CNTLREG_WRITE_ENABLE_MASK | MIIMWD); + out_be32(®s->lsw, (phy_addr << 5) | reg_addr); + out_be32(®s->ctl, CNTLREG_WRITE_ENABLE_MASK | MIIMAI | (emac << 10)); + xps_ll_temac_check_status(regs, 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) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + out_be32(®s->lsw, (phy_addr << 5) | reg_addr); + out_be32(®s->ctl, MIIMAI | (emac << 10)); + xps_ll_temac_check_status(regs, XTE_RSE_MIIM_RR_MASK); + return in_be32(®s->lsw); +} + +/* undirect write to ll_temac */ +static void xps_ll_temac_indirect_set(struct eth_device *dev, + int emac, int reg_offset, int reg_data) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + out_be32(®s->lsw, reg_data); + out_be32(®s->ctl, + CNTLREG_WRITE_ENABLE_MASK | (emac << 10) | reg_offset); + xps_ll_temac_check_status(regs, 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) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + out_be32(®s->ctl, (emac << 10) | reg_offset); + xps_ll_temac_check_status(regs, XTE_RSE_CFG_RR_MASK); + return in_be32(®s->lsw); +} + +#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) +{ + 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); + temp &= ~XTE_EMMC_LINKSPEED_MASK; + temp |= speed; + xps_ll_temac_indirect_set(dev, 0, EMMC, temp); + + return 1; +} + +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, TX_CHNL_STS) & CHNL_STS_ERROR_MASK; + err |= sdma_in_be32(priv, 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, DMA_CONTROL_REG, DMA_CONTROL_RESET); + while (sdma_in_be32(priv, DMA_CONTROL_REG) & DMA_CONTROL_RESET) + ; + + /* Now clear the interrupts. */ + r = sdma_in_be32(priv, TX_CHNL_CTRL); + r &= ~XLLDMA_CR_IRQ_ALL_EN_MASK; + sdma_out_be32(priv, TX_CHNL_CTRL, r); + + r = sdma_in_be32(priv, RX_CHNL_CTRL); + r &= ~XLLDMA_CR_IRQ_ALL_EN_MASK; + sdma_out_be32(priv, RX_CHNL_CTRL, r); + + /* Now ACK pending IRQs. */ + sdma_out_be32(priv, TX_IRQ_REG, XLLDMA_IRQ_ALL_MASK); + sdma_out_be32(priv, RX_IRQ_REG, XLLDMA_IRQ_ALL_MASK); + + /* Set tail-ptr mode, disable errors for both channels. */ + sdma_out_be32(priv, 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(&tx_bd, 0, sizeof(tx_bd)); + memset(&rx_bd, 0, sizeof(rx_bd)); + + rx_bd.phys_buf_p = rx_buffer; + + rx_bd.next_p = &rx_bd; + rx_bd.buf_len = PKTSIZE_ALIGN; + flush_cache((u32)&rx_bd, sizeof(tx_bd)); + flush_cache((u32)rx_bd.phys_buf_p, PKTSIZE_ALIGN); + + sdma_out_be32(priv, RX_CURDESC_PTR, (u32)&rx_bd); + sdma_out_be32(priv, RX_TAILDESC_PTR, (u32)&rx_bd); + sdma_out_be32(priv, RX_NXTDESC_PTR, (u32)&rx_bd); /* setup first fd */ + + tx_bd.phys_buf_p = tx_buffer; + tx_bd.next_p = &tx_bd; + + flush_cache((u32)&tx_bd, sizeof(tx_bd)); + sdma_out_be32(priv, TX_CURDESC_PTR, (u32)&tx_bd); +} + +static int ll_temac_send_sdma(struct eth_device *dev, + volatile void *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, (void *)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(tx_bd)); + + sdma_out_be32(priv, TX_CURDESC_PTR, (u32)&tx_bd); + sdma_out_be32(priv, TX_TAILDESC_PTR, (u32)&tx_bd); /* DMA start */ + + do { + flush_cache((u32)&tx_bd, sizeof(tx_bd)); + } while (!(tx_bd.stat & BDSTAT_COMPLETED_MASK)); + + return 0; +} + +static int 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(rx_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; /* max length mask */ + 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(rx_bd)); + sdma_out_be32(priv, RX_TAILDESC_PTR, (u32)&rx_bd); + + return length; +} + +#ifdef DEBUG +static void debugll(struct eth_device *dev, int count) +{ + struct ll_priv *priv = dev->priv; + struct ll_fifo_s *ll_fifo = (void *)priv->ctrl; + printf("%d fifo isr 0x%08x, fifo_ier 0x%08x, fifo_rdfr 0x%08x, " + "fifo_rdfo 0x%08x fifo_rlr 0x%08x\n", count, + in_be32(&ll_fifo->isr), in_be32(&ll_fifo->ier), + in_be32(&ll_fifo->rdfr), in_be32(&ll_fifo->rdfo), + in_be32(&ll_fifo->rlf)); +} +#endif + +static int ll_temac_send_fifo(struct eth_device *dev, + volatile void *buffer, int length) +{ + struct ll_priv *priv = dev->priv; + struct ll_fifo_s *ll_fifo = (void *)priv->ctrl; + u32 *buf = (u32 *)buffer; + u32 i; + + for (i = 0; i < length; i += 4) + out_be32(&ll_fifo->tdfd, *buf++); + + out_be32(&ll_fifo->tlf, length); + return 0; +} + +static int ll_temac_recv_fifo(struct eth_device *dev) +{ + struct ll_priv *priv = dev->priv; + struct ll_fifo_s *ll_fifo = (void *)priv->ctrl; + u32 i, len = 0; + u32 *buf = (u32 *)&rx_buffer; + + if (in_be32(&ll_fifo->isr) & LL_FIFO_ISR_RC_COMPLETE) { + out_be32(&ll_fifo->isr, 0xffffffff); /* reset isr */ + + /* while (ll_fifo->isr); */ + len = in_be32(&ll_fifo->rlf) & 0x7FF; + + for (i = 0; i < len; i += 4) + *buf++ = in_be32(&ll_fifo->rdfd); + +#ifdef DEBUG + debugll(dev, 1); +#endif + NetReceive((uchar *)&rx_buffer, len); + } + return len; +} + +/* setup mac addr */ +static int 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) +{ + struct ll_priv *priv = dev->priv; + struct ll_fifo_s *ll_fifo = (void *)priv->ctrl; + + if (priv->mode & SDMA_BIT) { + xps_ll_temac_reset_dma(dev); + xps_ll_temac_bd_init(dev); + } else { + out_be32(&ll_fifo->tdfr, 0x000000a5); /* Fifo reset key */ + out_be32(&ll_fifo->rdfr, 0x000000a5); /* Fifo reset key */ + out_be32(&ll_fifo->isr, 0xFFFFFFFF); /* Reset status register */ + out_be32(&ll_fifo->ier, 0); /* Disable all IRQs */ + } + + xps_ll_temac_indirect_set(dev, 0, MC, + MDIO_ENABLE_MASK | MDIO_CLOCK_DIV_100MHz); + + /* Promiscuous mode disable */ + xps_ll_temac_indirect_set(dev, 0, AFM, 0); + /* Enable Receiver - RX bit */ + xps_ll_temac_indirect_set(dev, 0, RCW1, 0x10000000); + /* Enable Transmitter - TX bit */ + 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 + struct ll_priv *priv = dev->priv; + /* Disable Receiver */ + xps_ll_temac_indirect_set(dev, 0, RCW1, 0); + /* Disable Transmitter */ + xps_ll_temac_indirect_set(dev, 0, TC, 0); + + if (priv->mode & SDMA_BIT) { + sdma_out_be32(priv->ctrl, DMA_CONTROL_REG, DMA_CONTROL_RESET); + while (sdma_in_be32(priv->ctrl, DMA_CONTROL_REG) + & DMA_CONTROL_RESET) + ; + } +#endif +} + +static int ll_temac_init(struct eth_device *dev, bd_t *bis) +{ +#if DEBUG + int i; +#endif + 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; + } + + return 0; +} + +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; +} + +/* mode bits: 0bit - fifo(0)/sdma(1):SDMA_BIT, 1bit - no dcr(0)/dcr(1):DCR_BIT + * ctrl - control address for file/sdma */ +int xilinx_ll_temac_initialize(bd_t *bis, unsigned long base_addr, + int mode, unsigned long ctrl) +{ + 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.%lx", base_addr); + + dev->iobase = base_addr; + priv->ctrl = ctrl; + priv->mode = mode; + +#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->write_hwaddr = ll_temac_addr_setup; + + if (priv->mode & SDMA_BIT) { + dev->send = ll_temac_send_sdma; + dev->recv = ll_temac_recv_sdma; + } else { + dev->send = ll_temac_send_fifo; + dev->recv = ll_temac_recv_fifo; + } + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + 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 26ce13d..e976659 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -92,6 +92,8 @@ int uec_standard_init(bd_t *bis); int uli526x_initialize(bd_t *bis); int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, int txpp, int rxpp); +int xilinx_ll_temac_initialize(bd_t *bis, unsigned long base_addr, + int mode, unsigned long ctrl); int sh_eth_initialize(bd_t *bis); int dm9000_initialize(bd_t *bis); int fecmxc_initialize(bd_t *bis);