[PATCH v1 3/3] firmware: arm_scmi: optee: Rework transport probe sequence

Cristian Marussi cristian.marussi at arm.com
Mon May 4 09:52:04 PDT 2026


Use the new per-instance transport handles helpers to synchronize and
optionally defer the core SCMI driver probe up until the transport driver
has completely been initialized and it is fully operational as a supplier.

Introduce proper module init/exit routines while removing the ugly trick of
registering a driver from within the probe sequence of another one, just to
avoid to have to deal with probe deferrals.

Signed-off-by: Cristian Marussi <cristian.marussi at arm.com>
---
 drivers/firmware/arm_scmi/transports/optee.c | 118 +++++++++++++++++--
 1 file changed, 105 insertions(+), 13 deletions(-)

diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c
index 07ae18d5279d..6950089fb659 100644
--- a/drivers/firmware/arm_scmi/transports/optee.c
+++ b/drivers/firmware/arm_scmi/transports/optee.c
@@ -154,6 +154,79 @@ static struct scmi_transport_core_operations *core;
 /* There can be only 1 SCMI service in OP-TEE we connect to */
 static struct scmi_optee_agent *scmi_optee_private;
 
+/**
+ * struct scmi_optee_suppliers  - Transport descriptor
+ * @mtx: A mutex to protect @available
+ * @available: A reference to an initialized transport device ready to use
+ *	       which  will cycle through the following 3 states:
+ *	       1. PTR_ERR(-EPROBE_DEFER) at start before transport is ready
+ *	       2. <supplier_dev> when transport is initialized, ready for use,
+ *	          but still unused
+ *	       3. PTR_ERR(-EBUSY) when transport supplier device is in use
+ * @th: An embedded transport handle object that embeds the helpers
+ *	implementing the above mentioned logic
+ *
+ * Note that this transport driver enforces single instance probing.
+ */
+struct scmi_optee_suppliers {
+	/* Protect @available */
+	struct mutex mtx;
+	struct device *available;
+	const struct scmi_transport_handle th;
+};
+
+#define to_osup(t)	container_of(t, struct scmi_optee_suppliers, th)
+
+static int scmi_optee_supplier_init(const struct scmi_transport_handle *th,
+				    struct device *dev)
+{
+	struct scmi_optee_suppliers *osup = to_osup(th);
+
+	guard(mutex)(&osup->mtx);
+	/* Was any transport already registered ? */
+	if (!IS_ERR(osup->available))
+		return -EBUSY;
+
+	osup->available = dev;
+
+	return 0;
+}
+
+static struct device *
+scmi_optee_supplier_get(const struct scmi_transport_handle *th)
+{
+	struct scmi_optee_suppliers *osup = to_osup(th);
+	struct device *supplier;
+
+	guard(mutex)(&osup->mtx);
+	supplier = osup->available;
+	if (!IS_ERR(supplier))
+		osup->available = ERR_PTR(-EBUSY);
+
+	return supplier;
+}
+
+static void scmi_optee_supplier_put(const struct scmi_transport_handle *th,
+				    struct device *supplier)
+{
+	struct scmi_optee_suppliers *osup = to_osup(th);
+
+	guard(mutex)(&osup->mtx);
+	osup->available = supplier;
+}
+
+static void scmi_optee_supplier_cleanup(const struct scmi_transport_handle *th)
+{
+	scmi_optee_supplier_put(th, ERR_PTR(-EPROBE_DEFER));
+}
+
+static struct scmi_optee_suppliers scmi_transport_suppliers = {
+	.mtx = __MUTEX_INITIALIZER(mutexname),
+	.available = INIT_ERR_PTR(-EPROBE_DEFER),
+	.th.supplier_get = scmi_optee_supplier_get,
+	.th.supplier_put = scmi_optee_supplier_put,
+};
+
 /* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */
 static int open_session(struct scmi_optee_agent *agent, u32 *tee_session)
 {
@@ -522,7 +595,7 @@ static struct scmi_desc scmi_optee_desc = {
 };
 
 static const struct of_device_id scmi_of_match[] = {
-	{ .compatible = "linaro,scmi-optee" },
+	{ .compatible = "linaro,scmi-optee", .data = &scmi_transport_suppliers.th},
 	{ /* Sentinel */ },
 };
 
@@ -561,15 +634,12 @@ static int scmi_optee_service_probe(struct tee_client_device *scmi_pta)
 	if (ret)
 		goto err;
 
-	/* Ensure agent resources are all visible before scmi_optee_private is */
-	smp_mb();
-	scmi_optee_private = agent;
-
-	ret = platform_driver_register(&scmi_optee_driver);
-	if (ret) {
-		scmi_optee_private = NULL;
+	ret = scmi_optee_supplier_init(&scmi_transport_suppliers.th, agent->dev);
+	if (ret)
 		goto err;
-	}
+
+	/* Ensure initialized scmi_optee_private is visible */
+	smp_store_mb(scmi_optee_private, agent);
 
 	return 0;
 
@@ -586,13 +656,12 @@ static void scmi_optee_service_remove(struct tee_client_device *scmi_pta)
 	if (!scmi_optee_private)
 		return;
 
-	platform_driver_unregister(&scmi_optee_driver);
-
 	if (!list_empty(&scmi_optee_private->channel_list))
 		return;
 
-	/* Ensure cleared reference is visible before resources are released */
+	/* Ensure scmi_optee_private is visible as NULL */
 	smp_store_mb(scmi_optee_private, NULL);
+	scmi_optee_supplier_cleanup(&scmi_transport_suppliers.th);
 
 	tee_client_close_context(agent->tee_ctx);
 }
@@ -616,7 +685,30 @@ static struct tee_client_driver scmi_optee_service_driver = {
 	},
 };
 
-module_tee_client_driver(scmi_optee_service_driver);
+static int __init scmi_transport_optee_init(void)
+{
+	int ret;
+
+	ret = tee_client_driver_register(&scmi_optee_service_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&scmi_optee_driver);
+	if (ret) {
+		tee_client_driver_unregister(&scmi_optee_service_driver);
+		return ret;
+	}
+
+	return ret;
+}
+module_init(scmi_transport_optee_init);
+
+static void __exit scmi_transport_optee_exit(void)
+{
+	platform_driver_unregister(&scmi_optee_driver);
+	tee_client_driver_unregister(&scmi_optee_service_driver);
+}
+module_exit(scmi_transport_optee_exit);
 
 MODULE_AUTHOR("Etienne Carriere <etienne.carriere at foss.st.com>");
 MODULE_DESCRIPTION("SCMI OPTEE Transport driver");
-- 
2.53.0




More information about the linux-arm-kernel mailing list