From patchwork Tue Mar 4 13:24:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 870416 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 92BA72040BE for ; Tue, 4 Mar 2025 13:25:00 +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=1741094702; cv=none; b=jik0ELrP3UrqAZfXMkl6yFVTpKuDfFQlPUC7FlJPEE/9b7lWOEVnoJkxsLNwGTwoIqwAAwFc0tih1mQ+lqDzsJK8psykAwfL0phJs7sORyyarn19AHW5mh19V9cAIE7hERM63HnYwuIAuucvxX6Pj+miG8ToYon39T+GZuurX74= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741094702; c=relaxed/simple; bh=97NucGCt7fLDCBZsxvtwk7nqDHmguV4mhYEZZK4elfA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=giiFDnHjUjQc4E1dm/Ln7xibhscc/LmlauYkKyj3MV1KitI8kZ+yptvd57fMDtQ1WjpNdBGUEULptISg01lqeCRp/SiGbQ2FtWmsCS8quTK/KlBUr4ijiwypABpXPbHw7cB/HRlb+4dyX4J+lTQ/DZqZ1jhJmC9wv/VrthLnQTk= 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 1tpSGB-00072A-JP; Tue, 04 Mar 2025 14:24:35 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tpSGA-003yz3-2t; Tue, 04 Mar 2025 14:24:34 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1tpSGA-000sk3-2b; Tue, 04 Mar 2025 14:24:34 +0100 From: Oleksij Rempel To: Sebastian Reichel , 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, =?utf-8?q?S?= =?utf-8?q?=C3=B8ren_Andersen?= Subject: [PATCH v3 4/7] nvmem: add support for device and sysfs-based cell lookups Date: Tue, 4 Mar 2025 14:24:30 +0100 Message-Id: <20250304132433.210355-5-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250304132433.210355-1-o.rempel@pengutronix.de> References: <20250304132433.210355-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-pm@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: linux-pm@vger.kernel.org Introduce new API functions to allow looking up NVMEM devices and cells by name, enhancing flexibility in cases where devicetree-based lookup is not available. Key changes: - Added `nvmem_device_get_by_name()`: Enables retrieving an NVMEM device by its name for systems where direct device reference is needed. - Added `nvmem_cell_get_by_sysfs_name()`: Allows retrieving an NVMEM cell based on its sysfs-style name (e.g., "cell@offset,bits"), making it possible to identify cells dynamically. - Introduced `nvmem_find_cell_entry_by_sysfs_name()`: A helper function that constructs sysfs-like names and searches for matching cell entries. Signed-off-by: Oleksij Rempel --- drivers/nvmem/core.c | 91 ++++++++++++++++++++++++++++++++++ include/linux/nvmem-consumer.h | 14 ++++++ 2 files changed, 105 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 928ac15f04a0..2ff0926dea23 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1203,6 +1203,20 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) EXPORT_SYMBOL_GPL(of_nvmem_device_get); #endif +/** + * nvmem_device_get_by_name - Look up an NVMEM device by its device name + * @name: String matching device name in the provider + * + * Return: A valid pointer to struct nvmem_device on success, + * or ERR_PTR(...) on failure. The caller must later call nvmem_device_put() to + * release the reference. + */ +struct nvmem_device *nvmem_device_get_by_name(const char *name) +{ + return __nvmem_device_get((void *)name, device_match_name); +} +EXPORT_SYMBOL_GPL(nvmem_device_get_by_name); + /** * nvmem_device_get() - Get nvmem device from a given id * @@ -1516,6 +1530,83 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) EXPORT_SYMBOL_GPL(of_nvmem_cell_get); #endif +/** + * nvmem_find_cell_entry_by_sysfs_name - Find an NVMEM cell entry by its sysfs + * name. + * @nvmem: The nvmem_device pointer where the cell is located. + * @sysfs_name: The full sysfs cell name, e.g. "mycell@0x100,8". + * + * This function constructs the sysfs-like name for each cell and compares it + * to @sysfs_name. If a match is found, the matching nvmem_cell_entry pointer + * is returned. This is analogous to nvmem_find_cell_entry_by_name(), except + * it matches on the sysfs naming convention used in the device's attributes. + * + * Return: Pointer to the matching nvmem_cell_entry on success, or NULL if no + * match is found. + */ +static struct nvmem_cell_entry * +nvmem_find_cell_entry_by_sysfs_name(struct nvmem_device *nvmem, + const char *sysfs_name) +{ + struct nvmem_cell_entry *entry; + char *tmp; + + mutex_lock(&nvmem_mutex); + list_for_each_entry(entry, &nvmem->cells, node) { + /* Reconstruct how the sysfs name is assigned */ + tmp = kasprintf(GFP_KERNEL, "%s@%x,%u", entry->name, + entry->offset, entry->bit_offset); + if (!tmp) + continue; + + if (!strcmp(tmp, sysfs_name)) { + kfree(tmp); + mutex_unlock(&nvmem_mutex); + return entry; + } + kfree(tmp); + } + mutex_unlock(&nvmem_mutex); + + return NULL; +} + +/** + * nvmem_cell_get_by_sysfs_name - Get a cell by its sysfs name from a given + * nvmem_device. + * @nvmem: The nvmem_device pointer where the cell is located. + * @sysfs_name: The sysfs-style name, e.g. "mycell@0x100,8". + * + * This function uses nvmem_find_cell_entry_by_sysfs_name() to locate the cell + * entry, increments the reference counts for the matching NVMEM device, and + * then creates a struct nvmem_cell for the caller. + * + * Return: On success, a valid pointer to an nvmem_cell. On failure, an + * ERR_PTR() encoded error (e.g., -ENOENT if the cell entry is not found). + */ +struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem, + const char *sysfs_name) +{ + struct nvmem_cell_entry *entry; + struct nvmem_cell *cell; + + entry = nvmem_find_cell_entry_by_sysfs_name(nvmem, sysfs_name); + if (!entry) + return ERR_PTR(-ENOENT); + + if (!try_module_get(nvmem->owner)) + return ERR_PTR(-EINVAL); + + kref_get(&nvmem->refcnt); + + cell = nvmem_create_cell(entry, entry->name, 0); + if (IS_ERR(cell)) + __nvmem_device_put(nvmem); + + return cell; +} +EXPORT_SYMBOL_GPL(nvmem_cell_get_by_sysfs_name); + /** * nvmem_cell_get() - Get nvmem cell of device form a given cell name * diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index bcb0e17e415d..6fc12cce8eb2 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -52,6 +52,8 @@ enum { /* Cell based interface */ struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id); struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id); +struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem, + const char *cell_name); void nvmem_cell_put(struct nvmem_cell *cell); void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); @@ -70,6 +72,7 @@ int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id, struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *name); +struct nvmem_device *nvmem_device_get_by_name(const char *name); void nvmem_device_put(struct nvmem_device *nvmem); void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem); int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, @@ -109,6 +112,12 @@ static inline struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem, + const char *cell_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell) { @@ -185,6 +194,11 @@ static inline struct nvmem_device *devm_nvmem_device_get(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +struct nvmem_device *nvmem_device_get_by_name(const char *name) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline void nvmem_device_put(struct nvmem_device *nvmem) { } From patchwork Tue Mar 4 13:24:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 870414 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 96D4D204594 for ; Tue, 4 Mar 2025 13:25:00 +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=1741094703; cv=none; b=ntDONl4Gvb9s2KszG2e9B2u0I6CLAiNtc8aXz73BeUj5cIHLpJ7XZy1L+AEWHiFmpJDrcVhqtK1DLLh7GeRkdApRzD2xKaLf7T4A4Y4vL/d5j75QHgVBJ8U/kwa0HCWaGCoTsLWRpNpCKQu8WJeSBZge+/UY/frcRl5yr/BOBO0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741094703; c=relaxed/simple; bh=dwoFRUYoGAVCvcBi/bhIYzgTcMHv0zq4ziZL/ISCsh8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IMYchnv4FlKw4aU8Lm9H4fMJUyoa65g8rPkZ/OEemwsatanmSMpuyxthBZPmp0jsdVtMVtkdYty/sW6nz+NxCfk3Y8v2KkVwNVUyhBt4HG50+kZ53r/VS6owkKCYUtmgKwA5F0iJqOPL4hLtxOiSO2GArlFbi2loks5W0qYEfLc= 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 1tpSGB-00072B-JP; Tue, 04 Mar 2025 14:24:35 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tpSGA-003yz6-2w; Tue, 04 Mar 2025 14:24:34 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1tpSGA-000skD-2f; Tue, 04 Mar 2025 14:24:34 +0100 From: Oleksij Rempel To: Sebastian Reichel , 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, =?utf-8?q?S?= =?utf-8?q?=C3=B8ren_Andersen?= Subject: [PATCH v3 5/7] power: reset: add PSCR NVMEM Driver for Recording Power State Change Reasons Date: Tue, 4 Mar 2025 14:24:31 +0100 Message-Id: <20250304132433.210355-6-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250304132433.210355-1-o.rempel@pengutronix.de> References: <20250304132433.210355-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-pm@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: linux-pm@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 | 254 ++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/power/reset/pscrr-nvmem.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 0f6aee415f1c..de7bae1b3be3 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -335,3 +335,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 dbd6ae6b26a4..532698552d40 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -33,6 +33,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..c6912a1a4f7b --- /dev/null +++ b/drivers/power/reset/pscrr-nvmem.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pscrr_nvmem.c - PSCRR backend for storing shutdown reasons in small NVMEM + * cells + * + * This backend provides a way to persist power state change reasons in a + * non-volatile memory (NVMEM) cell, ensuring that reboot causes can be + * analyzed post-mortem. Unlike traditional logging to eMMC or NAND, which + * may be unreliable during power failures, this approach allows storing + * reboot reasons in small, fast-access storage like RTC scratchpads, EEPROM, + * or FRAM. + * + * The module allows dynamic configuration of the NVMEM device and cell + * via module parameters: + * + * Example usage: + * modprobe pscrr-nvmem nvmem_name=pcf85063_nvram0 cell_name=pscr@0,0 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Module parameters: + * nvmem_name: Name of the NVMEM device (e.g. "pcf85063_nvram0"). + * cell_name : Sysfs name of the cell on that device (e.g. "pscr@0,0"). + */ +static char *nvmem_name; +module_param(nvmem_name, charp, 0444); +MODULE_PARM_DESC(nvmem_name, "Name of the NVMEM device (e.g. pcf85063_nvram0)"); + +static char *cell_name; +module_param(cell_name, charp, 0444); +MODULE_PARM_DESC(cell_name, "Sysfs name of the NVMEM cell (e.g. pscr@0,0)"); + +struct pscrr_nvmem_priv { + struct nvmem_device *nvmem; + struct nvmem_cell *cell; + + unsigned int total_bits; + size_t max_val; +}; + +static struct pscrr_nvmem_priv *priv; + +static int pscrr_nvmem_write_reason(enum pscr_reason reason) +{ + size_t required_bytes; + u32 val; + int ret; + + if (!priv || !priv->cell) + return -ENODEV; + + /* Ensure reason fits in the available storage */ + if (reason > priv->max_val) { + pr_err("PSCRR-nvmem: Reason %d exceeds max storable value %zu for %u-bit cell\n", + reason, priv->max_val, priv->total_bits); + return -ERANGE; + } + + val = reason; + + /* Determine required bytes for storing total_bits */ + required_bytes = (priv->total_bits + 7) / 8; + + /* Write the reason to the NVMEM cell */ + ret = nvmem_cell_write(priv->cell, &val, required_bytes); + if (ret < 0) { + pr_err("PSCRR-nvmem: Failed to write reason %d, err=%d (%pe)\n", + reason, ret, ERR_PTR(ret)); + return ret; + } + + pr_debug("PSCRR-nvmem: Successfully wrote reason %d\n", reason); + + return 0; +} + +static int pscrr_nvmem_read_reason(enum pscr_reason *reason) +{ + unsigned int required_bytes, val; + int ret = 0; + size_t len; + void *buf; + + if (!priv || !priv->cell) + return -ENODEV; + + buf = nvmem_cell_read(priv->cell, &len); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + pr_err("PSCRR-nvmem: Failed to read cell, err=%d (%pe)\n", ret, + ERR_PTR(ret)); + return ret; + } + + /* Calculate the required number of bytes */ + required_bytes = (priv->total_bits + 7) / 8; + + /* Validate that the returned length is large enough */ + if (len < required_bytes) { + pr_err("PSCRR-nvmem: Read length %zu is too small (need at least %u bytes)\n", + len, required_bytes); + kfree(buf); + return -EIO; + } + + /* Extract value safely with proper memory alignment handling */ + val = 0; + memcpy(&val, buf, required_bytes); + + /* Mask only the necessary bits to avoid garbage data */ + val &= (1U << priv->total_bits) - 1; + + kfree(buf); + + *reason = (enum pscr_reason)val; + + pr_debug("PSCRR-nvmem: Read reason => %d (from %zu bytes, %u bits used)\n", + *reason, len, priv->total_bits); + + return 0; +} + +static const struct pscrr_backend_ops pscrr_nvmem_ops = { + .write_reason = pscrr_nvmem_write_reason, + .read_reason = pscrr_nvmem_read_reason, +}; + +static int __init pscrr_nvmem_init(void) +{ + size_t bytes, bits; + int ret; + + if (!nvmem_name || !cell_name) { + pr_err("PSCRR-nvmem: Must specify both nvmem_name and cell_name.\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->nvmem = nvmem_device_get_by_name(nvmem_name); + if (IS_ERR(priv->nvmem)) { + ret = PTR_ERR(priv->nvmem); + pr_err("PSCRR-nvmem: nvmem_device_get_by_name(%s) failed: %d\n", + nvmem_name, ret); + priv->nvmem = NULL; + goto err_free; + } + + priv->cell = nvmem_cell_get_by_sysfs_name(priv->nvmem, cell_name); + if (IS_ERR(priv->cell)) { + ret = PTR_ERR(priv->cell); + pr_err("PSCRR-nvmem: nvmem_cell_get_by_sysfs_name(%s) failed, err=%pe\n", + cell_name, ERR_PTR(ret)); + priv->cell = NULL; + goto err_dev_put; + } + + ret = nvmem_cell_get_size(priv->cell, &bytes, &bits); + if (ret < 0) { + pr_err("PSCRR-nvmem: Failed to get cell size, err=%pe\n", + ERR_PTR(ret)); + goto err_cell_put; + } + + if (bits) + priv->total_bits = bits; + else + priv->total_bits = bytes * 8; + + if (priv->total_bits > 31) { + pr_err("PSCRR-nvmem: total_bits=%u is too large (max 31 allowed)\n", + priv->total_bits); + return -EOVERFLOW; + } + + priv->max_val = (1 << priv->total_bits) - 1; + pr_debug("PSCRR-nvmem: Cell size: %zu bytes + %zu bits => total_bits=%u\n", + bytes, bits, priv->total_bits); + + /* + * If we store reasons 0..PSCR_MAX_REASON, the largest needed is + * 'PSCR_MAX_REASON'. That must fit within total_bits. + * So the max storable integer is (1 << total_bits) - 1. + */ + if (priv->max_val < PSCR_MAX_REASON) { + pr_err("PSCRR-nvmem: Not enough bits (%u) to store up to reason=%d\n", + priv->total_bits, PSCR_MAX_REASON); + ret = -ENOSPC; + goto err_cell_put; + } + + /* 4. Register with pscrr_core. */ + ret = pscrr_core_init(&pscrr_nvmem_ops); + if (ret) { + pr_err("PSCRR-nvmem: pscrr_core_init() failed: %d\n", ret); + goto err_cell_put; + } + + pr_info("PSCRR-nvmem: Loaded (nvmem=%s, cell=%s), can store 0..%zu\n", + nvmem_name, cell_name, priv->max_val); + return 0; + +err_cell_put: + if (priv->cell) { + nvmem_cell_put(priv->cell); + priv->cell = NULL; + } +err_dev_put: + if (priv->nvmem) { + nvmem_device_put(priv->nvmem); + priv->nvmem = NULL; + } +err_free: + kfree(priv); + priv = NULL; + return ret; +} + +static void __exit pscrr_nvmem_exit(void) +{ + pscrr_core_exit(); + + if (priv) { + if (priv->cell) { + nvmem_cell_put(priv->cell); + priv->cell = NULL; + } + if (priv->nvmem) { + nvmem_device_put(priv->nvmem); + priv->nvmem = NULL; + } + kfree(priv); + priv = NULL; + } + + pr_info("pscrr-nvmem: Unloaded\n"); +} + +module_init(pscrr_nvmem_init); +module_exit(pscrr_nvmem_exit); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("PSCRR backend for storing reason code in NVMEM"); +MODULE_LICENSE("GPL"); From patchwork Tue Mar 4 13:24:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 870413 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 997112045A2 for ; Tue, 4 Mar 2025 13:25:00 +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=1741094705; cv=none; b=ATU1K/rAqXY1lqowscLxhsyecLv6fX4/N5lfsHUK90Au8DdjG3VDnzqNJeJXenqdVmKM5TLMqTtiBCKY3+xCS0TzcypTuQ537vbNWhhBg3pzIKH55juPEqHjhk4vivkrOgewa0bKyht8U3ucY2YJ6KupV5+Ljem8d69rvQbscqE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741094705; c=relaxed/simple; bh=ARAxY0xvTWqixzQG4QU/4LIxy5SECtoC51NXpgQiomA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KuXLr68FqZHbchxxL2mPBJPHmUEE2bP4vlfDJx6JvUKW1HKWaCpjDSxphEbtbzyg3APgRPhUcjkO1ycnc++HlYyCwIUs4Ks9+92d9Jv/taKm0UJiSE8bVMfPqe3MUF+0WKB02BecOO7UMDRa7tucuj5u+GIJPVjvSYpQlmnmv+U= 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 1tpSGB-00072C-JP; Tue, 04 Mar 2025 14:24:35 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tpSGA-003yz8-32; Tue, 04 Mar 2025 14:24:34 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1tpSGA-000skO-2j; Tue, 04 Mar 2025 14:24:34 +0100 From: Oleksij Rempel To: Sebastian Reichel , 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, =?utf-8?q?S?= =?utf-8?q?=C3=B8ren_Andersen?= Subject: [PATCH v3 6/7] regulator: set Power State Change Reason before hw_protection_shutdown() Date: Tue, 4 Mar 2025 14:24:32 +0100 Message-Id: <20250304132433.210355-7-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250304132433.210355-1-o.rempel@pengutronix.de> References: <20250304132433.210355-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-pm@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: linux-pm@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 89578b91c468..3d04631c9eab 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -5243,6 +5244,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); static void regulator_handle_critical(struct regulator_dev *rdev, unsigned long event) { + enum pscr_reason pscr; const char *reason = NULL; if (!rdev->constraints->system_critical) @@ -5251,17 +5253,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); } From patchwork Tue Mar 4 13:24:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 870415 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 92AAC20409B for ; Tue, 4 Mar 2025 13:25:00 +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=1741094703; cv=none; b=l84hvad8dX0rPL5sl0MLaKyBmuBKJEz0jGoFJLBQFsi06NY1FM7wISe0wzVPfi/v7mHjs/3IepIPwPxSBsTND/2/rSWc4m5HWOgSmvh7wUqxyzdggZFLmeaSkjTfN2xSKIl4VFcM1v/nG3H+vLooG0oWCtg/S/WR+YCkQbMlwZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741094703; c=relaxed/simple; bh=X22BauJ/yxhjyd9bdHmx73unUwfLqnlpQZoxOrJ9bLs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JzIKqJWDecB0gYV1OJO4zWMDzoafk8YUe5tUDa09cywFZl/iMjFUpq8CuGS5jjowMgpE88Icd1DMedjEI5AKj+rp/yHRwyKrsbmNGdmH6XXSfPhoH8U04M0dTBMz+ST1Pr9Lags62ZOYmTYAE2R291dVbkQ0omqs2LgFzk+D/a8= 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 1tpSGB-00072D-JP; Tue, 04 Mar 2025 14:24:35 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tpSGA-003yzA-37; Tue, 04 Mar 2025 14:24:34 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1tpSGA-000skZ-2n; Tue, 04 Mar 2025 14:24:34 +0100 From: Oleksij Rempel To: Sebastian Reichel , 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, =?utf-8?q?S?= =?utf-8?q?=C3=B8ren_Andersen?= Subject: [PATCH v3 7/7] thermal: core: Record PSCR before hw_protection_shutdown() Date: Tue, 4 Mar 2025 14:24:33 +0100 Message-Id: <20250304132433.210355-8-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250304132433.210355-1-o.rempel@pengutronix.de> References: <20250304132433.210355-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-pm@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: linux-pm@vger.kernel.org Enhance the thermal core to record the Power State Change Reason (PSCR) prior to invoking hw_protection_shutdown(). This change integrates the PSCR framework with the thermal subsystem, ensuring that reasons for power state changes, such as overtemperature events, are stored in a dedicated non-volatile memory (NVMEM) cell. This 'black box' recording is crucial for post-mortem analysis, enabling a deeper understanding of system failures and abrupt shutdowns, especially in scenarios where PMICs or watchdog timers are incapable of logging such events. The recorded data can be utilized during system recovery routines in the bootloader or early kernel stages of subsequent boots, significantly enhancing system diagnostics, reliability, and debugging capabilities. Signed-off-by: Oleksij Rempel --- drivers/thermal/thermal_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2328ac0d8561..af4e9cf22bf6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,8 @@ static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdo dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type); + set_power_state_change_reason(PSCR_OVERTEMPERATURE); + if (shutdown) hw_protection_shutdown(msg, poweroff_delay_ms); else