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
