Four of the six serial engines of the QCM2290 QUPv3 (SE2 to SE5) are minicores: they implement SPI, UART and I2C in hardware state machines and have no program RAM. Their SE_HW_PARAM_2 reads 0xf000, i.e. GEN_USE_MINICORES and the GEN_HW_FSM_* bits set and a PROG_RAM_DEPTH of 0 (SE0 and SE1 report 0x3fc and 0x1c8 and do have one). The loader refuses every protocol firmware on them: qcom_geni_serial 4a8c000.serial: Firmware size (444) exceeds RAM size (0) geni_spi 4a94000.spi: Firmware size (442) exceeds RAM size (0) On such an engine only the register configuration part of the image applies, and GENI_FW_REVISION and GENI_S_FW_REVISION still have to carry the protocol; U-Boot's "misc: qcom_geni: Add minicore support" handles such engines the same way. Detect the minicore bits, skip the depth check and the program RAM copy for them and keep the rest of the sequence. On the Arduino UNO Q this brings up the Bluetooth UART and the SPI engine, which were the only engines the boot firmware had not set up. Fixes: 522bfb4f33c0 ("soc: qcom: geni-se: Use HW PROG_RAM_DEPTH to validate firmware size") Cc: stable@vger.kernel.org Signed-off-by: Igor Opaniuk --- Tested on the Arduino UNO Q (QRB2210) with next-20261112, whose loader is the same as in drivers-for-7.5, booted through TF-A so that Linux loads the QUP firmware itself: - without the patch the UART (4a8c000, Bluetooth) and SPI (4a94000) engines refuse their images with the messages above and no spidev appears; - with it, both load and work (Bluetooth up, spidev to the MCU), I2C is unchanged, and the board test suite passes. U-Boot's minicore support [1] programs these engines the same way. Neil's "ignore invalid PROG_RAM_DEPTH when checking for firmware size" [2] would get them past the size check but still copy the image into a program RAM they do not have. Engines whose depth comes from the wrapper data (the I2C hub, 803f9950a6c6) are never treated as minicore here. Viken's pending "Leave resource management to protocol drivers" [3] touches the same function and still applies on top. [1] https://patch.msgid.link/20260721-minicore-v3-1-172e5f28138b@oss.qualcomm.com [2] https://lore.kernel.org/r/20260917-topic-sm8x50-i2c-hub-fw-ram-v1-1-f49286d8c8db@linaro.org [3] https://lore.kernel.org/r/20261025-gen-se-remove-resources-handling-v1-1-e81da6707b5f@oss.qualcomm.com --- drivers/soc/qcom/qcom-geni-se.c | 57 +++++++++++++++++++++++++++++++++++++--- include/linux/soc/qcom/geni-se.h | 6 +++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index ce4de4917a83..1cc4788942e7 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -1243,6 +1243,54 @@ int geni_se_resources_init(struct geni_se *se) } EXPORT_SYMBOL_GPL(geni_se_resources_init); +/** + * geni_se_is_minicore() - Check whether a protocol runs on a hardware FSM. + * @se: Pointer to the concerned serial engine. + * @protocol: Protocol to check. + * + * Minicore serial engines implement the protocol in a hardware state + * machine and have no program RAM: SE_HW_PARAM_2 reports a depth of 0 and + * sets GEN_USE_MINICORES plus the GEN_HW_FSM bit of each protocol the FSM + * can run. Only the revision registers need to be programmed to select the + * protocol on them. Engines whose RAM depth comes from the wrapper data have + * no SE_HW_PARAM_2 and are never minicore. + * + * Return: true when the engine runs @protocol on a hardware FSM. + */ +static bool geni_se_is_minicore(struct geni_se *se, enum geni_se_protocol_type protocol) +{ + u32 val, fsm; + + if (se->wrapper->prog_ram_depth) + return false; + + val = readl_relaxed(se->base + SE_HW_PARAM_2); + if (!(val & GEN_USE_MINICORES)) + return false; + + switch (protocol) { + case GENI_SE_SPI: + fsm = GEN_HW_FSM_SPI; + break; + case GENI_SE_UART: + fsm = GEN_HW_FSM_UART; + break; + case GENI_SE_I2C: + fsm = GEN_HW_FSM_I2C; + break; + case GENI_SE_I3C: + fsm = GEN_HW_FSM_I3C; + break; + case GENI_SE_SPI_SLAVE: + fsm = GEN_HW_FSM_SPI_S; + break; + default: + return false; + } + + return val & fsm; +} + /** * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. * @se: Pointer to the serial engine structure. @@ -1332,7 +1380,7 @@ static const struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const s else prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK, readl_relaxed(se->base + SE_HW_PARAM_2)); - if (fw_size >= prog_ram_depth) { + if (!geni_se_is_minicore(se, protocol) && fw_size >= prog_ram_depth) { dev_err(dev, "Firmware size (%u) exceeds RAM size (%u)\n", fw_size, prog_ram_depth); continue; @@ -1534,9 +1582,10 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, geni_write_fw_revision(se, le16_to_cpu(hdr->serial_protocol), le16_to_cpu(hdr->fw_version)); - /* Program RAM address space. */ - memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data, - fw_size_in_items * sizeof(u32)); + /* Program RAM address space, unless a hardware FSM runs the protocol. */ + if (!geni_se_is_minicore(se, protocol)) + memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data, + fw_size_in_items * sizeof(u32)); /* Put default values on GENI's output pads. */ writel_relaxed(0x1, se->base + GENI_FORCE_DEFAULT_REG); diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index f77c3a47c3d0..e18e1edd9408 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -332,6 +332,12 @@ struct geni_se { #define RX_FIFO_DEPTH_SHFT 16 /* SE_HW_PARAM_2 fields */ +#define GEN_HW_FSM_SPI_S BIT(17) +#define GEN_HW_FSM_I3C BIT(16) +#define GEN_HW_FSM_I2C BIT(15) +#define GEN_HW_FSM_UART BIT(14) +#define GEN_HW_FSM_SPI BIT(13) +#define GEN_USE_MINICORES BIT(12) #define PROG_RAM_DEPTH_MSK GENMASK(10, 0) #define HW_VER_MAJOR_MASK GENMASK(31, 28) --- base-commit: 011c427b6d209eaa944ac758a0924fc768244228 change-id: 20261114-geni-minicore-005c6f386802 Best regards, -- Igor Opaniuk