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: 10+ 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-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-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
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®