12439e4bfSJean-Christophe PLAGNIOL-VILLARD /* 22439e4bfSJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2005-2006 Atmel Corporation 32439e4bfSJean-Christophe PLAGNIOL-VILLARD * 41a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 52439e4bfSJean-Christophe PLAGNIOL-VILLARD */ 62439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <common.h> 72439e4bfSJean-Christophe PLAGNIOL-VILLARD 82439e4bfSJean-Christophe PLAGNIOL-VILLARD /* 92439e4bfSJean-Christophe PLAGNIOL-VILLARD * The u-boot networking stack is a little weird. It seems like the 102439e4bfSJean-Christophe PLAGNIOL-VILLARD * networking core allocates receive buffers up front without any 112439e4bfSJean-Christophe PLAGNIOL-VILLARD * regard to the hardware that's supposed to actually receive those 122439e4bfSJean-Christophe PLAGNIOL-VILLARD * packets. 132439e4bfSJean-Christophe PLAGNIOL-VILLARD * 142439e4bfSJean-Christophe PLAGNIOL-VILLARD * The MACB receives packets into 128-byte receive buffers, so the 152439e4bfSJean-Christophe PLAGNIOL-VILLARD * buffers allocated by the core isn't very practical to use. We'll 162439e4bfSJean-Christophe PLAGNIOL-VILLARD * allocate our own, but we need one such buffer in case a packet 172439e4bfSJean-Christophe PLAGNIOL-VILLARD * wraps around the DMA ring so that we have to copy it. 182439e4bfSJean-Christophe PLAGNIOL-VILLARD * 196d0f6bcfSJean-Christophe PLAGNIOL-VILLARD * Therefore, define CONFIG_SYS_RX_ETH_BUFFER to 1 in the board-specific 202439e4bfSJean-Christophe PLAGNIOL-VILLARD * configuration header. This way, the core allocates one RX buffer 212439e4bfSJean-Christophe PLAGNIOL-VILLARD * and one TX buffer, each of which can hold a ethernet packet of 222439e4bfSJean-Christophe PLAGNIOL-VILLARD * maximum size. 232439e4bfSJean-Christophe PLAGNIOL-VILLARD * 242439e4bfSJean-Christophe PLAGNIOL-VILLARD * For some reason, the networking core unconditionally specifies a 252439e4bfSJean-Christophe PLAGNIOL-VILLARD * 32-byte packet "alignment" (which really should be called 262439e4bfSJean-Christophe PLAGNIOL-VILLARD * "padding"). MACB shouldn't need that, but we'll refrain from any 272439e4bfSJean-Christophe PLAGNIOL-VILLARD * core modifications here... 282439e4bfSJean-Christophe PLAGNIOL-VILLARD */ 292439e4bfSJean-Christophe PLAGNIOL-VILLARD 302439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <net.h> 3189973f8aSBen Warren #include <netdev.h> 322439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <malloc.h> 330f751d6eSSemih Hazar #include <miiphy.h> 342439e4bfSJean-Christophe PLAGNIOL-VILLARD 352439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <linux/mii.h> 362439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h> 372439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <asm/dma-mapping.h> 382439e4bfSJean-Christophe PLAGNIOL-VILLARD #include <asm/arch/clk.h> 398314ccd8SBo Shen #include <asm-generic/errno.h> 402439e4bfSJean-Christophe PLAGNIOL-VILLARD 412439e4bfSJean-Christophe PLAGNIOL-VILLARD #include "macb.h" 422439e4bfSJean-Christophe PLAGNIOL-VILLARD 43ceef983bSAndreas Bießmann #define MACB_RX_BUFFER_SIZE 4096 44ceef983bSAndreas Bießmann #define MACB_RX_RING_SIZE (MACB_RX_BUFFER_SIZE / 128) 45ceef983bSAndreas Bießmann #define MACB_TX_RING_SIZE 16 46ceef983bSAndreas Bießmann #define MACB_TX_TIMEOUT 1000 47ceef983bSAndreas Bießmann #define MACB_AUTONEG_TIMEOUT 5000000 482439e4bfSJean-Christophe PLAGNIOL-VILLARD 492439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_dma_desc { 502439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 addr; 512439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 ctrl; 522439e4bfSJean-Christophe PLAGNIOL-VILLARD }; 532439e4bfSJean-Christophe PLAGNIOL-VILLARD 545ae0e382SWu, Josh #define DMA_DESC_BYTES(n) (n * sizeof(struct macb_dma_desc)) 555ae0e382SWu, Josh #define MACB_TX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_TX_RING_SIZE)) 565ae0e382SWu, Josh #define MACB_RX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_RX_RING_SIZE)) 575ae0e382SWu, Josh 582439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXADDR_USED 0x00000001 592439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXADDR_WRAP 0x00000002 602439e4bfSJean-Christophe PLAGNIOL-VILLARD 612439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_FRMLEN_MASK 0x00000fff 622439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_FRAME_START 0x00004000 632439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_FRAME_END 0x00008000 642439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_TYPEID_MATCH 0x00400000 652439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_ADDR4_MATCH 0x00800000 662439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_ADDR3_MATCH 0x01000000 672439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_ADDR2_MATCH 0x02000000 682439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_ADDR1_MATCH 0x04000000 692439e4bfSJean-Christophe PLAGNIOL-VILLARD #define RXBUF_BROADCAST 0x80000000 702439e4bfSJean-Christophe PLAGNIOL-VILLARD 712439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_FRMLEN_MASK 0x000007ff 722439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_FRAME_END 0x00008000 732439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_NOCRC 0x00010000 742439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_EXHAUSTED 0x08000000 752439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_UNDERRUN 0x10000000 762439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_MAXRETRY 0x20000000 772439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_WRAP 0x40000000 782439e4bfSJean-Christophe PLAGNIOL-VILLARD #define TXBUF_USED 0x80000000 792439e4bfSJean-Christophe PLAGNIOL-VILLARD 802439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device { 812439e4bfSJean-Christophe PLAGNIOL-VILLARD void *regs; 822439e4bfSJean-Christophe PLAGNIOL-VILLARD 832439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int rx_tail; 842439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int tx_head; 852439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int tx_tail; 862439e4bfSJean-Christophe PLAGNIOL-VILLARD 872439e4bfSJean-Christophe PLAGNIOL-VILLARD void *rx_buffer; 882439e4bfSJean-Christophe PLAGNIOL-VILLARD void *tx_buffer; 892439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_dma_desc *rx_ring; 902439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_dma_desc *tx_ring; 912439e4bfSJean-Christophe PLAGNIOL-VILLARD 922439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long rx_buffer_dma; 932439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long rx_ring_dma; 942439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long tx_ring_dma; 952439e4bfSJean-Christophe PLAGNIOL-VILLARD 962439e4bfSJean-Christophe PLAGNIOL-VILLARD const struct device *dev; 972439e4bfSJean-Christophe PLAGNIOL-VILLARD struct eth_device netdev; 982439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned short phy_addr; 99b1a0006eSBo Shen struct mii_dev *bus; 1002439e4bfSJean-Christophe PLAGNIOL-VILLARD }; 1012439e4bfSJean-Christophe PLAGNIOL-VILLARD #define to_macb(_nd) container_of(_nd, struct macb_device, netdev) 1022439e4bfSJean-Christophe PLAGNIOL-VILLARD 103d256be29SBo Shen static int macb_is_gem(struct macb_device *macb) 104d256be29SBo Shen { 105d256be29SBo Shen return MACB_BFEXT(IDNUM, macb_readl(macb, MID)) == 0x2; 106d256be29SBo Shen } 107d256be29SBo Shen 1082439e4bfSJean-Christophe PLAGNIOL-VILLARD static void macb_mdio_write(struct macb_device *macb, u8 reg, u16 value) 1092439e4bfSJean-Christophe PLAGNIOL-VILLARD { 1102439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long netctl; 1112439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long netstat; 1122439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long frame; 1132439e4bfSJean-Christophe PLAGNIOL-VILLARD 1142439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl = macb_readl(macb, NCR); 1152439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl |= MACB_BIT(MPE); 1162439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, netctl); 1172439e4bfSJean-Christophe PLAGNIOL-VILLARD 1182439e4bfSJean-Christophe PLAGNIOL-VILLARD frame = (MACB_BF(SOF, 1) 1192439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(RW, 1) 1202439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(PHYA, macb->phy_addr) 1212439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(REGA, reg) 1222439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(CODE, 2) 1232439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(DATA, value)); 1242439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, MAN, frame); 1252439e4bfSJean-Christophe PLAGNIOL-VILLARD 1262439e4bfSJean-Christophe PLAGNIOL-VILLARD do { 1272439e4bfSJean-Christophe PLAGNIOL-VILLARD netstat = macb_readl(macb, NSR); 1282439e4bfSJean-Christophe PLAGNIOL-VILLARD } while (!(netstat & MACB_BIT(IDLE))); 1292439e4bfSJean-Christophe PLAGNIOL-VILLARD 1302439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl = macb_readl(macb, NCR); 1312439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl &= ~MACB_BIT(MPE); 1322439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, netctl); 1332439e4bfSJean-Christophe PLAGNIOL-VILLARD } 1342439e4bfSJean-Christophe PLAGNIOL-VILLARD 1352439e4bfSJean-Christophe PLAGNIOL-VILLARD static u16 macb_mdio_read(struct macb_device *macb, u8 reg) 1362439e4bfSJean-Christophe PLAGNIOL-VILLARD { 1372439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long netctl; 1382439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long netstat; 1392439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long frame; 1402439e4bfSJean-Christophe PLAGNIOL-VILLARD 1412439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl = macb_readl(macb, NCR); 1422439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl |= MACB_BIT(MPE); 1432439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, netctl); 1442439e4bfSJean-Christophe PLAGNIOL-VILLARD 1452439e4bfSJean-Christophe PLAGNIOL-VILLARD frame = (MACB_BF(SOF, 1) 1462439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(RW, 2) 1472439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(PHYA, macb->phy_addr) 1482439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(REGA, reg) 1492439e4bfSJean-Christophe PLAGNIOL-VILLARD | MACB_BF(CODE, 2)); 1502439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, MAN, frame); 1512439e4bfSJean-Christophe PLAGNIOL-VILLARD 1522439e4bfSJean-Christophe PLAGNIOL-VILLARD do { 1532439e4bfSJean-Christophe PLAGNIOL-VILLARD netstat = macb_readl(macb, NSR); 1542439e4bfSJean-Christophe PLAGNIOL-VILLARD } while (!(netstat & MACB_BIT(IDLE))); 1552439e4bfSJean-Christophe PLAGNIOL-VILLARD 1562439e4bfSJean-Christophe PLAGNIOL-VILLARD frame = macb_readl(macb, MAN); 1572439e4bfSJean-Christophe PLAGNIOL-VILLARD 1582439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl = macb_readl(macb, NCR); 1592439e4bfSJean-Christophe PLAGNIOL-VILLARD netctl &= ~MACB_BIT(MPE); 1602439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, netctl); 1612439e4bfSJean-Christophe PLAGNIOL-VILLARD 1622439e4bfSJean-Christophe PLAGNIOL-VILLARD return MACB_BFEXT(DATA, frame); 1632439e4bfSJean-Christophe PLAGNIOL-VILLARD } 1642439e4bfSJean-Christophe PLAGNIOL-VILLARD 1651b8c18b9SJoe Hershberger void __weak arch_get_mdio_control(const char *name) 166416ce623SShiraz Hashim { 167416ce623SShiraz Hashim return; 168416ce623SShiraz Hashim } 169416ce623SShiraz Hashim 170b1a0006eSBo Shen #if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) 1710f751d6eSSemih Hazar 1725700bb63SMike Frysinger int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value) 1730f751d6eSSemih Hazar { 1740f751d6eSSemih Hazar struct eth_device *dev = eth_get_dev_by_name(devname); 1750f751d6eSSemih Hazar struct macb_device *macb = to_macb(dev); 1760f751d6eSSemih Hazar 1770f751d6eSSemih Hazar if (macb->phy_addr != phy_adr) 1780f751d6eSSemih Hazar return -1; 1790f751d6eSSemih Hazar 180416ce623SShiraz Hashim arch_get_mdio_control(devname); 1810f751d6eSSemih Hazar *value = macb_mdio_read(macb, reg); 1820f751d6eSSemih Hazar 1830f751d6eSSemih Hazar return 0; 1840f751d6eSSemih Hazar } 1850f751d6eSSemih Hazar 1865700bb63SMike Frysinger int macb_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value) 1870f751d6eSSemih Hazar { 1880f751d6eSSemih Hazar struct eth_device *dev = eth_get_dev_by_name(devname); 1890f751d6eSSemih Hazar struct macb_device *macb = to_macb(dev); 1900f751d6eSSemih Hazar 1910f751d6eSSemih Hazar if (macb->phy_addr != phy_adr) 1920f751d6eSSemih Hazar return -1; 1930f751d6eSSemih Hazar 194416ce623SShiraz Hashim arch_get_mdio_control(devname); 1950f751d6eSSemih Hazar macb_mdio_write(macb, reg, value); 1960f751d6eSSemih Hazar 1970f751d6eSSemih Hazar return 0; 1980f751d6eSSemih Hazar } 1990f751d6eSSemih Hazar #endif 2000f751d6eSSemih Hazar 2015ae0e382SWu, Josh #define RX 1 2025ae0e382SWu, Josh #define TX 0 2035ae0e382SWu, Josh static inline void macb_invalidate_ring_desc(struct macb_device *macb, bool rx) 2045ae0e382SWu, Josh { 2055ae0e382SWu, Josh if (rx) 2065ae0e382SWu, Josh invalidate_dcache_range(macb->rx_ring_dma, macb->rx_ring_dma + 2075ae0e382SWu, Josh MACB_RX_DMA_DESC_SIZE); 2085ae0e382SWu, Josh else 2095ae0e382SWu, Josh invalidate_dcache_range(macb->tx_ring_dma, macb->tx_ring_dma + 2105ae0e382SWu, Josh MACB_TX_DMA_DESC_SIZE); 2115ae0e382SWu, Josh } 2125ae0e382SWu, Josh 2135ae0e382SWu, Josh static inline void macb_flush_ring_desc(struct macb_device *macb, bool rx) 2145ae0e382SWu, Josh { 2155ae0e382SWu, Josh if (rx) 2165ae0e382SWu, Josh flush_dcache_range(macb->rx_ring_dma, macb->rx_ring_dma + 2175ae0e382SWu, Josh MACB_RX_DMA_DESC_SIZE); 2185ae0e382SWu, Josh else 2195ae0e382SWu, Josh flush_dcache_range(macb->tx_ring_dma, macb->tx_ring_dma + 2205ae0e382SWu, Josh MACB_TX_DMA_DESC_SIZE); 2215ae0e382SWu, Josh } 2225ae0e382SWu, Josh 2235ae0e382SWu, Josh static inline void macb_flush_rx_buffer(struct macb_device *macb) 2245ae0e382SWu, Josh { 2255ae0e382SWu, Josh flush_dcache_range(macb->rx_buffer_dma, macb->rx_buffer_dma + 2265ae0e382SWu, Josh MACB_RX_BUFFER_SIZE); 2275ae0e382SWu, Josh } 2285ae0e382SWu, Josh 2295ae0e382SWu, Josh static inline void macb_invalidate_rx_buffer(struct macb_device *macb) 2305ae0e382SWu, Josh { 2315ae0e382SWu, Josh invalidate_dcache_range(macb->rx_buffer_dma, macb->rx_buffer_dma + 2325ae0e382SWu, Josh MACB_RX_BUFFER_SIZE); 2335ae0e382SWu, Josh } 2340f751d6eSSemih Hazar 2352439e4bfSJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_CMD_NET) 2362439e4bfSJean-Christophe PLAGNIOL-VILLARD 2379d9a89beSJoe Hershberger static int macb_send(struct eth_device *netdev, void *packet, int length) 2382439e4bfSJean-Christophe PLAGNIOL-VILLARD { 2392439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device *macb = to_macb(netdev); 2402439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long paddr, ctrl; 2412439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int tx_head = macb->tx_head; 2422439e4bfSJean-Christophe PLAGNIOL-VILLARD int i; 2432439e4bfSJean-Christophe PLAGNIOL-VILLARD 2442439e4bfSJean-Christophe PLAGNIOL-VILLARD paddr = dma_map_single(packet, length, DMA_TO_DEVICE); 2452439e4bfSJean-Christophe PLAGNIOL-VILLARD 2462439e4bfSJean-Christophe PLAGNIOL-VILLARD ctrl = length & TXBUF_FRMLEN_MASK; 2472439e4bfSJean-Christophe PLAGNIOL-VILLARD ctrl |= TXBUF_FRAME_END; 248ceef983bSAndreas Bießmann if (tx_head == (MACB_TX_RING_SIZE - 1)) { 2492439e4bfSJean-Christophe PLAGNIOL-VILLARD ctrl |= TXBUF_WRAP; 2502439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_head = 0; 251ceef983bSAndreas Bießmann } else { 2522439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_head++; 253ceef983bSAndreas Bießmann } 2542439e4bfSJean-Christophe PLAGNIOL-VILLARD 2552439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_ring[tx_head].ctrl = ctrl; 2562439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_ring[tx_head].addr = paddr; 2572439e4bfSJean-Christophe PLAGNIOL-VILLARD barrier(); 2585ae0e382SWu, Josh macb_flush_ring_desc(macb, TX); 2595ae0e382SWu, Josh /* Do we need check paddr and length is dcache line aligned? */ 2605ae0e382SWu, Josh flush_dcache_range(paddr, paddr + length); 2612439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART)); 2622439e4bfSJean-Christophe PLAGNIOL-VILLARD 2632439e4bfSJean-Christophe PLAGNIOL-VILLARD /* 2642439e4bfSJean-Christophe PLAGNIOL-VILLARD * I guess this is necessary because the networking core may 2652439e4bfSJean-Christophe PLAGNIOL-VILLARD * re-use the transmit buffer as soon as we return... 2662439e4bfSJean-Christophe PLAGNIOL-VILLARD */ 267ceef983bSAndreas Bießmann for (i = 0; i <= MACB_TX_TIMEOUT; i++) { 2682439e4bfSJean-Christophe PLAGNIOL-VILLARD barrier(); 2695ae0e382SWu, Josh macb_invalidate_ring_desc(macb, TX); 2702439e4bfSJean-Christophe PLAGNIOL-VILLARD ctrl = macb->tx_ring[tx_head].ctrl; 2712439e4bfSJean-Christophe PLAGNIOL-VILLARD if (ctrl & TXBUF_USED) 2722439e4bfSJean-Christophe PLAGNIOL-VILLARD break; 2732439e4bfSJean-Christophe PLAGNIOL-VILLARD udelay(1); 2742439e4bfSJean-Christophe PLAGNIOL-VILLARD } 2752439e4bfSJean-Christophe PLAGNIOL-VILLARD 2762439e4bfSJean-Christophe PLAGNIOL-VILLARD dma_unmap_single(packet, length, paddr); 2772439e4bfSJean-Christophe PLAGNIOL-VILLARD 278ceef983bSAndreas Bießmann if (i <= MACB_TX_TIMEOUT) { 2792439e4bfSJean-Christophe PLAGNIOL-VILLARD if (ctrl & TXBUF_UNDERRUN) 2802439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: TX underrun\n", netdev->name); 2812439e4bfSJean-Christophe PLAGNIOL-VILLARD if (ctrl & TXBUF_EXHAUSTED) 2822439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: TX buffers exhausted in mid frame\n", 2832439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->name); 2842439e4bfSJean-Christophe PLAGNIOL-VILLARD } else { 2852439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: TX timeout\n", netdev->name); 2862439e4bfSJean-Christophe PLAGNIOL-VILLARD } 2872439e4bfSJean-Christophe PLAGNIOL-VILLARD 2882439e4bfSJean-Christophe PLAGNIOL-VILLARD /* No one cares anyway */ 2892439e4bfSJean-Christophe PLAGNIOL-VILLARD return 0; 2902439e4bfSJean-Christophe PLAGNIOL-VILLARD } 2912439e4bfSJean-Christophe PLAGNIOL-VILLARD 2922439e4bfSJean-Christophe PLAGNIOL-VILLARD static void reclaim_rx_buffers(struct macb_device *macb, 2932439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int new_tail) 2942439e4bfSJean-Christophe PLAGNIOL-VILLARD { 2952439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int i; 2962439e4bfSJean-Christophe PLAGNIOL-VILLARD 2972439e4bfSJean-Christophe PLAGNIOL-VILLARD i = macb->rx_tail; 2985ae0e382SWu, Josh 2995ae0e382SWu, Josh macb_invalidate_ring_desc(macb, RX); 3002439e4bfSJean-Christophe PLAGNIOL-VILLARD while (i > new_tail) { 3012439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_ring[i].addr &= ~RXADDR_USED; 3022439e4bfSJean-Christophe PLAGNIOL-VILLARD i++; 303ceef983bSAndreas Bießmann if (i > MACB_RX_RING_SIZE) 3042439e4bfSJean-Christophe PLAGNIOL-VILLARD i = 0; 3052439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3062439e4bfSJean-Christophe PLAGNIOL-VILLARD 3072439e4bfSJean-Christophe PLAGNIOL-VILLARD while (i < new_tail) { 3082439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_ring[i].addr &= ~RXADDR_USED; 3092439e4bfSJean-Christophe PLAGNIOL-VILLARD i++; 3102439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3112439e4bfSJean-Christophe PLAGNIOL-VILLARD 3122439e4bfSJean-Christophe PLAGNIOL-VILLARD barrier(); 3135ae0e382SWu, Josh macb_flush_ring_desc(macb, RX); 3142439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_tail = new_tail; 3152439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3162439e4bfSJean-Christophe PLAGNIOL-VILLARD 3172439e4bfSJean-Christophe PLAGNIOL-VILLARD static int macb_recv(struct eth_device *netdev) 3182439e4bfSJean-Christophe PLAGNIOL-VILLARD { 3192439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device *macb = to_macb(netdev); 3202439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int rx_tail = macb->rx_tail; 3212439e4bfSJean-Christophe PLAGNIOL-VILLARD void *buffer; 3222439e4bfSJean-Christophe PLAGNIOL-VILLARD int length; 3232439e4bfSJean-Christophe PLAGNIOL-VILLARD int wrapped = 0; 3242439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 status; 3252439e4bfSJean-Christophe PLAGNIOL-VILLARD 3262439e4bfSJean-Christophe PLAGNIOL-VILLARD for (;;) { 3275ae0e382SWu, Josh macb_invalidate_ring_desc(macb, RX); 3285ae0e382SWu, Josh 3292439e4bfSJean-Christophe PLAGNIOL-VILLARD if (!(macb->rx_ring[rx_tail].addr & RXADDR_USED)) 3302439e4bfSJean-Christophe PLAGNIOL-VILLARD return -1; 3312439e4bfSJean-Christophe PLAGNIOL-VILLARD 3322439e4bfSJean-Christophe PLAGNIOL-VILLARD status = macb->rx_ring[rx_tail].ctrl; 3332439e4bfSJean-Christophe PLAGNIOL-VILLARD if (status & RXBUF_FRAME_START) { 3342439e4bfSJean-Christophe PLAGNIOL-VILLARD if (rx_tail != macb->rx_tail) 3352439e4bfSJean-Christophe PLAGNIOL-VILLARD reclaim_rx_buffers(macb, rx_tail); 3362439e4bfSJean-Christophe PLAGNIOL-VILLARD wrapped = 0; 3372439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3382439e4bfSJean-Christophe PLAGNIOL-VILLARD 3392439e4bfSJean-Christophe PLAGNIOL-VILLARD if (status & RXBUF_FRAME_END) { 3402439e4bfSJean-Christophe PLAGNIOL-VILLARD buffer = macb->rx_buffer + 128 * macb->rx_tail; 3412439e4bfSJean-Christophe PLAGNIOL-VILLARD length = status & RXBUF_FRMLEN_MASK; 3425ae0e382SWu, Josh 3435ae0e382SWu, Josh macb_invalidate_rx_buffer(macb); 3442439e4bfSJean-Christophe PLAGNIOL-VILLARD if (wrapped) { 3452439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned int headlen, taillen; 3462439e4bfSJean-Christophe PLAGNIOL-VILLARD 347ceef983bSAndreas Bießmann headlen = 128 * (MACB_RX_RING_SIZE 3482439e4bfSJean-Christophe PLAGNIOL-VILLARD - macb->rx_tail); 3492439e4bfSJean-Christophe PLAGNIOL-VILLARD taillen = length - headlen; 3502439e4bfSJean-Christophe PLAGNIOL-VILLARD memcpy((void *)NetRxPackets[0], 3512439e4bfSJean-Christophe PLAGNIOL-VILLARD buffer, headlen); 3522439e4bfSJean-Christophe PLAGNIOL-VILLARD memcpy((void *)NetRxPackets[0] + headlen, 3532439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_buffer, taillen); 3542439e4bfSJean-Christophe PLAGNIOL-VILLARD buffer = (void *)NetRxPackets[0]; 3552439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3562439e4bfSJean-Christophe PLAGNIOL-VILLARD 3572439e4bfSJean-Christophe PLAGNIOL-VILLARD NetReceive(buffer, length); 358ceef983bSAndreas Bießmann if (++rx_tail >= MACB_RX_RING_SIZE) 3592439e4bfSJean-Christophe PLAGNIOL-VILLARD rx_tail = 0; 3602439e4bfSJean-Christophe PLAGNIOL-VILLARD reclaim_rx_buffers(macb, rx_tail); 3612439e4bfSJean-Christophe PLAGNIOL-VILLARD } else { 362ceef983bSAndreas Bießmann if (++rx_tail >= MACB_RX_RING_SIZE) { 3632439e4bfSJean-Christophe PLAGNIOL-VILLARD wrapped = 1; 3642439e4bfSJean-Christophe PLAGNIOL-VILLARD rx_tail = 0; 3652439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3662439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3672439e4bfSJean-Christophe PLAGNIOL-VILLARD barrier(); 3682439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3692439e4bfSJean-Christophe PLAGNIOL-VILLARD 3702439e4bfSJean-Christophe PLAGNIOL-VILLARD return 0; 3712439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3722439e4bfSJean-Christophe PLAGNIOL-VILLARD 3732439e4bfSJean-Christophe PLAGNIOL-VILLARD static void macb_phy_reset(struct macb_device *macb) 3742439e4bfSJean-Christophe PLAGNIOL-VILLARD { 3752439e4bfSJean-Christophe PLAGNIOL-VILLARD struct eth_device *netdev = &macb->netdev; 3762439e4bfSJean-Christophe PLAGNIOL-VILLARD int i; 3772439e4bfSJean-Christophe PLAGNIOL-VILLARD u16 status, adv; 3782439e4bfSJean-Christophe PLAGNIOL-VILLARD 3792439e4bfSJean-Christophe PLAGNIOL-VILLARD adv = ADVERTISE_CSMA | ADVERTISE_ALL; 3802439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_mdio_write(macb, MII_ADVERTISE, adv); 3812439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: Starting autonegotiation...\n", netdev->name); 3822439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE 3832439e4bfSJean-Christophe PLAGNIOL-VILLARD | BMCR_ANRESTART)); 3842439e4bfSJean-Christophe PLAGNIOL-VILLARD 385ceef983bSAndreas Bießmann for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) { 3862439e4bfSJean-Christophe PLAGNIOL-VILLARD status = macb_mdio_read(macb, MII_BMSR); 3872439e4bfSJean-Christophe PLAGNIOL-VILLARD if (status & BMSR_ANEGCOMPLETE) 3882439e4bfSJean-Christophe PLAGNIOL-VILLARD break; 3892439e4bfSJean-Christophe PLAGNIOL-VILLARD udelay(100); 3902439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3912439e4bfSJean-Christophe PLAGNIOL-VILLARD 3922439e4bfSJean-Christophe PLAGNIOL-VILLARD if (status & BMSR_ANEGCOMPLETE) 3932439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: Autonegotiation complete\n", netdev->name); 3942439e4bfSJean-Christophe PLAGNIOL-VILLARD else 3952439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: Autonegotiation timed out (status=0x%04x)\n", 3962439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->name, status); 3972439e4bfSJean-Christophe PLAGNIOL-VILLARD } 3982439e4bfSJean-Christophe PLAGNIOL-VILLARD 399fc01ea1eSGunnar Rangoy #ifdef CONFIG_MACB_SEARCH_PHY 400fc01ea1eSGunnar Rangoy static int macb_phy_find(struct macb_device *macb) 401fc01ea1eSGunnar Rangoy { 402fc01ea1eSGunnar Rangoy int i; 403fc01ea1eSGunnar Rangoy u16 phy_id; 404fc01ea1eSGunnar Rangoy 405fc01ea1eSGunnar Rangoy /* Search for PHY... */ 406fc01ea1eSGunnar Rangoy for (i = 0; i < 32; i++) { 407fc01ea1eSGunnar Rangoy macb->phy_addr = i; 408fc01ea1eSGunnar Rangoy phy_id = macb_mdio_read(macb, MII_PHYSID1); 409fc01ea1eSGunnar Rangoy if (phy_id != 0xffff) { 410fc01ea1eSGunnar Rangoy printf("%s: PHY present at %d\n", macb->netdev.name, i); 411fc01ea1eSGunnar Rangoy return 1; 412fc01ea1eSGunnar Rangoy } 413fc01ea1eSGunnar Rangoy } 414fc01ea1eSGunnar Rangoy 415fc01ea1eSGunnar Rangoy /* PHY isn't up to snuff */ 4166ed0e940SAndreas Bießmann printf("%s: PHY not found\n", macb->netdev.name); 417fc01ea1eSGunnar Rangoy 418fc01ea1eSGunnar Rangoy return 0; 419fc01ea1eSGunnar Rangoy } 420fc01ea1eSGunnar Rangoy #endif /* CONFIG_MACB_SEARCH_PHY */ 421fc01ea1eSGunnar Rangoy 422fc01ea1eSGunnar Rangoy 4232439e4bfSJean-Christophe PLAGNIOL-VILLARD static int macb_phy_init(struct macb_device *macb) 4242439e4bfSJean-Christophe PLAGNIOL-VILLARD { 4252439e4bfSJean-Christophe PLAGNIOL-VILLARD struct eth_device *netdev = &macb->netdev; 426b1a0006eSBo Shen #ifdef CONFIG_PHYLIB 427b1a0006eSBo Shen struct phy_device *phydev; 428b1a0006eSBo Shen #endif 4292439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 ncfgr; 4302439e4bfSJean-Christophe PLAGNIOL-VILLARD u16 phy_id, status, adv, lpa; 4312439e4bfSJean-Christophe PLAGNIOL-VILLARD int media, speed, duplex; 4322439e4bfSJean-Christophe PLAGNIOL-VILLARD int i; 4332439e4bfSJean-Christophe PLAGNIOL-VILLARD 434416ce623SShiraz Hashim arch_get_mdio_control(netdev->name); 435fc01ea1eSGunnar Rangoy #ifdef CONFIG_MACB_SEARCH_PHY 436fc01ea1eSGunnar Rangoy /* Auto-detect phy_addr */ 437ceef983bSAndreas Bießmann if (!macb_phy_find(macb)) 438fc01ea1eSGunnar Rangoy return 0; 439fc01ea1eSGunnar Rangoy #endif /* CONFIG_MACB_SEARCH_PHY */ 440fc01ea1eSGunnar Rangoy 4412439e4bfSJean-Christophe PLAGNIOL-VILLARD /* Check if the PHY is up to snuff... */ 4422439e4bfSJean-Christophe PLAGNIOL-VILLARD phy_id = macb_mdio_read(macb, MII_PHYSID1); 4432439e4bfSJean-Christophe PLAGNIOL-VILLARD if (phy_id == 0xffff) { 4442439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: No PHY present\n", netdev->name); 4452439e4bfSJean-Christophe PLAGNIOL-VILLARD return 0; 4462439e4bfSJean-Christophe PLAGNIOL-VILLARD } 4472439e4bfSJean-Christophe PLAGNIOL-VILLARD 448b1a0006eSBo Shen #ifdef CONFIG_PHYLIB 4498314ccd8SBo Shen /* need to consider other phy interface mode */ 4508314ccd8SBo Shen phydev = phy_connect(macb->bus, macb->phy_addr, netdev, 4518314ccd8SBo Shen PHY_INTERFACE_MODE_RGMII); 4528314ccd8SBo Shen if (!phydev) { 4538314ccd8SBo Shen printf("phy_connect failed\n"); 4548314ccd8SBo Shen return -ENODEV; 4558314ccd8SBo Shen } 4568314ccd8SBo Shen 457b1a0006eSBo Shen phy_config(phydev); 458b1a0006eSBo Shen #endif 459b1a0006eSBo Shen 4602439e4bfSJean-Christophe PLAGNIOL-VILLARD status = macb_mdio_read(macb, MII_BMSR); 4612439e4bfSJean-Christophe PLAGNIOL-VILLARD if (!(status & BMSR_LSTATUS)) { 4622439e4bfSJean-Christophe PLAGNIOL-VILLARD /* Try to re-negotiate if we don't have link already. */ 4632439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_phy_reset(macb); 4642439e4bfSJean-Christophe PLAGNIOL-VILLARD 465ceef983bSAndreas Bießmann for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) { 4662439e4bfSJean-Christophe PLAGNIOL-VILLARD status = macb_mdio_read(macb, MII_BMSR); 4672439e4bfSJean-Christophe PLAGNIOL-VILLARD if (status & BMSR_LSTATUS) 4682439e4bfSJean-Christophe PLAGNIOL-VILLARD break; 4692439e4bfSJean-Christophe PLAGNIOL-VILLARD udelay(100); 4702439e4bfSJean-Christophe PLAGNIOL-VILLARD } 4712439e4bfSJean-Christophe PLAGNIOL-VILLARD } 4722439e4bfSJean-Christophe PLAGNIOL-VILLARD 4732439e4bfSJean-Christophe PLAGNIOL-VILLARD if (!(status & BMSR_LSTATUS)) { 4742439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: link down (status: 0x%04x)\n", 4752439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->name, status); 4762439e4bfSJean-Christophe PLAGNIOL-VILLARD return 0; 477d256be29SBo Shen } 478d256be29SBo Shen 479d256be29SBo Shen /* First check for GMAC */ 480d256be29SBo Shen if (macb_is_gem(macb)) { 481d256be29SBo Shen lpa = macb_mdio_read(macb, MII_STAT1000); 482d256be29SBo Shen 483*47609577SAndreas Bießmann if (lpa & (LPA_1000FULL | LPA_1000HALF)) { 484*47609577SAndreas Bießmann duplex = ((lpa & LPA_1000FULL) ? 1 : 0); 485*47609577SAndreas Bießmann 486*47609577SAndreas Bießmann printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n", 487d256be29SBo Shen netdev->name, 488d256be29SBo Shen duplex ? "full" : "half", 489d256be29SBo Shen lpa); 490d256be29SBo Shen 491d256be29SBo Shen ncfgr = macb_readl(macb, NCFGR); 492*47609577SAndreas Bießmann ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); 493d256be29SBo Shen ncfgr |= GEM_BIT(GBE); 494*47609577SAndreas Bießmann 495d256be29SBo Shen if (duplex) 496d256be29SBo Shen ncfgr |= MACB_BIT(FD); 497*47609577SAndreas Bießmann 498d256be29SBo Shen macb_writel(macb, NCFGR, ncfgr); 499d256be29SBo Shen 500d256be29SBo Shen return 1; 501d256be29SBo Shen } 502d256be29SBo Shen } 503d256be29SBo Shen 504d256be29SBo Shen /* fall back for EMAC checking */ 5052439e4bfSJean-Christophe PLAGNIOL-VILLARD adv = macb_mdio_read(macb, MII_ADVERTISE); 5062439e4bfSJean-Christophe PLAGNIOL-VILLARD lpa = macb_mdio_read(macb, MII_LPA); 5072439e4bfSJean-Christophe PLAGNIOL-VILLARD media = mii_nway_result(lpa & adv); 5082439e4bfSJean-Christophe PLAGNIOL-VILLARD speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) 5092439e4bfSJean-Christophe PLAGNIOL-VILLARD ? 1 : 0); 5102439e4bfSJean-Christophe PLAGNIOL-VILLARD duplex = (media & ADVERTISE_FULL) ? 1 : 0; 5112439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", 5122439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->name, 5132439e4bfSJean-Christophe PLAGNIOL-VILLARD speed ? "100" : "10", 5142439e4bfSJean-Christophe PLAGNIOL-VILLARD duplex ? "full" : "half", 5152439e4bfSJean-Christophe PLAGNIOL-VILLARD lpa); 5162439e4bfSJean-Christophe PLAGNIOL-VILLARD 5172439e4bfSJean-Christophe PLAGNIOL-VILLARD ncfgr = macb_readl(macb, NCFGR); 5182439e4bfSJean-Christophe PLAGNIOL-VILLARD ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); 5192439e4bfSJean-Christophe PLAGNIOL-VILLARD if (speed) 5202439e4bfSJean-Christophe PLAGNIOL-VILLARD ncfgr |= MACB_BIT(SPD); 5212439e4bfSJean-Christophe PLAGNIOL-VILLARD if (duplex) 5222439e4bfSJean-Christophe PLAGNIOL-VILLARD ncfgr |= MACB_BIT(FD); 5232439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCFGR, ncfgr); 524d256be29SBo Shen 5252439e4bfSJean-Christophe PLAGNIOL-VILLARD return 1; 5262439e4bfSJean-Christophe PLAGNIOL-VILLARD } 5272439e4bfSJean-Christophe PLAGNIOL-VILLARD 5282439e4bfSJean-Christophe PLAGNIOL-VILLARD static int macb_init(struct eth_device *netdev, bd_t *bd) 5292439e4bfSJean-Christophe PLAGNIOL-VILLARD { 5302439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device *macb = to_macb(netdev); 5312439e4bfSJean-Christophe PLAGNIOL-VILLARD unsigned long paddr; 5322439e4bfSJean-Christophe PLAGNIOL-VILLARD int i; 5332439e4bfSJean-Christophe PLAGNIOL-VILLARD 5342439e4bfSJean-Christophe PLAGNIOL-VILLARD /* 5352439e4bfSJean-Christophe PLAGNIOL-VILLARD * macb_halt should have been called at some point before now, 5362439e4bfSJean-Christophe PLAGNIOL-VILLARD * so we'll assume the controller is idle. 5372439e4bfSJean-Christophe PLAGNIOL-VILLARD */ 5382439e4bfSJean-Christophe PLAGNIOL-VILLARD 5392439e4bfSJean-Christophe PLAGNIOL-VILLARD /* initialize DMA descriptors */ 5402439e4bfSJean-Christophe PLAGNIOL-VILLARD paddr = macb->rx_buffer_dma; 541ceef983bSAndreas Bießmann for (i = 0; i < MACB_RX_RING_SIZE; i++) { 542ceef983bSAndreas Bießmann if (i == (MACB_RX_RING_SIZE - 1)) 5432439e4bfSJean-Christophe PLAGNIOL-VILLARD paddr |= RXADDR_WRAP; 5442439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_ring[i].addr = paddr; 5452439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->rx_ring[i].ctrl = 0; 5462439e4bfSJean-Christophe PLAGNIOL-VILLARD paddr += 128; 5472439e4bfSJean-Christophe PLAGNIOL-VILLARD } 5485ae0e382SWu, Josh macb_flush_ring_desc(macb, RX); 5495ae0e382SWu, Josh macb_flush_rx_buffer(macb); 5505ae0e382SWu, Josh 551ceef983bSAndreas Bießmann for (i = 0; i < MACB_TX_RING_SIZE; i++) { 5522439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_ring[i].addr = 0; 553ceef983bSAndreas Bießmann if (i == (MACB_TX_RING_SIZE - 1)) 5542439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_ring[i].ctrl = TXBUF_USED | TXBUF_WRAP; 5552439e4bfSJean-Christophe PLAGNIOL-VILLARD else 5562439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->tx_ring[i].ctrl = TXBUF_USED; 5572439e4bfSJean-Christophe PLAGNIOL-VILLARD } 5585ae0e382SWu, Josh macb_flush_ring_desc(macb, TX); 5595ae0e382SWu, Josh 560ceef983bSAndreas Bießmann macb->rx_tail = 0; 561ceef983bSAndreas Bießmann macb->tx_head = 0; 562ceef983bSAndreas Bießmann macb->tx_tail = 0; 5632439e4bfSJean-Christophe PLAGNIOL-VILLARD 5642439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, RBQP, macb->rx_ring_dma); 5652439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, TBQP, macb->tx_ring_dma); 5662439e4bfSJean-Christophe PLAGNIOL-VILLARD 567d256be29SBo Shen if (macb_is_gem(macb)) { 568d256be29SBo Shen #ifdef CONFIG_RGMII 569d256be29SBo Shen gem_writel(macb, UR, GEM_BIT(RGMII)); 570d256be29SBo Shen #else 571d256be29SBo Shen gem_writel(macb, UR, 0); 572d256be29SBo Shen #endif 573d256be29SBo Shen } else { 5742439e4bfSJean-Christophe PLAGNIOL-VILLARD /* choose RMII or MII mode. This depends on the board */ 5752439e4bfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_RMII 576d8f64b44SBo Shen #ifdef CONFIG_AT91FAMILY 5777263ef19SStelian Pop macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN)); 5787263ef19SStelian Pop #else 5792439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, USRIO, 0); 5807263ef19SStelian Pop #endif 5817263ef19SStelian Pop #else 582d8f64b44SBo Shen #ifdef CONFIG_AT91FAMILY 5837263ef19SStelian Pop macb_writel(macb, USRIO, MACB_BIT(CLKEN)); 5842439e4bfSJean-Christophe PLAGNIOL-VILLARD #else 5852439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, USRIO, MACB_BIT(MII)); 5862439e4bfSJean-Christophe PLAGNIOL-VILLARD #endif 5877263ef19SStelian Pop #endif /* CONFIG_RMII */ 588d256be29SBo Shen } 5892439e4bfSJean-Christophe PLAGNIOL-VILLARD 5902439e4bfSJean-Christophe PLAGNIOL-VILLARD if (!macb_phy_init(macb)) 591422b1a01SBen Warren return -1; 5922439e4bfSJean-Christophe PLAGNIOL-VILLARD 5932439e4bfSJean-Christophe PLAGNIOL-VILLARD /* Enable TX and RX */ 5942439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE)); 5952439e4bfSJean-Christophe PLAGNIOL-VILLARD 596422b1a01SBen Warren return 0; 5972439e4bfSJean-Christophe PLAGNIOL-VILLARD } 5982439e4bfSJean-Christophe PLAGNIOL-VILLARD 5992439e4bfSJean-Christophe PLAGNIOL-VILLARD static void macb_halt(struct eth_device *netdev) 6002439e4bfSJean-Christophe PLAGNIOL-VILLARD { 6012439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device *macb = to_macb(netdev); 6022439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 ncr, tsr; 6032439e4bfSJean-Christophe PLAGNIOL-VILLARD 6042439e4bfSJean-Christophe PLAGNIOL-VILLARD /* Halt the controller and wait for any ongoing transmission to end. */ 6052439e4bfSJean-Christophe PLAGNIOL-VILLARD ncr = macb_readl(macb, NCR); 6062439e4bfSJean-Christophe PLAGNIOL-VILLARD ncr |= MACB_BIT(THALT); 6072439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, ncr); 6082439e4bfSJean-Christophe PLAGNIOL-VILLARD 6092439e4bfSJean-Christophe PLAGNIOL-VILLARD do { 6102439e4bfSJean-Christophe PLAGNIOL-VILLARD tsr = macb_readl(macb, TSR); 6112439e4bfSJean-Christophe PLAGNIOL-VILLARD } while (tsr & MACB_BIT(TGO)); 6122439e4bfSJean-Christophe PLAGNIOL-VILLARD 6132439e4bfSJean-Christophe PLAGNIOL-VILLARD /* Disable TX and RX, and clear statistics */ 6142439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCR, MACB_BIT(CLRSTAT)); 6152439e4bfSJean-Christophe PLAGNIOL-VILLARD } 6162439e4bfSJean-Christophe PLAGNIOL-VILLARD 6176bb46790SBen Warren static int macb_write_hwaddr(struct eth_device *dev) 6186bb46790SBen Warren { 6196bb46790SBen Warren struct macb_device *macb = to_macb(dev); 6206bb46790SBen Warren u32 hwaddr_bottom; 6216bb46790SBen Warren u16 hwaddr_top; 6226bb46790SBen Warren 6236bb46790SBen Warren /* set hardware address */ 6246c169c12Sandreas.devel@googlemail.com hwaddr_bottom = dev->enetaddr[0] | dev->enetaddr[1] << 8 | 6256c169c12Sandreas.devel@googlemail.com dev->enetaddr[2] << 16 | dev->enetaddr[3] << 24; 6266bb46790SBen Warren macb_writel(macb, SA1B, hwaddr_bottom); 6276c169c12Sandreas.devel@googlemail.com hwaddr_top = dev->enetaddr[4] | dev->enetaddr[5] << 8; 6286bb46790SBen Warren macb_writel(macb, SA1T, hwaddr_top); 6296bb46790SBen Warren return 0; 6306bb46790SBen Warren } 6316bb46790SBen Warren 632d256be29SBo Shen static u32 macb_mdc_clk_div(int id, struct macb_device *macb) 633d256be29SBo Shen { 634d256be29SBo Shen u32 config; 635d256be29SBo Shen unsigned long macb_hz = get_macb_pclk_rate(id); 636d256be29SBo Shen 637d256be29SBo Shen if (macb_hz < 20000000) 638d256be29SBo Shen config = MACB_BF(CLK, MACB_CLK_DIV8); 639d256be29SBo Shen else if (macb_hz < 40000000) 640d256be29SBo Shen config = MACB_BF(CLK, MACB_CLK_DIV16); 641d256be29SBo Shen else if (macb_hz < 80000000) 642d256be29SBo Shen config = MACB_BF(CLK, MACB_CLK_DIV32); 643d256be29SBo Shen else 644d256be29SBo Shen config = MACB_BF(CLK, MACB_CLK_DIV64); 645d256be29SBo Shen 646d256be29SBo Shen return config; 647d256be29SBo Shen } 648d256be29SBo Shen 649d256be29SBo Shen static u32 gem_mdc_clk_div(int id, struct macb_device *macb) 650d256be29SBo Shen { 651d256be29SBo Shen u32 config; 652d256be29SBo Shen unsigned long macb_hz = get_macb_pclk_rate(id); 653d256be29SBo Shen 654d256be29SBo Shen if (macb_hz < 20000000) 655d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV8); 656d256be29SBo Shen else if (macb_hz < 40000000) 657d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV16); 658d256be29SBo Shen else if (macb_hz < 80000000) 659d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV32); 660d256be29SBo Shen else if (macb_hz < 120000000) 661d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV48); 662d256be29SBo Shen else if (macb_hz < 160000000) 663d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV64); 664d256be29SBo Shen else 665d256be29SBo Shen config = GEM_BF(CLK, GEM_CLK_DIV96); 666d256be29SBo Shen 667d256be29SBo Shen return config; 668d256be29SBo Shen } 669d256be29SBo Shen 67032e4f6bfSBo Shen /* 67132e4f6bfSBo Shen * Get the DMA bus width field of the network configuration register that we 67232e4f6bfSBo Shen * should program. We find the width from decoding the design configuration 67332e4f6bfSBo Shen * register to find the maximum supported data bus width. 67432e4f6bfSBo Shen */ 67532e4f6bfSBo Shen static u32 macb_dbw(struct macb_device *macb) 67632e4f6bfSBo Shen { 67732e4f6bfSBo Shen switch (GEM_BFEXT(DBWDEF, gem_readl(macb, DCFG1))) { 67832e4f6bfSBo Shen case 4: 67932e4f6bfSBo Shen return GEM_BF(DBW, GEM_DBW128); 68032e4f6bfSBo Shen case 2: 68132e4f6bfSBo Shen return GEM_BF(DBW, GEM_DBW64); 68232e4f6bfSBo Shen case 1: 68332e4f6bfSBo Shen default: 68432e4f6bfSBo Shen return GEM_BF(DBW, GEM_DBW32); 68532e4f6bfSBo Shen } 68632e4f6bfSBo Shen } 68732e4f6bfSBo Shen 6882439e4bfSJean-Christophe PLAGNIOL-VILLARD int macb_eth_initialize(int id, void *regs, unsigned int phy_addr) 6892439e4bfSJean-Christophe PLAGNIOL-VILLARD { 6902439e4bfSJean-Christophe PLAGNIOL-VILLARD struct macb_device *macb; 6912439e4bfSJean-Christophe PLAGNIOL-VILLARD struct eth_device *netdev; 6922439e4bfSJean-Christophe PLAGNIOL-VILLARD u32 ncfgr; 6932439e4bfSJean-Christophe PLAGNIOL-VILLARD 6942439e4bfSJean-Christophe PLAGNIOL-VILLARD macb = malloc(sizeof(struct macb_device)); 6952439e4bfSJean-Christophe PLAGNIOL-VILLARD if (!macb) { 6962439e4bfSJean-Christophe PLAGNIOL-VILLARD printf("Error: Failed to allocate memory for MACB%d\n", id); 6972439e4bfSJean-Christophe PLAGNIOL-VILLARD return -1; 6982439e4bfSJean-Christophe PLAGNIOL-VILLARD } 6992439e4bfSJean-Christophe PLAGNIOL-VILLARD memset(macb, 0, sizeof(struct macb_device)); 7002439e4bfSJean-Christophe PLAGNIOL-VILLARD 7012439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev = &macb->netdev; 7022439e4bfSJean-Christophe PLAGNIOL-VILLARD 703ceef983bSAndreas Bießmann macb->rx_buffer = dma_alloc_coherent(MACB_RX_BUFFER_SIZE, 7042439e4bfSJean-Christophe PLAGNIOL-VILLARD &macb->rx_buffer_dma); 7055ae0e382SWu, Josh macb->rx_ring = dma_alloc_coherent(MACB_RX_DMA_DESC_SIZE, 7062439e4bfSJean-Christophe PLAGNIOL-VILLARD &macb->rx_ring_dma); 7075ae0e382SWu, Josh macb->tx_ring = dma_alloc_coherent(MACB_TX_DMA_DESC_SIZE, 7082439e4bfSJean-Christophe PLAGNIOL-VILLARD &macb->tx_ring_dma); 7092439e4bfSJean-Christophe PLAGNIOL-VILLARD 7105ae0e382SWu, Josh /* TODO: we need check the rx/tx_ring_dma is dcache line aligned */ 7115ae0e382SWu, Josh 7122439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->regs = regs; 7132439e4bfSJean-Christophe PLAGNIOL-VILLARD macb->phy_addr = phy_addr; 7142439e4bfSJean-Christophe PLAGNIOL-VILLARD 715d256be29SBo Shen if (macb_is_gem(macb)) 716d256be29SBo Shen sprintf(netdev->name, "gmac%d", id); 717d256be29SBo Shen else 7182439e4bfSJean-Christophe PLAGNIOL-VILLARD sprintf(netdev->name, "macb%d", id); 719d256be29SBo Shen 7202439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->init = macb_init; 7212439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->halt = macb_halt; 7222439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->send = macb_send; 7232439e4bfSJean-Christophe PLAGNIOL-VILLARD netdev->recv = macb_recv; 7246bb46790SBen Warren netdev->write_hwaddr = macb_write_hwaddr; 7252439e4bfSJean-Christophe PLAGNIOL-VILLARD 7262439e4bfSJean-Christophe PLAGNIOL-VILLARD /* 7272439e4bfSJean-Christophe PLAGNIOL-VILLARD * Do some basic initialization so that we at least can talk 7282439e4bfSJean-Christophe PLAGNIOL-VILLARD * to the PHY 7292439e4bfSJean-Christophe PLAGNIOL-VILLARD */ 730d256be29SBo Shen if (macb_is_gem(macb)) { 731d256be29SBo Shen ncfgr = gem_mdc_clk_div(id, macb); 73232e4f6bfSBo Shen ncfgr |= macb_dbw(macb); 733d256be29SBo Shen } else { 734d256be29SBo Shen ncfgr = macb_mdc_clk_div(id, macb); 735d256be29SBo Shen } 7362439e4bfSJean-Christophe PLAGNIOL-VILLARD 7372439e4bfSJean-Christophe PLAGNIOL-VILLARD macb_writel(macb, NCFGR, ncfgr); 7382439e4bfSJean-Christophe PLAGNIOL-VILLARD 7392439e4bfSJean-Christophe PLAGNIOL-VILLARD eth_register(netdev); 7402439e4bfSJean-Christophe PLAGNIOL-VILLARD 741b1a0006eSBo Shen #if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) 7420f751d6eSSemih Hazar miiphy_register(netdev->name, macb_miiphy_read, macb_miiphy_write); 743b1a0006eSBo Shen macb->bus = miiphy_get_dev_by_name(netdev->name); 7440f751d6eSSemih Hazar #endif 7452439e4bfSJean-Christophe PLAGNIOL-VILLARD return 0; 7462439e4bfSJean-Christophe PLAGNIOL-VILLARD } 7472439e4bfSJean-Christophe PLAGNIOL-VILLARD 7482439e4bfSJean-Christophe PLAGNIOL-VILLARD #endif 749