* [PATCH v2 0/3] iio: lsm303dlh-magn: endianness + boot-time fullscale selection
@ 2026-06-16 13:02 Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data Herman van Hazendonk
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:02 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij
Cc: linux-iio, linux-kernel, llvm, devicetree, stable,
Herman van Hazendonk
This series fixes two independent issues that together prevent the
LSM303DLH magnetometer from delivering usable readings out of the
box on at least the HP TouchPad (apq8060), and adds a small generic
extension to the ST sensors device-tree binding to allow boards to
declare a non-default initial full-scale.
PATCH 1/3 fixes st_sensors_core's read_axis_data() helper to honour
the channel's declared scan_type.endianness. The helper has
unconditionally decoded multi-byte results as little-endian since
it was introduced. Every other in-tree ST sensor declares IIO_LE
and was unaffected, but the LSM303DLH / LSM303DLHC / LSM303DLM
magnetometers all bind st_magn_16bit_channels (IIO_BE) and publish
their X / Y / Z words as big-endian pairs (high byte at the lower
register address, 0x03 / 0x05 / 0x07). The mismatch swapped the
high and low bytes of every magnetometer sample on these three
parts. The fix only affects the IIO_BE branch; existing IIO_LE
consumers are untouched.
PATCH 2/3 adds an optional st,fullscale-milligauss device-tree
property to the ST sensors binding. The driver core hardcodes
fs_avl[0] (the highest-sensitivity range) as the starting
full-scale, which is the right default for a desk-noise floor but
leaves no margin for boards that pick up DC bias from nearby PCB
structures. The property is scoped to magnetometer compatibles
via per-family enum clauses so DTSes with a misspelled value, or
that put the property on accel/gyro/pressure or fixed-FS magn
nodes, fail dt_binding_check rather than being silently no-op'd at
runtime.
PATCH 3/3 parses st,fullscale-milligauss in the magnetometer
common probe and selects the matching fs_avl entry. The LSM303DLH
on the HP TouchPad picks up enough DC bias from the surrounding
power planes that the chip-default +/-1.3 G range saturates the X
axis to the chip's 0xF000 overflow sentinel on every sample, while
Y and Z fall within range. Empirically any fs_avl >= 1 (+/-1.9 G
and up) works; on tenderloin the appropriate value is 2500 mg
(+/-2.5 G).
PATCH 1/3 is a standalone bug fix and is now Cc'd to stable;
PATCHES 2/3 and 3/3 form a unit.
Changes since v1
~~~~~~~~~~~~~~~~
PATCH 1/3 (endianness):
- Restructure around a single u32 tmp + one trailing
sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1), drop
the (s16) / (s32) casts (Andy Shevchenko).
- Make byte_for_channel == 0 || >= 4 an explicit -EINVAL return
(no in-tree caller hits this, but the prior code silently left
*data uninitialised).
- Add Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics
common library") and Cc: stable@vger.kernel.org (Jonathan
Cameron). The bug has been present since the helper was
introduced in 2013.
- Spell out that the fix changes in_magn_*_raw decoding for
LSM303DLHC and LSM303DLM too (same IIO_BE channel set), not
only LSM303DLH.
PATCH 2/3 (binding):
- Rename st,fullscale-mg to st,fullscale-milligauss. "-mg" is
the DT unit-suffix convention but already names milli-g in
the accelerometer parts of this same binding file; spelling
milligauss out keeps the unit unambiguous if a similar
tunable is ever added for accel/gyro/pressure.
- Scope the property to magnetometer compatibles via per-family
allOf:if-then enum clauses:
LSM303DLH/DLHC/DLM: enum [1300, 1900, 2500, 4000, 4700,
5600, 8100]
LIS3MDL/LSM9DS1/LSM303C: enum [4000, 8000, 12000, 16000]
LSM303AGR/LIS2MDL/IIS2MDC (fixed FS): rejected outright
everything else (accel/gyro/pressure/IMU): rejected outright
- Drop the "(or analogous engineering units for other sensor
families that may grow this property in the future)" hand-wave
from the description (Jonathan Cameron); the property is now
positively bound to magnetometers only.
- Drop the overstated "userspace cannot recover without racing
the driver" wording; document the actual probe-time window
(the in-tree IIO consumers cache the saturation sentinel
before any UDEV rule fires).
- Add an in-file maintenance comment before the catch-all NOT
clause so future contributors who add a new magnetometer
compatible know all four clauses must be updated together.
PATCH 3/3 (driver):
- Honour the rename to st,fullscale-milligauss.
- Restructure per Andy: const char *propname at the top,
device_property_present() pre-check, device_property_read_u32()
error path with explicit return.
- Gate the parse block on mdata->sensor_settings->fs.addr != 0
as defence in depth; the binding already rejects the property
on fixed-FS magnetometers, the gate keeps the code path
self-contained against stale DTBs.
To: Jonathan Cameron <jic23@kernel.org>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
To: Nathan Chancellor <nathan@kernel.org>
To: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
To: Bill Wendling <morbo@google.com>
To: Justin Stitt <justinstitt@google.com>
To: Denis Ciocca <denis.ciocca@gmail.com>
To: Lars-Peter Clausen <lars@metafoo.de>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Denis Ciocca <denis.ciocca@st.com>
To: Linus Walleij <linusw@kernel.org>
Cc: linux-iio@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: llvm@lists.linux.dev
Cc: devicetree@vger.kernel.org
v1: https://lore.kernel.org/linux-iio/cover.1780652883.git.github.com@herrie.org/
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20260616-submit-iio-lsm303dlh-magn-fixes-48153433b301
---
Herman van Hazendonk (3):
iio: common: st_sensors: honour channel endianness in read_axis_data
dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss
iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property
.../devicetree/bindings/iio/st,st-sensors.yaml | 71 ++++++++++++++++++++++
drivers/iio/common/st_sensors/st_sensors_core.c | 23 +++++--
drivers/iio/magnetometer/st_magn_core.c | 32 ++++++++++
3 files changed, 120 insertions(+), 6 deletions(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20260616-submit-iio-lsm303dlh-magn-fixes-48153433b301
Best regards,
--
Herman van Hazendonk <github.com@herrie.org>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data
2026-06-16 13:02 [PATCH v2 0/3] iio: lsm303dlh-magn: endianness + boot-time fullscale selection Herman van Hazendonk
@ 2026-06-16 13:02 ` Herman van Hazendonk
2026-06-17 10:02 ` Andy Shevchenko
2026-06-16 13:02 ` [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property Herman van Hazendonk
2 siblings, 1 reply; 7+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:02 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij
Cc: linux-iio, linux-kernel, llvm, devicetree, stable,
Herman van Hazendonk
st_sensors_read_axis_data() unconditionally decoded multi-byte
results with get_unaligned_le16() / get_unaligned_le24() regardless
of the channel's declared scan_type.endianness.
For every ST sensor that has used this helper since it was introduced
this happened to be fine because the ST IMU/accel/gyro/pressure
families publish their data registers as little-endian and the
channel specs in those drivers declare IIO_LE accordingly.
The LSM303DLH magnetometer however publishes its X/Y/Z output as a
pair of big-endian bytes (the H register sits at the lower address,
0x03/0x05/0x07, and the L register immediately after), and its
channel specs in st_magn_core.c correctly declare IIO_BE -- but
read_axis_data() ignored that and decoded as little-endian, swapping
the high and low bytes of every magnetometer sample. The LSM303DLHC
and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and
were therefore byte-swapped by the same bug; users of those parts
will see different in_magn_*_raw values after this fix lands.
The bug is most visible on a stationary chip: in earth's field the
true X reading is small and the high byte sits at 0x00, so swapping
the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0
= 240). Y and Z still appear "to vary" because their magnitudes are
larger and the noise in the low byte produces big swings in the
swapped high byte:
before (LSM303DLH flat, sysfs in_magn_*_raw):
X=240 (stuck), Y= 12032..23296, Z=-16128..-9728
after (direct i2c-dev big-endian decode, same chip same orientation):
X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's
ambient field at low gauss range)
Fix read_axis_data() to dispatch on ch->scan_type.endianness and
call get_unaligned_be16() / get_unaligned_be24() when the channel
declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro,
st_pressure, st_lsm6dsx and others) are unaffected because their
channel specs already declare IIO_LE and the LE path is unchanged.
While restructuring the branches, replace the previously implicit
silent-success-with-uninitialised-*data fall-through for
byte_for_channel outside 1..3 with an explicit return -EINVAL. No
in-tree ST sensor publishes such a channel, but the new behaviour
is strictly safer than handing userspace garbage.
Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch
Assisted-by: Sashiko:claude-opus-4-7
Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
---
drivers/iio/common/st_sensors/st_sensors_core.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index dbc5e16fbde4..76f91696f66a 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
u8 *outdata;
struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned int byte_for_channel;
+ u32 tmp;
byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
ch->scan_type.shift, 8);
@@ -508,12 +509,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
if (err < 0)
return err;
- if (byte_for_channel == 1)
- *data = (s8)*outdata;
- else if (byte_for_channel == 2)
- *data = (s16)get_unaligned_le16(outdata);
- else if (byte_for_channel == 3)
- *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23);
+ if (byte_for_channel == 1) {
+ tmp = *outdata;
+ } else if (byte_for_channel == 2) {
+ if (ch->scan_type.endianness == IIO_BE)
+ tmp = get_unaligned_be16(outdata);
+ else
+ tmp = get_unaligned_le16(outdata);
+ } else if (byte_for_channel == 3) {
+ if (ch->scan_type.endianness == IIO_BE)
+ tmp = get_unaligned_be24(outdata);
+ else
+ tmp = get_unaligned_le24(outdata);
+ } else {
+ return -EINVAL;
+ }
+ *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1);
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss
2026-06-16 13:02 [PATCH v2 0/3] iio: lsm303dlh-magn: endianness + boot-time fullscale selection Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data Herman van Hazendonk
@ 2026-06-16 13:02 ` Herman van Hazendonk
2026-06-16 15:41 ` Conor Dooley
2026-06-16 13:02 ` [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property Herman van Hazendonk
2 siblings, 1 reply; 7+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:02 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij
Cc: linux-iio, linux-kernel, llvm, devicetree, Herman van Hazendonk
Add an optional st,fullscale-milligauss property that selects the
initial magnetometer full-scale range at probe time, expressed in
milligauss.
The motivating case is the LSM303DLH magnetometer on the HP TouchPad
(apq8060 / tenderloin) where the kernel's chip-default +/-1.3 G range
saturates the X axis to the chip's 0xF000 overflow sentinel out of
probe, because the chip is mounted close to surrounding power planes
and picks up enough DC bias to exceed the smallest range.
The chip is not wedged by the saturation: a sysfs write of a wider
range to in_magn_x_scale recovers it on the next conversion, and a
UDEV rule on add of the IIO device is a viable steady-state
workaround. What the DT property buys is the probe-time window: the
in-tree consumers we use (sensorfw's iio-sensors-adaptor and the
geomagnetic / orientation services on top of it) start polling
in_magn_x_raw essentially as soon as the device node appears and
treat the 0xF000 sentinel as a legitimate sample. Until a UDEV rule
fires and commits the wider range, every read returns the stuck
sentinel, and on slow-boot paths the consumer may have already
cached a bogus calibration baseline by the time UDEV catches up.
st,fullscale-milligauss lets the device tree declare a wider
initial range up-front so the correct range is in effect before any
IIO consumer can open the device, and keeps the board-specific
magnetometer calibration alongside the rest of the hardware
description rather than splitting it between DTS and per-distro
UDEV rules.
The full property name is spelled out rather than abbreviated to
"-mg" because this same binding file already covers ST accelerometers
where the conventional shorthand "mg" reads as milli-g (acceleration);
the explicit "-milligauss" suffix makes the unit unambiguous if a
similar tunable is ever introduced for the accel/gyro/pressure
families.
The property is scoped to magnetometer compatibles via allOf/if-then
clauses, with per-family enum lists of the accepted milligauss
values (1300..8100 for LSM303DLH/DLHC/DLM, 4000..16000 for
LIS3MDL/LSM9DS1/LSM303C). LSM303AGR / LIS2MDL / IIS2MDC have a
single fixed full-scale (15000 mg) with no register to switch
ranges, so the property is rejected outright for them. DTSes that
misspell the value, place the property on an accelerometer /
gyroscope / pressure node, or set it on a fixed-FS magnetometer
fail dt_binding_check rather than emitting a runtime warning.
The property is purely additive: if absent, drivers fall back to
their existing chip default. No existing in-tree DTS is affected.
Assisted-by: Claude:claude-opus-4-7 dt_binding_check checkpatch
Assisted-by: Sashiko:claude-opus-4-7
Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
---
.../devicetree/bindings/iio/st,st-sensors.yaml | 71 ++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
index a1a958215cdb..f0805604c849 100644
--- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
+++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
@@ -126,6 +126,13 @@ properties:
mount-matrix:
description: an optional 3x3 mounting rotation matrix.
+ st,fullscale-milligauss:
+ description:
+ Initial magnetometer full-scale at probe time, in milligauss.
+ Per-chip allowed values are enumerated in the allOf clauses
+ below.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
allOf:
- if:
properties:
@@ -163,6 +170,70 @@ allOf:
maxItems: 1
st,drdy-int-pin: false
+ # Per-chip enum lists for st,fullscale-milligauss. Out-of-range
+ # values fail dt_binding_check instead of being demoted to a
+ # runtime warning by the driver.
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - st,lsm303dlh-magn
+ - st,lsm303dlhc-magn
+ - st,lsm303dlm-magn
+ then:
+ properties:
+ st,fullscale-milligauss:
+ enum: [1300, 1900, 2500, 4000, 4700, 5600, 8100]
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - st,lis3mdl-magn
+ - st,lsm303c-magn
+ - st,lsm9ds1-magn
+ then:
+ properties:
+ st,fullscale-milligauss:
+ enum: [4000, 8000, 12000, 16000]
+
+ # Single fixed full-scale, no register to switch: reject.
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - st,iis2mdc
+ - st,lis2mdl
+ - st,lsm303agr-magn
+ then:
+ properties:
+ st,fullscale-milligauss: false
+
+ # Reject st,fullscale-milligauss on non-magnetometer compatibles.
+ # Keep this enum in sync with the three magn clauses above when
+ # adding a new magnetometer compatible.
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - st,iis2mdc
+ - st,lis2mdl
+ - st,lis3mdl-magn
+ - st,lsm303agr-magn
+ - st,lsm303c-magn
+ - st,lsm303dlh-magn
+ - st,lsm303dlhc-magn
+ - st,lsm303dlm-magn
+ - st,lsm9ds1-magn
+ then:
+ properties:
+ st,fullscale-milligauss: false
+
required:
- compatible
- reg
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property
2026-06-16 13:02 [PATCH v2 0/3] iio: lsm303dlh-magn: endianness + boot-time fullscale selection Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss Herman van Hazendonk
@ 2026-06-16 13:02 ` Herman van Hazendonk
2026-06-17 10:05 ` Andy Shevchenko
2 siblings, 1 reply; 7+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:02 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij
Cc: linux-iio, linux-kernel, llvm, devicetree, Herman van Hazendonk
The ST magnetometer core's common probe hardcodes fs_avl[0] -- the
highest-sensitivity full-scale supported by the chip -- as the
starting range. For the LSM303DLH that is +/-1.3 G; for the
LSM303DLHC and LSM303DLM it is +/-2 G; for the LIS3MDL it is +/-4 G.
That is the right default for "minimal noise floor at a desk", but
it leaves no margin for boards that pick up appreciable DC bias from
nearby PCB structures. On the HP TouchPad (apq8060 / tenderloin) the
LSM303DLH magnetometer is mounted close enough to the surrounding
power planes that X reads back as the chip's 0xF000 overflow
sentinel (== -4096 raw, the value the chip publishes when the ADC
saturates) on every sample at the chip-default range, while Y and Z
fall well within the +/-1.3 G window.
Parse the st,fullscale-milligauss device-tree property (documented
separately in dt-bindings/iio/st,st-sensors.yaml) in the
magnetometer common probe to select the initial fs_avl entry by its
mg value. The DT binding pins the accepted value set per compatible
via allOf/if-then enum clauses, so a malformed mg value fails
dt_binding_check rather than reaching the driver. Sensors with a
fixed full-scale (fs.addr == 0: LSM303AGR, LIS2MDL, IIS2MDC) have no
register to switch and the property is rejected outright for them
in the binding; the parse block is additionally gated on fs.addr as
defence in depth against stale DTBs.
Per-sensor mg ranges are listed in st_magn_sensors_settings[]. For
LSM303DLH and LSM303DLHC/DLM the valid values are 1300, 1900, 2500,
4000, 4700, 5600 and 8100; for LIS3MDL, LSM9DS1-magn and LSM303C-magn
they are 4000, 8000, 12000, 16000.
Empirical scale sweep on the HP TouchPad confirmed that on this
board any fs_avl >= 1 produces non-saturated X readings:
scale (0.001 G/LSB) | X raw Y raw Z raw
--------------------+-------------------------------
1.100 | -4096 44 46 (X saturated)
0.855 | -547 37 37 (clean)
0.670 | -433 94 103 (clean)
0.450 | -266 44 71 (clean)
0.400 | -235 34 65 (clean)
0.330 | -196 27 56 (clean)
0.230 | -145 15 40 (clean)
2500 mg is the natural choice for tenderloin: comfortably outside
the saturation regime while keeping useful precision for compass
applications.
Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch
Assisted-by: Sashiko:claude-opus-4-7
Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
---
drivers/iio/magnetometer/st_magn_core.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index ef348d316c00..6f369e8dddea 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -608,6 +609,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
struct st_sensor_data *mdata = iio_priv(indio_dev);
struct device *parent = indio_dev->dev.parent;
struct st_sensors_platform_data *pdata = dev_get_platdata(parent);
+ const char *propname;
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -628,6 +630,36 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0];
mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;
+ /*
+ * Skip fixed-FS chips (fs.addr == 0): no register to switch.
+ * The binding rejects the property on these compatibles too;
+ * the gate guards stale DTBs.
+ */
+ propname = "st,fullscale-milligauss";
+ if (mdata->sensor_settings->fs.addr &&
+ device_property_present(parent, propname)) {
+ struct st_sensor_fullscale *fs = &mdata->sensor_settings->fs;
+ u32 fs_mg;
+ int i;
+
+ err = device_property_read_u32(parent, propname, &fs_mg);
+ if (err)
+ return err;
+
+ for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+ if (!fs->fs_avl[i].num)
+ break;
+ if (fs->fs_avl[i].num == fs_mg) {
+ mdata->current_fullscale = &fs->fs_avl[i];
+ break;
+ }
+ }
+ if (mdata->current_fullscale->num != fs_mg)
+ dev_warn(parent, "%s=%u not supported, using %u\n",
+ propname, fs_mg,
+ mdata->current_fullscale->num);
+ }
+
if (!pdata)
pdata = (struct st_sensors_platform_data *)&default_magn_pdata;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss
2026-06-16 13:02 ` [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss Herman van Hazendonk
@ 2026-06-16 15:41 ` Conor Dooley
0 siblings, 0 replies; 7+ messages in thread
From: Conor Dooley @ 2026-06-16 15:41 UTC (permalink / raw)
To: Herman van Hazendonk
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij,
linux-iio, linux-kernel, llvm, devicetree
[-- Attachment #1: Type: text/plain, Size: 78 bytes --]
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data
2026-06-16 13:02 ` [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data Herman van Hazendonk
@ 2026-06-17 10:02 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-06-17 10:02 UTC (permalink / raw)
To: Herman van Hazendonk
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij,
linux-iio, linux-kernel, llvm, devicetree, stable
On Tue, Jun 16, 2026 at 03:02:04PM +0200, Herman van Hazendonk wrote:
> st_sensors_read_axis_data() unconditionally decoded multi-byte
> results with get_unaligned_le16() / get_unaligned_le24() regardless
> of the channel's declared scan_type.endianness.
>
> For every ST sensor that has used this helper since it was introduced
> this happened to be fine because the ST IMU/accel/gyro/pressure
> families publish their data registers as little-endian and the
> channel specs in those drivers declare IIO_LE accordingly.
>
> The LSM303DLH magnetometer however publishes its X/Y/Z output as a
> pair of big-endian bytes (the H register sits at the lower address,
> 0x03/0x05/0x07, and the L register immediately after), and its
> channel specs in st_magn_core.c correctly declare IIO_BE -- but
> read_axis_data() ignored that and decoded as little-endian, swapping
> the high and low bytes of every magnetometer sample. The LSM303DLHC
> and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and
> were therefore byte-swapped by the same bug; users of those parts
> will see different in_magn_*_raw values after this fix lands.
>
> The bug is most visible on a stationary chip: in earth's field the
> true X reading is small and the high byte sits at 0x00, so swapping
> the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0
> = 240). Y and Z still appear "to vary" because their magnitudes are
> larger and the noise in the low byte produces big swings in the
> swapped high byte:
>
> before (LSM303DLH flat, sysfs in_magn_*_raw):
> X=240 (stuck), Y= 12032..23296, Z=-16128..-9728
>
> after (direct i2c-dev big-endian decode, same chip same orientation):
> X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's
> ambient field at low gauss range)
>
> Fix read_axis_data() to dispatch on ch->scan_type.endianness and
> call get_unaligned_be16() / get_unaligned_be24() when the channel
> declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro,
> st_pressure, st_lsm6dsx and others) are unaffected because their
> channel specs already declare IIO_LE and the LE path is unchanged.
>
> While restructuring the branches, replace the previously implicit
> silent-success-with-uninitialised-*data fall-through for
> byte_for_channel outside 1..3 with an explicit return -EINVAL. No
> in-tree ST sensor publishes such a channel, but the new behaviour
> is strictly safer than handing userspace garbage.
Sounds like inevitable change in ABI, but worth doing it.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property
2026-06-16 13:02 ` [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property Herman van Hazendonk
@ 2026-06-17 10:05 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-06-17 10:05 UTC (permalink / raw)
To: Herman van Hazendonk
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
Denis Ciocca, Lars-Peter Clausen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Denis Ciocca, Linus Walleij,
linux-iio, linux-kernel, llvm, devicetree
On Tue, Jun 16, 2026 at 03:02:06PM +0200, Herman van Hazendonk wrote:
> The ST magnetometer core's common probe hardcodes fs_avl[0] -- the
> highest-sensitivity full-scale supported by the chip -- as the
> starting range. For the LSM303DLH that is +/-1.3 G; for the
> LSM303DLHC and LSM303DLM it is +/-2 G; for the LIS3MDL it is +/-4 G.
>
> That is the right default for "minimal noise floor at a desk", but
> it leaves no margin for boards that pick up appreciable DC bias from
> nearby PCB structures. On the HP TouchPad (apq8060 / tenderloin) the
> LSM303DLH magnetometer is mounted close enough to the surrounding
> power planes that X reads back as the chip's 0xF000 overflow
> sentinel (== -4096 raw, the value the chip publishes when the ADC
> saturates) on every sample at the chip-default range, while Y and Z
> fall well within the +/-1.3 G window.
>
> Parse the st,fullscale-milligauss device-tree property (documented
> separately in dt-bindings/iio/st,st-sensors.yaml) in the
> magnetometer common probe to select the initial fs_avl entry by its
> mg value. The DT binding pins the accepted value set per compatible
> via allOf/if-then enum clauses, so a malformed mg value fails
> dt_binding_check rather than reaching the driver. Sensors with a
> fixed full-scale (fs.addr == 0: LSM303AGR, LIS2MDL, IIS2MDC) have no
> register to switch and the property is rejected outright for them
> in the binding; the parse block is additionally gated on fs.addr as
> defence in depth against stale DTBs.
>
> Per-sensor mg ranges are listed in st_magn_sensors_settings[]. For
> LSM303DLH and LSM303DLHC/DLM the valid values are 1300, 1900, 2500,
> 4000, 4700, 5600 and 8100; for LIS3MDL, LSM9DS1-magn and LSM303C-magn
> they are 4000, 8000, 12000, 16000.
>
> Empirical scale sweep on the HP TouchPad confirmed that on this
> board any fs_avl >= 1 produces non-saturated X readings:
>
> scale (0.001 G/LSB) | X raw Y raw Z raw
> --------------------+-------------------------------
> 1.100 | -4096 44 46 (X saturated)
> 0.855 | -547 37 37 (clean)
> 0.670 | -433 94 103 (clean)
> 0.450 | -266 44 71 (clean)
> 0.400 | -235 34 65 (clean)
> 0.330 | -196 27 56 (clean)
> 0.230 | -145 15 40 (clean)
>
> 2500 mg is the natural choice for tenderloin: comfortably outside
> the saturation regime while keeping useful precision for compass
> applications.
Not sure if we need that big commit message, better to move to the point.
> + propname = "st,fullscale-milligauss";
> + if (mdata->sensor_settings->fs.addr &&
> + device_property_present(parent, propname)) {
> + struct st_sensor_fullscale *fs = &mdata->sensor_settings->fs;
> + u32 fs_mg;
> + int i;
Instead...
> + err = device_property_read_u32(parent, propname, &fs_mg);
> + if (err)
> + return err;
> + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
for (unsigned int i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> + if (!fs->fs_avl[i].num)
> + break;
> + if (fs->fs_avl[i].num == fs_mg) {
> + mdata->current_fullscale = &fs->fs_avl[i];
> + break;
> + }
> + }
> + if (mdata->current_fullscale->num != fs_mg)
> + dev_warn(parent, "%s=%u not supported, using %u\n",
> + propname, fs_mg,
> + mdata->current_fullscale->num);
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-17 10:05 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-16 13:02 [PATCH v2 0/3] iio: lsm303dlh-magn: endianness + boot-time fullscale selection Herman van Hazendonk
2026-06-16 13:02 ` [PATCH v2 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data Herman van Hazendonk
2026-06-17 10:02 ` Andy Shevchenko
2026-06-16 13:02 ` [PATCH v2 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-milligauss Herman van Hazendonk
2026-06-16 15:41 ` Conor Dooley
2026-06-16 13:02 ` [PATCH v2 3/3] iio: magnetometer: st_magn: honour st,fullscale-milligauss DT property Herman van Hazendonk
2026-06-17 10:05 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox