lkml.org 
[lkml]   [2024]   [Apr]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3/4] can: mcp251xfd: add gpio functionality
    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>
    ---
    drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c | 138 ++++++++++++++++++++++-
    drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c | 21 +++-
    drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 4 +
    3 files changed, 159 insertions(+), 4 deletions(-)

    diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
    index eb699288c076..5ba9fd0af4b6 100644
    --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
    +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
    @@ -16,6 +16,7 @@
    #include <linux/bitfield.h>
    #include <linux/clk.h>
    #include <linux/device.h>
    +#include <linux/gpio/driver.h>
    #include <linux/mod_devicetable.h>
    #include <linux/module.h>
    #include <linux/pm_runtime.h>
    @@ -366,6 +367,8 @@ static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv)

    static inline int mcp251xfd_chip_sleep(const struct mcp251xfd_priv *priv)
    {
    + int ret;
    +
    if (priv->pll_enable) {
    u32 osc;
    int err;
    @@ -381,7 +384,12 @@ static inline int mcp251xfd_chip_sleep(const struct mcp251xfd_priv *priv)
    priv->spi->max_speed_hz = priv->spi_max_speed_hz_slow;
    }

    - return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP);
    + ret = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP);
    +
    + regcache_cache_only(priv->map_reg, true);
    + regcache_mark_dirty(priv->map_reg);
    +
    + return ret;
    }

    static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv)
    @@ -389,6 +397,8 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv)
    const __be16 cmd = mcp251xfd_cmd_reset();
    int err;

    + regcache_cache_only(priv->map_reg, false);
    +
    /* The Set Mode and SPI Reset command only works if the
    * controller is not in Sleep Mode.
    */
    @@ -401,7 +411,12 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv)
    return err;

    /* spi_write_then_read() works with non DMA-safe buffers */
    - return spi_write_then_read(priv->spi, &cmd, sizeof(cmd), NULL, 0);
    + err = spi_write_then_read(priv->spi, &cmd, sizeof(cmd), NULL, 0);
    + if (err)
    + return err;
    +
    + /* After reset restore cached register values to hardware */
    + return regcache_sync(priv->map_reg);
    }

    static int mcp251xfd_chip_softreset_check(const struct mcp251xfd_priv *priv)
    @@ -1772,6 +1787,119 @@ 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_PM0 << offset;
    +
    + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
    + pin_mask, pin_mask);
    +}
    +
    +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_TRIS0 << offset;
    + u32 val;
    +
    + regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
    +
    + 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_GPIO0 << offset;
    + u32 val;
    +
    + regcache_drop_region(priv->map_reg, MCP251XFD_REG_IOCON, MCP251XFD_REG_IOCON);
    + regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
    +
    + return !!(mask & val);
    +}
    +
    +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_TRIS0 << offset;
    + u32 val_mask = MCP251XFD_REG_IOCON_LAT0 << 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_TRIS0 << offset;
    +
    + return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
    + dir_mask, dir_mask);
    +}
    +
    +static void 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_LAT0 << 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_warn(&priv->spi->dev,
    + "Failed to set GPIO %u: %d\n", offset, 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;
    +
    + if (priv->rx_int)
    + return dev_err_probe(&priv->spi->dev, -EINVAL,
    + "Can't configure gpio-controller with RX-INT!\n");
    +
    + gc->label = dev_name(&priv->spi->dev);
    + gc->parent = &priv->spi->dev;
    + gc->owner = THIS_MODULE;
    + gc->request = mcp251xfd_gpio_request;
    + 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->set = mcp251xfd_gpio_set;
    + 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,
    @@ -2142,6 +2270,12 @@ static int mcp251xfd_probe(struct spi_device *spi)
    if (err)
    goto out_free_candev;

    + err = mcp251fdx_gpio_setup(priv);
    + if (err) {
    + dev_err_probe(&spi->dev, err, "Failed to register gpio-controller.\n");
    + goto out_free_candev;
    + }
    +
    err = mcp251xfd_register(priv);
    if (err) {
    dev_err_probe(&spi->dev, err, "Failed to detect %s.\n",
    diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
    index ab4e372baffb..868c424f22a4 100644
    --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
    +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
    @@ -447,6 +447,21 @@ static const struct regmap_access_table mcp251xfd_reg_table = {
    .n_yes_ranges = ARRAY_SIZE(mcp251xfd_reg_table_yes_range),
    };

    +static bool mcp251xfd_volatile_reg(struct device *dev, unsigned int reg)
    +{
    + switch (reg) {
    + case MCP251XFD_REG_ECCCON:
    + case MCP251XFD_REG_DEVID:
    + case MCP251XFD_REG_NBTCFG:
    + case MCP251XFD_REG_DBTCFG:
    + case MCP251XFD_REG_TDC:
    + case MCP251XFD_REG_TSCON:
    + case MCP251XFD_REG_IOCON:
    + return false;
    + }
    + return true;
    +}
    +
    static const struct regmap_config mcp251xfd_regmap_nocrc = {
    .name = "nocrc",
    .reg_bits = 16,
    @@ -456,7 +471,8 @@ static const struct regmap_config mcp251xfd_regmap_nocrc = {
    .max_register = 0xffc,
    .wr_table = &mcp251xfd_reg_table,
    .rd_table = &mcp251xfd_reg_table,
    - .cache_type = REGCACHE_NONE,
    + .cache_type = REGCACHE_MAPLE,
    + .volatile_reg = mcp251xfd_volatile_reg,
    .read_flag_mask = (__force unsigned long)
    cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ),
    .write_flag_mask = (__force unsigned long)
    @@ -483,7 +499,8 @@ static const struct regmap_config mcp251xfd_regmap_crc = {
    .max_register = 0xffc,
    .wr_table = &mcp251xfd_reg_table,
    .rd_table = &mcp251xfd_reg_table,
    - .cache_type = REGCACHE_NONE,
    + .cache_type = REGCACHE_MAPLE,
    + .volatile_reg = mcp251xfd_volatile_reg,
    };

    static const struct regmap_bus mcp251xfd_bus_crc = {
    diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
    index 24510b3b8020..e2ab486862d8 100644
    --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
    +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
    @@ -14,6 +14,7 @@
    #include <linux/can/core.h>
    #include <linux/can/dev.h>
    #include <linux/can/rx-offload.h>
    +#include <linux/gpio/driver.h>
    #include <linux/gpio/consumer.h>
    #include <linux/kernel.h>
    #include <linux/netdevice.h>
    @@ -660,6 +661,9 @@ struct mcp251xfd_priv {

    struct mcp251xfd_devtype_data devtype_data;
    struct can_berr_counter bec;
    +#ifdef CONFIG_GPIOLIB
    + struct gpio_chip gc;
    +#endif
    };

    #define MCP251XFD_IS(_model) \
    --
    2.34.1


    \
     
     \ /
      Last update: 2024-04-17 15:45    [W:3.123 / U:0.372 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site