1059 lines
28 KiB
C
1059 lines
28 KiB
C
/*
|
|
* board/ums/arasan-emac-ahb.c
|
|
*
|
|
* U-Boot driver for the Arasan EMAC-AHB Gigabit Ethernet controller.
|
|
*
|
|
* Copyright (c) Quantenna Communications Incorporated 2007.
|
|
* All rights reserved.
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <net.h>
|
|
#include <asm/io.h>
|
|
#include <command.h>
|
|
#include <asm/cache.h>
|
|
|
|
#ifdef CONFIG_ARASAN_GBE
|
|
|
|
#include <asm/arch/platform.h>
|
|
#include "ar8236.h"
|
|
#include "ar8237.h"
|
|
#include "ruby_board_cfg.h"
|
|
#include "board_cfg.h"
|
|
#include <asm/arch/arasan_emac_ahb.h>
|
|
#include "ruby.h"
|
|
|
|
#ifdef UBOOT_ENABLE_ETHERNET_DEBUG
|
|
#define DPRINTF(x...) printf(x)
|
|
#else
|
|
#define DPRINTF(x...)
|
|
#endif
|
|
|
|
static inline unsigned long emac_align_begin(unsigned long addr)
|
|
{
|
|
return (addr & (~(ARC_DCACHE_LINE_LEN - 1)));
|
|
}
|
|
|
|
static inline unsigned long emac_align_end(unsigned long addr)
|
|
{
|
|
unsigned long aligned = (addr & (~(ARC_DCACHE_LINE_LEN - 1)));
|
|
if (aligned != addr) {
|
|
aligned += ARC_DCACHE_LINE_LEN;
|
|
}
|
|
return aligned;
|
|
}
|
|
|
|
static inline void emac_cache_inv(unsigned long begin, unsigned long end)
|
|
{
|
|
invalidate_dcache_range(
|
|
emac_align_begin(begin),
|
|
emac_align_end(end));
|
|
}
|
|
|
|
static inline void emac_cache_flush(unsigned long begin, unsigned long end)
|
|
{
|
|
flush_and_inv_dcache_range( /* flush _and_ invalidate to free few cache entries as we are not going to read them back */
|
|
emac_align_begin(begin),
|
|
emac_align_end(end));
|
|
}
|
|
|
|
/* Taken from Linux MII driver */
|
|
#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
|
|
#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
|
|
#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
|
|
#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
|
|
#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
|
|
#define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */
|
|
#define LPA_1000HALF 0x0400 /* Link partner 1000BASE-T half duplex */
|
|
|
|
#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
|
|
#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
|
|
#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
|
|
#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */
|
|
#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
|
|
|
|
#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA)
|
|
|
|
#define PHY_MAX_ADDR 32
|
|
|
|
#define ARASAN_MDIO_BASE RUBY_ENET0_BASE_ADDR
|
|
|
|
enum DP83865PhyRegVals {
|
|
/* Basic mode control register */
|
|
PhySoftReset = (1 << 15),
|
|
PhyLoopback = (1 << 14),
|
|
PhySetSpeed1G = ((1 << 6) | (0 << 13)),
|
|
PhySetSpeed100M = ((0 << 6) | (1 << 13)),
|
|
PhySetSpeed10M = ((0 << 6) | (0 << 13)),
|
|
PhyAutoNegEnable = (1 << 12),
|
|
PhyPowerDown = (1 << 11),
|
|
PhyIsolate = (1 << 10),
|
|
PhyRestartAutoNeg = (1 << 9),
|
|
PhyFullDuplex = (1 << 8),
|
|
PhyColTest = (1 << 7),
|
|
/* Basic mode status register */
|
|
PhyAutoNegComplete = (1 << 5),
|
|
/* Link and AutoNegotiation register */
|
|
PhySpeedMask = (3 << 3),
|
|
PhySpeed10M = (0 << 3),
|
|
PhySpeed100M = (1 << 3),
|
|
PhySpeed1G = (2 << 3),
|
|
PhyLinkIsUp = (1 << 2),
|
|
PhyLinkIsFullDuplex = (1 << 1),
|
|
PhyInMasterMode = (1 << 0),
|
|
};
|
|
|
|
enum DP83865RegOffset {
|
|
PhyBMCR = 0,
|
|
PhyBMSR = 1,
|
|
PhyPhysID1 = 2,
|
|
PhyPhysID2 = 3,
|
|
PhyAdvertise = 4,
|
|
PhyLpa = 5,
|
|
PhyStat1000 = 10,
|
|
};
|
|
|
|
|
|
/* 8 byte alignment required as DMA is operating in 64 bit mode.
|
|
ARC_DCACHE_LINE_LEN alignment used to have safe cache invalidate/flush.
|
|
As ARC_DCACHE_LINE_LEN is 32, both requirements are satisfied.
|
|
*/
|
|
static volatile struct emac_desc tx_ring[2] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN)));
|
|
static volatile struct emac_desc rx_ring[NUM_RX_BUFS] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN)));
|
|
static volatile u8 tx_buf[TX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN)));
|
|
static volatile u8 rx_bufs[NUM_RX_BUFS * RX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN)));
|
|
|
|
#undef ENABLE_LOOPBACK
|
|
#ifdef ENABLE_LOOPBACK
|
|
static u32 loopback = 0 ;
|
|
#endif
|
|
|
|
static struct emac_private priv = {
|
|
.tx_ring_elements = 1,
|
|
.rx_ring_elements = NUM_RX_BUFS,
|
|
.tx_bufs = (u32)&tx_buf,
|
|
.rx_bufs = (u32)&rx_bufs[0],
|
|
.tx_descs = &tx_ring[0],
|
|
.rx_descs = &rx_ring[0],
|
|
.tx_buf_size = TX_BUF_SIZE,
|
|
.rx_buf_size = RX_BUF_SIZE,
|
|
.tx_index = 0,
|
|
.rx_index = 0,
|
|
.ar8236dev = NULL,
|
|
.phy_addr = 0,
|
|
.phy_flags = 0,
|
|
};
|
|
|
|
/* Utility functions for reading/writing registers in the Ethernet MAC */
|
|
|
|
static u32 emac_rdreg(u32 base, int reg)
|
|
{
|
|
return readl(base + reg);
|
|
}
|
|
|
|
static void emac_wrreg(u32 base, int reg, u32 val)
|
|
{
|
|
writel(val, base + reg);
|
|
}
|
|
|
|
static void emac_setbits(u32 base, int reg, u32 val)
|
|
{
|
|
emac_wrreg(base, reg, emac_rdreg(base, reg) | val);
|
|
}
|
|
|
|
static void emac_clrbits(u32 base, int reg, u32 val)
|
|
{
|
|
emac_wrreg(base, reg, emac_rdreg(base, reg) & ~val);
|
|
}
|
|
|
|
/* Utility functions for driving the MDIO interface to the PHY from the MAC */
|
|
|
|
static int mdio_operation_complete(u32 base)
|
|
{
|
|
return !(emac_rdreg(base, EMAC_MAC_MDIO_CTRL) & 0x8000);
|
|
}
|
|
|
|
static void mdio_postrd(u32 base, int phy, int reg)
|
|
{
|
|
int i = 0;
|
|
while (!mdio_operation_complete(base)) {
|
|
udelay(1000);
|
|
if (++i >= 2000) {
|
|
return;
|
|
}
|
|
}
|
|
emac_wrreg(base, EMAC_MAC_MDIO_CTRL, (phy & 31) |
|
|
((reg & 31) << 5) | (1 << 10) | (1 << 15));
|
|
}
|
|
|
|
static int mdio_postwr(u32 base, int phy, int reg, u16 val)
|
|
{
|
|
int i = 0;
|
|
|
|
while (!mdio_operation_complete(base)) {
|
|
udelay(100);
|
|
if (++i >= 20000) {
|
|
return -1;
|
|
}
|
|
}
|
|
emac_wrreg(base, EMAC_MAC_MDIO_DATA, val);
|
|
emac_wrreg(base, EMAC_MAC_MDIO_CTRL, (phy & 31) |
|
|
((reg & 31) << 5) | (1 << 15));
|
|
return 0;
|
|
}
|
|
|
|
/* Check to see if an MDIO posted read command (mdio_postrd) has completed.
|
|
* Returns the u16 data from the read, or (u32)-1 if the data is not available.
|
|
* Setting "wait" to TRUE makes the command poll until the data is available
|
|
* or the command times-out. Setting "wait" to FALSE stops the command
|
|
* from polling; it only checks if the data is available once.
|
|
*/
|
|
static u32 mdio_rdval(u32 base, int wait)
|
|
{
|
|
int i = 0;
|
|
if (wait) {
|
|
while (!mdio_operation_complete(base)) {
|
|
udelay(100);
|
|
if (++i >= 20000) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!mdio_operation_complete(base)) {
|
|
printf("GMII: MDIO read timed out (%08x)\n",
|
|
emac_rdreg(base, EMAC_MAC_MDIO_CTRL));
|
|
return (u32)-1;
|
|
}
|
|
return (u32)emac_rdreg(base, EMAC_MAC_MDIO_DATA);
|
|
}
|
|
|
|
/* Taken from Linux MII support. Return the link speed based on
|
|
* the IEEE register values from the Ethernet PHY.
|
|
*/
|
|
static inline unsigned int mii_nway_result(unsigned int negotiated)
|
|
{
|
|
unsigned int ret;
|
|
|
|
if (negotiated & LPA_100FULL)
|
|
ret = LPA_100FULL;
|
|
else if (negotiated & LPA_100BASE4)
|
|
ret = LPA_100BASE4;
|
|
else if (negotiated & LPA_100HALF)
|
|
ret = LPA_100HALF;
|
|
else if (negotiated & LPA_10FULL)
|
|
ret = LPA_10FULL;
|
|
else
|
|
ret = LPA_10HALF;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void emac_reset_dma(u32 base)
|
|
{
|
|
int i = 0;
|
|
u32 val;
|
|
|
|
/* Attempt to stop the DMA tx and rx in an orderly fashion */
|
|
emac_clrbits(base, EMAC_DMA_CTRL, DmaStartTx | DmaStartRx);
|
|
do {
|
|
udelay(10000);
|
|
val = emac_rdreg(base, EMAC_DMA_STATUS_IRQ);
|
|
if (++i >= 100) {
|
|
printf("GMII: Failed to stop DMA\n");
|
|
break;
|
|
}
|
|
} while (val & (DmaTxStateMask | DmaRxStateMask));
|
|
|
|
/* Don't read any registers whilst the block is reset (it hangs) */
|
|
emac_wrreg(base, EMAC_DMA_CONFIG, DmaSoftReset);
|
|
emac_wrreg(base, EMAC_DMA_CONFIG, 0);
|
|
}
|
|
|
|
static int rx_stats[] = { 0, 1, 2, 3, 4, 5, 6 };
|
|
#define NUM_RX_STATS (sizeof(rx_stats) / sizeof(int))
|
|
|
|
static int tx_stats[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
|
|
#define NUM_TX_STATS (sizeof(tx_stats) / sizeof(int))
|
|
|
|
static void emac_stats(struct eth_device *dev)
|
|
{
|
|
int i;
|
|
u32 val;
|
|
int base = dev->iobase;
|
|
int counter;
|
|
int n;
|
|
|
|
for(n = 0; n < NUM_RX_STATS; n++)
|
|
{
|
|
counter = rx_stats[n];
|
|
emac_wrreg(base, EMAC_MAC_RXSTAT_CTRL, 0);
|
|
emac_wrreg(base, EMAC_MAC_RXSTAT_CTRL, RxStatReadBusy | counter);
|
|
i = 0;
|
|
do
|
|
{
|
|
if(++i > 10000)
|
|
{
|
|
printf("GMII: Rx Stat timeout for %d\n", counter);
|
|
return;
|
|
}
|
|
}
|
|
while ((val = emac_rdreg(base, EMAC_MAC_RXSTAT_CTRL)) & RxStatReadBusy);
|
|
val = emac_rdreg(base, EMAC_MAC_RXSTAT_DATA_HIGH) << 16;
|
|
val |= (emac_rdreg(base, EMAC_MAC_RXSTAT_DATA_LOW) & 0xffff);
|
|
printf("%d: Rx Stat %d = 0x%08x\n", n, rx_stats[n], val);
|
|
}
|
|
|
|
for(n = 0; n < NUM_TX_STATS; n++)
|
|
{
|
|
counter = tx_stats[n];
|
|
emac_wrreg(base, EMAC_MAC_TXSTAT_CTRL, 0);
|
|
emac_wrreg(base, EMAC_MAC_TXSTAT_CTRL, TxStatReadBusy | counter);
|
|
i = 0;
|
|
do
|
|
{
|
|
if(++i > 10000)
|
|
{
|
|
printf("GMII: Tx Stat timeout for %d 0x%08x\n", counter, val);
|
|
return;
|
|
}
|
|
}
|
|
while ((val = emac_rdreg(base, EMAC_MAC_TXSTAT_CTRL)) & TxStatReadBusy);
|
|
val = emac_rdreg(base, EMAC_MAC_TXSTAT_DATA_HIGH) << 16;
|
|
val |= (emac_rdreg(base, EMAC_MAC_TXSTAT_DATA_LOW) & 0xffff);
|
|
printf("%d: Tx Stat %d = 0x%08x\n", n, tx_stats[n], val);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int emac_reset(struct eth_device *dev, bd_t *bd)
|
|
{
|
|
int i;
|
|
u32 base, mdio_base;
|
|
struct emac_private *priv;
|
|
u32 val, x;
|
|
u32 lpa2, media, duplex;
|
|
|
|
if (!dev || !dev->priv) {
|
|
return 0;
|
|
}
|
|
priv = (struct emac_private *)dev->priv;
|
|
base = dev->iobase;
|
|
mdio_base = ARASAN_MDIO_BASE;
|
|
|
|
emac_reset_dma(base);
|
|
|
|
/* Initialise the buffer descriptors for Tx */
|
|
tx_ring[0].status = 0;
|
|
tx_ring[0].control = TxDescEndOfRing;
|
|
tx_ring[0].bufaddr1 = virt_to_bus(&tx_buf);
|
|
tx_ring[0].bufaddr2 = 0;
|
|
|
|
/* Initialise the buffer descriptors for Rx */
|
|
for (i = 0; i < sizeof(rx_ring)/sizeof(struct emac_desc); i++) {
|
|
rx_ring[i].status = RxDescOwn;
|
|
rx_ring[i].control = (RX_BUF_SIZE & RxDescBuf1SizeMask)
|
|
<< RxDescBuf1SizeShift;
|
|
rx_ring[i].bufaddr1 = virt_to_bus((void*)(unsigned long)&rx_bufs[i * RX_BUF_SIZE]);
|
|
rx_ring[i].bufaddr2 = 0;
|
|
}
|
|
if (i > 0) {
|
|
rx_ring[i - 1].control |= RxDescEndOfRing;
|
|
}
|
|
|
|
priv->rx_index = 0;
|
|
priv->tx_index = 0;
|
|
|
|
/* We assume that all registers are in their default states from
|
|
* reset, so we only update those ones that are necessary.
|
|
*/
|
|
// !!! FIXME_UMS - do we need Robin+Wait flags here? | DmaWait4Done
|
|
emac_wrreg(base, EMAC_DMA_CONFIG, Dma16WordBurst | Dma64BitMode |
|
|
DmaRoundRobin );
|
|
emac_wrreg(base, EMAC_DMA_TX_AUTO_POLL, 16);
|
|
emac_wrreg(base, EMAC_DMA_TX_BASE_ADDR, virt_to_bus((void*)(unsigned long)tx_ring));
|
|
emac_wrreg(base, EMAC_DMA_RX_BASE_ADDR, virt_to_bus((void*)(unsigned long)rx_ring));
|
|
(void)emac_rdreg(base, EMAC_DMA_MISSED_FRAMES);
|
|
(void)emac_rdreg(base, EMAC_DMA_STOP_FLUSHES);
|
|
|
|
emac_wrreg(base, EMAC_MAC_ADDR_CTRL, MacAddr1Enable);
|
|
emac_wrreg(base, EMAC_MAC_ADDR1_HIGH, *(u16 *)&dev->enetaddr[0]);
|
|
emac_wrreg(base, EMAC_MAC_FLOW_SA_HIGH, *(u16 *)&dev->enetaddr[0]);
|
|
emac_wrreg(base, EMAC_MAC_ADDR1_MED, *(u16 *)&dev->enetaddr[2]);
|
|
emac_wrreg(base, EMAC_MAC_FLOW_SA_MED, *(u16 *)&dev->enetaddr[2]);
|
|
emac_wrreg(base, EMAC_MAC_ADDR1_LOW, *(u16 *)&dev->enetaddr[4]);
|
|
emac_wrreg(base, EMAC_MAC_FLOW_SA_LOW, *(u16 *)&dev->enetaddr[4]);
|
|
|
|
/* !!! FIXME_UMS - whether or not we need this depends on whether
|
|
* the auto-pause generation uses it. The auto function may just
|
|
* use 0xffff val to stop sending & then 0 to restart it.
|
|
*/
|
|
//!!!FIXME_UMS emac_wrreg(base, EMAC_MAC_FLOW_PAUSE_TIMEVAL, 0xffff);
|
|
emac_wrreg(base, EMAC_MAC_FLOW_PAUSE_TIMEVAL, 0);
|
|
|
|
/* Required by the datasheet */
|
|
// emac_wrreg(base, EMAC_MAC_TX_ALMOST_FULL, 0x8);
|
|
emac_wrreg(base, EMAC_MAC_TX_ALMOST_FULL, 0x1f8);
|
|
|
|
/* Use safe store & forward value - valid for all speeds */
|
|
emac_wrreg(base, EMAC_MAC_TX_START_THRESHOLD, 1518);
|
|
|
|
emac_wrreg(base, EMAC_MAC_FLOW_CTRL, MacFlowDecodeEnable |
|
|
MacFlowGenerationEnable | MacAutoFlowGenerationEnable |
|
|
MacFlowMulticastMode | MacBlockPauseFrames);
|
|
|
|
if (!(priv->phy_flags & EMAC_PHY_NOT_IN_USE)) {
|
|
if (priv->phy_flags & EMAC_PHY_RESET) {
|
|
/* Reset the PHY and wait for autonegotiation to complete. Set MII or
|
|
* GMII mode based on the result.
|
|
*/
|
|
mdio_postwr(mdio_base, priv->phy_addr, PhyBMCR, PhySoftReset);
|
|
i = 0;
|
|
printf("Resetting PHY...\n");
|
|
do {
|
|
/* Wait for PHY reset to complete */
|
|
udelay(100000);
|
|
mdio_postrd(mdio_base, priv->phy_addr, PhyBMCR);
|
|
if ((val = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
return 0;
|
|
}
|
|
if (i++ == 50) {
|
|
printf("GMII: PHY Reset stuck");
|
|
return 0;
|
|
}
|
|
} while (val & PhySoftReset);
|
|
|
|
printf("GMII: PHY reset complete\n");
|
|
/* Post-reset delay before any other register access allowed
|
|
* on vitesse PHY.
|
|
*/
|
|
udelay(4);
|
|
}
|
|
if ((priv->phy_flags & EMAC_PHY_AUTO_MASK) == 0) {
|
|
/* Vitesse VSC8641 has a bug regarding writes to register 4 or 9
|
|
* after a soft reset unless an MDIO access occurs in between.
|
|
* We avoid this bug implicitly by the polling loop here.
|
|
*/
|
|
printf("Autonegotiating\n");
|
|
|
|
i = 0;
|
|
do {
|
|
/* Wait for autonegotiation to complete */
|
|
udelay(500);
|
|
mdio_postrd(mdio_base, priv->phy_addr, PhyBMSR);
|
|
if ((val = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
return 0;
|
|
}
|
|
if (i++ == 30000) {
|
|
printf("GMII: Autonegotiation timed out\n");
|
|
return 0;
|
|
}
|
|
} while (!(val & PhyAutoNegComplete));
|
|
|
|
printf("Autonegotiation complete (BMSR=%08x)\n", val);
|
|
|
|
/* Work out autonegotiation result using only IEEE registers */
|
|
{
|
|
u32 advertise, lpa;
|
|
|
|
mdio_postrd(mdio_base, priv->phy_addr, PhyAdvertise);
|
|
if ((advertise = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("GMII: Failed to read Advertise reg\n");
|
|
return 0;
|
|
}
|
|
|
|
mdio_postrd(mdio_base, priv->phy_addr, PhyLpa);
|
|
if ((lpa = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("GMII: Failed to read Lpa reg\n");
|
|
return 0;
|
|
}
|
|
|
|
mdio_postrd(mdio_base, priv->phy_addr, PhyStat1000);
|
|
if ((lpa2 = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("GMII: Failed to read Lpa2 reg\n");
|
|
return 0;
|
|
}
|
|
|
|
//printf("Autoneg status: Advertise=%08x, Lpa=%08x, Lpa2=%08x\n",
|
|
// PhyAdvertise, PhyLpa, PhyStat1000);
|
|
printf("Autoneg status: Advertise=%08x, Lpa=%08x, Lpa2=%08x\n",
|
|
advertise, lpa, lpa2);
|
|
|
|
media = mii_nway_result(lpa & advertise);
|
|
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
|
|
if (lpa2 & LPA_1000FULL) {
|
|
duplex = 1;
|
|
}
|
|
}
|
|
printf("GMII: Link speed is ");
|
|
} else {
|
|
duplex = 0;
|
|
lpa2 = 0;
|
|
media = 0;
|
|
if (priv->phy_flags & EMAC_PHY_FORCE_1000MB) {
|
|
i = 0x0040; /* Force 1G */
|
|
lpa2 = LPA_1000FULL;
|
|
} else if (priv->phy_flags & EMAC_PHY_FORCE_100MB) {
|
|
i = 0x2000; /* Force 100M */
|
|
media = ADVERTISE_100FULL;
|
|
} else {
|
|
i = 0x0000; /* Force 10M */
|
|
if (priv->phy_flags & EMAC_PHY_FORCE_10MB ) {
|
|
printf("GMII: ethlink not 10, 100 or 1000 - forcing 10Mbps\n");
|
|
}
|
|
}
|
|
if (!(priv->phy_flags & EMAC_PHY_FORCE_HDX)) {
|
|
i |= 0x100; /* Full duplex */
|
|
duplex = 1;
|
|
}
|
|
|
|
mdio_postwr(mdio_base, priv->phy_addr, PhyBMCR, i); /* Force link speed & duplex */
|
|
printf("GMII: Forced link speed is ");
|
|
}
|
|
} else { // no phy
|
|
duplex = 0;
|
|
lpa2 = 0;
|
|
media = 0;
|
|
if (priv->phy_flags & EMAC_PHY_FORCE_1000MB) {
|
|
i = 0x0040; /* Force 1G */
|
|
lpa2 = LPA_1000FULL;
|
|
} else if (priv->phy_flags & EMAC_PHY_FORCE_100MB) {
|
|
i = 0x2000; /* Force 100M */
|
|
media = ADVERTISE_100FULL;
|
|
} else {
|
|
i = 0x0000; /* Force 10M */
|
|
if (priv->phy_flags & EMAC_PHY_FORCE_10MB ) {
|
|
printf("GMII: ethlink not 10, 100 or 1000 - forcing 10Mbps\n");
|
|
}
|
|
}
|
|
if (!(priv->phy_flags & EMAC_PHY_FORCE_HDX)) {
|
|
i |= 0x100; /* Full duplex */
|
|
duplex = 1;
|
|
}
|
|
}
|
|
x = 0;
|
|
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL_MASK,
|
|
RUBY_SYS_CTL_MASK_GMII0_TXCLK|RUBY_SYS_CTL_MASK_GMII1_TXCLK);
|
|
|
|
if (lpa2 & (LPA_1000FULL | LPA_1000HALF)) {
|
|
x |= MacSpeed1G;
|
|
printf("1G");
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL,
|
|
RUBY_SYS_CTL_MASK_GMII0_1000M|RUBY_SYS_CTL_MASK_GMII1_1000M);
|
|
}
|
|
else if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) {
|
|
x |= MacSpeed100M;
|
|
printf("100M");
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL,
|
|
RUBY_SYS_CTL_MASK_GMII0_100M|RUBY_SYS_CTL_MASK_GMII1_100M);
|
|
}
|
|
else {
|
|
x |= MacSpeed10M;
|
|
printf("10M");
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL,
|
|
RUBY_SYS_CTL_MASK_GMII0_10M|RUBY_SYS_CTL_MASK_GMII0_10M);
|
|
}
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL_MASK, 0);
|
|
|
|
if (duplex) {
|
|
x |= MacFullDuplex;
|
|
printf("-FD\n");
|
|
} else {
|
|
printf("-HD\n");
|
|
}
|
|
|
|
/* Flush descriptors */
|
|
emac_cache_flush((unsigned long)tx_ring, (unsigned long)tx_ring + sizeof(tx_ring));
|
|
emac_cache_flush((unsigned long)rx_ring, (unsigned long)rx_ring + sizeof(rx_ring));
|
|
|
|
/* Setup speed and run! */
|
|
emac_wrreg(base, EMAC_MAC_GLOBAL_CTRL, x);
|
|
emac_wrreg(base, EMAC_MAC_TX_CTRL, MacTxEnable | MacTxAutoRetry);
|
|
emac_wrreg(base, EMAC_MAC_RX_CTRL, MacRxEnable | MacRxStoreAndForward);
|
|
emac_setbits(base, EMAC_DMA_CTRL, DmaStartTx | DmaStartRx);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void emac_halt(struct eth_device *dev)
|
|
{
|
|
int base;
|
|
|
|
if (dev) {
|
|
base = dev->iobase;
|
|
emac_clrbits(base, EMAC_MAC_TX_CTRL, MacTxEnable);
|
|
emac_clrbits(base, EMAC_MAC_RX_CTRL, MacRxEnable);
|
|
emac_clrbits(base, EMAC_DMA_CTRL, DmaStartTx | DmaStartRx);
|
|
}
|
|
}
|
|
/*
|
|
|
|
static u32 rd_txstatistics_counter(struct eth_device *dev, int counter)
|
|
{
|
|
int i = 0;
|
|
u32 val;
|
|
int base = dev->iobase;
|
|
|
|
emac_wrreg(base, EMAC_MAC_TXSTAT_CTRL, TxStatReadBusy | counter);
|
|
do {
|
|
if (++i > 10000) {
|
|
printf("GMII: Tx stat read timeout\n");
|
|
return 0;
|
|
}
|
|
}
|
|
while (emac_rdreg(base, EMAC_MAC_TXSTAT_CTRL) & TxStatReadBusy);
|
|
val = emac_rdreg(base, EMAC_MAC_TXSTAT_DATA_HIGH) << 16;
|
|
val |= (emac_rdreg(base, EMAC_MAC_TXSTAT_DATA_LOW) & 0xffff);
|
|
return val;
|
|
}
|
|
|
|
static u32 rd_rxstatistics_counter(struct eth_device *dev, int counter)
|
|
{
|
|
int i = 0;
|
|
u32 val;
|
|
int base = dev->iobase;
|
|
|
|
emac_wrreg(base, EMAC_MAC_RXSTAT_CTRL, RxStatReadBusy | counter);
|
|
do {
|
|
if (++i > 10000) {
|
|
printf("GMII: Rx stat read timeout\n");
|
|
return 0;
|
|
}
|
|
}
|
|
while (emac_rdreg(base, EMAC_MAC_RXSTAT_CTRL) & RxStatReadBusy);
|
|
val = emac_rdreg(base, EMAC_MAC_RXSTAT_DATA_HIGH) << 16;
|
|
val |= (emac_rdreg(base, EMAC_MAC_RXSTAT_DATA_LOW) & 0xffff);
|
|
return val;
|
|
}
|
|
|
|
|
|
|
|
static void reset_block(struct eth_device *dev)
|
|
{
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_RESET_MASK, SYSCTRL_ETHERNET_RUN);
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_RESET, 0);
|
|
emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_RESET, SYSCTRL_ETHERNET_RUN);
|
|
emac_resetx(dev, this_bd, 0);
|
|
}
|
|
*/
|
|
|
|
|
|
static int emac_send(struct eth_device *dev, volatile void *packet, int length)
|
|
{
|
|
volatile struct emac_desc *ptx_desc;
|
|
struct emac_private *priv;
|
|
u32 base, now;
|
|
if (!dev || !dev->priv) {
|
|
printf("emac_send: NULL pointer\n");
|
|
return 0;
|
|
}
|
|
|
|
base = dev->iobase;
|
|
priv = (struct emac_private *)dev->priv;
|
|
|
|
ptx_desc = priv->tx_descs;
|
|
|
|
if (readl(&ptx_desc->status) & TxDescOwn) {
|
|
printf("No buffers available\n");
|
|
return 0;
|
|
}
|
|
|
|
/* This is a simplification - only handle packets <= 2kB */
|
|
if (length > priv->tx_buf_size) {
|
|
printf("Packet too big\n");
|
|
return 0;
|
|
}
|
|
|
|
/* copy packet */
|
|
memcpy((void*)bus_to_virt(ptx_desc->bufaddr1), (void*)packet, length);
|
|
emac_cache_flush((ulong)bus_to_virt(ptx_desc->bufaddr1),
|
|
(ulong)bus_to_virt(ptx_desc->bufaddr1) + length);
|
|
|
|
/* do tx */
|
|
writel(TxDescFirstSeg | TxDescLastSeg | TxDescEndOfRing | (length & TxDescBuf1SizeMask) << TxDescBuf1SizeShift,
|
|
&ptx_desc->control);
|
|
writel(TxDescOwn, &ptx_desc->status); /* Hand-off frame to Ethernet DMA engines.. */
|
|
emac_wrreg(base, EMAC_DMA_TX_POLL_DEMAND, 1); /* restart TX */
|
|
|
|
/* ..and wait for it to go */
|
|
now = get_timer(0);
|
|
while (readl(&ptx_desc->status) & TxDescOwn) {
|
|
if (get_timer(now) > TX_TIMEOUT_IN_TICKS) {
|
|
printf("Transmit timeout\n");
|
|
writel(0, &ptx_desc->status);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static int emac_recv(struct eth_device *dev)
|
|
{
|
|
volatile struct emac_desc *prx_desc;
|
|
u32 base, length;
|
|
struct emac_private *priv;
|
|
volatile u32 status;
|
|
|
|
if (!dev || !dev->priv) {
|
|
return 0;
|
|
}
|
|
|
|
base = dev->iobase;
|
|
priv = (struct emac_private *)dev->priv;
|
|
prx_desc = priv->rx_descs + priv->rx_index;
|
|
|
|
if ((status = readl(&prx_desc->status)) & RxDescOwn) {
|
|
/* Nothing received yet */
|
|
return 0;
|
|
}
|
|
|
|
length = (status >> RxDescFrameLenShift) & RxDescFrameLenMask;
|
|
if (length >= RX_BUF_SIZE) {
|
|
board_reset("\n\n***** reset: emac_recv: oversize packet\n\n");
|
|
}
|
|
if (length > 0) {
|
|
emac_cache_inv((ulong)bus_to_virt(prx_desc->bufaddr1),
|
|
(ulong)bus_to_virt(prx_desc->bufaddr1) + length);
|
|
#ifdef ENABLE_LOOPBACK
|
|
if (loopback) {
|
|
//printf("echo back %d\n",length);
|
|
emac_send(dev,ARC_SRAM_ADDRESS(prx_desc->bufaddr1), length);
|
|
} else {
|
|
NetReceive((unsigned char *)bus_to_virt(prx_desc->bufaddr1), length);
|
|
}
|
|
#else
|
|
NetReceive((unsigned char *)bus_to_virt(prx_desc->bufaddr1), length);
|
|
#endif
|
|
}
|
|
|
|
priv->rx_index++;
|
|
if (priv->rx_index == NUM_RX_BUFS) {
|
|
priv->rx_index = 0;
|
|
}
|
|
/* Give descriptor back to the DMA engines */
|
|
writel(RxDescOwn, &prx_desc->status);
|
|
emac_wrreg(base, EMAC_DMA_RX_POLL_DEMAND, 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int probe_phy(u32 mdio_base,int addr)
|
|
{
|
|
int val;
|
|
mdio_postrd(mdio_base, addr, PhyBMCR);
|
|
if ((val = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("Read from MDIO failed.\n");
|
|
return -1;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static void arasan_initialize_gpio_reset_pin(int pin)
|
|
{
|
|
printf("qtn emac init GPIO pin %d reset sequence\n", pin);
|
|
gpio_config(pin, GPIO_MODE_OUTPUT);
|
|
gpio_output(pin, 1);
|
|
udelay(100);
|
|
gpio_output(pin, 0);
|
|
udelay(100000);
|
|
gpio_output(pin, 1);
|
|
}
|
|
|
|
static void arasan_initialize_gpio_reset(uint32_t emac_cfg)
|
|
{
|
|
if (emac_cfg & EMAC_PHY_GPIO1_RESET) {
|
|
arasan_initialize_gpio_reset_pin(RUBY_GPIO_PIN1);
|
|
}
|
|
|
|
if (emac_cfg & EMAC_PHY_GPIO13_RESET) {
|
|
arasan_initialize_gpio_reset_pin(RUBY_GPIO_PIN13);
|
|
}
|
|
}
|
|
|
|
#define RXTX_DELAY 0x1e0000
|
|
struct eth_device *g_eth_dev;
|
|
static struct eth_device __g_eth_dev;
|
|
|
|
|
|
u32 get_phy_id (u32 mdio_base,int addr)
|
|
{
|
|
int phy_reg;
|
|
u32 phy_id;
|
|
|
|
|
|
mdio_postrd(mdio_base, addr, PhyPhysID1);
|
|
if ((phy_reg = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("Read from MDIO failed.\n");
|
|
return -1;
|
|
}
|
|
phy_id = (phy_reg & 0xffff) << 16;
|
|
|
|
mdio_postrd(mdio_base, addr, PhyPhysID2);
|
|
if ((phy_reg = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("Read from MDIO failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
phy_id |= (phy_reg & 0xffff);
|
|
|
|
return phy_id;
|
|
}
|
|
|
|
int board_eth_init(bd_t *bis)
|
|
{
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
struct eth_device *dev;
|
|
u32 base, mdio_base;
|
|
int i;
|
|
uint32_t rgmii_timing = board_config(gd->bd->bi_board_id, BOARD_CFG_RGMII_TIMING);
|
|
uint32_t emac0_cfg = board_config(gd->bd->bi_board_id, BOARD_CFG_EMAC0);
|
|
uint32_t emac1_cfg = board_config(gd->bd->bi_board_id, BOARD_CFG_EMAC1);
|
|
uint32_t emac_cfg = emac0_cfg | emac1_cfg;
|
|
u32 phy_id;
|
|
|
|
dev = &__g_eth_dev;
|
|
sprintf(dev->name, "QTN-EMAC");
|
|
|
|
if (emac0_cfg & EMAC_IN_USE) {
|
|
priv.phy_flags = emac0_cfg;
|
|
dev->iobase = RUBY_ENET0_BASE_ADDR;
|
|
priv.irq = RUBY_IRQ_ENET0;
|
|
priv.phy_addr = board_config(gd->bd->bi_board_id, BOARD_CFG_PHY0_ADDR);
|
|
} else if (emac1_cfg & EMAC_IN_USE) {
|
|
priv.phy_flags = emac1_cfg;
|
|
dev->iobase = RUBY_ENET1_BASE_ADDR;
|
|
priv.irq = RUBY_IRQ_ENET1;
|
|
priv.phy_addr = board_config(gd->bd->bi_board_id, BOARD_CFG_PHY1_ADDR);
|
|
} else {
|
|
printf("error: no emac enabled\n");
|
|
return -1;
|
|
}
|
|
|
|
arasan_initialize_gpio_reset(emac_cfg);
|
|
arasan_initialize_release_reset(emac0_cfg, emac1_cfg, rgmii_timing);
|
|
|
|
dev->priv = (void *)&priv;
|
|
dev->init = emac_reset;
|
|
dev->halt = emac_halt;
|
|
dev->send = emac_send;
|
|
dev->recv = emac_recv;
|
|
/* We have no ethernet address of our own, we rely on getting
|
|
* one from the U-Boot environment.
|
|
*/
|
|
for (i = 0; i < 6; i++) {
|
|
dev->enetaddr[i] = 0;
|
|
}
|
|
|
|
eth_register(dev);
|
|
|
|
emac_halt(dev);
|
|
base = dev->iobase;
|
|
mdio_base = ARASAN_MDIO_BASE;
|
|
|
|
if (!(priv.phy_flags & EMAC_PHY_NOT_IN_USE)) {
|
|
int found = 0;
|
|
i=0;
|
|
while (i++ < 100) {
|
|
if (priv.phy_addr >= PHY_MAX_ADDR) {
|
|
for (priv.phy_addr = 0;priv.phy_addr < PHY_MAX_ADDR;priv.phy_addr++) {
|
|
int id;
|
|
if ((id = probe_phy(mdio_base,priv.phy_addr)) != 0xffff) {
|
|
printf("PHY found on MDIO addr:%x %d\n",id,priv.phy_addr);
|
|
found = 1;
|
|
break;
|
|
}
|
|
udelay(200);
|
|
}
|
|
} else {
|
|
if (probe_phy(mdio_base,priv.phy_addr) != 0xffff) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
udelay(200);
|
|
}
|
|
}
|
|
if (!found) {
|
|
printf("No PHY found on MDIO addr:%d\n",priv.phy_addr);
|
|
return -1;
|
|
}
|
|
|
|
/* Reset the PHY which then kick-offs the autonegotiation */
|
|
mdio_postwr(mdio_base, priv.phy_addr, PhyBMCR, PhySoftReset | PhyAutoNegEnable);
|
|
|
|
//Turn off 125MHz and enable SSC for Realtek(RTL8211E) VB
|
|
phy_id = get_phy_id(mdio_base, priv.phy_addr);
|
|
printf("phy ID 0x%x is probed\n",phy_id);
|
|
if (phy_id == 0x1cc915){
|
|
u32 val;
|
|
mdio_postwr(mdio_base, priv.phy_addr, 31, 0);
|
|
mdio_postwr(mdio_base, priv.phy_addr, 16, 0x17E);
|
|
mdio_postwr(mdio_base, priv.phy_addr, 31, 7);
|
|
mdio_postwr(mdio_base, priv.phy_addr, 30, 0xA0);
|
|
mdio_postrd(mdio_base, priv.phy_addr, 26);
|
|
if ((val = mdio_rdval(mdio_base, 1)) == (u32)-1) {
|
|
printf("u-boot: Failed to read phy reg#26\n");
|
|
return 0;
|
|
}
|
|
mdio_postwr(mdio_base, priv.phy_addr, 26, val&~4);
|
|
mdio_postwr(mdio_base, priv.phy_addr, 31, 0);
|
|
printf("Enable phy SSC\n");
|
|
}
|
|
|
|
/* This is a dummy read that ensures the write has happened */
|
|
if (mdio_rdval(mdio_base, 1) == (u32)-1) {
|
|
return -1;
|
|
}
|
|
} else if (priv.phy_flags & EMAC_PHY_AR8236) {
|
|
// ignore error - it has already been reported,
|
|
// not much else we can really do
|
|
priv.phy_addr = ar8236_init(mdio_base,priv.phy_addr);
|
|
} else if (priv.phy_flags & EMAC_PHY_AR8327) {
|
|
// ignore error - it has already been reported,
|
|
// not much else we can really do
|
|
priv.phy_addr = ar8237_init(mdio_base,priv.phy_addr);
|
|
//printf("call 8327 init mdio_base[0x%x],priv.phy_addr[0x%x] \r\n",mdio_base,priv.phy_addr);
|
|
}
|
|
|
|
// Pass all multicast packets because it should be support multiboot server
|
|
emac_wrreg(dev->iobase, EMAC_MAC_TABLE1, 0xffff);
|
|
emac_wrreg(dev->iobase, EMAC_MAC_TABLE2, 0xffff);
|
|
emac_wrreg(dev->iobase, EMAC_MAC_TABLE3, 0xffff);
|
|
emac_wrreg(dev->iobase, EMAC_MAC_TABLE4, 0xffff);
|
|
emac_clrbits(dev->iobase, EMAC_MAC_ADDR_CTRL, MacPromiscuous);
|
|
|
|
g_eth_dev = dev;
|
|
return 1;
|
|
}
|
|
|
|
int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
u32 base = ARASAN_MDIO_BASE;
|
|
if (argc < 4) {
|
|
cmd_usage(cmdtp);
|
|
return 1;
|
|
}
|
|
if (strcmp(argv[1],"read") == 0) {
|
|
u32 a1,a2,val;
|
|
a1 = simple_strtoul (argv[2], NULL, 10);
|
|
a2 = simple_strtoul (argv[3], NULL, 16);
|
|
mdio_postrd(base, a1,a2);
|
|
val = mdio_rdval(base, 1);
|
|
printf("phy:%d reg:%d=0x%x\n",a1,a2,val);
|
|
} else if (strcmp(argv[1],"write") == 0) {
|
|
u32 a1, a2, a3;
|
|
a1 = simple_strtoul (argv[2], NULL, 10);
|
|
a2 = simple_strtoul (argv[3], NULL, 10);
|
|
a3 = simple_strtoul (argv[4], NULL, 16);
|
|
mdio_postwr(base, a1,a2,a3);
|
|
/* This is a dummy read that ensures the write has happened */
|
|
if (mdio_rdval(base, 1) == (u32)-1) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_CMD_ETHLOOP)
|
|
int mdio_write(int reg, u16 val)
|
|
{
|
|
u32 base = ARASAN_MDIO_BASE;
|
|
int ret = -1;
|
|
ret = mdio_postwr(base, priv.phy_addr, reg, val);
|
|
/* This is a dummy read that ensures the write has happened */
|
|
if (mdio_rdval(base, 1) == (u32)-1)
|
|
return -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mdio_read(int reg)
|
|
{
|
|
u32 val, base = ARASAN_MDIO_BASE;
|
|
mdio_postrd(base, priv.phy_addr, reg);
|
|
val = mdio_rdval(base, 1);
|
|
return val;
|
|
}
|
|
|
|
void enable_phy_loopback(void)
|
|
{
|
|
u32 val;
|
|
val = mdio_read(PhyBMCR);
|
|
val |= PhyLoopback; /* set loopback bit */
|
|
mdio_write(PhyBMCR, val);
|
|
}
|
|
|
|
void disable_phy_loopback(void)
|
|
{
|
|
u32 val;
|
|
val = mdio_read(PhyBMCR);
|
|
val &= ~PhyLoopback; /* reset loopback bit */
|
|
mdio_write(PhyBMCR, val);
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
#ifdef ENABLE_LOOPBACK
|
|
int do_emaclb(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
int a1;
|
|
a1 = simple_strtoul (argv[1], NULL, 16);
|
|
if (a1 == 0) {
|
|
loopback = 0;
|
|
} else {
|
|
loopback = 1;
|
|
}
|
|
while (1) {
|
|
NetLoop (NETCONS); /* kind of poll */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
emaclb, CFG_MAXARGS, 2, do_emaclb,
|
|
"emaclb - set emac loopback (0 or 1) \n",
|
|
NULL
|
|
);
|
|
#endif
|
|
#endif
|
|
#define CFG_MAXARGS 5
|
|
U_BOOT_CMD(
|
|
mdio, CFG_MAXARGS, 5, do_mdio,
|
|
"read/write mdio",
|
|
"mdio <read|write> <phy> <reg> <hex value>"
|
|
);
|
|
|
|
int do_emac(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
emac_stats(g_eth_dev);
|
|
return 0;
|
|
}
|
|
U_BOOT_CMD(
|
|
emac, CFG_MAXARGS, 3, do_emac,
|
|
"emac read stats",
|
|
NULL
|
|
);
|
|
#endif
|
|
|