iio: adc: ad4170-4: Add GPIO controller support

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

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

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
Link: https://patch.msgid.link/e031189d4b7e20cf02dd13220ab1ddf4798760c2.1751895245.git.marcelo.schmitt@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Marcelo Schmitt
2025-07-14 19:20:53 +01:00
committed by Jonathan Cameron
parent 4e5fde6677
commit c1e289a036
2 changed files with 224 additions and 1 deletions
+1
View File
@@ -92,6 +92,7 @@ config AD4170_4
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-4 SPI analog
to digital converters (ADC).
+223 -1
View File
@@ -18,6 +18,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>
@@ -68,6 +69,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)
@@ -106,6 +110,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 */
@@ -146,9 +156,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_ADC_CHANNELS 16
#define AD4170_MAX_IIO_CHANNELS (AD4170_MAX_ADC_CHANNELS + 1)
#define AD4170_MAX_ANALOG_PINS 8
@@ -177,6 +192,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,
@@ -214,6 +232,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,
};
@@ -365,6 +386,8 @@ struct ad4170_state {
struct clk_hw int_clk_hw;
unsigned int clock_ctrl;
unsigned int pins_fn[AD4170_NUM_ANALOG_PINS];
int gpio_fn[AD4170_NUM_GPIO_PINS];
struct gpio_chip gpiochip;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@@ -1475,6 +1498,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)
@@ -1818,7 +2029,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(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)