[PATCH v5 3/7] bus: mhi: Centralize Sahara firmware image table selection at probe time

From: Kishore Batta

Date: Thu Apr 16 2026 - 10:14:43 EST


The Sahara driver currently selects firmware image tables using scattered,
device specific conditionals in the probe path. This makes the logic harder
to follow, harder to extend for new devices, and spreads device knowledge
across multiple code paths.

Refactor firmware image table selection into a single, explicit probe time
mechanism by introducing a controller provided firmware mapping table that
captures device matching, Sahara image tables, firmware folder names, and
streaming behaviour in one place.

This centralizes device specific decisions in the controller driver,
simplifies the Sahara probe logic, and removes ad-hoc conditionals while
preserving existing behavior for all supported AIC devices. This is in
preparation for adding QDU100 support.

Signed-off-by: Kishore Batta <kishore.batta@xxxxxxxxxxxxxxxx>
---
drivers/accel/qaic/mhi_controller.c | 61 ++++++++++++++++++++++++++++
drivers/bus/mhi/host/clients/sahara/sahara.c | 60 +++++----------------------
include/linux/mhi.h | 17 ++++++++
3 files changed, 88 insertions(+), 50 deletions(-)

diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
index 4d787f77ce419fcd2b250f9cabaec9c26f2da8dc..1f9ef871421b976c35cfad59aed715da96c1813b 100644
--- a/drivers/accel/qaic/mhi_controller.c
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -20,6 +20,62 @@ static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */
module_param(mhi_timeout_ms, uint, 0600);
MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value");

+static const char * const aic100_image_table[] = {
+ [1] = "qcom/aic100/fw1.bin",
+ [2] = "qcom/aic100/fw2.bin",
+ [4] = "qcom/aic100/fw4.bin",
+ [5] = "qcom/aic100/fw5.bin",
+ [6] = "qcom/aic100/fw6.bin",
+ [8] = "qcom/aic100/fw8.bin",
+ [9] = "qcom/aic100/fw9.bin",
+ [10] = "qcom/aic100/fw10.bin",
+};
+
+static const char * const aic200_image_table[] = {
+ [5] = "qcom/aic200/uefi.elf",
+ [12] = "qcom/aic200/aic200-nsp.bin",
+ [23] = "qcom/aic200/aop.mbn",
+ [32] = "qcom/aic200/tz.mbn",
+ [33] = "qcom/aic200/hypvm.mbn",
+ [38] = "qcom/aic200/xbl_config.elf",
+ [39] = "qcom/aic200/aic200_abl.elf",
+ [40] = "qcom/aic200/apdp.mbn",
+ [41] = "qcom/aic200/devcfg.mbn",
+ [42] = "qcom/aic200/sec.elf",
+ [43] = "qcom/aic200/aic200-hlos.elf",
+ [49] = "qcom/aic200/shrm.elf",
+ [50] = "qcom/aic200/cpucp.elf",
+ [51] = "qcom/aic200/aop_devcfg.mbn",
+ [54] = "qcom/aic200/qupv3fw.elf",
+ [57] = "qcom/aic200/cpucp_dtbs.elf",
+ [62] = "qcom/aic200/uefi_dtbs.elf",
+ [63] = "qcom/aic200/xbl_ac_config.mbn",
+ [64] = "qcom/aic200/tz_ac_config.mbn",
+ [65] = "qcom/aic200/hyp_ac_config.mbn",
+ [66] = "qcom/aic200/pdp.elf",
+ [67] = "qcom/aic200/pdp_cdb.elf",
+ [68] = "qcom/aic200/sdi.mbn",
+ [69] = "qcom/aic200/dcd.mbn",
+ [73] = "qcom/aic200/gearvm.mbn",
+ [74] = "qcom/aic200/sti.bin",
+ [76] = "qcom/aic200/tz_qti_config.mbn",
+ [78] = "qcom/aic200/pvs.bin",
+};
+
+static const struct mhi_sahara_fw_table aic100_sahara_fw = {
+ .image_table = aic100_image_table,
+ .table_size = ARRAY_SIZE(aic100_image_table),
+ .fw_folder = "aic100",
+ .non_streaming = true,
+};
+
+static const struct mhi_sahara_fw_table aic200_sahara_fw = {
+ .image_table = aic200_image_table,
+ .table_size = ARRAY_SIZE(aic200_image_table),
+ .fw_folder = "aic200",
+ .non_streaming = false,
+};
+
static const char *fw_image_paths[FAMILY_MAX] = {
[FAMILY_AIC100] = "qcom/aic100/sbl.bin",
[FAMILY_AIC200] = "qcom/aic200/sbl.bin",
@@ -871,6 +927,11 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi
mhi_cntrl->name = "AIC100";
}

+ if (mhi_cntrl->name && !strcmp(mhi_cntrl->name, "AIC100"))
+ mhi_cntrl->sahara_fw = &aic100_sahara_fw;
+ else if (mhi_cntrl->name && !strcmp(mhi_cntrl->name, "AIC200"))
+ mhi_cntrl->sahara_fw = &aic200_sahara_fw;
+
/* use latest configured timeout */
mhi_config.timeout_ms = mhi_timeout_ms;
ret = mhi_register_controller(mhi_cntrl, &mhi_config);
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index 858dc5bc39c1ad42922cabef3b1abcd43bc4f0f4..e339c67e236af271645ca81cc517efd9eead87e4 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -179,48 +179,7 @@ struct sahara_context {
u32 read_data_length;
bool is_mem_dump_mode;
bool non_streaming;
-};
-
-static const char * const aic100_image_table[] = {
- [1] = "qcom/aic100/fw1.bin",
- [2] = "qcom/aic100/fw2.bin",
- [4] = "qcom/aic100/fw4.bin",
- [5] = "qcom/aic100/fw5.bin",
- [6] = "qcom/aic100/fw6.bin",
- [8] = "qcom/aic100/fw8.bin",
- [9] = "qcom/aic100/fw9.bin",
- [10] = "qcom/aic100/fw10.bin",
-};
-
-static const char * const aic200_image_table[] = {
- [5] = "qcom/aic200/uefi.elf",
- [12] = "qcom/aic200/aic200-nsp.bin",
- [23] = "qcom/aic200/aop.mbn",
- [32] = "qcom/aic200/tz.mbn",
- [33] = "qcom/aic200/hypvm.mbn",
- [38] = "qcom/aic200/xbl_config.elf",
- [39] = "qcom/aic200/aic200_abl.elf",
- [40] = "qcom/aic200/apdp.mbn",
- [41] = "qcom/aic200/devcfg.mbn",
- [42] = "qcom/aic200/sec.elf",
- [43] = "qcom/aic200/aic200-hlos.elf",
- [49] = "qcom/aic200/shrm.elf",
- [50] = "qcom/aic200/cpucp.elf",
- [51] = "qcom/aic200/aop_devcfg.mbn",
- [54] = "qcom/aic200/qupv3fw.elf",
- [57] = "qcom/aic200/cpucp_dtbs.elf",
- [62] = "qcom/aic200/uefi_dtbs.elf",
- [63] = "qcom/aic200/xbl_ac_config.mbn",
- [64] = "qcom/aic200/tz_ac_config.mbn",
- [65] = "qcom/aic200/hyp_ac_config.mbn",
- [66] = "qcom/aic200/pdp.elf",
- [67] = "qcom/aic200/pdp_cdb.elf",
- [68] = "qcom/aic200/sdi.mbn",
- [69] = "qcom/aic200/dcd.mbn",
- [73] = "qcom/aic200/gearvm.mbn",
- [74] = "qcom/aic200/sti.bin",
- [76] = "qcom/aic200/tz_qti_config.mbn",
- [78] = "qcom/aic200/pvs.bin",
+ const char *fw_folder;
};

static bool is_streaming(struct sahara_context *context)
@@ -796,6 +755,7 @@ static void sahara_read_data_processing(struct work_struct *work)

static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
+ const struct mhi_sahara_fw_table *sahara_fw;
struct sahara_context *context;
int ret;
int i;
@@ -808,14 +768,14 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
if (!context->rx)
return -ENOMEM;

- if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
- context->image_table = aic200_image_table;
- context->table_size = ARRAY_SIZE(aic200_image_table);
- } else {
- context->image_table = aic100_image_table;
- context->table_size = ARRAY_SIZE(aic100_image_table);
- context->non_streaming = true;
- }
+ sahara_fw = mhi_dev->mhi_cntrl->sahara_fw;
+ if (!sahara_fw || !sahara_fw->image_table || !sahara_fw->table_size)
+ return -ENODEV;
+
+ context->image_table = sahara_fw->image_table;
+ context->table_size = sahara_fw->table_size;
+ context->non_streaming = sahara_fw->non_streaming;
+ context->fw_folder = sahara_fw->fw_folder;

/*
* There are two firmware implementations for READ_DATA handling.
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index 88ccb3e14f481d6b85c2a314eb74ba960c2d4c81..060dafffac67c5c920adc1562a61a7233e8d583f 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -234,6 +234,21 @@ struct mhi_channel_config {
bool wake_capable;
};

+/**
+ * struct mhi_sahara_fw_table - Controller provided sahara firmware mapping
+ * @image_table: Sparse array indexed by Sahara image ID
+ * @table_size: Size of @image_table
+ * @fw_folder: Firmware folder name.
+ * @non_streaming: Streaming feature support (optional)
+ *
+ */
+struct mhi_sahara_fw_table {
+ const char *const *image_table;
+ u32 table_size;
+ const char *fw_folder;
+ bool non_streaming;
+};
+
/**
* struct mhi_event_config - Event ring configuration structure for controller
* @num_elements: The number of elements that can be queued to this ring
@@ -360,6 +375,7 @@ struct mhi_controller_config {
* @wake_set: Device wakeup set flag
* @irq_flags: irq flags passed to request_irq (optional)
* @mru: the default MRU for the MHI device
+ * @sahara_fw: Sahara firmware mapping
*
* Fields marked as (required) need to be populated by the controller driver
* before calling mhi_register_controller(). For the fields marked as (optional)
@@ -445,6 +461,7 @@ struct mhi_controller {
bool wake_set;
unsigned long irq_flags;
u32 mru;
+ const struct mhi_sahara_fw_table *sahara_fw;
};

/**

--
2.34.1