[PATCH net-next 07/11] net: dsa: rzn1-a5psw: Make switch topology configurable via OF data

Prabhakar
2025-11-21T11:36:13Z

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Move the switch topology description-the number of ports and the CPU-port
index-out of hard-coded constants and into SoC-specific OF match data. The
driver previously assumed a fixed 5-port layout with the last port acting
as the CPU port. That assumption does not hold for newer Renesas variants,
and embedding it in the code made the driver inflexible and error-prone.

Introduce a small a5psw_of_data structure carrying both the total number
of ports and the CPU-port identifier, and rely on this data everywhere the
driver previously used fixed values. This ensures that port loops, PCS
allocation, management-port setup, and bridge bookkeeping all reflect the
actual hardware configuration.

Making these attributes runtime-selectable allows the driver to support
RZ/T2H and RZ/N2H SoCs which use different port counts and CPU-port
assignments-without rewriting common logic or forking the driver, while
preserving correct behaviour on existing RZN1 systems.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
drivers/net/dsa/rzn1_a5psw.c | 26 +++++++++++++++++---------
drivers/net/dsa/rzn1_a5psw.h | 17 ++++++++++++++---
2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c
index 99098bc06efe..d957b6d40f05 100644
--- a/drivers/net/dsa/rzn1_a5psw.c
+++ b/drivers/net/dsa/rzn1_a5psw.c
@@ -382,13 +382,14 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,
struct dsa_bridge bridge)
{
struct a5psw *a5psw = ds->priv;
+ unsigned int cpu_port = a5psw->of_data->cpu_port;

a5psw->bridged_ports &= ~BIT(port);

a5psw_port_set_standalone(a5psw, port, true);

/* No more ports bridged */
- if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT))
+ if (a5psw->bridged_ports == BIT(cpu_port))
a5psw->br_dev = NULL;
}

@@ -924,20 +925,21 @@ static void a5psw_vlan_setup(struct a5psw *a5psw, int port)
static int a5psw_setup(struct dsa_switch *ds)
{
struct a5psw *a5psw = ds->priv;
+ unsigned int cpu_port = a5psw->of_data->cpu_port;
int port, vlan, ret;
struct dsa_port *dp;
u32 reg;

- /* Validate that there is only 1 CPU port with index A5PSW_CPU_PORT */
+ /* Validate that there is only 1 CPU port with index matching cpu_port */
dsa_switch_for_each_cpu_port(dp, ds) {
- if (dp->index != A5PSW_CPU_PORT) {
+ if (dp->index != cpu_port) {
dev_err(a5psw->dev, "Invalid CPU port\n");
return -EINVAL;
}
}

/* Configure management port */
- reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_ENABLE;
+ reg = cpu_port | A5PSW_MGMT_CFG_ENABLE;
a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg);

/* Set pattern 0 to forward all frame to mgmt port */
@@ -1147,7 +1149,7 @@ static void a5psw_pcs_free(struct a5psw *a5psw)
{
int i;

- for (i = 0; i < ARRAY_SIZE(a5psw->pcs); i++) {
+ for (i = 0; i < a5psw->of_data->nports - 1; i++) {
if (a5psw->pcs[i])
miic_destroy(a5psw->pcs[i]);
}
@@ -1174,7 +1176,7 @@ static int a5psw_pcs_get(struct a5psw *a5psw)
goto free_pcs;
}

- if (reg >= ARRAY_SIZE(a5psw->pcs)) {
+ if (reg >= a5psw->of_data->nports - 1) {
ret = -ENODEV;
goto free_pcs;
}
@@ -1223,7 +1225,8 @@ static int a5psw_probe(struct platform_device *pdev)
if (IS_ERR(a5psw->base))
return PTR_ERR(a5psw->base);

- a5psw->bridged_ports = BIT(A5PSW_CPU_PORT);
+ a5psw->of_data = of_device_get_match_data(dev);
+ a5psw->bridged_ports = BIT(a5psw->of_data->cpu_port);

ret = a5psw_pcs_get(a5psw);
if (ret)
@@ -1268,7 +1271,7 @@ static int a5psw_probe(struct platform_device *pdev)

ds = &a5psw->ds;
ds->dev = dev;
- ds->num_ports = A5PSW_PORTS_NUM;
+ ds->num_ports = a5psw->of_data->nports;
ds->ops = &a5psw_switch_ops;
ds->phylink_mac_ops = &a5psw_phylink_mac_ops;
ds->priv = a5psw;
@@ -1310,8 +1313,13 @@ static void a5psw_shutdown(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
}

+static const struct a5psw_of_data rzn1_of_data = {
+ .nports = 5,
+ .cpu_port = 4,
+};
+
static const struct of_device_id a5psw_of_mtable[] = {
- { .compatible = "renesas,rzn1-a5psw", },
+ { .compatible = "renesas,rzn1-a5psw", .data = &rzn1_of_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, a5psw_of_mtable);
diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h
index 81be30d6c55f..d1b2cc5b43e6 100644
--- a/drivers/net/dsa/rzn1_a5psw.h
+++ b/drivers/net/dsa/rzn1_a5psw.h
@@ -195,8 +195,7 @@
#define A5PSW_aCarrierSenseErrors 0x924

#define A5PSW_VLAN_TAG(prio, id) (((prio) << 12) | (id))
-#define A5PSW_PORTS_NUM 5
-#define A5PSW_CPU_PORT (A5PSW_PORTS_NUM - 1)
+#define A5PSW_MAX_PORTS 4
#define A5PSW_MDIO_DEF_FREQ 2500000
#define A5PSW_MDIO_TIMEOUT 100
#define A5PSW_JUMBO_LEN (10 * SZ_1K)
@@ -231,6 +230,16 @@ union lk_data {
struct fdb_entry entry;
};

+/**
+ * struct a5psw_of_data - OF data structure
+ * @nports: Number of ports in the switch
+ * @cpu_port: CPU port number
+ */
+struct a5psw_of_data {
+ unsigned int nports;
+ unsigned int cpu_port;
+};
+
/**
* struct a5psw - switch struct
* @base: Base address of the switch
@@ -238,6 +247,7 @@ union lk_data {
* @clk: clk_switch clock
* @ts: Timestamp clock
* @dev: Device associated to the switch
+ * @of_data: Pointer to OF data
* @mii_bus: MDIO bus struct
* @mdio_freq: MDIO bus frequency requested
* @pcs: Array of PCS connected to the switch ports (not for the CPU)
@@ -254,8 +264,9 @@ struct a5psw {
struct clk *clk;
struct clk *ts;
struct device *dev;
+ const struct a5psw_of_data *of_data;
struct mii_bus *mii_bus;
- struct phylink_pcs *pcs[A5PSW_PORTS_NUM - 1];
+ struct phylink_pcs *pcs[A5PSW_MAX_PORTS];
struct dsa_switch ds;
struct mutex lk_lock;
spinlock_t reg_lock;
--
2.52.0