[PATCH 1/4] iio: pressure: dps310: fix overflow in the pressure compensation

From: Paulius Zaleckas

Date: Mon Oct 12 2026 - 07:59:36 EST


From: Paulius Zaleckas <paulius.zaleckas@xxxxxxxxx>

dps310_calculate_pressure() sums the compensation polynomial as
p^n * c / kP^n fractions in s64. The raw pressure scales with kP, which
goes up to 7864320, so p^3 * c30 overflows at most oversampling rates
near sea level, kP^3 wraps negative at 4x and 8x, and the remainders
overflow when scaled by 10^9. The c30 term is then lost: at about
1006 hPa the pressure reads about 20 Pa low at oversampling 2, 4, 8, 64
and 128, and hundreds of Pa off towards the ends of the 300-1200 hPa
range.

Evaluate the polynomial in Q20 fixed point instead. The linear c10 and
c01 terms are still divided exactly, the higher order terms use the
scaled raw values. Over 300-1200 hPa and every oversampling rate the
result is within 1 Pa of the floating point formula.

Tested on a register compatible Zilltek ZTS8032 (support added later in
this series) at about 1007 hPa, against the datasheet formula evaluated
from the raw and coefficient registers. A vendor driver with the same
algebra read 13-20 Pa low at oversampling 2, 4, 8, 64 and 128; with this
series every rate is within 1 Pa. Not tested on a DPS310.

Fixes: d711a3c7dc82 ("iio: dps310: Add pressure sensing capability")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Paulius Zaleckas <paulius.zaleckas@xxxxxxxxx>
---
drivers/iio/pressure/dps310.c | 83 ++++++++++++++---------------------
1 file changed, 34 insertions(+), 49 deletions(-)

diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index 85df58ac1809..3bc690cc8609 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -660,71 +660,56 @@ static int dps310_write_raw(struct iio_dev *iio,
}
}

+/* Fraction bits of the fixed point pressure compensation */
+#define DPS310_Q 20
+#define DPS310_Q_ONE (1LL << DPS310_Q)
+
+static s64 dps310_mul_q(s64 a, s64 b)
+{
+ return (a * b) >> DPS310_Q;
+}
+
static int dps310_calculate_pressure(struct dps310_data *data, int *val)
__must_hold(&data->lock)
{
- int i;
int rc;
- int kpi;
- int kti;
- s64 rem = 0ULL;
- s64 pressure = 0ULL;
+ int kp;
+ int kt;
+ s64 pressure;
s64 p;
s64 t;
- s64 denoms[7];
- s64 nums[7];
- s64 rems[7];
- s64 kp;
- s64 kt;
+ s64 ps;
+ s64 ts;
+ s64 x2;
+ s64 x3;

- rc = dps310_get_pres_k(data, &kpi);
+ rc = dps310_get_pres_k(data, &kp);
if (rc)
return rc;

- rc = dps310_get_temp_k(data, &kti);
+ rc = dps310_get_temp_k(data, &kt);
if (rc)
return rc;

- kp = (s64)kpi;
- kt = (s64)kti;
-
p = (s64)data->pressure_raw;
t = (s64)data->temp_raw;

- /* Section 4.9.1 of the DPS310 spec; algebra'd to avoid underflow */
- nums[0] = (s64)data->c00;
- denoms[0] = 1LL;
- nums[1] = p * (s64)data->c10;
- denoms[1] = kp;
- nums[2] = p * p * (s64)data->c20;
- denoms[2] = kp * kp;
- nums[3] = p * p * p * (s64)data->c30;
- denoms[3] = kp * kp * kp;
- nums[4] = t * (s64)data->c01;
- denoms[4] = kt;
- nums[5] = t * p * (s64)data->c11;
- denoms[5] = kp * kt;
- nums[6] = t * p * p * (s64)data->c21;
- denoms[6] = kp * kp * kt;
-
- /* Kernel lacks a div64_s64_rem function; denoms are all positive */
- for (i = 0; i < 7; ++i) {
- u64 irem;
-
- if (nums[i] < 0LL) {
- pressure -= div64_u64_rem(-nums[i], denoms[i], &irem);
- rems[i] = -irem;
- } else {
- pressure += div64_u64_rem(nums[i], denoms[i], &irem);
- rems[i] = (s64)irem;
- }
- }
-
- /* Increase precision and calculate the remainder sum */
- for (i = 0; i < 7; ++i)
- rem += div64_s64((s64)rems[i] * 1000000000LL, denoms[i]);
-
- pressure += div_s64(rem, 1000000000LL);
+ /*
+ * Section 4.9.1 of the DPS310 spec in fixed point. Dividing p^3 * c30
+ * by kP^3 directly overflows s64 at most oversampling rates.
+ */
+ ps = div_s64(p * DPS310_Q_ONE, kp);
+ ts = div_s64(t * DPS310_Q_ONE, kt);
+ x2 = dps310_mul_q(ps, ps);
+ x3 = dps310_mul_q(x2, ps);
+
+ pressure = data->c00 * DPS310_Q_ONE;
+ pressure += div_s64(p * data->c10 * DPS310_Q_ONE, kp);
+ pressure += div_s64(t * data->c01 * DPS310_Q_ONE, kt);
+ pressure += dps310_mul_q(data->c11 * ps, ts);
+ pressure += data->c20 * x2 + data->c30 * x3;
+ pressure += dps310_mul_q(data->c21 * x2, ts);
+ pressure >>= DPS310_Q;
if (pressure < 0LL)
return -ERANGE;

--
2.55.0