[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251217055632.1776206-2-adrianhoyin.ng@altera.com>
Date: Wed, 17 Dec 2025 13:56:31 +0800
From: adrianhoyin.ng@...era.com
To: dinguyen@...nel.org,
mdf@...nel.org,
yilun.xu@...el.com,
trix@...hat.com,
linux-fpga@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: adrianhoyin.ng@...era.com
Subject: [PATCH 1/2] firmware: stratix10-svc: Add SMMU support for Agilex5
From: Adrian Ng Ho Yin <adrianhoyin.ng@...era.com>
Add SMMU support to the Stratix10 Service Layer driver for Agilex5
platforms.
On Stratix10 and Agilex7, the SDM accesses a reserved buffer located in
the first 32 MB of DDR, which starts at physical address 0x0. Agilex5
differs in that DDR begins at 0x8000_0000. Because the SDM address bus is
limited to 24 bits, it can only reach the 0–512 MB range and therefore
cannot directly access DDR on Agilex5.
Agilex5 platforms rely on SMMU translation to make DDR reachable to the
SDM. This patch enables SMMU usage when the driver is probed with the
new `"intel,agilex5-svc"` compatible. A carveout IOVA domain is created,
and allocated buffers are mapped through the SMMU so that the SDM can
access them via an IOVA that falls within its accessible range.
Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@...era.com>
---
drivers/firmware/stratix10-svc.c | 160 ++++++++++++++++++++++++++++---
1 file changed, 146 insertions(+), 14 deletions(-)
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index dbed404a71fc..c20afb666646 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -11,6 +11,8 @@
#include <linux/hashtable.h>
#include <linux/idr.h>
#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/iova.h>
#include <linux/kfifo.h>
#include <linux/kthread.h>
#include <linux/module.h>
@@ -36,12 +38,24 @@
* FPGA_CONFIG_STATUS_TIMEOUT_SEC - poll the FPGA configuration status,
* service layer will return error to FPGA manager when timeout occurs,
* timeout is set to 30 seconds (30 * 1000) at Intel Stratix10 SoC.
+ *
+ * IOMMU_LIMIT_ADDR - Define the maximum addressable range that can be
+ * accessed by the Secure Device Manager(SDM).
+ *
+ * IOMMU_STARTING_ADDR - Define the starting address that can be accessed
+ * by the Secure Device Manager(SDM).
+ *
+ * AGILEX5_SDM_DMA_ADDR_OFFSET - Define the starting address of the DDR
+ * memory for Agilex5.
*/
#define SVC_NUM_DATA_IN_FIFO 32
#define SVC_NUM_CHANNEL 4
#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS 200
#define FPGA_CONFIG_STATUS_TIMEOUT_SEC 30
-#define BYTE_TO_WORD_SIZE 4
+#define BYTE_TO_WORD_SIZE 4
+#define IOMMU_LIMIT_ADDR 0x20000000
+#define IOMMU_STARTING_ADDR 0x0
+#define AGILEX5_SDM_DMA_ADDR_OFFSET 0x80000000
/* stratix10 service layer clients */
#define STRATIX10_RSU "stratix10-rsu"
@@ -258,6 +272,10 @@ struct stratix10_async_ctrl {
* @invoke_fn: function to issue secure monitor call or hypervisor call
* @svc: manages the list of client svc drivers
* @actrl: async control structure
+ * @domain: IOMMU domain for service controller
+ * @is_smmu_enabled: Boolean flag IOMMU enablement
+ * @sdm_dma_addr_offset: Address offset sent to SDM for DMA ops
+ * @carveout: IOVA carveout for IOVA address allocation
*
* This struct is used to create communication channels for service clients, to
* handle secure monitor or hypervisor call.
@@ -276,6 +294,14 @@ struct stratix10_svc_controller {
svc_invoke_fn *invoke_fn;
struct stratix10_svc *svc;
struct stratix10_async_ctrl actrl;
+ struct iommu_domain *domain;
+ bool is_smmu_enabled;
+ dma_addr_t sdm_dma_addr_offset;
+ struct {
+ struct iova_domain domain;
+ unsigned long shift;
+ unsigned long limit;
+ } carveout;
};
/**
@@ -1727,6 +1753,14 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
if (p_mem->vaddr == p_msg->payload) {
p_data->paddr = p_mem->paddr;
p_data->size = p_msg->payload_length;
+ /* ATF have addr range check for to ensure data is
+ * within actual physical address range of DDR. Adding
+ * offset which will be stripped off by ATF prior to
+ * sending to SDM.
+ */
+ if (p_msg->command == COMMAND_RECONFIG_DATA_SUBMIT &&
+ chan->ctrl->is_smmu_enabled)
+ p_data->paddr += chan->ctrl->sdm_dma_addr_offset;
break;
}
if (p_msg->payload_output) {
@@ -1796,25 +1830,62 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan,
size_t size)
{
struct stratix10_svc_data_mem *pmem;
- unsigned long va;
+ unsigned long va_gen_pool;
phys_addr_t pa;
struct gen_pool *genpool = chan->ctrl->genpool;
- size_t s = roundup(size, 1 << genpool->min_alloc_order);
+ size_t s;
+ void *va;
+ int ret;
+ struct iova *alloc;
+ dma_addr_t dma_addr;
pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL);
if (!pmem)
return ERR_PTR(-ENOMEM);
guard(mutex)(&svc_mem_lock);
- va = gen_pool_alloc(genpool, s);
- if (!va)
- return ERR_PTR(-ENOMEM);
+ if (chan->ctrl->is_smmu_enabled) {
+ s = PAGE_ALIGN(size);
+ va = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO | __GFP_DMA, get_order(s));
+ if (!va) {
+ pr_debug("%s get_free_pages_failes\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
- memset((void *)va, 0, s);
- pa = gen_pool_virt_to_phys(genpool, va);
+ alloc = alloc_iova(&chan->ctrl->carveout.domain,
+ s >> chan->ctrl->carveout.shift,
+ chan->ctrl->carveout.limit >> chan->ctrl->carveout.shift,
+ true);
+
+ dma_addr = iova_dma_addr(&chan->ctrl->carveout.domain, alloc);
+
+ ret = iommu_map(chan->ctrl->domain, dma_addr, virt_to_phys(va),
+ s, IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO | IOMMU_CACHE,
+ GFP_KERNEL);
+ if (ret < 0) {
+ pr_debug("%s IOMMU map failed\n", __func__);
+ free_iova(&chan->ctrl->carveout.domain,
+ iova_pfn(&chan->ctrl->carveout.domain,
+ dma_addr));
+ free_pages((unsigned long)va, get_order(size));
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pmem->paddr = dma_addr;
+ } else {
+ s = roundup(size, 1 << genpool->min_alloc_order);
+ va_gen_pool = gen_pool_alloc(genpool, s);
+ if (!va_gen_pool)
+ return ERR_PTR(-ENOMEM);
+
+ va = (void *)va_gen_pool;
+
+ memset((void *)va, 0, s);
+ pa = gen_pool_virt_to_phys(genpool, va_gen_pool);
+ pmem->paddr = pa;
+ }
- pmem->vaddr = (void *)va;
- pmem->paddr = pa;
+ pmem->vaddr = va;
pmem->size = s;
list_add_tail(&pmem->node, &svc_data_mem);
pr_debug("%s: va=%p, pa=0x%016x\n", __func__,
@@ -1838,10 +1909,17 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
list_for_each_entry(pmem, &svc_data_mem, node)
if (pmem->vaddr == kaddr) {
- gen_pool_free(chan->ctrl->genpool,
- (unsigned long)kaddr, pmem->size);
- pmem->vaddr = NULL;
- list_del(&pmem->node);
+ if (chan->ctrl->is_smmu_enabled) {
+ iommu_unmap(chan->ctrl->domain, pmem->paddr, pmem->size);
+ free_iova(&chan->ctrl->carveout.domain,
+ iova_pfn(&chan->ctrl->carveout.domain,
+ pmem->paddr));
+ free_pages((unsigned long)pmem->vaddr, get_order(pmem->size));
+ } else {
+ gen_pool_free(chan->ctrl->genpool,
+ (unsigned long)kaddr, pmem->size);
+ pmem->vaddr = NULL;
+ }
return;
}
@@ -1852,6 +1930,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_free_memory);
static const struct of_device_id stratix10_svc_drv_match[] = {
{.compatible = "intel,stratix10-svc"},
{.compatible = "intel,agilex-svc"},
+ {.compatible = "intel,agilex5-svc"},
{},
};
@@ -1863,10 +1942,12 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
struct gen_pool *genpool;
struct stratix10_svc_sh_memory *sh_memory;
struct stratix10_svc *svc;
+ struct device_node *node = pdev->dev.of_node;
svc_invoke_fn *invoke_fn;
size_t fifo_size;
int ret;
+ unsigned long order;
/* get SMC or HVC function */
invoke_fn = get_invoke_func(dev);
@@ -1907,8 +1988,52 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
controller->genpool = genpool;
controller->task = NULL;
controller->invoke_fn = invoke_fn;
+ controller->is_smmu_enabled = false;
+ controller->sdm_dma_addr_offset = 0x0;
init_completion(&controller->complete_status);
+ if (of_device_is_compatible(node, "intel,agilex5-svc")) {
+ if (device_iommu_mapped(&pdev->dev)) {
+ controller->is_smmu_enabled = true;
+ controller->sdm_dma_addr_offset = AGILEX5_SDM_DMA_ADDR_OFFSET;
+ pr_debug("Intel Service Layer Driver: IOMMU Present\n");
+ controller->domain = iommu_get_dma_domain(dev);
+
+ if (!controller->domain) {
+ pr_warn("Intel Service Layer Driver: Error IOMMU domain\n");
+ ret = -ENODEV;
+ goto err_destroy_pool;
+ } else {
+ ret = iova_cache_get();
+ if (ret < 0) {
+ pr_warn("Intel Service Layer Driver: IOVA cache failed\n");
+ iommu_domain_free(controller->domain);
+ ret = -ENODEV;
+ goto err_destroy_pool;
+ }
+ ret = iommu_attach_device(controller->domain, dev);
+ if (ret) {
+ pr_warn("Intel Service Layer Driver: Error IOMMU attach failed\n");
+ iova_cache_put();
+ iommu_domain_free(controller->domain);
+ ret = -ENODEV;
+ goto err_destroy_pool;
+ }
+ }
+
+ order = __ffs(controller->domain->pgsize_bitmap);
+ init_iova_domain(&controller->carveout.domain, 1UL << order,
+ IOMMU_STARTING_ADDR);
+
+ controller->carveout.shift = iova_shift(&controller->carveout.domain);
+ controller->carveout.limit = IOMMU_LIMIT_ADDR - PAGE_SIZE;
+ } else {
+ pr_warn("Intel Service Layer Driver: IOMMU Not Present\n");
+ ret = -ENODEV;
+ goto err_destroy_pool;
+ }
+ }
+
ret = stratix10_svc_async_init(controller);
if (ret) {
dev_dbg(dev, "Intel Service Layer Driver: Error on stratix10_svc_async_init %d\n",
@@ -2011,6 +2136,13 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
of_platform_depopulate(ctrl->dev);
+ if (ctrl->domain) {
+ put_iova_domain(&ctrl->carveout.domain);
+ iova_cache_put();
+ iommu_detach_device(ctrl->domain, &pdev->dev);
+ iommu_domain_free(ctrl->domain);
+ }
+
platform_device_unregister(svc->intel_svc_fcs);
platform_device_unregister(svc->stratix10_svc_rsu);
--
2.49.GIT
Powered by blists - more mailing lists