drivers/net/phy: add helpers to get/set PLCA configuration
authorPiergiorgio Beruto <piergiorgio.beruto@gmail.com>
Mon, 9 Jan 2023 17:00:23 +0000 (18:00 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Jan 2023 08:35:02 +0000 (08:35 +0000)
This patch adds support in phylib to read/write PLCA configuration for
Ethernet PHYs that support the OPEN Alliance "10BASE-T1S PLCA
Management Registers" specifications. These can be found at
https://www.opensig.org/about/specifications/

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>
MAINTAINERS
drivers/net/phy/mdio-open-alliance.h [new file with mode: 0644]
drivers/net/phy/phy-c45.c
include/linux/phy.h

index 7b6e602..8880701 100644 (file)
@@ -16620,6 +16620,7 @@ PLCA RECONCILIATION SUBLAYER (IEEE802.3 Clause 148)
 M:     Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
+F:     drivers/net/phy/mdio-open-alliance.h
 F:     net/ethtool/plca.c
 
 PLDMFW LIBRARY
diff --git a/drivers/net/phy/mdio-open-alliance.h b/drivers/net/phy/mdio-open-alliance.h
new file mode 100644 (file)
index 0000000..931e146
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mdio-open-alliance.h - definition of OPEN Alliance SIG standard registers
+ */
+
+#ifndef __MDIO_OPEN_ALLIANCE__
+#define __MDIO_OPEN_ALLIANCE__
+
+#include <linux/mdio.h>
+
+/* NOTE: all OATC14 registers are located in MDIO_MMD_VEND2 */
+
+/* Open Alliance TC14 (10BASE-T1S) registers */
+#define MDIO_OATC14_PLCA_IDVER 0xca00  /* PLCA ID and version */
+#define MDIO_OATC14_PLCA_CTRL0 0xca01  /* PLCA Control register 0 */
+#define MDIO_OATC14_PLCA_CTRL1 0xca02  /* PLCA Control register 1 */
+#define MDIO_OATC14_PLCA_STATUS        0xca03  /* PLCA Status register */
+#define MDIO_OATC14_PLCA_TOTMR 0xca04  /* PLCA TO Timer register */
+#define MDIO_OATC14_PLCA_BURST 0xca05  /* PLCA BURST mode register */
+
+/* Open Alliance TC14 PLCA IDVER register */
+#define MDIO_OATC14_PLCA_IDM   0xff00  /* PLCA MAP ID */
+#define MDIO_OATC14_PLCA_VER   0x00ff  /* PLCA MAP version */
+
+/* Open Alliance TC14 PLCA CTRL0 register */
+#define MDIO_OATC14_PLCA_EN    BIT(15) /* PLCA enable */
+#define MDIO_OATC14_PLCA_RST   BIT(14) /* PLCA reset */
+
+/* Open Alliance TC14 PLCA CTRL1 register */
+#define MDIO_OATC14_PLCA_NCNT  0xff00  /* PLCA node count */
+#define MDIO_OATC14_PLCA_ID    0x00ff  /* PLCA local node ID */
+
+/* Open Alliance TC14 PLCA STATUS register */
+#define MDIO_OATC14_PLCA_PST   BIT(15) /* PLCA status indication */
+
+/* Open Alliance TC14 PLCA TOTMR register */
+#define MDIO_OATC14_PLCA_TOT   0x00ff
+
+/* Open Alliance TC14 PLCA BURST register */
+#define MDIO_OATC14_PLCA_MAXBC 0xff00
+#define MDIO_OATC14_PLCA_BTMR  0x00ff
+
+/* Version Identifiers */
+#define OATC14_IDM             0x0a00
+
+#endif /* __MDIO_OPEN_ALLIANCE__ */
index a87a4b3..cff8322 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/mii.h>
 #include <linux/phy.h>
 
+#include "mdio-open-alliance.h"
+
 /**
  * genphy_c45_baset1_able - checks if the PMA has BASE-T1 extended abilities
  * @phydev: target phy_device struct
@@ -931,6 +933,197 @@ int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
 }
 EXPORT_SYMBOL_GPL(genphy_c45_fast_retrain);
 
+/**
+ * genphy_c45_plca_get_cfg - get PLCA configuration from standard registers
+ * @phydev: target phy_device struct
+ * @plca_cfg: output structure to store the PLCA configuration
+ *
+ * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
+ *   Management Registers specifications, this function can be used to retrieve
+ *   the current PLCA configuration from the standard registers in MMD 31.
+ */
+int genphy_c45_plca_get_cfg(struct phy_device *phydev,
+                           struct phy_plca_cfg *plca_cfg)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_IDVER);
+       if (ret < 0)
+               return ret;
+
+       if ((ret & MDIO_OATC14_PLCA_IDM) != OATC14_IDM)
+               return -ENODEV;
+
+       plca_cfg->version = ret & ~MDIO_OATC14_PLCA_IDM;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL0);
+       if (ret < 0)
+               return ret;
+
+       plca_cfg->enabled = !!(ret & MDIO_OATC14_PLCA_EN);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       plca_cfg->node_cnt = (ret & MDIO_OATC14_PLCA_NCNT) >> 8;
+       plca_cfg->node_id = (ret & MDIO_OATC14_PLCA_ID);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR);
+       if (ret < 0)
+               return ret;
+
+       plca_cfg->to_tmr = ret & MDIO_OATC14_PLCA_TOT;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_BURST);
+       if (ret < 0)
+               return ret;
+
+       plca_cfg->burst_cnt = (ret & MDIO_OATC14_PLCA_MAXBC) >> 8;
+       plca_cfg->burst_tmr = (ret & MDIO_OATC14_PLCA_BTMR);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_plca_get_cfg);
+
+/**
+ * genphy_c45_plca_set_cfg - set PLCA configuration using standard registers
+ * @phydev: target phy_device struct
+ * @plca_cfg: structure containing the PLCA configuration. Fields set to -1 are
+ * not to be changed.
+ *
+ * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
+ *   Management Registers specifications, this function can be used to modify
+ *   the PLCA configuration using the standard registers in MMD 31.
+ */
+int genphy_c45_plca_set_cfg(struct phy_device *phydev,
+                           const struct phy_plca_cfg *plca_cfg)
+{
+       int ret;
+       u16 val;
+
+       // PLCA IDVER is read-only
+       if (plca_cfg->version >= 0)
+               return -EINVAL;
+
+       // first of all, disable PLCA if required
+       if (plca_cfg->enabled == 0) {
+               ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
+                                        MDIO_OATC14_PLCA_CTRL0,
+                                        MDIO_OATC14_PLCA_EN);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       // check if we need to set the PLCA node count, node ID, or both
+       if (plca_cfg->node_cnt >= 0 || plca_cfg->node_id >= 0) {
+               /* if one between node count and node ID is -not- to be
+                * changed, read the register to later perform merge/purge of
+                * the configuration as appropriate
+                */
+               if (plca_cfg->node_cnt < 0 || plca_cfg->node_id < 0) {
+                       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+                                          MDIO_OATC14_PLCA_CTRL1);
+
+                       if (ret < 0)
+                               return ret;
+
+                       val = ret;
+               }
+
+               if (plca_cfg->node_cnt >= 0)
+                       val = (val & ~MDIO_OATC14_PLCA_NCNT) |
+                             (plca_cfg->node_cnt << 8);
+
+               if (plca_cfg->node_id >= 0)
+                       val = (val & ~MDIO_OATC14_PLCA_ID) |
+                             (plca_cfg->node_id);
+
+               ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+                                   MDIO_OATC14_PLCA_CTRL1, val);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (plca_cfg->to_tmr >= 0) {
+               ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+                                   MDIO_OATC14_PLCA_TOTMR,
+                                   plca_cfg->to_tmr);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       // check if we need to set the PLCA burst count, burst timer, or both
+       if (plca_cfg->burst_cnt >= 0 || plca_cfg->burst_tmr >= 0) {
+               /* if one between burst count and burst timer is -not- to be
+                * changed, read the register to later perform merge/purge of
+                * the configuration as appropriate
+                */
+               if (plca_cfg->burst_cnt < 0 || plca_cfg->burst_tmr < 0) {
+                       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+                                          MDIO_OATC14_PLCA_BURST);
+
+                       if (ret < 0)
+                               return ret;
+
+                       val = ret;
+               }
+
+               if (plca_cfg->burst_cnt >= 0)
+                       val = (val & ~MDIO_OATC14_PLCA_MAXBC) |
+                             (plca_cfg->burst_cnt << 8);
+
+               if (plca_cfg->burst_tmr >= 0)
+                       val = (val & ~MDIO_OATC14_PLCA_BTMR) |
+                             (plca_cfg->burst_tmr);
+
+               ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+                                   MDIO_OATC14_PLCA_BURST, val);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       // if we need to enable PLCA, do it at the end
+       if (plca_cfg->enabled > 0) {
+               ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+                                      MDIO_OATC14_PLCA_CTRL0,
+                                      MDIO_OATC14_PLCA_EN);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_plca_set_cfg);
+
+/**
+ * genphy_c45_plca_get_status - get PLCA status from standard registers
+ * @phydev: target phy_device struct
+ * @plca_st: output structure to store the PLCA status
+ *
+ * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
+ *   Management Registers specifications, this function can be used to retrieve
+ *   the current PLCA status information from the standard registers in MMD 31.
+ */
+int genphy_c45_plca_get_status(struct phy_device *phydev,
+                              struct phy_plca_status *plca_st)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_STATUS);
+       if (ret < 0)
+               return ret;
+
+       plca_st->pst = !!(ret & MDIO_OATC14_PLCA_PST);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
+
 struct phy_driver genphy_c45_driver = {
        .phy_id         = 0xffffffff,
        .phy_id_mask    = 0xffffffff,
index 7c2ec16..b3cf1e0 100644 (file)
@@ -1753,6 +1753,12 @@ int genphy_c45_loopback(struct phy_device *phydev, bool enable);
 int genphy_c45_pma_resume(struct phy_device *phydev);
 int genphy_c45_pma_suspend(struct phy_device *phydev);
 int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable);
+int genphy_c45_plca_get_cfg(struct phy_device *phydev,
+                           struct phy_plca_cfg *plca_cfg);
+int genphy_c45_plca_set_cfg(struct phy_device *phydev,
+                           const struct phy_plca_cfg *plca_cfg);
+int genphy_c45_plca_get_status(struct phy_device *phydev,
+                              struct phy_plca_status *plca_st);
 
 /* Generic C45 PHY driver */
 extern struct phy_driver genphy_c45_driver;