]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
can: mcp251xfd: add gpio functionality
authorGregor Herburger <gregor.herburger@ew.tq-group.com>
Wed, 1 Oct 2025 09:10:05 +0000 (14:40 +0530)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 12 Nov 2025 18:30:33 +0000 (19:30 +0100)
The mcp251xfd devices allow two pins to be configured as gpio. Add this
functionality to driver.

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
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>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20251001091006.4003841-6-viken.dadhaniya@oss.qualcomm.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/spi/mcp251xfd/Kconfig
drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
drivers/net/can/spi/mcp251xfd/mcp251xfd.h

index 877e4356010ddd9ec601ef17840b2b5503dea725..7c29846e605179af3f36ea9fbaf7f9826ad897f7 100644 (file)
@@ -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.
index 565327bf9c43783013a6fd27b7602b5ebd8d0300..5134ebb85880b825700698ff3feff3d59459e37f 100644 (file)
@@ -1797,6 +1797,160 @@ 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;
+
+       if (value)
+               val = val_mask;
+       else
+               val = 0;
+
+       return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
+}
+
+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;
+
+       val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
+
+       return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+                                 MCP251XFD_REG_IOCON_LAT_MASK, val);
+}
+
+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 +2084,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;
index bd28510a65839384869e4ccfbcbadf0be03febf0..085d7101e595ed8463f4d85c98e2a9e975f0f648 100644 (file)
@@ -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) \