From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 CD06D4EE86E; Mon, 21 Sep 2026 17:41:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012465; cv=none; b=UFu7z4Tg2aU5mKOSFxx/q4kAAwDpBOu/MNnDPyudcosuqE4WBvAkoIKASKU1UhGpoF5ADFBdNHVittlQWu6nFbvw/s1Y2QLj9p4qfi6iRaoBSrWmpBsI2EhIYja5FBZsClRMvL5Z2XBGKO2tpqhvdmVXqqiITgWYNwm57DpjvY4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012465; c=relaxed/simple; bh=doy8CQVTkGDgR7A0QYfdmidqT7C6L19D+j8DtIbAz9Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sVRx7GDcoqtEClXg9uw1Ddhhfrr5WJwj6ln/cmSFdS3HYOYUh2BCcHau0vr2sqnYCC2RCNOgYfzEWd8GNjqBc0L0t6GoLSk+cvI+bWO/dQ2UoNB6yRzLcn8D69APujwHZuJsUOjYN86U+MOs9i3vavCpz/vsBY+NrUXjuq2VmEY= 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=I6vl1+W4; arc=none smtp.client-ip=205.220.168.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="I6vl1+W4" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LGUmsJ2707960; Mon, 21 Sep 2026 17:40:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= jEFAOUvkcm/hQj/EyLQud5GCptVe5eN3gxABCz93kmE=; b=I6vl1+W4t+i7i5zO scV1GtuagLIdFx7jzt5TmmHineroARZCGUY2Oa5771Evz/eFf0Y8kmMAjyYR+ovm R9YY/pA03e8kATRAz+QYtxijIHRA+1HgHffVR/Iin2a5CekVhzZy1C63PGfYwUda XHBta7++6nwIPB0pn+OLENuES+T7vTjwGpKMvGBEsCSlK3Prw0zmQM3ruDza+BhW U36yVTt/oC6cqkpRlRpxUqnvKc9QCrcb6QR7yj1sUmvMmcBbllYUAFD7h0euOl6k pWI5Hf557BsGq6R8d3guRHZAmBdofLlc3ddU3nE2gahDKq+GlNYBt3V5XBrIc6m8 8KZauQ== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu85vg9tb-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:42 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHecwv1524343; Mon, 21 Sep 2026 17:40:38 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebcq1524318; Mon, 21 Sep 2026 17:40:38 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:26 +0530 Subject: [PATCH 1/6] power: Extend power_on_reason.h for upcoming PSCRR framework Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-1-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari , Sebastian Reichel X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX9hQSn+1ILibk uua3dDOHtv8eYnct3xP+bHrWDz6Xz1CWa9liS7WoL25YwPj8p0YM4E1S2xhasdjKyX0hm2rhViT ou6dlh6dCEvAuVo8Oc9KXomLn0jYNsY= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX+OS/Mm8t3uZw /4AiK/l0fMq8Vap4qWCFWlAF7xgrg795o1WcndYHZQGtD5x3DrJHjTtTyWrgTskgo54pIQIYG8d HKKmd/ZMyByvSdkdOJXuDpUxOUB/2gPvU9MulPr0zmaUl/JqXcHlqw/0D9XA5esgMRyX5AW0Ypu hIbGtyg8C2+TClG/6Vbpq+WAiAhMNR+ljtNyF9TpxY/4Ai+U+BjTqdGDb1sSEflV0e7tTfN8d4D Le+cUu81xhBVDwbcAYupmF+JmKLF+j7JTKQALzuqzaWEnndlj+x2htAI/s7l+ANp1UoArd5vPQv tgIJ92VK9u8yinHqEDfOj4++LOT4z4YJZ88mIoIZ+Nbly7X7SbRarueBmA/fEjVnIQ+FrymlNXE cMFTNnWR8Hdz2U7kacUTaKJFK+gVmJ890UCaBkUYFkcpMGq3LYZ2AQVOS3nPWp+zgojXHpawO3b 9iGFny8baBD760/YjGQ== X-Authority-Analysis: v=2.4 cv=E8FYNqdl c=1 sm=1 tr=0 ts=6ab16c1a cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=QX4gbG5DAAAA:8 a=EUspDBNiAAAA:8 a=7kjejDXy1mYeYQkWB3oA:9 a=QEXdDO2ut3YA:10 a=AbAUZ8qAyYyZVLSsDulk:22 X-Proofpoint-ORIG-GUID: TR2ABAuOHAE6N-KUyuB0_fzGG6SITz1v X-Proofpoint-GUID: TR2ABAuOHAE6N-KUyuB0_fzGG6SITz1v X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 clxscore=1015 spamscore=0 lowpriorityscore=0 impostorscore=0 phishscore=0 suspectscore=0 priorityscore=1501 malwarescore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 Prepare for the introduction of the Power State Change Reason Recorder (PSCRR) framework by expanding the power_on_reason.h header. This extension includes new power-on reasons: - POWER_ON_REASON_OVER_CURRENT for over-current conditions. - POWER_ON_REASON_REGULATOR_FAILURE for regulator failures. - POWER_ON_REASON_OVER_TEMPERATURE for over temperature situations. - POWER_ON_REASON_EC_PANIC for EC panics. - POWER_ON_REASON_EXTERNAL for an external reset source. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Reviewed-by: Sebastian Reichel Signed-off-by: Faruque Ansari changes v12: - add POWER_ON_REASON_EXTERNAL changes v10: - add Reviewed-by: Sebastian Reichel ... changes v6: - add POWER_ON_REASON_EC_PANIC - s/POWER_ON_REASON_OVERTEMPERATURE/POWER_ON_REASON_OVER_TEMPERATURE (cherry picked from commit a6fb08a81864e2ed17cebba9f249af3d44e42c3f) --- include/linux/power/power_on_reason.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/power/power_on_reason.h b/include/linux/power/po= wer_on_reason.h index 95a1ec0c403c..13e61ace14f5 100644 --- a/include/linux/power/power_on_reason.h +++ b/include/linux/power/power_on_reason.h @@ -15,5 +15,10 @@ #define POWER_ON_REASON_XTAL_FAIL "crystal oscillator failure" #define POWER_ON_REASON_BROWN_OUT "brown-out reset" #define POWER_ON_REASON_UNKNOWN "unknown reason" +#define POWER_ON_REASON_OVER_CURRENT "over current" +#define POWER_ON_REASON_REGULATOR_FAILURE "regulator failure" +#define POWER_ON_REASON_OVER_TEMPERATURE "over temperature" +#define POWER_ON_REASON_EC_PANIC "EC panic" +#define POWER_ON_REASON_EXTERNAL "external reset" =20 #endif /* POWER_ON_REASON_H */ --=20 2.34.1 From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 A3F0B4EDCAB; Mon, 21 Sep 2026 17:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012463; cv=none; b=cDMducBxjH+RyJQjVU+I1hit3leJYbRo/ir0pj3HKqkQJNDpYY2FPNwBu/MKSYaMCccAjyp2/tGr0UNZy5ShG5lmQ66xFg5GUE4CjrDdDB0/2gzSXPp+Qwp5emmcNjirG/M3t7zEqD+UBVOJ9xHMgOON9gMYYORE23rWO8w4sGs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012463; c=relaxed/simple; bh=r5pwx0D4qyzdb1/pAk1vEAnmtsvN/8GvGQzOVNJmEoc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VFfg9a4b6q88RKbZr6jImhro7lxgJCb/LUOSYP9TEnwvI+LNArNap/ZO3P21uFXSILBXCXt/hjfuDGuqeTpxblV8faDNb9QvGbRuf+xhwWWksueklRZNHlxRymj45j0urS0fDQcqqZR51FPEIzWAC+uEnpAnkZyRm5irGglHyWw= 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=UmxPTV9Q; arc=none smtp.client-ip=205.220.168.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="UmxPTV9Q" Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHNWQq1314911; Mon, 21 Sep 2026 17:40:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= e91v/0IgGsGPh6qcVxGFQwWxAX6WmD95pIQ/G4euJps=; b=UmxPTV9QJRvqeiNv hY9Wj1uZkG297jVhAc38TqzpEafBgXEUSn5gfcuHl0tQES7BypMIZX8na8x/Zw7T VnKjBBkePFPRNfFYObXobZC7DSfzHEmauRSDwM5/IHc9tVasaSGp4y+1HdJvCYTg 1+W5lq+fIvtlDlJS92oWlkpBxlFhhdvcGs5NmyXfV+dF7UbioA09CUnPEOObkdZ5 TeUnkF+mZBwfpMUt+8E/u59jQPM57v5nGBpXpUJauftzsWvm3MsmPziVKFLZCK86 I/yYzqde+Mt60keGQVkGgI3ZwGrgUgszdKbxv0q1SeVepZ/sR+3o8k5so/u7HW50 5IpxeQ== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu8xer27p-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:41 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHecvR1524350; Mon, 21 Sep 2026 17:40:38 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebcr1524318; Mon, 21 Sep 2026 17:40:38 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:27 +0530 Subject: [PATCH 2/6] reboot: hw_protection_trigger: use standardized numeric shutdown/reboot reasons instead of strings Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-2-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari , Daniel Lezcano X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-ORIG-GUID: xcHJGzcYGQkhES7I358V-BKftQXLOfe8 X-Authority-Analysis: v=2.4 cv=PZhqFShd c=1 sm=1 tr=0 ts=6ab16c19 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_K5XuSEh1TEqbUxoQ0s3:22 a=pGLkceISAAAA:8 a=VwQbUJbxAAAA:8 a=KKAkSRfTAAAA:8 a=EUspDBNiAAAA:8 a=7sc-FwRdcYxzY__InfsA:9 a=QEXdDO2ut3YA:10 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-GUID: xcHJGzcYGQkhES7I358V-BKftQXLOfe8 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfXyjajyFsbUYpq sCMep7gC4vWEaTaMwZxvgElYFglC7RX1OTlhQ8gDuDREsnEcQIOEBeQYvfHtiTXL03GZylFDY+7 H8AJ+xp8fDKS1Jj1GMGp+hw6RZsKMzowm4hzyZoIQlC+bLvqWKXv/H+pr4H/uISl6noqtgVc1zy XL40jmpL8NqfUk+Z3DSIeV76UFY2YkFRcl4E7uVfQ2MRARCUflZ/9732CCdKZqrCLLlTsg9mBGh NoKwI/57cXOHr/Vj9Zh+UrRw+87pzY2DabmuJW274N6OmY9S2VTE0weWFiEwW+DcMnW1MfAVux3 C6YWjtrB/cKd7WXPE0XlN3YOacNSq9YLM5iDfE8gr/HFTbYRmxAOJweAzUywwSvLAn9r2LIbhw6 NGJGvcbTyrQOhWulfiBJbX+de4BGS6ouTLt161Kod00Acznog80Wqx2P6PgN9sG9wnMXeHJ1Seo RsSv7f3sH8oqOBF8k7g== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX9OnBx/95FzfK YG3yPKmWjYcpoe9gCNMJT4MEl+CJqrGImcoviv+LmMv90EO/En9mojShbf3yIpXTZLNpIZ6dVrf LCIrC4OHgkR92hdQD4pYsyeXxg/bdgA= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 malwarescore=0 spamscore=0 impostorscore=0 priorityscore=1501 lowpriorityscore=0 suspectscore=0 bulkscore=0 phishscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 Prepares the kernel for the Power State Change Reason (PSCR) recorder, which will store shutdown and reboot reasons in persistent storage. Instead of using string-based reason descriptions, which are often too large to fit within limited storage spaces (e.g., RTC clocks with only 8 bits of battery-backed storage), we introduce `enum psc_reason`. This enumerates predefined reasons for power state changes, making it efficient to store and retrieve shutdown causes. Key changes: - Introduced `enum psc_reason`, defining structured reasons for power state changes. - Replaced string-based shutdown reasons with `psc_reason` identifiers. - Implemented `get_psc_reason()` and `set_psc_reason()` for tracking the last shutdown cause. - Added `psc_reason_to_str()` to map enum values to human-readable strings. - Updated `hw_protection_trigger()` to use `psc_reason` instead of string parameters. - Updated all consumers of `hw_protection_trigger()` to pass an appropriate `psc_reason` value instead of a string. - All structured logs now go through a single `pr_emerg()` in `__hw_protection_trigger()`, providing consistent output: HARDWARE PROTECTION : () Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Reviewed-by: Matti Vaittinen Acked-by: Mark Brown Acked-by: Tzung-Bi Shih Acked-by: Daniel Lezcano Signed-off-by: Faruque Ansari changes v12: - drop Tested-by: Faruque Ansari and Francesco Valla; the end-to-end recording flow they tested has been reworked (multi-provider) and the recorder driver is no longer part of the series - re-testing is welcome changes v10 - regulator_handle_critical: set pscr =3D PSCR_UNKNOWN for default case - add Acked-by: Daniel Lezcano .. changes v9: - Remove redundant pr_crit() messages before hw_protection_trigger() - Replace psc_reason_to_str() switch with static const string array - Mark psc_last_reason as static changes v8: - add Acked/Reviewed-by. changes v6: - added in this version (cherry picked from commit 29591d88486c392e2264de73c4e90106f181b33c) --- drivers/platform/chrome/cros_ec_lpc.c | 2 +- drivers/regulator/core.c | 16 ++--- drivers/regulator/irq_helpers.c | 9 +-- drivers/thermal/thermal_core.c | 3 +- include/linux/reboot.h | 78 ++++++++++++++++++++++- kernel/reboot.c | 112 ++++++++++++++++++++++++++++++= ++-- 6 files changed, 198 insertions(+), 22 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrom= e/cros_ec_lpc.c index 78cfff80cdea..00e98d12ddbc 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -455,7 +455,7 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device,= u32 value, void *data) blocking_notifier_call_chain(&ec_dev->panic_notifier, 0, ec_dev); kobject_uevent_env(&ec_dev->dev->kobj, KOBJ_CHANGE, (char **)env); /* Begin orderly shutdown. EC will force reset after a short period. */ - __hw_protection_trigger("CrOS EC Panic", -1, HWPROT_ACT_SHUTDOWN); + __hw_protection_trigger(PSCR_EC_PANIC, -1, HWPROT_ACT_SHUTDOWN); /* Do not query for other events after a panic is reported */ return; } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 631b55142809..90c406a4a045 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5551,26 +5551,26 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); static void regulator_handle_critical(struct regulator_dev *rdev, unsigned long event) { - const char *reason =3D NULL; + enum psc_reason pscr; =20 if (!rdev->constraints->system_critical) return; =20 switch (event) { case REGULATOR_EVENT_UNDER_VOLTAGE: - reason =3D "System critical regulator: voltage drop detected"; + pscr =3D PSCR_UNDER_VOLTAGE; break; case REGULATOR_EVENT_OVER_CURRENT: - reason =3D "System critical regulator: over-current detected"; + pscr =3D PSCR_OVER_CURRENT; break; case REGULATOR_EVENT_FAIL: - reason =3D "System critical regulator: unknown error"; + pscr =3D PSCR_REGULATOR_FAILURE; + break; + default: + pscr =3D PSCR_UNKNOWN; } =20 - if (!reason) - return; - - hw_protection_trigger(reason, + hw_protection_trigger(pscr, rdev->constraints->uv_less_critical_window_ms); } =20 diff --git a/drivers/regulator/irq_helpers.c b/drivers/regulator/irq_helper= s.c index 5b3cfac28667..712e0ce840e7 100644 --- a/drivers/regulator/irq_helpers.c +++ b/drivers/regulator/irq_helpers.c @@ -64,15 +64,16 @@ static void regulator_notifier_isr_work(struct work_str= uct *work) reread: if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) { if (!d->die) - return hw_protection_trigger("Regulator HW failure? - no IC recovery", + return hw_protection_trigger(PSCR_REGULATOR_FAILURE, REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); + ret =3D d->die(rid); /* * If the 'last resort' IC recovery failed we will have * nothing else left to do... */ if (ret) - return hw_protection_trigger("Regulator HW failure. IC recovery failed", + return hw_protection_trigger(PSCR_REGULATOR_FAILURE, REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); =20 /* @@ -263,13 +264,13 @@ static irqreturn_t regulator_notifier_isr(int irq, vo= id *data) if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) { /* If we have no recovery, just try shut down straight away */ if (!d->die) { - hw_protection_trigger("Regulator failure. Retry count exceeded", + hw_protection_trigger(PSCR_REGULATOR_FAILURE, REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); } else { ret =3D d->die(rid); /* If die() failed shut down as a last attempt to save the HW */ if (ret) - hw_protection_trigger("Regulator failure. Recovery failed", + hw_protection_trigger(PSCR_REGULATOR_FAILURE, REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); } } diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ac928c199829..3618986c1cab 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -319,11 +319,10 @@ static void thermal_zone_device_halt(struct thermal_z= one_device *tz, * Its a must for forced_emergency_poweroff_work to be scheduled. */ int poweroff_delay_ms =3D CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; - const char *msg =3D "Temperature too high"; =20 dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type); =20 - __hw_protection_trigger(msg, poweroff_delay_ms, action); + __hw_protection_trigger(PSCR_OVER_TEMPERATURE, poweroff_delay_ms, action); } =20 void thermal_zone_device_critical(struct thermal_zone_device *tz) diff --git a/include/linux/reboot.h b/include/linux/reboot.h index aa08c3bbbf59..08a7549bbc40 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -178,6 +178,73 @@ void ctrl_alt_del(void); extern void orderly_poweroff(bool force); extern void orderly_reboot(void); =20 + +/** + * enum psc_reason - Enumerates reasons for power state changes. + * + * This enum defines various reasons why a system might transition into a + * shutdown, reboot, or kexec state. While originally intended for hardware + * protection events, `psc_reason` can be extended to track other system + * transitions, such as controlled reboots triggered by software or + * maintenance operations. + * + * The values in this enumeration provide structured and standardized + * identifiers that replace free-form string descriptions. They are design= ed + * to be stored efficiently, making them suitable for use in environments + * with limited storage, such as battery-backed RTC registers, non-volatile + * memory, or bootloader communication mechanisms. + * + * Importantly, the order of these values **must remain stable**, as + * bootloaders, user-space tools, or post-mortem investigation utilities + * may rely on their numerical representation for consistent behavior. + * + * @PSCR_UNKNOWN: Unknown or unspecified reason for the power state change. + * This value serves as a default when no explicit cause is recorded. + * + * @PSCR_UNDER_VOLTAGE: Shutdown or reboot triggered due to supply voltage + * dropping below a safe threshold. This helps prevent instability or + * corruption caused by insufficient power. + * + * @PSCR_OVER_CURRENT: System shutdown or reboot due to excessive current = draw, + * which may indicate a short circuit, an overloaded power rail, or o= ther + * hardware faults requiring immediate action. + * + * @PSCR_REGULATOR_FAILURE: A critical failure in a voltage regulator, cau= sing + * improper power delivery. This may be due to internal component fai= lure, + * transient conditions, or external load issues requiring mitigation. + * + * @PSCR_OVER_TEMPERATURE: System shutdown or reboot due to excessive ther= mal + * conditions. This attempts to prevent hardware damage when temperature + * sensors detect unsafe levels, often impacting CPUs, GPUs, or power + * components. + * + * @PSCR_EC_PANIC: Shutdown or reboot triggered by an Embedded Controller = (EC) + * panic. The EC is a microcontroller responsible for low-level system + * management, including power sequencing, thermal control, and battery + * management. An EC panic may indicate critical firmware issues, power + * management errors, or an unrecoverable hardware fault requiring + * immediate response. + * + * @PSCR_REASON_COUNT: Number of defined power state change reasons. This + * value is useful for range checking and potential future extensions + * while maintaining compatibility. + */ +enum psc_reason { + PSCR_UNKNOWN, + PSCR_UNDER_VOLTAGE, + PSCR_OVER_CURRENT, + PSCR_REGULATOR_FAILURE, + PSCR_OVER_TEMPERATURE, + PSCR_EC_PANIC, + + /* Number of reasons */ + PSCR_REASON_COUNT, +}; + +#define PSCR_MAX_REASON (PSCR_REASON_COUNT - 1) + +const char *psc_reason_to_str(enum psc_reason reason); + /** * enum hw_protection_action - Hardware protection action * @@ -191,13 +258,13 @@ extern void orderly_reboot(void); */ enum hw_protection_action { HWPROT_ACT_DEFAULT, HWPROT_ACT_SHUTDOWN, HWPRO= T_ACT_REBOOT }; =20 -void __hw_protection_trigger(const char *reason, int ms_until_forced, +void __hw_protection_trigger(enum psc_reason reason, int ms_until_forced, enum hw_protection_action action); =20 /** * hw_protection_trigger - Trigger default emergency system hardware prote= ction action * - * @reason: Reason of emergency shutdown or reboot to be printed. + * @reason: Reason of emergency shutdown or reboot. * @ms_until_forced: Time to wait for orderly shutdown or reboot before * triggering it. Negative value disables the forced * shutdown or reboot. @@ -206,11 +273,16 @@ void __hw_protection_trigger(const char *reason, int = ms_until_forced, * hardware from further damage. The exact action taken is controllable at * runtime and defaults to shutdown. */ -static inline void hw_protection_trigger(const char *reason, int ms_until_= forced) +static inline void hw_protection_trigger(enum psc_reason reason, + int ms_until_forced) { __hw_protection_trigger(reason, ms_until_forced, HWPROT_ACT_DEFAULT); } =20 +enum psc_reason get_psc_reason(void); +enum psc_reason get_psc_first_reason(void); +void set_psc_reason(enum psc_reason reason); + /* * Emergency restart, callable from an interrupt handler. */ diff --git a/kernel/reboot.c b/kernel/reboot.c index d177d89fcc33..d5a45db82c3d 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ int reboot_default =3D 1; int reboot_cpu; enum reboot_type reboot_type =3D BOOT_ACPI; int reboot_force; +static enum psc_reason psc_last_reason =3D PSCR_UNKNOWN; +static enum psc_reason psc_first_reason =3D PSCR_UNKNOWN; =20 struct sys_off_handler { struct notifier_block nb; @@ -1011,10 +1014,108 @@ static void hw_failure_emergency_schedule(enum hw_= protection_action action, msecs_to_jiffies(action_delay_ms)); } =20 +/** + * get_psc_reason - Retrieve the last recorded power state change reason. + * + * This function returns the most recent power state change reason stored + * in `psc_last_reason`. The value is set using `set_psc_reason()` when a + * shutdown, reboot, or kexec event occurs. + * + * The reason can be used for system diagnostics, post-mortem analysis, or + * debugging unexpected power state changes. Bootloaders or user-space too= ls + * may retrieve this value to determine why the system last transitioned to + * a new power state. + * + * Return: A value from `enum psc_reason`, indicating the last known power + * state change reason. + */ +enum psc_reason get_psc_reason(void) +{ + return READ_ONCE(psc_last_reason); +} +EXPORT_SYMBOL_GPL(get_psc_reason); + +/** + * get_psc_first_reason - Retrieve the first power state change reason. + * + * Returns the first meaningful (non-PSCR_UNKNOWN) reason recorded during = this + * boot, i.e. the root cause, as opposed to get_psc_reason() which returns= the + * most recent one. A later event (a watchdog pretimeout, a kernel panic, = ...) + * updates the last reason but leaves this one untouched, so a recorder us= ing + * the "first" record policy can preserve the original cause. + * + * Return: A value from `enum psc_reason`. + */ +enum psc_reason get_psc_first_reason(void) +{ + return READ_ONCE(psc_first_reason); +} +EXPORT_SYMBOL_GPL(get_psc_first_reason); + +/** + * set_psc_reason - Set the reason for the last power state change. + * + * @reason: A value from `enum psc_reason` indicating the cause of the pow= er + * state change. + * + * This function records the reason for a shutdown, reboot, or kexec event + * by storing it in `psc_last_reason`. It ensures that the value remains + * consistent within the running system, allowing retrieval via + * `get_psc_reason()` for diagnostics, logging, or post-mortem analysis. + * + * Persistence Consideration: + * - This function **does not persist** the recorded reason across power c= ycles. + * - After a system reset or complete power loss, the recorded reason is l= ost. + * - To store power state change reasons persistently, additional tools su= ch as + * the Power State Change Reason Recorder (PSCRR) framework should be us= ed. + */ +void set_psc_reason(enum psc_reason reason) +{ + WRITE_ONCE(psc_last_reason, reason); + + /* + * Latch the first meaningful reason of this boot as the root cause, so + * that a later event overwriting the last reason does not hide it from + * a recorder using the "first" record policy. + */ + if (reason !=3D PSCR_UNKNOWN && + READ_ONCE(psc_first_reason) =3D=3D PSCR_UNKNOWN) + WRITE_ONCE(psc_first_reason, reason); +} +EXPORT_SYMBOL_GPL(set_psc_reason); + +static const char * const pscr_reason_strs[] =3D { + [PSCR_UNKNOWN] =3D POWER_ON_REASON_UNKNOWN, + [PSCR_UNDER_VOLTAGE] =3D POWER_ON_REASON_BROWN_OUT, + [PSCR_OVER_CURRENT] =3D POWER_ON_REASON_OVER_CURRENT, + [PSCR_REGULATOR_FAILURE] =3D POWER_ON_REASON_REGULATOR_FAILURE, + [PSCR_OVER_TEMPERATURE] =3D POWER_ON_REASON_OVER_TEMPERATURE, + [PSCR_EC_PANIC] =3D POWER_ON_REASON_EC_PANIC, +}; + +/** + * psc_reason_to_str - Converts a power state change reason enum to a stri= ng. + * @reason: The `psc_reason` enum value to be converted. + * + * This function provides a human-readable string representation of the po= wer + * state change reason, making it easier to interpret logs and debug messa= ges. + * + * Return: + * - A string corresponding to the given `psc_reason` value. + * - `"Invalid"` if the value is not recognized. + */ +const char *psc_reason_to_str(enum psc_reason reason) +{ + if (reason < 0 || reason >=3D PSCR_REASON_COUNT) + return "Invalid"; + return pscr_reason_strs[reason]; +} +EXPORT_SYMBOL_GPL(psc_reason_to_str); + /** * __hw_protection_trigger - Trigger an emergency system shutdown or reboot * - * @reason: Reason of emergency shutdown or reboot to be printed. + * @reason: Reason of emergency shutdown or reboot. * @ms_until_forced: Time to wait for orderly shutdown or reboot before * triggering it. Negative value disables the forced * shutdown or reboot. @@ -1026,7 +1127,7 @@ static void hw_failure_emergency_schedule(enum hw_pro= tection_action action, * pending even if the previous request has given a large timeout for forc= ed * shutdown/reboot. */ -void __hw_protection_trigger(const char *reason, int ms_until_forced, +void __hw_protection_trigger(enum psc_reason reason, int ms_until_forced, enum hw_protection_action action) { static atomic_t allow_proceed =3D ATOMIC_INIT(1); @@ -1034,8 +1135,11 @@ void __hw_protection_trigger(const char *reason, int= ms_until_forced, if (action =3D=3D HWPROT_ACT_DEFAULT) action =3D hw_protection_action; =20 - pr_emerg("HARDWARE PROTECTION %s (%s)\n", - hw_protection_action_str(action), reason); + set_psc_reason(reason); + + pr_emerg("HARDWARE PROTECTION %s: %i (%s)\n", + hw_protection_action_str(action), reason, + psc_reason_to_str(reason)); =20 /* Shutdown should be initiated only once. */ if (!atomic_dec_and_test(&allow_proceed)) --=20 2.34.1 From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 5BEC04E2F13; Mon, 21 Sep 2026 17:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012461; cv=none; b=s4gPjCoj+SRUP/v/ftGPAy/jqI4PoaPeWONhqfiSf9LjiK8tc1OerriZvgzSaNDzvOqhv1dKsfJ84R9l1aZu6bwMI/3DGZiAl6TrnFbatt6CFCAIZJQbLLt13Kyl8jGlb1Bp2rxZgYY4QuAAcfiTWAaGf2IKOc+Q4Wm9naVSnDQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012461; c=relaxed/simple; bh=FpT41VuJx/RhuA3s0Y6+NIa72g6zJh/UBOwe5H0ThfE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JKoOj6StpVtBl/OdA+s/LleLZknANZzqPYUr8/yHkAfiJ6I41AZeQB49Pxb3GOZORaA0duhnZlEKC6hqAZFLDhm4E1pXbCy6T+e0vfO33TUo0w761DWFf+/uIeyr4Z7AZ52i5dAAHsM12QisvLIyeLgXTuw4sPVovt4orq/nXsA= 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=YebHuBnH; arc=none smtp.client-ip=205.220.168.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="YebHuBnH" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LGUiig2707774; Mon, 21 Sep 2026 17:40:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= XcuNqUtiQ6YkSBBXPvackNvDBCGeX7CHs7k1WjQ/M8M=; b=YebHuBnHeALEr2wB c/4O5WJyGmdlRsnWUGlZRXqBTT/HAoTAgepttPAcIv0ic95AkNXlU14ZNHqytqdJ 8407FNqfx6zc0Tj9SskfcJs3mICeVmXotZq7EwTSBtEL+QdIx+YoRK0wL+ifxes0 yo85uHK+fr3fAsWhEc1w2ADHANyxAhGzhBQGhUhif6v5V+p3brxwRRM7IRXp/lvb /Z+F9OfrA8FYLEFaGdJZ4bFf1osKerAxug2f3eYk4vtkU7nhepd62ABXsWZgynbG Was0HMxN8/O2ScRK+Zz8UQH8RlJ04naCWFSB9WDWM3CrMBXO1R5nb7ESMm5/n4YV H3hCyQ== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu85vg9tc-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:41 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHedcp1524364; Mon, 21 Sep 2026 17:40:39 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebcs1524318; Mon, 21 Sep 2026 17:40:39 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:28 +0530 Subject: [PATCH 3/6] reboot: add parsable tokens for power state change reasons Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-3-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX4odu3Hx9qTuR XrBNLM35nl6294GNCFHa9jOils2rh0F7AIGneRBYkj7r33tHaRuF9NVM/w27+DS91seTBlV0Y7t Nnd/EtVit9+g/yrSB/10VEamrf04tz8= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX9puUhBTCXfxw 7EUHrp+Q3ClcukcCzRwqerPzaZTSXbC7sGDeRijxr/vhYXZME7qdtfjosQFjR71sjFHjqA5yN/4 ss3YLUCzaefnK4X/h9e+xDApKg5ImKecR8+KMv4PzQoQAb6/sZTRsy8oY6fEvTRfjZqNpT54kaw Z9jpUmQCyMeo5hbdpLwaOFq85q5G2SYhR8YWwoofPjOWYumsM2gUYqLDPFVNRcBHCoAudP9JXJL ETzBmLjQEfugG3tGw5SKCne+UUbdA4LZsRP/9J6vMlioVkTi2HYAoBBGApPUzjNrx1AqVs0pDzv NmLT0bCgzeZ+ijTOJWhusdNXpkWemGo02K+RinF1fu9OoSCjwWJ0VQAiDNohIb3hFJjEaJjt6BH tIaOD+jcCI9bGRRV52sL73+6ERtZ8u/FsC1qMmZXtFAUemfsjZx9cLTFXvrGigQBHtYajzncS0p sYzjDjTh2vMqPpXxryA== X-Authority-Analysis: v=2.4 cv=E8FYNqdl c=1 sm=1 tr=0 ts=6ab16c1a cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=EUspDBNiAAAA:8 a=se0PJeKvf3ygCVTVq1UA:9 a=QEXdDO2ut3YA:10 X-Proofpoint-ORIG-GUID: wriquyE5rgTU-jcXkhOzzF_XKmC1aY1Y X-Proofpoint-GUID: wriquyE5rgTU-jcXkhOzzF_XKmC1aY1Y X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 clxscore=1015 spamscore=0 lowpriorityscore=0 impostorscore=0 phishscore=0 suspectscore=0 priorityscore=1501 malwarescore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 psc_reason_to_str() returns human-readable labels that contain spaces (e.g. "over temperature"). Those are fine for logs but unusable as values in a space-separated sysfs list or as a write target. Extend the single reason descriptor table with a stable, space-free token next to the existing label, and add psc_reason_to_token() and psc_reason_from_token() so consumers can emit and parse reasons without inventing their own string table. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Signed-off-by: Faruque Ansari changes v12: - new patch --- include/linux/reboot.h | 2 ++ kernel/reboot.c | 77 +++++++++++++++++++++++++++++++++++++++-------= ---- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 08a7549bbc40..4c5327dd7645 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -244,6 +244,8 @@ enum psc_reason { #define PSCR_MAX_REASON (PSCR_REASON_COUNT - 1) =20 const char *psc_reason_to_str(enum psc_reason reason); +const char *psc_reason_to_token(enum psc_reason reason); +int psc_reason_from_token(const char *token, enum psc_reason *reason); =20 /** * enum hw_protection_action - Hardware protection action diff --git a/kernel/reboot.c b/kernel/reboot.c index d5a45db82c3d..e53fb31bad06 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -1084,34 +1084,79 @@ void set_psc_reason(enum psc_reason reason) } EXPORT_SYMBOL_GPL(set_psc_reason); =20 -static const char * const pscr_reason_strs[] =3D { - [PSCR_UNKNOWN] =3D POWER_ON_REASON_UNKNOWN, - [PSCR_UNDER_VOLTAGE] =3D POWER_ON_REASON_BROWN_OUT, - [PSCR_OVER_CURRENT] =3D POWER_ON_REASON_OVER_CURRENT, - [PSCR_REGULATOR_FAILURE] =3D POWER_ON_REASON_REGULATOR_FAILURE, - [PSCR_OVER_TEMPERATURE] =3D POWER_ON_REASON_OVER_TEMPERATURE, - [PSCR_EC_PANIC] =3D POWER_ON_REASON_EC_PANIC, +/** + * struct psc_reason_desc - Descriptor for a power state change reason. + * @token: Stable, parsable identifier without spaces (e.g. "over-temperat= ure"). + * Suitable for use in sysfs values and as a user/kernel contract. + * @label: Human-readable description (e.g. "over temperature"), for logs. + */ +struct psc_reason_desc { + const char *token; + const char *label; +}; + +static const struct psc_reason_desc psc_reason_descs[] =3D { + [PSCR_UNKNOWN] =3D { "unknown", POWER_ON_REASON_UNKNOWN }, + [PSCR_UNDER_VOLTAGE] =3D { "under-voltage", POWER_ON_REASON_BROWN_OUT = }, + [PSCR_OVER_CURRENT] =3D { "over-current", POWER_ON_REASON_OVER_CURRENT= }, + [PSCR_REGULATOR_FAILURE] =3D { "regulator-failure", POWER_ON_REASON_REGUL= ATOR_FAILURE }, + [PSCR_OVER_TEMPERATURE] =3D { "over-temperature", POWER_ON_REASON_OVER_= TEMPERATURE }, + [PSCR_EC_PANIC] =3D { "ec-panic", POWER_ON_REASON_EC_PANIC }, }; =20 /** - * psc_reason_to_str - Converts a power state change reason enum to a stri= ng. - * @reason: The `psc_reason` enum value to be converted. - * - * This function provides a human-readable string representation of the po= wer - * state change reason, making it easier to interpret logs and debug messa= ges. + * psc_reason_to_str - Human-readable label for a power state change reaso= n. + * @reason: The `psc_reason` value to convert. * - * Return: - * - A string corresponding to the given `psc_reason` value. - * - `"Invalid"` if the value is not recognized. + * Return: The label string, or "Invalid" if @reason is out of range. For a + * stable, parsable form use psc_reason_to_token() instead. */ const char *psc_reason_to_str(enum psc_reason reason) { if (reason < 0 || reason >=3D PSCR_REASON_COUNT) return "Invalid"; - return pscr_reason_strs[reason]; + return psc_reason_descs[reason].label; } EXPORT_SYMBOL_GPL(psc_reason_to_str); =20 +/** + * psc_reason_to_token - Stable, parsable token for a power state change r= eason. + * @reason: The `psc_reason` value to convert. + * + * Return: The token string (no spaces), or "invalid" if @reason is out of + * range. Round-trips with psc_reason_from_token(). + */ +const char *psc_reason_to_token(enum psc_reason reason) +{ + if (reason < 0 || reason >=3D PSCR_REASON_COUNT) + return "invalid"; + return psc_reason_descs[reason].token; +} +EXPORT_SYMBOL_GPL(psc_reason_to_token); + +/** + * psc_reason_from_token - Parse a reason token into a `psc_reason` value. + * @token: A token as returned by psc_reason_to_token(). A trailing newlin= e is + * tolerated. + * @reason: Output; set on success. + * + * Return: 0 on success or -EINVAL if @token matches no known reason. + */ +int psc_reason_from_token(const char *token, enum psc_reason *reason) +{ + int i; + + for (i =3D 0; i < PSCR_REASON_COUNT; i++) { + if (sysfs_streq(token, psc_reason_descs[i].token)) { + *reason =3D i; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(psc_reason_from_token); + /** * __hw_protection_trigger - Trigger an emergency system shutdown or reboot * --=20 2.34.1 From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 6309A4DEC2E; Mon, 21 Sep 2026 17:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012461; cv=none; b=W3W2yYAd7a3fEFfYdk60ojI7NoPeAHZRKLRo0nSqsKsKt0DAV1hXB3h6lqKvXXG892Z0A4t5nQ4TBzdVAiXy7cH4s1ATIXepq1UO2ddko0sUvEdhm90xPSmJ6dZz3gtKXSq9WFGIluNwVu55d+o9KwaxlP4vDBJLKMrSbs+DldA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012461; c=relaxed/simple; bh=FHzNN028Wl7cxVh7NYXgrkxS7ahQnf7fNUunvOT8ZkM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pOnde9aeUNYVLMPc0+YdTx+oqRUWX9TaB643mHjn5fx02Up2W3DQcXp1NwoqMs5MiOn2CwmL0OUl+e3+xyfh+5Upt+eFMVWEJhU12/k7cw4dArjPYFMbP5ICWyR8+37D+lM6yoRAF70j/veL+9WcW1QXB0IUBGf8/RLaRAD7Kl8= 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=DNjcYsNS; arc=none smtp.client-ip=205.220.168.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="DNjcYsNS" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LGUiZb2707775; Mon, 21 Sep 2026 17:40:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= z/mAy5pF38WxS6+ONcT4R6fBRT5kBAMVlmEJjMoX/2U=; b=DNjcYsNSqi9bneOq UcllAm2AUlOS12JR2/QKR9QeZoQixC7+AKR4lvDmLf1BxgQbk6c+bncvT0SvuKYz msAD/dUmYwOaohy5N25JhBEbZo37ob31WAcmwhvgO23QQ0l5Pasn9Ftz025WRsAV RbNNzajKj/8ThXyn/BKnzgLYpT584zKcRavEL134fMjABCRI3UrGo5wQ2KjzS1B3 1iJQ1WahPc//L6Pgv4r9ZIJ7QiQKfMzKESUgH+wD8QqXT5lXovx828x8lAk6R7c/ v/FrhwzJaV6a2BKdoqqtEt1nqY8y13EoZPruO8eZcNqPfNyQGZtHaRTV4B0U7pJf fc8JQQ== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu85vg9te-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:42 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHedC21524374; Mon, 21 Sep 2026 17:40:39 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebct1524318; Mon, 21 Sep 2026 17:40:39 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:29 +0530 Subject: [PATCH 4/6] reboot: extend psc_reason with power-on and reset causes Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-4-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX0ARhF81L1CeY +QO13bQUETtWyiICM4JOzUT66muac+c1WOJ5x+1AegJ/zS9yWlNgbwmM5IS32uUasWfPWcguCJv +26vdFHE5wA3qOFtdvQlbTqbTaaHZOI= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX+/Jjy+KxipDf TqS0oAuiokwRg1qetWl9x6ZX75dBR9E/ubcn6KeJcwbDFgtN8riWpprN6/Qu8X2lvQb/nuslA8W G5t0fdHhK5HOAI0ET6bcavglVPkkcCo9WsG8B0nzycDhuUzLc8drCLqLLeE8Pv1aAlrBZMX+QZW 5/1lxiI1pa6x6SC4hGs/Z9AHcVs402f56z2MwJv1HpxvywB72DF9wCjQQNyljzDvT0JTxC0OyxJ zgTYmoLYLyGTwP4xf70ayn90QPqsYGbTMWz+U92Yuep0LypUbTNHf81wx1yI0nXgWgUqnA+0zp7 6mEucuAX2K1o4LIecCDPhdTGaMrTTiompx4BfI1VKdChbYMWWUJ5bwdaM1yBMvpCdW+5HAorAgl lFq4zv6cWGNFUeF9/3lTaPx/oMRAhptIOH8flm6uqjf5CcetVfiakkStDdpiXCeshzBPzFHRTgy Km7eK/AGiwBeU2xflBw== X-Authority-Analysis: v=2.4 cv=E8FYNqdl c=1 sm=1 tr=0 ts=6ab16c1a cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=EUspDBNiAAAA:8 a=uHPRDBdGixKVNKiypX4A:9 a=QEXdDO2ut3YA:10 X-Proofpoint-ORIG-GUID: 4aTFDfq70GWAJWcc_se_JWevQixNfRC0 X-Proofpoint-GUID: 4aTFDfq70GWAJWcc_se_JWevQixNfRC0 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 clxscore=1015 spamscore=0 lowpriorityscore=0 impostorscore=0 phishscore=0 suspectscore=0 priorityscore=1501 malwarescore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 psc_reason covered only the protection reasons. Extend it to mirror the full POWER_ON_REASON_* vocabulary - power-on, watchdog, software, external, RTC, reset button, CPU clock and crystal failures - so a provider can report why the system last powered on, not only why it was shut down. New values are appended to keep the existing numeric ABI. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Signed-off-by: Faruque Ansari changes v12: - new patch --- include/linux/reboot.h | 28 ++++++++++++++++++++++++++++ kernel/reboot.c | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 4c5327dd7645..13f004ad1066 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -225,9 +225,28 @@ extern void orderly_reboot(void); * management errors, or an unrecoverable hardware fault requiring * immediate response. * + * @PSCR_POWER_ON: Regular cold power-on (e.g. via a PMIC power-on request= ). + * + * @PSCR_WATCHDOG: Reset caused by a watchdog timeout. + * + * @PSCR_SOFTWARE: Software-initiated reset or reboot. + * + * @PSCR_EXTERNAL: Reset asserted through an external pin or reset input. + * + * @PSCR_RTC: Wake-up or power-on triggered by the RTC. + * + * @PSCR_RESET_BUTTON: Reset triggered by a user reset button. + * + * @PSCR_CPU_CLK_FAIL: Reset caused by a CPU clock failure. + * + * @PSCR_XTAL_FAIL: Reset caused by a crystal oscillator failure. + * * @PSCR_REASON_COUNT: Number of defined power state change reasons. This * value is useful for range checking and potential future extensions * while maintaining compatibility. + * + * The reasons mirror the POWER_ON_REASON_* strings in + * ; keep the two in sync when extending. */ enum psc_reason { PSCR_UNKNOWN, @@ -236,6 +255,15 @@ enum psc_reason { PSCR_REGULATOR_FAILURE, PSCR_OVER_TEMPERATURE, PSCR_EC_PANIC, + /* Append new reasons here; the numeric order is ABI. */ + PSCR_POWER_ON, + PSCR_WATCHDOG, + PSCR_SOFTWARE, + PSCR_EXTERNAL, + PSCR_RTC, + PSCR_RESET_BUTTON, + PSCR_CPU_CLK_FAIL, + PSCR_XTAL_FAIL, =20 /* Number of reasons */ PSCR_REASON_COUNT, diff --git a/kernel/reboot.c b/kernel/reboot.c index e53fb31bad06..5d4ecc82b35e 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -1102,6 +1102,14 @@ static const struct psc_reason_desc psc_reason_descs= [] =3D { [PSCR_REGULATOR_FAILURE] =3D { "regulator-failure", POWER_ON_REASON_REGUL= ATOR_FAILURE }, [PSCR_OVER_TEMPERATURE] =3D { "over-temperature", POWER_ON_REASON_OVER_= TEMPERATURE }, [PSCR_EC_PANIC] =3D { "ec-panic", POWER_ON_REASON_EC_PANIC }, + [PSCR_POWER_ON] =3D { "power-on", POWER_ON_REASON_REGULAR }, + [PSCR_WATCHDOG] =3D { "watchdog", POWER_ON_REASON_WATCHDOG }, + [PSCR_SOFTWARE] =3D { "software", POWER_ON_REASON_SOFTWARE }, + [PSCR_EXTERNAL] =3D { "external", POWER_ON_REASON_EXTERNAL }, + [PSCR_RTC] =3D { "rtc", POWER_ON_REASON_RTC }, + [PSCR_RESET_BUTTON] =3D { "reset-button", POWER_ON_REASON_RST_BTN }, + [PSCR_CPU_CLK_FAIL] =3D { "cpu-clock-failure", POWER_ON_REASON_CPU_CLK_F= AIL }, + [PSCR_XTAL_FAIL] =3D { "crystal-failure", POWER_ON_REASON_XTAL_FAIL }, }; =20 /** --=20 2.34.1 From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 A4B1D4EF133; Mon, 21 Sep 2026 17:41:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012473; cv=none; b=umfp7rqot/cHuyuGcxYmO9cgaoNdRMiQuPMIp6cuRrB3LN3qidNFDB3Y35ao/Ajy/fO9jp56nkIAhcauRocmk9lKiCtX5ZO75Kgod/6soh6eQScdUQ6XpylqygR66NjhA2v8LSP+yxRDeCkM0f/758si3rhUW/Od3jN90gz2JVs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012473; c=relaxed/simple; bh=3JNuDbC3MYiW0wD9BU0cQBSagn2V8MECWlf42eQnG5A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NUkuHOF7/TZppq9VBvtzQlECI4IHKfM8BEWmKjGpGrkHUxFx4OuOzAWFuexw+iRIp6tHd1lRaSA7KMhmM6mrpR40fIpPL5XKYoDXmoUv6BmJeV4IGDHJixLH5mEFoGRDgbnIkH5dn1fRHfmZjiijgGUsABx/tfVQMFO2GiIykAM= 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=gJba6/US; arc=none smtp.client-ip=205.220.168.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="gJba6/US" Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHNDCm1313647; Mon, 21 Sep 2026 17:40:43 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= D6NbZZHpPoqVn4Ft2AC0hXSIRwrxMSla9Aj6VmbIubU=; b=gJba6/USGTfg+l/N eweT4QxrowPVnYBVI6SXB9HqAnZ8YdZgitdI34RcpPddDOcyC2tsgtaKcKygP6Qi l9Jy2Jvq9yzW8xCMtrvHMrU0BnIOl+p/EK+iSJA45q1NCE6AY/UaHsNV863xRMrD nSs/nyKCl87nUK3OBl0nCSPUoRgQXZ/VtHz1FnrXdEj3JtBNkXldsoVMkojBoAYf R2AQICZQpyo6wVFE+w/VUTmW7wveLiqFo9tNlEE95V4YdW51JIuj7S/FxgeCVFXo DlUj8RnKvHo/BnP5Fl/mRG+FKwFnT1c05IMNpH1PcsjWfUL7TZPAIMX6/l4ze/8t m+FznQ== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu8xer27v-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:42 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHecvS1524350; Mon, 21 Sep 2026 17:40:40 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebcu1524318; Mon, 21 Sep 2026 17:40:39 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:30 +0530 Subject: [PATCH 5/6] power: reset: Introduce PSCR Recording Framework for Non-Volatile Storage Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-5-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari , Francesco Valla X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-ORIG-GUID: CAL5cIxUEIVy-S-qB5gVP8l0FZKgUHBS X-Authority-Analysis: v=2.4 cv=PZhqFShd c=1 sm=1 tr=0 ts=6ab16c1b cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_K5XuSEh1TEqbUxoQ0s3:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=7CQSdrXTAAAA:8 a=F-jeClLd32UZZCvIPigA:9 a=QEXdDO2ut3YA:10 a=a-qgeE7W1pNrGK8U0ZQC:22 X-Proofpoint-GUID: CAL5cIxUEIVy-S-qB5gVP8l0FZKgUHBS X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfXwvoiXW3WjZxg 3xfJqo7NDg7Yj893zUt2ro0liCNpUUKfFJpYCMKWOI3nyzR6LH/gGMV7b7OwkTGV4nP73Taem5+ izo8jVEfMMz56ULd6v+VBrjYJfo8IBWflkH+gmqAPyT9Qs8eZAeot2iRCQH3hhaDjh9oh1rKpKO jF2c/92QxbozaSpbGKRMWALuY9MYcXG1z8bALUesPtAdcOyolIfUNHmO1iAMhjNuTlaTZza8biS k2T/d2rYItMAzo204gr6w0cNFz8qQideLSowDeShR9wqDl0cOHonx8J4jFrSECl6cqeC9/HF6uZ 9ZFKVKaODLfhscp07w9s6h8blLGyblggsYqHoG/GdnyI9y3UhU6Me9rEmjeLvsBWd7O+8A9PIKC 6cV4wOZR08YZGoAYHw6B/MGQzRoDtR4rH248a61OEi0Tc/JWw5+JTvTonR/4uBCybQ/bjK3Lrcu /MFt2d2wbzsZXhzOpZg== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX4Qj86GTpqsga 3QsWNjwF2IbBufCaOhXD/eh67GM13xajdJIfT8znX9BXbFhV5/GzhKusMYJaPETquLMbgp1sN1T X4GUoB2gGiTcTNROxNGyuA5Wcz6kr8w= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 malwarescore=0 spamscore=0 impostorscore=0 priorityscore=1501 lowpriorityscore=0 suspectscore=0 bulkscore=0 phishscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 Introduce the Power State Change Reasons Recording (PSCRR) framework: a generic mechanism to record why the last power state change (shutdown or reboot) happened - under-voltage, thermal, watchdog, software-triggered, etc. - so a postmortem reason survives a reboot or an abrupt power loss. PSCRR is built around providers. A provider is either a hardware reason source (a PMIC, SoC reset controller or watchdog exposing a reset cause) or a recorder that persists the reason across a power cycle (an NVMEM or RTC scratch cell). Each provider gets a directory under /sys/kernel/pscrr/providerN/ exposing its name, backing device, the set of observed reasons (as tokens), its capabilities, the reasons it supports and - for recorders - a record policy. The reason set is deliberately not collapsed to a single winning cause, since resets are often multi-causal. Reasons are the numeric enum psc_reason values from reboot.h, shared with the POWER_ON_REASON_* vocabulary, so they store compactly in small battery-backed cells. The current reason (get/set_psc_reason(), set by the thermal/regulator/hw_protection paths) is written to every recorder from the reboot notifier. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Reported-by: Francesco Valla Closes: https://lore.kernel.org/all/178749889134.204450.5740187753642158305= .b4-review@b4/ Signed-off-by: Faruque Ansari changes v13: - Narrow pscrr_lock usage in pscrr_provider_register() to only protect pscrr_dirs list updates. - Check pscrr_root before allocation and limit locking around ida_alloc() and kobject/sysfs setup. changes v12: - rework into a multi-provider framework: one sysfs directory per provider instead of a single backend - add per-provider capabilities, supported_reasons and record policy - register providers through a const ops table via (devm_)pscrr_provider_register() - report reasons as parsable tokens - move the sources under drivers/power/reset/pscrr/ - add a MAINTAINERS entry; PSCRR depends on POWER_RESET - drop Reviewed-by: Matti Vaittinen; the framework was substantially reworked since it was given changes v10: - make g_pscrr static changes v8: - introduce struct pscrr_core to encapsulate backend and locking - replace global mutex and backend pointer with centralized pscrr_core - use DEFINE_GUARD() + guard(g_pscrr) for scoped mutex locking - simplify code using local backend pointer after locking - prepare code structure for future multi-backend support changes v7: - make write_reason optional - update documentation changes v6: - move enum pscr_reason to kernel reboot core - move reason storage to reboot core - add locking --- MAINTAINERS | 11 + drivers/power/reset/Kconfig | 2 + drivers/power/reset/Makefile | 1 + drivers/power/reset/pscrr/Kconfig | 33 ++ drivers/power/reset/pscrr/Makefile | 2 + drivers/power/reset/pscrr/pscrr.c | 633 +++++++++++++++++++++++++++++++++= ++++ include/linux/pscrr.h | 107 +++++++ 7 files changed, 789 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 05215fcc18a5..a7145dbb6eb7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21967,6 +21967,17 @@ F: Documentation/devicetree/bindings/connector/pci= e-m2-e-connector.yaml F: Documentation/devicetree/bindings/connector/pcie-m2-m-connector.yaml F: drivers/power/sequencing/pwrseq-pcie-m2.c =20 +POWER STATE CHANGE REASON RECORDING (PSCRR) +M: Oleksij Rempel +M: Faruque Ansari +R: Pengutronix Kernel Team +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-kernel-pscrr +F: drivers/power/reset/pscrr/ +F: include/linux/pscrr.h +F: tools/testing/selftests/pscrr/ + POWER STATE COORDINATION INTERFACE (PSCI) M: Mark Rutland M: Lorenzo Pieralisi diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index bce996bbef28..f24ff89dd94e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -377,3 +377,5 @@ config POWER_RESET_QEMU_VIRT_CTRL provides this controller, such as the m68k virt machine. =20 endif + +source "drivers/power/reset/pscrr/Kconfig" diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index e31cab4ba78e..c73dc747e36c 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) +=3D keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) +=3D syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) +=3D syscon-poweroff.o obj-$(CONFIG_POWER_RESET_RMOBILE) +=3D rmobile-reset.o +obj-$(CONFIG_PSCRR) +=3D pscrr/ obj-$(CONFIG_REBOOT_MODE) +=3D reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) +=3D syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SC27XX) +=3D sc27xx-poweroff.o diff --git a/drivers/power/reset/pscrr/Kconfig b/drivers/power/reset/pscrr/= Kconfig new file mode 100644 index 000000000000..72de82731b53 --- /dev/null +++ b/drivers/power/reset/pscrr/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig PSCRR + bool "Power State Change Reasons Recording (PSCRR) Framework" + depends on POWER_RESET + help + Enables the Power State Change Reasons Recording (PSCRR) framework. + + PSCRR records why the system last shut down or rebooted into + non-volatile storage, so the reason survives the reset and can be + read by the bootloader or early user space on the next boot. Reasons + come from software (thermal or regulator hardware-protection events, + a watchdog pretimeout, a kernel panic, a controlled reboot) or from + hardware reset-cause registers (PMIC, SoC reset controller, watchdog). + + It helps whenever the real cause would otherwise be lost or ambiguous + on the next boot, for example: + + - the reset is abrupt and block storage (eMMC/NAND) may not survive + it; on systems that can detect the event in time - such as an + imminent power loss with a short backup-capacitor window - the + reason can still be persisted first; + + - the hardware does not preserve the cause and the boot looks like a + plain power-on reset, e.g. a watchdog pretimeout that escalates + into a panic reboot would otherwise be indistinguishable from a + POR. + + The framework is extensible and exposes software-defined and + hardware-reported reasons through one interface. Sudden power cuts, + CPU freezes or other uncontrolled resets may still go unrecorded + unless hardware provides the reset cause. + + If unsure, say N. diff --git a/drivers/power/reset/pscrr/Makefile b/drivers/power/reset/pscrr= /Makefile new file mode 100644 index 000000000000..e5530a858971 --- /dev/null +++ b/drivers/power/reset/pscrr/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PSCRR) +=3D pscrr.o diff --git a/drivers/power/reset/pscrr/pscrr.c b/drivers/power/reset/pscrr/= pscrr.c new file mode 100644 index 000000000000..ab0974d0423e --- /dev/null +++ b/drivers/power/reset/pscrr/pscrr.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pscrr.c - Core Power State Change Reason Recording + * + * PSCRR records why the last power state change (shutdown/reboot) happene= d. + * Reasons come from providers: hardware reset-cause registers (PMIC, SoC = reset + * controller, watchdog), persistent recorders (NVMEM/RTC scratch), the + * bootloader's device-tree /chosen/reset-source (a built-in provider here= ), or + * test stubs. Each provider gets a directory under /sys/kernel/pscrr/ and= reports + * the full set of reasons it observed - the picture is deliberately not + * collapsed to a single "winning" cause, since resets are often multi-cau= sal. + * + * Sysfs (per provider, under /sys/kernel/pscrr/providerN/): + * name ro human label of the provider + * device symlink to the backing device (if any) + * reason the provider's reason set, as tokens; writable (re= cord + * one reason) when the provider supports it + * caps ro non-default capabilities ("writable"); empty f= or a + * read-only, single-slot provider + * supported_reasons ro reasons this provider can report or record + * record_policy recorders only: keep the "first" or "last" reason + * recorded in a power cycle + * + * The kernel keeps the first (root cause) and last power-state-change rea= son + * (get_psc_first_reason()/get_psc_reason(), set by the thermal, regulator= and + * hw_protection paths). At reboot each recorder is given the first or the= last + * reason according to its record policy. + * + * Copyright (C) 2025 Pengutronix, Oleksij Rempel + */ + +#define pr_fmt(fmt) "PSCRR: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct kobject *pscrr_root; +static DEFINE_MUTEX(pscrr_lock); +static LIST_HEAD(pscrr_dirs); +static DEFINE_IDA(pscrr_ida); + +/* + * Record policy: when several reasons are recorded in one power cycle, ke= ep the + * first (root cause) or overwrite with the last. Global, tunable via sysf= s. + */ +enum pscrr_record_policy { + PSCRR_RECORD_FIRST, + PSCRR_RECORD_LAST, +}; + +/* + * Per-provider sysfs directory. Core-owned and self-freeing on kobject_pu= t(), + * so its lifetime is decoupled from the caller-owned struct pscrr_provide= r. + */ +struct pscrr_provider_dir { + struct kobject kobj; + struct pscrr_provider *provider; + struct list_head node; + int id; + enum pscrr_record_policy policy; /* single-slot recorder: keep first/last= */ + bool recorded; /* a reason was recorded this power cycle */ +}; + +static inline struct pscrr_provider_dir *to_pscrr_dir(struct kobject *kobj) +{ + return container_of(kobj, struct pscrr_provider_dir, kobj); +} + +/*----------------------------------------------------------------------*/ +/* Per-provider attributes */ +/*----------------------------------------------------------------------*/ + +static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", to_pscrr_dir(kobj)->provider->name); +} + +static struct kobj_attribute pscrr_name_attr =3D __ATTR_RO(name); + +static int pscrr_parse_reason(const char *buf, enum psc_reason *out) +{ + unsigned int val; + + /* Accept either a decimal index or a reason token. */ + if (!kstrtouint(buf, 0, &val)) { + if (val >=3D PSCR_REASON_COUNT) + return -ERANGE; + *out =3D val; + return 0; + } + + return psc_reason_from_token(buf, out); +} + +static ssize_t reason_show(struct kobject *kobj, struct kobj_attribute *at= tr, + char *buf) +{ + struct pscrr_provider *p =3D to_pscrr_dir(kobj)->provider; + DECLARE_BITMAP(reasons, PSCR_REASON_COUNT); + ssize_t len =3D 0; + int bit, ret; + + bitmap_zero(reasons, PSCR_REASON_COUNT); + + ret =3D p->ops->read_reasons(p, reasons); + if (ret) + return ret; + + for_each_set_bit(bit, reasons, PSCR_REASON_COUNT) + len +=3D sysfs_emit_at(buf, len, "%s%s", len ? " " : "", + psc_reason_to_token(bit)); + len +=3D sysfs_emit_at(buf, len, "\n"); + + return len; +} + +/* Record @reason into @dir honouring the global record policy. */ +static int pscrr_do_record(struct pscrr_provider_dir *dir, enum psc_reason= reason) +{ + struct pscrr_provider *p =3D dir->provider; + int ret; + + if (!p->ops->write_reason) + return -EPERM; + + /* + * PSCR_UNKNOWN clears the slot and releases the latch, regardless of + * policy, so a reason recorded afterwards is taken again. + */ + if (reason =3D=3D PSCR_UNKNOWN) { + ret =3D p->ops->write_reason(p, reason); + if (ret) + return ret; + + dir->recorded =3D false; + return 0; + } + + /* Reject reasons the provider does not advertise (NULL =3D=3D all). */ + if (p->supported_reasons && !test_bit(reason, p->supported_reasons)) + return -EOPNOTSUPP; + + /* "first" policy: keep the first reason recorded this power cycle. */ + if (dir->policy =3D=3D PSCRR_RECORD_FIRST && dir->recorded) + return 0; + + ret =3D p->ops->write_reason(p, reason); + if (ret) + return ret; + + dir->recorded =3D true; + + return 0; +} + +static ssize_t reason_store(struct kobject *kobj, struct kobj_attribute *a= ttr, + const char *buf, size_t count) +{ + struct pscrr_provider_dir *dir =3D to_pscrr_dir(kobj); + enum psc_reason reason; + int ret; + + if (!dir->provider->ops->write_reason) + return -EPERM; + + ret =3D pscrr_parse_reason(buf, &reason); + if (ret) + return ret; + + /* Serialise the record state against concurrent stores and the notifier.= */ + scoped_guard(mutex, &pscrr_lock) + ret =3D pscrr_do_record(dir, reason); + if (ret) + return ret; + + return count; +} + +static struct kobj_attribute pscrr_reason_attr =3D + __ATTR(reason, 0644, reason_show, reason_store); + +static ssize_t caps_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct pscrr_provider *p =3D to_pscrr_dir(kobj)->provider; + ssize_t len =3D 0; + + /* Readable and single-slot are the defaults and not listed. */ + if (p->ops->write_reason) + len +=3D sysfs_emit_at(buf, len, "writable"); + + return len + sysfs_emit_at(buf, len, "\n"); +} + +static struct kobj_attribute pscrr_caps_attr =3D __ATTR_RO(caps); + +static ssize_t supported_reasons_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct pscrr_provider *p =3D to_pscrr_dir(kobj)->provider; + const unsigned long *sup =3D p->supported_reasons; + ssize_t len =3D 0; + int i; + + for (i =3D 0; i < PSCR_REASON_COUNT; i++) { + if (sup && !test_bit(i, sup)) /* NULL means all */ + continue; + len +=3D sysfs_emit_at(buf, len, "%s%s", len ? " " : "", + psc_reason_to_token(i)); + } + + return len + sysfs_emit_at(buf, len, "\n"); +} + +static struct kobj_attribute pscrr_supported_attr =3D + __ATTR(supported_reasons, 0444, supported_reasons_show, NULL); + +static ssize_t record_policy_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct pscrr_provider_dir *dir =3D to_pscrr_dir(kobj); + + return sysfs_emit(buf, "%s\n", + READ_ONCE(dir->policy) =3D=3D PSCRR_RECORD_FIRST ? + "first" : "last"); +} + +static ssize_t record_policy_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct pscrr_provider_dir *dir =3D to_pscrr_dir(kobj); + + guard(mutex)(&pscrr_lock); + + if (sysfs_streq(buf, "first")) + WRITE_ONCE(dir->policy, PSCRR_RECORD_FIRST); + else if (sysfs_streq(buf, "last")) + WRITE_ONCE(dir->policy, PSCRR_RECORD_LAST); + else + return -EINVAL; + + return count; +} + +static struct kobj_attribute pscrr_record_policy_attr =3D + __ATTR(record_policy, 0644, record_policy_show, record_policy_store); + +static struct attribute *pscrr_dir_attrs[] =3D { + &pscrr_name_attr.attr, + &pscrr_reason_attr.attr, + &pscrr_caps_attr.attr, + &pscrr_supported_attr.attr, + &pscrr_record_policy_attr.attr, + NULL, +}; + +static umode_t pscrr_dir_is_visible(struct kobject *kobj, struct attribute= *attr, + int n) +{ + struct pscrr_provider *p =3D to_pscrr_dir(kobj)->provider; + + /* A provider without write_reason() exposes reason read-only. */ + if (attr =3D=3D &pscrr_reason_attr.attr && !p->ops->write_reason) + return 0444; + + /* record_policy only applies to a (single-slot) recorder. */ + if (attr =3D=3D &pscrr_record_policy_attr.attr && !p->ops->write_reason) + return 0; + + return attr->mode; +} + +static const struct attribute_group pscrr_dir_group =3D { + .attrs =3D pscrr_dir_attrs, + .is_visible =3D pscrr_dir_is_visible, +}; + +static const struct attribute_group *pscrr_dir_groups[] =3D { + &pscrr_dir_group, + NULL, +}; + +static void pscrr_dir_release(struct kobject *kobj) +{ + kfree(to_pscrr_dir(kobj)); +} + +static const struct kobj_type pscrr_dir_ktype =3D { + .sysfs_ops =3D &kobj_sysfs_ops, + .release =3D pscrr_dir_release, + .default_groups =3D pscrr_dir_groups, +}; + +/*----------------------------------------------------------------------*/ +/* Provider registration */ +/*----------------------------------------------------------------------*/ + +/** + * pscrr_provider_register - register a power state change reason provider + * @p: caller-owned provider description + * + * Creates /sys/kernel/pscrr/providerN/ with "name" and "reason" attributes + * and, when @p->dev is set, a "device" symlink. @p->reason is writable wh= en + * @p provides write_reason(). The provider must outlive the matching + * pscrr_provider_unregister() call. + * + * Return: 0 on success or a negative errno. + */ +int pscrr_provider_register(struct pscrr_provider *p) +{ + struct pscrr_provider_dir *dir; + int ret; + + if (!p || !p->name || !p->ops || !p->ops->read_reasons) + return -EINVAL; + + /* + * pscrr_root is set once at core init and cleared at core exit; + * neither transition is serialised against this function by + * pscrr_lock, so check it up front rather than under the lock below. + */ + if (!pscrr_root) + return -ENODEV; + + dir =3D kzalloc_obj(*dir); + if (!dir) + return -ENOMEM; + + dir->provider =3D p; + dir->policy =3D PSCRR_RECORD_FIRST; + + /* ida_alloc() and the kobject/sysfs calls below are individually thread-= safe. */ + dir->id =3D ida_alloc(&pscrr_ida, GFP_KERNEL); + if (dir->id < 0) { + ret =3D dir->id; + kfree(dir); + return ret; + } + + ret =3D kobject_init_and_add(&dir->kobj, &pscrr_dir_ktype, pscrr_root, + "provider%d", dir->id); + if (ret) { + /* + * kobject_init_and_add() failed: per its contract only + * kobject_put() may follow, no kobject_del(). + */ + ida_free(&pscrr_ida, dir->id); + kobject_put(&dir->kobj); + return ret; + } + + if (p->dev) { + ret =3D sysfs_create_link(&dir->kobj, &p->dev->kobj, "device"); + if (ret) + goto err_del; + } + + /* pscrr_lock only serialises pscrr_dirs against concurrent (un)registrat= ion. */ + scoped_guard(mutex, &pscrr_lock) + list_add_tail(&dir->node, &pscrr_dirs); + + return 0; + +err_del: + kobject_del(&dir->kobj); + ida_free(&pscrr_ida, dir->id); + kobject_put(&dir->kobj); + return ret; +} +EXPORT_SYMBOL_GPL(pscrr_provider_register); + +/** + * pscrr_provider_unregister - remove a previously registered provider + * @p: the provider passed to pscrr_provider_register() + */ +void pscrr_provider_unregister(struct pscrr_provider *p) +{ + struct pscrr_provider_dir *dir =3D NULL, *iter; + + scoped_guard(mutex, &pscrr_lock) { + list_for_each_entry(iter, &pscrr_dirs, node) { + if (iter->provider =3D=3D p) { + dir =3D iter; + list_del(&dir->node); + break; + } + } + } + + if (!dir) + return; + + /* + * Tear the sysfs directory down outside pscrr_lock: kobject_del() + * drains in-flight reason/record_policy stores, which take pscrr_lock, + * so holding it here would deadlock. Release the id only once the + * directory is gone, so a concurrent register cannot reuse it and + * collide on the providerN name. + */ + if (p->dev) + sysfs_remove_link(&dir->kobj, "device"); + kobject_del(&dir->kobj); + ida_free(&pscrr_ida, dir->id); + kobject_put(&dir->kobj); +} +EXPORT_SYMBOL_GPL(pscrr_provider_unregister); + +static void pscrr_provider_devm_release(void *p) +{ + pscrr_provider_unregister(p); +} + +/** + * devm_pscrr_provider_register - device-managed reason provider registrat= ion + * @dev: device the provider belongs to; also backs the "device" symlink + * @name: human-readable provider label + * @ops: provider callback table; read_reasons() is required, write_reason= () is + * optional and makes the provider a recorder + * @supported_reasons: bitmap of the reasons the provider supports, or NULL + * for all; set before the provider is exposed in sysfs + * @priv: provider private data, handed back to the @ops callbacks + * + * Allocates and registers a struct pscrr_provider and schedules its + * unregistration when @dev is unbound, so the caller keeps no reference t= o it. + * + * Return: the registered provider on success or an ERR_PTR() on failure. = When + * CONFIG_PSCRR is disabled the call resolves to a stub returning NULL, so + * callers need no IS_ENABLED() guard. + */ +struct pscrr_provider * +devm_pscrr_provider_register(struct device *dev, const char *name, + const struct pscrr_provider_ops *ops, + const unsigned long *supported_reasons, void *priv) +{ + struct pscrr_provider *p; + int ret; + + p =3D devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return ERR_PTR(-ENOMEM); + + p->name =3D name; + p->dev =3D dev; + p->ops =3D ops; + p->supported_reasons =3D supported_reasons; + p->priv =3D priv; + + ret =3D pscrr_provider_register(p); + if (ret) + return ERR_PTR(ret); + + ret =3D devm_add_action_or_reset(dev, pscrr_provider_devm_release, p); + if (ret) + return ERR_PTR(ret); + + return p; +} +EXPORT_SYMBOL_GPL(devm_pscrr_provider_register); + +/*----------------------------------------------------------------------*/ +/* Record path: reboot notifier writes the current reason to recorders */ +/*----------------------------------------------------------------------*/ + +/* + * Record the current power-state-change reason into every provider, giving + * each the first (root cause) or the last reason according to its record + * policy. The kernel keeps both, so "first" is meaningful even when later + * events overwrite the last reason. The caller holds pscrr_lock, or runs = where + * the provider list is stable (panic). + */ +static void pscrr_record_current(void) +{ + enum psc_reason first =3D get_psc_first_reason(); + enum psc_reason last =3D get_psc_reason(); + struct pscrr_provider_dir *dir; + + list_for_each_entry(dir, &pscrr_dirs, node) + pscrr_do_record(dir, dir->policy =3D=3D PSCRR_RECORD_FIRST ? + first : last); +} + +static int pscrr_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + guard(mutex)(&pscrr_lock); + + /* + * A reboot, halt or power-off that reaches here with no more specific + * reason is software-initiated by definition. Record it as such rather + * than leaving it unattributed; a real cause set earlier (thermal, + * under-voltage, ...) is already latched and left untouched. + */ + if (get_psc_reason() =3D=3D PSCR_UNKNOWN) + set_psc_reason(PSCR_SOFTWARE); + + pscrr_record_current(); + + return NOTIFY_DONE; +} + +static struct notifier_block pscrr_reboot_nb =3D { + .notifier_call =3D pscrr_reboot_notifier, +}; + +/*----------------------------------------------------------------------*/ +/* Built-in provider: device-tree /chosen/reset-source */ +/*----------------------------------------------------------------------*/ + +/* + * Bootloaders such as barebox record the SoC reset cause in the standard + * device-tree /chosen/reset-source property. When it is present, surface = it as + * a read-only, device-less provider so the bootloader's view of the last = reset + * shows up next to any hardware or software providers - the framework just + * reads the property already there, with no dedicated node or new binding. + */ +static const struct { + const char *name; + enum psc_reason reason; +} pscrr_reset_source_map[] =3D { + { "POR", PSCR_POWER_ON }, + { "RST", PSCR_SOFTWARE }, + { "WDG", PSCR_WATCHDOG }, + { "THERM", PSCR_OVER_TEMPERATURE }, + { "EXT", PSCR_EXTERNAL }, + { "BROWNOUT", PSCR_UNDER_VOLTAGE }, +}; + +static const unsigned long +pscrr_reset_source_supported[BITS_TO_LONGS(PSCR_REASON_COUNT)] =3D { + BIT(PSCR_UNDER_VOLTAGE) | BIT(PSCR_OVER_TEMPERATURE) | + BIT(PSCR_POWER_ON) | BIT(PSCR_WATCHDOG) | BIT(PSCR_SOFTWARE) | + BIT(PSCR_EXTERNAL), +}; + +/* Parsed once at init; read back by the provider's read_reasons(). */ +static enum psc_reason pscrr_reset_source_reason =3D PSCR_UNKNOWN; + +static int pscrr_reset_source_read(struct pscrr_provider *p, + unsigned long *reasons) +{ + set_bit(pscrr_reset_source_reason, reasons); + + return 0; +} + +static const struct pscrr_provider_ops pscrr_reset_source_ops =3D { + .read_reasons =3D pscrr_reset_source_read, +}; + +static struct pscrr_provider pscrr_reset_source_provider =3D { + .name =3D "reset-source", + .ops =3D &pscrr_reset_source_ops, + .supported_reasons =3D pscrr_reset_source_supported, +}; + +static void __init pscrr_register_reset_source(void) +{ + const char *name; + int i, ret; + + if (!IS_ENABLED(CONFIG_OF) || !of_chosen) + return; + + if (of_property_read_string(of_chosen, "reset-source", &name)) + return; + + for (i =3D 0; i < ARRAY_SIZE(pscrr_reset_source_map); i++) + if (!strcmp(name, pscrr_reset_source_map[i].name)) { + pscrr_reset_source_reason =3D pscrr_reset_source_map[i].reason; + break; + } + + ret =3D pscrr_provider_register(&pscrr_reset_source_provider); + if (ret) + pr_warn("failed to register the reset-source provider: %d\n", ret); +} + +/*----------------------------------------------------------------------*/ +/* Module init/exit */ +/*----------------------------------------------------------------------*/ + +static int __init pscrr_core_init(void) +{ + int ret; + + pscrr_root =3D kobject_create_and_add("pscrr", kernel_kobj); + if (!pscrr_root) + return -ENOMEM; + + ret =3D register_reboot_notifier(&pscrr_reboot_nb); + if (ret) { + kobject_put(pscrr_root); + pscrr_root =3D NULL; + return ret; + } + + pscrr_register_reset_source(); + + return 0; +} + +static void __exit pscrr_core_exit(void) +{ + pscrr_provider_unregister(&pscrr_reset_source_provider); + unregister_reboot_notifier(&pscrr_reboot_nb); + kobject_put(pscrr_root); + pscrr_root =3D NULL; + ida_destroy(&pscrr_ida); +} + +/* Bring the core up before device drivers probe and register providers. */ +subsys_initcall(pscrr_core_init); +module_exit(pscrr_core_exit); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_AUTHOR("Faruque Ansari "); +MODULE_DESCRIPTION("Power State Change Reason Recording (PSCRR) core"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/pscrr.h b/include/linux/pscrr.h new file mode 100644 index 000000000000..330d6ac191bd --- /dev/null +++ b/include/linux/pscrr.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * pscrr.h - Public header for Power State Change Reason Recording (PSCRR). + * + * Copyright (C) 2025 Pengutronix, Oleksij Rempel + */ + +#ifndef __PSCRR_H__ +#define __PSCRR_H__ + +#include + +struct device; +struct pscrr_provider; + +/** + * struct pscrr_provider_ops - Provider callbacks. + * @read_reasons: Fill @reasons with the complete set this source observed. + * @reasons is a bitmap of PSCR_REASON_COUNT bits; set members + * with set_bit(PSCR_x, reasons). Return 0 on success or a + * negative errno. Required. Driver state is reached via p->priv. + * @write_reason: Persist @reason. Called from the reboot notifier for + * recorder-capable providers so the cause survives the power + * cycle. Leave NULL for read-only hardware sources. + * + * A const ops table per provider type; extend it with new callbacks witho= ut + * touching the registration signature or existing callers. + */ +struct pscrr_provider_ops { + int (*read_reasons)(struct pscrr_provider *p, unsigned long *reasons); + int (*write_reason)(struct pscrr_provider *p, enum psc_reason reason); +}; + +/** + * struct pscrr_provider - A source (and optionally recorder) of power sta= te + * change reasons. + * + * A provider represents one place the system can learn *why* the last pow= er + * state change happened: a hardware reset-cause register (PMIC, SoC SRC, + * watchdog), a persistent recorder (NVMEM/RTC scratch), or a test stub. E= ach + * registered provider gets its own directory under /sys/kernel/pscrr/, so= the + * full, un-prioritised picture is visible: several providers - and several + * reasons within one provider - can be reported simultaneously. + * + * @name: Human-readable label, exported as the "name" attribute. The + * directory itself is core-indexed (providerN), so this need not + * be unique. Required. + * @dev: Backing device. When set it is exported as the "device" symlink + * in the provider directory, tying the reason to real hardware. + * May be NULL (e.g. for a test provider). + * @ops: Provider callbacks. Required. + * @supported_reasons: Bitmap of the reasons this provider can store or re= port, + * limited e.g. by the storage size. 0 means all reasons. + * @priv: Provider private data, passed back through the callbacks. + * + * Providers are readable and single-slot by default; only capabilities be= yond + * that (currently: writable) are advertised. + */ +struct pscrr_provider { + const char *name; + struct device *dev; + const struct pscrr_provider_ops *ops; + const unsigned long *supported_reasons; + void *priv; +}; + +#if IS_ENABLED(CONFIG_PSCRR) +int pscrr_provider_register(struct pscrr_provider *p); +void pscrr_provider_unregister(struct pscrr_provider *p); + +/** + * devm_pscrr_provider_register - allocate, fill and register a provider + * @dev: device the provider belongs to (also the "device" symlink target) + * @name: provider label + * @ops: provider callbacks + * @supported_reasons: bitmap of supported reasons, or NULL for all + * @priv: driver state passed back through the callbacks + * + * The provider is unregistered automatically on device teardown. + * + * Return: the provider on success, ERR_PTR() on failure, or NULL when PSC= RR + * is not built (so the caller need not guard the call). + */ +struct pscrr_provider * +devm_pscrr_provider_register(struct device *dev, const char *name, + const struct pscrr_provider_ops *ops, + const unsigned long *supported_reasons, void *priv); +#else +static inline int pscrr_provider_register(struct pscrr_provider *p) +{ + return -EOPNOTSUPP; +} + +static inline void pscrr_provider_unregister(struct pscrr_provider *p) +{ +} + +static inline struct pscrr_provider * +devm_pscrr_provider_register(struct device *dev, const char *name, + const struct pscrr_provider_ops *ops, + const unsigned long *supported_reasons, void *priv) +{ + return NULL; +} +#endif + +#endif /* __PSCRR_H__ */ --=20 2.34.1 From nobody Thu Sep 24 20:02:44 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.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 89AE74ED19C; Mon, 21 Sep 2026 17:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012462; cv=none; b=sGgxNpmQeZH06Rl+RkT+KSk2SgfPB/y0vWySRl7Ij5ig8YfORXv4vkJvJlTKmngNzsbiOzGz1ull26grTHTNhNpbplzo4mky7IVhvFs7uJ18IhFnJgRCjriKiihjXIUpjGTCbkboxdQES6TqzaeVUu39yRo9hnGBcPHKbsHy1Ks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790012462; c=relaxed/simple; bh=X8qD955WHpybwj+yRCtCau0eQllUlA6eMCOWbXmSqzE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Rp8eGeGX2WYHcskrFR7gyk4dHk853ygE1pJnN5XjVoslKCYkW9WGodfR/+XWSFrsyih9MWg1peVE+RfUHVNSrRejRAp7WKH7ijZzch4wr55iqhe/PlHF075Cy+/4UV05vUaiIPbL8ZVUrYd1XoPfbNGejaIKQsGfI4ZnwRSWlyY= 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=FkRc21jo; arc=none smtp.client-ip=205.220.168.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="FkRc21jo" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAAnv827824; Mon, 21 Sep 2026 17:40:43 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= AdqM27vKJxtZ8aMPD1Fh0zcIEMbIa/m6XRbPgdIQBT8=; b=FkRc21jo8UwoJGSp LY6FEJe2h5YqgxqbkMpa/Y5/6i4/Kr2Cj0R7xg2ArvQ7uzcVuyTOSUrByuLtNRBm oyRXbLpwkk2xLRphQbxJdT7oOfxxKWbFFlKB5vlzbiTqPrKVhyDnc3ofzIzItZAt 1GKDOpBst9aJP10RmRPm6Yy8RdcuiImjx0zLZzKeuLyI5iKahdOBjrkiLF9Dhvyv 10x+ETpVEBHuMEtWUs8B3iRblcSJmC9s+8ygJVT3J2wcMh9YFsc1XKOq5+QhNWl+ BPQWteLPYVxr32Vqw50t8fp2HI59+S0tmj38YMmkUwOx4iGrYStVfHtiBj3c3rMX 9Tc73A== Received: from apblrppmta01.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com [103.229.18.19]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gu17gt8xe-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 17:40:42 +0000 (GMT) Received: from pps.filterd (apblrppmta01.qualcomm.com [127.0.0.1]) by APBLRPPMTA01.qualcomm.com (8.18.1.11/8.18.1.11) with ESMTP id 68LHedCq1524369; Mon, 21 Sep 2026 17:40:40 GMT Received: from hu-ffaruque-hyd.qualcomm.com (hu-ffaruque-hyd.qualcomm.com [10.147.245.31]) by APBLRPPMTA01.qualcomm.com (PPS) with ESMTP id 68LHebcv1524318; Mon, 21 Sep 2026 17:40:40 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 23:10:31 +0530 Subject: [PATCH 6/6] Documentation: Add sysfs documentation for PSCRR Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260921-pscrr-framework-v1-6-a5ebe994a98a@oss.qualcomm.com> References: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> In-Reply-To: <20260921-pscrr-framework-v1-0-a5ebe994a98a@oss.qualcomm.com> To: Sebastian Reichel , Benson Leung , Tzung-Bi Shih , Guenter Roeck , Liam Girdwood , Mark Brown , Matti Vaittinen , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , Oleksij Rempel , Pengutronix Kernel Team Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, avaneesh.dwivedi@oss.qualcomm.com, Umang Chheda , Faruque Ansari X-Mailer: b4 0.14.3 X-Proofpoint-Reinject: loops=2 maxloops=12 X-QCInternal: smtphost X-QCInternal: smtphost X-Proofpoint-GUID: kyL7eRc0bMQzdBo-nhaHhAe1tI4J8H8C X-Authority-Analysis: v=2.4 cv=IewSymqa c=1 sm=1 tr=0 ts=6ab16c1b cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=EUspDBNiAAAA:8 a=eG3WmpGALsbKIvMRAg8A:9 a=QEXdDO2ut3YA:10 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX7i1eEt+qZU/D gc5NaoTxYhnPQgzTQLRhCwcI/xDnkRE/xlLfNDa4dZNwPLMhb6CWhR5xCX5gD5ZVDB+vVROuwz+ CZ+/Fp/EaqIIxHtJOMMOAQkxrNKTQVqsWuQM5vet4KiMaI1q0V34p0ggedZIqBcd+JtI0WUKTq9 ABeKnT7y5PpbzvRT9Ybk2z9WquI4a9fvUsCCEny4ygwBJk7/SBt0kJ77tVtjXzxw/6mdhlGYNwL R6hXsP69Vc4zg/K9A5UdlBe6jLfRJUWeDNoqWNnj3y2cQpqA3cMvRh0BxeBWDeJUQpaNBNpaiTJ wLLeY+Szco4FGcK46QQTh/gERNETxa/NtUxDAQTGz0ptr2CQW8NhwcKS4gvHCAj5fOWKvrfTpnd cfNhcB23Uswp4rk3zcN28NecuSSGuehX9pgsfqceLDdxMRMBVjtPX2Xr4+/E7oWDz5iVnkQ+Uzn sfmO8ibC1d/rA+OuDpA== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI1OCBTYWx0ZWRfX9T6iJ171sBkl G/7IJAq5FKLEgA7dlqbsyJNGtFwBTwW56kAOnLRhQk6PIkViSick7Jvbav5U6WpJL3vHDXlUpP+ tfASBst6cE8fO8qWDXtFy6ti2gxXn68= X-Proofpoint-ORIG-GUID: kyL7eRc0bMQzdBo-nhaHhAe1tI4J8H8C X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-21_05,2026-09-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 priorityscore=1501 impostorscore=0 suspectscore=0 lowpriorityscore=0 spamscore=0 phishscore=0 malwarescore=0 adultscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210258 Document the Power State Change Reasons Recording (PSCRR) sysfs interface under /sys/kernel/pscrr/: the per-provider directories and their name, device, reason, caps, supported_reasons and record_policy attributes, including the stable reason token values. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Signed-off-by: Faruque Ansari changes v12: - rewrite for the per-provider interface (providerN/ directories with caps, supported_reasons and record_policy) - rename the file to sysfs-kernel-pscrr to match the sysfs path - refresh KernelVersion/Date - drop Reviewed-by: Matti Vaittinen; the documentation was rewritten changes v8: - simplify and clarify example sysfs value comments - add note that not all values are meaningful on every system changes v7: - document expected values --- Documentation/ABI/testing/sysfs-kernel-pscrr | 108 +++++++++++++++++++++++= ++++ 1 file changed, 108 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-kernel-pscrr b/Documentation/A= BI/testing/sysfs-kernel-pscrr new file mode 100644 index 000000000000..63aa411b7362 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-pscrr @@ -0,0 +1,108 @@ +What: /sys/kernel/pscrr/ +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + Root directory of the Power State Change Reason Recording + (PSCRR) framework. It contains one subdirectory per registered + reason provider, named providerN (N is an arbitrary, stable + index assigned at registration). + + A provider is either a hardware reason source (a PMIC, SoC + reset controller or watchdog exposing a reset cause) or a + recorder that persists the current reason across a power cycle + (e.g. an NVMEM or RTC scratch cell). The set of reasons is + deliberately not collapsed to a single "winning" cause, since + resets are often multi-causal. + +What: /sys/kernel/pscrr/providerN/name +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + (RO) Human-readable label identifying the provider, e.g. + "pca9450" or "nvmem". + +What: /sys/kernel/pscrr/providerN/device +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + Symbolic link to the backing struct device of the provider. + Present only for providers that are bound to a device. + +What: /sys/kernel/pscrr/providerN/reason +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + The set of power state change reasons observed by this + provider, as a space-separated list of reason tokens (an + empty line means no reason is recorded). + + The attribute is writable only for providers that can record + a reason; for a pure hardware source it is read-only. A write + records one reason and accepts either a reason token or its + decimal index. The tokens and their stable numeric values are: + + =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + 0 unknown Unknown or unspecified reason + 1 under-voltage Supply voltage dropped below a safe level + 2 over-current Excessive current draw / possible short + 3 regulator-failure Voltage regulator failure + 4 over-temperature Unsafe temperature detected + 5 ec-panic Embedded controller (EC) panic + 6 power-on Regular cold power-on + 7 watchdog Watchdog timeout + 8 software Software-initiated reset or reboot + 9 external External reset input asserted + 10 rtc RTC-triggered wake-up or power-on + 11 reset-button User reset button + 12 cpu-clock-failure CPU clock failure + 13 crystal-failure Crystal oscillator failure + =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + The numeric order is stable ABI: new reasons are only ever + appended. A provider may support only a subset of these; see + "supported_reasons". + +What: /sys/kernel/pscrr/providerN/caps +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + (RO) Space-separated list of the provider's non-default + capabilities. Being readable and storing a single reason are + the defaults and are not listed. Currently defined: + + =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + writable the provider can record a reason (see "reason" + and "record_policy") + =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + + An empty line therefore denotes a read-only, single-slot + provider. + +What: /sys/kernel/pscrr/providerN/supported_reasons +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + (RO) Space-separated list of the reason tokens (see "reason") + this provider is able to report or record. A provider that + supports every reason lists them all. + +What: /sys/kernel/pscrr/providerN/record_policy +Date: July 2026 +KernelVersion: 7.2 +Contact: Oleksij Rempel +Description: + (RW) Policy used when more than one reason is recorded during a + single power cycle. Present only for providers that can record + (see "caps"). Valid values are: + + =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + first keep the first reason recorded this cycle (the + root cause); this is the default + last overwrite with the most recently recorded reason + =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D --=20 2.34.1