diff mbox series

[net-next,v5,2/9] net: phy: add support for scanning PHY in PHY packages nodes

Message ID 20240201151747.7524-3-ansuelsmth@gmail.com (mailing list archive)
State Superseded
Headers show
Series net: phy: Introduce PHY Package concept | expand

Commit Message

Christian Marangi Feb. 1, 2024, 3:17 p.m. UTC
Add support for scanning PHY in PHY packages nodes. PHY packages nodes
are just container for actual PHY on the MDIO bus.

Their PHY address is defined as offset of the PHY package base address
defined in DT. of_mdio_parse_addr_offset helper is introduced to
validate the final address is correct.

mdio_bus.c and of_mdio.c is updated to now support and parse also
PHY package subnote by checking if the node name match
"ethernet-phy-package".

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 drivers/net/mdio/of_mdio.c | 75 +++++++++++++++++++++++++++-----------
 drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++-----
 include/linux/of_mdio.h    | 26 +++++++++++++
 3 files changed, 115 insertions(+), 30 deletions(-)

Comments

Antoine Tenart Feb. 1, 2024, 4:25 p.m. UTC | #1
Quoting Christian Marangi (2024-02-01 16:17:28)
> 
> +static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
> +                                  int base_addr, bool *scanphys)
> +{
> +       struct device_node *child;
> +       int addr, rc = 0;
> +
> +       /* Loop over the child nodes and register a phy_device for each phy */
> +       for_each_available_child_of_node(np, child) {
> +               if (of_node_name_eq(child, "ethernet-phy-package")) {
> +                       rc = of_property_read_u32(child, "reg", &addr);
> +                       if (rc)
> +                               goto exit;

This means a PHY package node w/o a reg property will prevent all other
PHYs in the same parent node to be found?

> +
> +                       rc = __of_mdiobus_parse_phys(mdio, child, addr, scanphys);

You might want to save passing scanphys down, PHYs w/o a reg property in
a PHY package won't be "auto scanned" later.

> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> index afbad1ad8683..7737d0101d7b 100644
> --- a/drivers/net/phy/mdio_bus.c
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -459,20 +459,33 @@ EXPORT_SYMBOL(of_mdio_find_bus);
>   * found, set the of_node pointer for the mdio device. This allows
>   * auto-probed phy devices to be supplied with information passed in
>   * via DT.
> + * If a PHY package is found, PHY is searched also there.
>   */
> -static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
> -                                   struct mdio_device *mdiodev)
> +static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
> +                              struct device_node *np, int base_addr)
>  {
> -       struct device *dev = &mdiodev->dev;
>         struct device_node *child;
>  
> -       if (dev->of_node || !bus->dev.of_node)
> -               return;
> +       for_each_available_child_of_node(np, child) {
> +               int addr, ret;
>  
> -       for_each_available_child_of_node(bus->dev.of_node, child) {
> -               int addr;
> +               if (of_node_name_eq(child, "ethernet-phy-package")) {
> +                       ret = of_property_read_u32(child, "reg", &addr);
> +                       if (ret)
> +                               return ret;

of_node_put
Christian Marangi Feb. 1, 2024, 5:20 p.m. UTC | #2
On Thu, Feb 01, 2024 at 05:25:36PM +0100, Antoine Tenart wrote:
> Quoting Christian Marangi (2024-02-01 16:17:28)
> > 
> > +static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
> > +                                  int base_addr, bool *scanphys)
> > +{
> > +       struct device_node *child;
> > +       int addr, rc = 0;
> > +
> > +       /* Loop over the child nodes and register a phy_device for each phy */
> > +       for_each_available_child_of_node(np, child) {
> > +               if (of_node_name_eq(child, "ethernet-phy-package")) {
> > +                       rc = of_property_read_u32(child, "reg", &addr);
> > +                       if (rc)
> > +                               goto exit;
> 
> This means a PHY package node w/o a reg property will prevent all other
> PHYs in the same parent node to be found?
>

Since this is something new, would it be a problem to make it mandatory
to define a reg? (And return error if we find something? Or print a
warn?)

> > +
> > +                       rc = __of_mdiobus_parse_phys(mdio, child, addr, scanphys);
> 
> You might want to save passing scanphys down, PHYs w/o a reg property in
> a PHY package won't be "auto scanned" later.
> 

I might be confused by this, but isn't this already done? (passing
scanphys in each recursive call so we can set it to true if needed?)

Also I think the scanphys should be skipped for the PHY package
(assuming we make reg mandatory, it would be an error condition and
should not be handled?)

> > diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> > index afbad1ad8683..7737d0101d7b 100644
> > --- a/drivers/net/phy/mdio_bus.c
> > +++ b/drivers/net/phy/mdio_bus.c
> > @@ -459,20 +459,33 @@ EXPORT_SYMBOL(of_mdio_find_bus);
> >   * found, set the of_node pointer for the mdio device. This allows
> >   * auto-probed phy devices to be supplied with information passed in
> >   * via DT.
> > + * If a PHY package is found, PHY is searched also there.
> >   */
> > -static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
> > -                                   struct mdio_device *mdiodev)
> > +static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
> > +                              struct device_node *np, int base_addr)
> >  {
> > -       struct device *dev = &mdiodev->dev;
> >         struct device_node *child;
> >  
> > -       if (dev->of_node || !bus->dev.of_node)
> > -               return;
> > +       for_each_available_child_of_node(np, child) {
> > +               int addr, ret;
> >  
> > -       for_each_available_child_of_node(bus->dev.of_node, child) {
> > -               int addr;
> > +               if (of_node_name_eq(child, "ethernet-phy-package")) {
> > +                       ret = of_property_read_u32(child, "reg", &addr);
> > +                       if (ret)
> > +                               return ret;
> 
> of_node_put
Andrew Lunn Feb. 2, 2024, 1:02 a.m. UTC | #3
On Thu, Feb 01, 2024 at 06:20:10PM +0100, Christian Marangi wrote:
> On Thu, Feb 01, 2024 at 05:25:36PM +0100, Antoine Tenart wrote:
> > Quoting Christian Marangi (2024-02-01 16:17:28)
> > > 
> > > +static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
> > > +                                  int base_addr, bool *scanphys)
> > > +{
> > > +       struct device_node *child;
> > > +       int addr, rc = 0;
> > > +
> > > +       /* Loop over the child nodes and register a phy_device for each phy */
> > > +       for_each_available_child_of_node(np, child) {
> > > +               if (of_node_name_eq(child, "ethernet-phy-package")) {
> > > +                       rc = of_property_read_u32(child, "reg", &addr);
> > > +                       if (rc)
> > > +                               goto exit;
> > 
> > This means a PHY package node w/o a reg property will prevent all other
> > PHYs in the same parent node to be found?
> >
> 
> Since this is something new, would it be a problem to make it mandatory
> to define a reg? (And return error if we find something? Or print a
> warn?)

Making reg mandatory within a package is reasonable. Please indicate
this in the DT schema.

     Andrew
Antoine Tenart Feb. 2, 2024, 10:05 a.m. UTC | #4
Quoting Christian Marangi (2024-02-01 18:20:10)
> On Thu, Feb 01, 2024 at 05:25:36PM +0100, Antoine Tenart wrote:
> > Quoting Christian Marangi (2024-02-01 16:17:28)
> > > +
> > > +                       rc = __of_mdiobus_parse_phys(mdio, child, addr, scanphys);
> > 
> > You might want to save passing scanphys down, PHYs w/o a reg property in
> > a PHY package won't be "auto scanned" later.
> 
> I might be confused by this, but isn't this already done? (passing
> scanphys in each recursive call so we can set it to true if needed?)
> 
> Also I think the scanphys should be skipped for the PHY package
> (assuming we make reg mandatory, it would be an error condition and
> should not be handled?)

Sorry if that wasn't clear, this is what I meant. scanphys doesn't need
to be set to true in a PHY package (both if we want reg to be mandatory
there and because my understanding of the auto-scan code is PHYs in a
PHY package won't be auto scanned anyway).

Thanks,
Antoine
diff mbox series

Patch

diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c
index 64ebcb6d235c..58b54c644f11 100644
--- a/drivers/net/mdio/of_mdio.c
+++ b/drivers/net/mdio/of_mdio.c
@@ -139,6 +139,54 @@  bool of_mdiobus_child_is_phy(struct device_node *child)
 }
 EXPORT_SYMBOL(of_mdiobus_child_is_phy);
 
+static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
+				   int base_addr, bool *scanphys)
+{
+	struct device_node *child;
+	int addr, rc = 0;
+
+	/* Loop over the child nodes and register a phy_device for each phy */
+	for_each_available_child_of_node(np, child) {
+		if (of_node_name_eq(child, "ethernet-phy-package")) {
+			rc = of_property_read_u32(child, "reg", &addr);
+			if (rc)
+				goto exit;
+
+			rc = __of_mdiobus_parse_phys(mdio, child, addr, scanphys);
+			if (rc && rc != -ENODEV)
+				goto exit;
+
+			continue;
+		}
+
+		if (base_addr)
+			addr = of_mdio_parse_addr_offset(&mdio->dev, child, base_addr);
+		else
+			addr = of_mdio_parse_addr(&mdio->dev, child);
+		if (addr < 0) {
+			*scanphys = true;
+			continue;
+		}
+
+		if (of_mdiobus_child_is_phy(child))
+			rc = of_mdiobus_register_phy(mdio, child, addr);
+		else
+			rc = of_mdiobus_register_device(mdio, child, addr);
+
+		if (rc == -ENODEV)
+			dev_err(&mdio->dev,
+				"MDIO device at address %d is missing.\n",
+				addr);
+		else if (rc)
+			goto exit;
+	}
+
+	return 0;
+exit:
+	of_node_put(child);
+	return rc;
+}
+
 /**
  * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
  * @mdio: pointer to mii_bus structure
@@ -180,25 +228,9 @@  int __of_mdiobus_register(struct mii_bus *mdio, struct device_node *np,
 		return rc;
 
 	/* Loop over the child nodes and register a phy_device for each phy */
-	for_each_available_child_of_node(np, child) {
-		addr = of_mdio_parse_addr(&mdio->dev, child);
-		if (addr < 0) {
-			scanphys = true;
-			continue;
-		}
-
-		if (of_mdiobus_child_is_phy(child))
-			rc = of_mdiobus_register_phy(mdio, child, addr);
-		else
-			rc = of_mdiobus_register_device(mdio, child, addr);
-
-		if (rc == -ENODEV)
-			dev_err(&mdio->dev,
-				"MDIO device at address %d is missing.\n",
-				addr);
-		else if (rc)
-			goto unregister;
-	}
+	rc = __of_mdiobus_parse_phys(mdio, np, 0, &scanphys);
+	if (rc)
+		goto unregister;
 
 	if (!scanphys)
 		return 0;
@@ -227,15 +259,16 @@  int __of_mdiobus_register(struct mii_bus *mdio, struct device_node *np,
 				if (!rc)
 					break;
 				if (rc != -ENODEV)
-					goto unregister;
+					goto put_unregister;
 			}
 		}
 	}
 
 	return 0;
 
-unregister:
+put_unregister:
 	of_node_put(child);
+unregister:
 	mdiobus_unregister(mdio);
 	return rc;
 }
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index afbad1ad8683..7737d0101d7b 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -459,20 +459,33 @@  EXPORT_SYMBOL(of_mdio_find_bus);
  * found, set the of_node pointer for the mdio device. This allows
  * auto-probed phy devices to be supplied with information passed in
  * via DT.
+ * If a PHY package is found, PHY is searched also there.
  */
-static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
-				    struct mdio_device *mdiodev)
+static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
+			       struct device_node *np, int base_addr)
 {
-	struct device *dev = &mdiodev->dev;
 	struct device_node *child;
 
-	if (dev->of_node || !bus->dev.of_node)
-		return;
+	for_each_available_child_of_node(np, child) {
+		int addr, ret;
 
-	for_each_available_child_of_node(bus->dev.of_node, child) {
-		int addr;
+		if (of_node_name_eq(child, "ethernet-phy-package")) {
+			ret = of_property_read_u32(child, "reg", &addr);
+			if (ret)
+				return ret;
 
-		addr = of_mdio_parse_addr(dev, child);
+			if (!of_mdiobus_find_phy(dev, mdiodev, child, addr)) {
+				of_node_put(child);
+				return 0;
+			}
+
+			continue;
+		}
+
+		if (base_addr)
+			addr = of_mdio_parse_addr_offset(dev, child, base_addr);
+		else
+			addr = of_mdio_parse_addr(dev, child);
 		if (addr < 0)
 			continue;
 
@@ -481,9 +494,22 @@  static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
 			/* The refcount on "child" is passed to the mdio
 			 * device. Do _not_ use of_node_put(child) here.
 			 */
-			return;
+			return 0;
 		}
 	}
+
+	return -ENODEV;
+}
+
+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
+				    struct mdio_device *mdiodev)
+{
+	struct device *dev = &mdiodev->dev;
+
+	if (dev->of_node || !bus->dev.of_node)
+		return;
+
+	of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node, 0);
 }
 #else /* !IS_ENABLED(CONFIG_OF_MDIO) */
 static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h
index 8a52ef2e6fa6..8566df2afbe6 100644
--- a/include/linux/of_mdio.h
+++ b/include/linux/of_mdio.h
@@ -72,6 +72,27 @@  static inline int of_mdio_parse_addr(struct device *dev,
 	return addr;
 }
 
+static inline int of_mdio_parse_addr_offset(struct device *dev,
+					    const struct device_node *np,
+					    u16 offset)
+{
+	int addr;
+
+	addr = of_mdio_parse_addr(dev, np);
+	if (addr < 0)
+		return addr;
+
+	/* Validate final address with offset */
+	addr += offset;
+	if (addr >= PHY_MAX_ADDR) {
+		dev_err(dev, "%s PHY address offset %i is too large\n",
+			np->full_name, addr);
+		return -EINVAL;
+	}
+
+	return addr;
+}
+
 #else /* CONFIG_OF_MDIO */
 static inline bool of_mdiobus_child_is_phy(struct device_node *child)
 {
@@ -130,6 +151,11 @@  static inline int of_mdio_parse_addr(struct device *dev,
 {
 	return -ENOSYS;
 }
+static inline int of_mdio_parse_addr_offset(struct device *dev,
+					    const struct device_node *np)
+{
+	return -ENOSYS;
+}
 static inline int of_phy_register_fixed_link(struct device_node *np)
 {
 	return -ENOSYS;