[PATCH v3 2/3] iio: adc: hx711: refactor to per-chip hx711_chip_info structure

Piyush Patle
2026-04-22T17:59:28Z

Introduce hx711_chip_info to hold per-variant static configuration:
device name, IIO channel spec, channel count, and iio_info pointer.
Store a chip_info pointer in hx711_data and populate indio_dev fields
from it at probe instead of hardcoding them.

Pass trailing pulse count directly to hx711_read() instead of
computing it inside the function, and change hx711_reset_read() to
take a const struct iio_chan_spec * instead of an integer channel
index so callers can pass the full channel descriptor.

Use device_get_match_data() to look up the chip_info from the
of_device_id table. No functional change for existing HX711 users.

Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
---
Changes in v3:
- New patch split out from the HX710B support patch.
- Keep this as a standalone refactor for the existing HX711 path.
- Use unsigned int for the channel count and trailing pulse count.
- Use a loop-local unsigned int for the trailing pulse loop.
- Add direct includes for ARRAY_SIZE and basic types.
- Align Kconfig, file header, and module description wording with the
"HX711 and compatible ADCs" phrasing.

Changes in v2:
- N/A; this standalone refactor patch is new in v3.

drivers/iio/adc/Kconfig | 7 ++--
drivers/iio/adc/hx711.c | 92 +++++++++++++++++++++++++++--------------
2 files changed, 65 insertions(+), 34 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 60038ae8dfc4..f18692aea795 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -784,13 +784,14 @@ config HI8435
called hi8435.

config HX711
- tristate "AVIA HX711 ADC for weight cells"
+ tristate "AVIA HX711 and compatible ADCs"
depends on GPIOLIB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- If you say yes here you get support for AVIA HX711 ADC which is used
- for weigh cells
+ If you say Y here you get support for the following AVIA ADCs:
+ - HX711
+ which are used for bridge sensors such as weigh cells.

This driver uses two GPIOs, one acts as the clock and controls the
channel selection and gain, the other one is used for the measurement
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 1db8b68a8f64..a444a2872257 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HX711: analog to digital converter for weight sensor module
+ * HX711 and compatible ADCs driver for weight sensor modules
*
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
*/
+#include <linux/array_size.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/types.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@@ -76,12 +78,27 @@ static int hx711_get_scale_to_gain(int scale)
return -EINVAL;
}

+/**
+ * struct hx711_chip_info - per-variant static configuration
+ * @name: IIO device name
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @iio_info: IIO info ops for this variant
+ */
+struct hx711_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const struct iio_info *iio_info;
+};
+
struct hx711_data {
struct device *dev;
struct gpio_desc *gpiod_pd_sck;
struct gpio_desc *gpiod_dout;
int gain_set; /* gain set on device */
int gain_chan_a; /* gain for channel A */
+ const struct hx711_chip_info *chip_info;
struct mutex lock;
/*
* triggered buffer
@@ -92,10 +109,7 @@ struct hx711_data {
aligned_s64 timestamp;
} buffer;
/*
- * delay after a rising edge on SCK until the data is ready DOUT
- * this is dependent on the hx711 where the datasheet tells a
- * maximum value of 100 ns
- * but also on potential parasitic capacities on the wiring
+ * Delay after SCK rising edge before sampling DOUT.
*/
u32 data_ready_delay_ns;
u32 clock_frequency;
@@ -139,9 +153,13 @@ static int hx711_cycle(struct hx711_data *hx711_data)
return gpiod_get_value(hx711_data->gpiod_dout);
}

-static int hx711_read(struct hx711_data *hx711_data)
+/*
+ * Clock out 24 data bits and then send trailing pulses to select the
+ * next channel/gain state.
+ */
+static int hx711_read(struct hx711_data *hx711_data, unsigned int trailing_pulses)
{
- int i, ret;
+ int ret;
int value = 0;
int val = gpiod_get_value(hx711_data->gpiod_dout);

@@ -149,7 +167,7 @@ static int hx711_read(struct hx711_data *hx711_data)
if (val)
return -EIO;

- for (i = 0; i < 24; i++) {
+ for (int i = 0; i < 24; i++) {
value <<= 1;
ret = hx711_cycle(hx711_data);
if (ret)
@@ -158,7 +176,7 @@ static int hx711_read(struct hx711_data *hx711_data)

value ^= 0x800000;

- for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
+ for (unsigned int i = 0; i < trailing_pulses; i++)
hx711_cycle(hx711_data);

return value;
@@ -213,6 +231,7 @@ static int hx711_reset(struct hx711_data *hx711_data)
return val;
}

+/* Select HX711 channel/gain for the next conversion. */
static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
{
int ret;
@@ -221,7 +240,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set == 32) {
hx711_data->gain_set = hx711_data->gain_chan_a;

- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;

@@ -233,7 +253,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set != 32) {
hx711_data->gain_set = 32;

- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;

@@ -246,27 +267,24 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
return 0;
}

-static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
+static int hx711_reset_read(struct hx711_data *hx711_data,
+ const struct iio_chan_spec *chan)
{
+ unsigned int trailing_pulses;
int ret;
- int val;

- /*
- * hx711_reset() must be called from here
- * because it could be calling hx711_read() by itself
- */
+ /* Reset first so the read starts from a known chip state. */
if (hx711_reset(hx711_data)) {
dev_err(hx711_data->dev, "reset failed!");
return -EIO;
}

- ret = hx711_set_gain_for_channel(hx711_data, chan);
+ ret = hx711_set_gain_for_channel(hx711_data, chan->channel);
if (ret < 0)
return ret;

- val = hx711_read(hx711_data);
-
- return val;
+ trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set);
+ return hx711_read(hx711_data, trailing_pulses);
}

static int hx711_read_raw(struct iio_dev *indio_dev,
@@ -279,7 +297,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
mutex_lock(&hx711_data->lock);

- *val = hx711_reset_read(hx711_data, chan->channel);
+ *val = hx711_reset_read(hx711_data, chan);

mutex_unlock(&hx711_data->lock);

@@ -332,7 +350,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev,
if (gain != 32)
hx711_data->gain_chan_a = gain;

- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0) {
mutex_unlock(&hx711_data->lock);
return ret;
@@ -368,7 +387,7 @@ static irqreturn_t hx711_trigger(int irq, void *p)

iio_for_each_active_channel(indio_dev, i) {
hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data,
- indio_dev->channels[i].channel);
+ &indio_dev->channels[i]);
j++;
}

@@ -455,10 +474,18 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};

+static const struct hx711_chip_info hx711_chip = {
+ .name = "hx711",
+ .channels = hx711_chan_spec,
+ .num_channels = ARRAY_SIZE(hx711_chan_spec),
+ .iio_info = &hx711_iio_info,
+};
+
static int hx711_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct hx711_data *hx711_data;
+ const struct hx711_chip_info *chip_info;
struct iio_dev *indio_dev;
int ret;
int i;
@@ -472,6 +499,9 @@ static int hx711_probe(struct platform_device *pdev)

mutex_init(&hx711_data->lock);

+ chip_info = device_get_match_data(dev);
+ hx711_data->chip_info = chip_info;
+
/*
* PD_SCK stands for power down and serial clock input of HX711
* in the driver it is an output
@@ -517,6 +547,8 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->gain_set = 128;
hx711_data->gain_chan_a = 128;

+ indio_dev->info = chip_info->iio_info;
+
hx711_data->clock_frequency = 400000;
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
&hx711_data->clock_frequency);
@@ -533,11 +565,10 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->data_ready_delay_ns =
1000000000 / hx711_data->clock_frequency;

- indio_dev->name = "hx711";
- indio_dev->info = &hx711_iio_info;
+ indio_dev->name = chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = hx711_chan_spec;
- indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;

ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
@@ -554,7 +585,7 @@ static int hx711_probe(struct platform_device *pdev)
}

static const struct of_device_id of_hx711_match[] = {
- { .compatible = "avia,hx711", },
+ { .compatible = "avia,hx711", .data = &hx711_chip },
{ }
};

@@ -571,7 +602,6 @@ static struct platform_driver hx711_driver = {
module_platform_driver(hx711_driver);

MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
-MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells");
+MODULE_DESCRIPTION("HX711 and compatible bitbanging ADC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:hx711-gpio");
-
--
2.43.0