[PATCH v1 5/7] iio: adc: ad4170: Add GPIO controller support

From: Marcelo Schmitt
Date: Wed Apr 09 2025 - 08:29:48 EST


The AD4170 has four multifunctional pins that can be used as GPIOs. The
GPIO functionality can be accessed when the AD4170 chip is not busy
performing continuous data capture or handling any other register
read/write request. Also, the AD4170 does not provide any interrupt based
on GPIO pin states so AD4170 GPIOs can't be used as interrupt sources.

Implement gpio_chip callbacks so to make AD4170 GPIO pins controllable
through the gpiochip interface.

Signed-off-by: Marcelo Schmitt <marcelo.schmitt@xxxxxxxxxx>
---
drivers/iio/adc/ad4170.c | 167 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c
index 97cf4465038f..b382e7f3dbe0 100644
--- a/drivers/iio/adc/ad4170.c
+++ b/drivers/iio/adc/ad4170.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/gpio/driver.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -79,6 +80,7 @@
#define AD4170_FIR_CTRL 0x141
#define AD4170_COEFF_DATA_REG 0x14A
#define AD4170_COEFF_ADDR_REG 0x14C
+#define AD4170_GPIO_MODE_REG 0x191
#define AD4170_GPIO_OUTPUT_REG 0x193
#define AD4170_GPIO_INPUT_REG 0x195

@@ -189,6 +191,7 @@
/* Device properties and auxiliary constants */

#define AD4170_NUM_ANALOG_PINS 9
+#define AD4170_NUM_GPIO_PINS 4
#define AD4170_MAX_CHANNELS 16
#define AD4170_MAX_ANALOG_PINS 8
#define AD4170_MAX_SETUPS 8
@@ -340,6 +343,7 @@ struct ad4170_state {
struct clk *ext_clk;
struct clk_hw int_clk_hw;
int pins_fn[AD4170_NUM_ANALOG_PINS];
+ struct gpio_chip gpiochip;
u32 int_pin_sel;
int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2];
struct completion completion;
@@ -1553,6 +1557,156 @@ static int ad4170_soft_reset(struct ad4170_state *st)
return 0;
}

+static int ad4170_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ad4170_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_read(st->regmap16, AD4170_GPIO_MODE_REG, &val);
+ if (ret)
+ goto err_release;
+
+ /*
+ * If the GPIO is configured as an input, read the current value from
+ * AD4170_GPIO_INPUT_REG. Otherwise, read the input value from
+ * AD4170_GPIO_OUTPUT_REG.
+ */
+ if (val & BIT(offset * 2))
+ ret = regmap_read(st->regmap16, AD4170_GPIO_INPUT_REG, &val);
+ else
+ ret = regmap_read(st->regmap16, AD4170_GPIO_OUTPUT_REG, &val);
+ if (ret)
+ goto err_release;
+
+ ret = !!(val & BIT(offset));
+err_release:
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4170_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ad4170_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_read(st->regmap16, AD4170_GPIO_MODE_REG, &val);
+ if (ret)
+ goto err_release;
+
+ if (val & BIT(offset * 2 + 1))
+ ret = regmap_update_bits(st->regmap16, AD4170_GPIO_OUTPUT_REG,
+ BIT(offset), value << offset);
+
+err_release:
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ad4170_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_read(st->regmap16, AD4170_GPIO_MODE_REG, &val);
+ if (ret)
+ goto err_release;
+
+ if (val & BIT(offset * 2 + 1))
+ ret = GPIO_LINE_DIRECTION_OUT;
+ else
+ ret = GPIO_LINE_DIRECTION_IN;
+
+err_release:
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ad4170_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_clear_bits(st->regmap16, AD4170_GPIO_MODE_REG,
+ BIT(offset * 2 + 1));
+ if (ret)
+ goto err_release;
+
+ ret = regmap_set_bits(st->regmap16, AD4170_GPIO_MODE_REG,
+ BIT(offset * 2));
+
+err_release:
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4170_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ad4170_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_clear_bits(st->regmap16, AD4170_GPIO_MODE_REG,
+ BIT(offset * 2));
+ if (ret)
+ goto err_release;
+
+ ret = regmap_set_bits(st->regmap16, AD4170_GPIO_MODE_REG,
+ BIT(offset * 2 + 1));
+
+err_release:
+ iio_device_release_direct(indio_dev);
+
+ ad4170_gpio_set(gc, offset, value);
+ return ret;
+}
+
+static int ad4170_gpio_init(struct iio_dev *indio_dev)
+{
+ struct ad4170_state *st = iio_priv(indio_dev);
+
+ st->gpiochip = (struct gpio_chip) {
+ .label = "ad4170_gpios",
+ .base = -1,
+ .ngpio = 4,
+ .parent = &st->spi->dev,
+ .can_sleep = true,
+ .get_direction = ad4170_gpio_get_direction,
+ .direction_input = ad4170_gpio_direction_input,
+ .direction_output = ad4170_gpio_direction_output,
+ .get = ad4170_gpio_get,
+ .set_rv = ad4170_gpio_set,
+ .owner = THIS_MODULE,
+ };
+
+ return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev);
+}
+
static int ad4170_parse_reference(struct ad4170_state *st,
struct fwnode_handle *child,
struct ad4170_setup *setup)
@@ -1855,7 +2009,18 @@ static int ad4170_parse_firmware(struct iio_dev *indio_dev)
if (ret)
return ret;

- return ad4170_parse_channels(indio_dev);
+ ret = ad4170_parse_channels(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Only create a GPIO chip if flagged for it */
+ if (device_property_read_bool(&st->spi->dev, "gpio-controller")) {
+ ret = ad4170_gpio_init(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
}

static int ad4170_initial_config(struct iio_dev *indio_dev)
--
2.47.2