Merge tag 'linux-can-next-for-6.19-20251126' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next

Marc Kleine-Budde says:

====================
pull-request: can-next 2025-11-26

this is a pull request of 27 patches for net-next/main.

The first 17 patches are by Vincent Mailhol and Oliver Hartkopp and
add CAN XL support to the CAN netlink interface.

Geert Uytterhoeven and Biju Das provide 7 patches for the rcar_canfd
driver to add suspend/resume support.

The next 2 patches are by Markus Schneider-Pargmann and add them as
the m_can maintainer.

Conor Dooley's patch updates the mpfs-can DT bindungs.

linux-can-next-for-6.19-20251126

* tag 'linux-can-next-for-6.19-20251126' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next: (27 commits)
  dt-bindings: can: mpfs: document resets
  MAINTAINERS: Simplify m_can section
  MAINTAINERS: Add myself as m_can maintainer
  can: rcar_canfd: Add suspend/resume support
  can: rcar_canfd: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
  can: rcar_canfd: Invert CAN clock and close_candev() order
  can: rcar_canfd: Extract rcar_canfd_global_{,de}init()
  can: rcar_canfd: Use devm_clk_get_optional() for RAM clk
  can: rcar_canfd: Invert global vs. channel teardown
  can: rcar_canfd: Invert reset assert order
  can: dev: print bitrate error with two decimal digits
  can: raw: instantly reject unsupported CAN frames
  can: add dummy_can driver
  can: calc_bittiming: add can_calc_sample_point_pwm()
  can: calc_bittiming: add can_calc_sample_point_nrz()
  can: calc_bittiming: replace misleading "nominal" by "reference"
  can: netlink: add PWM netlink interface
  can: calc_bittiming: add PWM calculation
  can: bittiming: add PWM validation
  can: bittiming: add PWM parameters
  ...
====================

Link: https://patch.msgid.link/20251126120106.154635-1-mkl@pengutronix.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2025-11-27 15:45:17 +01:00
14 changed files with 1166 additions and 171 deletions

View File

@@ -32,11 +32,15 @@ properties:
- description: AHB peripheral clock
- description: CAN bus clock
resets:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
- resets
additionalProperties: false
@@ -46,6 +50,7 @@ examples:
compatible = "microchip,mpfs-can";
reg = <0x2010c000 0x1000>;
clocks = <&clkcfg 17>, <&clkcfg 37>;
resets = <&clkcfg 17>;
interrupt-parent = <&plic>;
interrupts = <56>;
};

View File

@@ -15412,14 +15412,12 @@ S: Supported
F: drivers/net/phy/mxl-86110.c
F: drivers/net/phy/mxl-gpy.c
MCAN MMIO DEVICE DRIVER
M: Chandrasekar Ramakrishnan <rcsekar@samsung.com>
MCAN DEVICE DRIVER
M: Markus Schneider-Pargmann <msp@baylibre.com>
L: linux-can@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
F: drivers/net/can/m_can/m_can.c
F: drivers/net/can/m_can/m_can.h
F: drivers/net/can/m_can/m_can_platform.c
F: drivers/net/can/m_can/
MCBA MICROCHIP CAN BUS ANALYZER TOOL DRIVER
R: Yasushi SHOJI <yashi@spacecubics.com>

View File

@@ -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

View File

@@ -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/

View File

@@ -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;
}

View File

@@ -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;
else
sample_point_nominal = 875;
}
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_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;
}

View File

@@ -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;

View File

@@ -2,7 +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 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
* Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
*/
#include <linux/can/dev.h>
@@ -22,6 +22,10 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
[IFLA_CAN_TERMINATION] = { .type = NLA_U16 },
[IFLA_CAN_TDC] = { .type = NLA_NESTED },
[IFLA_CAN_CTRLMODE_EXT] = { .type = NLA_NESTED },
[IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },
[IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },
[IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },
[IFLA_CAN_XL_PWM] = { .type = NLA_NESTED },
};
static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
@@ -36,6 +40,18 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
[IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },
};
static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = {
[IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWML] = { .type = NLA_U32 },
[IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 },
};
static int can_validate_bittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_bittiming)
@@ -70,7 +86,7 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return -EOPNOTSUPP;
}
/* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
/* If one of the CAN_CTRLMODE_{,XL}_TDC_* flags is set then TDC
* must be set and vice-versa
*/
if ((tdc_auto || tdc_manual) && !data_tdc) {
@@ -82,8 +98,8 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return -EOPNOTSUPP;
}
/* If providing TDC parameters, at least TDCO is needed. TDCV
* is needed if and only if CAN_CTRLMODE_TDC_MANUAL is set
/* If providing TDC parameters, at least TDCO is needed. TDCV is
* needed if and only if CAN_CTRLMODE_{,XL}_TDC_MANUAL is set
*/
if (data_tdc) {
struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
@@ -116,6 +132,40 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return 0;
}
static int can_validate_pwm(struct nlattr *data[],
struct netlink_ext_ack *extack, u32 flags)
{
struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
int err;
if (!data[IFLA_CAN_XL_PWM])
return 0;
if (!(flags & CAN_CTRLMODE_XL_TMS)) {
NL_SET_ERR_MSG(extack, "PWM requires TMS");
return -EOPNOTSUPP;
}
err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM],
can_pwm_policy, extack);
if (err)
return err;
if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) {
NL_SET_ERR_MSG(extack,
"Provide either both PWMS and PWML, or none for automatic calculation");
return -EOPNOTSUPP;
}
if (tb_pwm[IFLA_CAN_PWM_PWMO] &&
(!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) {
NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML");
return -EOPNOTSUPP;
}
return 0;
}
static int can_validate_databittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_data_bittiming, u32 flags)
@@ -126,10 +176,10 @@ static int can_validate_databittiming(struct nlattr *data[],
bool is_on;
int err;
/* Make sure that valid CAN FD configurations always consist of
/* Make sure that valid CAN FD/XL configurations always consist of
* - nominal/arbitration bittiming
* - data bittiming
* - control mode with CAN_CTRLMODE_FD set
* - control mode with CAN_CTRLMODE_{FD,XL} set
* - TDC parameters are coherent (details in can_validate_tdc())
*/
@@ -139,7 +189,10 @@ static int can_validate_databittiming(struct nlattr *data[],
is_on = flags & CAN_CTRLMODE_FD;
type = "FD";
} else {
return -EOPNOTSUPP; /* Place holder for CAN XL */
data_tdc = data[IFLA_CAN_XL_TDC];
tdc_flags = flags & CAN_CTRLMODE_XL_TDC_MASK;
is_on = flags & CAN_CTRLMODE_XL;
type = "XL";
}
if (is_on) {
@@ -175,6 +228,32 @@ static int can_validate_databittiming(struct nlattr *data[],
return 0;
}
static int can_validate_xl_flags(struct netlink_ext_ack *extack,
u32 masked_flags, u32 mask)
{
if (masked_flags & CAN_CTRLMODE_XL) {
if (masked_flags & CAN_CTRLMODE_XL_TMS) {
const u32 tms_conflicts_mask = CAN_CTRLMODE_FD |
CAN_CTRLMODE_XL_TDC_MASK;
u32 tms_conflicts = masked_flags & tms_conflicts_mask;
if (tms_conflicts) {
NL_SET_ERR_MSG_FMT(extack,
"TMS and %s are mutually exclusive",
can_get_ctrlmode_str(tms_conflicts));
return -EOPNOTSUPP;
}
}
} else {
if (mask & CAN_CTRLMODE_XL_TMS) {
NL_SET_ERR_MSG(extack, "TMS requires CAN XL");
return -EOPNOTSUPP;
}
}
return 0;
}
static int can_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
@@ -188,6 +267,17 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
flags = cm->flags & cm->mask;
if ((flags & CAN_CTRLMODE_LISTENONLY) &&
(flags & CAN_CTRLMODE_RESTRICTED)) {
NL_SET_ERR_MSG(extack,
"LISTEN-ONLY and RESTRICTED modes are mutually exclusive");
return -EOPNOTSUPP;
}
err = can_validate_xl_flags(extack, flags, cm->mask);
if (err)
return err;
}
err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);
@@ -199,6 +289,15 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
if (err)
return err;
err = can_validate_databittiming(data, extack,
IFLA_CAN_XL_DATA_BITTIMING, flags);
if (err)
return err;
err = can_validate_pwm(data, extack, flags);
if (err)
return err;
return 0;
}
@@ -208,7 +307,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
{
struct can_priv *priv = netdev_priv(dev);
struct can_ctrlmode *cm;
u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
u32 ctrlstatic, maskedflags, deactivated, notsupp, ctrlstatic_missing;
if (!data[IFLA_CAN_CTRLMODE])
return 0;
@@ -220,6 +319,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
ctrlstatic = can_get_static_ctrlmode(priv);
maskedflags = cm->flags & cm->mask;
deactivated = ~cm->flags & cm->mask;
notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;
@@ -241,21 +341,40 @@ static int can_ctrlmode_changelink(struct net_device *dev,
return -EOPNOTSUPP;
}
/* If FD was active and is not turned off, check for XL conflicts */
if (priv->ctrlmode & CAN_CTRLMODE_FD & ~deactivated) {
if (maskedflags & CAN_CTRLMODE_XL_TMS) {
NL_SET_ERR_MSG(extack,
"TMS can not be activated while CAN FD is on");
return -EOPNOTSUPP;
}
}
/* If a top dependency flag is provided, reset all its dependencies */
if (cm->mask & CAN_CTRLMODE_FD)
priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
if (cm->mask & CAN_CTRLMODE_XL)
priv->ctrlmode &= ~(CAN_CTRLMODE_XL_TDC_MASK |
CAN_CTRLMODE_XL_TMS);
/* clear bits to be modified and copy the flag values */
priv->ctrlmode &= ~cm->mask;
priv->ctrlmode |= maskedflags;
/* Wipe potential leftovers from previous CAN FD config */
/* Wipe potential leftovers from previous CAN FD/XL config */
if (!(priv->ctrlmode & CAN_CTRLMODE_FD)) {
memset(&priv->fd.data_bittiming, 0,
sizeof(priv->fd.data_bittiming));
priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
memset(&priv->fd.tdc, 0, sizeof(priv->fd.tdc));
}
if (!(priv->ctrlmode & CAN_CTRLMODE_XL)) {
memset(&priv->xl.data_bittiming, 0,
sizeof(priv->fd.data_bittiming));
priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;
memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));
memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm));
}
can_set_default_mtu(dev);
@@ -330,7 +449,10 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
dbt_params = &priv->fd;
tdc_mask = CAN_CTRLMODE_FD_TDC_MASK;
} else {
return -EOPNOTSUPP; /* Place holder for CAN XL */
data_bittiming = data[IFLA_CAN_XL_DATA_BITTIMING];
data_tdc = data[IFLA_CAN_XL_TDC];
dbt_params = &priv->xl;
tdc_mask = CAN_CTRLMODE_XL_TDC_MASK;
}
if (!data_bittiming)
@@ -366,7 +488,8 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
need_tdc_calc = !(cm->mask & tdc_mask);
if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
need_tdc_calc = !(cm->mask & tdc_mask);
}
if (data_tdc) {
/* TDC parameters are provided: use them */
@@ -381,7 +504,7 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
*/
can_calc_tdco(&dbt_params->tdc, dbt_params->tdc_const, &dbt,
tdc_mask, &priv->ctrlmode, priv->ctrlmode_supported);
} /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly
} /* else: both CAN_CTRLMODE_{,XL}_TDC_{AUTO,MANUAL} are explicitly
* turned off. TDC is disabled: do nothing
*/
@@ -397,6 +520,76 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
return 0;
}
static int can_pwm_changelink(struct net_device *dev,
const struct nlattr *pwm_nla,
struct netlink_ext_ack *extack)
{
struct can_priv *priv = netdev_priv(dev);
const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
struct can_pwm pwm = { 0 };
int err;
if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
return 0;
if (!pwm_const) {
NL_SET_ERR_MSG(extack, "The device does not support PWM");
return -EOPNOTSUPP;
}
if (!pwm_nla)
return can_calc_pwm(dev, extack);
err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla,
can_pwm_policy, extack);
if (err)
return err;
if (tb_pwm[IFLA_CAN_PWM_PWMS]) {
pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]);
if (pwm.pwms < pwm_const->pwms_min ||
pwm.pwms > pwm_const->pwms_max) {
NL_SET_ERR_MSG_FMT(extack,
"PWMS: %u tqmin is out of range: %u...%u",
pwm.pwms, pwm_const->pwms_min,
pwm_const->pwms_max);
return -EINVAL;
}
}
if (tb_pwm[IFLA_CAN_PWM_PWML]) {
pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]);
if (pwm.pwml < pwm_const->pwml_min ||
pwm.pwml > pwm_const->pwml_max) {
NL_SET_ERR_MSG_FMT(extack,
"PWML: %u tqmin is out of range: %u...%u",
pwm.pwml, pwm_const->pwml_min,
pwm_const->pwml_max);
return -EINVAL;
}
}
if (tb_pwm[IFLA_CAN_PWM_PWMO]) {
pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]);
if (pwm.pwmo < pwm_const->pwmo_min ||
pwm.pwmo > pwm_const->pwmo_max) {
NL_SET_ERR_MSG_FMT(extack,
"PWMO: %u tqmin is out of range: %u...%u",
pwm.pwmo, pwm_const->pwmo_min,
pwm_const->pwmo_max);
return -EINVAL;
}
}
err = can_validate_pwm_bittiming(dev, &pwm, extack);
if (err)
return err;
priv->xl.pwm = pwm;
return 0;
}
static int can_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
@@ -486,6 +679,14 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
if (err)
return err;
/* CAN XL */
err = can_dbt_changelink(dev, data, false, extack);
if (err)
return err;
err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], extack);
if (err)
return err;
if (data[IFLA_CAN_TERMINATION]) {
const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
const unsigned int num_term = priv->termination_const_cnt;
@@ -553,14 +754,14 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para
{
size_t size = 0;
if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_{,XL}_DATA_BITTIMING */
size += nla_total_size(sizeof(dbt_params->data_bittiming));
if (dbt_params->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
if (dbt_params->data_bittiming_const) /* IFLA_CAN_{,XL}_DATA_BITTIMING_CONST */
size += nla_total_size(sizeof(*dbt_params->data_bittiming_const));
if (dbt_params->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
if (dbt_params->data_bitrate_const) /* IFLA_CAN_{,XL}_DATA_BITRATE_CONST */
size += nla_total_size(sizeof(*dbt_params->data_bitrate_const) *
dbt_params->data_bitrate_const_cnt);
size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_TDC */
size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */
return size;
}
@@ -571,6 +772,30 @@ static size_t can_ctrlmode_ext_get_size(void)
nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */
}
static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const,
bool pwm_on)
{
size_t size;
if (!pwm_const || !pwm_on)
return 0;
size = nla_total_size(0); /* nest IFLA_CAN_PWM */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MAX */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MAX */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MAX */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO */
return size;
}
static size_t can_get_size(const struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -600,6 +825,11 @@ static size_t can_get_size(const struct net_device *dev)
size += can_data_bittiming_get_size(&priv->fd,
priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
size += can_data_bittiming_get_size(&priv->xl,
priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */
priv->ctrlmode & CAN_CTRLMODE_XL_TMS);
return size;
}
@@ -644,7 +874,9 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
tdc_is_enabled = can_fd_tdc_is_enabled(priv);
tdc_manual = priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL;
} else {
return -EOPNOTSUPP; /* Place holder for CAN XL */
dbt_params = &priv->xl;
tdc_is_enabled = can_xl_tdc_is_enabled(priv);
tdc_manual = priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MANUAL;
}
tdc_const = dbt_params->tdc_const;
tdc = &dbt_params->tdc;
@@ -695,6 +927,42 @@ err_cancel:
return -EMSGSIZE;
}
static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv)
{
const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
const struct can_pwm *pwm = &priv->xl.pwm;
struct nlattr *nest;
if (!pwm_const)
return 0;
nest = nla_nest_start(skb, IFLA_CAN_XL_PWM);
if (!nest)
return -EMSGSIZE;
if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max))
goto err_cancel;
if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) ||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo))
goto err_cancel;
}
nla_nest_end(skb, nest);
return 0;
err_cancel:
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,
const struct can_priv *priv)
{
@@ -766,9 +1034,22 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) ||
can_ctrlmode_ext_fill_info(skb, priv)
)
can_ctrlmode_ext_fill_info(skb, priv) ||
can_bittiming_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING,
&priv->xl.data_bittiming) ||
can_bittiming_const_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING_CONST,
priv->xl.data_bittiming_const) ||
can_bitrate_const_fill_info(skb, IFLA_CAN_XL_DATA_BITRATE_CONST,
priv->xl.data_bitrate_const,
priv->xl.data_bitrate_const_cnt) ||
can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) ||
can_pwm_fill_info(skb, priv)
)
return -EMSGSIZE;
return 0;

285
drivers/net/can/dummy_can.c Normal file
View File

@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org> */
#include <linux/array_size.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/units.h>
#include <linux/string_choices.h>
#include <linux/can.h>
#include <linux/can/bittiming.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
struct dummy_can {
struct can_priv can;
struct net_device *dev;
};
static struct dummy_can *dummy_can;
static const struct can_bittiming_const dummy_can_bittiming_const = {
.name = "dummy_can CC",
.tseg1_min = 2,
.tseg1_max = 256,
.tseg2_min = 2,
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1
};
static const struct can_bittiming_const dummy_can_fd_databittiming_const = {
.name = "dummy_can FD",
.tseg1_min = 2,
.tseg1_max = 256,
.tseg2_min = 2,
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1
};
static const struct can_tdc_const dummy_can_fd_tdc_const = {
.tdcv_min = 0,
.tdcv_max = 0, /* Manual mode not supported. */
.tdco_min = 0,
.tdco_max = 127,
.tdcf_min = 0,
.tdcf_max = 127
};
static const struct can_bittiming_const dummy_can_xl_databittiming_const = {
.name = "dummy_can XL",
.tseg1_min = 2,
.tseg1_max = 256,
.tseg2_min = 2,
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1
};
static const struct can_tdc_const dummy_can_xl_tdc_const = {
.tdcv_min = 0,
.tdcv_max = 0, /* Manual mode not supported. */
.tdco_min = 0,
.tdco_max = 127,
.tdcf_min = 0,
.tdcf_max = 127
};
static const struct can_pwm_const dummy_can_pwm_const = {
.pwms_min = 1,
.pwms_max = 8,
.pwml_min = 2,
.pwml_max = 24,
.pwmo_min = 0,
.pwmo_max = 16,
};
static void dummy_can_print_bittiming(struct net_device *dev,
struct can_bittiming *bt)
{
netdev_dbg(dev, "\tbitrate: %u\n", bt->bitrate);
netdev_dbg(dev, "\tsample_point: %u\n", bt->sample_point);
netdev_dbg(dev, "\ttq: %u\n", bt->tq);
netdev_dbg(dev, "\tprop_seg: %u\n", bt->prop_seg);
netdev_dbg(dev, "\tphase_seg1: %u\n", bt->phase_seg1);
netdev_dbg(dev, "\tphase_seg2: %u\n", bt->phase_seg2);
netdev_dbg(dev, "\tsjw: %u\n", bt->sjw);
netdev_dbg(dev, "\tbrp: %u\n", bt->brp);
}
static void dummy_can_print_tdc(struct net_device *dev, struct can_tdc *tdc)
{
netdev_dbg(dev, "\t\ttdcv: %u\n", tdc->tdcv);
netdev_dbg(dev, "\t\ttdco: %u\n", tdc->tdco);
netdev_dbg(dev, "\t\ttdcf: %u\n", tdc->tdcf);
}
static void dummy_can_print_pwm(struct net_device *dev, struct can_pwm *pwm,
struct can_bittiming *dbt)
{
netdev_dbg(dev, "\t\tpwms: %u\n", pwm->pwms);
netdev_dbg(dev, "\t\tpwml: %u\n", pwm->pwml);
netdev_dbg(dev, "\t\tpwmo: %u\n", pwm->pwmo);
}
static void dummy_can_print_ctrlmode(struct net_device *dev)
{
struct dummy_can *priv = netdev_priv(dev);
struct can_priv *can_priv = &priv->can;
unsigned long supported = can_priv->ctrlmode_supported;
u32 enabled = can_priv->ctrlmode;
netdev_dbg(dev, "Control modes:\n");
netdev_dbg(dev, "\tsupported: 0x%08x\n", (u32)supported);
netdev_dbg(dev, "\tenabled: 0x%08x\n", enabled);
if (supported) {
int idx;
netdev_dbg(dev, "\tlist:");
for_each_set_bit(idx, &supported, BITS_PER_TYPE(u32))
netdev_dbg(dev, "\t\t%s: %s\n",
can_get_ctrlmode_str(BIT(idx)),
enabled & BIT(idx) ? "on" : "off");
}
}
static void dummy_can_print_bittiming_info(struct net_device *dev)
{
struct dummy_can *priv = netdev_priv(dev);
struct can_priv *can_priv = &priv->can;
netdev_dbg(dev, "Clock frequency: %u\n", can_priv->clock.freq);
netdev_dbg(dev, "Maximum bitrate: %u\n", can_priv->bitrate_max);
netdev_dbg(dev, "MTU: %u\n", dev->mtu);
netdev_dbg(dev, "\n");
dummy_can_print_ctrlmode(dev);
netdev_dbg(dev, "\n");
netdev_dbg(dev, "Classical CAN nominal bittiming:\n");
dummy_can_print_bittiming(dev, &can_priv->bittiming);
netdev_dbg(dev, "\n");
if (can_priv->ctrlmode & CAN_CTRLMODE_FD) {
netdev_dbg(dev, "CAN FD databittiming:\n");
dummy_can_print_bittiming(dev, &can_priv->fd.data_bittiming);
if (can_fd_tdc_is_enabled(can_priv)) {
netdev_dbg(dev, "\tCAN FD TDC:\n");
dummy_can_print_tdc(dev, &can_priv->fd.tdc);
}
}
netdev_dbg(dev, "\n");
if (can_priv->ctrlmode & CAN_CTRLMODE_XL) {
netdev_dbg(dev, "CAN XL databittiming:\n");
dummy_can_print_bittiming(dev, &can_priv->xl.data_bittiming);
if (can_xl_tdc_is_enabled(can_priv)) {
netdev_dbg(dev, "\tCAN XL TDC:\n");
dummy_can_print_tdc(dev, &can_priv->xl.tdc);
}
if (can_priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
netdev_dbg(dev, "\tCAN XL PWM:\n");
dummy_can_print_pwm(dev, &can_priv->xl.pwm,
&can_priv->xl.data_bittiming);
}
}
netdev_dbg(dev, "\n");
}
static int dummy_can_netdev_open(struct net_device *dev)
{
int ret;
struct can_priv *priv = netdev_priv(dev);
dummy_can_print_bittiming_info(dev);
netdev_dbg(dev, "error-signalling is %s\n",
str_enabled_disabled(!can_dev_in_xl_only_mode(priv)));
ret = open_candev(dev);
if (ret)
return ret;
netif_start_queue(dev);
netdev_dbg(dev, "dummy-can is up\n");
return 0;
}
static int dummy_can_netdev_close(struct net_device *dev)
{
netif_stop_queue(dev);
close_candev(dev);
netdev_dbg(dev, "dummy-can is down\n");
return 0;
}
static netdev_tx_t dummy_can_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
if (can_dev_dropped_skb(dev, skb))
return NETDEV_TX_OK;
can_put_echo_skb(skb, dev, 0, 0);
dev->stats.tx_packets++;
dev->stats.tx_bytes += can_get_echo_skb(dev, 0, NULL);
return NETDEV_TX_OK;
}
static const struct net_device_ops dummy_can_netdev_ops = {
.ndo_open = dummy_can_netdev_open,
.ndo_stop = dummy_can_netdev_close,
.ndo_start_xmit = dummy_can_start_xmit,
};
static const struct ethtool_ops dummy_can_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
};
static int __init dummy_can_init(void)
{
struct net_device *dev;
struct dummy_can *priv;
int ret;
dev = alloc_candev(sizeof(*priv), 1);
if (!dev)
return -ENOMEM;
dev->netdev_ops = &dummy_can_netdev_ops;
dev->ethtool_ops = &dummy_can_ethtool_ops;
priv = netdev_priv(dev);
priv->can.bittiming_const = &dummy_can_bittiming_const;
priv->can.bitrate_max = 20 * MEGA /* BPS */;
priv->can.clock.freq = 160 * MEGA /* Hz */;
priv->can.fd.data_bittiming_const = &dummy_can_fd_databittiming_const;
priv->can.fd.tdc_const = &dummy_can_fd_tdc_const;
priv->can.xl.data_bittiming_const = &dummy_can_xl_databittiming_const;
priv->can.xl.tdc_const = &dummy_can_xl_tdc_const;
priv->can.xl.pwm_const = &dummy_can_pwm_const;
priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_FD | CAN_CTRLMODE_TDC_AUTO |
CAN_CTRLMODE_RESTRICTED | CAN_CTRLMODE_XL |
CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TMS;
priv->dev = dev;
ret = register_candev(priv->dev);
if (ret) {
free_candev(priv->dev);
return ret;
}
dummy_can = priv;
netdev_dbg(dev, "dummy-can ready\n");
return 0;
}
static void __exit dummy_can_exit(void)
{
struct net_device *dev = dummy_can->dev;
netdev_dbg(dev, "dummy-can bye bye\n");
unregister_candev(dev);
free_candev(dev);
}
module_init(dummy_can_init);
module_exit(dummy_can_exit);
MODULE_DESCRIPTION("A dummy CAN driver, mainly to test the netlink interface");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vincent Mailhol <mailhol@kernel.org>");

View File

@@ -468,6 +468,7 @@ struct rcar_canfd_global {
struct platform_device *pdev; /* Respective platform device */
struct clk *clkp; /* Peripheral clock */
struct clk *can_clk; /* fCAN clock */
struct clk *clk_ram; /* Clock RAM */
unsigned long channels_mask; /* Enabled channels mask */
bool extclk; /* CANFD or Ext clock */
bool fdmode; /* CAN FD or Classical CAN only mode */
@@ -1569,8 +1570,8 @@ static int rcar_canfd_close(struct net_device *ndev)
netif_stop_queue(ndev);
rcar_canfd_stop(ndev);
napi_disable(&priv->napi);
clk_disable_unprepare(gpriv->can_clk);
close_candev(ndev);
clk_disable_unprepare(gpriv->can_clk);
phy_power_off(priv->transceiver);
return 0;
}
@@ -1960,22 +1961,120 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch)
}
}
static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
{
struct device *dev = &gpriv->pdev->dev;
u32 rule_entry = 0;
u32 ch, sts;
int err;
err = reset_control_reset(gpriv->rstc1);
if (err)
return err;
err = reset_control_reset(gpriv->rstc2);
if (err)
goto fail_reset1;
/* Enable peripheral clock for register access */
err = clk_prepare_enable(gpriv->clkp);
if (err) {
dev_err(dev, "failed to enable peripheral clock: %pe\n",
ERR_PTR(err));
goto fail_reset2;
}
/* Enable RAM clock */
err = clk_prepare_enable(gpriv->clk_ram);
if (err) {
dev_err(dev,
"failed to enable RAM clock, error %d\n", err);
goto fail_clk;
}
err = rcar_canfd_reset_controller(gpriv);
if (err) {
dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
goto fail_ram_clk;
}
/* Controller in Global reset & Channel reset mode */
rcar_canfd_configure_controller(gpriv);
/* Configure per channel attributes */
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
/* Configure Channel's Rx fifo */
rcar_canfd_configure_rx(gpriv, ch);
/* Configure Channel's Tx (Common) fifo */
rcar_canfd_configure_tx(gpriv, ch);
/* Configure receive rules */
rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
rule_entry += RCANFD_CHANNEL_NUMRULES;
}
/* Configure common interrupts */
rcar_canfd_enable_global_interrupts(gpriv);
/* Start Global operation mode */
rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
RCANFD_GCTR_GMDC_GOPM);
/* Verify mode change */
err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
!(sts & RCANFD_GSTS_GNOPM), 2, 500000);
if (err) {
dev_err(dev, "global operational mode failed\n");
goto fail_mode;
}
return 0;
fail_mode:
rcar_canfd_disable_global_interrupts(gpriv);
fail_ram_clk:
clk_disable_unprepare(gpriv->clk_ram);
fail_clk:
clk_disable_unprepare(gpriv->clkp);
fail_reset2:
reset_control_assert(gpriv->rstc2);
fail_reset1:
reset_control_assert(gpriv->rstc1);
return err;
}
static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full)
{
rcar_canfd_disable_global_interrupts(gpriv);
if (full) {
rcar_canfd_reset_controller(gpriv);
/* Enter global sleep mode */
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
}
clk_disable_unprepare(gpriv->clk_ram);
clk_disable_unprepare(gpriv->clkp);
reset_control_assert(gpriv->rstc2);
reset_control_assert(gpriv->rstc1);
}
static int rcar_canfd_probe(struct platform_device *pdev)
{
struct phy *transceivers[RCANFD_NUM_CHANNELS] = { NULL, };
const struct rcar_canfd_hw_info *info;
struct device *dev = &pdev->dev;
void __iomem *addr;
u32 sts, ch, fcan_freq;
struct rcar_canfd_global *gpriv;
struct device_node *of_child;
unsigned long channels_mask = 0;
int err, ch_irq, g_irq;
int g_err_irq, g_recc_irq;
u32 rule_entry = 0;
bool fdmode = true; /* CAN FD only mode - default */
char name[9] = "channelX";
struct clk *clk_ram;
u32 ch, fcan_freq;
int i;
info = of_device_get_match_data(dev);
@@ -2065,10 +2164,10 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->extclk = gpriv->info->external_clk;
}
clk_ram = devm_clk_get_optional_enabled(dev, "ram_clk");
if (IS_ERR(clk_ram))
return dev_err_probe(dev, PTR_ERR(clk_ram),
"cannot get enabled ram clock\n");
gpriv->clk_ram = devm_clk_get_optional(dev, "ram_clk");
if (IS_ERR(gpriv->clk_ram))
return dev_err_probe(dev, PTR_ERR(gpriv->clk_ram),
"cannot get ram clock\n");
addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(addr)) {
@@ -2117,59 +2216,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
}
}
err = reset_control_reset(gpriv->rstc1);
err = rcar_canfd_global_init(gpriv);
if (err)
goto fail_dev;
err = reset_control_reset(gpriv->rstc2);
if (err) {
reset_control_assert(gpriv->rstc1);
goto fail_dev;
}
/* Enable peripheral clock for register access */
err = clk_prepare_enable(gpriv->clkp);
if (err) {
dev_err(dev, "failed to enable peripheral clock: %pe\n",
ERR_PTR(err));
goto fail_reset;
}
err = rcar_canfd_reset_controller(gpriv);
if (err) {
dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
goto fail_clk;
}
/* Controller in Global reset & Channel reset mode */
rcar_canfd_configure_controller(gpriv);
/* Configure per channel attributes */
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
/* Configure Channel's Rx fifo */
rcar_canfd_configure_rx(gpriv, ch);
/* Configure Channel's Tx (Common) fifo */
rcar_canfd_configure_tx(gpriv, ch);
/* Configure receive rules */
rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
rule_entry += RCANFD_CHANNEL_NUMRULES;
}
/* Configure common interrupts */
rcar_canfd_enable_global_interrupts(gpriv);
/* Start Global operation mode */
rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
RCANFD_GCTR_GMDC_GOPM);
/* Verify mode change */
err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
!(sts & RCANFD_GSTS_GNOPM), 2, 500000);
if (err) {
dev_err(dev, "global operational mode failed\n");
goto fail_mode;
}
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq,
@@ -2188,12 +2237,7 @@ fail_channel:
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels)
rcar_canfd_channel_remove(gpriv, ch);
fail_mode:
rcar_canfd_disable_global_interrupts(gpriv);
fail_clk:
clk_disable_unprepare(gpriv->clkp);
fail_reset:
reset_control_assert(gpriv->rstc1);
reset_control_assert(gpriv->rstc2);
rcar_canfd_global_deinit(gpriv, false);
fail_dev:
return err;
}
@@ -2203,33 +2247,79 @@ static void rcar_canfd_remove(struct platform_device *pdev)
struct rcar_canfd_global *gpriv = platform_get_drvdata(pdev);
u32 ch;
rcar_canfd_reset_controller(gpriv);
rcar_canfd_disable_global_interrupts(gpriv);
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]);
rcar_canfd_channel_remove(gpriv, ch);
}
/* Enter global sleep mode */
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
clk_disable_unprepare(gpriv->clkp);
reset_control_assert(gpriv->rstc1);
reset_control_assert(gpriv->rstc2);
rcar_canfd_global_deinit(gpriv, true);
}
static int __maybe_unused rcar_canfd_suspend(struct device *dev)
static int rcar_canfd_suspend(struct device *dev)
{
struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
int err;
u32 ch;
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
struct rcar_canfd_channel *priv = gpriv->ch[ch];
struct net_device *ndev = priv->ndev;
if (!netif_running(ndev))
continue;
netif_device_detach(ndev);
err = rcar_canfd_close(ndev);
if (err) {
netdev_err(ndev, "rcar_canfd_close() failed %pe\n",
ERR_PTR(err));
return err;
}
priv->can.state = CAN_STATE_SLEEPING;
}
/* TODO Skip if wake-up (which is not yet supported) is enabled */
rcar_canfd_global_deinit(gpriv, false);
return 0;
}
static int __maybe_unused rcar_canfd_resume(struct device *dev)
static int rcar_canfd_resume(struct device *dev)
{
struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
int err;
u32 ch;
err = rcar_canfd_global_init(gpriv);
if (err) {
dev_err(dev, "rcar_canfd_global_init() failed %pe\n", ERR_PTR(err));
return err;
}
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
struct rcar_canfd_channel *priv = gpriv->ch[ch];
struct net_device *ndev = priv->ndev;
if (!netif_running(ndev))
continue;
err = rcar_canfd_open(ndev);
if (err) {
netdev_err(ndev, "rcar_canfd_open() failed %pe\n",
ERR_PTR(err));
return err;
}
netif_device_attach(ndev);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
rcar_canfd_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
rcar_canfd_resume);
static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = {
{ .compatible = "renesas,r8a779a0-canfd", .data = &rcar_gen4_hw_info },
@@ -2246,7 +2336,7 @@ static struct platform_driver rcar_canfd_driver = {
.driver = {
.name = RCANFD_DRV_NAME,
.of_match_table = of_match_ptr(rcar_canfd_of_table),
.pm = &rcar_canfd_pm_ops,
.pm = pm_sleep_ptr(&rcar_canfd_pm_ops),
},
.probe = rcar_canfd_probe,
.remove = rcar_canfd_remove,

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
* Copyright (c) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
* Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
*/
#ifndef _CAN_BITTIMING_H
@@ -16,10 +16,12 @@
#define CAN_CTRLMODE_FD_TDC_MASK \
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_TDC_MANUAL)
#define CAN_CTRLMODE_XL_TDC_MASK \
(CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TDC_MANUAL)
#define CAN_CTRLMODE_TDC_AUTO_MASK \
(CAN_CTRLMODE_TDC_AUTO)
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_XL_TDC_AUTO)
#define CAN_CTRLMODE_TDC_MANUAL_MASK \
(CAN_CTRLMODE_TDC_MANUAL)
(CAN_CTRLMODE_TDC_MANUAL | CAN_CTRLMODE_XL_TDC_MANUAL)
/*
* struct can_tdc - CAN FD Transmission Delay Compensation parameters
@@ -85,6 +87,11 @@ struct can_tdc {
u32 tdcf;
};
/* The transceiver decoding margin corresponds to t_Decode in ISO 11898-2 */
#define CAN_PWM_DECODE_NS 5
/* Maximum PWM symbol duration. Corresponds to t_SymbolNom_MAX - t_Decode */
#define CAN_PWM_NS_MAX (205 - CAN_PWM_DECODE_NS)
/*
* struct can_tdc_const - CAN hardware-dependent constant for
* Transmission Delay Compensation
@@ -118,11 +125,48 @@ struct can_tdc_const {
u32 tdcf_max;
};
/*
* struct can_pwm - CAN Pulse-Width Modulation (PWM) parameters
*
* @pwms: pulse width modulation short phase
* @pwml: pulse width modulation long phase
* @pwmo: pulse width modulation offset
*/
struct can_pwm {
u32 pwms;
u32 pwml;
u32 pwmo;
};
/*
* struct can_pwm - CAN hardware-dependent constants for Pulse-Width
* Modulation (PWM)
*
* @pwms_min: PWM short phase minimum value. Must be at least 1.
* @pwms_max: PWM short phase maximum value
* @pwml_min: PWM long phase minimum value. Must be at least 1.
* @pwml_max: PWM long phase maximum value
* @pwmo_min: PWM offset phase minimum value
* @pwmo_max: PWM offset phase maximum value
*/
struct can_pwm_const {
u32 pwms_min;
u32 pwms_max;
u32 pwml_min;
u32 pwml_max;
u32 pwmo_min;
u32 pwmo_max;
};
struct data_bittiming_params {
const struct can_bittiming_const *data_bittiming_const;
struct can_bittiming data_bittiming;
const struct can_tdc_const *tdc_const;
struct can_tdc tdc;
const struct can_pwm_const *pwm_const;
union {
struct can_tdc tdc;
struct can_pwm pwm;
};
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
int (*do_set_data_bittiming)(struct net_device *dev);
@@ -136,12 +180,14 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
const struct can_bittiming *dbt,
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported);
int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack);
#else /* !CONFIG_CAN_CALC_BITTIMING */
static inline int
can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc, struct netlink_ext_ack *extack)
{
netdev_err(dev, "bit-timing calculation not available\n");
NL_SET_ERR_MSG(extack, "bit-timing calculation not available\n");
return -EINVAL;
}
@@ -151,6 +197,14 @@ can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported)
{
}
static inline int
can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
{
NL_SET_ERR_MSG(extack,
"bit-timing calculation not available: manually provide PWML and PWMS\n");
return -EINVAL;
}
#endif /* CONFIG_CAN_CALC_BITTIMING */
void can_sjw_set_default(struct can_bittiming *bt);
@@ -164,6 +218,10 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const unsigned int bitrate_const_cnt,
struct netlink_ext_ack *extack);
int can_validate_pwm_bittiming(const struct net_device *dev,
const struct can_pwm *pwm,
struct netlink_ext_ack *extack);
/*
* can_get_relative_tdco() - TDCO relative to the sample point
*
@@ -206,4 +264,17 @@ static inline unsigned int can_bit_time(const struct can_bittiming *bt)
return CAN_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
}
/* Duration of one bit in minimum time quantum */
static inline unsigned int can_bit_time_tqmin(const struct can_bittiming *bt)
{
return can_bit_time(bt) * bt->brp;
}
/* Convert a duration from minimum a minimum time quantum to nano seconds */
static inline u32 can_tqmin_to_ns(u32 tqmin, u32 clock_freq)
{
return DIV_U64_ROUND_CLOSEST(mul_u32_u32(tqmin, NSEC_PER_SEC),
clock_freq);
}
#endif /* !_CAN_BITTIMING_H */

View File

@@ -47,7 +47,7 @@ struct can_priv {
const struct can_bittiming_const *bittiming_const;
struct can_bittiming bittiming;
struct data_bittiming_params fd;
struct data_bittiming_params fd, xl;
unsigned int bitrate_const_cnt;
const u32 *bitrate_const;
u32 bitrate_max;
@@ -85,6 +85,11 @@ static inline bool can_fd_tdc_is_enabled(const struct can_priv *priv)
return !!(priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
}
static inline bool can_xl_tdc_is_enabled(const struct can_priv *priv)
{
return !!(priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
}
static inline u32 can_get_static_ctrlmode(struct can_priv *priv)
{
return priv->ctrlmode & ~priv->ctrlmode_supported;
@@ -95,22 +100,6 @@ static inline bool can_is_canxl_dev_mtu(unsigned int mtu)
return (mtu >= CANXL_MIN_MTU && mtu <= CANXL_MAX_MTU);
}
/* drop skb if it does not contain a valid CAN frame for sending */
static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
{
struct can_priv *priv = netdev_priv(dev);
if (priv->ctrlmode & CAN_CTRLMODE_LISTENONLY) {
netdev_info_once(dev,
"interface in listen only mode, dropping skb\n");
kfree_skb(skb);
dev->stats.tx_dropped++;
return true;
}
return can_dropped_invalid_skb(dev, skb);
}
void can_setup(struct net_device *dev);
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
@@ -146,6 +135,51 @@ void can_bus_off(struct net_device *dev);
const char *can_get_state_str(const enum can_state state);
const char *can_get_ctrlmode_str(u32 ctrlmode);
static inline bool can_dev_in_xl_only_mode(struct can_priv *priv)
{
const u32 mixed_mode = CAN_CTRLMODE_FD | CAN_CTRLMODE_XL;
/* When CAN XL is enabled but FD is disabled we are running in
* the so-called 'CANXL-only mode' where the error signalling is
* disabled. This helper function determines the required value
* to disable error signalling in the CAN XL controller.
* The so-called CC/FD/XL 'mixed mode' requires error signalling.
*/
return ((priv->ctrlmode & mixed_mode) == CAN_CTRLMODE_XL);
}
/* drop skb if it does not contain a valid CAN frame for sending */
static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
{
struct can_priv *priv = netdev_priv(dev);
u32 silent_mode = priv->ctrlmode & (CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_RESTRICTED);
if (silent_mode) {
netdev_info_once(dev, "interface in %s mode, dropping skb\n",
can_get_ctrlmode_str(silent_mode));
goto invalid_skb;
}
if (!(priv->ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
netdev_info_once(dev, "CAN FD is disabled, dropping skb\n");
goto invalid_skb;
}
if (can_dev_in_xl_only_mode(priv) && !can_is_canxl_skb(skb)) {
netdev_info_once(dev,
"Error signaling is disabled, dropping skb\n");
goto invalid_skb;
}
return can_dropped_invalid_skb(dev, skb);
invalid_skb:
kfree_skb(skb);
dev->stats.tx_dropped++;
return true;
}
void can_state_get_by_berr_counter(const struct net_device *dev,
const struct can_berr_counter *bec,
enum can_state *tx_state,

View File

@@ -5,6 +5,7 @@
* Definitions for the CAN netlink interface
*
* Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
@@ -103,6 +104,11 @@ struct can_ctrlmode {
#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
#define CAN_CTRLMODE_TDC_AUTO 0x200 /* FD transceiver automatically calculates TDCV */
#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* FD TDCV is manually set up by user */
#define CAN_CTRLMODE_RESTRICTED 0x800 /* Restricted operation mode */
#define CAN_CTRLMODE_XL 0x1000 /* CAN XL mode */
#define CAN_CTRLMODE_XL_TDC_AUTO 0x2000 /* XL transceiver automatically calculates TDCV */
#define CAN_CTRLMODE_XL_TDC_MANUAL 0x4000 /* XL TDCV is manually set up by user */
#define CAN_CTRLMODE_XL_TMS 0x8000 /* Transceiver Mode Switching */
/*
* CAN device statistics
@@ -138,6 +144,11 @@ enum {
IFLA_CAN_BITRATE_MAX,
IFLA_CAN_TDC, /* FD */
IFLA_CAN_CTRLMODE_EXT,
IFLA_CAN_XL_DATA_BITTIMING,
IFLA_CAN_XL_DATA_BITTIMING_CONST,
IFLA_CAN_XL_DATA_BITRATE_CONST,
IFLA_CAN_XL_TDC,
IFLA_CAN_XL_PWM,
/* add new constants above here */
__IFLA_CAN_MAX,
@@ -179,6 +190,29 @@ enum {
IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1
};
/*
* CAN FD/XL Pulse-Width Modulation (PWM)
*
* Please refer to struct can_pwm_const and can_pwm in
* include/linux/can/bittiming.h for further details.
*/
enum {
IFLA_CAN_PWM_UNSPEC,
IFLA_CAN_PWM_PWMS_MIN, /* u32 */
IFLA_CAN_PWM_PWMS_MAX, /* u32 */
IFLA_CAN_PWM_PWML_MIN, /* u32 */
IFLA_CAN_PWM_PWML_MAX, /* u32 */
IFLA_CAN_PWM_PWMO_MIN, /* u32 */
IFLA_CAN_PWM_PWMO_MAX, /* u32 */
IFLA_CAN_PWM_PWMS, /* u32 */
IFLA_CAN_PWM_PWML, /* u32 */
IFLA_CAN_PWM_PWMO, /* u32 */
/* add new constants above here */
__IFLA_CAN_PWM,
IFLA_CAN_PWM_MAX = __IFLA_CAN_PWM - 1
};
/* u16 termination range: 1..65535 Ohms */
#define CAN_TERMINATION_DISABLED 0

View File

@@ -892,20 +892,58 @@ static void raw_put_canxl_vcid(struct raw_sock *ro, struct sk_buff *skb)
}
}
static unsigned int raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
static inline bool raw_dev_cc_enabled(struct net_device *dev,
struct can_priv *priv)
{
/* Classical CAN -> no checks for flags and device capabilities */
if (can_is_can_skb(skb))
/* The CANXL-only mode disables error-signalling on the CAN bus
* which is needed to send CAN CC/FD frames
*/
if (priv)
return !can_dev_in_xl_only_mode(priv);
/* virtual CAN interfaces always support CAN CC */
return true;
}
static inline bool raw_dev_fd_enabled(struct net_device *dev,
struct can_priv *priv)
{
/* check FD ctrlmode on real CAN interfaces */
if (priv)
return (priv->ctrlmode & CAN_CTRLMODE_FD);
/* check MTU for virtual CAN FD interfaces */
return (READ_ONCE(dev->mtu) >= CANFD_MTU);
}
static inline bool raw_dev_xl_enabled(struct net_device *dev,
struct can_priv *priv)
{
/* check XL ctrlmode on real CAN interfaces */
if (priv)
return (priv->ctrlmode & CAN_CTRLMODE_XL);
/* check MTU for virtual CAN XL interfaces */
return can_is_canxl_dev_mtu(READ_ONCE(dev->mtu));
}
static unsigned int raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb,
struct net_device *dev)
{
struct can_priv *priv = safe_candev_priv(dev);
/* Classical CAN */
if (can_is_can_skb(skb) && raw_dev_cc_enabled(dev, priv))
return CAN_MTU;
/* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
/* CAN FD */
if (ro->fd_frames && can_is_canfd_skb(skb) &&
(mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
raw_dev_fd_enabled(dev, priv))
return CANFD_MTU;
/* CAN XL -> needs to be enabled and a CAN XL device */
/* CAN XL */
if (ro->xl_frames && can_is_canxl_skb(skb) &&
can_is_canxl_dev_mtu(mtu))
raw_dev_xl_enabled(dev, priv))
return CANXL_MTU;
return 0;
@@ -961,7 +999,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
err = -EINVAL;
/* check for valid CAN (CC/FD/XL) frame content */
txmtu = raw_check_txframe(ro, skb, READ_ONCE(dev->mtu));
txmtu = raw_check_txframe(ro, skb, dev);
if (!txmtu)
goto free_skb;