In preparation for reporting PSE events via ethtool notifications,
introduce an attached_phydev field in the pse_control structure.
This field stores the phy_device associated with the PSE PI,
ensuring that notifications are sent to the correct network
interface.
The attached_phydev pointer is directly tied to the PHY lifecycle. It
is set when the PHY is registered and cleared when the PHY is removed.
There is no need to use a refcount, as doing so could interfere with
the PHY removal process.
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-1-78a1a645e2ee@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors");
 
 static struct pse_control *
-fwnode_find_pse_control(struct fwnode_handle *fwnode)
+fwnode_find_pse_control(struct fwnode_handle *fwnode,
+                       struct phy_device *phydev)
 {
        struct pse_control *psec;
        struct device_node *np;
        if (!np)
                return NULL;
 
-       psec = of_pse_control_get(np);
+       psec = of_pse_control_get(np, phydev);
        if (PTR_ERR(psec) == -ENOENT)
                return NULL;
 
        u32 phy_id;
        int rc;
 
-       psec = fwnode_find_pse_control(child);
-       if (IS_ERR(psec))
-               return PTR_ERR(psec);
-
        mii_ts = fwnode_find_mii_timestamper(child);
-       if (IS_ERR(mii_ts)) {
-               rc = PTR_ERR(mii_ts);
-               goto clean_pse;
-       }
+       if (IS_ERR(mii_ts))
+               return PTR_ERR(mii_ts);
 
        is_c45 = fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45");
        if (is_c45 || fwnode_get_phy_id(child, &phy_id))
                        goto clean_phy;
        }
 
+       psec = fwnode_find_pse_control(child, phy);
+       if (IS_ERR(psec)) {
+               rc = PTR_ERR(psec);
+               goto unregister_phy;
+       }
+
        phy->psec = psec;
 
        /* phy->mii_ts may already be defined by the PHY driver. A
 
        return 0;
 
+unregister_phy:
+       if (is_acpi_node(child) || is_of_node(child))
+               phy_device_remove(phy);
 clean_phy:
        phy_device_free(phy);
 clean_mii_ts:
        unregister_mii_timestamper(mii_ts);
-clean_pse:
-       pse_control_put(psec);
 
        return rc;
 }
 
  * @list: list entry for the pcdev's PSE controller list
  * @id: ID of the PSE line in the PSE controller device
  * @refcnt: Number of gets of this pse_control
+ * @attached_phydev: PHY device pointer attached by the PSE control
  */
 struct pse_control {
        struct pse_controller_dev *pcdev;
        struct list_head list;
        unsigned int id;
        struct kref refcnt;
+       struct phy_device *attached_phydev;
 };
 
 static int of_load_single_pse_pi_pairset(struct device_node *node,
 EXPORT_SYMBOL_GPL(pse_control_put);
 
 static struct pse_control *
-pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index)
+pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,
+                        struct phy_device *phydev)
 {
        struct pse_control *psec;
        int ret;
        psec->pcdev = pcdev;
        list_add(&psec->list, &pcdev->pse_control_head);
        psec->id = index;
+       psec->attached_phydev = phydev;
        kref_init(&psec->refcnt);
 
        return psec;
        return pse_spec->args[0];
 }
 
-struct pse_control *of_pse_control_get(struct device_node *node)
+struct pse_control *of_pse_control_get(struct device_node *node,
+                                      struct phy_device *phydev)
 {
        struct pse_controller_dev *r, *pcdev;
        struct of_phandle_args args;
        }
 
        /* pse_list_mutex also protects the pcdev's pse_control list */
-       psec = pse_control_get_internal(pcdev, psec_id);
+       psec = pse_control_get_internal(pcdev, psec_id, phydev);
 
 out:
        mutex_unlock(&pse_list_mutex);
 
 int devm_pse_controller_register(struct device *dev,
                                 struct pse_controller_dev *pcdev);
 
-struct pse_control *of_pse_control_get(struct device_node *node);
+struct pse_control *of_pse_control_get(struct device_node *node,
+                                      struct phy_device *phydev);
 void pse_control_put(struct pse_control *psec);
 
 int pse_ethtool_get_status(struct pse_control *psec,
 
 #else
 
-static inline struct pse_control *of_pse_control_get(struct device_node *node)
+static inline struct pse_control *of_pse_control_get(struct device_node *node,
+                                                    struct phy_device *phydev)
 {
        return ERR_PTR(-ENOENT);
 }