* [PATCH v3 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros
2026-11-06 22:00 [PATCH v3 0/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
@ 2026-11-06 22:00 ` David Lechner (TI)
2026-11-06 22:00 ` [PATCH v3 2/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
2026-11-14 22:45 ` [PATCH v3 0/2] " Jonathan Cameron
2 siblings, 0 replies; 5+ messages in thread
From: David Lechner (TI) @ 2026-11-06 22:00 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá,
Andy Shevchenko, Linus Walleij, Bartosz Golaszewski
Cc: linux-iio, linux-kernel, linux-gpio, Chris Hall, Patrick Edwards,
Kurt Borja, David Lechner (TI),
Bartosz Golaszewski
Replace the per-pin GPIO register field macros with parameterized ones.
This will be used in a later patch to access the fields by GPIO offset.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/iio/adc/ti-ads112c14.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index c0374bd8d001..498893bd01bc 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -63,10 +63,7 @@
#define ADS112C14_REG_STATUS_LSB 0x03
#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4)
-#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3)
-#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2)
-#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1)
-#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0)
+#define ADS112C14_STATUS_LSB_GPIO_DAT_IN(n) BIT(n)
#define ADS112C14_REG_CONVERSION_CTRL 0x04
#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2)
@@ -133,10 +130,7 @@
#define ADS112C14_DIGITAL_CFG_CODING BIT(1)
#define ADS112C14_REG_GPIO_CFG 0x0B
-#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6)
-#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4)
-#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2)
-#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0)
+#define ADS112C14_GPIO_CFG_GPIO_CFG(n) (GENMASK(1, 0) << (2 * (n)))
#define ADS112C14_GPIO_CFG_GPIO_CFG_DISABLED 0
#define ADS112C14_GPIO_CFG_GPIO_CFG_INPUT 1
#define ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL 2
@@ -147,10 +141,7 @@
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DAT_OUT 0
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DRDY 1
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6)
-#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3)
-#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2)
-#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1)
-#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO_DAT_OUT(n) BIT(n)
#define ADS112C14_REG_IDAC_MAG_CFG 0x0D
#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4)
@@ -2514,8 +2505,8 @@ static int ads112c14_probe(struct i2c_client *client)
* order to support open drain option here.
*/
ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
- ADS112C14_GPIO_CFG_GPIO3_CFG,
- FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG(3),
+ FIELD_PREP(ADS112C14_GPIO_CFG_GPIO_CFG(3),
ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL));
if (ret)
return ret;
@@ -2550,8 +2541,8 @@ static int ads112c14_probe(struct i2c_client *client)
if (clk) {
ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
- ADS112C14_GPIO_CFG_GPIO3_CFG,
- FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG(3),
+ FIELD_PREP(ADS112C14_GPIO_CFG_GPIO_CFG(3),
ADS112C14_GPIO_CFG_GPIO_CFG_INPUT));
if (ret)
return ret;
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v3 2/2] iio: adc: ti-ads112c14: add gpio support
2026-11-06 22:00 [PATCH v3 0/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
2026-11-06 22:00 ` [PATCH v3 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros David Lechner (TI)
@ 2026-11-06 22:00 ` David Lechner (TI)
2026-11-09 9:29 ` Bartosz Golaszewski
2026-11-14 22:45 ` [PATCH v3 0/2] " Jonathan Cameron
2 siblings, 1 reply; 5+ messages in thread
From: David Lechner (TI) @ 2026-11-06 22:00 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá,
Andy Shevchenko, Linus Walleij, Bartosz Golaszewski
Cc: linux-iio, linux-kernel, linux-gpio, Chris Hall, Patrick Edwards,
Kurt Borja, David Lechner (TI)
Add support for using the AIN4/GPIO0 to AIN7/GPIO3 pins as GPIOs when
the gpio-controller property is present.
Pins that are already used for something else according to the
devicetree are excluded from the valid GPIO mask. This includes analog
inputs and excitation current outputs used by channels, REFP/REFN when
an external reference is used, the /FAULT and /DRDY interrupts and the
external clock input.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/Kconfig | 2 +-
drivers/iio/adc/ti-ads112c14.c | 168 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 164 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index f8456c803988..9fd6693b109d 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1903,7 +1903,7 @@ config TI_ADS112C04
config TI_ADS112C14
tristate "Texas Instruments ADS112C14/ADS122C14"
- depends on I2C
+ depends on I2C && GPIOLIB
select CRC8
select REGMAP
help
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 498893bd01bc..1270299823be 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
@@ -157,6 +158,8 @@
#define ADS112C14_INT_REF0_mV 1250
#define ADS112C14_INT_REF1_mV 2500
+#define ADS112C14_NUM_GPIO 4
+
enum {
ADS112C14_VREF_SOURCE_INTERNAL_2_5V,
ADS112C14_VREF_SOURCE_INTERNAL_1_25V,
@@ -357,6 +360,9 @@ struct ads112c14_channel_state {
struct ads112c14_data {
const struct ads112c14_chip_info *chip_info;
struct regmap *regmap;
+ struct gpio_chip gc;
+ const char *gpio_names[ADS112C14_NUM_GPIO];
+ DECLARE_BITMAP(gpio_reserved, ADS112C14_NUM_GPIO);
struct iio_trigger *drdy_trig;
/* Synchronizes access to register value fields. */
struct mutex lock;
@@ -384,6 +390,135 @@ struct ads112c14_data {
ARRAY_SIZE(ads112c14_sys_mon_channels));
};
+static u32 ads112c14_reserve_ain_pin(unsigned long *reserved, u32 ain)
+{
+ if (ain >= 4 && ain <= 7)
+ bitmap_set(reserved, ain - 4, 1);
+
+ return ain;
+}
+
+static int ads112c14_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_GPIO_CFG, ®_val);
+ if (ret)
+ return ret;
+
+ switch (field_get(ADS112C14_GPIO_CFG_GPIO_CFG(offset), reg_val)) {
+ case ADS112C14_GPIO_CFG_GPIO_CFG_INPUT:
+ return GPIO_LINE_DIRECTION_IN;
+ case ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL:
+ case ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_OPEN_DRAIN:
+ return GPIO_LINE_DIRECTION_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ return regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG(offset),
+ field_prep(ADS112C14_GPIO_CFG_GPIO_CFG(offset),
+ ADS112C14_GPIO_CFG_GPIO_CFG_INPUT));
+}
+
+static int ads112c14_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_assign_bits(data->regmap, ADS112C14_REG_GPIO_DATA_OUTPUT,
+ ADS112C14_GPIO_DATA_OUTPUT_GPIO_DAT_OUT(offset),
+ value);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG(offset),
+ field_prep(ADS112C14_GPIO_CFG_GPIO_CFG(offset),
+ ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL));
+}
+
+static int ads112c14_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_LSB, ®_val);
+ if (ret)
+ return ret;
+
+ return field_get(ADS112C14_STATUS_LSB_GPIO_DAT_IN(offset), reg_val);
+}
+
+static int ads112c14_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ return regmap_assign_bits(data->regmap, ADS112C14_REG_GPIO_DATA_OUTPUT,
+ ADS112C14_GPIO_DATA_OUTPUT_GPIO_DAT_OUT(offset),
+ value);
+}
+
+static int ads112c14_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(gc);
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ bitmap_andnot(valid_mask, valid_mask, data->gpio_reserved, ngpios);
+
+ return 0;
+}
+
+static int ads112c14_gpio_init(struct iio_dev *indio_dev)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+
+ for (unsigned int i = 0; i < ADS112C14_NUM_GPIO; i++) {
+ data->gpio_names[i] = devm_kasprintf(dev, GFP_KERNEL, "%s:GPIO%u",
+ dev_name(&indio_dev->dev), i);
+ if (!data->gpio_names[i])
+ return -ENOMEM;
+ }
+
+ data->gc = (struct gpio_chip) {
+ .owner = THIS_MODULE,
+ .label = dev_name(dev),
+ .parent = dev,
+ .base = -1,
+ .ngpio = ADS112C14_NUM_GPIO,
+ .names = data->gpio_names,
+ .can_sleep = true,
+ .init_valid_mask = ads112c14_gpio_init_valid_mask,
+ .get_direction = ads112c14_gpio_get_direction,
+ .direction_input = ads112c14_gpio_direction_input,
+ .direction_output = ads112c14_gpio_direction_output,
+ .get = ads112c14_gpio_get,
+ .set = ads112c14_gpio_set,
+ };
+
+ return devm_gpiochip_add_data(dev, &data->gc, indio_dev);
+}
+
static irqreturn_t ads112c14_drdy_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
@@ -1959,7 +2094,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
return dev_err_probe(dev, -EINVAL,
"single-channel value must be between 0 and 7\n");
- spec->channel = pair[0];
+ spec->channel = ads112c14_reserve_ain_pin(data->gpio_reserved, pair[0]);
/*
* NB: channel2 is unused by iio core code in this case.
* Let's us avoid special case for negative input mux
@@ -1978,8 +2113,8 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
"diff-channels values must be between 0 and 7\n");
spec->differential = 1;
- spec->channel = pair[0];
- spec->channel2 = pair[1];
+ spec->channel = ads112c14_reserve_ain_pin(data->gpio_reserved, pair[0]);
+ spec->channel2 = ads112c14_reserve_ain_pin(data->gpio_reserved, pair[1]);
} else {
return dev_err_probe(dev, -EINVAL,
"channel node missing channel type property\n");
@@ -2008,8 +2143,11 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
return dev_err_probe(dev, -EINVAL,
"excitation-channels values must be between 0 and 7\n");
- measurement->idac1_mux = pair[0];
- measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0;
+ measurement->idac1_mux =
+ ads112c14_reserve_ain_pin(data->gpio_reserved, pair[0]);
+ if (measurement->iadc_count > 1)
+ measurement->idac2_mux =
+ ads112c14_reserve_ain_pin(data->gpio_reserved, pair[1]);
ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp",
pair, measurement->iadc_count);
@@ -2414,6 +2552,10 @@ static int ads112c14_probe(struct i2c_client *client)
return dev_err_probe(dev, -EINVAL,
"external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n");
+ /* REFP and REFN share pins with GPIO0 and GPIO1. */
+ if (data->ext_ref_uV || data->ext_ref_ohms)
+ bitmap_set(data->gpio_reserved, 0, 2);
+
clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to get clk\n");
@@ -2490,12 +2632,19 @@ static int ads112c14_probe(struct i2c_client *client)
if (ret)
return ret;
+ /* FAULT shares a pin with GPIO2. */
+ if (device_property_match_string(dev, "interrupt-names", "fault") >= 0)
+ bitmap_set(data->gpio_reserved, 2, 1);
+
if (fwnode_property_match_string(dev_fwnode(dev), "interrupt-names", "drdy") >= 0) {
data->drdy_irq = fwnode_irq_get_byname(dev_fwnode(dev), "drdy");
if (data->drdy_irq < 0)
return dev_err_probe(dev, data->drdy_irq,
"failed to get drdy interrupt\n");
+ /* DRDY shares a pin with GPIO3. */
+ bitmap_set(data->gpio_reserved, 3, 1);
+
if (clk)
return dev_err_probe(dev, -EINVAL,
"cannot use both DRDY and CLK - they share the same pin\n");
@@ -2540,6 +2689,9 @@ static int ads112c14_probe(struct i2c_client *client)
}
if (clk) {
+ /* CLK shares a pin with GPIO3. */
+ bitmap_set(data->gpio_reserved, 3, 1);
+
ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
ADS112C14_GPIO_CFG_GPIO_CFG(3),
FIELD_PREP(ADS112C14_GPIO_CFG_GPIO_CFG(3),
@@ -2568,6 +2720,12 @@ static int ads112c14_probe(struct i2c_client *client)
if (ret)
return ret;
+ if (device_property_present(dev, "gpio-controller")) {
+ ret = ads112c14_gpio_init(indio_dev);
+ if (ret)
+ return ret;
+ }
+
return devm_iio_device_register(dev, indio_dev);
}
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread