From: Vandhiadevan Karunamoorthy <[email protected]>

geni_se_of_to_plat() caches need_firmware_load once at bind time by
scanning each child's FW_REVISION_RO register. qcom_geni_fw_initialise()
only ever consults that cached flag, so it cannot detect firmware that
was loaded after bind time, and a second call cannot pick up a wrapper
that still needs firmware if storage became available later in boot
(e.g. after a fastboot-triggered re-probe).

Extract the scan into geni_se_needs_firmware() and re-run it inside
qcom_geni_fw_initialise() against live hardware state instead of only
trusting the cached flag, so repeated calls are safe: a call with
nothing left to load is a no-op that returns success.

Expose qcom_geni_fw_initialise() and add the 'qcom_geni fw init'
command so a retry can be triggered manually or from other subsystems
without hardcoding SoC-specific calls into generic code.

Signed-off-by: Vandhiadevan Karunamoorthy 
<[email protected]>
---
 cmd/Kconfig                    |  9 +++++++++
 cmd/Makefile                   |  1 +
 cmd/qcom_geni.c                | 26 ++++++++++++++++++++++++++
 drivers/misc/qcom_geni.c       | 24 ++++++++++++++++--------
 include/soc/qcom/qup-fw-load.h |  7 +++++++
 5 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index c71c6824a19..453ee94f6f5 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2884,6 +2884,15 @@ config CMD_SCMI
          This command provides user interfaces to several SCMI (System
          Control and Management Interface) protocols available on Arm
          platforms to manage system resources.
+
+config CMD_QCOM_GENI
+       bool "qcom_geni - Qualcomm GENI SE firmware control"
+       depends on QCOM_GENI
+       help
+         Provides the 'qcom_geni fw init' command, which (re)loads firmware
+         for Qualcomm GENI SE wrappers that still need it and probes their
+         dependent children (SPI/I2C/UART). Useful to retry firmware loading
+         once it was previously skipped.
 endmenu
 
 menu "Filesystem commands"
diff --git a/cmd/Makefile b/cmd/Makefile
index bbbdfcc4ded..1db96718938 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -164,6 +164,7 @@ obj-$(CONFIG_CMD_KASLRSEED) += kaslrseed.o
 obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
 obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o
 obj-$(CONFIG_CMD_RTC) += rtc.o
+obj-$(CONFIG_CMD_QCOM_GENI) += qcom_geni.o
 obj-$(CONFIG_SANDBOX) += host.o
 obj-$(CONFIG_CMD_SATA) += sata.o
 obj-$(CONFIG_CMD_NVME) += nvme.o
diff --git a/cmd/qcom_geni.c b/cmd/qcom_geni.c
new file mode 100644
index 00000000000..31cb3f454d4
--- /dev/null
+++ b/cmd/qcom_geni.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <command.h>
+#include <linux/string.h>
+#include <soc/qcom/qup-fw-load.h>
+
+static int do_qcom_geni(struct cmd_tbl *cmdtp, int flag, int argc, char *const 
argv[])
+{
+       if (argc != 3 || strcmp(argv[1], "fw") || strcmp(argv[2], "init"))
+               return CMD_RET_USAGE;
+
+       if (qcom_geni_fw_initialise())
+               return CMD_RET_FAILURE;
+
+       return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(qcom_geni, 3, 1, do_qcom_geni,
+          "Qualcomm GENI SE firmware control",
+          "fw init\tLoad QUP firmware for GENI SE wrappers that still need it 
and\n"
+          "\t\tprobe their dependent children (SPI/I2C/UART). Safe to run 
again\n"
+          "\t\tonce firmware is already loaded.\n"
+);
diff --git a/drivers/misc/qcom_geni.c b/drivers/misc/qcom_geni.c
index 2e8f72d5c8e..41a6fd153a3 100644
--- a/drivers/misc/qcom_geni.c
+++ b/drivers/misc/qcom_geni.c
@@ -396,14 +396,11 @@ int qcom_geni_load_firmware(phys_addr_t qup_base,
  * We need to determine if firmware loading is necessary. Best way to do that 
is to check the FW
  * revision of each QUP and see if it has already been loaded.
  */
-static int geni_se_of_to_plat(struct udevice *dev)
+static bool geni_se_needs_firmware(struct udevice *dev)
 {
        ofnode child;
        struct resource res;
        u32 proto;
-       struct geni_se_plat *plat = dev_get_plat(dev);
-
-       plat->need_firmware_load = false;
 
        dev_for_each_subnode(child, dev) {
                if (!ofnode_is_enabled(child))
@@ -417,9 +414,18 @@ static int geni_se_of_to_plat(struct udevice *dev)
                proto >>= FW_REV_PROTOCOL_SHFT;
 
                if (proto == GENI_SE_INVALID_PROTO)
-                       plat->need_firmware_load = true;
+                       return true;
        }
 
+       return false;
+}
+
+static int geni_se_of_to_plat(struct udevice *dev)
+{
+       struct geni_se_plat *plat = dev_get_plat(dev);
+
+       plat->need_firmware_load = geni_se_needs_firmware(dev);
+
        return 0;
 }
 
@@ -496,7 +502,7 @@ static int probe_children_load_firmware(struct udevice *dev)
  * Load firmware for QCOM GENI peripherals from the dedicated partition on 
storage and bind/probe
  * all the peripheral devices that need firmware to be loaded.
  */
-static int qcom_geni_fw_initialise(void)
+int qcom_geni_fw_initialise(void)
 {
        debug("Loading firmware for QCOM GENI SE\n");
        struct udevice *geni_wrapper, *blk_dev;
@@ -506,19 +512,21 @@ static int qcom_geni_fw_initialise(void)
        size_t fw_size = MAX_FW_BUF_SIZE;
        struct geni_se_plat *plat;
 
-       /* Find the first GENI SE wrapper that needs fw loading */
+       /* Find the first GENI SE wrapper that still needs fw loading */
        for (uclass_first_device(UCLASS_MISC, &geni_wrapper);
             geni_wrapper;
             uclass_next_device(&geni_wrapper)) {
                if (device_get_uclass_id(geni_wrapper) == UCLASS_MISC &&
                    !strcmp(geni_wrapper->driver->name, "geni-se-qup")) {
                        plat = dev_get_plat(geni_wrapper);
+                       /* Refresh against live hardware state for repeated 
calls. */
+                       plat->need_firmware_load = 
geni_se_needs_firmware(geni_wrapper);
                        if (plat->need_firmware_load)
                                break;
                }
        }
        if (!geni_wrapper) {
-               pr_err("GENI SE wrapper not found\n");
+               debug("GENI firmware is already initialized\n");
                return 0;
        }
 
diff --git a/include/soc/qcom/qup-fw-load.h b/include/soc/qcom/qup-fw-load.h
index a67a93c72a4..f4417fc1baf 100644
--- a/include/soc/qcom/qup-fw-load.h
+++ b/include/soc/qcom/qup-fw-load.h
@@ -175,4 +175,11 @@ struct udevice;
 
 int qcom_geni_load_firmware(phys_addr_t qup_base, struct udevice *dev);
 
+/*
+ * Load firmware for QCOM GENI SE wrappers that still need it and probe
+ * their dependent children. Idempotent: a call with nothing left to load
+ * is a no-op that returns success.
+ */
+int qcom_geni_fw_initialise(void);
+
 #endif /* _LINUX_QCOM_QUP_FW_LOAD */

-- 
2.34.1

Reply via email to