linux-can-fixes-for-6.18-20251014

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEn/sM2K9nqF/8FWzzDHRl3/mQkZwFAmjuAZATHG1rbEBwZW5n
 dXRyb25peC5kZQAKCRAMdGXf+ZCRnPeECACtwzFozRla2Y+WTR7+BZiBlRtcWZ8d
 aNHrvtjefaX4TkgEVgC9Qt+VI7Wzv2TDlVWIWZnm3lotufmQsbRdDdEyfeRHPR7m
 9yZcqRvLGQ17LDnC13W66YaZXhhz263rvCTwzcLyuB7tnO+zyYfakTZfR+xtfJ3x
 LBT6yNVlujy+/I4NCRNwlLzJc5fdGTKaSbt8ECf7qygcbQfZ/AcLUV9/AIjweVP1
 Tcp2qumrM+HIU0lPQzfyiEGJn/weLRfajVbzcsv7NNc9vtlP2Ayi0LnLoXl98Pfk
 00nJVXmV6rAJCm/LqDKoWvP98jEL4B41+06RRwsm5sJQ7caRS+knuVVn
 =Wml2
 -----END PGP SIGNATURE-----

Merge tag 'linux-can-fixes-for-6.18-20251014' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can

Marc Kleine-Budde says:

====================
pull-request: can 2025-10-14

The first 2 paches are by Celeste Liu and target the gS_usb driver.
The first patch remove the limitation to 3 CAN interface per USB
device. The second patch adds the missing population of
net_device->dev_port.

The next 4 patches are by me and fix the m_can driver. They add a
missing pm_runtime_disable(), fix the CAN state transition back to
Error Active and fix the state after ifup and suspend/resume.

Another patch by me targets the m_can driver, too and replaces Dong
Aisheng's old email address.

The next 2 patches are by Vincent Mailhol and update the CAN
networking Documentation.

Tetsuo Handa contributes the last patch that add missing cleanup calls
in the NETDEV_UNREGISTER notification handler.

* tag 'linux-can-fixes-for-6.18-20251014' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can:
  can: j1939: add missing calls in NETDEV_UNREGISTER notification handler
  can: add Transmitter Delay Compensation (TDC) documentation
  can: remove false statement about 1:1 mapping between DLC and length
  can: m_can: replace Dong Aisheng's old email address
  can: m_can: fix CAN state in system PM
  can: m_can: m_can_chip_config(): bring up interface in correct state
  can: m_can: m_can_handle_state_errors(): fix CAN state transition to Error Active
  can: m_can: m_can_plat_remove(): add missing pm_runtime_disable()
  can: gs_usb: gs_make_candev(): populate net_device->dev_port
  can: gs_usb: increase max interface to U8_MAX
====================

Link: https://patch.msgid.link/20251014122140.990472-1-mkl@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-10-15 17:56:20 -07:00
commit 5e655aadda
6 changed files with 123 additions and 48 deletions

View File

@ -227,6 +227,7 @@ Dmitry Safonov <0x7f454c46@gmail.com> <dima@arista.com>
Dmitry Safonov <0x7f454c46@gmail.com> <d.safonov@partner.samsung.com>
Dmitry Safonov <0x7f454c46@gmail.com> <dsafonov@virtuozzo.com>
Domen Puncer <domen@coderock.org>
Dong Aisheng <aisheng.dong@nxp.com> <b29396@freescale.com>
Douglas Gilbert <dougg@torque.net>
Drew Fustini <fustini@kernel.org> <drew@pdp7.com>
<duje@dujemihanovic.xyz> <duje.mihanovic@skole.hr>

View File

@ -1398,10 +1398,9 @@ second bit timing has to be specified in order to enable the CAN FD bitrate.
Additionally CAN FD capable CAN controllers support up to 64 bytes of
payload. The representation of this length in can_frame.len and
canfd_frame.len for userspace applications and inside the Linux network
layer is a plain value from 0 .. 64 instead of the CAN 'data length code'.
The data length code was a 1:1 mapping to the payload length in the Classical
CAN frames anyway. The payload length to the bus-relevant DLC mapping is
only performed inside the CAN drivers, preferably with the helper
layer is a plain value from 0 .. 64 instead of the Classical CAN length
which ranges from 0 to 8. The payload length to the bus-relevant DLC mapping
is only performed inside the CAN drivers, preferably with the helper
functions can_fd_dlc2len() and can_fd_len2dlc().
The CAN netdevice driver capabilities can be distinguished by the network
@ -1465,6 +1464,70 @@ Example when 'fd-non-iso on' is added on this switchable CAN FD adapter::
can <FD,FD-NON-ISO> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
Transmitter Delay Compensation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At high bit rates, the propagation delay from the TX pin to the RX pin of
the transceiver might become greater than the actual bit time causing
measurement errors: the RX pin would still be measuring the previous bit.
The Transmitter Delay Compensation (thereafter, TDC) resolves this problem
by introducing a Secondary Sample Point (SSP) equal to the distance, in
minimum time quantum, from the start of the bit time on the TX pin to the
actual measurement on the RX pin. The SSP is calculated as the sum of two
configurable values: the TDC Value (TDCV) and the TDC offset (TDCO).
TDC, if supported by the device, can be configured together with CAN-FD
using the ip tool's "tdc-mode" argument as follow:
**omitted**
When no "tdc-mode" option is provided, the kernel will automatically
decide whether TDC should be turned on, in which case it will
calculate a default TDCO and use the TDCV as measured by the
device. This is the recommended method to use TDC.
**"tdc-mode off"**
TDC is explicitly disabled.
**"tdc-mode auto"**
The user must provide the "tdco" argument. The TDCV will be
automatically calculated by the device. This option is only
available if the device supports the TDC-AUTO CAN controller mode.
**"tdc-mode manual"**
The user must provide both the "tdco" and "tdcv" arguments. This
option is only available if the device supports the TDC-MANUAL CAN
controller mode.
Note that some devices may offer an additional parameter: "tdcf" (TDC Filter
window). If supported by your device, this can be added as an optional
argument to either "tdc-mode auto" or "tdc-mode manual".
Example configuring a 500 kbit/s arbitration bitrate, a 5 Mbit/s data
bitrate, a TDCO of 15 minimum time quantum and a TDCV automatically measured
by the device::
$ ip link set can0 up type can bitrate 500000 \
fd on dbitrate 4000000 \
tdc-mode auto tdco 15
$ ip -details link show can0
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 72 qdisc pfifo_fast state UP \
mode DEFAULT group default qlen 10
link/can promiscuity 0 allmulti 0 minmtu 72 maxmtu 72
can <FD,TDC-AUTO> state ERROR-ACTIVE restart-ms 0
bitrate 500000 sample-point 0.875
tq 12 prop-seg 69 phase-seg1 70 phase-seg2 20 sjw 10 brp 1
ES582.1/ES584.1: tseg1 2..256 tseg2 2..128 sjw 1..128 brp 1..512 \
brp_inc 1
dbitrate 4000000 dsample-point 0.750
dtq 12 dprop-seg 7 dphase-seg1 7 dphase-seg2 5 dsjw 2 dbrp 1
tdco 15 tdcf 0
ES582.1/ES584.1: dtseg1 2..32 dtseg2 1..16 dsjw 1..8 dbrp 1..32 \
dbrp_inc 1
tdco 0..127 tdcf 0..127
clock 80000000
Supported CAN Hardware
----------------------

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// CAN bus driver for Bosch M_CAN controller
// Copyright (C) 2014 Freescale Semiconductor, Inc.
// Dong Aisheng <b29396@freescale.com>
// Dong Aisheng <aisheng.dong@nxp.com>
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
/* Bosch M_CAN user manual can be obtained from:
@ -812,6 +812,9 @@ static int m_can_handle_state_change(struct net_device *dev,
u32 timestamp = 0;
switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
break;
case CAN_STATE_ERROR_WARNING:
/* error warning state */
cdev->can.can_stats.error_warning++;
@ -841,6 +844,12 @@ static int m_can_handle_state_change(struct net_device *dev,
__m_can_get_berr_counter(dev, &bec);
switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
cf->data[1] = CAN_ERR_CRTL_ACTIVE;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
break;
case CAN_STATE_ERROR_WARNING:
/* error warning state */
cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
@ -877,30 +886,33 @@ static int m_can_handle_state_change(struct net_device *dev,
return 1;
}
static int m_can_handle_state_errors(struct net_device *dev, u32 psr)
static enum can_state
m_can_state_get_by_psr(struct m_can_classdev *cdev)
{
u32 reg_psr;
reg_psr = m_can_read(cdev, M_CAN_PSR);
if (reg_psr & PSR_BO)
return CAN_STATE_BUS_OFF;
if (reg_psr & PSR_EP)
return CAN_STATE_ERROR_PASSIVE;
if (reg_psr & PSR_EW)
return CAN_STATE_ERROR_WARNING;
return CAN_STATE_ERROR_ACTIVE;
}
static int m_can_handle_state_errors(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
int work_done = 0;
enum can_state new_state;
if (psr & PSR_EW && cdev->can.state != CAN_STATE_ERROR_WARNING) {
netdev_dbg(dev, "entered error warning state\n");
work_done += m_can_handle_state_change(dev,
CAN_STATE_ERROR_WARNING);
}
new_state = m_can_state_get_by_psr(cdev);
if (new_state == cdev->can.state)
return 0;
if (psr & PSR_EP && cdev->can.state != CAN_STATE_ERROR_PASSIVE) {
netdev_dbg(dev, "entered error passive state\n");
work_done += m_can_handle_state_change(dev,
CAN_STATE_ERROR_PASSIVE);
}
if (psr & PSR_BO && cdev->can.state != CAN_STATE_BUS_OFF) {
netdev_dbg(dev, "entered error bus off state\n");
work_done += m_can_handle_state_change(dev,
CAN_STATE_BUS_OFF);
}
return work_done;
return m_can_handle_state_change(dev, new_state);
}
static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus)
@ -1031,8 +1043,7 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus)
}
if (irqstatus & IR_ERR_STATE)
work_done += m_can_handle_state_errors(dev,
m_can_read(cdev, M_CAN_PSR));
work_done += m_can_handle_state_errors(dev);
if (irqstatus & IR_ERR_BUS_30X)
work_done += m_can_handle_bus_errors(dev, irqstatus,
@ -1606,7 +1617,7 @@ static int m_can_start(struct net_device *dev)
netdev_queue_set_dql_min_limit(netdev_get_tx_queue(cdev->net, 0),
cdev->tx_max_coalesced_frames);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
cdev->can.state = m_can_state_get_by_psr(cdev);
m_can_enable_all_interrupts(cdev);
@ -2492,12 +2503,11 @@ int m_can_class_suspend(struct device *dev)
}
m_can_clk_stop(cdev);
cdev->can.state = CAN_STATE_SLEEPING;
}
pinctrl_pm_select_sleep_state(dev);
cdev->can.state = CAN_STATE_SLEEPING;
return ret;
}
EXPORT_SYMBOL_GPL(m_can_class_suspend);
@ -2510,8 +2520,6 @@ int m_can_class_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(ndev)) {
ret = m_can_clk_start(cdev);
if (ret)
@ -2529,6 +2537,8 @@ int m_can_class_resume(struct device *dev)
if (cdev->ops->init)
ret = cdev->ops->init(cdev);
cdev->can.state = m_can_state_get_by_psr(cdev);
m_can_write(cdev, M_CAN_IE, cdev->active_interrupts);
} else {
ret = m_can_start(ndev);
@ -2546,7 +2556,7 @@ int m_can_class_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(m_can_class_resume);
MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// IOMapped CAN bus driver for Bosch M_CAN controller
// Copyright (C) 2014 Freescale Semiconductor, Inc.
// Dong Aisheng <b29396@freescale.com>
// Dong Aisheng <aisheng.dong@nxp.com>
//
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
@ -180,7 +180,7 @@ static void m_can_plat_remove(struct platform_device *pdev)
struct m_can_classdev *mcan_class = &priv->cdev;
m_can_class_unregister(mcan_class);
pm_runtime_disable(mcan_class->dev);
m_can_class_free_dev(mcan_class->net);
}
@ -236,7 +236,7 @@ static struct platform_driver m_can_plat_driver = {
module_platform_driver(m_can_plat_driver);
MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");

View File

@ -289,11 +289,6 @@ struct gs_host_frame {
#define GS_MAX_RX_URBS 30
#define GS_NAPI_WEIGHT 32
/* Maximum number of interfaces the driver supports per device.
* Current hardware only supports 3 interfaces. The future may vary.
*/
#define GS_MAX_INTF 3
struct gs_tx_context {
struct gs_can *dev;
unsigned int echo_id;
@ -324,7 +319,6 @@ struct gs_can {
/* usb interface struct */
struct gs_usb {
struct gs_can *canch[GS_MAX_INTF];
struct usb_anchor rx_submitted;
struct usb_device *udev;
@ -336,9 +330,11 @@ struct gs_usb {
unsigned int hf_size_rx;
u8 active_channels;
u8 channel_cnt;
unsigned int pipe_in;
unsigned int pipe_out;
struct gs_can *canch[] __counted_by(channel_cnt);
};
/* 'allocate' a tx context.
@ -599,7 +595,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
}
/* device reports out of range channel id */
if (hf->channel >= GS_MAX_INTF)
if (hf->channel >= parent->channel_cnt)
goto device_detach;
dev = parent->canch[hf->channel];
@ -699,7 +695,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
/* USB failure take down all interfaces */
if (rc == -ENODEV) {
device_detach:
for (rc = 0; rc < GS_MAX_INTF; rc++) {
for (rc = 0; rc < parent->channel_cnt; rc++) {
if (parent->canch[rc])
netif_device_detach(parent->canch[rc]->netdev);
}
@ -1249,6 +1245,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
netdev->dev_id = channel;
netdev->dev_port = channel;
/* dev setup */
strcpy(dev->bt_const.name, KBUILD_MODNAME);
@ -1460,17 +1457,19 @@ static int gs_usb_probe(struct usb_interface *intf,
icount = dconf.icount + 1;
dev_info(&intf->dev, "Configuring for %u interfaces\n", icount);
if (icount > GS_MAX_INTF) {
if (icount > type_max(parent->channel_cnt)) {
dev_err(&intf->dev,
"Driver cannot handle more that %u CAN interfaces\n",
GS_MAX_INTF);
type_max(parent->channel_cnt));
return -EINVAL;
}
parent = kzalloc(sizeof(*parent), GFP_KERNEL);
parent = kzalloc(struct_size(parent, canch, icount), GFP_KERNEL);
if (!parent)
return -ENOMEM;
parent->channel_cnt = icount;
init_usb_anchor(&parent->rx_submitted);
usb_set_intfdata(intf, parent);
@ -1531,7 +1530,7 @@ static void gs_usb_disconnect(struct usb_interface *intf)
return;
}
for (i = 0; i < GS_MAX_INTF; i++)
for (i = 0; i < parent->channel_cnt; i++)
if (parent->canch[i])
gs_destroy_candev(parent->canch[i]);

View File

@ -378,6 +378,8 @@ static int j1939_netdev_notify(struct notifier_block *nb,
j1939_ecu_unmap_all(priv);
break;
case NETDEV_UNREGISTER:
j1939_cancel_active_session(priv, NULL);
j1939_sk_netdev_event_netdown(priv);
j1939_sk_netdev_event_unregister(priv);
break;
}