[PATCH] firmware: qcom: scm: clear download mode and SDI before a restart
From: Royyan Zahir
Date: Mon Sep 21 2026 - 03:34:03 EST
With qcom_scm.download_mode=1 an IQ-9075 never completes a warm reboot.
XBL stops in download mode and only a cold power cycle recovers the board:
B - 857233 - TCSR reg value 0x10
B - 865071 - RamDump - Image Load, Start
B - 884317 - boot_dload_entry
B - 6774477 - USB DUMP failed, because USB cable disconnect
0x10 is FIELD_PREP(QCOM_DLOAD_MASK, QCOM_DLOAD_FULLDUMP), so the cookie
written at probe is still set when XBL reads it, even though systemd
stopped every unit in order and nothing panicked.
Two latches have to come down and neither reliably does.
qcom_scm_shutdown() clears the cookie from .shutdown, which
device_shutdown() calls after the SCM device's own dependencies may
already be torn down, so the write can fail with nothing left to report
it to. SDI is disabled only from probe, and only when download_mode was
zero at that moment, so a board booted with download mode on keeps
watchdog debug armed for the life of the boot. Writing "off" to the
module parameter later clears the cookie but never reaches SDI, which is
why that runtime workaround does not restore warm reboot.
Clear both from a reboot notifier. kernel_restart_prepare() calls the
reboot_notifier_list before device_shutdown(), so the SCM device and its
interconnect path are still up. SYS_OFF_MODE_RESTART_PREPARE is not
usable here: kernel_restart() runs do_kernel_restart_prepare() after
kernel_restart_prepare(), which is after device_shutdown().
The notifier reaches its instance with container_of() rather than the
global, so it does not conflict with the series dropping __scm [1].
Registration warns rather than failing probe, because the instance is
already published at that point and callers may be using it.
.shutdown stays for the power-off path.
Tested on an IQ-9075 EVK with download mode armed: warm reboot returns in
~36s, and a console capture across a cold boot and the warm reboot shows
two complete boot cycles with no TCSR cookie read and no boot_dload_entry.
[1] https://lore.kernel.org/linux-arm-msm/20260914-scm-device-api-v1-0-3573e2596c51@xxxxxxxxxx/
Signed-off-by: Royyan Zahir <royzah@xxxxxxxxx>
---
drivers/firmware/qcom/qcom_scm.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 3eaa4c9..06cba43 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -27,6 +27,7 @@
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/reset-controller.h>
#include <linux/remoteproc.h>
#include <linux/sizes.h>
@@ -63,6 +64,8 @@ struct qcom_scm {
struct qcom_tzmem_pool *mempool;
unsigned int wq_cnt;
+
+ struct notifier_block reboot_nb;
};
struct qcom_scm_current_perm_info {
@@ -2790,6 +2793,20 @@ static const struct kernel_param_ops minidump_dest_param_ops = {
module_param_cb(minidump_dest, &minidump_dest_param_ops, NULL, 0644);
MODULE_PARM_DESC(minidump_dest, "Minidump SRAM destination: usb (default) or storage");
+/* reboot_notifier_list runs before device_shutdown(); RESTART_PREPARE does not. */
+static int qcom_scm_reboot_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct qcom_scm *scm = container_of(nb, struct qcom_scm, reboot_nb);
+
+ qcom_scm_set_download_mode(scm, QCOM_DLOAD_NODUMP);
+
+ /* Separate latch: probe only disables SDI when download mode is off. */
+ qcom_scm_disable_sdi();
+
+ return NOTIFY_DONE;
+}
+
static int qcom_scm_probe(struct platform_device *pdev)
{
struct qcom_tzmem_pool_config pool_config;
@@ -2941,6 +2958,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
/* Initialize the Gunyah watchdog platform device. */
qcom_scm_gunyah_wdt_init(scm);
+ scm->reboot_nb.notifier_call = qcom_scm_reboot_notify;
+ ret = devm_register_reboot_notifier(&pdev->dev, &scm->reboot_nb);
+ if (ret)
+ dev_warn(scm->dev, "failed to register reboot notifier: %d\n", ret);
+
return 0;
err_rmem:
--
2.55.0