* [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* 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
* [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* 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
* [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 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