From nobody Sat Jun 13 03:31:07 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 022722DA75C for ; Mon, 11 May 2026 07:10:34 +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=1778483436; cv=none; b=afSqlYhPwohzFvzp0wo0khlO0J94nn+fg5aZk07z1b7DBKwXXAwmHndFGQQ+12QiHQfoKhqGLTz/MDwf+KKTDpggMhzvEbcPj0ZxvnQA7ER+IuKhoIHMjt8y9nKtEitaCVrMe1XDoODFsN2p6FKrAS9AWYdICoXobiWu4eTcN+w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778483436; c=relaxed/simple; bh=VLVpg27Lyq+MGOrxKQpN5kYrH2wV5P36mETGnfpo6Ac=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=IyB1HC7f+LtSvma180AFIA5nTuEIrfK19CrYJlVRhQLFG0b0UGeSAM93i5zRjBT6/3Ki9XuPoKYZhXI3d34K+OTodJwx5RUKbjCsaT4PZfYqwtJFSn8q7UB0FR4gjXAj1nnOGGGrsCGCx+z7O2gfneSgazQbjyL3T/I6LCfBEIk= 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=UvC/Ma2v; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=cxHcGQmn; 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="UvC/Ma2v"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="cxHcGQmn" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 64B7A4VH1258491 for ; Mon, 11 May 2026 07:10:34 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=lshARZavRy/jl7k+sudxw/52HO8R0au0eyu 81zNJgbY=; b=UvC/Ma2vNb1YwDTqRT5EaOrjLLkHX32cAbmOmyzRvhHf2uveera KIyGtQpnFUCgr3O8UOiP6WFv0xjGhAJetWbI9HoAjkLxBZ8CD36MuLZcWENnCqs+ 8UpkYMPL62sLV2EO9ISjRmwjBp0tH9vVSXsAv3g/zFgZxwgux33aH8lo2+qS1xLs viBSfqpcGNFr2TjkCdouR96M8c4Md33QZNZ5b3uIICKPAA6o8HPdaaA4N7KL5OtA t5jZDgygHUpR4U+ZxDrgHjzV/5/mmEf5qwv0iR6n3E4YDiyTq4to+DOPrOZcMKYF zLYMnNm5DIiZfqa8w9QuRijnnJwn6HG1zBQ== Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4e3ag20020-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 11 May 2026 07:10:33 +0000 (GMT) Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-365fc4636bbso7828472a91.2 for ; Mon, 11 May 2026 00:10:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1778483433; x=1779088233; 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=lshARZavRy/jl7k+sudxw/52HO8R0au0eyu81zNJgbY=; b=cxHcGQmnDMKIRFiMNRVHOBtXPMfviYM87h9t5sFOwzlEh3cQv8NEl8bEkb2z8Poxfg o/p36bydxQ6GM92wRzBnMNJgBMQd5ADcbFUglGMebZtJs8bQOaiiyb+EJ8d59iaVikQd iJAoLAYwLPuqGGA4ZSUX4+mnfppF2ntEit25885/1l0NEWixhzNzXumF39kSyAC0VhFd NYUvdlFbmaUj7iN7W8yqLeGA5lxPG6ain8Q4u0QgixOQGSyfYe2uII6FNSYUKJ1bGweT J8Xvd93i+d5tpAtgbjXvFUzDAVtHycIy6mLUd7tbk+3lvuMftW9KRO1SvjpvB1gkFSVb nw7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778483433; x=1779088233; 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=lshARZavRy/jl7k+sudxw/52HO8R0au0eyu81zNJgbY=; b=Jv5fUfj2tMPMK6/WaKNmlWlWx5ASqqugdJRtNdcIhB1QRJKgXPx6A7dEmqK5z0epsR Qrji35vDTsdEiOl7dcXh8Yq2nZKe5lIEedya6mBY6+Jys9jAi1E4kM5wrmeh2a7BGG5u 0nEk1d98Szdgto8EMgmmvbRjxkD98GUWKxUi3rH3kkW4SO/hS0tEh1mR2XgmtFXaqa7c Av4IcqvoJfRx5ShuHySDtHEFCLoJKxugOID9FKDaNsjubJdDIjdGA8I9TtmQpcMdt/kE KIU1ie8VoiNpo8xFs/Hdhdsf8KmJXLPt4T2vb4NIg0UteF/JBKHC8zyHsS4R89XpU0vT ddNA== X-Forwarded-Encrypted: i=1; AFNElJ/bRRL5dRPOJrmIUqQ+tvS5g/S4jeipQFTZO05Cfp9hpaNe2jr3SZ2SJvDR8yC5Epw4IJppGp/0UiNyqkk=@vger.kernel.org X-Gm-Message-State: AOJu0Yw3b1v1UxFfmzsOqDiFZ0lqUBbSh9/t2Hzpno/GUuFgdA/7pcQo DiGPdaWzvMLIIyCnZgU1V8EIVugWAVl76jdT1M+DCInxhknWM0OMMaO9eeH4BBreQjjubIaLO33 gBvw4/XWT6busYy2hqo1C6aMz318mlVqakVTHNdcoMThoufikrwNC+KKypPyKPngQeUQ= X-Gm-Gg: Acq92OHZS0wtnf49bMekdY7p8i3bnJ6ZfxCHIyPUde4txlBbermYXlM+BCU90liHZWF vQFADWWWQT1UmY5nXPVmHGWsV2DrJFUIccI6LOj2ZHUmgqvymiazv3Gyco2Lq3e3H7xFSkRJLtK 9fBLuMPNOhMPy7mgYKRgUinc4mYSiIroUgXo6Qr2FL1zcD+WU1/RR4dQVvhonmXptbZ45tZtfW/ OcrKRCxUiBx9F0ZL1sesXZDlrwiUedrjl0GYRoYq53VRM0BxcyWJ/XUElmqlvqMhXRriEmhLnad HsvEVP28ap8dWd5vdO1S54h99YjPc825GHv4SRluPbf6ZNoJRq5UyF4AjQw+mTBNs1LzVR+VdTr P6O/pbCA7bmEM9uZUOz+bRnPoo8XsnGRtyhAQVtLgqb8HaMnVBl9zweA= X-Received: by 2002:a17:90b:3d4b:b0:368:341a:a925 with SMTP id 98e67ed59e1d1-368341ab057mr4403104a91.23.1778483432766; Mon, 11 May 2026 00:10:32 -0700 (PDT) X-Received: by 2002:a17:90b:3d4b:b0:368:341a:a925 with SMTP id 98e67ed59e1d1-368341ab057mr4403035a91.23.1778483432008; Mon, 11 May 2026 00:10:32 -0700 (PDT) Received: from hu-priyjain-hyd.qualcomm.com ([202.46.22.19]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-367d687bedesm8295828a91.16.2026.05.11.00.10.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 May 2026 00:10:31 -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 Subject: [PATCH v3] thermal: qcom: tsens: atomic temperature read with hardware-guided retries Date: Mon, 11 May 2026 12:40:24 +0530 Message-ID: <20260511071024.3130247-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-ORIG-GUID: uRMu80YPIU-K-Bf67_dyyIXOOc7P4RuA X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNTExMDA3NyBTYWx0ZWRfX5Eyctnygtsiz jZHG7ApKEsjw2oT2tts7/wDYyJHb045FIrO2vj/fA4yZt6uI4vizWfV3oX9KhNKpZrc24vSmAfQ jxUmtXg2od+2t+NYLRo+nfDLEPpuVxb/Ldak8aJjUQEhYLRxA9QQqbee2aml4gDkISsFkfZm3XB MPp82yKrQZEO5VnLvdFoRNGabnFf+Oy9TPPKoFxOC9DebG2CafZRlXBUSoXWUm0HxpN7bhzi1TA wg1ryhYG7Ii0sJ2AzWCbPlmjH2ugAvMRYdBqX6kAd+bzgnS/wKmMhnTpPUYOK58nw5Z6asmTQGT EuBfQNGW9TIVXTF+dlMWEDTJP2/lYNft0KmS50gDm0DYdL6Ho4xpang4vbLpJdYqnOx9JKgbJnQ EMd39C5ZTc6TMpYqLIc1C06G+LmqRLm8RcixIWeui28F/tFinzxAg/dHoM2J91upGEFmmGvixJW 1BTL5+yqxDnzb5bBslQ== X-Proofpoint-GUID: uRMu80YPIU-K-Bf67_dyyIXOOc7P4RuA X-Authority-Analysis: v=2.4 cv=NODlPU6g c=1 sm=1 tr=0 ts=6a0180e9 cx=c_pps a=RP+M6JBNLl+fLTcSJhASfg==:117 a=fChuTYTh2wq5r3m49p7fHw==:17 a=NGcC8JguVDcA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=6pG74zlKLNrpVVWoZpsA:9 a=iS9zxrgQBfv6-_F4QbHw:22 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-11_02,2026-05-08_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 bulkscore=0 adultscore=0 suspectscore=0 malwarescore=0 lowpriorityscore=0 impostorscore=0 spamscore=0 phishscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2604200000 definitions=main-2605110077 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 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. 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 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 Signed-off-by: Priyansh Jain --- 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..72583af7ddd6 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -315,10 +315,67 @@ static inline int code_to_degc(u32 adc_code, const st= ruct tsens_sensor *s) return degc; } =20 +/** + * 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 =3D 0; + 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 + s->hw_id] ? 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 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 ret; +} + /** * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. * @s: Pointer to sensor struct * @field: Index into regmap_field array pointing to temperature data + * @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,12 @@ 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); + if (ret) + return ret; } else { /* No mask register on older TSENS */ d->up_irq_mask =3D 0; @@ -524,9 +579,14 @@ static int tsens_read_irq_state(struct tsens_priv *pri= v, u32 hw_id, d->crit_irq_mask =3D 0; d->crit_thresh =3D 0; } - - 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 +810,16 @@ 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 ret; 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; + ret =3D tsens_read_temp(s, temp_idx, temp); =20 - /* 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); + 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