net: phy: phy-c45: add SQI and SQI+ support for OATC14 10Base-T1S PHYs

Add support for reading Signal Quality Indicator (SQI) and enhanced SQI+
from OATC14 10Base-T1S PHYs.

- Introduce MDIO register definitions for DCQ_SQI and DCQ_SQIPLUS.
- Add `genphy_c45_oatc14_get_sqi_max()` to return the maximum supported
  SQI/SQI+ level.
- Add `genphy_c45_oatc14_get_sqi()` to return the current SQI or SQI+
  value.
- Update `include/linux/phy.h` to expose the new APIs.

SQI+ capability is read from the Advanced Diagnostic Features Capability
register (ADFCAP). If SQI+ is supported, the driver calculates the value
from the MSBs of the DCQ_SQIPLUS register; otherwise, it falls back to
basic SQI (0-7 levels). This enables ethtool to report the SQI value for
OATC14 10Base-T1S PHYs.

Open Alliance TC14 10BASE-T1S Advanced Diagnostic PHY Features
Specification ref:
https://opensig.org/wp-content/uploads/2025/06/OPEN_Alliance_10BASE-T1S_Advanced_PHY_features_for-automotive_Ethernet_V2.1b.pdf

Signed-off-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20251201032346.6699-2-parthiban.veerasooran@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
pull/1354/merge
Parthiban Veerasooran 2025-12-01 08:53:45 +05:30 committed by Jakub Kicinski
parent 8d537e333e
commit 5e1bf5ae5e
3 changed files with 179 additions and 0 deletions

View File

@ -56,6 +56,8 @@
/* Advanced Diagnostic Features Capability Register*/
#define MDIO_OATC14_ADFCAP 0xcc00
#define OATC14_ADFCAP_HDD_CAPABILITY GENMASK(10, 8)
#define OATC14_ADFCAP_SQIPLUS_CAPABILITY GENMASK(4, 1)
#define OATC14_ADFCAP_SQI_CAPABILITY BIT(0)
/* Harness Defect Detection Register */
#define MDIO_OATC14_HDD 0xcc01
@ -65,6 +67,17 @@
#define OATC14_HDD_VALID BIT(2)
#define OATC14_HDD_SHORT_OPEN_STATUS GENMASK(1, 0)
/* Dynamic Channel Quality SQI Register */
#define MDIO_OATC14_DCQ_SQI 0xcc03
#define OATC14_DCQ_SQI_VALUE GENMASK(2, 0)
/* Dynamic Channel Quality SQI Plus Register */
#define MDIO_OATC14_DCQ_SQIPLUS 0xcc04
#define OATC14_DCQ_SQIPLUS_VALUE GENMASK(7, 0)
/* SQI is supported using 3 bits means 8 levels (0-7) */
#define OATC14_SQI_MAX_LEVEL 7
/* Bus Short/Open Status:
* 0 0 - no fault; everything is ok. (Default)
* 0 1 - detected as an open or missing termination(s)

View File

@ -1695,3 +1695,140 @@ int genphy_c45_oatc14_cable_test_start(struct phy_device *phydev)
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 (38), corresponding to 8256 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 07 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);

View File

@ -530,6 +530,30 @@ struct phy_c45_device_ids {
struct macsec_context;
struct macsec_ops;
/**
* struct phy_oatc14_sqi_capability - SQI capability information for OATC14
* 10Base-T1S PHY
* @updated: Indicates whether the SQI capability fields have been updated.
* @sqi_max: Maximum supported Signal Quality Indicator (SQI) level reported by
* the PHY.
* @sqiplus_bits: Bits for SQI+ levels supported by the PHY.
* 0 - SQI+ is not supported
* 3 - SQI+ is supported, using 3 bits (8 levels)
* 4 - SQI+ is supported, using 4 bits (16 levels)
* 5 - SQI+ is supported, using 5 bits (32 levels)
* 6 - SQI+ is supported, using 6 bits (64 levels)
* 7 - SQI+ is supported, using 7 bits (128 levels)
* 8 - SQI+ is supported, using 8 bits (256 levels)
*
* This structure is used by the OATC14 10Base-T1S PHY driver to store the SQI
* and SQI+ capability information retrieved from the PHY.
*/
struct phy_oatc14_sqi_capability {
bool updated;
int sqi_max;
u8 sqiplus_bits;
};
/**
* struct phy_device - An instance of a PHY
*
@ -626,6 +650,7 @@ struct macsec_ops;
* @link_down_events: Number of times link was lost
* @shared: Pointer to private data shared by phys in one package
* @priv: Pointer to driver private data
* @oatc14_sqi_capability: SQI capability information for OATC14 10Base-T1S PHY
*
* interrupts currently only supports enabled or disabled,
* but could be changed in the future to support enabling
@ -772,6 +797,8 @@ struct phy_device {
/* MACsec management functions */
const struct macsec_ops *macsec_ops;
#endif
struct phy_oatc14_sqi_capability oatc14_sqi_capability;
};
/* Generic phy_device::dev_flags */
@ -2257,6 +2284,8 @@ int genphy_c45_an_config_eee_aneg(struct phy_device *phydev);
int genphy_c45_oatc14_cable_test_start(struct phy_device *phydev);
int genphy_c45_oatc14_cable_test_get_status(struct phy_device *phydev,
bool *finished);
int genphy_c45_oatc14_get_sqi_max(struct phy_device *phydev);
int genphy_c45_oatc14_get_sqi(struct phy_device *phydev);
/* The gen10g_* functions are the old Clause 45 stub */
int gen10g_config_aneg(struct phy_device *phydev);