Skip to content

Commit

Permalink
Merge patch series "Add ECC feature support to Tx and Rx FIFOs for Xi…
Browse files Browse the repository at this point in the history
…linx 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.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
  • Loading branch information
marckleinebudde committed Feb 16, 2024
2 parents 2403357 + e1d1698 commit a93fca9
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 4 deletions.
5 changes: 5 additions & 0 deletions Documentation/devicetree/bindings/net/can/xilinx,can.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -137,6 +141,7 @@ examples:
interrupts = <GIC_SPI 59 IRQ_TYPE_EDGE_RISING>;
tx-fifo-depth = <0x40>;
rx-fifo-depth = <0x40>;
xlnx,has-ecc;
};
- |
Expand Down
169 changes: 165 additions & 4 deletions drivers/net/can/xilinx_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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,
};

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit a93fca9

Please sign in to comment.