Merge patch series "can: mcp251xfd: add XSTBYEN transceiver standby control"

Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> says:

The MCP251xFD provides a dedicated transceiver standby control function via
the INT0/GPIO0/XSTBY pin, controlled by the XSTBYEN bit in IOCON. When
enabled, the hardware automatically drives the pin low while the controller
is active and high when it enters Sleep mode, allowing automatic standby
control of an external CAN transceiver without software intervention.

This series adds driver support for the XSTBYEN-based transceiver standby
control feature.

Tested on QCS6490 RB3 Gen2 with a PCAN-USB FD adapter: the transceiver is
active in normal mode, CAN communication works correctly, and the pin is
automatically managed across sleep and wake transitions.

Link: https://patch.msgid.link/20260321135031.3107408-1-viken.dadhaniya@oss.qualcomm.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde 2026-03-24 09:22:48 +01:00
commit 572a36d279
3 changed files with 46 additions and 0 deletions

View File

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

View File

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

View File

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