* [PATCH 1/3] mmc: sdhci-msm: Enable ICE before enabling crypto support
2026-10-22 9:51 [PATCH 0/3] ICE: Enable runtime PM support Kuldeep Singh
@ 2026-10-22 9:51 ` Kuldeep Singh
2026-10-24 18:29 ` Eric Biggers
2026-10-22 9:51 ` [PATCH 2/3] ufs: ufs-qcom: " Kuldeep Singh
2026-10-22 9:51 ` [PATCH 3/3] soc: qcom: ice: Add runtime PM support Kuldeep Singh
2 siblings, 1 reply; 5+ messages in thread
From: Kuldeep Singh @ 2026-10-22 9:51 UTC (permalink / raw)
To: Adrian Hunter, Ulf Hansson, Abel Vesa, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Eric Biggers,
Bjorn Andersson, Konrad Dybcio
Cc: linux-mmc, linux-arm-msm, linux-kernel, linux-scsi, linux-crypto,
Kuldeep Singh
sdhci-msm currently exposes MMC crypto profile and sets MMC_CAP2_CRYPTO
before enabling ICE. This leaves a window where blk-crypto can issue
keyslot programming calls before sdhci-msm has explicitly enabled ICE.
This works today because ICE clocks remain enabled after ICE probe, but
the storage consumer should not rely on that implicit clock state. Once
ICE supports runtime PM and clocks are suspended after probe, accessing
ICE registers before qcom_ice_enable() call can fail due to unclocked
register access.
Enable ICE during the sdhci-msm crypto early init path which ensures ICE
is ready before blk-crypto can issue key programming calls.
Remove ICE enable call from CQE path, since ICE is now enabled
during SDHCI crypto initialization only.
Fixes: c7eed31e235c ("mmc: sdhci-msm: Switch to the new ICE API")
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
drivers/mmc/host/sdhci-msm.c | 21 ++++++---------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index aecd425bf095..85065cfae132 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1928,6 +1928,12 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
msm_host->ice = ice;
+ err = qcom_ice_enable(msm_host->ice);
+ if (err) {
+ dev_err(dev, "%s: failed to enable ICE: %d\n", __func__, err);
+ return err;
+ }
+
/* Initialize the blk_crypto_profile */
caps.reg_val = cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
@@ -1963,12 +1969,6 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
return 0;
}
-static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
-{
- if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
- qcom_ice_enable(msm_host->ice);
-}
-
static int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
@@ -2141,10 +2141,6 @@ static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
return 0;
}
-static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
-{
-}
-
static inline int
sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
@@ -2182,12 +2178,7 @@ static u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask)
static void sdhci_msm_cqe_enable(struct mmc_host *mmc)
{
- struct sdhci_host *host = mmc_priv(mmc);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
-
sdhci_cqe_enable(mmc);
- sdhci_msm_ice_enable(msm_host);
}
static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/3] ufs: ufs-qcom: Enable ICE before enabling crypto support
2026-10-22 9:51 [PATCH 0/3] ICE: Enable runtime PM support Kuldeep Singh
2026-10-22 9:51 ` [PATCH 1/3] mmc: sdhci-msm: Enable ICE before enabling crypto support Kuldeep Singh
@ 2026-10-22 9:51 ` Kuldeep Singh
2026-10-22 9:51 ` [PATCH 3/3] soc: qcom: ice: Add runtime PM support Kuldeep Singh
2 siblings, 0 replies; 5+ messages in thread
From: Kuldeep Singh @ 2026-10-22 9:51 UTC (permalink / raw)
To: Adrian Hunter, Ulf Hansson, Abel Vesa, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Eric Biggers,
Bjorn Andersson, Konrad Dybcio
Cc: linux-mmc, linux-arm-msm, linux-kernel, linux-scsi, linux-crypto,
Kuldeep Singh
ufs-qcom currently exposes UFS crypto profile and sets UFSHCD_CAP_CRYPTO
before enabling ICE. This leaves a window where blk-crypto can issue
keyslot programming calls before ufs-qcom has explicitly enabled ICE.
Similar to MMC, UFS also works today as ICE clocks remain enabled after
the probe, but it should not rely on that implicit clock state. Once ICE
supports runtime PM, ICE clocks can be suspended after probe, and
accessing ICE registers before qcom_ice_enable() call can fail due to
unclocked register access in qcom_ice_program_key().
Enable ICE during ufs crypto init path to ensure ICE is ready before
blk-crypto can issue key programming calls.
Also call qcom_ice_enable() from HCE PRE_CHANGE path once crypto support
has been enabled. UFS controller reset can reset ICE state, and the UFS
core can reprogram keys during HCE start before POST_CHANGE.
Re-enabling ICE in PRE_CHANGE makes ICE ready before reset-time key
reprogramming and for any later ICE operation.
Fixes: 56541c7c4468 ("scsi: ufs: ufs-qcom: Switch to the new ICE API")
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
drivers/ufs/host/ufs-qcom.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index fee9b54ba5e1..7e5aa4573e0c 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -158,13 +158,15 @@ static void ufs_qcom_config_ice_allocator(struct ufs_qcom_host *host)
ufshcd_writel(hba, config, REG_UFS_MEM_ICE_NUM_CORE);
}
-static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host)
+static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops; /* forward decl */
+
+static inline int ufs_qcom_ice_enable(struct ufs_qcom_host *host)
{
if (host->hba->caps & UFSHCD_CAP_CRYPTO)
- qcom_ice_enable(host->ice);
-}
+ return qcom_ice_enable(host->ice);
-static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops; /* forward decl */
+ return 0;
+}
static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
{
@@ -188,6 +190,12 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
host->ice = ice;
+ err = qcom_ice_enable(host->ice);
+ if (err) {
+ dev_err(dev, "Failed to enable ICE: %d\n", err);
+ return err;
+ }
+
/* Initialize the blk_crypto_profile */
caps.reg_val = cpu_to_le32(ufshcd_readl(hba, REG_UFS_CCAP));
@@ -318,11 +326,12 @@ static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {
#else
-static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host)
+static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
{
+ return 0;
}
-static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
+static inline int ufs_qcom_ice_enable(struct ufs_qcom_host *host)
{
return 0;
}
@@ -636,12 +645,15 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
* is initialized.
*/
err = ufs_qcom_enable_lane_clks(host);
+ if (err)
+ return err;
+
+ err = ufs_qcom_ice_enable(host);
break;
case POST_CHANGE:
/* check if UFS PHY moved from DISABLED to HIBERN8 */
err = ufs_qcom_check_hibern8(hba);
ufs_qcom_enable_hw_clk_gating(hba);
- ufs_qcom_ice_enable(host);
ufs_qcom_config_ice_allocator(host);
break;
default:
@@ -660,10 +672,9 @@ static int ufs_qcom_fw_managed_hce_enable_notify(struct ufs_hba *hba,
switch (status) {
case PRE_CHANGE:
ufs_qcom_select_unipro_mode(host);
- break;
+ return ufs_qcom_ice_enable(host);
case POST_CHANGE:
ufs_qcom_enable_hw_clk_gating(hba);
- ufs_qcom_ice_enable(host);
break;
default:
dev_err(hba->dev, "Invalid status %d\n", status);
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/3] soc: qcom: ice: Add runtime PM support
2026-10-22 9:51 [PATCH 0/3] ICE: Enable runtime PM support Kuldeep Singh
2026-10-22 9:51 ` [PATCH 1/3] mmc: sdhci-msm: Enable ICE before enabling crypto support Kuldeep Singh
2026-10-22 9:51 ` [PATCH 2/3] ufs: ufs-qcom: " Kuldeep Singh
@ 2026-10-22 9:51 ` Kuldeep Singh
2 siblings, 0 replies; 5+ messages in thread
From: Kuldeep Singh @ 2026-10-22 9:51 UTC (permalink / raw)
To: Adrian Hunter, Ulf Hansson, Abel Vesa, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Eric Biggers,
Bjorn Andersson, Konrad Dybcio
Cc: linux-mmc, linux-arm-msm, linux-kernel, linux-scsi, linux-crypto,
Kuldeep Singh
Ice driver currently enables its clocks during probe and keeps them
prepared/enabled even after probe is complete. On targets where ICE
exists as separate DT node and storage consumers reference it through
"qcom,ice", leaves ICE clock voted while the system is idle.
To mitigate this, add runtime PM callbacks to gate the core and iface
clocks. During probe, enable the clocks to read the ICE version/fuse
state, and then disable clocks and mark runtime PM state as suspended.
For legacy binding, keep clock ownership with the storage consumer
dev as it created the ICE instance.
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
drivers/soc/qcom/ice.c | 139 +++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 116 insertions(+), 23 deletions(-)
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index e256595b9826..f9939e2d037c 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/xarray.h>
#include <linux/firmware/qcom/qcom_scm.h>
@@ -113,6 +114,7 @@ struct qcom_ice {
bool use_hwkm;
bool hwkm_init_complete;
u8 hwkm_version;
+ bool use_pm_runtime;
};
static DEFINE_XARRAY(ice_handles);
@@ -303,42 +305,99 @@ static void qcom_ice_hwkm_init(struct qcom_ice *ice)
int qcom_ice_enable(struct qcom_ice *ice)
{
+ bool pm_acquired = false;
+ int err;
+
+ if (ice->use_pm_runtime && pm_runtime_suspended(ice->dev)) {
+ err = pm_runtime_resume_and_get(ice->dev);
+ if (err) {
+ dev_err(ice->dev, "runtime resume failed: %d\n", err);
+ return err;
+ }
+ pm_acquired = true;
+ }
+
qcom_ice_low_power_mode_enable(ice);
qcom_ice_optimization_enable(ice);
qcom_ice_hwkm_init(ice);
- return qcom_ice_wait_bist_status(ice);
+
+ err = qcom_ice_wait_bist_status(ice);
+ if (err && pm_acquired) {
+ dev_err(ice->dev, "BIST failed, ret: %d\n", err);
+ pm_runtime_put_sync_suspend(ice->dev);
+ }
+
+ return err;
}
EXPORT_SYMBOL_GPL(qcom_ice_enable);
-int qcom_ice_resume(struct qcom_ice *ice)
+static int qcom_ice_enable_clocks(struct qcom_ice *ice)
{
- struct device *dev = ice->dev;
int err;
err = clk_prepare_enable(ice->core_clk);
- if (err) {
- dev_err(dev, "Failed to enable core clock: %d\n", err);
+ if (err)
return err;
- }
err = clk_prepare_enable(ice->iface_clk);
if (err) {
- dev_err(dev, "Failed to enable iface clock: %d\n", err);
+ clk_disable_unprepare(ice->core_clk);
return err;
}
- qcom_ice_hwkm_init(ice);
- return qcom_ice_wait_bist_status(ice);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(qcom_ice_resume);
-int qcom_ice_suspend(struct qcom_ice *ice)
+static void qcom_ice_disable_clocks(struct qcom_ice *ice)
{
clk_disable_unprepare(ice->iface_clk);
clk_disable_unprepare(ice->core_clk);
+}
+
+static int qcom_ice_do_resume(struct qcom_ice *ice)
+{
+ int err = 0;
+
+ err = qcom_ice_enable_clocks(ice);
+ if (err)
+ return err;
+
+ qcom_ice_hwkm_init(ice);
+
+ err = qcom_ice_wait_bist_status(ice);
+ if (err) {
+ dev_err(ice->dev, "Bist failed, ret: %d\n", err);
+ qcom_ice_disable_clocks(ice);
+ ice->hwkm_init_complete = false;
+ }
+
+ return err;
+}
+
+int qcom_ice_resume(struct qcom_ice *ice)
+{
+ if (ice->use_pm_runtime)
+ return pm_runtime_resume_and_get(ice->dev);
+
+ return qcom_ice_do_resume(ice);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_resume);
+
+static int qcom_ice_do_suspend(struct qcom_ice *ice)
+{
ice->hwkm_init_complete = false;
+ qcom_ice_disable_clocks(ice);
return 0;
}
+
+int qcom_ice_suspend(struct qcom_ice *ice)
+{
+ if (ice->use_pm_runtime)
+ return pm_runtime_put_sync_suspend(ice->dev);
+
+ return qcom_ice_do_suspend(ice);
+}
EXPORT_SYMBOL_GPL(qcom_ice_suspend);
static unsigned int translate_hwkm_slot(struct qcom_ice *ice, unsigned int slot)
@@ -560,10 +619,11 @@ int qcom_ice_import_key(struct qcom_ice *ice,
}
EXPORT_SYMBOL_GPL(qcom_ice_import_key);
-static struct qcom_ice *qcom_ice_create(struct device *dev,
- void __iomem *base)
+static struct qcom_ice *qcom_ice_create(struct device *dev, void __iomem *base,
+ bool use_pm_runtime)
{
struct qcom_ice *engine;
+ int err;
if (!qcom_scm_is_available())
return ERR_PTR(-EPROBE_DEFER);
@@ -579,36 +639,54 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
engine->dev = dev;
engine->base = base;
+ engine->use_pm_runtime = use_pm_runtime;
/*
* Legacy DT binding uses different clk names for each consumer,
* so lets try those first. If none of those are a match, it means
* the we only have one clock and it is part of the dedicated DT node.
- * Also, enable the clock before we check what HW version the driver
- * supports.
*/
- engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk");
+ engine->core_clk = devm_clk_get_optional(dev, "ice_core_clk");
if (!engine->core_clk)
- engine->core_clk = devm_clk_get_optional_enabled(dev, "ice");
+ engine->core_clk = devm_clk_get_optional(dev, "ice");
if (!engine->core_clk)
- engine->core_clk = devm_clk_get_optional_enabled(dev, "core");
+ engine->core_clk = devm_clk_get_optional(dev, "core");
if (!engine->core_clk)
- engine->core_clk = devm_clk_get_enabled(dev, NULL);
+ engine->core_clk = devm_clk_get(dev, NULL);
if (IS_ERR(engine->core_clk))
return ERR_CAST(engine->core_clk);
- engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface");
+ engine->iface_clk = devm_clk_get_optional(dev, "iface");
if (IS_ERR(engine->iface_clk))
return ERR_CAST(engine->iface_clk);
- if (!qcom_ice_check_supported(engine))
+ err = qcom_ice_enable_clocks(engine);
+ if (err)
+ return ERR_PTR(err);
+
+ if (!qcom_ice_check_supported(engine)) {
+ qcom_ice_disable_clocks(engine);
return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ if (use_pm_runtime)
+ qcom_ice_disable_clocks(engine);
dev_dbg(dev, "Registered Qualcomm Inline Crypto Engine\n");
return engine;
}
+static int qcom_ice_runtime_resume(struct device *dev)
+{
+ return qcom_ice_do_resume(dev_get_drvdata(dev));
+}
+
+static int qcom_ice_runtime_suspend(struct device *dev)
+{
+ return qcom_ice_do_suspend(dev_get_drvdata(dev));
+}
+
/**
* of_qcom_ice_get() - get an ICE instance from a DT node
* @dev: device pointer for the consumer device
@@ -645,7 +723,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
return ERR_CAST(base);
/* create ICE instance using consumer dev */
- return qcom_ice_create(&pdev->dev, base);
+ return qcom_ice_create(&pdev->dev, base, false);
}
guard(mutex)(&ice_mutex);
@@ -738,6 +816,7 @@ static int qcom_ice_probe(struct platform_device *pdev)
unsigned long phandle = pdev->dev.of_node->phandle;
struct qcom_ice *engine;
void __iomem *base;
+ int err;
guard(mutex)(&ice_mutex);
@@ -749,13 +828,21 @@ static int qcom_ice_probe(struct platform_device *pdev)
return PTR_ERR(base);
}
- engine = qcom_ice_create(&pdev->dev, base);
+ engine = qcom_ice_create(&pdev->dev, base, true);
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);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ err = devm_pm_runtime_enable(&pdev->dev);
+ if (err) {
+ xa_store(&ice_handles, phandle, ERR_PTR(err), GFP_KERNEL);
+ return err;
+ }
xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
return 0;
@@ -775,12 +862,18 @@ static const struct of_device_id qcom_ice_of_match_table[] = {
};
MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table);
+static const struct dev_pm_ops qcom_ice_pm_ops = {
+ RUNTIME_PM_OPS(qcom_ice_runtime_suspend, qcom_ice_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
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,
+ .pm = pm_ptr(&qcom_ice_pm_ops),
},
};
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread