Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: qcom: Add PHY counter support

Add PHY counter functionality to the shared library. The implementation
is identical for the current QCA807X and QCA808X PHYs.

The PHY counter can be configured to perform CRC checking for both received
and transmitted packets. Additionally, the packet counter can be set to
automatically clear after it is read.

The PHY counter includes 32-bit packet counters for both RX (received) and
TX (transmitted) packets, as well as 16-bit counters for recording CRC
error packets for both RX and TX.

Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250715-qcom_phy_counter-v3-1-8b0e460a527b@quicinc.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Luo Jie and committed by
Jakub Kicinski
22bf4bd8 a93f38eb

+98
+75
drivers/net/phy/qcom/qcom-phy-lib.c
··· 699 699 return 0; 700 700 } 701 701 EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set); 702 + 703 + /* Enable CRC checking for both received and transmitted frames to ensure 704 + * accurate counter recording. The hardware supports a 32-bit counter, 705 + * configure the counter to clear after it is read to facilitate the 706 + * implementation of a 64-bit software counter 707 + */ 708 + int qcom_phy_counter_config(struct phy_device *phydev) 709 + { 710 + return phy_set_bits_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_CTRL, 711 + QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN | 712 + QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN); 713 + } 714 + EXPORT_SYMBOL_GPL(qcom_phy_counter_config); 715 + 716 + int qcom_phy_update_stats(struct phy_device *phydev, 717 + struct qcom_phy_hw_stats *hw_stats) 718 + { 719 + int ret; 720 + u32 cnt; 721 + 722 + /* PHY 32-bit counter for RX packets. */ 723 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_15_0); 724 + if (ret < 0) 725 + return ret; 726 + 727 + cnt = ret; 728 + 729 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_31_16); 730 + if (ret < 0) 731 + return ret; 732 + 733 + cnt |= ret << 16; 734 + hw_stats->rx_pkts += cnt; 735 + 736 + /* PHY 16-bit counter for RX CRC error packets. */ 737 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_ERR_PKT); 738 + if (ret < 0) 739 + return ret; 740 + 741 + hw_stats->rx_err_pkts += ret; 742 + 743 + /* PHY 32-bit counter for TX packets. */ 744 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_15_0); 745 + if (ret < 0) 746 + return ret; 747 + 748 + cnt = ret; 749 + 750 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_31_16); 751 + if (ret < 0) 752 + return ret; 753 + 754 + cnt |= ret << 16; 755 + hw_stats->tx_pkts += cnt; 756 + 757 + /* PHY 16-bit counter for TX CRC error packets. */ 758 + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_ERR_PKT); 759 + if (ret < 0) 760 + return ret; 761 + 762 + hw_stats->tx_err_pkts += ret; 763 + 764 + return 0; 765 + } 766 + EXPORT_SYMBOL_GPL(qcom_phy_update_stats); 767 + 768 + void qcom_phy_get_stats(struct ethtool_phy_stats *stats, 769 + struct qcom_phy_hw_stats hw_stats) 770 + { 771 + stats->tx_packets = hw_stats.tx_pkts; 772 + stats->tx_errors = hw_stats.tx_err_pkts; 773 + stats->rx_packets = hw_stats.rx_pkts; 774 + stats->rx_errors = hw_stats.rx_err_pkts; 775 + } 776 + EXPORT_SYMBOL_GPL(qcom_phy_get_stats);
+23
drivers/net/phy/qcom/qcom.h
··· 195 195 #define AT803X_MIN_DOWNSHIFT 2 196 196 #define AT803X_MAX_DOWNSHIFT 9 197 197 198 + #define QCA808X_MMD7_CNT_CTRL 0x8029 199 + #define QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN BIT(1) 200 + #define QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN BIT(0) 201 + 202 + #define QCA808X_MMD7_CNT_RX_PKT_31_16 0x802a 203 + #define QCA808X_MMD7_CNT_RX_PKT_15_0 0x802b 204 + #define QCA808X_MMD7_CNT_RX_ERR_PKT 0x802c 205 + #define QCA808X_MMD7_CNT_TX_PKT_31_16 0x802d 206 + #define QCA808X_MMD7_CNT_TX_PKT_15_0 0x802e 207 + #define QCA808X_MMD7_CNT_TX_ERR_PKT 0x802f 208 + 198 209 enum stat_access_type { 199 210 PHY, 200 211 MMD ··· 221 210 struct at803x_ss_mask { 222 211 u16 speed_mask; 223 212 u8 speed_shift; 213 + }; 214 + 215 + struct qcom_phy_hw_stats { 216 + u64 rx_pkts; 217 + u64 rx_err_pkts; 218 + u64 tx_pkts; 219 + u64 tx_err_pkts; 224 220 }; 225 221 226 222 int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); ··· 264 246 int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, 265 247 unsigned long *delay_on, 266 248 unsigned long *delay_off); 249 + int qcom_phy_counter_config(struct phy_device *phydev); 250 + int qcom_phy_update_stats(struct phy_device *phydev, 251 + struct qcom_phy_hw_stats *hw_stats); 252 + void qcom_phy_get_stats(struct ethtool_phy_stats *stats, 253 + struct qcom_phy_hw_stats hw_stats);