[PATCH v5 14/16] can: grcan: Add CANFD RX support alongside legacy CAN
From: Arun Muthusamy <hidden>
Date: 2026-02-16 13:54:02
Also in:
linux-can, lkml
Subsystem:
can network drivers, the rest · Maintainers:
Marc Kleine-Budde, Vincent Mailhol, Linus Torvalds
Include CANFD RX support with the legacy CAN support, enabling support for extended data payloads to provide higher bit rates. Signed-off-by: Arun Muthusamy <redacted> --- drivers/net/can/grcan.c | 89 +++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 30 deletions(-)
diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c
index ae9f6fd4c8bf..e54214f6112d 100644
--- a/drivers/net/can/grcan.c
+++ b/drivers/net/can/grcan.c@@ -235,6 +235,9 @@ struct grcan_registers { #define GRCAN_TX_BRS BIT(25) #define GRCAN_TX_FDF BIT(26) +#define GRCAN_RX_BRS BIT(25) +#define GRCAN_RX_FDF BIT(26) + /* Hardware capabilities */ struct grcan_hwcap { /* CAN-FD capable, indicates GRCANFD IP.
@@ -1242,20 +1245,29 @@ static inline union grcan_msg_slot *grcan_tx_msg_slot(struct grcan_dma *dma, u32 return (union grcan_msg_slot *)((u8 *)dma->tx.msg_slot + off); } +static inline union grcan_msg_slot *grcan_rx_msg_slot(struct grcan_dma *dma, u32 off) +{ + return (union grcan_msg_slot *)((u8 *)dma->rx.msg_slot + off); +} + static int grcan_receive(struct net_device *dev, int budget) { + struct net_device_stats *stats = &dev->stats; struct grcan_priv *priv = netdev_priv(dev); - struct grcan_registers __iomem *regs = priv->regs; + struct grcan_registers __iomem *regs; struct grcan_dma *dma = &priv->dma; - struct net_device_stats *stats = &dev->stats; - struct can_frame *cf; + u32 bds, copy_len, payload_offset; + union grcan_msg_slot *msg; + u32 wr, rd, dlc, startrd; + struct canfd_frame *cf; + int i, work_done = 0; struct sk_buff *skb; - u32 wr, rd, startrd; - u32 *slot; u32 rtr, eff; - int work_done = 0; + u8 *data; + regs = priv->regs; rd = grcan_read_reg(®s->rxrd); + startrd = rd; for (work_done = 0; work_done < budget; work_done++) { /* Check for packet to receive */
@@ -1263,43 +1275,60 @@ static int grcan_receive(struct net_device *dev, int budget) if (rd == wr) break; - /* Take care of packet */ - skb = alloc_can_skb(dev, &cf); - if (skb == NULL) { - netdev_err(dev, - "dropping frame: skb allocation failed\n"); + msg = grcan_rx_msg_slot(dma, rd); + if (msg->header.ctrl & GRCAN_RX_FDF) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + + if (unlikely(!skb)) { + netdev_err(dev, "dropping frame: skb allocation failed\n"); stats->rx_dropped++; continue; } - slot = (u32 *)((u8 *)dma->rx.msg_slot + rd); - eff = slot[0] & GRCAN_MSG_IDE; - rtr = slot[0] & GRCAN_MSG_RTR; + dlc = FIELD_GET(GRCAN_MSG_DLC_MASK, msg->header.ctrl); + if (msg->header.ctrl & GRCAN_RX_FDF) + cf->len = can_fd_dlc2len(dlc); + else + cf->len = can_cc_dlc2len(dlc); + + bds = grcan_numbds(cf->len); + payload_offset = 0; + data = cf->data; + + eff = msg->header.id & GRCAN_MSG_IDE; + rtr = msg->header.id & GRCAN_MSG_RTR; + if (eff) { - cf->can_id = ((slot[0] & GRCAN_MSG_EID) - >> GRCAN_MSG_EID_BIT); + cf->can_id = FIELD_GET(GRCAN_MSG_EID_MASK, msg->header.id); cf->can_id |= CAN_EFF_FLAG; } else { - cf->can_id = ((slot[0] & GRCAN_MSG_BID) - >> GRCAN_MSG_BID_BIT); + cf->can_id = FIELD_GET(GRCAN_MSG_BID_MASK, msg->header.id); } - cf->len = can_cc_dlc2len((slot[1] & GRCAN_MSG_DLC) - >> GRCAN_MSG_DLC_BIT); - if (rtr) { + if (rtr) cf->can_id |= CAN_RTR_FLAG; - } else { - if (cf->can_dlc > 0) - memcpy(cf->data, &slot[2], sizeof(u32)); - if (cf->can_dlc > 4) - memcpy(cf->data + 4, &slot[3], sizeof(u32)); - stats->rx_bytes += cf->len; + copy_len = min_t(u32, cf->len, GRCAN_CLASSIC_DATA_SIZE); + memcpy(data, msg->header.data, copy_len); + payload_offset += copy_len; + + rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); + + for (i = 1; i < bds; i++) { + msg = grcan_rx_msg_slot(dma, rd); + + copy_len = min_t(u32, (u32)cf->len - payload_offset, (u32)GRCAN_MSG_SIZE); + memcpy(data + payload_offset, msg->frags.data, copy_len); + payload_offset += copy_len; + + rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); } - stats->rx_packets++; + /* Update statistics and read pointer */ + stats->rx_packets++; + stats->rx_bytes += cf->len; netif_receive_skb(skb); - - rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); } /* Make sure everything is read before allowing hardware to
--
2.51.0