/*
* Copyright (C) 2005-2006 Atmel Corporation
*
- * 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
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/dma-mapping.h>
#include <asm/arch/clk.h>
+#include <asm-generic/errno.h>
#include "macb.h"
-#define barrier() asm volatile("" ::: "memory")
-
-#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096
-#define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128)
-#define CONFIG_SYS_MACB_TX_RING_SIZE 16
-#define CONFIG_SYS_MACB_TX_TIMEOUT 1000
-#define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
+#define MACB_RX_BUFFER_SIZE 4096
+#define MACB_RX_RING_SIZE (MACB_RX_BUFFER_SIZE / 128)
+#define MACB_TX_RING_SIZE 16
+#define MACB_TX_TIMEOUT 1000
+#define MACB_AUTONEG_TIMEOUT 5000000
struct macb_dma_desc {
u32 addr;
u32 ctrl;
};
+#define DMA_DESC_BYTES(n) (n * sizeof(struct macb_dma_desc))
+#define MACB_TX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_TX_RING_SIZE))
+#define MACB_RX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_RX_RING_SIZE))
+#define MACB_TX_DUMMY_DMA_DESC_SIZE (DMA_DESC_BYTES(1))
+
#define RXADDR_USED 0x00000001
#define RXADDR_WRAP 0x00000002
unsigned long rx_ring_dma;
unsigned long tx_ring_dma;
+ struct macb_dma_desc *dummy_desc;
+ unsigned long dummy_desc_dma;
+
const struct device *dev;
struct eth_device netdev;
unsigned short phy_addr;
+ struct mii_dev *bus;
};
#define to_macb(_nd) container_of(_nd, struct macb_device, netdev)
+static int macb_is_gem(struct macb_device *macb)
+{
+ return MACB_BFEXT(IDNUM, macb_readl(macb, MID)) == 0x2;
+}
+
static void macb_mdio_write(struct macb_device *macb, u8 reg, u16 value)
{
unsigned long netctl;
return MACB_BFEXT(DATA, frame);
}
-#if defined(CONFIG_CMD_MII)
+void __weak arch_get_mdio_control(const char *name)
+{
+ return;
+}
+
+#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value)
{
struct eth_device *dev = eth_get_dev_by_name(devname);
struct macb_device *macb = to_macb(dev);
- if ( macb->phy_addr != phy_adr )
+ if (macb->phy_addr != phy_adr)
return -1;
+ arch_get_mdio_control(devname);
*value = macb_mdio_read(macb, reg);
return 0;
struct eth_device *dev = eth_get_dev_by_name(devname);
struct macb_device *macb = to_macb(dev);
- if ( macb->phy_addr != phy_adr )
+ if (macb->phy_addr != phy_adr)
return -1;
+ arch_get_mdio_control(devname);
macb_mdio_write(macb, reg, value);
return 0;
}
#endif
+#define RX 1
+#define TX 0
+static inline void macb_invalidate_ring_desc(struct macb_device *macb, bool rx)
+{
+ if (rx)
+ invalidate_dcache_range(macb->rx_ring_dma, macb->rx_ring_dma +
+ MACB_RX_DMA_DESC_SIZE);
+ else
+ invalidate_dcache_range(macb->tx_ring_dma, macb->tx_ring_dma +
+ MACB_TX_DMA_DESC_SIZE);
+}
+
+static inline void macb_flush_ring_desc(struct macb_device *macb, bool rx)
+{
+ if (rx)
+ flush_dcache_range(macb->rx_ring_dma, macb->rx_ring_dma +
+ MACB_RX_DMA_DESC_SIZE);
+ else
+ flush_dcache_range(macb->tx_ring_dma, macb->tx_ring_dma +
+ MACB_TX_DMA_DESC_SIZE);
+}
+
+static inline void macb_flush_rx_buffer(struct macb_device *macb)
+{
+ flush_dcache_range(macb->rx_buffer_dma, macb->rx_buffer_dma +
+ MACB_RX_BUFFER_SIZE);
+}
+
+static inline void macb_invalidate_rx_buffer(struct macb_device *macb)
+{
+ invalidate_dcache_range(macb->rx_buffer_dma, macb->rx_buffer_dma +
+ MACB_RX_BUFFER_SIZE);
+}
#if defined(CONFIG_CMD_NET)
-static int macb_send(struct eth_device *netdev, volatile void *packet,
- int length)
+static int macb_send(struct eth_device *netdev, void *packet, int length)
{
struct macb_device *macb = to_macb(netdev);
unsigned long paddr, ctrl;
ctrl = length & TXBUF_FRMLEN_MASK;
ctrl |= TXBUF_FRAME_END;
- if (tx_head == (CONFIG_SYS_MACB_TX_RING_SIZE - 1)) {
+ if (tx_head == (MACB_TX_RING_SIZE - 1)) {
ctrl |= TXBUF_WRAP;
macb->tx_head = 0;
- } else
+ } else {
macb->tx_head++;
+ }
macb->tx_ring[tx_head].ctrl = ctrl;
macb->tx_ring[tx_head].addr = paddr;
barrier();
+ macb_flush_ring_desc(macb, TX);
+ /* Do we need check paddr and length is dcache line aligned? */
+ flush_dcache_range(paddr, paddr + length);
macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* I guess this is necessary because the networking core may
* re-use the transmit buffer as soon as we return...
*/
- for (i = 0; i <= CONFIG_SYS_MACB_TX_TIMEOUT; i++) {
+ for (i = 0; i <= MACB_TX_TIMEOUT; i++) {
barrier();
+ macb_invalidate_ring_desc(macb, TX);
ctrl = macb->tx_ring[tx_head].ctrl;
if (ctrl & TXBUF_USED)
break;
dma_unmap_single(packet, length, paddr);
- if (i <= CONFIG_SYS_MACB_TX_TIMEOUT) {
+ if (i <= MACB_TX_TIMEOUT) {
if (ctrl & TXBUF_UNDERRUN)
printf("%s: TX underrun\n", netdev->name);
if (ctrl & TXBUF_EXHAUSTED)
unsigned int i;
i = macb->rx_tail;
+
+ macb_invalidate_ring_desc(macb, RX);
while (i > new_tail) {
macb->rx_ring[i].addr &= ~RXADDR_USED;
i++;
- if (i > CONFIG_SYS_MACB_RX_RING_SIZE)
+ if (i > MACB_RX_RING_SIZE)
i = 0;
}
}
barrier();
+ macb_flush_ring_desc(macb, RX);
macb->rx_tail = new_tail;
}
u32 status;
for (;;) {
+ macb_invalidate_ring_desc(macb, RX);
+
if (!(macb->rx_ring[rx_tail].addr & RXADDR_USED))
return -1;
if (status & RXBUF_FRAME_END) {
buffer = macb->rx_buffer + 128 * macb->rx_tail;
length = status & RXBUF_FRMLEN_MASK;
+
+ macb_invalidate_rx_buffer(macb);
if (wrapped) {
unsigned int headlen, taillen;
- headlen = 128 * (CONFIG_SYS_MACB_RX_RING_SIZE
+ headlen = 128 * (MACB_RX_RING_SIZE
- macb->rx_tail);
taillen = length - headlen;
- memcpy((void *)NetRxPackets[0],
+ memcpy((void *)net_rx_packets[0],
buffer, headlen);
- memcpy((void *)NetRxPackets[0] + headlen,
+ memcpy((void *)net_rx_packets[0] + headlen,
macb->rx_buffer, taillen);
- buffer = (void *)NetRxPackets[0];
+ buffer = (void *)net_rx_packets[0];
}
- NetReceive(buffer, length);
- if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE)
+ net_process_received_packet(buffer, length);
+ if (++rx_tail >= MACB_RX_RING_SIZE)
rx_tail = 0;
reclaim_rx_buffers(macb, rx_tail);
} else {
- if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE) {
+ if (++rx_tail >= MACB_RX_RING_SIZE) {
wrapped = 1;
rx_tail = 0;
}
macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE
| BMCR_ANRESTART));
- for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) {
+ for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) {
status = macb_mdio_read(macb, MII_BMSR);
if (status & BMSR_ANEGCOMPLETE)
break;
}
/* PHY isn't up to snuff */
- printf("%s: PHY not found", macb->netdev.name);
+ printf("%s: PHY not found\n", macb->netdev.name);
return 0;
}
static int macb_phy_init(struct macb_device *macb)
{
struct eth_device *netdev = &macb->netdev;
+#ifdef CONFIG_PHYLIB
+ struct phy_device *phydev;
+#endif
u32 ncfgr;
u16 phy_id, status, adv, lpa;
int media, speed, duplex;
int i;
+ arch_get_mdio_control(netdev->name);
#ifdef CONFIG_MACB_SEARCH_PHY
/* Auto-detect phy_addr */
- if (!macb_phy_find(macb)) {
+ if (!macb_phy_find(macb))
return 0;
- }
#endif /* CONFIG_MACB_SEARCH_PHY */
/* Check if the PHY is up to snuff... */
return 0;
}
+#ifdef CONFIG_PHYLIB
+ /* need to consider other phy interface mode */
+ phydev = phy_connect(macb->bus, macb->phy_addr, netdev,
+ PHY_INTERFACE_MODE_RGMII);
+ if (!phydev) {
+ printf("phy_connect failed\n");
+ return -ENODEV;
+ }
+
+ phy_config(phydev);
+#endif
+
status = macb_mdio_read(macb, MII_BMSR);
if (!(status & BMSR_LSTATUS)) {
/* Try to re-negotiate if we don't have link already. */
macb_phy_reset(macb);
- for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) {
+ for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) {
status = macb_mdio_read(macb, MII_BMSR);
if (status & BMSR_LSTATUS)
break;
printf("%s: link down (status: 0x%04x)\n",
netdev->name, status);
return 0;
- } else {
- adv = macb_mdio_read(macb, MII_ADVERTISE);
- lpa = macb_mdio_read(macb, MII_LPA);
- media = mii_nway_result(lpa & adv);
- speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
- ? 1 : 0);
- duplex = (media & ADVERTISE_FULL) ? 1 : 0;
- printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
- netdev->name,
- speed ? "100" : "10",
- duplex ? "full" : "half",
- lpa);
-
- ncfgr = macb_readl(macb, NCFGR);
- ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
- if (speed)
- ncfgr |= MACB_BIT(SPD);
- if (duplex)
- ncfgr |= MACB_BIT(FD);
- macb_writel(macb, NCFGR, ncfgr);
- return 1;
}
+
+ /* First check for GMAC */
+ if (macb_is_gem(macb)) {
+ lpa = macb_mdio_read(macb, MII_STAT1000);
+
+ if (lpa & (LPA_1000FULL | LPA_1000HALF)) {
+ duplex = ((lpa & LPA_1000FULL) ? 1 : 0);
+
+ printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n",
+ netdev->name,
+ duplex ? "full" : "half",
+ lpa);
+
+ ncfgr = macb_readl(macb, NCFGR);
+ ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
+ ncfgr |= GEM_BIT(GBE);
+
+ if (duplex)
+ ncfgr |= MACB_BIT(FD);
+
+ macb_writel(macb, NCFGR, ncfgr);
+
+ return 1;
+ }
+ }
+
+ /* fall back for EMAC checking */
+ adv = macb_mdio_read(macb, MII_ADVERTISE);
+ lpa = macb_mdio_read(macb, MII_LPA);
+ media = mii_nway_result(lpa & adv);
+ speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
+ ? 1 : 0);
+ duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+ printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
+ netdev->name,
+ speed ? "100" : "10",
+ duplex ? "full" : "half",
+ lpa);
+
+ ncfgr = macb_readl(macb, NCFGR);
+ ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | GEM_BIT(GBE));
+ if (speed)
+ ncfgr |= MACB_BIT(SPD);
+ if (duplex)
+ ncfgr |= MACB_BIT(FD);
+ macb_writel(macb, NCFGR, ncfgr);
+
+ return 1;
+}
+
+static int gmac_init_multi_queues(struct macb_device *macb)
+{
+ int i, num_queues = 1;
+ u32 queue_mask;
+
+ /* bit 0 is never set but queue 0 always exists */
+ queue_mask = gem_readl(macb, DCFG6) & 0xff;
+ queue_mask |= 0x1;
+
+ for (i = 1; i < MACB_MAX_QUEUES; i++)
+ if (queue_mask & (1 << i))
+ num_queues++;
+
+ macb->dummy_desc->ctrl = TXBUF_USED;
+ macb->dummy_desc->addr = 0;
+ flush_dcache_range(macb->dummy_desc_dma, macb->dummy_desc_dma +
+ MACB_TX_DUMMY_DMA_DESC_SIZE);
+
+ for (i = 1; i < num_queues; i++)
+ gem_writel_queue_TBQP(macb, macb->dummy_desc_dma, i - 1);
+
+ return 0;
}
static int macb_init(struct eth_device *netdev, bd_t *bd)
/* initialize DMA descriptors */
paddr = macb->rx_buffer_dma;
- for (i = 0; i < CONFIG_SYS_MACB_RX_RING_SIZE; i++) {
- if (i == (CONFIG_SYS_MACB_RX_RING_SIZE - 1))
+ for (i = 0; i < MACB_RX_RING_SIZE; i++) {
+ if (i == (MACB_RX_RING_SIZE - 1))
paddr |= RXADDR_WRAP;
macb->rx_ring[i].addr = paddr;
macb->rx_ring[i].ctrl = 0;
paddr += 128;
}
- for (i = 0; i < CONFIG_SYS_MACB_TX_RING_SIZE; i++) {
+ macb_flush_ring_desc(macb, RX);
+ macb_flush_rx_buffer(macb);
+
+ for (i = 0; i < MACB_TX_RING_SIZE; i++) {
macb->tx_ring[i].addr = 0;
- if (i == (CONFIG_SYS_MACB_TX_RING_SIZE - 1))
+ if (i == (MACB_TX_RING_SIZE - 1))
macb->tx_ring[i].ctrl = TXBUF_USED | TXBUF_WRAP;
else
macb->tx_ring[i].ctrl = TXBUF_USED;
}
- macb->rx_tail = macb->tx_head = macb->tx_tail = 0;
+ macb_flush_ring_desc(macb, TX);
+
+ macb->rx_tail = 0;
+ macb->tx_head = 0;
+ macb->tx_tail = 0;
macb_writel(macb, RBQP, macb->rx_ring_dma);
macb_writel(macb, TBQP, macb->tx_ring_dma);
+ if (macb_is_gem(macb)) {
+ /* Check the multi queue and initialize the queue for tx */
+ gmac_init_multi_queues(macb);
+
+ /*
+ * When the GMAC IP with GE feature, this bit is used to
+ * select interface between RGMII and GMII.
+ * When the GMAC IP without GE feature, this bit is used
+ * to select interface between RMII and MII.
+ */
+#if defined(CONFIG_RGMII) || defined(CONFIG_RMII)
+ gem_writel(macb, UR, GEM_BIT(RGMII));
+#else
+ gem_writel(macb, UR, 0);
+#endif
+ } else {
/* choose RMII or MII mode. This depends on the board */
#ifdef CONFIG_RMII
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
- defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
- defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE)
+#ifdef CONFIG_AT91FAMILY
macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, 0);
#endif
#else
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
- defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
- defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE)
+#ifdef CONFIG_AT91FAMILY
macb_writel(macb, USRIO, MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, MACB_BIT(MII));
#endif
#endif /* CONFIG_RMII */
+ }
if (!macb_phy_init(macb))
return -1;
return 0;
}
+static u32 macb_mdc_clk_div(int id, struct macb_device *macb)
+{
+ u32 config;
+ unsigned long macb_hz = get_macb_pclk_rate(id);
+
+ if (macb_hz < 20000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV8);
+ else if (macb_hz < 40000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV16);
+ else if (macb_hz < 80000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV32);
+ else
+ config = MACB_BF(CLK, MACB_CLK_DIV64);
+
+ return config;
+}
+
+static u32 gem_mdc_clk_div(int id, struct macb_device *macb)
+{
+ u32 config;
+ unsigned long macb_hz = get_macb_pclk_rate(id);
+
+ if (macb_hz < 20000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV8);
+ else if (macb_hz < 40000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV16);
+ else if (macb_hz < 80000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV32);
+ else if (macb_hz < 120000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV48);
+ else if (macb_hz < 160000000)
+ config = GEM_BF(CLK, GEM_CLK_DIV64);
+ else
+ config = GEM_BF(CLK, GEM_CLK_DIV96);
+
+ return config;
+}
+
+/*
+ * Get the DMA bus width field of the network configuration register that we
+ * should program. We find the width from decoding the design configuration
+ * register to find the maximum supported data bus width.
+ */
+static u32 macb_dbw(struct macb_device *macb)
+{
+ switch (GEM_BFEXT(DBWDEF, gem_readl(macb, DCFG1))) {
+ case 4:
+ return GEM_BF(DBW, GEM_DBW128);
+ case 2:
+ return GEM_BF(DBW, GEM_DBW64);
+ case 1:
+ default:
+ return GEM_BF(DBW, GEM_DBW32);
+ }
+}
+
int macb_eth_initialize(int id, void *regs, unsigned int phy_addr)
{
struct macb_device *macb;
struct eth_device *netdev;
- unsigned long macb_hz;
u32 ncfgr;
macb = malloc(sizeof(struct macb_device));
netdev = &macb->netdev;
- macb->rx_buffer = dma_alloc_coherent(CONFIG_SYS_MACB_RX_BUFFER_SIZE,
+ macb->rx_buffer = dma_alloc_coherent(MACB_RX_BUFFER_SIZE,
&macb->rx_buffer_dma);
- macb->rx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_RX_RING_SIZE
- * sizeof(struct macb_dma_desc),
+ macb->rx_ring = dma_alloc_coherent(MACB_RX_DMA_DESC_SIZE,
&macb->rx_ring_dma);
- macb->tx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_TX_RING_SIZE
- * sizeof(struct macb_dma_desc),
+ macb->tx_ring = dma_alloc_coherent(MACB_TX_DMA_DESC_SIZE,
&macb->tx_ring_dma);
+ macb->dummy_desc = dma_alloc_coherent(MACB_TX_DUMMY_DMA_DESC_SIZE,
+ &macb->dummy_desc_dma);
+
+ /* TODO: we need check the rx/tx_ring_dma is dcache line aligned */
macb->regs = regs;
macb->phy_addr = phy_addr;
- sprintf(netdev->name, "macb%d", id);
+ if (macb_is_gem(macb))
+ sprintf(netdev->name, "gmac%d", id);
+ else
+ sprintf(netdev->name, "macb%d", id);
+
netdev->init = macb_init;
netdev->halt = macb_halt;
netdev->send = macb_send;
* Do some basic initialization so that we at least can talk
* to the PHY
*/
- macb_hz = get_macb_pclk_rate(id);
- if (macb_hz < 20000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV8);
- else if (macb_hz < 40000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV16);
- else if (macb_hz < 80000000)
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV32);
- else
- ncfgr = MACB_BF(CLK, MACB_CLK_DIV64);
+ if (macb_is_gem(macb)) {
+ ncfgr = gem_mdc_clk_div(id, macb);
+ ncfgr |= macb_dbw(macb);
+ } else {
+ ncfgr = macb_mdc_clk_div(id, macb);
+ }
macb_writel(macb, NCFGR, ncfgr);
eth_register(netdev);
-#if defined(CONFIG_CMD_MII)
+#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
miiphy_register(netdev->name, macb_miiphy_read, macb_miiphy_write);
+ macb->bus = miiphy_get_dev_by_name(netdev->name);
#endif
return 0;
}