From: Alex Elder <elder@riscstar.com>
To: maxime.chevallier@bootlin.com, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org
Cc: Daniel Thompson <daniel@riscstar.com>,
mcoquelin.stm32@gmail.com, alexandre.torgue@foss.st.com,
hkallweit1@gmail.com, linux@armlinux.org.uk,
richardcochran@gmail.com, p.zabel@pengutronix.de,
peppe.cavallaro@st.com, joabreu@synopsys.com,
mohd.anwar@oss.qualcomm.com, lorenzo.bianconi@oss.qualcomm.com,
netdev@vger.kernel.org, devicetree@vger.kernel.org,
linux-stm32@st-md-mailman.stormreply.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: [PATCH net-next 5/5] net: stmmac: support the TC9564 SoC
Date: Sun, 18 Oct 2026 22:01:28 -0500 [thread overview]
Message-ID: <20261019030129.1470307-6-elder@riscstar.com> (raw)
In-Reply-To: <20261019030129.1470307-1-elder@riscstar.com>
From: Daniel Thompson <daniel@riscstar.com>
Add support for the Ethernet XGMAC functionality present in the
Toshiba TC9564 SoC.
The TC9564 is a fairly complex chip that attaches to a host via
the upstream port of a PCIe switch it implements. That switch
has three downstream ports, and one of those has an embedded PCI
endpoint. The PCI endpoint implements two PCI functions, and a
10 Gbps-capable XGMAC interface is attached to each.
The TC9564 implements additional peripherals, and access to them
is provided by a PCI endpoint bus associated with BAR 4 of the
first PCI function. A PCI driver sets up the PCI function for
use, and peripherals using the bus use platform drivers, matched
by compatible string.
We use the pci-ep-bus approach for the stmmac driver as well, with
each PCI function exposing access to its BAR 4 via an endpoint bus.
The XGMAC device is defined within a pci-ep-bus node, and is bound
to the platform driver based on its compatible string.
Signed-off-by: Daniel Thompson <daniel@riscstar.com>
Co-developed-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
MAINTAINERS | 1 +
drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 +
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
.../ethernet/stmicro/stmmac/dwmac-tc9564.c | 590 ++++++++++++++++++
4 files changed, 603 insertions(+)
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-tc9564.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 972c9faead5b3..69f301aa26eb9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28313,6 +28313,7 @@ M: Alex Elder <elder@kernel.org>
M: Daniel Thompson <danielt@kernel.org>
S: Maintained
F: Documentation/devicetree/bindings/net/toshiba,tc9564-dwmac.yaml
+F: drivers/net/ethernet/stmicro/stmmac/dwmac-tc9564.c
TOSHIBA WMI HOTKEYS DRIVER
M: Azael Avalos <coproscefalo@gmail.com>
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index ab3c4cf964239..5613921188df4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -363,6 +363,17 @@ config DWMAC_LOONGSON1
device driver. This driver is used for Loongson1-based boards
like Loongson LS1B/LS1C.
+config DWMAC_TC9564
+ tristate "Toshiba TC9564 DWMAC support"
+ depends on OF && COMMON_CLK && TC9564_PCI
+ default TC9564_PCI
+ help
+ Support for the Ethernet controllers on the Toshiba TC9564.
+
+ This enables glue layer support in the stmmac driver support
+ for the Toshiba TC9564. This chip implements two embedded
+ PCIe functions, each of which has an XGMAC interface.
+
config DWMAC_TEGRA
tristate "NVIDIA Tegra MGBE support"
depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index b16a456b730ec..c4de25db93715 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -52,4 +52,5 @@ obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o
obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o
obj-$(CONFIG_DWMAC_MOTORCOMM) += dwmac-motorcomm.o
+obj-$(CONFIG_DWMAC_TC9564) += dwmac-tc9564.o
stmmac-pci-objs:= stmmac_pci.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tc9564.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tc9564.c
new file mode 100644
index 0000000000000..21098f91e3bbf
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tc9564.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2026 by RISCstar Solutions Corporation. All rights reserved.
+ *
+ * Derived from code having the following copyrights:
+ * Copyright (C) 2011-2012 Vayavya Labs Pvt Ltd
+ * Copyright (C) 2025 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pcs/pcs-xpcs-regmap.h>
+#include <linux/pcs/pcs-xpcs.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+#include <linux/stmmac.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include "common.h"
+#include "dwxgmac2.h"
+#include "stmmac.h"
+#include "stmmac_platform.h"
+
+#define DRIVER_NAME "dwmac-tc9564"
+
+/* Start of range representing PCI space on the TC9564 internal bus */
+#define TC9564_SLV00_SRC_ADDR 0x0000001000000000ULL
+
+#define TC9564_PTP_CLOCK_RATE (250 * HZ_PER_MHZ)
+
+#define TC9564_RX_FIFO_SIZE (46 * SZ_1K)
+#define TC9564_TX_FIFO_SIZE (46 * SZ_1K)
+
+/* Fields and values for the EMACTL registers */
+#define EMAC_SP_SEL_MASK GENMASK(3, 0)
+#define SP_SEL_2500BASEX 4
+#define SP_SEL_SGMII_1000M 5
+#define SP_SEL_SGMII_100M 6
+#define SP_SEL_SGMII_10M 7
+#define EMAC_PHY_INF_SEL_MASK GENMASK(5, 4)
+#define PCS_CLK_PHY 1 /* Clock from PHY */
+#define EMAC_INV_SGM_SIG_DET BIT(6) /* 1 = polarity inverted */
+#define EMAC_LPIHWCLKEN BIT(8) /* 1 = low power mode */
+#define EMAC_INIT_DONE BIT(21)
+
+/* Offset to the XPCS memory block, relative to the EMAC address range */
+#define DWMAC_XPCS_OFFSET 0x3a00
+
+/* Offset to the PMATOP memory block, relative to the EMAC address range */
+#define DWMAC_PMATOP_OFFSET 0x4000
+
+#define PMA_CML_GL_PM_CFG0 0x01b8
+
+/*
+ * Five sets three registers must be configured for PMA. The HWT_REFCLK
+ * registers are each separated by 0x14 bytes. The Common0 configuration
+ * registers are separated by 0x8 bytes.
+ */
+#define PMA_REG_COUNT 5
+
+#define PMA_HWT_REFCK_R_EN 0x1080
+#define PMA_HWT_REFCK_TERM_EN 0x1090
+#define PMA_HWT_REFCK_STRIDE 0x0014
+
+#define PMA_COMM_CFG_0_1 0x1888
+#define PMA_COMM_CFG_0_1_STRIDE 0x0008
+
+/* PMA_COMM_CFG_0_1 fields (WRITE_MASK is a field name) */
+#define COMM_CFG_WRITE_MASK_MASK GENMASK(16, 9)
+#define WRITE_MASK_VALUE 0xf7 /* Power-on value */
+#define COMM_CFG_ENABLE BIT(8)
+#define COMM_CFG_WRITE_DATA_MASK GENMASK(7, 0)
+#define WRITE_DATA_VALUE 0x04 /* Power-on value */
+
+enum {
+ RESET_ID_MAC,
+ RESET_ID_XPCS,
+ RESET_ID_PMA,
+};
+
+static const char * const tc9564_reset_names[] = {
+ [RESET_ID_MAC] = "mac",
+ [RESET_ID_XPCS] = "xpcs",
+ [RESET_ID_PMA] = "pma",
+};
+
+/**
+ * struct tc9564_data - Toshiba-specific platform data
+ * @dev: Device pointer
+ * @ioaddr: Pointer to mapped eMAC memory
+ * @plat: Pointer to our stmmac platform data
+ * @resets: Reset controller bulk array
+ * @clocks: Clock controller bulk array
+ * @clock_count: Number of valid elements in the clock array
+ * @config_regmap: Regmap used to access eMAC configuration registers
+ * @emac_ctl_offset: Offset of the eMAC control register within the regmap
+ * @res: Structure passed to stmmac_dvr_probe()
+ * @desc: DMA descriptor data used by mac_device_info
+ * @dma: DMA operations data used by mac_device_info
+ */
+struct tc9564_data {
+ struct device *dev;
+ void __iomem *ioaddr;
+ struct plat_stmmacenet_data *plat;
+ struct reset_control_bulk_data resets[ARRAY_SIZE(tc9564_reset_names)];
+ struct clk_bulk_data *clocks;
+ u32 clock_count;
+
+ struct regmap *config_regmap;
+ u32 emac_ctl_offset;
+
+ /* This field is data passed to stmmac_dvr_probe() */
+ struct stmmac_resources res;
+
+ /* These two fields are used by the mac_device_info structure */
+ struct stmmac_desc_ops desc;
+ struct stmmac_dma_ops dma;
+};
+
+struct tc9564_mac_speed {
+ phy_interface_t phy_interface;
+ int speed;
+ u32 sp_sel;
+};
+
+static struct tc9564_mac_speed mac_speed[] = {
+ { PHY_INTERFACE_MODE_2500BASEX, SPEED_2500, SP_SEL_2500BASEX },
+ { PHY_INTERFACE_MODE_SGMII, SPEED_1000, SP_SEL_SGMII_1000M },
+ { PHY_INTERFACE_MODE_SGMII, SPEED_100, SP_SEL_SGMII_100M },
+ { PHY_INTERFACE_MODE_SGMII, SPEED_10, SP_SEL_SGMII_10M },
+};
+
+/* TC9564 uses indirect addressing so this need only describe a 1KiB range */
+static const struct regmap_config xpcs_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_base = 0x00, /* Minimum XPCS reg offset */
+ .max_register = 0xff, /* Register DW_VR_CSR_VIEWPORT */
+ .reg_shift = REGMAP_UPSHIFT(2),
+};
+
+/**
+ * tc9564_pma_init() - Initialize PMA
+ * @td: bsp_priv pointer
+ *
+ * Initialize (or re-initialize) the PMA, configure the clocks and wait for the
+ * eMAC to be ready.
+ */
+static void tc9564_pma_init(struct tc9564_data *td)
+{
+ struct reset_control *rstc = td->resets[RESET_ID_PMA].rstc;
+ struct regmap *regmap = td->config_regmap;
+ void __iomem *pmatop;
+ u32 val;
+ int ret;
+ u32 i;
+
+ /*
+ * When we re-initialize the PMA then the reset will already have
+ * been deasserted. We must make sure the PMA reset is asserted
+ * before we change the clock settings.
+ */
+ WARN_ON(reset_control_assert(rstc));
+
+ pmatop = td->res.addr + DWMAC_PMATOP_OFFSET;
+
+ /* Power on CML buffer (0 = normal mode, 1 = power down) */
+ writel(0, pmatop + PMA_CML_GL_PM_CFG0);
+
+ /* This value switches clock from C0_REFCK to CLK_REF_I */
+ val = u32_encode_bits(WRITE_MASK_VALUE, COMM_CFG_WRITE_MASK_MASK);
+ val |= COMM_CFG_ENABLE;
+ val |= u32_encode_bits(WRITE_DATA_VALUE, COMM_CFG_WRITE_DATA_MASK);
+
+ for (i = 0; i < PMA_REG_COUNT; i++) {
+ u32 offset = i * PMA_HWT_REFCK_STRIDE;
+
+ /* Disable C0_REFCK and 100 ohm termination */
+ writel(0, pmatop + PMA_HWT_REFCK_R_EN + offset);
+ writel(0, pmatop + PMA_HWT_REFCK_TERM_EN + offset);
+
+ /* Switch clock from C0_REFCK to CLK_REF_I */
+ offset = i * PMA_COMM_CFG_0_1_STRIDE;
+ writel(val, pmatop + PMA_COMM_CFG_0_1 + offset);
+ }
+
+ WARN_ON(reset_control_deassert(rstc));
+
+ ret = regmap_read_poll_timeout(regmap, td->emac_ctl_offset,
+ val, val & EMAC_INIT_DONE, 50, 1000000);
+ WARN(ret, "timeout waiting for MAC init done\n");
+}
+
+static int tc9564_mac_speed_select(struct tc9564_data *td,
+ phy_interface_t interface, int speed)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mac_speed); i++) {
+ if (mac_speed[i].phy_interface != interface)
+ continue;
+
+ if (speed == mac_speed[i].speed || speed == SPEED_UNKNOWN)
+ return mac_speed[i].sp_sel;
+ }
+ dev_err(td->dev, "%s/%d unsupported\n", phy_modes(interface), speed);
+
+ return -EOPNOTSUPP;
+}
+
+static void tc9564_mac_configure(struct tc9564_data *td,
+ phy_interface_t interface, int speed)
+{
+ struct regmap *regmap = td->config_regmap;
+ u32 offset = td->emac_ctl_offset;
+ int sp_sel;
+ u32 val;
+
+ sp_sel = tc9564_mac_speed_select(td, interface, speed);
+ if (sp_sel < 0)
+ return;
+
+ /* No error checking needed for MMIO regmap */
+ regmap_read(regmap, offset, &val);
+ val |= EMAC_LPIHWCLKEN;
+ val &= ~EMAC_INV_SGM_SIG_DET;
+ val = u32_replace_bits(val, PCS_CLK_PHY, EMAC_PHY_INF_SEL_MASK);
+ val = u32_replace_bits(val, sp_sel, EMAC_SP_SEL_MASK);
+ regmap_write(regmap, offset, val);
+}
+
+static void tc9564_mac_enable(struct tc9564_data *td)
+{
+ WARN_ON(clk_bulk_prepare_enable(td->clock_count, td->clocks));
+
+ WARN_ON(reset_control_deassert(td->resets[RESET_ID_MAC].rstc));
+
+ tc9564_mac_configure(td, td->plat->phy_interface, SPEED_UNKNOWN);
+ tc9564_pma_init(td);
+
+ WARN_ON(reset_control_deassert(td->resets[RESET_ID_XPCS].rstc));
+}
+
+static void tc9564_mac_disable(struct tc9564_data *td)
+{
+ WARN_ON(reset_control_bulk_assert(ARRAY_SIZE(td->resets), td->resets));
+
+ clk_bulk_disable_unprepare(td->clock_count, td->clocks);
+}
+
+/*
+ * Override method for dwxgmac301_dma_ops->init_rx_chan
+ *
+ * This differs from the dwxgmac301_dma_ops->init_rx_chan by translating the DMA
+ * address for TC9564 internal bus. The window that provides DMA access to PCI
+ * is linearly mapped at 0x10_0000_0000.
+ */
+static void tc9564_dma_init_rx_chan(struct stmmac_priv *priv,
+ void __iomem *ioaddr,
+ struct stmmac_dma_cfg *dma_cfg,
+ dma_addr_t phy, u32 chan)
+{
+ u64 translated = phy + TC9564_SLV00_SRC_ADDR;
+
+ dwxgmac2_dma_init_rx_chan(priv, ioaddr, dma_cfg, phy, chan);
+
+ writel(upper_32_bits(translated),
+ ioaddr + XGMAC_DMA_CH_RxDESC_HADDR(chan));
+ writel(lower_32_bits(translated),
+ ioaddr + XGMAC_DMA_CH_RxDESC_LADDR(chan));
+}
+
+/* Override method for dwxgmac301_dma_ops->init_tx_chan */
+static void tc9564_dma_init_tx_chan(struct stmmac_priv *priv,
+ void __iomem *ioaddr,
+ struct stmmac_dma_cfg *dma_cfg,
+ dma_addr_t phy, u32 chan)
+{
+ u64 translated = phy + TC9564_SLV00_SRC_ADDR;
+
+ dwxgmac2_dma_init_tx_chan(priv, ioaddr, dma_cfg, phy, chan);
+
+ writel(upper_32_bits(translated),
+ ioaddr + XGMAC_DMA_CH_TxDESC_HADDR(chan));
+ writel(lower_32_bits(translated),
+ ioaddr + XGMAC_DMA_CH_TxDESC_LADDR(chan));
+}
+
+/* Override method for dwxgmac210_desc_ops->set_addr */
+static void tc9564_desc_set_addr(struct dma_desc *p, dma_addr_t addr)
+{
+ u64 translated = addr + TC9564_SLV00_SRC_ADDR;
+
+ p->des0 = cpu_to_le32(lower_32_bits(translated));
+ p->des1 = cpu_to_le32(upper_32_bits(translated));
+}
+
+/* Override method for dwxgmac210_desc_ops->set_sec_addr */
+static void tc9564_desc_set_sec_addr(struct dma_desc *p, dma_addr_t addr,
+ bool is_valid)
+{
+ u64 translated = addr + TC9564_SLV00_SRC_ADDR;
+
+ p->des2 = cpu_to_le32(lower_32_bits(translated));
+ p->des3 = cpu_to_le32(upper_32_bits(translated));
+}
+
+/*
+ * Use mac_setup to apply the override methods above.
+ *
+ * The memory for the modified ops structures is pre-allocated as part of
+ * struct tc9564_data.
+ */
+static int tc9564_mac_setup(void *apriv, struct mac_device_info *mac)
+{
+ struct stmmac_priv *priv = apriv;
+ struct tc9564_data *td;
+
+ td = priv->plat->bsp_priv;
+
+ /* dwxgmac301_dma_ops needs extending to provide DMA address translation */
+ td->dma = dwxgmac301_dma_ops;
+ td->dma.init_rx_chan = tc9564_dma_init_rx_chan;
+ td->dma.init_tx_chan = tc9564_dma_init_tx_chan;
+ mac->dma = &td->dma;
+
+ /* dwxgmac210_desc_ops also needs extending for the same reason */
+ td->desc = dwxgmac210_desc_ops;
+ td->desc.set_addr = tc9564_desc_set_addr;
+ td->desc.set_sec_addr = tc9564_desc_set_sec_addr;
+ mac->desc = &td->desc;
+
+ priv->hw = mac;
+
+ return dwxgmac2_setup(priv);
+}
+
+static int tc9564_pcs_init(struct stmmac_priv *priv)
+{
+ void __iomem *emac = priv->ioaddr;
+ struct regmap *xpcs_regmap;
+ void __iomem *xpcs_addr;
+ struct dw_xpcs *xpcs;
+
+ xpcs_addr = emac + DWMAC_XPCS_OFFSET;
+ xpcs_regmap = devm_regmap_init_mmio(priv->device, xpcs_addr,
+ &xpcs_regmap_config);
+ if (IS_ERR(xpcs_regmap))
+ return PTR_ERR(xpcs_regmap);
+
+ xpcs = devm_xpcs_regmap_register(priv->device, xpcs_regmap);
+ if (IS_ERR(xpcs))
+ return PTR_ERR(xpcs);
+
+ xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
+ priv->hw->phylink_pcs = xpcs_to_phylink_pcs(xpcs);
+
+ return 0;
+}
+
+static struct phylink_pcs *tc9564_select_pcs(struct stmmac_priv *priv,
+ phy_interface_t interface)
+{
+ return priv->hw->phylink_pcs;
+}
+
+static void tc9564_fix_mac_speed(void *bsp_priv, phy_interface_t interface,
+ int speed, unsigned int mode)
+{
+ struct tc9564_data *td = bsp_priv;
+
+ tc9564_mac_configure(td, interface, speed);
+ tc9564_pma_init(td);
+}
+
+static int tc9564_dwmac_suspend(struct device *dev, void *bsp_priv)
+{
+ struct tc9564_data *td = bsp_priv;
+
+ tc9564_mac_disable(td);
+
+ return 0;
+}
+
+static int tc9564_dwmac_resume(struct device *dev, void *bsp_priv)
+{
+ struct tc9564_data *td = bsp_priv;
+
+ tc9564_mac_enable(td);
+
+ return 0;
+}
+
+static int tc9564_probe_config_dt(struct tc9564_data *td)
+{
+ struct platform_device *pdev = to_platform_device(td->dev);
+ struct device_node *np = td->dev->of_node;
+ struct plat_stmmacenet_data *plat;
+ phy_interface_t phy_interface;
+ struct device *dev = td->dev;
+ int ret;
+
+ ret = device_get_phy_mode(dev);
+ if (ret < 0)
+ return -ENODEV;
+ phy_interface = ret;
+
+ /* The platform structure is allocated with devm_kzalloc() */
+ plat = devm_stmmac_probe_config_dt(pdev, td->res.mac);
+ if (IS_ERR(plat))
+ return PTR_ERR(plat);
+
+ /*
+ * Core driver cannot deduce or validate fifo size because the bits
+ * that provide FIFO size only worth in powers of two.
+ */
+ if (plat->rx_fifo_size == 0 || plat->tx_fifo_size == 0)
+ return dev_err_probe(dev, -EINVAL, "fifo size is not set\n");
+ if (plat->rx_fifo_size > TC9564_RX_FIFO_SIZE ||
+ plat->tx_fifo_size > TC9564_TX_FIFO_SIZE)
+ return dev_err_probe(dev, -EINVAL, "fifo size too big (%u/%u)\n",
+ plat->rx_fifo_size, plat->tx_fifo_size);
+
+ /*
+ * toshiba,tc9564-xgmac is *not* compatible with snps,dwxgmac because
+ * generic drivers cannot possibly work on TC9564 (without clocks
+ * reading registers wedges the bus). However, without snps,dwxgmac,
+ * devm_stmmac_probe_config_dt(), can't figure of the core type and
+ * won't parse a couple of extra properties. Let's handle that here!
+ */
+ plat->core_type = DWMAC_CORE_XGMAC;
+ plat->pmt = true;
+ if (of_property_read_bool(np, "snps,tso"))
+ plat->flags |= STMMAC_FLAG_TSO_EN;
+ of_property_read_u32(np, "snps,multicast-filter-bins",
+ &plat->multicast_filter_bins);
+ of_property_read_u32(np, "snps,perfect-filter-entries",
+ &plat->unicast_filter_entries);
+
+ plat->phy_interface = phy_interface;
+
+ /*
+ * Set a few additional flags and settings that are always needed for
+ * TC9564.
+ */
+ plat->default_an_inband = true;
+ plat->host_dma_width = 36;
+ plat->flags |= STMMAC_FLAG_MULTI_MSI_EN;
+ for (int i = 4; i < plat->tx_queues_to_use; i++) {
+ plat->tx_queues_cfg[i].coe_unsupported = true;
+ plat->tx_queues_cfg[i].tbs_en = 1;
+ }
+
+ plat->fix_mac_speed = tc9564_fix_mac_speed;
+ plat->suspend = tc9564_dwmac_suspend;
+ plat->resume = tc9564_dwmac_resume;
+ plat->mac_setup = tc9564_mac_setup;
+ plat->pcs_init = tc9564_pcs_init;
+ plat->select_pcs = tc9564_select_pcs;
+
+ plat->bsp_priv = td;
+
+ td->plat = plat;
+
+ return 0;
+}
+
+/* Look up resets, and ensure they're initially all asserted */
+static int tc9564_reset_init(struct tc9564_data *td)
+{
+ u32 reset_count = ARRAY_SIZE(td->resets);
+ int ret;
+ u32 i;
+
+ for (i = 0; i < reset_count; i++)
+ td->resets[i].id = tc9564_reset_names[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(td->dev, reset_count,
+ td->resets);
+ if (ret)
+ return ret;
+
+ return reset_control_bulk_assert(reset_count, td->resets);
+}
+
+static int tc9564_clock_init(struct tc9564_data *td)
+{
+ struct device *dev = td->dev;
+ int ret;
+
+ ret = devm_clk_bulk_get_all(dev, &td->clocks);
+ if (ret < 0)
+ return ret;
+ td->clock_count = ret;
+
+ return 0;
+}
+
+static int tc9564_dwmac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tc9564_data *td;
+ struct device_node *np;
+ struct regmap *regmap;
+ int ret;
+
+ td = devm_kzalloc(dev, sizeof(*td), GFP_KERNEL);
+ if (!td)
+ return -ENOMEM;
+
+ td->dev = dev;
+
+ np = dev_of_node(dev);
+ regmap = syscon_regmap_lookup_by_phandle_args(np,
+ "toshiba,config-syscon",
+ 1, &td->emac_ctl_offset);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to get config regmap\n");
+ td->config_regmap = regmap;
+
+ ret = tc9564_reset_init(td);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize resets\n");
+
+ ret = tc9564_clock_init(td);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize clocks\n");
+
+ ret = tc9564_probe_config_dt(td);
+ if (ret)
+ return ret;
+
+ ret = stmmac_get_platform_resources(pdev, &td->res);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to initialize stmmac resources\n");
+
+ tc9564_mac_enable(td);
+
+ ret = stmmac_dvr_probe(dev, td->plat, &td->res);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed stmmac probe\n");
+ goto err_disable_mac;
+ }
+
+ return 0;
+
+err_disable_mac:
+ tc9564_mac_disable(td);
+
+ return ret;
+}
+
+static void tc9564_dwmac_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ struct tc9564_data *td = priv->plat->bsp_priv;
+
+ stmmac_dvr_remove(dev);
+ tc9564_mac_disable(td);
+}
+
+static const struct of_device_id tc9564_dwmac_ids[] = {
+ { .compatible = "toshiba,tc9564-xgmac", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tc9564_dwmac_ids);
+
+static struct platform_driver tc9564_dwmac_driver = {
+ .probe = tc9564_dwmac_probe,
+ .remove = tc9564_dwmac_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = tc9564_dwmac_ids,
+ .pm = &stmmac_simple_pm_ops,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(tc9564_dwmac_driver);
+
+MODULE_DESCRIPTION("Toshiba TC9564 PCIe Ethernet Network Driver");
+MODULE_LICENSE("GPL");
--
2.53.0
next prev parent reply other threads:[~2026-10-19 3:01 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 3:01 [PATCH net-next 0/5] net: stmmac: enable Toshiba TC9564 Alex Elder
2026-10-19 3:01 ` [PATCH net-next 1/5] dt-bindings: net: add support for " Alex Elder
2026-10-19 12:41 ` Andrew Lunn
2026-10-20 3:01 ` sashiko-bot
2026-10-20 15:12 ` Rob Herring (Arm)
2026-10-19 3:01 ` [PATCH net-next 2/5] net: pcs: pcs-xpcs-regmap: support XPCS memory-mapped MDIO bus via regmap Alex Elder
2026-10-20 3:01 ` sashiko-bot
2026-10-19 3:01 ` [PATCH net-next 3/5] net: stmmac: dwxgmac2: Add XGMAC 3.01a support Alex Elder
2026-10-19 7:58 ` Maxime Chevallier
2026-10-19 3:01 ` [PATCH net-next 4/5] net: stmmac: dwxgmac2: export symbols for XGMAC 3.01a DMA Alex Elder
2026-10-19 3:01 ` Alex Elder [this message]
2026-10-19 7:52 ` [PATCH net-next 5/5] net: stmmac: support the TC9564 SoC Maxime Chevallier
2026-10-19 9:14 ` Mohd Ayaan Anwar
2026-10-20 3:01 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261019030129.1470307-6-elder@riscstar.com \
--to=elder@riscstar.com \
--cc=alexandre.torgue@foss.st.com \
--cc=andrew+netdev@lunn.ch \
--cc=conor+dt@kernel.org \
--cc=daniel@riscstar.com \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@kernel.org \
--cc=hkallweit1@gmail.com \
--cc=joabreu@synopsys.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-stm32@st-md-mailman.stormreply.com \
--cc=linux@armlinux.org.uk \
--cc=lorenzo.bianconi@oss.qualcomm.com \
--cc=maxime.chevallier@bootlin.com \
--cc=mcoquelin.stm32@gmail.com \
--cc=mohd.anwar@oss.qualcomm.com \
--cc=netdev@vger.kernel.org \
--cc=p.zabel@pengutronix.de \
--cc=pabeni@redhat.com \
--cc=peppe.cavallaro@st.com \
--cc=richardcochran@gmail.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.