[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <ac555bd20e66b09c246cea301f9132ee0d1ec9c5.1748829860.git.marcelo.schmitt@analog.com>
Date: Mon, 2 Jun 2025 08:39:23 -0300
From: Marcelo Schmitt <marcelo.schmitt@...log.com>
To: <linux-iio@...r.kernel.org>, <devicetree@...r.kernel.org>,
<linux-gpio@...r.kernel.org>, <linux-kernel@...r.kernel.org>
CC: <jic23@...nel.org>, <lars@...afoo.de>, <Michael.Hennerich@...log.com>,
<dlechner@...libre.com>, <nuno.sa@...log.com>, <andy@...nel.org>,
<robh@...nel.org>, <krzk+dt@...nel.org>, <conor+dt@...nel.org>,
<linus.walleij@...aro.org>, <brgl@...ev.pl>,
<marcelo.schmitt1@...il.com>,
Bartosz Golaszewski <bartosz.golaszewski@...aro.org>
Subject: [PATCH v4 08/11] iio: adc: ad4170: Add GPIO controller support
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 to make AD4170 GPIO pins controllable through
the gpiochip interface.
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@...aro.org>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@...log.com>
---
Change log v3 -> v4
- Made AD4170 depend on GPIOLIB.
drivers/iio/adc/Kconfig | 1 +
drivers/iio/adc/ad4170.c | 224 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 224 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 32e5177ceebe..0c16b2d5947d 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -92,6 +92,7 @@ config AD4170
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
depends on COMMON_CLK
+ depends on GPIOLIB
help
Say yes here to build support for Analog Devices AD4170 SPI analog
to digital converters (ADC).
diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c
index 2173e79ce96c..3aa1553c0e3d 100644
--- a/drivers/iio/adc/ad4170.c
+++ b/drivers/iio/adc/ad4170.c
@@ -15,6 +15,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/trigger.h>
@@ -63,6 +64,9 @@
#define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x))
#define AD4170_OFFSET_REG(x) (0xCA + 14 * (x))
#define AD4170_GAIN_REG(x) (0xCD + 14 * (x))
+#define AD4170_GPIO_MODE_REG 0x191
+#define AD4170_GPIO_OUTPUT_REG 0x193
+#define AD4170_GPIO_INPUT_REG 0x195
#define AD4170_ADC_CTRL_CONT_READ_EXIT_REG 0x200 /* virtual reg */
#define AD4170_REG_READ_MASK BIT(14)
@@ -101,6 +105,12 @@
/* AD4170_FILTER_REG */
#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0)
+/* AD4170_GPIO_MODE_REG */
+#define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0)
+#define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2)
+#define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4)
+#define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6)
+
/* AD4170 register constants */
/* AD4170_CLOCK_CTRL_REG constants */
@@ -141,9 +151,14 @@
#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4
#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6
+/* AD4170_GPIO_MODE_REG constants */
+#define AD4170_GPIO_MODE_GPIO_INPUT 1
+#define AD4170_GPIO_MODE_GPIO_OUTPUT 2
+
/* 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
@@ -170,6 +185,9 @@
#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5
+/* GPIO pin functions */
+#define AD4170_GPIO_UNASSIGNED 0x00
+
static const unsigned int ad4170_reg_size[] = {
[AD4170_CONFIG_A_REG] = 1,
[AD4170_DATA_24B_REG] = 3,
@@ -207,6 +225,9 @@ static const unsigned int ad4170_reg_size[] = {
[AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] = 3,
[AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] = 3,
[AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] = 3,
+ [AD4170_GPIO_MODE_REG] = 2,
+ [AD4170_GPIO_OUTPUT_REG] = 2,
+ [AD4170_GPIO_INPUT_REG] = 2,
[AD4170_ADC_CTRL_CONT_READ_EXIT_REG] = 0,
};
@@ -347,7 +368,9 @@ struct ad4170_state {
int pins_fn[AD4170_NUM_ANALOG_PINS];
u32 int_pin_sel;
struct clk_hw int_clk_hw;
+ struct gpio_chip gpiochip;
unsigned int clock_ctrl;
+ int gpio_fn[AD4170_NUM_GPIO_PINS];
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@@ -1468,6 +1491,194 @@ 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->regmap, 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->regmap, AD4170_GPIO_INPUT_REG, &val);
+ else
+ ret = regmap_read(st->regmap, 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);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_assign_bits(st->regmap, AD4170_GPIO_OUTPUT_REG,
+ BIT(offset), !!value);
+
+ 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->regmap, 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);
+ unsigned long gpio_mask;
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ switch (offset) {
+ case 0:
+ gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK;
+ break;
+ case 1:
+ gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK;
+ break;
+ case 2:
+ gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK;
+ break;
+ case 3:
+ gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_release;
+ }
+ ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
+ AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset));
+
+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);
+ unsigned long gpio_mask;
+ int ret;
+
+ ret = ad4170_gpio_set(gc, offset, value);
+ if (ret)
+ return ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ switch (offset) {
+ case 0:
+ gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK;
+ break;
+ case 1:
+ gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK;
+ break;
+ case 2:
+ gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK;
+ break;
+ case 3:
+ gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_release;
+ }
+ ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
+ AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset));
+
+err_release:
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4170_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct ad4170_state *st = gpiochip_get_data(gc);
+ unsigned int i;
+
+ /* Only expose GPIOs that were not assigned any other function. */
+ for (i = 0; i < ngpios; i++) {
+ bool valid = st->gpio_fn[i] == AD4170_GPIO_UNASSIGNED;
+
+ __assign_bit(i, valid_mask, valid);
+ }
+
+ return 0;
+}
+
+static int ad4170_gpio_init(struct iio_dev *indio_dev)
+{
+ struct ad4170_state *st = iio_priv(indio_dev);
+
+ st->gpiochip.label = "ad4170_gpios";
+ st->gpiochip.base = -1;
+ st->gpiochip.ngpio = AD4170_NUM_GPIO_PINS;
+ st->gpiochip.parent = &st->spi->dev;
+ st->gpiochip.can_sleep = true;
+ st->gpiochip.init_valid_mask = ad4170_gpio_init_valid_mask;
+ st->gpiochip.get_direction = ad4170_gpio_get_direction;
+ st->gpiochip.direction_input = ad4170_gpio_direction_input;
+ st->gpiochip.direction_output = ad4170_gpio_direction_output;
+ st->gpiochip.get = ad4170_gpio_get;
+ st->gpiochip.set_rv = ad4170_gpio_set;
+ st->gpiochip.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)
@@ -1781,7 +1992,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)
+ return ret;
+ }
+
+ return 0;
}
static int ad4170_initial_config(struct iio_dev *indio_dev)
--
2.47.2
Powered by blists - more mailing lists