drivers/net/phy: add connection between ethtool and phylib for PLCA
authorPiergiorgio Beruto <piergiorgio.beruto@gmail.com>
Mon, 9 Jan 2023 17:00:10 +0000 (18:00 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Jan 2023 08:35:02 +0000 (08:35 +0000)
This patch adds the required connection between netlink ethtool and
phylib to resolve PLCA get/set config and get status messages.

Signed-off-by: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
include/linux/phy.h

index e5b6cb1a77f95a94d3cb642892f1816ddc5b6117..3378ca4f49b6c684214119ba2d2679d1b2531e7a 100644 (file)
@@ -543,6 +543,198 @@ int phy_ethtool_get_stats(struct phy_device *phydev,
 }
 EXPORT_SYMBOL(phy_ethtool_get_stats);
 
+/**
+ * phy_ethtool_get_plca_cfg - Get PLCA RS configuration
+ * @phydev: the phy_device struct
+ * @plca_cfg: where to store the retrieved configuration
+ *
+ * Retrieve the PLCA configuration from the PHY. Return 0 on success or a
+ * negative value if an error occurred.
+ */
+int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
+                            struct phy_plca_cfg *plca_cfg)
+{
+       int ret;
+
+       if (!phydev->drv) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (!phydev->drv->get_plca_cfg) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       mutex_lock(&phydev->lock);
+       ret = phydev->drv->get_plca_cfg(phydev, plca_cfg);
+
+       mutex_unlock(&phydev->lock);
+out:
+       return ret;
+}
+
+/**
+ * plca_check_valid - Check PLCA configuration before enabling
+ * @phydev: the phy_device struct
+ * @plca_cfg: current PLCA configuration
+ * @extack: extack for reporting useful error messages
+ *
+ * Checks whether the PLCA and PHY configuration are consistent and it is safe
+ * to enable PLCA. Returns 0 on success or a negative value if the PLCA or PHY
+ * configuration is not consistent.
+ */
+static int plca_check_valid(struct phy_device *phydev,
+                           const struct phy_plca_cfg *plca_cfg,
+                           struct netlink_ext_ack *extack)
+{
+       int ret = 0;
+
+       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
+                              phydev->advertising)) {
+               ret = -EOPNOTSUPP;
+               NL_SET_ERR_MSG(extack,
+                              "Point to Multi-Point mode is not enabled");
+       } else if (plca_cfg->node_id >= 255) {
+               NL_SET_ERR_MSG(extack, "PLCA node ID is not set");
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * phy_ethtool_set_plca_cfg - Set PLCA RS configuration
+ * @phydev: the phy_device struct
+ * @plca_cfg: new PLCA configuration to apply
+ * @extack: extack for reporting useful error messages
+ *
+ * Sets the PLCA configuration in the PHY. Return 0 on success or a
+ * negative value if an error occurred.
+ */
+int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
+                            const struct phy_plca_cfg *plca_cfg,
+                            struct netlink_ext_ack *extack)
+{
+       struct phy_plca_cfg *curr_plca_cfg;
+       int ret;
+
+       if (!phydev->drv) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (!phydev->drv->set_plca_cfg ||
+           !phydev->drv->get_plca_cfg) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       curr_plca_cfg = kmalloc(sizeof(*curr_plca_cfg), GFP_KERNEL);
+       if (!curr_plca_cfg) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mutex_lock(&phydev->lock);
+
+       ret = phydev->drv->get_plca_cfg(phydev, curr_plca_cfg);
+       if (ret)
+               goto out_drv;
+
+       if (curr_plca_cfg->enabled < 0 && plca_cfg->enabled >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'enable' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       if (curr_plca_cfg->node_id < 0 && plca_cfg->node_id >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'local node ID' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       if (curr_plca_cfg->node_cnt < 0 && plca_cfg->node_cnt >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'node count' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       if (curr_plca_cfg->to_tmr < 0 && plca_cfg->to_tmr >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'TO timer' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       if (curr_plca_cfg->burst_cnt < 0 && plca_cfg->burst_cnt >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'burst count' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       if (curr_plca_cfg->burst_tmr < 0 && plca_cfg->burst_tmr >= 0) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY does not support changing the PLCA 'burst timer' attribute");
+               ret = -EINVAL;
+               goto out_drv;
+       }
+
+       // if enabling PLCA, perform a few sanity checks
+       if (plca_cfg->enabled > 0) {
+               // allow setting node_id concurrently with enabled
+               if (plca_cfg->node_id >= 0)
+                       curr_plca_cfg->node_id = plca_cfg->node_id;
+
+               ret = plca_check_valid(phydev, curr_plca_cfg, extack);
+               if (ret)
+                       goto out_drv;
+       }
+
+       ret = phydev->drv->set_plca_cfg(phydev, plca_cfg);
+
+out_drv:
+       kfree(curr_plca_cfg);
+       mutex_unlock(&phydev->lock);
+out:
+       return ret;
+}
+
+/**
+ * phy_ethtool_get_plca_status - Get PLCA RS status information
+ * @phydev: the phy_device struct
+ * @plca_st: where to store the retrieved status information
+ *
+ * Retrieve the PLCA status information from the PHY. Return 0 on success or a
+ * negative value if an error occurred.
+ */
+int phy_ethtool_get_plca_status(struct phy_device *phydev,
+                               struct phy_plca_status *plca_st)
+{
+       int ret;
+
+       if (!phydev->drv) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (!phydev->drv->get_plca_status) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       mutex_lock(&phydev->lock);
+       ret = phydev->drv->get_plca_status(phydev, plca_st);
+
+       mutex_unlock(&phydev->lock);
+out:
+       return ret;
+}
+
 /**
  * phy_start_cable_test - Start a cable test
  *
index 1cde41d3919660a0b9743946b0b45af5750976e9..0d371a0a49f27588db2cc29157eb250fa8172396 100644 (file)
@@ -3283,6 +3283,9 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
        .get_sset_count         = phy_ethtool_get_sset_count,
        .get_strings            = phy_ethtool_get_strings,
        .get_stats              = phy_ethtool_get_stats,
+       .get_plca_cfg           = phy_ethtool_get_plca_cfg,
+       .set_plca_cfg           = phy_ethtool_set_plca_cfg,
+       .get_plca_status        = phy_ethtool_get_plca_status,
        .start_cable_test       = phy_start_cable_test,
        .start_cable_test_tdr   = phy_start_cable_test_tdr,
 };
index 63b199f574f7275fa1d510009dd5c08207b0e66a..7c2ec16509752917dbe69647dfedf479e6f63194 100644 (file)
@@ -1851,6 +1851,13 @@ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data);
 int phy_ethtool_get_sset_count(struct phy_device *phydev);
 int phy_ethtool_get_stats(struct phy_device *phydev,
                          struct ethtool_stats *stats, u64 *data);
+int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
+                            struct phy_plca_cfg *plca_cfg);
+int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
+                            const struct phy_plca_cfg *plca_cfg,
+                            struct netlink_ext_ack *extack);
+int phy_ethtool_get_plca_status(struct phy_device *phydev,
+                               struct phy_plca_status *plca_st);
 
 static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
 {