From: Haibo Chen <haibo.chen@xxxxxxx> Add gpio function support for MFD adp5585. Signed-off-by: Haibo Chen <haibo.chen@xxxxxxx> Reviewed-by: Jun Li <jun.li@xxxxxxx> Signed-off-by: Frank Li <Frank.Li@xxxxxxx> --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-adp5585.c | 184 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 58f43bcced7c1..20daa3d70abc7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1233,6 +1233,13 @@ config GPIO_ADP5520 This option enables support for on-chip GPIO found on Analog Devices ADP5520 PMICs. +config GPIO_ADP5585 + tristate "GPIO Support for ADP5585" + depends on MFD_ADP5585 + help + This option enables support for on-chip GPIO found on Analog + devices ADP5585. + config GPIO_ALTERA_A10SR tristate "Altera Arria10 System Resource GPIO" depends on MFD_ALTERA_A10SR diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 64dd6d9d730d5..1429e8c0229b9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o +obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c new file mode 100644 index 0000000000000..7c9edbc16975b --- /dev/null +++ b/drivers/gpio/gpio-adp5585.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO driver for Analog Devices ADP5585 MFD + * + * Copyright 2024 NXP + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mfd/adp5585.h> +#include <linux/gpio/driver.h> + +#define ADP5585_GPIO_MAX 10 + +struct adp5585_gpio_dev { + struct device *parent; + struct gpio_chip gpio_chip; + struct mutex lock; + u8 dat_out[2]; + u8 dir[2]; +}; + +static int adp5585_gpio_reg_read(struct adp5585_gpio_dev *adp5585_gpio, u8 reg, u8 *val) +{ + struct adp5585_dev *adp5585 = dev_get_drvdata(adp5585_gpio->parent); + + return adp5585->read_reg(adp5585, reg, val); +} + +static int adp5585_gpio_reg_write(struct adp5585_gpio_dev *adp5585_gpio, u8 reg, u8 val) +{ + struct adp5585_dev *adp5585 = dev_get_drvdata(adp5585_gpio->parent); + + return adp5585->write_reg(adp5585, reg, val); +} + +static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) +{ + struct adp5585_gpio_dev *adp5585_gpio; + unsigned int bank, bit; + u8 val; + + adp5585_gpio = gpiochip_get_data(chip); + bank = ADP5585_BANK(off); + bit = ADP5585_BIT(off); + + guard(mutex)(&adp5585_gpio->lock); + + /* There are dedicated registers for GPIO IN/OUT. */ + if (adp5585_gpio->dir[bank] & bit) + val = adp5585_gpio->dat_out[bank]; + else + adp5585_gpio_reg_read(adp5585_gpio, ADP5585_GPI_STATUS_A + bank, &val); + + return !!(val & bit); +} + +static void adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val) +{ + struct adp5585_gpio_dev *adp5585_gpio; + unsigned int bank, bit; + + adp5585_gpio = gpiochip_get_data(chip); + bank = ADP5585_BANK(off); + bit = ADP5585_BIT(off); + + guard(mutex)(&adp5585_gpio->lock); + + if (val) + adp5585_gpio->dat_out[bank] |= bit; + else + adp5585_gpio->dat_out[bank] &= ~bit; + + adp5585_gpio_reg_write(adp5585_gpio, ADP5585_GPO_DATA_OUT_A + bank, + adp5585_gpio->dat_out[bank]); +} + +static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off) +{ + struct adp5585_gpio_dev *adp5585_gpio; + unsigned int bank, bit; + int ret; + + adp5585_gpio = gpiochip_get_data(chip); + bank = ADP5585_BANK(off); + bit = ADP5585_BIT(off); + + guard(mutex)(&adp5585_gpio->lock); + + adp5585_gpio->dir[bank] &= ~bit; + ret = adp5585_gpio_reg_write(adp5585_gpio, ADP5585_GPIO_DIRECTION_A + bank, + adp5585_gpio->dir[bank]); + return ret; +} + +static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val) +{ + struct adp5585_gpio_dev *adp5585_gpio; + unsigned int bank, bit; + int ret; + + adp5585_gpio = gpiochip_get_data(chip); + bank = ADP5585_BANK(off); + bit = ADP5585_BIT(off); + + guard(mutex)(&adp5585_gpio->lock); + + adp5585_gpio->dir[bank] |= bit; + + if (val) + adp5585_gpio->dat_out[bank] |= bit; + else + adp5585_gpio->dat_out[bank] &= ~bit; + + ret = adp5585_gpio_reg_write(adp5585_gpio, ADP5585_GPO_DATA_OUT_A + bank, + adp5585_gpio->dat_out[bank]); + ret |= adp5585_gpio_reg_write(adp5585_gpio, ADP5585_GPIO_DIRECTION_A + bank, + adp5585_gpio->dir[bank]); + + return ret; +} + +static int adp5585_gpio_probe(struct platform_device *pdev) +{ + struct adp5585_gpio_dev *adp5585_gpio; + struct device *dev = &pdev->dev; + struct gpio_chip *gc; + int i; + + adp5585_gpio = devm_kzalloc(&pdev->dev, sizeof(struct adp5585_gpio_dev), GFP_KERNEL); + if (!adp5585_gpio) + return -ENOMEM; + + adp5585_gpio->parent = pdev->dev.parent; + + gc = &adp5585_gpio->gpio_chip; + gc->parent = dev; + gc->direction_input = adp5585_gpio_direction_input; + gc->direction_output = adp5585_gpio_direction_output; + gc->get = adp5585_gpio_get_value; + gc->set = adp5585_gpio_set_value; + gc->can_sleep = true; + + gc->base = -1; + gc->ngpio = ADP5585_GPIO_MAX; + gc->label = pdev->name; + gc->owner = THIS_MODULE; + + mutex_init(&adp5585_gpio->lock); + + for (i = 0; i < 2; i++) { + u8 *dat_out, *dir; + + dat_out = adp5585_gpio->dat_out; + dir = adp5585_gpio->dir; + adp5585_gpio_reg_read(adp5585_gpio, ADP5585_GPO_DATA_OUT_A + i, dat_out + i); + adp5585_gpio_reg_read(adp5585_gpio, ADP5585_GPIO_DIRECTION_A + i, dir + i); + } + + return devm_gpiochip_add_data(&pdev->dev, &adp5585_gpio->gpio_chip, adp5585_gpio); +} + +static const struct of_device_id adp5585_of_match[] = { + {.compatible = "adp5585-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, adp5585_of_match); + +static struct platform_driver adp5585_gpio_driver = { + .driver = { + .name = "adp5585-gpio", + .of_match_table = adp5585_of_match, + }, + .probe = adp5585_gpio_probe, +}; + +module_platform_driver(adp5585_gpio_driver); + +MODULE_AUTHOR("Haibo Chen <haibo.chen@xxxxxxx>"); +MODULE_DESCRIPTION("GPIO ADP5585 Driver"); +MODULE_LICENSE("GPL"); -- 2.34.1