mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
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:
committed by
Jonathan Cameron
parent
4e5fde6677
commit
c1e289a036
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user