diff options
Diffstat (limited to 'drivers/net/can/usb')
20 files changed, 1270 insertions, 94 deletions
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 9dae0c71a2e1..cf65a90816b9 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. @@ -133,6 +134,17 @@ config CAN_MCBA_USB This driver supports the CAN BUS Analyzer interface from Microchip (http://www.microchip.com/development-tools/). +config CAN_NCT6694 + tristate "Nuvoton NCT6694 Socket CANfd support" + depends on MFD_NCT6694 + select CAN_RX_OFFLOAD + help + If you say yes to this option, support will be included for Nuvoton + NCT6694, a USB device to socket CANfd controller. + + This driver can also be built as a module. If so, the module will + be called nct6694_canfd. + config CAN_PEAK_USB tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD" help diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index 8b11088e9a59..fcafb1ac262e 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_CAN_F81604) += f81604.o obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/ obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o +obj-$(CONFIG_CAN_NCT6694) += nct6694_canfd.o obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ obj-$(CONFIG_CAN_UCAN) += ucan.o diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c index 27a3818885c2..9bc1824d7be6 100644 --- a/drivers/net/can/usb/esd_usb.c +++ b/drivers/net/can/usb/esd_usb.c @@ -9,6 +9,7 @@ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/err.h> #include <linux/ethtool.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -274,6 +275,7 @@ struct esd_usb { int net_count; u32 version; int rxinitdone; + int in_usb_disconnect; void *rxbuf[ESD_USB_MAX_RX_URBS]; dma_addr_t rxbuf_dma[ESD_USB_MAX_RX_URBS]; }; @@ -480,7 +482,7 @@ static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv, static void esd_usb_read_bulk_callback(struct urb *urb) { struct esd_usb *dev = urb->context; - int retval; + int err; int pos = 0; int i; @@ -496,7 +498,7 @@ static void esd_usb_read_bulk_callback(struct urb *urb) default: dev_info(dev->udev->dev.parent, - "Rx URB aborted (%d)\n", urb->status); + "Rx URB aborted (%pe)\n", ERR_PTR(urb->status)); goto resubmit_urb; } @@ -539,15 +541,15 @@ resubmit_urb: urb->transfer_buffer, ESD_USB_RX_BUFFER_SIZE, esd_usb_read_bulk_callback, dev); - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval == -ENODEV) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err == -ENODEV) { for (i = 0; i < dev->net_count; i++) { if (dev->nets[i]) netif_device_detach(dev->nets[i]->netdev); } - } else if (retval) { + } else if (err) { dev_err(dev->udev->dev.parent, - "failed resubmitting read bulk urb: %d\n", retval); + "failed resubmitting read bulk urb: %pe\n", ERR_PTR(err)); } } @@ -572,7 +574,7 @@ static void esd_usb_write_bulk_callback(struct urb *urb) return; if (urb->status) - netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + netdev_info(netdev, "Tx URB aborted (%pe)\n", ERR_PTR(urb->status)); netif_trans_update(netdev); } @@ -758,7 +760,7 @@ out: if (err == -ENODEV) netif_device_detach(netdev); if (err) - netdev_err(netdev, "couldn't start device: %d\n", err); + netdev_err(netdev, "couldn't start device: %pe\n", ERR_PTR(err)); kfree(msg); return err; @@ -800,7 +802,6 @@ static int esd_usb_open(struct net_device *netdev) /* finally start device */ err = esd_usb_start(priv); if (err) { - netdev_warn(netdev, "couldn't start device: %d\n", err); close_candev(netdev); return err; } @@ -923,7 +924,7 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb, if (err == -ENODEV) netif_device_detach(netdev); else - netdev_warn(netdev, "failed tx_urb %d\n", err); + netdev_warn(netdev, "failed tx_urb %pe\n", ERR_PTR(err)); goto releasebuf; } @@ -947,10 +948,11 @@ nourbmem: return ret; } -static int esd_usb_close(struct net_device *netdev) +/* Stop interface */ +static int esd_usb_stop(struct esd_usb_net_priv *priv) { - struct esd_usb_net_priv *priv = netdev_priv(netdev); union esd_usb_msg *msg; + int err; int i; msg = kmalloc(sizeof(*msg), GFP_KERNEL); @@ -964,8 +966,11 @@ static int esd_usb_close(struct net_device *netdev) msg->filter.option = ESD_USB_ID_ENABLE; /* start with segment 0 */ for (i = 0; i <= ESD_USB_MAX_ID_SEGMENT; i++) msg->filter.mask[i] = 0; - if (esd_usb_send_msg(priv->usb, msg) < 0) - netdev_err(netdev, "sending idadd message failed\n"); + err = esd_usb_send_msg(priv->usb, msg); + if (err < 0) { + netdev_err(priv->netdev, "sending idadd message failed: %pe\n", ERR_PTR(err)); + goto bail; + } /* set CAN controller to reset mode */ msg->hdr.len = sizeof(struct esd_usb_set_baudrate_msg) / sizeof(u32); /* # of 32bit words */ @@ -973,8 +978,25 @@ static int esd_usb_close(struct net_device *netdev) msg->setbaud.net = priv->index; msg->setbaud.rsvd = 0; msg->setbaud.baud = cpu_to_le32(ESD_USB_NO_BAUDRATE); - if (esd_usb_send_msg(priv->usb, msg) < 0) - netdev_err(netdev, "sending setbaud message failed\n"); + err = esd_usb_send_msg(priv->usb, msg); + if (err < 0) + netdev_err(priv->netdev, "sending setbaud message failed: %pe\n", ERR_PTR(err)); + +bail: + kfree(msg); + + return err; +} + +static int esd_usb_close(struct net_device *netdev) +{ + struct esd_usb_net_priv *priv = netdev_priv(netdev); + int err = 0; + + if (!priv->usb->in_usb_disconnect) { + /* It's moot to try this in usb_disconnect()! */ + err = esd_usb_stop(priv); + } priv->can.state = CAN_STATE_STOPPED; @@ -982,9 +1004,7 @@ static int esd_usb_close(struct net_device *netdev) close_candev(netdev); - kfree(msg); - - return 0; + return err; } static const struct net_device_ops esd_usb_netdev_ops = { @@ -1251,14 +1271,14 @@ static int esd_usb_probe_one_net(struct usb_interface *intf, int index) err = register_candev(netdev); if (err) { - dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + dev_err(&intf->dev, "couldn't register CAN device: %pe\n", ERR_PTR(err)); free_candev(netdev); err = -ENOMEM; goto done; } dev->nets[index] = priv; - netdev_info(netdev, "device %s registered\n", netdev->name); + netdev_info(netdev, "registered\n"); done: return err; @@ -1354,9 +1374,11 @@ static void esd_usb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (dev) { + dev->in_usb_disconnect = 1; for (i = 0; i < dev->net_count; i++) { if (dev->nets[i]) { netdev = dev->nets[i]->netdev; + netdev_info(netdev, "unregister\n"); unregister_netdev(netdev); free_candev(netdev); } diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index db1acf6d504c..adc91873c083 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -7,7 +7,7 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (c) 2020-2025 Vincent Mailhol <mailhol@kernel.org> */ #include <linux/unaligned.h> @@ -1977,6 +1977,7 @@ static const struct net_device_ops es58x_netdev_ops = { .ndo_stop = es58x_stop, .ndo_start_xmit = es58x_start_xmit, .ndo_eth_ioctl = can_eth_ioctl_hwts, + .ndo_change_mtu = can_change_mtu, }; static const struct ethtool_ops es58x_ethtool_ops = { diff --git a/drivers/net/can/usb/etas_es58x/es58x_fd.c b/drivers/net/can/usb/etas_es58x/es58x_fd.c index d924b053677b..6476add1c105 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_fd.c +++ b/drivers/net/can/usb/etas_es58x/es58x_fd.c @@ -429,7 +429,7 @@ static int es58x_fd_enable_channel(struct es58x_priv *priv) es58x_fd_convert_bittiming(&tx_conf_msg.data_bittiming, &priv->can.fd.data_bittiming); - if (can_tdc_is_enabled(&priv->can)) { + if (can_fd_tdc_is_enabled(&priv->can)) { tx_conf_msg.tdc_enabled = 1; tx_conf_msg.tdco = cpu_to_le16(priv->can.fd.tdc.tdco); tx_conf_msg.tdcf = cpu_to_le16(priv->can.fd.tdc.tdcf); diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index bb6335278e46..c9482d6e947b 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -420,7 +420,7 @@ static inline int gs_usb_get_timestamp(const struct gs_usb *parent, return 0; } -static u64 gs_usb_timestamp_read(const struct cyclecounter *cc) __must_hold(&dev->tc_lock) +static u64 gs_usb_timestamp_read(struct cyclecounter *cc) __must_hold(&dev->tc_lock) { struct gs_usb *parent = container_of(cc, struct gs_usb, cc); u32 timestamp = 0; 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 f6c77eca9f43..46a1b6907a50 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 <linux/spinlock.h> #include <linux/types.h> #include <linux/usb.h> +#include <net/devlink.h> #include <linux/can.h> #include <linux/can/dev.h> @@ -47,6 +48,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 { @@ -54,6 +59,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]; @@ -78,6 +88,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; @@ -96,12 +112,15 @@ 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 * 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; @@ -112,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 */ @@ -149,6 +169,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 +197,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); @@ -204,6 +228,11 @@ 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; + +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 daf42080f942..90e77fa0ff4a 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 @@ resubmit_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, @@ -753,6 +756,31 @@ freeurb: 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,30 +791,35 @@ 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) { 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); + kvaser_usb_devlink_port_unregister(priv); + free_candev(priv->netdev); } } @@ -852,26 +885,35 @@ 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; 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; @@ -881,6 +923,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; @@ -890,17 +933,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); @@ -911,55 +957,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; - - dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", - ((dev->fw_version >> 24) & 0xff), - ((dev->fw_version >> 16) & 0xff), - (dev->fw_version & 0xffff)); + 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) @@ -972,6 +1029,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..e838b82298ae --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +/* kvaser_usb devlink functions + * + * Copyright (C) 2025 KVASER AB, Sweden. All rights reserved. + */ +#include "kvaser_usb.h" + +#include <linux/netdevice.h> +#include <net/devlink.h> + +#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, +}; + +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); +} 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..a59f20dad692 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 <linux/bitfield.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/gfp.h> @@ -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 @@ -111,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; @@ -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: @@ -1817,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); @@ -1841,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) { @@ -1892,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) @@ -1946,6 +1976,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 +2209,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..c29828a94ad0 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 <linux/bitfield.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/gfp.h> @@ -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 @@ -135,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 { @@ -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 @@ -718,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) @@ -761,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) @@ -775,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; @@ -820,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; } @@ -924,6 +962,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 +1704,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 +1995,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, diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c index 41c0a1c399bf..1f9b915094e6 100644 --- a/drivers/net/can/usb/mcba_usb.c +++ b/drivers/net/can/usb/mcba_usb.c @@ -761,6 +761,7 @@ static const struct net_device_ops mcba_netdev_ops = { .ndo_open = mcba_usb_open, .ndo_stop = mcba_usb_close, .ndo_start_xmit = mcba_usb_start_xmit, + .ndo_change_mtu = can_change_mtu, }; static const struct ethtool_ops mcba_ethtool_ops = { diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c new file mode 100644 index 000000000000..8deff16491a1 --- /dev/null +++ b/drivers/net/can/usb/nct6694_canfd.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Nuvoton NCT6694 Socket CANfd driver based on USB interface. + * + * Copyright (C) 2025 Nuvoton Technology Corp. + */ + +#include <linux/bitfield.h> +#include <linux/can/dev.h> +#include <linux/can/rx-offload.h> +#include <linux/ethtool.h> +#include <linux/idr.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/mfd/nct6694.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> + +#define DEVICE_NAME "nct6694-canfd" + +/* USB command module type for NCT6694 CANfd controller. + * This defines the module type used for communication with the NCT6694 + * CANfd controller over the USB interface. + */ +#define NCT6694_CANFD_MOD 0x05 + +/* Command 00h - CAN Setting and Initialization */ +#define NCT6694_CANFD_SETTING 0x00 +#define NCT6694_CANFD_SETTING_ACTIVE_CTRL1 BIT(0) +#define NCT6694_CANFD_SETTING_ACTIVE_CTRL2 BIT(1) +#define NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP BIT(2) +#define NCT6694_CANFD_SETTING_CTRL1_MON BIT(0) +#define NCT6694_CANFD_SETTING_CTRL1_NISO BIT(1) +#define NCT6694_CANFD_SETTING_CTRL1_LBCK BIT(2) +#define NCT6694_CANFD_SETTING_NBTP_NTSEG2 GENMASK(6, 0) +#define NCT6694_CANFD_SETTING_NBTP_NTSEG1 GENMASK(15, 8) +#define NCT6694_CANFD_SETTING_NBTP_NBRP GENMASK(24, 16) +#define NCT6694_CANFD_SETTING_NBTP_NSJW GENMASK(31, 25) +#define NCT6694_CANFD_SETTING_DBTP_DSJW GENMASK(3, 0) +#define NCT6694_CANFD_SETTING_DBTP_DTSEG2 GENMASK(7, 4) +#define NCT6694_CANFD_SETTING_DBTP_DTSEG1 GENMASK(12, 8) +#define NCT6694_CANFD_SETTING_DBTP_DBRP GENMASK(20, 16) +#define NCT6694_CANFD_SETTING_DBTP_TDC BIT(23) + +/* Command 01h - CAN Information */ +#define NCT6694_CANFD_INFORMATION 0x01 +#define NCT6694_CANFD_INFORMATION_SEL 0x00 + +/* Command 02h - CAN Event */ +#define NCT6694_CANFD_EVENT 0x02 +#define NCT6694_CANFD_EVENT_SEL(idx, mask) \ + ((idx ? 0x80 : 0x00) | ((mask) & 0x7F)) + +#define NCT6694_CANFD_EVENT_MASK GENMASK(5, 0) +#define NCT6694_CANFD_EVT_TX_FIFO_EMPTY BIT(7) /* Read-clear */ +#define NCT6694_CANFD_EVT_RX_DATA_LOST BIT(5) /* Read-clear */ +#define NCT6694_CANFD_EVT_RX_DATA_IN BIT(7) /* Read-clear */ + +/* Command 10h - CAN Deliver */ +#define NCT6694_CANFD_DELIVER 0x10 +#define NCT6694_CANFD_DELIVER_SEL(buf_cnt) \ + ((buf_cnt) & 0xFF) + +/* Command 11h - CAN Receive */ +#define NCT6694_CANFD_RECEIVE 0x11 +#define NCT6694_CANFD_RECEIVE_SEL(idx, buf_cnt) \ + ((idx ? 0x80 : 0x00) | ((buf_cnt) & 0x7F)) + +#define NCT6694_CANFD_FRAME_TAG(idx) (0xC0 | (idx)) +#define NCT6694_CANFD_FRAME_FLAG_EFF BIT(0) +#define NCT6694_CANFD_FRAME_FLAG_RTR BIT(1) +#define NCT6694_CANFD_FRAME_FLAG_FD BIT(2) +#define NCT6694_CANFD_FRAME_FLAG_BRS BIT(3) +#define NCT6694_CANFD_FRAME_FLAG_ERR BIT(4) + +#define NCT6694_NAPI_WEIGHT 32 + +enum nct6694_event_err { + NCT6694_CANFD_EVT_ERR_NO_ERROR = 0, + NCT6694_CANFD_EVT_ERR_CRC_ERROR, + NCT6694_CANFD_EVT_ERR_STUFF_ERROR, + NCT6694_CANFD_EVT_ERR_ACK_ERROR, + NCT6694_CANFD_EVT_ERR_FORM_ERROR, + NCT6694_CANFD_EVT_ERR_BIT_ERROR, + NCT6694_CANFD_EVT_ERR_TIMEOUT_ERROR, + NCT6694_CANFD_EVT_ERR_UNKNOWN_ERROR, +}; + +enum nct6694_event_status { + NCT6694_CANFD_EVT_STS_ERROR_ACTIVE = 0, + NCT6694_CANFD_EVT_STS_ERROR_PASSIVE, + NCT6694_CANFD_EVT_STS_BUS_OFF, + NCT6694_CANFD_EVT_STS_WARNING, +}; + +struct __packed nct6694_canfd_setting { + __le32 nbr; + __le32 dbr; + u8 active; + u8 reserved[3]; + __le16 ctrl1; + __le16 ctrl2; + __le32 nbtp; + __le32 dbtp; +}; + +struct __packed nct6694_canfd_information { + u8 tx_fifo_cnt; + u8 rx_fifo_cnt; + u8 reserved[2]; + __le32 can_clk; +}; + +struct __packed nct6694_canfd_event { + u8 err; + u8 status; + u8 tx_evt; + u8 rx_evt; + u8 rec; + u8 tec; + u8 reserved[2]; +}; + +struct __packed nct6694_canfd_frame { + u8 tag; + u8 flag; + u8 reserved; + u8 length; + __le32 id; + u8 data[CANFD_MAX_DLEN]; +}; + +struct nct6694_canfd_priv { + struct can_priv can; /* must be the first member */ + struct can_rx_offload offload; + struct net_device *ndev; + struct nct6694 *nct6694; + struct workqueue_struct *wq; + struct work_struct tx_work; + struct nct6694_canfd_frame tx; + struct nct6694_canfd_frame rx; + struct nct6694_canfd_event event[2]; + struct can_berr_counter bec; +}; + +static inline struct nct6694_canfd_priv *rx_offload_to_priv(struct can_rx_offload *offload) +{ + return container_of(offload, struct nct6694_canfd_priv, offload); +} + +static const struct can_bittiming_const nct6694_canfd_bittiming_nominal_const = { + .name = DEVICE_NAME, + .tseg1_min = 1, + .tseg1_max = 256, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static const struct can_bittiming_const nct6694_canfd_bittiming_data_const = { + .name = DEVICE_NAME, + .tseg1_min = 1, + .tseg1_max = 32, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + +static void nct6694_canfd_rx_offload(struct can_rx_offload *offload, + struct sk_buff *skb) +{ + struct nct6694_canfd_priv *priv = rx_offload_to_priv(offload); + int ret; + + ret = can_rx_offload_queue_tail(offload, skb); + if (ret) + priv->ndev->stats.rx_fifo_errors++; +} + +static void nct6694_canfd_handle_lost_msg(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + netdev_dbg(ndev, "RX FIFO overflow, message(s) lost.\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) + return; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + nct6694_canfd_rx_offload(&priv->offload, skb); +} + +static void nct6694_canfd_handle_rx(struct net_device *ndev, u8 rx_evt) +{ + struct net_device_stats *stats = &ndev->stats; + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct nct6694_canfd_frame *frame = &priv->rx; + const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_RECEIVE, + .sel = NCT6694_CANFD_RECEIVE_SEL(ndev->dev_port, 1), + .len = cpu_to_le16(sizeof(*frame)) + }; + struct sk_buff *skb; + int ret; + + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, frame); + if (ret) + return; + + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_FD) { + struct canfd_frame *cfd; + + skb = alloc_canfd_skb(priv->ndev, &cfd); + if (!skb) { + stats->rx_dropped++; + return; + } + + cfd->can_id = le32_to_cpu(frame->id); + cfd->len = canfd_sanitize_len(frame->length); + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF) + cfd->can_id |= CAN_EFF_FLAG; + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_BRS) + cfd->flags |= CANFD_BRS; + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_ERR) + cfd->flags |= CANFD_ESI; + + memcpy(cfd->data, frame->data, cfd->len); + } else { + struct can_frame *cf; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + cf->can_id = le32_to_cpu(frame->id); + cf->len = can_cc_dlc2len(frame->length); + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF) + cf->can_id |= CAN_EFF_FLAG; + + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, frame->data, cf->len); + } + + nct6694_canfd_rx_offload(&priv->offload, skb); +} + +static int nct6694_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + + *bec = priv->bec; + + return 0; +} + +static void nct6694_canfd_handle_state_change(struct net_device *ndev, u8 status) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + enum can_state new_state, rx_state, tx_state; + struct can_berr_counter bec; + struct can_frame *cf; + struct sk_buff *skb; + + nct6694_canfd_get_berr_counter(ndev, &bec); + can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state); + + new_state = max(tx_state, rx_state); + + /* state hasn't changed */ + if (new_state == priv->can.state) + return; + + skb = alloc_can_err_skb(ndev, &cf); + + can_change_state(ndev, cf, tx_state, rx_state); + + if (new_state == CAN_STATE_BUS_OFF) { + can_bus_off(ndev); + } else if (cf) { + cf->can_id |= CAN_ERR_CNT; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + } + + if (skb) + nct6694_canfd_rx_offload(&priv->offload, skb); +} + +static void nct6694_canfd_handle_bus_err(struct net_device *ndev, u8 bus_err) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct can_frame *cf; + struct sk_buff *skb; + + priv->can.can_stats.bus_error++; + + skb = alloc_can_err_skb(ndev, &cf); + if (cf) + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (bus_err) { + case NCT6694_CANFD_EVT_ERR_CRC_ERROR: + netdev_dbg(ndev, "CRC error\n"); + ndev->stats.rx_errors++; + if (cf) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + break; + + case NCT6694_CANFD_EVT_ERR_STUFF_ERROR: + netdev_dbg(ndev, "Stuff error\n"); + ndev->stats.rx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + + case NCT6694_CANFD_EVT_ERR_ACK_ERROR: + netdev_dbg(ndev, "Ack error\n"); + ndev->stats.tx_errors++; + if (cf) { + cf->can_id |= CAN_ERR_ACK; + cf->data[2] |= CAN_ERR_PROT_TX; + } + break; + + case NCT6694_CANFD_EVT_ERR_FORM_ERROR: + netdev_dbg(ndev, "Form error\n"); + ndev->stats.rx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + + case NCT6694_CANFD_EVT_ERR_BIT_ERROR: + netdev_dbg(ndev, "Bit error\n"); + ndev->stats.tx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_TX | CAN_ERR_PROT_BIT; + break; + + default: + break; + } + + if (skb) + nct6694_canfd_rx_offload(&priv->offload, skb); +} + +static void nct6694_canfd_handle_tx(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + + stats->tx_bytes += can_rx_offload_get_echo_skb_queue_tail(&priv->offload, + 0, NULL); + stats->tx_packets++; + netif_wake_queue(ndev); +} + +static irqreturn_t nct6694_canfd_irq(int irq, void *data) +{ + struct net_device *ndev = data; + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct nct6694_canfd_event *event = &priv->event[ndev->dev_port]; + const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_EVENT, + .sel = NCT6694_CANFD_EVENT_SEL(ndev->dev_port, NCT6694_CANFD_EVENT_MASK), + .len = cpu_to_le16(sizeof(priv->event)) + }; + irqreturn_t handled = IRQ_NONE; + int ret; + + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, priv->event); + if (ret < 0) + return handled; + + if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_IN) { + nct6694_canfd_handle_rx(ndev, event->rx_evt); + handled = IRQ_HANDLED; + } + + if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_LOST) { + nct6694_canfd_handle_lost_msg(ndev); + handled = IRQ_HANDLED; + } + + if (event->status) { + nct6694_canfd_handle_state_change(ndev, event->status); + handled = IRQ_HANDLED; + } + + if (event->err != NCT6694_CANFD_EVT_ERR_NO_ERROR) { + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + nct6694_canfd_handle_bus_err(ndev, event->err); + handled = IRQ_HANDLED; + } + + if (event->tx_evt & NCT6694_CANFD_EVT_TX_FIFO_EMPTY) { + nct6694_canfd_handle_tx(ndev); + handled = IRQ_HANDLED; + } + + if (handled) + can_rx_offload_threaded_irq_finish(&priv->offload); + + priv->bec.rxerr = event->rec; + priv->bec.txerr = event->tec; + + return handled; +} + +static void nct6694_canfd_tx_work(struct work_struct *work) +{ + struct nct6694_canfd_priv *priv = container_of(work, + struct nct6694_canfd_priv, + tx_work); + struct nct6694_canfd_frame *frame = &priv->tx; + struct net_device *ndev = priv->ndev; + struct net_device_stats *stats = &ndev->stats; + struct sk_buff *skb = priv->can.echo_skb[0]; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_DELIVER, + .sel = NCT6694_CANFD_DELIVER_SEL(1), + .len = cpu_to_le16(sizeof(*frame)) + }; + u32 txid; + int err; + + memset(frame, 0, sizeof(*frame)); + + frame->tag = NCT6694_CANFD_FRAME_TAG(ndev->dev_port); + + if (can_is_canfd_skb(skb)) { + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + + if (cfd->flags & CANFD_BRS) + frame->flag |= NCT6694_CANFD_FRAME_FLAG_BRS; + + if (cfd->can_id & CAN_EFF_FLAG) { + txid = cfd->can_id & CAN_EFF_MASK; + frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF; + } else { + txid = cfd->can_id & CAN_SFF_MASK; + } + frame->flag |= NCT6694_CANFD_FRAME_FLAG_FD; + frame->id = cpu_to_le32(txid); + frame->length = canfd_sanitize_len(cfd->len); + + memcpy(frame->data, cfd->data, frame->length); + } else { + struct can_frame *cf = (struct can_frame *)skb->data; + + if (cf->can_id & CAN_EFF_FLAG) { + txid = cf->can_id & CAN_EFF_MASK; + frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF; + } else { + txid = cf->can_id & CAN_SFF_MASK; + } + + if (cf->can_id & CAN_RTR_FLAG) + frame->flag |= NCT6694_CANFD_FRAME_FLAG_RTR; + else + memcpy(frame->data, cf->data, cf->len); + + frame->id = cpu_to_le32(txid); + frame->length = cf->len; + } + + err = nct6694_write_msg(priv->nct6694, &cmd_hd, frame); + if (err) { + can_free_echo_skb(ndev, 0, NULL); + stats->tx_dropped++; + stats->tx_errors++; + netif_wake_queue(ndev); + } +} + +static netdev_tx_t nct6694_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + + if (can_dev_dropped_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + can_put_echo_skb(skb, ndev, 0, 0); + queue_work(priv->wq, &priv->tx_work); + + return NETDEV_TX_OK; +} + +static int nct6694_canfd_start(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + const struct can_bittiming *n_bt = &priv->can.bittiming; + const struct can_bittiming *d_bt = &priv->can.fd.data_bittiming; + struct nct6694_canfd_setting *setting __free(kfree) = NULL; + const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_SETTING, + .sel = ndev->dev_port, + .len = cpu_to_le16(sizeof(*setting)) + }; + u32 en_tdc; + int ret; + + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) + return -ENOMEM; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_NISO); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_LBCK); + + /* Disable clock divider */ + setting->ctrl2 = 0; + + setting->nbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NSJW, + n_bt->sjw - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NBRP, + n_bt->brp - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG2, + n_bt->phase_seg2 - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG1, + n_bt->prop_seg + n_bt->phase_seg1 - 1)); + + if (d_bt->brp <= 2) + en_tdc = NCT6694_CANFD_SETTING_DBTP_TDC; + else + en_tdc = 0; + + setting->dbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DSJW, + d_bt->sjw - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DBRP, + d_bt->brp - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG2, + d_bt->phase_seg2 - 1) | + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG1, + d_bt->prop_seg + d_bt->phase_seg1 - 1) | + en_tdc); + + setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1 | + NCT6694_CANFD_SETTING_ACTIVE_CTRL2 | + NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP; + + ret = nct6694_write_msg(priv->nct6694, &cmd_hd, setting); + if (ret) + return ret; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static void nct6694_canfd_stop(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + struct nct6694_canfd_setting *setting __free(kfree) = NULL; + const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_SETTING, + .sel = ndev->dev_port, + .len = cpu_to_le16(sizeof(*setting)) + }; + + /* The NCT6694 cannot be stopped. To ensure safe operation and avoid + * interference, the control mode is set to Listen-Only mode. This + * mode allows the device to monitor bus activity without actively + * participating in communication. + */ + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) + return; + + nct6694_read_msg(priv->nct6694, &cmd_hd, setting); + setting->ctrl1 = cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON); + setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1; + nct6694_write_msg(priv->nct6694, &cmd_hd, setting); + + priv->can.state = CAN_STATE_STOPPED; +} + +static int nct6694_canfd_close(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + nct6694_canfd_stop(ndev); + destroy_workqueue(priv->wq); + free_irq(ndev->irq, ndev); + can_rx_offload_disable(&priv->offload); + close_candev(ndev); + return 0; +} + +static int nct6694_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + ret = nct6694_canfd_start(ndev); + if (ret) + return ret; + + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return ret; +} + +static int nct6694_canfd_open(struct net_device *ndev) +{ + struct nct6694_canfd_priv *priv = netdev_priv(ndev); + int ret; + + ret = open_candev(ndev); + if (ret) + return ret; + + can_rx_offload_enable(&priv->offload); + + ret = request_threaded_irq(ndev->irq, NULL, + nct6694_canfd_irq, IRQF_ONESHOT, + "nct6694_canfd", ndev); + if (ret) { + netdev_err(ndev, "Failed to request IRQ\n"); + goto can_rx_offload_disable; + } + + priv->wq = alloc_ordered_workqueue("%s-nct6694_wq", + WQ_FREEZABLE | WQ_MEM_RECLAIM, + ndev->name); + if (!priv->wq) { + ret = -ENOMEM; + goto free_irq; + } + + ret = nct6694_canfd_start(ndev); + if (ret) + goto destroy_wq; + + netif_start_queue(ndev); + + return 0; + +destroy_wq: + destroy_workqueue(priv->wq); +free_irq: + free_irq(ndev->irq, ndev); +can_rx_offload_disable: + can_rx_offload_disable(&priv->offload); + close_candev(ndev); + return ret; +} + +static const struct net_device_ops nct6694_canfd_netdev_ops = { + .ndo_open = nct6694_canfd_open, + .ndo_stop = nct6694_canfd_close, + .ndo_start_xmit = nct6694_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct ethtool_ops nct6694_canfd_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static int nct6694_canfd_get_clock(struct nct6694_canfd_priv *priv) +{ + struct nct6694_canfd_information *info __free(kfree) = NULL; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_CANFD_MOD, + .cmd = NCT6694_CANFD_INFORMATION, + .sel = NCT6694_CANFD_INFORMATION_SEL, + .len = cpu_to_le16(sizeof(*info)) + }; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, info); + if (ret) + return ret; + + return le32_to_cpu(info->can_clk); +} + +static int nct6694_canfd_probe(struct platform_device *pdev) +{ + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); + struct nct6694_canfd_priv *priv; + struct net_device *ndev; + int port, irq, ret, can_clk; + + port = ida_alloc(&nct6694->canfd_ida, GFP_KERNEL); + if (port < 0) + return port; + + irq = irq_create_mapping(nct6694->domain, + NCT6694_IRQ_CAN0 + port); + if (!irq) { + ret = -EINVAL; + goto free_ida; + } + + ndev = alloc_candev(sizeof(struct nct6694_canfd_priv), 1); + if (!ndev) { + ret = -ENOMEM; + goto dispose_irq; + } + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + ndev->dev_port = port; + ndev->netdev_ops = &nct6694_canfd_netdev_ops; + ndev->ethtool_ops = &nct6694_canfd_ethtool_ops; + + priv = netdev_priv(ndev); + priv->nct6694 = nct6694; + priv->ndev = ndev; + + can_clk = nct6694_canfd_get_clock(priv); + if (can_clk < 0) { + ret = dev_err_probe(&pdev->dev, can_clk, + "Failed to get clock\n"); + goto free_candev; + } + + INIT_WORK(&priv->tx_work, nct6694_canfd_tx_work); + + priv->can.clock.freq = can_clk; + priv->can.bittiming_const = &nct6694_canfd_bittiming_nominal_const; + priv->can.fd.data_bittiming_const = &nct6694_canfd_bittiming_data_const; + priv->can.do_set_mode = nct6694_canfd_set_mode; + priv->can.do_get_berr_counter = nct6694_canfd_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_FD_NON_ISO; + + ret = can_set_static_ctrlmode(ndev, CAN_CTRLMODE_FD); + if (ret) + goto free_candev; + + ret = can_rx_offload_add_manual(ndev, &priv->offload, + NCT6694_NAPI_WEIGHT); + if (ret) { + dev_err_probe(&pdev->dev, ret, "Failed to add rx_offload\n"); + goto free_candev; + } + + platform_set_drvdata(pdev, priv); + SET_NETDEV_DEV(priv->ndev, &pdev->dev); + + ret = register_candev(priv->ndev); + if (ret) + goto rx_offload_del; + + return 0; + +rx_offload_del: + can_rx_offload_del(&priv->offload); +free_candev: + free_candev(ndev); +dispose_irq: + irq_dispose_mapping(irq); +free_ida: + ida_free(&nct6694->canfd_ida, port); + return ret; +} + +static void nct6694_canfd_remove(struct platform_device *pdev) +{ + struct nct6694_canfd_priv *priv = platform_get_drvdata(pdev); + struct nct6694 *nct6694 = priv->nct6694; + struct net_device *ndev = priv->ndev; + int port = ndev->dev_port; + int irq = ndev->irq; + + unregister_candev(ndev); + can_rx_offload_del(&priv->offload); + free_candev(ndev); + irq_dispose_mapping(irq); + ida_free(&nct6694->canfd_ida, port); +} + +static struct platform_driver nct6694_canfd_driver = { + .driver = { + .name = DEVICE_NAME, + }, + .probe = nct6694_canfd_probe, + .remove = nct6694_canfd_remove, +}; + +module_platform_driver(nct6694_canfd_driver); + +MODULE_DESCRIPTION("USB-CAN FD driver for NCT6694"); +MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 6b293a9056c2..9278a1522aae 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -3,8 +3,8 @@ * CAN driver for PEAK System PCAN-USB adapter * Derived from the PCAN project file driver/src/pcan_usb.c * - * Copyright (C) 2003-2010 PEAK System-Technik GmbH - * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2003-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> * * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> */ @@ -919,7 +919,7 @@ static int pcan_usb_init(struct peak_usb_device *dev) CAN_CTRLMODE_LOOPBACK; } else { dev_info(dev->netdev->dev.parent, - "Firmware update available. Please contact support@peak-system.com\n"); + "Firmware update available. Please contact support.peak@hms-networks.com\n"); } return 0; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 117637b9b995..c74302ca7cee 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -3,8 +3,8 @@ * CAN driver for PEAK System USB adapters * Derived from the PCAN project file driver/src/pcan_usb_core.c * - * Copyright (C) 2003-2010 PEAK System-Technik GmbH - * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2003-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> * * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> */ @@ -24,7 +24,7 @@ #include "pcan_usb_core.h" -MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_AUTHOR("Stéphane Grosjean <stephane.grosjean@hms-networks.com>"); MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); MODULE_LICENSE("GPL v2"); @@ -111,7 +111,7 @@ void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now) u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1; if (time_ref->ts_dev_2 < time_ref->ts_dev_1) - delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1; + delta_ts &= (1ULL << time_ref->adapter->ts_used_bits) - 1; time_ref->ts_total += delta_ts; } diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index abab00930b9d..d1c1897d47b9 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -3,8 +3,8 @@ * CAN driver for PEAK System USB adapters * Derived from the PCAN project file driver/src/pcan_usb_core.c * - * Copyright (C) 2003-2010 PEAK System-Technik GmbH - * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2003-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> * * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> */ diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 4d85b29a17b7..be84191cde56 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -2,7 +2,8 @@ /* * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter * - * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2013-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> */ #include <linux/ethtool.h> #include <linux/module.h> @@ -49,7 +50,7 @@ struct __packed pcan_ufd_fw_info { __le32 ser_no; /* S/N */ __le32 flags; /* special functions */ - /* extended data when type == PCAN_USBFD_TYPE_EXT */ + /* extended data when type >= PCAN_USBFD_TYPE_EXT */ u8 cmd_out_ep; /* ep for cmd */ u8 cmd_in_ep; /* ep for replies */ u8 data_out_ep[2]; /* ep for CANx TX */ @@ -982,10 +983,11 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO; } - /* if vendor rsp is of type 2, then it contains EP numbers to - * use for cmds pipes. If not, then default EP should be used. + /* if vendor rsp type is greater than or equal to 2, then it + * contains EP numbers to use for cmds pipes. If not, then + * default EP should be used. */ - if (fw_info->type != cpu_to_le16(PCAN_USBFD_TYPE_EXT)) { + if (le16_to_cpu(fw_info->type) < PCAN_USBFD_TYPE_EXT) { fw_info->cmd_out_ep = PCAN_USBPRO_EP_CMDOUT; fw_info->cmd_in_ep = PCAN_USBPRO_EP_CMDIN; } @@ -1018,11 +1020,11 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) dev->can_channel_id = le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]); - /* if vendor rsp is of type 2, then it contains EP numbers to - * use for data pipes. If not, then statically defined EP are used - * (see peak_usb_create_dev()). + /* if vendor rsp type is greater than or equal to 2, then it contains EP + * numbers to use for data pipes. If not, then statically defined EP are + * used (see peak_usb_create_dev()). */ - if (fw_info->type == cpu_to_le16(PCAN_USBFD_TYPE_EXT)) { + if (le16_to_cpu(fw_info->type) >= PCAN_USBFD_TYPE_EXT) { dev->ep_msg_in = fw_info->data_in_ep; dev->ep_msg_out = fw_info->data_out_ep[dev->ctrl_idx]; } diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index f736196383ac..7be286293b1a 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -3,8 +3,8 @@ * CAN driver for PEAK System PCAN-USB Pro adapter * Derived from the PCAN project file driver/src/pcan_usbpro.c * - * Copyright (C) 2003-2011 PEAK System-Technik GmbH - * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2003-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> */ #include <linux/ethtool.h> #include <linux/module.h> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h index 28e740af905d..162c7546d3a8 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -3,8 +3,8 @@ * CAN driver for PEAK System PCAN-USB Pro adapter * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h * - * Copyright (C) 2003-2011 PEAK System-Technik GmbH - * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * Copyright (C) 2003-2025 PEAK System-Technik GmbH + * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com> */ #ifndef PCAN_USB_PRO_H #define PCAN_USB_PRO_H |