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