* [PATCH 0/2] can: add arasan canfd controller support
@ 2026-08-03 14:05 Jisheng Zhang
2026-08-03 14:05 ` [PATCH 1/2] dt-bindings: can: add arasan canfd controller Jisheng Zhang
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
0 siblings, 2 replies; 8+ messages in thread
From: Jisheng Zhang @ 2026-08-03 14:05 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-can, devicetree, linux-kernel
This series add arasan canfd controller support.
patch1 documents the dt-bindings for arasan canfd controller
patch2 adds the driver for the arasan canfd controller
Jisheng Zhang (2):
dt-bindings: can: add arasan canfd controller
can: arasan-canfd: add driver for arasan CAN-FD controller
.../bindings/net/can/arasan,canfd.yaml | 63 +
drivers/net/can/Kconfig | 6 +
drivers/net/can/Makefile | 1 +
drivers/net/can/arasan-canfd.c | 1057 +++++++++++++++++
4 files changed, 1127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
create mode 100644 drivers/net/can/arasan-canfd.c
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] dt-bindings: can: add arasan canfd controller
2026-08-03 14:05 [PATCH 0/2] can: add arasan canfd controller support Jisheng Zhang
@ 2026-08-03 14:05 ` Jisheng Zhang
2026-08-03 14:31 ` sashiko-bot
2026-08-03 15:17 ` Krzysztof Kozlowski
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
1 sibling, 2 replies; 8+ messages in thread
From: Jisheng Zhang @ 2026-08-03 14:05 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-can, devicetree, linux-kernel
Add documentation for the arasan canfd controller.
Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
---
.../bindings/net/can/arasan,canfd.yaml | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
diff --git a/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
new file mode 100644
index 000000000000..a120c11ce304
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/can/arasan,canfd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title:
+ Arasan CAN-FD controller
+
+maintainers:
+ - Jisheng Zhang <jszhang@kernel.org>
+
+allOf:
+ - $ref: can-controller.yaml#
+
+properties:
+ compatible:
+ const: arasan,canfd
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: can
+ - const: sys
+
+ resets:
+ maxItems: 2
+
+ reset-names:
+ items:
+ - const: can
+ - const: sys
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ can@e5060000 {
+ compatible = "arasan,canfd";
+ reg = <0xe5060000 0x1000>;
+ interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk 32>, <&clk 16>;
+ clock-names = "can", "sys";
+ resets = <&rst 3>, <&rst 2>;
+ reset-names = "can", "sys";
+ };
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller
2026-08-03 14:05 [PATCH 0/2] can: add arasan canfd controller support Jisheng Zhang
2026-08-03 14:05 ` [PATCH 1/2] dt-bindings: can: add arasan canfd controller Jisheng Zhang
@ 2026-08-03 14:05 ` Jisheng Zhang
2026-08-03 14:43 ` sashiko-bot
` (2 more replies)
1 sibling, 3 replies; 8+ messages in thread
From: Jisheng Zhang @ 2026-08-03 14:05 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-can, devicetree, linux-kernel
Add driver for the arasan CAN-FD controller.
Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
---
drivers/net/can/Kconfig | 6 +
drivers/net/can/Makefile | 1 +
drivers/net/can/arasan-canfd.c | 1057 ++++++++++++++++++++++++++++++++
3 files changed, 1064 insertions(+)
create mode 100644 drivers/net/can/arasan-canfd.c
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index a8fad6fe5302..f81a46efc4a6 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -83,6 +83,12 @@ config CAN_CALC_BITTIMING
config CAN_RX_OFFLOAD
bool
+config CAN_ARASAN_CANFD
+ tristate "Arasan CAN FD controller"
+ depends on COMMON_CLK && HAS_IOMEM
+ help
+ Say Y here if you want to support for Arasan CAN FD.
+
config CAN_AT91
tristate "Atmel AT91 onchip CAN controller"
depends on (ARCH_AT91 || COMPILE_TEST) && HAS_IOMEM
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 4010d17f8583..3b50bf39d473 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -15,6 +15,7 @@ obj-y += spi/
obj-y += usb/
obj-y += softing/
+obj-$(CONFIG_CAN_ARASAN_CANFD) += arasan-canfd.o
obj-$(CONFIG_CAN_AT91) += at91_can.o
obj-$(CONFIG_CAN_BXCAN) += bxcan.o
obj-$(CONFIG_CAN_CAN327) += can327.o
diff --git a/drivers/net/can/arasan-canfd.c b/drivers/net/can/arasan-canfd.c
new file mode 100644
index 000000000000..63a6191f84f6
--- /dev/null
+++ b/drivers/net/can/arasan-canfd.c
@@ -0,0 +1,1057 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Arasan CANFD driver
+ *
+ * Copyright (C) 2026 Synaptics Incorporated
+ *
+ * Author: Jisheng Zhang <jszhang@kernel.org>
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/phy/phy.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/u64_stats_sync.h>
+
+#define INTERRUPT_MASK0 0x3fc
+#define INTERRUPT_MASK1 0x400
+#define OPERATIONAL 0x404
+#define OPERATIONAL_EN BIT(0)
+#define LOOPBACK BIT(7)
+#define CONTROL 0x408
+#define SOFTRESET BIT(0)
+#define DONTCHECKID BIT(4)
+#define DISABLE_ECC BIT(5)
+#define LVL_ISOFD BIT(6)
+#define DMA_SOFTRESET BIT(20)
+#define STATUS 0x40c
+#define STATUS_REC_MASK GENMASK(9, 0)
+#define STATUS_TEC_MASK GENMASK(19, 10)
+#define HITXFIFO 0x410
+#define LOTXFIFO 0x414
+#define LVL_CLASSIC_BAUD 0x424
+#define LVL_FD_BAUD 0x428
+#define LVL_ALLOWABLE_CLASSIC_JUMP 0x42c
+#define LVL_ALLOWABLE_FD_JUMP 0x430
+#define LVL_CLASSIC_END_OF_SYNC_SEG 0x434
+#define LVL_FD_END_OF_SYNC_SEG 0x438
+#define LVL_CLASSIC_END_OF_PROP_SEG 0x43c
+#define LVL_FD_END_OF_PROP_SEG 0x440
+#define LVL_CLASSIC_END_OF_PHASE_SEG1 0x444
+#define LVL_FD_END_OF_PHASE_SEG1 0x448
+#define LVL_TUR_INCREMENT 0x450
+#define INTERRUPTS_FROM_HW 0x48c
+#define OVERLOAD BIT(0)
+#define GOOD_RX_FRAME BIT(1)
+#define TX_RETRANSMIT BIT(2)
+#define ERROR_RX_FRAME BIT(6)
+#define TX_FIFO_EMPTY BIT(8)
+#define USED_BUFFERS_NOT_EMPTY BIT(11)
+#define CL_NO_ACK BIT(12)
+#define FD_NO_ACK BIT(13)
+#define CLTX_GOOD BIT(14)
+#define FDTX_GOOD BIT(15)
+#define BUS_STATE1 BIT(16)
+#define BUS_STATE2 BIT(17)
+#define PANICS BIT(26)
+#define BASE_ADDRESS 0x490
+#define NUMBER_OF_PAIRS 0x494
+#define WATCHDOG_LIMIT 0x498
+#define SELF_ADDRESS 0x49c
+#define ALLOWED_RETRANSMITS 0x4a0
+#define RX_PTR_BUFS 0x4a4
+#define RX_PTR_BUFS_COUNT 0x4a8
+#define USED_BUFS_PTR 0x4ac
+#define TYPE_MASK GENMASK(1, 0)
+#define TYPE_TX 1
+#define TYPE_RX 2
+#define TYPE_TX_ABORT 3
+#define USED_BUFS_PTR_COUNT 0x4b0
+#define LVL_SSP_DELAY 0x4d0
+#define ACTUAL_SSP_DELAY 0x4d4
+#define R_BASE_ADDR 0xf00
+#define R_ECC_ADDR 0xf04
+#define W_BASE_ADDR 0xf08
+#define W_ECC_ADDR 0xf0c
+
+#define CAN_MAX_RX_QUEUE 8
+#define CAN_MAX_TX_QUEUE 8
+#define CAN_MAX_FILTER 255
+#define ARASAN_CANFD_NAPI_WEIGHT 8
+#define DEFAULT_SSP 17
+
+#define TX_HEADER0_ID GENMASK(22, 12)
+#define TX_HEADER0_ESI BIT(24)
+#define TX_HEADER0_BRS BIT(25)
+#define TX_HEADER0_RTR BIT(26)
+#define TX_HEADER0_IDE BIT(27)
+#define TX_HEADER0_TYPE_MSK GENMASK(31, 28)
+#define TX_HEADER0_TYPE_CL 1
+#define TX_HEADER0_TYPE_FD 2
+
+#define RX_HEADER0_TYPE_MSK GENMASK(1, 0)
+#define RX_HEADER0_TYPE_CL 1
+#define RX_HEADER0_TYPE_FD 2
+#define RX_HEADER0_B_EXT BIT(2)
+#define RX_HEADER0_B_RTR BIT(3)
+#define RX_HEADER0_STATUS_MSK GENMASK(13, 4)
+#define RX_HEADER0_DLC_MSK GENMASK(24, 14)
+#define RX_HEADER0_ID_0_6 GENMASK(31, 25)
+#define RX_HEADER1_ID_7_10 GENMASK(3, 0)
+#define RX_HEADER1_EXTID GENMASK(21, 4)
+#define RX_HEADER1_EXTID GENMASK(21, 4)
+#define RX_HEADER2_CL_RTR BIT(8)
+#define RX_HEADER2_CL_IDE BIT(9)
+#define RX_HEADER2_FD_IDE BIT(18)
+#define RX_HEADER2_FD_ESI BIT(19)
+#define RX_HEADER2_FD_BRS BIT(20)
+
+struct can_rx_filter {
+ u32 mask;
+ u32 id;
+};
+
+struct can_rx_frame {
+ struct {
+ u32 header0;
+ u32 header1;
+ u32 header2;
+ u32 resv_0;
+ u32 timestamp_l;
+ u32 timestamp_h;
+ } header;
+ u8 data[CANFD_MAX_DLEN];
+};
+
+struct can_tx_frame {
+ struct header {
+ u32 header0;
+ u32 ext_id;
+ u32 resv_0;
+ } header;
+ u8 data[CANFD_MAX_DLEN];
+};
+
+struct can_read_buffer {
+ struct can_tx_frame tx_frames[CAN_MAX_TX_QUEUE];
+ struct can_rx_filter rx_filters[CAN_MAX_FILTER];
+};
+
+struct can_write_buffer {
+ struct can_rx_frame rx_frames[CAN_MAX_RX_QUEUE];
+};
+
+struct can_ecc_buffer {
+ struct {
+ u8 tx_frames[CAN_MAX_TX_QUEUE][sizeof(struct can_tx_frame) / 4];
+ u8 rx_filters[CAN_MAX_FILTER][sizeof(struct can_rx_filter) / 4];
+ } read;
+
+ struct {
+ u8 rx_frames[CAN_MAX_RX_QUEUE][sizeof(struct can_rx_frame) / 4];
+ } write;
+};
+
+struct can_buffer {
+ struct can_read_buffer r_buffer;
+ struct can_write_buffer w_buffer;
+ struct can_ecc_buffer ecc_buffer;
+};
+
+struct arasan_canfd_priv {
+ struct can_priv can;
+ /* Lock for synchronizing TX handling */
+ spinlock_t tx_lock;
+ u32 txq_avail;
+ struct napi_struct napi;
+ struct device *dev;
+ void __iomem *base;
+ struct can_buffer *buf;
+ dma_addr_t buf_dma;
+ u32 ssp;
+ struct clk *sys_clk;
+ struct clk *can_clk;
+ struct reset_control *sys_rst;
+ struct reset_control *can_rst;
+ struct phy *transceiver;
+};
+
+static const struct can_bittiming_const arasan_canfd_bittiming_const = {
+ .name = KBUILD_MODNAME,
+ .tseg1_min = 1,
+ .tseg1_max = 256,
+ .tseg2_min = 1,
+ .tseg2_max = 128,
+ .sjw_max = 128,
+ .brp_min = 1,
+ .brp_max = 65536,
+ .brp_inc = 1,
+};
+
+static const struct can_bittiming_const arasan_canfd_data_bittiming_const = {
+ .name = KBUILD_MODNAME,
+ .tseg1_min = 1,
+ .tseg1_max = 256,
+ .tseg2_min = 1,
+ .tseg2_max = 128,
+ .sjw_max = 128,
+ .brp_min = 1,
+ .brp_max = 65536,
+ .brp_inc = 1,
+};
+
+static const u32 ecc_syndrom[7] = {
+ 0xC14840FF,
+ 0x2124FF90,
+ 0x6CFF0808,
+ 0xFF01A444,
+ 0x16F092A6,
+ 0x101F7161,
+ 0x8A820F1B,
+};
+
+static inline int parity(unsigned int x)
+{
+ /*
+ * public domain code snippet, lifted from
+ * http://www-graphics.stanford.edu/~seander/bithacks.html
+ */
+ x ^= x >> 1;
+ x ^= x >> 2;
+ x = (x & 0x11111111U) * 0x11111111U;
+ return (x >> 28) & 1;
+}
+
+static u8 ecc_calc(u32 data)
+{
+ int i;
+ u8 result = 0;
+
+ for (i = 0; i < 7; i++) {
+ if (parity(data & ecc_syndrom[i]))
+ result |= BIT(i);
+ }
+
+ return result;
+}
+
+static inline u32 arasan_canfd_read(const struct arasan_canfd_priv *priv, u32 off)
+{
+ return readl_relaxed(priv->base + off);
+}
+
+static inline void arasan_canfd_write(const struct arasan_canfd_priv *priv, u32 off,
+ u32 value)
+{
+ writel_relaxed(value, priv->base + off);
+}
+
+static int arasan_canfd_set_bittiming(struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct can_bittiming *dbt = &priv->can.fd.data_bittiming;
+ u32 cl_2, cl_4, fd_2, fd_4, classic_cycle, fd_cycle;
+
+ if (arasan_canfd_read(priv, OPERATIONAL) & OPERATIONAL_EN) {
+ netdev_alert(ndev, "BUG! Cannot set bittiming when CAN operational is enabled\n");
+ return -EPERM;
+ }
+
+ arasan_canfd_write(priv, LVL_TUR_INCREMENT, 0x8000);
+
+ classic_cycle = priv->can.clock.freq / bt->bitrate;
+ cl_2 = classic_cycle / 2;
+ cl_4 = classic_cycle / 4;
+ arasan_canfd_write(priv, LVL_CLASSIC_BAUD, classic_cycle);
+ arasan_canfd_write(priv, LVL_ALLOWABLE_CLASSIC_JUMP, cl_2);
+ arasan_canfd_write(priv, LVL_CLASSIC_END_OF_SYNC_SEG, cl_4);
+ arasan_canfd_write(priv, LVL_CLASSIC_END_OF_PROP_SEG, cl_2 - 1);
+ arasan_canfd_write(priv, LVL_CLASSIC_END_OF_PHASE_SEG1,
+ classic_cycle * bt->sample_point / 1000);
+
+ fd_cycle = priv->can.clock.freq / dbt->bitrate;
+ fd_2 = fd_cycle / 2;
+ fd_4 = fd_cycle / 4;
+ arasan_canfd_write(priv, LVL_FD_BAUD, fd_cycle);
+ arasan_canfd_write(priv, LVL_ALLOWABLE_FD_JUMP, fd_2);
+ arasan_canfd_write(priv, LVL_FD_END_OF_SYNC_SEG, fd_4);
+ arasan_canfd_write(priv, LVL_FD_END_OF_PROP_SEG, fd_2 - 1);
+ arasan_canfd_write(priv, LVL_FD_END_OF_PHASE_SEG1, fd_cycle * dbt->sample_point / 1000);
+
+ if (dbt->bitrate > 1000000)
+ arasan_canfd_write(priv, LVL_SSP_DELAY, priv->ssp);
+
+ return 0;
+}
+
+static int arasan_canfd_chip_start(struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ u32 ier, ctrl, op = OPERATIONAL_EN;
+ int ret;
+
+ ret = arasan_canfd_set_bittiming(ndev);
+ if (ret < 0)
+ return ret;
+
+ ier = GENMASK(31, 0);
+ ier &= ~(GOOD_RX_FRAME | TX_FIFO_EMPTY | CLTX_GOOD | FDTX_GOOD | PANICS);
+ arasan_canfd_write(priv, INTERRUPT_MASK0, ier);
+
+ /* loopback? */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ op |= LOOPBACK;
+
+ ctrl = arasan_canfd_read(priv, CONTROL);
+ ctrl |= DONTCHECKID;
+ arasan_canfd_write(priv, CONTROL, ctrl);
+
+ arasan_canfd_write(priv, OPERATIONAL, op);
+
+ priv->txq_avail = GENMASK(CAN_MAX_TX_QUEUE - 1, 0);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ return 0;
+}
+
+/**
+ * arasan_canfd_do_set_mode - This sets the 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 arasan_canfd_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ int ret;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ ret = arasan_canfd_chip_start(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "arasan_canfd_chip_start failed!\n");
+ return ret;
+ }
+ netif_wake_queue(ndev);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static void arasan_canfd_err_interrupt(struct net_device *ndev, u32 isr)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ u32 val = arasan_canfd_read(priv, STATUS);
+ u32 txerr = FIELD_GET(STATUS_TEC_MASK, val);
+ u32 rxerr = FIELD_GET(STATUS_REC_MASK, val);
+ struct can_frame cf = { };
+
+ if (isr & OVERLOAD) {
+ cf.can_id |= CAN_ERR_PROT;
+ cf.data[2] |= CAN_ERR_PROT_OVERLOAD;
+ }
+
+ if (isr & ERROR_RX_FRAME) {
+ stats->rx_errors++;
+ cf.data[2] |= CAN_ERR_PROT_FORM;
+ }
+
+ if (isr & TX_RETRANSMIT) {
+ priv->can.can_stats.arbitration_lost++;
+ cf.can_id |= CAN_ERR_LOSTARB;
+ cf.data[0] |= CAN_ERR_LOSTARB_UNSPEC;
+ }
+
+ if (isr & (CL_NO_ACK | FD_NO_ACK)) {
+ stats->tx_errors++;
+ cf.can_id |= CAN_ERR_ACK;
+ cf.data[3] |= CAN_ERR_PROT_LOC_ACK;
+ }
+
+ if (isr & BUS_STATE2) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(ndev);
+ cf.can_id |= CAN_ERR_BUSOFF;
+ }
+
+ if (isr & BUS_STATE1) {
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ priv->can.can_stats.error_passive++;
+ cf.can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
+ cf.data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ cf.data[6] = txerr;
+ cf.data[7] = rxerr;
+ }
+
+ if (cf.can_id) {
+ struct can_frame *skb_cf;
+ struct sk_buff *skb = alloc_can_err_skb(ndev, &skb_cf);
+
+ if (skb) {
+ skb_cf->can_id |= cf.can_id;
+ memcpy(skb_cf->data, cf.data, CAN_ERR_DLC);
+ netif_rx(skb);
+ }
+ }
+}
+
+static void arasan_canfd_write_frame(struct net_device *ndev, struct sk_buff *skb, int idx)
+{
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ struct can_tx_frame tx_frame;
+ u32 *data = (u32 *)&tx_frame;
+ u8 *ecc = priv->buf->ecc_buffer.read.tx_frames[idx];
+ int i;
+
+ tx_frame.header.header0 = can_fd_len2dlc(cf->len);
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ /* Extended CAN ID format */
+ tx_frame.header.header0 |= TX_HEADER0_IDE;
+ tx_frame.header.header0 |= FIELD_PREP(TX_HEADER0_ID,
+ (cf->can_id & CAN_EFF_MASK) >> 18);
+ tx_frame.header.ext_id = cf->can_id;
+ } else {
+ /* Standard CAN ID format */
+ tx_frame.header.header0 |= FIELD_PREP(TX_HEADER0_ID, cf->can_id & CAN_SFF_MASK);
+ tx_frame.header.ext_id = 0;
+ }
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ tx_frame.header.header0 |= TX_HEADER0_RTR;
+
+ if (can_is_canfd_skb(skb)) {
+ /* CAN FD frame format */
+ tx_frame.header.header0 |= FIELD_PREP(TX_HEADER0_TYPE_MSK, TX_HEADER0_TYPE_FD);
+
+ if (cf->flags & CANFD_BRS)
+ tx_frame.header.header0 |= TX_HEADER0_BRS;
+
+ if (cf->flags & CANFD_ESI)
+ tx_frame.header.header0 |= TX_HEADER0_ESI;
+
+ } else {
+ /* CAN 2.0 frame format */
+ tx_frame.header.header0 |= FIELD_PREP(TX_HEADER0_TYPE_MSK, TX_HEADER0_TYPE_CL);
+ }
+
+ memcpy(tx_frame.data, cf->data, cf->len);
+ priv->buf->r_buffer.tx_frames[idx] = tx_frame;
+
+ for (i = 0; i < sizeof(tx_frame) / sizeof(u32); i++)
+ ecc[i] = ecc_calc(data[i]);
+
+ arasan_canfd_write(priv, HITXFIFO,
+ priv->buf_dma + offsetof(struct can_buffer, r_buffer.tx_frames[idx]));
+
+ can_put_echo_skb(skb, ndev, idx % CAN_MAX_TX_QUEUE, 0);
+}
+
+static int arasan_canfd_start_xmit_queue(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ int idx;
+
+ guard(spinlock)(&priv->tx_lock);
+
+ idx = ffs(priv->txq_avail);
+ if (unlikely(!idx))
+ return -ENOSPC;
+
+ --idx;
+ priv->txq_avail &= ~(1 << idx);
+ arasan_canfd_write_frame(ndev, skb, idx);
+
+ /* Check if the TX queue is full */
+ if (!priv->txq_avail)
+ netif_stop_queue(ndev);
+
+ return 0;
+}
+
+static netdev_tx_t arasan_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int ret;
+
+ if (can_dev_dropped_skb(ndev, skb))
+ return NETDEV_TX_OK;
+
+ ret = arasan_canfd_start_xmit_queue(skb, ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "BUG!, TX full when queue awake!\n");
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static int arasan_canfd_tx(struct net_device *ndev, unsigned long addr)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ int idx;
+
+ guard(spinlock)(&priv->tx_lock);
+
+ idx = (addr - priv->buf_dma - offsetof(struct can_buffer, r_buffer.tx_frames)) /
+ sizeof(struct can_tx_frame);
+ if (idx > CAN_MAX_TX_QUEUE)
+ return 0;
+
+ stats->tx_bytes += can_get_echo_skb(ndev, idx % CAN_MAX_TX_QUEUE, NULL);
+ stats->tx_packets++;
+ priv->txq_avail |= (1 << idx);
+
+ netif_wake_queue(ndev);
+
+ return 1;
+}
+
+static int arasan_canfd_rx(struct net_device *ndev, unsigned long addr)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct canfd_frame *cf;
+ struct sk_buff *skb;
+ u32 header0, header1, header2;
+ struct can_rx_frame *rx_frame;
+ int idx, type;
+
+ idx = (addr - priv->buf_dma - offsetof(struct can_buffer, w_buffer.rx_frames)) /
+ sizeof(struct can_rx_frame);
+ if (idx > CAN_MAX_RX_QUEUE)
+ return 0;
+
+ rx_frame = &priv->buf->w_buffer.rx_frames[idx];
+ header0 = rx_frame->header.header0;
+ header1 = rx_frame->header.header1;
+ header2 = rx_frame->header.header2;
+
+ type = FIELD_GET(RX_HEADER0_TYPE_MSK, header0);
+ if (type == RX_HEADER0_TYPE_FD)
+ skb = alloc_canfd_skb(ndev, &cf);
+ else
+ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
+
+ if (unlikely(!skb)) {
+ stats->rx_dropped++;
+ return 0;
+ }
+
+ if (type == RX_HEADER0_TYPE_FD) {
+ cf->len = can_fd_dlc2len(FIELD_GET(RX_HEADER0_DLC_MSK, header0));
+
+ if (header2 & RX_HEADER2_FD_BRS)
+ cf->flags |= CANFD_BRS;
+
+ if (header2 & RX_HEADER2_FD_ESI)
+ cf->flags |= CANFD_ESI;
+ } else {
+ cf->len = can_cc_dlc2len(FIELD_GET(RX_HEADER0_DLC_MSK, header0));
+ }
+
+ if (header0 & RX_HEADER0_B_EXT) {
+ /* The received frame is an Extended format frame */
+ cf->can_id = FIELD_GET(RX_HEADER1_EXTID, header1);
+ cf->can_id |= (FIELD_GET(RX_HEADER0_ID_0_6, header0) |
+ (FIELD_GET(RX_HEADER1_ID_7_10, header1) << 7) << 18);
+ cf->can_id |= CAN_EFF_FLAG;
+ } else {
+ /* The received frame is a standard format frame */
+ cf->can_id = FIELD_GET(RX_HEADER0_ID_0_6, header0) |
+ (FIELD_GET(RX_HEADER1_ID_7_10, header1) << 7);
+ }
+ if (header0 & RX_HEADER0_B_RTR)
+ cf->can_id |= CAN_RTR_FLAG;
+
+ memcpy(cf->data, rx_frame->data, cf->len);
+
+ if (!(cf->can_id & CAN_RTR_FLAG))
+ stats->rx_bytes += cf->len;
+ stats->rx_packets++;
+
+ netif_receive_skb(skb);
+
+ return 1;
+}
+
+static int arasan_canfd_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *ndev = napi->dev;
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ u32 buf_cnt, ier;
+ int type, work_done = 0;
+ unsigned long addr;
+
+ while ((buf_cnt = arasan_canfd_read(priv, USED_BUFS_PTR_COUNT)) > 0 &&
+ (work_done < quota)) {
+ addr = arasan_canfd_read(priv, USED_BUFS_PTR);
+ type = FIELD_GET(TYPE_MASK, addr);
+ addr &= ~TYPE_MASK;
+ switch (type) {
+ case TYPE_RX:
+ work_done += arasan_canfd_rx(ndev, addr);
+ arasan_canfd_write(priv, RX_PTR_BUFS, addr);
+ break;
+ case TYPE_TX:
+ work_done += arasan_canfd_tx(ndev, addr);
+ break;
+ case TYPE_TX_ABORT:
+ netdev_err(ndev, "tx abort\n");
+ break;
+ default:
+ netdev_err(ndev, "Unknown type: %d\n", type);
+ break;
+ }
+ }
+
+ if (work_done < quota) {
+ if (napi_complete_done(napi, work_done)) {
+ ier = arasan_canfd_read(priv, INTERRUPT_MASK0);
+ ier |= USED_BUFFERS_NOT_EMPTY;
+ arasan_canfd_write(priv, INTERRUPT_MASK0, ier);
+ }
+ }
+ return work_done;
+}
+
+static irqreturn_t arasan_canfd_isr(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ u32 isr_errors, isr, ier;
+
+ isr = arasan_canfd_read(priv, INTERRUPTS_FROM_HW);
+ if (!isr)
+ return IRQ_NONE;
+
+ arasan_canfd_write(priv, INTERRUPTS_FROM_HW, isr);
+
+ ier = GENMASK(31, 0);
+ ier &= ~(GOOD_RX_FRAME | TX_FIFO_EMPTY | CLTX_GOOD | FDTX_GOOD | USED_BUFFERS_NOT_EMPTY);
+ isr_errors = isr & ier;
+ if (isr_errors)
+ arasan_canfd_err_interrupt(ndev, isr_errors);
+
+ if (isr & USED_BUFFERS_NOT_EMPTY) {
+ ier = arasan_canfd_read(priv, INTERRUPT_MASK0);
+ ier &= ~USED_BUFFERS_NOT_EMPTY;
+ arasan_canfd_write(priv, INTERRUPT_MASK0, ier);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void arasan_canfd_chip_stop(struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+
+ arasan_canfd_write(priv, OPERATIONAL, 0);
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int arasan_canfd_open(struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ret = phy_power_on(priv->transceiver);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", __func__, ret);
+ goto err;
+ }
+
+ ret = request_irq(ndev->irq, arasan_canfd_isr, 0, ndev->name, ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "Failed to request irq\n");
+ goto err;
+ }
+
+ ret = open_candev(ndev);
+ if (ret)
+ goto err_irq;
+
+ ret = arasan_canfd_chip_start(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "arasan_canfd_chip_start failed!\n");
+ goto err_candev;
+ }
+
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+
+ return 0;
+
+err_candev:
+ close_candev(ndev);
+err_irq:
+ free_irq(ndev->irq, ndev);
+err:
+ pm_runtime_put(priv->dev);
+ phy_power_off(priv->transceiver);
+
+ return ret;
+}
+
+static int arasan_canfd_close(struct net_device *ndev)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ arasan_canfd_chip_stop(ndev);
+ free_irq(ndev->irq, ndev);
+ close_candev(ndev);
+
+ pm_runtime_put(priv->dev);
+ phy_power_off(priv->transceiver);
+
+ return 0;
+}
+
+static int arasan_canfd_get_berr_counter(const struct net_device *ndev,
+ struct can_berr_counter *bec)
+{
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ int ret;
+ u32 val;
+
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", __func__, ret);
+ pm_runtime_put(priv->dev);
+ return ret;
+ }
+
+ val = arasan_canfd_read(priv, STATUS);
+ bec->txerr = FIELD_GET(STATUS_TEC_MASK, val);
+ bec->rxerr = FIELD_GET(STATUS_REC_MASK, val);
+
+ pm_runtime_put(priv->dev);
+
+ return 0;
+}
+
+static void arasan_canfd_init(struct arasan_canfd_priv *priv)
+{
+ u32 val;
+ int i;
+
+ /* soft reset */
+ val = arasan_canfd_read(priv, CONTROL);
+ val |= (SOFTRESET | DMA_SOFTRESET);
+ arasan_canfd_write(priv, CONTROL, val);
+ udelay(1);
+ val &= ~(SOFTRESET | DMA_SOFTRESET);
+ arasan_canfd_write(priv, CONTROL, val);
+
+ /* setup features */
+ val |= LVL_ISOFD;
+ val &= ~DISABLE_ECC;
+ arasan_canfd_write(priv, CONTROL, val);
+ arasan_canfd_write(priv, SELF_ADDRESS, 0);
+ arasan_canfd_write(priv, ALLOWED_RETRANSMITS, 0);
+ arasan_canfd_write(priv, WATCHDOG_LIMIT, 0);
+
+ /* setup base registers */
+ arasan_canfd_write(priv, R_BASE_ADDR,
+ priv->buf_dma + offsetof(struct can_buffer, r_buffer));
+ arasan_canfd_write(priv, R_ECC_ADDR,
+ priv->buf_dma + offsetof(struct can_buffer, ecc_buffer.read));
+ arasan_canfd_write(priv, W_BASE_ADDR,
+ priv->buf_dma + offsetof(struct can_buffer, w_buffer));
+ arasan_canfd_write(priv, W_ECC_ADDR,
+ priv->buf_dma + offsetof(struct can_buffer, ecc_buffer.write));
+
+ /* setup RX buffers */
+ for (i = 0; i < CAN_MAX_RX_QUEUE; i++)
+ arasan_canfd_write(priv, RX_PTR_BUFS, priv->buf_dma +
+ offsetof(struct can_buffer, w_buffer.rx_frames[i]));
+
+ /* setup filter buffers */
+ arasan_canfd_write(priv, NUMBER_OF_PAIRS, 0);
+ arasan_canfd_write(priv, BASE_ADDRESS,
+ priv->buf_dma + offsetof(struct can_buffer, r_buffer.rx_filters[0]));
+}
+
+static const struct net_device_ops arasan_canfd_netdev_ops = {
+ .ndo_open = arasan_canfd_open,
+ .ndo_stop = arasan_canfd_close,
+ .ndo_start_xmit = arasan_canfd_start_xmit,
+};
+
+static const struct ethtool_ops arasan_canfd_ethtool_ops = {
+ .get_ts_info = ethtool_op_get_ts_info,
+};
+
+static int arasan_canfd_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ if (netif_running(ndev)) {
+ netif_stop_queue(ndev);
+ netif_device_detach(ndev);
+ arasan_canfd_chip_stop(ndev);
+ }
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int arasan_canfd_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_force_resume failed on resume\n");
+ return ret;
+ }
+
+ arasan_canfd_init(priv);
+
+ if (netif_running(ndev)) {
+ ret = arasan_canfd_chip_start(ndev);
+ if (ret) {
+ dev_err(dev, "arasan_canfd_chip_start failed on resume\n");
+ return ret;
+ }
+
+ netif_device_attach(ndev);
+ netif_start_queue(ndev);
+ }
+
+ return 0;
+}
+
+static int arasan_canfd_runtime_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+
+ clk_disable_unprepare(priv->sys_clk);
+ clk_disable_unprepare(priv->can_clk);
+
+ return 0;
+}
+
+static int arasan_canfd_runtime_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->sys_clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+ ret = clk_prepare_enable(priv->can_clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ clk_disable_unprepare(priv->sys_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops arasan_canfd_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(arasan_canfd_suspend, arasan_canfd_resume)
+ RUNTIME_PM_OPS(arasan_canfd_runtime_suspend, arasan_canfd_runtime_resume, NULL)
+};
+
+static int arasan_canfd_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct arasan_canfd_priv *priv;
+ struct phy *transceiver;
+ void __iomem *addr;
+ int ret;
+
+ addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ ndev = alloc_candev(sizeof(struct arasan_canfd_priv), CAN_MAX_TX_QUEUE);
+ if (!ndev)
+ return -ENOMEM;
+ priv = netdev_priv(ndev);
+
+ priv->sys_rst = devm_reset_control_get(&pdev->dev, "sys");
+ if (IS_ERR(priv->sys_rst)) {
+ dev_err(&pdev->dev, "failed to get sys reset\n");
+ ret = PTR_ERR(priv->sys_rst);
+ goto err_free;
+ }
+ reset_control_assert(priv->sys_rst);
+ reset_control_deassert(priv->sys_rst);
+
+ priv->can_rst = devm_reset_control_get(&pdev->dev, "can");
+ if (IS_ERR(priv->can_rst)) {
+ dev_err(&pdev->dev, "failed to get can reset\n");
+ ret = PTR_ERR(priv->can_rst);
+ goto err_reset_sys;
+ }
+ reset_control_assert(priv->can_rst);
+ reset_control_deassert(priv->can_rst);
+
+ priv->can_clk = devm_clk_get(&pdev->dev, "can");
+ if (IS_ERR(priv->can_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->can_clk),
+ "failed to get can clock\n");
+ goto err_reset;
+ }
+
+ priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
+ if (IS_ERR(priv->sys_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->sys_clk),
+ "failed to get sys clock\n");
+ goto err_reset;
+ }
+
+ priv->ssp = DEFAULT_SSP;
+
+ transceiver = devm_phy_optional_get(&pdev->dev, NULL);
+ if (IS_ERR(transceiver)) {
+ ret = PTR_ERR(transceiver);
+ dev_err_probe(&pdev->dev, ret, "failed to get phy\n");
+ goto err_reset;
+ }
+ priv->transceiver = transceiver;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto err_reset;
+ ndev->irq = ret;
+
+ spin_lock_init(&priv->tx_lock);
+ platform_set_drvdata(pdev, ndev);
+ priv->dev = &pdev->dev;
+ priv->base = addr;
+
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", __func__, ret);
+ goto err_disableclks;
+ }
+
+ priv->buf = dmam_alloc_coherent(&pdev->dev, sizeof(*priv->buf), &priv->buf_dma, GFP_KERNEL);
+ if (!priv->buf) {
+ ret = -ENOMEM;
+ goto err_disableclks;
+ }
+
+ priv->can.bittiming_const = &arasan_canfd_bittiming_const;
+ priv->can.fd.data_bittiming_const = &arasan_canfd_data_bittiming_const;
+ priv->can.do_set_mode = arasan_canfd_do_set_mode;
+ priv->can.do_get_berr_counter = arasan_canfd_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_FD;
+ priv->can.clock.freq = clk_get_rate(priv->can_clk);
+
+ ndev->flags |= IFF_ECHO;
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ ndev->netdev_ops = &arasan_canfd_netdev_ops;
+ ndev->ethtool_ops = &arasan_canfd_ethtool_ops;
+
+ netif_napi_add_weight(ndev, &priv->napi, arasan_canfd_poll, ARASAN_CANFD_NAPI_WEIGHT);
+
+ arasan_canfd_init(priv);
+
+ ret = register_candev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
+ goto err_napidel;
+ }
+
+ of_can_transceiver(ndev);
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+
+err_napidel:
+ netif_napi_del(&priv->napi);
+err_disableclks:
+ pm_runtime_put(priv->dev);
+ pm_runtime_disable(&pdev->dev);
+err_reset:
+ reset_control_assert(priv->can_rst);
+err_reset_sys:
+ reset_control_assert(priv->sys_rst);
+err_free:
+ free_candev(ndev);
+
+ return ret;
+}
+
+static void arasan_canfd_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct arasan_canfd_priv *priv = netdev_priv(ndev);
+
+ unregister_candev(ndev);
+ netif_napi_del(&priv->napi);
+ pm_runtime_disable(&pdev->dev);
+ reset_control_assert(priv->sys_rst);
+ reset_control_assert(priv->can_rst);
+ free_candev(ndev);
+}
+
+static const struct of_device_id arasan_canfd_of_match[] = {
+ { .compatible = "arasan,canfd" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, arasan_canfd_of_match);
+
+static struct platform_driver arasan_canfd_driver = {
+ .probe = arasan_canfd_probe,
+ .remove = arasan_canfd_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = pm_ptr(&arasan_canfd_dev_pm_ops),
+ .of_match_table = arasan_canfd_of_match,
+ },
+};
+module_platform_driver(arasan_canfd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
+MODULE_DESCRIPTION("Arasan CANFD driver");
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] dt-bindings: can: add arasan canfd controller
2026-08-03 14:05 ` [PATCH 1/2] dt-bindings: can: add arasan canfd controller Jisheng Zhang
@ 2026-08-03 14:31 ` sashiko-bot
2026-08-03 15:17 ` Krzysztof Kozlowski
1 sibling, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-03 14:31 UTC (permalink / raw)
To: Jisheng Zhang
Cc: Oleksij Rempel, conor+dt, linux-can, Marc Kleine-Budde, robh,
Vincent Mailhol, devicetree, Oliver Hartkopp
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The schema uses `additionalProperties: false` instead of `unevaluatedProperties: false`, which breaks property inheritance from `can-controller.yaml`.
- [Medium] Missing `clock-names` and `reset-names` in the `required` list, and missing `minItems` constraints.
--
--- Patch [1]: [PATCH 1/2] dt-bindings: can: add arasan canfd controller ---
commit 282ace981cf63f65fd400dda84f1806d370fbc9b
Author: Jisheng Zhang <jszhang@kernel.org>
dt-bindings: can: add arasan canfd controller
This commit adds device tree binding documentation for the Arasan CAN-FD
controller.
> diff --git a/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
> new file mode 100644
> index 0000000000000..a120c11ce3045
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
[ ... ]
> +allOf:
> + - $ref: can-controller.yaml#
[ ... ]
> + clocks:
> + maxItems: 2
> +
> + clock-names:
> + items:
> + - const: can
> + - const: sys
> +
> + resets:
> + maxItems: 2
> +
> + reset-names:
> + items:
> + - const: can
> + - const: sys
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - resets
[Severity: Medium]
Should clock-names and reset-names be included in this required list? A device
tree omitting these names might pass validation, but could cause the driver to
fail during probe when requesting clocks or resets by name.
Also, should clocks and resets specify minItems: 2, since the definition
expects two distinct items for each?
> +
> +additionalProperties: false
[Severity: Medium]
Should this use unevaluatedProperties: false instead? Since the schema includes
can-controller.yaml via $ref earlier in the file, using additionalProperties:
false will prevent the inheritance of valid standard properties from the
included schema.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260803140531.12692-1-jszhang@kernel.org?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
@ 2026-08-03 14:43 ` sashiko-bot
2026-08-03 15:21 ` Philipp Zabel
2026-08-03 16:05 ` Oliver Hartkopp
2 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-03 14:43 UTC (permalink / raw)
To: Jisheng Zhang
Cc: Oleksij Rempel, linux-can, Vincent Mailhol, Oliver Hartkopp,
devicetree, Marc Kleine-Budde, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 16 potential issue(s) to consider:
- [Critical] Divide-by-zero kernel panic when configuring the device in standard CAN 2.0 mode.
- [High] Off-by-one and potential negative index bypass in RX/TX queue index validation.
- [High] Uninitialized kernel stack memory is copied to the DMA buffer and leaked.
- [High] Missing NAPI work_done accounting on SKB allocation failure or TX abort causes infinite softirq loop.
- [High] Incorrect operator precedence corrupts Extended CAN IDs during reception.
- [High] Enabling NAPI after enabling hardware interrupts causes a permanent stall if an interrupt fires early.
- [High] Pending NAPI poll can run concurrently with system suspend, accessing powered-down hardware.
- [High] Driver completely ignores CAN framework Bit Timing configurations.
- [High] Unhandled TX aborts permanently leak TX queue slots and stall transmission.
- [Medium] Asynchronous pm_runtime_put() is cancelled by pm_runtime_disable(), leaving hardware incorrectly powered on and leaking clocks.
- [Medium] Leaking Linux internal CAN flags into hardware TX registers.
- [Medium] Inconsistent internal state transitioning when multiple bus error interrupts trigger.
- [Medium] Lockless call to netif_stop_queue on the TX error path can race with netif_wake_queue.
- [Medium] Missing dma_wmb() and dma_rmb() barriers when interacting with DMA buffers and device registers using relaxed I/O.
- [Medium] Truncation of 64-bit DMA addresses causing hardware misdirection.
- [Medium] DMA descriptors and data are processed using CPU-native byte order without endianness conversions.
--
--- Patch [2]: [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller ---
commit fa1764e16e0ce790d11207dabd342ce03079236b
Author: Jisheng Zhang <jszhang@kernel.org>
can: arasan-canfd: add driver for arasan CAN-FD controller
Add driver for the arasan CAN-FD controller.
> diff --git a/drivers/net/can/arasan-canfd.c b/drivers/net/can/arasan-canfd.c
> new file mode 100644
> index 0000000000000..63a6191f84f63
> --- /dev/null
> +++ b/drivers/net/can/arasan-canfd.c
[ ... ]
> +static int arasan_canfd_set_bittiming(struct net_device *ndev)
> +{
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + struct can_bittiming *dbt = &priv->can.fd.data_bittiming;
> + u32 cl_2, cl_4, fd_2, fd_4, classic_cycle, fd_cycle;
[ ... ]
> + fd_cycle = priv->can.clock.freq / dbt->bitrate;
[Severity: Critical]
Can this cause a divide-by-zero crash when configuring the device in standard
CAN 2.0 mode?
When the interface is brought up without CAN FD mode enabled, the CAN framework
does not populate the fd.data_bittiming structure, leaving dbt->bitrate as 0.
> + fd_2 = fd_cycle / 2;
> + fd_4 = fd_cycle / 4;
> + arasan_canfd_write(priv, LVL_FD_BAUD, fd_cycle);
> + arasan_canfd_write(priv, LVL_ALLOWABLE_FD_JUMP, fd_2);
> + arasan_canfd_write(priv, LVL_FD_END_OF_SYNC_SEG, fd_4);
[Severity: High]
Does this driver completely bypass the CAN framework's calculated timing
parameters?
The code derives cycle lengths directly from the clock frequency and assigns
fixed proportions for timing segments instead of using the calculated values
in bt like bt->prop_seg, bt->phase_seg1, and bt->sjw. This prevents userspace
from configuring specific sample points via netlink.
[ ... ]
> +static void arasan_canfd_err_interrupt(struct net_device *ndev, u32 isr)
> +{
[ ... ]
> + if (isr & BUS_STATE2) {
> + priv->can.state = CAN_STATE_BUS_OFF;
> + priv->can.can_stats.bus_off++;
> + can_bus_off(ndev);
> + cf.can_id |= CAN_ERR_BUSOFF;
> + }
> +
> + if (isr & BUS_STATE1) {
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
[Severity: Medium]
Could an independent evaluation of these states cause a lower-priority state
to overwrite a terminal state?
If the hardware latches both conditions simultaneously, the driver processes
Bus Off, but then immediately processes Error Passive and overwrites the
internal state to CAN_STATE_ERROR_PASSIVE, breaking the CAN state machine.
Should this use an else if structure?
[ ... ]
> +static void arasan_canfd_write_frame(struct net_device *ndev, struct sk_buff *skb, int idx)
> +{
> + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> + struct can_tx_frame tx_frame;
[Severity: High]
Does allocating tx_frame on the stack without initializing it leak kernel
memory?
The code copies only cf->len bytes into the data array, leaving the remainder
of the 64-byte array and the resv_0 field uninitialized. The entire 76-byte
structure is then copied into the DMA-mapped buffer, potentially leaking
uninitialized kernel stack data to the device.
> + u32 *data = (u32 *)&tx_frame;
> + u8 *ecc = priv->buf->ecc_buffer.read.tx_frames[idx];
> + int i;
> +
> + tx_frame.header.header0 = can_fd_len2dlc(cf->len);
[Severity: Medium]
Are these CPU-native bitwise operations and assignments safe on big-endian
architectures?
The DMA descriptors are populated using CPU-native types and directly copied to
DMA memory. On big-endian systems, this data will be stored in big-endian
format. If the hardware expects little-endian byte ordering, it will interpret
swapped bits.
> +
> + if (cf->can_id & CAN_EFF_FLAG) {
> + /* Extended CAN ID format */
> + tx_frame.header.header0 |= TX_HEADER0_IDE;
> + tx_frame.header.header0 |= FIELD_PREP(TX_HEADER0_ID,
> + (cf->can_id & CAN_EFF_MASK) >> 18);
> + tx_frame.header.ext_id = cf->can_id;
[Severity: Medium]
Does this direct assignment write Linux internal CAN flags into the hardware
descriptor?
cf->can_id contains internal flags such as CAN_EFF_FLAG and CAN_RTR_FLAG.
Writing these directly into the hardware's TX descriptor field might corrupt
hardware configuration bits. Should this be masked using CAN_EFF_MASK?
[ ... ]
> + memcpy(tx_frame.data, cf->data, cf->len);
> + priv->buf->r_buffer.tx_frames[idx] = tx_frame;
> +
> + for (i = 0; i < sizeof(tx_frame) / sizeof(u32); i++)
> + ecc[i] = ecc_calc(data[i]);
> +
> + arasan_canfd_write(priv, HITXFIFO,
> + priv->buf_dma + offsetof(struct can_buffer, r_buffer.tx_frames[idx]));
[Severity: Medium]
Is a dma_wmb() barrier needed here before writing to the HITXFIFO register?
Because writel_relaxed lacks memory ordering guarantees, the CPU can reorder
the register write before the DMA memory writes complete, potentially causing
the hardware to transmit stale data.
[ ... ]
> +static netdev_tx_t arasan_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + int ret;
> +
> + if (can_dev_dropped_skb(ndev, skb))
> + return NETDEV_TX_OK;
> +
> + ret = arasan_canfd_start_xmit_queue(skb, ndev);
> + if (ret < 0) {
> + netdev_err(ndev, "BUG!, TX full when queue awake!\n");
> + netif_stop_queue(ndev);
[Severity: Medium]
Can this lockless call to netif_stop_queue race with netif_wake_queue?
If a concurrent TX completion occurs right after arasan_canfd_start_xmit_queue
releases the lock, the completion handler will free a slot and call
netif_wake_queue. This lockless netif_stop_queue could then execute,
overwriting the wake and deadlocking transmission.
> + return NETDEV_TX_BUSY;
> + }
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static int arasan_canfd_tx(struct net_device *ndev, unsigned long addr)
> +{
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + int idx;
> +
> + guard(spinlock)(&priv->tx_lock);
> +
> + idx = (addr - priv->buf_dma - offsetof(struct can_buffer, r_buffer.tx_frames)) /
> + sizeof(struct can_tx_frame);
> + if (idx > CAN_MAX_TX_QUEUE)
> + return 0;
[Severity: High]
Does this bounds check fail to protect against negative index values?
The driver calculates the signed integer idx by subtracting priv->buf_dma from
a hardware-provided address. If the hardware address is unexpectedly small,
it can cause unsigned wrap-around, resulting in a negative value when cast to
int. A negative value passes the idx > CAN_MAX_TX_QUEUE check.
Additionally, the check uses > instead of >=, so idx == CAN_MAX_TX_QUEUE
passes, causing an off-by-one out-of-bounds access.
[ ... ]
> +static int arasan_canfd_rx(struct net_device *ndev, unsigned long addr)
> +{
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct canfd_frame *cf;
> + struct sk_buff *skb;
> + u32 header0, header1, header2;
> + struct can_rx_frame *rx_frame;
> + int idx, type;
> +
> + idx = (addr - priv->buf_dma - offsetof(struct can_buffer, w_buffer.rx_frames)) /
> + sizeof(struct can_rx_frame);
> + if (idx > CAN_MAX_RX_QUEUE)
> + return 0;
[Severity: High]
Does this bounds check have the same off-by-one and negative index bypass
issue as the TX path?
The idx calculation can produce a negative integer or exactly equal
CAN_MAX_RX_QUEUE, both of which bypass this check and lead to out-of-bounds
memory access.
[ ... ]
> + if (type == RX_HEADER0_TYPE_FD)
> + skb = alloc_canfd_skb(ndev, &cf);
> + else
> + skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
> +
> + if (unlikely(!skb)) {
> + stats->rx_dropped++;
> + return 0;
> + }
[Severity: High]
Could returning 0 on allocation failure lead to an infinite softirq loop?
In the NAPI poll loop, work_done is incremented by the return value of this
function. If this returns 0 under memory pressure, work_done is not advanced.
Under continuous CAN traffic, work_done will never reach quota, and the poll
loop will spin the CPU indefinitely in softirq context.
[ ... ]
> + if (header0 & RX_HEADER0_B_EXT) {
> + /* The received frame is an Extended format frame */
> + cf->can_id = FIELD_GET(RX_HEADER1_EXTID, header1);
> + cf->can_id |= (FIELD_GET(RX_HEADER0_ID_0_6, header0) |
> + (FIELD_GET(RX_HEADER1_ID_7_10, header1) << 7) << 18);
[Severity: High]
Does faulty operator precedence corrupt the CAN ID reconstruction here?
Because the bitwise OR operator has lower precedence than the left shift
operator, the compiler evaluates this as:
A | ((B << 7) << 18)
This means the lower 7 bits of the standard ID are never shifted up by 18,
corrupting bits 0-6 instead.
[ ... ]
> +static int arasan_canfd_poll(struct napi_struct *napi, int quota)
> +{
> + struct net_device *ndev = napi->dev;
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> + u32 buf_cnt, ier;
> + int type, work_done = 0;
> + unsigned long addr;
> +
> + while ((buf_cnt = arasan_canfd_read(priv, USED_BUFS_PTR_COUNT)) > 0 &&
> + (work_done < quota)) {
> + addr = arasan_canfd_read(priv, USED_BUFS_PTR);
> + type = FIELD_GET(TYPE_MASK, addr);
[Severity: Medium]
Is a dma_rmb() barrier needed here after reading USED_BUFS_PTR?
Without a read memory barrier, the CPU might reorder memory accesses and
read stale RX descriptor data in arasan_canfd_rx before the hardware has
fully written it.
> + addr &= ~TYPE_MASK;
> + switch (type) {
> + case TYPE_RX:
> + work_done += arasan_canfd_rx(ndev, addr);
> + arasan_canfd_write(priv, RX_PTR_BUFS, addr);
> + break;
> + case TYPE_TX:
> + work_done += arasan_canfd_tx(ndev, addr);
> + break;
> + case TYPE_TX_ABORT:
> + netdev_err(ndev, "tx abort\n");
> + break;
[Severity: High]
Does this TX abort case permanently leak TX queue slots?
This case does not extract the transmission index, free the socket buffer,
or reclaim the queue slot. The SKB and TX queue slot are leaked, which will
permanently stall transmission once the queue fills up. It also fails to
increment work_done, risking a softirq stall similar to the RX allocation
failure path.
[ ... ]
> +static int arasan_canfd_open(struct net_device *ndev)
> +{
[ ... ]
> + ret = arasan_canfd_chip_start(ndev);
> + if (ret < 0) {
> + netdev_err(ndev, "arasan_canfd_chip_start failed!\n");
> + goto err_candev;
> + }
> +
> + napi_enable(&priv->napi);
> + netif_start_queue(ndev);
[Severity: High]
Does enabling NAPI after hardware interrupts are unmasked create a deadlock
window?
arasan_canfd_chip_start enables hardware interrupts. If an interrupt fires
before napi_enable is called, the ISR will mask the interrupt and schedule
a NAPI poll. Since NAPI isn't enabled yet, the poll is discarded and the
interrupt remains masked permanently.
[ ... ]
> +static void arasan_canfd_init(struct arasan_canfd_priv *priv)
> +{
[ ... ]
> + /* setup base registers */
> + arasan_canfd_write(priv, R_BASE_ADDR,
> + priv->buf_dma + offsetof(struct can_buffer, r_buffer));
[Severity: Medium]
Can this truncate a 64-bit DMA address?
priv->buf_dma can be a 64-bit address on platforms without a restricted DMA
mask, but it is written directly into a 32-bit register. If the address is
above 4GB, the hardware will be directed to the wrong memory location.
[ ... ]
> +static int arasan_canfd_suspend(struct device *dev)
> +{
> + struct net_device *ndev = dev_get_drvdata(dev);
> +
> + if (netif_running(ndev)) {
> + netif_stop_queue(ndev);
> + netif_device_detach(ndev);
> + arasan_canfd_chip_stop(ndev);
> + }
> +
> + return pm_runtime_force_suspend(dev);
> +}
[Severity: High]
Can a pending NAPI poll run concurrently with system suspend here?
This function stops the chip but fails to call napi_disable() and disable_irq()
to wait for and terminate concurrent executions. pm_runtime_force_suspend will
shut off the device clocks, meaning a concurrent NAPI poll could access powered
down hardware and cause a kernel panic.
[ ... ]
> +static int arasan_canfd_probe(struct platform_device *pdev)
> +{
[ ... ]
> +err_disableclks:
> + pm_runtime_put(priv->dev);
> + pm_runtime_disable(&pdev->dev);
[Severity: Medium]
Does pm_runtime_disable cancel the asynchronous suspend request?
Calling pm_runtime_disable immediately after an asynchronous pm_runtime_put
cancels the pending suspend work. This means the suspend callback is never
executed, leaving the hardware incorrectly powered on and leaking clocks.
Should this use pm_runtime_put_sync instead?
[ ... ]
> +static void arasan_canfd_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct arasan_canfd_priv *priv = netdev_priv(ndev);
> +
> + unregister_candev(ndev);
> + netif_napi_del(&priv->napi);
> + pm_runtime_disable(&pdev->dev);
[Severity: Medium]
Does this also suffer from the cancelled asynchronous suspend issue?
unregister_candev invokes arasan_canfd_close, which performs an asynchronous
pm_runtime_put. The immediate pm_runtime_disable cancels it, again leaking
clocks and leaving the device powered on.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260803140531.12692-1-jszhang@kernel.org?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] dt-bindings: can: add arasan canfd controller
2026-08-03 14:05 ` [PATCH 1/2] dt-bindings: can: add arasan canfd controller Jisheng Zhang
2026-08-03 14:31 ` sashiko-bot
@ 2026-08-03 15:17 ` Krzysztof Kozlowski
1 sibling, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-03 15:17 UTC (permalink / raw)
To: Jisheng Zhang, Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-can, devicetree, linux-kernel
On 03/08/2026 16:05, Jisheng Zhang wrote:
> Add documentation for the arasan canfd controller.
Here goes hardware description. What is arasan canfd?
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> .../bindings/net/can/arasan,canfd.yaml | 63 +++++++++++++++++++
> 1 file changed, 63 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
>
> diff --git a/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
> new file mode 100644
> index 000000000000..a120c11ce304
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/arasan,canfd.yaml
> @@ -0,0 +1,63 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/can/arasan,canfd.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title:
> + Arasan CAN-FD controller
> +
> +maintainers:
> + - Jisheng Zhang <jszhang@kernel.org>
> +
> +allOf:
> + - $ref: can-controller.yaml#
> +
> +properties:
> + compatible:
> + const: arasan,canfd
This is way too generic. Why isn't there any integration compatible?
Please follow writing bindings.
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 2
Instead list and describe items
> +
> + clock-names:
> + items:
> + - const: can
can is too generic, not really useful. Same for resets.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
2026-08-03 14:43 ` sashiko-bot
@ 2026-08-03 15:21 ` Philipp Zabel
2026-08-03 16:05 ` Oliver Hartkopp
2 siblings, 0 replies; 8+ messages in thread
From: Philipp Zabel @ 2026-08-03 15:21 UTC (permalink / raw)
To: Jisheng Zhang, Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-can, devicetree, linux-kernel
On Mo, 2026-08-03 at 22:05 +0800, Jisheng Zhang wrote:
> Add driver for the arasan CAN-FD controller.
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> drivers/net/can/Kconfig | 6 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/arasan-canfd.c | 1057 ++++++++++++++++++++++++++++++++
> 3 files changed, 1064 insertions(+)
> create mode 100644 drivers/net/can/arasan-canfd.c
>
[...]
> diff --git a/drivers/net/can/arasan-canfd.c b/drivers/net/can/arasan-canfd.c
> new file mode 100644
> index 000000000000..63a6191f84f6
> --- /dev/null
> +++ b/drivers/net/can/arasan-canfd.c
> @@ -0,0 +1,1057 @@
[...]
> +static int arasan_canfd_probe(struct platform_device *pdev)
> +{
> + struct net_device *ndev;
> + struct arasan_canfd_priv *priv;
> + struct phy *transceiver;
> + void __iomem *addr;
> + int ret;
> +
> + addr = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(addr))
> + return PTR_ERR(addr);
> +
> + ndev = alloc_candev(sizeof(struct arasan_canfd_priv), CAN_MAX_TX_QUEUE);
> + if (!ndev)
> + return -ENOMEM;
> + priv = netdev_priv(ndev);
> +
> + priv->sys_rst = devm_reset_control_get(&pdev->dev, "sys");
> + if (IS_ERR(priv->sys_rst)) {
> + dev_err(&pdev->dev, "failed to get sys reset\n");
> + ret = PTR_ERR(priv->sys_rst);
> + goto err_free;
> + }
> + reset_control_assert(priv->sys_rst);
> + reset_control_deassert(priv->sys_rst);
> +
> + priv->can_rst = devm_reset_control_get(&pdev->dev, "can");
Please use devm_reset_control_get_exclusive() directly.
regards
Philipp
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
2026-08-03 14:43 ` sashiko-bot
2026-08-03 15:21 ` Philipp Zabel
@ 2026-08-03 16:05 ` Oliver Hartkopp
2 siblings, 0 replies; 8+ messages in thread
From: Oliver Hartkopp @ 2026-08-03 16:05 UTC (permalink / raw)
To: Jisheng Zhang, Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
Cc: linux-can, devicetree, linux-kernel
On 03.08.26 16:05, Jisheng Zhang wrote:
> Add driver for the arasan CAN-FD controller.
>
> +
> +static const u32 ecc_syndrom[7] = {
> + 0xC14840FF,
> + 0x2124FF90,
> + 0x6CFF0808,
> + 0xFF01A444,
> + 0x16F092A6,
> + 0x101F7161,
> + 0x8A820F1B,
> +};
> +
> +static inline int parity(unsigned int x)
int / unsigned int ?
u32 might have been better.
> +{
> + /*
> + * public domain code snippet, lifted from
> + * http://www-graphics.stanford.edu/~seander/bithacks.html
> + */
> + x ^= x >> 1;
> + x ^= x >> 2;
> + x = (x & 0x11111111U) * 0x11111111U;
> + return (x >> 28) & 1;
> +}
> +
> +static u8 ecc_calc(u32 data)
> +{
> + int i;
> + u8 result = 0;
> +
> + for (i = 0; i < 7; i++) {
> + if (parity(data & ecc_syndrom[i]))
> + result |= BIT(i);
> + }
> +
> + return result;
> +}
> +
IIRC there's a even better solution with already existing kernel
functions (e.g. hweight32()) which can use optimized CPU operations e.g.
to count bits in a value than open coding the parity function.
#include <linux/bitops.h>
#include <linux/types.h>
static const u32 ecc_syndrom[7] = {
0xC14840FF,
0x2124FF90,
0x6CFF0808,
0xFF01A444,
0x16F092A6,
0x101F7161,
0x8A820F1B,
};
static u8 ecc_calc(u32 data)
{
int i;
u8 result = 0;
for (i = 0; i < 7; i++) {
if (hweight32(data & ecc_syndrom[i]) & 1)
result |= BIT(i);
}
return result;
}
Please re-check if you can use existing kernel code here.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-03 16:11 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 14:05 [PATCH 0/2] can: add arasan canfd controller support Jisheng Zhang
2026-08-03 14:05 ` [PATCH 1/2] dt-bindings: can: add arasan canfd controller Jisheng Zhang
2026-08-03 14:31 ` sashiko-bot
2026-08-03 15:17 ` Krzysztof Kozlowski
2026-08-03 14:05 ` [PATCH 2/2] can: arasan-canfd: add driver for arasan CAN-FD controller Jisheng Zhang
2026-08-03 14:43 ` sashiko-bot
2026-08-03 15:21 ` Philipp Zabel
2026-08-03 16:05 ` Oliver Hartkopp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox