From patchwork Wed Jan 24 12:21:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 765718 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 3EB6F64CC6 for ; Wed, 24 Jan 2024 12:22:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098952; cv=none; b=ZyQzbjQ34NVscRdy1PwL+qRvk/LO5mEurLRkQD1eorFAt7qpMJTZDY5PxQ8LjKSk8e5/O9ApmhwrgsoQpjkAovVXFpXxfhFqrMRxA9giG2N6E1YRiw6cfaPhg484TE/THOuDM+D19q17Hi+yMKYSsU7Ui/5evUSHBkGnktgjx8M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098952; c=relaxed/simple; bh=qJyZABOkVgOrJuobub1GO0uw86ox/Ky9FOo5Kw77m4E=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ND1oLYGbnP1nNC8IouFAkiX67g/mugmoEb7S0gEVXkpR0jATgIyXwbROFax/PP1AJrAfMCBgBesW/3DosWqw+6V1U4pnqgrGXIJ+RhBLB6HpsMxI8WwZ1lkoBN6Pcieodbp8UVlDGa+mK/ulrItAypZJeGduD3PtITLDp/7NHos= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rScGd-0007o5-1B; Wed, 24 Jan 2024 13:22:07 +0100 Received: from [2a0a:edc0:0:1101:1d::ac] (helo=dude04.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rScGb-0023Zb-Ux; Wed, 24 Jan 2024 13:22:05 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1rScGb-00340u-2t; Wed, 24 Jan 2024 13:22:05 +0100 From: Oleksij Rempel To: Sebastian Reichel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Srinivas Kandagatla Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, Liam Girdwood , Mark Brown , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, =?utf-8?q?S=C3=B8ren_Andersen?= Subject: [PATCH v2 1/8] power: Extend power_on_reason.h for upcoming PSCRR framework Date: Wed, 24 Jan 2024 13:21:57 +0100 Message-Id: <20240124122204.730370-2-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240124122204.730370-1-o.rempel@pengutronix.de> References: <20240124122204.730370-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: devicetree@vger.kernel.org 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_OVERTEMPERATURE for over temperature situations. Signed-off-by: Oleksij Rempel --- include/linux/power/power_on_reason.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/power/power_on_reason.h b/include/linux/power/power_on_reason.h index 95a1ec0c403c..f2446cece277 100644 --- a/include/linux/power/power_on_reason.h +++ b/include/linux/power/power_on_reason.h @@ -15,5 +15,8 @@ #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_OVERTEMPERATURE "overtemperature" #endif /* POWER_ON_REASON_H */ From patchwork Wed Jan 24 12:21:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 765716 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 5E59D65BD6 for ; Wed, 24 Jan 2024 12:22:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098955; cv=none; b=txZVOGUqpLoLGbI/IZEDlotO8q5+Y//xOHtogy1+u2OqbE4XOtT31NbaGRssGESnYdpPK/32E6XcJ+yaeS7mtWRa959lhkEeUz4fNVqxvOi1/Y2ZmnyNK1CX3V1YFScBCKM25+ecyTkgIdtUsK43MEg+DERv8eONaOqjbmOceII= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098955; c=relaxed/simple; bh=Q7CJ22s7cVnisV7JjfaOll6mFN9dCNJwXRW0hKY8Xlg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Ggbd9GzHsQDzewyIhSxdiCkddtosp4BIRuLtXkMzkHhAaZ6+kxsKA/jG8qdMuRXrg8oVbVOqH1D4sKO+f9HDrPi6CPcESsnd2UUWylOVZ7nhEf5c7s4VCHR7f9LFzbRAmjfwDh1uO5Vrfxs7K0DsIFka4lyPAtw02Ekz7AfY5go= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rScGd-0007o7-1C; Wed, 24 Jan 2024 13:22:07 +0100 Received: from [2a0a:edc0:0:1101:1d::ac] (helo=dude04.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rScGc-0023Zd-0H; Wed, 24 Jan 2024 13:22:06 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1rScGb-00341E-30; Wed, 24 Jan 2024 13:22:05 +0100 From: Oleksij Rempel To: Sebastian Reichel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Srinivas Kandagatla Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, Liam Girdwood , Mark Brown , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, =?utf-8?q?S=C3=B8ren_Andersen?= Subject: [PATCH v2 3/8] power: reset: Introduce PSCR Recording Framework for Non-Volatile Storage Date: Wed, 24 Jan 2024 13:21:59 +0100 Message-Id: <20240124122204.730370-4-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240124122204.730370-1-o.rempel@pengutronix.de> References: <20240124122204.730370-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: devicetree@vger.kernel.org This commit introduces the Power State Change Reasons Recording (PSCRR) framework into the kernel. The framework is vital for systems where PMICs or watchdogs cannot provide information on power state changes. It stores reasons for system shutdowns and reboots, like under-voltage or software-triggered events, in non-volatile hardware storage. This approach is essential for postmortem analysis in scenarios where traditional storage methods (block devices, RAM) are not feasible. The framework aids bootloaders and early-stage system components in recovery decision-making, although it does not cover resets caused by hardware issues like system freezes or watchdog timeouts. Signed-off-by: Oleksij Rempel --- drivers/power/reset/Kconfig | 19 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/pscrr.c | 353 +++++++++++++++++++++++++++++++++++ include/linux/pscrr.h | 73 ++++++++ 4 files changed, 446 insertions(+) create mode 100644 drivers/power/reset/pscrr.c create mode 100644 include/linux/pscrr.h diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index fece990af4a7..c6ce7e647048 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 PSCRR + bool "Power State Change Reasons (PSCR) Recording Framework" + help + Enables the Power State Change Reasons (PSCR) Recording 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..e618c34a30f9 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_PSCRR) += pscrr.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/pscrr.c b/drivers/power/reset/pscrr.c new file mode 100644 index 000000000000..651fc210878d --- /dev/null +++ b/drivers/power/reset/pscrr.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd +// Copyright (c) 2024 Pengutronix, Oleksij Rempel +/* + * Based on drivers/power/reset/reboot-mode.c + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "pscr-" + +enum pscr system_pscr; + +/* + * struct pscr_to_magic_entry - Entry for mapping PSCR to magic values. + * + * This structure represents a single entry in a list that maps Power State + * Change Reason (PSCR) values to corresponding magic values. Each entry + * associates a specific PSCR with a unique magic value, facilitating easy + * translation between the two. + * + * @pscr: The PSCR value. + * @magic: The corresponding magic value. + * @list: List head for chaining multiple such entries. + */ +struct pscr_to_magic_entry { + enum pscr pscr; + u32 magic; + struct list_head list; +}; + +/* + * struct pscr_map - Maps device tree property names to PSCR values. + * + * @dt_prop_name: Device tree property name without the "pscr-" prefix. + * @pscr: The corresponding PSCR enum value for the given property name. + */ +struct pscr_map { + const char *dt_prop_name; + enum pscr pscr; +}; + +/* + * struct pscr_map - Maps shortened DT property names to PSCR values. + * + * This structure maps device tree property names, with the "pscr-" prefix + * omitted, to their corresponding Power State Change Reason (PSCR) values. + */ +struct pscr_map pscr_map_table[] = { + { "under-voltage", PSCR_UNDER_VOLTAGE }, + { "over-current", PSCR_OVER_CURRENT }, + { "regulator-failure", PSCR_REGULATOR_FAILURE }, + { "over-temperature", PSCR_OVERTEMPERATURE }, +}; + +/* + * pscr_find_from_dt_name - Finds the PSCR value for a given DT property name. + * + * @dt_prop_name: The device tree property name, without the "pscr-" prefix, to + * look up. + * Returns the corresponding PSCR value or -ENOENT if not found. + */ +static int find_pscr_by_string(const char *dt_prop_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pscr_map_table); i++) { + if (!strcmp(dt_prop_name, pscr_map_table[i].dt_prop_name)) + return pscr_map_table[i].pscr; + } + + return -ENOENT; +} + +static enum pscr get_pscr_by_magic(struct pscrr_device *pscrr_dev, u32 magic) +{ + struct pscr_to_magic_entry *map_entry; + + list_for_each_entry(map_entry, &pscrr_dev->pscr_map_list, list) { + if (map_entry->magic == magic) + return map_entry->pscr; + } + + return 0; +} + +static u32 get_magic_by_pscr(struct pscrr_device *pscrr_dev, enum pscr pscr) +{ + struct pscr_to_magic_entry *map_entry; + + list_for_each_entry(map_entry, &pscrr_dev->pscr_map_list, list) { + if (map_entry->pscr == pscr) + return map_entry->magic; + } + + return 0; +} + +/** + * set_power_state_change_reason() - Set the system's power state change reason + * @pscr: 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 pscr pscr) +{ + system_pscr = pscr; +} +EXPORT_SYMBOL_GPL(set_power_state_change_reason); + +static const char *pscr_to_por_string(enum pscr pscr) +{ + switch (pscr) { + case PSCR_UNDER_VOLTAGE: + return POWER_ON_REASON_BROWN_OUT; + case PSCR_OVER_CURRENT: + return POWER_ON_REASON_OVER_CURRENT; + case PSCR_REGULATOR_FAILURE: + return POWER_ON_REASON_REGULATOR_FAILURE; + case PSCR_OVERTEMPERATURE: + return POWER_ON_REASON_OVERTEMPERATURE; + default: + } + + return POWER_ON_REASON_UNKNOWN; +} + +static ssize_t power_state_change_reason_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pscrr_device *pscrr_dev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", pscr_to_por_string(pscrr_dev->last_pscr)); +} +static DEVICE_ATTR_RO(power_state_change_reason); + +int handle_last_pscr(struct pscrr_device *pscrr_dev) +{ + u32 magic; + int ret; + + ret = pscrr_dev->read(pscrr_dev, &magic); + if (ret) + return ret; + + pscrr_dev->last_pscr = get_pscr_by_magic(pscrr_dev, magic); + + dev_info(pscrr_dev->dev, "Last recorded power state change reason: %s\n", + pscr_to_por_string(pscrr_dev->last_pscr)); + + ret = pscrr_dev->write(pscrr_dev, 0); + if (ret) + dev_err(pscrr_dev->dev, "Failed to clear power state change reason\n"); + + return ret; +} + +static int pscrr_notify(struct notifier_block *this, unsigned long x, void *c) +{ + struct pscrr_device *pscrr_dev = container_of(this, struct pscrr_device, + reboot_notifier); + u32 magic; + + magic = get_magic_by_pscr(pscrr_dev, system_pscr); + pscrr_dev->write(pscrr_dev, magic); + + return NOTIFY_DONE; +} + +/** + * pscrr_process_property() - Process a power state change reason property + * @pscrr_dev: Pointer to the pscrr_device 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 pscrr_process_property(struct pscrr_device *pscrr_dev, + struct property *prop) +{ + struct pscr_to_magic_entry *map_entry; + struct device *dev = pscrr_dev->dev; + size_t len = strlen(PREFIX); + int ret; + + if (strncmp(prop->name, PREFIX, len)) + return 0; + + map_entry = devm_kzalloc(dev, sizeof(*map_entry), GFP_KERNEL); + if (!map_entry) + return -ENOMEM; + + ret = of_property_read_u32(dev->of_node, prop->name, &map_entry->magic); + if (ret) { + dev_err(dev, "Can't read magic number for %s: %pe\n", + prop->name, ERR_PTR(ret)); + devm_kfree(dev, map_entry); + return 0; + } + + if (!map_entry->magic) { + dev_err(dev, "%s with magic number == 0\n", prop->name); + devm_kfree(dev, map_entry); + return 0; + } + + map_entry->pscr = find_pscr_by_string(prop->name + len); + if (map_entry->pscr < 0) { + dev_err(dev, "unsupported reason name(%s): %pe\n", + prop->name, ERR_PTR(map_entry->pscr)); + devm_kfree(dev, map_entry); + return 0; + } + + if (map_entry->magic > pscrr_dev->max_magic_val) + pscrr_dev->max_magic_val = map_entry->magic; + + dev_dbg(dev, "registering reason = %s, magic = %d, pscr = %d\n", + prop->name, map_entry->magic, map_entry->pscr); + list_add_tail(&map_entry->list, &pscrr_dev->pscr_map_list); + + return 0; +} + +/* + * pscrr_register() - Register the pscr driver and initialize power state change + * reasons + * @pscrr_dev: Pointer to the pscrr_device 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 pscrr_register(struct pscrr_device *pscrr_dev) +{ + struct device_node *np = pscrr_dev->dev->of_node; + struct property *prop; + int ret; + + INIT_LIST_HEAD(&pscrr_dev->pscr_map_list); + + for_each_property_of_node(np, prop) { + ret = pscrr_process_property(pscrr_dev, prop); + if (ret) + return ret; + } + + pscrr_dev->reboot_notifier.notifier_call = pscrr_notify; + register_reboot_notifier(&pscrr_dev->reboot_notifier); + + dev_set_drvdata(pscrr_dev->dev, pscrr_dev); + + ret = device_create_file(pscrr_dev->dev, &dev_attr_power_state_change_reason); + if (ret) + dev_err(pscrr_dev->dev, "Could not create sysfs entry\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(pscrr_register); + +/* + * pscrr_unregister() - Unregister the pscr driver's reboot notifier + * @pscrr_dev: Pointer to the pscrr_device structure + * + * This function unregisters the reboot notifier for the pscr driver. + */ +void pscrr_unregister(struct pscrr_device *pscrr_dev) +{ + unregister_reboot_notifier(&pscrr_dev->reboot_notifier); +} +EXPORT_SYMBOL_GPL(pscrr_unregister); + +static void devm_pscrr_release(struct device *dev, void *res) +{ + pscrr_unregister(*(struct pscrr_device **)res); +} + +/** + * devm_pscrr_register - Register a device-managed PSCR driver + * @dev: Device to associate the PSCR driver with + * @pscrr_dev: 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_pscrr_register(struct device *dev, struct pscrr_device *pscrr_dev) +{ + struct pscrr_device **dr; + int rc; + + dr = devres_alloc(devm_pscrr_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = pscrr_register(pscrr_dev); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = pscrr_dev; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_pscrr_register); + +static int devm_pscrr_match(struct device *dev, void *res, void *data) +{ + struct pscrr_device **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +/** + * devm_pscrr_unregister - Unregister a managed PSCR driver + * @dev: Device associated with the PSCR driver + * @pscrr_dev: 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_pscrr_unregister(struct device *dev, + struct pscrr_device *pscrr_dev) +{ + WARN_ON(devres_release(dev, + devm_pscrr_release, + devm_pscrr_match, pscrr_dev)); +} +EXPORT_SYMBOL_GPL(devm_pscrr_unregister); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Power State Change Reason (PSCR) Recording framework"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/pscrr.h b/include/linux/pscrr.h new file mode 100644 index 000000000000..2de9d9bc9aeb --- /dev/null +++ b/include/linux/pscrr.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PSCRR_H__ +#define __PSCRR_H__ + +/* + * enum pscr - Enumerates reasons for power state changes. + * + * This enum lists the various reasons why a power state change might + * occur in a system. Each value represents a specific condition that + * could trigger a change in power state, such as shutdown or reboot. + * + * PSCR_UNKNOWN: Represents an unknown or unspecified reason. + * PSCR_UNDER_VOLTAGE: Indicates a power state change due to under-voltage. + * PSCR_OVER_CURRENT: Indicates a power state change due to over-current. + * PSCR_REGULATOR_FAILURE: Indicates a failure in a voltage regulator. + * PSCR_OVERTEMPERATURE: Indicates an over-temperature condition. + */ +enum pscr { + PSCR_UNKNOWN, + PSCR_UNDER_VOLTAGE, + PSCR_OVER_CURRENT, + PSCR_REGULATOR_FAILURE, + PSCR_OVERTEMPERATURE, +}; + +/* + * struct pscrr_device - Manages a Power State Change Reason Recorder device. + * + * This structure is utilized for controlling a device responsible for + * recording reasons for power state changes (PSCR). It includes mechanisms + * for mapping PSCR values to specific magic codes, storing these mappings, + * and recovering the last PSCR value from storage during system start-up. + * + * @dev: Device structure pointer. + * @pscr_map_list: List head for structs holding PSCR to magic code mappings. + * @write: Function pointer to write a new mapped PSCR value. + * @read: Function pointer to read the current mapped PSCR value. + * @reboot_notifier: Notifier block for recording PSCR at reboot. + * @max_magic_val: Maximum permissible magic code, used for verifying storage + * capacity and mapping integrity. + * @last_pscr: Last PSCR value recovered from storage at system start, + * representing the reason for the last system power cycle. + */ +struct pscrr_device { + struct device *dev; + struct list_head pscr_map_list; + int (*write)(struct pscrr_device *pscrr_dev, u32 magic); + int (*read)(struct pscrr_device *pscrr_dev, u32 *magic); + struct notifier_block reboot_notifier; + u32 max_magic_val; + enum pscr last_pscr; +}; + +int pscrr_register(struct pscrr_device *pscrr_dev); +void pscrr_unregister(struct pscrr_device *pscrr_dev); +int devm_pscrr_register(struct device *dev, + struct pscrr_device *pscrr_dev); +void devm_pscrr_unregister(struct device *dev, + struct pscrr_device *pscrr_dev); +int handle_last_pscr(struct pscrr_device *pscrr_dev); + +#if IS_ENABLED(CONFIG_PSCRR) + +void set_power_state_change_reason(enum pscr pscr); + +#else + +static inline void set_power_state_change_reason(enum pscr pscr) +{ +} +#endif + +#endif From patchwork Wed Jan 24 12:22:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 765717 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 EECCE651BF for ; Wed, 24 Jan 2024 12:22:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098952; cv=none; b=H588uhT5IlGxRdxQhU6eUiSwyHQIHsSrFcuR6zoFW2P3uedaMizCuAURSVH/sRaQxrYL5XBvHj0d5mXDKljObutJ1n3qhBitd5hiKLVi2QjQHT1SnSAUddhnzEsklwBPathSddxzTlfCVvFjVuP75c+y8dWWxNe9jtYI+VCqNj0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098952; c=relaxed/simple; bh=20N1VN4Z4ICpskuEnxLNHaytZDaOloomTZFu/N940iA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KAYWQHDYSkJUQSpNw/5KIZ8kJhTatzYvB9vixfLp+5gqlcZaXabcVnYtmaCaIVuPTtpFrbTfQQAD3DA9QsA+0fwnCxi6rWvrHAhveWKv7crCPadclMZIQE4TTMAUJ7V5/S/yqqP1qUWHtyKsgJAsx41OsUIQTR3MgDoL/qldKhM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rScGd-0007oA-1C; Wed, 24 Jan 2024 13:22:07 +0100 Received: from [2a0a:edc0:0:1101:1d::ac] (helo=dude04.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rScGc-0023Zi-2q; Wed, 24 Jan 2024 13:22:06 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1rScGb-00341i-3B; Wed, 24 Jan 2024 13:22:05 +0100 From: Oleksij Rempel To: Sebastian Reichel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Srinivas Kandagatla Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, Liam Girdwood , Mark Brown , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, =?utf-8?q?S=C3=B8ren_Andersen?= Subject: [PATCH v2 6/8] power: reset: add PSCR NVMEM Driver for Recording Power State Change Reasons Date: Wed, 24 Jan 2024 13:22:02 +0100 Message-Id: <20240124122204.730370-7-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240124122204.730370-1-o.rempel@pengutronix.de> References: <20240124122204.730370-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: devicetree@vger.kernel.org This driver utilizes the Power State Change Reasons Recording (PSCRR) framework to store specific power state change information, such as shutdown or reboot reasons, into a designated non-volatile memory (NVMEM) cell. Signed-off-by: Oleksij Rempel --- drivers/power/reset/Kconfig | 11 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/pscrr-nvmem.c | 121 ++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 drivers/power/reset/pscrr-nvmem.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index c6ce7e647048..8e50e495ef98 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -324,3 +324,14 @@ menuconfig PSCRR timeouts. If unsure, say N. + +if PSCRR + +config PSCRR_NVMEM + tristate "Generic NVMEM-based Power State Change Reason Recorder" + depends on OF + help + Enabling this option adds support for recording power state change + reasons in a NVMEM cell. + +endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index e618c34a30f9..3860dbbacb23 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -32,6 +32,7 @@ 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_PSCRR) += pscrr.o +obj-$(CONFIG_PSCRR_NVMEM) += pscrr-nvmem.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/pscrr-nvmem.c b/drivers/power/reset/pscrr-nvmem.c new file mode 100644 index 000000000000..fe9053b78b79 --- /dev/null +++ b/drivers/power/reset/pscrr-nvmem.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) Vaisala Oyj. All rights reserved. +// Copyright (c) 2024 Pengutronix, Oleksij Rempel +/* + * Based on drivers/power/reset/nvmem-reboot-mode.c + * Copyright (c) Vaisala Oyj. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pscrr_nvmem { + struct pscrr_device pscrr_dev; + struct nvmem_cell *cell; + size_t max_magic_bytes; +}; + +static int pscrr_nvmem_write(struct pscrr_device *pscrr_dev, u32 magic) +{ + struct pscrr_nvmem *priv = container_of(pscrr_dev, struct pscrr_nvmem, + pscrr_dev); + size_t size = min(priv->max_magic_bytes, sizeof(magic)); + int ret; + + ret = nvmem_cell_write(priv->cell, &magic, size); + if (ret < 0) { + dev_err(pscrr_dev->dev, "update reason bits failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + return 0; +} + +static int pscrr_nvmem_read(struct pscrr_device *pscrr_dev, u32 *magic) +{ + struct pscrr_nvmem *priv = container_of(pscrr_dev, struct pscrr_nvmem, + pscrr_dev); + size_t len; + void *buf; + + buf = nvmem_cell_read(priv->cell, &len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + *magic = 0; + memcpy(magic, buf, min(len, sizeof(*magic))); + kfree(buf); + + return 0; +} + +static int pscrr_nvmem_probe(struct platform_device *pdev) +{ + size_t bytes, bits, magic_bits; + struct pscrr_nvmem *priv; + const char *pscr = "pscr"; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pscrr_dev.dev = &pdev->dev; + priv->pscrr_dev.write = pscrr_nvmem_write; + priv->pscrr_dev.read = pscrr_nvmem_read; + + priv->cell = devm_nvmem_cell_get(&pdev->dev, pscr); + if (IS_ERR(priv->cell)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->cell), + "failed to get the nvmem %s cell\n", pscr); + + ret = nvmem_cell_get_size(priv->cell, &bytes, &bits); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to get the nvmem %s size\n", + pscr); + + if (!bytes || bytes > sizeof(u32) || bits > 32) + return dev_err_probe(&pdev->dev, -EINVAL, "invalid nvmem %s size. bytes: %zu, bits: %zu\n", + pscr, bytes, bits); + + ret = devm_pscrr_register(&pdev->dev, &priv->pscrr_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register pscr driver\n"); + + magic_bits = fls(priv->pscrr_dev.max_magic_val); + priv->max_magic_bytes = DIV_ROUND_UP(magic_bits, 8); + + if (!bits) + bits = bytes * 8; + + if (magic_bits > bits) + return dev_err_probe(&pdev->dev, -EINVAL, "provided magic can't fit into nvmem %s. bytes: %zu, bits: %zu, magic_bits: %zu\n", + pscr, bytes, bits, magic_bits); + + return handle_last_pscr(&priv->pscrr_dev); +} + +static const struct of_device_id pscrr_nvmem_of_match[] = { + { .compatible = "pscrr-nvmem" }, + {} +}; +MODULE_DEVICE_TABLE(of, pscrr_nvmem_of_match); + +static struct platform_driver pscrr_nvmem_driver = { + .probe = pscrr_nvmem_probe, + .driver = { + .name = "pscrr-nvmem", + .of_match_table = pscrr_nvmem_of_match, + }, +}; +module_platform_driver(pscrr_nvmem_driver); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("NVMEM Driver for Power State Change Reason Recording"); +MODULE_LICENSE("GPL"); From patchwork Wed Jan 24 12:22:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 765720 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 BF39264CE0 for ; Wed, 24 Jan 2024 12:22:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098951; cv=none; b=U0ZGSKwLva9lEY4PqzgJyYp8HPFx83yoeK4MrS9zZ08l35LU+cObu+rtujkHWeaB7ucEIqy3bxfKyHajmrNyTX+JItIitH3JL6lOue9Tj8sguQneZLrR9JpI76GJ67l73EzGJthjwD8da6KLE5T9ZMpLuHOQjfbve0r8Isgn0/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706098951; c=relaxed/simple; bh=aGoYIr6WP6qwdiccu6/EeWFB5o3ysjwmHOnTwOOjDkQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=L+Mcc9bdCLp5Uipv7exnSJS+H9jLYsw7R2YrjWxvjDhqo6ANGD0QuZFnWKpAQ4KkGS0MkJzxevbXhHG5itRWqlMR1bfRWgl/KLt+6L4LrNFy/31op3NKZwqqQLypfodOmHS5NIWMZjC6b2ftpRQZi9UbsFeXfmwxJuGRGwgdN5w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rScGd-0007oB-1C; Wed, 24 Jan 2024 13:22:07 +0100 Received: from [2a0a:edc0:0:1101:1d::ac] (helo=dude04.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rScGc-0023Zk-2z; Wed, 24 Jan 2024 13:22:06 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1rScGc-00341u-00; Wed, 24 Jan 2024 13:22:06 +0100 From: Oleksij Rempel To: Sebastian Reichel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Srinivas Kandagatla Cc: Oleksij Rempel , Mark Brown , kernel@pengutronix.de, linux-kernel@vger.kernel.org, Liam Girdwood , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, =?utf-8?q?S=C3=B8ren_Andersen?= Subject: [PATCH v2 7/8] regulator: set Power State Change Reason before hw_protection_shutdown() Date: Wed, 24 Jan 2024 13:22:03 +0100 Message-Id: <20240124122204.730370-8-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240124122204.730370-1-o.rempel@pengutronix.de> References: <20240124122204.730370-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: devicetree@vger.kernel.org Store the state change reason to some black box, for later investigation. Signed-off-by: Oleksij Rempel Reviewed-by: Mark Brown --- drivers/regulator/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a968dabb48f5..a811a5ff2273 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -5095,6 +5096,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); static void regulator_handle_critical(struct regulator_dev *rdev, unsigned long event) { + enum pscr pscr; const char *reason = NULL; if (!rdev->constraints->system_critical) @@ -5103,17 +5105,21 @@ static void regulator_handle_critical(struct regulator_dev *rdev, switch (event) { case REGULATOR_EVENT_UNDER_VOLTAGE: reason = "System critical regulator: voltage drop detected"; + pscr = PSCR_UNDER_VOLTAGE; break; case REGULATOR_EVENT_OVER_CURRENT: reason = "System critical regulator: over-current detected"; + pscr = PSCR_OVER_CURRENT; break; case REGULATOR_EVENT_FAIL: reason = "System critical regulator: unknown error"; + pscr = PSCR_REGULATOR_FAILURE; } if (!reason) return; + set_power_state_change_reason(pscr); hw_protection_shutdown(reason, rdev->constraints->uv_less_critical_window_ms); }