| /* |
| * 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> |
| |
| #ifdef CONFIG_ARASAN_GBE |
| |
| #include "ruby.h" |
| #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> |
| |
| #ifdef UBOOT_ENABLE_ETHERNET_DEBUG |
| #define DPRINTF(x...) printf(x) |
| #else |
| #define DPRINTF(x...) |
| #endif |
| |
| |
| #define RTL8211DS_PAGSEL 0x1F /* Page mode slelect */ |
| #define PAGSEL_PAGEXT 0x7 /* Switch to extension page */ |
| #define PAGSEL_PAG0 0x0 /* Switch to page 0 */ |
| |
| #define RTL8211DS_PAGNO 0x1E /* Page number */ |
| #define PAGNO_140 0x8c /* Page 140 */ |
| |
| #define RTL8211DS_RXCONFIG 0x18 /* Tx config ( phy -> mac) */ |
| #define RXCONFIG_LINK 0x8000 |
| #define RXCONFIG_DUPLEX 0x1000 |
| #define RXCONFIG_SPEED 0x0c00 |
| #define RXCONFIG_SPEED_BIT 10 |
| |
| #define RTL8211DS_SDSR 0x16 /* SerDes register */ |
| #define SDSR_SPEED 0x3000 /* Rx speed */ |
| #define SDSR_1000 0x2000 |
| #define SDSR_100 0x1000 |
| #define SDSR_10 0x0000 |
| |
| |
| #define MAC_IS_MULTICAST(addr) ((addr)[0] & 0x1) |
| |
| #if (defined(EMAC_SWITCH)) && ((defined(EMAC_BOND_MASTER))) |
| #error EMAC_SWITCH and EMAC_BOND_MASTER can not be defined the same time |
| #endif |
| int mdc_clk_divisor = 1; |
| static int emac_phy_init(struct emac_private *priv); |
| |
| |
| 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)); |
| } |
| |
| |
| /* 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_0[1] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| static volatile struct emac_desc rx_ring_0[NUM_RX_BUFS] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| static volatile u8 tx_buf_0[TX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| static volatile u8 rx_bufs_0[NUM_RX_BUFS * RX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| |
| static volatile struct emac_desc tx_ring_1[1] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))) ; |
| static volatile struct emac_desc rx_ring_1[NUM_RX_BUFS] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| static volatile u8 tx_buf_1[TX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| static volatile u8 rx_bufs_1[NUM_RX_BUFS * RX_BUF_SIZE] __attribute__ ((aligned (ARC_DCACHE_LINE_LEN))); |
| |
| static struct br_private __g_br_priv = {{0}}; |
| static struct eth_device __g_br_dev; |
| static uint32_t emac_mdio_read(struct emac_private *priv, uint8_t reg); |
| static int emac_mdio_write(struct emac_private *priv, uint8_t reg, uint16_t val); |
| static struct emac_private_mdio __g_emac_mdio[2] = { |
| { |
| .base = ARASAN_MDIO_BASE, |
| .read = &emac_mdio_read, |
| .write = &emac_mdio_write, |
| }, |
| { |
| .base = ARASAN_MDIO_BASE, |
| .read = &emac_mdio_read, |
| .write = &emac_mdio_write, |
| } |
| }; |
| |
| #undef ENABLE_LOOPBACK |
| #ifdef ENABLE_LOOPBACK |
| static uint32_t loopback = 0 ; |
| #endif |
| |
| static struct emac_private __g_emac_priv[2] = { |
| { |
| .tx_bufs = (uintptr_t) &tx_buf_0[0], |
| .rx_bufs = (uintptr_t) &rx_bufs_0[0], |
| .tx_descs = &tx_ring_0[0], |
| .rx_descs = &rx_ring_0[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, |
| .emac = 0, |
| .io_base= RUBY_ENET0_BASE_ADDR, |
| .mdio = &__g_emac_mdio[0], |
| .parent = &__g_br_dev, |
| }, |
| { |
| .tx_bufs = (uintptr_t) &tx_buf_1[0], |
| .rx_bufs = (uintptr_t) &rx_bufs_1[0], |
| .tx_descs = &tx_ring_1[0], |
| .rx_descs = &rx_ring_1[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, |
| .emac = 1, |
| .io_base = RUBY_ENET1_BASE_ADDR, |
| .mdio = &__g_emac_mdio[1], |
| .parent = &__g_br_dev, |
| } |
| }; |
| |
| /* Utility functions for reading/writing registers in the Ethernet MAC */ |
| |
| static uint32_t emac_rdreg(uintptr_t base, int reg) |
| { |
| return readl(base + reg); |
| } |
| |
| static void emac_wrreg(uintptr_t base, int reg, uint32_t val) |
| { |
| writel(val, base + reg); |
| } |
| |
| static void emac_setbits(uintptr_t base, int reg, uint32_t val) |
| { |
| emac_wrreg(base, reg, emac_rdreg(base, reg) | val); |
| } |
| |
| static void emac_clrbits(uintptr_t base, int reg, uint32_t 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(uintptr_t base) |
| { |
| return !(emac_rdreg(base, EMAC_MAC_MDIO_CTRL) & 0x8000); |
| } |
| |
| int mdio_poll_complete(uintptr_t base) |
| { |
| int i = 0; |
| |
| while (!mdio_operation_complete(base)) { |
| udelay(1000); |
| if (++i >= 2000) { |
| printf("mdio timeout\n"); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void mdio_postrd_raw(uintptr_t base, int phy, int reg) |
| { |
| if (mdio_poll_complete(base)) { |
| return; |
| } |
| |
| emac_wrreg(base, EMAC_MAC_MDIO_CTRL, (phy & 31) | |
| ((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) | |
| ((reg & 31) << 5) | (1 << 10) | (1 << 15)); |
| } |
| |
| int mdio_postwr_raw(uintptr_t base, int phy, int reg, u16 val) |
| { |
| if (mdio_poll_complete(base)) { |
| return -1; |
| } |
| emac_wrreg(base, EMAC_MAC_MDIO_DATA, val); |
| emac_wrreg(base, EMAC_MAC_MDIO_CTRL, (phy & 31) | |
| ((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) | |
| ((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 MDIO_READ_FAIL 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. |
| */ |
| uint32_t mdio_rdval_raw(uintptr_t base, int wait) |
| { |
| if (wait) { |
| mdio_poll_complete(base); |
| } |
| if (!mdio_operation_complete(base)) { |
| printf("GMII: MDIO read timed out (%08x)\n", |
| emac_rdreg(base, EMAC_MAC_MDIO_CTRL)); |
| return MDIO_READ_FAIL; |
| } |
| return (uint32_t)emac_rdreg(base, EMAC_MAC_MDIO_DATA); |
| } |
| |
| static uint32_t emac_mdio_read(struct emac_private *priv, uint8_t reg) |
| { |
| uint32_t val; |
| |
| mdio_postrd_raw(priv->mdio->base, priv->phy_addr, reg); |
| if ((val = mdio_rdval_raw(priv->mdio->base, 1)) == MDIO_READ_FAIL) { |
| return MDIO_READ_FAIL; |
| } |
| |
| return val; |
| } |
| |
| static int emac_mdio_write(struct emac_private *priv, uint8_t reg, uint16_t val) |
| { |
| return mdio_postwr_raw(priv->mdio->base, priv->phy_addr, reg, val); |
| } |
| |
| /* 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_stop_dma(uint32_t base) |
| { |
| int i = 0; |
| uint32_t 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)); |
| } |
| |
| |
| |
| static void emac_reset_dma(uint32_t base) |
| { |
| emac_stop_dma(base); |
| |
| /* 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); |
| } |
| |
| #ifndef RUBY_MINI |
| static int rx_stats[] = { 0, 1, 2, 3, 4, 5, 6 }; |
| #define NUM_RX_STATS (ARRAY_SIZE(rx_stats)) |
| |
| static int tx_stats[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
| #define NUM_TX_STATS (ARRAY_SIZE(tx_stats)) |
| |
| static void emac_stats(struct emac_private * priv) |
| { |
| int i; |
| uint32_t val; |
| int base = priv->io_base; |
| int counter; |
| unsigned 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); |
| } |
| } |
| |
| static void emacs_stats(void) |
| { |
| struct br_private * br_priv = &__g_br_priv; |
| int i; |
| |
| for (i = 0; i < br_priv->nemacs; i++) { |
| struct emac_private * emac_priv = br_priv->emacs[i]; |
| if (emac_priv) { |
| emac_stats(emac_priv); |
| } |
| } |
| } |
| #endif |
| |
| |
| static int emac_reset(struct emac_private * priv) |
| { |
| unsigned int i; |
| uintptr_t base; |
| uint16_t macaddr16[3]; |
| uint16_t rx_desc_size = 0; |
| uint16_t tx_ring_size = 0; |
| uint16_t rx_ring_size = 0; |
| struct eth_device * br = priv->parent; |
| |
| base = priv->io_base; |
| memcpy(macaddr16, br->enetaddr, sizeof(macaddr16)); |
| emac_reset_dma(base); |
| |
| /* Initialise the buffer descriptors for Tx */ |
| priv->tx_descs[0].status = 0; |
| priv->tx_descs[0].control = TxDescEndOfRing; |
| priv->tx_descs[0].bufaddr1 = virt_to_bus((void *)priv->tx_bufs); |
| priv->tx_descs[0].bufaddr2 = 0; |
| |
| if (priv->emac){ |
| rx_desc_size = ARRAY_SIZE(rx_ring_1); |
| } else { |
| rx_desc_size = ARRAY_SIZE(rx_ring_0); |
| } |
| /* Initialise the buffer descriptors for Rx */ |
| for (i = 0; i < rx_desc_size; i++) { |
| priv->rx_descs[i].status = RxDescOwn; |
| priv->rx_descs[i].control = (RX_BUF_SIZE & RxDescBuf1SizeMask) |
| << RxDescBuf1SizeShift; |
| priv->rx_descs[i].bufaddr1 = virt_to_bus((void *)((int)priv->rx_bufs + (i * RX_BUF_SIZE))); |
| priv->rx_descs[i].bufaddr2 = 0; |
| } |
| if (i > 0) { |
| priv->rx_descs[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 *)(uintptr_t) priv->tx_descs)); |
| emac_wrreg(base, EMAC_DMA_RX_BASE_ADDR, virt_to_bus((void *)(uintptr_t) priv->rx_descs)); |
| emac_rdreg(base, EMAC_DMA_MISSED_FRAMES); |
| emac_rdreg(base, EMAC_DMA_STOP_FLUSHES); |
| #ifdef EMAC_SWITCH |
| emac_wrreg(base, EMAC_MAC_ADDR_CTRL, MacAddr1Enable | MacPromiscuous); |
| #else |
| emac_wrreg(base, EMAC_MAC_ADDR_CTRL, MacAddr1Enable); |
| #endif |
| emac_wrreg(base, EMAC_MAC_ADDR1_HIGH, macaddr16[0]); |
| emac_wrreg(base, EMAC_MAC_FLOW_SA_HIGH, macaddr16[0]); |
| emac_wrreg(base, EMAC_MAC_ADDR1_MED, macaddr16[1]); |
| emac_wrreg(base, EMAC_MAC_FLOW_SA_MED, macaddr16[1]); |
| emac_wrreg(base, EMAC_MAC_ADDR1_LOW, macaddr16[2]); |
| emac_wrreg(base, EMAC_MAC_FLOW_SA_LOW, macaddr16[2]); |
| |
| /* !!! 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->emac){ |
| tx_ring_size = sizeof(tx_ring_1); |
| rx_ring_size = sizeof(rx_ring_1); |
| } else { |
| tx_ring_size = sizeof(tx_ring_0); |
| rx_ring_size = sizeof(rx_ring_0); |
| } |
| |
| emac_cache_flush((uintptr_t) priv->tx_descs, (uintptr_t) priv->tx_descs + tx_ring_size); |
| emac_cache_flush((uintptr_t) priv->rx_descs, (uintptr_t) priv->rx_descs + rx_ring_size); |
| |
| /* Setup speed and run! */ |
| if (!emac_phy_init(priv)) { |
| return 0; |
| } |
| |
| /* Setup speed and run! */ |
| 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 emac_private *priv) |
| { |
| int base; |
| |
| if (priv) { |
| base = priv->io_base; |
| emac_clrbits(base, EMAC_MAC_TX_CTRL, MacTxEnable); |
| emac_clrbits(base, EMAC_MAC_RX_CTRL, MacRxEnable); |
| emac_stop_dma(base); |
| } |
| } |
| |
| static int emac_send(struct emac_private * priv, volatile void *packet, int length) |
| { |
| volatile struct emac_desc *ptx_desc; |
| uint32_t base, now; |
| |
| base = priv->io_base; |
| |
| 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; |
| } |
| |
| |
| |
| #ifdef EMAC_SWITCH |
| extern int memcmp(const void *, const void *, __kernel_size_t); |
| |
| static int emac_forward(struct emac_private * priv, volatile unsigned char *packet, int length) |
| { |
| int ret; |
| struct eth_device * br = priv->parent; |
| struct br_private * br_priv = br->priv; |
| if ((ret=memcmp((const void *)packet, br->enetaddr, 6))) { |
| int i; |
| if (MAC_IS_MULTICAST(packet)) |
| ret = 0; |
| for (i = 0; i < br_priv->nemacs; i++){ |
| struct emac_private * emac_priv = br_priv->emacs[i]; |
| if ((emac_priv) && (priv->emac!=emac_priv->emac) && br_priv->dev_state & (1<<i)) { |
| emac_send(emac_priv, packet, length); |
| } |
| } |
| } |
| return ret; |
| } |
| #endif |
| |
| static int emac_recv(struct emac_private *priv) |
| { |
| volatile struct emac_desc *prx_desc; |
| uint32_t base, length; |
| volatile uint32_t status; |
| |
| if (priv == NULL) |
| return 0; |
| |
| base = priv->io_base; |
| prx_desc = priv->rx_descs + priv->rx_index; |
| status = readl(&prx_desc->status); |
| if (status & RxDescOwn) { |
| 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(priv, ARC_SRAM_ADDRESS(prx_desc->bufaddr1), length); |
| } else { |
| NetReceive((unsigned char *)bus_to_virt(prx_desc->bufaddr1), length); |
| } |
| #else |
| #ifdef EMAC_SWITCH |
| if (emac_forward(priv, bus_to_virt(prx_desc->bufaddr1), length)==0) |
| NetReceive((unsigned char *)bus_to_virt(prx_desc->bufaddr1), length); |
| #else |
| NetReceive((unsigned char *)bus_to_virt(prx_desc->bufaddr1), length); |
| #endif |
| #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; |
| } |
| |
| #define PHY_CHECK_RETRY 10 |
| static int br_init(struct eth_device *dev, bd_t *bd) |
| { |
| struct br_private *const priv = dev->priv; |
| int i, j; |
| |
| priv->dev_state = 0; |
| priv->send_mask = 0; |
| for (j = 0; j < PHY_CHECK_RETRY; j++) { |
| for (i = 0; i < priv->nemacs; i++) { |
| if (emac_reset(priv->emacs[i])) { |
| priv->dev_state |= (1 << i); |
| priv->send_mask |= (1 << i); |
| } |
| if (had_ctrlc()) |
| goto out; |
| } |
| if (priv->dev_state) |
| break; |
| } |
| out: |
| #ifdef EMAC_BOND_MASTER |
| #if (EMAC_BOND_MASTER == 0) |
| priv->send_mask &= 1; |
| #elif (EMAC_BOND_MASTER == 1) |
| priv->send_mask &= 2; |
| #endif |
| #endif |
| return !!priv->dev_state; |
| } |
| |
| static void br_halt(struct eth_device *dev) |
| { |
| struct br_private *const priv = dev->priv; |
| int i; |
| |
| for (i = 0; i < priv->nemacs; i++) { |
| emac_halt(priv->emacs[i]); |
| } |
| priv->dev_state = 0; |
| priv->send_mask = 0; |
| } |
| |
| static int br_send(struct eth_device *dev, volatile void *packet, int length) |
| { |
| struct br_private *const priv = dev->priv; |
| int i; |
| int ret = 0; |
| |
| for (i = 0; i < priv->nemacs; i++) { |
| if ((priv->dev_state & (1 << i)) && (priv->send_mask & (1 << i))) { |
| int rc; |
| struct emac_private *emac_priv = priv->emacs[i]; |
| if (emac_priv) { |
| rc = emac_send(emac_priv, (void *) packet, length); |
| if (rc) { |
| ret = rc; |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int br_recv(struct eth_device *dev) |
| { |
| struct br_private *const priv = dev->priv; |
| int i; |
| int ret = 0; |
| |
| for (i = 0; i < priv->nemacs; i++) { |
| if (priv->dev_state & (1 << i)) { |
| int rc = 0; |
| rc = emac_recv(priv->emacs[i]); |
| if (rc) { |
| ret = rc; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| uint32_t probe_phy(struct emac_private *priv) |
| { |
| uint32_t val; |
| |
| if ((val = priv->mdio->read(priv, PhyBMCR)) == MDIO_READ_FAIL) { |
| printf("Probe PHY Failed\n"); |
| return -1; |
| } |
| |
| return val; |
| } |
| |
| static void arasan_initialize_gpio_reset_pin(int pin) |
| { |
| printf("emac init GPIO pin %d reset seq\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 |
| static struct eth_device __g_br_dev = { |
| .priv = &__g_br_priv, |
| .init = br_init, |
| .halt = br_halt, |
| .send = br_send, |
| .recv = br_recv, |
| |
| }; |
| |
| static uint32_t get_phy_id(struct emac_private *priv) |
| { |
| int phy_reg; |
| uint32_t phy_id; |
| |
| if ((phy_reg = priv->mdio->read(priv, PhyPhysID1)) == MDIO_READ_FAIL) { |
| printf("Read from MDIO failed.\n"); |
| return -1; |
| } |
| phy_id = (phy_reg & 0xffff) << 16; |
| |
| if ((phy_reg = priv->mdio->read(priv, PhyPhysID2)) == MDIO_READ_FAIL) { |
| printf("Read from MDIO failed.\n"); |
| return -1; |
| } |
| |
| phy_id |= (phy_reg & 0xffff); |
| |
| return phy_id; |
| } |
| |
| static int phy_init(struct emac_private *priv) |
| { |
| unsigned long mdio_base; |
| uint32_t phy_id; |
| struct br_private * br_priv = priv->parent->priv; |
| mdio_base = priv->mdio->base; |
| |
| if (!(priv->phy_flags & EMAC_PHY_NOT_IN_USE)) { |
| int found = 0; |
| int 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 (br_priv->phy_addr_mask & (1 << priv->phy_addr)) |
| continue; |
| if ((id = probe_phy(priv)) != 0xffff) { |
| printf("PHY found on MDIO addr:%x %d\n", id, priv->phy_addr); |
| found = 1; |
| break; |
| } |
| udelay(200); |
| } |
| } else { |
| if (probe_phy(priv) != 0xffff) { |
| printf("PHY found on MDIO addr:%d\n", priv->phy_addr); |
| found = 1; |
| break; |
| } |
| udelay(200); |
| } |
| } |
| if (!found) { |
| printf("No PHY found on MDIO addr:%d\n", priv->phy_addr); |
| return 0; |
| } |
| |
| /* Reset the PHY which then kick-offs the autonegotiation */ |
| #if TOPAZ_FPGA_PLATFORM |
| /* Must reset PHYs in the order listed below - PHY ADDR=(4, 1, 2, 3) |
| Only FPGA-B will bring-up the PHYs and configure them since FPGA-A |
| doesn't have a physical MDIO bus to configure the PHYs */ |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAB_PHY0_ADDR, PhyBMCR, PhySoftReset | PhyAutoNegEnable); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAB_PHY1_ADDR, PhyBMCR, PhySoftReset | PhyAutoNegEnable); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAA_PHY0_ADDR, PhyBMCR, PhySoftReset | PhyAutoNegEnable); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAA_PHY1_ADDR, PhyBMCR, PhySoftReset | PhyAutoNegEnable); |
| #else |
| priv->mdio->write(priv, PhyBMCR, PhySoftReset | PhyAutoNegEnable); |
| #endif /* #if TOPAZ_FPGA_PLATFORM */ |
| |
| //Turn off 125MHz and enable SSC for Realtek(RTL8211E) VB |
| phy_id = get_phy_id(priv); |
| printf("PHY 0x%x found on MDIO addr:%d\n", phy_id, priv->phy_addr); |
| if (phy_id == 0x1cc915) { |
| uint32_t val; |
| priv->mdio->write(priv, 31, 0); |
| priv->mdio->write(priv, 16, 0x17E); |
| priv->mdio->write(priv, 31, 7); |
| priv->mdio->write(priv, 30, 0xA0); |
| if ((val = priv->mdio->read(priv, 26)) == MDIO_READ_FAIL) { |
| printf("u-boot: Failed to read phy reg#26\n"); |
| return 0; |
| } |
| priv->mdio->write(priv, 26, val & ~4); |
| priv->mdio->write(priv, 31, 0); |
| printf("Enable phy SSC\n"); |
| } |
| |
| /* This is a dummy read that ensures the write has happened */ |
| if (mdio_rdval_raw(priv->mdio->base, 1) == MDIO_READ_FAIL) { |
| return 0; |
| } |
| } else if (priv->phy_flags & EMAC_PHY_AR8236) { |
| priv->phy_addr = ar8236_init(mdio_base, priv->phy_addr); |
| } else if (priv->phy_flags & EMAC_PHY_AR8327) { |
| priv->phy_addr = ar8237_init(mdio_base, priv->phy_addr); |
| } |
| |
| if (priv->phy_addr<PHY_MAX_ADDR) |
| br_priv->phy_addr_mask |= (1 << priv->phy_addr); |
| return 1; |
| } |
| static void emac_probe_phy(struct emac_private * priv, uint32_t cfg, uint32_t phy_addr) |
| { |
| struct br_private * br_priv = priv->parent->priv; |
| |
| priv->phy_flags = cfg; |
| priv->phy_addr = phy_addr; |
| |
| if (phy_init(priv)){ |
| emac_halt(priv); |
| br_priv->emacs[br_priv->nemacs++] = priv; |
| } |
| } |
| |
| int board_eth_init(bd_t *bis) |
| { |
| DECLARE_GLOBAL_DATA_PTR; |
| struct eth_device *br_dev; |
| struct br_private *br_priv; |
| struct emac_private *emac_priv = &__g_emac_priv[0]; |
| 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; |
| const char *dbg_bus = getenv("debug_bus"); |
| int i; |
| |
| printf("Resetting QCA8337\n"); |
| gpio_config(7, GPIO_MODE_OUTPUT); |
| gpio_output(7, 1); |
| udelay(10); |
| |
| if (!((emac0_cfg & EMAC_IN_USE) || (emac1_cfg & EMAC_IN_USE))) { |
| printf("error: no emac enabled\n"); |
| return -1; |
| } |
| |
| br_dev = &__g_br_dev; |
| br_priv = &__g_br_priv; |
| br_dev->priv = br_priv; |
| for (i = 0; i < 6; i++) { |
| br_dev->enetaddr[i] = gd->bd->bi_enetaddr[i]; |
| } |
| |
| sprintf(br_dev->name, "br"); |
| |
| arasan_initialize_gpio_reset(emac_cfg); |
| arasan_initialize_release_reset(emac0_cfg, emac1_cfg, rgmii_timing, 1); |
| |
| br_priv->nemacs = 0; |
| br_priv->phy_addr_mask = 0; |
| |
| if (dbg_bus != NULL) { |
| /* clear any other forced speeds possibly set */ |
| emac0_cfg &= ~(EMAC_PHY_FORCE_1000MB | EMAC_PHY_FORCE_100MB | EMAC_PHY_FORCE_10MB); |
| emac1_cfg &= ~(EMAC_PHY_FORCE_1000MB | EMAC_PHY_FORCE_100MB | EMAC_PHY_FORCE_10MB); |
| emac0_cfg |= EMAC_PHY_FORCE_1000MB; |
| emac1_cfg |= EMAC_PHY_FORCE_1000MB; |
| } |
| if (emac0_cfg & EMAC_IN_USE) { |
| emac_priv = &__g_emac_priv[0]; |
| |
| emac_probe_phy(emac_priv, emac0_cfg, board_config(gd->bd->bi_board_id, BOARD_CFG_PHY0_ADDR)); |
| } |
| if (emac1_cfg & EMAC_IN_USE) { |
| emac_priv = &__g_emac_priv[1]; |
| emac_probe_phy(emac_priv, emac1_cfg, board_config(gd->bd->bi_board_id, BOARD_CFG_PHY1_ADDR)); |
| } |
| /* |
| * if debug bus is set in hardware, then mdio is disabled so auto-negotiation |
| * will be set to 10Mbs. default speed will be 1Gbs if debug_bus=1. |
| */ |
| |
| eth_register(br_dev); |
| |
| return 1; |
| } |
| |
| |
| #ifndef RUBY_MINI |
| int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
| { |
| unsigned long base = ARASAN_MDIO_BASE; |
| if (argc < 4) { |
| cmd_usage(cmdtp); |
| return 1; |
| } |
| if (strcmp(argv[1],"read") == 0) { |
| uint32_t a1,a2,val; |
| a1 = simple_strtoul (argv[2], NULL, 10); |
| a2 = simple_strtoul (argv[3], NULL, 10); |
| mdio_postrd_raw(base, a1, a2); |
| val = mdio_rdval_raw(base, 1); |
| printf("phy:%d reg:%d=0x%x\n",a1,a2,val); |
| } else if (strcmp(argv[1],"write") == 0) { |
| uint32_t 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_raw(base, a1, a2, a3); |
| /* This is a dummy read that ensures the write has happened */ |
| if (mdio_rdval_raw(base, 1) == MDIO_READ_FAIL) { |
| return -1; |
| } |
| } else { |
| return -1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| #if defined(CONFIG_CMD_ETHLOOP) |
| int mdio_write(int reg, u16 val) |
| { |
| uint32_t 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) == (uint32_t)-1) |
| return -1; |
| |
| return ret; |
| } |
| |
| int mdio_read(int reg) |
| { |
| uint32_t val, base = ARASAN_MDIO_BASE; |
| mdio_postrd(base, priv.phy_addr, reg); |
| val = mdio_rdval(base, 1); |
| return val; |
| } |
| |
| void enable_phy_loopback(void) |
| { |
| uint32_t val; |
| val = mdio_read(PhyBMCR); |
| val |= PhyLoopback; /* set loopback bit */ |
| mdio_write(PhyBMCR, val); |
| } |
| |
| void disable_phy_loopback(void) |
| { |
| uint32_t val; |
| val = mdio_read(PhyBMCR); |
| val &= ~PhyLoopback; /* reset loopback bit */ |
| mdio_write(PhyBMCR, val); |
| } |
| #endif |
| #ifndef RUBY_MINI |
| #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 |
| #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[]) |
| { |
| emacs_stats(); |
| return 0; |
| } |
| U_BOOT_CMD( |
| emac, CFG_MAXARGS, 3, do_emac, |
| "emac read stats", |
| NULL |
| ); |
| #endif |
| |
| static int emac_phy_init(struct emac_private *priv) |
| { |
| const uintptr_t base = priv->io_base; |
| const int emac = priv->emac; |
| int i; |
| uint32_t val, x; |
| uint32_t advertise; |
| uint32_t lpa = 0; |
| uint32_t lpa2 = 0; |
| uint32_t media = 0; |
| uint32_t duplex = 0; |
| #if (TOPAZ_FPGA_PLATFORM) |
| const uintptr_t mdio_base = priv->mdio->base; |
| #endif |
| if (!(priv->phy_flags & EMAC_PHY_NOT_IN_USE)) { |
| if (priv->phy_flags & EMAC_PHY_RESET) { |
| udelay(4); |
| } |
| if (priv->phy_flags & EMAC_PHY_RTL8211DS) { |
| int link_status = 0; |
| int speed_sel = 0; |
| duplex = 0; |
| lpa2 = 0; |
| media = 0; |
| priv->mdio->write(priv, RTL8211DS_PAGSEL, PAGSEL_PAGEXT); |
| priv->mdio->write(priv, RTL8211DS_PAGNO, PAGNO_140); |
| link_status = priv->mdio->read(priv, RTL8211DS_RXCONFIG); |
| if (link_status & RXCONFIG_LINK) { |
| speed_sel = priv->mdio->read(priv, RTL8211DS_SDSR); |
| speed_sel &= ~SDSR_SPEED; |
| switch ((link_status & RXCONFIG_SPEED) >> RXCONFIG_SPEED_BIT) { |
| case 0: |
| //printf(": 0x%x, 10-", speed_sel); |
| speed_sel |= SDSR_10; |
| break; |
| case 1: |
| //printf(": 0x%x, 100-", speed_sel); |
| media = ADVERTISE_100FULL; |
| speed_sel |= SDSR_100; |
| break; |
| case 2: |
| //printf(": 0x%x, 1000-", speed_sel); |
| lpa2 = LPA_1000FULL; |
| speed_sel |= SDSR_1000; |
| break; |
| default: |
| return 0; |
| } |
| if (link_status & RXCONFIG_DUPLEX) { |
| //printf("Full: 0x%x\n",speed_sel); |
| duplex = 1; |
| } |
| priv->mdio->write(priv, RTL8211DS_SDSR, speed_sel); |
| priv->mdio->write(priv, RTL8211DS_PAGSEL, PAGSEL_PAG0); |
| } |
| else { |
| printf("SGMIILinkDown\n"); |
| priv->mdio->write(priv, RTL8211DS_PAGSEL, PAGSEL_PAG0); |
| return 0; |
| } |
| } else 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("AutoNegoEMAC%d\n",priv->emac); |
| |
| i = 0; |
| do { |
| /* Wait for autonegotiation to complete */ |
| udelay(500); |
| if ((val = priv->mdio->read(priv, PhyBMSR)) == MDIO_READ_FAIL) { |
| return 0; |
| } |
| i++; |
| |
| if (i >= 30000) { |
| printf("AutoNegoTimeout\n"); |
| return 0; |
| } |
| if (!(val & PhyLinkIsUp) && (i >= 5000)) { |
| printf("LinkDown\n"); |
| return 0; |
| } |
| if (ctrlc()) { |
| printf("Ctrl+C detected\n"); |
| return 0; |
| } |
| } while (!(val & PhyAutoNegComplete)); |
| |
| printf("Autonegotiation Done (BMSR=%08x)\n", val); |
| |
| /* Work out autonegotiation result using only IEEE registers */ |
| if ((advertise = priv->mdio->read(priv, PhyAdvertise)) == MDIO_READ_FAIL) { |
| printf("GMII: Failed to read Advertise reg\n"); |
| return 0; |
| } |
| |
| if ((lpa = priv->mdio->read(priv, PhyLpa)) == MDIO_READ_FAIL) { |
| printf("GMII: Failed to read Lpa reg\n"); |
| return 0; |
| } |
| |
| if ((lpa2 = priv->mdio->read(priv, PhyStat1000)) == MDIO_READ_FAIL) { |
| printf("GMII: Failed to read Lpa2 reg\n"); |
| return 0; |
| } |
| |
| 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; |
| } |
| |
| } else { |
| 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; |
| } |
| |
| /* Force link speed & duplex */ |
| #if (TOPAZ_FPGA_PLATFORM) |
| /* |
| * Must reset PHYs in the order listed below - PHY ADDR=(4, 1, 2, 3) |
| * Only FPGA-B will bring-up the PHYs and configure them since FPGA-A |
| * doesn't have a physical MDIO bus to configure the PHYs |
| */ |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAB_PHY0_ADDR, PhyBMCR, i); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAB_PHY1_ADDR, PhyBMCR, i); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAA_PHY0_ADDR, PhyBMCR, i); |
| mdio_postwr_raw(mdio_base, TOPAZ_FPGAA_PHY1_ADDR, PhyBMCR, i); |
| #else |
| priv->mdio->write(priv, PhyBMCR, i); |
| i = 0; |
| do { |
| /* Wait for phy linkup */ |
| udelay(500); |
| if ((val = priv->mdio->read(priv, PhyBMSR)) == MDIO_READ_FAIL) { |
| return 0; |
| } |
| i++; |
| |
| if (!(val & PhyLinkIsUp) && (i >= 3000)) { |
| printf("LinkDown\n"); |
| return 0; |
| } |
| if (ctrlc()) { |
| printf("Ctrl+C detected\n"); |
| return 0; |
| } |
| } while (!(val & PhyLinkIsUp)); |
| #endif |
| |
| printf("GMII: Forced link speed is "); |
| } |
| } else { // no phy |
| 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, |
| emac ? RUBY_SYS_CTL_MASK_GMII1_TXCLK : RUBY_SYS_CTL_MASK_GMII0_TXCLK); |
| |
| if (lpa2 & (LPA_1000FULL | LPA_1000HALF)) { |
| x |= MacSpeed1G; |
| printf("1G"); |
| emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL, |
| emac ? RUBY_SYS_CTL_MASK_GMII1_1000M : RUBY_SYS_CTL_MASK_GMII0_1000M); |
| |
| } |
| else if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) { |
| x |= MacSpeed100M; |
| printf("100M"); |
| emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL, |
| emac ? RUBY_SYS_CTL_MASK_GMII1_100M : RUBY_SYS_CTL_MASK_GMII0_100M); |
| } |
| else { |
| x |= MacSpeed10M; |
| printf("10M"); |
| emac_wrreg(RUBY_SYS_CTL_BASE_ADDR, SYSCTRL_CTRL, |
| emac ? RUBY_SYS_CTL_MASK_GMII1_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"); |
| } |
| |
| |
| emac_wrreg(base, EMAC_MAC_GLOBAL_CTRL, x); |
| |
| return 1; |
| } |
| #endif |