[PATCH 06/10] net: add support for Designware XGMAC (10gb) ethernet
Steffen Trumtrar
s.trumtrar at pengutronix.de
Tue Oct 29 01:42:36 PDT 2024
The Designware XGMAC is designed for 1/2.5/5/10G ethernet applications.
It is mostly identical to the eqos driver but has a different register
layout.
Signed-off-by: Steffen Trumtrar <s.trumtrar at pengutronix.de>
---
drivers/net/Kconfig | 18 +
drivers/net/Makefile | 2 +
drivers/net/designware_xgmac.c | 829 +++++++++++++++++++++++++++++++++
drivers/net/designware_xgmac.h | 294 ++++++++++++
drivers/net/designware_xgmac_socfpga.c | 156 +++++++
5 files changed, 1299 insertions(+)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 13e9ff6924bbf454c77487ca247c68c458d18f1a..b5134be4b8b3186cd765a1005ade6fade5dd939e 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -140,6 +140,24 @@ config DRIVER_NET_DESIGNWARE_ROCKCHIP
help
This option enables support for the Ethernet MAC on different Rockchip SoCs
+config DRIVER_NET_DESIGNWARE_XGMAC
+ bool "Designware XGMAC Ethernet driver support" if COMPILE_TEST
+ depends on HAS_DMA && OFTREE
+ select PHYLIB
+ select MFD_SYSCON
+ help
+ This option enables support for the Synopsys Designware Ethernet XGMAC (10G Ethernet MAC).
+
+config DRIVER_NET_DESIGNWARE_XGMAC_SOCFPGA
+ bool "Designware XGMAC Ethernet driver support for SOCFPGA"
+ select DRIVER_NET_DESIGNWARE_XGMAC
+ depends on (ARCH_SOCFPGA_AGILEX5 || COMPILE_TEST)
+ select RESET_CONTROLLER if ARCH_SOCFPGA
+ select RESET_SIMPLE if ARCH_SOCFPGA_AGILEX5
+ help
+ This option enables support for the Synopsys Designware Ethernet XGMAC with specific configuration
+ for the Intel SoC FPGA chip.
+
config DRIVER_NET_DM9K
bool "Davicom dm9k[E|A|B] ethernet driver"
depends on HAS_DM9000 || COMPILE_TEST
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 207345cfa3688a4d65d2f7f20bd56cef32759024..76582fde366393905b05629772be175a3910b4c6 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -19,6 +19,8 @@ obj-$(CONFIG_DRIVER_NET_DESIGNWARE_IMX8) += designware_imx.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_ROCKCHIP) += designware_rockchip.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_XGMAC) += designware_xgmac.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_XGMAC_SOCFPGA) += designware_xgmac_socfpga.o
obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o
obj-$(CONFIG_DRIVER_NET_E1000) += e1000/
obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o
diff --git a/drivers/net/designware_xgmac.c b/drivers/net/designware_xgmac.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b7c288ada559095d1c4fefb0a7823a5ba358ee7
--- /dev/null
+++ b/drivers/net/designware_xgmac.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Intel Corporation.
+ *
+ * Portions based on U-Boot's dwc_eth_qos.c.
+ */
+
+/*
+ * This driver supports the Synopsys Designware Ethernet XGMAC (10G Ethernet
+ * MAC) IP block. The IP supports multiple options for bus type, clocking/
+ * reset structure, and feature list.
+ *
+ * The driver is written such that generic core logic is kept separate from
+ * configuration-specific logic. Code that interacts with configuration-
+ * specific resources is split out into separate functions to avoid polluting
+ * common code. If/when this driver is enhanced to support multiple
+ * configurations, the core code should be adapted to call all configuration-
+ * specific functions through function pointers, with the definition of those
+ * function pointers being supplied by struct udevice_id xgmac_ids[]'s .data
+ * field.
+ *
+ * This configuration uses an AXI master/DMA bus, an AHB slave/register bus,
+ * contains the DMA, MTL, and MAC sub-blocks, and supports a single RGMII PHY.
+ * This configuration also has SW control over all clock and reset signals to
+ * the HW block.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <dma.h>
+#include <errno.h>
+#include <malloc.h>
+#include <net.h>
+#include <of_net.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <asm/cache.h>
+#include "designware_xgmac.h"
+
+static int xgmac_mdio_wait_idle(struct xgmac_priv *xgmac)
+{
+ u32 idle;
+
+ return readl_poll_timeout(&xgmac->mac_regs->mdio_data, idle,
+ !(idle & XGMAC_MAC_MDIO_ADDRESS_SBUSY),
+ XGMAC_TIMEOUT_100MS);
+}
+
+static int xgmac_mdio_read(struct mii_bus *bus, int mdio_addr, int mdio_reg)
+{
+ struct xgmac_priv *xgmac = bus->priv;
+ u32 val;
+ u32 hw_addr;
+ u32 idle;
+ int ret;
+
+ ret = readl_poll_timeout(&xgmac->mac_regs->mdio_data, idle,
+ !(idle & XGMAC_MAC_MDIO_ADDRESS_SBUSY),
+ XGMAC_TIMEOUT_100MS);
+ if (ret) {
+ pr_err("MDIO not idle at entry: %d\n", ret);
+ return ret;
+ }
+
+ /* Set clause 22 format */
+ val = BIT(mdio_addr);
+ writel(val, &xgmac->mac_regs->mdio_clause_22_port);
+
+ hw_addr = (mdio_addr << XGMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
+ (mdio_reg & XGMAC_MAC_MDIO_REG_ADDR_C22P_MASK);
+
+ val = xgmac->config->config_mac_mdio <<
+ XGMAC_MAC_MDIO_ADDRESS_CR_SHIFT;
+
+ val |= XGMAC_MAC_MDIO_ADDRESS_SADDR |
+ XGMAC_MDIO_SINGLE_CMD_ADDR_CMD_READ |
+ XGMAC_MAC_MDIO_ADDRESS_SBUSY;
+
+ ret = readl_poll_timeout(&xgmac->mac_regs->mdio_data, idle,
+ !(idle & XGMAC_MAC_MDIO_ADDRESS_SBUSY),
+ XGMAC_TIMEOUT_100MS);
+ if (ret) {
+ pr_err("MDIO not idle at entry: %d\n", ret);
+ return ret;
+ }
+
+ writel(hw_addr, &xgmac->mac_regs->mdio_address);
+ writel(val, &xgmac->mac_regs->mdio_data);
+
+ ret = readl_poll_timeout(&xgmac->mac_regs->mdio_data, idle,
+ !(idle & XGMAC_MAC_MDIO_ADDRESS_SBUSY),
+ XGMAC_TIMEOUT_100MS);
+ if (ret) {
+ pr_err("MDIO read didn't complete: %d\n", ret);
+ return ret;
+ }
+
+ val = readl(&xgmac->mac_regs->mdio_data);
+ val &= XGMAC_MAC_MDIO_DATA_GD_MASK;
+
+ return val;
+}
+
+static int xgmac_mdio_write(struct mii_bus *bus, int mdio_addr, int mdio_reg,
+ u16 mdio_val)
+{
+ struct xgmac_priv *xgmac = bus->priv;
+ u32 val;
+ u32 hw_addr;
+ int ret;
+
+ ret = xgmac_mdio_wait_idle(xgmac);
+ if (ret) {
+ pr_err("MDIO not idle at entry: %d\n", ret);
+ return ret;
+ }
+
+ /* Set clause 22 format */
+ val = BIT(mdio_addr);
+ writel(val, &xgmac->mac_regs->mdio_clause_22_port);
+
+ hw_addr = (mdio_addr << XGMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
+ (mdio_reg & XGMAC_MAC_MDIO_REG_ADDR_C22P_MASK);
+
+ hw_addr |= (mdio_reg >> XGMAC_MAC_MDIO_ADDRESS_PA_SHIFT) <<
+ XGMAC_MAC_MDIO_ADDRESS_DA_SHIFT;
+
+ val = (xgmac->config->config_mac_mdio <<
+ XGMAC_MAC_MDIO_ADDRESS_CR_SHIFT);
+
+ val |= XGMAC_MAC_MDIO_ADDRESS_SADDR |
+ mdio_val | XGMAC_MDIO_SINGLE_CMD_ADDR_CMD_WRITE |
+ XGMAC_MAC_MDIO_ADDRESS_SBUSY;
+
+ ret = xgmac_mdio_wait_idle(xgmac);
+ if (ret) {
+ pr_err("MDIO not idle at entry: %d\n", ret);
+ return ret;
+ }
+
+ writel(hw_addr, &xgmac->mac_regs->mdio_address);
+ writel(val, &xgmac->mac_regs->mdio_data);
+
+ ret = xgmac_mdio_wait_idle(xgmac);
+ if (ret) {
+ pr_err("MDIO write didn't complete: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xgmac_set_full_duplex(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ clrbits_le32(&xgmac->mac_regs->mac_extended_conf, XGMAC_MAC_EXT_CONF_HD);
+
+ return 0;
+}
+
+static int xgmac_set_half_duplex(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ setbits_le32(&xgmac->mac_regs->mac_extended_conf, XGMAC_MAC_EXT_CONF_HD);
+
+ /* WAR: Flush TX queue when switching to half-duplex */
+ setbits_le32(&xgmac->mtl_regs->txq0_operation_mode,
+ XGMAC_MTL_TXQ0_OPERATION_MODE_FTQ);
+
+ return 0;
+}
+
+static int xgmac_set_gmii_speed(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ clrsetbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_SS_SHIFT_MASK,
+ XGMAC_MAC_CONF_SS_1G_GMII << XGMAC_MAC_CONF_SS_SHIFT);
+
+ return 0;
+}
+
+static int xgmac_set_mii_speed_100(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ clrsetbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_SS_SHIFT_MASK,
+ XGMAC_MAC_CONF_SS_100M_MII << XGMAC_MAC_CONF_SS_SHIFT);
+
+ return 0;
+}
+
+static int xgmac_set_mii_speed_10(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ clrsetbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_SS_SHIFT_MASK,
+ XGMAC_MAC_CONF_SS_2_10M_MII << XGMAC_MAC_CONF_SS_SHIFT);
+
+ return 0;
+}
+
+static void xgmac_adjust_link(struct eth_device *edev)
+{
+ struct device *dev = edev->parent;
+ bool en_calibration;
+ int ret;
+
+ if (edev->phydev->duplex)
+ ret = xgmac_set_full_duplex(dev);
+ else
+ ret = xgmac_set_half_duplex(dev);
+ if (ret < 0) {
+ pr_err("xgmac_set_*_duplex() failed: %d\n", ret);
+ }
+
+ switch (edev->phydev->speed) {
+ case SPEED_1000:
+ en_calibration = true;
+ ret = xgmac_set_gmii_speed(dev);
+ break;
+ case SPEED_100:
+ en_calibration = true;
+ ret = xgmac_set_mii_speed_100(dev);
+ break;
+ case SPEED_10:
+ en_calibration = false;
+ ret = xgmac_set_mii_speed_10(dev);
+ break;
+ default:
+ pr_err("invalid speed %d\n", edev->phydev->speed);
+ }
+ if (ret < 0)
+ pr_err("xgmac_set_*mii_speed*() failed: %d\n", ret);
+}
+
+static int xgmac_write_hwaddr(struct eth_device *edev, const unsigned char *mac)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ u32 val;
+
+ memcpy(xgmac->macaddr, mac, ETH_ALEN);
+
+ /*
+ * This function may be called before start() or after stop(). At that
+ * time, on at least some configurations of the XGMAC HW, all clocks to
+ * the XGMAC HW block will be stopped, and a reset signal applied. If
+ * any register access is attempted in this state, bus timeouts or CPU
+ * hangs may occur. This check prevents that.
+ *
+ * A simple solution to this problem would be to not implement
+ * write_hwaddr(), since start() always writes the MAC address into HW
+ * anyway. However, it is desirable to implement write_hwaddr() to
+ * support the case of SW that runs subsequent to U-Boot which expects
+ * the MAC address to already be programmed into the XGMAC registers,
+ * which must happen irrespective of whether the U-Boot user (or
+ * scripts) actually made use of the XGMAC device, and hence
+ * irrespective of whether start() was ever called.
+ *
+ */
+ if (!xgmac->config->reg_access_always_ok && !xgmac->reg_access_ok)
+ return 0;
+
+ /* Update the MAC address */
+ val = (xgmac->macaddr[5] << 8) |
+ (xgmac->macaddr[4]);
+ writel(val, &xgmac->mac_regs->address0_high);
+ val = (xgmac->macaddr[3] << 24) |
+ (xgmac->macaddr[2] << 16) |
+ (xgmac->macaddr[1] << 8) |
+ (xgmac->macaddr[0]);
+ writel(val, &xgmac->mac_regs->address0_low);
+ return 0;
+}
+
+static int xgmac_read_rom_hwaddr(struct eth_device *edev, unsigned char *mac)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ int ret;
+
+ ret = xgmac->config->ops->xgmac_get_enetaddr(edev->parent);
+ if (ret < 0)
+ return ret;
+
+ return !is_valid_ether_addr(xgmac->macaddr);
+}
+
+static int xgmac_start(struct eth_device *edev)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ int ret, i;
+ u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl;
+ ulong last_rx_desc;
+ ulong desc_pad;
+ u32 idle;
+
+ ret = phy_device_connect(edev, &xgmac->miibus, xgmac->phy_addr,
+ xgmac_adjust_link, 0, xgmac->interface);
+ if (ret)
+ return ret;
+
+ ret = xgmac->config->ops->xgmac_start_resets(edev->parent);
+ if (ret < 0) {
+ pr_err("xgmac_start_resets() failed: %d\n", ret);
+ goto err;
+ }
+
+ xgmac->reg_access_ok = true;
+
+ setbits_le32(&xgmac->dma_regs->mode, XGMAC_DMA_MODE_SWR);
+
+ ret = readl_poll_timeout(&xgmac->dma_regs->mode, idle,
+ !(idle & XGMAC_DMA_MODE_SWR),
+ XGMAC_TIMEOUT_100MS);
+ if (ret) {
+ pr_err("XGMAC_DMA_MODE_SWR stuck: %d\n", ret);
+ goto err;
+ }
+
+ /* Configure MTL */
+
+ /* Enable Store and Forward mode for TX */
+ /* Program Tx operating mode */
+ setbits_le32(&xgmac->mtl_regs->txq0_operation_mode,
+ XGMAC_MTL_TXQ0_OPERATION_MODE_TSF |
+ (XGMAC_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED <<
+ XGMAC_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT));
+
+ /* Transmit Queue weight */
+ writel(0x10, &xgmac->mtl_regs->txq0_quantum_weight);
+
+ /* Enable Store and Forward mode for RX, since no jumbo frame */
+ setbits_le32(&xgmac->mtl_regs->rxq0_operation_mode,
+ XGMAC_MTL_RXQ0_OPERATION_MODE_RSF);
+
+ /* Transmit/Receive queue fifo size; use all RAM for 1 queue */
+ val = readl(&xgmac->mac_regs->hw_feature1);
+ tx_fifo_sz = (val >> XGMAC_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT) &
+ XGMAC_MAC_HW_FEATURE1_TXFIFOSIZE_MASK;
+ rx_fifo_sz = (val >> XGMAC_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) &
+ XGMAC_MAC_HW_FEATURE1_RXFIFOSIZE_MASK;
+
+ /*
+ * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting.
+ * r/tqs is encoded as (n / 256) - 1.
+ */
+ tqs = (128 << tx_fifo_sz) / 256 - 1;
+ rqs = (128 << rx_fifo_sz) / 256 - 1;
+
+ clrsetbits_le32(&xgmac->mtl_regs->txq0_operation_mode,
+ XGMAC_MTL_TXQ0_OPERATION_MODE_TQS_MASK <<
+ XGMAC_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT,
+ tqs << XGMAC_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT);
+ clrsetbits_le32(&xgmac->mtl_regs->rxq0_operation_mode,
+ XGMAC_MTL_RXQ0_OPERATION_MODE_RQS_MASK <<
+ XGMAC_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT,
+ rqs << XGMAC_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT);
+
+ setbits_le32(&xgmac->mtl_regs->rxq0_operation_mode,
+ XGMAC_MTL_RXQ0_OPERATION_MODE_EHFC);
+
+ /* Configure MAC */
+ clrsetbits_le32(&xgmac->mac_regs->rxq_ctrl0,
+ XGMAC_MAC_RXQ_CTRL0_RXQ0EN_MASK <<
+ XGMAC_MAC_RXQ_CTRL0_RXQ0EN_SHIFT,
+ xgmac->config->config_mac <<
+ XGMAC_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
+
+ /* Multicast and Broadcast Queue Enable */
+ setbits_le32(&xgmac->mac_regs->rxq_ctrl1,
+ XGMAC_MAC_RXQ_CTRL1_MCBCQEN);
+
+ /* enable promisc mode and receive all mode */
+ setbits_le32(&xgmac->mac_regs->mac_packet_filter,
+ XGMAC_MAC_PACKET_FILTER_RA |
+ XGMAC_MAC_PACKET_FILTER_PR);
+
+ /* Set TX flow control parameters */
+ /* Set Pause Time */
+ setbits_le32(&xgmac->mac_regs->q0_tx_flow_ctrl,
+ XGMAC_MAC_Q0_TX_FLOW_CTRL_PT_MASK <<
+ XGMAC_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT);
+
+ /* Assign priority for RX flow control */
+ clrbits_le32(&xgmac->mac_regs->rxq_ctrl2,
+ XGMAC_MAC_RXQ_CTRL2_PSRQ0_MASK <<
+ XGMAC_MAC_RXQ_CTRL2_PSRQ0_SHIFT);
+
+ /* Enable flow control */
+ setbits_le32(&xgmac->mac_regs->q0_tx_flow_ctrl,
+ XGMAC_MAC_Q0_TX_FLOW_CTRL_TFE);
+ setbits_le32(&xgmac->mac_regs->rx_flow_ctrl,
+ XGMAC_MAC_RX_FLOW_CTRL_RFE);
+
+ clrbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_JD);
+
+ clrbits_le32(&xgmac->mac_regs->rx_configuration,
+ XGMAC_MAC_CONF_JE |
+ XGMAC_MAC_CONF_GPSLCE |
+ XGMAC_MAC_CONF_WD);
+
+ setbits_le32(&xgmac->mac_regs->rx_configuration,
+ XGMAC_MAC_CONF_ACS |
+ XGMAC_MAC_CONF_CST);
+
+ /* Configure DMA */
+ clrsetbits_le32(&xgmac->dma_regs->sysbus_mode,
+ XGMAC_DMA_SYSBUS_MODE_AAL,
+ XGMAC_DMA_SYSBUS_MODE_EAME |
+ XGMAC_DMA_SYSBUS_MODE_UNDEF);
+
+ /* Enable OSP mode */
+ setbits_le32(&xgmac->dma_regs->ch0_tx_control,
+ XGMAC_DMA_CH0_TX_CONTROL_OSP);
+
+ /* RX buffer size. Must be a multiple of bus width */
+ clrsetbits_le32(&xgmac->dma_regs->ch0_rx_control,
+ XGMAC_DMA_CH0_RX_CONTROL_RBSZ_MASK <<
+ XGMAC_DMA_CH0_RX_CONTROL_RBSZ_SHIFT,
+ XGMAC_MAX_PACKET_SIZE <<
+ XGMAC_DMA_CH0_RX_CONTROL_RBSZ_SHIFT);
+
+ desc_pad = 0;
+
+ setbits_le32(&xgmac->dma_regs->ch0_control,
+ XGMAC_DMA_CH0_CONTROL_PBLX8 |
+ (desc_pad << XGMAC_DMA_CH0_CONTROL_DSL_SHIFT));
+
+ /*
+ * Burst length must be < 1/2 FIFO size.
+ * FIFO size in tqs is encoded as (n / 256) - 1.
+ * Each burst is n * 8 (PBLX8) * 16 (AXI width) == 128 bytes.
+ * Half of n * 256 is n * 128, so pbl == tqs, modulo the -1.
+ */
+ pbl = tqs + 1;
+ if (pbl > 32)
+ pbl = 32;
+
+ clrsetbits_le32(&xgmac->dma_regs->ch0_tx_control,
+ XGMAC_DMA_CH0_TX_CONTROL_TXPBL_MASK <<
+ XGMAC_DMA_CH0_TX_CONTROL_TXPBL_SHIFT,
+ pbl << XGMAC_DMA_CH0_TX_CONTROL_TXPBL_SHIFT);
+
+ clrsetbits_le32(&xgmac->dma_regs->ch0_rx_control,
+ XGMAC_DMA_CH0_RX_CONTROL_RXPBL_MASK <<
+ XGMAC_DMA_CH0_RX_CONTROL_RXPBL_SHIFT,
+ 8 << XGMAC_DMA_CH0_RX_CONTROL_RXPBL_SHIFT);
+
+ /* DMA performance configuration */
+ val = (XGMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_MASK <<
+ XGMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT) |
+ (XGMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_MASK <<
+ XGMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_SHIFT) |
+ XGMAC_DMA_SYSBUS_MODE_EAME |
+ XGMAC_DMA_SYSBUS_MODE_BLEN16 |
+ XGMAC_DMA_SYSBUS_MODE_BLEN8 |
+ XGMAC_DMA_SYSBUS_MODE_BLEN4 |
+ XGMAC_DMA_SYSBUS_MODE_BLEN32;
+
+ writel(val, &xgmac->dma_regs->sysbus_mode);
+
+ /* Set up descriptors */
+
+ xgmac->tx_desc_idx = 0;
+ xgmac->rx_desc_idx = 0;
+
+ for (i = 0; i < XGMAC_DESCRIPTORS_NUM; i++) {
+ struct xgmac_desc *rx_desc = &xgmac->rx_descs[i];
+
+ writel(XGMAC_DESC3_OWN, &rx_desc->des3);
+ }
+
+ writel(0, &xgmac->dma_regs->ch0_txdesc_list_haddress);
+ writel(xgmac->tx_descs_phys, &xgmac->dma_regs->ch0_txdesc_list_address);
+ writel(XGMAC_DESCRIPTORS_NUM - 1,
+ &xgmac->dma_regs->ch0_txdesc_ring_length);
+
+ writel(0, &xgmac->dma_regs->ch0_rxdesc_list_haddress);
+ writel(xgmac->rx_descs_phys, &xgmac->dma_regs->ch0_rxdesc_list_address);
+ writel(XGMAC_DESCRIPTORS_NUM - 1,
+ &xgmac->dma_regs->ch0_rxdesc_ring_length);
+
+ /* Enable everything */
+ setbits_le32(&xgmac->dma_regs->ch0_tx_control,
+ XGMAC_DMA_CH0_TX_CONTROL_ST);
+ setbits_le32(&xgmac->dma_regs->ch0_rx_control,
+ XGMAC_DMA_CH0_RX_CONTROL_SR);
+ setbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_TE);
+ setbits_le32(&xgmac->mac_regs->rx_configuration,
+ XGMAC_MAC_CONF_RE);
+
+ /* TX tail pointer not written until we need to TX a packet */
+ /*
+ * Point RX tail pointer at last descriptor. Ideally, we'd point at the
+ * first descriptor, implying all descriptors were available. However,
+ * that's not distinguishable from none of the descriptors being
+ * available.
+ */
+ last_rx_desc = (ulong)&xgmac->rx_descs[(XGMAC_DESCRIPTORS_NUM - 1)];
+ writel(last_rx_desc, &xgmac->dma_regs->ch0_rxdesc_tail_pointer);
+
+ xgmac->started = true;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static void xgmac_stop(struct eth_device *edev)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ u64 start_time;
+ u32 val;
+ u32 trcsts;
+ u32 txqsts;
+ u32 prxq;
+ u32 rxqsts;
+
+ debug("%s(dev=%p):\n", __func__, edev);
+
+ if (!xgmac->started)
+ return;
+ xgmac->started = false;
+ xgmac->reg_access_ok = false;
+
+ /* Disable TX DMA */
+ clrbits_le32(&xgmac->dma_regs->ch0_tx_control,
+ XGMAC_DMA_CH0_TX_CONTROL_ST);
+
+ /* Wait for TX all packets to drain out of MTL */
+ start_time = get_time_ns();
+
+ while (!is_timeout(start_time, XGMAC_TIMEOUT_100MS)) {
+ val = readl(&xgmac->mtl_regs->txq0_debug);
+
+ trcsts = (val >> XGMAC_MTL_TXQ0_DEBUG_TRCSTS_SHIFT) &
+ XGMAC_MTL_TXQ0_DEBUG_TRCSTS_MASK;
+
+ txqsts = val & XGMAC_MTL_TXQ0_DEBUG_TXQSTS;
+
+ if (trcsts != XGMAC_MTL_TXQ0_DEBUG_TRCSTS_READ_STATE && !txqsts)
+ break;
+ }
+
+ /* Turn off MAC TX and RX */
+ clrbits_le32(&xgmac->mac_regs->tx_configuration,
+ XGMAC_MAC_CONF_RE);
+ clrbits_le32(&xgmac->mac_regs->rx_configuration,
+ XGMAC_MAC_CONF_RE);
+
+ /* Wait for all RX packets to drain out of MTL */
+ start_time = get_time_ns();
+
+ while (!is_timeout(start_time, XGMAC_TIMEOUT_100MS)) {
+ val = readl(&xgmac->mtl_regs->rxq0_debug);
+
+ prxq = (val >> XGMAC_MTL_RXQ0_DEBUG_PRXQ_SHIFT) &
+ XGMAC_MTL_RXQ0_DEBUG_PRXQ_MASK;
+
+ rxqsts = (val >> XGMAC_MTL_RXQ0_DEBUG_RXQSTS_SHIFT) &
+ XGMAC_MTL_RXQ0_DEBUG_RXQSTS_MASK;
+
+ if (!prxq && !rxqsts)
+ break;
+ }
+
+ /* Turn off RX DMA */
+ clrbits_le32(&xgmac->dma_regs->ch0_rx_control,
+ XGMAC_DMA_CH0_RX_CONTROL_SR);
+}
+
+static int xgmac_send(struct eth_device *edev, void *packet, int length)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ struct xgmac_desc *tx_desc;
+ dma_addr_t dma;
+ u32 des3_prev, des3;
+ int ret;
+
+ tx_desc = &xgmac->tx_descs[xgmac->tx_desc_idx];
+ xgmac->tx_desc_idx++;
+ xgmac->tx_desc_idx %= XGMAC_DESCRIPTORS_NUM;
+
+ dma = dma_map_single(edev->parent, packet, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(edev->parent, dma))
+ return -EFAULT;
+
+ tx_desc->des0 = dma;
+ tx_desc->des1 = 0;
+ tx_desc->des2 = length;
+ /*
+ * Make sure that if HW sees the _OWN write below, it will see all the
+ * writes to the rest of the descriptor too.
+ */
+ barrier();
+
+ des3_prev = XGMAC_DESC3_OWN | XGMAC_DESC3_FD | XGMAC_DESC3_LD | length;
+ writel(des3_prev, &tx_desc->des3);
+ writel((ulong)(tx_desc + 1), &xgmac->dma_regs->ch0_txdesc_tail_pointer); // <-- TODO
+
+ ret = readl_poll_timeout(&tx_desc->des3, des3,
+ !(des3 & XGMAC_DESC3_OWN),
+ 100 * USEC_PER_MSEC);
+
+ dma_unmap_single(edev->parent, dma, length, DMA_TO_DEVICE);
+
+ if (ret == -ETIMEDOUT)
+ debug("%s: TX timeout 0x%08x\n", __func__, des3);
+
+ return ret;
+}
+
+static void xgmac_recv(struct eth_device *edev)
+{
+ struct xgmac_priv *xgmac = edev->priv;
+ struct xgmac_desc *rx_desc;
+ dma_addr_t dma;
+ void *pkt;
+ int length;
+
+ rx_desc = &xgmac->rx_descs[xgmac->rx_desc_idx];
+
+ if (rx_desc->des3 & XGMAC_DESC3_OWN)
+ return;
+
+ dma = xgmac->dma_rx_buf[xgmac->rx_desc_idx];
+ pkt = phys_to_virt(dma);
+ length = rx_desc->des3 & XGMAC_RDES3_PKT_LENGTH_MASK;
+
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)pkt, length,
+ DMA_FROM_DEVICE);
+ net_receive(edev, pkt, length);
+ dma_sync_single_for_device(edev->parent, (unsigned long)pkt,
+ length, DMA_FROM_DEVICE);
+
+ /* Read Format RX descriptor */
+ rx_desc = &xgmac->rx_descs[xgmac->rx_desc_idx];
+ rx_desc->des0 = dma;
+ rx_desc->des1 = 0;
+ rx_desc->des2 = 0;
+ /*
+ * Make sure that if HW sees the _OWN write below, it will see all the
+ * writes to the rest of the descriptor too.
+ */
+ rx_desc->des3 = XGMAC_DESC3_OWN;
+ barrier();
+
+ writel((ulong)rx_desc, &xgmac->dma_regs->ch0_rxdesc_tail_pointer);
+
+ xgmac->rx_desc_idx++;
+ xgmac->rx_desc_idx %= XGMAC_DESCRIPTORS_NUM;
+}
+
+static int xgmac_probe_resources_core(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+ unsigned int desc_step;
+ int ret = 0;
+ void *p;
+ int i;
+
+ /* Maximum distance between neighboring descriptors, in Bytes. */
+ desc_step = sizeof(struct xgmac_desc);
+
+ xgmac->tx_descs = dma_alloc_coherent(XGMAC_DESCRIPTORS_SIZE,
+ &xgmac->tx_descs_phys);
+ if (!xgmac->tx_descs) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ xgmac->rx_descs = dma_alloc_coherent(XGMAC_DESCRIPTORS_SIZE,
+ &xgmac->rx_descs_phys);
+ if (!xgmac->rx_descs) {
+ ret = -ENOMEM;
+ goto err_free_tx_descs;
+ }
+
+ p = dma_alloc(XGMAC_RX_BUFFER_SIZE);
+ if (!p)
+ goto err_free_descs;
+
+ for (i = 0; i < XGMAC_DESCRIPTORS_NUM; i++) {
+ struct xgmac_desc *rx_desc = &xgmac->rx_descs[i];
+ dma_addr_t dma;
+
+ dma = dma_map_single(dev, p, XGMAC_MAX_PACKET_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, dma)) {
+ ret = -EFAULT;
+ goto err_free_dma_bufs;
+ }
+
+ rx_desc->des0 = dma;
+ xgmac->dma_rx_buf[i] = dma;
+
+ p += XGMAC_MAX_PACKET_SIZE;
+ }
+
+ return 0;
+
+err_free_dma_bufs:
+ dma_free(phys_to_virt(xgmac->rx_descs[0].des0));
+err_free_descs:
+ dma_free_coherent(xgmac->rx_descs, xgmac->rx_descs_phys, XGMAC_DESCRIPTORS_SIZE);
+err_free_tx_descs:
+ dma_free_coherent(xgmac->tx_descs, xgmac->tx_descs_phys, XGMAC_DESCRIPTORS_SIZE);
+err:
+
+ return ret;
+}
+
+static int xgmac_remove_resources_core(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ dma_free(xgmac->rx_dma_buf);
+ dma_free_coherent(xgmac->rx_descs, xgmac->rx_descs_phys, XGMAC_DESCRIPTORS_SIZE);
+ dma_free_coherent(xgmac->tx_descs, xgmac->tx_descs_phys, XGMAC_DESCRIPTORS_SIZE);
+
+ return 0;
+}
+
+static void xgmac_probe_dt(struct device *dev, struct xgmac_priv *xgmac)
+{
+ struct device_node *child;
+
+ xgmac->interface = of_get_phy_mode(dev->of_node);
+ xgmac->phy_addr = 0;
+
+ /* Set MDIO bus device node, if present. */
+ for_each_child_of_node(dev->of_node, child) {
+ if (of_device_is_compatible(child, "snps,dwmac-mdio") ||
+ (child->name && !of_node_cmp(child->name, "mdio"))) {
+ xgmac->miibus.dev.of_node = child;
+ break;
+ }
+ }
+}
+
+int xgmac_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct mii_bus *miibus;
+ struct xgmac_priv *xgmac;
+ struct resource *iores;
+ struct eth_device *edev;
+ int ret = 0;
+
+ xgmac = xzalloc(sizeof(*xgmac));
+
+ xgmac->dev = dev;
+ ret = dev_get_drvdata(dev, (const void **)&xgmac->config);
+ if (ret < 0) {
+ pr_err("xgmac_probe() failed to get driver data: %d\n", ret);
+ return ret;
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ xgmac->regs = IOMEM(iores->start);
+
+ xgmac->mac_regs = (void *)(xgmac->regs + XGMAC_MAC_REGS_BASE);
+ xgmac->mtl_regs = (void *)(xgmac->regs + XGMAC_MTL_REGS_BASE);
+ xgmac->dma_regs = (void *)(xgmac->regs + XGMAC_DMA_REGS_BASE);
+
+ xgmac_probe_dt(dev, xgmac);
+
+ edev = &xgmac->netdev;
+ dev->priv = edev->priv = xgmac;
+
+ edev->parent = dev;
+ edev->open = xgmac_start;
+ edev->send = xgmac_send;
+ edev->halt = xgmac_stop;
+ edev->recv = xgmac_recv;
+ edev->get_ethaddr = xgmac_read_rom_hwaddr;
+ edev->set_ethaddr = xgmac_write_hwaddr;
+
+ of_property_read_u32(np, "max-speed", &xgmac->max_speed);
+
+ miibus = &xgmac->miibus;
+ miibus->parent = edev->parent;
+ miibus->read = xgmac_mdio_read;
+ miibus->write = xgmac_mdio_write;
+ miibus->priv = xgmac;
+
+ ret = xgmac_probe_resources_core(dev);
+ if (ret < 0) {
+ pr_err("xgmac_probe_resources_core() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = xgmac->config->ops->xgmac_probe_resources(dev);
+ if (ret < 0) {
+ pr_err("xgmac_probe_resources() failed: %d\n", ret);
+ goto err_remove_resources_core;
+ }
+
+ ret = mdiobus_register(miibus);
+ if (ret)
+ return ret;
+
+ return eth_register(edev);
+
+err_remove_resources_core:
+ xgmac_remove_resources_core(dev);
+
+ return ret;
+}
+
+void xgmac_remove(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ eth_unregister(&xgmac->netdev);
+ mdiobus_unregister(&xgmac->miibus);
+
+ xgmac_remove_resources_core(dev);
+}
diff --git a/drivers/net/designware_xgmac.h b/drivers/net/designware_xgmac.h
new file mode 100644
index 0000000000000000000000000000000000000000..f6d39adf6c21cd7c17805add7aee3ffe396e18a4
--- /dev/null
+++ b/drivers/net/designware_xgmac.h
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Intel Coporation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/phy.h>
+
+/* Core registers */
+
+#define XGMAC_MAC_REGS_BASE 0x000
+
+struct xgmac_mac_regs {
+ u32 tx_configuration; /* 0x000 */
+ u32 rx_configuration; /* 0x004 */
+ u32 mac_packet_filter; /* 0x008 */
+ u32 unused_00c[(0x070 - 0x00c) / 4]; /* 0x00c */
+ u32 q0_tx_flow_ctrl; /* 0x070 */
+ u32 unused_070[(0x090 - 0x074) / 4]; /* 0x074 */
+ u32 rx_flow_ctrl; /* 0x090 */
+ u32 unused_094[(0x0a0 - 0x094) / 4]; /* 0x094 */
+ u32 rxq_ctrl0; /* 0x0a0 */
+ u32 rxq_ctrl1; /* 0x0a4 */
+ u32 rxq_ctrl2; /* 0x0a8 */
+ u32 unused_0ac[(0x0dc - 0x0ac) / 4]; /* 0x0ac */
+ u32 us_tic_counter; /* 0x0dc */
+ u32 unused_0e0[(0x11c - 0x0e0) / 4]; /* 0x0e0 */
+ u32 hw_feature0; /* 0x11c */
+ u32 hw_feature1; /* 0x120 */
+ u32 hw_feature2; /* 0x124 */
+ u32 hw_feature3; /* 0x128 */
+ u32 hw_feature4; /* 0x12c */
+ u32 unused_130[(0x140 - 0x130) / 4]; /* 0x130 */
+ u32 mac_extended_conf; /* 0x140 */
+ u32 unused_144[(0x200 - 0x144) / 4]; /* 0x144 */
+ u32 mdio_address; /* 0x200 */
+ u32 mdio_data; /* 0x204 */
+ u32 mdio_cont_write_addr; /* 0x208 */
+ u32 mdio_cont_write_data; /* 0x20c */
+ u32 mdio_cont_scan_port_enable; /* 0x210 */
+ u32 mdio_intr_status; /* 0x214 */
+ u32 mdio_intr_enable; /* 0x218 */
+ u32 mdio_port_cnct_dsnct_status; /* 0x21c */
+ u32 mdio_clause_22_port; /* 0x220 */
+ u32 unused_224[(0x300 - 0x224) / 4]; /* 0x224 */
+ u32 address0_high; /* 0x300 */
+ u32 address0_low; /* 0x304 */
+};
+
+#define XGMAC_TIMEOUT_100MS 100000
+#define XGMAC_MAC_CONF_SS_SHIFT 29
+#define XGMAC_MAC_CONF_SS_SHIFT_MASK GENMASK(31, 29)
+#define XGMAC_MAC_CONF_SS_10G_XGMII 0
+#define XGMAC_MAC_CONF_SS_2_5G_GMII 2
+#define XGMAC_MAC_CONF_SS_1G_GMII 3
+#define XGMAC_MAC_CONF_SS_100M_MII 4
+#define XGMAC_MAC_CONF_SS_5G_XGMII 5
+#define XGMAC_MAC_CONF_SS_2_5G_XGMII 6
+#define XGMAC_MAC_CONF_SS_2_10M_MII 7
+
+#define XGMAC_MAC_CONF_JD BIT(16)
+#define XGMAC_MAC_CONF_JE BIT(8)
+#define XGMAC_MAC_CONF_WD BIT(7)
+#define XGMAC_MAC_CONF_GPSLCE BIT(6)
+#define XGMAC_MAC_CONF_CST BIT(2)
+#define XGMAC_MAC_CONF_ACS BIT(1)
+#define XGMAC_MAC_CONF_TE BIT(0)
+#define XGMAC_MAC_CONF_RE BIT(0)
+
+#define XGMAC_MAC_EXT_CONF_HD BIT(24)
+
+#define XGMAC_MAC_PACKET_FILTER_RA BIT(31)
+#define XGMAC_MAC_PACKET_FILTER_PR BIT(0)
+
+#define XGMAC_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT 16
+#define XGMAC_MAC_Q0_TX_FLOW_CTRL_PT_MASK GENMASK(15, 0)
+#define XGMAC_MAC_Q0_TX_FLOW_CTRL_TFE BIT(1)
+
+#define XGMAC_MAC_RX_FLOW_CTRL_RFE BIT(0)
+#define XGMAC_MAC_RXQ_CTRL0_RXQ0EN_SHIFT 0
+#define XGMAC_MAC_RXQ_CTRL0_RXQ0EN_MASK GENMASK(1, 0)
+#define XGMAC_MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED 0
+#define XGMAC_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB 2
+#define XGMAC_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV 1
+
+#define XGMAC_MAC_RXQ_CTRL1_MCBCQEN BIT(15)
+
+#define XGMAC_MAC_RXQ_CTRL2_PSRQ0_SHIFT 0
+#define XGMAC_MAC_RXQ_CTRL2_PSRQ0_MASK GENMASK(7, 0)
+
+#define XGMAC_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT 6
+#define XGMAC_MAC_HW_FEATURE1_TXFIFOSIZE_MASK GENMASK(4, 0)
+#define XGMAC_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT 0
+#define XGMAC_MAC_HW_FEATURE1_RXFIFOSIZE_MASK GENMASK(4, 0)
+
+#define XGMAC_MDIO_SINGLE_CMD_SHIFT 16
+#define XGMAC_MDIO_SINGLE_CMD_ADDR_CMD_READ 3 << XGMAC_MDIO_SINGLE_CMD_SHIFT
+#define XGMAC_MDIO_SINGLE_CMD_ADDR_CMD_WRITE BIT(16)
+#define XGMAC_MAC_MDIO_ADDRESS_PA_SHIFT 16
+#define XGMAC_MAC_MDIO_ADDRESS_PA_MASK GENMASK(15, 0)
+#define XGMAC_MAC_MDIO_ADDRESS_DA_SHIFT 21
+#define XGMAC_MAC_MDIO_ADDRESS_CR_SHIFT 19
+#define XGMAC_MAC_MDIO_ADDRESS_CR_100_150 0
+#define XGMAC_MAC_MDIO_ADDRESS_CR_150_250 1
+#define XGMAC_MAC_MDIO_ADDRESS_CR_250_300 2
+#define XGMAC_MAC_MDIO_ADDRESS_CR_300_350 3
+#define XGMAC_MAC_MDIO_ADDRESS_CR_350_400 4
+#define XGMAC_MAC_MDIO_ADDRESS_CR_400_500 5
+#define XGMAC_MAC_MDIO_ADDRESS_SADDR BIT(18)
+#define XGMAC_MAC_MDIO_ADDRESS_SBUSY BIT(22)
+#define XGMAC_MAC_MDIO_REG_ADDR_C22P_MASK GENMASK(4, 0)
+#define XGMAC_MAC_MDIO_DATA_GD_MASK GENMASK(15, 0)
+
+/* MTL Registers */
+
+#define XGMAC_MTL_REGS_BASE 0x1000
+
+struct xgmac_mtl_regs {
+ u32 mtl_operation_mode; /* 0x1000 */
+ u32 unused_1004[(0x1030 - 0x1004) / 4]; /* 0x1004 */
+ u32 mtl_rxq_dma_map0; /* 0x1030 */
+ u32 mtl_rxq_dma_map1; /* 0x1034 */
+ u32 mtl_rxq_dma_map2; /* 0x1038 */
+ u32 mtl_rxq_dma_map3; /* 0x103c */
+ u32 mtl_tc_prty_map0; /* 0x1040 */
+ u32 mtl_tc_prty_map1; /* 0x1044 */
+ u32 unused_1048[(0x1100 - 0x1048) / 4]; /* 0x1048 */
+ u32 txq0_operation_mode; /* 0x1100 */
+ u32 unused_1104; /* 0x1104 */
+ u32 txq0_debug; /* 0x1108 */
+ u32 unused_100c[(0x1118 - 0x110c) / 4]; /* 0x110c */
+ u32 txq0_quantum_weight; /* 0x1118 */
+ u32 unused_111c[(0x1140 - 0x111c) / 4]; /* 0x111c */
+ u32 rxq0_operation_mode; /* 0x1140 */
+ u32 unused_1144; /* 0x1144 */
+ u32 rxq0_debug; /* 0x1148 */
+};
+
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT 16
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_TQS_MASK GENMASK(8, 0)
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT 2
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED 2
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_TSF BIT(1)
+#define XGMAC_MTL_TXQ0_OPERATION_MODE_FTQ BIT(0)
+
+#define XGMAC_MTL_TXQ0_DEBUG_TXQSTS BIT(4)
+#define XGMAC_MTL_TXQ0_DEBUG_TRCSTS_SHIFT 1
+#define XGMAC_MTL_TXQ0_DEBUG_TRCSTS_MASK GENMASK(2, 0)
+#define XGMAC_MTL_TXQ0_DEBUG_TRCSTS_READ_STATE 0x1
+
+#define XGMAC_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT 16
+#define XGMAC_MTL_RXQ0_OPERATION_MODE_RQS_MASK GENMASK(9, 0)
+#define XGMAC_MTL_RXQ0_OPERATION_MODE_EHFC BIT(7)
+#define XGMAC_MTL_RXQ0_OPERATION_MODE_RSF BIT(5)
+
+#define XGMAC_MTL_RXQ0_DEBUG_PRXQ_SHIFT 16
+#define XGMAC_MTL_RXQ0_DEBUG_PRXQ_MASK GENMASK(14, 0)
+#define XGMAC_MTL_RXQ0_DEBUG_RXQSTS_SHIFT 4
+#define XGMAC_MTL_RXQ0_DEBUG_RXQSTS_MASK GENMASK(1, 0)
+
+/* DMA Registers */
+
+#define XGMAC_DMA_REGS_BASE 0x3000
+
+struct xgmac_dma_regs {
+ u32 mode; /* 0x3000 */
+ u32 sysbus_mode; /* 0x3004 */
+ u32 unused_3008[(0x3100 - 0x3008) / 4]; /* 0x3008 */
+ u32 ch0_control; /* 0x3100 */
+ u32 ch0_tx_control; /* 0x3104 */
+ u32 ch0_rx_control; /* 0x3108 */
+ u32 slot_func_control_status; /* 0x310c */
+ u32 ch0_txdesc_list_haddress; /* 0x3110 */
+ u32 ch0_txdesc_list_address; /* 0x3114 */
+ u32 ch0_rxdesc_list_haddress; /* 0x3118 */
+ u32 ch0_rxdesc_list_address; /* 0x311c */
+ u32 unused_3120; /* 0x3120 */
+ u32 ch0_txdesc_tail_pointer; /* 0x3124 */
+ u32 unused_3128; /* 0x3128 */
+ u32 ch0_rxdesc_tail_pointer; /* 0x312c */
+ u32 ch0_txdesc_ring_length; /* 0x3130 */
+ u32 ch0_rxdesc_ring_length; /* 0x3134 */
+ u32 unused_3138[(0x3160 - 0x3138) / 4]; /* 0x3138 */
+ u32 ch0_status; /* 0x3160 */
+};
+
+#define XGMAC_DMA_MODE_SWR BIT(0)
+#define XGMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_SHIFT 24
+#define XGMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_MASK GENMASK(4, 0)
+#define XGMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT 16
+#define XGMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_MASK GENMASK(4, 0)
+#define XGMAC_DMA_SYSBUS_MODE_AAL BIT(12)
+#define XGMAC_DMA_SYSBUS_MODE_EAME BIT(11)
+#define XGMAC_DMA_SYSBUS_MODE_BLEN32 BIT(4)
+#define XGMAC_DMA_SYSBUS_MODE_BLEN16 BIT(3)
+#define XGMAC_DMA_SYSBUS_MODE_BLEN8 BIT(2)
+#define XGMAC_DMA_SYSBUS_MODE_BLEN4 BIT(1)
+#define XGMAC_DMA_SYSBUS_MODE_UNDEF BIT(0)
+
+#define XGMAC_DMA_CH0_CONTROL_DSL_SHIFT 18
+#define XGMAC_DMA_CH0_CONTROL_PBLX8 BIT(16)
+
+#define XGMAC_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16
+#define XGMAC_DMA_CH0_TX_CONTROL_TXPBL_MASK GENMASK(5, 0)
+#define XGMAC_DMA_CH0_TX_CONTROL_OSP BIT(4)
+#define XGMAC_DMA_CH0_TX_CONTROL_ST BIT(0)
+
+#define XGMAC_DMA_CH0_RX_CONTROL_RXPBL_SHIFT 16
+#define XGMAC_DMA_CH0_RX_CONTROL_RXPBL_MASK GENMASK(5, 0)
+#define XGMAC_DMA_CH0_RX_CONTROL_RBSZ_SHIFT 4
+#define XGMAC_DMA_CH0_RX_CONTROL_RBSZ_MASK GENMASK(10, 0)
+#define XGMAC_DMA_CH0_RX_CONTROL_SR BIT(0)
+
+/* Descriptors */
+#define XGMAC_DESCRIPTOR_WORDS 4
+#define XGMAC_DESCRIPTOR_SIZE (XGMAC_DESCRIPTOR_WORDS * 4)
+#define XGMAC_DESCRIPTORS_NUM 8
+#define XGMAC_DESCRIPTOR_ALIGN 64
+#define XGMAC_DESCRIPTORS_SIZE ALIGN(XGMAC_DESCRIPTORS_NUM * \
+ XGMAC_DESCRIPTOR_SIZE, XGMAC_DESCRIPTOR_ALIGN)
+#define XGMAC_BUFFER_ALIGN XGMAC_DESCRIPTOR_ALIGN
+#define XGMAC_MAX_PACKET_SIZE ALIGN(1568, XGMAC_DESCRIPTOR_ALIGN)
+#define XGMAC_RX_BUFFER_SIZE (XGMAC_DESCRIPTORS_NUM * XGMAC_MAX_PACKET_SIZE)
+
+#define XGMAC_RDES3_PKT_LENGTH_MASK GENMASK(13, 0)
+
+struct xgmac_desc {
+ u32 des0;
+ u32 des1;
+ u32 des2;
+ u32 des3;
+};
+
+#define XGMAC_DESC3_OWN BIT(31)
+#define XGMAC_DESC3_FD BIT(29)
+#define XGMAC_DESC3_LD BIT(28)
+
+#define XGMAC_AXI_WIDTH_32 4
+#define XGMAC_AXI_WIDTH_64 8
+#define XGMAC_AXI_WIDTH_128 16
+
+struct xgmac_config {
+ bool reg_access_always_ok;
+ int swr_wait;
+ int config_mac;
+ int config_mac_mdio;
+ unsigned int axi_bus_width;
+ phy_interface_t interface;
+ struct xgmac_ops *ops;
+};
+
+struct xgmac_ops {
+ int (*xgmac_probe_resources)(struct device *dev);
+ int (*xgmac_start_resets)(struct device *dev);
+ int (*xgmac_get_enetaddr)(struct device *dev);
+};
+
+struct xgmac_priv {
+ struct eth_device netdev;
+ struct device *dev;
+ const struct xgmac_config *config;
+ void __iomem *regs;
+ struct xgmac_mac_regs *mac_regs;
+ struct xgmac_mtl_regs *mtl_regs;
+ struct xgmac_dma_regs *dma_regs;
+ struct reset_control *rst;
+ struct reset_control *rst_ocp;
+ struct mii_bus miibus;
+ int phy_addr;
+ phy_interface_t interface;
+
+ u8 macaddr[6];
+
+ u32 reg_offset, reg_shift;
+ u32 max_speed;
+ struct xgmac_desc *tx_descs, *rx_descs;
+ dma_addr_t tx_descs_phys, rx_descs_phys;
+ dma_addr_t dma_rx_buf[XGMAC_DESCRIPTORS_NUM];
+ int tx_desc_idx, rx_desc_idx;
+ unsigned int desc_size;
+ unsigned int desc_per_cacheline;
+ void *rx_dma_buf;
+ bool started;
+ bool reg_access_ok;
+ bool clk_ck_enabled;
+};
+
+int xgmac_probe(struct device *dev);
+void xgmac_remove(struct device *dev);
+void xgmac_flush_desc_generic(void *desc);
+void xgmac_flush_buffer_generic(void *buf, size_t size);
+int xgmac_null_ops(struct device *dev);
+
+extern struct xgmac_config xgmac_socfpga_config;
diff --git a/drivers/net/designware_xgmac_socfpga.c b/drivers/net/designware_xgmac_socfpga.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d8dba3c17cdb3fe7c82a6fa9315fecfdbfcc32a
--- /dev/null
+++ b/drivers/net/designware_xgmac_socfpga.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Intel Corporation
+ */
+#define DEBUG
+#include <common.h>
+#include <init.h>
+#include <errno.h>
+#include <malloc.h>
+#include <net.h>
+#include <linux/reset.h>
+#include <linux/phy.h>
+#include <mfd/syscon.h>
+#include <linux/clk.h>
+#include <mach/socfpga/soc64-system-manager.h>
+#include <mach/socfpga/secure_reg_helper.h>
+
+#include "designware_xgmac.h"
+
+#define SOCFPGA_XGMAC_SYSCON_ARG_COUNT 2
+
+static int dwxgmac_socfpga_do_setphy(struct device *dev, u32 modereg)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+ u32 modemask = SYSMGR_EMACGRP_CTRL_PHYSEL_MASK;
+ u32 index = (xgmac->reg_offset - SYSMGR_SOC64_EMAC0) >> 2;
+ u32 id = SOCFPGA_SECURE_REG_SYSMGR_SOC64_EMAC0 + index;
+ int ret;
+
+ ret = socfpga_secure_reg_update32(id, modemask, modereg);
+ if (ret) {
+ dev_err(dev, "Failed to set PHY register via SMC call\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xgmac_probe_resources_socfpga(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+ phy_interface_t interface;
+ int ret;
+ u32 modereg;
+
+ interface = xgmac->interface;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_GMII:
+ modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
+ break;
+ default:
+ dev_err(dev, "Unsupported PHY mode\n");
+ return -EINVAL;
+ }
+
+ /* Get PHY syscon */
+ ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon",
+ 1, &xgmac->reg_offset);
+ if (ret) {
+ dev_err(dev, "Could not read reg_offset from sysmgr-syscon! Please update the devicetree.\n");
+
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon",
+ 2, &xgmac->reg_shift);
+ if (ret) {
+ dev_err(dev, "Could not read reg_shift from sysmgr-syscon! Please update the devicetree.\n");
+ return -EINVAL;
+ }
+
+ xgmac->rst = reset_control_get(dev, "stmmaceth");
+ if (IS_ERR(xgmac->rst)) {
+ dev_err(dev, "Invalid reset line 'stmmaceth'.\n");
+ return PTR_ERR(xgmac->rst);
+ }
+ xgmac->rst_ocp = reset_control_get(dev, "stmmaceth-ocp");
+ if (IS_ERR(xgmac->rst_ocp)) {
+ dev_err(dev, "Invalid reset line 'stmmaceth-ocp'.\n");
+ return PTR_ERR(xgmac->rst_ocp);
+ }
+
+ reset_control_assert(xgmac->rst_ocp);
+ reset_control_assert(xgmac->rst);
+
+ ret = dwxgmac_socfpga_do_setphy(dev, modereg);
+ if (ret)
+ return ret;
+
+ reset_control_deassert(xgmac->rst_ocp);
+ reset_control_deassert(xgmac->rst);
+
+ return 0;
+}
+
+static int xgmac_get_enetaddr_socfpga(struct device *dev)
+{
+ return -ENOTSUPP;
+}
+
+static int xgmac_start_resets_socfpga(struct device *dev)
+{
+ struct xgmac_priv *xgmac = dev_get_priv(dev);
+
+ reset_control_assert(xgmac->rst);
+ reset_control_assert(xgmac->rst_ocp);
+
+ udelay(2);
+
+ reset_control_deassert(xgmac->rst);
+ reset_control_deassert(xgmac->rst_ocp);
+
+ return 0;
+}
+
+static struct xgmac_ops xgmac_socfpga_ops = {
+ .xgmac_probe_resources = xgmac_probe_resources_socfpga,
+ .xgmac_start_resets = xgmac_start_resets_socfpga,
+ .xgmac_get_enetaddr = xgmac_get_enetaddr_socfpga,
+};
+
+struct xgmac_config __maybe_unused xgmac_socfpga_config = {
+ .reg_access_always_ok = false,
+ .swr_wait = 50,
+ .config_mac = XGMAC_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = XGMAC_MAC_MDIO_ADDRESS_CR_350_400,
+ .axi_bus_width = XGMAC_AXI_WIDTH_64,
+ .ops = &xgmac_socfpga_ops
+};
+
+static __maybe_unused struct of_device_id xgmac_socfpga_compatible[] = {
+ {
+ .compatible = "intel,socfpga-dwxgmac",
+ .data = &xgmac_socfpga_config
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, xgmac_socfpga_compatible);
+
+static struct driver xgmac_socfpga_driver = {
+ .name = "designware-xgmac-socfpga",
+ .probe = xgmac_probe,
+ .remove = xgmac_remove,
+ .of_compatible = DRV_OF_COMPAT(xgmac_socfpga_compatible),
+};
+device_platform_driver(xgmac_socfpga_driver);
--
2.46.0
More information about the barebox
mailing list