From: Raju Lakkaraju <raju.lakkar...@microchip.com>

Add the Cable diagnostics command to VSC85xx PHYs.

Signed-off-by: Raju Lakkaraju <raju.lakkar...@microchip.com>
---
 drivers/net/phy/mscc.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)

diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index 28676af..98e3925 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -153,9 +153,29 @@ enum rgmii_rx_clock_delay {
 #define MSCC_PHY_EXT_PHY_CNTL_4                  23
 #define PHY_CNTL_4_ADDR_POS              11
 
+#define MSCC_PHY_VERIPHY_CNTL_1           24
+#define VERIPHY_TRIGGER_CNTL_MASK        0x8000
+#define VERIPHY_VALID_MASK               0x4000
+#define VERIPHY_PAIR_A_DISTANCE_MASK     0x2F00
+#define VERIPHY_PAIR_A_DISTANCE_POS      8
+#define VERIPHY_PAIR_B_DISTANCE_MASK     0x002F
+#define VERIPHY_PAIR_B_DISTANCE_POS      0
+
 #define MSCC_PHY_VERIPHY_CNTL_2                  25
+#define VERIPHY_PAIR_C_DISTANCE_MASK     0x2F00
+#define VERIPHY_PAIR_C_DISTANCE_POS      8
+#define VERIPHY_PAIR_D_DISTANCE_MASK     0x002F
+#define VERIPHY_PAIR_D_DISTANCE_POS      0
 
 #define MSCC_PHY_VERIPHY_CNTL_3                  26
+#define VERIPHY_PAIR_A_STATUS_MASK       0xF000
+#define VERIPHY_PAIR_A_STATUS_POS        12
+#define VERIPHY_PAIR_B_STATUS_MASK       0x0F00
+#define VERIPHY_PAIR_B_STATUS_POS        8
+#define VERIPHY_PAIR_C_STATUS_MASK       0x00F0
+#define VERIPHY_PAIR_C_STATUS_POS        4
+#define VERIPHY_PAIR_D_STATUS_MASK       0x000F
+#define VERIPHY_PAIR_D_STATUS_POS        0
 
 /* Extended Page 2 Registers */
 #define MSCC_PHY_CU_PMD_TX_CNTL                  16
@@ -442,6 +462,107 @@ static int vsc85xx_phy_write_page(struct phy_device 
*phydev, int page)
        return __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
 }
 
+static int vsc85xx_cabdiag_request(struct phy_device *phydev,
+                                  struct phy_cabdiag_req *cfg)
+{
+       u16 reg_val;
+       u8 timeout_cnt = 0;
+       int rc;
+
+       if (cfg->pairs_bitmask < CABDIAG_PAIR_A_MASK   ||
+           (cfg->pairs_bitmask > (CABDIAG_PAIR_A_MASK |
+                                  CABDIAG_PAIR_B_MASK |
+                                  CABDIAG_PAIR_C_MASK |
+                                  CABDIAG_PAIR_D_MASK))) {
+               cfg->op_status = CD_REQ_INVALID_PAIR_MASK;
+               return 0;
+       }
+       if (cfg->timeout_cnt == 0) {
+               cfg->op_status = CD_REQ_INVALID_TIMEOUT;
+               return 0;
+       }
+
+       mutex_lock(&phydev->lock);
+       rc = phy_select_page(phydev, MSCC_PHY_PAGE_EXTENDED);
+       if (rc < 0)
+               goto out_unlock;
+
+       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_1);
+       if (reg_val & VERIPHY_TRIGGER_CNTL_MASK) {
+               cfg->op_status = CD_REQ_REJECTED_BUSY;
+               goto out_unlock;
+       }
+       /* Start Cable Diagnostics operation */
+       reg_val |= VERIPHY_TRIGGER_CNTL_MASK;
+       __phy_write(phydev, MSCC_PHY_VERIPHY_CNTL_1, reg_val);
+
+       /* Wait till VeriPHY has completed */
+       do {
+               msleep(30);
+               reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_1);
+       } while ((reg_val & VERIPHY_TRIGGER_CNTL_MASK) &&
+                (timeout_cnt++ < cfg->timeout_cnt));
+
+       if (timeout_cnt >= cfg->timeout_cnt) {
+               cfg->op_status = CD_STATUS_FAILED_TIMEOUT;
+               goto out_unlock;
+       }
+       cfg->timeout_cnt = timeout_cnt;
+
+       if (reg_val & VERIPHY_VALID_MASK) {
+               /* VeriPHY results are valid */
+               if (cfg->pairs_bitmask & CABDIAG_PAIR_A_MASK) {
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_1);
+                       cfg->pairs[CABDIAG_PAIR_A].length =
+                       (reg_val & VERIPHY_PAIR_A_DISTANCE_MASK) >>
+                       VERIPHY_PAIR_A_DISTANCE_POS;
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_3);
+                       cfg->pairs[CABDIAG_PAIR_A].status =
+                       (reg_val & VERIPHY_PAIR_A_STATUS_MASK) >>
+                       VERIPHY_PAIR_A_STATUS_POS;
+               }
+               if (cfg->pairs_bitmask & CABDIAG_PAIR_B_MASK) {
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_1);
+                       cfg->pairs[CABDIAG_PAIR_B].length =
+                       (reg_val & VERIPHY_PAIR_B_DISTANCE_MASK) >>
+                       VERIPHY_PAIR_B_DISTANCE_POS;
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_3);
+                       cfg->pairs[CABDIAG_PAIR_B].status =
+                       (reg_val & VERIPHY_PAIR_B_STATUS_MASK) >>
+                       VERIPHY_PAIR_B_STATUS_POS;
+               }
+               if (cfg->pairs_bitmask & CABDIAG_PAIR_C_MASK) {
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_2);
+                       cfg->pairs[CABDIAG_PAIR_C].length =
+                       (reg_val & VERIPHY_PAIR_C_DISTANCE_MASK) >>
+                       VERIPHY_PAIR_C_DISTANCE_POS;
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_3);
+                       cfg->pairs[CABDIAG_PAIR_C].status =
+                       (reg_val & VERIPHY_PAIR_C_STATUS_MASK) >>
+                       VERIPHY_PAIR_C_STATUS_POS;
+               }
+               if (cfg->pairs_bitmask & CABDIAG_PAIR_D_MASK) {
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_2);
+                       cfg->pairs[CABDIAG_PAIR_D].length =
+                       (reg_val & VERIPHY_PAIR_D_DISTANCE_MASK) >>
+                       VERIPHY_PAIR_D_DISTANCE_POS;
+                       reg_val = __phy_read(phydev, MSCC_PHY_VERIPHY_CNTL_3);
+                       cfg->pairs[CABDIAG_PAIR_D].status =
+                       (reg_val & VERIPHY_PAIR_D_STATUS_MASK) >>
+                       VERIPHY_PAIR_D_STATUS_POS;
+               }
+               cfg->op_status = CD_STATUS_SUCCESS;
+       } else {
+               cfg->op_status = CD_STATUS_FAILED_INVALID;
+       }
+
+out_unlock:
+       phy_restore_page(phydev, rc, rc > 0 ? 0 : rc);
+       mutex_unlock(&phydev->lock);
+
+       return rc;
+}
+
 static int vsc85xx_get_sset_count(struct phy_device *phydev)
 {
        struct vsc8531_private *priv = phydev->priv;
@@ -2343,6 +2464,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8530,
@@ -2368,6 +2490,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8531,
@@ -2393,6 +2516,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8540,
@@ -2418,6 +2542,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8541,
@@ -2443,6 +2568,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8574,
@@ -2469,6 +2595,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 },
 {
        .phy_id         = PHY_ID_VSC8584,
@@ -2493,6 +2620,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .get_sset_count = &vsc85xx_get_sset_count,
        .get_strings    = &vsc85xx_get_strings,
        .get_stats      = &vsc85xx_get_stats,
+       .request_cable_diag = &vsc85xx_cabdiag_request,
 }
 
 };
-- 
2.7.4

Reply via email to