[PATCH 3/4] firmware: qcom: scm: Add minidump SRAM support
From: Mukesh Ojha <hidden>
Date: 2026-05-07 08:07:50
Also in:
linux-arm-msm, lkml
Subsystem:
arm/qualcomm mailing list, arm/qualcomm support, the rest · Maintainers:
Bjorn Andersson, Konrad Dybcio, Linus Torvalds
On most Qualcomm SoCs where minidump is supported, a word in always-on SRAM is shared between the kernel and boot firmware. Before DDR is initialised on the warm reset following a crash, firmware reads this word to decide if minidump is enabled and collect a minidump and where to deliver it (USB upload to a host, or save to local storage). The SRAM region is described by a 'sram'/'sram-names' phandle pair on the SCM DT node. If the property is absent the feature is silently disabled, keeping existing SoCs unaffected. Expose a 'minidump_dest' module parameter (default: usb) so the user can select the destination. Only the string names "usb" or "storage" are accepted; an invalid value is rejected with -EINVAL. Changing the destination while minidump mode is already active updates SRAM immediately. Suggested-by: Konrad Dybcio <redacted> Signed-off-by: Mukesh Ojha <redacted> --- drivers/firmware/qcom/qcom_scm.c | 95 ++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index f65b132004a5..b57f8cce7a8c 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c@@ -57,6 +57,7 @@ struct qcom_scm { int scm_vote_count; u64 dload_mode_addr; + void __iomem *minidump_sram; struct qcom_tzmem_pool *mempool; unsigned int wq_cnt;
@@ -141,6 +142,18 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { #define QCOM_DLOAD_MINIDUMP 2 #define QCOM_DLOAD_BOTHDUMP 3 +/* Minidump destination values written to always-on SRAM for boot firmware */ +#define QCOM_MINIDUMP_DEST_USB 0x0 +#define QCOM_MINIDUMP_DEST_STORAGE 0x2 + +static u32 minidump_dest = QCOM_MINIDUMP_DEST_USB; + +static const char * const minidump_dest_name[] = { "usb", "storage" }; +static const u32 minidump_dest_val[] = { + QCOM_MINIDUMP_DEST_USB, + QCOM_MINIDUMP_DEST_STORAGE, +}; + #define QCOM_SCM_DEFAULT_WAITQ_COUNT 1 static const char * const qcom_scm_convention_names[] = {
@@ -568,6 +581,17 @@ static void qcom_scm_set_download_mode(u32 dload_mode) if (ret) dev_err(__scm->dev, "failed to set download mode: %d\n", ret); + + /* + * Mirror the destination into the always-on SRAM so boot firmware + * can read it before DDR is initialised on the next warm reset. + * Only written when minidump is active; skip if SRAM already holds + * the requested destination to avoid unnecessary writes. + */ + if (__scm->minidump_sram && (dload_mode & QCOM_DLOAD_MINIDUMP)) { + if (readl_relaxed(__scm->minidump_sram) != minidump_dest) + writel_relaxed(minidump_dest, __scm->minidump_sram); + } } /**
@@ -2055,6 +2079,37 @@ int qcom_scm_gpu_init_regs(u32 gpu_req) } EXPORT_SYMBOL_GPL(qcom_scm_gpu_init_regs); +static int qcom_scm_map_minidump_sram(struct device *dev, void __iomem **out) +{ + struct device_node *np = dev->of_node; + struct device_node *sram_np; + struct resource res; + int ret; + + if (of_property_match_string(np, "sram-names", "minidump") < 0) + return 0; + + sram_np = of_parse_phandle(np, "sram", 0); + if (!sram_np) + return -EINVAL; + + ret = of_address_to_resource(sram_np, 0, &res); + of_node_put(sram_np); + if (ret) + return ret; + + if (resource_size(&res) < sizeof(u32)) { + dev_err(dev, "minidump SRAM region too small\n"); + return -EINVAL; + } + + *out = devm_ioremap(dev, res.start, resource_size(&res)); + if (!*out) + return -ENOMEM; + + return 0; +} + static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) { struct device_node *tcsr;
@@ -2748,6 +2803,41 @@ static const struct kernel_param_ops download_mode_param_ops = { module_param_cb(download_mode, &download_mode_param_ops, NULL, 0644); MODULE_PARM_DESC(download_mode, "download mode: off/0/N for no dump mode, full/on/1/Y for full dump mode, mini for minidump mode and full,mini for both full and minidump mode together are acceptable values"); +static int get_minidump_dest(char *buffer, const struct kernel_param *kp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(minidump_dest_val); i++) + if (minidump_dest == minidump_dest_val[i]) + return sysfs_emit(buffer, "%s\n", minidump_dest_name[i]); + + return sysfs_emit(buffer, "unknown\n"); +} + +static int set_minidump_dest(const char *val, const struct kernel_param *kp) +{ + int i; + + i = sysfs_match_string(minidump_dest_name, val); + if (i < 0) + return -EINVAL; + + minidump_dest = minidump_dest_val[i]; + if (__scm && __scm->minidump_sram && (download_mode & QCOM_DLOAD_MINIDUMP) && + readl_relaxed(__scm->minidump_sram) != minidump_dest) + writel_relaxed(minidump_dest, __scm->minidump_sram); + + return 0; +} + +static const struct kernel_param_ops minidump_dest_param_ops = { + .get = get_minidump_dest, + .set = set_minidump_dest, +}; + +module_param_cb(minidump_dest, &minidump_dest_param_ops, NULL, 0644); +MODULE_PARM_DESC(minidump_dest, "Minidump SRAM destination: usb (default) or storage"); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_tzmem_pool_config pool_config;
@@ -2765,6 +2855,11 @@ static int qcom_scm_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to find download mode address\n"); + ret = qcom_scm_map_minidump_sram(&pdev->dev, &scm->minidump_sram); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to map minidump SRAM\n"); + mutex_init(&scm->scm_bw_lock); scm->path = devm_of_icc_get(&pdev->dev, NULL);
--
2.53.0