From: Marcello Sylvester Bauer <[email protected]> Add ethernet driver for the UMAC controller found on HPE GXP SoCs. This controller is connected to an external Marvell 88E1415 PHY via an SGMII interface.
Co-developed-by: Jean-Marie Verdun <[email protected]> Signed-off-by: Jean-Marie Verdun <[email protected]> Signed-off-by: Marcello Sylvester Bauer <[email protected]> Signed-off-by: Tan Siewert <[email protected]> --- MAINTAINERS | 2 + arch/arm/dts/hpe-gxp-u-boot.dtsi | 24 ++ arch/arm/dts/hpe-gxp.dtsi | 42 +++ configs/gxp_defconfig | 7 + drivers/net/Kconfig | 9 + drivers/net/Makefile | 1 + drivers/net/gxp_umac.c | 572 +++++++++++++++++++++++++++++++++++++++ drivers/net/gxp_umac.h | 136 ++++++++++ 8 files changed, 793 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 420a3bbf730..64354cf0c62 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -366,6 +366,8 @@ F: arch/arm/mach-hpe/ F: board/hpe/ F: configs/gxp_defconfig F: doc/device-tree-bindings/spi/hpe,gxp-spi.yaml +F: drivers/net/gxp_umac.c +F: drivers/net/gxp_umac.h F: drivers/misc/gxp_eeprom.c F: drivers/sysinfo/gxp.c F: drivers/timer/gxp-timer.c diff --git a/arch/arm/dts/hpe-gxp-u-boot.dtsi b/arch/arm/dts/hpe-gxp-u-boot.dtsi index 63a1a11fed2..31f898f09f4 100644 --- a/arch/arm/dts/hpe-gxp-u-boot.dtsi +++ b/arch/arm/dts/hpe-gxp-u-boot.dtsi @@ -23,3 +23,27 @@ }; }; }; + +&umac0 { + bootph-all; +}; + +&mdio0 { + bootph-all; +}; + +&phy0 { + bootph-all; +}; + +&umac1 { + bootph-all; +}; + +&mdio1 { + bootph-all; +}; + +&phy1 { + bootph-all; +}; diff --git a/arch/arm/dts/hpe-gxp.dtsi b/arch/arm/dts/hpe-gxp.dtsi index 66ddbd924f7..9e13e941ff0 100644 --- a/arch/arm/dts/hpe-gxp.dtsi +++ b/arch/arm/dts/hpe-gxp.dtsi @@ -163,6 +163,48 @@ interrupts = <6>; interrupt-parent = <&vic0>; }; + + umac0: ethernet@4000 { + compatible = "hpe,gxp-umac"; + reg = <0x4000 0x100>; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + nvmem-cells = <&mac_address0>; + nvmem-cell-names = "mac-address"; + status = "okay"; + + mdio0: mdio { + compatible = "hpe,gxp-umac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + }; + }; + + umac1: ethernet@5000 { + compatible = "hpe,gxp-umac"; + reg = <0x5000 0x100>; + phy-mode = "gmii"; + phy-handle = <&phy1>; + nvmem-cells = <&mac_address1>; + nvmem-cell-names = "mac-address"; + status = "okay"; + + mdio1: mdio { + compatible = "hpe,gxp-umac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + phy1: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + }; + }; }; }; }; diff --git a/configs/gxp_defconfig b/configs/gxp_defconfig index d63c675d746..200ccc8d7a5 100644 --- a/configs/gxp_defconfig +++ b/configs/gxp_defconfig @@ -39,6 +39,13 @@ CONFIG_CMD_FAT=y CONFIG_ENV_IS_IN_SPI_FLASH=y CONFIG_ENV_REDUNDANT=y CONFIG_NETCONSOLE=y +CONFIG_NETDEVICES=y +CONFIG_PHYLIB=y +CONFIG_PHY_MARVELL=y +CONFIG_MII=y +CONFIG_GXP_UMAC=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_CMD_MII=y CONFIG_MISC=y CONFIG_GXP_EEPROM=y # CONFIG_MMC is not set diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c057504cf66..5bc504d00f1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -861,6 +861,15 @@ config GMAC_ROCKCHIP This driver provides Rockchip SoCs network support based on the Synopsys Designware driver. +config GXP_UMAC + bool "HPE GXP UMAC ethernet controller" + depends on ARCH_GXP + select PHYLIB + select MII + help + This driver supports the UMAC ethernet controller found on + HPE GXP BMC SoCs. + config RENESAS_ETHER_SWITCH bool "Renesas Ethernet Switch support" depends on DM_ETH && (R8A779F0 || R8A78000) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 96ec6fb187a..5330bfa00cd 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_FSL_PFE) += pfe_eth/ obj-$(CONFIG_FTGMAC100) += ftgmac100.o obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o +obj-$(CONFIG_GXP_UMAC) += gxp_umac.o obj-$(CONFIG_HIFEMAC_ETH) += hifemac.o obj-$(CONFIG_HIFEMAC_MDIO) += hifemac_mdio.o obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o diff --git a/drivers/net/gxp_umac.c b/drivers/net/gxp_umac.c new file mode 100644 index 00000000000..78f5d6b6669 --- /dev/null +++ b/drivers/net/gxp_umac.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HPE GXP UMAC ethernet driver + * + * Copyright (C) 2020 Hewlett Packard Enterprise Development LP. + * Copyright (C) 2026 9elements GmbH + */ + +#include <cpu_func.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <miiphy.h> +#include <net.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/iopoll.h> + +#include "gxp_umac.h" + +#define GXP_UMAC_TX_TIMEOUT_MS 1000 + +static int gxp_umac_mdio_read(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + struct gxp_umac_priv *priv = bus->priv; + struct gxp_mdio_regs *mdio = priv->mdio_regs; + u32 val; + int ret; + + val = readl(&mdio->mmi); + val &= ~(UMAC_MMI_PHY_ADDR_MASK | UMAC_MMI_REG_ADDR); + val |= (phy_addr << UMAC_MMI_PHY_ADDR_SHIFT) & UMAC_MMI_PHY_ADDR_MASK; + val |= reg_addr & UMAC_MMI_REG_ADDR; + val |= UMAC_MMI_MRNW; /* Read operation */ + writel(val, &mdio->mmi); + + /* Activate transfer */ + val |= UMAC_MMI_MOWNER; + writel(val, &mdio->mmi); + + /* Wait for transfer to complete */ + ret = readl_poll_timeout(&mdio->mmi, val, !(val & UMAC_MMI_MOWNER), + GXP_MDIO_TIMEOUT_USEC); + if (ret) { + pr_err("%s: mdio read timeout (phy:%d reg:%x)\n", + bus->name, phy_addr, reg_addr); + return ret; + } + + return readl(&mdio->mmi_data) & UMAC_MMI_DATA_MASK; +} + +static int gxp_umac_mdio_write(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr, u16 value) +{ + struct gxp_umac_priv *priv = bus->priv; + struct gxp_mdio_regs *mdio = priv->mdio_regs; + u32 val; + int ret; + + /* Write data first */ + writel(value & UMAC_MMI_DATA_MASK, &mdio->mmi_data); + + val = readl(&mdio->mmi); + val &= ~(UMAC_MMI_PHY_ADDR_MASK | UMAC_MMI_REG_ADDR | UMAC_MMI_MRNW); + val |= (phy_addr << UMAC_MMI_PHY_ADDR_SHIFT) & UMAC_MMI_PHY_ADDR_MASK; + val |= reg_addr & UMAC_MMI_REG_ADDR; + /* MRNW cleared = write operation */ + writel(val, &mdio->mmi); + + /* Activate transfer */ + val |= UMAC_MMI_MOWNER; + writel(val, &mdio->mmi); + + /* Wait for transfer to complete */ + ret = readl_poll_timeout(&mdio->mmi, val, !(val & UMAC_MMI_MOWNER), + GXP_MDIO_TIMEOUT_USEC); + if (ret) { + pr_err("%s: mdio write timeout (phy:%d reg:%x)\n", + bus->name, phy_addr, reg_addr); + } + + return ret; +} + +static int gxp_umac_mdio_init(struct udevice *dev) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + struct mii_dev *bus; + int ret; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + bus->read = gxp_umac_mdio_read; + bus->write = gxp_umac_mdio_write; + bus->priv = priv; + + ret = mdio_register_seq(bus, dev_seq(dev)); + if (ret) { + free(bus); + return ret; + } + + priv->bus = bus; + return 0; +} + +static u32 gxp_umac_get_ring_size_val(u32 num_entries) +{ + switch (num_entries) { + case 4: + return 0; + case 8: + return 1; + case 16: + return 3; + case 32: + return 7; + case 64: + return 0xf; + case 128: + return 0x1f; + case 256: + return 0x3f; + default: + return 1; /* Default to 8 entries */ + } +} + +static void gxp_umac_init_rings(struct gxp_umac_priv *priv) +{ + int i; + + memset(priv->txdes, 0, UMAC_MAX_RING_ENTRIES * sizeof(*priv->txdes)); + memset(priv->rxdes, 0, UMAC_MAX_RING_ENTRIES * sizeof(*priv->rxdes)); + + priv->tx_index = 0; + priv->rx_index = 0; + + /* Initialize RX descriptors with buffer addresses */ + for (i = 0; i < UMAC_MAX_RING_ENTRIES; i++) { + priv->rxdes[i].dma_address = (u32)&priv->rx_packets[i]; + priv->rxdes[i].count = UMAC_MAX_FRAME_SIZE; + priv->rxdes[i].status = UMAC_RING_ENTRY_HW_OWN; + } + + /* Flush descriptors to memory */ + flush_dcache_range((ulong)priv->txdes, + (ulong)priv->txdes + UMAC_MAX_RING_ENTRIES * sizeof(*priv->txdes)); + flush_dcache_range((ulong)priv->rxdes, + (ulong)priv->rxdes + UMAC_MAX_RING_ENTRIES * sizeof(*priv->rxdes)); + flush_dcache_range((ulong)priv->rx_packets, + (ulong)priv->rx_packets + + UMAC_MAX_RING_ENTRIES * sizeof(*priv->rx_packets)); +} + +static void gxp_umac_set_mac_addr(struct gxp_umac_priv *priv, const u8 *addr) +{ + struct gxp_umac_regs *regs = priv->regs; + + writel((addr[0] << 8) | addr[1], ®s->mac_addr_hi); + writel((addr[2] << 8) | addr[3], ®s->mac_addr_mid); + writel((addr[4] << 8) | addr[5], ®s->mac_addr_lo); +} + +static void gxp_umac_set_channel_enable(struct gxp_umac_priv *priv, int enable) +{ + struct gxp_umac_regs *regs = priv->regs; + u32 val; + + if (enable) { + val = readl(®s->config_status); + val |= UMAC_CFG_TXEN | UMAC_CFG_RXEN; + writel(val, ®s->config_status); + writel(0, ®s->ring_prompt); + } else { + writel(0, ®s->config_status); + } +} + +static void gxp_umac_adjust_link(struct gxp_umac_priv *priv) +{ + struct gxp_umac_regs *regs = priv->regs; + struct phy_device *phydev = priv->phydev; + u32 val; + + /* Disable MAC during configuration */ + gxp_umac_set_channel_enable(priv, 0); + + /* Disable both clocks first */ + val = readl(®s->config_status); + val &= ~(UMAC_CFG_GIGE_EN | UMAC_CFG_100M_EN); + writel(val, ®s->config_status); + udelay(2); + + /* Configure speed and duplex */ + val &= ~(UMAC_CFG_GIGE_EN | UMAC_CFG_100M_EN | UMAC_CFG_GIGE_MODE | + UMAC_CFG_FULL_DUPLEX); + + if (phydev->speed == 1000) + val |= UMAC_CFG_GIGE_EN | UMAC_CFG_GIGE_MODE; + else + val |= UMAC_CFG_100M_EN; + + if (phydev->duplex) + val |= UMAC_CFG_FULL_DUPLEX; + + writel(val, ®s->config_status); + udelay(2); + + /* Re-enable MAC */ + gxp_umac_set_channel_enable(priv, 1); +} + +static void gxp_umac_init_hw(struct gxp_umac_priv *priv) +{ + struct gxp_umac_regs *regs = priv->regs; + u32 ring_size; + + /* Reset ring pointers */ + writel(0, ®s->ring_ptr); + + /* Clear missed status */ + writel(0, ®s->clear_status); + + /* Disable checksum generation */ + writel(0, ®s->cksum_config); + + /* Set ring size */ + ring_size = gxp_umac_get_ring_size_val(UMAC_MAX_RING_ENTRIES); + writel((ring_size << UMAC_RING_SIZE_TX_SHIFT) | + (ring_size << UMAC_RING_SIZE_RX_SHIFT), ®s->ring_size); + + /* Set ring base addresses */ + writel((u32)priv->rxdes, ®s->rx_ring_addr); + writel((u32)priv->txdes, ®s->tx_ring_addr); + + /* Set DMA burst size */ + writel(0x22, ®s->dma_config); + + /* Disable clocks initially */ + writel(0, ®s->config_status); +} + +static int gxp_umac_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct gxp_umac_priv *priv = dev_get_priv(dev); + struct phy_device *phydev = priv->phydev; + int ret; + + /* Initialize ring buffers */ + gxp_umac_init_rings(priv); + + /* Initialize hardware */ + gxp_umac_init_hw(priv); + + /* Set MAC address */ + gxp_umac_set_mac_addr(priv, pdata->enetaddr); + + /* Start PHY */ + ret = phy_startup(phydev); + if (ret) { + dev_err(dev, "Could not start PHY\n"); + return ret; + } + + if (!phydev->link) { + dev_err(dev, "No link\n"); + return -ENOLINK; + } + + /* Configure MAC for link speed/duplex */ + gxp_umac_adjust_link(priv); + + printf("%s: link up, %d Mbps %s-duplex\n", dev->name, + phydev->speed, phydev->duplex ? "full" : "half"); + + return 0; +} + +static void gxp_umac_stop(struct udevice *dev) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + + gxp_umac_set_channel_enable(priv, 0); + + if (priv->phydev) + phy_shutdown(priv->phydev); +} + +static int gxp_umac_send(struct udevice *dev, void *packet, int length) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + struct gxp_umac_regs *regs = priv->regs; + struct gxp_umac_tx_desc *txdes = &priv->txdes[priv->tx_index]; + ulong des_start, des_end, data_start, data_end; + u32 val; + int timeout; + + /* Wait for descriptor to be available */ + des_start = (ulong)txdes & ~(ARCH_DMA_MINALIGN - 1); + des_end = des_start + roundup(sizeof(*txdes), ARCH_DMA_MINALIGN); + invalidate_dcache_range(des_start, des_end); + + if (txdes->status & UMAC_RING_ENTRY_HW_OWN) { + dev_err(dev, "TX ring full\n"); + return -EBUSY; + } + + /* Pad short frames */ + if (length < UMAC_MIN_FRAME_SIZE) + length = UMAC_MIN_FRAME_SIZE; + + /* Set up descriptor */ + txdes->dma_address = (u32)packet; + txdes->count = length; + txdes->cksum_offset = 0; + txdes->status = UMAC_RING_ENTRY_HW_OWN; + + /* Flush packet data and descriptor */ + data_start = (ulong)packet & ~(ARCH_DMA_MINALIGN - 1); + data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); + flush_dcache_range(data_start, data_end); + flush_dcache_range(des_start, des_end); + + /* Trigger transmission */ + writel(0, ®s->ring_prompt); + + /* Wait for TX interrupt */ + timeout = GXP_UMAC_TX_TIMEOUT_MS * 1000; + do { + val = readl(®s->interrupt); + if (val & UMAC_INT_TX) + break; + udelay(1); + } while (--timeout > 0); + + /* Clear TX interrupt */ + writel(UMAC_INT_TX, ®s->interrupt); + + /* Wait for descriptor to be released */ + timeout = GXP_UMAC_TX_TIMEOUT_MS * 1000; + do { + invalidate_dcache_range(des_start, des_end); + if (!(txdes->status & UMAC_RING_ENTRY_HW_OWN)) + break; + udelay(1); + } while (--timeout > 0); + + if (timeout <= 0) { + dev_err(dev, "TX timeout\n"); + return -ETIMEDOUT; + } + + /* Move to next descriptor */ + priv->tx_index = (priv->tx_index + 1) % UMAC_MAX_RING_ENTRIES; + + return 0; +} + +static int gxp_umac_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + struct gxp_umac_regs *regs = priv->regs; + struct gxp_umac_rx_desc *rxdes = &priv->rxdes[priv->rx_index]; + ulong des_start, des_end, data_start, data_end; + u32 val; + int length; + + /* Check and clear interrupts */ + val = readl(®s->interrupt); + if (val & UMAC_INT_OVERRUN) + dev_warn(dev, "RX overrun\n"); + if (val) + writel(val, ®s->interrupt); + + /* Check for missed frames */ + val = readl(®s->config_status); + if (val & UMAC_CFG_MISSED) { + dev_warn(dev, "RX missed frame\n"); + writel(0, ®s->clear_status); + } + + /* Invalidate descriptor */ + des_start = (ulong)rxdes & ~(ARCH_DMA_MINALIGN - 1); + des_end = des_start + roundup(sizeof(*rxdes), ARCH_DMA_MINALIGN); + invalidate_dcache_range(des_start, des_end); + + /* Check if descriptor is owned by hardware */ + if (rxdes->status & UMAC_RING_ENTRY_HW_OWN) { + /* Check for errors even if HW owned */ + if (rxdes->status & UMAC_RING_RX_ERR_MASK) { + rxdes->status &= ~UMAC_RING_RX_ERR_MASK; + flush_dcache_range(des_start, des_end); + } + return -EAGAIN; + } + + length = rxdes->count; + + /* Invalidate received data */ + data_start = (ulong)rxdes->dma_address & ~(ARCH_DMA_MINALIGN - 1); + data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); + invalidate_dcache_range(data_start, data_end); + + *packetp = (uchar *)rxdes->dma_address; + + return length; +} + +static int gxp_umac_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + struct gxp_umac_regs *regs = priv->regs; + struct gxp_umac_rx_desc *rxdes = &priv->rxdes[priv->rx_index]; + ulong des_start, des_end; + + /* Return descriptor to hardware */ + rxdes->count = UMAC_MAX_FRAME_SIZE; + rxdes->status = UMAC_RING_ENTRY_HW_OWN; + + /* Flush descriptor */ + des_start = (ulong)rxdes & ~(ARCH_DMA_MINALIGN - 1); + des_end = des_start + roundup(sizeof(*rxdes), ARCH_DMA_MINALIGN); + flush_dcache_range(des_start, des_end); + + /* Prompt hardware to check for new buffers */ + writel(0, ®s->ring_prompt); + + /* Move to next descriptor */ + priv->rx_index = (priv->rx_index + 1) % UMAC_MAX_RING_ENTRIES; + + return 0; +} + +static int gxp_umac_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct gxp_umac_priv *priv = dev_get_priv(dev); + + gxp_umac_set_mac_addr(priv, pdata->enetaddr); + return 0; +} + +static int gxp_umac_of_to_plat(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + + pdata->iobase = dev_read_addr(dev); + if (pdata->iobase == FDT_ADDR_T_NONE) { + dev_err(dev, "Could not read register address\n"); + return -EINVAL; + } + + pdata->phy_interface = dev_read_phy_mode(dev); + if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) + pdata->phy_interface = PHY_INTERFACE_MODE_SGMII; + + return 0; +} + +static int gxp_umac_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct gxp_umac_priv *priv = dev_get_priv(dev); + int ret; + + priv->regs = (struct gxp_umac_regs *)pdata->iobase; + priv->mdio_regs = (struct gxp_mdio_regs *)(pdata->iobase + 0x80); + priv->phy_mode = pdata->phy_interface; + priv->phy_addr = 0; + + /* Allocate DMA-aligned ring buffers */ + priv->txdes = memalign(ARCH_DMA_MINALIGN, + UMAC_MAX_RING_ENTRIES * sizeof(*priv->txdes)); + if (!priv->txdes) + return -ENOMEM; + + priv->rxdes = memalign(ARCH_DMA_MINALIGN, + UMAC_MAX_RING_ENTRIES * sizeof(*priv->rxdes)); + if (!priv->rxdes) { + ret = -ENOMEM; + goto err_free_txdes; + } + + priv->rx_packets = memalign(ARCH_DMA_MINALIGN, + UMAC_MAX_RING_ENTRIES * sizeof(*priv->rx_packets)); + if (!priv->rx_packets) { + ret = -ENOMEM; + goto err_free_rxdes; + } + + /* Initialize MDIO bus */ + ret = gxp_umac_mdio_init(dev); + if (ret) { + dev_err(dev, "Failed to initialize MDIO bus\n"); + goto err_free_rx_packets; + } + + priv->phydev = phy_connect(priv->bus, priv->phy_addr, dev, priv->phy_mode); + if (!priv->phydev) { + dev_err(dev, "Failed to connect to PHY\n"); + ret = -ENODEV; + goto err_free_mdio; + } + + /* Configure PHY */ + priv->phydev->supported &= PHY_GBIT_FEATURES; + priv->phydev->advertising = priv->phydev->supported; + phy_config(priv->phydev); + + return 0; + +err_free_mdio: + mdio_unregister(priv->bus); + mdio_free(priv->bus); +err_free_rx_packets: + free(priv->rx_packets); +err_free_rxdes: + free(priv->rxdes); +err_free_txdes: + free(priv->txdes); + return ret; +} + +static int gxp_umac_remove(struct udevice *dev) +{ + struct gxp_umac_priv *priv = dev_get_priv(dev); + + free(priv->phydev); + mdio_unregister(priv->bus); + mdio_free(priv->bus); + free(priv->rx_packets); + free(priv->rxdes); + free(priv->txdes); + + return 0; +} + +static const struct eth_ops gxp_umac_ops = { + .start = gxp_umac_start, + .send = gxp_umac_send, + .recv = gxp_umac_recv, + .stop = gxp_umac_stop, + .free_pkt = gxp_umac_free_pkt, + .write_hwaddr = gxp_umac_write_hwaddr, +}; + +static const struct udevice_id gxp_umac_ids[] = { + { .compatible = "hpe,gxp-umac" }, + { } +}; + +U_BOOT_DRIVER(gxp_umac) = { + .name = "gxp_umac", + .id = UCLASS_ETH, + .of_match = gxp_umac_ids, + .of_to_plat = gxp_umac_of_to_plat, + .probe = gxp_umac_probe, + .remove = gxp_umac_remove, + .ops = &gxp_umac_ops, + .priv_auto = sizeof(struct gxp_umac_priv), + .plat_auto = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/net/gxp_umac.h b/drivers/net/gxp_umac.h new file mode 100644 index 00000000000..42930e39864 --- /dev/null +++ b/drivers/net/gxp_umac.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * HPE GXP UMAC ethernet driver + * + * Copyright (C) 2020 Hewlett Packard Enterprise Development LP. + * Copyright (C) 2026 9elements GmbH + */ + +#ifndef _GXP_UMAC_H_ +#define _GXP_UMAC_H_ + +struct gxp_umac_regs { + u32 config_status; /* 0x00: Configuration and Status Register I */ + u32 ring_ptr; /* 0x04: Ring Pointer Register */ + u32 ring_prompt; /* 0x08: Ring Prompt Register (W) */ + u32 clear_status; /* 0x0c: Clear Status Register (W) */ + u32 cksum_config; /* 0x10: Checksum Config Register */ + u32 ring_size; /* 0x14: Ring Size Register */ + u32 mac_addr_hi; /* 0x18: MAC Address[47:32] */ + u32 mac_addr_mid; /* 0x1c: MAC Address[31:16] */ + u32 mac_addr_lo; /* 0x20: MAC Address[15:0] */ + u32 mc_addr_filt_hi; /* 0x24: LAF[63:32] */ + u32 mc_addr_filt_lo; /* 0x28: LAF[31:0] */ + u32 config_status2; /* 0x2c: Configuration and Status Register II */ + u32 interrupt; /* 0x30: MAC Interrupt Config and Status */ + u32 overrun_count; /* 0x34: Overrun Counter Register */ + u32 rx_int_config; /* 0x38: Rx Interrupt Config Register */ + u32 tx_int_config; /* 0x3c: Tx Interrupt Config Register */ + u32 packet_length; /* 0x40: Packet Length Register */ + u32 bcast_filter; /* 0x44: Broadcast Filter Config Register */ + u32 bcast_prompt; /* 0x48: Broadcast Prompt Register (W) */ + u32 rx_ring_addr; /* 0x4c: Rx Ring Base Address Register */ + u32 tx_ring_addr; /* 0x50: Tx Ring Base Address Register */ + u32 dma_config; /* 0x54: DMA Config Register */ + u32 burst_config; /* 0x58: Bursting Config Register */ + u32 pause_config; /* 0x5c: PAUSE Frame Config Register */ + u32 pause_control; /* 0x60: PAUSE Frame Control and Status */ + u32 congestion_config; /* 0x64: Channel Congestion Config */ + u32 frame_filter_config; /* 0x68: Frame Type Filter Config */ + u32 rx_fifo_config_status; /* 0x6c: RX FIFO Config and Status */ + u32 rx_ring1_base_addr; /* 0x70: RX Ring 1 Base Address */ + u32 config_status3; /* 0x74: Configuration and Status Register III */ + u32 reserved[2]; /* 0x78-0x7c: Reserved */ +}; + +/* MDIO registers (at offset 0x80 from UMAC base) */ +struct gxp_mdio_regs { + u32 mmi; /* 0x00: MMI Register */ + u32 mmi_data; /* 0x04: MMI Data Register */ +}; + +/* Config/Status Register I bits */ +#define UMAC_CFG_TXEN BIT(12) +#define UMAC_CFG_RXEN BIT(11) +#define UMAC_CFG_GIGE_EN BIT(10) +#define UMAC_CFG_100M_EN BIT(9) +#define UMAC_CFG_GIGE_MODE BIT(2) +#define UMAC_CFG_FULL_DUPLEX BIT(0) +#define UMAC_CFG_MISSED BIT(7) + +/* Interrupt register bits */ +#define UMAC_INT_TX BIT(0) +#define UMAC_INT_RX BIT(2) +#define UMAC_INT_OVERRUN BIT(4) + +/* MMI register bits */ +#define UMAC_MMI_PHY_ADDR_MASK 0x001f0000 +#define UMAC_MMI_PHY_ADDR_SHIFT 16 +#define UMAC_MMI_MOWNER BIT(9) +#define UMAC_MMI_MRNW BIT(8) +#define UMAC_MMI_REG_ADDR 0x0000001f +#define UMAC_MMI_DATA_MASK 0x0000ffff + +/* Ring size register shifts */ +#define UMAC_RING_SIZE_TX_SHIFT 24 +#define UMAC_RING_SIZE_RX_SHIFT 16 + +/* Ring entry status bits */ +#define UMAC_RING_ENTRY_HW_OWN BIT(15) +#define UMAC_RING_RX_ERR_MASK 0x38e0 + +/* Frame sizes */ +#define UMAC_MIN_FRAME_SIZE 60 +#define UMAC_MAX_FRAME_SIZE 1518 +#define UMAC_MAX_PACKET_ROUNDED 0x600 /* 1536, aligned */ + +/* Ring configuration */ +#define UMAC_MAX_RING_ENTRIES 8 +#define UMAC_RING_ENTRY_SIZE 16 + +/* Ring entry structures */ +struct gxp_umac_rx_desc { + u32 dma_address; + u16 status; + u16 count; + u16 checksum; + u16 control; + u32 reserved; +} __packed; + +struct gxp_umac_tx_desc { + u32 dma_address; + u16 status; + u16 count; + u32 cksum_offset; + u32 reserved; +} __packed; + +/* Packet buffer structure */ +struct gxp_umac_packet { + u8 data[UMAC_MAX_FRAME_SIZE]; + u8 pad[UMAC_MAX_PACKET_ROUNDED - UMAC_MAX_FRAME_SIZE]; +}; + +/* Private data structure */ +struct gxp_umac_priv { + struct gxp_umac_regs __iomem *regs; + struct gxp_mdio_regs __iomem *mdio_regs; + + struct gxp_umac_tx_desc *txdes; + struct gxp_umac_rx_desc *rxdes; + struct gxp_umac_packet *rx_packets; + + int tx_index; + int rx_index; + + struct mii_dev *bus; + struct phy_device *phydev; + u32 phy_addr; + u32 phy_mode; +}; + +/* MDIO timeout */ +#define GXP_MDIO_TIMEOUT_USEC 10000 + +#endif /* _GXP_UMAC_H_ */ --- base-commit: 8d7bc8add17bbc69a58dfe0002eea039dd2dc1bc change-id: 20260818-gxp-net-9caa4fdff62d Best regards, -- Tan Siewert <[email protected]>
