From nobody Fri Jun 12 14:19:20 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B24673BB11D for ; Thu, 14 May 2026 11:36:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778758615; cv=none; b=cYK6+m+/iy53LR7vbZmITbYYiBKzdiHCeN7tf4ofvKgWT25BC2F1xumEUQkzlWUepAXfJ+twyCv5I8fxrD1Iq2O59ZTFZvkiguMcri/KExWjv2jGaWZ3NtNm33nrrJdAQ7AqPH2iraEM2nPQWao42+vqLbGFO4rVCzDbLEAykZA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778758615; c=relaxed/simple; bh=DG5RTzkKQb9dNd7h3PMz3Uho11h1YkmAiGNLFN6vKD0=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=NSB1rsweuq0WrGV7TWMh9k5RaEcqIfx40f8T0bJGq9MpFGNQ9ASNvlWA59gxpliYItbNnJmVr1WV1h4bh9hQIo6xI/ytRt6OFKI/eno4+ttipv1nxRN1Ve7tn0IQhe8l3eekfE8wUcqgINfEEiUc0f22qz8pWa9Hw0PwJaWKJb8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=WEGaNaHB; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=AGvDW5wV; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="WEGaNaHB"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="AGvDW5wV" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 64E9k7oH2592239 for ; Thu, 14 May 2026 11:36:52 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:message-id:mime-version :subject:to; s=qcppdkim1; bh=qN16thgwwCwB2Rdns6XBfhr/Ra0hBXWE7M0 kvmpsYQA=; b=WEGaNaHBFTPS1V9JNwpy/ptJJBgh+DuoaNWj6RbM8WdIIeRz+uM 4Jtnz7ddnGcDbP+R+qdm1lmGSxl0ugNxDW5ruw6Hzez970JTlHvLal2JcJYAXMFU j5kJmCzguULAWfBlQsKFMSIOcvVcHKZbfk0LSCub663cUPKEUF1pRpbtee8O78Tw SrqekMkyKz8cjjvezzZBp57GE3TjMOgLsPouGErMHXig3MwOXVcKrwWikYsT9i86 e2Sfhbj97qUkgDdII6I2WPXHwg2SYTepUXW7Y8AXQbpXjaY+GltILRjw+Zj+qw31 TKbwygNng+6tGL9caTixTIPsgYz7NAktTjg== Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4e5899s703-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Thu, 14 May 2026 11:36:52 +0000 (GMT) Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2b2e8bba2e6so102759745ad.1 for ; Thu, 14 May 2026 04:36:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1778758611; x=1779363411; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=qN16thgwwCwB2Rdns6XBfhr/Ra0hBXWE7M0kvmpsYQA=; b=AGvDW5wVPyBlpe5D9mpFnYOhMDMPFzkrc7t130paCXkqUmBYDvFkOi8ehTSFjU6IR8 rHkS8XAkZSGJMss4rQS25yP7gfvSIXUA1L9B38GTiJn46kFpp7dEBiSTb+LehGt1wBkQ vJ6RwZYTv7d4WowXT/3S0GtvBAVRx/IWsAdKytUyL8WIGxxeAhdow8u+qpQX3bfpqOJQ oTGs0CvbYzI3yPJpndDTzX9lMvQydHHMrGsL2sQbQIdIHSoOYb8uy4BUpMwVvT8YsEeN IHnxinsTrrxY3ATYCSmgcziuplbRBn7QRsxO9SQd6rCzPpSBuhPFnSPdCPT2sMmQezRA 80oA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778758611; x=1779363411; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=qN16thgwwCwB2Rdns6XBfhr/Ra0hBXWE7M0kvmpsYQA=; b=T1lXs/moWA+FyFDli/ojIrT5Ea/WV3gridCKrzROX/kbOa8nEVmDiuOPjkdYoCyep+ PHHmJJEqknCcGdtwAJhcADBddVwQIV5ou9sZjW6PhGMyQQ61XzcLHsa752XjfhsxVJ15 MPGIIyIwa8fWr/NoiaPf2gSF0fV7AEJbeF33RVJi++cNSM7oYCqbwoNwPSCNCgp7Thwx L7h/wY9ZC7X7ha0hswT1x/ElSgoqiMI0WYa0xveFnjwvsdFGstAUruti1KCp3pWLz1ru af5pmUKad2sr+NpPB5dUJ3nxIsiMKwVI5S8e94rfk23o0mM9fRZTmMmsSwzbb7GixwC8 f5lQ== X-Forwarded-Encrypted: i=1; AFNElJ/bNN05haQh5AcZuPjYtPVDOSOCCngD/4IQxZadrswWp9ZoVvm0AMY4K/V6Ju1nhQnAMBghXY/+cwsvDTU=@vger.kernel.org X-Gm-Message-State: AOJu0YxnbnUGb8zhR4rho4pSNICYugu42QAETYy9Sk0BOOFBBDy7UPHq UBgSOyJQj1nHvFyre3UgzNyN0Zdw3HwXWcTOm/TkF98Az6aAf9Z4i0+st51BUVeKEeorkQJRnHG xM9Eha6CysZjTA0eiqhuKxgT0KSwKZ3b107wvnk1FRVGweHwyoSwB0NoXbikAtV8xRAY= X-Gm-Gg: Acq92OFWMzeoVegDAWpmLIgaus0eeqs55MqpvZkRH5LKVhng8EJTcETLGNHrWZlSvQD Tr6S4XtEfFBlNwD1qg04wRhZ2JlEyvHqqLlI1S79O6xs0H+POAn39xrU/D03ArJHhkRoe8p7zU+ 1pcFkImacHN3XyoVxyxFiylJGl5FP+UeStTbgpNdjGUUXaDiphP1Jv2Z9YrY5+/0rqzkt0jvdsM f1DgFzpsMwGrPoUdkri89F8tVBUbFFwvg3N3wzC/vanE0XevQYCsQJTL8rPH7CEVrf5BFtyW8cf 2W7lW+t32jBzXMxFXWIruCPKYPgL6z0lpS1RMAreR1j3nFTrP2YCVXfo1anziko67jTQ99BJ4xv 3lB0y56ndGlXzMp/BBCmafZHEBU4HMjjBNB8As6RyxB4JTqdJzsgynY0= X-Received: by 2002:a17:903:320d:b0:2b9:6458:1a2c with SMTP id d9443c01a7336-2bd272b4403mr78742205ad.13.1778758611341; Thu, 14 May 2026 04:36:51 -0700 (PDT) X-Received: by 2002:a17:903:320d:b0:2b9:6458:1a2c with SMTP id d9443c01a7336-2bd272b4403mr78741775ad.13.1778758610675; Thu, 14 May 2026 04:36:50 -0700 (PDT) Received: from hu-priyjain-hyd.qualcomm.com ([202.46.22.19]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bd5c05f27csm21756845ad.25.2026.05.14.04.36.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 May 2026 04:36:50 -0700 (PDT) From: Priyansh Jain To: Amit Kucheria , Thara Gopinath , "Rafael J . Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-pm@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, manaf.pallikunhi@oss.qualcomm.com, Priyansh Jain , Konrad Dybcio Subject: [PATCH v5] thermal: qcom: tsens: atomic temperature read with hardware-guided retries Date: Thu, 14 May 2026 17:06:43 +0530 Message-ID: <20260514113643.1954111-1-priyansh.jain@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNTE0MDExNiBTYWx0ZWRfX4rgRwMCvWuwN NXNNyVAPlBst+yBDR9doVlLi7J66Bli8bnJQmQTpNGHcRQhXbolrINj+peNz/d6VKSP/09obzA0 FIy7iC1/1s20/pBuwmlaXhDeLkLDaOkGvCuwNAzHDGQMegnybgp91g0kytARTbXcE9BRvLr04Q5 rqHlzjDdY+4YjpsTYEQVsvZQWp9SWGoqIQyG86Rp+ognXgrvO6GDUJwWsepi82qMu6aOj7TEnnp J7GpH5feHzvc+hLGDm+bLs46du7E+dAbq8Pc96wt9ZedGJaPDQ+tUbQGTvuZX7BSf9uMBI5Rqnz ww9KVHXQ7Ik8Kh6jePn7K9JrJmOxHOX7UQCLdu4t7iWA4/qhovUWQuifQoLYOZ8fIyOoiyNJSPy t0m8e4lNVbXfbHgIWOXchKkIXwctHlypFtU5kWZY219JbUTRumrm3XCISKks7VCExoRzhEs6Uu6 d2Rjr5jkJnAMWpcEGwQ== X-Proofpoint-ORIG-GUID: C3JAw58j5-hZMtiNbs-YhZErWG2pkzm9 X-Authority-Analysis: v=2.4 cv=N6AZ0W9B c=1 sm=1 tr=0 ts=6a05b3d4 cx=c_pps a=cmESyDAEBpBGqyK7t0alAg==:117 a=fChuTYTh2wq5r3m49p7fHw==:17 a=NGcC8JguVDcA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=wJ5UwyVIi5aKMrvtPbQA:9 a=1OuFwYUASf3TG4hYMiVC:22 X-Proofpoint-GUID: C3JAw58j5-hZMtiNbs-YhZErWG2pkzm9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-05-14_02,2026-05-13_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 priorityscore=1501 bulkscore=0 malwarescore=0 clxscore=1015 impostorscore=0 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2605050000 definitions=main-2605140116 Content-Type: text/plain; charset="utf-8" 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 yields 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;if neither pair matches, -EAGAIN 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 Reviewed-by: Konrad Dybcio --- Changes in v5: - Reformat multi-line comment to follow kernel style - Add spacing for improved readability - Minor code cleanup and variable declaration adjustment - Link in v4: https://lore.kernel.org/all/20260513054412.911048-1-priyansh.= jain@oss.qualcomm.com/ Changes in v4: - Fix indentation issue based on review feedback - Link to v3: https://lore.kernel.org/all/15e8fb71-1eb0-4440-a8cf-26f19f66b= 2bb@oss.qualcomm.com/ Changes in v3: - Remove valid_bit, last_temp_mask, and last_temp_resolution fields from struct tsens_features in tsens.h - Compute last_temp_mask, resolution, and valid_bit on-the-fly using regmap field definitions - Remove field initializations from all platform data files (tsens-v0_1.c, tsens-v1.c, tsens-v2.c) - Remove the initialization line in init_common() that was computing last_temp_mask - Link to v2: https://lore.kernel.org/all/6e0081a3-a28b-49e5-825d-405cc1572= 54f@oss.qualcomm.com/ Changes in v2: - Reverted merging of the valid-bit and LAST_TEMP register field logic to preserve the regmap differences between TSENS versions - Defined valid-bit support and last temperature resolution for all TSENS v1 and v2 feature structures - Defined last temperature resolution for Tsens v0 feature structure - Dropped tsens version checks in favor of valid-bit capability - Computed masks from resolution to keep a single source of truth - Minor code cleanups based on review feedback - Link to v1: https://lore.kernel.org/all/c07fd488-a455-413f-b25f-9f9f1afda= 097@oss.qualcomm.com/ --- drivers/thermal/qcom/tsens.c | 111 ++++++++++++++++++++++++----------- drivers/thermal/qcom/tsens.h | 1 + 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index a2422ebee816..40f27c1a1cd5 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -316,9 +316,66 @@ static inline int code_to_degc(u32 adc_code, const str= uct tsens_sensor *s) } =20 /** - * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * tsens_read_temp - 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 *t= emp) +{ + struct tsens_priv *priv =3D s->priv; + int temp_val[MAX_READ_RETRY] =3D {0}; + u32 status; + int ret; + u32 last_temp_mask =3D GENMASK(priv->fields[LAST_TEMP_0].msb, + priv->fields[LAST_TEMP_0].lsb); + u32 valid_bit =3D priv->rf[VALID_0] ? BIT(priv->fields[VALID_0].lsb) : 0; + + for (int i =3D 0; i < MAX_READ_RETRY; i++) { + ret =3D regmap_read(priv->tm_map, priv->fields[field].reg, &status); + if (ret) + return ret; + + /* VER_0 doesn't have a VALID bit */ + if (!valid_bit) { + *temp =3D status & last_temp_mask; + return 0; + } + + temp_val[i] =3D status & last_temp_mask; + + if (status & valid_bit) { + *temp =3D temp_val[i]; + return 0; + } + } + + /* + * As per the HW guidelines, 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. + */ + if (temp_val[0] =3D=3D temp_val[1]) + *temp =3D temp_val[1]; + else if (temp_val[1] =3D=3D temp_val[2]) + *temp =3D temp_val[2]; + else + return -EAGAIN; + + return 0; +} + +/** + * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * @s: Pointer to sensor struct + * @temp: temperature in milliCelsius to be read from hardware * * This function handles temperature returned in ADC code or deciCelsius * depending on IP version. @@ -326,20 +383,14 @@ static inline int code_to_degc(u32 adc_code, const st= ruct tsens_sensor *s) * Return: Temperature in milliCelsius on success, a negative errno will * be returned in error cases */ -static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) +static int tsens_hw_to_mC(const struct tsens_sensor *s, int temp) { struct tsens_priv *priv =3D s->priv; u32 resolution; - u32 temp =3D 0; - int ret; =20 resolution =3D priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb; =20 - ret =3D regmap_field_read(priv->rf[field], &temp); - if (ret) - return ret; - /* Convert temperature from ADC code to milliCelsius */ if (priv->feat->adc) return code_to_degc(temp, s) * 1000; @@ -514,8 +565,10 @@ static int tsens_read_irq_state(struct tsens_priv *pri= v, u32 hw_id, &d->crit_irq_mask); if (ret) return ret; - - d->crit_thresh =3D tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); + ret =3D regmap_field_read(priv->rf[CRIT_THRESH_0 + hw_id], &d->crit_thre= sh); + if (ret) + return ret; + d->crit_thresh =3D tsens_hw_to_mC(s, d->crit_thresh); } else { /* No mask register on older TSENS */ d->up_irq_mask =3D 0; @@ -525,8 +578,16 @@ static int tsens_read_irq_state(struct tsens_priv *pri= v, u32 hw_id, d->crit_thresh =3D 0; } =20 - d->up_thresh =3D tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); - d->low_thresh =3D tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); + ret =3D regmap_field_read(priv->rf[UP_THRESH_0 + hw_id], &d->up_thresh); + if (ret) + return ret; + + d->up_thresh =3D tsens_hw_to_mC(s, d->up_thresh); + ret =3D regmap_field_read(priv->rf[LOW_THRESH_0 + hw_id], &d->low_thresh); + if (ret) + return ret; + + d->low_thresh =3D tsens_hw_to_mC(s, d->low_thresh); =20 dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u= |%u|%u)\n", hw_id, __func__, @@ -750,33 +811,15 @@ static void tsens_disable_irq(struct tsens_priv *priv) =20 int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) { - struct tsens_priv *priv =3D s->priv; int hw_id =3D s->hw_id; u32 temp_idx =3D LAST_TEMP_0 + hw_id; - u32 valid_idx =3D VALID_0 + hw_id; - u32 valid; int ret; =20 - /* VER_0 doesn't have VALID bit */ - if (tsens_version(priv) =3D=3D VER_0) - goto get_temp; - - /* Valid bit is 0 for 6 AHB clock cycles. - * At 19.2MHz, 1 AHB clock is ~60ns. - * We should enter this loop very, very rarely. - * Wait 1 us since it's the min of poll_timeout macro. - * Old value was 400 ns. - */ - ret =3D regmap_field_read_poll_timeout(priv->rf[valid_idx], valid, - valid, 1, 20 * USEC_PER_MSEC); - if (ret) - return ret; - -get_temp: - /* Valid bit is set, OK to read the temperature */ - *temp =3D tsens_hw_to_mC(s, temp_idx); + ret =3D tsens_read_temp(s, temp_idx, temp); + if (!ret) + *temp =3D tsens_hw_to_mC(s, *temp); =20 - return 0; + return ret; } =20 int get_temp_common(const struct tsens_sensor *s, int *temp) diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 2a7afa4c899b..ab57ad88c3f7 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -21,6 +21,7 @@ #define THRESHOLD_MIN_ADC_CODE 0x0 =20 #define MAX_SENSORS 16 +#define MAX_READ_RETRY 3 =20 #include #include --=20 2.43.0