Re: [PATCH 1/2] thermal: qcom: tsens: atomic temperature read with hardware-guided retries

Konrad Dybcio
2026-04-30T16:00:43Z

On 4/30/26 7:44 AM, Priyansh Jain wrote:
> The existing TSENS temperature read logic polls the valid bit and then
> reads the temperature register. When temperature reads are triggered
> at very short intervals, this can race with hardware updates and allow
> the temperature field to be read while it is still being updated.
>
> In this case, the valid bit may already be asserted even though the
> temperature value is transitioning, resulting in an incorrect reading.
>
> Hardware programming guidelines require the temperature value and the
> valid bit to be sampled atomically in the same read transaction. A
> reading is considered valid only if the valid bit is observed set in
> that same sample.
>
> The guidelines further specify that software should attempt the
> temperature read up to three times to account for transient update
> windows. If none of the attempts observe a valid sample, a stable
> fallback value must be returned: if the first and second samples match,
> the second value is returned; otherwise, if the second and third
> samples match, the third value is returned.
>
> Update the TSENS sensor read logic to implement atomic sampling along
> with the recommended retry-and-compare fallback behavior. This removes
> the race window and ensures deterministic temperature values in
> accordance with hardware requirements.
>
> Signed-off-by: Priyansh Jain <priyansh.jain@oss.qualcomm.com>
> ---

[...]

> +/**
> + * tsens_read_temp - To read temperature from hw in deciCelsius.

"Retrieve temperature readings from the hardware"

> + * @s: Pointer to sensor struct
> + * @field: Index into regmap_field array pointing to temperature data
> + * @temp: temperature in deciCelsius to be read from hardware
> + *
> + * This function handles temperature returned in ADC code or deciCelsius
> + * depending on IP version.
> + *
> + * Return: 0 on success, a negative errno will be returned in error cases
> + */
> +static int tsens_read_temp(const struct tsens_sensor *s, int field, int *temp)
> +{
> + struct tsens_priv *priv = s->priv;
> + int temp_val[3] = {0};
> + unsigned int status = 0;

This should be a u32 to reflect this is a known-size register

> + int ret = 0, i;

You can declare the iterator inside the loop

'ret' doesn't need to be initialized, as it's immediately overwritten

> + int max_retry = 3;

This should be a constant, perhaps a #defined one, reused for the size
of temp_val

> +
> + ret = regmap_field_read(priv->rf[field], &status);
> + if (ret)
> + return ret;
> +
> + /* VER_0 doesn't have VALID bit */
> + if (tsens_version(priv) == VER_0) {
> + *temp = status;
> + return ret;

Ret will always be a '0' here (and in below cases)

> + }
> +
> + for (i = 0; i < max_retry; i++) {
> + temp_val[i] = status & priv->feat->last_temp_mask;
> + if (status & priv->feat->valid_bit) {
> + *temp = temp_val[i];
> + return ret;
> + }
> + ret = regmap_field_read(priv->rf[field], &status);

Please add a \n above for readability> + if (ret)
> + return ret;
> + }
> +
> + if (temp_val[0] == temp_val[1])
> + *temp = temp_val[1];
> + else if (temp_val[1] == temp_val[2])
> + *temp = temp_val[2];
> + else
> + return -EAGAIN;

This deserves a comment. Your description in the commit message is good,
but the reasoning behind this logic is not obvious and will get lost in
time, as 'git blame' will be bloated by cleanups

Konrad