From nobody Mon Feb 9 21:21:25 2026 Received: from smtp-out3.simply.com (smtp-out3.simply.com [94.231.106.210]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E385732F74D; Tue, 23 Dec 2025 10:56:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=94.231.106.210 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766487402; cv=none; b=Oi3/ficcDMHbjXXnxp1L234OHyFHyp0vuoIcdblUCJWiEp1To6RHDehz3spSDpkDRAWBxyeSzR2T3KcO+UmSZOYUX39zvF4Vvs6vqWZp4Jf+yuOf5wpA4XnYEr2LNARBl+0isgKvoL2xPkQ/LdpKMNTIWwBBAejNQZIif8jBCGE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766487402; c=relaxed/simple; bh=ltaff+K3CBi9xy5oHKq6c52XPNs2opiJ5o0B5rhk+bg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CjjQ1T1cy40cX2cE2LMQWT7tM/qx/CdpBVxb7cHK4nKK3OBUl/rT8YoNbUJIOtVW4CZWR9Gng0lZ/17ng40Hz6LKQVnzVVjD5pBryFXctrH5Jf2YoVW5/IfM/TNhFXOOnYtIJZ2l0T9NlfqPHNNc6weH54BglC8RMwCwkMCxnu0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gaisler.com; spf=pass smtp.mailfrom=gaisler.com; dkim=fail (0-bit key) header.d=gaisler.com header.i=@gaisler.com header.b=gOH5OfAw reason="key not found in DNS"; arc=none smtp.client-ip=94.231.106.210 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gaisler.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gaisler.com Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=gaisler.com header.i=@gaisler.com header.b="gOH5OfAw" Received: from localhost (localhost [127.0.0.1]) by smtp.simply.com (Simply.com) with ESMTP id 4dbBlD4Nl7z1DQqm; Tue, 23 Dec 2025 11:56:24 +0100 (CET) Received: from d-5xj5g74.got.gaisler.com.com (h-98-128-223-123.NA.cust.bahnhof.se [98.128.223.123]) by smtp.simply.com (Simply.com) with ESMTPA id 4dbBlD2Mhyz1DDMt; Tue, 23 Dec 2025 11:56:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaisler.com; s=simplycom2; t=1766487384; bh=rk7e03y+KxiKbdtetBVfrUyemjQA746lgNFpnPVD81Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=gOH5OfAw1KSlabJZ6uz5c4Gd6rJPjfmmYd2asuLojwvUbIqjbGSr/5Eke+ih2LAAl ZvSbd/1tad6sEejQme1kKLXdXe55HAXPdVHE7hw97ZX90+9sljrjFaHwxcFjfptkVF /m9NnIp8zeMM03Az/TCwGytn2xmT/BeXUSz5UXGn2bqFhJC0knWEDSs8X+81M82Sl1 vRueZtl/PUIdsCWYdno4GInwaXaK1NfCtZ2xUZERgCyWaiPjK7jNQkgQzD1m+w8U22 XJbBBh0QPLgx4qtFKb3CW9FQotKBt8GcNioNtFDL9hXBfsBWs3zujUUdLy11RRp2IM F/Y70k2mM6Ghw== From: Arun Muthusamy To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, mkl@pengutronix.de, mailhol@kernel.org Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-can@vger.kernel.org, Daniel Hellstrom , Arun Muthusamy Subject: [PATCH v2 05/10] can: grcan: add FD capability detection and nominal bit-timing Date: Tue, 23 Dec 2025 11:55:59 +0100 Message-ID: <20251223105604.12675-6-arun.muthusamy@gaisler.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251223105604.12675-1-arun.muthusamy@gaisler.com> References: <20251223105604.12675-1-arun.muthusamy@gaisler.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Daniel Hellstrom Add capability for the driver to detect CAN FD support and adjust accordingly. Introduce structures and functions for setting nominal bit-timing for standard CAN and CAN FD. The `grcan_hwcap` structure defines hardware capabilities like CAN FD support and baud-rate options. Additionally, improved device tree compatibility by updating the `of_device_id` table for better matching of GRCAN and GRCANFD devices. Also update Kconfig to mention GRCANFD support. Signed-off-by: Daniel Hellstrom Signed-off-by: Arun Muthusamy --- drivers/net/can/Kconfig | 6 +- drivers/net/can/grcan.c | 208 ++++++++++++++++++++++++++++------------ 2 files changed, 151 insertions(+), 63 deletions(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d43d56694667..816b6e71a990 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -133,10 +133,12 @@ config CAN_FLEXCAN Say Y here if you want to support for Freescale FlexCAN. =20 config CAN_GRCAN - tristate "Aeroflex Gaisler GRCAN and GRHCAN CAN devices" + tristate "Aeroflex Gaisler GRCAN, GRCANFD and GRHCAN CAN devices" depends on OF && HAS_DMA && HAS_IOMEM help - Say Y here if you want to use Aeroflex Gaisler GRCAN or GRHCAN. + Say Y here if you want to use Aeroflex Gaisler GRCAN or GRCANFD + or GRHCAN. + Note that the driver supports little endian, even though little endian syntheses of the cores would need some modifications on the hardware level to work. diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 538a9b4f82ab..cac85fbe6acf 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -33,9 +33,11 @@ #include #include #include +#include #include #include #include +#include =20 #define DRV_NAME "grcan" =20 @@ -50,7 +52,11 @@ struct grcan_registers { u32 __reserved1[GRCAN_RESERVE_SIZE(0x08, 0x18)]; u32 smask; /* 0x18 - CanMASK */ u32 scode; /* 0x1c - CanCODE */ - u32 __reserved2[GRCAN_RESERVE_SIZE(0x1c, 0x100)]; + u32 __reserved2[GRCAN_RESERVE_SIZE(0x1c, 0x40)]; + u32 nbtr; /* 0x40 */ + u32 fdbtr; /* 0x44 */ + u32 tdelay; /* 0x48 */ + u32 __reserved2_[GRCAN_RESERVE_SIZE(0x48, 0x100)]; u32 pimsr; /* 0x100 */ u32 pimr; /* 0x104 */ u32 pisr; /* 0x108 */ @@ -93,16 +99,6 @@ struct grcan_registers { (GRCAN_CONF_BPR | GRCAN_CONF_RSJ | GRCAN_CONF_PS1 \ | GRCAN_CONF_PS2 | GRCAN_CONF_SCALER) =20 -#define GRCAN_CONF_RSJ_MIN 1 -#define GRCAN_CONF_RSJ_MAX 4 -#define GRCAN_CONF_PS1_MIN 1 -#define GRCAN_CONF_PS1_MAX 15 -#define GRCAN_CONF_PS2_MIN 2 -#define GRCAN_CONF_PS2_MAX 8 -#define GRCAN_CONF_SCALER_MIN 0 -#define GRCAN_CONF_SCALER_MAX 255 -#define GRCAN_CONF_SCALER_INC 1 - #define GRCAN_CONF_BPR_BIT 8 #define GRCAN_CONF_RSJ_BIT 12 #define GRCAN_CONF_PS1_BIT 20 @@ -212,6 +208,39 @@ struct grcan_registers { #error "Invalid default buffer size" #endif =20 +#define GRCANFD_NBTR_SCALER GENMASK(23, 16) +#define GRCANFD_NBTR_PS1 GENMASK(17, 10) +#define GRCANFD_NBTR_PS2 GENMASK(9, 5) +#define GRCANFD_NBTR_SJW GENMASK(4, 0) +#define GRCANFD_NBTR_TIMING \ + (GRCANFD_NBTR_SCALER | GRCANFD_NBTR_PS1 | GRCANFD_NBTR_PS2 | \ + GRCANFD_NBTR_SJW) + +#define GRCANFD_FDBTR_SCALER 0x00ff0000 +#define GRCANFD_FDBTR_PS1 0x00003c00 +#define GRCANFD_FDBTR_PS2 0x000001e0 +#define GRCANFD_FDBTR_SJW 0x0000000f +#define GRCANFD_FDBTR_TIMING \ + (GRCANFD_FDBTR_SCALER | GRCANFD_FDBTR_PS1 | GRCANFD_FDBTR_PS2 | \ + GRCANFD_FDBTR_SJW) + +#define GRCANFD_FDBTR_SCALER_BIT 16 +#define GRCANFD_FDBTR_PS1_BIT 10 +#define GRCANFD_FDBTR_PS2_BIT 5 +#define GRCANFD_FDBTR_SJW_BIT 0 + +/* Hardware capabilities */ +struct grcan_hwcap { + /* CAN-FD capable, indicates GRCANFD IP. + * The GRCANFD has different baud-rate registers and extended DMA + * format to also describe FD-frames. + */ + const struct can_bittiming_const *bt_const; + int (*set_bittiming)(struct net_device *dev); + bool txbug_possible; + bool fd; +}; + struct grcan_dma_buffer { size_t size; void *buf; @@ -254,6 +283,7 @@ struct grcan_priv { struct napi_struct napi; =20 struct grcan_registers __iomem *regs; /* ioremap'ed registers */ + const struct grcan_hwcap *hwcap; struct grcan_device_config config; struct grcan_dma dma; =20 @@ -304,6 +334,7 @@ struct grcan_priv { */ bool resetting; bool closing; + }; =20 /* Wait time for a short wait for ongoing to clear */ @@ -392,55 +423,79 @@ static struct grcan_device_config grcan_module_config= =3D =20 static const struct can_bittiming_const grcan_bittiming_const =3D { .name =3D DRV_NAME, - .tseg1_min =3D GRCAN_CONF_PS1_MIN + 1, - .tseg1_max =3D GRCAN_CONF_PS1_MAX + 1, - .tseg2_min =3D GRCAN_CONF_PS2_MIN, - .tseg2_max =3D GRCAN_CONF_PS2_MAX, - .sjw_max =3D GRCAN_CONF_RSJ_MAX, - .brp_min =3D GRCAN_CONF_SCALER_MIN + 1, - .brp_max =3D GRCAN_CONF_SCALER_MAX + 1, - .brp_inc =3D GRCAN_CONF_SCALER_INC, + .tseg1_min =3D 2, + .tseg1_max =3D 16, + .tseg2_min =3D 2, + .tseg2_max =3D 8, + .sjw_max =3D 4, + .brp_min =3D 1, + .brp_max =3D 256, + .brp_inc =3D 1, +}; + +/* GRCANFD nominal boundaries for baud-rate parameters */ +static const struct can_bittiming_const grcanfd_bittiming_const =3D { + .name =3D DRV_NAME, + .tseg1_min =3D 2, + .tseg1_max =3D 63, + .tseg2_min =3D 2, + .tseg2_max =3D 16, + .sjw_max =3D 16, + .brp_min =3D 1, + .brp_max =3D 256, + .brp_inc =3D 1, }; =20 static int grcan_set_bittiming(struct net_device *dev) { struct grcan_priv *priv =3D netdev_priv(dev); - struct grcan_registers __iomem *regs =3D priv->regs; - struct can_bittiming *bt =3D &priv->can.bittiming; - u32 timing =3D 0; + struct grcan_registers __iomem *regs; int bpr, rsj, ps1, ps2, scaler; + struct can_bittiming *bt; + u32 timing =3D 0; =20 - /* Should never happen - function will not be called when - * device is up - */ - if (grcan_read_bits(®s->ctrl, GRCAN_CTRL_ENABLE)) - return -EBUSY; + regs =3D priv->regs; + bt =3D &priv->can.bittiming; =20 bpr =3D 0; /* Note bpr and brp are different concepts */ rsj =3D bt->sjw; ps1 =3D (bt->prop_seg + bt->phase_seg1) - 1; /* tseg1 - 1 */ ps2 =3D bt->phase_seg2; - scaler =3D (bt->brp - 1); - netdev_dbg(dev, "Request for BPR=3D%d, RSJ=3D%d, PS1=3D%d, PS2=3D%d, SCAL= ER=3D%d", - bpr, rsj, ps1, ps2, scaler); - if (!(ps1 > ps2)) { - netdev_err(dev, "PS1 > PS2 must hold: PS1=3D%d, PS2=3D%d\n", - ps1, ps2); - return -EINVAL; - } - if (!(ps2 >=3D rsj)) { - netdev_err(dev, "PS2 >=3D RSJ must hold: PS2=3D%d, RSJ=3D%d\n", - ps2, rsj); - return -EINVAL; - } + scaler =3D bt->brp - 1; + + timing |=3D FIELD_PREP(GRCAN_CONF_BPR, bpr); + timing |=3D FIELD_PREP(GRCAN_CONF_RSJ, rsj); + timing |=3D FIELD_PREP(GRCAN_CONF_PS1, ps1); + timing |=3D FIELD_PREP(GRCAN_CONF_PS2, ps2); + timing |=3D FIELD_PREP(GRCAN_CONF_SCALER, scaler); + netdev_dbg(dev, "setting timing=3D0x%x\n", timing); + grcan_write_bits(®s->conf, timing, GRCAN_CONF_TIMING); + + return 0; +} + +static int grcanfd_set_bittiming(struct net_device *dev) +{ + struct grcan_priv *priv =3D netdev_priv(dev); + struct grcan_registers __iomem *regs; + int sjw, ps1, ps2, scaler; + struct can_bittiming *bt; + u32 timing =3D 0; + + regs =3D priv->regs; + bt =3D &priv->can.bittiming; + + sjw =3D bt->sjw; + ps1 =3D (bt->prop_seg + bt->phase_seg1); + ps2 =3D bt->phase_seg2; + scaler =3D bt->brp - 1; =20 - timing |=3D (bpr << GRCAN_CONF_BPR_BIT) & GRCAN_CONF_BPR; - timing |=3D (rsj << GRCAN_CONF_RSJ_BIT) & GRCAN_CONF_RSJ; - timing |=3D (ps1 << GRCAN_CONF_PS1_BIT) & GRCAN_CONF_PS1; - timing |=3D (ps2 << GRCAN_CONF_PS2_BIT) & GRCAN_CONF_PS2; - timing |=3D (scaler << GRCAN_CONF_SCALER_BIT) & GRCAN_CONF_SCALER; + timing |=3D FIELD_PREP(GRCANFD_NBTR_SJW, sjw); + timing |=3D FIELD_PREP(GRCANFD_NBTR_PS1, ps1); + timing |=3D FIELD_PREP(GRCANFD_NBTR_PS2, ps2); + timing |=3D FIELD_PREP(GRCANFD_NBTR_SCALER, scaler); netdev_info(dev, "setting timing=3D0x%x\n", timing); - grcan_write_bits(®s->conf, timing, GRCAN_CONF_TIMING); + grcan_write_bits(®s->nbtr, timing, GRCANFD_NBTR_TIMING); =20 return 0; } @@ -1569,7 +1624,8 @@ static const struct ethtool_ops grcan_ethtool_ops =3D= { =20 static int grcan_setup_netdev(struct platform_device *ofdev, void __iomem *base, - int irq, u32 ambafreq, bool txbug) + int irq, u32 ambafreq, bool txbug, + const struct grcan_hwcap *hwcap) { struct net_device *dev; struct grcan_priv *priv; @@ -1592,14 +1648,14 @@ static int grcan_setup_netdev(struct platform_devic= e *ofdev, priv->dev =3D dev; priv->ofdev_dev =3D &ofdev->dev; priv->regs =3D base; - priv->can.bittiming_const =3D &grcan_bittiming_const; - priv->can.do_set_bittiming =3D grcan_set_bittiming; + priv->can.bittiming_const =3D hwcap->bt_const; priv->can.do_set_mode =3D grcan_set_mode; priv->can.do_get_berr_counter =3D grcan_get_berr_counter; priv->can.clock.freq =3D ambafreq; priv->can.ctrlmode_supported =3D CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_ONE_SHOT; priv->need_txbug_workaround =3D txbug; + priv->hwcap =3D hwcap; =20 /* Discover if triple sampling is supported by hardware */ regs =3D priv->regs; @@ -1644,22 +1700,33 @@ static int grcan_probe(struct platform_device *ofde= v) { struct device_node *np =3D ofdev->dev.of_node; struct device_node *sysid_parent; + const struct grcan_hwcap *hwcap; struct clk *clk; u32 sysid, ambafreq; int irq, err; void __iomem *base; bool txbug =3D true; =20 + hwcap =3D device_get_match_data(&ofdev->dev); + if (!hwcap) { + dev_err(&ofdev->dev, "Platform data not provided!\n"); + return -ENODEV; + } + /* Compare GRLIB version number with the first that does not * have the tx bug (see start_xmit) */ - sysid_parent =3D of_find_node_by_path("/ambapp0"); - if (sysid_parent) { - err =3D of_property_read_u32(sysid_parent, "systemid", &sysid); - if (!err && ((sysid & GRLIB_VERSION_MASK) >=3D - GRCAN_TXBUG_SAFE_GRLIB_VERSION)) - txbug =3D false; - of_node_put(sysid_parent); + if (!hwcap->txbug_possible) { + txbug =3D false; + } else { + sysid_parent =3D of_find_node_by_path("/ambapp0"); + if (sysid_parent) { + err =3D of_property_read_u32(sysid_parent, "systemid", &sysid); + if (!err && ((sysid & GRLIB_VERSION_MASK) >=3D + GRCAN_TXBUG_SAFE_GRLIB_VERSION)) + txbug =3D false; + of_node_put(sysid_parent); + } } =20 err =3D of_property_read_u32(np, "freq", &ambafreq); @@ -1695,7 +1762,7 @@ static int grcan_probe(struct platform_device *ofdev) =20 grcan_sanitize_module_config(ofdev); =20 - err =3D grcan_setup_netdev(ofdev, base, irq, ambafreq, txbug); + err =3D grcan_setup_netdev(ofdev, base, irq, ambafreq, txbug, hwcap); if (err) goto exit_dispose_irq; =20 @@ -1722,11 +1789,30 @@ static void grcan_remove(struct platform_device *of= dev) free_candev(dev); } =20 +static const struct grcan_hwcap grcan_hwcap =3D { + .fd =3D false, + .txbug_possible =3D true, + .bt_const =3D &grcan_bittiming_const, + .set_bittiming =3D grcan_set_bittiming, +}; + +static const struct grcan_hwcap grcanfd_hwcap =3D { + .fd =3D true, + .txbug_possible =3D false, + .bt_const =3D &grcanfd_bittiming_const, + .set_bittiming =3D grcanfd_set_bittiming, +}; + static const struct of_device_id grcan_match[] =3D { - {.name =3D "GAISLER_GRCAN"}, - {.name =3D "01_03d"}, - {.name =3D "GAISLER_GRHCAN"}, - {.name =3D "01_034"}, + {.name =3D "GAISLER_GRCAN", .data =3D &grcan_hwcap}, + {.name =3D "01_03d", .data =3D &grcan_hwcap}, + {.name =3D "GAISLER_GRHCAN", .data =3D &grcan_hwcap}, + {.name =3D "01_034", .data =3D &grcan_hwcap}, + {.compatible =3D "gaisler,grcan", .data =3D &grcan_hwcap}, + /* GRCANFD */ + {.compatible =3D "gaisler,grcanfd", .data =3D &grcanfd_hwcap}, + {.name =3D "GAISLER_GRCANFD", .data =3D &grcanfd_hwcap}, + {.name =3D "01_0B5", .data =3D &grcanfd_hwcap}, {}, }; =20 --=20 2.51.0