* [PATCH v6 0/3] hwmon: (ads7871) Fix endianness and modernize driver
@ 2026-05-02 2:08 Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads Tabrez Ahmed
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Tabrez Ahmed @ 2026-05-02 2:08 UTC (permalink / raw)
To: linux-hwmon, linux-kernel
Cc: linux, david.laight.linux, me, shuah, Tabrez Ahmed
This patch series addresses a critical endianness issue in the ads7871
hardware monitoring driver and updates the driver to use the modern
hwmon API.
The series is broken down as follows:
- Patch 1: Fixes corrupted voltage readings on Big-Endian architectures
by safely converting the 16-bit sensor output using le16_to_cpu().
- Patch 2: Modernizes the driver by migrating it from the deprecated
hwmon API to hwmon_device_register_with_info().
- Patch 3: Ensures all SPI write operations use a properly allocated,
DMA-safe buffer.
Note: I do not have access to the physical ADS7871 hardware. This
series has been compile-tested only.
Changes in v6:
- patch 1:
- Switched back to using spi_w8r16() combined with le16_to_cpu()
for the endian conversion, removing the manual byte array.
- Added an early return for negative error codes from the SPI core
to prevent mangling the error status before conversion.
- Dropped the now-unused <linux/unaligned.h> header.
- patches 2 & 3:
- Unchanged from v5.
Changes in v5:
- Fixed a bisectability issue where the <linux/unaligned.h> include
modernization was accidentally squashed into Patch 3 instead of Patch 1.
Changes in v4:
- Patch 1: Refactored 16-bit register reads to use a dedicated 'u8'
transmit variable, eliminating a 32-bit pointer endianness hazard.
Changes in v3:
- Added Patch 1 to fix the pre-existing endianness bug in 16-bit reads.
- Fixed multiple formatting and alignment issues caught by checkpatch
--strict, as requested by Guenter Roeck.
- Added "While at it, fix checkpatch violations" to Patch 2 commit message.
Changes in v2:
- Dropped custom mutex in favor of native hwmon core serialization.
- Split API migration and DMA fix into separate, logical patches.
- Corrected output scaling and sign extension to meet hwmon ABI.
Tabrez Ahmed (3):
hwmon: (ads7871) Fix endianness bug in 16-bit register reads
hwmon: (ads7871) Convert to hwmon_device_register_with_info
hwmon: (ads7871) Use DMA-safe buffer for SPI writes
drivers/hwmon/ads7871.c | 115 +++++++++++++++++++++++-----------------
1 file changed, 65 insertions(+), 50 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads
2026-05-02 2:08 [PATCH v6 0/3] hwmon: (ads7871) Fix endianness and modernize driver Tabrez Ahmed
@ 2026-05-02 2:08 ` Tabrez Ahmed
2026-05-02 18:18 ` Guenter Roeck
2026-05-02 2:08 ` [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes Tabrez Ahmed
2 siblings, 1 reply; 7+ messages in thread
From: Tabrez Ahmed @ 2026-05-02 2:08 UTC (permalink / raw)
To: linux-hwmon, linux-kernel
Cc: linux, david.laight.linux, me, shuah, Tabrez Ahmed, Sashiko
The ads7871_read_reg16() function relies on spi_w8r16() to read the
16-bit sensor output. The ADS7871 device transmits the Least Significant
Byte (LSB) first.
On Little-Endian architectures, spi_w8r16() correctly reconstructs the
16-bit value. However, on Big-Endian architectures, the byte swapping
causes the first received byte (LSB) to be placed in the most significant
byte of the u16, resulting in corrupted voltage readings.
To fix this, cast the integer result of spi_w8r16() to a restricted
__le16 type and convert it to the host CPU's native byte order using
le16_to_cpu(). Negative error codes returned by the SPI core are caught
and returned prior to the conversion to avoid mangling the error status.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260418034601.90226-1-tabreztalks@gmail.com
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
---
drivers/hwmon/ads7871.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index 9bfdf9e6bcd77..52584bb77ffb7 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -61,7 +61,6 @@
#include <linux/delay.h>
#define DEVICE_NAME "ads7871"
-
struct ads7871_data {
struct spi_device *spi;
};
@@ -77,9 +76,13 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg)
static int ads7871_read_reg16(struct spi_device *spi, int reg)
{
int ret;
+
reg = reg | INST_READ_BM | INST_16BIT_BM;
ret = spi_w8r16(spi, reg);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return le16_to_cpu((__force __le16)ret);
}
static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info
2026-05-02 2:08 [PATCH v6 0/3] hwmon: (ads7871) Fix endianness and modernize driver Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads Tabrez Ahmed
@ 2026-05-02 2:08 ` Tabrez Ahmed
2026-05-02 18:18 ` Guenter Roeck
2026-05-02 2:08 ` [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes Tabrez Ahmed
2 siblings, 1 reply; 7+ messages in thread
From: Tabrez Ahmed @ 2026-05-02 2:08 UTC (permalink / raw)
To: linux-hwmon, linux-kernel
Cc: linux, david.laight.linux, me, shuah, Tabrez Ahmed
Convert the ads7871 driver from the legacy hwmon_device_register() to the
modern hwmon_device_register_with_info() API. This migration simplifies
the driver by using the structured hwmon_channel_info approach and
prepares the codebase for the transition to a shared DMA-safe buffer.
While at it, fix checkpatch violations.
Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
---
drivers/hwmon/ads7871.c | 75 ++++++++++++++++++++++-------------------
1 file changed, 40 insertions(+), 35 deletions(-)
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index 52584bb77ffb7..8ffcc4c823a60 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -56,7 +56,6 @@
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/delay.h>
@@ -65,6 +64,16 @@ struct ads7871_data {
struct spi_device *spi;
};
+static umode_t ads7871_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_in && attr == hwmon_in_input)
+ return 0444;
+
+ return 0;
+}
+
static int ads7871_read_reg8(struct spi_device *spi, int reg)
{
int ret;
@@ -88,19 +97,20 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg)
static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
{
u8 tmp[2] = {reg, val};
+
return spi_write(spi, tmp, sizeof(tmp));
}
-static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
- char *buf)
+static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
struct ads7871_data *pdata = dev_get_drvdata(dev);
struct spi_device *spi = pdata->spi;
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- int ret, val, i = 0;
- uint8_t channel, mux_cnv;
+ int ret, raw_val, i = 0;
+ u8 mux_cnv;
- channel = attr->index;
+ if (type != hwmon_in || attr != hwmon_in_input)
+ return -EOPNOTSUPP;
/*
* TODO: add support for conversions
* other than single ended with a gain of 1
@@ -130,39 +140,34 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
}
if (mux_cnv == 0) {
- val = ads7871_read_reg16(spi, REG_LS_BYTE);
- if (val < 0)
- return val;
+ raw_val = ads7871_read_reg16(spi, REG_LS_BYTE);
+ if (raw_val < 0)
+ return raw_val;
+
/*result in volts*10000 = (val/8192)*2.5*10000*/
- val = ((val >> 2) * 25000) / 8192;
- return sysfs_emit(buf, "%d\n", val);
+ *val = ((raw_val >> 2) * 25000) / 8192;
+ return 0;
}
return -ETIMEDOUT;
}
-static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0);
-static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1);
-static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2);
-static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3);
-static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4);
-static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5);
-static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6);
-static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7);
-
-static struct attribute *ads7871_attrs[] = {
- &sensor_dev_attr_in0_input.dev_attr.attr,
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
+static const struct hwmon_channel_info * const ads7871_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT,
+ HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT),
NULL
};
-ATTRIBUTE_GROUPS(ads7871);
+static const struct hwmon_ops ads7871_hwmon_ops = {
+ .is_visible = ads7871_is_visible,
+ .read = ads7871_read,
+};
+
+static const struct hwmon_chip_info ads7871_chip_info = {
+ .ops = &ads7871_hwmon_ops,
+ .info = ads7871_info,
+};
static int ads7871_probe(struct spi_device *spi)
{
@@ -197,10 +202,10 @@ static int ads7871_probe(struct spi_device *spi)
return -ENOMEM;
pdata->spi = spi;
-
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias,
- pdata,
- ads7871_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias,
+ pdata,
+ &ads7871_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes
2026-05-02 2:08 [PATCH v6 0/3] hwmon: (ads7871) Fix endianness and modernize driver Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info Tabrez Ahmed
@ 2026-05-02 2:08 ` Tabrez Ahmed
2026-05-02 18:23 ` Guenter Roeck
2 siblings, 1 reply; 7+ messages in thread
From: Tabrez Ahmed @ 2026-05-02 2:08 UTC (permalink / raw)
To: linux-hwmon, linux-kernel
Cc: linux, david.laight.linux, me, shuah, Tabrez Ahmed
The driver currently passes a stack-allocated buffer to spi_write(),
which is incompatible with DMA on systems with CONFIG_VMAP_STACK
enabled.
Move the transfer buffer into the driver's private data structure
to ensure it is DMA-safe. Since this shared buffer now requires
serialization, this change depends on the previous commit which
migrated the driver to the hwmon 'with_info' API.
While moving the logic, also:
- Corrected the sign extension for 14-bit data by casting to s16.
- Scaled the output to millivolts (2500mV full scale
) to comply with the hwmon ABI.
Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
---
drivers/hwmon/ads7871.c | 35 +++++++++++++++++++++--------------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index 8ffcc4c823a60..83ea09a935bc4 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -62,6 +62,7 @@
#define DEVICE_NAME "ads7871"
struct ads7871_data {
struct spi_device *spi;
+ u8 tx_buf[2] ____cacheline_aligned;
};
static umode_t ads7871_is_visible(const void *data,
@@ -94,11 +95,12 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg)
return le16_to_cpu((__force __le16)ret);
}
-static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
+static int ads7871_write_reg8(struct ads7871_data *pdata, int reg, u8 val)
{
- u8 tmp[2] = {reg, val};
+ pdata->tx_buf[0] = reg;
+ pdata->tx_buf[1] = val;
- return spi_write(spi, tmp, sizeof(tmp));
+ return spi_write(pdata->spi, pdata->tx_buf, 2);
}
static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
@@ -117,7 +119,7 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
*/
/*MUX_M3_BM forces single ended*/
/*This is also where the gain of the PGA would be set*/
- ret = ads7871_write_reg8(spi, REG_GAIN_MUX,
+ ret = ads7871_write_reg8(pdata, REG_GAIN_MUX,
(MUX_CNV_BM | MUX_M3_BM | channel));
if (ret < 0)
return ret;
@@ -125,6 +127,7 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
if (ret < 0)
return ret;
+
mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
/*
* on 400MHz arm9 platform the conversion
@@ -144,8 +147,11 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
if (raw_val < 0)
return raw_val;
- /*result in volts*10000 = (val/8192)*2.5*10000*/
- *val = ((raw_val >> 2) * 25000) / 8192;
+ /*
+ * Use (s16) to ensure the sign bit is preserved during the shift.
+ * Report millivolts (2.5V = 2500mV).
+ */
+ *val = ((s16)raw_val >> 2) * 2500 / 8192;
return 0;
}
@@ -182,11 +188,17 @@ static int ads7871_probe(struct spi_device *spi)
spi->bits_per_word = 8;
spi_setup(spi);
- ads7871_write_reg8(spi, REG_SER_CONTROL, 0);
- ads7871_write_reg8(spi, REG_AD_CONTROL, 0);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->spi = spi;
+
+ ads7871_write_reg8(pdata, REG_SER_CONTROL, 0);
+ ads7871_write_reg8(pdata, REG_AD_CONTROL, 0);
val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM);
- ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
+ ads7871_write_reg8(pdata, REG_OSC_CONTROL, val);
ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret);
@@ -197,11 +209,6 @@ static int ads7871_probe(struct spi_device *spi)
if (val != ret)
return -ENODEV;
- pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- pdata->spi = spi;
hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias,
pdata,
&ads7871_chip_info,
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads
2026-05-02 2:08 ` [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads Tabrez Ahmed
@ 2026-05-02 18:18 ` Guenter Roeck
0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-05-02 18:18 UTC (permalink / raw)
To: Tabrez Ahmed
Cc: linux-hwmon, linux-kernel, david.laight.linux, me, shuah, Sashiko
On Sat, May 02, 2026 at 07:38:42AM +0530, Tabrez Ahmed wrote:
> The ads7871_read_reg16() function relies on spi_w8r16() to read the
> 16-bit sensor output. The ADS7871 device transmits the Least Significant
> Byte (LSB) first.
>
> On Little-Endian architectures, spi_w8r16() correctly reconstructs the
> 16-bit value. However, on Big-Endian architectures, the byte swapping
> causes the first received byte (LSB) to be placed in the most significant
> byte of the u16, resulting in corrupted voltage readings.
>
> To fix this, cast the integer result of spi_w8r16() to a restricted
> __le16 type and convert it to the host CPU's native byte order using
> le16_to_cpu(). Negative error codes returned by the SPI core are caught
> and returned prior to the conversion to avoid mangling the error status.
>
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://sashiko.dev/#/patchset/20260418034601.90226-1-tabreztalks@gmail.com
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
Sorry for the confusion - this is the one I applied.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info
2026-05-02 2:08 ` [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info Tabrez Ahmed
@ 2026-05-02 18:18 ` Guenter Roeck
0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-05-02 18:18 UTC (permalink / raw)
To: Tabrez Ahmed; +Cc: linux-hwmon, linux-kernel, david.laight.linux, me, shuah
On Sat, May 02, 2026 at 07:38:43AM +0530, Tabrez Ahmed wrote:
> Convert the ads7871 driver from the legacy hwmon_device_register() to the
> modern hwmon_device_register_with_info() API. This migration simplifies
> the driver by using the structured hwmon_channel_info approach and
> prepares the codebase for the transition to a shared DMA-safe buffer.
> While at it, fix checkpatch violations.
>
> Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
Applied.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes
2026-05-02 2:08 ` [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes Tabrez Ahmed
@ 2026-05-02 18:23 ` Guenter Roeck
0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-05-02 18:23 UTC (permalink / raw)
To: Tabrez Ahmed; +Cc: linux-hwmon, linux-kernel, david.laight.linux, me, shuah
On Sat, May 02, 2026 at 07:38:44AM +0530, Tabrez Ahmed wrote:
> The driver currently passes a stack-allocated buffer to spi_write(),
> which is incompatible with DMA on systems with CONFIG_VMAP_STACK
> enabled.
>
> Move the transfer buffer into the driver's private data structure
> to ensure it is DMA-safe. Since this shared buffer now requires
> serialization, this change depends on the previous commit which
> migrated the driver to the hwmon 'with_info' API.
>
> While moving the logic, also:
> - Corrected the sign extension for 14-bit data by casting to s16.
> - Scaled the output to millivolts (2500mV full scale
> ) to comply with the hwmon ABI.
>
> Signed-off-by: Tabrez Ahmed <tabreztalks@gmail.com>
Applied.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-05-02 18:23 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-02 2:08 [PATCH v6 0/3] hwmon: (ads7871) Fix endianness and modernize driver Tabrez Ahmed
2026-05-02 2:08 ` [PATCH v6 1/3] hwmon: (ads7871) Fix endianness bug in 16-bit register reads Tabrez Ahmed
2026-05-02 18:18 ` Guenter Roeck
2026-05-02 2:08 ` [PATCH v6 2/3] hwmon: (ads7871) Convert to hwmon_device_register_with_info Tabrez Ahmed
2026-05-02 18:18 ` Guenter Roeck
2026-05-02 2:08 ` [PATCH v6 3/3] hwmon: (ads7871) Use DMA-safe buffer for SPI writes Tabrez Ahmed
2026-05-02 18:23 ` Guenter Roeck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox