[RFC PATCH 2/2] net: Add Support for GENET Ethernet controller
Amit Singh Tomar
amittomer25 at gmail.com
Fri Dec 13 16:42:22 CET 2019
This patch adds driver for GENET Ethernet controller.
It has been tested(ping and tftp works for small files) on
Raspberry Pi 4.
Signed-off-by: Amit Singh Tomar <amittomer25 at gmail.com>
---
configs/rpi_4_defconfig | 2 +
drivers/net/Kconfig | 7 +
drivers/net/Makefile | 1 +
drivers/net/bcmgenet.c | 770 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 780 insertions(+)
create mode 100644 drivers/net/bcmgenet.c
diff --git a/configs/rpi_4_defconfig b/configs/rpi_4_defconfig
index 8cf1bb8..b0f9cf1 100644
--- a/configs/rpi_4_defconfig
+++ b/configs/rpi_4_defconfig
@@ -24,6 +24,8 @@ CONFIG_DM_KEYBOARD=y
CONFIG_DM_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_BCM2835=y
+CONFIG_DM_ETH=y
+CONFIG_BCMGENET=y
CONFIG_PINCTRL=y
# CONFIG_PINCTRL_GENERIC is not set
# CONFIG_REQUIRE_SERIAL_CONSOLE is not set
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 142a2c6..999714d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -136,6 +136,13 @@ config BCM6368_ETH
help
This driver supports the BCM6368 Ethernet MAC.
+config BCMGENET
+ bool "BCMGENET V5 support"
+ depends on DM_ETH
+ select PHYLIB
+ help
+ This driver supports the BCMGENET Ethernet MAC.
+
config DWC_ETH_QOS
bool "Synopsys DWC Ethernet QOS device support"
depends on DM_ETH
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3099183..6e0a688 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_AG7XXX) += ag7xxx.o
obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o
obj-$(CONFIG_BCM6348_ETH) += bcm6348-eth.o
obj-$(CONFIG_BCM6368_ETH) += bcm6368-eth.o
+obj-$(CONFIG_BCMGENET) += bcmgenet.o
obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o
obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
diff --git a/drivers/net/bcmgenet.c b/drivers/net/bcmgenet.c
new file mode 100644
index 0000000..bad5ffb
--- /dev/null
+++ b/drivers/net/bcmgenet.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ethernet driver for GENET controller found on RPI4.
+ *
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <fdt_support.h>
+#include <linux/err.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <net.h>
+#include <dm/of_access.h>
+#include <dm/ofnode.h>
+#include <linux/libfdt.h>
+#include <linux/iopoll.h>
+#include <asm/dma-mapping.h>
+
+/* Register definitions derived from Linux source */
+#define SYS_REV_CTRL 0x00
+
+#define SYS_PORT_CTRL 0x04
+#define PORT_MODE_EXT_GPHY 3
+
+#define GENET_SYS_OFF 0x0000
+#define SYS_RBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x08)
+#define SYS_TBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x0C)
+
+#define GENET_EXT_OFF 0x0080
+#define EXT_RGMII_OOB_CTRL (GENET_EXT_OFF + 0x0C)
+#define RGMII_MODE_EN_V123 BIT(0)
+#define RGMII_LINK BIT(4)
+#define OOB_DISABLE BIT(5)
+#define RGMII_MODE_EN BIT(6)
+#define ID_MODE_DIS BIT(16)
+
+#define GENET_RBUF_OFF 0x0300
+#define RBUF_FLUSH_CTRL_V1 (GENET_RBUF_OFF + 0x04)
+#define RBUF_TBUF_SIZE_CTRL (GENET_RBUF_OFF + 0xb4)
+#define RBUF_CTRL (GENET_RBUF_OFF + 0x00)
+#define RBUF_64B_EN BIT(0)
+#define RBUF_ALIGN_2B BIT(1)
+#define RBUF_BAD_DIS BIT(2)
+
+#define GENET_UMAC_OFF 0x0800
+#define UMAC_MIB_CTRL (GENET_UMAC_OFF + 0x580)
+#define UMAC_MAX_FRAME_LEN (GENET_UMAC_OFF + 0x014)
+#define UMAC_MAC0 (GENET_UMAC_OFF + 0x00C)
+#define UMAC_MAC1 (GENET_UMAC_OFF + 0x010)
+#define UMAC_CMD (GENET_UMAC_OFF + 0x008)
+#define MDIO_CMD (GENET_UMAC_OFF + 0x614)
+#define UMAC_TX_FLUSH (GENET_UMAC_OFF + 0x334)
+#define MDIO_START_BUSY BIT(29)
+#define MDIO_READ_FAIL BIT(28)
+#define MDIO_RD (2 << 26)
+#define MDIO_WR BIT(26)
+#define MDIO_PMD_SHIFT 21
+#define MDIO_PMD_MASK 0x1F
+#define MDIO_REG_SHIFT 16
+#define MDIO_REG_MASK 0x1F
+
+#define CMD_TX_EN BIT(0)
+#define CMD_RX_EN BIT(1)
+#define UMAC_SPEED_10 0
+#define UMAC_SPEED_100 1
+#define UMAC_SPEED_1000 2
+#define UMAC_SPEED_2500 3
+#define CMD_SPEED_SHIFT 2
+#define CMD_SPEED_MASK 3
+#define CMD_SW_RESET BIT(13)
+#define CMD_LCL_LOOP_EN BIT(15)
+#define CMD_TX_EN BIT(0)
+#define CMD_RX_EN BIT(1)
+
+#define MIB_RESET_RX BIT(0)
+#define MIB_RESET_RUNT BIT(1)
+#define MIB_RESET_TX BIT(2)
+
+/* total number of Buffer Descriptors, same for Rx/Tx */
+#define TOTAL_DESC 256
+
+#define DEFAULT_Q 0x10
+
+/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
+ * 1536 is multiple of 256 bytes
+ */
+#define ENET_BRCM_TAG_LEN 6
+#define ENET_PAD 8
+#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
+ ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
+
+/* Tx/Rx Dma Descriptor common bits*/
+#define DMA_EN BIT(0)
+#define DMA_RING_BUF_EN_SHIFT 0x01
+#define DMA_RING_BUF_EN_MASK 0xFFFF
+#define DMA_BUFLENGTH_MASK 0x0fff
+#define DMA_BUFLENGTH_SHIFT 16
+#define DMA_RING_SIZE_SHIFT 16
+#define DMA_OWN 0x8000
+#define DMA_EOP 0x4000
+#define DMA_SOP 0x2000
+#define DMA_WRAP 0x1000
+#define DMA_MAX_BURST_LENGTH 0x8
+/* Tx specific Dma descriptor bits */
+#define DMA_TX_UNDERRUN 0x0200
+#define DMA_TX_APPEND_CRC 0x0040
+#define DMA_TX_OW_CRC 0x0020
+#define DMA_TX_DO_CSUM 0x0010
+#define DMA_TX_QTAG_SHIFT 7
+#define GENET_TDMA_REG_OFF (0x4000 + \
+ TOTAL_DESC * DMA_DESC_SIZE)
+#define GENET_RDMA_REG_OFF (0x2000 + \
+ TOTAL_DESC * DMA_DESC_SIZE)
+
+/* DMA rings size */
+#define DMA_RING_SIZE (0x40)
+#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DEFAULT_Q + 1))
+
+/* DMA Descriptor */
+#define DMA_DESC_LENGTH_STATUS 0x00
+#define DMA_DESC_ADDRESS_LO 0x04
+#define DMA_DESC_ADDRESS_HI 0x08
+#define DMA_DESC_SIZE 0xc
+
+#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
+#define DMA_FC_THRESH_LO 5
+#define DMA_XOFF_THRESHOLD_SHIFT 16
+
+#define TDMA_RING_REG_BASE(QUEUE_NUMBER) (GENET_TDMA_REG_OFF \
+ + (DMA_RING_SIZE * (QUEUE_NUMBER)))
+#define TDMA_READ_PTR 0x00
+#define TDMA_CONS_INDEX 0x08
+#define TDMA_PROD_INDEX 0x0C
+#define DMA_RING_BUF_SIZE 0x10
+#define DMA_START_ADDR 0x14
+#define DMA_END_ADDR 0x1C
+#define DMA_MBUF_DONE_THRESH 0x24
+#define TDMA_FLOW_PERIOD 0x28
+#define TDMA_WRITE_PTR 0x2C
+
+#define RDMA_RING_REG_BASE(QUEUE_NUMBER) (GENET_RDMA_REG_OFF \
+ + (DMA_RING_SIZE * (QUEUE_NUMBER)))
+#define RDMA_WRITE_PTR TDMA_READ_PTR
+#define RDMA_READ_PTR TDMA_WRITE_PTR
+#define RDMA_PROD_INDEX TDMA_CONS_INDEX
+#define RDMA_CONS_INDEX TDMA_PROD_INDEX
+#define RDMA_XON_XOFF_THRESH TDMA_FLOW_PERIOD
+
+#define TDMA_REG_BASE (GENET_TDMA_REG_OFF + DMA_RINGS_SIZE)
+#define RDMA_REG_BASE (GENET_RDMA_REG_OFF + DMA_RINGS_SIZE)
+#define DMA_RING_CFG 0x0
+#define DMA_CTRL 0x04
+#define DMA_SCB_BURST_SIZE 0x0C
+
+#define RX_BUF_LENGTH 2048
+#define RX_TOTAL_BUFSIZE (RX_BUF_LENGTH * TOTAL_DESC)
+#define RX_BUF_OFFSET 2
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct bcmgenet_eth_priv {
+ void *mac_reg;
+ char rxbuffer[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);
+ void *tx_desc_base;
+ void *rx_desc_base;
+ int tx_index;
+ int rx_index;
+ int c_index;
+ int phyaddr;
+ u32 interface;
+ u32 speed;
+ struct phy_device *phydev;
+ struct mii_dev *bus;
+};
+
+static void bcmgenet_umac_reset(struct bcmgenet_eth_priv *priv)
+{
+ u32 reg;
+
+ reg = readl(priv->mac_reg + SYS_RBUF_FLUSH_CTRL);
+ reg |= BIT(1);
+ writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+
+ reg &= ~BIT(1);
+ writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+}
+
+static void reset_umac(struct bcmgenet_eth_priv *priv)
+{
+ writel(0, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+
+ writel(0, priv->mac_reg + UMAC_CMD);
+
+ writel(CMD_SW_RESET | CMD_LCL_LOOP_EN, priv->mac_reg + UMAC_CMD);
+ udelay(2);
+ writel(0, priv->mac_reg + UMAC_CMD);
+}
+
+static void init_umac(struct bcmgenet_eth_priv *priv)
+{
+ u32 reg;
+
+ reset_umac(priv);
+
+ /* clear tx/rx counter */
+ writel(MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT,
+ priv->mac_reg + UMAC_MIB_CTRL);
+ writel(0, priv->mac_reg + UMAC_MIB_CTRL);
+
+ writel(ENET_MAX_MTU_SIZE, priv->mac_reg + UMAC_MAX_FRAME_LEN);
+
+ /* init rx registers, enable ip header optimization */
+ reg = readl(priv->mac_reg + RBUF_CTRL);
+ reg |= RBUF_ALIGN_2B;
+ writel(reg, (priv->mac_reg + RBUF_CTRL));
+
+ writel(1, (priv->mac_reg + RBUF_TBUF_SIZE_CTRL));
+}
+
+static int _bcmgenet_write_hwaddr(struct bcmgenet_eth_priv *priv,
+ unsigned char *addr)
+{
+ writel_relaxed(((addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8)
+ | addr[3]), priv->mac_reg + UMAC_MAC0);
+ writel_relaxed((addr[4] << 8) | addr[5], priv->mac_reg + UMAC_MAC1);
+
+ return 0;
+}
+
+static int bcmgenet_gmac_write_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+
+ return _bcmgenet_write_hwaddr(priv, pdata->enetaddr);
+}
+
+static u32 bcmgenet_dma_disable(struct bcmgenet_eth_priv *priv)
+{
+ u32 reg;
+ u32 dma_ctrl;
+
+ dma_ctrl = 1 << (DEFAULT_Q + DMA_RING_BUF_EN_SHIFT) | DMA_EN;
+ reg = readl(priv->mac_reg + TDMA_REG_BASE + DMA_CTRL);
+ reg &= ~dma_ctrl;
+ writel(reg, (priv->mac_reg + TDMA_REG_BASE + DMA_CTRL));
+
+ reg = readl(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL);
+ reg &= ~dma_ctrl;
+ writel(reg, (priv->mac_reg + RDMA_REG_BASE + DMA_CTRL));
+
+ writel(1, priv->mac_reg + UMAC_TX_FLUSH);
+ udelay(10);
+ writel(0, priv->mac_reg + UMAC_TX_FLUSH);
+
+ return dma_ctrl;
+}
+
+static void bcmgenet_enable_dma(struct bcmgenet_eth_priv *priv, u32 dma_ctrl)
+{
+ u32 reg;
+
+ dma_ctrl |= (1 << (DEFAULT_Q + DMA_RING_BUF_EN_SHIFT));
+ dma_ctrl |= DMA_EN;
+
+ writel(dma_ctrl, priv->mac_reg + TDMA_REG_BASE + DMA_CTRL);
+
+ reg = readl(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL);
+ reg |= dma_ctrl;
+ writel(reg, priv->mac_reg + RDMA_REG_BASE + DMA_CTRL);
+}
+
+static int _bcmgenet_gmac_eth_send(struct bcmgenet_eth_priv *priv, void *packet,
+ int len)
+{
+ u32 size, len_stat, prod_index, cons;
+ u32 tries = 100;
+
+ void *desc_base = (priv->tx_desc_base + (priv->tx_index * DMA_DESC_SIZE));
+
+ len_stat = (len << DMA_BUFLENGTH_SHIFT) | (0x3F << DMA_TX_QTAG_SHIFT);
+ len_stat |= (DMA_TX_APPEND_CRC | DMA_SOP | DMA_EOP);
+
+ prod_index = readl(priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_PROD_INDEX);
+
+ size = roundup(len, ARCH_DMA_MINALIGN);
+ flush_dcache_range((ulong)packet, (ulong)packet + size);
+
+ /* Set-up packet for transmission */
+ writel(lower_32_bits((ulong)packet), (desc_base + DMA_DESC_ADDRESS_LO));
+ writel(upper_32_bits((ulong)packet), (desc_base + DMA_DESC_ADDRESS_HI));
+ writel(len_stat, (desc_base + DMA_DESC_LENGTH_STATUS));
+
+ /* Increment index and wrap-up */
+ priv->tx_index++;
+ if (!(priv->tx_index % TOTAL_DESC)) {
+ priv->tx_index = 0;
+ }
+
+ prod_index++;
+
+ /* Start Transmisson */
+ writel(prod_index, (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_PROD_INDEX));
+
+ do {
+ cons = readl(priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_CONS_INDEX);
+ } while ((cons & 0xffff) < prod_index && --tries);
+ if (!tries)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int _bcmgenet_gmac_eth_recv(struct bcmgenet_eth_priv *priv, uchar **packetp)
+{
+ u32 len_stat;
+ u32 len;
+ u32 addr;
+ u32 length = 0;
+ void *desc_base = (priv->rx_desc_base + (priv->rx_index * DMA_DESC_SIZE));
+
+ len_stat = readl(desc_base + DMA_DESC_LENGTH_STATUS);
+
+ if (!(len_stat & DMA_OWN)) {
+ len = readl(desc_base + DMA_DESC_LENGTH_STATUS);
+ addr = readl(desc_base + DMA_DESC_ADDRESS_LO);
+
+ length = (len >> DMA_BUFLENGTH_SHIFT) & DMA_BUFLENGTH_MASK;
+
+ invalidate_dcache_range((uintptr_t)addr,
+ (addr + RX_TOTAL_BUFSIZE));
+
+ /*
+ * two dummy bytes are added for IP alignment, this can be
+ * avoided by not programming RBUF_ALIGN_2B bit in RBUF_CTRL
+ */
+ *packetp = (uchar *)(ulong)addr + RX_BUF_OFFSET;
+
+ return (length - RX_BUF_OFFSET);
+ }
+
+ return -EAGAIN;
+}
+
+static int _bcmgenet_free_pkt(struct bcmgenet_eth_priv *priv, int len)
+{
+ priv->c_index = (priv->c_index + 1) & 0xFFFF;
+ writel(priv->c_index,
+ priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_CONS_INDEX);
+ udelay(1000);
+
+ priv->rx_index++;
+ if (!(priv->rx_index % TOTAL_DESC)) {
+ priv->rx_index = 0;
+ }
+
+ return 0;
+}
+
+static void rx_descs_init(struct bcmgenet_eth_priv *priv)
+{
+ char *rxbuffs = &priv->rxbuffer[0];
+ u32 len_stat, i;
+ void *desc_base = priv->rx_desc_base;
+
+ priv->c_index = 0;
+
+ len_stat = ((RX_BUF_LENGTH << DMA_BUFLENGTH_SHIFT) | DMA_OWN);
+
+ for (i = 0; i < TOTAL_DESC; i++) {
+ writel(lower_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]),
+ (((desc_base + (i * DMA_DESC_SIZE)) + DMA_DESC_ADDRESS_LO)));
+ writel(upper_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]),
+ (((desc_base + (i * DMA_DESC_SIZE)) + DMA_DESC_ADDRESS_HI)));
+ writel(len_stat,
+ ((desc_base + (i * DMA_DESC_SIZE) + DMA_DESC_LENGTH_STATUS)));
+ }
+}
+
+static void rx_ring_init(struct bcmgenet_eth_priv *priv)
+{
+ writel(DMA_MAX_BURST_LENGTH,
+ (priv->mac_reg + RDMA_REG_BASE + DMA_SCB_BURST_SIZE));
+ writel(0x0,
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + DMA_START_ADDR));
+ writel(0x0,
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_READ_PTR));
+ writel(0x0,
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_WRITE_PTR));
+ writel((TOTAL_DESC * (DMA_DESC_SIZE - 1)),
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + DMA_END_ADDR));
+ writel(0x0,
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_PROD_INDEX));
+ writel(0x0,
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_CONS_INDEX));
+ writel(((TOTAL_DESC << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH),
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + DMA_RING_BUF_SIZE));
+ writel(((DMA_FC_THRESH_LO << DMA_XOFF_THRESHOLD_SHIFT) | DMA_FC_THRESH_HI),
+ (priv->mac_reg + RDMA_RING_REG_BASE(DEFAULT_Q) + RDMA_XON_XOFF_THRESH));
+ writel((1 << DEFAULT_Q),
+ (priv->mac_reg + RDMA_REG_BASE + DMA_RING_CFG));
+}
+
+static void tx_ring_init(struct bcmgenet_eth_priv *priv)
+{
+ writel(DMA_MAX_BURST_LENGTH,
+ (priv->mac_reg + TDMA_REG_BASE + DMA_SCB_BURST_SIZE));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + DMA_START_ADDR));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_READ_PTR));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_WRITE_PTR));
+ writel((TOTAL_DESC * (DMA_DESC_SIZE - 1)),
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + DMA_END_ADDR));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_PROD_INDEX));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_CONS_INDEX));
+ writel(0x1,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + DMA_MBUF_DONE_THRESH));
+ writel(0x0,
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + TDMA_FLOW_PERIOD));
+ writel(((TOTAL_DESC << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH),
+ (priv->mac_reg + TDMA_RING_REG_BASE(DEFAULT_Q) + DMA_RING_BUF_SIZE));
+ writel((1 << DEFAULT_Q),
+ (priv->mac_reg + TDMA_REG_BASE + DMA_RING_CFG));
+}
+
+static int bcmgenet_gmac_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+
+ return _bcmgenet_gmac_eth_send(priv, packet, length);
+}
+
+static int bcmgenet_gmac_eth_recv(struct udevice *dev, int flags,
+ uchar **packetp)
+{
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+
+ return _bcmgenet_gmac_eth_recv(priv, packetp);
+}
+
+static void bcmgenet_adjust_link(struct bcmgenet_eth_priv *priv)
+{
+ struct phy_device *phy_dev = priv->phydev;
+ u32 reg = 0, reg_rgmii;
+
+ switch (phy_dev->speed) {
+ case SPEED_1000:
+ reg = UMAC_SPEED_1000;
+ break;
+ case SPEED_100:
+ reg = UMAC_SPEED_100;
+ break;
+ case SPEED_10:
+ reg = UMAC_SPEED_10;
+ break;
+ }
+
+ reg <<= CMD_SPEED_SHIFT;
+
+ reg_rgmii = readl(priv->mac_reg + EXT_RGMII_OOB_CTRL);
+ reg_rgmii &= ~OOB_DISABLE;
+ reg_rgmii |= (RGMII_LINK | RGMII_MODE_EN | ID_MODE_DIS);
+ writel(reg_rgmii, priv->mac_reg + EXT_RGMII_OOB_CTRL);
+
+ writel(reg, (priv->mac_reg + UMAC_CMD));
+}
+
+static int _bcmgenet_eth_init(struct bcmgenet_eth_priv *priv, u8 *enetaddr)
+{
+ u32 reg, dma_ctrl;
+
+ priv->tx_desc_base = priv->mac_reg + 0x4000;
+ priv->rx_desc_base = priv->mac_reg + 0x2000;
+ priv->tx_index = 0x0;
+ priv->rx_index = 0x0;
+
+ bcmgenet_umac_reset(priv);
+
+ init_umac(priv);
+
+ _bcmgenet_write_hwaddr(priv, enetaddr);
+
+ /* Disable RX/TX DMA and flush TX queues */
+ dma_ctrl = bcmgenet_dma_disable(priv);
+
+ rx_ring_init(priv);
+ rx_descs_init(priv);
+
+ tx_ring_init(priv);
+
+ /* Enable RX/TX DMA */
+ bcmgenet_enable_dma(priv, dma_ctrl);
+
+ /* PHY Start Up, read PHY properties over the wire
+ * from generic PHY set-up
+ */
+ phy_startup(priv->phydev);
+
+ /* Update MAC registers based on PHY property */
+ bcmgenet_adjust_link(priv);
+
+ /* Enable Rx/Tx */
+ reg = readl(priv->mac_reg + UMAC_CMD);
+ reg |= (CMD_TX_EN | CMD_RX_EN);
+ writel(reg, (priv->mac_reg + UMAC_CMD));
+
+ return 0;
+}
+
+static int bcmgenet_gmac_eth_start(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+
+ return _bcmgenet_eth_init(dev->priv, pdata->enetaddr);
+}
+
+static int bcmgenet_phy_init(struct bcmgenet_eth_priv *priv, void *dev)
+{
+ struct phy_device *phydev;
+ int ret;
+
+ phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface);
+ if (!phydev)
+ return -ENODEV;
+
+ phydev->supported &= PHY_GBIT_FEATURES;
+ if (priv->speed) {
+ ret = phy_set_supported(priv->phydev, priv->speed);
+ if (ret)
+ return ret;
+ }
+ phydev->advertising = phydev->supported;
+
+ phy_connect_dev(phydev, dev);
+
+ priv->phydev = phydev;
+ phy_config(priv->phydev);
+
+ return 0;
+}
+
+static inline void bcmgenet_mdio_start(struct bcmgenet_eth_priv *priv)
+{
+ u32 reg;
+
+ reg = readl_relaxed(priv->mac_reg + MDIO_CMD);
+ reg |= MDIO_START_BUSY;
+ writel_relaxed(reg, priv->mac_reg + MDIO_CMD);
+}
+
+static int bcmgenet_mdio_write(struct mii_dev *bus, int addr, int devad,
+ int reg, u16 value)
+{
+ struct udevice *dev = bus->priv;
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+ u32 status, val;
+ ulong start_time;
+ ulong timeout_us = 20000;
+
+ start_time = timer_get_us();
+
+ /* Prepare the read operation */
+ val = MDIO_WR | (addr << MDIO_PMD_SHIFT) |
+ (reg << MDIO_REG_SHIFT) | (0xffff & value);
+ writel_relaxed(val, priv->mac_reg + MDIO_CMD);
+
+ /* Start MDIO transaction */
+ bcmgenet_mdio_start(priv);
+
+ for (;;) {
+ status = readl_relaxed(priv->mac_reg + MDIO_CMD);
+ if (!(status & MDIO_START_BUSY))
+ break;
+ if (timeout_us > 0 && (timer_get_us() - start_time)
+ >= timeout_us)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int bcmgenet_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ struct udevice *dev = bus->priv;
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+ u32 status, val;
+ ulong start_time;
+ ulong timeout_us = 20000;
+
+ start_time = timer_get_us();
+
+ /* Prepare the read operation */
+ val = MDIO_RD | (addr << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
+ writel_relaxed(val, priv->mac_reg + MDIO_CMD);
+
+ /* Start MDIO transaction */
+ bcmgenet_mdio_start(priv);
+
+ for (;;) {
+ status = readl_relaxed(priv->mac_reg + MDIO_CMD);
+ if (!(status & MDIO_START_BUSY))
+ break;
+ if (timeout_us > 0 && (timer_get_us() - start_time) >= timeout_us)
+ return -ETIMEDOUT;
+ }
+
+ val = readl_relaxed(priv->mac_reg + MDIO_CMD);
+
+ return val & 0xffff;
+}
+
+static int bcmgenet_mdio_init(const char *name, struct udevice *priv)
+{
+ struct mii_dev *bus = mdio_alloc();
+
+ if (!bus) {
+ debug("Failed to allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->read = bcmgenet_mdio_read;
+ bus->write = bcmgenet_mdio_write;
+ snprintf(bus->name, sizeof(bus->name), name);
+ bus->priv = (void *)priv;
+
+ return mdio_register(bus);
+}
+
+static int bcmgenet_interface_set(struct bcmgenet_eth_priv *priv)
+{
+ phy_interface_t phy_mode = priv->interface;
+
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ writel(PORT_MODE_EXT_GPHY, priv->mac_reg + SYS_PORT_CTRL);
+ break;
+ default:
+ printf("unknown phy mode: %d\n", priv->interface);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bcmgenet_eth_probe(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+ int offset = dev_of_offset(dev);
+ const char *name;
+ int reg;
+ u8 major;
+
+ priv->mac_reg = (void *)pdata->iobase;
+ priv->interface = pdata->phy_interface;
+ priv->speed = pdata->max_speed;
+
+ /* Read GENET HW version */
+ reg = readl_relaxed(priv->mac_reg + SYS_REV_CTRL);
+ major = (reg >> 24 & 0x0f);
+ if (major == 6)
+ major = 5;
+ else if (major == 5)
+ major = 4;
+ else if (major == 0)
+ major = 1;
+
+ debug("GENET version is %1d.%1d EPHY: 0x%04x",
+ major, (reg >> 16) & 0x0f, reg & 0xffff);
+
+ bcmgenet_interface_set(priv);
+
+ offset = fdt_first_subnode(gd->fdt_blob, offset);
+ name = fdt_get_name(gd->fdt_blob, offset, NULL);
+
+ bcmgenet_mdio_init(name, dev);
+ priv->bus = miiphy_get_dev_by_name(name);
+
+ return bcmgenet_phy_init(priv, dev);
+}
+
+static void bcmgenet_gmac_eth_stop(struct udevice *dev)
+{
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+ u32 reg, dma_ctrl;
+
+ reg = readl(priv->mac_reg + UMAC_CMD);
+ reg &= ~(CMD_TX_EN | CMD_RX_EN);
+ writel(reg, (priv->mac_reg + UMAC_CMD));
+
+ dma_ctrl = 1 << (DEFAULT_Q + DMA_RING_BUF_EN_SHIFT) | DMA_EN;
+ reg = readl(priv->mac_reg + TDMA_REG_BASE + DMA_CTRL);
+ reg &= ~dma_ctrl;
+ writel(reg, (priv->mac_reg + TDMA_REG_BASE + DMA_CTRL));
+}
+
+static int bcmgenet_gmac_free_pkt(struct udevice *dev, uchar *packet,
+ int length)
+{
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+
+ return _bcmgenet_free_pkt(priv, length);
+}
+
+static const struct eth_ops bcmgenet_gmac_eth_ops = {
+ .start = bcmgenet_gmac_eth_start,
+ .write_hwaddr = bcmgenet_gmac_write_hwaddr,
+ .send = bcmgenet_gmac_eth_send,
+ .recv = bcmgenet_gmac_eth_recv,
+ .free_pkt = bcmgenet_gmac_free_pkt,
+ .stop = bcmgenet_gmac_eth_stop,
+};
+
+static int bcmgenet_eth_ofdata_to_platdata(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcmgenet_eth_priv *priv = dev_get_priv(dev);
+ const char *phy_mode;
+ int node = dev_of_offset(dev);
+ int offset = 0;
+
+ pdata->iobase = (phys_addr_t)devfdt_get_addr(dev);
+
+ /* Get phy mode from DT */
+ pdata->phy_interface = -1;
+ phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode",
+ NULL);
+
+ if (phy_mode)
+ pdata->phy_interface = phy_get_interface_by_name(phy_mode);
+ if (pdata->phy_interface == -1) {
+ debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
+ return -EINVAL;
+ }
+
+ offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle");
+ if (offset > 0) {
+ priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", 0);
+ pdata->max_speed = fdtdec_get_int(gd->fdt_blob, offset, "max-speed", 0);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id bcmgenet_eth_ids[] = {
+ {.compatible = "brcm,genet-v5"},
+ { }
+};
+
+U_BOOT_DRIVER(eth_bcmgenet) = {
+ .name = "eth_bcmgenet",
+ .id = UCLASS_ETH,
+ .of_match = bcmgenet_eth_ids,
+ .ofdata_to_platdata = bcmgenet_eth_ofdata_to_platdata,
+ .probe = bcmgenet_eth_probe,
+ .ops = &bcmgenet_gmac_eth_ops,
+ .priv_auto_alloc_size = sizeof(struct bcmgenet_eth_priv),
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
--
2.7.4
More information about the U-Boot
mailing list