diff --git a/drivers/net/dsa/sja1105/Makefile b/drivers/net/dsa/sja1105/Makefile
index 7b5537d67072..049d198f681e 100644
--- a/drivers/net/dsa/sja1105/Makefile
+++ b/drivers/net/dsa/sja1105/Makefile
@@ -4,7 +4,6 @@ obj-$(CONFIG_NET_DSA_SJA1105) += sja1105.o
sja1105-objs := \
sja1105_spi.o \
sja1105_main.o \
- sja1105_mdio.o \
sja1105_subdev.o \
sja1105_flower.o \
sja1105_ethtool.o \diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index 1b52beba62d4..7fce1507eb12 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -91,11 +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 pcs_base[SJA1105_MAX_NUM_PORTS];
-};
-
-struct sja1105_mdio_private {
- struct sja1105_private *priv;
};
enum {@@ -159,10 +154,6 @@ struct sja1105_info {
bool (*rxtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
void (*txtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
int (*clocking_setup)(struct sja1105_private *priv);
- int (*pcs_mdio_read_c45)(struct mii_bus *bus, int phy, int mmd,
- int reg);
- int (*pcs_mdio_write_c45)(struct mii_bus *bus, int phy, int mmd,
- int reg, u16 val);
int (*disable_microcontroller)(struct sja1105_private *priv);
const char *name;
bool supports_mii[SJA1105_MAX_NUM_PORTS];@@ -285,7 +276,6 @@ struct sja1105_private {
struct mutex dynamic_config_lock;
struct devlink_region **regions;
struct sja1105_cbs_entry *cbs;
- struct mii_bus *mdio_pcs;
struct phylink_pcs *pcs[SJA1105_MAX_NUM_PORTS];
struct fwnode_handle *pcs_fwnode[SJA1105_MAX_NUM_PORTS];
struct of_changeset of_cs;@@ -316,16 +306,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
struct netlink_ext_ack *extack);
void sja1105_frame_memory_partitioning(struct sja1105_private *priv);
-/* From sja1105_mdio.c */
-int sja1105_mdiobus_register(struct dsa_switch *ds);
-void sja1105_mdiobus_unregister(struct dsa_switch *ds);
-int sja1105_pcs_mdio_read_c45(struct mii_bus *bus, int phy, int mmd, int reg);
-int sja1105_pcs_mdio_write_c45(struct mii_bus *bus, int phy, int mmd, int reg,
- u16 val);
-int sja1110_pcs_mdio_read_c45(struct mii_bus *bus, int phy, int mmd, int reg);
-int sja1110_pcs_mdio_write_c45(struct mii_bus *bus, int phy, int mmd, int reg,
- u16 val);
-
/* From sja1105_devlink.c */
int sja1105_devlink_setup(struct dsa_switch *ds);
void sja1105_devlink_teardown(struct dsa_switch *ds);
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 84c0f7c676e2..b60a890ba416 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
+#include <linux/pcs/pcs-xpcs.h>
#include <linux/netdev_features.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
@@ -3005,6 +3006,44 @@ static int sja1105_port_bridge_flags(struct dsa_switch *ds, int port,
return 0;
}
+static int sja1105_create_pcs(struct dsa_switch *ds, int port)
+{
+ struct sja1105_private *priv = ds->priv;
+ struct phylink_pcs *pcs;
+
+ if (priv->phy_mode[port] != PHY_INTERFACE_MODE_SGMII &&
+ priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
+ return 0;
+
+ pcs = xpcs_create_pcs_fwnode(priv->pcs_fwnode[port]);
+ if (IS_ERR(pcs))
+ return PTR_ERR(pcs);
+
+ priv->pcs[port] = pcs;
+
+ return 0;
+}
+
+static void sja1105_destroy_pcs(struct dsa_switch *ds, int port)
+{
+ struct sja1105_private *priv = ds->priv;
+
+ if (priv->pcs[port]) {
+ xpcs_destroy_pcs(priv->pcs[port]);
+ priv->pcs[port] = NULL;
+ }
+}
+
+static int sja1105_port_setup(struct dsa_switch *ds, int port)
+{
+ return sja1105_create_pcs(ds, port);
+}
+
+static void sja1105_port_teardown(struct dsa_switch *ds, int port)
+{
+ sja1105_destroy_pcs(ds, port);
+}
+
/* The programming model for the SJA1105 switch is "all-at-once" via static
* configuration tables. Some of these can be dynamically modified at runtime,
* but not the xMII mode parameters table.@@ -3059,16 +3098,9 @@ static int sja1105_setup(struct dsa_switch *ds)
goto out_flower_teardown;
}
- rc = sja1105_mdiobus_register(ds);
- if (rc < 0) {
- dev_err(ds->dev, "Failed to register MDIO bus: %pe\n",
- ERR_PTR(rc));
- goto out_ptp_clock_unregister;
- }
-
rc = sja1105_devlink_setup(ds);
if (rc < 0)
- goto out_mdiobus_unregister;
+ goto out_ptp_clock_unregister;
rtnl_lock();
rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));@@ -3098,8 +3130,6 @@ static int sja1105_setup(struct dsa_switch *ds)
out_devlink_teardown:
sja1105_devlink_teardown(ds);
-out_mdiobus_unregister:
- sja1105_mdiobus_unregister(ds);
out_ptp_clock_unregister:
sja1105_ptp_clock_unregister(ds);
out_flower_teardown:
@@ -3120,7 +3150,6 @@ static void sja1105_teardown(struct dsa_switch *ds)
rtnl_unlock();
sja1105_devlink_teardown(ds);
- sja1105_mdiobus_unregister(ds);
sja1105_ptp_clock_unregister(ds);
sja1105_flower_teardown(ds);
sja1105_tas_teardown(ds);
@@ -3139,6 +3168,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.connect_tag_protocol = sja1105_connect_tag_protocol,
.setup = sja1105_setup,
.teardown = sja1105_teardown,
+ .port_setup = sja1105_port_setup,
+ .port_teardown = sja1105_port_teardown,
.set_ageing_time = sja1105_set_ageing_time,
.port_change_mtu = sja1105_change_mtu,
.port_max_mtu = sja1105_get_max_mtu,diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c
deleted file mode 100644
index b803ce71f5cc..000000000000
--- a/drivers/net/dsa/sja1105/sja1105_mdio.c
+++ /dev/null
@@ -1,239 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright 2021 NXP
- */
-#include <linux/pcs/pcs-xpcs.h>
-#include <linux/of_mdio.h>
-#include "sja1105.h"
-
-#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc)
-
-int sja1105_pcs_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 = (mmd << 16) | reg;
-
- if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
- return 0xffff;
-
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
- return NXP_SJA1105_XPCS_ID >> 16;
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
- return NXP_SJA1105_XPCS_ID & GENMASK(15, 0);
-
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
-
- return tmp & 0xffff;
-}
-
-int sja1105_pcs_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;
-
- addr = (mmd << 16) | reg;
- tmp = val;
-
- if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
- return -EINVAL;
-
- return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
-}
-
-int sja1110_pcs_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;
- const struct sja1105_regs *regs = priv->info->regs;
- int offset, bank;
- u64 addr;
- u32 tmp;
- int rc;
-
- if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
- return -ENODEV;
-
- addr = (mmd << 16) | reg;
-
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
- return NXP_SJA1110_XPCS_ID >> 16;
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
- return NXP_SJA1110_XPCS_ID & GENMASK(15, 0);
-
- bank = addr >> 8;
- offset = addr & GENMASK(7, 0);
-
- /* This addressing scheme reserves register 0xff for the bank address
- * register, so that can never be addressed.
- */
- if (WARN_ON(offset == 0xff))
- return -ENODEV;
-
- tmp = bank;
-
- rc = sja1105_xfer_u32(priv, SPI_WRITE,
- regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
- &tmp, NULL);
- if (rc < 0)
- return rc;
-
- rc = sja1105_xfer_u32(priv, SPI_READ, regs->pcs_base[phy] + offset,
- &tmp, NULL);
- if (rc < 0)
- return rc;
-
- return tmp & 0xffff;
-}
-
-int sja1110_pcs_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;
- const struct sja1105_regs *regs = priv->info->regs;
- int offset, bank;
- u64 addr;
- u32 tmp;
- int rc;
-
- if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
- return -ENODEV;
-
- addr = (mmd << 16) | reg;
-
- bank = addr >> 8;
- offset = addr & GENMASK(7, 0);
-
- /* This addressing scheme reserves register 0xff for the bank address
- * register, so that can never be addressed.
- */
- if (WARN_ON(offset == 0xff))
- return -ENODEV;
-
- tmp = bank;
-
- rc = sja1105_xfer_u32(priv, SPI_WRITE,
- regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
- &tmp, NULL);
- if (rc < 0)
- return rc;
-
- tmp = val;
-
- return sja1105_xfer_u32(priv, SPI_WRITE, regs->pcs_base[phy] + offset,
- &tmp, NULL);
-}
-
-static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
-{
- struct sja1105_mdio_private *mdio_priv;
- struct dsa_switch *ds = priv->ds;
- struct mii_bus *bus;
- int rc = 0;
- int port;
-
- if (!priv->info->pcs_mdio_read_c45 || !priv->info->pcs_mdio_write_c45)
- return 0;
-
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus)
- return -ENOMEM;
-
- bus->name = "SJA1105 PCS MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs",
- dev_name(ds->dev));
- bus->read_c45 = priv->info->pcs_mdio_read_c45;
- bus->write_c45 = priv->info->pcs_mdio_write_c45;
- bus->parent = ds->dev;
- /* There is no PHY on this MDIO bus => mask out all PHY addresses
- * from auto probing.
- */
- bus->phy_mask = ~0;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
-
- rc = mdiobus_register(bus);
- if (rc) {
- mdiobus_free(bus);
- return rc;
- }
-
- for (port = 0; port < ds->num_ports; port++) {
- struct phylink_pcs *pcs;
-
- if (dsa_is_unused_port(ds, port))
- continue;
-
- if (priv->phy_mode[port] != PHY_INTERFACE_MODE_SGMII &&
- priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
- continue;
-
- pcs = xpcs_create_pcs_mdiodev(bus, port);
- if (IS_ERR(pcs)) {
- rc = PTR_ERR(pcs);
- goto out_pcs_free;
- }
-
- priv->pcs[port] = pcs;
- }
-
- priv->mdio_pcs = bus;
-
- return 0;
-
-out_pcs_free:
- for (port = 0; port < ds->num_ports; port++) {
- if (priv->pcs[port]) {
- xpcs_destroy_pcs(priv->pcs[port]);
- priv->pcs[port] = NULL;
- }
- }
-
- mdiobus_unregister(bus);
- mdiobus_free(bus);
-
- return rc;
-}
-
-static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
-{
- struct dsa_switch *ds = priv->ds;
- int port;
-
- if (!priv->mdio_pcs)
- return;
-
- for (port = 0; port < ds->num_ports; port++) {
- if (priv->pcs[port]) {
- xpcs_destroy_pcs(priv->pcs[port]);
- priv->pcs[port] = NULL;
- }
- }
-
- mdiobus_unregister(priv->mdio_pcs);
- mdiobus_free(priv->mdio_pcs);
- priv->mdio_pcs = NULL;
-}
-
-int sja1105_mdiobus_register(struct dsa_switch *ds)
-{
- struct sja1105_private *priv = ds->priv;
-
- return sja1105_mdiobus_pcs_register(priv);
-}
-
-void sja1105_mdiobus_unregister(struct dsa_switch *ds)
-{
- struct sja1105_private *priv = ds->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 4d4da69b3c30..27cac00eba32 100644
--- a/drivers/net/dsa/sja1105/sja1105_spi.c
+++ b/drivers/net/dsa/sja1105/sja1105_spi.c
@@ -615,9 +615,6 @@ static const struct sja1105_regs sja1110_regs = {
.ptpclkrate = SJA1110_SPI_ADDR(0x74),
.ptpclkcorp = SJA1110_SPI_ADDR(0x80),
.ptpsyncts = SJA1110_SPI_ADDR(0x84),
- .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},
};
/* See port compatibility matrix in Documentation/networking/dsa/sja1105.rst */@@ -791,8 +788,6 @@ const struct sja1105_info sja1105r_info = {
.ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing,
.rxtstamp = sja1105_rxtstamp,
.clocking_setup = sja1105_clocking_setup,
- .pcs_mdio_read_c45 = sja1105_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1105_pcs_mdio_write_c45,
.regs = &sja1105pqrs_regs,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,@@ -830,8 +825,6 @@ const struct sja1105_info sja1105s_info = {
.ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing,
.rxtstamp = sja1105_rxtstamp,
.clocking_setup = sja1105_clocking_setup,
- .pcs_mdio_read_c45 = sja1105_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1105_pcs_mdio_write_c45,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 3,@@ -871,8 +864,6 @@ const struct sja1105_info sja1110a_info = {
.rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp,
.disable_microcontroller = sja1110_disable_microcontroller,
- .pcs_mdio_read_c45 = sja1110_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1110_pcs_mdio_write_c45,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4,@@ -924,8 +915,6 @@ const struct sja1105_info sja1110b_info = {
.rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp,
.disable_microcontroller = sja1110_disable_microcontroller,
- .pcs_mdio_read_c45 = sja1110_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1110_pcs_mdio_write_c45,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4,@@ -977,8 +966,6 @@ const struct sja1105_info sja1110c_info = {
.rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp,
.disable_microcontroller = sja1110_disable_microcontroller,
- .pcs_mdio_read_c45 = sja1110_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1110_pcs_mdio_write_c45,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4,@@ -1030,8 +1017,6 @@ const struct sja1105_info sja1110d_info = {
.rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp,
.disable_microcontroller = sja1110_disable_microcontroller,
- .pcs_mdio_read_c45 = sja1110_pcs_mdio_read_c45,
- .pcs_mdio_write_c45 = sja1110_pcs_mdio_write_c45,
.port_speed = {
[SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4,diff --git a/drivers/net/dsa/sja1105/sja1105_subdev.c b/drivers/net/dsa/sja1105/sja1105_subdev.c
index 085d77947dc3..5ca613a4549d 100644
--- a/drivers/net/dsa/sja1105/sja1105_subdev.c
+++ b/drivers/net/dsa/sja1105/sja1105_subdev.c
@@ -78,6 +78,167 @@ static int devm_of_subdev_add(const struct platform_device_info *pdevinfo)
return devm_add_action_or_reset(parent, of_subdev_del, pdev);
}
+static int of_subdev_collect_resources(struct device *parent, struct device_node *np,
+ size_t address_cells, size_t size_cells,
+ struct resource **res, size_t *num_res)
+{
+ size_t reg_len;
+ u32 *reg;
+ int err;
+
+ err = of_property_count_u32_elems(np, "reg");
+ if (!err)
+ err = -EINVAL;
+ if (err < 0) {
+ dev_err(parent, "Failed to read subdev %pOF \"reg\" property: %pe\n",
+ np, ERR_PTR(err));
+ return err;
+ }
+ reg_len = err;
+
+ if (reg_len % (address_cells + size_cells)) {
+ dev_err(parent, "Invalid \"reg\" specifier for %pOF\n", np);
+ return -EINVAL;
+ }
+
+ *num_res = reg_len / (address_cells + size_cells);
+ *res = kcalloc(*num_res, sizeof(**res), GFP_KERNEL);
+ if (!*res)
+ return -ENOMEM;
+
+ reg = kcalloc(reg_len, sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ kfree(*res);
+ return -ENOMEM;
+ }
+
+ err = of_property_read_u32_array(np, "reg", reg, reg_len);
+ if (err) {
+ kfree(reg);
+ kfree(*res);
+ return err;
+ }
+
+ for (int cur_res = 0; cur_res < *num_res; cur_res++) {
+ int idx, address_cell, size_cell;
+ phys_addr_t start = 0, size = 0;
+
+ for (address_cell = 0; address_cell < address_cells; address_cell++) {
+ idx = cur_res * (address_cells + size_cells) + address_cell;
+ start = (unsigned long long)start << 32 | reg[idx];
+ }
+ for (size_cell = 0; size_cell < size_cells; size_cell++) {
+ idx = cur_res * (address_cells + size_cells) + address_cells + size_cell;
+ size = (unsigned long long)size << 32 | reg[idx];
+ }
+
+ (*res)[cur_res].start = start;
+ (*res)[cur_res].end = start + size - 1;
+ (*res)[cur_res].flags = IORESOURCE_REG;
+ of_property_read_string_index(np, "reg-names", cur_res, &(*res)[cur_res].name);
+ }
+
+ return 0;
+}
+
+/* Custom version of of_device_make_bus_id() which derives the name from the
+ * parent device plus the subdev name and untranslatable address.
+ * We don't set the resource address in the platform ID because that would
+ * print it as decimal rather than hex.
+ */
+static void of_subdev_make_bus_id(char *name, size_t name_len,
+ const struct device *parent,
+ struct device_node *child,
+ const struct resource *res)
+{
+ if (res)
+ snprintf(name, name_len, "%s.%llx.%pOFn", dev_name(parent),
+ (unsigned long long)res->start, child);
+ else
+ snprintf(name, name_len, "%s.%pOFn", dev_name(parent), child);
+}
+
+/**
+ * devm_of_subdevs_populate() - Populate platform sub-devices from device tree
+ * @parent: Parent device for all created sub-devices
+ * @np: Device tree node containing child nodes to be converted to sub-devices
+ *
+ * The device tree node @np describes the (MMIO-like but untranslatable) linear
+ * address space of device @parent, as can sometimes be found when such device
+ * is accessed through a SPI-to-AHB bridge.
+ *
+ * This function parses the device tree node @np and creates platform devices
+ * for each available child node. It reads the #address-cells and #size-cells
+ * properties to properly parse the "reg" properties of child nodes.
+ *
+ * For each child node, the function:
+ * - Creates a platform device with a name based on the parent and child node
+ * - Auto-detects and attaches resources to sub-devices based on parsed device
+ * tree "reg" and "reg-names" properties
+ * - Uses the first resource's start address as the platform device ID
+ * - Registers the device with automatic cleanup via devres
+ *
+ * This is similar to of_platform_populate() except it expects to find
+ * IORESOURCE_REG resources rather than IORESOURCE_MEM/IORESOURCE_IO.
+ * It is also similar to mfd_add_devices() except we don't have to specify the
+ * mfd_cells[], but rather, the resources are embedded into the device tree.
+ * More importantly, this allows for the parent to have a hybrid function
+ * (MFD parent + the main function of the device) and a custom device tree
+ * binding, whereas MFD does not.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int devm_of_subdevs_populate(struct device *parent, struct device_node *np)
+{
+ u32 address_cells, size_cells;
+ int err;
+
+ err = of_property_read_u32(np, "#address-cells", &address_cells);
+ if (err)
+ return err;
+
+ err = of_property_read_u32(np, "#size-cells", &size_cells);
+ if (err)
+ return err;
+
+ if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) ?
+ (address_cells > 2 || size_cells > 2) :
+ (address_cells > 1 || size_cells > 1)) {
+ dev_err(parent, "Subdev address space exceeds phys_addr_t possibilities\n");
+ return -EINVAL;
+ }
+
+ for_each_available_child_of_node_scoped(np, child) {
+ struct platform_device_info subdev;
+ struct resource *res;
+ size_t num_res;
+ char name[64];
+
+ err = of_subdev_collect_resources(parent, child, address_cells,
+ size_cells, &res, &num_res);
+ if (err)
+ return err;
+
+ of_subdev_make_bus_id(name, sizeof(name), parent, child,
+ num_res ? &res[0] : NULL);
+ subdev = (struct platform_device_info) {
+ .parent = parent,
+ .fwnode = of_fwnode_handle(child),
+ .name = name,
+ .id = PLATFORM_DEVID_NONE,
+ .res = res,
+ .num_res = num_res,
+ };
+
+ err = devm_of_subdev_add(&subdev);
+ kfree(res);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int devm_sja1105_add_mdio_subdev(struct device *parent,
struct device_node *np,
const struct resource *res,@@ -139,9 +300,17 @@ static int devm_sja1105_add_mdio_subdevs(struct dsa_switch *ds,
int devm_sja1105_add_subdevs(struct dsa_switch *ds)
{
struct device_node *switch_node = dev_of_node(ds->dev);
- struct device_node *mdio_node;
+ struct device_node *regs_node, *mdio_node;
int rc = 0;
+ regs_node = of_get_available_child_by_name(switch_node, "regs");
+ if (regs_node) {
+ rc = devm_of_subdevs_populate(ds->dev, regs_node);
+ of_node_put(regs_node);
+ if (rc)
+ return rc;
+ }
+
mdio_node = of_get_available_child_by_name(switch_node, "mdios");
if (mdio_node) {
rc = devm_sja1105_add_mdio_subdevs(ds, mdio_node);--
2.34.1