From nobody Thu Sep 24 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8697210F2; Mon, 21 Sep 2026 16:57:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009871; cv=none; b=bmM7my5KrzN//h2BRhNOLh60ynESgoe5qR+uSrvDKdXKWTx7n3i7nDTGblmFH7Z/p4s/anoylLTwverubsV7ERhotV7lD4ykPEvtRMObnmWYrpmWQKH80twVSKXuiWqrZZdOOGEwWXVd4/eZKyqWHveRXVA3qThiQg8ChVpw2jM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009871; c=relaxed/simple; bh=doy8CQVTkGDgR7A0QYfdmidqT7C6L19D+j8DtIbAz9Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lP/AGtBopPER4QG8rTmBu9pwPlj0DKZJx27QyBeyXUQekVogePTdb7y7EJLLfHx1savZ9uCKt4+F035BhwWa9OV1yXWo2HoKafOHnjKPQgKt4HgTYK/ramFZzDbkyD3K+7/eNWLntv39W0WhN6YLrs3d1ANi1GSV/Tc2OefJRiM= 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=icj0lyWj; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="icj0lyWj" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAq7H1438653; Mon, 21 Sep 2026 16:57:29 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=icj0lyWjVBS2wEFb Zl4siVJhVunSYN1YYte7IJQVi0/AKdv2CMG5X4u9o7pBgBKlSEBI/rpKu6Mbk3so wt8yrPvKI3Xx1wbv+J0iDSXKfw3pj8IkIUr/AhJngkJC4tKAnAJmTSJot0VAKWbg fn309kxXxhZM3r2V8KbHPD2flVmqwjTvoTPVppWo0+r2GYP6SUsr7LtydBvUkiXd Y4KBP/WfTjzw9NZ1RhO4/ZuIv00gViESx5UJg9Jmz/FkQBZUq2ACiQ3Menoy6Hsy bnJUastc0JPJwMbFYpvLFXa4/cGBt5g4ZrPFkVdVmIojqtuRl9ntrYHRe+Opqq5q K6LIag== 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 4gtwf5jyrk-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvNFG1464271; Mon, 21 Sep 2026 16:57:23 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 68LGvMa91464229; Mon, 21 Sep 2026 16:57:23 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:09 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-Authority-Analysis: v=2.4 cv=SJLXx+vH c=1 sm=1 tr=0 ts=6ab161f8 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=QX4gbG5DAAAA:8 a=EUspDBNiAAAA:8 a=7kjejDXy1mYeYQkWB3oA:9 a=QEXdDO2ut3YA:10 a=AbAUZ8qAyYyZVLSsDulk:22 X-Proofpoint-GUID: WSGOzgBtPC3-gc5vZHWMPrbI3yHgWO4c X-Proofpoint-ORIG-GUID: WSGOzgBtPC3-gc5vZHWMPrbI3yHgWO4c X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfXwsVRyEy9dEII gPM0kToH0gd3HzA+ANdnLERDBkYtGAaJPisl/jRJQ/69pcqUXnk8TgsJICTOqkgU7bNao/wjIql TwlSp14KV39BxNdrGmtY7wbabTNgo8Q= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX5lOg2xmsRIvV oRHY6oZAOliXSNt+DBJ2UwRkrEDzlnO/lZzmG8+Tw499WN2T9/LpTaPtYMid+NUk5PagqKaTF2U hgfhOxB3/Oi9tjFEmFiyXatBndLi8+pwLQq1pMy5r42eIbh6pxnhi2zGaswOnf0HG3p2ql+7i3G mQUnN2N7IsZoQItORbzyGuZhphFVhZtD2dAPKkYW8T10Yg3WgiUxlK5LqCl6rSlBv8luQc7Onz4 d5VvKtTeoQzbQTMmeng5MKjmvelyd5j0d+4q2BK9Ynn/wG/64d0LOSGY2zBcZAUmrlKLh4nItJo 8nPzjFriGMENMcs/2CEt5VTwW8ySolMTJDrt7fxOrXr4E9ya38R6+J44xlU8btN+ynJk6u25X8a WEpbvfYmYmR92buAAkg3d9T5UCHpQvQRu3LfZYExDeiILZ5AvqaTN5VwAw7k4/J0CNF1I5VWt7n CUvBxba0L3zg04XkOQA== 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 lowpriorityscore=0 priorityscore=1501 impostorscore=0 suspectscore=0 malwarescore=0 adultscore=0 clxscore=1015 bulkscore=0 spamscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B27414E1C74; Mon, 21 Sep 2026 16:57:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009875; cv=none; b=XQfPSQAM12oISVwyCLA/5FUxKJfCOgPxstTsE4datyBTtogV+30TvsP37EHAVI5/MQrwhNxTCt/AxJwUBI7OGJqZUDPqrL2HIuwjvFBI0EbjQVHIm+Aql7lIoBYYN/O0UyNJcefzFTOBzZDO2TJzM0efpK3RSKHxU96Qh8vfU8M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009875; c=relaxed/simple; bh=fZpsBj5rscCpcqM5vXniLGnsPqDQAysthyMVlBnfvpg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=T2vHPZRba7AzXA9olJNDEgV0zNrPwhfSJ/XgTC+EVqeRKseOQMnfXlgH9ydtG3PnM87U+hGxlEPNvSyvAaai2LU32VDRMbbDwqy31Br3H52SdGCuVa/ZCd23O7aNK2NznOhty8Oepnvdb4ufyVpTNsXmbuR9qgNuuuxi9dmJ8q8= 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=XmWzW+B6; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="XmWzW+B6" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAG3M2163320; Mon, 21 Sep 2026 16:57:29 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= NlCu/iPRonYQuztknHWABCSu8tmmz5q5wbSIyRp0Dwg=; b=XmWzW+B6h8bcGupv dYiF+62+HIDo7zwjlgEjM4eKiqTpd/POLO5swrgV656sApXUmVyjHJvETjJo/ANm RqP4yKgj4HWysJR0WtV/C7EVO2GGuvb9t3a155sl9tn0aq9FRFCtadOpouoXhNG2 UXWc4ybt3aL0CLPMJszfyhH6peK+5KxWZThfdplN1o8afefBIrYl8CpBmdZzXjET 4LNbBoRkrA3DKdXHxO8k2Qqcaehk930Si4btMr2jhFlAI7aLUgZmofJjRtnKi55f tB6uYa/bdQbCLP6zTQyGB40hW88rzAp2MsWn/pDdyPeyxM1KvuGXivWj6OjHenTz RsBXiQ== 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 4gtysw2drb-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvOp31464279; Mon, 21 Sep 2026 16:57:24 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 68LGvMaA1464229; Mon, 21 Sep 2026 16:57:23 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:10 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfXxcKQvsvsv0C9 93Td5LSOKPNk3loy5yesnpzgqtcZdU0ovffQeWPeCojBTmesdgiT3Oo2B1nZuPToOnC0oj4YHSg 92Ewxhp69O11WCIrszCBVo1czLy+4r48a6Ik094RFOJz4urXt81EGC98IXJZAELbL8PwAPymQkX VCjByEl5XoMu2WtVHJqQKGF+SZhc8/rRKapPOk86YC//AUMRcWzF8RHvwyPHyshZOZHQz6PTgKm dgaqaW70AEFYxwOg7ewZa/NoEJqkss8rE9khp1V2xltT0XMbPaGqRLI9aIpLIMkaSdxvLjP9UoO mDUb5CL1o0qKWXvU8k16ox2QT6hMhA1IXVBQ/4GTpjDnK11B7kcNFKJZ+inJTSfWNckh/OBv4e4 8JxQq3EDmfGITIrNft58cNWOMtxGvtVokRm6RLR6P7k3VS4d/XvAAtN6VFuChwHmJYHNI/0Vbyq FYqRkBd/zP7V1rDBykA== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfXwcCJbf9sgsay TUnKEOs5OyiMi7lUViRLRUtrOTsRdCaGqx/XshPP1Fbu5I/f+NG1hVj2qgtI2on3LJOP114tWIi PqyccFRZmEeI0xqEu90eFy1YaRf3y3g= X-Proofpoint-ORIG-GUID: S5qp04RtFnDE5TSixSIXIjQMFLIOZ_-5 X-Proofpoint-GUID: S5qp04RtFnDE5TSixSIXIjQMFLIOZ_-5 X-Authority-Analysis: v=2.4 cv=W7etxhWk c=1 sm=1 tr=0 ts=6ab161f9 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5: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-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 adultscore=0 spamscore=0 malwarescore=0 suspectscore=0 bulkscore=0 impostorscore=0 priorityscore=1501 phishscore=0 lowpriorityscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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 6a4008f387b5..1afd72daba5c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5550,26 +5550,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 82e2f0d8a26d..4aeb686d2882 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 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1FEBD4E1C6A; Mon, 21 Sep 2026 16:57:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009873; cv=none; b=tkn461dx+9X5wr3XyhShdpPk5+MoqkLONM8oscpITMY4eOe602Ztg3VxshZS1CkkljRrn+AvEguB7YhUGCmzOuy6Ly8/KZ/5xriykFq5YWLp5RDzGBvcuEvQAQ29h2snszeFQxV3r653RQwQgFvDhT3JZOb3+ZIDuj2wTP+FFo0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009873; 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=UusLcaJGEahMc1E6vMJVJdn+tHZ3va2JJrFB05pQk4Pv7v6DdhTxkM0NPsmOW7GNr19DAjSugefDHemppX5DI3KP8jNHWPjZrfdI+T0jZ6OimlD46EXMdNt0I2t//V9iadezPk82AYqsq7sngn401cZk0E5FeM+1OIMMpQFIB5w= 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=Sn1PkELv; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="Sn1PkELv" Received: from pps.filterd (m0279873.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAB2a1532389; Mon, 21 Sep 2026 16:57:29 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=Sn1PkELv0p/mmWIu u08eulf+eXSUc6NKbJgizcm5Mol8Ax4MlSwk3/aTDVm1g1oxP5IUAp1Yq8yYdu4b BUlm0p0SwP2OggHHEybyZatxt5ICTxc5vqWkt/TeRtssNTF93JCGcKnDuzJou7Rg GgG7odoZBq9qzYsRP9YfcWbn9wg0u/fgZOUviagsaJpEs/mpxqDtLKh3zTbfpgJW PVTILqrEHm8RuKim6NdyISvVBLmF5K7YpJy7tdBBWMvXg4lF7Fb1E7HtaWAPsT8s Ls/OmBWqjINtNBkDHle6LiiMJqfII4/zUFqDENr0Pt/0TZjMUIugqC87m9pcVpPD SSJ7ZQ== 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 4gtq8bbrvv-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvOGG1464386; Mon, 21 Sep 2026 16:57:24 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 68LGvMaB1464229; Mon, 21 Sep 2026 16:57:24 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:11 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX130GmW4Q4t2o 1PXo1t0LV/62MKo2MZsVvQMcldurboLCvxXFQjq5TQ/Xp0vz/qBOXTEOvTj+rmn0qnsW1zL5OSR mv9KHAsAWg9NlLW4Z3mvayvuNeBv4rXcPd1Ua6K6LLdLLkJzGDNr+5xiri0ZvzfK3d2RssRlz0C 35reorOP4+ToAk15Bfk94HPLnmgulALW4blpuQ0BA41cF82pf/C2Cum5/mTKDF4e9W2/SCyK+vr MbrTnApWKtF8AyD+jI0hADfCwVUpSNfFrgmt/KrSNWsEYX0yHDhEaiKLO1cmEwhULvTG8tU9siZ U/Rnzdo/JlACEFnmq79lmtnt598ZDgP/14MjPqfasLWRuan6F1ENOZG6XQ5ckr9woxlCsEoOmQP vX0gu13R2+VItRbpg+0GWYAlQomQd8GOI0PXOunvPjfC9f+iC1MjsrxVpfVAg+OR2/qajBth+6N sSH5HwPqWmYHsIYDTfg== X-Proofpoint-GUID: 13JeXqn5BEpHJKL56onLNC0YBLEThPzP X-Authority-Analysis: v=2.4 cv=T/8Z3PKQ c=1 sm=1 tr=0 ts=6ab161f8 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=rJkE3RaqiGZ5pbrm-msn:22 a=EUspDBNiAAAA:8 a=se0PJeKvf3ygCVTVq1UA:9 a=QEXdDO2ut3YA:10 X-Proofpoint-ORIG-GUID: 13JeXqn5BEpHJKL56onLNC0YBLEThPzP X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX4TAM5gWfQmbz v004xqtjlNRBWFzCtSR4GCoUVEeK/riy9v3MCBXrpyPswV/ecYosHH74KcuuxVTlY+FLeFwSZ2Z CaHdUcbpJQ0dCcozFLRz5E/j/jDmknc= 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 phishscore=0 spamscore=0 adultscore=0 clxscore=1015 malwarescore=0 lowpriorityscore=0 suspectscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1FD6D4E1C69; Mon, 21 Sep 2026 16:57:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009873; cv=none; b=aX+PccPfH7iSB2PsdNIkeh48S23680e7JVVYeOnafgYYZ8K2n909x40JWJWuFhiwx2rJB53ugZ/+0bgWFhRUXQF/ilYSjOL5meCWvP3Sdj7cH6cLJ1rXppC8aLCWHp2ih0OdoSJzyO6lXFd2JLCCWacskcbWX6Sqb55gRWmrSuM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009873; c=relaxed/simple; bh=FHzNN028Wl7cxVh7NYXgrkxS7ahQnf7fNUunvOT8ZkM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OTQAXT8oJzAKCugPa3EhmJXmw+2v3cPc56eOwHlb2xnZ9QL9UvlpSlv3tmniGAA+c6bd96z8mdig9D0y/PYHxKAYhpHBrWf44sxRmaYzNyp12ixQvui/P/8WLGGoncvQBaZ7Qi0VOuU8xOHW0UG8OuVsHrLpCvA9eaKaw1vVjzA= 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=MDLwRKIb; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="MDLwRKIb" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAaul2163509; Mon, 21 Sep 2026 16:57:29 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=MDLwRKIbuoh3pxU+ IIoi+kFwb3pn7l45KmC22/gvdd0nwj859CAVYDXmV4BoJ9JRk6YVJFQXh3D0LoSD i0RA64I9PIr8NbWcHbnltWjBJVsFQkLwzK1H7XuVIGA3EZWhvfEnMGqYlpOIhWTd kZ15HDbLs0pYT6pkSbYBfxlZ8rFxWRoU3WY5L6lEmVd+tc1gVehPifMXeh2AVra2 OC0l30bPb4LEKHmlatfaqdt9AOV2S2Gt9lV0JTIXLqY8KORvEgW1gs1PQIU0l8M3 J41qsCaLaY2F4FCMC/cbtdSDALzRwk+CU0PnJV/NWRYBdLXlEcJMxG95eAsl6/zA FnZfQg== 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 4gtysw2drc-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvPKa1464510; Mon, 21 Sep 2026 16:57:25 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 68LGvMaC1464229; Mon, 21 Sep 2026 16:57:24 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:12 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX16ysUFSLL8rk IOGYUfdikwnrGYVH3bJFoDnAD2qSvd/TbBxSZ3x4zKqUA0CXpOkrm5UaubvwDXifOvPBtQGW2WH PHbpZFHxJmz7Fc04X+uqrKvwFdrlj383vDaCsphHawMRUQ7hbi96D8U1Dkl/kxkKqPc3Bap3MBJ NFXixvK68KHMjE4MQih2L7OwKIx2LET2StHt3RSyvXg3ZMYgRBMCtCTPX03bIGNOi7TxdkjIYLa fWyWrv2OrMiocXJxUNvljrBWsh2/nOvF0S3zEJibQzMUMChdBuK2qqTWMiGQufSHmxUC2r50a5Z JOIIBRLXR8SorBJqS3Mc/a1aWqRYubZ91gcz9pAmfGM0CY8KKvm5ZIfWVuIzQS5u1TtCkurzfta hsDP65EA2LeOLt225SC2bPVX2hpei53tzpQhi2JSZzZAa4mn1DS4bW3fLpVWjeyDLfouT6C2nkw PiZ1L+oX+f/+tVanu+w== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX/s8GnSK0X6eM Bgi34wp8RdlxKFIHOhBsej/OwjKB/L8SAANQQUlwLpFouMsaSZjklEBMZxKUCMSymUxwSPDQ+e4 IUTtRmbqInGH9+HwRlXv4ztxdEfPNGM= X-Proofpoint-ORIG-GUID: dq1KvukhuicU5b4H8PnVQWjqIS8eYC-T X-Proofpoint-GUID: dq1KvukhuicU5b4H8PnVQWjqIS8eYC-T X-Authority-Analysis: v=2.4 cv=W7etxhWk c=1 sm=1 tr=0 ts=6ab161f8 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=uHPRDBdGixKVNKiypX4A:9 a=QEXdDO2ut3YA:10 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 adultscore=0 spamscore=0 malwarescore=0 suspectscore=0 bulkscore=0 impostorscore=0 priorityscore=1501 phishscore=0 lowpriorityscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 550E44C9E1E; Mon, 21 Sep 2026 16:58:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009882; cv=none; b=dIJ0tZouG741oSnDVKX2Nb5P/FiSSK5jGT5KfU99LP609b57pksazduHoaQ2cPFGPus1vKyeNA3sm864p/IIZUkFASVpTB+WcJsE+iagMLbEqhcVnape25RWxbOxUzGN/gdDUxK+YSID2B845Z5wx1TOuxoFFHzRzKz160fp+HM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009882; c=relaxed/simple; bh=k4U62bSXIhUThCCQ7TVBsCt+dkC/3E1LVJ559kj3OIY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QWj+M0k7JasWxuFcvm3URbpFPk/zHVgxue7XzXbaIAHRvxzyxCcN4x4sISkLIdBM7ytS/hUxYcIROP4+stCVO6erDRxyI08pwd2+BftsNeTZq4ckXxuKmo1f/r5mwaOhv8OokLg1flOAhvV27PM1BEuwHsHPhRln5RKwQSRalw4= 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=Uo/f7vsY; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="Uo/f7vsY" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAJrX1817144; Mon, 21 Sep 2026 16:57:29 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= XXmSXZmLO4puZAiY5cbEZR7bjOfGjsAm5n6VfEHWxs8=; b=Uo/f7vsYmmLqZ700 UIggFvQDog5RvDZKHMxoTYxXjHcRaojBRQTzR3pvxg+cddWUKzxAd7PN71a4s+tU e80oghybGyf2aTPWsvO/XPpEOdbO7EjAdAK4fRH3oMDLAdZfMSwCeOaqOkcF3vP1 NWge1QbUH0bgIXNHmyBnM48cnVl4EbdQH9+bk22DbPfzTfY17qupEhtAiBaCPbOK gvTQo2GmxyTtgYeRWmm1+CL2yZerKxJIktJi7O5/6PwtTRBZQM1TO3JfpDTQQIXR oNJsBINZEhvVEbda6z3yX+OmoPhhwG1VXFHvxbTNF1HY6x/LE27H4mI0MlTeLKwK 1S2JAQ== 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 4gu011adae-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvNf81464236; Mon, 21 Sep 2026 16:57:25 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 68LGvMaD1464229; Mon, 21 Sep 2026 16:57:25 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:13 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-GUID: S9pnyG3cRmLpmbwSH1aICaT2zT0uwlKz X-Authority-Analysis: v=2.4 cv=dPczC+Zb c=1 sm=1 tr=0 ts=6ab161f8 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=gowsoOTTUOVcmtlkKump:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=7CQSdrXTAAAA:8 a=F-jeClLd32UZZCvIPigA:9 a=QEXdDO2ut3YA:10 a=a-qgeE7W1pNrGK8U0ZQC:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX5NoKINSpwHso L8CYJkdggGWpTriAxon/VX5baTnr6uDpdpMpz7aETDdoJjlac/8vwXqOy3DWCq1LnIxM+WqvmWe Tdnr3InZ2O1eAY6ulrPJz2K/GSnNv8WFWxZ9Le6ufRisp+UotDV/JAde4Su/YG4DooeBPwgekN5 3QGa3FchUTgdx4v+ImubXnbQpC5Geau4F6SucOS7aHS8/e9UBp5F7d789e9xVco64i0gfMFECEI dE0Ay7P8THFV7jB2zh/5N4/hTf96MBw7LSKLDFmtZGc6XZOtQfnHa+/OC8EJ8LtqRETuDiNw5Gn uiCjjTjzpyIPdbQJfe/VjvgWoop383lOwl5rXLGN/+6RlNeWIQuB1NGmTKJq+/8Ud0E/LgY0Ffi 2n/ZZz4Vsvoq1ifxR2YMPAkfEEup6FbTC3p8Kx6sv6ECWpkeWcp+c+BmjSbaJtMuZ5tCXC65zBJ 6GAU1iVxLLz1Hr87ICg== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX93zgxpQzzT/c 62+XU6BwOVSLUQzW2nPUGQzhd0VbpLAWwROptmaN6OY6iUMipAqTzR95gvcPi7MCp8Mpavf9dlX x+krN7QNfi1OGqjVuiH/Qg326gr8Ifs= X-Proofpoint-ORIG-GUID: S9pnyG3cRmLpmbwSH1aICaT2zT0uwlKz 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 impostorscore=0 malwarescore=0 phishscore=0 lowpriorityscore=0 priorityscore=1501 bulkscore=0 clxscore=1015 adultscore=0 spamscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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 95227134bcc1..cce0a183581e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21828,6 +21828,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 18:38:18 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC45E481647; Mon, 21 Sep 2026 16:57:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009874; cv=none; b=hR6HffKgQLH8osn4HBIoRI+k8fpXbhkMMfRwRozDrJDbzFuY7LPPSFp3jUDujmxqWX6rBW9msnd4kVtJiF5FqxdUagnK0Gw6CIJTcw8Gxsm6n+X0VokfriXdXf6QNOiP1nGnnBhgUdnl+UmcoO8V6gCr7dFsGnl2S1p6/k5TvAs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790009874; c=relaxed/simple; bh=X8qD955WHpybwj+yRCtCau0eQllUlA6eMCOWbXmSqzE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PlhgHeOyusl0wQ2+C3Fir/nPCLdeJ+9Q7OIHIZnBOmLy8mpblRke60UikRpz9cuD/WI2Hi3ZrwFXLXAj0ubh2pzTKNT13mCLeJNItpdYmOy7AdO7EJbn6NVV353G7mf97RdleaLO79HjocyKvI2oQ6H14e9ZNr/qj2B/dSC1gr4= 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=SvxAKZH4; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="SvxAKZH4" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68LFAq7G1438653; Mon, 21 Sep 2026 16:57:28 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=SvxAKZH4VwTqEkgt PTV1/jaQJ5qYckhFY1pyAjCpcDdUoV5bCyY2haYGI5cyGziaiMfRuEXdxsUU/ECy NryNxZKeCIuRDfml1ahlEobEFb38SfXzeuMZ1/+0NrOFg9kIhwn/tUcitOi7yi38 jldQOw+7EcdGT+3LtyXLsvZInBgLyhd3rKj0AyFho4tPH399juIp9mbDSyqf+hEs iDZMe0XbYGk0JX2Gj1fvF50IorUF73vRF+VXnndUmQP3l7ERBqJPiQeDB6n1308q Y5ICAgMjvxi3U6hwIuSKtMHN3jx2NWjf4vJYqyM1OVd3Q87lRwhfUj+jZX6lXPMo AW4BsQ== 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 4gtwf5jyrn-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 21 Sep 2026 16:57:28 +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 68LGvPjF1464547; Mon, 21 Sep 2026 16:57:25 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 68LGvMaE1464229; Mon, 21 Sep 2026 16:57:25 +0000 (GMT) From: Faruque Ansari Date: Mon, 21 Sep 2026 22:27:14 +0530 Subject: [PATCH v13 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: References: In-Reply-To: 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-Authority-Analysis: v=2.4 cv=SJLXx+vH c=1 sm=1 tr=0 ts=6ab161f8 cx=c_pps a=Ou0eQOY4+eZoSc0qltEV5Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=EUspDBNiAAAA:8 a=eG3WmpGALsbKIvMRAg8A:9 a=QEXdDO2ut3YA:10 X-Proofpoint-GUID: 4J01QMKyxk9nl6W3OXR308W9EhIsWBtQ X-Proofpoint-ORIG-GUID: 4J01QMKyxk9nl6W3OXR308W9EhIsWBtQ X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX7BVqwjD0A7WX NYHCOH7K+UFaOT9ITBV1GoAXynhmymGandC17FKMmM0YM5TWF8uXllpmbXqDo9yjRgMDLS8wZQx LGv/Ywi71/IaTeXSHO+ZiPJ2S1vi1WM= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIxMDI0NyBTYWx0ZWRfX8VXjAVHsYYKU 3RQszFH2BjFTVsxFLgeEr6WysLtP5PJQjS0MreyLhzpoFEoDt34cvoVnIJiyL4aTnObj65YhcIP p9Ti2iVZZphejRCNpfmlHxQQjd2BVL3hVKftRTPD8D/Oqtm2cHQkqN2pq2mDCv1J6rXm0VNxe2n MN87YNfvsxLzXL+cxqUo2uhpEmtzLzlX9vVEOVP6EFSBpX+64bxZkwqO+PIbfT+31LIFQRPa+bC Xty2vbTKEvqWWB97O2BrsBVWZXrhmDhoo0R9akqDS9ArImZ0BSfPg+zJlITqVYOxJu4ldfjOhx0 v78E8yBSx3j1xy9LC5lLBBnd2x0YTU9IYUomicNphlu2DqydKGQNKW9XbbL6MJ1rT62Srh0RzFj 7AfQGJVyrH2yZ+xQ2zMKFX1n4iztreyjB3LxbjQW4r+oIZMQevLqQ7MJiTZZ9wZuijim/PpDZcI jvAWPO1A5ZD+7WJyysg== 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 lowpriorityscore=0 priorityscore=1501 impostorscore=0 suspectscore=0 malwarescore=0 adultscore=0 clxscore=1015 bulkscore=0 spamscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609210247 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