Add driver for maxim MAX7317 SPI-Interfaced 10 Port GPIO Expander. v2: adjust driver to use regmap Co-developed-by: Lukasz Zemla <Lukasz.Zemla@xxxxxxxxxxxx> Signed-off-by: Lukasz Zemla <Lukasz.Zemla@xxxxxxxxxxxx> Signed-off-by: Edmund Berenson <edmund.berenson@xxxxxxxxx> --- drivers/gpio/Kconfig | 11 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max7317.c | 161 ++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/gpio/gpio-max7317.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 13be729710f2..109cf09d8c05 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1615,6 +1615,17 @@ config GPIO_MAX7301 help GPIO driver for Maxim MAX7301 SPI-based GPIO expander. +config GPIO_MAX7317 + tristate "Maxim MAX7317 GPIO expander" + select REGMAP_SPI + help + GPIO driver for Maxim MAX7317 SPI-based GPIO expander. + The MAX7317 is a serial-interfaced gpio extender, with + 10 ports. + + This driver can also be built as a module. If so, the module will be + called gpio-max7317. + config GPIO_MC33880 tristate "Freescale MC33880 high-side/low-side switch" help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c048ba003367..8dce549bb6c5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o +obj-$(CONFIG_GPIO_MAX7317) += gpio-max7317.o obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o diff --git a/drivers/gpio/gpio-max7317.c b/drivers/gpio/gpio-max7317.c new file mode 100644 index 000000000000..65824efaad6c --- /dev/null +++ b/drivers/gpio/gpio-max7317.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021, Lukasz Zemla, Woodward Inc. + */ + +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#define MAX7317_PIN_NUMBER 10 +#define REG_CODE_READ_PORTS_7_TO_0 ((u8)0x0E) +#define REG_CODE_READ_PORTS_9_TO_8 ((u8)0x0F) + +struct max7317 { + struct gpio_chip chip; + struct regmap *regmap; + int direction[MAX7317_PIN_NUMBER]; +}; + +struct max7317_platform_data { + unsigned int gpio_base; +}; + +static const struct regmap_config max7317_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max7317_get(struct gpio_chip *chip, unsigned int offset) +{ + struct max7317 *ts = gpiochip_get_data(chip); + unsigned int val; + int ret; + u8 reg = (offset < 8) ? REG_CODE_READ_PORTS_7_TO_0 : REG_CODE_READ_PORTS_9_TO_8; + + ret = regmap_read(ts->regmap, reg, &val); + if (ret) + return ret; + + return val & BIT(offset % 8); +} + +/* + * After writing the register an additional read is performed in order for + * changes to take effect. + */ +static void max7317_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct max7317 *ts = gpiochip_get_data(chip); + + if (regmap_write(ts->regmap, offset, value ? 1 : 0)) + dev_err(chip->parent, "Failed to set pin: %d\n", offset); + max7317_get(chip, offset); +} + +static int max7317_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct max7317 *ts = gpiochip_get_data(chip); + + max7317_set(chip, offset, 1); + ts->direction[offset] = GPIO_LINE_DIRECTION_IN; + return 0; +} + +static int max7317_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct max7317 *ts = gpiochip_get_data(chip); + + ts->direction[offset] = GPIO_LINE_DIRECTION_OUT; + return 0; +} + +static int max7317_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct max7317 *ts = gpiochip_get_data(chip); + + return ts->direction[offset]; +} + +static int max7317_probe(struct spi_device *spi) +{ + struct max7317 *ts; + struct device *dev; + struct max7317_platform_data *pdata; + int i; + + ts = devm_kzalloc(&spi->dev, sizeof(struct max7317), GFP_KERNEL); + if (!ts) + return -ENOMEM; + dev_set_drvdata(&spi->dev, ts); + + pdata = dev_get_platdata(dev); + if (pdata) + ts->chip.base = pdata->gpio_base; + else + ts->chip.base = -1; + + ts->chip.label = dev_name(&spi->dev); + ts->chip.direction_input = max7317_direction_input; + ts->chip.get = max7317_get; + ts->chip.direction_output = max7317_direction_output; + ts->chip.set = max7317_set; + ts->chip.get_direction = max7317_get_direction; + + ts->chip.ngpio = MAX7317_PIN_NUMBER; + ts->chip.can_sleep = true; + ts->chip.parent = &spi->dev; + ts->chip.owner = THIS_MODULE; + + for (i = 0; i < MAX7317_PIN_NUMBER; i++) + ts->direction[i] = GPIO_LINE_DIRECTION_IN; + + ts->regmap = devm_regmap_init_spi(spi, &max7317_regmap_cfg); + if (IS_ERR(ts->regmap)) + return PTR_ERR(ts->regmap); + + return devm_gpiochip_add_data(&spi->dev, &ts->chip, ts); +} + +static void max7317_remove(struct spi_device *spi) +{ + struct max7317 *ts = dev_get_drvdata(&spi->dev); + + if (!ts) + return; + + gpiochip_remove(&ts->chip); +} + +static const struct spi_device_id max7317_id[] = { + { "max7317", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max7317_id); + +static const struct of_device_id max7317_dt_ids[] = { + { .compatible = "maxim,max7317" }, + { } +}; +MODULE_DEVICE_TABLE(of, max7317_dt_ids); + +static struct spi_driver max7317_driver = { + .driver = { + .name = "max7317", + .of_match_table = max7317_dt_ids, + }, + .probe = max7317_probe, + .remove = max7317_remove, + .id_table = max7317_id, +}; +module_spi_driver(max7317_driver); + +MODULE_AUTHOR("Lukasz Zemla"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MAX7317 GPIO-Expander"); -- 2.39.2