diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index fece990af4a7..da76e84302b9 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -305,3 +305,22 @@ config POWER_MLXBF
 	  This driver supports reset or low power mode handling for Mellanox BlueField.
 
 endif
+
+menuconfig PSCR
+    bool "Power State Change Reasons (PSCR) Tracking Framework"
+    help
+      Enables the Power State Change Reasons (PSCR) tracking framework.
+
+      This framework is designed to store reasons for system shutdowns or
+      reboots,  like under voltage or software-triggered events, in non-volatile
+      hardware storage. It is particularly useful for postmortem analysis, where
+      traditional storage methods (like block devices or RAM) are not feasible
+      due to immediate power-down requirements or insufficient power to retain
+      data.
+
+      This is useful for bootloaders or other early-stage system components to
+      make recovery decisions based on the last known system state. Note that it
+      does not cover hardware-induced resets like system freezes or watchdog
+      timeouts.
+
+      If unsure, say N.
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index a95d1bd275d1..d9d744302c68 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
 obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
 obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
 obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
+obj-$(CONFIG_PSCR) += pscr.o
 obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
 obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
 obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
diff --git a/drivers/power/reset/pscr.c b/drivers/power/reset/pscr.c
new file mode 100644
index 000000000000..7506ecbe1aad
--- /dev/null
+++ b/drivers/power/reset/pscr.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd
+// Copyright (c) 2024 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
+/*
+ * Based on drivers/power/reset/reboot-mode.c
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/pscr.h>
+
+#define PREFIX "pscr-"
+
+struct reason_info {
+	enum power_state_change_reason pscr;
+	u32 magic;
+	struct list_head list;
+};
+
+enum power_state_change_reason system_pscr;
+
+struct pscr_map {
+	const char *reason;
+	enum power_state_change_reason pscr;
+};
+
+struct pscr_map pscr_map_table[] = {
+	{ "unknown", PSCR_UNKNOWN },
+	{ "under-voltage", PSCR_UNDER_VOLTAGE },
+	{ "over-current", PSCR_OVER_CURRENT },
+	{ "regulator-failure", PSCR_REGULATOR_FAILURE },
+	{ "over-temperature", PSCR_OVERTEMPERATURE },
+};
+
+static int find_reason_by_string(const char *reason)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pscr_map_table); i++) {
+		if (!strcmp(reason, pscr_map_table[i].reason))
+			return pscr_map_table[i].pscr;
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * set_power_state_change_reason() - Set the system's power state change reason
+ * @reason: The enum value representing the power state change reason
+ *
+ * This function sets the system's power state change reason based on the
+ * provided enum value.
+ */
+void set_power_state_change_reason(enum power_state_change_reason reason)
+{
+	system_pscr = reason;
+}
+EXPORT_SYMBOL_GPL(set_power_state_change_reason);
+
+static unsigned int get_pscr_magic(struct pscr_driver *pscr_drv,
+					  const char *cmd)
+{
+	struct reason_info *info;
+
+	list_for_each_entry(info, &pscr_drv->head, list) {
+		if (info->pscr == system_pscr)
+			return info->magic;
+	}
+
+	return 0;
+}
+
+static int pscr_notify(struct notifier_block *this,
+			      unsigned long reason, void *cmd)
+{
+	struct pscr_driver *pscr_drv = container_of(this, struct pscr_driver,
+						    reboot_notifier);
+	unsigned int magic;
+
+	magic = get_pscr_magic(pscr_drv, cmd);
+	if (magic)
+		pscr_drv->write(pscr_drv, magic);
+
+	return NOTIFY_DONE;
+}
+
+/**
+ * pscr_process_property() - Process a power state change reason property
+ * @pscr_drv: Pointer to the pscr_driver structure
+ * @prop: Pointer to the property structure to be processed
+ *
+ * This function processes a device tree property representing a power state
+ * change reason and initializes the relevant data structures.
+ *
+ * Returns: 0 on success, -ENOMEM on memory allocation failure.
+ */
+static int pscr_process_property(struct pscr_driver *pscr_drv,
+				 struct property *prop)
+{
+	struct device *dev = pscr_drv->dev;
+	size_t len = strlen(PREFIX);
+	struct reason_info *info;
+	int ret;
+
+	if (strncmp(prop->name, PREFIX, len))
+		return 0;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(dev->of_node, prop->name, &info->magic);
+	if (ret) {
+		dev_err(dev, "Can't read magic number for %s: %pe\n",
+			prop->name, ERR_PTR(ret));
+		devm_kfree(dev, info);
+		return 0;
+	}
+
+	if (!info->magic) {
+		dev_err(dev, "%s with magic number == 0\n", prop->name);
+		devm_kfree(dev, info);
+		return 0;
+	}
+
+	info->pscr = find_reason_by_string(prop->name + len);
+	if (info->pscr < 0) {
+		dev_err(dev, "unsupported reason name(%s): %pe\n",
+				prop->name, ERR_PTR(info->pscr));
+		devm_kfree(dev, info);
+		return 0;
+	}
+
+	if (info->magic > pscr_drv->max_magic)
+		pscr_drv->max_magic = info->magic;
+
+	dev_dbg(dev, "registering reason = %s, magic = %d, pscr = %d\n",
+		prop->name, info->magic, info->pscr);
+	list_add_tail(&info->list, &pscr_drv->head);
+
+	return 0;
+}
+
+/*
+ * pscr_register() - Register the pscr driver and initialize power state change
+ *                   reasons
+ * @pscr_drv: Pointer to the pscr_driver structure
+ *
+ * This function registers the pscr driver and initializes power state change
+ * reasons based on device tree properties.
+ *
+ * Returns: 0 on success, -ENOMEM on memory allocation failure
+ */
+int pscr_register(struct pscr_driver *pscr_drv)
+{
+	struct device_node *np = pscr_drv->dev->of_node;
+	struct property *prop;
+
+	INIT_LIST_HEAD(&pscr_drv->head);
+
+	for_each_property_of_node(np, prop) {
+		int ret = pscr_process_property(pscr_drv, prop);
+		if (ret)
+			return ret;
+	}
+
+	pscr_drv->reboot_notifier.notifier_call = pscr_notify;
+	register_reboot_notifier(&pscr_drv->reboot_notifier);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pscr_register);
+
+/*
+ * pscr_unregister() - Unregister the pscr driver's reboot notifier
+ * @pscr_drv: Pointer to the pscr_driver structure
+ *
+ * This function unregisters the reboot notifier for the pscr driver.
+ */
+void pscr_unregister(struct pscr_driver *pscr_drv)
+{
+	unregister_reboot_notifier(&pscr_drv->reboot_notifier);
+}
+EXPORT_SYMBOL_GPL(pscr_unregister);
+
+static void devm_pscr_release(struct device *dev, void *res)
+{
+	pscr_unregister(*(struct pscr_driver **)res);
+}
+
+/**
+ * devm_pscr_register - Register a device-managed PSCR driver
+ * @dev: Device to associate the PSCR driver with
+ * @pscr_drv: Pointer to the PSCR driver to be registered
+ *
+ * Registers a Power State Change Reason (PSCR) driver as a device-managed
+ * resource.
+ *
+ * Returns: 0 on successful registration or a negative error code on failure.
+ */
+int devm_pscr_register(struct device *dev,
+			      struct pscr_driver *pscr_drv)
+{
+	struct pscr_driver **dr;
+	int rc;
+
+	dr = devres_alloc(devm_pscr_release, sizeof(*dr), GFP_KERNEL);
+	if (!dr)
+		return -ENOMEM;
+
+	rc = pscr_register(pscr_drv);
+	if (rc) {
+		devres_free(dr);
+		return rc;
+	}
+
+	*dr = pscr_drv;
+	devres_add(dev, dr);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devm_pscr_register);
+
+static int devm_pscr_match(struct device *dev, void *res, void *data)
+{
+	struct pscr_driver **p = res;
+
+	if (WARN_ON(!p || !*p))
+		return 0;
+
+	return *p == data;
+}
+
+/**
+ * devm_pscr_unregister - Unregister a managed PSCR driver
+ * @dev: Device associated with the PSCR driver
+ * @pscr_drv: Pointer to the PSCR driver to unregister
+ *
+ * Unregisters a device-managed Power State Change Reason (PSCR) driver.
+ * It handles the cleanup and release of resources associated with the PSCR
+ * driver which was previously registered.
+ */
+void devm_pscr_unregister(struct device *dev,
+				 struct pscr_driver *pscr_drv)
+{
+	WARN_ON(devres_release(dev,
+			       devm_pscr_release,
+			       devm_pscr_match, pscr_drv));
+}
+EXPORT_SYMBOL_GPL(devm_pscr_unregister);
+
+MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
+MODULE_DESCRIPTION("Power State Change Reason (PSCR) tracking framework");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/pscr.h b/include/linux/pscr.h
new file mode 100644
index 000000000000..bf66dd69d96d
--- /dev/null
+++ b/include/linux/pscr.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PSCR_H__
+#define __PSCR_H__
+
+enum power_state_change_reason {
+	PSCR_UNKNOWN,
+	PSCR_UNDER_VOLTAGE,
+	PSCR_OVER_CURRENT,
+	PSCR_REGULATOR_FAILURE,
+	PSCR_OVERTEMPERATURE,
+};
+
+struct pscr_driver {
+	struct device *dev;
+	struct list_head head;
+	int (*write)(struct pscr_driver *pscr_drv, u32 magic);
+	struct notifier_block reboot_notifier;
+	u32 max_magic;
+};
+
+int pscr_register(struct pscr_driver *pscr_drv);
+void pscr_unregister(struct pscr_driver *pscr_drv);
+int devm_pscr_register(struct device *dev,
+		       struct pscr_driver *pscr_drv);
+void devm_pscr_unregister(struct device *dev,
+			  struct pscr_driver *pscr_drv);
+
+
+#if IS_ENABLED(CONFIG_PSCR)
+
+void set_power_state_change_reason(enum power_state_change_reason reason);
+
+#else
+
+static inline void set_power_state_change_reason(enum power_state_change_reason reason)
+{
+}
+#endif
+
+#endif
