[PATCH v3 4/4] firmware: stratix10-svc: enable Agilex5 SMMU support in probe

From: Adrian Ng Ho Yin

Date: Mon Sep 07 2026 - 04:36:22 EST


Wire up the Agilex5-specific path in stratix10_svc_drv_probe().

Add INTEL_SIP_SMC_SDM_REMAPPER_CONFIG to stratix10-smc.h and issue
INTEL_SIP_SMC_SDM_REMAPPER_BYPASS from probe. On Agilex5 REV B the
hardware SDM address remapper must be bypassed when the SMMU is active
so no extra offset is applied on top of the IOVA translation.

Extend stratix10_svc_pdata with use_dma_mem and add intel,agilex5-svc to
the of_device_id match table with that flag set. Probe reads the flag via
of_device_get_match_data() so it shares the same pdata mechanism used for
needs_psci_cpu_off.

On Agilex5, DDR starts at 0x8000_0000 which is outside the SDM's
addressable range, making the SMMU mandatory. Fail probe with -ENODEV
if no IOMMU domain is attached to the device.

Register svc_data_mem_cleanup() as a devm action on the DMA path to
free any buffers leaked by service clients on driver unbind. Guard
err_destroy_pool against NULL genpool for the early-exit DMA path.

Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@xxxxxxxxxx>
---
drivers/firmware/stratix10-svc.c | 83 +++++++++++++++++---
include/linux/firmware/intel/stratix10-smc.h | 23 ++++++
2 files changed, 94 insertions(+), 12 deletions(-)

diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index d790ae239cf4..fa2335378536 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -122,12 +122,17 @@

struct stratix10_svc_pdata {
bool needs_psci_cpu_off;
+ bool use_dma_mem;
};

static const struct stratix10_svc_pdata psci_cpu_off_pdata = {
.needs_psci_cpu_off = true,
};

+static const struct stratix10_svc_pdata agilex5_pdata = {
+ .use_dma_mem = true,
+};
+
typedef void (svc_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
@@ -2169,6 +2174,7 @@ static void psci_cpu_off_teardown(struct stratix10_svc_controller *ctrl)
static const struct of_device_id stratix10_svc_drv_match[] = {
{ .compatible = "intel,stratix10-svc", .data = &psci_cpu_off_pdata },
{ .compatible = "intel,agilex-svc", .data = &psci_cpu_off_pdata },
+ { .compatible = "intel,agilex5-svc", .data = &agilex5_pdata },
{},
};

@@ -2179,14 +2185,38 @@ static const char * const chan_names[SVC_NUM_CHANNEL] = {
SVC_CLIENT_HWMON
};

+static void svc_data_mem_cleanup(void *data)
+{
+ struct stratix10_svc_controller *ctrl = data;
+ struct stratix10_svc_data_mem *pmem, *tmp;
+
+ guard(mutex)(&svc_mem_lock);
+
+ list_for_each_entry_safe(pmem, tmp, &svc_data_mem, node) {
+ dev_warn(ctrl->dev, "leaked svc buffer %p, freeing on unbind\n",
+ pmem->vaddr);
+ if (ctrl->use_dma_mem) {
+ dma_free_coherent(ctrl->dev, pmem->size,
+ pmem->vaddr, pmem->dma_addr);
+ } else {
+ gen_pool_free(ctrl->genpool,
+ (unsigned long)pmem->vaddr, pmem->size);
+ }
+ list_del(&pmem->node);
+ kfree(pmem);
+ }
+}
+
static int stratix10_svc_drv_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct stratix10_svc_controller *controller;
- struct gen_pool *genpool;
+ struct gen_pool *genpool = NULL;
struct stratix10_svc_sh_memory *sh_memory;
struct stratix10_svc *svc = NULL;
const struct stratix10_svc_pdata *pdata = of_device_get_match_data(dev);
+ struct arm_smccc_res res;
+ bool use_dma_mem = false;

svc_invoke_fn *invoke_fn;
size_t fifo_size;
@@ -2197,18 +2227,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
if (IS_ERR(invoke_fn))
return -EINVAL;

- sh_memory = devm_kzalloc(dev, sizeof(*sh_memory), GFP_KERNEL);
- if (!sh_memory)
- return -ENOMEM;
+ use_dma_mem = pdata && pdata->use_dma_mem;

- sh_memory->invoke_fn = invoke_fn;
- ret = svc_get_sh_memory(pdev, sh_memory);
- if (ret)
- return ret;
+ if (use_dma_mem) {
+ if (!iommu_get_domain_for_dev(dev)) {
+ dev_err(dev,
+ "SMMU is required for agilex5-svc but no IOMMU domain found\n");
+ dev_err(dev,
+ "Ensure the SMMU node is enabled in the device tree and 'iommus' is set for this node\n");
+ return -ENODEV;
+ }
+
+ invoke_fn(INTEL_SIP_SMC_SDM_REMAPPER_CONFIG,
+ INTEL_SIP_SMC_SDM_REMAPPER_BYPASS,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ ret = svc_setup_dma_memory(pdev);
+ if (ret)
+ return ret;
+ } else {
+ sh_memory = devm_kzalloc(dev, sizeof(*sh_memory), GFP_KERNEL);
+ if (!sh_memory)
+ return -ENOMEM;

- genpool = svc_create_memory_pool(pdev, sh_memory);
- if (IS_ERR(genpool))
- return PTR_ERR(genpool);
+ sh_memory->invoke_fn = invoke_fn;
+ ret = svc_get_sh_memory(pdev, sh_memory);
+ if (ret)
+ return ret;
+
+ genpool = svc_create_memory_pool(pdev, sh_memory);
+ if (IS_ERR(genpool))
+ return PTR_ERR(genpool);
+ }

/* allocate service controller and supporting channel */
controller = devm_kzalloc(dev, struct_size(controller, chans, SVC_NUM_CHANNEL),
@@ -2223,9 +2273,17 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
controller->num_active_client = 0;
controller->genpool = genpool;
controller->invoke_fn = invoke_fn;
+ controller->use_dma_mem = use_dma_mem;
+ controller->dma_addr_offset = use_dma_mem ? SVC_SDM_DMA_ADDR_OFFSET : 0;
INIT_LIST_HEAD(&controller->node);
init_completion(&controller->complete_status);

+ if (use_dma_mem) {
+ ret = devm_add_action_or_reset(dev, svc_data_mem_cleanup, controller);
+ if (ret)
+ goto err_destroy_pool;
+ }
+
if (pdata && pdata->needs_psci_cpu_off) {
controller->psci_reboot_nb.notifier_call =
psci_cpu_off_reboot_notifier;
@@ -2333,7 +2391,8 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
err_free_notifier:
psci_cpu_off_teardown(controller);
err_destroy_pool:
- gen_pool_destroy(genpool);
+ if (genpool)
+ gen_pool_destroy(genpool);

return ret;
}
diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h
index 366309260121..b0d42d585a75 100644
--- a/include/linux/firmware/intel/stratix10-smc.h
+++ b/include/linux/firmware/intel/stratix10-smc.h
@@ -813,4 +813,27 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE)
#define INTEL_SIP_SMC_ASYNC_FUNC_ID_RSU_NOTIFY (0xEC)
#define INTEL_SIP_SMC_ASYNC_RSU_NOTIFY \
INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_RSU_NOTIFY)
+
+/**
+ * Request INTEL_SIP_SMC_SDM_REMAPPER_CONFIG
+ *
+ * Sync call to configure the SDM address remapper. On Agilex5, the remapper
+ * must be bypassed when the SMMU is active to avoid conflicts with IOMMU
+ * address translation.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_SDM_REMAPPER_CONFIG
+ * a1: INTEL_SIP_SMC_SDM_REMAPPER_ENABLE or INTEL_SIP_SMC_SDM_REMAPPER_BYPASS
+ * a2-7: not used
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK
+ * a1-3: not used
+ */
+#define INTEL_SIP_SMC_FUNCID_SDM_REMAPPER_CONFIG 513
+#define INTEL_SIP_SMC_SDM_REMAPPER_CONFIG \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_SDM_REMAPPER_CONFIG)
+#define INTEL_SIP_SMC_SDM_REMAPPER_ENABLE 0
+#define INTEL_SIP_SMC_SDM_REMAPPER_BYPASS 1
+
#endif
--
2.49.GIT