Commit 113aa910 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde
Browse files

Merge patch series "can: netlink: add CAN XL support"

Marc Kleine-Budde <mkl@pengutronix.de> says:

Similarly to how CAN FD reuses the bittiming logic of Classical CAN, CAN XL
also reuses the entirety of CAN FD features, and, on top of that, adds new
features which are specific to CAN XL.

A so-called 'mixed-mode' is intended to have (XL-tolerant) CAN FD nodes and
CAN XL nodes on one CAN segment, where the FD-controllers can talk CC/FD
and the XL-controllers can talk CC/FD/XL. This mixed-mode utilizes the
known error-signalling (ES) for sending CC/FD/XL frames. For CAN FD and CAN
XL the tranceiver delay compensation (TDC) is supported to use common CAN
and CAN-SIG transceivers.

The CANXL-only mode disables the error-signalling in the CAN XL controller.
This mode does not allow CC/FD frames to be sent but additionally offers a
CAN XL transceiver mode switching (TMS) to send CAN XL frames with up to
20Mbit/s data rate. The TMS utilizes a PWM configuration which is added to
the netlink interface.

Configured with CAN_CTRLMODE_FD and CAN_CTRLMODE_XL this leads to:

FD=0 XL=0 CC-only mode         (ES=1)
FD=1 XL=0 FD/CC mixed-mode     (ES=1)
FD=1 XL=1 XL/FD/CC mixed-mode  (ES=1)
FD=0 XL=1 XL-only mode         (ES=0, TMS optional)

Patch #1 print defined ctrlmode strings capitalized to increase the
readability and to be in line with the 'ip' tool (iproute2).

Patch #2 is a small clean-up which makes can_calc_bittiming() use
NL_SET_ERR_MSG() instead of netdev_err().

Patch #3 adds a check in can_dev_dropped_skb() to drop CAN FD frames
when CAN FD is turned off.

Patch #4 adds CAN_CTRLMODE_RESTRICTED. Note that contrary to the other
CAN_CTRL_MODE_XL_* that are introduced in the later patches, this control
mode is not specific to CAN XL. The nuance is that because this restricted
mode was only added in ISO 11898-1:2024, it is made mandatory for CAN XL
devices but optional for other protocols. This is why this patch is added
as a preparation before introducing the core CAN XL logic.

Patch #5 adds all the CAN XL features which are inherited from CAN FD: the
nominal bittiming, the data bittiming and the TDC.

Patch #6 add a new CAN_CTRLMODE_XL_TMS control mode which is specific to
CAN XL to enable the transceiver mode switching (TMS) in XL-only mode.

Patch #7 adds a check in can_dev_dropped_skb() to drop CAN CC/FD frames
when the CAN XL controller is in CAN XL-only mode. The introduced
can_dev_in_xl_only_mode() function also determines the error-signalling
configuration for the CAN XL controllers.

Patch #8 to #11 add the PWM logic for the CAN XL TMS mode.

Patch #12 to #14 add different default sample-points for standard CAN and
CAN SIG transceivers (with TDC) and CAN XL transceivers using PWM in the
CAN XL TMS mode.

Patch #15 add a dummy_can driver for netlink testing and debugging.

Patch #16 check CAN frame type (CC/FD/XL) when writing those frames to the
CAN_RAW socket and reject them if it's not supported by the CAN interface.

Patch #17 increase the resolution when printing the bitrate error and
round-up the value to 0.01% in the case the resolution would still provide
values which would lead to 0.00%.

Link: https://patch.msgid.link/20251126-canxl-v8-0-e7e3eb74f889@pengutronix.de


Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parents ab084f0b b360a13d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -124,6 +124,23 @@ config CAN_CAN327

	  If this driver is built as a module, it will be called can327.

config CAN_DUMMY
	tristate "Dummy CAN"
	help
	  A dummy CAN module supporting Classical CAN, CAN FD and CAN XL. It
	  exposes bittiming values which can be configured through the netlink
	  interface.

	  The module will simply echo any frame sent to it. If debug messages
	  are activated, it prints all the CAN bittiming information in the
	  kernel log. Aside from that it does nothing.

	  This is convenient for testing the CAN netlink interface. Most of the
	  users will never need this. If unsure, say NO.

	  To compile this driver as a module, choose M here: the module will be
	  called dummy-can.

config CAN_FLEXCAN
	tristate "Support for Freescale FLEXCAN based chips"
	depends on OF || COLDFIRE || COMPILE_TEST
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ obj-$(CONFIG_CAN_CAN327) += can327.o
obj-$(CONFIG_CAN_CC770)		+= cc770/
obj-$(CONFIG_CAN_C_CAN)		+= c_can/
obj-$(CONFIG_CAN_CTUCANFD)	+= ctucanfd/
obj-$(CONFIG_CAN_DUMMY)		+= dummy_can.o
obj-$(CONFIG_CAN_FLEXCAN)	+= flexcan/
obj-$(CONFIG_CAN_GRCAN)		+= grcan.o
obj-$(CONFIG_CAN_IFI_CANFD)	+= ifi_canfd/
+63 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
 * Copyright (C) 2006 Andrey Volkov, Varma Electronics
 * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
 * Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org>
 */

#include <linux/can/dev.h>
@@ -151,3 +152,65 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,

	return -EINVAL;
}

int can_validate_pwm_bittiming(const struct net_device *dev,
			       const struct can_pwm *pwm,
			       struct netlink_ext_ack *extack)
{
	const struct can_priv *priv = netdev_priv(dev);
	u32 xl_bit_time_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
	u32 nom_bit_time_tqmin = can_bit_time_tqmin(&priv->bittiming);
	u32 pwms_ns = can_tqmin_to_ns(pwm->pwms, priv->clock.freq);
	u32 pwml_ns = can_tqmin_to_ns(pwm->pwml, priv->clock.freq);

	if (pwms_ns + pwml_ns > CAN_PWM_NS_MAX) {
		NL_SET_ERR_MSG_FMT(extack,
				   "The PWM symbol duration: %u ns may not exceed %u ns",
				   pwms_ns + pwml_ns, CAN_PWM_NS_MAX);
		return -EINVAL;
	}

	if (pwms_ns < CAN_PWM_DECODE_NS) {
		NL_SET_ERR_MSG_FMT(extack,
				   "PWMS: %u ns shall be at least %u ns",
				   pwms_ns, CAN_PWM_DECODE_NS);
		return -EINVAL;
	}

	if (pwm->pwms >= pwm->pwml) {
		NL_SET_ERR_MSG_FMT(extack,
				   "PWMS: %u tqmin shall be smaller than PWML: %u tqmin",
				   pwm->pwms, pwm->pwml);
		return -EINVAL;
	}

	if (pwml_ns - pwms_ns < 2 * CAN_PWM_DECODE_NS) {
		NL_SET_ERR_MSG_FMT(extack,
				   "At least %u ns shall separate PWMS: %u ns from PMWL: %u ns",
				   2 * CAN_PWM_DECODE_NS, pwms_ns, pwml_ns);
		return -EINVAL;
	}

	if (xl_bit_time_tqmin % (pwm->pwms + pwm->pwml) != 0) {
		NL_SET_ERR_MSG_FMT(extack,
				   "PWM duration: %u tqmin does not divide XL's bit time: %u tqmin",
				   pwm->pwms + pwm->pwml, xl_bit_time_tqmin);
		return -EINVAL;
	}

	if (pwm->pwmo >= pwm->pwms + pwm->pwml) {
		NL_SET_ERR_MSG_FMT(extack,
				   "PWMO: %u tqmin can not be greater than PWMS + PWML: %u tqmin",
				   pwm->pwmo, pwm->pwms + pwm->pwml);
		return -EINVAL;
	}

	if (nom_bit_time_tqmin % (pwm->pwms + pwm->pwml) != pwm->pwmo) {
		NL_SET_ERR_MSG_FMT(extack,
				   "Can not assemble nominal bit time: %u tqmin out of PWMS + PMWL and PWMO",
				   nom_bit_time_tqmin);
		return -EINVAL;
	}

	return 0;
}
+88 −26
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
 * Copyright (C) 2006 Andrey Volkov, Varma Electronics
 * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
 * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
 */

#include <linux/units.h>
@@ -9,6 +10,33 @@

#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */

/* CiA recommended sample points for Non Return to Zero encoding. */
static int can_calc_sample_point_nrz(const struct can_bittiming *bt)
{
	if (bt->bitrate > 800 * KILO /* BPS */)
		return 750;

	if (bt->bitrate > 500 * KILO /* BPS */)
		return 800;

	return 875;
}

/* Sample points for Pulse-Width Modulation encoding. */
static int can_calc_sample_point_pwm(const struct can_bittiming *bt)
{
	if (bt->bitrate > 15 * MEGA /* BPS */)
		return 625;

	if (bt->bitrate > 9 * MEGA /* BPS */)
		return 600;

	if (bt->bitrate > 4 * MEGA /* BPS */)
		return 560;

	return 520;
}

/* Bit-timing calculation derived from:
 *
 * Code based on LinCAN sources and H8S2638 project
@@ -23,7 +51,7 @@
 */
static int
can_update_sample_point(const struct can_bittiming_const *btc,
			const unsigned int sample_point_nominal, const unsigned int tseg,
			const unsigned int sample_point_reference, const unsigned int tseg,
			unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
			unsigned int *sample_point_error_ptr)
{
@@ -34,7 +62,7 @@ can_update_sample_point(const struct can_bittiming_const *btc,

	for (i = 0; i <= 1; i++) {
		tseg2 = tseg + CAN_SYNC_SEG -
			(sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
			(sample_point_reference * (tseg + CAN_SYNC_SEG)) /
			1000 - i;
		tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
		tseg1 = tseg - tseg2;
@@ -45,9 +73,9 @@ can_update_sample_point(const struct can_bittiming_const *btc,

		sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
			(tseg + CAN_SYNC_SEG);
		sample_point_error = abs(sample_point_nominal - sample_point);
		sample_point_error = abs(sample_point_reference - sample_point);

		if (sample_point <= sample_point_nominal &&
		if (sample_point <= sample_point_reference &&
		    sample_point_error < best_sample_point_error) {
			best_sample_point = sample_point;
			best_sample_point_error = sample_point_error;
@@ -67,28 +95,24 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
{
	struct can_priv *priv = netdev_priv(dev);
	unsigned int bitrate;			/* current bitrate */
	unsigned int bitrate_error;		/* difference between current and nominal value */
	unsigned int bitrate_error;		/* diff between calculated and reference value */
	unsigned int best_bitrate_error = UINT_MAX;
	unsigned int sample_point_error;	/* difference between current and nominal value */
	unsigned int sample_point_error;	/* diff between calculated and reference value */
	unsigned int best_sample_point_error = UINT_MAX;
	unsigned int sample_point_nominal;	/* nominal sample point */
	unsigned int sample_point_reference;	/* reference sample point */
	unsigned int best_tseg = 0;		/* current best value for tseg */
	unsigned int best_brp = 0;		/* current best value for brp */
	unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
	u64 v64;
	int err;

	/* Use CiA recommended sample points */
	if (bt->sample_point) {
		sample_point_nominal = bt->sample_point;
	} else {
		if (bt->bitrate > 800 * KILO /* BPS */)
			sample_point_nominal = 750;
		else if (bt->bitrate > 500 * KILO /* BPS */)
			sample_point_nominal = 800;
	if (bt->sample_point)
		sample_point_reference = bt->sample_point;
	else if (btc == priv->xl.data_bittiming_const &&
		 (priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
		sample_point_reference = can_calc_sample_point_pwm(bt);
	else
			sample_point_nominal = 875;
	}
		sample_point_reference = can_calc_sample_point_nrz(bt);

	/* tseg even = round down, odd = round up */
	for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
@@ -114,7 +138,7 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
		if (bitrate_error < best_bitrate_error)
			best_sample_point_error = UINT_MAX;

		can_update_sample_point(btc, sample_point_nominal, tseg / 2,
		can_update_sample_point(btc, sample_point_reference, tseg / 2,
					&tseg1, &tseg2, &sample_point_error);
		if (sample_point_error >= best_sample_point_error)
			continue;
@@ -129,23 +153,26 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
	}

	if (best_bitrate_error) {
		/* Error in one-tenth of a percent */
		v64 = (u64)best_bitrate_error * 1000;
		/* Error in one-hundredth of a percent */
		v64 = (u64)best_bitrate_error * 10000;
		do_div(v64, bt->bitrate);
		bitrate_error = (u32)v64;
		/* print at least 0.01% if the error is smaller */
		bitrate_error = max(bitrate_error, 1U);
		if (bitrate_error > CAN_CALC_MAX_ERROR) {
			NL_SET_ERR_MSG_FMT(extack,
					   "bitrate error: %u.%u%% too high",
					   bitrate_error / 10, bitrate_error % 10);
					   "bitrate error: %u.%02u%% too high",
					   bitrate_error / 100,
					   bitrate_error % 100);
			return -EINVAL;
		}
		NL_SET_ERR_MSG_FMT(extack,
				   "bitrate error: %u.%u%%",
				   bitrate_error / 10, bitrate_error % 10);
				   "bitrate error: %u.%02u%%",
				   bitrate_error / 100, bitrate_error % 100);
	}

	/* real sample point */
	bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
	bt->sample_point = can_update_sample_point(btc, sample_point_reference,
						   best_tseg, &tseg1, &tseg2,
						   NULL);

@@ -198,3 +225,38 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
		*ctrlmode |= tdc_auto;
	}
}

int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
{
	struct can_priv *priv = netdev_priv(dev);
	const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
	struct can_pwm *pwm = &priv->xl.pwm;
	u32 xl_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
	u32 xl_ns = can_tqmin_to_ns(xl_tqmin, priv->clock.freq);
	u32 nom_tqmin = can_bit_time_tqmin(&priv->bittiming);
	int pwm_per_bit_max = xl_tqmin / (pwm_const->pwms_min + pwm_const->pwml_min);
	int pwm_per_bit;
	u32 pwm_tqmin;

	/* For 5 MB/s databitrate or greater, xl_ns < CAN_PWM_NS_MAX
	 * giving us a pwm_per_bit of 1 and the loop immediately breaks
	 */
	for (pwm_per_bit = DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX);
	     pwm_per_bit <= pwm_per_bit_max; pwm_per_bit++)
		if (xl_tqmin % pwm_per_bit == 0)
			break;

	if (pwm_per_bit > pwm_per_bit_max) {
		NL_SET_ERR_MSG_FMT(extack,
				   "Can not divide the XL data phase's bit time: %u tqmin into multiple PWM symbols",
				   xl_tqmin);
		return -EINVAL;
	}

	pwm_tqmin = xl_tqmin / pwm_per_bit;
	pwm->pwms = DIV_ROUND_UP_POW2(pwm_tqmin, 4);
	pwm->pwml = pwm_tqmin - pwm->pwms;
	pwm->pwmo = nom_tqmin % pwm_tqmin;

	return 0;
}
+29 −13
Original line number Diff line number Diff line
@@ -92,29 +92,39 @@ const char *can_get_ctrlmode_str(u32 ctrlmode)
{
	switch (ctrlmode & ~(ctrlmode - 1)) {
	case 0:
		return "none";
		return "(none)";
	case CAN_CTRLMODE_LOOPBACK:
		return "loopback";
		return "LOOPBACK";
	case CAN_CTRLMODE_LISTENONLY:
		return "listen-only";
		return "LISTEN-ONLY";
	case CAN_CTRLMODE_3_SAMPLES:
		return "triple-sampling";
		return "TRIPLE-SAMPLING";
	case CAN_CTRLMODE_ONE_SHOT:
		return "one-shot";
		return "ONE-SHOT";
	case CAN_CTRLMODE_BERR_REPORTING:
		return "berr-reporting";
		return "BERR-REPORTING";
	case CAN_CTRLMODE_FD:
		return "fd";
		return "FD";
	case CAN_CTRLMODE_PRESUME_ACK:
		return "presume-ack";
		return "PRESUME-ACK";
	case CAN_CTRLMODE_FD_NON_ISO:
		return "fd-non-iso";
		return "FD-NON-ISO";
	case CAN_CTRLMODE_CC_LEN8_DLC:
		return "cc-len8-dlc";
		return "CC-LEN8-DLC";
	case CAN_CTRLMODE_TDC_AUTO:
		return "fd-tdc-auto";
		return "TDC-AUTO";
	case CAN_CTRLMODE_TDC_MANUAL:
		return "fd-tdc-manual";
		return "TDC-MANUAL";
	case CAN_CTRLMODE_RESTRICTED:
		return "RESTRICTED";
	case CAN_CTRLMODE_XL:
		return "XL";
	case CAN_CTRLMODE_XL_TDC_AUTO:
		return "XL-TDC-AUTO";
	case CAN_CTRLMODE_XL_TDC_MANUAL:
		return "XL-TDC-MANUAL";
	case CAN_CTRLMODE_XL_TMS:
		return "TMS";
	default:
		return "<unknown>";
	}
@@ -348,7 +358,13 @@ void can_set_default_mtu(struct net_device *dev)
{
	struct can_priv *priv = netdev_priv(dev);

	if (priv->ctrlmode & CAN_CTRLMODE_FD) {
	if (priv->ctrlmode & CAN_CTRLMODE_XL) {
		if (can_is_canxl_dev_mtu(dev->mtu))
			return;
		dev->mtu = CANXL_MTU;
		dev->min_mtu = CANXL_MIN_MTU;
		dev->max_mtu = CANXL_MAX_MTU;
	} else if (priv->ctrlmode & CAN_CTRLMODE_FD) {
		dev->mtu = CANFD_MTU;
		dev->min_mtu = CANFD_MTU;
		dev->max_mtu = CANFD_MTU;
Loading