From 2f41d7867800a78f339fbec3ab3a64147c33a9f1 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Sat, 21 Mar 2026 19:20:30 +0530 Subject: [PATCH 1/6] dt-bindings: can: mcp251xfd: add microchip,xstbyen property Add the boolean property 'microchip,xstbyen' to enable the dedicated transceiver standby control function on the INT0/GPIO0/XSTBY pin of the MCP251xFD family. Signed-off-by: Viken Dadhaniya Acked-by: Conor Dooley Link: https://patch.msgid.link/20260321135031.3107408-2-viken.dadhaniya@oss.qualcomm.com Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/microchip,mcp251xfd.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml index 2d13638ebc6a..28e494262cd9 100644 --- a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml @@ -44,6 +44,14 @@ properties: signals a pending RX interrupt. maxItems: 1 + microchip,xstbyen: + type: boolean + description: + If present, configure the INT0/GPIO0/XSTBY pin as transceiver standby + control. The pin is driven low when the controller is active and high + when it enters Sleep mode, allowing automatic standby control of an + external CAN transceiver connected to this pin. + spi-max-frequency: description: Must be half or less of "clocks" frequency. From 1e41cbbe68e6753e786ddff528de15050bc52803 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 03:41:33 -0800 Subject: [PATCH 2/6] net: can: ctucanfd: remove useless copy of PCI_DEVICE_DATA macro The ctucanfd driver has its own copy of the PCI_DEVICE_DATA macro. I assume this was done to support older kernel versions where it didn't exist, but that is irrelevant once the driver is in the mainline kernel. Remove it. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260130114134.47421-1-enelsonmoore@gmail.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_pci.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c index 7b847b667973..625788fa8976 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c @@ -22,14 +22,6 @@ #include "ctucanfd.h" -#ifndef PCI_DEVICE_DATA -#define PCI_DEVICE_DATA(vend, dev, data) \ -.vendor = PCI_VENDOR_ID_##vend, \ -.device = PCI_DEVICE_ID_##vend##_##dev, \ -.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \ -.driver_data = (kernel_ulong_t)(data) -#endif - #ifndef PCI_VENDOR_ID_TEDIA #define PCI_VENDOR_ID_TEDIA 0x1760 #endif From 495fac90b8ec3d6de11539b2b05c55ba360586ab Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:18:16 +0200 Subject: [PATCH 3/6] can: kvaser_usb: leaf: refactor endpoint lookup Use the common USB helper for looking up bulk and interrupt endpoints instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260330101817.1664787-2-johan@kernel.org Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) 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 fd191ec5738b..df737cfc5ea0 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -1957,27 +1957,18 @@ static int kvaser_usb_leaf_get_berr_counter(const struct net_device *netdev, static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) { - const struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i; + struct usb_host_interface *iface_desc; + int ret; iface_desc = dev->intf->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; + /* use first bulk endpoint for in and out */ + ret = usb_find_common_endpoints(iface_desc, &dev->bulk_in, &dev->bulk_out, + NULL, NULL); + if (ret) + return -ENODEV; - if (!dev->bulk_in && usb_endpoint_is_bulk_in(endpoint)) - dev->bulk_in = endpoint; - - if (!dev->bulk_out && usb_endpoint_is_bulk_out(endpoint)) - dev->bulk_out = endpoint; - - /* use first bulk endpoint for in and out */ - if (dev->bulk_in && dev->bulk_out) - return 0; - } - - return -ENODEV; + return 0; } const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { From ae20301b6119257f533332836c46d8e8824433dd Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Sat, 21 Mar 2026 19:20:31 +0530 Subject: [PATCH 4/6] can: mcp251xfd: add support for XSTBYEN transceiver standby control The MCP251xFD has a dedicated transceiver standby control function on the INT0/GPIO0/XSTBY pin, controlled by the XSTBYEN bit in IOCON. When enabled, the hardware automatically manages the transceiver standby state: the pin is driven low when the controller is active and high when it enters Sleep mode. Enable this feature when the 'microchip,xstbyen' device tree property is present. Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260321135031.3107408-3-viken.dadhaniya@oss.qualcomm.com Signed-off-by: Marc Kleine-Budde --- .../net/can/spi/mcp251xfd/mcp251xfd-core.c | 37 +++++++++++++++++++ drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 1 + 2 files changed, 38 insertions(+) diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index 9c86df08c2c5..92a86083c896 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -764,6 +764,31 @@ static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG); } +static int mcp251xfd_chip_xstbyen_enable(const struct mcp251xfd_priv *priv) +{ + /* Configure the INT0/GPIO0/XSTBY pin as transceiver standby control: + * + * - XSTBYEN=1: route the pin to the transceiver standby function + * - TRIS0=0: set output direction; the reset default is 1 (input), + * which leaves the pin floating HIGH and keeps the + * transceiver in standby regardless of XSTBYEN + * - LAT0=0: drive pin LOW => transceiver active (not in standby) + * + * All three bits are included in the mask; only XSTBYEN is set in + * val, so TRIS0 and LAT0 are cleared to 0 atomically. + * + * Pin behaviour by mode: + * - Config mode: controlled by LAT0 (LAT0=0 => LOW => active) + * - Normal mode: hardware drives pin LOW (active) + * - Sleep mode: hardware drives pin HIGH (standby) + */ + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, + MCP251XFD_REG_IOCON_XSTBYEN | + MCP251XFD_REG_IOCON_TRIS0 | + MCP251XFD_REG_IOCON_LAT0, + MCP251XFD_REG_IOCON_XSTBYEN); +} + static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) { int err; @@ -796,6 +821,12 @@ static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) priv->can.state = CAN_STATE_ERROR_ACTIVE; + if (priv->xstbyen) { + err = mcp251xfd_chip_xstbyen_enable(priv); + if (err) + goto out_chip_stop; + } + err = mcp251xfd_chip_set_normal_mode(priv); if (err) goto out_chip_stop; @@ -1805,6 +1836,11 @@ static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset) u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset); int ret; + if (priv->xstbyen && offset == 0) { + netdev_err(priv->ndev, "Can't use GPIO 0 with XSTBYEN!\n"); + return -EINVAL; + } + if (priv->rx_int && offset == 1) { netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n"); return -EINVAL; @@ -2271,6 +2307,7 @@ static int mcp251xfd_probe(struct spi_device *spi) priv->pll_enable = pll_enable; priv->reg_vdd = reg_vdd; priv->reg_xceiver = reg_xceiver; + priv->xstbyen = device_property_present(&spi->dev, "microchip,xstbyen"); priv->devtype_data = *(struct mcp251xfd_devtype_data *)spi_get_device_match_data(spi); /* Errata Reference: diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h index 085d7101e595..d3f4704e2678 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -672,6 +672,7 @@ struct mcp251xfd_priv { struct gpio_desc *rx_int; struct clk *clk; bool pll_enable; + bool xstbyen; struct regulator *reg_vdd; struct regulator *reg_xceiver; From 11d94d3516c0c549752061b6a576e6c547d61e86 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 5 Mar 2026 11:14:37 +0100 Subject: [PATCH 5/6] can: rcar_can: Convert to FIELD_MODIFY() Use the FIELD_MODIFY() helper instead of open-coding the same operation. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/ee2e6aaacd5e061c972716ecaf8a929be7ef5f2e.1772705647.git.geert+renesas@glider.be Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_can.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c index fc3df328e877..2727c5ce029c 100644 --- a/drivers/net/can/rcar/rcar_can.c +++ b/drivers/net/can/rcar/rcar_can.c @@ -496,8 +496,7 @@ static void rcar_can_start(struct net_device *ndev) priv->can.state = CAN_STATE_ERROR_ACTIVE; /* Go to operation mode */ - ctlr &= ~RCAR_CAN_CTLR_CANM; - ctlr |= FIELD_PREP(RCAR_CAN_CTLR_CANM, RCAR_CAN_CTLR_CANM_OPER); + FIELD_MODIFY(RCAR_CAN_CTLR_CANM, &ctlr, RCAR_CAN_CTLR_CANM_OPER); writew(ctlr, &priv->regs->ctlr); for (i = 0; i < MAX_STR_READS; i++) { if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)) From 581281cb5a1b72fe31ab67e0074feabb1b5abcc7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:18:17 +0200 Subject: [PATCH 6/6] can: ucan: refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining endpoint numbers and max packet sizes) instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260330101817.1664787-3-johan@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ucan.c | 40 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 0ea0ac75e42f..506d9e74aa79 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -1302,13 +1302,12 @@ static int ucan_probe(struct usb_interface *intf, const struct usb_device_id *id) { int ret; - int i; u32 protocol_version; struct usb_device *udev; struct net_device *netdev; struct usb_host_interface *iface_desc; struct ucan_priv *up; - struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in, *ep_out; u16 in_ep_size; u16 out_ep_size; u8 in_ep_addr; @@ -1343,37 +1342,20 @@ static int ucan_probe(struct usb_interface *intf, } /* check interface endpoints */ - in_ep_addr = 0; - out_ep_addr = 0; - in_ep_size = 0; - out_ep_size = 0; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - ep = &iface_desc->endpoint[i].desc; - - if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) && - ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_BULK)) { - /* In Endpoint */ - in_ep_addr = ep->bEndpointAddress; - in_ep_addr &= USB_ENDPOINT_NUMBER_MASK; - in_ep_size = le16_to_cpu(ep->wMaxPacketSize); - } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == - 0) && - ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_BULK)) { - /* Out Endpoint */ - out_ep_addr = ep->bEndpointAddress; - out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; - out_ep_size = le16_to_cpu(ep->wMaxPacketSize); - } - } - - /* check if interface is sane */ - if (!in_ep_addr || !out_ep_addr) { + ret = usb_find_common_endpoints_reverse(iface_desc, &ep_in, &ep_out, + NULL, NULL); + if (ret) { dev_err(&udev->dev, "%s: invalid endpoint configuration\n", UCAN_DRIVER_NAME); goto err_firmware_needs_update; } + + in_ep_addr = usb_endpoint_num(ep_in); + out_ep_addr = usb_endpoint_num(ep_out); + in_ep_size = usb_endpoint_maxp(ep_in); + out_ep_size = usb_endpoint_maxp(ep_out); + + /* check if interface is sane */ if (in_ep_size < sizeof(struct ucan_message_in)) { dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n", UCAN_DRIVER_NAME);