From d922113ef91e6e7e8065e9070f349365341ba32e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:17 +0530 Subject: [PATCH 1/5] soc: qcom: ice: Fix race between qcom_ice_probe() and of_qcom_ice_get() The current platform driver design causes probe ordering races with consumers (UFS, eMMC) due to ICE's dependency on SCM firmware calls. If ICE probe fails (missing ICE SCM or DT registers), devm_of_qcom_ice_get() loops with -EPROBE_DEFER, leaving consumers non-functional even when ICE should be gracefully disabled. devm_of_qcom_ice_get() doesn't know if the ICE driver probe has failed due to above reasons or it is waiting for the SCM driver. Moreover, there is no devlink dependency between ICE and consumer drivers as 'qcom,ice' is not considered as a DT 'supplier'. So the consumer drivers have no idea of when the ICE driver is going to probe. To address these issues, store the error pointer in a global xarray with ice node phandle as a key during probe in addition to the valid ice pointer and synchronize both qcom_ice_probe() and of_qcom_ice_get() using a mutex. If the xarray entry is NULL, then it implies that the driver is not probed yet, so return -EPROBE_DEFER. If it has any error pointer, return that error pointer directly. Otherwise, add the devlink as usual and return the valid pointer to the consumer. Xarray is used instead of platform drvdata, since driver core frees the drvdata during probe failure. So it cannot be used to pass the error pointer to the consumers. Note that this change only fixes the standalone ICE DT node bindings and not the ones with 'ice' range embedded in the consumer nodes, where there is no issue. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reported-by: Sumit Garg Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Cc: stable@vger.kernel.org # 6.4 Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-1-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..91991864b4a3 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -113,6 +114,9 @@ struct qcom_ice { u8 hwkm_version; }; +static DEFINE_XARRAY(ice_handles); +static DEFINE_MUTEX(ice_mutex); + static bool qcom_ice_check_supported(struct qcom_ice *ice) { u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); @@ -631,6 +635,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return qcom_ice_create(&pdev->dev, base); } + guard(mutex)(&ice_mutex); + /* * If the consumer node does not provider an 'ice' reg range * (legacy DT binding), then it must at least provide a phandle @@ -647,12 +653,13 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return ERR_PTR(-EPROBE_DEFER); } - ice = platform_get_drvdata(pdev); - if (!ice) { - dev_err(dev, "Cannot get ice instance from %s\n", - dev_name(&pdev->dev)); + ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); + if (IS_ERR_OR_NULL(ice)) { platform_device_put(pdev); - return ERR_PTR(-EPROBE_DEFER); + if (!ice) + return ERR_PTR(-EPROBE_DEFER); + else + return ice; } link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); @@ -716,24 +723,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get); static int qcom_ice_probe(struct platform_device *pdev) { + unsigned long phandle = pdev->dev.of_node->phandle; struct qcom_ice *engine; void __iomem *base; + guard(mutex)(&ice_mutex); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_warn(&pdev->dev, "ICE registers not found\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL); return PTR_ERR(base); } engine = qcom_ice_create(&pdev->dev, base); - if (IS_ERR(engine)) + if (IS_ERR(engine)) { + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return PTR_ERR(engine); + } - platform_set_drvdata(pdev, engine); + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return 0; } +static void qcom_ice_remove(struct platform_device *pdev) +{ + unsigned long phandle = pdev->dev.of_node->phandle; + + guard(mutex)(&ice_mutex); + xa_store(&ice_handles, phandle, NULL, GFP_KERNEL); +} + static const struct of_device_id qcom_ice_of_match_table[] = { { .compatible = "qcom,inline-crypto-engine" }, { }, @@ -742,6 +765,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table); static struct platform_driver qcom_ice_driver = { .probe = qcom_ice_probe, + .remove = qcom_ice_remove, .driver = { .name = "qcom-ice", .of_match_table = qcom_ice_of_match_table, From 5a4dc805a80e6fe303d6a4748cd451ea15987ffd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:18 +0530 Subject: [PATCH 2/5] soc: qcom: ice: Return -ENODEV if the ICE platform device is not found By the time the consumer driver calls devm_of_qcom_ice_get(), all the platform devices for ICE nodes would've been created by of_platform_default_populate(). So for the absence of any platform device, -ENODEV should not returned, not -EPROBE_DEFER. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-2-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 91991864b4a3..85deb9ea4a68 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -650,7 +650,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) pdev = of_find_device_by_node(node); if (!pdev) { dev_err(dev, "Cannot find device node %s\n", node->name); - return ERR_PTR(-EPROBE_DEFER); + return ERR_PTR(-ENODEV); } ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); From b9ab7217dd7d567c50311afa94d6d6746cb77e04 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:19 +0530 Subject: [PATCH 3/5] soc: qcom: ice: Return proper error codes from devm_of_qcom_ice_get() instead of NULL devm_of_qcom_ice_get() currently returns NULL if ICE SCM is not available or "qcom,ice" property is not found in DT. But this confuses the clients since NULL doesn't convey the reason for failure. So return proper error codes instead of NULL. Reported-by: Sumit Garg Reviewed-by: Konrad Dybcio Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-3-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 85deb9ea4a68..2b592aa42941 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -563,7 +563,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!qcom_scm_ice_available()) { dev_warn(dev, "ICE SCM interface not found\n"); - return NULL; + return ERR_PTR(-EOPNOTSUPP); } engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); @@ -645,7 +645,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return NULL; + return ERR_PTR(-ENODEV); pdev = of_find_device_by_node(node); if (!pdev) { @@ -698,8 +698,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res) * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * - * Return: ICE pointer on success, NULL if there is no ICE data provided by the - * consumer or ERR_PTR() on error. + * Return: ICE pointer on success, ERR_PTR() on error. */ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) { @@ -710,7 +709,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) return ERR_PTR(-ENOMEM); ice = of_qcom_ice_get(dev); - if (!IS_ERR_OR_NULL(ice)) { + if (!IS_ERR(ice)) { *dr = ice; devres_add(dev, dr); } else { From 2ccbb3fa5cf47d05849cf6722aad1b4cc14df6d9 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:20 +0530 Subject: [PATCH 4/5] mmc: sdhci-msm: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Ulf Hansson Acked-by: Adrian Hunter Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-4-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/mmc/host/sdhci-msm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 633462c0be5f..0882ce74e0c9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1918,13 +1918,13 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, return 0; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } msm_host->ice = ice; From 4ac19b36bf4108706238cbc4f300b17dba8b881e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:21 +0530 Subject: [PATCH 5/5] scsi: ufs: ufs-qcom: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Martin K. Petersen # UFS Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-5-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/ufs/host/ufs-qcom.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index bc037db46624..9c0973a7ffc3 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -177,13 +177,13 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host) int i; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } host->ice = ice;