[PATCH net-next v11 7/7] net: stmmac: qcom-ethqos: add support for sa8255p

From: Bartosz Golaszewski

Date: Mon Jun 29 2026 - 07:33:10 EST


Extend the driver to support a new model - sa8255p. Unlike the previously
supported variants, this one's power management is done in the firmware
over SCMI. This is modeled in linux using power domains so add a new
emac data variant and a separate setup callback.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
.../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 83 ++++++++++++++++++++++
1 file changed, 83 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index f379570f80680e96f027873cda6a6bca398e22dc..47175670a32631369a2cf8b00388d9359513e090 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -108,6 +108,7 @@ struct qcom_ethqos {
struct clk *link_clk;
struct phy *serdes_phy;
phy_interface_t phy_mode;
+ struct dev_pm_domain_list *pds;
const struct ethqos_emac_driver_data *data;
};

@@ -206,6 +207,8 @@ static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos)

static int ethqos_hlos_setup(struct qcom_ethqos *ethqos,
struct plat_stmmacenet_data *plat_dat);
+static int ethqos_scmi_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat);

static const struct ethqos_emac_por emac_v2_3_0_por[] = {
{ .offset = RGMII_IO_MACRO_CONFIG, .value = 0x00C01343 },
@@ -310,6 +313,29 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = {
.setup = ethqos_hlos_setup,
};

+static const struct ethqos_emac_driver_data emac_v4_0_0_scmi_data = {
+ .has_emac_ge_3 = true,
+ .needs_sgmii_loopback = true,
+ .dma_addr_width = 36,
+ .dwmac4_addrs = {
+ .dma_chan = 0x00008100,
+ .dma_chan_offset = 0x1000,
+ .mtl_chan = 0x00008000,
+ .mtl_chan_offset = 0x1000,
+ .mtl_ets_ctrl = 0x00008010,
+ .mtl_ets_ctrl_offset = 0x1000,
+ .mtl_txq_weight = 0x00008018,
+ .mtl_txq_weight_offset = 0x1000,
+ .mtl_send_slp_cred = 0x0000801c,
+ .mtl_send_slp_cred_offset = 0x1000,
+ .mtl_high_cred = 0x00008020,
+ .mtl_high_cred_offset = 0x1000,
+ .mtl_low_cred = 0x00008024,
+ .mtl_low_cred_offset = 0x1000,
+ },
+ .setup = ethqos_scmi_setup,
+};
+
static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
{
struct device *dev = &ethqos->pdev->dev;
@@ -749,6 +775,62 @@ static int ethqos_hlos_setup(struct qcom_ethqos *ethqos,
return 0;
}

+static const char *const ethqos_scmi_pd_names[] = { "core", "mdio" };
+
+static int ethqos_scmi_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat)
+{
+ const struct dev_pm_domain_attach_data pd_data = {
+ .pd_names = ethqos_scmi_pd_names,
+ .num_pd_names = ARRAY_SIZE(ethqos_scmi_pd_names),
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ };
+
+ struct platform_device *pdev = ethqos->pdev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = devm_pm_domain_attach_list(dev, &pd_data, &ethqos->pds);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to attach power domains\n");
+
+ /*
+ * The SerDes lane, its clocks and the MAC AXI/AHB clocks are owned by
+ * firmware and brought up through the SCMI power domains above. The
+ * MAC wrapper itself, however is in the kernel's register space: the
+ * mux that feeds the SerDes recovered RX clock into the MAC's clk_rx_i
+ * is not configured by firmware. Without it, clk_rx_i never toggles
+ * and the DMA SW-reset polled in dwmac4_dma_reset() never completes.
+ *
+ * Map the wrapper and program the same loopback/functional clock bits
+ * the non-firmware platforms rely on (see ethqos_clks_config) so the
+ * RX clock is present by the time the DMA engine is reset.
+ */
+ ethqos->rgmii_base = devm_platform_ioremap_resource_byname(pdev, "rgmii");
+ if (IS_ERR(ethqos->rgmii_base))
+ return dev_err_probe(dev, PTR_ERR(ethqos->rgmii_base),
+ "Failed to map rgmii resource\n");
+
+ /*
+ * Run on every runtime resume, which stmmac performs after the power
+ * domains are on but before serdes_powerup() and the DMA reset, so the
+ * wrapper is always configured ahead of the reset.
+ */
+ plat_dat->clks_config = ethqos_clks_config;
+
+ switch (ethqos->phy_mode) {
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ plat_dat->fix_mac_speed = ethqos_fix_mac_speed_sgmii;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int qcom_ethqos_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -836,6 +918,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)

static const struct of_device_id qcom_ethqos_match[] = {
{ .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data},
+ { .compatible = "qcom,sa8255p-ethqos", .data = &emac_v4_0_0_scmi_data},
{ .compatible = "qcom,sa8775p-ethqos", .data = &emac_v4_0_0_data},
{ .compatible = "qcom,sc8280xp-ethqos", .data = &emac_v3_0_0_data},
{ .compatible = "qcom,sm8150-ethqos", .data = &emac_v2_1_0_data},

--
2.47.3