mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/2] iio: adc: ti-ads112c14: add gpio support
@ 2026-10-09 23:25 David Lechner (TI)
  2026-10-09 23:25 ` [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros David Lechner (TI)
  2026-10-09 23:25 ` [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
  0 siblings, 2 replies; 6+ messages in thread
From: David Lechner (TI) @ 2026-10-09 23:25 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 GPIO controller support to the TI ADS112C14 driver. The first patch
is a small cleanup to make the GPIO register field macros usable with a
GPIO offset. The second patch adds the GPIO support.

Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Changes in v2:
- Split out GPIO register field macro changes into a separate patch.
- Make ads112c14_reserve_ain_pin() return ain and use it in assignments.
- Shorten names of GPIO reservation helper and mask.
- Set reserved bits directly in struct instead of copying from probe.
- Use non-atomic bit ops.
- Drop unneeded bitmap_fill() in init_valid_mask().
- Add GPIOLIB dependency.
- Link to v1: https://patch.msgid.link/20260925-iio-adc-ti-ads112c14-gpio-v1-1-2a2b218ebf3a@baylibre.com

---
David Lechner (TI) (2):
      iio: adc: ti-ads112c14: parameterize GPIO register field macros
      iio: adc: ti-ads112c14: add gpio support

 drivers/iio/adc/Kconfig        |   2 +-
 drivers/iio/adc/ti-ads112c14.c | 191 ++++++++++++++++++++++++++++++++++++-----
 2 files changed, 171 insertions(+), 22 deletions(-)
---
base-commit: a3b3580713f3ac5a32dc2874ee546828977a1d68
change-id: 20260925-iio-adc-ti-ads112c14-gpio-197bb4c70411

Best regards,
--  
David Lechner (TI) <dlechner@baylibre.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros
  2026-10-09 23:25 [PATCH v2 0/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
@ 2026-10-09 23:25 ` David Lechner (TI)
  2026-10-10  9:00   ` Bartosz Golaszewski
  2026-10-09 23:25 ` [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
  1 sibling, 1 reply; 6+ messages in thread
From: David Lechner (TI) @ 2026-10-09 23:25 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)

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>
---
 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 3c877126b0be..cbd82de03127 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)
@@ -2520,8 +2511,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;
@@ -2556,8 +2547,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] 6+ messages in thread

* [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support
  2026-10-09 23:25 [PATCH v2 0/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
  2026-10-09 23:25 ` [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros David Lechner (TI)
@ 2026-10-09 23:25 ` David Lechner (TI)
  2026-10-10  9:00   ` Bartosz Golaszewski
  1 sibling, 1 reply; 6+ messages in thread
From: David Lechner (TI) @ 2026-10-09 23:25 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 a802d8ee4dd9..b0242b2fdb2d 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1878,7 +1878,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 cbd82de03127..e0cadc445a44 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, &reg_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, &reg_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;
@@ -1965,7 +2100,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
@@ -1984,8 +2119,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");
@@ -2014,8 +2149,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);
@@ -2420,6 +2558,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");
@@ -2496,12 +2638,19 @@ static int ads112c14_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
+	/* FAULT shares a pin with GPIO2. */
+	if (fwnode_property_match_string(dev_fwnode(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");
@@ -2546,6 +2695,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),
@@ -2574,6 +2726,12 @@ static int ads112c14_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
+	if (device_property_read_bool(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] 6+ messages in thread

* Re: [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support
  2026-10-09 23:25 ` [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
@ 2026-10-10  9:00   ` Bartosz Golaszewski
  2026-10-10 13:46     ` David Lechner
  0 siblings, 1 reply; 6+ messages in thread
From: Bartosz Golaszewski @ 2026-10-10  9:00 UTC (permalink / raw)
  To: David Lechner (TI)
  Cc: linux-iio, linux-kernel, linux-gpio, Chris Hall, Patrick Edwards,
	Kurt Borja, Jonathan Cameron, Nuno Sá,
	Andy Shevchenko, Linus Walleij, Bartosz Golaszewski

On Sat, 10 Oct 2026 01:25:52 +0200, "David Lechner (TI)"
<dlechner@baylibre.com> said:
> 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 a802d8ee4dd9..b0242b2fdb2d 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -1878,7 +1878,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 cbd82de03127..e0cadc445a44 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, &reg_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, &reg_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;
> @@ -1965,7 +2100,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
> @@ -1984,8 +2119,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");
> @@ -2014,8 +2149,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);
> @@ -2420,6 +2558,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");
> @@ -2496,12 +2638,19 @@ static int ads112c14_probe(struct i2c_client *client)
>  	if (ret)
>  		return ret;
>
> +	/* FAULT shares a pin with GPIO2. */
> +	if (fwnode_property_match_string(dev_fwnode(dev), "interrupt-names", "fault") >= 0)

With device_property_match_string() you avoid the dev_fwnode().

> +		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");
> @@ -2546,6 +2695,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),
> @@ -2574,6 +2726,12 @@ static int ads112c14_probe(struct i2c_client *client)
>  	if (ret)
>  		return ret;
>
> +	if (device_property_read_bool(dev, "gpio-controller")) {

I think the right one to use is device_property_present().

> +		ret = ads112c14_gpio_init(indio_dev);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	return devm_iio_device_register(dev, indio_dev);
>  }
>
>
> --
> 2.53.0
>
>

Bart

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros
  2026-10-09 23:25 ` [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros David Lechner (TI)
@ 2026-10-10  9:00   ` Bartosz Golaszewski
  0 siblings, 0 replies; 6+ messages in thread
From: Bartosz Golaszewski @ 2026-10-10  9:00 UTC (permalink / raw)
  To: David Lechner (TI)
  Cc: linux-iio, linux-kernel, linux-gpio, Chris Hall, Patrick Edwards,
	Kurt Borja, Jonathan Cameron, Nuno Sá,
	Andy Shevchenko, Linus Walleij, Bartosz Golaszewski

On Sat, 10 Oct 2026 01:25:51 +0200, "David Lechner (TI)"
<dlechner@baylibre.com> said:
> 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>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support
  2026-10-10  9:00   ` Bartosz Golaszewski
@ 2026-10-10 13:46     ` David Lechner
  0 siblings, 0 replies; 6+ messages in thread
From: David Lechner @ 2026-10-10 13:46 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-iio, linux-kernel, linux-gpio, Chris Hall, Patrick Edwards,
	Kurt Borja, Jonathan Cameron, Nuno Sá,
	Andy Shevchenko, Linus Walleij

On 10/10/26 4:00 AM, Bartosz Golaszewski wrote:
> On Sat, 10 Oct 2026 01:25:52 +0200, "David Lechner (TI)"
> <dlechner@baylibre.com> said:
>> Add support for using the AIN4/GPIO0 to AIN7/GPIO3 pins as GPIOs when
>> the gpio-controller property is present.
>>

...

>> @@ -2574,6 +2726,12 @@ static int ads112c14_probe(struct i2c_client *client)
>>  	if (ret)
>>  		return ret;
>>
>> +	if (device_property_read_bool(dev, "gpio-controller")) {
> 
> I think the right one to use is device_property_present().

I thought this was the one case where device_property_read_bool() is
actually correct because gpio-controller property is a
/schemas/types.yaml#/definitions/flag.

Otherwise, we should just remove device_property_read_bool() from the tree
because there would never be a reason to use it.

My understanding is we should use device_property_present() for non-boolean
properties when we want to know if it is present or not before reading the
value of it.

> 
>> +		ret = ads112c14_gpio_init(indio_dev);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>>  	return devm_iio_device_register(dev, indio_dev);
>>  }
>>
>>
>> --
>> 2.53.0
>>
>>
> 
> Bart


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-10 13:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-09 23:25 [PATCH v2 0/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
2026-10-09 23:25 ` [PATCH v2 1/2] iio: adc: ti-ads112c14: parameterize GPIO register field macros David Lechner (TI)
2026-10-10  9:00   ` Bartosz Golaszewski
2026-10-09 23:25 ` [PATCH v2 2/2] iio: adc: ti-ads112c14: add gpio support David Lechner (TI)
2026-10-10  9:00   ` Bartosz Golaszewski
2026-10-10 13:46     ` David Lechner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®