From: Raviteja Narayanam <raviteja.narayanam@xxxxxxxxxx> Dialog semiconductors SLG7XL45106 is an 8-bit I2C GPO expander. The output port is controlled by a data byte with register address. Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xxxxxxxxxx> Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xxxxxxxxxx> --- MAINTAINERS | 7 ++ drivers/gpio/Kconfig | 9 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-slg7xl45106.c | 133 ++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 drivers/gpio/gpio-slg7xl45106.c diff --git a/MAINTAINERS b/MAINTAINERS index 1fc9ead83d2a..3b3f322b5012 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5875,6 +5875,13 @@ F: include/linux/regulator/da9211.h F: include/sound/da[79]*.h F: sound/soc/codecs/da[79]*.[ch] +DIALOG SLG7XL45106 GPO DRIVER +M: Shubhrajyoti Datta <shubhrajyoti.datta@xxxxxxx> +L: linux-gpio@xxxxxxxxxxxxxxx +S: Maintained +F: Documentation/devicetree/bindings/gpio/gpio-slg7xl45106.yaml +F: drivers/gpio/gpio-slg7xl45106.c + DIAMOND SYSTEMS GPIO-MM GPIO DRIVER M: William Breathitt Gray <vilhelm.gray@xxxxxxxxx> L: linux-gpio@xxxxxxxxxxxxxxx diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b01961999ced..1e10f96c9c09 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1101,6 +1101,15 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_SLG7XL45106 + tristate "SLG7XL45106 8-Bit I2C GPO expander" + help + Say yes here to enable the GPO driver for the Dialog SLG7XL45106 chip. + This expander has 8 output pins. + + To compile this driver as a module, choose M here: the module will + be called gpio-slg7xl45106. + config GPIO_TPIC2810 tristate "TPIC2810 8-Bit I2C GPO expander" help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 14352f6dfe8e..8248e4fcc22f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIM) += gpio-sim.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o +obj-$(CONFIG_GPIO_SLG7XL45106) += gpio-slg7xl45106.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o diff --git a/drivers/gpio/gpio-slg7xl45106.c b/drivers/gpio/gpio-slg7xl45106.c new file mode 100644 index 000000000000..61935b8538d1 --- /dev/null +++ b/drivers/gpio/gpio-slg7xl45106.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for slg7xl45106 I2C GPO expander + * + * Copyright (C) 2021 Xilinx, Inc. + * Copyright (C) 2022 AMD, Inc. + * + * Based on gpio-pca9570.c + * Copyright (C) 2020 Sungbo Eo <mans0n@xxxxxxxxxx> + */ + +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> + +#define SLG7XL45106_GPO_REG 0xDB + +/** + * struct slg7xl45106 - GPIO driver data + * @chip: GPIO controller chip + * @lock: Protects write sequences + */ +struct slg7xl45106 { + struct gpio_chip chip; + struct mutex lock; /* To protect writes */ +}; + +static int slg7xl45106_read(struct slg7xl45106 *gpio) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + + return i2c_smbus_read_byte_data(client, SLG7XL45106_GPO_REG); +} + +static int slg7xl45106_write(struct slg7xl45106 *gpio, u8 value) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + + return i2c_smbus_write_byte_data(client, SLG7XL45106_GPO_REG, value); +} + +static int slg7xl45106_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* This device always output */ + return GPIO_LINE_DIRECTION_OUT; +} + +static int slg7xl45106_get(struct gpio_chip *chip, unsigned int offset) +{ + struct slg7xl45106 *gpio = gpiochip_get_data(chip); + int ret; + + ret = slg7xl45106_read(gpio); + if (ret < 0) + return ret; + + return !!(ret & BIT(offset)); +} + +static void slg7xl45106_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct slg7xl45106 *gpio = gpiochip_get_data(chip); + u8 buffer; + + mutex_lock(&gpio->lock); + + buffer = slg7xl45106_read(gpio); + if (buffer < 0) + goto out; + + if (value) + buffer |= BIT(offset); + else + buffer &= ~BIT(offset); + + slg7xl45106_write(gpio, buffer); + +out: + mutex_unlock(&gpio->lock); +} + +static int slg7xl45106_probe(struct i2c_client *client) +{ + struct slg7xl45106 *gpio; + + gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->chip.label = client->name; + gpio->chip.parent = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get_direction = slg7xl45106_get_direction; + gpio->chip.get = slg7xl45106_get; + gpio->chip.set = slg7xl45106_set; + gpio->chip.base = -1; + gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev); + gpio->chip.can_sleep = true; + + mutex_init(&gpio->lock); + + i2c_set_clientdata(client, gpio); + + return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); +} + +static const struct i2c_device_id slg7xl45106_id_table[] = { + { "slg7xl45106", 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, slg7xl45106_id_table); + +static const struct of_device_id slg7xl45106_of_match_table[] = { + { .compatible = "dlg,slg7xl45106", .data = (void *)8 }, + { } +}; +MODULE_DEVICE_TABLE(of, slg7xl45106_of_match_table); + +static struct i2c_driver slg7xl45106_driver = { + .driver = { + .name = "slg7xl45106", + .of_match_table = slg7xl45106_of_match_table, + }, + .probe_new = slg7xl45106_probe, + .id_table = slg7xl45106_id_table, +}; +module_i2c_driver(slg7xl45106_driver); + +MODULE_AUTHOR("Raviteja Narayanam <raviteja.narayanam@xxxxxxxxxx>"); +MODULE_DESCRIPTION("GPIO expander driver for slg7xl45106"); +MODULE_LICENSE("GPL"); -- 2.17.1