Commit e1d1698e authored by Srinivas Goud's avatar Srinivas Goud Committed by Marc Kleine-Budde

can: xilinx_can: Add ethtool stats interface for ECC errors

Add ethtool stats interface for reading FIFO 1bit/2bit ECC errors
information.
Signed-off-by: default avatarSrinivas Goud <srinivas.goud@amd.com>
Link: https://lore.kernel.org/all/20240213-xilinx_ecc-v8-3-8d75f8b80771@pengutronix.deSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 8e6fbf7f
...@@ -228,6 +228,7 @@ struct xcan_devtype_data { ...@@ -228,6 +228,7 @@ struct xcan_devtype_data {
* @transceiver: Optional pointer to associated CAN transceiver * @transceiver: Optional pointer to associated CAN transceiver
* @rstc: Pointer to reset control * @rstc: Pointer to reset control
* @ecc_enable: ECC enable flag * @ecc_enable: ECC enable flag
* @syncp: synchronization for ECC error stats
* @ecc_rx_2_bit_errors: RXFIFO 2bit ECC count * @ecc_rx_2_bit_errors: RXFIFO 2bit ECC count
* @ecc_rx_1_bit_errors: RXFIFO 1bit ECC count * @ecc_rx_1_bit_errors: RXFIFO 1bit ECC count
* @ecc_txol_2_bit_errors: TXOLFIFO 2bit ECC count * @ecc_txol_2_bit_errors: TXOLFIFO 2bit ECC count
...@@ -254,6 +255,7 @@ struct xcan_priv { ...@@ -254,6 +255,7 @@ struct xcan_priv {
struct phy *transceiver; struct phy *transceiver;
struct reset_control *rstc; struct reset_control *rstc;
bool ecc_enable; bool ecc_enable;
struct u64_stats_sync syncp;
u64_stats_t ecc_rx_2_bit_errors; u64_stats_t ecc_rx_2_bit_errors;
u64_stats_t ecc_rx_1_bit_errors; u64_stats_t ecc_rx_1_bit_errors;
u64_stats_t ecc_txol_2_bit_errors; u64_stats_t ecc_txol_2_bit_errors;
...@@ -347,6 +349,24 @@ static const struct can_tdc_const xcan_tdc_const_canfd2 = { ...@@ -347,6 +349,24 @@ static const struct can_tdc_const xcan_tdc_const_canfd2 = {
.tdcf_max = 0, .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 * xcan_write_reg_le - Write a value to the device register little endian
* @priv: Driver private data structure * @priv: Driver private data structure
...@@ -1182,6 +1202,8 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr) ...@@ -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 | priv->write_reg(priv, XCAN_ECC_CFG_OFFSET, XCAN_ECC_CFG_REECRX_MASK |
XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK); XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK);
u64_stats_update_begin(&priv->syncp);
if (isr & XCAN_IXR_E2BERX_MASK) { if (isr & XCAN_IXR_E2BERX_MASK) {
u64_stats_add(&priv->ecc_rx_2_bit_errors, u64_stats_add(&priv->ecc_rx_2_bit_errors,
FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_rx_ecc)); 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) ...@@ -1211,6 +1233,8 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
u64_stats_add(&priv->ecc_txtl_1_bit_errors, u64_stats_add(&priv->ecc_txtl_1_bit_errors,
FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txtl_ecc)); FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txtl_ecc));
} }
u64_stats_update_end(&priv->syncp);
} }
if (cf.can_id) { if (cf.can_id) {
...@@ -1637,6 +1661,43 @@ static int xcan_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv) ...@@ -1637,6 +1661,43 @@ static int xcan_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv)
return 0; 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 = { static const struct net_device_ops xcan_netdev_ops = {
.ndo_open = xcan_open, .ndo_open = xcan_open,
.ndo_stop = xcan_close, .ndo_stop = xcan_close,
...@@ -1646,6 +1707,9 @@ static const struct net_device_ops xcan_netdev_ops = { ...@@ -1646,6 +1707,9 @@ static const struct net_device_ops xcan_netdev_ops = {
static const struct ethtool_ops xcan_ethtool_ops = { static const struct ethtool_ops xcan_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info, .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,
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment