[PATCH v6 03/22] firmware: arm_scmi: Introduce protocol instance notifiers
From: Cristian Marussi
Date: Fri Jul 24 2026 - 10:51:13 EST
Allow protocols themselves to register for their own notifications and
provide their own notifier callbacks. Each protocol can now register one
unique per-protocol instance notifier block whose callback will be
registered on the proper notification chain as usual: such notifier will
be automatically removed during the protocol de-initialiazation phase.
Signed-off-by: Cristian Marussi <cristian.marussi@xxxxxxx>
---
v5 -->v6
- move protocol notifier registration out of proto_init
v2-->v3
- split out unrelated changes on event sizing
v1-->v2
- Fixed multiline comment format
---
drivers/firmware/arm_scmi/common.h | 12 ++++
drivers/firmware/arm_scmi/driver.c | 86 +++++++++++++++++++++------
drivers/firmware/arm_scmi/notify.c | 14 +++++
drivers/firmware/arm_scmi/protocols.h | 10 ++++
4 files changed, 103 insertions(+), 19 deletions(-)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 767f08be416c..f8fbb1a46482 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/property.h>
+#include <linux/notifier.h>
#include <linux/refcount.h>
#include <linux/scmi_protocol.h>
#include <linux/spinlock.h>
@@ -708,8 +709,19 @@ static struct platform_driver __drv = { \
.probe = __tag##_probe, \
}
+struct scmi_protocol_notifier {
+ u8 proto_id;
+ u8 evt_id;
+ const u32 *src_id;
+ struct notifier_block *nb;
+};
+
void scmi_notification_instance_data_set(const struct scmi_handle *handle,
void *priv);
void *scmi_notification_instance_data_get(const struct scmi_handle *handle);
+int scmi_protocol_notifier_register(const struct scmi_handle *handle,
+ struct scmi_protocol_notifier *pno);
+int scmi_protocol_notifier_unregister(const struct scmi_handle *handle,
+ struct scmi_protocol_notifier *pno);
int scmi_inflight_count(const struct scmi_handle *handle);
#endif /* _SCMI_COMMON_H */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 8e06e40d1a11..0f9e8dfc6137 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -90,6 +90,8 @@ struct scmi_xfers_info {
* @gid: A reference for per-protocol devres management.
* @users: A refcount to track effective users of this protocol.
* @priv: Reference for optional protocol private data.
+ * @pno: A protocol instance notifier descriptor: active only when the
+ * included notifier block @nb is non-NULL.
* @version: Protocol version supported by the platform as detected at runtime.
* @negotiated_version: When the platform supports a newer protocol version,
* the agent will try to negotiate with the platform the
@@ -109,6 +111,7 @@ struct scmi_protocol_instance {
void *gid;
refcount_t users;
void *priv;
+ struct scmi_protocol_notifier pno;
unsigned int version;
unsigned int negotiated_version;
struct scmi_protocol_handle ph;
@@ -1657,6 +1660,24 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
return pi->priv;
}
+static int
+scmi_register_instance_notifier(const struct scmi_protocol_handle *ph, u8 evt_id,
+ const u32 *src_id, struct notifier_block *nb)
+{
+ struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ pi->pno.proto_id = pi->proto->id;
+ pi->pno.evt_id = evt_id;
+ pi->pno.src_id = src_id;
+ /*
+ * Ensure the descriptor fields are visibile when the notifier block is
+ * made available
+ */
+ smp_store_mb(pi->pno.nb, nb);
+
+ return 0;
+}
+
static const struct scmi_xfer_ops xfer_ops = {
.xfer_get_init = xfer_get_init,
.reset_rx_to_maxsz = reset_rx_to_maxsz,
@@ -2257,6 +2278,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
pi->ph.hops = &helpers_ops;
pi->ph.set_priv = scmi_set_protocol_priv;
pi->ph.get_priv = scmi_get_protocol_priv;
+ pi->ph.instance_notifier_register = scmi_register_instance_notifier;
refcount_set(&pi->users, 1);
/*
@@ -2319,24 +2341,37 @@ static struct scmi_protocol_instance * __must_check
scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
{
struct scmi_protocol_instance *pi;
+ struct notifier_block *proto_notifier_nb = NULL;
struct scmi_info *info = handle_to_scmi_info(handle);
- mutex_lock(&info->protocols_mtx);
- pi = idr_find(&info->protocols, protocol_id);
+ scoped_guard(mutex, &info->protocols_mtx) {
+ pi = idr_find(&info->protocols, protocol_id);
+ if (pi) {
+ refcount_inc(&pi->users);
+ } else {
+ const struct scmi_protocol *proto;
- if (pi) {
- refcount_inc(&pi->users);
- } else {
- const struct scmi_protocol *proto;
+ /* Fails if protocol not registered on bus */
+ proto = scmi_protocol_get(protocol_id, &info->version);
+ if (!proto)
+ return ERR_PTR(-EPROBE_DEFER);
- /* Fails if protocol not registered on bus */
- proto = scmi_protocol_get(protocol_id, &info->version);
- if (proto)
pi = scmi_alloc_init_protocol_instance(info, proto);
- else
- pi = ERR_PTR(-EPROBE_DEFER);
+ if (IS_ERR(pi))
+ return pi;
+
+ proto_notifier_nb = READ_ONCE(pi->pno.nb);
+ }
+ }
+
+ if (proto_notifier_nb) {
+ int ret;
+
+ ret = scmi_protocol_notifier_register(pi->handle, &pi->pno);
+ if (ret)
+ dev_warn(handle->dev,
+ "Failed to register protocol notifier\n");
}
- mutex_unlock(&info->protocols_mtx);
return pi;
}
@@ -2367,13 +2402,29 @@ int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id)
void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
{
struct scmi_info *info = handle_to_scmi_info(handle);
+ struct notifier_block *proto_notifier_nb = NULL;
struct scmi_protocol_instance *pi;
- mutex_lock(&info->protocols_mtx);
- pi = idr_find(&info->protocols, protocol_id);
- if (WARN_ON(!pi))
- goto out;
+ scoped_guard(mutex, &info->protocols_mtx) {
+ pi = idr_find(&info->protocols, protocol_id);
+ if (WARN_ON(!pi))
+ return;
+
+ proto_notifier_nb = pi->pno.nb;
+ /* Ensure NULL is visible */
+ smp_store_mb(pi->pno.nb, NULL);
+ }
+ if (proto_notifier_nb) {
+ int ret;
+
+ ret = scmi_protocol_notifier_unregister(pi->handle, &pi->pno);
+ if (ret)
+ dev_err(handle->dev,
+ "Failed to release protocol notifier\n");
+ }
+
+ guard(mutex)(&info->protocols_mtx);
if (refcount_dec_and_test(&pi->users)) {
void *gid = pi->gid;
@@ -2391,9 +2442,6 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
protocol_id);
}
-
-out:
- mutex_unlock(&info->protocols_mtx);
}
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 8995dd1b12aa..938d575e22fe 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -1420,6 +1420,13 @@ static int scmi_notifier_register(const struct scmi_handle *handle,
return ret;
}
+int scmi_protocol_notifier_register(const struct scmi_handle *handle,
+ struct scmi_protocol_notifier *pno)
+{
+ return scmi_notifier_register(handle, pno->proto_id, pno->evt_id,
+ pno->src_id, pno->nb);
+}
+
/**
* scmi_notifier_unregister() - Unregister a notifier_block for an event
* @handle: The handle identifying the platform instance against which the
@@ -1477,6 +1484,13 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle,
return 0;
}
+int scmi_protocol_notifier_unregister(const struct scmi_handle *handle,
+ struct scmi_protocol_notifier *pno)
+{
+ return scmi_notifier_unregister(handle, pno->proto_id, pno->evt_id,
+ pno->src_id, pno->nb);
+}
+
struct scmi_notifier_devres {
const struct scmi_handle *handle;
u8 proto_id;
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index 15ad5162e37a..b7f29003a7dc 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -166,6 +166,13 @@ struct scmi_proto_helpers_ops;
* can be used by the protocol implementation to generate SCMI messages.
* @set_priv: A method to set protocol private data for this instance.
* @get_priv: A method to get protocol private data previously set.
+ * @instance_notifier_register: A method to register interest for specific
+ * notifications from within a protocol
+ * implementation unit: ONLY one instance notifier
+ * can be registered per-protocol.
+ * The related notifier block will be registered
+ * right after the protocol is initialized and
+ * automatically removed on protocol release.
*
* This structure represents a protocol initialized against specific SCMI
* instance and it will be used as follows:
@@ -185,6 +192,9 @@ struct scmi_protocol_handle {
const struct scmi_proto_helpers_ops *hops;
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
void *(*get_priv)(const struct scmi_protocol_handle *ph);
+ int (*instance_notifier_register)(const struct scmi_protocol_handle *ph,
+ u8 evt_id, const u32 *src_id,
+ struct notifier_block *nb);
};
/**
--
2.54.0