From: Khaled Elnaggar As recommended in Documentation/filesystems/sysfs.rst, show() callbacks should use sysfs_emit() or sysfs_emit_at() to format values returned to userspace. Replace scnprintf() with sysfs_emit() in fwinfo_show(). Signed-off-by: Khaled Elnaggar Link: https://patch.msgid.link/20250712133609.331904-1-khaledelnaggarlinux@gmail.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/janz-ican3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 60c7b83b4539..bfa5cbe88017 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1867,7 +1867,7 @@ static ssize_t fwinfo_show(struct device *dev, { struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); - return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo); + return sysfs_emit(buf, "%s\n", mod->fwinfo); } static DEVICE_ATTR_RW(termination); base-commit: 06baf9bfa6ca8db7d5f32e12e27d1dc1b7cb3a8a -- 2.47.2 From: Vincent Mailhol Fix below default (W=0) warning: drivers/net/can/ti_hecc.c: In function 'ti_hecc_start': drivers/net/can/ti_hecc.c:386:20: warning: conversion from 'long unsigned int' to 'u32' {aka 'unsigned int'} changes value from '18446744073709551599' to '4294967279' [-Woverflow] 386 | mbx_mask = ~BIT(HECC_RX_LAST_MBOX); | ^ Signed-off-by: Vincent Mailhol Link: https://patch.msgid.link/20250715-can-compile-test-v2-1-f7fd566db86f@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ti_hecc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 644e8b8eb91e..e6d6661a908a 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -383,7 +383,7 @@ static void ti_hecc_start(struct net_device *ndev) * overflows instead of the hardware silently dropping the * messages. */ - mbx_mask = ~BIT(HECC_RX_LAST_MBOX); + mbx_mask = ~BIT_U32(HECC_RX_LAST_MBOX); hecc_write(priv, HECC_CANOPC, mbx_mask); /* Enable interrupts */ -- 2.47.2 From: Vincent Mailhol ti_hecc depends on ARM. Add COMPILE_TEST to the dependency list so that this driver can also be built on other platforms. Signed-off-by: Vincent Mailhol Link: https://patch.msgid.link/20250715-can-compile-test-v2-2-f7fd566db86f@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index cf989bea9aa3..d58fab0161b3 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -201,7 +201,7 @@ config CAN_SUN4I be called sun4i_can. config CAN_TI_HECC - depends on ARM + depends on ARM || COMPILE_TEST tristate "TI High End CAN Controller" select CAN_RX_OFFLOAD help -- 2.47.2 From: Vincent Mailhol tscan1 depends on ISA. It also has a hidden dependency on HAS_IOPORT as reported by the kernel test bot [1]. That dependency is implied by ISA which explains why this was not an issue so far. Add both COMPILE_TEST and HAS_IOPORT to the dependency list so that this driver can also be built on other platforms. [1] https://lore.kernel.org/linux-can/202507141417.qAMrchyV-lkp@intel.com/ Signed-off-by: Vincent Mailhol Link: https://patch.msgid.link/20250715-can-compile-test-v2-3-f7fd566db86f@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/sja1000/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index 2f516cc6d22c..ba16d7bc09ef 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -105,7 +105,7 @@ config CAN_SJA1000_PLATFORM config CAN_TSCAN1 tristate "TS-CAN1 PC104 boards" - depends on ISA + depends on ISA || (COMPILE_TEST && HAS_IOPORT) help This driver is for Technologic Systems' TSCAN-1 PC104 boards. https://www.embeddedts.com/products/TS-CAN1 -- 2.47.2 From: Randy Dunlap Add a dependency on PC104 to limit (restrict) this driver kconfig prompt to kernel configs that have PC104 set. Add COMPILE_TEST as a possibility for more complete build coverage. I tested this build config on x86_64 5 times without problems. Signed-off-by: Randy Dunlap Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/20250721002823.3548945-1-rdunlap@infradead.org [mkl: fix conflict, remove Fixes: tag] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/sja1000/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index ba16d7bc09ef..e061e35769bf 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -105,7 +105,7 @@ config CAN_SJA1000_PLATFORM config CAN_TSCAN1 tristate "TS-CAN1 PC104 boards" - depends on ISA || (COMPILE_TEST && HAS_IOPORT) + depends on (ISA && PC104) || (COMPILE_TEST && HAS_IOPORT) help This driver is for Technologic Systems' TSCAN-1 PC104 boards. https://www.embeddedts.com/products/TS-CAN1 -- 2.47.2 From: Luis Felipe Hernandez Fix kernel-doc formatting issue causing unexpected indentation error in ctucanfd driver documentation build. Convert main return values to bullet list format while preserving numbered sub-list in order to correct indentation error and visual structure in rendered html. Signed-off-by: Luis Felipe Hernandez Reviewed-by: Randy Dunlap Tested-by: Randy Dunlap Reviewed-by: Vincent Mailhol Reviewed-by: Pavel Pisa Link: https://patch.msgid.link/20250722035352.21807-1-luis.hernandez093@gmail.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_base.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index bf6398772960..8bd3f0fc385c 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -506,11 +506,12 @@ static bool ctucan_is_txt_buf_writable(struct ctucan_priv *priv, u8 buf) * @buf: TXT Buffer index to which frame is inserted (0-based) * @isfdf: True - CAN FD Frame, False - CAN 2.0 Frame * - * Return: True - Frame inserted successfully - * False - Frame was not inserted due to one of: - * 1. TXT Buffer is not writable (it is in wrong state) - * 2. Invalid TXT buffer index - * 3. Invalid frame length + * Return: + * * True - Frame inserted successfully + * * False - Frame was not inserted due to one of: + * 1. TXT Buffer is not writable (it is in wrong state) + * 2. Invalid TXT buffer index + * 3. Invalid frame length */ static bool ctucan_insert_frame(struct ctucan_priv *priv, const struct canfd_frame *cf, u8 buf, bool isfdf) -- 2.47.2 From: Jimmy Assarsson Add support to turn on/off CAN LEDs on device. Turn off all CAN LEDs in probe, since they are default on after a reset or power on. Reviewed-by: Axel Forsman Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-2-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 09510663988c..c8f530ef416e 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -66,6 +66,7 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); #define KVASER_PCIEFD_KCAN_FIFO_LAST_REG 0x180 #define KVASER_PCIEFD_KCAN_CTRL_REG 0x2c0 #define KVASER_PCIEFD_KCAN_CMD_REG 0x400 +#define KVASER_PCIEFD_KCAN_IOC_REG 0x404 #define KVASER_PCIEFD_KCAN_IEN_REG 0x408 #define KVASER_PCIEFD_KCAN_IRQ_REG 0x410 #define KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG 0x414 @@ -136,6 +137,9 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); /* Request status packet */ #define KVASER_PCIEFD_KCAN_CMD_SRQ BIT(0) +/* Control CAN LED, active low */ +#define KVASER_PCIEFD_KCAN_IOC_LED BIT(0) + /* Transmitter unaligned */ #define KVASER_PCIEFD_KCAN_IRQ_TAL BIT(17) /* Tx FIFO empty */ @@ -410,6 +414,7 @@ struct kvaser_pciefd_can { struct kvaser_pciefd *kv_pcie; void __iomem *reg_base; struct can_berr_counter bec; + u32 ioc; u8 cmd_seq; u8 tx_max_count; u8 tx_idx; @@ -528,6 +533,16 @@ static inline void kvaser_pciefd_abort_flush_reset(struct kvaser_pciefd_can *can kvaser_pciefd_send_kcan_cmd(can, KVASER_PCIEFD_KCAN_CMD_AT); } +static inline void kvaser_pciefd_set_led(struct kvaser_pciefd_can *can, bool on) +{ + if (on) + can->ioc &= ~KVASER_PCIEFD_KCAN_IOC_LED; + else + can->ioc |= KVASER_PCIEFD_KCAN_IOC_LED; + + iowrite32(can->ioc, can->reg_base + KVASER_PCIEFD_KCAN_IOC_REG); +} + static void kvaser_pciefd_enable_err_gen(struct kvaser_pciefd_can *can) { u32 mode; @@ -990,6 +1005,9 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) /* Disable Bus load reporting */ iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_BUS_LOAD_REG); + can->ioc = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_IOC_REG); + kvaser_pciefd_set_led(can, false); + tx_nr_packets_max = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_MAX_MASK, ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG)); -- 2.47.2 From: Jimmy Assarsson Add support for ethtool set_phys_id(), to physically locate devices by flashing a LED on the device. Reviewed-by: Axel Forsman Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-3-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index c8f530ef416e..ed1ea8a9a6d2 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -968,8 +968,32 @@ static const struct net_device_ops kvaser_pciefd_netdev_ops = { .ndo_change_mtu = can_change_mtu, }; +static int kvaser_pciefd_set_phys_id(struct net_device *netdev, + enum ethtool_phys_id_state state) +{ + struct kvaser_pciefd_can *can = netdev_priv(netdev); + + switch (state) { + case ETHTOOL_ID_ACTIVE: + return 3; /* 3 On/Off cycles per second */ + + case ETHTOOL_ID_ON: + kvaser_pciefd_set_led(can, true); + return 0; + + case ETHTOOL_ID_OFF: + case ETHTOOL_ID_INACTIVE: + kvaser_pciefd_set_led(can, false); + return 0; + + default: + return -EINVAL; + } +} + static const struct ethtool_ops kvaser_pciefd_ethtool_ops = { .get_ts_info = can_ethtool_op_get_ts_info_hwts, + .set_phys_id = kvaser_pciefd_set_phys_id, }; static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) -- 2.47.2 From: Jimmy Assarsson Add intermediate variable, for readability and to simplify future patches. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-4-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index ed1ea8a9a6d2..4bdb1132ecf9 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -1813,10 +1813,11 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; + struct device *dev = &pdev->dev; struct kvaser_pciefd *pcie; const struct kvaser_pciefd_irq_mask *irq_mask; - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; @@ -1855,7 +1856,7 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, ret = pci_alloc_irq_vectors(pcie->pci, 1, 1, PCI_IRQ_INTX | PCI_IRQ_MSI); if (ret < 0) { - dev_err(&pcie->pci->dev, "Failed to allocate IRQ vectors.\n"); + dev_err(dev, "Failed to allocate IRQ vectors.\n"); goto err_teardown_can_ctrls; } @@ -1868,7 +1869,7 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, ret = request_irq(pcie->pci->irq, kvaser_pciefd_irq_handler, IRQF_SHARED, KVASER_PCIEFD_DRV_NAME, pcie); if (ret) { - dev_err(&pcie->pci->dev, "Failed to request IRQ %d\n", pcie->pci->irq); + dev_err(dev, "Failed to request IRQ %d\n", pcie->pci->irq); goto err_pci_free_irq_vectors; } iowrite32(KVASER_PCIEFD_SRB_IRQ_DPD0 | KVASER_PCIEFD_SRB_IRQ_DPD1, -- 2.47.2 From: Jimmy Assarsson Store firmware version in kvaser_pciefd_fw_version struct, specifying the different components of the version number. And drop debug prinout of firmware version, since later patches will expose it via the devlink interface. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-5-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 4bdb1132ecf9..7153b9ea0d3d 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -325,6 +325,12 @@ struct kvaser_pciefd_driver_data { const struct kvaser_pciefd_dev_ops *ops; }; +struct kvaser_pciefd_fw_version { + u8 major; + u8 minor; + u16 build; +}; + static const struct kvaser_pciefd_address_offset kvaser_pciefd_altera_address_offset = { .serdes = 0x1000, .pci_ien = 0x50, @@ -437,6 +443,7 @@ struct kvaser_pciefd { u32 bus_freq; u32 freq; u32 freq_to_ticks_div; + struct kvaser_pciefd_fw_version fw_version; }; struct kvaser_pciefd_rx_packet { @@ -1205,14 +1212,12 @@ static int kvaser_pciefd_setup_board(struct kvaser_pciefd *pcie) u32 version, srb_status, build; version = ioread32(KVASER_PCIEFD_SYSID_ADDR(pcie) + KVASER_PCIEFD_SYSID_VERSION_REG); + build = ioread32(KVASER_PCIEFD_SYSID_ADDR(pcie) + KVASER_PCIEFD_SYSID_BUILD_REG); pcie->nr_channels = min(KVASER_PCIEFD_MAX_CAN_CHANNELS, FIELD_GET(KVASER_PCIEFD_SYSID_VERSION_NR_CHAN_MASK, version)); - - build = ioread32(KVASER_PCIEFD_SYSID_ADDR(pcie) + KVASER_PCIEFD_SYSID_BUILD_REG); - dev_dbg(&pcie->pci->dev, "Version %lu.%lu.%lu\n", - FIELD_GET(KVASER_PCIEFD_SYSID_VERSION_MAJOR_MASK, version), - FIELD_GET(KVASER_PCIEFD_SYSID_VERSION_MINOR_MASK, version), - FIELD_GET(KVASER_PCIEFD_SYSID_BUILD_SEQ_MASK, build)); + pcie->fw_version.major = FIELD_GET(KVASER_PCIEFD_SYSID_VERSION_MAJOR_MASK, version); + pcie->fw_version.minor = FIELD_GET(KVASER_PCIEFD_SYSID_VERSION_MINOR_MASK, version); + pcie->fw_version.build = FIELD_GET(KVASER_PCIEFD_SYSID_BUILD_SEQ_MASK, build); srb_status = ioread32(KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_STAT_REG); if (!(srb_status & KVASER_PCIEFD_SRB_STAT_DMA)) { -- 2.47.2 From: Jimmy Assarsson Store device channel index in netdev.dev_port. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-6-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 7153b9ea0d3d..8dcb1d1c67e4 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -1028,6 +1028,7 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) can->completed_tx_bytes = 0; can->bec.txerr = 0; can->bec.rxerr = 0; + can->can.dev->dev_port = i; init_completion(&can->start_comp); init_completion(&can->flush_comp); -- 2.47.2 From: Jimmy Assarsson Split driver into C-file and header-file, to simplify future patches. Move common definitions and declarations to a header file. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-7-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Makefile | 2 +- drivers/net/can/kvaser_pciefd/Makefile | 3 + drivers/net/can/kvaser_pciefd/kvaser_pciefd.h | 90 +++++++++++++++++++ .../kvaser_pciefd_core.c} | 72 +-------------- 4 files changed, 96 insertions(+), 71 deletions(-) create mode 100644 drivers/net/can/kvaser_pciefd/Makefile create mode 100644 drivers/net/can/kvaser_pciefd/kvaser_pciefd.h rename drivers/net/can/{kvaser_pciefd.c => kvaser_pciefd/kvaser_pciefd_core.c} (97%) diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index a71db2cfe990..56138d8ddfd2 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -25,7 +25,7 @@ obj-$(CONFIG_CAN_FLEXCAN) += flexcan/ obj-$(CONFIG_CAN_GRCAN) += grcan.o obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o -obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o +obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd/ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ diff --git a/drivers/net/can/kvaser_pciefd/Makefile b/drivers/net/can/kvaser_pciefd/Makefile new file mode 100644 index 000000000000..ea1bf1000760 --- /dev/null +++ b/drivers/net/can/kvaser_pciefd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o +kvaser_pciefd-y = kvaser_pciefd_core.o diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h new file mode 100644 index 000000000000..55bb7e078340 --- /dev/null +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* kvaser_pciefd common definitions and declarations + * + * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved. + */ + +#ifndef _KVASER_PCIEFD_H +#define _KVASER_PCIEFD_H + +#include +#include +#include +#include +#include +#include + +#define KVASER_PCIEFD_MAX_CAN_CHANNELS 8UL +#define KVASER_PCIEFD_DMA_COUNT 2U +#define KVASER_PCIEFD_DMA_SIZE (4U * 1024U) +#define KVASER_PCIEFD_CAN_TX_MAX_COUNT 17U + +struct kvaser_pciefd; + +struct kvaser_pciefd_address_offset { + u32 serdes; + u32 pci_ien; + u32 pci_irq; + u32 sysid; + u32 loopback; + u32 kcan_srb_fifo; + u32 kcan_srb; + u32 kcan_ch0; + u32 kcan_ch1; +}; + +struct kvaser_pciefd_irq_mask { + u32 kcan_rx0; + u32 kcan_tx[KVASER_PCIEFD_MAX_CAN_CHANNELS]; + u32 all; +}; + +struct kvaser_pciefd_dev_ops { + void (*kvaser_pciefd_write_dma_map)(struct kvaser_pciefd *pcie, + dma_addr_t addr, int index); +}; + +struct kvaser_pciefd_driver_data { + const struct kvaser_pciefd_address_offset *address_offset; + const struct kvaser_pciefd_irq_mask *irq_mask; + const struct kvaser_pciefd_dev_ops *ops; +}; + +struct kvaser_pciefd_fw_version { + u8 major; + u8 minor; + u16 build; +}; + +struct kvaser_pciefd_can { + struct can_priv can; + struct kvaser_pciefd *kv_pcie; + void __iomem *reg_base; + struct can_berr_counter bec; + u32 ioc; + u8 cmd_seq; + u8 tx_max_count; + u8 tx_idx; + u8 ack_idx; + int err_rep_cnt; + unsigned int completed_tx_pkts; + unsigned int completed_tx_bytes; + spinlock_t lock; /* Locks sensitive registers (e.g. MODE) */ + struct timer_list bec_poll_timer; + struct completion start_comp, flush_comp; +}; + +struct kvaser_pciefd { + struct pci_dev *pci; + void __iomem *reg_base; + struct kvaser_pciefd_can *can[KVASER_PCIEFD_MAX_CAN_CHANNELS]; + const struct kvaser_pciefd_driver_data *driver_data; + void *dma_data[KVASER_PCIEFD_DMA_COUNT]; + u8 nr_channels; + u32 bus_freq; + u32 freq; + u32 freq_to_ticks_div; + struct kvaser_pciefd_fw_version fw_version; +}; + +#endif /* _KVASER_PCIEFD_H */ diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c similarity index 97% rename from drivers/net/can/kvaser_pciefd.c rename to drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c index 8dcb1d1c67e4..97cbe07c4ee3 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c @@ -5,6 +5,8 @@ * - PEAK linux canfd driver */ +#include "kvaser_pciefd.h" + #include #include #include @@ -27,10 +29,6 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); #define KVASER_PCIEFD_WAIT_TIMEOUT msecs_to_jiffies(1000) #define KVASER_PCIEFD_BEC_POLL_FREQ (jiffies + msecs_to_jiffies(200)) #define KVASER_PCIEFD_MAX_ERR_REP 256U -#define KVASER_PCIEFD_CAN_TX_MAX_COUNT 17U -#define KVASER_PCIEFD_MAX_CAN_CHANNELS 8UL -#define KVASER_PCIEFD_DMA_COUNT 2U -#define KVASER_PCIEFD_DMA_SIZE (4U * 1024U) #define KVASER_PCIEFD_VENDOR 0x1a07 @@ -296,41 +294,6 @@ static void kvaser_pciefd_write_dma_map_sf2(struct kvaser_pciefd *pcie, static void kvaser_pciefd_write_dma_map_xilinx(struct kvaser_pciefd *pcie, dma_addr_t addr, int index); -struct kvaser_pciefd_address_offset { - u32 serdes; - u32 pci_ien; - u32 pci_irq; - u32 sysid; - u32 loopback; - u32 kcan_srb_fifo; - u32 kcan_srb; - u32 kcan_ch0; - u32 kcan_ch1; -}; - -struct kvaser_pciefd_dev_ops { - void (*kvaser_pciefd_write_dma_map)(struct kvaser_pciefd *pcie, - dma_addr_t addr, int index); -}; - -struct kvaser_pciefd_irq_mask { - u32 kcan_rx0; - u32 kcan_tx[KVASER_PCIEFD_MAX_CAN_CHANNELS]; - u32 all; -}; - -struct kvaser_pciefd_driver_data { - const struct kvaser_pciefd_address_offset *address_offset; - const struct kvaser_pciefd_irq_mask *irq_mask; - const struct kvaser_pciefd_dev_ops *ops; -}; - -struct kvaser_pciefd_fw_version { - u8 major; - u8 minor; - u16 build; -}; - static const struct kvaser_pciefd_address_offset kvaser_pciefd_altera_address_offset = { .serdes = 0x1000, .pci_ien = 0x50, @@ -415,37 +378,6 @@ static const struct kvaser_pciefd_driver_data kvaser_pciefd_xilinx_driver_data = .ops = &kvaser_pciefd_xilinx_dev_ops, }; -struct kvaser_pciefd_can { - struct can_priv can; - struct kvaser_pciefd *kv_pcie; - void __iomem *reg_base; - struct can_berr_counter bec; - u32 ioc; - u8 cmd_seq; - u8 tx_max_count; - u8 tx_idx; - u8 ack_idx; - int err_rep_cnt; - unsigned int completed_tx_pkts; - unsigned int completed_tx_bytes; - spinlock_t lock; /* Locks sensitive registers (e.g. MODE) */ - struct timer_list bec_poll_timer; - struct completion start_comp, flush_comp; -}; - -struct kvaser_pciefd { - struct pci_dev *pci; - void __iomem *reg_base; - struct kvaser_pciefd_can *can[KVASER_PCIEFD_MAX_CAN_CHANNELS]; - const struct kvaser_pciefd_driver_data *driver_data; - void *dma_data[KVASER_PCIEFD_DMA_COUNT]; - u8 nr_channels; - u32 bus_freq; - u32 freq; - u32 freq_to_ticks_div; - struct kvaser_pciefd_fw_version fw_version; -}; - struct kvaser_pciefd_rx_packet { u32 header[2]; u64 timestamp; -- 2.47.2 From: Jimmy Assarsson Add devlink support at device level. Example output: $ devlink dev pci/0000:07:00.0 pci/0000:08:00.0 pci/0000:09:00.0 $ devlink dev info pci/0000:07:00.0: driver kvaser_pciefd pci/0000:08:00.0: driver kvaser_pciefd pci/0000:09:00.0: driver kvaser_pciefd Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-8-extja@kvaser.com [mkl: kvaser_pciefd_remove(): fix use-after-free] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 1 + drivers/net/can/kvaser_pciefd/Makefile | 2 +- drivers/net/can/kvaser_pciefd/kvaser_pciefd.h | 2 ++ .../net/can/kvaser_pciefd/kvaser_pciefd_core.c | 15 ++++++++++++--- .../net/can/kvaser_pciefd/kvaser_pciefd_devlink.c | 11 +++++++++++ 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d58fab0161b3..d43d56694667 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -154,6 +154,7 @@ config CAN_JANZ_ICAN3 config CAN_KVASER_PCIEFD depends on PCI tristate "Kvaser PCIe FD cards" + select NET_DEVLINK help This is a driver for the Kvaser PCI Express CAN FD family. diff --git a/drivers/net/can/kvaser_pciefd/Makefile b/drivers/net/can/kvaser_pciefd/Makefile index ea1bf1000760..8c5b8cdc6b5f 100644 --- a/drivers/net/can/kvaser_pciefd/Makefile +++ b/drivers/net/can/kvaser_pciefd/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o -kvaser_pciefd-y = kvaser_pciefd_core.o +kvaser_pciefd-y = kvaser_pciefd_core.o kvaser_pciefd_devlink.o diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h index 55bb7e078340..34ba393d6093 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h @@ -13,6 +13,7 @@ #include #include #include +#include #define KVASER_PCIEFD_MAX_CAN_CHANNELS 8UL #define KVASER_PCIEFD_DMA_COUNT 2U @@ -87,4 +88,5 @@ struct kvaser_pciefd { struct kvaser_pciefd_fw_version fw_version; }; +extern const struct devlink_ops kvaser_pciefd_devlink_ops; #endif /* _KVASER_PCIEFD_H */ diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c index 97cbe07c4ee3..86509a2d2b90 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c @@ -1751,14 +1751,16 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; + struct devlink *devlink; struct device *dev = &pdev->dev; struct kvaser_pciefd *pcie; const struct kvaser_pciefd_irq_mask *irq_mask; - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) + devlink = devlink_alloc(&kvaser_pciefd_devlink_ops, sizeof(*pcie), dev); + if (!devlink) return -ENOMEM; + pcie = devlink_priv(devlink); pci_set_drvdata(pdev, pcie); pcie->pci = pdev; pcie->driver_data = (const struct kvaser_pciefd_driver_data *)id->driver_data; @@ -1766,7 +1768,7 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) - return ret; + goto err_free_devlink; ret = pci_request_regions(pdev, KVASER_PCIEFD_DRV_NAME); if (ret) @@ -1830,6 +1832,8 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, if (ret) goto err_free_irq; + devlink_register(devlink); + return 0; err_free_irq: @@ -1853,6 +1857,9 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, err_disable_pci: pci_disable_device(pdev); +err_free_devlink: + devlink_free(devlink); + return ret; } @@ -1876,9 +1883,11 @@ static void kvaser_pciefd_remove(struct pci_dev *pdev) for (i = 0; i < pcie->nr_channels; ++i) free_candev(pcie->can[i]->can.dev); + devlink_unregister(priv_to_devlink(pcie)); pci_iounmap(pdev, pcie->reg_base); pci_release_regions(pdev); pci_disable_device(pdev); + devlink_free(priv_to_devlink(pcie)); } static struct pci_driver kvaser_pciefd = { diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c new file mode 100644 index 000000000000..7c2040ed53d7 --- /dev/null +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* kvaser_pciefd devlink functions + * + * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved. + */ +#include "kvaser_pciefd.h" + +#include + +const struct devlink_ops kvaser_pciefd_devlink_ops = { +}; -- 2.47.2 From: Jimmy Assarsson Expose device firmware version via devlink info_get(). Example output: $ devlink dev pci/0000:07:00.0 pci/0000:08:00.0 pci/0000:09:00.0 $ devlink dev info pci/0000:07:00.0: driver kvaser_pciefd versions: running: fw 1.3.75 pci/0000:08:00.0: driver kvaser_pciefd versions: running: fw 2.4.29 pci/0000:09:00.0: driver kvaser_pciefd versions: running: fw 1.3.72 Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-9-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../can/kvaser_pciefd/kvaser_pciefd_devlink.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c index 7c2040ed53d7..1fbb40dbbb7a 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c @@ -7,5 +7,29 @@ #include +static int kvaser_pciefd_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct kvaser_pciefd *pcie = devlink_priv(devlink); + char buf[] = "xxx.xxx.xxxxx"; + int ret; + + if (pcie->fw_version.major) { + snprintf(buf, sizeof(buf), "%u.%u.%u", + pcie->fw_version.major, + pcie->fw_version.minor, + pcie->fw_version.build); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (ret) + return ret; + } + + return 0; +} + const struct devlink_ops kvaser_pciefd_devlink_ops = { + .info_get = kvaser_pciefd_devlink_info_get, }; -- 2.47.2 From: Jimmy Assarsson Register each CAN channel of the device as an devlink physical port. This makes it easier to get device information for a given network interface (i.e. can2). Example output: $ devlink dev pci/0000:07:00.0 pci/0000:08:00.0 pci/0000:09:00.0 $ devlink port pci/0000:07:00.0/0: type eth netdev can0 flavour physical port 0 splittable false pci/0000:07:00.0/1: type eth netdev can1 flavour physical port 1 splittable false pci/0000:07:00.0/2: type eth netdev can2 flavour physical port 2 splittable false pci/0000:07:00.0/3: type eth netdev can3 flavour physical port 3 splittable false pci/0000:08:00.0/0: type eth netdev can4 flavour physical port 0 splittable false pci/0000:08:00.0/1: type eth netdev can5 flavour physical port 1 splittable false pci/0000:09:00.0/0: type eth netdev can6 flavour physical port 0 splittable false pci/0000:09:00.0/1: type eth netdev can7 flavour physical port 1 splittable false pci/0000:09:00.0/2: type eth netdev can8 flavour physical port 2 splittable false pci/0000:09:00.0/3: type eth netdev can9 flavour physical port 3 splittable false $ devlink port show can2 pci/0000:07:00.0/2: type eth netdev can2 flavour physical port 2 splittable false $ devlink dev info pci/0000:07:00.0: driver kvaser_pciefd versions: running: fw 1.3.75 pci/0000:08:00.0: driver kvaser_pciefd versions: running: fw 2.4.29 pci/0000:09:00.0: driver kvaser_pciefd versions: running: fw 1.3.72 $ sudo ethtool -i can2 driver: kvaser_pciefd version: 6.8.0-40-generic firmware-version: 1.3.75 expansion-rom-version: bus-info: 0000:07:00.0 supports-statistics: no supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-10-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/kvaser_pciefd/kvaser_pciefd.h | 4 +++ .../can/kvaser_pciefd/kvaser_pciefd_core.c | 8 ++++++ .../can/kvaser_pciefd/kvaser_pciefd_devlink.c | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h index 34ba393d6093..08c9ddc1ee85 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd.h @@ -59,6 +59,7 @@ struct kvaser_pciefd_fw_version { struct kvaser_pciefd_can { struct can_priv can; + struct devlink_port devlink_port; struct kvaser_pciefd *kv_pcie; void __iomem *reg_base; struct can_berr_counter bec; @@ -89,4 +90,7 @@ struct kvaser_pciefd { }; extern const struct devlink_ops kvaser_pciefd_devlink_ops; + +int kvaser_pciefd_devlink_port_register(struct kvaser_pciefd_can *can); +void kvaser_pciefd_devlink_port_unregister(struct kvaser_pciefd_can *can); #endif /* _KVASER_PCIEFD_H */ diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c index 86509a2d2b90..0880023611be 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c @@ -943,6 +943,7 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) struct net_device *netdev; struct kvaser_pciefd_can *can; u32 status, tx_nr_packets_max; + int ret; netdev = alloc_candev(sizeof(struct kvaser_pciefd_can), roundup_pow_of_two(KVASER_PCIEFD_CAN_TX_MAX_COUNT)); @@ -1013,6 +1014,11 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) pcie->can[i] = can; kvaser_pciefd_pwm_start(can); + ret = kvaser_pciefd_devlink_port_register(can); + if (ret) { + dev_err(&pcie->pci->dev, "Failed to register devlink port\n"); + return ret; + } } return 0; @@ -1732,6 +1738,7 @@ static void kvaser_pciefd_teardown_can_ctrls(struct kvaser_pciefd *pcie) if (can) { iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); kvaser_pciefd_pwm_stop(can); + kvaser_pciefd_devlink_port_unregister(can); free_candev(can->can.dev); } } @@ -1874,6 +1881,7 @@ static void kvaser_pciefd_remove(struct pci_dev *pdev) unregister_candev(can->can.dev); timer_delete(&can->bec_poll_timer); kvaser_pciefd_pwm_stop(can); + kvaser_pciefd_devlink_port_unregister(can); } kvaser_pciefd_disable_irq_srcs(pcie); diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c index 1fbb40dbbb7a..1d61a8b0eeba 100644 --- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c +++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c @@ -5,6 +5,7 @@ */ #include "kvaser_pciefd.h" +#include #include static int kvaser_pciefd_devlink_info_get(struct devlink *devlink, @@ -33,3 +34,27 @@ static int kvaser_pciefd_devlink_info_get(struct devlink *devlink, const struct devlink_ops kvaser_pciefd_devlink_ops = { .info_get = kvaser_pciefd_devlink_info_get, }; + +int kvaser_pciefd_devlink_port_register(struct kvaser_pciefd_can *can) +{ + int ret; + struct devlink_port_attrs attrs = { + .flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL, + .phys.port_number = can->can.dev->dev_port, + }; + devlink_port_attrs_set(&can->devlink_port, &attrs); + + ret = devlink_port_register(priv_to_devlink(can->kv_pcie), + &can->devlink_port, can->can.dev->dev_port); + if (ret) + return ret; + + SET_NETDEV_DEVLINK_PORT(can->can.dev, &can->devlink_port); + + return 0; +} + +void kvaser_pciefd_devlink_port_unregister(struct kvaser_pciefd_can *can) +{ + devlink_port_unregister(&can->devlink_port); +} -- 2.47.2 From: Jimmy Assarsson List the version information reported by the kvaser_pciefd driver through devlink. Suggested-by: Vincent Mailhol Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123230.8-11-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- Documentation/networking/devlink/index.rst | 1 + .../networking/devlink/kvaser_pciefd.rst | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 Documentation/networking/devlink/kvaser_pciefd.rst diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index 250ae71f4023..053fbafeb491 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -85,6 +85,7 @@ parameters, info versions, and other features it supports. ionic ice ixgbe + kvaser_pciefd mlx4 mlx5 mlxsw diff --git a/Documentation/networking/devlink/kvaser_pciefd.rst b/Documentation/networking/devlink/kvaser_pciefd.rst new file mode 100644 index 000000000000..075edd2a508a --- /dev/null +++ b/Documentation/networking/devlink/kvaser_pciefd.rst @@ -0,0 +1,24 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================= +kvaser_pciefd devlink support +============================= + +This document describes the devlink features implemented by the +``kvaser_pciefd`` device driver. + +Info versions +============= + +The ``kvaser_pciefd`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the device. Also available + through ``ethtool -i`` as ``firmware-version``. -- 2.47.2 From: Jimmy Assarsson Add support to turn on/off CAN LEDs on device. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-2-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 9 ++++ .../net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 53 ++++++++++++++++++ .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 54 +++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index f6c77eca9f43..032dc1821f04 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -54,6 +54,11 @@ enum kvaser_usb_leaf_family { KVASER_USBCAN, }; +enum kvaser_usb_led_state { + KVASER_USB_LED_ON = 0, + KVASER_USB_LED_OFF = 1, +}; + #define KVASER_USB_HYDRA_MAX_CMD_LEN 128 struct kvaser_usb_dev_card_data_hydra { u8 channel_to_he[KVASER_USB_MAX_NET_DEVICES]; @@ -149,6 +154,7 @@ struct kvaser_usb_net_priv { * @dev_get_software_details: get software details * @dev_get_card_info: get card info * @dev_get_capabilities: discover device capabilities + * @dev_set_led: turn on/off device LED * * @dev_set_opt_mode: set ctrlmod * @dev_start_chip: start the CAN controller @@ -176,6 +182,9 @@ struct kvaser_usb_dev_ops { int (*dev_get_software_details)(struct kvaser_usb *dev); int (*dev_get_card_info)(struct kvaser_usb *dev); int (*dev_get_capabilities)(struct kvaser_usb *dev); + int (*dev_set_led)(struct kvaser_usb_net_priv *priv, + enum kvaser_usb_led_state state, + u16 duration_ms); int (*dev_set_opt_mode)(const struct kvaser_usb_net_priv *priv); int (*dev_start_chip)(struct kvaser_usb_net_priv *priv); int (*dev_stop_chip)(struct kvaser_usb_net_priv *priv); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 8e88b5917796..a4402b4845c6 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -12,6 +12,7 @@ * distinguish between ERROR_WARNING and ERROR_ACTIVE. */ +#include #include #include #include @@ -67,6 +68,8 @@ static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_rt; #define CMD_SET_BUSPARAMS_RESP 85 #define CMD_GET_CAPABILITIES_REQ 95 #define CMD_GET_CAPABILITIES_RESP 96 +#define CMD_LED_ACTION_REQ 101 +#define CMD_LED_ACTION_RESP 102 #define CMD_RX_MESSAGE 106 #define CMD_MAP_CHANNEL_REQ 200 #define CMD_MAP_CHANNEL_RESP 201 @@ -217,6 +220,22 @@ struct kvaser_cmd_get_busparams_res { u8 reserved[20]; } __packed; +/* The device has two LEDs per CAN channel + * The LSB of action field controls the state: + * 0 = ON + * 1 = OFF + * The remaining bits of action field is the LED index + */ +#define KVASER_USB_HYDRA_LED_IDX_MASK GENMASK(31, 1) +#define KVASER_USB_HYDRA_LED_YELLOW_CH0_IDX 3 +#define KVASER_USB_HYDRA_LEDS_PER_CHANNEL 2 +struct kvaser_cmd_led_action_req { + u8 action; + u8 padding; + __le16 duration_ms; + u8 reserved[24]; +} __packed; + /* Ctrl modes */ #define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 #define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 @@ -299,6 +318,8 @@ struct kvaser_cmd { struct kvaser_cmd_get_busparams_req get_busparams_req; struct kvaser_cmd_get_busparams_res get_busparams_res; + struct kvaser_cmd_led_action_req led_action_req; + struct kvaser_cmd_chip_state_event chip_state_event; struct kvaser_cmd_set_ctrlmode set_ctrlmode; @@ -1390,6 +1411,7 @@ static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev, /* Ignored commands */ case CMD_SET_BUSPARAMS_RESP: case CMD_SET_BUSPARAMS_FD_RESP: + case CMD_LED_ACTION_RESP: break; default: @@ -1946,6 +1968,36 @@ static int kvaser_usb_hydra_get_capabilities(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_hydra_set_led(struct kvaser_usb_net_priv *priv, + enum kvaser_usb_led_state state, + u16 duration_ms) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + size_t cmd_len; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_LED_ACTION_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + kvaser_usb_hydra_set_cmd_dest_he(cmd, dev->card_data.hydra.sysdbg_he); + kvaser_usb_hydra_set_cmd_transid(cmd, kvaser_usb_hydra_get_next_transid(dev)); + + cmd->led_action_req.duration_ms = cpu_to_le16(duration_ms); + cmd->led_action_req.action = state | + FIELD_PREP(KVASER_USB_HYDRA_LED_IDX_MASK, + KVASER_USB_HYDRA_LED_YELLOW_CH0_IDX + + KVASER_USB_HYDRA_LEDS_PER_CHANNEL * priv->channel); + + ret = kvaser_usb_send_cmd(dev, cmd, cmd_len); + kfree(cmd); + + return ret; +} + static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) { struct kvaser_usb *dev = priv->dev; @@ -2149,6 +2201,7 @@ const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = { .dev_get_software_details = kvaser_usb_hydra_get_software_details, .dev_get_card_info = kvaser_usb_hydra_get_card_info, .dev_get_capabilities = kvaser_usb_hydra_get_capabilities, + .dev_set_led = kvaser_usb_hydra_set_led, .dev_set_opt_mode = kvaser_usb_hydra_set_opt_mode, .dev_start_chip = kvaser_usb_hydra_start_chip, .dev_stop_chip = kvaser_usb_hydra_stop_chip, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 6a45adcc45bd..a67855521ccc 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -10,6 +10,7 @@ * Copyright (C) 2015 Valeo S.A. */ +#include #include #include #include @@ -81,6 +82,8 @@ #define CMD_FLUSH_QUEUE_REPLY 68 #define CMD_GET_CAPABILITIES_REQ 95 #define CMD_GET_CAPABILITIES_RESP 96 +#define CMD_LED_ACTION_REQ 101 +#define CMD_LED_ACTION_RESP 102 #define CMD_LEAF_LOG_MESSAGE 106 @@ -173,6 +176,21 @@ struct kvaser_cmd_busparams { struct kvaser_usb_busparams busparams; } __packed; +/* The device has one LED per CAN channel + * The LSB of action field controls the state: + * 0 = ON + * 1 = OFF + * The remaining bits of action field is the LED index + */ +#define KVASER_USB_LEAF_LED_IDX_MASK GENMASK(31, 1) +#define KVASER_USB_LEAF_LED_YELLOW_CH0_IDX 2 +struct kvaser_cmd_led_action_req { + u8 tid; + u8 action; + __le16 duration_ms; + u8 padding[24]; +} __packed; + struct kvaser_cmd_tx_can { u8 channel; u8 tid; @@ -359,6 +377,8 @@ struct kvaser_cmd { struct kvaser_cmd_cardinfo cardinfo; struct kvaser_cmd_busparams busparams; + struct kvaser_cmd_led_action_req led_action_req; + struct kvaser_cmd_rx_can_header rx_can_header; struct kvaser_cmd_tx_acknowledge_header tx_acknowledge_header; @@ -409,6 +429,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, + [CMD_LED_ACTION_RESP] = CMD_SIZE_ANY, }; static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { @@ -423,6 +444,8 @@ static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event), [CMD_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = kvaser_fsize(u.usbcan.clk_overflow_event), + /* ignored events: */ + [CMD_LED_ACTION_RESP] = CMD_SIZE_ANY, }; /* Summary of a kvaser error event, for a unified Leaf/Usbcan error @@ -924,6 +947,34 @@ static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_leaf_set_led(struct kvaser_usb_net_priv *priv, + enum kvaser_usb_led_state state, + u16 duration_ms) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_LED_ACTION_REQ; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_led_action_req); + cmd->u.led_action_req.tid = 0xff; + + cmd->u.led_action_req.duration_ms = cpu_to_le16(duration_ms); + cmd->u.led_action_req.action = state | + FIELD_PREP(KVASER_USB_LEAF_LED_IDX_MASK, + KVASER_USB_LEAF_LED_YELLOW_CH0_IDX + + priv->channel); + + ret = kvaser_usb_send_cmd(dev, cmd, cmd->len); + kfree(cmd); + + return ret; +} + static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev) { int err = 0; @@ -1638,6 +1689,8 @@ static void kvaser_usb_leaf_handle_command(struct kvaser_usb *dev, if (dev->driver_info->family != KVASER_LEAF) goto warn; break; + case CMD_LED_ACTION_RESP: + break; default: warn: dev_warn(&dev->intf->dev, "Unhandled command (%d)\n", cmd->id); @@ -1927,6 +1980,7 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { .dev_get_software_details = NULL, .dev_get_card_info = kvaser_usb_leaf_get_card_info, .dev_get_capabilities = kvaser_usb_leaf_get_capabilities, + .dev_set_led = kvaser_usb_leaf_set_led, .dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode, .dev_start_chip = kvaser_usb_leaf_start_chip, .dev_stop_chip = kvaser_usb_leaf_stop_chip, -- 2.47.2 From: Jimmy Assarsson Add support for ethtool set_phys_id(), to physically locate devices by flashing a LED on the device. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-3-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index daf42080f942..c74875f978c4 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -753,6 +753,31 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, return ret; } +static int kvaser_usb_set_phys_id(struct net_device *netdev, + enum ethtool_phys_id_state state) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + const struct kvaser_usb_dev_ops *ops = priv->dev->driver_info->ops; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + return 3; /* 3 On/Off cycles per second */ + + case ETHTOOL_ID_ON: + return ops->dev_set_led(priv, KVASER_USB_LED_ON, 1000); + + case ETHTOOL_ID_OFF: + return ops->dev_set_led(priv, KVASER_USB_LED_OFF, 1000); + + case ETHTOOL_ID_INACTIVE: + /* Turn LED off and restore standard function after 1ms */ + return ops->dev_set_led(priv, KVASER_USB_LED_OFF, 1); + + default: + return -EINVAL; + } +} + static const struct net_device_ops kvaser_usb_netdev_ops = { .ndo_open = kvaser_usb_open, .ndo_stop = kvaser_usb_close, @@ -763,6 +788,7 @@ static const struct net_device_ops kvaser_usb_netdev_ops = { static const struct ethtool_ops kvaser_usb_ethtool_ops = { .get_ts_info = can_ethtool_op_get_ts_info_hwts, + .set_phys_id = kvaser_usb_set_phys_id, }; static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) -- 2.47.2 From: Jimmy Assarsson Assign netdev.dev_port based on the device channel index, to indicate the port number of the network device. While this driver already uses netdev.dev_id for that purpose, dev_port is more appropriate. However, retain dev_id to avoid potential regressions. Fixes: 3e66d0138c05 ("can: populate netdev::dev_id for udev discrimination") Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-4-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index c74875f978c4..7be8604bf760 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -878,6 +878,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) netdev->ethtool_ops = &kvaser_usb_ethtool_ops; SET_NETDEV_DEV(netdev, &dev->intf->dev); netdev->dev_id = channel; + netdev->dev_port = channel; dev->nets[channel] = priv; -- 2.47.2 From: Jimmy Assarsson Add intermediate variables, for readability and to simplify future patches. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-5-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 7be8604bf760..46e6cda0bf8d 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -364,10 +364,13 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) err = usb_submit_urb(urb, GFP_ATOMIC); if (err == -ENODEV) { for (i = 0; i < dev->nchannels; i++) { - if (!dev->nets[i]) + struct kvaser_usb_net_priv *priv; + + priv = dev->nets[i]; + if (!priv) continue; - netif_device_detach(dev->nets[i]->netdev); + netif_device_detach(priv->netdev); } } else if (err) { dev_err(&dev->intf->dev, @@ -795,24 +798,27 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) { const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; int i; + struct kvaser_usb_net_priv *priv; for (i = 0; i < dev->nchannels; i++) { - if (!dev->nets[i]) + priv = dev->nets[i]; + if (!priv) continue; - unregister_candev(dev->nets[i]->netdev); + unregister_candev(priv->netdev); } kvaser_usb_unlink_all_urbs(dev); for (i = 0; i < dev->nchannels; i++) { - if (!dev->nets[i]) + priv = dev->nets[i]; + if (!priv) continue; if (ops->dev_remove_channel) - ops->dev_remove_channel(dev->nets[i]); + ops->dev_remove_channel(priv); - free_candev(dev->nets[i]->netdev); + free_candev(priv->netdev); } } -- 2.47.2 From: Jimmy Assarsson Move comment regarding max_tx_urbs, to where the struct member is declared. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-6-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index 032dc1821f04..fba972e7220d 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -101,12 +101,12 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; + u32 fw_version; + unsigned int nchannels; /* @max_tx_urbs: Firmware-reported maximum number of outstanding, * not yet ACKed, transmissions on this device. This value is * also used as a sentinel for marking free tx contexts. */ - u32 fw_version; - unsigned int nchannels; unsigned int max_tx_urbs; struct kvaser_usb_dev_card_data card_data; -- 2.47.2 From: Jimmy Assarsson Store firmware version in kvaser_usb_fw_version struct, specifying the different components of the version number. And drop debug prinout of firmware version, since later patches will expose it via the devlink interface. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-7-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 12 +++++++++++- drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c | 5 ----- drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 6 +++++- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index fba972e7220d..a36d86494113 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -47,6 +47,10 @@ #define KVASER_USB_CAP_EXT_CAP 0x02 #define KVASER_USB_HYDRA_CAP_EXT_CMD 0x04 +#define KVASER_USB_SW_VERSION_MAJOR_MASK GENMASK(31, 24) +#define KVASER_USB_SW_VERSION_MINOR_MASK GENMASK(23, 16) +#define KVASER_USB_SW_VERSION_BUILD_MASK GENMASK(15, 0) + struct kvaser_usb_dev_cfg; enum kvaser_usb_leaf_family { @@ -83,6 +87,12 @@ struct kvaser_usb_tx_urb_context { u32 echo_index; }; +struct kvaser_usb_fw_version { + u8 major; + u8 minor; + u16 build; +}; + struct kvaser_usb_busparams { __le32 bitrate; u8 tseg1; @@ -101,7 +111,7 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; - u32 fw_version; + struct kvaser_usb_fw_version fw_version; unsigned int nchannels; /* @max_tx_urbs: Firmware-reported maximum number of outstanding, * not yet ACKed, transmissions on this device. This value is diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 46e6cda0bf8d..2313fbc1a2c3 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -963,11 +963,6 @@ static int kvaser_usb_probe(struct usb_interface *intf, if (WARN_ON(!dev->cfg)) return -ENODEV; - dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", - ((dev->fw_version >> 24) & 0xff), - ((dev->fw_version >> 16) & 0xff), - (dev->fw_version & 0xffff)); - dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs); err = ops->dev_get_card_info(dev); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index a4402b4845c6..388ebf2b1a5b 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1839,6 +1839,7 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) size_t cmd_len; int err; u32 flags; + u32 fw_version; struct kvaser_usb_dev_card_data *card_data = &dev->card_data; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1863,7 +1864,10 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) if (err) goto end; - dev->fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version); + fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version); + dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK, fw_version); + dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK, fw_version); + dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK, fw_version); flags = le32_to_cpu(cmd->sw_detail_res.sw_flags); if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BAD) { diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index a67855521ccc..b4f5d4fba630 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -741,9 +741,13 @@ static int kvaser_usb_leaf_send_simple_cmd(const struct kvaser_usb *dev, static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev, const struct leaf_cmd_softinfo *softinfo) { + u32 fw_version; u32 sw_options = le32_to_cpu(softinfo->sw_options); - dev->fw_version = le32_to_cpu(softinfo->fw_version); + fw_version = le32_to_cpu(softinfo->fw_version); + dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK, fw_version); + dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK, fw_version); + dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK, fw_version); dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx); if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP) @@ -784,6 +788,7 @@ static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev) { struct kvaser_cmd cmd; int err; + u32 fw_version; err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO, 0); if (err) @@ -798,7 +803,13 @@ static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev) kvaser_usb_leaf_get_software_info_leaf(dev, &cmd.u.leaf.softinfo); break; case KVASER_USBCAN: - dev->fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version); + fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version); + dev->fw_version.major = FIELD_GET(KVASER_USB_SW_VERSION_MAJOR_MASK, + fw_version); + dev->fw_version.minor = FIELD_GET(KVASER_USB_SW_VERSION_MINOR_MASK, + fw_version); + dev->fw_version.build = FIELD_GET(KVASER_USB_SW_VERSION_BUILD_MASK, + fw_version); dev->max_tx_urbs = le16_to_cpu(cmd.u.usbcan.softinfo.max_outstanding_tx); dev->cfg = &kvaser_usb_leaf_usbcan_dev_cfg; -- 2.47.2 From: Jimmy Assarsson Store additional device information; EAN (product number), serial_number and hardware revision. Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-8-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 3 +++ drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 6 +++++- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index a36d86494113..35c2cf3d4486 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -111,7 +111,10 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; + u32 ean[2]; + u32 serial_number; struct kvaser_usb_fw_version fw_version; + u8 hw_revision; unsigned int nchannels; /* @max_tx_urbs: Firmware-reported maximum number of outstanding, * not yet ACKed, transmissions on this device. This value is diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 388ebf2b1a5b..a59f20dad692 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -114,7 +114,7 @@ struct kvaser_cmd_card_info { __le32 clock_res; __le32 mfg_date; __le32 ean[2]; - u8 hw_version; + u8 hw_revision; u8 usb_mode; u8 hw_type; u8 reserved0; @@ -1918,6 +1918,10 @@ static int kvaser_usb_hydra_get_card_info(struct kvaser_usb *dev) err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CARD_INFO_RESP, &cmd); if (err) return err; + dev->ean[1] = le32_to_cpu(cmd.card_info.ean[1]); + dev->ean[0] = le32_to_cpu(cmd.card_info.ean[0]); + dev->serial_number = le32_to_cpu(cmd.card_info.serial_number); + dev->hw_revision = cmd.card_info.hw_revision; dev->nchannels = cmd.card_info.nchannels; if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index b4f5d4fba630..c29828a94ad0 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -138,7 +138,7 @@ struct kvaser_cmd_cardinfo { __le32 padding0; __le32 clock_resolution; __le32 mfgdate; - u8 ean[8]; + __le32 ean[2]; u8 hw_revision; union { struct { @@ -854,6 +854,10 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev) (dev->driver_info->family == KVASER_USBCAN && dev->nchannels > MAX_USBCAN_NET_DEVICES)) return -EINVAL; + dev->ean[1] = le32_to_cpu(cmd.u.cardinfo.ean[1]); + dev->ean[0] = le32_to_cpu(cmd.u.cardinfo.ean[0]); + dev->serial_number = le32_to_cpu(cmd.u.cardinfo.serial_number); + dev->hw_revision = cmd.u.cardinfo.hw_revision; return 0; } -- 2.47.2 From: Jimmy Assarsson Add devlink support at device level. Example output: $ devlink dev usb/1-1.3:1.0 $ devlink dev info usb/1-1.3:1.0: driver kvaser_usb Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-9-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/Kconfig | 1 + drivers/net/can/usb/kvaser_usb/Makefile | 2 +- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 3 + .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 72 ++++++++++++------- .../can/usb/kvaser_usb/kvaser_usb_devlink.c | 11 +++ 5 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 9dae0c71a2e1..a7547a83120e 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -66,6 +66,7 @@ config CAN_GS_USB config CAN_KVASER_USB tristate "Kvaser CAN/USB interface" + select NET_DEVLINK help This driver adds support for Kvaser CAN/USB devices like Kvaser Leaf Light, Kvaser USBcan II and Kvaser Memorator Pro 5xHS. diff --git a/drivers/net/can/usb/kvaser_usb/Makefile b/drivers/net/can/usb/kvaser_usb/Makefile index cf260044f0b9..41b4a11555aa 100644 --- a/drivers/net/can/usb/kvaser_usb/Makefile +++ b/drivers/net/can/usb/kvaser_usb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o -kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o +kvaser_usb-y = kvaser_usb_core.o kvaser_usb_devlink.o kvaser_usb_leaf.o kvaser_usb_hydra.o diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index 35c2cf3d4486..d5f913ac9b44 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -226,6 +227,8 @@ struct kvaser_usb_dev_cfg { extern const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops; extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops; +extern const struct devlink_ops kvaser_usb_devlink_ops; + void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv); int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 2313fbc1a2c3..b9b2e120a5cd 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -914,6 +914,7 @@ static int kvaser_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct kvaser_usb *dev; + struct devlink *devlink; int err; int i; const struct kvaser_usb_driver_info *driver_info; @@ -923,17 +924,20 @@ static int kvaser_usb_probe(struct usb_interface *intf, if (!driver_info) return -ENODEV; - dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + devlink = devlink_alloc(&kvaser_usb_devlink_ops, sizeof(*dev), &intf->dev); + if (!devlink) return -ENOMEM; + dev = devlink_priv(devlink); dev->intf = intf; dev->driver_info = driver_info; ops = driver_info->ops; err = ops->dev_setup_endpoints(dev); - if (err) - return dev_err_probe(&intf->dev, err, "Cannot get usb endpoint(s)"); + if (err) { + dev_err_probe(&intf->dev, err, "Cannot get usb endpoint(s)"); + goto free_devlink; + } dev->udev = interface_to_usbdev(intf); @@ -944,50 +948,66 @@ static int kvaser_usb_probe(struct usb_interface *intf, dev->card_data.ctrlmode_supported = 0; dev->card_data.capabilities = 0; err = ops->dev_init_card(dev); - if (err) - return dev_err_probe(&intf->dev, err, - "Failed to initialize card\n"); + if (err) { + dev_err_probe(&intf->dev, err, + "Failed to initialize card\n"); + goto free_devlink; + } err = ops->dev_get_software_info(dev); - if (err) - return dev_err_probe(&intf->dev, err, - "Cannot get software info\n"); + if (err) { + dev_err_probe(&intf->dev, err, + "Cannot get software info\n"); + goto free_devlink; + } if (ops->dev_get_software_details) { err = ops->dev_get_software_details(dev); - if (err) - return dev_err_probe(&intf->dev, err, - "Cannot get software details\n"); + if (err) { + dev_err_probe(&intf->dev, err, + "Cannot get software details\n"); + goto free_devlink; + } } - if (WARN_ON(!dev->cfg)) - return -ENODEV; + if (WARN_ON(!dev->cfg)) { + err = -ENODEV; + goto free_devlink; + } dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs); err = ops->dev_get_card_info(dev); - if (err) - return dev_err_probe(&intf->dev, err, - "Cannot get card info\n"); + if (err) { + dev_err_probe(&intf->dev, err, + "Cannot get card info\n"); + goto free_devlink; + } if (ops->dev_get_capabilities) { err = ops->dev_get_capabilities(dev); if (err) { - kvaser_usb_remove_interfaces(dev); - return dev_err_probe(&intf->dev, err, - "Cannot get capabilities\n"); + dev_err_probe(&intf->dev, err, + "Cannot get capabilities\n"); + goto remove_interfaces; } } for (i = 0; i < dev->nchannels; i++) { err = kvaser_usb_init_one(dev, i); - if (err) { - kvaser_usb_remove_interfaces(dev); - return err; - } + if (err) + goto remove_interfaces; } + devlink_register(devlink); return 0; + +remove_interfaces: + kvaser_usb_remove_interfaces(dev); +free_devlink: + devlink_free(devlink); + + return err; } static void kvaser_usb_disconnect(struct usb_interface *intf) @@ -1000,6 +1020,8 @@ static void kvaser_usb_disconnect(struct usb_interface *intf) return; kvaser_usb_remove_interfaces(dev); + devlink_unregister(priv_to_devlink(dev)); + devlink_free(priv_to_devlink(dev)); } static struct usb_driver kvaser_usb_driver = { diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c new file mode 100644 index 000000000000..dbe7fa64558a --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* kvaser_usb devlink functions + * + * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved. + */ +#include "kvaser_usb.h" + +#include + +const struct devlink_ops kvaser_usb_devlink_ops = { +}; -- 2.47.2 From: Jimmy Assarsson Expose device information via devlink info_get(): * Serial number * Firmware version * Hardware revision * EAN (product number) Example output: $ devlink dev usb/1-1.2:1.0 $ devlink dev info usb/1-1.2:1.0: driver kvaser_usb serial_number 1020 versions: fixed: board.rev 1 board.id 7330130009653 running: fw 3.22.527 Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-10-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../can/usb/kvaser_usb/kvaser_usb_devlink.c | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c index dbe7fa64558a..aa06bd1fa125 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c @@ -7,5 +7,56 @@ #include +#define KVASER_USB_EAN_MSB 0x00073301 + +static int kvaser_usb_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct kvaser_usb *dev = devlink_priv(devlink); + char buf[] = "73301XXXXXXXXXX"; + int ret; + + if (dev->serial_number) { + snprintf(buf, sizeof(buf), "%u", dev->serial_number); + ret = devlink_info_serial_number_put(req, buf); + if (ret) + return ret; + } + + if (dev->fw_version.major) { + snprintf(buf, sizeof(buf), "%u.%u.%u", + dev->fw_version.major, + dev->fw_version.minor, + dev->fw_version.build); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (ret) + return ret; + } + + if (dev->hw_revision) { + snprintf(buf, sizeof(buf), "%u", dev->hw_revision); + ret = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, + buf); + if (ret) + return ret; + } + + if (dev->ean[1] == KVASER_USB_EAN_MSB) { + snprintf(buf, sizeof(buf), "%x%08x", dev->ean[1], dev->ean[0]); + ret = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, + buf); + if (ret) + return ret; + } + + return 0; +} + const struct devlink_ops kvaser_usb_devlink_ops = { + .info_get = kvaser_usb_devlink_info_get, }; -- 2.47.2 From: Jimmy Assarsson Register each CAN channel of the device as an devlink physical port. This makes it easier to get device information for a given network interface (i.e. can2). Example output: $ devlink dev usb/1-1.3:1.0 $ devlink port usb/1-1.3:1.0/0: type eth netdev can0 flavour physical port 0 splittable false usb/1-1.3:1.0/1: type eth netdev can1 flavour physical port 1 splittable false $ devlink port show can1 usb/1-1.3:1.0/1: type eth netdev can1 flavour physical port 0 splittable false $ devlink dev info usb/1-1.3:1.0: driver kvaser_usb serial_number 1020 versions: fixed: board.rev 1 board.id 7330130009653 running: fw 3.22.527 $ ethtool -i can1 driver: kvaser_usb version: 6.12.10-arch1-1 firmware-version: 3.22.527 expansion-rom-version: bus-info: 1-1.3:1.0 supports-statistics: no supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-11-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 4 +++ .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 15 ++++++++--- .../can/usb/kvaser_usb/kvaser_usb_devlink.c | 25 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index d5f913ac9b44..46a1b6907a50 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -131,6 +131,7 @@ struct kvaser_usb { struct kvaser_usb_net_priv { struct can_priv can; + struct devlink_port devlink_port; struct can_berr_counter bec; /* subdriver-specific data */ @@ -229,6 +230,9 @@ extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops; extern const struct devlink_ops kvaser_usb_devlink_ops; +int kvaser_usb_devlink_port_register(struct kvaser_usb_net_priv *priv); +void kvaser_usb_devlink_port_unregister(struct kvaser_usb_net_priv *priv); + void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv); int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index b9b2e120a5cd..90e77fa0ff4a 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -818,6 +818,7 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) if (ops->dev_remove_channel) ops->dev_remove_channel(priv); + kvaser_usb_devlink_port_unregister(priv); free_candev(priv->netdev); } } @@ -891,20 +892,28 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) if (ops->dev_init_channel) { err = ops->dev_init_channel(priv); if (err) - goto err; + goto candev_free; + } + + err = kvaser_usb_devlink_port_register(priv); + if (err) { + dev_err(&dev->intf->dev, "Failed to register devlink port\n"); + goto candev_free; } err = register_candev(netdev); if (err) { dev_err(&dev->intf->dev, "Failed to register CAN device\n"); - goto err; + goto unregister_devlink_port; } netdev_dbg(netdev, "device registered\n"); return 0; -err: +unregister_devlink_port: + kvaser_usb_devlink_port_unregister(priv); +candev_free: free_candev(netdev); dev->nets[channel] = NULL; return err; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c index aa06bd1fa125..e838b82298ae 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c @@ -5,6 +5,7 @@ */ #include "kvaser_usb.h" +#include #include #define KVASER_USB_EAN_MSB 0x00073301 @@ -60,3 +61,27 @@ static int kvaser_usb_devlink_info_get(struct devlink *devlink, const struct devlink_ops kvaser_usb_devlink_ops = { .info_get = kvaser_usb_devlink_info_get, }; + +int kvaser_usb_devlink_port_register(struct kvaser_usb_net_priv *priv) +{ + int ret; + struct devlink_port_attrs attrs = { + .flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL, + .phys.port_number = priv->channel, + }; + devlink_port_attrs_set(&priv->devlink_port, &attrs); + + ret = devlink_port_register(priv_to_devlink(priv->dev), + &priv->devlink_port, priv->channel); + if (ret) + return ret; + + SET_NETDEV_DEVLINK_PORT(priv->netdev, &priv->devlink_port); + + return 0; +} + +void kvaser_usb_devlink_port_unregister(struct kvaser_usb_net_priv *priv) +{ + devlink_port_unregister(&priv->devlink_port); +} -- 2.47.2 From: Jimmy Assarsson List the version information reported by the kvaser_usb driver through devlink. Suggested-by: Vincent Mailhol Reviewed-by: Vincent Mailhol Signed-off-by: Jimmy Assarsson Link: https://patch.msgid.link/20250725123452.41-12-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- Documentation/networking/devlink/index.rst | 1 + .../networking/devlink/kvaser_usb.rst | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Documentation/networking/devlink/kvaser_usb.rst diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index 053fbafeb491..270a65a01411 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -86,6 +86,7 @@ parameters, info versions, and other features it supports. ice ixgbe kvaser_pciefd + kvaser_usb mlx4 mlx5 mlxsw diff --git a/Documentation/networking/devlink/kvaser_usb.rst b/Documentation/networking/devlink/kvaser_usb.rst new file mode 100644 index 000000000000..403db3766cb4 --- /dev/null +++ b/Documentation/networking/devlink/kvaser_usb.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +kvaser_usb devlink support +========================== + +This document describes the devlink features implemented by the +``kvaser_usb`` device driver. + +Info versions +============= + +The ``kvaser_usb`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the device. Also available + through ``ethtool -i`` as ``firmware-version``. + * - ``board.rev`` + - fixed + - The device hardware revision. + * - ``board.id`` + - fixed + - The device EAN (product number). + * - ``serial_number`` + - fixed + - The device serial number. -- 2.47.2