Commit a93fca92 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

Merge patch series "Add ECC feature support to Tx and Rx FIFOs for Xilinx CAN Controller."

Marc Kleine-Budde <mkl@pengutronix.de> says:

ECC is an IP configuration option where counter registers are added in
IP for 1bit/2bit ECC errors count and reset.

Also driver reports 1bit/2bit ECC errors for FIFOs based on ECC error
interrupts.

Add xlnx,has-ecc optional property for Xilinx AXI CAN controller
to support ECC if the ECC block is enabled in the HW.

Add ethtool stats interface for getting all the ECC errors information.

There is no public documentation for it available.

Changes in v8:
- Use u64_stats_sync instead of spinlock
- Renamed stats strings: use "_" instead of "-"
- Renamed stats strings: add "_errors" trailer
- Renamed stats variables similar to stats strings

Changes in v7:
- Update with spinlock only for stats counters

Changes in v6:
- Update commit description

Changes in v5:
- Fix review comments
- Change the sequence of updates the stats
- Add get_strings and get_sset_count stats interface
- Use u64 stats helper function

Changes in v4:
- Fix DT binding check warning
- Update xlnx,has-ecc property description

Changes in v3:
- Update mailing list
- Update commit description

Changes in v2:
- Address review comments
- Add ethtool stats interface
- Update commit description

Link: https://lore.kernel.org/all/20240213-xilinx_ecc-v8-0-8d75f8b80771@pengutronix.deSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parents 24033573 e1d1698e
......@@ -49,6 +49,10 @@ properties:
resets:
maxItems: 1
xlnx,has-ecc:
$ref: /schemas/types.yaml#/definitions/flag
description: CAN TX_OL, TX_TL and RX FIFOs have ECC support(AXI CAN)
required:
- compatible
- reg
......@@ -137,6 +141,7 @@ examples:
interrupts = <GIC_SPI 59 IRQ_TYPE_EDGE_RISING>;
tx-fifo-depth = <0x40>;
rx-fifo-depth = <0x40>;
xlnx,has-ecc;
};
- |
......
......@@ -31,6 +31,7 @@
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/u64_stats_sync.h>
#define DRIVER_NAME "xilinx_can"
......@@ -58,6 +59,13 @@ enum xcan_reg {
*/
XCAN_F_BTR_OFFSET = 0x08C, /* Data Phase Bit Timing */
XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */
/* only on AXI CAN cores */
XCAN_ECC_CFG_OFFSET = 0xC8, /* ECC Configuration */
XCAN_TXTLFIFO_ECC_OFFSET = 0xCC, /* TXTL FIFO ECC error counter */
XCAN_TXOLFIFO_ECC_OFFSET = 0xD0, /* TXOL FIFO ECC error counter */
XCAN_RXFIFO_ECC_OFFSET = 0xD4, /* RX FIFO ECC error counter */
XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */
XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */
XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */
......@@ -124,6 +132,18 @@ enum xcan_reg {
#define XCAN_IXR_TXFLL_MASK 0x00000004 /* Tx FIFO Full intr */
#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */
#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */
#define XCAN_IXR_E2BERX_MASK BIT(23) /* RX FIFO two bit ECC error */
#define XCAN_IXR_E1BERX_MASK BIT(22) /* RX FIFO one bit ECC error */
#define XCAN_IXR_E2BETXOL_MASK BIT(21) /* TXOL FIFO two bit ECC error */
#define XCAN_IXR_E1BETXOL_MASK BIT(20) /* TXOL FIFO One bit ECC error */
#define XCAN_IXR_E2BETXTL_MASK BIT(19) /* TXTL FIFO Two bit ECC error */
#define XCAN_IXR_E1BETXTL_MASK BIT(18) /* TXTL FIFO One bit ECC error */
#define XCAN_IXR_ECC_MASK (XCAN_IXR_E2BERX_MASK | \
XCAN_IXR_E1BERX_MASK | \
XCAN_IXR_E2BETXOL_MASK | \
XCAN_IXR_E1BETXOL_MASK | \
XCAN_IXR_E2BETXTL_MASK | \
XCAN_IXR_E1BETXTL_MASK)
#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */
#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */
#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */
......@@ -137,6 +157,11 @@ enum xcan_reg {
#define XCAN_2_FSR_RI_MASK 0x0000003F /* RX Read Index */
#define XCAN_DLCR_EDL_MASK 0x08000000 /* EDL Mask in DLC */
#define XCAN_DLCR_BRS_MASK 0x04000000 /* BRS Mask in DLC */
#define XCAN_ECC_CFG_REECRX_MASK BIT(2) /* Reset RX FIFO ECC error counters */
#define XCAN_ECC_CFG_REECTXOL_MASK BIT(1) /* Reset TXOL FIFO ECC error counters */
#define XCAN_ECC_CFG_REECTXTL_MASK BIT(0) /* Reset TXTL FIFO ECC error counters */
#define XCAN_ECC_1BIT_CNT_MASK GENMASK(15, 0) /* FIFO ECC 1bit count mask */
#define XCAN_ECC_2BIT_CNT_MASK GENMASK(31, 16) /* FIFO ECC 2bit count mask */
/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
#define XCAN_BRPR_TDC_ENABLE BIT(16) /* Transmitter Delay Compensation (TDC) Enable */
......@@ -202,6 +227,14 @@ struct xcan_devtype_data {
* @devtype: Device type specific constants
* @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
* @ecc_txol_1_bit_errors: TXOLFIFO 1bit ECC count
* @ecc_txtl_2_bit_errors: TXTLFIFO 2bit ECC count
* @ecc_txtl_1_bit_errors: TXTLFIFO 1bit ECC count
*/
struct xcan_priv {
struct can_priv can;
......@@ -221,6 +254,14 @@ struct xcan_priv {
struct xcan_devtype_data devtype;
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;
u64_stats_t ecc_txol_1_bit_errors;
u64_stats_t ecc_txtl_2_bit_errors;
u64_stats_t ecc_txtl_1_bit_errors;
};
/* CAN Bittiming constants as per Xilinx CAN specs */
......@@ -308,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
......@@ -523,6 +582,9 @@ static int xcan_chip_start(struct net_device *ndev)
XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
XCAN_IXR_ARBLST_MASK | xcan_rx_int_mask(priv);
if (priv->ecc_enable)
ier |= XCAN_IXR_ECC_MASK;
if (priv->devtype.flags & XCAN_FLAG_RXMNF)
ier |= XCAN_IXR_RXMNF_MASK;
......@@ -1127,6 +1189,54 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
priv->can.can_stats.bus_error++;
}
if (priv->ecc_enable && isr & XCAN_IXR_ECC_MASK) {
u32 reg_rx_ecc, reg_txol_ecc, reg_txtl_ecc;
reg_rx_ecc = priv->read_reg(priv, XCAN_RXFIFO_ECC_OFFSET);
reg_txol_ecc = priv->read_reg(priv, XCAN_TXOLFIFO_ECC_OFFSET);
reg_txtl_ecc = priv->read_reg(priv, XCAN_TXTLFIFO_ECC_OFFSET);
/* The counter reaches its maximum at 0xffff and does not overflow.
* Accept the small race window between reading and resetting ECC counters.
*/
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));
}
if (isr & XCAN_IXR_E1BERX_MASK) {
u64_stats_add(&priv->ecc_rx_1_bit_errors,
FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_rx_ecc));
}
if (isr & XCAN_IXR_E2BETXOL_MASK) {
u64_stats_add(&priv->ecc_txol_2_bit_errors,
FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_txol_ecc));
}
if (isr & XCAN_IXR_E1BETXOL_MASK) {
u64_stats_add(&priv->ecc_txol_1_bit_errors,
FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txol_ecc));
}
if (isr & XCAN_IXR_E2BETXTL_MASK) {
u64_stats_add(&priv->ecc_txtl_2_bit_errors,
FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_txtl_ecc));
}
if (isr & XCAN_IXR_E1BETXTL_MASK) {
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) {
struct can_frame *skb_cf;
struct sk_buff *skb = alloc_can_err_skb(ndev, &skb_cf);
......@@ -1354,8 +1464,8 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct xcan_priv *priv = netdev_priv(ndev);
u32 isr_errors, mask;
u32 isr, ier;
u32 isr_errors;
u32 rx_int_mask = xcan_rx_int_mask(priv);
/* Get the interrupt status from Xilinx CAN */
......@@ -1374,10 +1484,15 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
if (isr & XCAN_IXR_TXOK_MASK)
xcan_tx_interrupt(ndev, isr);
mask = XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK |
XCAN_IXR_RXMNF_MASK;
if (priv->ecc_enable)
mask |= XCAN_IXR_ECC_MASK;
/* Check for the type of error interrupt and Processing it */
isr_errors = isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK |
XCAN_IXR_RXMNF_MASK);
isr_errors = isr & mask;
if (isr_errors) {
priv->write_reg(priv, XCAN_ICR_OFFSET, isr_errors);
xcan_err_interrupt(ndev, isr);
......@@ -1546,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,
......@@ -1555,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,
};
/**
......@@ -1793,6 +1948,7 @@ static int xcan_probe(struct platform_device *pdev)
return -ENOMEM;
priv = netdev_priv(ndev);
priv->ecc_enable = of_property_read_bool(pdev->dev.of_node, "xlnx,has-ecc");
priv->dev = &pdev->dev;
priv->can.bittiming_const = devtype->bittiming_const;
priv->can.do_set_mode = xcan_do_set_mode;
......@@ -1909,6 +2065,11 @@ static int xcan_probe(struct platform_device *pdev)
priv->reg_base, ndev->irq, priv->can.clock.freq,
hw_tx_max, priv->tx_max);
if (priv->ecc_enable) {
/* Reset FIFO ECC counters */
priv->write_reg(priv, XCAN_ECC_CFG_OFFSET, XCAN_ECC_CFG_REECRX_MASK |
XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK);
}
return 0;
err_disableclks:
......
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