diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2010-12-10 15:17:00 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2010-12-10 15:17:00 +0000 |
commit | a084fb6a3f54905f5b3b76fcae6d17f88c857da2 (patch) | |
tree | d3580e98458fcd89474451f420ab5d25f0047cd6 /package/uboot-ar71xx/files/drivers/net/ag71xx.c | |
parent | 339295d309219f502cde34e01ee8f1586050fecb (diff) | |
download | master-31e0f0ae-a084fb6a3f54905f5b3b76fcae6d17f88c857da2.tar.gz master-31e0f0ae-a084fb6a3f54905f5b3b76fcae6d17f88c857da2.tar.bz2 master-31e0f0ae-a084fb6a3f54905f5b3b76fcae6d17f88c857da2.zip |
ar71xx: adding u-boot for nbg460n
This adds u-boot for nbg460n ar71xx target,
as it is required as second stage bootloader.
Signed-off-by: Michael Kurz <michi.kurz@googlemail.com>
SVN-Revision: 24418
Diffstat (limited to 'package/uboot-ar71xx/files/drivers/net/ag71xx.c')
-rw-r--r-- | package/uboot-ar71xx/files/drivers/net/ag71xx.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/package/uboot-ar71xx/files/drivers/net/ag71xx.c b/package/uboot-ar71xx/files/drivers/net/ag71xx.c new file mode 100644 index 0000000000..b3324c0197 --- /dev/null +++ b/package/uboot-ar71xx/files/drivers/net/ag71xx.c @@ -0,0 +1,809 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2010 Michael Kurz <michi.kurz@googlemail.com> + * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Based on Atheros' AG7100 driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> + +#include <asm/ar71xx.h> + +#include "ag71xx.h" + +#ifdef AG71XX_DEBUG +#define DBG(fmt,args...) printf(fmt ,##args) +#else +#define DBG(fmt,args...) +#endif + + +static struct ag71xx agtable[] = { + { + .mac_base = KSEG1ADDR(AR71XX_GE0_BASE), + .mii_ctrl = KSEG1ADDR(AR71XX_MII_BASE + MII_REG_MII0_CTRL), + .mii_if = CONFIG_AG71XX_MII0_IIF, + } , { + .mac_base = KSEG1ADDR(AR71XX_GE1_BASE), + .mii_ctrl = KSEG1ADDR(AR71XX_MII_BASE + MII_REG_MII1_CTRL), + .mii_if = CONFIG_AG71XX_MII1_IIF, + } +}; + +static int ag71xx_ring_alloc(struct ag71xx_ring *ring, unsigned int size) +{ + int err; + int i; + int rsize; + + ring->desc_size = sizeof(struct ag71xx_desc); + if (ring->desc_size % (CONFIG_SYS_CACHELINE_SIZE)) { + rsize = roundup(ring->desc_size, CONFIG_SYS_CACHELINE_SIZE); + DBG("ag71xx: ring %p, desc size %u rounded to %u\n", + ring, ring->desc_size, + rsize); + ring->desc_size = rsize; + } + + ring->descs_cpu = (u8 *) malloc((size * ring->desc_size) + + CONFIG_SYS_CACHELINE_SIZE - 1); + if (!ring->descs_cpu) { + err = -1; + goto err; + } + ring->descs_cpu = (u8 *) UNCACHED_SDRAM((((u32) ring->descs_cpu + + CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1))); + ring->descs_dma = (u8 *) virt_to_phys(ring->descs_cpu); + + ring->size = size; + + ring->buf = malloc(size * sizeof(*ring->buf)); + if (!ring->buf) { + err = -1; + goto err; + } + memset(ring->buf, 0, size * sizeof(*ring->buf)); + + for (i = 0; i < size; i++) { + ring->buf[i].desc = + (struct ag71xx_desc *)&ring->descs_cpu[i * ring->desc_size]; + DBG("ag71xx: ring %p, desc %d at %p\n", + ring, i, ring->buf[i].desc); + } + + flush_cache( (u32) ring->buf, size * sizeof(*ring->buf)); + + return 0; + + err: + return err; +} + +static void ag71xx_ring_tx_init(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->tx_ring; + int i; + + for (i = 0; i < AG71XX_TX_RING_SIZE; i++) { + ring->buf[i].desc->next = (u32) virt_to_phys((ring->descs_dma + + ring->desc_size * ((i + 1) % AG71XX_TX_RING_SIZE))); + + ring->buf[i].desc->ctrl = DESC_EMPTY; + ring->buf[i].skb = NULL; + } + + ring->curr = 0; +} + +static void ag71xx_ring_rx_clean(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + int i; + + if (!ring->buf) + return; + + for (i = 0; i < AG71XX_RX_RING_SIZE; i++) { + ring->buf[i].desc->data = (u32) virt_to_phys(NetRxPackets[i]); + flush_cache((u32) NetRxPackets[i], PKTSIZE_ALIGN); + ring->buf[i].desc->ctrl = DESC_EMPTY; + } + + ring->curr = 0; +} + +static int ag71xx_ring_rx_init(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + unsigned int i; + + for (i = 0; i < AG71XX_RX_RING_SIZE; i++) { + ring->buf[i].desc->next = (u32) virt_to_phys((ring->descs_dma + + ring->desc_size * ((i + 1) % AG71XX_RX_RING_SIZE))); + + DBG("ag71xx: RX desc at %p, next is %08x\n", + ring->buf[i].desc, + ring->buf[i].desc->next); + } + + for (i = 0; i < AG71XX_RX_RING_SIZE; i++) { + ring->buf[i].desc->data = (u32) virt_to_phys(NetRxPackets[i]); + ring->buf[i].desc->ctrl = DESC_EMPTY; + } + + ring->curr = 0; + + return 0; +} + +static int ag71xx_rings_init(struct ag71xx *ag) +{ + int ret; + + ret = ag71xx_ring_alloc(&ag->tx_ring, AG71XX_TX_RING_SIZE); + if (ret) + return ret; + + ag71xx_ring_tx_init(ag); + + ret = ag71xx_ring_alloc(&ag->rx_ring, AG71XX_RX_RING_SIZE); + if (ret) + return ret; + + ret = ag71xx_ring_rx_init(ag); + return ret; +} + +static void ar71xx_set_pll(u32 cfg_reg, u32 pll_reg, u32 pll_val, u32 shift) +{ + uint32_t base = KSEG1ADDR(AR71XX_PLL_BASE); + u32 t; + + t = readl(base + cfg_reg); + t &= ~(3 << shift); + t |= (2 << shift); + writel(t, base + cfg_reg); + udelay(100); + + writel(pll_val, base + pll_reg); + + t |= (3 << shift); + writel(t, base + cfg_reg); + udelay(100); + + t &= ~(3 << shift); + writel(t, base + cfg_reg); + udelay(100); + + debug("ar71xx: pll_reg %#x: %#x\n", (unsigned int)(base + pll_reg), + readl(base + pll_reg)); +} + +static void ar91xx_set_pll_ge0(int speed) +{ + //u32 val = ar71xx_get_eth_pll(0, speed); + u32 pll_val; + + switch (speed) { + case SPEED_10: + pll_val = 0x00441099; + break; + case SPEED_100: + pll_val = 0x13000a44; + break; + case SPEED_1000: + pll_val = 0x1a000000; + break; + default: + BUG(); + } + + ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH0_INT_CLOCK, + pll_val, AR91XX_ETH0_PLL_SHIFT); +} + +static void ar91xx_set_pll_ge1(int speed) +{ + //u32 val = ar71xx_get_eth_pll(1, speed); + u32 pll_val; + + switch (speed) { + case SPEED_10: + pll_val = 0x00441099; + break; + case SPEED_100: + pll_val = 0x13000a44; + break; + case SPEED_1000: + pll_val = 0x1a000000; + break; + default: + BUG(); + } + + ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH1_INT_CLOCK, + pll_val, AR91XX_ETH1_PLL_SHIFT); +} + +static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) +{ + u32 t; + + t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16) + | (((u32) mac[3]) << 8) | ((u32) mac[2]); + + ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); + + t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16); + ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); +} + +static void ag71xx_dma_reset(struct ag71xx *ag) +{ + u32 val; + int i; + + DBG("%s: txdesc reg: 0x%08x rxdesc reg: 0x%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_TX_DESC), + ag71xx_rr(ag, AG71XX_REG_RX_DESC)); + + /* stop RX and TX */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); + + /* clear descriptor addresses */ + ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0); + + /* clear pending RX/TX interrupts */ + for (i = 0; i < 256; i++) { + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); + } + + /* clear pending errors */ + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); + + val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); + if (val) + printf("%s: unable to clear DMA Rx status: %08x\n", + ag->dev->name, val); + + val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); + + /* mask out reserved bits */ + val &= ~0xff000000; + + if (val) + printf("%s: unable to clear DMA Tx status: %08x\n", + ag->dev->name, val); +} + +static void ag71xx_halt(struct eth_device *dev) +{ + struct ag71xx *ag = (struct ag71xx *) dev->priv; + + /* stop RX engine */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); + + ag71xx_dma_reset(ag); +} + +#define MAX_WAIT 1000 + +static int ag71xx_send(struct eth_device *dev, volatile void *packet, + int length) +{ + struct ag71xx *ag = (struct ag71xx *) dev->priv; + struct ag71xx_ring *ring = &ag->tx_ring; + struct ag71xx_desc *desc; + int i; + + i = ring->curr % AG71XX_TX_RING_SIZE; + desc = ring->buf[i].desc; + + if (!ag71xx_desc_empty(desc)) { + printf("%s: tx buffer full\n", ag->dev->name); + return 1; + } + + flush_cache((u32) packet, length); + desc->data = (u32) virt_to_phys(packet); + desc->ctrl = (length & DESC_PKTLEN_M); + + DBG("%s: sending %#08x length %#08x\n", + ag->dev->name, desc->data, desc->ctrl); + + ring->curr++; + if (ring->curr >= AG71XX_TX_RING_SIZE){ + ring->curr = 0; + } + + /* enable TX engine */ + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); + + for (i = 0; i < MAX_WAIT; i++) + { + if (ag71xx_desc_empty(desc)) + break; + udelay(10); + } + if (i == MAX_WAIT) { + printf("%s: tx timed out!\n", ag->dev->name); + return -1; + } + + /* disable TX engine */ + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); + desc->data = 0; + desc->ctrl = DESC_EMPTY; + + return 0; +} + +static int ag71xx_recv(struct eth_device *dev) +{ + struct ag71xx *ag = (struct ag71xx *) dev->priv; + struct ag71xx_ring *ring = &ag->rx_ring; + + for (;;) { + unsigned int i = ring->curr % AG71XX_RX_RING_SIZE; + struct ag71xx_desc *desc = ring->buf[i].desc; + int pktlen; + + if (ag71xx_desc_empty(desc)) + break; + + DBG("%s: rx packets, curr=%u\n", dev->name, ring->curr); + + pktlen = ag71xx_desc_pktlen(desc); + pktlen -= ETH_FCS_LEN; + + + NetReceive(NetRxPackets[i] , pktlen); + flush_cache( (u32) NetRxPackets[i], PKTSIZE_ALIGN); + + ring->buf[i].desc->ctrl = DESC_EMPTY; + ring->curr++; + if (ring->curr >= AG71XX_RX_RING_SIZE){ + ring->curr = 0; + } + + } + + if ((ag71xx_rr(ag, AG71XX_REG_RX_CTRL) & RX_CTRL_RXE) == 0) { + /* start RX engine */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + } + + return 0; +} + +#ifdef AG71XX_DEBUG +static char *ag71xx_speed_str(struct ag71xx *ag) +{ + switch (ag->speed) { + case SPEED_1000: + return "1000"; + case SPEED_100: + return "100"; + case SPEED_10: + return "10"; + } + + return "?"; +} +#endif + +void ag71xx_link_adjust(struct ag71xx *ag) +{ + u32 cfg2; + u32 ifctl; + u32 fifo5; + u32 mii_speed; + + if (!ag->link) { + DBG("%s: link down\n", ag->dev->name); + return; + } + + cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); + cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); + cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; + + ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); + ifctl &= ~(MAC_IFCTL_SPEED); + + fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); + fifo5 &= ~FIFO_CFG5_BM; + + switch (ag->speed) { + case SPEED_1000: + mii_speed = MII_CTRL_SPEED_1000; + cfg2 |= MAC_CFG2_IF_1000; + fifo5 |= FIFO_CFG5_BM; + break; + case SPEED_100: + mii_speed = MII_CTRL_SPEED_100; + cfg2 |= MAC_CFG2_IF_10_100; + ifctl |= MAC_IFCTL_SPEED; + break; + case SPEED_10: + mii_speed = MII_CTRL_SPEED_10; + cfg2 |= MAC_CFG2_IF_10_100; + break; + default: + BUG(); + return; + } + + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x00780fff); + + if (ag->macNum == 0) + ar91xx_set_pll_ge0(ag->speed); + else + ar91xx_set_pll_ge1(ag->speed); + + ag71xx_mii_ctrl_set_speed(ag, mii_speed); + + ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); + ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); + + DBG("%s: link up (%sMbps/%s duplex)\n", + ag->dev->name, + ag71xx_speed_str(ag), + (1 == ag->duplex) ? "Full" : "Half"); + + DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); + + DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); + + DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x, mii_ctrl=%#x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), + ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), + ag71xx_mii_ctrl_rr(ag)); +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static int ag71xx_getMiiSpeed(struct ag71xx *ag) +{ + uint16_t phyreg, cap; + + if (miiphy_read(ag->phyname, ag->phyid, + PHY_BMSR, &phyreg)) { + puts("PHY_BMSR read failed, assuming no link\n"); + return -1; + } + + if ((phyreg & PHY_BMSR_LS) == 0) { + return -1; + } + + if (miiphy_read(ag->phyname, ag->phyid, + PHY_1000BTSR, &phyreg)) + return -1; + + if (phyreg & PHY_1000BTSR_1000FD) { + ag->speed = SPEED_1000; + ag->duplex = 1; + } else if (phyreg & PHY_1000BTSR_1000HD) { + ag->speed = SPEED_1000; + ag->duplex = 0; + } else { + if (miiphy_read(ag->phyname, ag->phyid, + PHY_ANAR, &cap)) + return -1; + + if (miiphy_read(ag->phyname, ag->phyid, + PHY_ANLPAR, &phyreg)) + return -1; + + cap &= phyreg; + if (cap & PHY_ANLPAR_TXFD) { + ag->speed = SPEED_100; + ag->duplex = 1; + } else if (cap & PHY_ANLPAR_TX) { + ag->speed = SPEED_100; + ag->duplex = 0; + } else if (cap & PHY_ANLPAR_10FD) { + ag->speed = SPEED_10; + ag->duplex = 1; + } else { + ag->speed = SPEED_10; + ag->duplex = 0; + } + } + + ag->link = 1; + + return 0; +} +#endif + +static int ag71xx_hw_start(struct eth_device *dev, bd_t * bd) +{ + struct ag71xx *ag = (struct ag71xx *) dev->priv; + + ag71xx_dma_reset(ag); + + ag71xx_ring_rx_clean(ag); + ag71xx_ring_tx_init(ag); + + ag71xx_wr(ag, AG71XX_REG_TX_DESC, + (u32) virt_to_phys(ag->tx_ring.descs_dma)); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, + (u32) virt_to_phys(ag->rx_ring.descs_dma)); + + ag71xx_hw_set_macaddr(ag, ag->dev->enetaddr); + + if (ag->phyfixed) { + ag->link = 1; + ag->duplex = 1; + ag->speed = SPEED_1000; + } else { + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) + if (ag71xx_getMiiSpeed(ag)) + return -1; +#else + /* only fixed, without mii */ + return -1; +#endif + + } + ag71xx_link_adjust(ag); + + DBG("%s: txdesc reg: %#08x rxdesc reg: %#08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_TX_DESC), + ag71xx_rr(ag, AG71XX_REG_RX_DESC)); + + /* start RX engine */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + + return 0; +} + +#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) + +#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ + FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ + FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ + FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ + FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ + FIFO_CFG4_VT) + +#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ + FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ + FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ + FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ + FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ + FIFO_CFG5_17 | FIFO_CFG5_SF) + +static int ag71xx_hw_init(struct ag71xx *ag) +{ + int ret = 0; + uint32_t reg; + uint32_t mask, mii_type; + + if (ag->macNum == 0) { + mask = (RESET_MODULE_GE0_MAC | RESET_MODULE_GE0_PHY); + mii_type = 0x13; + } else { + mask = (RESET_MODULE_GE1_MAC | RESET_MODULE_GE1_PHY); + mii_type = 0x11; + } + + // mac soft reset + ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); + udelay(20); + + // device stop + reg = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE); + ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, reg | mask); + udelay(100 * 1000); + + // device start + reg = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE); + ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, reg & ~mask); + udelay(100 * 1000); + + /* setup MAC configuration registers */ + ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, (MAC_CFG1_RXE | MAC_CFG1_TXE)); + + ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, + MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); + + /* setup FIFO configuration register 0 */ + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); + + /* setup MII interface type */ + ag71xx_mii_ctrl_set_if(ag, ag->mii_if); + + /* setup mdio clock divisor */ + ag71xx_wr(ag, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_20); + + /* setup FIFO configuration registers */ + ag71xx_sb(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); + + ag71xx_dma_reset(ag); + + ret = ag71xx_rings_init(ag); + if (ret) + return -1; + + ag71xx_wr(ag, AG71XX_REG_TX_DESC, + (u32) virt_to_phys(ag->tx_ring.descs_dma)); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, + (u32) virt_to_phys(ag->rx_ring.descs_dma)); + + ag71xx_hw_set_macaddr(ag, ag->dev->enetaddr); + + return 0; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#define AG71XX_MDIO_RETRY 1000 +#define AG71XX_MDIO_DELAY 5 + +static inline struct ag71xx *ag71xx_name2mac(char *devname) +{ + if (strcmp(devname, agtable[0].dev->name) == 0) + return &agtable[0]; + else if (strcmp(devname, agtable[1].dev->name) == 0) + return &agtable[1]; + else + return NULL; +} + +static inline void ag71xx_mdio_wr(struct ag71xx *ag, unsigned reg, + u32 value) +{ + uint32_t r; + + r = ag->mac_base + reg; + writel(value, r); + + /* flush write */ + (void) readl(r); +} + +static inline u32 ag71xx_mdio_rr(struct ag71xx *ag, unsigned reg) +{ + return readl(ag->mac_base + reg); +} + +static int ag71xx_mdio_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *val) +{ + struct ag71xx *ag = ag71xx_name2mac(devname); + uint16_t regData; + int i; + + ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + ag71xx_mdio_wr(ag, AG71XX_REG_MII_ADDR, + ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); + ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_READ); + + i = AG71XX_MDIO_RETRY; + while (ag71xx_mdio_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) { + if (i-- == 0) { + printf("%s: mii_read timed out\n", + ag->dev->name); + return -1; + } + udelay(AG71XX_MDIO_DELAY); + } + + regData = (uint16_t) ag71xx_mdio_rr(ag, AG71XX_REG_MII_STATUS) & 0xffff; + ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + + DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, regData); + + if (val) + *val = regData; + + return 0; +} + +static int ag71xx_mdio_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short val) +{ + struct ag71xx *ag = ag71xx_name2mac(devname); + int i; + + if (ag == NULL) + return 1; + + DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); + + ag71xx_mdio_wr(ag, AG71XX_REG_MII_ADDR, + ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); + ag71xx_mdio_wr(ag, AG71XX_REG_MII_CTRL, val); + + i = AG71XX_MDIO_RETRY; + while (ag71xx_mdio_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) { + if (i-- == 0) { + printf("%s: mii_write timed out\n", + ag->dev->name); + break; + } + udelay(AG71XX_MDIO_DELAY); + } + + return 0; +} +#endif + +int ag71xx_register(bd_t * bis, char *phyname[], uint16_t phyid[], uint16_t phyfixed[]) +{ + int i, num = 0; + u8 used_ports[MAX_AG71XX_DEVS] = CONFIG_AG71XX_PORTS; + + for (i = 0; i < MAX_AG71XX_DEVS; i++) { + /*skip if port is configured not to use */ + if (used_ports[i] == 0) + continue; + + agtable[i].dev = malloc(sizeof(struct eth_device)); + if (agtable[i].dev == NULL) { + puts("malloc failed\n"); + return 0; + } + memset(agtable[i].dev, 0, sizeof(struct eth_device)); + sprintf(agtable[i].dev->name, "eth%d", i); + + agtable[i].dev->iobase = 0; + agtable[i].dev->init = ag71xx_hw_start; + agtable[i].dev->halt = ag71xx_halt; + agtable[i].dev->send = ag71xx_send; + agtable[i].dev->recv = ag71xx_recv; + agtable[i].dev->priv = (void *) (&agtable[i]); + agtable[i].macNum = i; + eth_register(agtable[i].dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + + if ((phyname == NULL) || (phyid == NULL) || (phyfixed == NULL)) + return -1; + + agtable[i].phyname = strdup(phyname[i]); + agtable[i].phyid = phyid[i]; + agtable[i].phyfixed = phyfixed[i]; + + miiphy_register(agtable[i].dev->name, ag71xx_mdio_read, + ag71xx_mdio_write); +#endif + + if (ag71xx_hw_init(&agtable[i])) + continue; + + num++; + } + + return num; +} |