mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
can: mcp251xfd: add gpio functionality
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>
This commit is contained in:
committed by
Marc Kleine-Budde
parent
d35fa005f5
commit
c6106336ec
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) \
|
||||
|
||||
Reference in New Issue
Block a user