[RFC v1 net-next 6/7] net: dsa: felix: allow external parsing of port nodes

From: Colin Foster
Date: Thu Feb 16 2023 - 02:54:08 EST


When the felix driver parses the device tree, it does so in such a way that
every node is parsed in a loop. This is done in the felix_init_structs()
function. After this is done, a separate loop will invoke
ocelot_init_port() on each port.

This causes problems if a user of the felix driver needs to retain some
information from the device tree during port initialization. A driver
might, for example, need to create call phylink_create() during
ocelot_init_port(), which requires a reference to the fwnode_handle.

Add a hook from felix into the sub-drivers, where they can optionally grab
references to the device_node when needed.

Signed-off-by: Colin Foster <colin.foster@xxxxxxxxxxxxxxxx>
---
drivers/net/dsa/ocelot/felix.c | 54 +++++++++++++++++++++++++---------
drivers/net/dsa/ocelot/felix.h | 5 ++++
2 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 21dcb9cadc12..b6e3a88addb8 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1245,7 +1245,8 @@ static int felix_parse_ports_node(struct felix *felix,
struct device_node *ports_node,
phy_interface_t *port_phy_modes)
{
- struct device *dev = felix->ocelot.dev;
+ struct ocelot *ocelot = &felix->ocelot;
+ struct device *dev = ocelot->dev;
struct device_node *child;

for_each_available_child_of_node(ports_node, child) {
@@ -1285,6 +1286,19 @@ static int felix_parse_ports_node(struct felix *felix,
}

port_phy_modes[port] = phy_mode;
+
+ if (dsa_is_cpu_port(felix->ds, port) ||
+ !felix->info->parse_port_node)
+ continue;
+
+ err = felix->info->parse_port_node(ocelot, child, phy_mode,
+ port);
+ if (err < 0) {
+ dev_err(dev, "Unable to create etherdev for port %d\n",
+ port);
+ of_node_put(child);
+ return err;
+ }
}

return 0;
@@ -1396,8 +1410,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)

err = felix_parse_dt(felix, port_phy_modes);
if (err) {
- kfree(port_phy_modes);
- return err;
+ goto free_port_modes;
}

for (i = 0; i < TARGET_MAX; i++) {
@@ -1406,8 +1419,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
dev_err(ocelot->dev,
"Failed to map device memory space: %pe\n",
target);
- kfree(port_phy_modes);
- return PTR_ERR(target);
+ err = PTR_ERR(target);
+ goto free_port_modes;
}

ocelot->targets[i] = target;
@@ -1416,8 +1429,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
err = ocelot_regfields_init(ocelot, felix->info->regfields);
if (err) {
dev_err(ocelot->dev, "failed to init reg fields map\n");
- kfree(port_phy_modes);
- return err;
+ goto free_port_modes;
}

for (port = 0; port < num_phys_ports; port++) {
@@ -1429,8 +1441,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
if (!ocelot_port) {
dev_err(ocelot->dev,
"failed to allocate port memory\n");
- kfree(port_phy_modes);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_port_modes;
}

target = felix_request_port_regmap(felix, port);
@@ -1438,8 +1450,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
dev_err(ocelot->dev,
"Failed to map memory space for port %d: %pe\n",
port, target);
- kfree(port_phy_modes);
- return PTR_ERR(target);
+ err = PTR_ERR(target);
+ goto free_port_modes;
}

ocelot_port->phy_mode = port_phy_modes[port];
@@ -1449,15 +1461,21 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
ocelot->ports[port] = ocelot_port;
}

- kfree(port_phy_modes);
-
if (felix->info->mdio_bus_alloc) {
err = felix->info->mdio_bus_alloc(ocelot);
if (err < 0)
- return err;
+ goto free_port_modes;
}

+ kfree(port_phy_modes);
+
return 0;
+
+free_port_modes:
+ if (felix->info->phylink_of_cleanup)
+ felix->info->phylink_of_cleanup(ocelot);
+ kfree(port_phy_modes);
+ return err;
}

static void ocelot_port_purge_txtstamp_skb(struct ocelot *ocelot, int port,
@@ -1574,12 +1592,18 @@ static int felix_setup(struct dsa_switch *ds)
dsa_switch_for_each_available_port(dp, ds) {
ocelot_init_port(ocelot, dp->index);

+ if (felix->info->phylink_create)
+ felix->info->phylink_create(ocelot, dp->index);
+
/* Set the default QoS Classification based on PCP and DEI
* bits of vlan tag.
*/
felix_port_qos_map_init(ocelot, dp->index);
}

+ if (felix->info->phylink_of_cleanup)
+ felix->info->phylink_of_cleanup(ocelot);
+
err = ocelot_devlink_sb_register(ocelot);
if (err)
goto out_deinit_ports;
@@ -1604,6 +1628,8 @@ static int felix_setup(struct dsa_switch *ds)
ocelot_deinit(ocelot);

out_mdiobus_free:
+ if (felix->info->phylink_of_cleanup)
+ felix->info->phylink_of_cleanup(ocelot);
if (felix->info->mdio_bus_free)
felix->info->mdio_bus_free(ocelot);

diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index d5d0b30c0b75..ffb60bcf1817 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -58,6 +58,11 @@ struct felix_info {
void (*tas_guard_bands_update)(struct ocelot *ocelot, int port);
void (*port_sched_speed_set)(struct ocelot *ocelot, int port,
u32 speed);
+ int (*parse_port_node)(struct ocelot *ocelot,
+ struct device_node *ports_node,
+ phy_interface_t phy_mode, int port);
+ int (*phylink_create)(struct ocelot *ocelot, int port);
+ void (*phylink_of_cleanup)(struct ocelot *ocelot);
};

/* Methods for initializing the hardware resources specific to a tagging
--
2.25.1