Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Add Loongson CAN-FD controller driver
@ 2026-06-08  8:49 Binbin Zhou
  2026-06-08  8:49 ` [PATCH v2 1/2] can: " Binbin Zhou
  2026-06-08  8:49 ` [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support Binbin Zhou
  0 siblings, 2 replies; 5+ messages in thread
From: Binbin Zhou @ 2026-06-08  8:49 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Marc Kleine-Budde, Vincent Mailhol,
	Bingxiong Li
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-can, jeffbai,
	Binbin Zhou

Hi all:

This patchset adds support for the CAN-FD controller found on Loongson
CPUs.

Patch 1 introduces the basic CAN-FD controller driver with support
for classic CAN and CAN FD, including bit timing, error handling,
NAPI-based RX, and multiple TX buffers.

Patch 2 adds optional DMA support for RX path using the Loongson APB
CMC DMA engine, which significantly reduces CPU load under high
receive throughput.

The driver has been tested on Loongson-2K3000 platforms with various
CAN/CAN FD traffic patterns.

Finally, I'd like to thank Binxiong, the original author of this driver,
for his efforts in working on the patch.

Thanks.
Binbin

-------
V2:
Patch (1/2):
 - Put all code into one file;
 - Add COMPILE_TEST Kconfig option;
 - Rewrite Kconfig description;
 - Use `regmap_test_bits()` to simplify bit field checks;
 - Drop odd FIELD_GET() usage;
 - Don't use FIELD_GET() for a single bit;
 - Use an if/else instead of the ternary operator;
 - Use the CAN TDC framework to get the SSP value;
 - Use guard(spinlock)/scoped_guard(spinlock);
 - Use netdev_debug() to be less verboss;
 - Check for memory allocation failure;
 - Add CAN_ERR_CNT flag;
 - Drop unused REG_DATA_xx_yy_W_DATA_yy definition;
 - Add more function comment;

Link to V1:
https://lore.kernel.org/all/cover.1777273055.git.zhoubinbin@loongson.cn/

Binbin Zhou (2):
  can: Add Loongson CAN-FD controller driver
  can: loongson_canfd: Add RXDMA support

 MAINTAINERS                      |    7 +
 drivers/net/can/Kconfig          |   15 +
 drivers/net/can/Makefile         |    1 +
 drivers/net/can/loongson_canfd.c | 1935 ++++++++++++++++++++++++++++++
 4 files changed, 1958 insertions(+)
 create mode 100644 drivers/net/can/loongson_canfd.c


base-commit: f1359c240191e686614847905fc861cbda480b47
-- 
2.52.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/2] can: Add Loongson CAN-FD controller driver
  2026-06-08  8:49 [PATCH v2 0/2] Add Loongson CAN-FD controller driver Binbin Zhou
@ 2026-06-08  8:49 ` Binbin Zhou
  2026-06-08 17:43   ` Vincent Mailhol
  2026-06-08  8:49 ` [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support Binbin Zhou
  1 sibling, 1 reply; 5+ messages in thread
From: Binbin Zhou @ 2026-06-08  8:49 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Marc Kleine-Budde, Vincent Mailhol,
	Bingxiong Li
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-can, jeffbai,
	Binbin Zhou

Add support for the CAN-FD controller integrated into Loongson-2K series
SoCs. The controller supports both Classic CAN and CAN FD, with up to 8
transmit buffers, hardware timestamping, error counters, and various
control modes (loopback, listen-only, one-shot, non-ISO FD, etc.).

The driver implements:
- NAPI for RX path
- TX buffer management with echo skb support
- Bus error reporting and fault confinement state handling
- Bit timing configuration for nominal and data phase
- Secondary Sample Point (SSP) configuration for high bitrates

Co-developed-by: Bingxiong Li <libingxiong@loongson.cn>
Signed-off-by: Bingxiong Li <libingxiong@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 MAINTAINERS                      |    7 +
 drivers/net/can/Kconfig          |   15 +
 drivers/net/can/Makefile         |    1 +
 drivers/net/can/loongson_canfd.c | 1766 ++++++++++++++++++++++++++++++
 4 files changed, 1789 insertions(+)
 create mode 100644 drivers/net/can/loongson_canfd.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a2ffd9d37d5..1ee16edae734 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14935,6 +14935,13 @@ F:	arch/loongarch/
 F:	drivers/*/*loongarch*
 F:	drivers/cpufreq/loongson3_cpufreq.c
 
+LOONGSON CAN FD DRIVER
+M:	Bingxiong Li <libingxiong@loongson.cn>
+M:	Binbin Zhou <zhoubinbin@loongson.cn>
+L:	linux-can@vger.kernel.org
+S:	Maintained
+F:	drivers/net/can/loongson_canfd.c
+
 LOONGSON GPIO DRIVER
 M:	Yinbo Zhu <zhuyinbo@loongson.cn>
 L:	linux-gpio@vger.kernel.org
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index e15e320db476..28014e264f30 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -189,6 +189,21 @@ config CAN_KVASER_PCIEFD
 	    Kvaser M.2 PCIe 4xCAN
 	    Kvaser PCIe 8xCAN
 
+config CAN_LOONGSON_CANFD
+	tristate "Loongson CAN-FD controller"
+	depends on HAS_IOMEM || COMPILE_TEST
+	select REGMAP_MMIO
+	help
+	  This is a canfd driver switch for the Loongson platform,
+	  integrated with the Loongson-2K series SoCs.
+
+	  It supports both Classic CAN and CAN FD, with up to 8 transmit
+	  buffers, hardware timestamping, error counters, and various
+	  control modes.
+
+	  To compile as a module, choose M here: the module will be
+	  called loongson_canfd.
+
 config CAN_SLCAN
 	tristate "Serial / USB serial CAN Adaptors (slcan)"
 	depends on TTY
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index d7bc10a6b8ea..1ce78443d710 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_GRCAN)		+= grcan.o
 obj-$(CONFIG_CAN_IFI_CANFD)	+= ifi_canfd/
 obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 obj-$(CONFIG_CAN_KVASER_PCIEFD)	+= kvaser_pciefd/
+obj-$(CONFIG_CAN_LOONGSON_CANFD)	+= loongson_canfd.o
 obj-$(CONFIG_CAN_MSCAN)		+= mscan/
 obj-$(CONFIG_CAN_M_CAN)		+= m_can/
 obj-$(CONFIG_CAN_PEAK_PCIEFD)	+= peak_canfd/
diff --git a/drivers/net/can/loongson_canfd.c b/drivers/net/can/loongson_canfd.c
new file mode 100644
index 000000000000..3794700e04c8
--- /dev/null
+++ b/drivers/net/can/loongson_canfd.c
@@ -0,0 +1,1766 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LOONGSON CANFD controller
+ *
+ * Copyright (C) 2024-2026 Loongson Technology Corporation Limited
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define LOONGSON_CANFD_DEVICE_ID	0x0	/* CANFD controller ID Register */
+#define LOONGSON_CANFD_MODE		0x4	/* Mode Configuration Register */
+#define LOONGSON_CANFD_CONF		0x8	/* Configure Register */
+#define LOONGSON_CANFD_STAT		0xc	/* Status Register */
+#define LOONGSON_CANFD_CMD		0x10	/* Command Register */
+#define LOONGSON_CANFD_INT_STAT		0x14	/* Interrupt Status Register */
+#define LOONGSON_CANFD_INT_ENA		0x18	/* Interrupt Enable Register */
+#define LOONGSON_CANFD_INT_MASK		0x1c	/* Interrupt Mask Register */
+#define LOONGSON_CANFD_BTR_NORM		0x20	/* Normal Rate Configuration Register */
+#define LOONGSON_CANFD_BTR_FD		0x24	/* FD Data Rate Configuration Register */
+#define LOONGSON_CANFD_ERL		0x28	/* Error Threshold Configuration Register */
+#define LOONGSON_CANFD_FSTAT		0x2c	/* Error Status Register */
+#define LOONGSON_CANFD_ERC		0x30	/* Error Count Register */
+#define LOONGSON_CANFD_BRE		0x34	/* Rate Error Count Register */
+#define LOONGSON_CANFD_CTR_PRES		0x38	/* Error Count Debug Register */
+#define LOONGSON_CANFD_ERR_CAPT		0x3c	/* Error Capture Status Register */
+#define LOONGSON_CANFD_RETX_CNT		0x40	/* Retransmission Count Register */
+#define LOONGSON_CANFD_ALC		0x44	/* Lost Arbitration Capture Register */
+#define LOONGSON_CANFD_TRV_DLY		0x48	/* Transmission Delay Measurement Register */
+#define LOONGSON_CANFD_SSP_CFG		0x4c	/* Second Sampling Point Configuration Register */
+#define LOONGSON_CANFD_RX_FR_CNT	0x50	/* Receive Message Count Register */
+#define LOONGSON_CANFD_TX_FR_CNT	0x54	/* Transmit Message Count Register */
+#define LOONGSON_CANFD_DEBUG		0x58	/* Debug Register */
+#define LOONGSON_CANFD_TS		0x5c	/* Timestamp Register */
+#define LOONGSON_CANFD_TX_FRM_TST	0x60	/* Transmit Message Debug Register */
+#define LOONGSON_CANFD_FRC_DIV		0x64	/* Fractional Divider Ratio Register */
+#define LOONGSON_CANFD_FLT_A_MASK	0x68	/* Filter A Mask Register */
+#define LOONGSON_CANFD_FLT_A_VAL	0x6c	/* Filter A value Register */
+#define LOONGSON_CANFD_FLT_B_MASK	0x70	/* Filter B Mask Register */
+#define LOONGSON_CANFD_FLT_B_VAL	0x74	/* Filter B value Register */
+#define LOONGSON_CANFD_FLT_C_MASK	0x78	/* Filter C Mask Register */
+#define LOONGSON_CANFD_FLT_C_VAL	0x7c	/* Filter C value Register */
+#define LOONGSON_CANFD_FLT_R_LOW	0x80	/* Range Filter Low Threshold Register */
+#define LOONGSON_CANFD_FLT_R_HI		0x84	/* Range Filter High Threshold Register */
+#define LOONGSON_CANFD_FLT_CTRL		0x88	/* Filter Control Register */
+#define LOONGSON_CANFD_RX_MEM_INFO	0x8c	/* Receive Buffer Information Register */
+#define LOONGSON_CANFD_RX_PRT		0x90	/* Receive Buffer Pointer Register */
+#define LOONGSON_CANFD_RX_STAT		0x94	/* Receive Buffer Status Register */
+#define LOONGSON_CANFD_RX_DATA		0x98	/* Receive Data Register */
+#define LOONGSON_CANFD_TX_STAT		0x9c	/* Transmit Buffer Status Register */
+#define LOONGSON_CANFD_TX_CMD		0xa0	/* Transmit Command Register */
+#define LOONGSON_CANFD_TX_SEL		0xa4	/* Transmit Buffer Selection Register */
+#define LOONGSON_CANFD_TX_DATA_1	0xb0
+#define LOONGSON_CANFD_TX_DATA_2	0xb4
+#define LOONGSON_CANFD_TX_DATA_3	0xb8
+#define LOONGSON_CANFD_TX_DATA_4	0xbc
+#define LOONGSON_CANFD_TX_DATA_5	0xc0
+#define LOONGSON_CANFD_TX_DATA_6	0xc4
+#define LOONGSON_CANFD_TX_DATA_7	0xc8
+#define LOONGSON_CANFD_TX_DATA_8	0xcc
+#define LOONGSON_CANFD_TX_DATA_9	0xd0
+#define LOONGSON_CANFD_TX_DATA_10	0xd4
+#define LOONGSON_CANFD_TX_DATA_11	0xd8
+#define LOONGSON_CANFD_TX_DATA_12	0xdc
+#define LOONGSON_CANFD_TX_DATA_13	0xe0
+#define LOONGSON_CANFD_TX_DATA_14	0xe4
+#define LOONGSON_CANFD_TX_DATA_15	0xe8
+#define LOONGSON_CANFD_TX_DATA_16	0xec
+#define LOONGSON_CANFD_TX_DATA_17	0xf0
+#define LOONGSON_CANFD_TX_DATA_18	0xf4
+
+/* Bitfields of CANFD controller ID register */
+#define REG_ID_MASK			GENMASK(15, 0)
+#define REG_ID_VER_MIN			GENMASK(23, 16)
+#define REG_ID_VER_MAJ			GENMASK(31, 24)
+
+/* Bitfields of Mode Configuration register */
+#define REG_MODE_RST			BIT(0)
+#define REG_MODE_BMM			BIT(1)
+#define REG_MODE_STM			BIT(2)
+#define REG_MODE_AFM			BIT(3)
+#define REG_MODE_FDE			BIT(4)
+#define REG_MODE_TTTM			BIT(5)
+#define REG_MODE_ROM			BIT(6)
+#define REG_MODE_ACF			BIT(7)
+#define REG_MODE_TSTM			BIT(8)
+#define REG_MODE_RXBAM			BIT(9)
+#define REG_MODE_ITSM			BIT(10)
+#define REG_MODE_RTSOP			BIT(12)
+#define REG_MODE_BUFM			BIT(13)
+
+/* Bitfields of Configure register */
+#define REG_CONF_RTRLE			BIT(0)
+#define REG_CONF_RTRTH			GENMASK(4, 1)
+#define REG_CONF_ILBP			BIT(5)
+#define REG_CONF_ENA			BIT(6)
+#define REG_CONF_NISOFD			BIT(7)
+#define REG_CONF_PEX			BIT(8)
+#define REG_CONF_FDRF			BIT(10)
+
+/* Bitfields of Status register */
+#define REG_STAT_RXNE			BIT(0)
+#define REG_STAT_DOR			BIT(1)
+#define REG_STAT_TXNF			BIT(2)
+#define REG_STAT_EFT			BIT(3)
+#define REG_STAT_RXS			BIT(4)
+#define REG_STAT_TXS			BIT(5)
+#define REG_STAT_EWL			BIT(6)
+#define REG_STAT_IDLE			BIT(7)
+#define REG_STAT_PEXS			BIT(8)
+#define REG_STAT_STCNT			BIT(16)
+
+/* Bitfields of Command register */
+#define REG_CMD_RXRPMV			BIT(1)
+#define REG_CMD_RRB			BIT(2)
+#define REG_CMD_CDO			BIT(3)
+#define REG_CMD_ERCRST			BIT(4)
+#define REG_CMD_RXFCRST			BIT(5)
+#define REG_CMD_TXFCRST			BIT(6)
+#define REG_CMD_CPEXS			BIT(7)
+
+/* Bitfields of Interrupt Status register */
+#define REG_INT_STAT_RXI		BIT(0)
+#define REG_INT_STAT_TXI		BIT(1)
+#define REG_INT_STAT_EWLI		BIT(2)
+#define REG_INT_STAT_DOI		BIT(3)
+#define REG_INT_STAT_FCSI		BIT(4)
+#define REG_INT_STAT_ALI		BIT(5)
+#define REG_INT_STAT_BEI		BIT(6)
+#define REG_INT_STAT_RXFI		BIT(7)
+#define REG_INT_STAT_BSI		BIT(8)
+#define REG_INT_STAT_RBNEI		BIT(9)
+#define REG_INT_STAT_TXBHCI		BIT(10)
+#define REG_INT_STAT_OFI		BIT(11)
+#define REG_INT_STAT_DMADI		BIT(12)
+
+#define REG_INT_STAT_ERRORI	(REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI)
+
+/* Bitfields of Interrupt Enable register */
+#define REG_INT_ENA_CLR			GENMASK(28, 16)
+#define REG_INT_ENA_SET			GENMASK(12, 0)
+
+/* Bitfields of Interrupt Mask register */
+#define REG_INT_MASK_SET		GENMASK(12, 0)
+#define REG_INT_MASK_CLR		GENMASK(28, 16)
+
+/* Bitfields of Normal Rate Configuration register */
+#define REG_BTR_PROP			GENMASK(6, 0)
+#define REG_BTR_PH1			GENMASK(12, 7)
+#define REG_BTR_PH2			GENMASK(18, 13)
+#define REG_BTR_BRP			GENMASK(22, 19)
+#define REG_BTR_SJW			GENMASK(31, 27)
+
+/* Bitfields of FD Data Rate Configuration register */
+#define REG_BTR_FD_PROP			GENMASK(6, 0)
+#define REG_BTR_FD_PH1			GENMASK(11, 7)
+#define REG_BTR_FD_PH2			GENMASK(17, 13)
+#define REG_BTR_FD_BRP			GENMASK(26, 19)
+#define REG_BTR_FD_SJW			GENMASK(31, 27)
+
+/* Bitfields of Error Threshold Configuration register */
+#define REG_ERL_ERP			GENMASK(7, 0)
+#define REG_ERL_EW			GENMASK(23, 16)
+
+/* Bitfields of Error Status register */
+#define REG_FSTAT_ERA			BIT(0)
+#define REG_FSTAT_ERP			BIT(1)
+#define REG_FSTAT_BOF			BIT(2)
+
+#define REG_FSTAT_MASK			GENMASK(2, 0)
+
+/* Bitfields of Error Count register */
+#define REG_ERC_TEC			GENMASK(8, 0)
+#define REG_ERC_REC			GENMASK(24, 16)
+
+/* Bitfields of Rate Error Count register */
+#define REG_BRE_NORM			GENMASK(15, 0)
+#define REG_BRE_FD_DATA			GENMASK(31, 16)
+
+/* Bitfields of Error Count Debug register */
+#define REG_CTR_PRES_CTPV		GENMASK(8, 0)
+#define REG_CTR_PRES_PTX		BIT(9)
+#define REG_CTR_PRES_PRX		BIT(10)
+
+/* Bitfields of Error Capture Status register */
+#define REG_ERR_CAPT_POS		GENMASK(4, 0)
+#define REG_ERR_CAPT_TYPE		GENMASK(7, 5)
+
+/* Bitfields of Retransmission Count register */
+#define REG_RETX_CNT_VAL		GENMASK(3, 0)
+
+/* Bitfields of Lost Arbitration Capture register */
+#define REG_ALC_BIT_POS			GENMASK(4, 0)
+#define REG_ALC_ID_FIELD		GENMASK(7, 5)
+
+/* Bitfields of Transmission Delay Measurement register */
+#define REG_TRV_DLY_VAL			GENMASK(6, 0)
+
+/* Bitfields of Second Sampling Point Configuration register */
+#define REG_SSP_CFG_OFF			GENMASK(7, 0)
+#define REG_SSP_CFG_SRC			GENMASK(9, 8)
+#define REG_SSP_CFG_SAT			BIT(10)
+
+/* Bitfields of Receive Message Count register */
+#define REG_RX_FR_CNT_VAL		GENMASK(31, 0)
+
+/* Bitfields of Transmit Message Count register */
+#define REG_TX_FR_CNT_VAL		GENMASK(31, 0)
+
+/* Bitfields of Debug register */
+#define REG_DEBUG_STF_CNT		GENMASK(2, 0)
+#define REG_DEBUG_DSTF_CNT		GENMASK(5, 3)
+#define REG_DEBUG_PC_ARB		BIT(6)
+#define REG_DEBUG_PC_CON		BIT(7)
+#define REG_DEBUG_PC_DAT		BIT(8)
+#define REG_DEBUG_PC_STC		BIT(9)
+#define REG_DEBUG_PC_CRC		BIT(10)
+#define REG_DEBUG_PC_CRCD		BIT(11)
+#define REG_DEBUG_PC_ACK		BIT(12)
+#define REG_DEBUG_PC_ACKD		BIT(13)
+#define REG_DEBUG_PC_EOF		BIT(14)
+#define REG_DEBUG_PC_INT		BIT(15)
+#define REG_DEBUG_PC_SUSP		BIT(16)
+#define REG_DEBUG_PC_OVR		BIT(17)
+#define REG_DEBUG_PC_SOF		BIT(18)
+
+/* Bitfields of Timestamp register */
+#define REG_TS_TIMESTAMP		GENMASK(15, 0)
+#define REG_TS_PSC			GENMASK(24, 16)
+
+/* Bitfields of Fractional Divider Ratio register */
+#define REG_FRC_FRC_DBT			GENMASK(15, 8)
+#define REG_FRC_FRC_NBT			GENMASK(7, 0)
+
+/* Bitfields of Filter A Mask register */
+#define REG_FIL_A_MASK			GENMASK(28, 0)
+
+/* Bitfields of Filter A value register */
+#define REG_FIL_A_VAL			GENMASK(28, 0)
+
+/* Bitfields of Filter B Mask register */
+#define REG_FIL_B_MASK			GENMASK(28, 0)
+
+/* Bitfields of Filter B value register */
+#define REG_FIL_B_VAL			GENMASK(28, 0)
+
+/* Bitfields of Filter C Mask register */
+#define REG_FIL_C_MASK			GENMASK(28, 0)
+
+/* Bitfields of Filter C value register */
+#define REG_FIL_C_VAL			GENMASK(28, 0)
+
+/* Bitfields of Range Filter Low Threshold register */
+#define REG_FIL_R_LOW_VAL		GENMASK(28, 0)
+
+/* Bitfields of Range Filter High Threshold register */
+#define REG_FIL_R_HI_VAL		GENMASK(28, 0)
+
+/* Bitfields of Filter Control register */
+#define REG_FIL_CTRL_FANB		BIT(0)
+#define REG_FIL_CTRL_FANE		BIT(1)
+#define REG_FIL_CTRL_FAFB		BIT(2)
+#define REG_FIL_CTRL_FAFE		BIT(3)
+#define REG_FIL_CTRL_FBNB		BIT(4)
+#define REG_FIL_CTRL_FBNE		BIT(5)
+#define REG_FIL_CTRL_FBFB		BIT(6)
+#define REG_FIL_CTRL_FBFE		BIT(7)
+#define REG_FIL_CTRL_FCNB		BIT(8)
+#define REG_FIL_CTRL_FCNE		BIT(9)
+#define REG_FIL_CTRL_FCFB		BIT(10)
+#define REG_FIL_CTRL_FCFE		BIT(11)
+#define REG_FIL_CTRL_FRNB		BIT(12)
+#define REG_FIL_CTRL_FRNE		BIT(13)
+#define REG_FIL_CTRL_FRFB		BIT(14)
+#define REG_FIL_CTRL_FRFE		BIT(15)
+#define REG_FIL_CTRL_SFA		BIT(16)
+#define REG_FIL_CTRL_SFB		BIT(17)
+#define REG_FIL_CTRL_SFC		BIT(18)
+#define REG_FIL_CTRL_SFR		BIT(19)
+
+/* Bitfields of Receive Buffer Information register */
+#define REG_RX_MEM_INFO_BUFF_SIZE	GENMASK(12, 0)
+#define REG_RX_MEM_INFO_MEM_FREE	GENMASK(28, 16)
+
+/* Bitfields of Receive Buffer Pointer register */
+#define REG_RX_PTR_WPP			GENMASK(11, 0)
+#define REG_RX_PTR_RPP			GENMASK(27, 16)
+
+/* Bitfields of Receive Buffer Status register */
+#define REG_RX_STAT_RXE			BIT(0)
+#define REG_RX_STAT_RXF			BIT(1)
+#define REG_RX_STAT_RXMOF		BIT(2)
+#define REG_RX_STAT_RXFRC		GENMASK(14, 4)
+#define REG_RX_STAT_RTSOP		BIT(16)
+
+/* Bitfields of Receive Data register */
+#define REG_RX_DATA_VAL			GENMASK(31, 0)
+
+/* Bitfields of Transmit Buffer Status register */
+#define REG_TX_STAT_BRP			GENMASK(7, 0)
+#define REG_TX_STAT_TXS			GENMASK(10, 8)
+#define REG_TX_STAT_BS			GENMASK(31, 16)
+
+#define REG_TX_STAT_BS_TX0		GENMASK(17, 16)
+#define REG_TX_STAT_BS_TX1		GENMASK(19, 18)
+#define REG_TX_STAT_BS_TX2		GENMASK(21, 20)
+#define REG_TX_STAT_BS_TX3		GENMASK(23, 22)
+#define REG_TX_STAT_BS_TX4		GENMASK(25, 24)
+#define REG_TX_STAT_BS_TX5		GENMASK(27, 26)
+#define REG_TX_STAT_BS_TX6		GENMASK(29, 28)
+#define REG_TX_STAT_BS_TX7		GENMASK(31, 30)
+
+/* Bitfields of Transmit Command register */
+#define REG_TX_CMD_BAR			GENMASK(7, 0)
+#define REG_TX_CMD_BCR			GENMASK(15, 8)
+#define REG_TX_CMD_BSC			GENMASK(23, 16)
+
+/* Bitfields of Transmit Buffer Selection register */
+#define REG_TX_SEL_BUF_SEL		GENMASK(3, 0)
+#define REG_TX_SEL_BUF_CNT		GENMASK(7, 4)
+
+/* Loongson CANFD Frame format */
+#define LOONGSON_CANFD_FRAME_META0	0x0
+#define LOONGSON_CANFD_FRAME_META1	0x4
+#define LOONGSON_CANFD_FRAME_DB_1	0x8
+#define LOONGSON_CANFD_FRAME_DB_2	0xc
+#define LOONGSON_CANFD_FRAME_DB_3	0x10
+#define LOONGSON_CANFD_FRAME_DB_4	0x14
+#define LOONGSON_CANFD_FRAME_DB_5	0x18
+#define LOONGSON_CANFD_FRAME_DB_6	0x1c
+#define LOONGSON_CANFD_FRAME_DB_7	0x20
+#define LOONGSON_CANFD_FRAME_DB_8	0x24
+#define LOONGSON_CANFD_FRAME_DB_9	0x28
+#define LOONGSON_CANFD_FRAME_DB_10	0x2c
+#define LOONGSON_CANFD_FRAME_DB_11	0x30
+#define LOONGSON_CANFD_FRAME_DB_12	0x34
+#define LOONGSON_CANFD_FRAME_DB_13	0x38
+#define LOONGSON_CANFD_FRAME_DB_14	0x3c
+#define LOONGSON_CANFD_FRAME_DB_16	0x40
+#define LOONGSON_CANFD_FRAME_DB_17	0x44
+
+/* Bitfields of FRAME META0 */
+#define REG_FRAME_META0_ID_EXT		GENMASK(17, 0)
+#define REG_FRAME_META0_ID_BASE		GENMASK(28, 18)
+#define REG_FRAME_META0_RTR		BIT(29)	/* Remote Transmission Request, only in CAN2.0 */
+/* Extended Identifier Type. 1: 29 bits ID, 0: 11 bits ID. */
+#define REG_FRAME_META0_XDT		BIT(30)
+#define REG_FRAME_META0_ESI		BIT(31)	/* Error State Indicator, only in CAN-FD */
+
+/* Bitfields of FRAME META1 */
+#define REG_FRAME_META1_TIMESTAMP	GENMASK(15, 0)
+#define REG_FRAME_META1_DLC		GENMASK(19, 16)	/* Data Length Code */
+#define REG_FRAME_META1_BRS		BIT(20)	/* Bit Rate Shift, Only in CANFD */
+/* Flexible Data-rate Format. 1: CANFD, 0: CAN2.0 */
+#define REG_FRAME_META1_FDF		BIT(21)
+#define REG_FRAME_META1_RWCNT		GENMASK(28, 24)	/* Read Word Counter */
+
+/* Bitfields of FRAME TEST */
+#define REG_FRAME_TEST_FSTC		BIT(0)
+#define REG_FRAME_TEST_FCRC		BIT(1)
+#define REG_FRAME_TEST_SDLC		BIT(2)
+#define REG_FRAME_TEST_TPRM		GENMASK(12, 8)
+
+#define DEV_NAME			"loongson_canfd"
+#define LOONGSON_CANFD_ID		0xBABE
+#define LOONGSON_CANFD_DW_BYTE		4
+#define LOONGSON_CANFD_TXBUF_NUM	8
+#define LOONGSON_CANFD_MAX_RTXTH	0xf
+
+/**
+ * struct loongson_canfd_priv - This definition define CAN driver instance
+ * @can: CAN private data structure.
+ * @napi: NAPI structure
+ * @regmap: regmap of the CAN device
+ * @res: Pointer to the CAN device respurce
+ * @tx_lock: Lock for synchronizing TX interrupt handling
+ */
+struct loongson_canfd_priv {
+	struct can_priv		can;		/* must be first member! */
+	struct napi_struct	napi;
+	struct regmap		*regmap;
+	struct resource		*res;
+	spinlock_t		tx_lock;	/* protect the sending queue */
+};
+
+/**
+ * enum loongson_canfd_txbuf_sts - status of TX buffer
+ * @TX_BS_IDLE: Status processed or not sent.
+ * @TX_BS_VALID: Sending successful.
+ * @TX_BS_FAIL: Sending failed.
+ * @TX_BS_CANCEL: Sending cancelled.
+ */
+enum loongson_canfd_txbuf_sts {
+	TX_BS_IDLE	= 0x0,
+	TX_BS_VALID	= 0x1,
+	TX_BS_FAIL	= 0x2,
+	TX_BS_CANCEL	= 0x3
+};
+
+/**
+ * enum loongson_canfd_txbuf_cmd - command of TX buffer
+ * @TXT_CMD_ADD: Buffer add request.
+ * @TXT_CMD_CANCEL: Buffer cancel request.
+ * @TXT_CMD_SR_CLEAN: Buffer send record clear.
+ *     1: to clear the send completion record (LOONGSON_CANFD_TX_STAT[BS]).
+ */
+enum loongson_canfd_txbuf_cmd {
+	TXT_CMD_ADD		= 0x01,
+	TXT_CMD_CANCEL		= 0x02,
+	TXT_CMD_SR_CLEAN	= 0x03
+};
+
+static const struct can_bittiming_const loongson_canfd_bit_timing = {
+	.name		= DEV_NAME,
+	.tseg1_min	= 2,
+	.tseg1_max	= 190,
+	.tseg2_min	= 2,
+	.tseg2_max	= 63,
+	.sjw_max	= 31,
+	.brp_min	= 1,
+	.brp_max	= 15,
+	.brp_inc	= 1,
+};
+
+static const struct can_bittiming_const loongson_canfd_bit_timing_data = {
+	.name		= DEV_NAME,
+	.tseg1_min	= 2,
+	.tseg1_max	= 190,
+	.tseg2_min	= 2,
+	.tseg2_max	= 63,
+	.sjw_max	= 31,
+	.brp_min	= 1,
+	.brp_max	= 255,
+	.brp_inc	= 1,
+};
+
+/* CAN FD Transmission Delay Compensation constants */
+static const struct can_tdc_const loongson_canfd_tdc_data = {
+	.tdcv_min	= 0,
+	.tdcv_max	= 0,
+	.tdco_min	= 1,
+	.tdco_max	= 127,
+	.tdcf_min	= 0,	/* Filter window not supported */
+	.tdcf_max	= 0,
+};
+
+/**
+ * loongson_canfd_enabled() - Gets the controller is enabled
+ * @priv: Pointer to private data
+ *
+ * Return: True - the controller is enabled.
+ *	   False - the controller is disabled.
+ */
+static bool loongson_canfd_enabled(struct loongson_canfd_priv *priv)
+{
+	return !!regmap_test_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA);
+}
+
+/**
+ * loongson_canfd_txbuf_freed() - Gets the flag for TX buffer to be sent
+ * @priv: Pointer to private data
+ *
+ * Return: True - TX buffer is empty.
+ *	   False - TX buffer is processing
+ */
+static bool loongson_canfd_txbuf_freed(struct loongson_canfd_priv *priv)
+{
+	return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, REG_TX_STAT_BRP);
+}
+
+/**
+ * loongson_canfd_get_txbuf_sts() - Gets status of TX buffer
+ * @priv: Pointer to private data
+ * @buf_id: Buffer index (0-based)
+ *
+ * Return: Status of TX buffer
+ */
+static enum loongson_canfd_txbuf_sts
+loongson_canfd_get_txbuf_sts(struct loongson_canfd_priv *priv, u8 buf_id)
+{
+	u32 sts, mask;
+
+	switch (buf_id) {
+	case 0:
+		mask = REG_TX_STAT_BS_TX0;
+		break;
+	case 1:
+		mask = REG_TX_STAT_BS_TX1;
+		break;
+	case 2:
+		mask = REG_TX_STAT_BS_TX2;
+		break;
+	case 3:
+		mask = REG_TX_STAT_BS_TX3;
+		break;
+	case 4:
+		mask = REG_TX_STAT_BS_TX4;
+		break;
+	case 5:
+		mask = REG_TX_STAT_BS_TX5;
+		break;
+	case 6:
+		mask = REG_TX_STAT_BS_TX6;
+		break;
+	case 7:
+		mask = REG_TX_STAT_BS_TX7;
+		break;
+	}
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &sts);
+
+	return field_get(mask, sts);
+}
+
+/**
+ * loongson_canfd_get_txbuf_id() - Gets the index of a non-idle TX buffer
+ * @priv: Pointer to private data
+ * @sts: Status of TXT buffer to outside
+ *
+ * Return: Buffer index (0-based)
+ */
+static unsigned int loongson_canfd_get_txbuf_id(struct loongson_canfd_priv *priv,
+						enum loongson_canfd_txbuf_sts *sts)
+{
+	unsigned int i;
+
+	for (i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) {
+		if (loongson_canfd_get_txbuf_sts(priv, i)) {
+			*sts = loongson_canfd_get_txbuf_sts(priv, i);
+			break;
+		}
+	}
+
+	if (i == LOONGSON_CANFD_TXBUF_NUM)
+		*sts = TX_BS_IDLE;
+
+	return i;
+}
+
+/**
+ * loongson_canfd_txbuf_is_writable() - Checks if frame can be inserted to TX Buffer
+ * @priv: Pointer to private data
+ * @buf_id: Buffer index (0-based)
+ *
+ * Return: True - Frame can be inserted to TXT Buffer,
+ *	   False - If attempted, frame will not be inserted to TX Buffer
+ */
+static bool loongson_canfd_txbuf_is_writable(struct loongson_canfd_priv *priv, u8 buf_id)
+{
+	enum loongson_canfd_txbuf_sts bs;
+
+	bs = loongson_canfd_get_txbuf_sts(priv, buf_id);
+	if (bs)
+		return false;
+
+	return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, BIT(buf_id));
+}
+
+/**
+ * loongson_canfd_set_txbuf_cmd() - Applies command on TX buffer
+ * @ndev: Pointer to net_device structure
+ * @cmd: Command to set
+ * @buf_id: Buffer index (0-based)
+ */
+static void loongson_canfd_set_txbuf_cmd(struct net_device *ndev,
+					 enum loongson_canfd_txbuf_cmd cmd, u8 buf_id)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 mask;
+
+	switch (cmd) {
+	case TXT_CMD_ADD:
+		mask = REG_TX_CMD_BAR;
+		break;
+	case TXT_CMD_CANCEL:
+		mask = REG_TX_CMD_BCR;
+		break;
+	case TXT_CMD_SR_CLEAN:
+		mask = REG_TX_CMD_BSC;
+		break;
+	default:
+		netdev_err(ndev, "Unknown command id: %x\n", cmd);
+		return;
+	}
+
+	regmap_write(priv->regmap, LOONGSON_CANFD_TX_CMD, field_prep(mask, 0x1 << buf_id));
+}
+
+/**
+ * loongson_canfd_reset() - Issues software reset request to Loongson CANFD
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 for success, -%ETIMEDOUT if CAN controller does not leave reset
+ */
+static int loongson_canfd_reset(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+
+	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RST);
+	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RXBAM | REG_MODE_BUFM);
+
+	return 0;
+}
+
+/**
+ * loongson_canfd_set_btr() - Sets CAN bus bit timing in Loongson CANFD
+ * @ndev: Pointer to net_device structure
+ * @bt: Pointer to Bit timing structure
+ * @nominal: True - Nominal bit timing, False - Data bit timing
+ *
+ * Return: 0 - OK, -%EPERM if controller is enabled
+ */
+static int loongson_canfd_set_btr(struct net_device *ndev, struct can_bittiming *bt, bool nominal)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 phase_seg1 = bt->phase_seg1;
+	u32 prop_seg = bt->prop_seg;
+	int max_ph1_len = 31;
+	u32 btr = 0;
+
+	if (loongson_canfd_enabled(priv)) {
+		netdev_err(ndev, "BUG! Cannot set bittiming - CAN is enabled\n");
+		return -EPERM;
+	}
+
+	if (nominal)
+		max_ph1_len = 63;
+
+	/*
+	 * The timing calculation functions have only constraints on tseg1,
+	 * which is prop_seg + phase1_seg combined.
+	 * tseg1 is then split in half and stored into prog_seg and phase_seg1.
+	 * In Loongson CAN-FD, PROP is 6/7 bits wide but PH1 only 6/5, so we must
+	 * re-distribute the values here.
+	 */
+	if (phase_seg1 > max_ph1_len) {
+		prop_seg += phase_seg1 - max_ph1_len;
+		phase_seg1 = max_ph1_len;
+		bt->prop_seg = prop_seg;
+		bt->phase_seg1 = phase_seg1;
+	}
+
+	if (nominal) {
+		btr = FIELD_PREP(REG_BTR_PROP, prop_seg) |
+		      FIELD_PREP(REG_BTR_PH1, phase_seg1) |
+		      FIELD_PREP(REG_BTR_PH2, bt->phase_seg2) |
+		      FIELD_PREP(REG_BTR_BRP, bt->brp) |
+		      FIELD_PREP(REG_BTR_SJW, bt->sjw);
+
+		regmap_write(priv->regmap, LOONGSON_CANFD_BTR_NORM, btr);
+	} else {
+		btr = FIELD_PREP(REG_BTR_FD_PROP, prop_seg) |
+		      FIELD_PREP(REG_BTR_FD_PH1, phase_seg1) |
+		      FIELD_PREP(REG_BTR_FD_PH2, bt->phase_seg2) |
+		      FIELD_PREP(REG_BTR_FD_BRP, bt->brp) |
+		      FIELD_PREP(REG_BTR_FD_SJW, bt->sjw);
+
+		regmap_write(priv->regmap, LOONGSON_CANFD_BTR_FD, btr);
+	}
+
+	return 0;
+}
+
+/**
+ * loongson_canfd_set_bittiming() - CAN set nominal bit timing routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 on success, -%EPERM on error
+ */
+static int loongson_canfd_set_bittiming(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+
+	/* Note that bt may be modified here */
+	return loongson_canfd_set_btr(ndev, bt, true);
+}
+
+/**
+ * loongson_canfd_set_data_bittiming() - CAN set data bit timing routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 on success, -%EPERM on error
+ */
+static int loongson_canfd_set_data_bittiming(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *dbt = &priv->can.fd.data_bittiming;
+
+	/* Note that dbt may be modified here */
+	return loongson_canfd_set_btr(ndev, dbt, false);
+}
+
+/**
+ * loongson_canfd_get_auto_tdcv - Get Transmitter Delay Compensation Value
+ * @ndev: Pointer to net_device structure
+ * @tdcv: Pointer to TDCV value
+ *
+ * Return: 0 on success
+ */
+static int loongson_canfd_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 val;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_TRV_DLY, &val);
+	*tdcv = FIELD_GET(REG_TRV_DLY_VAL, val);
+
+	return 0;
+}
+
+/**
+ * loongson_canfd_set_secondary_sample_point() - Set secondary sample point in Loongson CANFD
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 on success, -%EPERM if controller is enabled
+ */
+static int loongson_canfd_set_secondary_sample_point(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 ssp_cfg;
+
+	if (loongson_canfd_enabled(priv)) {
+		netdev_err(ndev, "BUG! Cannot set SSP - CAN is enabled\n");
+		return -EPERM;
+	}
+
+	if (can_fd_tdc_is_enabled(&priv->can))
+		ssp_cfg = FIELD_PREP(REG_SSP_CFG_OFF, priv->can.fd.tdc.tdco) |
+			  FIELD_PREP(REG_SSP_CFG_SRC, 0x0);
+	else
+		ssp_cfg = FIELD_PREP(REG_SSP_CFG_SRC, 0x1);
+
+	regmap_write(priv->regmap, LOONGSON_CANFD_SSP_CFG, ssp_cfg);
+
+	return 0;
+}
+
+/**
+ * loongson_canfd_set_mode() - Sets Loongson CANFD mode
+ * @priv: Pointer to private data
+ * @ctrlmode: Pointer to controller modes to be set
+ */
+static void loongson_canfd_set_mode(struct loongson_canfd_priv *priv,
+				    const struct can_ctrlmode *ctrlmode)
+{
+	u32 mode, conf;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_MODE, &mode);
+
+	if (ctrlmode->flags & CAN_CTRLMODE_LISTENONLY)
+		mode |= REG_MODE_BMM;
+	else
+		mode &= ~REG_MODE_BMM;
+
+	if (ctrlmode->flags & CAN_CTRLMODE_FD)
+		mode |= REG_MODE_FDE;
+	else
+		mode &= ~REG_MODE_FDE;
+
+	if (ctrlmode->flags & CAN_CTRLMODE_PRESUME_ACK)
+		mode |= REG_MODE_ACF;
+	else
+		mode &= ~REG_MODE_ACF;
+
+	/*
+	 * Some bits fixed:
+	 * TSTM - Off, User shall not be able to change REC/TEC by hand
+	 * during operation
+	 */
+	mode &= ~REG_MODE_TSTM;
+	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, mode);
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_CONF, &conf);
+
+	if (ctrlmode->flags & CAN_CTRLMODE_LOOPBACK)
+		conf |= REG_CONF_ILBP;
+	else
+		conf &= ~REG_CONF_ILBP;
+
+	if (ctrlmode->flags & CAN_CTRLMODE_FD_NON_ISO)
+		conf |= REG_CONF_NISOFD;
+	else
+		conf &= ~REG_CONF_NISOFD;
+
+	/* One shot mode supported indirectly via Retransmit limit */
+	conf &= ~FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH);
+
+	if (ctrlmode->flags & CAN_CTRLMODE_ONE_SHOT)
+		conf |= REG_CONF_RTRLE;
+	else
+		conf |= REG_CONF_RTRLE | FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH);
+
+	regmap_write(priv->regmap, LOONGSON_CANFD_CONF, conf);
+}
+
+/**
+ * loongson_canfd_chip_start() - This routine starts the driver
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int loongson_canfd_chip_start(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct can_ctrlmode mode;
+	u16 int_ena, int_msk;
+	int ret;
+
+	/* Configure bit-rates and ssp */
+	ret = loongson_canfd_set_bittiming(ndev);
+	if (ret < 0)
+		return ret;
+
+	ret = loongson_canfd_set_data_bittiming(ndev);
+	if (ret < 0)
+		return ret;
+
+	ret = loongson_canfd_set_secondary_sample_point(ndev);
+	if (ret < 0)
+		return ret;
+
+	/* Configure modes */
+	mode.flags = priv->can.ctrlmode;
+	mode.mask = 0xFFFFFFFF;
+	loongson_canfd_set_mode(priv, &mode);
+
+	/* Configure interrupts */
+	int_ena = REG_INT_STAT_RBNEI | REG_INT_STAT_TXBHCI |
+		  REG_INT_STAT_EWLI | REG_INT_STAT_FCSI;
+
+	/* Bus error reporting */
+	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		int_ena |= REG_INT_STAT_ALI | REG_INT_STAT_BEI;
+
+	int_msk = ~int_ena; /* Mask all disabled interrupts */
+
+	/* It's after reset, so there is no need to clear anything */
+	regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, int_msk);
+	regmap_write(priv->regmap, LOONGSON_CANFD_INT_ENA, int_ena);
+
+	/* Controller enters ERROR_ACTIVE on initial FCSI */
+	priv->can.state = CAN_STATE_STOPPED;
+
+	/* Enable the controller */
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, REG_CONF_ENA);
+
+	return 0;
+}
+
+/**
+ * loongson_canfd_do_set_mode() - Sets mode of the driver
+ * @ndev: Pointer to net_device structure
+ * @mode: Tells the mode of the driver
+ *
+ * This check the drivers state and calls the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int loongson_canfd_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	int ret;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		ret = loongson_canfd_reset(ndev);
+		if (ret < 0)
+			return ret;
+
+		ret = loongson_canfd_chip_start(ndev);
+		if (ret < 0) {
+			netdev_err(ndev, "loongson_canfd_chip_start failed!\n");
+			return ret;
+		}
+
+		netif_wake_queue(ndev);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * loongson_canfd_insert_frame() - Inserts frame to TXT buffer
+ * @priv: Pointer to private data
+ * @cf:	 Pointer to CAN frame to be inserted
+ * @buf: TXT Buffer index to which frame is inserted (0-based)
+ * @isfdf: True - CAN FD Frame, False - CAN 2.0 Frame
+ *
+ * Return:
+ * * True - Frame inserted successfully
+ * * False - Frame was not inserted due to one of:
+ *	1. TXT Buffer is not writable (it is in wrong state)
+ *	2. Invalid TXT buffer index
+ *	3. Invalid frame length
+ */
+static bool loongson_canfd_insert_frame(struct loongson_canfd_priv *priv,
+					const struct canfd_frame *cf, u8 buf, bool isfdf)
+{
+	u32 meta0, meta1;
+
+	if (buf >= LOONGSON_CANFD_TXBUF_NUM)
+		return false;
+
+	if (!loongson_canfd_txbuf_is_writable(priv, buf))
+		return false;
+
+	/* Prepare identifier */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		meta0 = cf->can_id & CAN_EFF_MASK;
+		meta0 |= REG_FRAME_META0_XDT;
+	} else {
+		meta0 = FIELD_PREP(REG_FRAME_META0_ID_BASE, cf->can_id & CAN_SFF_MASK);
+	}
+
+	/* Prepare Frame format */
+	if (cf->can_id & CAN_RTR_FLAG)
+		meta0 |= REG_FRAME_META0_RTR;
+
+	if (isfdf) {
+		meta1 = REG_FRAME_META1_FDF;
+
+		if (cf->flags & CANFD_BRS)
+			meta1 |= REG_FRAME_META1_BRS;
+	}
+
+	meta1 |= FIELD_PREP(REG_FRAME_META1_DLC, can_fd_len2dlc(cf->len));
+
+	/* TXT buffer select */
+	regmap_write(priv->regmap, LOONGSON_CANFD_TX_SEL, buf);
+
+	/* Write ID, Frame format */
+	regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META0, meta0);
+	regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META1, meta1);
+
+	/* Write Data payload */
+	if (!(cf->can_id & CAN_RTR_FLAG)) {
+		for (unsigned int i = 0; i < cf->len; i += LOONGSON_CANFD_DW_BYTE) {
+			regmap_write(priv->regmap,
+				     LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_DB_1 + i,
+				     le32_to_cpu(*(__le32 *)(cf->data + i)));
+		}
+	}
+
+	return true;
+}
+
+/**
+ * loongson_canfd_start_xmit() - Starts the transmission
+ * @skb: sk_buff pointer that contains data to be Txed
+ * @ndev: Pointer to net_device structure
+ *
+ * Invoked from upper layers to initiate transmission. Uses the next available free TX Buffer and
+ * populates its fields to start the transmission.
+ *
+ * Return: %NETDEV_TX_OK on success,
+ *         %NETDEV_TX_BUSY when no free TX buffer is available, negative return values reserved
+ *         for error cases.
+ */
+static netdev_tx_t loongson_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 buf_id, tx_stat, i = 0;
+	unsigned long tx_brp;
+	u16 tx_bs;
+
+	if (can_dropped_invalid_skb(ndev, skb))
+		return NETDEV_TX_OK;
+
+	if (unlikely(!loongson_canfd_txbuf_freed(priv))) {
+		netif_stop_queue(ndev);
+		netdev_err(ndev, "BUG!, no TXB free when queue awake!\n");
+		return NETDEV_TX_BUSY;
+	}
+
+	guard(spinlock_irqsave)(&priv->tx_lock);
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &tx_stat);
+	tx_brp = FIELD_GET(REG_TX_STAT_BRP, tx_stat);
+	tx_bs = FIELD_GET(REG_TX_STAT_BS, tx_stat);
+
+	for_each_clear_bit(i, &tx_brp, LOONGSON_CANFD_TXBUF_NUM) {
+		u32 transmission_complete_mask = 0x3 << (i * 2);
+
+		if (!(tx_bs & transmission_complete_mask)) {
+			buf_id = i;
+			break;
+		}
+	}
+
+	if (!loongson_canfd_insert_frame(priv, cf, buf_id, can_is_canfd_skb(skb))) {
+		netdev_err(ndev, "TXNF set but cannot insert frame into TXTB! HW Bug?");
+		kfree_skb(skb);
+		ndev->stats.tx_dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	can_put_echo_skb(skb, ndev, buf_id, 0);
+
+	loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_ADD, buf_id);
+
+	/* Check if all TX buffers are full */
+	if (!loongson_canfd_txbuf_freed(priv))
+		netif_stop_queue(ndev);
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * loongson_canfd_read_rx_frame() - Reads frame from RX FIFO
+ * @priv: Pointer to  private data
+ * @cf:	 Pointer to CAN frame struct
+ * @meta0: The first `frame format` read previously
+ * @meta1: The second `frame format` read previously
+ */
+static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struct canfd_frame *cf,
+					 u32 meta0, u32 meta1)
+{
+	u32 data, i, wc, len;
+
+	/* Extended Identifier Type */
+	if (meta0 & REG_FRAME_META0_XDT)
+		cf->can_id = (meta0 & CAN_EFF_MASK) | CAN_EFF_FLAG;
+	else
+		cf->can_id = FIELD_GET(REG_FRAME_META0_ID_BASE, meta0) & CAN_SFF_MASK;
+
+	/* BRS, ESI, RTR Flags */
+	cf->flags = 0;
+
+	if (meta1 & REG_FRAME_META1_FDF) {
+		if (meta1 & REG_FRAME_META1_BRS)
+			cf->flags |= CANFD_BRS;
+
+		if (meta0 & REG_FRAME_META0_ESI)
+			cf->flags |= CANFD_ESI;
+	} else if (meta0 & REG_FRAME_META0_RTR) {
+		cf->can_id |= CAN_RTR_FLAG;
+	}
+
+	/* Timesamp */
+	cf->__res0 = meta1;
+	cf->__res1 = meta1 >> 8;
+
+	wc = FIELD_GET(REG_FRAME_META1_RWCNT, meta1) - 2;
+
+	/* Data Length Code */
+	len = FIELD_GET(REG_FRAME_META1_DLC, meta1);
+	if (len > 8) {
+		if (meta1 & REG_FRAME_META1_FDF)
+			len = wc << 2;
+		else
+			len = 8;
+	}
+
+	cf->len = len;
+	if (unlikely(len > wc * LOONGSON_CANFD_DW_BYTE))
+		len = wc * LOONGSON_CANFD_DW_BYTE;
+
+	/* Data */
+	for (i = 0; i < len; i += LOONGSON_CANFD_DW_BYTE) {
+		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
+		*(__le32 *)(cf->data + i) = cpu_to_le32(data);
+	}
+
+	while (unlikely(i < wc * LOONGSON_CANFD_DW_BYTE)) {
+		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
+		i += LOONGSON_CANFD_DW_BYTE;
+	}
+}
+
+/**
+ * loongson_canfd_rx() -  Called from CAN ISR to complete the received frame processing
+ * @ndev: Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr to process the Rx frames. It does minimal
+ * processing and invokes "netif_receive_skb" to complete further processing.
+ * Return: 1 when frame is passed to the network layer, 0 when the first frame word is read but
+ *	   system is out of free SKBs temporally and left code to resolve SKB allocation later,
+ *         -%EAGAIN in a case of empty Rx FIFO.
+ */
+static int loongson_canfd_rx(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct canfd_frame *cf;
+	struct sk_buff *skb;
+	u32 meta0, meta1;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta0);
+	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta1);
+
+	/* Number of characters received */
+	if (!FIELD_GET(REG_FRAME_META1_RWCNT, meta1))
+		return -EAGAIN;
+
+	/* Flexible Data-rate Format */
+	if (meta1 & REG_FRAME_META1_FDF)
+		skb = alloc_canfd_skb(ndev, &cf);
+	else
+		skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
+	if (unlikely(!skb))
+		return -ENOMEM;
+
+	loongson_canfd_read_rx_frame(priv, cf, meta0, meta1);
+
+	netif_receive_skb(skb);
+
+	return 1;
+}
+
+/**
+ * loongson_canfd_read_fault_state() - Reads Loongson CANFD fault state.
+ * @ndev: Pointer to net_device structure
+ *
+ * Returns: Fault confinement state of controller
+ */
+static enum can_state loongson_canfd_read_fault_state(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	enum can_state sts = CAN_STATE_ERROR_PASSIVE;
+	u32 fstat;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_FSTAT, &fstat);
+
+	switch (FIELD_GET(REG_FSTAT_MASK, fstat)) {
+	case REG_FSTAT_ERA:
+		u32 ewl, erl, rec_tec, max_tec;
+
+		regmap_read(priv->regmap, LOONGSON_CANFD_ERL, &erl);
+		regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &rec_tec);
+
+		ewl = FIELD_GET(REG_ERL_EW, erl);
+		max_tec = max(FIELD_GET(REG_ERC_REC, rec_tec), FIELD_GET(REG_ERC_TEC, rec_tec));
+
+		if (ewl > max_tec)
+			sts = CAN_STATE_ERROR_ACTIVE;
+		else
+			sts = CAN_STATE_ERROR_WARNING;
+		break;
+	case REG_FSTAT_ERP:
+		sts = CAN_STATE_ERROR_PASSIVE;
+		break;
+	case REG_FSTAT_BOF:
+		sts = CAN_STATE_BUS_OFF;
+		break;
+	default:
+		netdev_err(ndev, "Invalid error state.\n");
+		break;
+	}
+
+	return sts;
+}
+
+/**
+ * loongson_canfd_get_bec() - Reads REC/TEC counter values from controller
+ * @priv: Pointer to private data
+ * @bec: Pointer to Error counter structure
+ */
+static void loongson_canfd_get_bec(struct loongson_canfd_priv *priv, struct can_berr_counter *bec)
+{
+	u32 erc;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &erc);
+	bec->rxerr = FIELD_GET(REG_ERC_REC, erc);
+	bec->txerr = FIELD_GET(REG_ERC_TEC, erc);
+}
+
+/**
+ * loongson_canfd_get_berr_counter() - error counter routine
+ * @ndev: Pointer to net_device structure
+ * @bec: Pointer to can_berr_counter structure
+ *
+ * Return: 0 always
+ */
+static int loongson_canfd_get_berr_counter(const struct net_device *ndev,
+					   struct can_berr_counter *bec)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+
+	loongson_canfd_get_bec(priv, bec);
+	return 0;
+}
+
+/**
+ * loongson_canfd_err_interrupt() - Error frame ISR
+ * @ndev: net_device pointer
+ * @isr: interrupt status register value
+ *
+ * This is the CAN error interrupt and it will check the type of error and forward the error
+ * frame to upper layers.
+ */
+static void loongson_canfd_err_interrupt(struct net_device *ndev, u32 isr)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_berr_counter bec;
+	enum can_state state;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u32 err_capt, alc;
+
+	loongson_canfd_get_bec(priv, &bec);
+
+	state = loongson_canfd_read_fault_state(ndev);
+	regmap_read(priv->regmap, LOONGSON_CANFD_ERR_CAPT, &err_capt);
+	regmap_read(priv->regmap, LOONGSON_CANFD_ALC, &alc);
+
+	netdev_dbg(ndev, "%s: ISR 0x%08x, rxerr %d, txerr %d, error type %lu, pos %lu, ALC id_field %lu, bit %lu\n",
+		   __func__, isr, bec.rxerr, bec.txerr,
+		   FIELD_GET(REG_ERR_CAPT_TYPE, err_capt),
+		   FIELD_GET(REG_ERR_CAPT_POS, err_capt),
+		   FIELD_GET(REG_ALC_ID_FIELD, alc),
+		   FIELD_GET(REG_ALC_BIT_POS, alc));
+
+	skb = alloc_can_err_skb(ndev, &cf);
+
+	/*
+	 * EWLI: error warning limit condition met
+	 * FCSI: fault confinement state changed
+	 * ALI:  arbitration lost (just informative)
+	 * BEI:  bus error interrupt
+	 */
+	if ((isr & REG_INT_STAT_FCSI) || (isr & REG_INT_STAT_EWLI)) {
+		netdev_info(ndev, "state changes from %s to %s\n",
+			    can_get_state_str(priv->can.state), can_get_state_str(state));
+
+		if (priv->can.state == state)
+			netdev_warn(ndev, "cur and pre state is the same!(miss intr?)\n");
+
+		isr = REG_INT_STAT_FCSI | REG_INT_STAT_EWLI;
+		priv->can.state = state;
+		switch (state) {
+		case CAN_STATE_BUS_OFF:
+			priv->can.can_stats.bus_off++;
+			if (priv->can.restart_ms)
+				regmap_write(priv->regmap, LOONGSON_CANFD_CMD,
+					     REG_CMD_ERCRST);
+
+			can_bus_off(ndev);
+			if (skb)
+				cf->can_id |= CAN_ERR_BUSOFF;
+			break;
+		case CAN_STATE_ERROR_PASSIVE:
+			priv->can.can_stats.error_passive++;
+			if (skb) {
+				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
+				if (bec.rxerr >= CAN_ERROR_PASSIVE_THRESHOLD)
+					cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE;
+				else
+					cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE;
+				cf->data[6] = bec.txerr;
+				cf->data[7] = bec.rxerr;
+			}
+			break;
+		case CAN_STATE_ERROR_WARNING:
+			priv->can.can_stats.error_warning++;
+			if (skb) {
+				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
+				if (bec.txerr > bec.rxerr)
+					cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+				else
+					cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+				cf->data[6] = bec.txerr;
+				cf->data[7] = bec.rxerr;
+			}
+			break;
+		case CAN_STATE_ERROR_ACTIVE:
+			cf->can_id |= CAN_ERR_CNT;
+			cf->data[1] = CAN_ERR_CRTL_ACTIVE;
+			cf->data[6] = bec.txerr;
+			cf->data[7] = bec.rxerr;
+			break;
+		default:
+			netdev_err(ndev, "Unexpected state: %d, %s!\n", state,
+				   can_get_state_str(state));
+			break;
+		}
+	}
+
+	/* Check for Arbitration Lost interrupt */
+	if (isr & REG_INT_STAT_ALI) {
+		isr = REG_INT_STAT_ALI;
+		netdev_err(ndev, "Arbitration Lost interrupt\n");
+		priv->can.can_stats.arbitration_lost++;
+		if (skb) {
+			cf->can_id |= CAN_ERR_LOSTARB;
+			cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+		}
+	}
+
+	/* Check for Bus Error interrupt */
+	if (isr & REG_INT_STAT_BEI) {
+		isr = REG_INT_STAT_BEI;
+		netdev_err(ndev, "Bus Error interrupt\n");
+		priv->can.can_stats.bus_error++;
+		stats->rx_errors++;
+		if (skb) {
+			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+			cf->data[2] = CAN_ERR_PROT_UNSPEC;
+			cf->data[3] = CAN_ERR_PROT_LOC_UNSPEC;
+		}
+	}
+
+	if (skb)
+		netif_rx(skb);
+
+	regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, isr);
+	regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, isr << 16);
+}
+
+static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota)
+{
+	struct net_device *ndev = napi->dev;
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	int work_done = 0, ret = 1;
+	u32 sts, rx_frc, rx_sts;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
+	rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
+
+	while (rx_frc && work_done < quota && ret > 0) {
+		ret = loongson_canfd_rx(ndev);
+		work_done++;
+		regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
+		rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
+	}
+
+	/* Check for RX FIFO Overflow */
+	regmap_read(priv->regmap, LOONGSON_CANFD_STAT, &sts);
+	if (sts & REG_STAT_DOR) {
+		struct net_device_stats *stats = &ndev->stats;
+		struct can_frame *cf;
+		struct sk_buff *skb;
+
+		netdev_info(ndev, "Loongson canfd RX overflow\n");
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+
+		skb = alloc_can_err_skb(ndev, &cf);
+		if (skb) {
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+			stats->rx_packets++;
+			stats->rx_bytes += cf->can_dlc;
+			netif_rx(skb);
+		}
+
+		/* Clear Data Overrun */
+		regmap_write(priv->regmap, LOONGSON_CANFD_CMD, REG_CMD_CDO);
+	}
+
+	if (!rx_frc && ret != 0) {
+		if (napi_complete_done(napi, work_done)) {
+			/*
+			 * Clear and enable RBNEI. It is level-triggered, so
+			 * there is no race condition.
+			 */
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT,
+				     REG_INT_STAT_RBNEI);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK,
+				     (REG_INT_STAT_RBNEI << 16));
+		}
+	}
+
+	return work_done;
+}
+
+static void loongson_canfd_tx_interrupt(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	enum loongson_canfd_txbuf_sts sts;
+	u32 buf_id;
+
+	guard(spinlock_irqsave)(&priv->tx_lock);
+
+	while ((buf_id = loongson_canfd_get_txbuf_id(priv, &sts)) < LOONGSON_CANFD_TXBUF_NUM) {
+		switch (sts) {
+		case TX_BS_VALID:
+			stats->tx_bytes += can_get_echo_skb(ndev, buf_id, NULL);
+			stats->tx_packets++;
+			break;
+		case TX_BS_FAIL:
+			u32 cnt;
+			/*
+			 * This indicated that retransmit limit has been reached.
+			 * Obviously we should not echo the frame, but also not indicate any
+			 * kind of error. If desired, it was already reported (possible
+			 * multiple times) on each arbitration lost.
+			 */
+			regmap_read(priv->regmap, LOONGSON_CANFD_TX_FR_CNT, &cnt);
+			netdev_warn(ndev, "TXB in FAIL state, TX frame count: %d\n", cnt);
+			can_free_echo_skb(ndev, buf_id, NULL);
+			stats->tx_dropped++;
+			break;
+		case TX_BS_CANCEL:
+			/*
+			 * We *could* re-queue the frame, but multiqueue/abort is
+			 * not supported yet anyway.
+			 */
+			netdev_warn(ndev, "TXB in CANCEL state\n");
+			can_free_echo_skb(ndev, buf_id, NULL);
+			stats->tx_dropped++;
+			break;
+		default:
+			break;
+		}
+
+		loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_SR_CLEAN, buf_id);
+	}
+
+	/*
+	 * Clear the interrupt again. We do not want to receive again interrupt
+	 * for the buffer already handled. If it is the last finished one then
+	 * it would cause log of spurious interrupt.
+	 */
+	regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_TXBHCI);
+
+	/* Check if at least one TX buffer is free */
+	if (loongson_canfd_txbuf_freed(priv))
+		netif_wake_queue(ndev);
+}
+
+/**
+ * loongson_canfd_interrupt() - CAN Isr
+ * @irq: irq number
+ * @dev_id: device id pointer
+ *
+ * This is the Loongson CANFD ISR. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t loongson_canfd_interrupt(int irq, void *dev_id)
+{
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	u32 isr, imask;
+
+	for (unsigned int irq_loops = 0; irq_loops < 10000; irq_loops++) {
+		/* Get the interrupt status */
+		regmap_read(priv->regmap, LOONGSON_CANFD_INT_STAT, &isr);
+		if (!isr)
+			return irq_loops ? IRQ_HANDLED : IRQ_NONE;
+
+		/* Receive Buffer Not Empty Interrupt */
+		if (isr & REG_INT_STAT_RBNEI) {
+			/*
+			 * Mask RXBNEI the first, then clear interrupt and schedule NAPI.
+			 * Even if another IRQ fires, RBNEI will always be 0 (masked).
+			 */
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, REG_INT_STAT_RBNEI);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI);
+			napi_schedule(&priv->napi);
+		}
+
+		/* TX Buffer HW Command Interrupt */
+		if (isr & REG_INT_STAT_TXBHCI)
+			loongson_canfd_tx_interrupt(ndev);
+
+		/* Error interrupts */
+		imask = isr & REG_INT_STAT_ERRORI;
+		if (imask) {
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, imask);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, imask);
+			loongson_canfd_err_interrupt(ndev, isr);
+		}
+
+		/* Ignore RI, TI, LFI, RFI, BSI */
+	}
+
+	netdev_err(ndev, "Intterupt state: 0x%x.\n", isr);
+
+	if (isr & REG_INT_STAT_TXBHCI) {
+		for (unsigned int i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) {
+			u32 sts = loongson_canfd_get_txbuf_sts(priv, i);
+
+			netdev_err(ndev, "TX buf %d status: 0x%x.\n", i, sts);
+		}
+	}
+
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR);
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK,
+			   REG_INT_MASK_SET, REG_INT_MASK_SET);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * loongson_canfd_chip_stop() - Driver stop routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the interrupts and
+ * disable the controller.
+ */
+static void loongson_canfd_chip_stop(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+
+	/* Disable interrupts and disable CAN */
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR);
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK,
+			   REG_INT_MASK_SET, REG_INT_MASK_SET);
+	regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, 0);
+
+	priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * loongson_canfd_open() - Driver open routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int loongson_canfd_open(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	ret = loongson_canfd_reset(ndev);
+	if (ret < 0)
+		return ret;
+
+	/* Common open */
+	ret = open_candev(ndev);
+	if (ret) {
+		netdev_warn(ndev, "open_candev failed!\n");
+		return ret;
+	}
+
+	ret = request_irq(ndev->irq, loongson_canfd_interrupt, IRQF_SHARED, ndev->name, ndev);
+	if (ret < 0) {
+		netdev_err(ndev, "irq allocation for CAN failed\n");
+		goto err_irq;
+	}
+
+	ret = loongson_canfd_chip_start(ndev);
+	if (ret < 0) {
+		netdev_err(ndev, "loongson_canfd_chip_start failed!\n");
+		goto err_chip_start;
+	}
+
+	netdev_info(ndev, "loongson_canfd_device registered\n");
+	napi_enable(&priv->napi);
+	netif_start_queue(ndev);
+
+	return 0;
+
+err_chip_start:
+	free_irq(ndev->irq, ndev);
+err_irq:
+	close_candev(ndev);
+	return ret;
+}
+
+/**
+ * loongson_canfd_close() - Driver close routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int loongson_canfd_close(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+
+	netif_stop_queue(ndev);
+	napi_disable(&priv->napi);
+	loongson_canfd_chip_stop(ndev);
+	free_irq(ndev->irq, ndev);
+	close_candev(ndev);
+
+	return 0;
+}
+
+static const struct net_device_ops loongson_canfd_netdev_ops = {
+	.ndo_open       = loongson_canfd_open,
+	.ndo_stop       = loongson_canfd_close,
+	.ndo_start_xmit = loongson_canfd_start_xmit,
+};
+
+static const struct regmap_range loongson_canfd_reg_table_wr_range[] = {
+	regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_CONF),
+	regmap_reg_range(LOONGSON_CANFD_CMD, LOONGSON_CANFD_CMD),
+	regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_ERL),
+	regmap_reg_range(LOONGSON_CANFD_CTR_PRES, LOONGSON_CANFD_CTR_PRES),
+	regmap_reg_range(LOONGSON_CANFD_SSP_CFG, LOONGSON_CANFD_SSP_CFG),
+	regmap_reg_range(LOONGSON_CANFD_TS, LOONGSON_CANFD_FLT_CTRL),
+	regmap_reg_range(LOONGSON_CANFD_TX_CMD, LOONGSON_CANFD_TX_DATA_18),
+};
+
+static const struct regmap_range loongson_canfd_reg_table_rd_range[] = {
+	regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_STAT),
+	regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_BRE),
+	regmap_reg_range(LOONGSON_CANFD_ERR_CAPT, LOONGSON_CANFD_TX_STAT),
+	regmap_reg_range(LOONGSON_CANFD_TX_SEL, LOONGSON_CANFD_TX_DATA_18),
+};
+
+static const struct regmap_access_table loongson_canfd_reg_table_wr = {
+	.yes_ranges = loongson_canfd_reg_table_wr_range,
+	.n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_wr_range),
+};
+
+static const struct regmap_access_table loongson_canfd_reg_table_rd = {
+	.yes_ranges = loongson_canfd_reg_table_rd_range,
+	.n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_rd_range),
+};
+
+static bool loongson_canfd_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case LOONGSON_CANFD_MODE:
+	case LOONGSON_CANFD_CONF:
+	case LOONGSON_CANFD_STAT:
+	case LOONGSON_CANFD_INT_STAT:
+	case LOONGSON_CANFD_INT_ENA:
+	case LOONGSON_CANFD_INT_MASK:
+	case LOONGSON_CANFD_ERL:
+	case LOONGSON_CANFD_FSTAT:
+	case LOONGSON_CANFD_ERC:
+	case LOONGSON_CANFD_ERR_CAPT:
+	case LOONGSON_CANFD_ALC:
+	case LOONGSON_CANFD_TX_FR_CNT:
+	case LOONGSON_CANFD_RX_STAT:
+	case LOONGSON_CANFD_RX_DATA:
+	case LOONGSON_CANFD_TX_STAT:
+	case LOONGSON_CANFD_TX_SEL:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config loongson_cangfd_regmap = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.wr_table	= &loongson_canfd_reg_table_wr,
+	.rd_table	= &loongson_canfd_reg_table_rd,
+	.volatile_reg	= loongson_canfd_volatile_reg,
+	.max_register	= LOONGSON_CANFD_TX_DATA_18,
+	.cache_type	= REGCACHE_MAPLE,
+};
+
+static int loongson_canfd_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct loongson_canfd_priv *priv;
+	struct net_device *ndev;
+	struct regmap *regmap;
+	struct resource *res;
+	void __iomem *base;
+	u32 clk_rate;
+	int ret, irq;
+
+	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap = devm_regmap_init_mmio(dev, base, &loongson_cangfd_regmap);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	device_property_read_u32(dev, "clock-frequency", &clk_rate);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	/* Create a CAN device instance */
+	ndev = alloc_candev(sizeof(*priv), LOONGSON_CANFD_TXBUF_NUM);
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+	spin_lock_init(&priv->tx_lock);
+	priv->regmap = regmap;
+	priv->res = res;
+
+	priv->can.clock.freq = clk_rate;
+	priv->can.bittiming_const = &loongson_canfd_bit_timing;
+	priv->can.fd.data_bittiming_const = &loongson_canfd_bit_timing_data;
+	priv->can.fd.tdc_const = &loongson_canfd_tdc_data;
+
+	priv->can.fd.do_set_data_bittiming = loongson_canfd_set_data_bittiming;
+	priv->can.fd.do_get_auto_tdcv = loongson_canfd_get_auto_tdcv;
+	priv->can.do_set_mode = loongson_canfd_do_set_mode;
+	priv->can.do_set_bittiming = loongson_canfd_set_bittiming;
+	priv->can.do_get_berr_counter = loongson_canfd_get_berr_counter;
+
+	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY |
+				       CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_ONE_SHOT |
+				       CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD |
+				       CAN_CTRLMODE_PRESUME_ACK | CAN_CTRLMODE_FD_NON_ISO |
+				       CAN_CTRLMODE_TDC_AUTO;
+
+	ndev->irq = irq;
+	ndev->flags |= IFF_ECHO;	/* We support local echo */
+	platform_set_drvdata(pdev, ndev);
+	ndev->netdev_ops = &loongson_canfd_netdev_ops;
+	SET_NETDEV_DEV(ndev, dev);
+
+	ret = loongson_canfd_reset(ndev);
+	if (ret < 0)
+		goto err_candev_free;
+
+	netif_napi_add(ndev, &priv->napi, loongson_canfd_rx_napi);
+
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(dev, "register_candev failed with %d\n", ret);
+		goto err_candev_free;
+	}
+
+	return 0;
+
+err_candev_free:
+	free_candev(ndev);
+	return ret;
+}
+
+static void loongson_canfd_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+
+	unregister_candev(ndev);
+	netif_napi_del(&priv->napi);
+	free_candev(ndev);
+}
+
+static const struct acpi_device_id loongson_canfd_acpi_match[] = {
+	{ "LOON0015" },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, loongson_canfd_acpi_match);
+
+static struct platform_driver loongson_canfd_driver = {
+	.probe  = loongson_canfd_probe,
+	.remove = loongson_canfd_remove,
+	.driver = {
+		.name = DEV_NAME,
+		.acpi_match_table = loongson_canfd_acpi_match,
+	},
+};
+module_platform_driver(loongson_canfd_driver);
+
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_DESCRIPTION("Loongson CAN-FD Controller driver");
+MODULE_LICENSE("GPL");
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support
  2026-06-08  8:49 [PATCH v2 0/2] Add Loongson CAN-FD controller driver Binbin Zhou
  2026-06-08  8:49 ` [PATCH v2 1/2] can: " Binbin Zhou
@ 2026-06-08  8:49 ` Binbin Zhou
  2026-06-08 12:13   ` Vincent Mailhol
  1 sibling, 1 reply; 5+ messages in thread
From: Binbin Zhou @ 2026-06-08  8:49 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Marc Kleine-Budde, Vincent Mailhol,
	Bingxiong Li
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-can, jeffbai,
	Binbin Zhou

Add optional DMA support for RX path using the Loongson APB CMC DMA
engine. When a DMA channel is successfully requested, the driver:

- Uses DMA cyclic transfers to write incoming CAN frames directly to
  a coherent DMA buffer
- Replaces RXBNEI (RX buffer not empty interrupt) with DMADI (DMA
  done interrupt)
- Dynamically switches between DMA and PIO modes based on channel
  availability

This significantly reduces CPU intervention under high RX load,
especially beneficial for CAN FD at higher data rates.

Co-developed-by: Bingxiong Li <libingxiong@loongson.cn>
Signed-off-by: Bingxiong Li <libingxiong@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/net/can/Kconfig          |   2 +-
 drivers/net/can/loongson_canfd.c | 209 ++++++++++++++++++++++++++++---
 2 files changed, 190 insertions(+), 21 deletions(-)

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 28014e264f30..16e07be6438c 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -191,7 +191,7 @@ config CAN_KVASER_PCIEFD
 
 config CAN_LOONGSON_CANFD
 	tristate "Loongson CAN-FD controller"
-	depends on HAS_IOMEM || COMPILE_TEST
+	depends on HAS_IOMEM && (LOONGSON2_APB_CMC_DMA || COMPILE_TEST)
 	select REGMAP_MMIO
 	help
 	  This is a canfd driver switch for the Loongson platform,
diff --git a/drivers/net/can/loongson_canfd.c b/drivers/net/can/loongson_canfd.c
index 3794700e04c8..131d6d008a5d 100644
--- a/drivers/net/can/loongson_canfd.c
+++ b/drivers/net/can/loongson_canfd.c
@@ -6,10 +6,14 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/acpi_dma.h>
 #include <linux/bitfield.h>
 #include <linux/bits.h>
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -376,8 +380,10 @@
 #define DEV_NAME			"loongson_canfd"
 #define LOONGSON_CANFD_ID		0xBABE
 #define LOONGSON_CANFD_DW_BYTE		4
+#define LOONGSON_CANFD_RXBUF_SZ		SZ_1K
 #define LOONGSON_CANFD_TXBUF_NUM	8
 #define LOONGSON_CANFD_MAX_RTXTH	0xf
+#define LOONGSON_CANFD_RXDMA_NUM	(LOONGSON_CANFD_RXBUF_SZ / DMA_SLAVE_BUSWIDTH_4_BYTES)
 
 /**
  * struct loongson_canfd_priv - This definition define CAN driver instance
@@ -385,14 +391,26 @@
  * @napi: NAPI structure
  * @regmap: regmap of the CAN device
  * @res: Pointer to the CAN device respurce
+ * @rx_ch: CAN DMA rx channel
+ * @rx_dma_buf: CAN DMA rx buffer bus address
+ * @rx_buf: CAN DMA rx buffer cpu address
+ * @rx_frc: Last rx data in DMA route
  * @tx_lock: Lock for synchronizing TX interrupt handling
+ * @get_rx_data:  Callback of reading CAN rx data
+ * @get_rx_frc: Callback of the number of messages in the receive buffer
  */
 struct loongson_canfd_priv {
 	struct can_priv		can;		/* must be first member! */
 	struct napi_struct	napi;
 	struct regmap		*regmap;
 	struct resource		*res;
+	struct dma_chan		*rx_ch;
+	dma_addr_t		rx_dma_buf;	/* dma rx buffer bus address */
+	unsigned int		*rx_buf;	/* dma rx buffer cpu address */
+	u16			rx_frc;
 	spinlock_t		tx_lock;	/* protect the sending queue */
+	u32 (*get_rx_data)(struct loongson_canfd_priv *priv);
+	bool (*get_rx_frc)(struct loongson_canfd_priv *priv);
 };
 
 /**
@@ -614,6 +632,125 @@ static int loongson_canfd_reset(struct net_device *ndev)
 	return 0;
 }
 
+/**
+ * loongson_canfd_get_rxdata_in_dma() - Reading RX data in DMA mode
+ * @priv: Pointer to private data
+ *
+ * Return: The CANFD RX data.
+ */
+static u32 loongson_canfd_get_rxdata_in_dma(struct loongson_canfd_priv *priv)
+{
+	u32 c = 0;
+
+	c = priv->rx_buf[LOONGSON_CANFD_RXDMA_NUM - priv->rx_frc--];
+	if (priv->rx_frc == 0)
+		priv->rx_frc = LOONGSON_CANFD_RXDMA_NUM;
+
+	return c;
+}
+
+/**
+ * loongson_canfd_rxfrc_in_dma() - Gets he number of messages in the receive buffer in DMA mode
+ * @priv: Pointer to private data
+ *
+ * Return: The number of messages in the receive buffer
+ */
+static bool loongson_canfd_rxfrc_in_dma(struct loongson_canfd_priv *priv)
+{
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(priv->rx_ch, priv->rx_ch->cookie, &state);
+
+	return priv->rx_frc != (state.residue / DMA_SLAVE_BUSWIDTH_4_BYTES) &&
+	       (status == DMA_IN_PROGRESS);
+}
+
+static void loongson_canfd_rxdma_remove(struct loongson_canfd_priv *priv, struct device *dev)
+{
+	dma_free_coherent(dev, LOONGSON_CANFD_RXBUF_SZ, priv->rx_buf, priv->rx_dma_buf);
+}
+
+/**
+ * loongson_canfd_rxdma_init() - Loongson canfd RXDMA initialization
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: The number of messages in the receive buffer
+ */
+static int loongson_canfd_rxdma_init(struct net_device *ndev)
+{
+	struct loongson_canfd_priv *priv = netdev_priv(ndev);
+	struct dma_async_tx_descriptor *desc = NULL;
+	struct device *dev = ndev->dev.parent;
+	struct dma_slave_config config;
+	int ret;
+
+	priv->rx_buf = dma_alloc_coherent(dev, LOONGSON_CANFD_RXBUF_SZ,
+					  &priv->rx_dma_buf, GFP_KERNEL);
+	if (!priv->rx_buf)
+		return -ENOMEM;
+
+	/* Configure DMA channel */
+	memset(&config, 0, sizeof(config));
+	config.src_addr = priv->res->start + LOONGSON_CANFD_RX_DATA;
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	ret = dmaengine_slave_config(priv->rx_ch, &config);
+	if (ret < 0) {
+		netdev_err(ndev, "Loongson canfd rxdma channel config failed\n");
+		goto err_config;
+	}
+
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(priv->rx_ch, priv->rx_dma_buf,
+					 LOONGSON_CANFD_RXBUF_SZ, LOONGSON_CANFD_RXBUF_SZ,
+					 DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+	if (!desc) {
+		netdev_err(ndev, "Loongson canfd rxdma cyclic transaction failed\n");
+		ret = -EBUSY;
+		goto err_config;
+	}
+
+	dmaengine_submit(desc);
+	dma_async_issue_pending(priv->rx_ch);
+
+	return 0;
+
+err_config:
+	loongson_canfd_rxdma_remove(priv, dev);
+	return ret;
+}
+
+/**
+ * loongson_canfd_get_rxdata_in_poll() - Reading RX data in poll mode
+ * @priv: Pointer to private data
+ *
+ * Return: The CANFD RX data.
+ */
+static u32 loongson_canfd_get_rxdata_in_poll(struct loongson_canfd_priv *priv)
+{
+	u32 data;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
+
+	return data;
+}
+
+/**
+ * loongson_canfd_rxfrc_in_poll() - Gets he number of messages in the receive buffer in poll mode
+ * @priv: Pointer to private data
+ *
+ * Return: The number of messages in the receive buffer
+ */
+static bool loongson_canfd_rxfrc_in_poll(struct loongson_canfd_priv *priv)
+{
+	u32 rx_sts;
+
+	regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
+
+	return FIELD_GET(REG_RX_STAT_RXFRC, rx_sts) ? true : false;
+}
+
 /**
  * loongson_canfd_set_btr() - Sets CAN bus bit timing in Loongson CANFD
  * @ndev: Pointer to net_device structure
@@ -838,8 +975,8 @@ static int loongson_canfd_chip_start(struct net_device *ndev)
 	loongson_canfd_set_mode(priv, &mode);
 
 	/* Configure interrupts */
-	int_ena = REG_INT_STAT_RBNEI | REG_INT_STAT_TXBHCI |
-		  REG_INT_STAT_EWLI | REG_INT_STAT_FCSI;
+	int_ena = REG_INT_STAT_TXBHCI | REG_INT_STAT_EWLI | REG_INT_STAT_FCSI;
+	int_ena |= priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI;
 
 	/* Bus error reporting */
 	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
@@ -1074,12 +1211,12 @@ static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struc
 
 	/* Data */
 	for (i = 0; i < len; i += LOONGSON_CANFD_DW_BYTE) {
-		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
+		data = priv->get_rx_data(priv);
 		*(__le32 *)(cf->data + i) = cpu_to_le32(data);
 	}
 
 	while (unlikely(i < wc * LOONGSON_CANFD_DW_BYTE)) {
-		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
+		data = priv->get_rx_data(priv);
 		i += LOONGSON_CANFD_DW_BYTE;
 	}
 }
@@ -1101,8 +1238,8 @@ static int loongson_canfd_rx(struct net_device *ndev)
 	struct sk_buff *skb;
 	u32 meta0, meta1;
 
-	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta0);
-	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta1);
+	meta0 = priv->get_rx_data(priv);
+	meta1 = priv->get_rx_data(priv);
 
 	/* Number of characters received */
 	if (!FIELD_GET(REG_FRAME_META1_RWCNT, meta1))
@@ -1328,16 +1465,16 @@ static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota)
 	struct net_device *ndev = napi->dev;
 	struct loongson_canfd_priv *priv = netdev_priv(ndev);
 	int work_done = 0, ret = 1;
-	u32 sts, rx_frc, rx_sts;
+	int int_ena = priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI;
+	bool rx_frc;
+	u32 sts;
 
-	regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
-	rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
+	rx_frc = priv->get_rx_frc(priv);
 
 	while (rx_frc && work_done < quota && ret > 0) {
 		ret = loongson_canfd_rx(ndev);
 		work_done++;
-		regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
-		rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
+		rx_frc = priv->get_rx_frc(priv);
 	}
 
 	/* Check for RX FIFO Overflow */
@@ -1367,13 +1504,11 @@ static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota)
 	if (!rx_frc && ret != 0) {
 		if (napi_complete_done(napi, work_done)) {
 			/*
-			 * Clear and enable RBNEI. It is level-triggered, so
+			 * Clear and enable RBNEI/DMADI. It is level-triggered, so
 			 * there is no race condition.
 			 */
-			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT,
-				     REG_INT_STAT_RBNEI);
-			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK,
-				     (REG_INT_STAT_RBNEI << 16));
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, int_ena);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, (int_ena << 16));
 		}
 	}
 
@@ -1460,13 +1595,14 @@ static irqreturn_t loongson_canfd_interrupt(int irq, void *dev_id)
 			return irq_loops ? IRQ_HANDLED : IRQ_NONE;
 
 		/* Receive Buffer Not Empty Interrupt */
-		if (isr & REG_INT_STAT_RBNEI) {
+		imask = priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI;
+		if (isr & imask) {
 			/*
 			 * Mask RXBNEI the first, then clear interrupt and schedule NAPI.
 			 * Even if another IRQ fires, RBNEI will always be 0 (masked).
 			 */
-			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, REG_INT_STAT_RBNEI);
-			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, imask);
+			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, imask);
 			napi_schedule(&priv->napi);
 		}
 
@@ -1663,6 +1799,7 @@ static int loongson_canfd_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct loongson_canfd_priv *priv;
 	struct net_device *ndev;
+	struct dma_chan *rx_ch;
 	struct regmap *regmap;
 	struct resource *res;
 	void __iomem *base;
@@ -1683,14 +1820,24 @@ static int loongson_canfd_probe(struct platform_device *pdev)
 	if (irq < 0)
 		return irq;
 
+	rx_ch = dma_request_chan(dev, "rx");
+	if (PTR_ERR(rx_ch) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	if (IS_ERR(rx_ch)) {
+		dev_warn(dev, "Fall back in poll mode for any non-deferral error.\n");
+		rx_ch = NULL;
+	}
+
 	/* Create a CAN device instance */
 	ndev = alloc_candev(sizeof(*priv), LOONGSON_CANFD_TXBUF_NUM);
 	if (!ndev)
-		return -ENOMEM;
+		goto err_dma_rx;
 
 	priv = netdev_priv(ndev);
 	spin_lock_init(&priv->tx_lock);
 	priv->regmap = regmap;
+	priv->rx_ch = rx_ch;
 	priv->res = res;
 
 	priv->can.clock.freq = clk_rate;
@@ -1720,6 +1867,19 @@ static int loongson_canfd_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto err_candev_free;
 
+	if (priv->rx_ch) {
+		priv->get_rx_data = loongson_canfd_get_rxdata_in_dma;
+		priv->get_rx_frc = loongson_canfd_rxfrc_in_dma;
+		priv->rx_frc = LOONGSON_CANFD_RXDMA_NUM;
+		ret = loongson_canfd_rxdma_init(ndev);
+		if (ret) {
+			dev_err(dev, "interrupt mode used for rx (no dma)\n");
+			goto err_candev_free;
+		}
+	} else {
+		priv->get_rx_data = loongson_canfd_get_rxdata_in_poll;
+		priv->get_rx_frc = loongson_canfd_rxfrc_in_poll;
+	}
 	netif_napi_add(ndev, &priv->napi, loongson_canfd_rx_napi);
 
 	ret = register_candev(ndev);
@@ -1732,6 +1892,9 @@ static int loongson_canfd_probe(struct platform_device *pdev)
 
 err_candev_free:
 	free_candev(ndev);
+err_dma_rx:
+	if (rx_ch)
+		dma_release_channel(rx_ch);
 	return ret;
 }
 
@@ -1740,6 +1903,11 @@ static void loongson_canfd_remove(struct platform_device *pdev)
 	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct loongson_canfd_priv *priv = netdev_priv(ndev);
 
+	if (priv->rx_ch) {
+		loongson_canfd_rxdma_remove(priv, &pdev->dev);
+		dma_release_channel(priv->rx_ch);
+	}
+
 	unregister_candev(ndev);
 	netif_napi_del(&priv->napi);
 	free_candev(ndev);
@@ -1761,6 +1929,7 @@ static struct platform_driver loongson_canfd_driver = {
 };
 module_platform_driver(loongson_canfd_driver);
 
+MODULE_SOFTDEP("pre: loongson2-apb-cmc-dma");
 MODULE_AUTHOR("Loongson Technology Corporation Limited");
 MODULE_DESCRIPTION("Loongson CAN-FD Controller driver");
 MODULE_LICENSE("GPL");
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support
  2026-06-08  8:49 ` [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support Binbin Zhou
@ 2026-06-08 12:13   ` Vincent Mailhol
  0 siblings, 0 replies; 5+ messages in thread
From: Vincent Mailhol @ 2026-06-08 12:13 UTC (permalink / raw)
  To: Binbin Zhou, Binbin Zhou, Huacai Chen, Marc Kleine-Budde,
	Bingxiong Li
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-can, jeffbai

On 08/06/2026 at 10:49, Binbin Zhou wrote:
> Add optional DMA support for RX path using the Loongson APB CMC DMA
> engine. When a DMA channel is successfully requested, the driver:
> 
> - Uses DMA cyclic transfers to write incoming CAN frames directly to
>   a coherent DMA buffer
> - Replaces RXBNEI (RX buffer not empty interrupt) with DMADI (DMA
>   done interrupt)
> - Dynamically switches between DMA and PIO modes based on channel
>   availability
> 
> This significantly reduces CPU intervention under high RX load,
> especially beneficial for CAN FD at higher data rates.
> 
> Co-developed-by: Bingxiong Li <libingxiong@loongson.cn>
> Signed-off-by: Bingxiong Li <libingxiong@loongson.cn>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---

Please check the W=2 warnings:

  $ make W=12 drivers/net/can/loongson_canfd.o
    DESCEND objtool
    INSTALL libsubcmd_headers
    CC      drivers/net/can/loongson_canfd.o
  drivers/net/can/loongson_canfd.c:1917:9: warning: missing initializer for field 'driver_data' of 'const struct acpi_device_id' [-Wmissing-field-initializers]
   1917 |         { "LOON0015" },
        |         ^
  In file included from ./include/linux/acpi.h:16,
                   from drivers/net/can/loongson_canfd.c:8:
  ./include/linux/mod_devicetable.h:219:24: note: 'driver_data' declared here
    219 |         kernel_ulong_t driver_data;
        |                        ^~~~~~~~~~~
  drivers/net/can/loongson_canfd.c: In function 'loongson_canfd_start_xmit':
  drivers/net/can/loongson_canfd.c:1116:13: warning: 'buf_id' may be used uninitialized [-Wmaybe-uninitialized]
   1116 |         u32 buf_id, tx_stat, i = 0;
        |             ^~~~~~
  drivers/net/can/loongson_canfd.c:1116:13: note: 'buf_id' was declared here
   1116 |         u32 buf_id, tx_stat, i = 0;
        |             ^~~~~~
  In function 'loongson_canfd_insert_frame',
      inlined from 'loongson_canfd_start_xmit' at drivers/net/can/loongson_canfd.c:1144:7:
  drivers/net/can/loongson_canfd.c:1079:15: warning: 'meta1' may be used uninitialized [-Wmaybe-uninitialized]
   1079 |         meta1 |= FIELD_PREP(REG_FRAME_META1_DLC, can_fd_len2dlc(cf->len));
        |               ^~
  drivers/net/can/loongson_canfd.c: In function 'loongson_canfd_start_xmit':
  drivers/net/can/loongson_canfd.c:1052:20: note: 'meta1' was declared here
   1052 |         u32 meta0, meta1;
        |                    ^~~~~
  drivers/net/can/loongson_canfd.c: In function 'loongson_canfd_probe':
  drivers/net/can/loongson_canfd.c:1807:13: warning: 'ret' may be used uninitialized [-Wmaybe-uninitialized]
   1807 |         int ret, irq;
        |             ^~~
  drivers/net/can/loongson_canfd.c:1807:13: note: 'ret' was declared here
   1807 |         int ret, irq;
        |             ^~~
  drivers/net/can/loongson_canfd.c: At top level:
  drivers/net/can/loongson_canfd.c:47:9: warning: macro 'LOONGSON_CANFD_RX_FR_CNT' is not used [-Wunused-macros]
     47 | #define LOONGSON_CANFD_RX_FR_CNT        0x50    /* Receive Message Count Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:354:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_14' is not used [-Wunused-macros]
    354 | #define LOONGSON_CANFD_FRAME_DB_14      0x3c
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:307:9: warning: macro 'REG_RX_STAT_RXF' is not used [-Wunused-macros]
    307 | #define REG_RX_STAT_RXF                 BIT(1)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:255:9: warning: macro 'REG_FIL_A_VAL' is not used [-Wunused-macros]
    255 | #define REG_FIL_A_VAL                   GENMASK(28, 0)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:105:9: warning: macro 'REG_MODE_RTSOP' is not used [-Wunused-macros]
    105 | #define REG_MODE_RTSOP                  BIT(12)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:221:9: warning: macro 'REG_RX_FR_CNT_VAL' is not used [-Wunused-macros]
    221 | #define REG_RX_FR_CNT_VAL               GENMASK(31, 0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:273:9: warning: macro 'REG_FIL_R_HI_VAL' is not used [-Wunused-macros]
    273 | #define REG_FIL_R_HI_VAL                GENMASK(28, 0)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:234:9: warning: macro 'REG_DEBUG_PC_CRCD' is not used [-Wunused-macros]
    234 | #define REG_DEBUG_PC_CRCD               BIT(11)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:249:9: warning: macro 'REG_FRC_FRC_NBT' is not used [-Wunused-macros]
    249 | #define REG_FRC_FRC_NBT                 GENMASK(7, 0)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:336:9: warning: macro 'REG_TX_SEL_BUF_CNT' is not used [-Wunused-macros]
    336 | #define REG_TX_SEL_BUF_CNT              GENMASK(7, 4)
        |         ^~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:218:9: warning: macro 'REG_SSP_CFG_SAT' is not used [-Wunused-macros]
    218 | #define REG_SSP_CFG_SAT                 BIT(10)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:91:9: warning: macro 'REG_ID_VER_MAJ' is not used [-Wunused-macros]
     91 | #define REG_ID_VER_MAJ                  GENMASK(31, 24)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:359:9: warning: macro 'REG_FRAME_META0_ID_EXT' is not used [-Wunused-macros]
    359 | #define REG_FRAME_META0_ID_EXT          GENMASK(17, 0)
        |         ^~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:245:9: warning: macro 'REG_TS_PSC' is not used [-Wunused-macros]
    245 | #define REG_TS_PSC                      GENMASK(24, 16)
        |         ^~~~~~~~~~
  drivers/net/can/loongson_canfd.c:231:9: warning: macro 'REG_DEBUG_PC_DAT' is not used [-Wunused-macros]
    231 | #define REG_DEBUG_PC_DAT                BIT(8)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:227:9: warning: macro 'REG_DEBUG_STF_CNT' is not used [-Wunused-macros]
    227 | #define REG_DEBUG_STF_CNT               GENMASK(2, 0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:193:9: warning: macro 'REG_BRE_NORM' is not used [-Wunused-macros]
    193 | #define REG_BRE_NORM                    GENMASK(15, 0)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:238:9: warning: macro 'REG_DEBUG_PC_INT' is not used [-Wunused-macros]
    238 | #define REG_DEBUG_PC_INT                BIT(15)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:56:9: warning: macro 'LOONGSON_CANFD_FLT_B_VAL' is not used [-Wunused-macros]
     56 | #define LOONGSON_CANFD_FLT_B_VAL        0x74    /* Filter B value Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:136:9: warning: macro 'REG_CMD_CPEXS' is not used [-Wunused-macros]
    136 | #define REG_CMD_CPEXS                   BIT(7)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:126:9: warning: macro 'REG_STAT_PEXS' is not used [-Wunused-macros]
    126 | #define REG_STAT_PEXS                   BIT(8)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:78:9: warning: macro 'LOONGSON_CANFD_TX_DATA_10' is not used [-Wunused-macros]
     78 | #define LOONGSON_CANFD_TX_DATA_10       0xd4
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:79:9: warning: macro 'LOONGSON_CANFD_TX_DATA_11' is not used [-Wunused-macros]
     79 | #define LOONGSON_CANFD_TX_DATA_11       0xd8
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:81:9: warning: macro 'LOONGSON_CANFD_TX_DATA_13' is not used [-Wunused-macros]
     81 | #define LOONGSON_CANFD_TX_DATA_13       0xe0
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:83:9: warning: macro 'LOONGSON_CANFD_TX_DATA_15' is not used [-Wunused-macros]
     83 | #define LOONGSON_CANFD_TX_DATA_15       0xe8
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:84:9: warning: macro 'LOONGSON_CANFD_TX_DATA_16' is not used [-Wunused-macros]
     84 | #define LOONGSON_CANFD_TX_DATA_16       0xec
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:85:9: warning: macro 'LOONGSON_CANFD_TX_DATA_17' is not used [-Wunused-macros]
     85 | #define LOONGSON_CANFD_TX_DATA_17       0xf0
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:352:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_12' is not used [-Wunused-macros]
    352 | #define LOONGSON_CANFD_FRAME_DB_12      0x34
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:353:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_13' is not used [-Wunused-macros]
    353 | #define LOONGSON_CANFD_FRAME_DB_13      0x38
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:356:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_17' is not used [-Wunused-macros]
    356 | #define LOONGSON_CANFD_FRAME_DB_17      0x44
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:139:9: warning: macro 'REG_INT_STAT_RXI' is not used [-Wunused-macros]
    139 | #define REG_INT_STAT_RXI                BIT(0)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:267:9: warning: macro 'REG_FIL_C_VAL' is not used [-Wunused-macros]
    267 | #define REG_FIL_C_VAL                   GENMASK(28, 0)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:252:9: warning: macro 'REG_FIL_A_MASK' is not used [-Wunused-macros]
    252 | #define REG_FIL_A_MASK                  GENMASK(28, 0)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:292:9: warning: macro 'REG_FIL_CTRL_SFA' is not used [-Wunused-macros]
    292 | #define REG_FIL_CTRL_SFA                BIT(16)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:232:9: warning: macro 'REG_DEBUG_PC_STC' is not used [-Wunused-macros]
    232 | #define REG_DEBUG_PC_STC                BIT(9)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:59:9: warning: macro 'LOONGSON_CANFD_FLT_R_LOW' is not used [-Wunused-macros]
     59 | #define LOONGSON_CANFD_FLT_R_LOW        0x80    /* Range Filter Low Threshold Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:58:9: warning: macro 'LOONGSON_CANFD_FLT_C_VAL' is not used [-Wunused-macros]
     58 | #define LOONGSON_CANFD_FLT_C_VAL        0x7c    /* Filter C value Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:235:9: warning: macro 'REG_DEBUG_PC_ACK' is not used [-Wunused-macros]
    235 | #define REG_DEBUG_PC_ACK                BIT(12)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:140:9: warning: macro 'REG_INT_STAT_TXI' is not used [-Wunused-macros]
    140 | #define REG_INT_STAT_TXI                BIT(1)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:57:9: warning: macro 'LOONGSON_CANFD_FLT_C_MASK' is not used [-Wunused-macros]
     57 | #define LOONGSON_CANFD_FLT_C_MASK       0x78    /* Filter C Mask Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:317:9: warning: macro 'REG_TX_STAT_TXS' is not used [-Wunused-macros]
    317 | #define REG_TX_STAT_TXS                 GENMASK(10, 8)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:306:9: warning: macro 'REG_RX_STAT_RXE' is not used [-Wunused-macros]
    306 | #define REG_RX_STAT_RXE                 BIT(0)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:104:9: warning: macro 'REG_MODE_ITSM' is not used [-Wunused-macros]
    104 | #define REG_MODE_ITSM                   BIT(10)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:100:9: warning: macro 'REG_MODE_ROM' is not used [-Wunused-macros]
    100 | #define REG_MODE_ROM                    BIT(6)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:298:9: warning: macro 'REG_RX_MEM_INFO_BUFF_SIZE' is not used [-Wunused-macros]
    298 | #define REG_RX_MEM_INFO_BUFF_SIZE       GENMASK(12, 0)
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:248:9: warning: macro 'REG_FRC_FRC_DBT' is not used [-Wunused-macros]
    248 | #define REG_FRC_FRC_DBT                 GENMASK(15, 8)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:230:9: warning: macro 'REG_DEBUG_PC_CON' is not used [-Wunused-macros]
    230 | #define REG_DEBUG_PC_CON                BIT(7)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:233:9: warning: macro 'REG_DEBUG_PC_CRC' is not used [-Wunused-macros]
    233 | #define REG_DEBUG_PC_CRC                BIT(10)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:198:9: warning: macro 'REG_CTR_PRES_PTX' is not used [-Wunused-macros]
    198 | #define REG_CTR_PRES_PTX                BIT(9)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:124:9: warning: macro 'REG_STAT_EWL' is not used [-Wunused-macros]
    124 | #define REG_STAT_EWL                    BIT(6)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:114:9: warning: macro 'REG_CONF_PEX' is not used [-Wunused-macros]
    114 | #define REG_CONF_PEX                    BIT(8)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:96:9: warning: macro 'REG_MODE_STM' is not used [-Wunused-macros]
     96 | #define REG_MODE_STM                    BIT(2)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:150:9: warning: macro 'REG_INT_STAT_OFI' is not used [-Wunused-macros]
    150 | #define REG_INT_STAT_OFI                BIT(11)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:89:9: warning: macro 'REG_ID_MASK' is not used [-Wunused-macros]
     89 | #define REG_ID_MASK                     GENMASK(15, 0)
        |         ^~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:194:9: warning: macro 'REG_BRE_FD_DATA' is not used [-Wunused-macros]
    194 | #define REG_BRE_FD_DATA                 GENMASK(31, 16)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:270:9: warning: macro 'REG_FIL_R_LOW_VAL' is not used [-Wunused-macros]
    270 | #define REG_FIL_R_LOW_VAL               GENMASK(28, 0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:261:9: warning: macro 'REG_FIL_B_VAL' is not used [-Wunused-macros]
    261 | #define REG_FIL_B_VAL                   GENMASK(28, 0)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:55:9: warning: macro 'LOONGSON_CANFD_FLT_B_MASK' is not used [-Wunused-macros]
     55 | #define LOONGSON_CANFD_FLT_B_MASK       0x70    /* Filter B Mask Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:43:9: warning: macro 'LOONGSON_CANFD_RETX_CNT' is not used [-Wunused-macros]
     43 | #define LOONGSON_CANFD_RETX_CNT         0x40    /* Retransmission Count Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:367:9: warning: macro 'REG_FRAME_META1_TIMESTAMP' is not used [-Wunused-macros]
    367 | #define REG_FRAME_META1_TIMESTAMP       GENMASK(15, 0)
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:229:9: warning: macro 'REG_DEBUG_PC_ARB' is not used [-Wunused-macros]
    229 | #define REG_DEBUG_PC_ARB                BIT(6)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:375:9: warning: macro 'REG_FRAME_TEST_FSTC' is not used [-Wunused-macros]
    375 | #define REG_FRAME_TEST_FSTC             BIT(0)
        |         ^~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:178:9: warning: macro 'REG_ERL_ERP' is not used [-Wunused-macros]
    178 | #define REG_ERL_ERP                     GENMASK(7, 0)
        |         ^~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:378:9: warning: macro 'REG_FRAME_TEST_TPRM' is not used [-Wunused-macros]
    378 | #define REG_FRAME_TEST_TPRM             GENMASK(12, 8)
        |         ^~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:264:9: warning: macro 'REG_FIL_C_MASK' is not used [-Wunused-macros]
    264 | #define REG_FIL_C_MASK                  GENMASK(28, 0)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:241:9: warning: macro 'REG_DEBUG_PC_SOF' is not used [-Wunused-macros]
    241 | #define REG_DEBUG_PC_SOF                BIT(18)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:290:9: warning: macro 'REG_FIL_CTRL_FRFB' is not used [-Wunused-macros]
    290 | #define REG_FIL_CTRL_FRFB               BIT(14)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:288:9: warning: macro 'REG_FIL_CTRL_FRNB' is not used [-Wunused-macros]
    288 | #define REG_FIL_CTRL_FRNB               BIT(12)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:244:9: warning: macro 'REG_TS_TIMESTAMP' is not used [-Wunused-macros]
    244 | #define REG_TS_TIMESTAMP                GENMASK(15, 0)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:118:9: warning: macro 'REG_STAT_RXNE' is not used [-Wunused-macros]
    118 | #define REG_STAT_RXNE                   BIT(0)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:157:9: warning: macro 'REG_INT_ENA_SET' is not used [-Wunused-macros]
    157 | #define REG_INT_ENA_SET                 GENMASK(12, 0)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:299:9: warning: macro 'REG_RX_MEM_INFO_MEM_FREE' is not used [-Wunused-macros]
    299 | #define REG_RX_MEM_INFO_MEM_FREE        GENMASK(28, 16)
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:161:9: warning: macro 'REG_INT_MASK_CLR' is not used [-Wunused-macros]
    161 | #define REG_INT_MASK_CLR                GENMASK(28, 16)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:281:9: warning: macro 'REG_FIL_CTRL_FBNE' is not used [-Wunused-macros]
    281 | #define REG_FIL_CTRL_FBNE               BIT(5)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:279:9: warning: macro 'REG_FIL_CTRL_FAFE' is not used [-Wunused-macros]
    279 | #define REG_FIL_CTRL_FAFE               BIT(3)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:123:9: warning: macro 'REG_STAT_TXS' is not used [-Wunused-macros]
    123 | #define REG_STAT_TXS                    BIT(5)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:80:9: warning: macro 'LOONGSON_CANFD_TX_DATA_12' is not used [-Wunused-macros]
     80 | #define LOONGSON_CANFD_TX_DATA_12       0xdc
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:82:9: warning: macro 'LOONGSON_CANFD_TX_DATA_14' is not used [-Wunused-macros]
     82 | #define LOONGSON_CANFD_TX_DATA_14       0xe4
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:70:9: warning: macro 'LOONGSON_CANFD_TX_DATA_2' is not used [-Wunused-macros]
     70 | #define LOONGSON_CANFD_TX_DATA_2        0xb4
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:74:9: warning: macro 'LOONGSON_CANFD_TX_DATA_6' is not used [-Wunused-macros]
     74 | #define LOONGSON_CANFD_TX_DATA_6        0xc4
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:75:9: warning: macro 'LOONGSON_CANFD_TX_DATA_7' is not used [-Wunused-macros]
     75 | #define LOONGSON_CANFD_TX_DATA_7        0xc8
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:77:9: warning: macro 'LOONGSON_CANFD_TX_DATA_9' is not used [-Wunused-macros]
     77 | #define LOONGSON_CANFD_TX_DATA_9        0xd0
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:291:9: warning: macro 'REG_FIL_CTRL_FRFE' is not used [-Wunused-macros]
    291 | #define REG_FIL_CTRL_FRFE               BIT(15)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:335:9: warning: macro 'REG_TX_SEL_BUF_SEL' is not used [-Wunused-macros]
    335 | #define REG_TX_SEL_BUF_SEL              GENMASK(3, 0)
        |         ^~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:134:9: warning: macro 'REG_CMD_RXFCRST' is not used [-Wunused-macros]
    134 | #define REG_CMD_RXFCRST                 BIT(5)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:147:9: warning: macro 'REG_INT_STAT_BSI' is not used [-Wunused-macros]
    147 | #define REG_INT_STAT_BSI                BIT(8)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:99:9: warning: macro 'REG_MODE_TTTM' is not used [-Wunused-macros]
     99 | #define REG_MODE_TTTM                   BIT(5)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:51:9: warning: macro 'LOONGSON_CANFD_TX_FRM_TST' is not used [-Wunused-macros]
     51 | #define LOONGSON_CANFD_TX_FRM_TST       0x60    /* Transmit Message Debug Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:206:9: warning: macro 'REG_RETX_CNT_VAL' is not used [-Wunused-macros]
    206 | #define REG_RETX_CNT_VAL                GENMASK(3, 0)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:228:9: warning: macro 'REG_DEBUG_DSTF_CNT' is not used [-Wunused-macros]
    228 | #define REG_DEBUG_DSTF_CNT              GENMASK(5, 3)
        |         ^~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:199:9: warning: macro 'REG_CTR_PRES_PRX' is not used [-Wunused-macros]
    199 | #define REG_CTR_PRES_PRX                BIT(10)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:142:9: warning: macro 'REG_INT_STAT_DOI' is not used [-Wunused-macros]
    142 | #define REG_INT_STAT_DOI                BIT(3)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:125:9: warning: macro 'REG_STAT_IDLE' is not used [-Wunused-macros]
    125 | #define REG_STAT_IDLE                   BIT(7)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:197:9: warning: macro 'REG_CTR_PRES_CTPV' is not used [-Wunused-macros]
    197 | #define REG_CTR_PRES_CTPV               GENMASK(8, 0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:97:9: warning: macro 'REG_MODE_AFM' is not used [-Wunused-macros]
     97 | #define REG_MODE_AFM                    BIT(3)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:240:9: warning: macro 'REG_DEBUG_PC_OVR' is not used [-Wunused-macros]
    240 | #define REG_DEBUG_PC_OVR                BIT(17)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:310:9: warning: macro 'REG_RX_STAT_RTSOP' is not used [-Wunused-macros]
    310 | #define REG_RX_STAT_RTSOP               BIT(16)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:313:9: warning: macro 'REG_RX_DATA_VAL' is not used [-Wunused-macros]
    313 | #define REG_RX_DATA_VAL                 GENMASK(31, 0)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:63:9: warning: macro 'LOONGSON_CANFD_RX_PRT' is not used [-Wunused-macros]
     63 | #define LOONGSON_CANFD_RX_PRT           0x90    /* Receive Buffer Pointer Register */
        |         ^~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:258:9: warning: macro 'REG_FIL_B_MASK' is not used [-Wunused-macros]
    258 | #define REG_FIL_B_MASK                  GENMASK(28, 0)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:289:9: warning: macro 'REG_FIL_CTRL_FRNE' is not used [-Wunused-macros]
    289 | #define REG_FIL_CTRL_FRNE               BIT(13)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:377:9: warning: macro 'REG_FRAME_TEST_SDLC' is not used [-Wunused-macros]
    377 | #define REG_FRAME_TEST_SDLC             BIT(2)
        |         ^~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:293:9: warning: macro 'REG_FIL_CTRL_SFB' is not used [-Wunused-macros]
    293 | #define REG_FIL_CTRL_SFB                BIT(17)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:294:9: warning: macro 'REG_FIL_CTRL_SFC' is not used [-Wunused-macros]
    294 | #define REG_FIL_CTRL_SFC                BIT(18)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:295:9: warning: macro 'REG_FIL_CTRL_SFR' is not used [-Wunused-macros]
    295 | #define REG_FIL_CTRL_SFR                BIT(19)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:224:9: warning: macro 'REG_TX_FR_CNT_VAL' is not used [-Wunused-macros]
    224 | #define REG_TX_FR_CNT_VAL               GENMASK(31, 0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:60:9: warning: macro 'LOONGSON_CANFD_FLT_R_HI' is not used [-Wunused-macros]
     60 | #define LOONGSON_CANFD_FLT_R_HI         0x84    /* Range Filter High Threshold Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:71:9: warning: macro 'LOONGSON_CANFD_TX_DATA_3' is not used [-Wunused-macros]
     71 | #define LOONGSON_CANFD_TX_DATA_3        0xb8
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:73:9: warning: macro 'LOONGSON_CANFD_TX_DATA_5' is not used [-Wunused-macros]
     73 | #define LOONGSON_CANFD_TX_DATA_5        0xc0
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:76:9: warning: macro 'LOONGSON_CANFD_TX_DATA_8' is not used [-Wunused-macros]
     76 | #define LOONGSON_CANFD_TX_DATA_8        0xcc
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:115:9: warning: macro 'REG_CONF_FDRF' is not used [-Wunused-macros]
    115 | #define REG_CONF_FDRF                   BIT(10)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:122:9: warning: macro 'REG_STAT_RXS' is not used [-Wunused-macros]
    122 | #define REG_STAT_RXS                    BIT(4)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:303:9: warning: macro 'REG_RX_PTR_RPP' is not used [-Wunused-macros]
    303 | #define REG_RX_PTR_RPP                  GENMASK(27, 16)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:286:9: warning: macro 'REG_FIL_CTRL_FCFB' is not used [-Wunused-macros]
    286 | #define REG_FIL_CTRL_FCFB               BIT(10)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:287:9: warning: macro 'REG_FIL_CTRL_FCFE' is not used [-Wunused-macros]
    287 | #define REG_FIL_CTRL_FCFE               BIT(11)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:121:9: warning: macro 'REG_STAT_EFT' is not used [-Wunused-macros]
    121 | #define REG_STAT_EFT                    BIT(3)
        |         ^~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:130:9: warning: macro 'REG_CMD_RXRPMV' is not used [-Wunused-macros]
    130 | #define REG_CMD_RXRPMV                  BIT(1)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:342:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_2' is not used [-Wunused-macros]
    342 | #define LOONGSON_CANFD_FRAME_DB_2       0xc
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:344:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_4' is not used [-Wunused-macros]
    344 | #define LOONGSON_CANFD_FRAME_DB_4       0x14
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:347:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_7' is not used [-Wunused-macros]
    347 | #define LOONGSON_CANFD_FRAME_DB_7       0x20
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:348:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_8' is not used [-Wunused-macros]
    348 | #define LOONGSON_CANFD_FRAME_DB_8       0x24
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:343:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_3' is not used [-Wunused-macros]
    343 | #define LOONGSON_CANFD_FRAME_DB_3       0x10
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:349:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_9' is not used [-Wunused-macros]
    349 | #define LOONGSON_CANFD_FRAME_DB_9       0x28
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:345:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_5' is not used [-Wunused-macros]
    345 | #define LOONGSON_CANFD_FRAME_DB_5       0x18
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:346:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_6' is not used [-Wunused-macros]
    346 | #define LOONGSON_CANFD_FRAME_DB_6       0x1c
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:52:9: warning: macro 'LOONGSON_CANFD_FRC_DIV' is not used [-Wunused-macros]
     52 | #define LOONGSON_CANFD_FRC_DIV          0x64    /* Fractional Divider Ratio Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:350:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_10' is not used [-Wunused-macros]
    350 | #define LOONGSON_CANFD_FRAME_DB_10      0x2c
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:351:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_11' is not used [-Wunused-macros]
    351 | #define LOONGSON_CANFD_FRAME_DB_11      0x30
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:355:9: warning: macro 'LOONGSON_CANFD_FRAME_DB_16' is not used [-Wunused-macros]
    355 | #define LOONGSON_CANFD_FRAME_DB_16      0x40
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:54:9: warning: macro 'LOONGSON_CANFD_FLT_A_VAL' is not used [-Wunused-macros]
     54 | #define LOONGSON_CANFD_FLT_A_VAL        0x6c    /* Filter A value Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:237:9: warning: macro 'REG_DEBUG_PC_EOF' is not used [-Wunused-macros]
    237 | #define REG_DEBUG_PC_EOF                BIT(14)
        |         ^~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:49:9: warning: macro 'LOONGSON_CANFD_DEBUG' is not used [-Wunused-macros]
     49 | #define LOONGSON_CANFD_DEBUG            0x58    /* Debug Register */
        |         ^~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:131:9: warning: macro 'REG_CMD_RRB' is not used [-Wunused-macros]
    131 | #define REG_CMD_RRB                     BIT(2)
        |         ^~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:302:9: warning: macro 'REG_RX_PTR_WPP' is not used [-Wunused-macros]
    302 | #define REG_RX_PTR_WPP                  GENMASK(11, 0)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:239:9: warning: macro 'REG_DEBUG_PC_SUSP' is not used [-Wunused-macros]
    239 | #define REG_DEBUG_PC_SUSP               BIT(16)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:376:9: warning: macro 'REG_FRAME_TEST_FCRC' is not used [-Wunused-macros]
    376 | #define REG_FRAME_TEST_FCRC             BIT(1)
        |         ^~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:278:9: warning: macro 'REG_FIL_CTRL_FAFB' is not used [-Wunused-macros]
    278 | #define REG_FIL_CTRL_FAFB               BIT(2)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:276:9: warning: macro 'REG_FIL_CTRL_FANB' is not used [-Wunused-macros]
    276 | #define REG_FIL_CTRL_FANB               BIT(0)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:277:9: warning: macro 'REG_FIL_CTRL_FANE' is not used [-Wunused-macros]
    277 | #define REG_FIL_CTRL_FANE               BIT(1)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:282:9: warning: macro 'REG_FIL_CTRL_FBFB' is not used [-Wunused-macros]
    282 | #define REG_FIL_CTRL_FBFB               BIT(6)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:283:9: warning: macro 'REG_FIL_CTRL_FBFE' is not used [-Wunused-macros]
    283 | #define REG_FIL_CTRL_FBFE               BIT(7)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:308:9: warning: macro 'REG_RX_STAT_RXMOF' is not used [-Wunused-macros]
    308 | #define REG_RX_STAT_RXMOF               BIT(2)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:280:9: warning: macro 'REG_FIL_CTRL_FBNB' is not used [-Wunused-macros]
    280 | #define REG_FIL_CTRL_FBNB               BIT(4)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:90:9: warning: macro 'REG_ID_VER_MIN' is not used [-Wunused-macros]
     90 | #define REG_ID_VER_MIN                  GENMASK(23, 16)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:62:9: warning: macro 'LOONGSON_CANFD_RX_MEM_INFO' is not used [-Wunused-macros]
     62 | #define LOONGSON_CANFD_RX_MEM_INFO      0x8c    /* Receive Buffer Information Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:127:9: warning: macro 'REG_STAT_STCNT' is not used [-Wunused-macros]
    127 | #define REG_STAT_STCNT                  BIT(16)
        |         ^~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:236:9: warning: macro 'REG_DEBUG_PC_ACKD' is not used [-Wunused-macros]
    236 | #define REG_DEBUG_PC_ACKD               BIT(13)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:146:9: warning: macro 'REG_INT_STAT_RXFI' is not used [-Wunused-macros]
    146 | #define REG_INT_STAT_RXFI               BIT(7)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:120:9: warning: macro 'REG_STAT_TXNF' is not used [-Wunused-macros]
    120 | #define REG_STAT_TXNF                   BIT(2)
        |         ^~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:135:9: warning: macro 'REG_CMD_TXFCRST' is not used [-Wunused-macros]
    135 | #define REG_CMD_TXFCRST                 BIT(6)
        |         ^~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:381:9: warning: macro 'LOONGSON_CANFD_ID' is not used [-Wunused-macros]
    381 | #define LOONGSON_CANFD_ID               0xBABE
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:72:9: warning: macro 'LOONGSON_CANFD_TX_DATA_4' is not used [-Wunused-macros]
     72 | #define LOONGSON_CANFD_TX_DATA_4        0xbc
        |         ^~~~~~~~~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:284:9: warning: macro 'REG_FIL_CTRL_FCNB' is not used [-Wunused-macros]
    284 | #define REG_FIL_CTRL_FCNB               BIT(8)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:285:9: warning: macro 'REG_FIL_CTRL_FCNE' is not used [-Wunused-macros]
    285 | #define REG_FIL_CTRL_FCNE               BIT(9)
        |         ^~~~~~~~~~~~~~~~~
  drivers/net/can/loongson_canfd.c:53:9: warning: macro 'LOONGSON_CANFD_FLT_A_MASK' is not used [-Wunused-macros]
     53 | #define LOONGSON_CANFD_FLT_A_MASK       0x68    /* Filter A Mask Register */
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~

>  drivers/net/can/Kconfig          |   2 +-
>  drivers/net/can/loongson_canfd.c | 209 ++++++++++++++++++++++++++++---
>  2 files changed, 190 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 28014e264f30..16e07be6438c 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -191,7 +191,7 @@ config CAN_KVASER_PCIEFD
>  
>  config CAN_LOONGSON_CANFD
>  	tristate "Loongson CAN-FD controller"
> -	depends on HAS_IOMEM || COMPILE_TEST
> +	depends on HAS_IOMEM && (LOONGSON2_APB_CMC_DMA || COMPILE_TEST)

The logic is odd here. If your driver can be COMPILE_TESTed without
HAS_IOMEM, then patch 1 should be:

	depends on HAS_IOMEM || COMPILE_TEST

and patch 2 should be:

	depends on (HAS_IOMEM && LOONGSON2_APB_CMC_DMA) || COMPILE_TEST

If your driver need HAS_IOMEM even for a compile test, then patch 1
should be:

	depends on HAS_IOMEM

and patch 2 should be:

	depends on HAS_IOMEM && (LOONGSON2_APB_CMC_DMA || COMPILE_TEST)

Here, you are doing a weird mix.

>  	select REGMAP_MMIO
>  	help
>  	  This is a canfd driver switch for the Loongson platform,


Yours sincerely,
Vincent Mailhol

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 1/2] can: Add Loongson CAN-FD controller driver
  2026-06-08  8:49 ` [PATCH v2 1/2] can: " Binbin Zhou
@ 2026-06-08 17:43   ` Vincent Mailhol
  0 siblings, 0 replies; 5+ messages in thread
From: Vincent Mailhol @ 2026-06-08 17:43 UTC (permalink / raw)
  To: Binbin Zhou, Binbin Zhou, Huacai Chen, Marc Kleine-Budde,
	Bingxiong Li
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-can, jeffbai

On 08/06/2026 at 10:49, Binbin Zhou wrote:
> Add support for the CAN-FD controller integrated into Loongson-2K series
> SoCs. The controller supports both Classic CAN and CAN FD, with up to 8
> transmit buffers, hardware timestamping, error counters, and various
> control modes (loopback, listen-only, one-shot, non-ISO FD, etc.).
> 
> The driver implements:
> - NAPI for RX path
> - TX buffer management with echo skb support
> - Bus error reporting and fault confinement state handling
> - Bit timing configuration for nominal and data phase
> - Secondary Sample Point (SSP) configuration for high bitrates
> 
> Co-developed-by: Bingxiong Li <libingxiong@loongson.cn>
> Signed-off-by: Bingxiong Li <libingxiong@loongson.cn>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  MAINTAINERS                      |    7 +
>  drivers/net/can/Kconfig          |   15 +
>  drivers/net/can/Makefile         |    1 +
>  drivers/net/can/loongson_canfd.c | 1766 ++++++++++++++++++++++++++++++
>  4 files changed, 1789 insertions(+)
>  create mode 100644 drivers/net/can/loongson_canfd.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7a2ffd9d37d5..1ee16edae734 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14935,6 +14935,13 @@ F:	arch/loongarch/
>  F:	drivers/*/*loongarch*
>  F:	drivers/cpufreq/loongson3_cpufreq.c
>  
> +LOONGSON CAN FD DRIVER
> +M:	Bingxiong Li <libingxiong@loongson.cn>
> +M:	Binbin Zhou <zhoubinbin@loongson.cn>
> +L:	linux-can@vger.kernel.org
> +S:	Maintained
> +F:	drivers/net/can/loongson_canfd.c
> +
>  LOONGSON GPIO DRIVER
>  M:	Yinbo Zhu <zhuyinbo@loongson.cn>
>  L:	linux-gpio@vger.kernel.org
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index e15e320db476..28014e264f30 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -189,6 +189,21 @@ config CAN_KVASER_PCIEFD
>  	    Kvaser M.2 PCIe 4xCAN
>  	    Kvaser PCIe 8xCAN
>  
> +config CAN_LOONGSON_CANFD
> +	tristate "Loongson CAN-FD controller"
> +	depends on HAS_IOMEM || COMPILE_TEST
> +	select REGMAP_MMIO
> +	help
> +	  This is a canfd driver switch for the Loongson platform,
> +	  integrated with the Loongson-2K series SoCs.
> +
> +	  It supports both Classic CAN and CAN FD, with up to 8 transmit
> +	  buffers, hardware timestamping, error counters, and various
> +	  control modes.
> +
> +	  To compile as a module, choose M here: the module will be
> +	  called loongson_canfd.
> +
>  config CAN_SLCAN
>  	tristate "Serial / USB serial CAN Adaptors (slcan)"
>  	depends on TTY
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index d7bc10a6b8ea..1ce78443d710 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_GRCAN)		+= grcan.o
>  obj-$(CONFIG_CAN_IFI_CANFD)	+= ifi_canfd/
>  obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
>  obj-$(CONFIG_CAN_KVASER_PCIEFD)	+= kvaser_pciefd/
> +obj-$(CONFIG_CAN_LOONGSON_CANFD)	+= loongson_canfd.o
>  obj-$(CONFIG_CAN_MSCAN)		+= mscan/
>  obj-$(CONFIG_CAN_M_CAN)		+= m_can/
>  obj-$(CONFIG_CAN_PEAK_PCIEFD)	+= peak_canfd/
> diff --git a/drivers/net/can/loongson_canfd.c b/drivers/net/can/loongson_canfd.c
> new file mode 100644
> index 000000000000..3794700e04c8
> --- /dev/null
> +++ b/drivers/net/can/loongson_canfd.c
> @@ -0,0 +1,1766 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * LOONGSON CANFD controller
> + *
> + * Copyright (C) 2024-2026 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/skbuff.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#define LOONGSON_CANFD_DEVICE_ID	0x0	/* CANFD controller ID Register */
> +#define LOONGSON_CANFD_MODE		0x4	/* Mode Configuration Register */
> +#define LOONGSON_CANFD_CONF		0x8	/* Configure Register */
> +#define LOONGSON_CANFD_STAT		0xc	/* Status Register */
> +#define LOONGSON_CANFD_CMD		0x10	/* Command Register */
> +#define LOONGSON_CANFD_INT_STAT		0x14	/* Interrupt Status Register */
> +#define LOONGSON_CANFD_INT_ENA		0x18	/* Interrupt Enable Register */
> +#define LOONGSON_CANFD_INT_MASK		0x1c	/* Interrupt Mask Register */
> +#define LOONGSON_CANFD_BTR_NORM		0x20	/* Normal Rate Configuration Register */
> +#define LOONGSON_CANFD_BTR_FD		0x24	/* FD Data Rate Configuration Register */
> +#define LOONGSON_CANFD_ERL		0x28	/* Error Threshold Configuration Register */
> +#define LOONGSON_CANFD_FSTAT		0x2c	/* Error Status Register */
> +#define LOONGSON_CANFD_ERC		0x30	/* Error Count Register */
> +#define LOONGSON_CANFD_BRE		0x34	/* Rate Error Count Register */
> +#define LOONGSON_CANFD_CTR_PRES		0x38	/* Error Count Debug Register */
> +#define LOONGSON_CANFD_ERR_CAPT		0x3c	/* Error Capture Status Register */
> +#define LOONGSON_CANFD_RETX_CNT		0x40	/* Retransmission Count Register */
> +#define LOONGSON_CANFD_ALC		0x44	/* Lost Arbitration Capture Register */
> +#define LOONGSON_CANFD_TRV_DLY		0x48	/* Transmission Delay Measurement Register */
> +#define LOONGSON_CANFD_SSP_CFG		0x4c	/* Second Sampling Point Configuration Register */
> +#define LOONGSON_CANFD_RX_FR_CNT	0x50	/* Receive Message Count Register */
> +#define LOONGSON_CANFD_TX_FR_CNT	0x54	/* Transmit Message Count Register */
> +#define LOONGSON_CANFD_DEBUG		0x58	/* Debug Register */
> +#define LOONGSON_CANFD_TS		0x5c	/* Timestamp Register */
> +#define LOONGSON_CANFD_TX_FRM_TST	0x60	/* Transmit Message Debug Register */
> +#define LOONGSON_CANFD_FRC_DIV		0x64	/* Fractional Divider Ratio Register */
> +#define LOONGSON_CANFD_FLT_A_MASK	0x68	/* Filter A Mask Register */
> +#define LOONGSON_CANFD_FLT_A_VAL	0x6c	/* Filter A value Register */
> +#define LOONGSON_CANFD_FLT_B_MASK	0x70	/* Filter B Mask Register */
> +#define LOONGSON_CANFD_FLT_B_VAL	0x74	/* Filter B value Register */
> +#define LOONGSON_CANFD_FLT_C_MASK	0x78	/* Filter C Mask Register */
> +#define LOONGSON_CANFD_FLT_C_VAL	0x7c	/* Filter C value Register */
> +#define LOONGSON_CANFD_FLT_R_LOW	0x80	/* Range Filter Low Threshold Register */
> +#define LOONGSON_CANFD_FLT_R_HI		0x84	/* Range Filter High Threshold Register */
> +#define LOONGSON_CANFD_FLT_CTRL		0x88	/* Filter Control Register */
> +#define LOONGSON_CANFD_RX_MEM_INFO	0x8c	/* Receive Buffer Information Register */
> +#define LOONGSON_CANFD_RX_PRT		0x90	/* Receive Buffer Pointer Register */
> +#define LOONGSON_CANFD_RX_STAT		0x94	/* Receive Buffer Status Register */
> +#define LOONGSON_CANFD_RX_DATA		0x98	/* Receive Data Register */
> +#define LOONGSON_CANFD_TX_STAT		0x9c	/* Transmit Buffer Status Register */
> +#define LOONGSON_CANFD_TX_CMD		0xa0	/* Transmit Command Register */
> +#define LOONGSON_CANFD_TX_SEL		0xa4	/* Transmit Buffer Selection Register */
> +#define LOONGSON_CANFD_TX_DATA_1	0xb0
> +#define LOONGSON_CANFD_TX_DATA_2	0xb4
> +#define LOONGSON_CANFD_TX_DATA_3	0xb8
> +#define LOONGSON_CANFD_TX_DATA_4	0xbc
> +#define LOONGSON_CANFD_TX_DATA_5	0xc0
> +#define LOONGSON_CANFD_TX_DATA_6	0xc4
> +#define LOONGSON_CANFD_TX_DATA_7	0xc8
> +#define LOONGSON_CANFD_TX_DATA_8	0xcc
> +#define LOONGSON_CANFD_TX_DATA_9	0xd0
> +#define LOONGSON_CANFD_TX_DATA_10	0xd4
> +#define LOONGSON_CANFD_TX_DATA_11	0xd8
> +#define LOONGSON_CANFD_TX_DATA_12	0xdc
> +#define LOONGSON_CANFD_TX_DATA_13	0xe0
> +#define LOONGSON_CANFD_TX_DATA_14	0xe4
> +#define LOONGSON_CANFD_TX_DATA_15	0xe8
> +#define LOONGSON_CANFD_TX_DATA_16	0xec
> +#define LOONGSON_CANFD_TX_DATA_17	0xf0
> +#define LOONGSON_CANFD_TX_DATA_18	0xf4
> +
> +/* Bitfields of CANFD controller ID register */
> +#define REG_ID_MASK			GENMASK(15, 0)
> +#define REG_ID_VER_MIN			GENMASK(23, 16)
> +#define REG_ID_VER_MAJ			GENMASK(31, 24)
> +
> +/* Bitfields of Mode Configuration register */
> +#define REG_MODE_RST			BIT(0)
> +#define REG_MODE_BMM			BIT(1)
> +#define REG_MODE_STM			BIT(2)
> +#define REG_MODE_AFM			BIT(3)
> +#define REG_MODE_FDE			BIT(4)
> +#define REG_MODE_TTTM			BIT(5)
> +#define REG_MODE_ROM			BIT(6)
> +#define REG_MODE_ACF			BIT(7)
> +#define REG_MODE_TSTM			BIT(8)
> +#define REG_MODE_RXBAM			BIT(9)
> +#define REG_MODE_ITSM			BIT(10)
> +#define REG_MODE_RTSOP			BIT(12)
> +#define REG_MODE_BUFM			BIT(13)
> +
> +/* Bitfields of Configure register */
> +#define REG_CONF_RTRLE			BIT(0)
> +#define REG_CONF_RTRTH			GENMASK(4, 1)
> +#define REG_CONF_ILBP			BIT(5)
> +#define REG_CONF_ENA			BIT(6)
> +#define REG_CONF_NISOFD			BIT(7)
> +#define REG_CONF_PEX			BIT(8)
> +#define REG_CONF_FDRF			BIT(10)
> +
> +/* Bitfields of Status register */
> +#define REG_STAT_RXNE			BIT(0)
> +#define REG_STAT_DOR			BIT(1)
> +#define REG_STAT_TXNF			BIT(2)
> +#define REG_STAT_EFT			BIT(3)
> +#define REG_STAT_RXS			BIT(4)
> +#define REG_STAT_TXS			BIT(5)
> +#define REG_STAT_EWL			BIT(6)
> +#define REG_STAT_IDLE			BIT(7)
> +#define REG_STAT_PEXS			BIT(8)
> +#define REG_STAT_STCNT			BIT(16)
> +
> +/* Bitfields of Command register */
> +#define REG_CMD_RXRPMV			BIT(1)
> +#define REG_CMD_RRB			BIT(2)
> +#define REG_CMD_CDO			BIT(3)
> +#define REG_CMD_ERCRST			BIT(4)
> +#define REG_CMD_RXFCRST			BIT(5)
> +#define REG_CMD_TXFCRST			BIT(6)
> +#define REG_CMD_CPEXS			BIT(7)
> +
> +/* Bitfields of Interrupt Status register */
> +#define REG_INT_STAT_RXI		BIT(0)
> +#define REG_INT_STAT_TXI		BIT(1)
> +#define REG_INT_STAT_EWLI		BIT(2)
> +#define REG_INT_STAT_DOI		BIT(3)
> +#define REG_INT_STAT_FCSI		BIT(4)
> +#define REG_INT_STAT_ALI		BIT(5)
> +#define REG_INT_STAT_BEI		BIT(6)
> +#define REG_INT_STAT_RXFI		BIT(7)
> +#define REG_INT_STAT_BSI		BIT(8)
> +#define REG_INT_STAT_RBNEI		BIT(9)
> +#define REG_INT_STAT_TXBHCI		BIT(10)
> +#define REG_INT_STAT_OFI		BIT(11)
> +#define REG_INT_STAT_DMADI		BIT(12)
> +
> +#define REG_INT_STAT_ERRORI	(REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI)
> +
> +/* Bitfields of Interrupt Enable register */
> +#define REG_INT_ENA_CLR			GENMASK(28, 16)
> +#define REG_INT_ENA_SET			GENMASK(12, 0)
> +
> +/* Bitfields of Interrupt Mask register */
> +#define REG_INT_MASK_SET		GENMASK(12, 0)
> +#define REG_INT_MASK_CLR		GENMASK(28, 16)
> +
> +/* Bitfields of Normal Rate Configuration register */
> +#define REG_BTR_PROP			GENMASK(6, 0)
> +#define REG_BTR_PH1			GENMASK(12, 7)
> +#define REG_BTR_PH2			GENMASK(18, 13)
> +#define REG_BTR_BRP			GENMASK(22, 19)
> +#define REG_BTR_SJW			GENMASK(31, 27)
> +
> +/* Bitfields of FD Data Rate Configuration register */
> +#define REG_BTR_FD_PROP			GENMASK(6, 0)
> +#define REG_BTR_FD_PH1			GENMASK(11, 7)
> +#define REG_BTR_FD_PH2			GENMASK(17, 13)
> +#define REG_BTR_FD_BRP			GENMASK(26, 19)
> +#define REG_BTR_FD_SJW			GENMASK(31, 27)
> +
> +/* Bitfields of Error Threshold Configuration register */
> +#define REG_ERL_ERP			GENMASK(7, 0)
> +#define REG_ERL_EW			GENMASK(23, 16)
> +
> +/* Bitfields of Error Status register */
> +#define REG_FSTAT_ERA			BIT(0)
> +#define REG_FSTAT_ERP			BIT(1)
> +#define REG_FSTAT_BOF			BIT(2)
> +
> +#define REG_FSTAT_MASK			GENMASK(2, 0)
> +
> +/* Bitfields of Error Count register */
> +#define REG_ERC_TEC			GENMASK(8, 0)
> +#define REG_ERC_REC			GENMASK(24, 16)
> +
> +/* Bitfields of Rate Error Count register */
> +#define REG_BRE_NORM			GENMASK(15, 0)
> +#define REG_BRE_FD_DATA			GENMASK(31, 16)
> +
> +/* Bitfields of Error Count Debug register */
> +#define REG_CTR_PRES_CTPV		GENMASK(8, 0)
> +#define REG_CTR_PRES_PTX		BIT(9)
> +#define REG_CTR_PRES_PRX		BIT(10)
> +
> +/* Bitfields of Error Capture Status register */
> +#define REG_ERR_CAPT_POS		GENMASK(4, 0)
> +#define REG_ERR_CAPT_TYPE		GENMASK(7, 5)
> +
> +/* Bitfields of Retransmission Count register */
> +#define REG_RETX_CNT_VAL		GENMASK(3, 0)
> +
> +/* Bitfields of Lost Arbitration Capture register */
> +#define REG_ALC_BIT_POS			GENMASK(4, 0)
> +#define REG_ALC_ID_FIELD		GENMASK(7, 5)
> +
> +/* Bitfields of Transmission Delay Measurement register */
> +#define REG_TRV_DLY_VAL			GENMASK(6, 0)
> +
> +/* Bitfields of Second Sampling Point Configuration register */
> +#define REG_SSP_CFG_OFF			GENMASK(7, 0)
> +#define REG_SSP_CFG_SRC			GENMASK(9, 8)
> +#define REG_SSP_CFG_SAT			BIT(10)
> +
> +/* Bitfields of Receive Message Count register */
> +#define REG_RX_FR_CNT_VAL		GENMASK(31, 0)
> +
> +/* Bitfields of Transmit Message Count register */
> +#define REG_TX_FR_CNT_VAL		GENMASK(31, 0)
> +
> +/* Bitfields of Debug register */
> +#define REG_DEBUG_STF_CNT		GENMASK(2, 0)
> +#define REG_DEBUG_DSTF_CNT		GENMASK(5, 3)
> +#define REG_DEBUG_PC_ARB		BIT(6)
> +#define REG_DEBUG_PC_CON		BIT(7)
> +#define REG_DEBUG_PC_DAT		BIT(8)
> +#define REG_DEBUG_PC_STC		BIT(9)
> +#define REG_DEBUG_PC_CRC		BIT(10)
> +#define REG_DEBUG_PC_CRCD		BIT(11)
> +#define REG_DEBUG_PC_ACK		BIT(12)
> +#define REG_DEBUG_PC_ACKD		BIT(13)
> +#define REG_DEBUG_PC_EOF		BIT(14)
> +#define REG_DEBUG_PC_INT		BIT(15)
> +#define REG_DEBUG_PC_SUSP		BIT(16)
> +#define REG_DEBUG_PC_OVR		BIT(17)
> +#define REG_DEBUG_PC_SOF		BIT(18)
> +
> +/* Bitfields of Timestamp register */
> +#define REG_TS_TIMESTAMP		GENMASK(15, 0)
> +#define REG_TS_PSC			GENMASK(24, 16)
> +
> +/* Bitfields of Fractional Divider Ratio register */
> +#define REG_FRC_FRC_DBT			GENMASK(15, 8)
> +#define REG_FRC_FRC_NBT			GENMASK(7, 0)
> +
> +/* Bitfields of Filter A Mask register */
> +#define REG_FIL_A_MASK			GENMASK(28, 0)
> +
> +/* Bitfields of Filter A value register */
> +#define REG_FIL_A_VAL			GENMASK(28, 0)
> +
> +/* Bitfields of Filter B Mask register */
> +#define REG_FIL_B_MASK			GENMASK(28, 0)
> +
> +/* Bitfields of Filter B value register */
> +#define REG_FIL_B_VAL			GENMASK(28, 0)
> +
> +/* Bitfields of Filter C Mask register */
> +#define REG_FIL_C_MASK			GENMASK(28, 0)
> +
> +/* Bitfields of Filter C value register */
> +#define REG_FIL_C_VAL			GENMASK(28, 0)
> +
> +/* Bitfields of Range Filter Low Threshold register */
> +#define REG_FIL_R_LOW_VAL		GENMASK(28, 0)
> +
> +/* Bitfields of Range Filter High Threshold register */
> +#define REG_FIL_R_HI_VAL		GENMASK(28, 0)
> +
> +/* Bitfields of Filter Control register */
> +#define REG_FIL_CTRL_FANB		BIT(0)
> +#define REG_FIL_CTRL_FANE		BIT(1)
> +#define REG_FIL_CTRL_FAFB		BIT(2)
> +#define REG_FIL_CTRL_FAFE		BIT(3)
> +#define REG_FIL_CTRL_FBNB		BIT(4)
> +#define REG_FIL_CTRL_FBNE		BIT(5)
> +#define REG_FIL_CTRL_FBFB		BIT(6)
> +#define REG_FIL_CTRL_FBFE		BIT(7)
> +#define REG_FIL_CTRL_FCNB		BIT(8)
> +#define REG_FIL_CTRL_FCNE		BIT(9)
> +#define REG_FIL_CTRL_FCFB		BIT(10)
> +#define REG_FIL_CTRL_FCFE		BIT(11)
> +#define REG_FIL_CTRL_FRNB		BIT(12)
> +#define REG_FIL_CTRL_FRNE		BIT(13)
> +#define REG_FIL_CTRL_FRFB		BIT(14)
> +#define REG_FIL_CTRL_FRFE		BIT(15)
> +#define REG_FIL_CTRL_SFA		BIT(16)
> +#define REG_FIL_CTRL_SFB		BIT(17)
> +#define REG_FIL_CTRL_SFC		BIT(18)
> +#define REG_FIL_CTRL_SFR		BIT(19)
> +
> +/* Bitfields of Receive Buffer Information register */
> +#define REG_RX_MEM_INFO_BUFF_SIZE	GENMASK(12, 0)
> +#define REG_RX_MEM_INFO_MEM_FREE	GENMASK(28, 16)
> +
> +/* Bitfields of Receive Buffer Pointer register */
> +#define REG_RX_PTR_WPP			GENMASK(11, 0)
> +#define REG_RX_PTR_RPP			GENMASK(27, 16)
> +
> +/* Bitfields of Receive Buffer Status register */
> +#define REG_RX_STAT_RXE			BIT(0)
> +#define REG_RX_STAT_RXF			BIT(1)
> +#define REG_RX_STAT_RXMOF		BIT(2)
> +#define REG_RX_STAT_RXFRC		GENMASK(14, 4)
> +#define REG_RX_STAT_RTSOP		BIT(16)
> +
> +/* Bitfields of Receive Data register */
> +#define REG_RX_DATA_VAL			GENMASK(31, 0)
> +
> +/* Bitfields of Transmit Buffer Status register */
> +#define REG_TX_STAT_BRP			GENMASK(7, 0)
> +#define REG_TX_STAT_TXS			GENMASK(10, 8)
> +#define REG_TX_STAT_BS			GENMASK(31, 16)
> +
> +#define REG_TX_STAT_BS_TX0		GENMASK(17, 16)
> +#define REG_TX_STAT_BS_TX1		GENMASK(19, 18)
> +#define REG_TX_STAT_BS_TX2		GENMASK(21, 20)
> +#define REG_TX_STAT_BS_TX3		GENMASK(23, 22)
> +#define REG_TX_STAT_BS_TX4		GENMASK(25, 24)
> +#define REG_TX_STAT_BS_TX5		GENMASK(27, 26)
> +#define REG_TX_STAT_BS_TX6		GENMASK(29, 28)
> +#define REG_TX_STAT_BS_TX7		GENMASK(31, 30)
> +
> +/* Bitfields of Transmit Command register */
> +#define REG_TX_CMD_BAR			GENMASK(7, 0)
> +#define REG_TX_CMD_BCR			GENMASK(15, 8)
> +#define REG_TX_CMD_BSC			GENMASK(23, 16)
> +
> +/* Bitfields of Transmit Buffer Selection register */
> +#define REG_TX_SEL_BUF_SEL		GENMASK(3, 0)
> +#define REG_TX_SEL_BUF_CNT		GENMASK(7, 4)
> +
> +/* Loongson CANFD Frame format */
> +#define LOONGSON_CANFD_FRAME_META0	0x0
> +#define LOONGSON_CANFD_FRAME_META1	0x4
> +#define LOONGSON_CANFD_FRAME_DB_1	0x8
> +#define LOONGSON_CANFD_FRAME_DB_2	0xc
> +#define LOONGSON_CANFD_FRAME_DB_3	0x10
> +#define LOONGSON_CANFD_FRAME_DB_4	0x14
> +#define LOONGSON_CANFD_FRAME_DB_5	0x18
> +#define LOONGSON_CANFD_FRAME_DB_6	0x1c
> +#define LOONGSON_CANFD_FRAME_DB_7	0x20
> +#define LOONGSON_CANFD_FRAME_DB_8	0x24
> +#define LOONGSON_CANFD_FRAME_DB_9	0x28
> +#define LOONGSON_CANFD_FRAME_DB_10	0x2c
> +#define LOONGSON_CANFD_FRAME_DB_11	0x30
> +#define LOONGSON_CANFD_FRAME_DB_12	0x34
> +#define LOONGSON_CANFD_FRAME_DB_13	0x38
> +#define LOONGSON_CANFD_FRAME_DB_14	0x3c
> +#define LOONGSON_CANFD_FRAME_DB_16	0x40
> +#define LOONGSON_CANFD_FRAME_DB_17	0x44
> +
> +/* Bitfields of FRAME META0 */
> +#define REG_FRAME_META0_ID_EXT		GENMASK(17, 0)
> +#define REG_FRAME_META0_ID_BASE		GENMASK(28, 18)
> +#define REG_FRAME_META0_RTR		BIT(29)	/* Remote Transmission Request, only in CAN2.0 */
> +/* Extended Identifier Type. 1: 29 bits ID, 0: 11 bits ID. */
> +#define REG_FRAME_META0_XDT		BIT(30)
> +#define REG_FRAME_META0_ESI		BIT(31)	/* Error State Indicator, only in CAN-FD */
> +
> +/* Bitfields of FRAME META1 */
> +#define REG_FRAME_META1_TIMESTAMP	GENMASK(15, 0)
> +#define REG_FRAME_META1_DLC		GENMASK(19, 16)	/* Data Length Code */
> +#define REG_FRAME_META1_BRS		BIT(20)	/* Bit Rate Shift, Only in CANFD */
> +/* Flexible Data-rate Format. 1: CANFD, 0: CAN2.0 */
> +#define REG_FRAME_META1_FDF		BIT(21)
> +#define REG_FRAME_META1_RWCNT		GENMASK(28, 24)	/* Read Word Counter */
> +
> +/* Bitfields of FRAME TEST */
> +#define REG_FRAME_TEST_FSTC		BIT(0)
> +#define REG_FRAME_TEST_FCRC		BIT(1)
> +#define REG_FRAME_TEST_SDLC		BIT(2)
> +#define REG_FRAME_TEST_TPRM		GENMASK(12, 8)
> +
> +#define DEV_NAME			"loongson_canfd"
> +#define LOONGSON_CANFD_ID		0xBABE
> +#define LOONGSON_CANFD_DW_BYTE		4
> +#define LOONGSON_CANFD_TXBUF_NUM	8
> +#define LOONGSON_CANFD_MAX_RTXTH	0xf
> +
> +/**
> + * struct loongson_canfd_priv - This definition define CAN driver instance
> + * @can: CAN private data structure.
> + * @napi: NAPI structure
> + * @regmap: regmap of the CAN device
> + * @res: Pointer to the CAN device respurce
> + * @tx_lock: Lock for synchronizing TX interrupt handling
> + */
> +struct loongson_canfd_priv {
> +	struct can_priv		can;		/* must be first member! */
> +	struct napi_struct	napi;
> +	struct regmap		*regmap;
> +	struct resource		*res;
> +	spinlock_t		tx_lock;	/* protect the sending queue */
> +};
> +
> +/**
> + * enum loongson_canfd_txbuf_sts - status of TX buffer
> + * @TX_BS_IDLE: Status processed or not sent.
> + * @TX_BS_VALID: Sending successful.
> + * @TX_BS_FAIL: Sending failed.
> + * @TX_BS_CANCEL: Sending cancelled.
> + */
> +enum loongson_canfd_txbuf_sts {
> +	TX_BS_IDLE	= 0x0,
> +	TX_BS_VALID	= 0x1,
> +	TX_BS_FAIL	= 0x2,
> +	TX_BS_CANCEL	= 0x3
> +};
> +
> +/**
> + * enum loongson_canfd_txbuf_cmd - command of TX buffer
> + * @TXT_CMD_ADD: Buffer add request.
> + * @TXT_CMD_CANCEL: Buffer cancel request.
> + * @TXT_CMD_SR_CLEAN: Buffer send record clear.
> + *     1: to clear the send completion record (LOONGSON_CANFD_TX_STAT[BS]).
> + */
> +enum loongson_canfd_txbuf_cmd {
> +	TXT_CMD_ADD		= 0x01,
> +	TXT_CMD_CANCEL		= 0x02,
> +	TXT_CMD_SR_CLEAN	= 0x03
> +};
> +
> +static const struct can_bittiming_const loongson_canfd_bit_timing = {
> +	.name		= DEV_NAME,
> +	.tseg1_min	= 2,
> +	.tseg1_max	= 190,
> +	.tseg2_min	= 2,
> +	.tseg2_max	= 63,
> +	.sjw_max	= 31,
> +	.brp_min	= 1,
> +	.brp_max	= 15,
> +	.brp_inc	= 1,
> +};
> +
> +static const struct can_bittiming_const loongson_canfd_bit_timing_data = {
> +	.name		= DEV_NAME,
> +	.tseg1_min	= 2,
> +	.tseg1_max	= 190,
> +	.tseg2_min	= 2,
> +	.tseg2_max	= 63,
> +	.sjw_max	= 31,
> +	.brp_min	= 1,
> +	.brp_max	= 255,
> +	.brp_inc	= 1,
> +};
> +
> +/* CAN FD Transmission Delay Compensation constants */
> +static const struct can_tdc_const loongson_canfd_tdc_data = {
> +	.tdcv_min	= 0,
> +	.tdcv_max	= 0,
> +	.tdco_min	= 1,
> +	.tdco_max	= 127,
> +	.tdcf_min	= 0,	/* Filter window not supported */
> +	.tdcf_max	= 0,
> +};
> +
> +/**
> + * loongson_canfd_enabled() - Gets the controller is enabled
> + * @priv: Pointer to private data
> + *
> + * Return: True - the controller is enabled.
> + *	   False - the controller is disabled.
> + */
> +static bool loongson_canfd_enabled(struct loongson_canfd_priv *priv)
> +{
> +	return !!regmap_test_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA);
> +}
> +
> +/**
> + * loongson_canfd_txbuf_freed() - Gets the flag for TX buffer to be sent
> + * @priv: Pointer to private data
> + *
> + * Return: True - TX buffer is empty.
> + *	   False - TX buffer is processing
> + */
> +static bool loongson_canfd_txbuf_freed(struct loongson_canfd_priv *priv)
> +{
> +	return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, REG_TX_STAT_BRP);
> +}
> +
> +/**
> + * loongson_canfd_get_txbuf_sts() - Gets status of TX buffer
> + * @priv: Pointer to private data
> + * @buf_id: Buffer index (0-based)
> + *
> + * Return: Status of TX buffer
> + */
> +static enum loongson_canfd_txbuf_sts
> +loongson_canfd_get_txbuf_sts(struct loongson_canfd_priv *priv, u8 buf_id)
> +{
> +	u32 sts, mask;
> +
> +	switch (buf_id) {
> +	case 0:
> +		mask = REG_TX_STAT_BS_TX0;
> +		break;
> +	case 1:
> +		mask = REG_TX_STAT_BS_TX1;
> +		break;
> +	case 2:
> +		mask = REG_TX_STAT_BS_TX2;
> +		break;
> +	case 3:
> +		mask = REG_TX_STAT_BS_TX3;
> +		break;
> +	case 4:
> +		mask = REG_TX_STAT_BS_TX4;
> +		break;
> +	case 5:
> +		mask = REG_TX_STAT_BS_TX5;
> +		break;
> +	case 6:
> +		mask = REG_TX_STAT_BS_TX6;
> +		break;
> +	case 7:
> +		mask = REG_TX_STAT_BS_TX7;
> +		break;
> +	}
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &sts);
> +
> +	return field_get(mask, sts);
> +}

I think it is easier to compute the mask than doing this switch
case. Maybe something like this:

	#define REG_TX_STAT_BS_TX_BASE 16

	/* ... */

	static enum loongson_canfd_txbuf_sts
	loongson_canfd_get_txbuf_sts(struct loongson_canfd_priv *priv, u8 buf_id)
	{
		u32 mask, sts;

		mask = GENMASK(REG_TX_STAT_BS_TX_BASE + 2 * buf_id + 1,
			       REG_TX_STAT_BS_TX_BASE + 2 * buf_id);
		regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &sts);
	
		return field_get(mask, sts);
	}

Or equivalent:

	#define REG_TX_STAT_BS_TX_MASK_BASE GENMASK(17, 16)

	/* ... */

	static enum loongson_canfd_txbuf_sts
	loongson_canfd_get_txbuf_sts(struct loongson_canfd_priv *priv, u8 buf_id)
	{
		u32 mask, sts;

		mask = REG_TX_STAT_BS_TX_MASK_BASE << 2 * buf_id;
		regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &sts);
	
		return field_get(mask, sts);
	}

> +/**
> + * loongson_canfd_get_txbuf_id() - Gets the index of a non-idle TX buffer
> + * @priv: Pointer to private data
> + * @sts: Status of TXT buffer to outside
> + *
> + * Return: Buffer index (0-based)
> + */
> +static unsigned int loongson_canfd_get_txbuf_id(struct loongson_canfd_priv *priv,
> +						enum loongson_canfd_txbuf_sts *sts)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) {
> +		if (loongson_canfd_get_txbuf_sts(priv, i)) {
> +			*sts = loongson_canfd_get_txbuf_sts(priv, i);
> +			break;
> +		}
> +	}
> +
> +	if (i == LOONGSON_CANFD_TXBUF_NUM)
> +		*sts = TX_BS_IDLE;
> +
> +	return i;
> +}
> +
> +/**
> + * loongson_canfd_txbuf_is_writable() - Checks if frame can be inserted to TX Buffer
> + * @priv: Pointer to private data
> + * @buf_id: Buffer index (0-based)
> + *
> + * Return: True - Frame can be inserted to TXT Buffer,
> + *	   False - If attempted, frame will not be inserted to TX Buffer
> + */
> +static bool loongson_canfd_txbuf_is_writable(struct loongson_canfd_priv *priv, u8 buf_id)
> +{
> +	enum loongson_canfd_txbuf_sts bs;
> +
> +	bs = loongson_canfd_get_txbuf_sts(priv, buf_id);
> +	if (bs)
> +		return false;
> +
> +	return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, BIT(buf_id));
> +}
> +
> +/**
> + * loongson_canfd_set_txbuf_cmd() - Applies command on TX buffer
> + * @ndev: Pointer to net_device structure
> + * @cmd: Command to set
> + * @buf_id: Buffer index (0-based)
> + */
> +static void loongson_canfd_set_txbuf_cmd(struct net_device *ndev,
> +					 enum loongson_canfd_txbuf_cmd cmd, u8 buf_id)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 mask;
> +
> +	switch (cmd) {
> +	case TXT_CMD_ADD:
> +		mask = REG_TX_CMD_BAR;
> +		break;
> +	case TXT_CMD_CANCEL:
> +		mask = REG_TX_CMD_BCR;
> +		break;
> +	case TXT_CMD_SR_CLEAN:
> +		mask = REG_TX_CMD_BSC;
> +		break;
> +	default:
> +		netdev_err(ndev, "Unknown command id: %x\n", cmd);
> +		return;
> +	}
> +
> +	regmap_write(priv->regmap, LOONGSON_CANFD_TX_CMD, field_prep(mask, 0x1 << buf_id));
                                                                           ^^^^^^^^^^^^^
Nitpick:
	regmap_write(priv->regmap, LOONGSON_CANFD_TX_CMD, field_prep(mask, BIT(buf_id)));

> +}
> +
> +/**
> + * loongson_canfd_reset() - Issues software reset request to Loongson CANFD
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 for success, -%ETIMEDOUT if CAN controller does not leave reset
                             ^^^^^^^^^^^
This part of the documentation does not match the code.

> + */
> +static int loongson_canfd_reset(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +
> +	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RST);
> +	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RXBAM | REG_MODE_BUFM);
> +
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_set_btr() - Sets CAN bus bit timing in Loongson CANFD
> + * @ndev: Pointer to net_device structure
> + * @bt: Pointer to Bit timing structure
> + * @nominal: True - Nominal bit timing, False - Data bit timing
> + *
> + * Return: 0 - OK, -%EPERM if controller is enabled
> + */
> +static int loongson_canfd_set_btr(struct net_device *ndev, struct can_bittiming *bt, bool nominal)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 phase_seg1 = bt->phase_seg1;
> +	u32 prop_seg = bt->prop_seg;

No need for those phase_seg1 and prop_seg variables. Directly do the
math on bt->phase_seg1 and bt->prop_seg.

> +	int max_ph1_len = 31;

Replace this magic value by a macro. If I understand correctly your
intent, this 31 value correspond to the maximum value which can be
taken by your phase 1 register. So the correct code is:

	int max_ph1_len = FIELD_MAX(REG_BTR_FD_PH1);

> +	u32 btr = 0;
> +
> +	if (loongson_canfd_enabled(priv)) {
> +		netdev_err(ndev, "BUG! Cannot set bittiming - CAN is enabled\n");
> +		return -EPERM;
> +	}

The framework should already prevent this from occuring. You can drop
this check (and other similar checks) and change the return type to
such functions to void.

If you were somehow able to reach trigger this function while the CAN
device is enabled, let us know!

> +	if (nominal)
> +		max_ph1_len = 63;

It is disturbing to have the data bittiming value and the nominal
bittiming value separated. Group them together. With the magic number
replaced by the adequate macros, this gives you something like:

	int ph1_max;

	/* ... */

	if (nominal)
		ph1_max = FIELD_MAX(REG_BTR_PH1);
	else
		ph1_max = FIELD_MAX(REG_BTR_FD_PH1);

(while at it, I renamed to ph1_max because the this is not a length,
so I am not sure why you put a _len suffix).

> +	/*
> +	 * The timing calculation functions have only constraints on tseg1,
> +	 * which is prop_seg + phase1_seg combined.
> +	 * tseg1 is then split in half and stored into prog_seg and phase_seg1.
> +	 * In Loongson CAN-FD, PROP is 6/7 bits wide but PH1 only 6/5, so we must
> +	 * re-distribute the values here.
> +	 */
> +	if (phase_seg1 > max_ph1_len) {
> +		prop_seg += phase_seg1 - max_ph1_len;
> +		phase_seg1 = max_ph1_len;
> +		bt->prop_seg = prop_seg;
> +		bt->phase_seg1 = phase_seg1;
> +	}
> +
> +	if (nominal) {
> +		btr = FIELD_PREP(REG_BTR_PROP, prop_seg) |
> +		      FIELD_PREP(REG_BTR_PH1, phase_seg1) |
> +		      FIELD_PREP(REG_BTR_PH2, bt->phase_seg2) |
> +		      FIELD_PREP(REG_BTR_BRP, bt->brp) |
> +		      FIELD_PREP(REG_BTR_SJW, bt->sjw);
> +
> +		regmap_write(priv->regmap, LOONGSON_CANFD_BTR_NORM, btr);
> +	} else {
> +		btr = FIELD_PREP(REG_BTR_FD_PROP, prop_seg) |
> +		      FIELD_PREP(REG_BTR_FD_PH1, phase_seg1) |
> +		      FIELD_PREP(REG_BTR_FD_PH2, bt->phase_seg2) |
> +		      FIELD_PREP(REG_BTR_FD_BRP, bt->brp) |
> +		      FIELD_PREP(REG_BTR_FD_SJW, bt->sjw);
> +
> +		regmap_write(priv->regmap, LOONGSON_CANFD_BTR_FD, btr);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_set_bittiming() - CAN set nominal bit timing routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 on success, -%EPERM on error
> + */
> +static int loongson_canfd_set_bittiming(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct can_bittiming *bt = &priv->can.bittiming;
> +
> +	/* Note that bt may be modified here */
> +	return loongson_canfd_set_btr(ndev, bt, true);
> +}
> +
> +/**
> + * loongson_canfd_set_data_bittiming() - CAN set data bit timing routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 on success, -%EPERM on error
> + */
> +static int loongson_canfd_set_data_bittiming(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct can_bittiming *dbt = &priv->can.fd.data_bittiming;
> +
> +	/* Note that dbt may be modified here */
> +	return loongson_canfd_set_btr(ndev, dbt, false);
> +}
> +
> +/**
> + * loongson_canfd_get_auto_tdcv - Get Transmitter Delay Compensation Value
> + * @ndev: Pointer to net_device structure
> + * @tdcv: Pointer to TDCV value
> + *
> + * Return: 0 on success
> + */
> +static int loongson_canfd_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 val;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_TRV_DLY, &val);
> +	*tdcv = FIELD_GET(REG_TRV_DLY_VAL, val);
> +
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_set_secondary_sample_point() - Set secondary sample point in Loongson CANFD
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 on success, -%EPERM if controller is enabled
> + */
> +static int loongson_canfd_set_secondary_sample_point(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 ssp_cfg;
> +
> +	if (loongson_canfd_enabled(priv)) {
> +		netdev_err(ndev, "BUG! Cannot set SSP - CAN is enabled\n");
> +		return -EPERM;
> +	}
> +
> +	if (can_fd_tdc_is_enabled(&priv->can))
> +		ssp_cfg = FIELD_PREP(REG_SSP_CFG_OFF, priv->can.fd.tdc.tdco) |
> +			  FIELD_PREP(REG_SSP_CFG_SRC, 0x0);
> +	else
> +		ssp_cfg = FIELD_PREP(REG_SSP_CFG_SRC, 0x1);
> +
> +	regmap_write(priv->regmap, LOONGSON_CANFD_SSP_CFG, ssp_cfg);
> +
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_set_mode() - Sets Loongson CANFD mode
> + * @priv: Pointer to private data
> + * @ctrlmode: Pointer to controller modes to be set
> + */
> +static void loongson_canfd_set_mode(struct loongson_canfd_priv *priv,
> +				    const struct can_ctrlmode *ctrlmode)

Rename this to loongson_canfd_set_ctrlmode(). Otherwhise there is a
risk of confusion with loongson_canfd_do_set_mode()

> +{
> +	u32 mode, conf;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_MODE, &mode);
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_LISTENONLY)
> +		mode |= REG_MODE_BMM;
> +	else
> +		mode &= ~REG_MODE_BMM;
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_FD)
> +		mode |= REG_MODE_FDE;
> +	else
> +		mode &= ~REG_MODE_FDE;
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_PRESUME_ACK)
> +		mode |= REG_MODE_ACF;
> +	else
> +		mode &= ~REG_MODE_ACF;
> +
> +	/*
> +	 * Some bits fixed:
> +	 * TSTM - Off, User shall not be able to change REC/TEC by hand
> +	 * during operation
> +	 */
> +	mode &= ~REG_MODE_TSTM;
> +	regmap_write(priv->regmap, LOONGSON_CANFD_MODE, mode);
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_CONF, &conf);
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_LOOPBACK)
> +		conf |= REG_CONF_ILBP;
> +	else
> +		conf &= ~REG_CONF_ILBP;
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_FD_NON_ISO)
> +		conf |= REG_CONF_NISOFD;
> +	else
> +		conf &= ~REG_CONF_NISOFD;
> +
> +	/* One shot mode supported indirectly via Retransmit limit */
> +	conf &= ~FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH);
> +
> +	if (ctrlmode->flags & CAN_CTRLMODE_ONE_SHOT)
> +		conf |= REG_CONF_RTRLE;
> +	else
> +		conf |= REG_CONF_RTRLE | FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH);
> +
> +	regmap_write(priv->regmap, LOONGSON_CANFD_CONF, conf);
> +}
> +
> +/**
> + * loongson_canfd_chip_start() - This routine starts the driver
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int loongson_canfd_chip_start(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct can_ctrlmode mode;
> +	u16 int_ena, int_msk;
> +	int ret;
> +
> +	/* Configure bit-rates and ssp */
> +	ret = loongson_canfd_set_bittiming(ndev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = loongson_canfd_set_data_bittiming(ndev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = loongson_canfd_set_secondary_sample_point(ndev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Configure modes */
> +	mode.flags = priv->can.ctrlmode;
> +	mode.mask = 0xFFFFFFFF;
> +	loongson_canfd_set_mode(priv, &mode);

Why do you need to set the mask to 0xFFFFFFFF? It seems that you are
not even using it in loongson_canfd_set_mode(). Isn't it possible to
directly pass priv->can.ctrlmode?

> +	/* Configure interrupts */
> +	int_ena = REG_INT_STAT_RBNEI | REG_INT_STAT_TXBHCI |
> +		  REG_INT_STAT_EWLI | REG_INT_STAT_FCSI;
> +
> +	/* Bus error reporting */
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		int_ena |= REG_INT_STAT_ALI | REG_INT_STAT_BEI;
> +
> +	int_msk = ~int_ena; /* Mask all disabled interrupts */
> +
> +	/* It's after reset, so there is no need to clear anything */
> +	regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, int_msk);

Nitpick: you can directly do:

	regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, ~int_ena);

and remove your int_msk variable.

> +	regmap_write(priv->regmap, LOONGSON_CANFD_INT_ENA, int_ena);
> +
> +	/* Controller enters ERROR_ACTIVE on initial FCSI */
> +	priv->can.state = CAN_STATE_STOPPED;
> +
> +	/* Enable the controller */
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, REG_CONF_ENA);
> +
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_do_set_mode() - Sets mode of the driver
> + * @ndev: Pointer to net_device structure
> + * @mode: Tells the mode of the driver
> + *
> + * This check the drivers state and calls the corresponding modes to set.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int loongson_canfd_do_set_mode(struct net_device *ndev, enum can_mode mode)
> +{
> +	int ret;
> +
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		ret = loongson_canfd_reset(ndev);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = loongson_canfd_chip_start(ndev);
> +		if (ret < 0) {
> +			netdev_err(ndev, "loongson_canfd_chip_start failed!\n");
> +			return ret;
> +		}
> +
> +		netif_wake_queue(ndev);
> +		break;
> +	default:
> +		ret = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * loongson_canfd_insert_frame() - Inserts frame to TXT buffer
> + * @priv: Pointer to private data
> + * @cf:	 Pointer to CAN frame to be inserted
> + * @buf: TXT Buffer index to which frame is inserted (0-based)
> + * @isfdf: True - CAN FD Frame, False - CAN 2.0 Frame
> + *
> + * Return:
> + * * True - Frame inserted successfully
> + * * False - Frame was not inserted due to one of:
> + *	1. TXT Buffer is not writable (it is in wrong state)
> + *	2. Invalid TXT buffer index
> + *	3. Invalid frame length
> + */
> +static bool loongson_canfd_insert_frame(struct loongson_canfd_priv *priv,
> +					const struct canfd_frame *cf, u8 buf, bool isfdf)
> +{
> +	u32 meta0, meta1;
> +
> +	if (buf >= LOONGSON_CANFD_TXBUF_NUM)
> +		return false;
> +
> +	if (!loongson_canfd_txbuf_is_writable(priv, buf))
> +		return false;
> +
> +	/* Prepare identifier */
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		meta0 = cf->can_id & CAN_EFF_MASK;
> +		meta0 |= REG_FRAME_META0_XDT;
> +	} else {
> +		meta0 = FIELD_PREP(REG_FRAME_META0_ID_BASE, cf->can_id & CAN_SFF_MASK);
> +	}
> +
> +	/* Prepare Frame format */
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		meta0 |= REG_FRAME_META0_RTR;
> +
> +	if (isfdf) {
> +		meta1 = REG_FRAME_META1_FDF;
> +
> +		if (cf->flags & CANFD_BRS)
> +			meta1 |= REG_FRAME_META1_BRS;
> +	}
> +
> +	meta1 |= FIELD_PREP(REG_FRAME_META1_DLC, can_fd_len2dlc(cf->len));
> +
> +	/* TXT buffer select */
> +	regmap_write(priv->regmap, LOONGSON_CANFD_TX_SEL, buf);
> +
> +	/* Write ID, Frame format */
> +	regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META0, meta0);
> +	regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META1, meta1);
> +
> +	/* Write Data payload */
> +	if (!(cf->can_id & CAN_RTR_FLAG)) {
> +		for (unsigned int i = 0; i < cf->len; i += LOONGSON_CANFD_DW_BYTE) {
> +			regmap_write(priv->regmap,
> +				     LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_DB_1 + i,
> +				     le32_to_cpu(*(__le32 *)(cf->data + i)));
                                                  ^^^^^^^^^^
This cast is just wrong. What if the code is run on a big endian
machine?

To begin with, here, you are writing to your registers. So it should
be the other way around: cpu_to_le32(). And to add to it, if you need
endian conversion, populate regmap_config.val_format_endian.

Also, why do you need some endian conversion only here and not on the
other registers?

> +		}
> +	}
> +
> +	return true;
> +}
> +
> +/**
> + * loongson_canfd_start_xmit() - Starts the transmission
> + * @skb: sk_buff pointer that contains data to be Txed
> + * @ndev: Pointer to net_device structure
> + *
> + * Invoked from upper layers to initiate transmission. Uses the next available free TX Buffer and
> + * populates its fields to start the transmission.
> + *
> + * Return: %NETDEV_TX_OK on success,
> + *         %NETDEV_TX_BUSY when no free TX buffer is available, negative return values reserved
> + *         for error cases.
> + */
> +static netdev_tx_t loongson_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> +	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 buf_id, tx_stat, i = 0;
> +	unsigned long tx_brp;
> +	u16 tx_bs;
> +
> +	if (can_dropped_invalid_skb(ndev, skb))
> +		return NETDEV_TX_OK;
> +
> +	if (unlikely(!loongson_canfd_txbuf_freed(priv))) {
> +		netif_stop_queue(ndev);
> +		netdev_err(ndev, "BUG!, no TXB free when queue awake!\n");
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	guard(spinlock_irqsave)(&priv->tx_lock);
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &tx_stat);
> +	tx_brp = FIELD_GET(REG_TX_STAT_BRP, tx_stat);
> +	tx_bs = FIELD_GET(REG_TX_STAT_BS, tx_stat);
> +
> +	for_each_clear_bit(i, &tx_brp, LOONGSON_CANFD_TXBUF_NUM) {
> +		u32 transmission_complete_mask = 0x3 << (i * 2);
> +
> +		if (!(tx_bs & transmission_complete_mask)) {
> +			buf_id = i;
> +			break;
> +		}
> +	}
> +
> +	if (!loongson_canfd_insert_frame(priv, cf, buf_id, can_is_canfd_skb(skb))) {
> +		netdev_err(ndev, "TXNF set but cannot insert frame into TXTB! HW Bug?");
> +		kfree_skb(skb);
> +		ndev->stats.tx_dropped++;
> +		return NETDEV_TX_OK;
> +	}
> +
> +	can_put_echo_skb(skb, ndev, buf_id, 0);
> +
> +	loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_ADD, buf_id);
> +
> +	/* Check if all TX buffers are full */
> +	if (!loongson_canfd_txbuf_freed(priv))
> +		netif_stop_queue(ndev);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +/**
> + * loongson_canfd_read_rx_frame() - Reads frame from RX FIFO
> + * @priv: Pointer to  private data
> + * @cf:	 Pointer to CAN frame struct
> + * @meta0: The first `frame format` read previously
> + * @meta1: The second `frame format` read previously
> + */
> +static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struct canfd_frame *cf,
> +					 u32 meta0, u32 meta1)
> +{
> +	u32 data, i, wc, len;
> +
> +	/* Extended Identifier Type */
> +	if (meta0 & REG_FRAME_META0_XDT)
> +		cf->can_id = (meta0 & CAN_EFF_MASK) | CAN_EFF_FLAG;
> +	else
> +		cf->can_id = FIELD_GET(REG_FRAME_META0_ID_BASE, meta0) & CAN_SFF_MASK;
> +
> +	/* BRS, ESI, RTR Flags */
> +	cf->flags = 0;
> +
> +	if (meta1 & REG_FRAME_META1_FDF) {
> +		if (meta1 & REG_FRAME_META1_BRS)
> +			cf->flags |= CANFD_BRS;
> +
> +		if (meta0 & REG_FRAME_META0_ESI)
> +			cf->flags |= CANFD_ESI;
> +	} else if (meta0 & REG_FRAME_META0_RTR) {
> +		cf->can_id |= CAN_RTR_FLAG;
> +	}
> +
> +	/* Timesamp */
           ^^^^^^^^
Typo: timestamp.

> +	cf->__res0 = meta1;
> +	cf->__res1 = meta1 >> 8;

No, you can not use the canfd_frame->__res0 and canfd_frame->__res1
fields at all. These are *reserved*. Refer to what the other CAN
drivers are doing for the hardware timestamps and use the existing
framework.

> +	wc = FIELD_GET(REG_FRAME_META1_RWCNT, meta1) - 2;
> +
> +	/* Data Length Code */
> +	len = FIELD_GET(REG_FRAME_META1_DLC, meta1);
> +	if (len > 8) {
> +		if (meta1 & REG_FRAME_META1_FDF)
> +			len = wc << 2;
> +		else
> +			len = 8;
> +	}
> +
> +	cf->len = len;
> +	if (unlikely(len > wc * LOONGSON_CANFD_DW_BYTE))
> +		len = wc * LOONGSON_CANFD_DW_BYTE;
> +
> +	/* Data */
> +	for (i = 0; i < len; i += LOONGSON_CANFD_DW_BYTE) {
> +		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
> +		*(__le32 *)(cf->data + i) = cpu_to_le32(data);
> +	}
> +
> +	while (unlikely(i < wc * LOONGSON_CANFD_DW_BYTE)) {
> +		regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data);
> +		i += LOONGSON_CANFD_DW_BYTE;
> +	}
> +}
> +
> +/**
> + * loongson_canfd_rx() -  Called from CAN ISR to complete the received frame processing
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from the CAN isr to process the Rx frames. It does minimal
> + * processing and invokes "netif_receive_skb" to complete further processing.
> + * Return: 1 when frame is passed to the network layer, 0 when the first frame word is read but
> + *	   system is out of free SKBs temporally and left code to resolve SKB allocation later,
> + *         -%EAGAIN in a case of empty Rx FIFO.
> + */
> +static int loongson_canfd_rx(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct canfd_frame *cf;
> +	struct sk_buff *skb;
> +	u32 meta0, meta1;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta0);
> +	regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta1);
> +
> +	/* Number of characters received */
> +	if (!FIELD_GET(REG_FRAME_META1_RWCNT, meta1))
> +		return -EAGAIN;
> +
> +	/* Flexible Data-rate Format */
> +	if (meta1 & REG_FRAME_META1_FDF)
> +		skb = alloc_canfd_skb(ndev, &cf);
> +	else
> +		skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
> +	if (unlikely(!skb))
> +		return -ENOMEM;
> +
> +	loongson_canfd_read_rx_frame(priv, cf, meta0, meta1);
> +
> +	netif_receive_skb(skb);
> +
> +	return 1;
> +}
> +
> +/**
> + * loongson_canfd_read_fault_state() - Reads Loongson CANFD fault state.
> + * @ndev: Pointer to net_device structure
> + *
> + * Returns: Fault confinement state of controller
> + */
> +static enum can_state loongson_canfd_read_fault_state(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	enum can_state sts = CAN_STATE_ERROR_PASSIVE;
> +	u32 fstat;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_FSTAT, &fstat);
> +
> +	switch (FIELD_GET(REG_FSTAT_MASK, fstat)) {
> +	case REG_FSTAT_ERA:
> +		u32 ewl, erl, rec_tec, max_tec;
> +
> +		regmap_read(priv->regmap, LOONGSON_CANFD_ERL, &erl);
> +		regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &rec_tec);
> +
> +		ewl = FIELD_GET(REG_ERL_EW, erl);
> +		max_tec = max(FIELD_GET(REG_ERC_REC, rec_tec), FIELD_GET(REG_ERC_TEC, rec_tec));
> +
> +		if (ewl > max_tec)
> +			sts = CAN_STATE_ERROR_ACTIVE;
> +		else
> +			sts = CAN_STATE_ERROR_WARNING;
> +		break;
> +	case REG_FSTAT_ERP:
> +		sts = CAN_STATE_ERROR_PASSIVE;
> +		break;
> +	case REG_FSTAT_BOF:
> +		sts = CAN_STATE_BUS_OFF;
> +		break;
> +	default:
> +		netdev_err(ndev, "Invalid error state.\n");
> +		break;
> +	}
> +
> +	return sts;
> +}
> +
> +/**
> + * loongson_canfd_get_bec() - Reads REC/TEC counter values from controller
> + * @priv: Pointer to private data
> + * @bec: Pointer to Error counter structure
> + */
> +static void loongson_canfd_get_bec(struct loongson_canfd_priv *priv, struct can_berr_counter *bec)
> +{
> +	u32 erc;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &erc);
> +	bec->rxerr = FIELD_GET(REG_ERC_REC, erc);
> +	bec->txerr = FIELD_GET(REG_ERC_TEC, erc);
> +}
> +
> +/**
> + * loongson_canfd_get_berr_counter() - error counter routine
> + * @ndev: Pointer to net_device structure
> + * @bec: Pointer to can_berr_counter structure
> + *
> + * Return: 0 always
> + */
> +static int loongson_canfd_get_berr_counter(const struct net_device *ndev,
> +					   struct can_berr_counter *bec)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +
> +	loongson_canfd_get_bec(priv, bec);
> +	return 0;
> +}
> +
> +/**
> + * loongson_canfd_err_interrupt() - Error frame ISR
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This is the CAN error interrupt and it will check the type of error and forward the error
> + * frame to upper layers.
> + */
> +static void loongson_canfd_err_interrupt(struct net_device *ndev, u32 isr)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct net_device_stats *stats = &ndev->stats;
> +	struct can_berr_counter bec;
> +	enum can_state state;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	u32 err_capt, alc;
> +
> +	loongson_canfd_get_bec(priv, &bec);
> +
> +	state = loongson_canfd_read_fault_state(ndev);
> +	regmap_read(priv->regmap, LOONGSON_CANFD_ERR_CAPT, &err_capt);
> +	regmap_read(priv->regmap, LOONGSON_CANFD_ALC, &alc);
> +
> +	netdev_dbg(ndev, "%s: ISR 0x%08x, rxerr %d, txerr %d, error type %lu, pos %lu, ALC id_field %lu, bit %lu\n",
> +		   __func__, isr, bec.rxerr, bec.txerr,
> +		   FIELD_GET(REG_ERR_CAPT_TYPE, err_capt),
> +		   FIELD_GET(REG_ERR_CAPT_POS, err_capt),
> +		   FIELD_GET(REG_ALC_ID_FIELD, alc),
> +		   FIELD_GET(REG_ALC_BIT_POS, alc));
> +
> +	skb = alloc_can_err_skb(ndev, &cf);
> +
> +	/*
> +	 * EWLI: error warning limit condition met
> +	 * FCSI: fault confinement state changed
> +	 * ALI:  arbitration lost (just informative)
> +	 * BEI:  bus error interrupt
> +	 */
> +	if ((isr & REG_INT_STAT_FCSI) || (isr & REG_INT_STAT_EWLI)) {
> +		netdev_info(ndev, "state changes from %s to %s\n",
> +			    can_get_state_str(priv->can.state), can_get_state_str(state));
> +
> +		if (priv->can.state == state)
> +			netdev_warn(ndev, "cur and pre state is the same!(miss intr?)\n");
> +
> +		isr = REG_INT_STAT_FCSI | REG_INT_STAT_EWLI;
> +		priv->can.state = state;
> +		switch (state) {
> +		case CAN_STATE_BUS_OFF:
> +			priv->can.can_stats.bus_off++;
> +			if (priv->can.restart_ms)
> +				regmap_write(priv->regmap, LOONGSON_CANFD_CMD,
> +					     REG_CMD_ERCRST);
> +
> +			can_bus_off(ndev);
> +			if (skb)
> +				cf->can_id |= CAN_ERR_BUSOFF;
> +			break;
> +		case CAN_STATE_ERROR_PASSIVE:
> +			priv->can.can_stats.error_passive++;
> +			if (skb) {
> +				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
> +				if (bec.rxerr >= CAN_ERROR_PASSIVE_THRESHOLD)
> +					cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE;
> +				else
> +					cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE;
> +				cf->data[6] = bec.txerr;
> +				cf->data[7] = bec.rxerr;
> +			}
> +			break;
> +		case CAN_STATE_ERROR_WARNING:
> +			priv->can.can_stats.error_warning++;
> +			if (skb) {
> +				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
> +				if (bec.txerr > bec.rxerr)
> +					cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
> +				else
> +					cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
> +				cf->data[6] = bec.txerr;
> +				cf->data[7] = bec.rxerr;
> +			}
> +			break;
> +		case CAN_STATE_ERROR_ACTIVE:
> +			cf->can_id |= CAN_ERR_CNT;
> +			cf->data[1] = CAN_ERR_CRTL_ACTIVE;
> +			cf->data[6] = bec.txerr;
> +			cf->data[7] = bec.rxerr;
> +			break;
> +		default:
> +			netdev_err(ndev, "Unexpected state: %d, %s!\n", state,
> +				   can_get_state_str(state));
> +			break;
> +		}
> +	}
> +
> +	/* Check for Arbitration Lost interrupt */
> +	if (isr & REG_INT_STAT_ALI) {
> +		isr = REG_INT_STAT_ALI;
> +		netdev_err(ndev, "Arbitration Lost interrupt\n");
> +		priv->can.can_stats.arbitration_lost++;
> +		if (skb) {
> +			cf->can_id |= CAN_ERR_LOSTARB;
> +			cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
> +		}
> +	}
> +
> +	/* Check for Bus Error interrupt */
> +	if (isr & REG_INT_STAT_BEI) {
> +		isr = REG_INT_STAT_BEI;
> +		netdev_err(ndev, "Bus Error interrupt\n");
> +		priv->can.can_stats.bus_error++;
> +		stats->rx_errors++;
> +		if (skb) {
> +			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +			cf->data[2] = CAN_ERR_PROT_UNSPEC;
> +			cf->data[3] = CAN_ERR_PROT_LOC_UNSPEC;
> +		}
> +	}
> +
> +	if (skb)
> +		netif_rx(skb);
> +
> +	regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, isr);
> +	regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, isr << 16);
> +}
> +
> +static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota)
> +{
> +	struct net_device *ndev = napi->dev;
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	int work_done = 0, ret = 1;
> +	u32 sts, rx_frc, rx_sts;
> +
> +	regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
> +	rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
> +
> +	while (rx_frc && work_done < quota && ret > 0) {
> +		ret = loongson_canfd_rx(ndev);
> +		work_done++;
> +		regmap_read(priv->regmap, LOONGSON_CANFD_RX_STAT, &rx_sts);
> +		rx_frc = FIELD_GET(REG_RX_STAT_RXFRC, rx_sts);
> +	}
> +
> +	/* Check for RX FIFO Overflow */
> +	regmap_read(priv->regmap, LOONGSON_CANFD_STAT, &sts);
> +	if (sts & REG_STAT_DOR) {
> +		struct net_device_stats *stats = &ndev->stats;
> +		struct can_frame *cf;
> +		struct sk_buff *skb;
> +
> +		netdev_info(ndev, "Loongson canfd RX overflow\n");
> +		stats->rx_over_errors++;
> +		stats->rx_errors++;
> +
> +		skb = alloc_can_err_skb(ndev, &cf);
> +		if (skb) {
> +			cf->can_id |= CAN_ERR_CRTL;
> +			cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> +			stats->rx_packets++;
> +			stats->rx_bytes += cf->can_dlc;

The socket CAN error frames do not correspond to actual frames on the
bus. Do not increase stats->rx_packets and stats->rx_bytes.

> +			netif_rx(skb);
> +		}
> +
> +		/* Clear Data Overrun */
> +		regmap_write(priv->regmap, LOONGSON_CANFD_CMD, REG_CMD_CDO);
> +	}
> +
> +	if (!rx_frc && ret != 0) {
> +		if (napi_complete_done(napi, work_done)) {
> +			/*
> +			 * Clear and enable RBNEI. It is level-triggered, so
> +			 * there is no race condition.
> +			 */
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT,
> +				     REG_INT_STAT_RBNEI);
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK,
> +				     (REG_INT_STAT_RBNEI << 16));
> +		}
> +	}
> +
> +	return work_done;
> +}
> +
> +static void loongson_canfd_tx_interrupt(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	struct net_device_stats *stats = &ndev->stats;
> +	enum loongson_canfd_txbuf_sts sts;
> +	u32 buf_id;
> +
> +	guard(spinlock_irqsave)(&priv->tx_lock);
> +
> +	while ((buf_id = loongson_canfd_get_txbuf_id(priv, &sts)) < LOONGSON_CANFD_TXBUF_NUM) {
> +		switch (sts) {
> +		case TX_BS_VALID:
> +			stats->tx_bytes += can_get_echo_skb(ndev, buf_id, NULL);
> +			stats->tx_packets++;
> +			break;
> +		case TX_BS_FAIL:
> +			u32 cnt;
> +			/*
> +			 * This indicated that retransmit limit has been reached.
> +			 * Obviously we should not echo the frame, but also not indicate any
> +			 * kind of error. If desired, it was already reported (possible
> +			 * multiple times) on each arbitration lost.
> +			 */
> +			regmap_read(priv->regmap, LOONGSON_CANFD_TX_FR_CNT, &cnt);
> +			netdev_warn(ndev, "TXB in FAIL state, TX frame count: %d\n", cnt);
> +			can_free_echo_skb(ndev, buf_id, NULL);
> +			stats->tx_dropped++;
> +			break;
> +		case TX_BS_CANCEL:
> +			/*
> +			 * We *could* re-queue the frame, but multiqueue/abort is
> +			 * not supported yet anyway.
> +			 */
> +			netdev_warn(ndev, "TXB in CANCEL state\n");
> +			can_free_echo_skb(ndev, buf_id, NULL);
> +			stats->tx_dropped++;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_SR_CLEAN, buf_id);
> +	}
> +
> +	/*
> +	 * Clear the interrupt again. We do not want to receive again interrupt
> +	 * for the buffer already handled. If it is the last finished one then
> +	 * it would cause log of spurious interrupt.
> +	 */
> +	regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_TXBHCI);
> +
> +	/* Check if at least one TX buffer is free */
> +	if (loongson_canfd_txbuf_freed(priv))
> +		netif_wake_queue(ndev);
> +}
> +
> +/**
> + * loongson_canfd_interrupt() - CAN Isr
> + * @irq: irq number
> + * @dev_id: device id pointer
> + *
> + * This is the Loongson CANFD ISR. It checks for the type of interrupt
> + * and invokes the corresponding ISR.
> + *
> + * Return:
> + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
> + */
> +static irqreturn_t loongson_canfd_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *ndev = (struct net_device *)dev_id;
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	u32 isr, imask;
> +
> +	for (unsigned int irq_loops = 0; irq_loops < 10000; irq_loops++) {
> +		/* Get the interrupt status */
> +		regmap_read(priv->regmap, LOONGSON_CANFD_INT_STAT, &isr);
> +		if (!isr)
> +			return irq_loops ? IRQ_HANDLED : IRQ_NONE;

			if (irq_loops)
				return IRQ_HANDLED;
			else
				return IRQ_NONE;

> +
> +		/* Receive Buffer Not Empty Interrupt */
> +		if (isr & REG_INT_STAT_RBNEI) {
> +			/*
> +			 * Mask RXBNEI the first, then clear interrupt and schedule NAPI.
> +			 * Even if another IRQ fires, RBNEI will always be 0 (masked).
> +			 */
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, REG_INT_STAT_RBNEI);
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI);
> +			napi_schedule(&priv->napi);
> +		}
> +
> +		/* TX Buffer HW Command Interrupt */
> +		if (isr & REG_INT_STAT_TXBHCI)
> +			loongson_canfd_tx_interrupt(ndev);
> +
> +		/* Error interrupts */
> +		imask = isr & REG_INT_STAT_ERRORI;
> +		if (imask) {
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, imask);
> +			regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, imask);
> +			loongson_canfd_err_interrupt(ndev, isr);
> +		}
> +
> +		/* Ignore RI, TI, LFI, RFI, BSI */
> +	}
> +
> +	netdev_err(ndev, "Intterupt state: 0x%x.\n", isr);
> +
> +	if (isr & REG_INT_STAT_TXBHCI) {
> +		for (unsigned int i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) {
> +			u32 sts = loongson_canfd_get_txbuf_sts(priv, i);
> +
> +			netdev_err(ndev, "TX buf %d status: 0x%x.\n", i, sts);
> +		}
> +	}
> +
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR);
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK,
> +			   REG_INT_MASK_SET, REG_INT_MASK_SET);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * loongson_canfd_chip_stop() - Driver stop routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers stop routine. It will disable the interrupts and
> + * disable the controller.
> + */
> +static void loongson_canfd_chip_stop(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +
> +	/* Disable interrupts and disable CAN */
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR);
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK,
> +			   REG_INT_MASK_SET, REG_INT_MASK_SET);
> +	regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, 0);
> +
> +	priv->can.state = CAN_STATE_STOPPED;
> +}
> +
> +/**
> + * loongson_canfd_open() - Driver open routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver open routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int loongson_canfd_open(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +	int ret;
> +
> +	ret = loongson_canfd_reset(ndev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Common open */
> +	ret = open_candev(ndev);
> +	if (ret) {
> +		netdev_warn(ndev, "open_candev failed!\n");
> +		return ret;
> +	}
> +
> +	ret = request_irq(ndev->irq, loongson_canfd_interrupt, IRQF_SHARED, ndev->name, ndev);
> +	if (ret < 0) {
> +		netdev_err(ndev, "irq allocation for CAN failed\n");
> +		goto err_irq;
> +	}
> +
> +	ret = loongson_canfd_chip_start(ndev);
> +	if (ret < 0) {
> +		netdev_err(ndev, "loongson_canfd_chip_start failed!\n");
> +		goto err_chip_start;
> +	}
> +
> +	netdev_info(ndev, "loongson_canfd_device registered\n");
> +	napi_enable(&priv->napi);
> +	netif_start_queue(ndev);
> +
> +	return 0;
> +
> +err_chip_start:
> +	free_irq(ndev->irq, ndev);
> +err_irq:
> +	close_candev(ndev);
> +	return ret;
> +}
> +
> +/**
> + * loongson_canfd_close() - Driver close routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 always
> + */
> +static int loongson_canfd_close(struct net_device *ndev)
> +{
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +
> +	netif_stop_queue(ndev);
> +	napi_disable(&priv->napi);
> +	loongson_canfd_chip_stop(ndev);
> +	free_irq(ndev->irq, ndev);
> +	close_candev(ndev);
> +
> +	return 0;
> +}
> +
> +static const struct net_device_ops loongson_canfd_netdev_ops = {
> +	.ndo_open       = loongson_canfd_open,
> +	.ndo_stop       = loongson_canfd_close,
> +	.ndo_start_xmit = loongson_canfd_start_xmit,
> +};

Also add a struct ethtool_ops and populate
ethtool_ops.get_ts_info. Something like:

	static const struct ethtool_ops es58x_ethtool_ops = {
		.get_ts_info = can_ethtool_op_get_ts_info_hwts,
	};

Refer to what the other CAN drivers with hardware timestamps are
doing.

> +static const struct regmap_range loongson_canfd_reg_table_wr_range[] = {
> +	regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_CONF),
> +	regmap_reg_range(LOONGSON_CANFD_CMD, LOONGSON_CANFD_CMD),
> +	regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_ERL),
> +	regmap_reg_range(LOONGSON_CANFD_CTR_PRES, LOONGSON_CANFD_CTR_PRES),
> +	regmap_reg_range(LOONGSON_CANFD_SSP_CFG, LOONGSON_CANFD_SSP_CFG),
> +	regmap_reg_range(LOONGSON_CANFD_TS, LOONGSON_CANFD_FLT_CTRL),
> +	regmap_reg_range(LOONGSON_CANFD_TX_CMD, LOONGSON_CANFD_TX_DATA_18),
> +};
> +
> +static const struct regmap_range loongson_canfd_reg_table_rd_range[] = {
> +	regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_STAT),
> +	regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_BRE),
> +	regmap_reg_range(LOONGSON_CANFD_ERR_CAPT, LOONGSON_CANFD_TX_STAT),
> +	regmap_reg_range(LOONGSON_CANFD_TX_SEL, LOONGSON_CANFD_TX_DATA_18),
> +};
> +
> +static const struct regmap_access_table loongson_canfd_reg_table_wr = {
> +	.yes_ranges = loongson_canfd_reg_table_wr_range,
> +	.n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_wr_range),
> +};
> +
> +static const struct regmap_access_table loongson_canfd_reg_table_rd = {
> +	.yes_ranges = loongson_canfd_reg_table_rd_range,
> +	.n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_rd_range),
> +};
> +
> +static bool loongson_canfd_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case LOONGSON_CANFD_MODE:
> +	case LOONGSON_CANFD_CONF:
> +	case LOONGSON_CANFD_STAT:
> +	case LOONGSON_CANFD_INT_STAT:
> +	case LOONGSON_CANFD_INT_ENA:
> +	case LOONGSON_CANFD_INT_MASK:
> +	case LOONGSON_CANFD_ERL:
> +	case LOONGSON_CANFD_FSTAT:
> +	case LOONGSON_CANFD_ERC:
> +	case LOONGSON_CANFD_ERR_CAPT:
> +	case LOONGSON_CANFD_ALC:
> +	case LOONGSON_CANFD_TX_FR_CNT:
> +	case LOONGSON_CANFD_RX_STAT:
> +	case LOONGSON_CANFD_RX_DATA:
> +	case LOONGSON_CANFD_TX_STAT:
> +	case LOONGSON_CANFD_TX_SEL:
> +		return true;
> +	default:
> +		return false;
> +	};
> +}
> +
> +static const struct regmap_config loongson_cangfd_regmap = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.wr_table	= &loongson_canfd_reg_table_wr,
> +	.rd_table	= &loongson_canfd_reg_table_rd,
> +	.volatile_reg	= loongson_canfd_volatile_reg,
> +	.max_register	= LOONGSON_CANFD_TX_DATA_18,
> +	.cache_type	= REGCACHE_MAPLE,
> +};
> +
> +static int loongson_canfd_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct loongson_canfd_priv *priv;
> +	struct net_device *ndev;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	void __iomem *base;
> +	u32 clk_rate;
> +	int ret, irq;
> +
> +	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	regmap = devm_regmap_init_mmio(dev, base, &loongson_cangfd_regmap);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	device_property_read_u32(dev, "clock-frequency", &clk_rate);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	/* Create a CAN device instance */
> +	ndev = alloc_candev(sizeof(*priv), LOONGSON_CANFD_TXBUF_NUM);
> +	if (!ndev)
> +		return -ENOMEM;
> +
> +	priv = netdev_priv(ndev);
> +	spin_lock_init(&priv->tx_lock);
> +	priv->regmap = regmap;
> +	priv->res = res;
> +
> +	priv->can.clock.freq = clk_rate;
> +	priv->can.bittiming_const = &loongson_canfd_bit_timing;
> +	priv->can.fd.data_bittiming_const = &loongson_canfd_bit_timing_data;
> +	priv->can.fd.tdc_const = &loongson_canfd_tdc_data;
> +
> +	priv->can.fd.do_set_data_bittiming = loongson_canfd_set_data_bittiming;
> +	priv->can.fd.do_get_auto_tdcv = loongson_canfd_get_auto_tdcv;
> +	priv->can.do_set_mode = loongson_canfd_do_set_mode;
> +	priv->can.do_set_bittiming = loongson_canfd_set_bittiming;
> +	priv->can.do_get_berr_counter = loongson_canfd_get_berr_counter;
> +
> +	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY |
> +				       CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_ONE_SHOT |
> +				       CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD |
> +				       CAN_CTRLMODE_PRESUME_ACK | CAN_CTRLMODE_FD_NON_ISO |
> +				       CAN_CTRLMODE_TDC_AUTO;

Does your device allow to send and receive Classical CAN frames with a
DLC greater than 8? c.f. CAN_CTRLMODE_CC_LEN8_DLC.

> +	ndev->irq = irq;
> +	ndev->flags |= IFF_ECHO;	/* We support local echo */
> +	platform_set_drvdata(pdev, ndev);
> +	ndev->netdev_ops = &loongson_canfd_netdev_ops;
> +	SET_NETDEV_DEV(ndev, dev);
> +
> +	ret = loongson_canfd_reset(ndev);
> +	if (ret < 0)
> +		goto err_candev_free;
> +
> +	netif_napi_add(ndev, &priv->napi, loongson_canfd_rx_napi);
> +
> +	ret = register_candev(ndev);
> +	if (ret) {
> +		dev_err(dev, "register_candev failed with %d\n", ret);
> +		goto err_candev_free;
> +	}
> +
> +	return 0;
> +
> +err_candev_free:
> +	free_candev(ndev);
> +	return ret;
> +}
> +
> +static void loongson_canfd_remove(struct platform_device *pdev)
> +{
> +	struct net_device *ndev = platform_get_drvdata(pdev);
> +	struct loongson_canfd_priv *priv = netdev_priv(ndev);
> +
> +	unregister_candev(ndev);
> +	netif_napi_del(&priv->napi);
> +	free_candev(ndev);
> +}
> +
> +static const struct acpi_device_id loongson_canfd_acpi_match[] = {
> +	{ "LOON0015" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, loongson_canfd_acpi_match);
> +
> +static struct platform_driver loongson_canfd_driver = {
> +	.probe  = loongson_canfd_probe,
> +	.remove = loongson_canfd_remove,
> +	.driver = {
> +		.name = DEV_NAME,
> +		.acpi_match_table = loongson_canfd_acpi_match,
> +	},
> +};
> +module_platform_driver(loongson_canfd_driver);
> +
> +MODULE_AUTHOR("Loongson Technology Corporation Limited");
> +MODULE_DESCRIPTION("Loongson CAN-FD Controller driver");
> +MODULE_LICENSE("GPL");


Yours sincerely,
Vincent Mailhol

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-06-08 17:43 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-08  8:49 [PATCH v2 0/2] Add Loongson CAN-FD controller driver Binbin Zhou
2026-06-08  8:49 ` [PATCH v2 1/2] can: " Binbin Zhou
2026-06-08 17:43   ` Vincent Mailhol
2026-06-08  8:49 ` [PATCH v2 2/2] can: loongson_canfd: Add RXDMA support Binbin Zhou
2026-06-08 12:13   ` Vincent Mailhol

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox