.../iio/magnetometer/qstcorp,qmc5883p.yaml | 48 ++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + MAINTAINERS | 7 + drivers/iio/magnetometer/Kconfig | 11 + drivers/iio/magnetometer/Makefile | 2 + drivers/iio/magnetometer/qmc5883p.c | 673 +++++++++++++++++++++ 6 files changed, 743 insertions(+)
This series adds an IIO driver for the QST QMC5883P, a 3-axis
anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC,
communicating over I2C. To my knowledge there is no existing
upstream driver for this device (see "Prior-art register-map check"
below).
The driver supports:
- Raw magnetic field readings on X, Y and Z axes
- Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G),
selectable via IIO_CHAN_INFO_SCALE
- Four output data rates (10, 50, 100, 200 Hz), selectable via
IIO_CHAN_INFO_SAMP_FREQ
- Four oversampling ratios (1, 2, 4, 8), selectable via
IIO_CHAN_INFO_OVERSAMPLING_RATIO
- Runtime PM with a 2 s autosuspend delay
- System suspend/resume delegated to the runtime callbacks
Regmap with an rbtree cache is used throughout. CTRL_1 and CTRL_2
bit fields are accessed via regmap_field to avoid read-modify-write
races. The STATUS register is marked precious so regmap never reads
it speculatively and clears the DRDY/OVFL bits unexpectedly.
The probe-time init sequence is: soft reset, wait 300 us for POR
to complete, deassert reset, drop the register cache so subsequent
RMW writes read fresh values, then enter normal mode. 300 us
comfortably covers the 250 us POR time given in the datasheet.
Patches:
1/5 - dt-bindings: vendor-prefixes: Add QST Corporation
2/5 - dt-bindings: iio: magnetometer: QSTCORP QMC5883P
3/5 - iio: magnetometer: add driver for QST QMC5883P
4/5 - iio: magnetometer: qmc5883p: add oversampling ratio support
5/5 - iio: magnetometer: qmc5883p: add PM support
Patches 4 and 5 are split out from the main driver so that the core
(1-3) can be reviewed and picked independently, per review feedback
on v2. 4/5 exposes the CTRL_1 OSR field via
IIO_CHAN_INFO_OVERSAMPLING_RATIO. 5/5 adds runtime PM that puts the
chip into MODE_SUSPEND when idle and wakes it to MODE_NORMAL on
demand.
Changes in v3
=============
Addressing review feedback on v2:
- Moved the driver out of staging and into drivers/iio/magnetometer/
(Greg Kroah-Hartman).
- Changed the vendor prefix from "qst" to "qstcorp" to match the
manufacturer's domain (qstcorp.com) (Krzysztof Kozlowski).
- Subject of the binding patch no longer says "Add binding for";
"dt-bindings:" already conveys that (Krzysztof Kozlowski).
- Dropped the redundant last sentence of the binding commit message
(Krzysztof Kozlowski).
- VDD supply is now made required (Krzysztof Kozlowski).
- Split runtime PM + system sleep handling out of the core driver
patch into its own patch (5/5), so the core driver can be reviewed
independently (David Lechner).
- Split oversampling-ratio support out into its own patch (4/5)
(David Lechner).
- Dropped the custom downsampling_ratio sysfs attribute entirely.
The datasheet describes OSR2 only as "another filter ... depth can
be adjusted through OSR2", with no further characterisation, and
no application note clarifying it. Without a precise definition
of what the filter actually does it is not possible to map OSR2
to any existing IIO ABI, so support for it is dropped from this
series (David Lechner).
- qmc5883p_verify_chip_id() -> qmc5883p_read_chip_id() no longer
treats an ID mismatch as a probe failure; the chip-ID check is
informational only (David Lechner).
- qmc5883p_chip_init() no longer programs driver-chosen defaults
for RNG/OSR/DSR/ODR. The hardware defaults are sufficient and the
explicit writes were a development artifact (David Lechner).
- Post-reset delay in qmc5883p_chip_init() uses fsleep() with a
comment citing the 250 us POR time from the datasheet
(David Lechner).
- Timeout in regmap_read_poll_timeout() written as
150 * (MICRO / MILLI) instead of 150000 (David Lechner).
- Channel spec duplication collapsed behind a QMC5883P_CHAN(ch)
macro (David Lechner).
- qmc5883p_rf_init() moved up in probe, before the regulator and
chip-ID reads, so the regmap fields are available by the time
they are needed (David Lechner).
- Trailing comma and extra whitespace in the of_device_id and
i2c_device_id sentinel entries cleaned up (David Lechner).
- Verified that there is no existing driver in drivers/iio/,
drivers/hwmon/, drivers/input/, drivers/staging/iio/ or
drivers/misc/ that matches the QMC5883P register map. Summary
of candidates inspected is included in the "Testing" section
below (Andy Shevchenko).
- Waited ~10 days before sending v3 to allow time for review
(Andy Shevchenko).
Additional v3 changes not directly from review:
- Scale encoding changed from IIO_VAL_FRACTIONAL to
IIO_VAL_INT_PLUS_NANO with a matching write_raw_get_fmt(),
because the IIO core defaults sysfs writes to
IIO_VAL_INT_PLUS_MICRO and was silently truncating nano-precision
writes. The truncation on the 8 G and 2 G entries is documented
in a comment above the scale table.
- STATUS register marked precious (in addition to volatile) so
regmap never reads it speculatively and clears DRDY/OVFL.
- Added regcache_drop_region() after the soft-reset deassert, so
subsequent RMW writes read fresh values rather than cached
pre-reset values.
Changes in v2
=============
- Use get_unaligned_le16() from <linux/unaligned.h> instead of
manual byte-shifting for deserialising axis data (review feedback).
- Fix pm_runtime_* calls in downsampling_ratio_store() to use
data->dev (the i2c parent device) instead of dev (the iio
device), avoiding PM refcount imbalances (review feedback).
- Replace manual pm_runtime_disable() devm action with
devm_pm_runtime_enable(), which avoids a kcfi-violating function
pointer cast (review feedback).
- Move chip suspend into a devm action (qmc5883p_suspend_action)
registered before devm_iio_device_register() so that devres LIFO
ordering guarantees the IIO interface is fully unregistered
before the hardware is put to sleep, closing a race window on
removal (review feedback).
- Drop qmc5883p_remove() and the .remove hook entirely, as the
above devm action subsumes it (review feedback).
- Remove the empty qmc5883p_runtime_idle() stub; passing NULL in
RUNTIME_PM_OPS already provides the correct default behaviour.
- Add regulator support: use devm_regulator_get_enable_optional()
for the vdd-supply documented in the dt-binding, with a 50 ms
post-enable delay per datasheet section 5.3 (PSUP ramp + POR
time) (review feedback).
- Reinitialise the chip in qmc5883p_system_resume() via
qmc5883p_chip_init() followed by regcache_mark_dirty() +
regcache_sync(), so that the driver recovers correctly if the
regulator was physically cut during system suspend and POR
reset all registers (review feedback).
Links
=====
v1: https://lore.kernel.org/all/20260409162308.2590385-1-hardik.phalet@pm.me/
v2: https://lore.kernel.org/all/20260409210639.3197576-1-hardik.phalet@pm.me/
Testing
=======
Hardware
A GY-271 HM-246 breakout (this board is a QMC5883P, not a QMC5883L,
despite what some vendors put on the silkscreen), connected over
I2C bus 1 to a Raspberry Pi 4B running a mainline aarch64 kernel.
The chip enumerates at address 0x2C via i2cdetect, and CHIP_ID
(register 0x00) reads back 0x80 as expected.
Prior-art register-map check (for Andy)
I grepped drivers/iio/magnetometer/, drivers/hwmon/,
drivers/input/misc/, drivers/staging/iio/ and drivers/misc/ for
the distinctive offsets 0x09 (STATUS) and 0x0A (CTRL_1), narrowed
to files containing both, and manually compared each candidate's
register layout and control-bit encoding against the QMC5883P.
Closest candidates:
ak8975.c - four register offsets coincide (0x00, 0x09, 0x0A,
0x0B) but the data registers sit at 0x03-0x08
(shifted +2 vs QMC5883P's 0x01-0x06), DRDY is in
ST1 at 0x02 rather than STATUS at 0x09, and CNTL
encodes a 4-bit mode only - no packed ODR/OSR/range
fields.
hmc5843.c - STATUS matches at 0x09, but 0x0A is a read-only ID
register, configuration spans 0x00-0x02 rather than
a single CTRL_1 byte, and data is MSB-first at 0x03
in X/Z/Y order.
af8133j.c - 0x0A and 0x0B carry mode and range, but STATUS is
at 0x02, data starts at 0x03, and the mode field is
2-valued (standby/work) rather than 4-valued.
mmc35240.c - data at 0x00-0x05 overlaps, but STATUS and control
land at 0x06-0x08.
No overlap worth discussing: mmc5633, mag3110, tlv493d, tmag5273,
bmc150_magn, rm3100, yamaha-yas530, st_magn, si7210, als31300. No
magnetometer driver under drivers/hwmon/, drivers/input/misc/ or
drivers/staging/.
Conclusion: no existing driver can be extended to cover the
QMC5883P without restructuring its register addressing and
control-bit model. A new driver is warranted.
Functional testing on v3
- Chip ID read: 0x80 (matches datasheet).
- Raw axis reads: in_magn_{x,y,z}_raw return stable s16 values
and track manual reorientation of the board.
- Scale: all four ranges (+/-2/8/12/30 G) selectable via
in_magn_scale; in_magn_scale_available lists all four; sysfs
write-back round-trips cleanly at nano precision.
- Sampling frequency: 10/50/100/200 Hz all selectable via
in_magn_sampling_frequency; _available lists all four.
- Oversampling ratio (patch 4): 1/2/4/8 selectable via
in_magn_oversampling_ratio; _available lists all four.
- DRDY polling: verified STATUS.DRDY asserts and clears on read,
and that OVFL is captured in the same read as DRDY.
- Soft reset: register state after qmc5883p_chip_init() matches
the datasheet defaults; regcache_drop_region() confirmed by
observing fresh values being read on the first post-reset RMW.
- Runtime PM (patch 5): power/runtime_status transitions to
"suspended" after the 2 s autosuspend delay (MODE_SUSPEND on
the wire, verified by i2cdump); next sysfs read resumes the
device and returns valid data.
- System sleep: echo mem > /sys/power/state (s2idle on the Pi)
followed by wake; readings are valid after resume.
- Unbind: echo <dev> > /sys/bus/i2c/drivers/qmc5883p/unbind
leaves the chip in MODE_SUSPEND, confirming the devm LIFO
teardown order.
- Build: CONFIG_QMC5883P=y and =m both clean; W=1 clean on
aarch64; sparse clean; checkpatch --strict clean.
- dt_binding_check: passes for patch 2/5.
Signed-off-by: Hardik Phalet <hardik.phalet@pm.me>
---
Hardik Phalet (5):
dt-bindings: vendor-prefixes: Add QST Corporation
dt-bindings: iio: magnetometer: QSTCORP QMC5883P
iio: magnetometer: add driver for QST QMC5883P
iio: magnetometer: qmc5883p: add oversampling ratio support
iio: magnetometer: qmc5883p: add PM support
.../iio/magnetometer/qstcorp,qmc5883p.yaml | 48 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 11 +
drivers/iio/magnetometer/Makefile | 2 +
drivers/iio/magnetometer/qmc5883p.c | 673 +++++++++++++++++++++
6 files changed, 743 insertions(+)
---
base-commit: d2a4ec19d2a2e54c23b5180e939994d3da4a6b91
change-id: 20260418-qmc5883p-driver-dcc74bd4a789
Best regards,
--
Hardik Phalet <hardik.phalet@pm.me>
On Sun, 19 Apr 2026 22:32:09 +0000 Hardik Phalet <hardik.phalet@pm.me> wrote: > This series adds an IIO driver for the QST QMC5883P, a 3-axis > anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC, > communicating over I2C. To my knowledge there is no existing > upstream driver for this device (see "Prior-art register-map check" > below). > > The driver supports: > - Raw magnetic field readings on X, Y and Z axes > - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G), > selectable via IIO_CHAN_INFO_SCALE > - Four output data rates (10, 50, 100, 200 Hz), selectable via > IIO_CHAN_INFO_SAMP_FREQ > - Four oversampling ratios (1, 2, 4, 8), selectable via > IIO_CHAN_INFO_OVERSAMPLING_RATIO I'm suspicious about this one based on a very quick read of the datasheet. Conventional oversampling would involve running the internal sampling engine at a multiple of the sampling frequency, and then averaging the results. The datasheet describes this as: "Over sample Rate (OSR1) registers are used to control bandwidth of an internal digital filter. Larger OSR value leads to smaller filter bandwidth, less in-band noise and higher power consumption. It could be used to reach a good balance between noise and power. Four over sample ratios can be selected, 8,4,2 or 1." That sounds like a boxcar filter to me not oversampling (which would be a combination of box car and reducing the output data rate). If possible, can you enable the data ready output and put a scope on it to see if that changes frequency when OSR or OSR2 are modified. Trickier to do would be looking at the noise levels whilst playing with these filters and see if they at least match with standard filter types. If we can't figure these out, then it may be a case of picking something that works well and hard coding that rather than letting userspace change things in a fashion that might not match the ABI. > - Runtime PM with a 2 s autosuspend delay > - System suspend/resume delegated to the runtime callbacks
On Mon Apr 20, 2026 at 7:15 PM IST, Jonathan Cameron wrote:
> On Sun, 19 Apr 2026 22:32:09 +0000
> Hardik Phalet <hardik.phalet@pm.me> wrote:
>
>> This series adds an IIO driver for the QST QMC5883P, a 3-axis
>> anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC,
>> communicating over I2C. To my knowledge there is no existing
>> upstream driver for this device (see "Prior-art register-map check"
>> below).
>>
>> The driver supports:
>> - Raw magnetic field readings on X, Y and Z axes
>> - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G),
>> selectable via IIO_CHAN_INFO_SCALE
>> - Four output data rates (10, 50, 100, 200 Hz), selectable via
>> IIO_CHAN_INFO_SAMP_FREQ
>> - Four oversampling ratios (1, 2, 4, 8), selectable via
>> IIO_CHAN_INFO_OVERSAMPLING_RATIO
>
> I'm suspicious about this one based on a very quick read of the datasheet.
> Conventional oversampling would involve running the internal sampling
> engine at a multiple of the sampling frequency, and then averaging the
> results. The datasheet describes this as:
> "Over sample Rate (OSR1) registers are used to control bandwidth of an
> internal digital filter. Larger OSR value leads to smaller filter bandwidth,
> less in-band noise and higher power consumption. It could be used to reach a
> good balance between noise and power. Four over sample ratios can be selected,
> 8,4,2 or 1."
>
> That sounds like a boxcar filter to me not oversampling (which would be
> a combination of box car and reducing the output data rate).
>
> If possible, can you enable the data ready output and put a scope on it
> to see if that changes frequency when OSR or OSR2 are modified.
>
> Trickier to do would be looking at the noise levels whilst playing with
> these filters and see if they at least match with standard filter types.
>
> If we can't figure these out, then it may be a case of picking something
> that works well and hard coding that rather than letting userspace
> change things in a fashion that might not match the ABI.
I could not arrange a oscilloscope. So wrote a small standalone tool that drives
the chip directly over /dev/i2c-N (skipping the driver) on a Pi 4 and
characterised all three fields in CTRL_1 with timing + noise measurements.
Summary:
Field Bits Role per measurement
ODR [3:2] output rate, accurate to ~3% of nominal in Normal Mode
OSR1 [5:4] low-pass filter (bandwidth control), no rate change
OSR2 [7:6] N-sample averaging, no rate change (real oversampling)
1. DRDY interval vs each field (median of 500 samples per setting,
ms; SCHED_FIFO, pinned CPU):
vary ODR (OSR1=1, OSR2=1):
ODR=10 Hz -> 100.9 ms ODR=100 Hz -> 10.30 ms
ODR=50 Hz -> 20.5 ms ODR=200 Hz -> 5.14 ms
vary OSR1 (ODR=200 Hz, OSR2=1):
OSR1=1 -> 5.142 ms OSR1=4 -> 5.139 ms
OSR1=2 -> 5.143 ms OSR1=8 -> 5.141 ms
vary OSR2 (ODR=200 Hz, OSR1=1):
OSR2=1 -> 5.142 ms OSR2=4 -> 5.142 ms
OSR2=2 -> 5.140 ms OSR2=8 -> 5.141 ms
ODR is the only knob that changes the rate.
2. Per-axis noise stddev (2000 samples, sensor still, ratios vs N=1):
vary OSR1: vary OSR2:
OSR1=1 -> 1.000 OSR2=1 -> 1.000
OSR1=2 -> 0.723 OSR2=2 -> 0.706
OSR1=4 -> 0.544 OSR2=4 -> 0.494
OSR1=8 -> 0.416 OSR2=8 -> 0.333
Reference for N-sample averaging (1/sqrt(N)):
1.000 / 0.707 / 0.500 / 0.354
OSR2 matches within measurement noise; OSR1 trails the curve,
which is the expected signature of a bandwidth-limiting filter
on non-white input noise.
So to my understanding, OSR2 is true oversampling. OSR1 is a filter.
v4 plan, pending your input on (c):
(a) Add an OSR2 regmap_field and expose it as
IIO_CHAN_INFO_OVERSAMPLING_RATIO with values {1, 2, 4, 8}.
(b) Drop the OVERSAMPLING_RATIO mapping on OSR1.
(c) For OSR1, two options:
i) expose as IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY.
The public datasheet does not give cutoff frequencies,
so I'd have to either characterise them empirically and
document them in the driver, or use placeholder values
derived from the OSR1 setting alone (which feels wrong).
ii) hard-code OSR1=1 in chip_init and not expose it. Loses
the noise-vs-bandwidth tradeoff but keeps the ABI clean.
Slight preference for (ii) unless you'd rather see the filter
knob exposed.
(d) Keep ODR mapped to IIO_CHAN_INFO_SAMP_FREQ as in v3.
One other finding worth mentioning: the datasheet's setup examples
(sec 7.1, 7.2, 7.3) all write 0x06 to register 0x29 before entering
an active mode, with the comment "Define the sign for X Y and Z axis".
This register is not listed in the documented register map but the
write is required for axes Y and Z to come out with the documented
orientation. v4 will add this write to chip_init().
If you have other measurements you'd like to see before v4, happy to
run them.
>
>> - Runtime PM with a 2 s autosuspend delay
>> - System suspend/resume delegated to the runtime callbacks
Best regards,
Hardik
On 5/17/26 2:17 PM, Hardik Phalet wrote:
> On Mon Apr 20, 2026 at 7:15 PM IST, Jonathan Cameron wrote:
>> On Sun, 19 Apr 2026 22:32:09 +0000
>> Hardik Phalet <hardik.phalet@pm.me> wrote:
>>
>>> This series adds an IIO driver for the QST QMC5883P, a 3-axis
>>> anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC,
>>> communicating over I2C. To my knowledge there is no existing
>>> upstream driver for this device (see "Prior-art register-map check"
>>> below).
>>>
>>> The driver supports:
>>> - Raw magnetic field readings on X, Y and Z axes
>>> - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G),
>>> selectable via IIO_CHAN_INFO_SCALE
>>> - Four output data rates (10, 50, 100, 200 Hz), selectable via
>>> IIO_CHAN_INFO_SAMP_FREQ
>>> - Four oversampling ratios (1, 2, 4, 8), selectable via
>>> IIO_CHAN_INFO_OVERSAMPLING_RATIO
>>
>> I'm suspicious about this one based on a very quick read of the datasheet.
>> Conventional oversampling would involve running the internal sampling
>> engine at a multiple of the sampling frequency, and then averaging the
>> results. The datasheet describes this as:
>> "Over sample Rate (OSR1) registers are used to control bandwidth of an
>> internal digital filter. Larger OSR value leads to smaller filter bandwidth,
>> less in-band noise and higher power consumption. It could be used to reach a
>> good balance between noise and power. Four over sample ratios can be selected,
>> 8,4,2 or 1."
>>
>> That sounds like a boxcar filter to me not oversampling (which would be
>> a combination of box car and reducing the output data rate).
>>
>> If possible, can you enable the data ready output and put a scope on it
>> to see if that changes frequency when OSR or OSR2 are modified.
>>
>> Trickier to do would be looking at the noise levels whilst playing with
>> these filters and see if they at least match with standard filter types.
>>
>> If we can't figure these out, then it may be a case of picking something
>> that works well and hard coding that rather than letting userspace
>> change things in a fashion that might not match the ABI.
>
> I could not arrange a oscilloscope. So wrote a small standalone tool that drives
> the chip directly over /dev/i2c-N (skipping the driver) on a Pi 4 and
> characterised all three fields in CTRL_1 with timing + noise measurements.
> Summary:
>
> Field Bits Role per measurement
> ODR [3:2] output rate, accurate to ~3% of nominal in Normal Mode
> OSR1 [5:4] low-pass filter (bandwidth control), no rate change
> OSR2 [7:6] N-sample averaging, no rate change (real oversampling)
>
> 1. DRDY interval vs each field (median of 500 samples per setting,
> ms; SCHED_FIFO, pinned CPU):
>
> vary ODR (OSR1=1, OSR2=1):
> ODR=10 Hz -> 100.9 ms ODR=100 Hz -> 10.30 ms
> ODR=50 Hz -> 20.5 ms ODR=200 Hz -> 5.14 ms
>
> vary OSR1 (ODR=200 Hz, OSR2=1):
> OSR1=1 -> 5.142 ms OSR1=4 -> 5.139 ms
> OSR1=2 -> 5.143 ms OSR1=8 -> 5.141 ms
>
> vary OSR2 (ODR=200 Hz, OSR1=1):
> OSR2=1 -> 5.142 ms OSR2=4 -> 5.142 ms
> OSR2=2 -> 5.140 ms OSR2=8 -> 5.141 ms
>
> ODR is the only knob that changes the rate.
>
> 2. Per-axis noise stddev (2000 samples, sensor still, ratios vs N=1):
>
> vary OSR1: vary OSR2:
> OSR1=1 -> 1.000 OSR2=1 -> 1.000
> OSR1=2 -> 0.723 OSR2=2 -> 0.706
> OSR1=4 -> 0.544 OSR2=4 -> 0.494
> OSR1=8 -> 0.416 OSR2=8 -> 0.333
>
> Reference for N-sample averaging (1/sqrt(N)):
> 1.000 / 0.707 / 0.500 / 0.354
>
> OSR2 matches within measurement noise; OSR1 trails the curve,
> which is the expected signature of a bandwidth-limiting filter
> on non-white input noise.
>
> So to my understanding, OSR2 is true oversampling. OSR1 is a filter.
The datasheet says that power consumption changes based on OSR1,
so that makes me think that OSR1 is really oversampling since it
would take more energy to do more conversions in the same amount
of time. (Note 1 on table 2.1)
Another chip by the same mfg that looks like it has the same OSR1/
OSR2 has a bit more info in the same table [1]. It looks like OSR2
affects the sensitivity (and calls OSR2 a low pass filter instead
of "down sampling rate").
[1] https://www.qstcorp.com/upload/pdf/202601/CF382A94E1424763B3DE87DC967757FC.pdf
>
> v4 plan, pending your input on (c):
>
> (a) Add an OSR2 regmap_field and expose it as
> IIO_CHAN_INFO_OVERSAMPLING_RATIO with values {1, 2, 4, 8}.
> (b) Drop the OVERSAMPLING_RATIO mapping on OSR1.
> (c) For OSR1, two options:
> i) expose as IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY.
> The public datasheet does not give cutoff frequencies,
> so I'd have to either characterise them empirically and
> document them in the driver, or use placeholder values
> derived from the OSR1 setting alone (which feels wrong).
> ii) hard-code OSR1=1 in chip_init and not expose it. Loses
> the noise-vs-bandwidth tradeoff but keeps the ABI clean.
> Slight preference for (ii) unless you'd rather see the filter
> knob exposed.
It sounds like we already figured out that OSR2 is just a moving average
so might as well implement IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY.
> (d) Keep ODR mapped to IIO_CHAN_INFO_SAMP_FREQ as in v3.
>
> One other finding worth mentioning: the datasheet's setup examples
> (sec 7.1, 7.2, 7.3) all write 0x06 to register 0x29 before entering
> an active mode, with the comment "Define the sign for X Y and Z axis".
> This register is not listed in the documented register map but the
> write is required for axes Y and Z to come out with the documented
> orientation. v4 will add this write to chip_init().
>
> If you have other measurements you'd like to see before v4, happy to
> run them.
>
>>
>>> - Runtime PM with a 2 s autosuspend delay
>>> - System suspend/resume delegated to the runtime callbacks
>
> Best regards,
> Hardik
On Sun, 17 May 2026 15:28:22 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 5/17/26 2:17 PM, Hardik Phalet wrote:
> > On Mon Apr 20, 2026 at 7:15 PM IST, Jonathan Cameron wrote:
> >> On Sun, 19 Apr 2026 22:32:09 +0000
> >> Hardik Phalet <hardik.phalet@pm.me> wrote:
> >>
> >>> This series adds an IIO driver for the QST QMC5883P, a 3-axis
> >>> anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC,
> >>> communicating over I2C. To my knowledge there is no existing
> >>> upstream driver for this device (see "Prior-art register-map check"
> >>> below).
> >>>
> >>> The driver supports:
> >>> - Raw magnetic field readings on X, Y and Z axes
> >>> - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G),
> >>> selectable via IIO_CHAN_INFO_SCALE
> >>> - Four output data rates (10, 50, 100, 200 Hz), selectable via
> >>> IIO_CHAN_INFO_SAMP_FREQ
> >>> - Four oversampling ratios (1, 2, 4, 8), selectable via
> >>> IIO_CHAN_INFO_OVERSAMPLING_RATIO
> >>
> >> I'm suspicious about this one based on a very quick read of the datasheet.
> >> Conventional oversampling would involve running the internal sampling
> >> engine at a multiple of the sampling frequency, and then averaging the
> >> results. The datasheet describes this as:
> >> "Over sample Rate (OSR1) registers are used to control bandwidth of an
> >> internal digital filter. Larger OSR value leads to smaller filter bandwidth,
> >> less in-band noise and higher power consumption. It could be used to reach a
> >> good balance between noise and power. Four over sample ratios can be selected,
> >> 8,4,2 or 1."
> >>
> >> That sounds like a boxcar filter to me not oversampling (which would be
> >> a combination of box car and reducing the output data rate).
> >>
> >> If possible, can you enable the data ready output and put a scope on it
> >> to see if that changes frequency when OSR or OSR2 are modified.
> >>
> >> Trickier to do would be looking at the noise levels whilst playing with
> >> these filters and see if they at least match with standard filter types.
> >>
> >> If we can't figure these out, then it may be a case of picking something
> >> that works well and hard coding that rather than letting userspace
> >> change things in a fashion that might not match the ABI.
> >
> > I could not arrange a oscilloscope. So wrote a small standalone tool that drives
> > the chip directly over /dev/i2c-N (skipping the driver) on a Pi 4 and
> > characterised all three fields in CTRL_1 with timing + noise measurements.
> > Summary:
> >
> > Field Bits Role per measurement
> > ODR [3:2] output rate, accurate to ~3% of nominal in Normal Mode
> > OSR1 [5:4] low-pass filter (bandwidth control), no rate change
> > OSR2 [7:6] N-sample averaging, no rate change (real oversampling)
> >
> > 1. DRDY interval vs each field (median of 500 samples per setting,
> > ms; SCHED_FIFO, pinned CPU):
> >
> > vary ODR (OSR1=1, OSR2=1):
> > ODR=10 Hz -> 100.9 ms ODR=100 Hz -> 10.30 ms
> > ODR=50 Hz -> 20.5 ms ODR=200 Hz -> 5.14 ms
> >
> > vary OSR1 (ODR=200 Hz, OSR2=1):
> > OSR1=1 -> 5.142 ms OSR1=4 -> 5.139 ms
> > OSR1=2 -> 5.143 ms OSR1=8 -> 5.141 ms
> >
> > vary OSR2 (ODR=200 Hz, OSR1=1):
> > OSR2=1 -> 5.142 ms OSR2=4 -> 5.142 ms
> > OSR2=2 -> 5.140 ms OSR2=8 -> 5.141 ms
> >
> > ODR is the only knob that changes the rate.
> >
> > 2. Per-axis noise stddev (2000 samples, sensor still, ratios vs N=1):
> >
> > vary OSR1: vary OSR2:
> > OSR1=1 -> 1.000 OSR2=1 -> 1.000
> > OSR1=2 -> 0.723 OSR2=2 -> 0.706
> > OSR1=4 -> 0.544 OSR2=4 -> 0.494
> > OSR1=8 -> 0.416 OSR2=8 -> 0.333
> >
> > Reference for N-sample averaging (1/sqrt(N)):
> > 1.000 / 0.707 / 0.500 / 0.354
> >
> > OSR2 matches within measurement noise; OSR1 trails the curve,
> > which is the expected signature of a bandwidth-limiting filter
> > on non-white input noise.
Nice data.
> >
> > So to my understanding, OSR2 is true oversampling. OSR1 is a filter.
Oversampling might also be tied to an integration period which would
reduce as oversampling goes up, somewhat undoing the noise improvements.
So it might be worse than an averaging. Without introducing
some dynamic motion it's going to be hard to figure this out. Maybe stick
the thing on a pendulum or if you happen to have one a record deck?
(assuming you can wire it!)
Anything to be able to repeat something that will vary the signal.
That should let you separate box car across a fixed sampling rate
from oversampling.
>
> The datasheet says that power consumption changes based on OSR1,
> so that makes me think that OSR1 is really oversampling since it
> would take more energy to do more conversions in the same amount
> of time. (Note 1 on table 2.1)
Is it possible it's a sampling / resampling ADC or similar so the
actual resolution is dropping? That saves some power. Mind you
puttting that in a place called OSR1 would be odd ;)
Or as you suggest it's actually triggering burst conversion and averaging.
So datarate fixed but actual sampling time is much lower so it can
do lots of samples without the effective sampling rate changing.
>
> Another chip by the same mfg that looks like it has the same OSR1/
> OSR2 has a bit more info in the same table [1]. It looks like OSR2
> affects the sensitivity (and calls OSR2 a low pass filter instead
> of "down sampling rate").
>
> [1] https://www.qstcorp.com/upload/pdf/202601/CF382A94E1424763B3DE87DC967757FC.pdf
Ah ok. Good detective work. Though it would be far from the first datasheet
to confuse oversampling with straight forward filtering.
>
> >
> > v4 plan, pending your input on (c):
> >
> > (a) Add an OSR2 regmap_field and expose it as
> > IIO_CHAN_INFO_OVERSAMPLING_RATIO with values {1, 2, 4, 8}.
> > (b) Drop the OVERSAMPLING_RATIO mapping on OSR1.
> > (c) For OSR1, two options:
> > i) expose as IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY.
> > The public datasheet does not give cutoff frequencies,
> > so I'd have to either characterise them empirically and
> > document them in the driver, or use placeholder values
> > derived from the OSR1 setting alone (which feels wrong).
> > ii) hard-code OSR1=1 in chip_init and not expose it. Loses
> > the noise-vs-bandwidth tradeoff but keeps the ABI clean.
> > Slight preference for (ii) unless you'd rather see the filter
> > knob exposed.
>
> It sounds like we already figured out that OSR2 is just a moving average
> so might as well implement IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY.
Maybe... We might have to just go for best guess and document it clearly.
Jonathan
>
> > (d) Keep ODR mapped to IIO_CHAN_INFO_SAMP_FREQ as in v3.
> >
> > One other finding worth mentioning: the datasheet's setup examples
> > (sec 7.1, 7.2, 7.3) all write 0x06 to register 0x29 before entering
> > an active mode, with the comment "Define the sign for X Y and Z axis".
> > This register is not listed in the documented register map but the
> > write is required for axes Y and Z to come out with the documented
> > orientation. v4 will add this write to chip_init().
> >
> > If you have other measurements you'd like to see before v4, happy to
> > run them.
> >
> >>
> >>> - Runtime PM with a 2 s autosuspend delay
> >>> - System suspend/resume delegated to the runtime callbacks
> >
> > Best regards,
> > Hardik
>
>
On Sun, Apr 19, 2026 at 10:32:09PM +0000, Hardik Phalet wrote: > This series adds an IIO driver for the QST QMC5883P, a 3-axis > anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC, > communicating over I2C. To my knowledge there is no existing > upstream driver for this device (see "Prior-art register-map check" > below). > > The driver supports: > - Raw magnetic field readings on X, Y and Z axes > - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G), > selectable via IIO_CHAN_INFO_SCALE > - Four output data rates (10, 50, 100, 200 Hz), selectable via > IIO_CHAN_INFO_SAMP_FREQ > - Four oversampling ratios (1, 2, 4, 8), selectable via > IIO_CHAN_INFO_OVERSAMPLING_RATIO > - Runtime PM with a 2 s autosuspend delay > - System suspend/resume delegated to the runtime callbacks > > Regmap with an rbtree cache is used throughout. CTRL_1 and CTRL_2 I'm not sure this paragraph answers the question "why not maple tree?" > bit fields are accessed via regmap_field to avoid read-modify-write > races. The STATUS register is marked precious so regmap never reads > it speculatively and clears the DRDY/OVFL bits unexpectedly. > > The probe-time init sequence is: soft reset, wait 300 us for POR > to complete, deassert reset, drop the register cache so subsequent > RMW writes read fresh values, then enter normal mode. 300 us > comfortably covers the 250 us POR time given in the datasheet. > > Patches: > 1/5 - dt-bindings: vendor-prefixes: Add QST Corporation > 2/5 - dt-bindings: iio: magnetometer: QSTCORP QMC5883P > 3/5 - iio: magnetometer: add driver for QST QMC5883P > 4/5 - iio: magnetometer: qmc5883p: add oversampling ratio support > 5/5 - iio: magnetometer: qmc5883p: add PM support > > Patches 4 and 5 are split out from the main driver so that the core > (1-3) can be reviewed and picked independently, per review feedback > on v2. 4/5 exposes the CTRL_1 OSR field via > IIO_CHAN_INFO_OVERSAMPLING_RATIO. 5/5 adds runtime PM that puts the > chip into MODE_SUSPEND when idle and wakes it to MODE_NORMAL on > demand. -- With Best Regards, Andy Shevchenko
© 2016 - 2026 Red Hat, Inc.