diff --git a/drivers/net/dsa/sja1105/Makefile b/drivers/net/dsa/sja1105/Makefile
index 40d69e6c0bae..7b5537d67072 100644
--- a/drivers/net/dsa/sja1105/Makefile
+++ b/drivers/net/dsa/sja1105/Makefile
@@ -5,6 +5,7 @@ sja1105-objs := \
sja1105_spi.o \
sja1105_main.o \
sja1105_mdio.o \
+ sja1105_subdev.o \
sja1105_flower.o \
sja1105_ethtool.o \
sja1105_devlink.o \diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index 30903fb62302..cf718e7c2b7b 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -91,8 +91,6 @@ struct sja1105_regs {
u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS];
u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
u64 stats[__MAX_SJA1105_STATS_AREA][SJA1105_MAX_NUM_PORTS];
- u64 mdio_100base_tx;
- u64 mdio_100base_t1;
u64 pcs_base[SJA1105_MAX_NUM_PORTS];
};
@@ -278,8 +276,6 @@ struct sja1105_private {
struct mutex dynamic_config_lock;
struct devlink_region **regions;
struct sja1105_cbs_entry *cbs;
- struct mii_bus *mdio_base_t1;
- struct mii_bus *mdio_base_tx;
struct mii_bus *mdio_pcs;
struct phylink_pcs *pcs[SJA1105_MAX_NUM_PORTS];
struct sja1105_ptp_data ptp_data;diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index e9e091cf8998..d3fb42772071 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -23,6 +23,7 @@
#include <linux/units.h>
#include "sja1105.h"
+#include "sja1105_subdev.h"
#include "sja1105_tas.h"
#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull
@@ -3329,6 +3330,13 @@ static int sja1105_probe(struct spi_device *spi)
return rc;
}
+ rc = devm_sja1105_add_subdevs(ds);
+ if (rc) {
+ dev_err(ds->dev, "Failed to create child devices: %pe\n",
+ ERR_PTR(rc));
+ return rc;
+ }
+
if (IS_ENABLED(CONFIG_NET_SCH_CBS)) {
priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers,
sizeof(struct sja1105_cbs_entry),diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c
index 8d535c033cef..b803ce71f5cc 100644
--- a/drivers/net/dsa/sja1105/sja1105_mdio.c
+++ b/drivers/net/dsa/sja1105/sja1105_mdio.c
@@ -133,238 +133,6 @@ int sja1110_pcs_mdio_write_c45(struct mii_bus *bus, int phy, int mmd, int reg,
&tmp, NULL);
}
-enum sja1105_mdio_opcode {
- SJA1105_C45_ADDR = 0,
- SJA1105_C22 = 1,
- SJA1105_C45_DATA = 2,
- SJA1105_C45_DATA_AUTOINC = 3,
-};
-
-static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv,
- int phy, enum sja1105_mdio_opcode op,
- int xad)
-{
- const struct sja1105_regs *regs = priv->info->regs;
-
- return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0);
-}
-
-static int sja1105_base_t1_mdio_read_c22(struct mii_bus *bus, int phy, int reg)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- int rc;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
-
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
-
- return tmp & 0xffff;
-}
-
-static int sja1105_base_t1_mdio_read_c45(struct mii_bus *bus, int phy,
- int mmd, int reg)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- int rc;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, mmd);
-
- rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, ®, NULL);
- if (rc < 0)
- return rc;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, mmd);
-
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
-
- return tmp & 0xffff;
-}
-
-static int sja1105_base_t1_mdio_write_c22(struct mii_bus *bus, int phy, int reg,
- u16 val)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
-
- tmp = val & 0xffff;
-
- return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
-}
-
-static int sja1105_base_t1_mdio_write_c45(struct mii_bus *bus, int phy,
- int mmd, int reg, u16 val)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- int rc;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, mmd);
-
- rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, ®, NULL);
- if (rc < 0)
- return rc;
-
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, mmd);
-
- tmp = val & 0xffff;
-
- return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
-}
-
-static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- u32 tmp;
- int rc;
-
- rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg,
- &tmp, NULL);
- if (rc < 0)
- return rc;
-
- return tmp & 0xffff;
-}
-
-static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg,
- u16 val)
-{
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- u32 tmp = val;
-
- return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg,
- &tmp, NULL);
-}
-
-static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv,
- struct device_node *mdio_node)
-{
- struct sja1105_mdio_private *mdio_priv;
- struct device_node *np;
- struct mii_bus *bus;
- int rc = 0;
-
- np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-tx-mdio");
- if (!np)
- return 0;
-
- if (!of_device_is_available(np))
- goto out_put_np;
-
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus) {
- rc = -ENOMEM;
- goto out_put_np;
- }
-
- bus->name = "SJA1110 100base-TX MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx",
- dev_name(priv->ds->dev));
- bus->read = sja1105_base_tx_mdio_read;
- bus->write = sja1105_base_tx_mdio_write;
- bus->parent = priv->ds->dev;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
-
- rc = of_mdiobus_register(bus, np);
- if (rc) {
- mdiobus_free(bus);
- goto out_put_np;
- }
-
- priv->mdio_base_tx = bus;
-
-out_put_np:
- of_node_put(np);
-
- return rc;
-}
-
-static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv)
-{
- if (!priv->mdio_base_tx)
- return;
-
- mdiobus_unregister(priv->mdio_base_tx);
- mdiobus_free(priv->mdio_base_tx);
- priv->mdio_base_tx = NULL;
-}
-
-static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv,
- struct device_node *mdio_node)
-{
- struct sja1105_mdio_private *mdio_priv;
- struct device_node *np;
- struct mii_bus *bus;
- int rc = 0;
-
- np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-t1-mdio");
- if (!np)
- return 0;
-
- if (!of_device_is_available(np))
- goto out_put_np;
-
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus) {
- rc = -ENOMEM;
- goto out_put_np;
- }
-
- bus->name = "SJA1110 100base-T1 MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1",
- dev_name(priv->ds->dev));
- bus->read = sja1105_base_t1_mdio_read_c22;
- bus->write = sja1105_base_t1_mdio_write_c22;
- bus->read_c45 = sja1105_base_t1_mdio_read_c45;
- bus->write_c45 = sja1105_base_t1_mdio_write_c45;
- bus->parent = priv->ds->dev;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
-
- rc = of_mdiobus_register(bus, np);
- if (rc) {
- mdiobus_free(bus);
- goto out_put_np;
- }
-
- priv->mdio_base_t1 = bus;
-
-out_put_np:
- of_node_put(np);
-
- return rc;
-}
-
-static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
-{
- if (!priv->mdio_base_t1)
- return;
-
- mdiobus_unregister(priv->mdio_base_t1);
- mdiobus_free(priv->mdio_base_t1);
- priv->mdio_base_t1 = NULL;
-}
-
static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
{
struct sja1105_mdio_private *mdio_priv;@@ -459,49 +227,13 @@ static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
int sja1105_mdiobus_register(struct dsa_switch *ds)
{
struct sja1105_private *priv = ds->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- struct device_node *switch_node = ds->dev->of_node;
- struct device_node *mdio_node;
- int rc;
-
- rc = sja1105_mdiobus_pcs_register(priv);
- if (rc)
- return rc;
-
- mdio_node = of_get_available_child_by_name(switch_node, "mdios");
- if (!mdio_node)
- return 0;
- if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) {
- rc = sja1105_mdiobus_base_tx_register(priv, mdio_node);
- if (rc)
- goto err_put_mdio_node;
- }
-
- if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) {
- rc = sja1105_mdiobus_base_t1_register(priv, mdio_node);
- if (rc)
- goto err_free_base_tx_mdiobus;
- }
-
- of_node_put(mdio_node);
-
- return 0;
-
-err_free_base_tx_mdiobus:
- sja1105_mdiobus_base_tx_unregister(priv);
-err_put_mdio_node:
- of_node_put(mdio_node);
- sja1105_mdiobus_pcs_unregister(priv);
-
- return rc;
+ return sja1105_mdiobus_pcs_register(priv);
}
void sja1105_mdiobus_unregister(struct dsa_switch *ds)
{
struct sja1105_private *priv = ds->priv;
- sja1105_mdiobus_base_t1_unregister(priv);
- sja1105_mdiobus_base_tx_unregister(priv);
sja1105_mdiobus_pcs_unregister(priv);
}diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c
index 856a751de53a..20757e166b08 100644
--- a/drivers/net/dsa/sja1105/sja1105_spi.c
+++ b/drivers/net/dsa/sja1105/sja1105_spi.c
@@ -495,8 +495,6 @@ static const struct sja1105_regs sja1105et_regs = {
.ptpclkval = 0x18, /* Spans 0x18 to 0x19 */
.ptpclkrate = 0x1A,
.ptpclkcorp = 0x1D,
- .mdio_100base_tx = SJA1105_RSV_ADDR,
- .mdio_100base_t1 = SJA1105_RSV_ADDR,
};
static const struct sja1105_regs sja1105pqrs_regs = {@@ -534,8 +532,6 @@ static const struct sja1105_regs sja1105pqrs_regs = {
.ptpclkrate = 0x1B,
.ptpclkcorp = 0x1E,
.ptpsyncts = 0x1F,
- .mdio_100base_tx = SJA1105_RSV_ADDR,
- .mdio_100base_t1 = SJA1105_RSV_ADDR,
};
static const struct sja1105_regs sja1110_regs = {@@ -618,8 +614,6 @@ static const struct sja1105_regs sja1110_regs = {
.ptpclkrate = SJA1110_SPI_ADDR(0x74),
.ptpclkcorp = SJA1110_SPI_ADDR(0x80),
.ptpsyncts = SJA1110_SPI_ADDR(0x84),
- .mdio_100base_tx = 0x1c2400,
- .mdio_100base_t1 = 0x1c1000,
.pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000,
SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},diff --git a/drivers/net/dsa/sja1105/sja1105_subdev.c b/drivers/net/dsa/sja1105/sja1105_subdev.c
new file mode 100644
index 000000000000..06957d44f084
--- /dev/null
+++ b/drivers/net/dsa/sja1105/sja1105_subdev.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2025 NXP
+ */
+#include <linux/device/devres.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+#include "sja1105.h"
+#include "sja1105_subdev.h"
+
+static const struct resource sja1110_mdio_cbt1_res =
+ DEFINE_RES_REG_NAMED(0x704000, 0x4000, "mdio_cbt1");
+
+static const struct resource sja1110_mdio_cbtx_res =
+ DEFINE_RES_REG_NAMED(0x709000, 0x1000, "mdio_cbtx");
+
+static bool fwnode_is_hierarchical_child(struct fwnode_handle *child,
+ struct fwnode_handle *parent)
+{
+ struct fwnode_handle *next = child;
+
+ do {
+ if (next == parent)
+ return true;
+ next = fwnode_get_parent(next);
+ } while (next);
+
+ return false;
+}
+
+static void of_subdev_del(void *data)
+{
+ struct platform_device *pdev = data;
+
+ platform_device_unregister(pdev);
+}
+
+/**
+ * devm_of_subdev_add() - Register an OF sub-device as a managed platform device
+ * @pdevinfo: Platform device information structure containing parent, fwnode,
+ * name, resources, etc.
+ *
+ * This function registers a platform device as a sub-device of
+ * @pdevinfo.parent using the information provided in @pdevinfo. The sub-device
+ * will be automatically unregistered when @pdevinfo.parent is removed, thanks
+ * to devres management.
+ *
+ * If the fwnode specified in @pdevinfo is not available (disabled in device
+ * tree), this function returns success without creating the device.
+ *
+ * For the sub-device drivers to access their registers, a form of
+ * devm_regmap_init(parent) should have been called prior to this, which
+ * makes the parent regmap visible via dev_get_regmap(&pdev->dev.parent)
+ * in the sub-device driver. The entire address space is made available through
+ * this regmap to all sub-devices, although they are expected to segment it
+ * according to the given resources.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int devm_of_subdev_add(const struct platform_device_info *pdevinfo)
+{
+ struct device *parent = pdevinfo->parent;
+ struct platform_device *pdev;
+
+ if (!fwnode_device_is_available(pdevinfo->fwnode))
+ return 0;
+
+ /* To avoid API abuse, ensure that the sub-device fwnode is,
+ * in fact, related to the parent.
+ */
+ if (!fwnode_is_hierarchical_child(pdevinfo->fwnode, dev_fwnode(parent)))
+ return -EINVAL;
+
+ pdev = platform_device_register_full(pdevinfo);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return devm_add_action_or_reset(parent, of_subdev_del, pdev);
+}
+
+static int devm_sja1105_add_mdio_subdev(struct device *parent,
+ struct device_node *np,
+ const struct resource *res,
+ size_t num_res)
+{
+ struct platform_device_info subdev;
+ char name[64];
+ u32 reg;
+ int err;
+
+ err = of_property_read_u32(np, "reg", ®);
+ if (err)
+ return err;
+
+ snprintf(name, sizeof(name), "%s.%pOFn", dev_name(parent), np);
+ subdev = (struct platform_device_info) {
+ .parent = parent,
+ .fwnode = of_fwnode_handle(np),
+ .name = name,
+ .id = reg,
+ .res = res,
+ .num_res = num_res,
+ };
+
+ return devm_of_subdev_add(&subdev);
+}
+
+/* Legacy nodes which lack a proper resource description in the device tree,
+ * so we need to specify it manually.
+ */
+static int devm_sja1105_add_mdio_subdevs(struct dsa_switch *ds,
+ struct device_node *mdio_node)
+{
+ struct device *parent = ds->dev;
+ struct device_node *np;
+ int err;
+
+ np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-tx-mdio");
+ if (np) {
+ err = devm_sja1105_add_mdio_subdev(parent, np,
+ &sja1110_mdio_cbtx_res, 1);
+ of_node_put(np);
+ if (err)
+ return err;
+ }
+
+ np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-t1-mdio");
+ if (np) {
+ err = devm_sja1105_add_mdio_subdev(parent, np,
+ &sja1110_mdio_cbt1_res, 1);
+ of_node_put(np);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int devm_sja1105_add_subdevs(struct dsa_switch *ds)
+{
+ struct device_node *switch_node = dev_of_node(ds->dev);
+ struct device_node *mdio_node;
+ int rc = 0;
+
+ mdio_node = of_get_available_child_by_name(switch_node, "mdios");
+ if (mdio_node) {
+ rc = devm_sja1105_add_mdio_subdevs(ds, mdio_node);
+ of_node_put(mdio_node);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}diff --git a/drivers/net/dsa/sja1105/sja1105_subdev.h b/drivers/net/dsa/sja1105/sja1105_subdev.h
new file mode 100644
index 000000000000..9b5a02401399
--- /dev/null
+++ b/drivers/net/dsa/sja1105/sja1105_subdev.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2025-2026 NXP
+ */
+#ifndef _SJA1105_SUBDEV_H
+#define _SJA1105_SUBDEV_H
+
+int devm_sja1105_add_subdevs(struct dsa_switch *ds);
+
+#endif
--
2.34.1