net: tn40xx: create swnode for mdio and aqr105 phy and add to mdiobus
authorHans-Frieder Vogt <hfdevel@gmx.net>
Sat, 22 Mar 2025 10:45:56 +0000 (11:45 +0100)
committerJakub Kicinski <kuba@kernel.org>
Tue, 25 Mar 2025 20:53:21 +0000 (13:53 -0700)
In case of an AQR105-based device, create a software node for the mdio
function, with a child node for the Aquantia AQR105 PHY, providing a
firmware-name (and a bit more, which may be used for future checks) to
allow the PHY to load a MAC specific firmware from the file system.

The name of the PHY software node follows the naming convention suggested
in the patch for the mdiobus_scan function (in the same patch series).

Signed-off-by: Hans-Frieder Vogt <hfdevel@gmx.net>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250322-tn9510-v3a-v7-5-672a9a3d8628@gmx.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/tehuti/tn40.c
drivers/net/ethernet/tehuti/tn40.h
drivers/net/ethernet/tehuti/tn40_mdio.c

index 259bdac..a4dd04f 100644 (file)
@@ -1778,7 +1778,7 @@ static int tn40_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        ret = tn40_phy_register(priv);
        if (ret) {
                dev_err(&pdev->dev, "failed to set up PHY.\n");
-               goto err_free_irq;
+               goto err_cleanup_swnodes;
        }
 
        ret = tn40_priv_init(priv);
@@ -1795,6 +1795,8 @@ static int tn40_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        return 0;
 err_unregister_phydev:
        tn40_phy_unregister(priv);
+err_cleanup_swnodes:
+       tn40_swnodes_cleanup(priv);
 err_free_irq:
        pci_free_irq_vectors(pdev);
 err_unset_drvdata:
@@ -1816,6 +1818,7 @@ static void tn40_remove(struct pci_dev *pdev)
        unregister_netdev(ndev);
 
        tn40_phy_unregister(priv);
+       tn40_swnodes_cleanup(priv);
        pci_free_irq_vectors(priv->pdev);
        pci_set_drvdata(pdev, NULL);
        iounmap(priv->regs);
index 490781f..25da868 100644 (file)
@@ -4,10 +4,13 @@
 #ifndef _TN40_H_
 #define _TN40_H_
 
+#include <linux/property.h>
 #include "tn40_regs.h"
 
 #define TN40_DRV_NAME "tn40xx"
 
+#define PCI_DEVICE_ID_TEHUTI_TN9510    0x4025
+
 #define TN40_MDIO_SPEED_1MHZ (1)
 #define TN40_MDIO_SPEED_6MHZ (6)
 
@@ -102,10 +105,39 @@ struct tn40_txdb {
        int size; /* Number of elements in the db */
 };
 
+#define NODE_PROP(_NAME, _PROP)        (               \
+       (const struct software_node) {          \
+               .name = _NAME,                  \
+               .properties = _PROP,            \
+       })
+
+#define NODE_PAR_PROP(_NAME, _PAR, _PROP)      (       \
+       (const struct software_node) {          \
+               .name = _NAME,                  \
+               .parent = _PAR,                 \
+               .properties = _PROP,            \
+       })
+
+enum tn40_swnodes {
+       SWNODE_MDIO,
+       SWNODE_PHY,
+       SWNODE_MAX
+};
+
+struct tn40_nodes {
+       char phy_name[32];
+       char mdio_name[32];
+       struct property_entry phy_props[3];
+       struct software_node swnodes[SWNODE_MAX];
+       const struct software_node *group[SWNODE_MAX + 1];
+};
+
 struct tn40_priv {
        struct net_device *ndev;
        struct pci_dev *pdev;
 
+       struct tn40_nodes nodes;
+
        struct napi_struct napi;
        /* RX FIFOs: 1 for data (full) descs, and 2 for free descs */
        struct tn40_rxd_fifo rxd_fifo0;
@@ -225,6 +257,7 @@ static inline void tn40_write_reg(struct tn40_priv *priv, u32 reg, u32 val)
 
 int tn40_set_link_speed(struct tn40_priv *priv, u32 speed);
 
+void tn40_swnodes_cleanup(struct tn40_priv *priv);
 int tn40_mdiobus_init(struct tn40_priv *priv);
 
 int tn40_phy_register(struct tn40_priv *priv);
index af18615..5bb0cbc 100644 (file)
@@ -14,6 +14,8 @@
         (FIELD_PREP(TN40_MDIO_PRTAD_MASK, (port))))
 #define TN40_MDIO_CMD_READ BIT(15)
 
+#define AQR105_FIRMWARE "tehuti/aqr105-tn40xx.cld"
+
 static void tn40_mdio_set_speed(struct tn40_priv *priv, u32 speed)
 {
        void __iomem *regs = priv->regs;
@@ -111,6 +113,56 @@ static int tn40_mdio_write_c45(struct mii_bus *mii_bus, int addr, int devnum,
        return  tn40_mdio_write(mii_bus->priv, addr, devnum, regnum, val);
 }
 
+/* registers an mdio node and an aqr105 PHY at address 1
+ * tn40_mdio-%id {
+ *     ethernet-phy@1 {
+ *             compatible = "ethernet-phy-id03a1.b4a3";
+ *             reg = <1>;
+ *             firmware-name = AQR105_FIRMWARE;
+ *     };
+ * };
+ */
+static int tn40_swnodes_register(struct tn40_priv *priv)
+{
+       struct tn40_nodes *nodes = &priv->nodes;
+       struct pci_dev *pdev = priv->pdev;
+       struct software_node *swnodes;
+       u32 id;
+
+       id = pci_dev_id(pdev);
+
+       snprintf(nodes->phy_name, sizeof(nodes->phy_name), "ethernet-phy@1");
+       snprintf(nodes->mdio_name, sizeof(nodes->mdio_name), "tn40_mdio-%x",
+                id);
+
+       swnodes = nodes->swnodes;
+
+       swnodes[SWNODE_MDIO] = NODE_PROP(nodes->mdio_name, NULL);
+
+       nodes->phy_props[0] = PROPERTY_ENTRY_STRING("compatible",
+                                                   "ethernet-phy-id03a1.b4a3");
+       nodes->phy_props[1] = PROPERTY_ENTRY_U32("reg", 1);
+       nodes->phy_props[2] = PROPERTY_ENTRY_STRING("firmware-name",
+                                                   AQR105_FIRMWARE);
+       swnodes[SWNODE_PHY] = NODE_PAR_PROP(nodes->phy_name,
+                                           &swnodes[SWNODE_MDIO],
+                                           nodes->phy_props);
+
+       nodes->group[SWNODE_PHY] = &swnodes[SWNODE_PHY];
+       nodes->group[SWNODE_MDIO] = &swnodes[SWNODE_MDIO];
+       return software_node_register_node_group(nodes->group);
+}
+
+void tn40_swnodes_cleanup(struct tn40_priv *priv)
+{
+       /* cleanup of swnodes is only needed for AQR105-based cards */
+       if (priv->pdev->device == PCI_DEVICE_ID_TEHUTI_TN9510) {
+               fwnode_handle_put(dev_fwnode(&priv->mdio->dev));
+               device_remove_software_node(&priv->mdio->dev);
+               software_node_unregister_node_group(priv->nodes.group);
+       }
+}
+
 int tn40_mdiobus_init(struct tn40_priv *priv)
 {
        struct pci_dev *pdev = priv->pdev;
@@ -129,14 +181,40 @@ int tn40_mdiobus_init(struct tn40_priv *priv)
 
        bus->read_c45 = tn40_mdio_read_c45;
        bus->write_c45 = tn40_mdio_write_c45;
+       priv->mdio = bus;
+
+       /* provide swnodes for AQR105-based cards only */
+       if (pdev->device == PCI_DEVICE_ID_TEHUTI_TN9510) {
+               ret = tn40_swnodes_register(priv);
+               if (ret) {
+                       pr_err("swnodes failed\n");
+                       return ret;
+               }
+
+               ret = device_add_software_node(&bus->dev,
+                                              priv->nodes.group[SWNODE_MDIO]);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "device_add_software_node failed: %d\n", ret);
+                       goto err_swnodes_unregister;
+               }
+       }
 
        ret = devm_mdiobus_register(&pdev->dev, bus);
        if (ret) {
                dev_err(&pdev->dev, "failed to register mdiobus %d %u %u\n",
                        ret, bus->state, MDIOBUS_UNREGISTERED);
-               return ret;
+               goto err_swnodes_cleanup;
        }
        tn40_mdio_set_speed(priv, TN40_MDIO_SPEED_6MHZ);
-       priv->mdio = bus;
        return 0;
+
+err_swnodes_unregister:
+       software_node_unregister_node_group(priv->nodes.group);
+       return ret;
+err_swnodes_cleanup:
+       tn40_swnodes_cleanup(priv);
+       return ret;
 }
+
+MODULE_FIRMWARE(AQR105_FIRMWARE);