[PATCH v22 7/7] remoteproc: stm32: add OP-TEE backend support

Arnaud Pouliquen arnaud.pouliquen at foss.st.com
Tue Apr 14 08:29:02 PDT 2026


Integrate OP-TEE-controlled firmware loading into stm32_rproc
adding support for the st,rproc-tee devicetree property.

When st,rproc-tee is present, the driver:
- parses the backend phandle + ID from DT and validates it
- uses the remoteproc_tee operation set and registration path
- keeps reset/holdboot handling only for the non-TEE case
- uses a TEE-specific flow.

Notice that the attach/detach is not yet supported and should be part
of a separate patchset.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen at foss.st.com>
---
V21 updates:
- reapply the V19 patch as the stm32_rproc-tee  driver can no more
  be used.
- update v19 patch to support the st,rproc-tee phandle.
- rework the commit message.
---
 drivers/remoteproc/stm32_rproc.c | 167 +++++++++++++++++++++++--------
 1 file changed, 123 insertions(+), 44 deletions(-)

diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 4bcd6a784935..61d89f3a78b1 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -18,6 +18,7 @@
 #include <linux/pm_wakeirq.h>
 #include <linux/regmap.h>
 #include <linux/remoteproc.h>
+#include <linux/remoteproc_tee.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
@@ -89,6 +90,7 @@ struct stm32_rproc {
 	struct stm32_rproc_mem *rmems;
 	struct stm32_mbox mb[MBOX_NB_MBX];
 	struct workqueue_struct *workqueue;
+	struct of_phandle_args tee_phandle;
 	bool hold_boot_smc;
 	void __iomem *rsc_va;
 };
@@ -255,6 +257,19 @@ static int stm32_rproc_release(struct rproc *rproc)
 	return 0;
 }
 
+static int stm32_rproc_tee_stop(struct rproc *rproc)
+{
+	int err;
+
+	stm32_rproc_request_shutdown(rproc);
+
+	err = rproc_tee_stop(rproc);
+	if (err)
+		return err;
+
+	return stm32_rproc_release(rproc);
+}
+
 static int stm32_rproc_prepare(struct rproc *rproc)
 {
 	struct device *dev = rproc->dev.parent;
@@ -683,6 +698,17 @@ static const struct rproc_ops st_rproc_ops = {
 	.get_boot_addr	= rproc_elf_get_boot_addr,
 };
 
+static const struct rproc_ops st_rproc_tee_ops = {
+	.prepare	= stm32_rproc_prepare,
+	.start		= rproc_tee_start,
+	.stop		= stm32_rproc_tee_stop,
+	.kick		= stm32_rproc_kick,
+	.load		= rproc_tee_load_fw,
+	.parse_fw	= rproc_tee_parse_fw,
+	.find_loaded_rsc_table = rproc_tee_find_loaded_rsc_table,
+	.release_fw	= rproc_tee_release_fw,
+};
+
 static const struct of_device_id stm32_rproc_match[] = {
 	{ .compatible = "st,stm32mp1-m4" },
 	{},
@@ -716,6 +742,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev,
 {
 	struct device *dev = &pdev->dev;
 	struct device_node *np = dev->of_node;
+	struct of_phandle_args tee_phandle;
 	struct stm32_syscon tz;
 	unsigned int tzen;
 	int err, irq;
@@ -741,51 +768,69 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev,
 		dev_info(dev, "wdg irq registered\n");
 	}
 
-	ddata->rst = devm_reset_control_get_optional(dev, "mcu_rst");
-	if (!ddata->rst) {
-		/* Try legacy fallback method: get it by index */
-		ddata->rst = devm_reset_control_get_by_index(dev, 0);
+	if (of_find_property(np, "st,rproc-tee", NULL)) {
+		err = of_parse_phandle_with_fixed_args(np, "st,rproc-tee",
+						       1, 0, &tee_phandle);
+		if (err)
+			return dev_err_probe(dev, err,
+					     "failed to parse st,rproc-tee\n");
+
+		if (!IS_ENABLED(CONFIG_REMOTEPROC_TEE)) {
+			of_node_put(tee_phandle.np);
+			return dev_err_probe(dev, -ENODEV,
+					     "st,rproc-tee requires REMOTEPROC_TEE support\n");
+		}
+
+		ddata->tee_phandle = tee_phandle;
 	}
-	if (IS_ERR(ddata->rst))
-		return dev_err_probe(dev, PTR_ERR(ddata->rst),
-				     "failed to get mcu_reset\n");
 
-	/*
-	 * Three ways to manage the hold boot
-	 * - using SCMI: the hold boot is managed as a reset
-	 *    The DT "reset-mames" property should be defined with 2 items:
-	 *        reset-names = "mcu_rst", "hold_boot";
-	 * - using SMC call (deprecated): use SMC reset interface
-	 *    The DT "reset-mames" property is optional, "st,syscfg-tz" is required
-	 * - default(no SCMI, no SMC): the hold boot is managed as a syscon register
-	 *    The DT "reset-mames" property is optional, "st,syscfg-holdboot" is required
-	 */
+	if (!ddata->tee_phandle.np) {
+		ddata->rst = devm_reset_control_get_optional(dev, "mcu_rst");
+		if (!ddata->rst) {
+			/* Try legacy fallback method: get it by index */
+			ddata->rst = devm_reset_control_get_by_index(dev, 0);
+		}
+		if (IS_ERR(ddata->rst))
+			return dev_err_probe(dev, PTR_ERR(ddata->rst),
+					"failed to get mcu_reset\n");
 
-	ddata->hold_boot_rst = devm_reset_control_get_optional(dev, "hold_boot");
-	if (IS_ERR(ddata->hold_boot_rst))
-		return dev_err_probe(dev, PTR_ERR(ddata->hold_boot_rst),
-				     "failed to get hold_boot reset\n");
+		/*
+		 * Three ways to manage the hold boot
+		 * - using SCMI: the hold boot is managed as a reset
+		 *    The DT "reset-mames" property should be defined with 2 items:
+		 *        reset-names = "mcu_rst", "hold_boot";
+		 * - using SMC call (deprecated): use SMC reset interface
+		 *    The DT "reset-mames" property is optional, "st,syscfg-tz" is required
+		 * - default(no SCMI, no SMC): the hold boot is managed as a syscon register
+		 *    The DT "reset-mames" property is optional, "st,syscfg-holdboot" is required
+		 */
 
-	if (!ddata->hold_boot_rst && IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)) {
-		/* Manage the MCU_BOOT using SMC call */
-		err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz);
-		if (!err) {
-			err = regmap_read(tz.map, tz.reg, &tzen);
-			if (err) {
-				dev_err(dev, "failed to read tzen\n");
-				return err;
+		ddata->hold_boot_rst = devm_reset_control_get_optional(dev, "hold_boot");
+		if (IS_ERR(ddata->hold_boot_rst))
+			return dev_err_probe(dev, PTR_ERR(ddata->hold_boot_rst),
+					"failed to get hold_boot reset\n");
+
+		if (!ddata->hold_boot_rst && IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)) {
+			/* Manage the MCU_BOOT using SMC call */
+			err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz);
+			if (!err) {
+				err = regmap_read(tz.map, tz.reg, &tzen);
+				if (err) {
+					dev_err(dev, "failed to read tzen\n");
+					return err;
+				}
+				ddata->hold_boot_smc = tzen & tz.mask;
 			}
-			ddata->hold_boot_smc = tzen & tz.mask;
 		}
-	}
 
-	if (!ddata->hold_boot_rst && !ddata->hold_boot_smc) {
-		/* Default: hold boot manage it through the syscon controller */
-		err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot",
-					     &ddata->hold_boot);
-		if (err) {
-			dev_err(dev, "failed to get hold boot\n");
-			return err;
+		if (!ddata->hold_boot_rst && !ddata->hold_boot_smc) {
+			/* Default: hold boot manage it through the syscon controller */
+			err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot",
+						     &ddata->hold_boot);
+			if (err) {
+				dev_err(dev, "failed to get hold boot\n");
+				return err;
+			}
 		}
 	}
 
@@ -857,18 +902,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
 	if (ret < 0 && ret != -EINVAL)
 		return ret;
 
-	rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, fw_name, sizeof(*ddata));
+	if (of_find_property(np, "st,rproc-tee", NULL))
+		rproc = devm_rproc_alloc(dev, np->name, &st_rproc_tee_ops, fw_name,
+					 sizeof(*ddata));
+	else
+		rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, fw_name,
+					 sizeof(*ddata));
+
 	if (!rproc)
 		return -ENOMEM;
 
 	ddata = rproc->priv;
-
 	rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
 
 	ret = stm32_rproc_parse_dt(pdev, ddata, &rproc->auto_boot);
 	if (ret)
 		goto free_rproc;
 
+	if (ddata->tee_phandle.np &&
+	    !of_device_is_available(ddata->tee_phandle.np)) {
+		of_node_put(ddata->tee_phandle.np);
+		ddata->tee_phandle.np = NULL;
+		ret = -EPROBE_DEFER;
+		goto free_rproc;
+	}
+
 	ret = stm32_rproc_of_memory_translations(pdev, ddata);
 	if (ret)
 		goto free_rproc;
@@ -877,8 +935,14 @@ static int stm32_rproc_probe(struct platform_device *pdev)
 	if (ret)
 		goto free_rproc;
 
-	if (state == M4_STATE_CRUN)
+	if (state == M4_STATE_CRUN) {
+		if (ddata->tee_phandle.np) {
+			dev_err(dev, "TEE support not yet compatible with attached state\n");
+			ret = -EINVAL;
+			goto free_rproc;
+		}
 		rproc->state = RPROC_DETACHED;
+	}
 
 	rproc->has_iommu = false;
 	ddata->workqueue = create_workqueue(dev_name(dev));
@@ -894,10 +958,12 @@ static int stm32_rproc_probe(struct platform_device *pdev)
 	if (ret)
 		goto free_wkq;
 
-	ret = rproc_add(rproc);
+	if (ddata->tee_phandle.np)
+		ret = rproc_tee_register(dev, rproc, &ddata->tee_phandle);
+	else
+		ret = rproc_add(rproc);
 	if (ret)
 		goto free_mb;
-
 	return 0;
 
 free_mb:
@@ -911,6 +977,11 @@ static int stm32_rproc_probe(struct platform_device *pdev)
 		dev_pm_clear_wake_irq(dev);
 		device_init_wakeup(dev, false);
 	}
+	if (ddata->tee_phandle.np) {
+		of_node_put(ddata->tee_phandle.np);
+		ddata->tee_phandle.np = NULL;
+	}
+
 	return ret;
 }
 
@@ -919,11 +990,11 @@ static void stm32_rproc_remove(struct platform_device *pdev)
 	struct rproc *rproc = platform_get_drvdata(pdev);
 	struct stm32_rproc *ddata = rproc->priv;
 	struct device *dev = &pdev->dev;
+	struct device_node *tee_np = ddata->tee_phandle.np;
 
 	if (atomic_read(&rproc->power) > 0)
 		rproc_shutdown(rproc);
 
-	rproc_del(rproc);
 	stm32_rproc_free_mbox(rproc);
 	destroy_workqueue(ddata->workqueue);
 
@@ -931,6 +1002,14 @@ static void stm32_rproc_remove(struct platform_device *pdev)
 		dev_pm_clear_wake_irq(dev);
 		device_init_wakeup(dev, false);
 	}
+
+	if (tee_np) {
+		ddata->tee_phandle.np = NULL;
+		rproc_tee_unregister(dev, rproc);
+		of_node_put(tee_np);
+	} else {
+		rproc_del(rproc);
+	}
 }
 
 static int stm32_rproc_suspend(struct device *dev)
-- 
2.43.0




More information about the linux-arm-kernel mailing list