* [PATCH v5 1/6] can: mcp251xfd: move chip sleep mode into runtime pm
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 2/6] can: mcp251xfd: utilize gather_write function for all non-CRC writes Viken Dadhaniya
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger, Viken Dadhaniya
From: Marc Kleine-Budde <mkl@pengutronix.de>
This is a preparation patch to add GPIO support.
Up to now, the Vdd regulator and the clocks have been managed by
Runtime-PM (on systems without CONFIG_PM these remain permanently
switched on).
During the mcp251xfd_open() callback the mcp251xfd is powered,
soft-reset and configured. In mcp251xfd_stop() the chip is shut down
again. To support the on-chip GPIOs, the chip must be supplied with
power while GPIOs are being requested, even if the networking
interface is down.
To support this, move the functions mcp251xfd_chip_softreset() and
mcp251xfd_chip_clock_init() from mcp251xfd_chip_start() to
mcp251xfd_runtime_resume(). Instead of setting the controller to sleep
mode in mcp251xfd_chip_stop(), bring it into configuration mode. This
way it doesn't take part in bus activity and doesn't enter sleep mode.
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
.../net/can/spi/mcp251xfd/mcp251xfd-core.c | 91 ++++++++++++-------
1 file changed, 57 insertions(+), 34 deletions(-)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index 7450ea42c1ea..f9eabb1810cf 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -767,21 +767,13 @@ static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv,
mcp251xfd_chip_interrupts_disable(priv);
mcp251xfd_chip_rx_int_disable(priv);
mcp251xfd_timestamp_stop(priv);
- mcp251xfd_chip_sleep(priv);
+ mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
}
static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv)
{
int err;
- err = mcp251xfd_chip_softreset(priv);
- if (err)
- goto out_chip_stop;
-
- err = mcp251xfd_chip_clock_init(priv);
- if (err)
- goto out_chip_stop;
-
err = mcp251xfd_chip_timestamp_init(priv);
if (err)
goto out_chip_stop;
@@ -1625,8 +1617,11 @@ static int mcp251xfd_open(struct net_device *ndev)
return err;
err = pm_runtime_resume_and_get(ndev->dev.parent);
- if (err)
+ if (err) {
+ if (err == -ETIMEDOUT || err == -ENODEV)
+ pm_runtime_set_suspended(ndev->dev.parent);
goto out_close_candev;
+ }
err = mcp251xfd_ring_alloc(priv);
if (err)
@@ -1907,53 +1902,53 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
struct net_device *ndev = priv->ndev;
int err;
+ mcp251xfd_register_quirks(priv);
+
err = mcp251xfd_clks_and_vdd_enable(priv);
if (err)
return err;
- pm_runtime_get_noresume(ndev->dev.parent);
- err = pm_runtime_set_active(ndev->dev.parent);
- if (err)
- goto out_runtime_put_noidle;
- pm_runtime_enable(ndev->dev.parent);
-
- mcp251xfd_register_quirks(priv);
-
err = mcp251xfd_chip_softreset(priv);
if (err == -ENODEV)
- goto out_runtime_disable;
+ goto out_clks_and_vdd_disable;
if (err)
goto out_chip_sleep;
err = mcp251xfd_chip_clock_init(priv);
if (err == -ENODEV)
- goto out_runtime_disable;
+ goto out_clks_and_vdd_disable;
if (err)
goto out_chip_sleep;
+ pm_runtime_get_noresume(ndev->dev.parent);
+ err = pm_runtime_set_active(ndev->dev.parent);
+ if (err)
+ goto out_runtime_put_noidle;
+ pm_runtime_enable(ndev->dev.parent);
+
err = mcp251xfd_register_chip_detect(priv);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
err = mcp251xfd_register_check_rx_int(priv);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
mcp251xfd_ethtool_init(priv);
err = register_candev(ndev);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
err = mcp251xfd_register_done(priv);
if (err)
goto out_unregister_candev;
- /* Put controller into sleep mode and let pm_runtime_put()
- * disable the clocks and vdd. If CONFIG_PM is not enabled,
- * the clocks and vdd will stay powered.
+ /* Put controller into Config mode and let pm_runtime_put()
+ * put in sleep mode, disable the clocks and vdd. If CONFIG_PM
+ * is not enabled, the clocks and vdd will stay powered.
*/
- err = mcp251xfd_chip_sleep(priv);
+ err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
if (err)
goto out_unregister_candev;
@@ -1963,12 +1958,13 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
out_unregister_candev:
unregister_candev(ndev);
-out_chip_sleep:
- mcp251xfd_chip_sleep(priv);
out_runtime_disable:
pm_runtime_disable(ndev->dev.parent);
out_runtime_put_noidle:
pm_runtime_put_noidle(ndev->dev.parent);
+out_chip_sleep:
+ mcp251xfd_chip_sleep(priv);
+out_clks_and_vdd_disable:
mcp251xfd_clks_and_vdd_disable(priv);
return err;
@@ -1980,10 +1976,12 @@ static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv)
unregister_candev(ndev);
- if (pm_runtime_enabled(ndev->dev.parent))
+ if (pm_runtime_enabled(ndev->dev.parent)) {
pm_runtime_disable(ndev->dev.parent);
- else
+ } else {
+ mcp251xfd_chip_sleep(priv);
mcp251xfd_clks_and_vdd_disable(priv);
+ }
}
static const struct of_device_id mcp251xfd_of_match[] = {
@@ -2206,16 +2204,41 @@ static void mcp251xfd_remove(struct spi_device *spi)
static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device)
{
- const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ mcp251xfd_chip_sleep(priv);
return mcp251xfd_clks_and_vdd_disable(priv);
}
static int __maybe_unused mcp251xfd_runtime_resume(struct device *device)
{
- const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ int err;
+
+ err = mcp251xfd_clks_and_vdd_enable(priv);
+ if (err)
+ return err;
- return mcp251xfd_clks_and_vdd_enable(priv);
+ err = mcp251xfd_chip_softreset(priv);
+ if (err == -ENODEV)
+ goto out_clks_and_vdd_disable;
+ if (err)
+ goto out_chip_sleep;
+
+ err = mcp251xfd_chip_clock_init(priv);
+ if (err == -ENODEV)
+ goto out_clks_and_vdd_disable;
+ if (err)
+ goto out_chip_sleep;
+
+ return 0;
+
+out_chip_sleep:
+ mcp251xfd_chip_sleep(priv);
+out_clks_and_vdd_disable:
+ mcp251xfd_clks_and_vdd_disable(priv);
+
+ return err;
}
static const struct dev_pm_ops mcp251xfd_pm_ops = {
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v5 2/6] can: mcp251xfd: utilize gather_write function for all non-CRC writes
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 1/6] can: mcp251xfd: move chip sleep mode into runtime pm Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 3/6] can: mcp251xfd: add workaround for errata 5 Viken Dadhaniya
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger, Viken Dadhaniya
From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
This is a preparation patch to add errata workaround for non crc writes.
Currently for non-crc writes to the chip can go through the
.gather_write, .write or the reg_update_bits callback.
To allow the addition of the errata fix at a single location use
mcp251xfd_regmap_nocrc_gather_write for all non-CRC write instructions,
similar to the crc regmap.
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
.../net/can/spi/mcp251xfd/mcp251xfd-regmap.c | 25 ++++++++++---------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
index 8c5be8d1c519..e61cbd209955 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
@@ -12,14 +12,6 @@
static const struct regmap_config mcp251xfd_regmap_crc;
-static int
-mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
-{
- struct spi_device *spi = context;
-
- return spi_write(spi, data, count);
-}
-
static int
mcp251xfd_regmap_nocrc_gather_write(void *context,
const void *reg, size_t reg_len,
@@ -47,6 +39,15 @@ mcp251xfd_regmap_nocrc_gather_write(void *context,
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
+static int
+mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
+{
+ const size_t data_offset = sizeof(__be16);
+
+ return mcp251xfd_regmap_nocrc_gather_write(context, data, data_offset,
+ data + data_offset, count - data_offset);
+}
+
static inline bool
mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
unsigned int reg)
@@ -64,6 +65,7 @@ mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
case MCP251XFD_REG_CON:
case MCP251XFD_REG_OSC:
case MCP251XFD_REG_ECCCON:
+ case MCP251XFD_REG_IOCON:
return true;
default:
mcp251xfd_for_each_rx_ring(priv, ring, n) {
@@ -139,10 +141,9 @@ mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg,
tmp_le32 = orig_le32 & ~mask_le32;
tmp_le32 |= val_le32 & mask_le32;
- mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte);
- memcpy(buf_tx->data, &tmp_le32, len);
-
- return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len);
+ reg += first_byte;
+ mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg);
+ return mcp251xfd_regmap_nocrc_gather_write(context, &buf_tx->cmd, 2, &tmp_le32, len);
}
static int
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v5 3/6] can: mcp251xfd: add workaround for errata 5
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 1/6] can: mcp251xfd: move chip sleep mode into runtime pm Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 2/6] can: mcp251xfd: utilize gather_write function for all non-CRC writes Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 4/6] can: mcp251xfd: only configure PIN1 when rx_int is set Viken Dadhaniya
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger, Viken Dadhaniya
From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
According to Errata DS80000789E 5 writing IOCON register using one SPI
write command clears LAT0/LAT1.
Errata Fix/Work Around suggests to write registers with single byte write
instructions. However, it seems that every write to the second byte
causes the overwrite of LAT0/LAT1.
Never write byte 2 of IOCON register to avoid clearing of LAT0/LAT1.
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
.../net/can/spi/mcp251xfd/mcp251xfd-regmap.c | 89 +++++++++++++++++--
1 file changed, 83 insertions(+), 6 deletions(-)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
index e61cbd209955..70d5ff0ae7ac 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
@@ -13,9 +13,9 @@
static const struct regmap_config mcp251xfd_regmap_crc;
static int
-mcp251xfd_regmap_nocrc_gather_write(void *context,
- const void *reg, size_t reg_len,
- const void *val, size_t val_len)
+_mcp251xfd_regmap_nocrc_gather_write(void *context,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len)
{
struct spi_device *spi = context;
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
@@ -39,6 +39,45 @@ mcp251xfd_regmap_nocrc_gather_write(void *context,
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
+static int
+mcp251xfd_regmap_nocrc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ const u16 byte_exclude = MCP251XFD_REG_IOCON +
+ mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
+ u16 reg = be16_to_cpu(*(__be16 *)reg_p) & MCP251XFD_SPI_ADDRESS_MASK;
+ int ret;
+
+ /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
+ *
+ * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
+ * SPI write command clears LAT0/LAT1.
+ *
+ * Errata Fix/Work Around suggests to write registers with single byte
+ * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
+ * is for read-only access and writing to it causes the clearing of LAT0/LAT1.
+ */
+ if (reg <= byte_exclude && reg + val_len > byte_exclude) {
+ size_t len = byte_exclude - reg;
+
+ /* Write up to 0xe05 */
+ ret = _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len, val, len);
+ if (ret)
+ return ret;
+
+ /* Write from 0xe07 on */
+ reg += len + 1;
+ reg = (__force unsigned short)cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | reg);
+ return _mcp251xfd_regmap_nocrc_gather_write(context, ®, reg_len,
+ val + len + 1,
+ val_len - len - 1);
+ }
+
+ return _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len,
+ val, val_len);
+}
+
static int
mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
{
@@ -197,9 +236,9 @@ mcp251xfd_regmap_nocrc_read(void *context,
}
static int
-mcp251xfd_regmap_crc_gather_write(void *context,
- const void *reg_p, size_t reg_len,
- const void *val, size_t val_len)
+_mcp251xfd_regmap_crc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
{
struct spi_device *spi = context;
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
@@ -230,6 +269,44 @@ mcp251xfd_regmap_crc_gather_write(void *context,
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
+static int
+mcp251xfd_regmap_crc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ const u16 byte_exclude = MCP251XFD_REG_IOCON +
+ mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
+ u16 reg = *(u16 *)reg_p;
+ int ret;
+
+ /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
+ *
+ * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
+ * SPI write command clears LAT0/LAT1.
+ *
+ * Errata Fix/Work Around suggests to write registers with single byte
+ * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
+ * is for read-only access and writing to it causes the clearing of LAT0/LAT1.
+ */
+ if (reg <= byte_exclude && reg + val_len > byte_exclude) {
+ size_t len = byte_exclude - reg;
+
+ /* Write up to 0xe05 */
+ ret = _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len, val, len);
+ if (ret)
+ return ret;
+
+ /* Write from 0xe07 on */
+ reg += len + 1;
+ return _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len,
+ val + len + 1,
+ val_len - len - 1);
+ }
+
+ return _mcp251xfd_regmap_crc_gather_write(context, reg_p, reg_len,
+ val, val_len);
+}
+
static int
mcp251xfd_regmap_crc_write(void *context,
const void *data, size_t count)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v5 4/6] can: mcp251xfd: only configure PIN1 when rx_int is set
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
` (2 preceding siblings ...)
2025-09-26 13:30 ` [PATCH v5 3/6] can: mcp251xfd: add workaround for errata 5 Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 5/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
2025-09-26 13:30 ` [PATCH v5 6/6] dt-bindings: can: mcp251xfd: add gpio-controller property Viken Dadhaniya
5 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger, Viken Dadhaniya
From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
When rx_int is used th mcp251xfd_chip_rx_int_enable and
mcp251xfd_chip_rx_int_disable function configure both PIN0 and PIN1. To
prepare the support of the GPIOS only configure PIN1 with
regmap_update_bits.
This way PIN0 can be used as GPIO while PIN1 is used as rx_int
interrupt.
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
.../net/can/spi/mcp251xfd/mcp251xfd-core.c | 22 +++++++------------
drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 6 +++++
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index f9eabb1810cf..ea41f04ae1a6 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -608,23 +608,21 @@ static int mcp251xfd_set_bittiming(const struct mcp251xfd_priv *priv)
static int mcp251xfd_chip_rx_int_enable(const struct mcp251xfd_priv *priv)
{
- u32 val;
+ u32 val, mask;
if (!priv->rx_int)
return 0;
- /* Configure GPIOs:
- * - PIN0: GPIO Input
- * - PIN1: GPIO Input/RX Interrupt
+ /* Configure PIN1 as RX Interrupt:
*
* PIN1 must be Input, otherwise there is a glitch on the
* rx-INT line. It happens between setting the PIN as output
* (in the first byte of the SPI transfer) and configuring the
* PIN as interrupt (in the last byte of the SPI transfer).
*/
- val = MCP251XFD_REG_IOCON_PM0 | MCP251XFD_REG_IOCON_TRIS1 |
- MCP251XFD_REG_IOCON_TRIS0;
- return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
+ val = MCP251XFD_REG_IOCON_TRIS(1);
+ mask = MCP251XFD_REG_IOCON_TRIS(1) | MCP251XFD_REG_IOCON_PM(1);
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, mask, val);
}
static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
@@ -634,13 +632,9 @@ static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
if (!priv->rx_int)
return 0;
- /* Configure GPIOs:
- * - PIN0: GPIO Input
- * - PIN1: GPIO Input
- */
- val = MCP251XFD_REG_IOCON_PM1 | MCP251XFD_REG_IOCON_PM0 |
- MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_TRIS0;
- return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
+ /* Configure PIN1 as GPIO Input */
+ val = MCP251XFD_REG_IOCON_PM(1) | MCP251XFD_REG_IOCON_TRIS(1);
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val, val);
}
static int mcp251xfd_chip_ecc_init(struct mcp251xfd_priv *priv)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
index dcbbd2b2fae8..bd28510a6583 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
@@ -335,13 +335,19 @@
#define MCP251XFD_REG_IOCON_TXCANOD BIT(28)
#define MCP251XFD_REG_IOCON_PM1 BIT(25)
#define MCP251XFD_REG_IOCON_PM0 BIT(24)
+#define MCP251XFD_REG_IOCON_PM(n) (MCP251XFD_REG_IOCON_PM0 << (n))
#define MCP251XFD_REG_IOCON_GPIO1 BIT(17)
#define MCP251XFD_REG_IOCON_GPIO0 BIT(16)
+#define MCP251XFD_REG_IOCON_GPIO(n) (MCP251XFD_REG_IOCON_GPIO0 << (n))
+#define MCP251XFD_REG_IOCON_GPIO_MASK GENMASK(17, 16)
#define MCP251XFD_REG_IOCON_LAT1 BIT(9)
#define MCP251XFD_REG_IOCON_LAT0 BIT(8)
+#define MCP251XFD_REG_IOCON_LAT(n) (MCP251XFD_REG_IOCON_LAT0 << (n))
+#define MCP251XFD_REG_IOCON_LAT_MASK GENMASK(9, 8)
#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6)
#define MCP251XFD_REG_IOCON_TRIS1 BIT(1)
#define MCP251XFD_REG_IOCON_TRIS0 BIT(0)
+#define MCP251XFD_REG_IOCON_TRIS(n) (MCP251XFD_REG_IOCON_TRIS0 << (n))
#define MCP251XFD_REG_CRC 0xe08
#define MCP251XFD_REG_CRC_FERRIE BIT(25)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v5 5/6] can: mcp251xfd: add gpio functionality
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
` (3 preceding siblings ...)
2025-09-26 13:30 ` [PATCH v5 4/6] can: mcp251xfd: only configure PIN1 when rx_int is set Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
2025-09-29 13:38 ` Bartosz Golaszewski
2025-09-26 13:30 ` [PATCH v5 6/6] dt-bindings: can: mcp251xfd: add gpio-controller property Viken Dadhaniya
5 siblings, 1 reply; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger, Viken Dadhaniya
From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
The mcp251xfd devices allow two pins to be configured as gpio. Add this
functionality to driver.
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
drivers/net/can/spi/mcp251xfd/Kconfig | 1 +
.../net/can/spi/mcp251xfd/mcp251xfd-core.c | 172 ++++++++++++++++++
drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 2 +
3 files changed, 175 insertions(+)
diff --git a/drivers/net/can/spi/mcp251xfd/Kconfig b/drivers/net/can/spi/mcp251xfd/Kconfig
index 877e4356010d..7c29846e6051 100644
--- a/drivers/net/can/spi/mcp251xfd/Kconfig
+++ b/drivers/net/can/spi/mcp251xfd/Kconfig
@@ -5,6 +5,7 @@ config CAN_MCP251XFD
select CAN_RX_OFFLOAD
select REGMAP
select WANT_DEV_COREDUMP
+ select GPIOLIB
help
Driver for the Microchip MCP251XFD SPI FD-CAN controller
family.
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index ea41f04ae1a6..88035d4404b5 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -1797,6 +1797,172 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv)
return 0;
}
+static const char * const mcp251xfd_gpio_names[] = { "GPIO0", "GPIO1" };
+
+static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset);
+ int ret;
+
+ if (priv->rx_int && offset == 1) {
+ netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n");
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(priv->ndev->dev.parent);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ pin_mask, pin_mask);
+}
+
+static void mcp251xfd_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+
+ pm_runtime_put(priv->ndev->dev.parent);
+}
+
+static int mcp251xfd_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = MCP251XFD_REG_IOCON_TRIS(offset);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ if (mask & val)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int mcp251xfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = MCP251XFD_REG_IOCON_GPIO(offset);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ return !!(mask & val);
+}
+
+static int mcp251xfd_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bit)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ *bit = FIELD_GET(MCP251XFD_REG_IOCON_GPIO_MASK, val) & *mask;
+
+ return 0;
+}
+
+static int mcp251xfd_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
+ u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
+ u32 val;
+
+ if (value)
+ val = val_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ dir_mask | val_mask, val);
+}
+
+static int mcp251xfd_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ dir_mask, dir_mask);
+}
+
+static int mcp251xfd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
+ u32 val;
+ int ret;
+
+ if (value)
+ val = val_mask;
+ else
+ val = 0;
+
+ ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
+ if (ret)
+ dev_err(&priv->spi->dev, "Failed to set GPIO %u: %d\n", offset, ret);
+
+ return ret;
+}
+
+static int mcp251xfd_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val;
+ int ret;
+
+ val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
+
+ ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ MCP251XFD_REG_IOCON_LAT_MASK, val);
+ if (ret)
+ dev_err(&priv->spi->dev, "Failed to set GPIOs %d\n", ret);
+
+ return ret;
+}
+
+static int mcp251fdx_gpio_setup(struct mcp251xfd_priv *priv)
+{
+ struct gpio_chip *gc = &priv->gc;
+
+ if (!device_property_present(&priv->spi->dev, "gpio-controller"))
+ return 0;
+
+ gc->label = dev_name(&priv->spi->dev);
+ gc->parent = &priv->spi->dev;
+ gc->owner = THIS_MODULE;
+ gc->request = mcp251xfd_gpio_request;
+ gc->free = mcp251xfd_gpio_free;
+ gc->get_direction = mcp251xfd_gpio_get_direction;
+ gc->direction_output = mcp251xfd_gpio_direction_output;
+ gc->direction_input = mcp251xfd_gpio_direction_input;
+ gc->get = mcp251xfd_gpio_get;
+ gc->get_multiple = mcp251xfd_gpio_get_multiple;
+ gc->set = mcp251xfd_gpio_set;
+ gc->set_multiple = mcp251xfd_gpio_set_multiple;
+ gc->base = -1;
+ gc->can_sleep = true;
+ gc->ngpio = ARRAY_SIZE(mcp251xfd_gpio_names);
+ gc->names = mcp251xfd_gpio_names;
+
+ return devm_gpiochip_add_data(&priv->spi->dev, gc, priv);
+}
+
static int
mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id,
u32 *effective_speed_hz_slow,
@@ -1930,6 +2096,12 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
mcp251xfd_ethtool_init(priv);
+ err = mcp251fdx_gpio_setup(priv);
+ if (err) {
+ dev_err_probe(&priv->spi->dev, err, "Failed to register gpio-controller.\n");
+ goto out_runtime_disable;
+ }
+
err = register_candev(ndev);
if (err)
goto out_runtime_disable;
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
index bd28510a6583..085d7101e595 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
@@ -15,6 +15,7 @@
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/regmap.h>
@@ -676,6 +677,7 @@ struct mcp251xfd_priv {
struct mcp251xfd_devtype_data devtype_data;
struct can_berr_counter bec;
+ struct gpio_chip gc;
};
#define MCP251XFD_IS(_model) \
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v5 5/6] can: mcp251xfd: add gpio functionality
2025-09-26 13:30 ` [PATCH v5 5/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
@ 2025-09-29 13:38 ` Bartosz Golaszewski
2025-09-30 11:25 ` Viken Dadhaniya
0 siblings, 1 reply; 9+ messages in thread
From: Bartosz Golaszewski @ 2025-09-29 13:38 UTC (permalink / raw)
To: Viken Dadhaniya
Cc: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, linux-can, devicetree, linux-kernel,
mukesh.savaliya, anup.kulkarni, Gregor Herburger
On Fri, Sep 26, 2025 at 3:30 PM Viken Dadhaniya
<viken.dadhaniya@oss.qualcomm.com> wrote:
>
> From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
>
> The mcp251xfd devices allow two pins to be configured as gpio. Add this
> functionality to driver.
>
> Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
> Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
> Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
> ---
> drivers/net/can/spi/mcp251xfd/Kconfig | 1 +
> .../net/can/spi/mcp251xfd/mcp251xfd-core.c | 172 ++++++++++++++++++
> drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 2 +
> 3 files changed, 175 insertions(+)
>
> diff --git a/drivers/net/can/spi/mcp251xfd/Kconfig b/drivers/net/can/spi/mcp251xfd/Kconfig
> index 877e4356010d..7c29846e6051 100644
> --- a/drivers/net/can/spi/mcp251xfd/Kconfig
> +++ b/drivers/net/can/spi/mcp251xfd/Kconfig
> @@ -5,6 +5,7 @@ config CAN_MCP251XFD
> select CAN_RX_OFFLOAD
> select REGMAP
> select WANT_DEV_COREDUMP
> + select GPIOLIB
> help
> Driver for the Microchip MCP251XFD SPI FD-CAN controller
> family.
> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
> index ea41f04ae1a6..88035d4404b5 100644
> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
> @@ -1797,6 +1797,172 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv)
> return 0;
> }
>
> +static const char * const mcp251xfd_gpio_names[] = { "GPIO0", "GPIO1" };
> +
> +static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset);
> + int ret;
> +
> + if (priv->rx_int && offset == 1) {
> + netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n");
> + return -EINVAL;
> + }
> +
> + ret = pm_runtime_resume_and_get(priv->ndev->dev.parent);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
> + pin_mask, pin_mask);
> +}
> +
> +static void mcp251xfd_gpio_free(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> +
> + pm_runtime_put(priv->ndev->dev.parent);
> +}
> +
> +static int mcp251xfd_gpio_get_direction(struct gpio_chip *chip,
> + unsigned int offset)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 mask = MCP251XFD_REG_IOCON_TRIS(offset);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
> + if (ret)
> + return ret;
> +
> + if (mask & val)
> + return GPIO_LINE_DIRECTION_IN;
> +
> + return GPIO_LINE_DIRECTION_OUT;
> +}
> +
> +static int mcp251xfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 mask = MCP251XFD_REG_IOCON_GPIO(offset);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
> + if (ret)
> + return ret;
> +
> + return !!(mask & val);
> +}
> +
> +static int mcp251xfd_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
> + unsigned long *bit)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
> + if (ret)
> + return ret;
> +
> + *bit = FIELD_GET(MCP251XFD_REG_IOCON_GPIO_MASK, val) & *mask;
> +
> + return 0;
> +}
> +
> +static int mcp251xfd_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
> + u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
> + u32 val;
> +
> + if (value)
> + val = val_mask;
> + else
> + val = 0;
> +
> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
> + dir_mask | val_mask, val);
> +}
> +
> +static int mcp251xfd_gpio_direction_input(struct gpio_chip *chip,
> + unsigned int offset)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
> +
> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
> + dir_mask, dir_mask);
> +}
> +
> +static int mcp251xfd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
> + u32 val;
> + int ret;
> +
> + if (value)
> + val = val_mask;
> + else
> + val = 0;
> +
> + ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
> + if (ret)
> + dev_err(&priv->spi->dev, "Failed to set GPIO %u: %d\n", offset, ret);
Why do you loudly complain here but not in other callbacks? I assume
it's because you had a log here in your previous version (the one
rebased on v6.16) and just didn't remove it when you switched to the
new API? Maybe just do `return regmap_update...`?
Otherwise looks good. With that addressed:
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> +
> + return ret;
> +}
> +
> +static int mcp251xfd_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
> + unsigned long *bits)
> +{
> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
> + u32 val;
> + int ret;
> +
> + val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
> +
> + ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
> + MCP251XFD_REG_IOCON_LAT_MASK, val);
> + if (ret)
> + dev_err(&priv->spi->dev, "Failed to set GPIOs %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int mcp251fdx_gpio_setup(struct mcp251xfd_priv *priv)
> +{
> + struct gpio_chip *gc = &priv->gc;
> +
> + if (!device_property_present(&priv->spi->dev, "gpio-controller"))
> + return 0;
> +
> + gc->label = dev_name(&priv->spi->dev);
> + gc->parent = &priv->spi->dev;
> + gc->owner = THIS_MODULE;
> + gc->request = mcp251xfd_gpio_request;
> + gc->free = mcp251xfd_gpio_free;
> + gc->get_direction = mcp251xfd_gpio_get_direction;
> + gc->direction_output = mcp251xfd_gpio_direction_output;
> + gc->direction_input = mcp251xfd_gpio_direction_input;
> + gc->get = mcp251xfd_gpio_get;
> + gc->get_multiple = mcp251xfd_gpio_get_multiple;
> + gc->set = mcp251xfd_gpio_set;
> + gc->set_multiple = mcp251xfd_gpio_set_multiple;
> + gc->base = -1;
> + gc->can_sleep = true;
> + gc->ngpio = ARRAY_SIZE(mcp251xfd_gpio_names);
> + gc->names = mcp251xfd_gpio_names;
> +
> + return devm_gpiochip_add_data(&priv->spi->dev, gc, priv);
> +}
> +
> static int
> mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id,
> u32 *effective_speed_hz_slow,
> @@ -1930,6 +2096,12 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
>
> mcp251xfd_ethtool_init(priv);
>
> + err = mcp251fdx_gpio_setup(priv);
> + if (err) {
> + dev_err_probe(&priv->spi->dev, err, "Failed to register gpio-controller.\n");
> + goto out_runtime_disable;
> + }
> +
> err = register_candev(ndev);
> if (err)
> goto out_runtime_disable;
> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
> index bd28510a6583..085d7101e595 100644
> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
> @@ -15,6 +15,7 @@
> #include <linux/can/dev.h>
> #include <linux/can/rx-offload.h>
> #include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> #include <linux/kernel.h>
> #include <linux/netdevice.h>
> #include <linux/regmap.h>
> @@ -676,6 +677,7 @@ struct mcp251xfd_priv {
>
> struct mcp251xfd_devtype_data devtype_data;
> struct can_berr_counter bec;
> + struct gpio_chip gc;
> };
>
> #define MCP251XFD_IS(_model) \
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v5 5/6] can: mcp251xfd: add gpio functionality
2025-09-29 13:38 ` Bartosz Golaszewski
@ 2025-09-30 11:25 ` Viken Dadhaniya
0 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-30 11:25 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, linux-can, devicetree, linux-kernel,
mukesh.savaliya, anup.kulkarni, Gregor Herburger
On 9/29/2025 7:08 PM, Bartosz Golaszewski wrote:
> On Fri, Sep 26, 2025 at 3:30 PM Viken Dadhaniya
> <viken.dadhaniya@oss.qualcomm.com> wrote:
>>
>> From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
>>
>> The mcp251xfd devices allow two pins to be configured as gpio. Add this
>> functionality to driver.
>>
>> Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
>> Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
>> Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
>> ---
>> drivers/net/can/spi/mcp251xfd/Kconfig | 1 +
>> .../net/can/spi/mcp251xfd/mcp251xfd-core.c | 172 ++++++++++++++++++
>> drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 2 +
>> 3 files changed, 175 insertions(+)
>>
>> diff --git a/drivers/net/can/spi/mcp251xfd/Kconfig b/drivers/net/can/spi/mcp251xfd/Kconfig
>> index 877e4356010d..7c29846e6051 100644
>> --- a/drivers/net/can/spi/mcp251xfd/Kconfig
>> +++ b/drivers/net/can/spi/mcp251xfd/Kconfig
>> @@ -5,6 +5,7 @@ config CAN_MCP251XFD
>> select CAN_RX_OFFLOAD
>> select REGMAP
>> select WANT_DEV_COREDUMP
>> + select GPIOLIB
>> help
>> Driver for the Microchip MCP251XFD SPI FD-CAN controller
>> family.
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> index ea41f04ae1a6..88035d4404b5 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> @@ -1797,6 +1797,172 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv)
>> return 0;
>> }
>>
>> +static const char * const mcp251xfd_gpio_names[] = { "GPIO0", "GPIO1" };
>> +
>> +static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset);
>> + int ret;
>> +
>> + if (priv->rx_int && offset == 1) {
>> + netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n");
>> + return -EINVAL;
>> + }
>> +
>> + ret = pm_runtime_resume_and_get(priv->ndev->dev.parent);
>> + if (ret)
>> + return ret;
>> +
>> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
>> + pin_mask, pin_mask);
>> +}
>> +
>> +static void mcp251xfd_gpio_free(struct gpio_chip *chip, unsigned int offset)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> +
>> + pm_runtime_put(priv->ndev->dev.parent);
>> +}
>> +
>> +static int mcp251xfd_gpio_get_direction(struct gpio_chip *chip,
>> + unsigned int offset)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 mask = MCP251XFD_REG_IOCON_TRIS(offset);
>> + u32 val;
>> + int ret;
>> +
>> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
>> + if (ret)
>> + return ret;
>> +
>> + if (mask & val)
>> + return GPIO_LINE_DIRECTION_IN;
>> +
>> + return GPIO_LINE_DIRECTION_OUT;
>> +}
>> +
>> +static int mcp251xfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 mask = MCP251XFD_REG_IOCON_GPIO(offset);
>> + u32 val;
>> + int ret;
>> +
>> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
>> + if (ret)
>> + return ret;
>> +
>> + return !!(mask & val);
>> +}
>> +
>> +static int mcp251xfd_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
>> + unsigned long *bit)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 val;
>> + int ret;
>> +
>> + ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
>> + if (ret)
>> + return ret;
>> +
>> + *bit = FIELD_GET(MCP251XFD_REG_IOCON_GPIO_MASK, val) & *mask;
>> +
>> + return 0;
>> +}
>> +
>> +static int mcp251xfd_gpio_direction_output(struct gpio_chip *chip,
>> + unsigned int offset, int value)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
>> + u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
>> + u32 val;
>> +
>> + if (value)
>> + val = val_mask;
>> + else
>> + val = 0;
>> +
>> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
>> + dir_mask | val_mask, val);
>> +}
>> +
>> +static int mcp251xfd_gpio_direction_input(struct gpio_chip *chip,
>> + unsigned int offset)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
>> +
>> + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
>> + dir_mask, dir_mask);
>> +}
>> +
>> +static int mcp251xfd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
>> + u32 val;
>> + int ret;
>> +
>> + if (value)
>> + val = val_mask;
>> + else
>> + val = 0;
>> +
>> + ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
>> + if (ret)
>> + dev_err(&priv->spi->dev, "Failed to set GPIO %u: %d\n", offset, ret);
>
> Why do you loudly complain here but not in other callbacks? I assume
> it's because you had a log here in your previous version (the one
> rebased on v6.16) and just didn't remove it when you switched to the
> new API? Maybe just do `return regmap_update...`?
Sure, I’ll update and post v6.
>
> Otherwise looks good. With that addressed:
>
> Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
>> +
>> + return ret;
>> +}
>> +
>> +static int mcp251xfd_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
>> + unsigned long *bits)
>> +{
>> + struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
>> + u32 val;
>> + int ret;
>> +
>> + val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
>> +
>> + ret = regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
>> + MCP251XFD_REG_IOCON_LAT_MASK, val);
>> + if (ret)
>> + dev_err(&priv->spi->dev, "Failed to set GPIOs %d\n", ret);
>> +
>> + return ret;
>> +}
>> +
>> +static int mcp251fdx_gpio_setup(struct mcp251xfd_priv *priv)
>> +{
>> + struct gpio_chip *gc = &priv->gc;
>> +
>> + if (!device_property_present(&priv->spi->dev, "gpio-controller"))
>> + return 0;
>> +
>> + gc->label = dev_name(&priv->spi->dev);
>> + gc->parent = &priv->spi->dev;
>> + gc->owner = THIS_MODULE;
>> + gc->request = mcp251xfd_gpio_request;
>> + gc->free = mcp251xfd_gpio_free;
>> + gc->get_direction = mcp251xfd_gpio_get_direction;
>> + gc->direction_output = mcp251xfd_gpio_direction_output;
>> + gc->direction_input = mcp251xfd_gpio_direction_input;
>> + gc->get = mcp251xfd_gpio_get;
>> + gc->get_multiple = mcp251xfd_gpio_get_multiple;
>> + gc->set = mcp251xfd_gpio_set;
>> + gc->set_multiple = mcp251xfd_gpio_set_multiple;
>> + gc->base = -1;
>> + gc->can_sleep = true;
>> + gc->ngpio = ARRAY_SIZE(mcp251xfd_gpio_names);
>> + gc->names = mcp251xfd_gpio_names;
>> +
>> + return devm_gpiochip_add_data(&priv->spi->dev, gc, priv);
>> +}
>> +
>> static int
>> mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id,
>> u32 *effective_speed_hz_slow,
>> @@ -1930,6 +2096,12 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
>>
>> mcp251xfd_ethtool_init(priv);
>>
>> + err = mcp251fdx_gpio_setup(priv);
>> + if (err) {
>> + dev_err_probe(&priv->spi->dev, err, "Failed to register gpio-controller.\n");
>> + goto out_runtime_disable;
>> + }
>> +
>> err = register_candev(ndev);
>> if (err)
>> goto out_runtime_disable;
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> index bd28510a6583..085d7101e595 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> @@ -15,6 +15,7 @@
>> #include <linux/can/dev.h>
>> #include <linux/can/rx-offload.h>
>> #include <linux/gpio/consumer.h>
>> +#include <linux/gpio/driver.h>
>> #include <linux/kernel.h>
>> #include <linux/netdevice.h>
>> #include <linux/regmap.h>
>> @@ -676,6 +677,7 @@ struct mcp251xfd_priv {
>>
>> struct mcp251xfd_devtype_data devtype_data;
>> struct can_berr_counter bec;
>> + struct gpio_chip gc;
>> };
>>
>> #define MCP251XFD_IS(_model) \
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5 6/6] dt-bindings: can: mcp251xfd: add gpio-controller property
2025-09-26 13:30 [PATCH v5 0/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
` (4 preceding siblings ...)
2025-09-26 13:30 ` [PATCH v5 5/6] can: mcp251xfd: add gpio functionality Viken Dadhaniya
@ 2025-09-26 13:30 ` Viken Dadhaniya
5 siblings, 0 replies; 9+ messages in thread
From: Viken Dadhaniya @ 2025-09-26 13:30 UTC (permalink / raw)
To: mkl, mani, thomas.kopp, mailhol.vincent, robh, krzk+dt, conor+dt,
linus.walleij, brgl, linux-can, devicetree, linux-kernel
Cc: mukesh.savaliya, anup.kulkarni, Gregor Herburger,
Krzysztof Kozlowski, Viken Dadhaniya
From: Gregor Herburger <gregor.herburger@ew.tq-group.com>
The mcp251xfd has two pins that can be used as gpio. Add gpio-controller
property to binding description.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Gregor Herburger <gregor.herburger@ew.tq-group.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
.../devicetree/bindings/net/can/microchip,mcp251xfd.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
index c155c9c6db39..2d13638ebc6a 100644
--- a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
@@ -49,6 +49,11 @@ properties:
Must be half or less of "clocks" frequency.
maximum: 20000000
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
required:
- compatible
- reg
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread