summaryrefslogtreecommitdiff
path: root/drivers/net/phy/phy-c45.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/phy-c45.c')
-rw-r--r--drivers/net/phy/phy-c45.c287
1 files changed, 273 insertions, 14 deletions
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 61670be0f095..d48aa7231b37 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -7,6 +7,7 @@
#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/ethtool_netlink.h>
#include "mdio-open-alliance.h"
#include "phylib-internal.h"
@@ -147,12 +148,12 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev)
ctrl2 |= MDIO_PMA_CTRL2_1000BT;
break;
case SPEED_2500:
- ctrl1 |= MDIO_CTRL1_SPEED2_5G;
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED2_5G;
/* Assume 2.5Gbase-T */
ctrl2 |= MDIO_PMA_CTRL2_2_5GBT;
break;
case SPEED_5000:
- ctrl1 |= MDIO_CTRL1_SPEED5G;
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED5G;
/* Assume 5Gbase-T */
ctrl2 |= MDIO_PMA_CTRL2_5GBT;
break;
@@ -485,8 +486,8 @@ static int genphy_c45_baset1_read_lpa(struct phy_device *phydev)
mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, 0);
mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, 0);
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
return 0;
}
@@ -498,8 +499,8 @@ static int genphy_c45_baset1_read_lpa(struct phy_device *phydev)
return val;
mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, val);
- phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP ? 1 : 0;
- phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM ? 1 : 0;
+ phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP;
+ phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_M);
if (val < 0)
@@ -536,8 +537,8 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
phydev->lp_advertising);
mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, 0);
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
return 0;
}
@@ -551,8 +552,8 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
return val;
mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, val);
- phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
- phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
+ phydev->pause = val & LPA_PAUSE_CAP;
+ phydev->asym_pause = val & LPA_PAUSE_ASYM;
/* Read the link partner's 10G advertisement */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
@@ -617,10 +618,10 @@ int genphy_c45_read_pma(struct phy_device *phydev)
case MDIO_PMA_CTRL1_SPEED1000:
phydev->speed = SPEED_1000;
break;
- case MDIO_CTRL1_SPEED2_5G:
+ case MDIO_PMA_CTRL1_SPEED2_5G:
phydev->speed = SPEED_2500;
break;
- case MDIO_CTRL1_SPEED5G:
+ case MDIO_PMA_CTRL1_SPEED5G:
phydev->speed = SPEED_5000;
break;
case MDIO_CTRL1_SPEED10G:
@@ -1171,8 +1172,8 @@ int genphy_c45_read_status(struct phy_device *phydev)
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_read_lpa(phydev);
@@ -1573,3 +1574,261 @@ int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
return ret;
}
EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);
+
+/**
+ * oatc14_cable_test_get_result_code - Convert hardware cable test status to
+ * ethtool result code.
+ * @status: The hardware-reported cable test status
+ *
+ * This helper function maps the OATC14 HDD cable test status to the
+ * corresponding ethtool cable test result code. It provides a translation
+ * between the device-specific status values and the standardized ethtool
+ * result codes.
+ *
+ * Return:
+ * * ETHTOOL_A_CABLE_RESULT_CODE_OK - Cable is OK
+ * * ETHTOOL_A_CABLE_RESULT_CODE_OPEN - Open circuit detected
+ * * ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT - Short circuit detected
+ * * ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC - Status not detectable or invalid
+ */
+static int oatc14_cable_test_get_result_code(enum oatc14_hdd_status status)
+{
+ switch (status) {
+ case OATC14_HDD_STATUS_CABLE_OK:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+ case OATC14_HDD_STATUS_OPEN:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+ case OATC14_HDD_STATUS_SHORT:
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+ case OATC14_HDD_STATUS_NOT_DETECTABLE:
+ default:
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+ }
+}
+
+/**
+ * genphy_c45_oatc14_cable_test_get_status - Get status of OATC14 10Base-T1S
+ * PHY cable test.
+ * @phydev: pointer to the PHY device structure
+ * @finished: pointer to a boolean set true if the test is complete
+ *
+ * Retrieves the current status of the OATC14 10Base-T1S PHY cable test.
+ * This function reads the OATC14 HDD register to determine whether the test
+ * results are valid and whether the test has finished.
+ *
+ * If the test is complete, the function reports the cable test result via
+ * the ethtool cable test interface using ethnl_cable_test_result(), and then
+ * clears the test control bit in the PHY register to reset the test state.
+ *
+ * Return: 0 on success, or a negative error code on failure (e.g. register
+ * read/write error).
+ */
+int genphy_c45_oatc14_cable_test_get_status(struct phy_device *phydev,
+ bool *finished)
+{
+ int ret;
+ u8 sts;
+
+ *finished = false;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & OATC14_HDD_VALID))
+ return 0;
+
+ *finished = true;
+
+ sts = FIELD_GET(OATC14_HDD_SHORT_OPEN_STATUS, ret);
+
+ ret = ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
+ oatc14_cable_test_get_result_code(sts));
+ if (ret)
+ return ret;
+
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
+ MDIO_OATC14_HDD, OATC14_HDD_CONTROL);
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_cable_test_get_status);
+
+/**
+ * genphy_c45_oatc14_cable_test_start - Start a cable test on an OATC14
+ * 10Base-T1S PHY.
+ * @phydev: Pointer to the PHY device structure
+ *
+ * This function initiates a cable diagnostic test on a Clause 45 OATC14
+ * 10Base-T1S capable PHY device. It first reads the PHY’s advanced diagnostic
+ * capability register to check if High Definition Diagnostics (HDD) mode is
+ * supported. If the PHY does not report HDD capability, cable testing is not
+ * supported and the function returns -EOPNOTSUPP.
+ *
+ * For PHYs that support HDD, the function sets the appropriate control bits in
+ * the OATC14_HDD register to enable and start the cable diagnostic test.
+ *
+ * Return:
+ * * 0 on success
+ * * -EOPNOTSUPP if the PHY does not support HDD capability
+ * * A negative error code on I/O or register access failures
+ */
+int genphy_c45_oatc14_cable_test_start(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_ADFCAP);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & OATC14_ADFCAP_HDD_CAPABILITY))
+ return -EOPNOTSUPP;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD,
+ OATC14_HDD_CONTROL);
+ if (ret)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD);
+ if (ret < 0)
+ return ret;
+
+ return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD,
+ OATC14_HDD_START_CONTROL);
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_cable_test_start);
+
+/**
+ * oatc14_update_sqi_capability - Read and update OATC14 10Base-T1S PHY SQI/SQI+
+ * capability
+ * @phydev: Pointer to the PHY device structure
+ *
+ * This helper reads the OATC14 ADFCAP capability register to determine whether
+ * the PHY supports SQI or SQI+ reporting.
+ *
+ * SQI+ capability is detected first. The SQI+ field indicates the number of
+ * valid MSBs (3–8), corresponding to 8–256 SQI+ levels. When present, the
+ * function stores the number of SQI+ bits and computes the maximum SQI+ value
+ * as (2^bits - 1).
+ *
+ * If SQI+ is not supported, the function checks for basic SQI capability,
+ * which provides 0–7 SQI levels.
+ *
+ * On success, the capability information is stored in
+ * @phydev->oatc14_sqi_capability and marked as updated.
+ *
+ * Return:
+ * * 0 - capability successfully read and stored
+ * * -EOPNOTSUPP - SQI/SQI+ not supported by this PHY
+ * * Negative errno on read failure
+ */
+static int oatc14_update_sqi_capability(struct phy_device *phydev)
+{
+ u8 bits;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_ADFCAP);
+ if (ret < 0)
+ return ret;
+
+ /* Check for SQI+ capability
+ * 0 - SQI+ is not supported
+ * (3-8) bits for (8-256) SQI+ levels supported
+ */
+ bits = FIELD_GET(OATC14_ADFCAP_SQIPLUS_CAPABILITY, ret);
+ if (bits) {
+ phydev->oatc14_sqi_capability.sqiplus_bits = bits;
+ /* Max sqi+ level supported: (2 ^ bits) - 1 */
+ phydev->oatc14_sqi_capability.sqi_max = BIT(bits) - 1;
+ goto update_done;
+ }
+
+ /* Check for SQI capability
+ * 0 - SQI is not supported
+ * 1 - SQI is supported (0-7 levels)
+ */
+ if (ret & OATC14_ADFCAP_SQI_CAPABILITY) {
+ phydev->oatc14_sqi_capability.sqi_max = OATC14_SQI_MAX_LEVEL;
+ goto update_done;
+ }
+
+ return -EOPNOTSUPP;
+
+update_done:
+ phydev->oatc14_sqi_capability.updated = true;
+ return 0;
+}
+
+/**
+ * genphy_c45_oatc14_get_sqi_max - Get maximum supported SQI or SQI+ level of
+ * OATC14 10Base-T1S PHY
+ * @phydev: pointer to the PHY device structure
+ *
+ * This function returns the maximum supported Signal Quality Indicator (SQI) or
+ * SQI+ level. The SQI capability is updated on first invocation if it has not
+ * already been updated.
+ *
+ * Return:
+ * * Maximum SQI/SQI+ level supported
+ * * Negative errno on capability read failure
+ */
+int genphy_c45_oatc14_get_sqi_max(struct phy_device *phydev)
+{
+ int ret;
+
+ if (!phydev->oatc14_sqi_capability.updated) {
+ ret = oatc14_update_sqi_capability(phydev);
+ if (ret)
+ return ret;
+ }
+
+ return phydev->oatc14_sqi_capability.sqi_max;
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_get_sqi_max);
+
+/**
+ * genphy_c45_oatc14_get_sqi - Get Signal Quality Indicator (SQI) from an OATC14
+ * 10Base-T1S PHY
+ * @phydev: pointer to the PHY device structure
+ *
+ * This function reads the SQI+ or SQI value from an OATC14-compatible
+ * 10Base-T1S PHY. If SQI+ capability is supported, the function returns the
+ * extended SQI+ value; otherwise, it returns the basic SQI value. The SQI
+ * capability is updated on first invocation if it has not already been updated.
+ *
+ * Return:
+ * * SQI/SQI+ value on success
+ * * Negative errno on read failure
+ */
+int genphy_c45_oatc14_get_sqi(struct phy_device *phydev)
+{
+ u8 shift;
+ int ret;
+
+ if (!phydev->oatc14_sqi_capability.updated) {
+ ret = oatc14_update_sqi_capability(phydev);
+ if (ret)
+ return ret;
+ }
+
+ /* Calculate and return SQI+ value if supported */
+ if (phydev->oatc14_sqi_capability.sqiplus_bits) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ MDIO_OATC14_DCQ_SQIPLUS);
+ if (ret < 0)
+ return ret;
+
+ /* SQI+ uses N MSBs out of 8 bits, left-aligned with padding 1's
+ * Calculate the right-shift needed to isolate the N bits.
+ */
+ shift = 8 - phydev->oatc14_sqi_capability.sqiplus_bits;
+
+ return (ret & OATC14_DCQ_SQIPLUS_VALUE) >> shift;
+ }
+
+ /* Read and return SQI value if SQI+ capability is not supported */
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_DCQ_SQI);
+ if (ret < 0)
+ return ret;
+
+ return ret & OATC14_DCQ_SQI_VALUE;
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_get_sqi);