can: xilinx_can: Add ethtool stats interface for ECC errors
authorSrinivas Goud <srinivas.goud@amd.com>
Tue, 13 Feb 2024 10:36:45 +0000 (11:36 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Fri, 16 Feb 2024 13:18:31 +0000 (14:18 +0100)
Add ethtool stats interface for reading FIFO 1bit/2bit ECC errors
information.

Signed-off-by: Srinivas Goud <srinivas.goud@amd.com>
Link: https://lore.kernel.org/all/20240213-xilinx_ecc-v8-3-8d75f8b80771@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/xilinx_can.c

index af2af4eade3cdb25f27270629d6dde900fcc4fc3..fae0120473f895706ced8c2956937f24fb72d2e5 100644 (file)
@@ -228,6 +228,7 @@ struct xcan_devtype_data {
  * @transceiver:               Optional pointer to associated CAN transceiver
  * @rstc:                      Pointer to reset control
  * @ecc_enable:                        ECC enable flag
+ * @syncp:                     synchronization for ECC error stats
  * @ecc_rx_2_bit_errors:       RXFIFO 2bit ECC count
  * @ecc_rx_1_bit_errors:       RXFIFO 1bit ECC count
  * @ecc_txol_2_bit_errors:     TXOLFIFO 2bit ECC count
@@ -254,6 +255,7 @@ struct xcan_priv {
        struct phy *transceiver;
        struct reset_control *rstc;
        bool ecc_enable;
+       struct u64_stats_sync syncp;
        u64_stats_t ecc_rx_2_bit_errors;
        u64_stats_t ecc_rx_1_bit_errors;
        u64_stats_t ecc_txol_2_bit_errors;
@@ -347,6 +349,24 @@ static const struct can_tdc_const xcan_tdc_const_canfd2 = {
        .tdcf_max = 0,
 };
 
+enum xcan_stats_type {
+       XCAN_ECC_RX_2_BIT_ERRORS,
+       XCAN_ECC_RX_1_BIT_ERRORS,
+       XCAN_ECC_TXOL_2_BIT_ERRORS,
+       XCAN_ECC_TXOL_1_BIT_ERRORS,
+       XCAN_ECC_TXTL_2_BIT_ERRORS,
+       XCAN_ECC_TXTL_1_BIT_ERRORS,
+};
+
+static const char xcan_priv_flags_strings[][ETH_GSTRING_LEN] = {
+       [XCAN_ECC_RX_2_BIT_ERRORS] = "ecc_rx_2_bit_errors",
+       [XCAN_ECC_RX_1_BIT_ERRORS] = "ecc_rx_1_bit_errors",
+       [XCAN_ECC_TXOL_2_BIT_ERRORS] = "ecc_txol_2_bit_errors",
+       [XCAN_ECC_TXOL_1_BIT_ERRORS] = "ecc_txol_1_bit_errors",
+       [XCAN_ECC_TXTL_2_BIT_ERRORS] = "ecc_txtl_2_bit_errors",
+       [XCAN_ECC_TXTL_1_BIT_ERRORS] = "ecc_txtl_1_bit_errors",
+};
+
 /**
  * xcan_write_reg_le - Write a value to the device register little endian
  * @priv:      Driver private data structure
@@ -1182,6 +1202,8 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
                priv->write_reg(priv, XCAN_ECC_CFG_OFFSET, XCAN_ECC_CFG_REECRX_MASK |
                                XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK);
 
+               u64_stats_update_begin(&priv->syncp);
+
                if (isr & XCAN_IXR_E2BERX_MASK) {
                        u64_stats_add(&priv->ecc_rx_2_bit_errors,
                                      FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_rx_ecc));
@@ -1211,6 +1233,8 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
                        u64_stats_add(&priv->ecc_txtl_1_bit_errors,
                                      FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txtl_ecc));
                }
+
+               u64_stats_update_end(&priv->syncp);
        }
 
        if (cf.can_id) {
@@ -1637,6 +1661,43 @@ static int xcan_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv)
        return 0;
 }
 
+static void xcan_get_strings(struct net_device *ndev, u32 stringset, u8 *buf)
+{
+       switch (stringset) {
+       case ETH_SS_STATS:
+               memcpy(buf, &xcan_priv_flags_strings,
+                      sizeof(xcan_priv_flags_strings));
+       }
+}
+
+static int xcan_get_sset_count(struct net_device *netdev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(xcan_priv_flags_strings);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void xcan_get_ethtool_stats(struct net_device *ndev,
+                                  struct ethtool_stats *stats, u64 *data)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       unsigned int start;
+
+       do {
+               start = u64_stats_fetch_begin(&priv->syncp);
+
+               data[XCAN_ECC_RX_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_rx_2_bit_errors);
+               data[XCAN_ECC_RX_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_rx_1_bit_errors);
+               data[XCAN_ECC_TXOL_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_txol_2_bit_errors);
+               data[XCAN_ECC_TXOL_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_txol_1_bit_errors);
+               data[XCAN_ECC_TXTL_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_txtl_2_bit_errors);
+               data[XCAN_ECC_TXTL_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_txtl_1_bit_errors);
+       } while (u64_stats_fetch_retry(&priv->syncp, start));
+}
+
 static const struct net_device_ops xcan_netdev_ops = {
        .ndo_open       = xcan_open,
        .ndo_stop       = xcan_close,
@@ -1646,6 +1707,9 @@ static const struct net_device_ops xcan_netdev_ops = {
 
 static const struct ethtool_ops xcan_ethtool_ops = {
        .get_ts_info = ethtool_op_get_ts_info,
+       .get_strings = xcan_get_strings,
+       .get_sset_count = xcan_get_sset_count,
+       .get_ethtool_stats = xcan_get_ethtool_stats,
 };
 
 /**