linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/8] SCMI vendor protocols and modularization
@ 2020-10-28 20:29 Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface Cristian Marussi
                   ` (8 more replies)
  0 siblings, 9 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Hi all,

The current SCMI implementation does not provide an interface to easily
develop and include a custom vendor protocol implementation as prescribed
by the SCMI standard, also because, there is not currently any custom
protocol in the upstream to justify the development of a custom interface
and its maintenance.

Moreover the current interface exposes protocol operations to the SCMI
driver users attaching per-protocol operations directly to the handle
structure, which, in this way, tends to grow indefinitely for each new
protocol addition.

Beside this, protocols private data are also exposed via handle *_priv
pointers, making such private data accessible also to the SCMI drivers
even if neither really needed nor advisable.

This series tris to address this simplifying the SCMI protocols interface
and reducing it, roughly, to these common generic operations:

handle->get_ops() / handle->put_ops() / handle->notify_ops()

and a few related devres managed flavours.

All protocols' private data pointers are removed from handle too and made
accessible only to the protocols code through dedicated internal helpers.

The concept of protocol handle is introduced in the SCMI protocol code
to represent a protocol instance initialized against a specific SCMI
instance(handle): so that all the new protocol code uses such protocol
handles wherever previously SCMI handle was used: this enable tighter
control of what is exposed to the protocol code vs the SCMI drivers.

Moreover protocol initialization is moved away from device probe and now
happens on demand when the first user shows up (first .get_ops), while
de-initialization is performed once the last user of the protocol (even in
terms of notifications) is gone, with the SCMI core taking care to perform
all the needed underlying resource accounting.

This way any new future standard or custom protocol implementation will
expose a common unified interface which does not need to be extended
endlessly: no need to maintain a custom interface only for vendor protos.
SCMI drivers written on top of standard or custom protocols will use this
same common interface to access any protocol operations.
All existent upstream SCMI drivers are converted to this new interface.

Leveraging this new centralized and common initialization flow, patches 5,7
take care also to refactor and simplify protocol-events registration and
remove *notify_priv from the handle interface making it accessible only to
the notification core.

Finally, patch 8 builds on top of this new interface and introduces a
mechanism to define an SCMI protocol as a full blown module (possibly
loadable) while leaving the core dealing with proper resource accounting.

Standard protocols are still kept as builtins, though.

The whole SCMI stack can be built alternatively as a module (incudling all
the standard protocols).

On top of this series an example SCMI Custom protocol 0x99 and related
SCMI Custom Dummy driver has been built and it is available at [2] as a
series of DEBUG patches on top this same series.

The current revision of this series still does not address the possibility
of creating dynamically the SCMI devices: any new protocols must be added
to the SCMI embedded module device table as of now, while it could be
desirable to have such devices created dynamically whenever a new protocol
is added and loaded into the system.

The series is currently based on for-next/scmi [1] on top of:

commit b9ceca6be432 ("firmware: arm_scmi: Fix duplicate workqueue name")

Any feedback welcome.

Thanks,

Cristian

---

v1 --> v2
- rebased on for-next/scmi v5.10-rc1
- introduced protocol handles
- added devres managed devm_ variant for protocols operations
- made all scmi_protocol refs const
- introduced IDR to handle protocols instead of static array
- refactored code around fast path

[1]:https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/log/?h=for-next/scmi
[2]:https://gitlab.arm.com/linux-arm/linux-cm/-/commits/scmi_modules_ext_V2/

Cristian Marussi (8):
  firmware: arm_scmi: review protocol registration interface
  firmware: arm_scmi: introduce protocol handles
  firmware: arm_scmi: introduce new protocol operations support
  firmware: arm_scmi: make notifications aware of protocol usage
  firmware: arm_scmi: refactor events registration
  firmware: arm_scmi: port all protocols and drivers to the new API
  firmware: arm_scmi: make notify_priv really private
  firmware: arm_scmi: add protocol modularization support

 drivers/clk/clk-scmi.c                     |  27 +-
 drivers/cpufreq/scmi-cpufreq.c             |  38 +-
 drivers/firmware/arm_scmi/base.c           | 140 +++---
 drivers/firmware/arm_scmi/bus.c            |  70 +--
 drivers/firmware/arm_scmi/clock.c          | 127 +++---
 drivers/firmware/arm_scmi/common.h         | 114 ++++-
 drivers/firmware/arm_scmi/driver.c         | 474 +++++++++++++++++++--
 drivers/firmware/arm_scmi/notify.c         | 303 ++++++++++---
 drivers/firmware/arm_scmi/notify.h         |  38 +-
 drivers/firmware/arm_scmi/perf.c           | 257 +++++------
 drivers/firmware/arm_scmi/power.c          | 132 +++---
 drivers/firmware/arm_scmi/reset.c          | 144 ++++---
 drivers/firmware/arm_scmi/scmi_pm_domain.c |  26 +-
 drivers/firmware/arm_scmi/sensors.c        | 135 +++---
 drivers/firmware/arm_scmi/system.c         |  60 +--
 drivers/hwmon/scmi-hwmon.c                 |  24 +-
 drivers/reset/reset-scmi.c                 |  33 +-
 include/linux/scmi_protocol.h              | 142 +++---
 18 files changed, 1569 insertions(+), 715 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-11-04 16:16   ` Thara Gopinath
  2020-10-28 20:29 ` [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles Cristian Marussi
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Extend common protocol registration routines and provide some new generic
protocols' init/deinit helpers that tracks protocols' users and automatically
perform the proper initialization/de-initialization on demand.

Convert all protocols to use new registration schema while modifying only Base
protocol to use also the new initialization helpers.

All other standard protocols' initialization is still umodified and bound to
SCMI devices probing.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
v1 --> v2
- made all scmi_protocol refs const
- introduced IDR to handle protocols instead of static array
- refactored code around fast path
---
 drivers/firmware/arm_scmi/base.c    |  10 +-
 drivers/firmware/arm_scmi/bus.c     |  61 +++++++---
 drivers/firmware/arm_scmi/clock.c   |  10 +-
 drivers/firmware/arm_scmi/common.h  |  31 ++++-
 drivers/firmware/arm_scmi/driver.c  | 168 +++++++++++++++++++++++++++-
 drivers/firmware/arm_scmi/notify.c  |   3 +-
 drivers/firmware/arm_scmi/perf.c    |  10 +-
 drivers/firmware/arm_scmi/power.c   |  10 +-
 drivers/firmware/arm_scmi/reset.c   |  10 +-
 drivers/firmware/arm_scmi/sensors.c |  10 +-
 drivers/firmware/arm_scmi/system.c  |   8 +-
 include/linux/scmi_protocol.h       |   6 +-
 12 files changed, 299 insertions(+), 38 deletions(-)

diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 017e5d8bd869..d3b309e34ae5 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -318,7 +318,7 @@ static const struct scmi_event_ops base_event_ops = {
 	.fill_custom_report = scmi_base_fill_custom_report,
 };
 
-int scmi_base_protocol_init(struct scmi_handle *h)
+static int scmi_base_protocol_init(struct scmi_handle *h)
 {
 	int id, ret;
 	u8 *prot_imp;
@@ -365,3 +365,11 @@ int scmi_base_protocol_init(struct scmi_handle *h)
 
 	return 0;
 }
+
+static const struct scmi_protocol scmi_base = {
+	.id = SCMI_PROTOCOL_BASE,
+	.init = &scmi_base_protocol_init,
+	.ops = NULL,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 1377ec76a45d..0410cf77233c 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -16,7 +16,7 @@
 #include "common.h"
 
 static DEFINE_IDA(scmi_bus_id);
-static DEFINE_IDR(scmi_protocols);
+static DEFINE_IDR(scmi_available_protocols);
 static DEFINE_SPINLOCK(protocol_lock);
 
 static const struct scmi_device_id *
@@ -51,13 +51,29 @@ static int scmi_dev_match(struct device *dev, struct device_driver *drv)
 	return 0;
 }
 
+const struct scmi_protocol *scmi_get_protocol(int protocol_id)
+{
+	const struct scmi_protocol *proto;
+
+	proto = idr_find(&scmi_available_protocols, protocol_id);
+	if (!proto) {
+		pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
+		return NULL;
+	}
+
+	pr_debug("GOT SCMI Protocol 0x%x\n", protocol_id);
+
+	return proto;
+}
+
 static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
 {
-	scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
+	const struct scmi_protocol *proto;
 
-	if (unlikely(!fn))
+	proto = scmi_get_protocol(protocol_id);
+	if (!proto)
 		return -EINVAL;
-	return fn(handle);
+	return proto->init(handle);
 }
 
 static int scmi_protocol_dummy_init(struct scmi_handle *handle)
@@ -84,7 +100,7 @@ static int scmi_dev_probe(struct device *dev)
 		return ret;
 
 	/* Skip protocol initialisation for additional devices */
-	idr_replace(&scmi_protocols, &scmi_protocol_dummy_init,
+	idr_replace(&scmi_available_protocols, &scmi_protocol_dummy_init,
 		    scmi_dev->protocol_id);
 
 	return scmi_drv->probe(scmi_dev);
@@ -194,26 +210,45 @@ void scmi_set_handle(struct scmi_device *scmi_dev)
 	scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
 }
 
-int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn)
+int scmi_protocol_register(const struct scmi_protocol *proto)
 {
 	int ret;
 
+	if (!proto) {
+		pr_err("invalid protocol\n");
+		return -EINVAL;
+	}
+
+	if (!proto->init) {
+		pr_err("missing .init() for protocol 0x%x\n", proto->id);
+		return -EINVAL;
+	}
+
 	spin_lock(&protocol_lock);
-	ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1,
-			GFP_ATOMIC);
+	ret = idr_alloc(&scmi_available_protocols, (void *)proto,
+			proto->id, proto->id + 1, GFP_ATOMIC);
 	spin_unlock(&protocol_lock);
-	if (ret != protocol_id)
-		pr_err("unable to allocate SCMI idr slot, err %d\n", ret);
+	if (ret != proto->id) {
+		pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
+		       proto->id, ret);
+		return ret;
+	}
+
+	pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);
 
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(scmi_protocol_register);
 
-void scmi_protocol_unregister(int protocol_id)
+void scmi_protocol_unregister(const struct scmi_protocol *proto)
 {
 	spin_lock(&protocol_lock);
-	idr_remove(&scmi_protocols, protocol_id);
+	idr_remove(&scmi_available_protocols, proto->id);
 	spin_unlock(&protocol_lock);
+
+	pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
+
+	return;
 }
 EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
 
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 4645677d86f1..e8c84cff9922 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface (SCMI) Clock Protocol
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #include <linux/sort.h>
@@ -366,4 +366,10 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_CLOCK, clock)
+static const struct scmi_protocol scmi_clock = {
+	.id = SCMI_PROTOCOL_CLOCK,
+	.init = &scmi_clock_protocol_init,
+	.ops = &clk_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 65063fa948d4..b08a8ddbc22a 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -19,6 +19,8 @@
 
 #include <asm/unaligned.h>
 
+#define SCMI_MAX_PROTO		256
+
 #define PROTOCOL_REV_MINOR_MASK	GENMASK(15, 0)
 #define PROTOCOL_REV_MAJOR_MASK	GENMASK(31, 16)
 #define PROTOCOL_REV_MAJOR(x)	(u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
@@ -156,7 +158,22 @@ int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
 void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
 				     u8 *prot_imp);
 
-int scmi_base_protocol_init(struct scmi_handle *h);
+typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
+
+/**
+ * struct scmi_protocol  - Protocol descriptor
+ * @id: Protocol ID.
+ * @init: Mandatory protocol initialization function.
+ * @deinit: Optional protocol de-initialization function.
+ * @ops: Optional reference to the operations provided by the protocol and
+ *	 exposed in scmi_protocol.h.
+ */
+struct scmi_protocol {
+	const u8				id;
+	const scmi_prot_init_fn_t		init;
+	const scmi_prot_init_fn_t		deinit;
+	const void				*ops;
+};
 
 int __init scmi_bus_init(void);
 void __exit scmi_bus_exit(void);
@@ -164,6 +181,7 @@ void __exit scmi_bus_exit(void);
 #define DECLARE_SCMI_REGISTER_UNREGISTER(func)		\
 	int __init scmi_##func##_register(void);	\
 	void __exit scmi_##func##_unregister(void)
+DECLARE_SCMI_REGISTER_UNREGISTER(base);
 DECLARE_SCMI_REGISTER_UNREGISTER(clock);
 DECLARE_SCMI_REGISTER_UNREGISTER(perf);
 DECLARE_SCMI_REGISTER_UNREGISTER(power);
@@ -171,17 +189,22 @@ DECLARE_SCMI_REGISTER_UNREGISTER(reset);
 DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
 DECLARE_SCMI_REGISTER_UNREGISTER(system);
 
-#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
+#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
 int __init scmi_##name##_register(void) \
 { \
-	return scmi_protocol_register((id), &scmi_##name##_protocol_init); \
+	return scmi_protocol_register(&(proto)); \
 } \
 \
 void __exit scmi_##name##_unregister(void) \
 { \
-	scmi_protocol_unregister((id)); \
+	scmi_protocol_unregister(&(proto)); \
 }
 
+const struct scmi_protocol *scmi_get_protocol(int protocol_id);
+
+int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id);
+void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id);
+
 /* SCMI Transport */
 /**
  * struct scmi_chan_info - Structure representing a SCMI channel information
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 3dfd8b6a0ebf..beae8991422d 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -11,11 +11,12 @@
  * various power domain DVFS including the core/cluster, certain system
  * clocks configuration, thermal sensors and many others.
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #include <linux/bitmap.h>
 #include <linux/export.h>
+#include <linux/idr.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/ktime.h>
@@ -23,6 +24,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/processor.h>
+#include <linux/refcount.h>
 #include <linux/slab.h>
 
 #include "common.h"
@@ -68,6 +70,21 @@ struct scmi_xfers_info {
 	spinlock_t xfer_lock;
 };
 
+/**
+ * struct scmi_protocol_instance  - Describe an initialized protocol instance.
+ * @proto: A reference to the protocol descriptor.
+ * @gid: A reference for per-protocol devres management.
+ * @users: A refcount to track effective users of this protocol.
+ *
+ * Each protocol is initialized independently once for each SCMI platform in
+ * which is defined by DT and implemented by the SCMI server fw.
+ */
+struct scmi_protocol_instance {
+	const struct scmi_protocol	*proto;
+	void				*gid;
+	refcount_t			users;
+};
+
 /**
  * struct scmi_info - Structure representing a SCMI instance
  *
@@ -80,6 +97,10 @@ struct scmi_xfers_info {
  * @rx_minfo: Universal Receive Message management info
  * @tx_idr: IDR object to map protocol id to Tx channel info pointer
  * @rx_idr: IDR object to map protocol id to Rx channel info pointer
+ * @protocols: IDR for protocols' instance descriptors initialized for
+ *	       this SCMI instance: populated on protocol's first attempted
+ *	       usage.
+ * @protocols_mtx: A mutex to protect protocols instances initialization.
  * @protocols_imp: List of protocols implemented, currently maximum of
  *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
  * @node: List head
@@ -94,6 +115,9 @@ struct scmi_info {
 	struct scmi_xfers_info rx_minfo;
 	struct idr tx_idr;
 	struct idr rx_idr;
+	struct idr protocols;
+	/* Ensure mutual exclusive access to protocols instance array */
+	struct mutex protocols_mtx;
 	u8 *protocols_imp;
 	struct list_head node;
 	int users;
@@ -519,6 +543,127 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
 	return ret;
 }
 
+/**
+ * scmi_get_protocol_instance  - Protocol initialization helper.
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * In case the required protocol has never been requested before for this
+ * instance, allocate and initialize all the needed structures while handling
+ * resource allocation with a dedicated per-protocol devres subgroup.
+ *
+ * Return: A reference to an initialized protocol instance or error on failure.
+ */
+static struct scmi_protocol_instance * __must_check
+scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
+{
+	int ret = -ENOMEM;
+	void *gid;
+	struct scmi_protocol_instance *pi;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	mutex_lock(&info->protocols_mtx);
+	pi = idr_find(&info->protocols, protocol_id);
+
+	if (pi) {
+		refcount_inc(&pi->users);
+	} else {
+		const struct scmi_protocol *proto;
+
+		/* Fail if protocol not registered on bus */
+		proto = scmi_get_protocol(protocol_id);
+		if (!proto) {
+			ret = -ENODEV;
+			goto out;
+		}
+
+		/* Protocol specific devres group */
+		gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
+		if (!gid)
+			goto out;
+
+		pi = devm_kzalloc(handle->dev, sizeof(*pi), GFP_KERNEL);
+		if (!pi)
+			goto clean;
+
+		pi->gid = gid;
+		pi->proto = proto;
+		refcount_set(&pi->users, 1);
+		/* proto->init is assured NON NULL by scmi_protocol_register */
+		ret = pi->proto->init(handle);
+		if (ret)
+			goto clean;
+
+		ret = idr_alloc(&info->protocols, pi,
+				protocol_id, protocol_id + 1, GFP_KERNEL);
+		if (ret != protocol_id)
+			goto clean;
+
+		devres_close_group(handle->dev, pi->gid);
+		dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
+			protocol_id);
+	}
+	mutex_unlock(&info->protocols_mtx);
+
+	return pi;
+
+clean:
+	devres_release_group(handle->dev, gid);
+out:
+	mutex_unlock(&info->protocols_mtx);
+	return ERR_PTR(ret);
+}
+
+/**
+ * scmi_acquire_protocol  - Protocol acquire
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * Register a new user for the requested protocol on the specified SCMI
+ * platform instance, possibly triggering its initialization on first user.
+ *
+ * Return: 0 if protocol was acquired successfully.
+ */
+int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id)
+{
+	return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
+}
+
+/**
+ * scmi_release_protocol  - Protocol de-initialization helper.
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * Remove one user for the specified protocol and triggers de-initialization
+ * and resources de-allocation once the last user has gone.
+ */
+void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+	struct scmi_protocol_instance *pi;
+
+	mutex_lock(&info->protocols_mtx);
+	pi = idr_find(&info->protocols, protocol_id);
+	if (WARN_ON(!pi))
+		goto out;
+
+	if (refcount_dec_and_test(&pi->users)) {
+		void *gid = pi->gid;
+
+		if (pi->proto->deinit)
+			pi->proto->deinit(handle);
+
+		idr_remove(&info->protocols, protocol_id);
+
+		devres_release_group(handle->dev, gid);
+		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_handle *handle,
 				     u8 *prot_imp)
 {
@@ -785,6 +930,8 @@ static int scmi_probe(struct platform_device *pdev)
 	info->dev = dev;
 	info->desc = desc;
 	INIT_LIST_HEAD(&info->node);
+	idr_init(&info->protocols);
+	mutex_init(&info->protocols_mtx);
 
 	platform_set_drvdata(pdev, info);
 	idr_init(&info->tx_idr);
@@ -805,9 +952,14 @@ static int scmi_probe(struct platform_device *pdev)
 	if (scmi_notification_init(handle))
 		dev_err(dev, "SCMI Notifications NOT available.\n");
 
-	ret = scmi_base_protocol_init(handle);
+	/*
+	 * Trigger SCMI Base protocol initialization.
+	 * It's mandatory and won't be ever released/deinit until the
+	 * SCMI stack is shutdown/unloaded as a whole.
+	 */
+	ret = scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE);
 	if (ret) {
-		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
+		dev_err(dev, "unable to communicate with SCMI\n");
 		return ret;
 	}
 
@@ -859,6 +1011,10 @@ static int scmi_remove(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	mutex_lock(&info->protocols_mtx);
+	idr_destroy(&info->protocols);
+	mutex_unlock(&info->protocols_mtx);
+
 	/* Safe to free channels since no more users */
 	ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
 	idr_destroy(&info->tx_idr);
@@ -941,6 +1097,8 @@ static int __init scmi_driver_init(void)
 {
 	scmi_bus_init();
 
+	scmi_base_register();
+
 	scmi_clock_register();
 	scmi_perf_register();
 	scmi_power_register();
@@ -954,7 +1112,7 @@ subsys_initcall(scmi_driver_init);
 
 static void __exit scmi_driver_exit(void)
 {
-	scmi_bus_exit();
+	scmi_base_unregister();
 
 	scmi_clock_unregister();
 	scmi_perf_unregister();
@@ -963,6 +1121,8 @@ static void __exit scmi_driver_exit(void)
 	scmi_sensors_unregister();
 	scmi_system_unregister();
 
+	scmi_bus_exit();
+
 	platform_driver_unregister(&scmi_driver);
 }
 module_exit(scmi_driver_exit);
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index c24e427dce0d..eae58b2a92cc 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -91,10 +91,9 @@
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
+#include "common.h"
 #include "notify.h"
 
-#define SCMI_MAX_PROTO		256
-
 #define PROTO_ID_MASK		GENMASK(31, 24)
 #define EVT_ID_MASK		GENMASK(23, 16)
 #define SRC_ID_MASK		GENMASK(15, 0)
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 82fb3babff72..bba43f1a2ede 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface (SCMI) Performance Protocol
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
@@ -892,4 +892,10 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PERF, perf)
+static const struct scmi_protocol scmi_perf = {
+	.id = SCMI_PROTOCOL_PERF,
+	.init = &scmi_perf_protocol_init,
+	.ops = &perf_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(perf, scmi_perf)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 1f37258e9bee..b620c5fec855 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface (SCMI) Power Protocol
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
@@ -301,4 +301,10 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_POWER, power)
+static const struct scmi_protocol scmi_power = {
+	.id = SCMI_PROTOCOL_POWER,
+	.init = &scmi_power_protocol_init,
+	.ops = &power_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index a981a22cfe89..3283dde30641 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface (SCMI) Reset Protocol
  *
- * Copyright (C) 2019 ARM Ltd.
+ * Copyright (C) 2019-2020 ARM Ltd.
  */
 
 #define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
@@ -311,4 +311,10 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_RESET, reset)
+static const struct scmi_protocol scmi_reset = {
+	.id = SCMI_PROTOCOL_RESET,
+	.init = &scmi_reset_protocol_init,
+	.ops = &reset_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index b4232d611033..7ed1c4c6ec50 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface (SCMI) Sensor Protocol
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
@@ -367,4 +367,10 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_SENSOR, sensors)
+static const struct scmi_protocol scmi_sensors = {
+	.id = SCMI_PROTOCOL_SENSOR,
+	.init = &scmi_sensors_protocol_init,
+	.ops = &sensor_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(sensors, scmi_sensors)
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 283e12d5f24b..94a68bfc79d6 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -128,4 +128,10 @@ static int scmi_system_protocol_init(struct scmi_handle *handle)
 	return 0;
 }
 
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_SYSTEM, system)
+static const struct scmi_protocol scmi_system = {
+	.id = SCMI_PROTOCOL_SYSTEM,
+	.init = &scmi_system_protocol_init,
+	.ops = NULL,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 9cd312a1ff92..d726ddc3dcc0 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -376,9 +376,9 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
 #define module_scmi_driver(__scmi_driver)	\
 	module_driver(__scmi_driver, scmi_register, scmi_unregister)
 
-typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
-int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
-void scmi_protocol_unregister(int protocol_id);
+struct scmi_protocol;
+int scmi_protocol_register(const struct scmi_protocol *proto);
+void scmi_protocol_unregister(const struct scmi_protocol *proto);
 
 /* SCMI Notification API - Custom Event Reports */
 enum scmi_notification_events {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-11-04 16:16   ` Thara Gopinath
  2020-10-28 20:29 ` [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support Cristian Marussi
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Add basic protocol handles definitions and helpers support.
All protocols initialization code and SCMI drivers probing is still
performed using the handle based interface.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index b08a8ddbc22a..f0678be02a09 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
 		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
 void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
 			    struct scmi_xfer *xfer);
+
+struct scmi_xfer_ops;
+
+/**
+ * struct scmi_protocol_handle  - Reference to an initialized protocol instance
+ *
+ * @dev: A reference to the associated SCMI instance device (handle->dev).
+ * @xops: A reference to a struct holding refs to the core xfer operations that
+ *	  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.
+ *
+ * This structure represents a protocol initialized against specific SCMI
+ * instance and it will be used as follows:
+ * - as a parameter fed from the core to the protocol initialization code so
+ *   that it can access the core xfer operations to build and generate SCMI
+ *   messages exclusively for the specific underlying protocol instance.
+ * - as an opaque handle fed by an SCMI driver user when it tries to access
+ *   this protocol through its own protocol operations.
+ *   In this case this handle will be returned as an opaque object together
+ *   with the related protocol operations when the SCMI driver tries to access
+ *   the protocol.
+ */
+struct scmi_protocol_handle {
+	struct device *dev;
+	const struct scmi_xfer_ops *xops;
+	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
+	void *(*get_priv)(const struct scmi_protocol_handle *ph);
+};
+
+/**
+ * struct scmi_xfer_ops  - References to the core SCMI xfer operations.
+ * @version_get: Get this version protocol.
+ * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
+ * @reset_rx_to_maxsz: Reset rx size to max transport size.
+ * @do_xfer: Do the SCMI transfer.
+ * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
+ * @xfer_put: Free the xfer slot.
+ *
+ * Note that all this operations expect a protocol handle as first parameter;
+ * they then internally use it to infer the underlying protocol number: this
+ * way is not possible for a protocol implementation to forge messages for
+ * another protocol.
+ */
+struct scmi_xfer_ops {
+	int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
+	int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
+			     size_t tx_size, size_t rx_size,
+			     struct scmi_xfer **p);
+	void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
+				  struct scmi_xfer *xfer);
+	int (*do_xfer)(const struct scmi_protocol_handle *ph,
+		       struct scmi_xfer *xfer);
+	int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
+				     struct scmi_xfer *xfer);
+	void (*xfer_put)(const struct scmi_protocol_handle *ph,
+			 struct scmi_xfer *xfer);
+};
+
+struct scmi_revision_info *
+scmi_get_revision_area(const struct scmi_protocol_handle *ph);
 int scmi_handle_put(const struct scmi_handle *handle);
 struct scmi_handle *scmi_handle_get(struct device *dev);
 void scmi_set_handle(struct scmi_device *scmi_dev);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index beae8991422d..8ca04acb6abb 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -72,19 +72,28 @@ struct scmi_xfers_info {
 
 /**
  * struct scmi_protocol_instance  - Describe an initialized protocol instance.
+ * @handle: Reference to the SCMI handle associated to this protocol instance.
  * @proto: A reference to the protocol descriptor.
  * @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.
+ * @ph: An embedded protocol handle that will be passed down to protocol
+ *	initialization code to identify this instance.
  *
  * Each protocol is initialized independently once for each SCMI platform in
  * which is defined by DT and implemented by the SCMI server fw.
  */
 struct scmi_protocol_instance {
+	const struct scmi_handle	*handle;
 	const struct scmi_protocol	*proto;
 	void				*gid;
 	refcount_t			users;
+	void				*priv;
+	struct scmi_protocol_handle	ph;
 };
 
+#define ph_to_pi(h)	container_of(h, struct scmi_protocol_instance, ph)
+
 /**
  * struct scmi_info - Structure representing a SCMI instance
  *
@@ -543,6 +552,57 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
 	return ret;
 }
 
+/**
+ * scmi_set_protocol_priv  - Set protocol specific data at init time
+ *
+ * @ph: A reference to the protocol handle.
+ * @priv: The private data to set.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
+				  void *priv)
+{
+	struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+	pi->priv = priv;
+
+	return 0;
+}
+
+/**
+ * scmi_get_protocol_priv  - Set protocol specific data at init time
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * Return: Protocol private data if any was set.
+ */
+static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
+{
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+	return pi->priv;
+}
+
+/**
+ * scmi_get_revision_area  - Retrieve version memory area.
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * A helper to grab the version memory area reference during SCMI Base protocol
+ * initialization.
+ *
+ * Return: A reference to the version memory area associated to the SCMI
+ *	   instance underlying this protocol handle.
+ */
+struct scmi_revision_info *
+scmi_get_revision_area(const struct scmi_protocol_handle *ph)
+{
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+	return pi->handle->version;
+}
+
 /**
  * scmi_get_protocol_instance  - Protocol initialization helper.
  * @handle: A reference to the SCMI platform instance.
@@ -588,6 +648,10 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
 
 		pi->gid = gid;
 		pi->proto = proto;
+		pi->handle = handle;
+		pi->ph.dev = handle->dev;
+		pi->ph.set_priv = scmi_set_protocol_priv;
+		pi->ph.get_priv = scmi_get_protocol_priv;
 		refcount_set(&pi->users, 1);
 		/* proto->init is assured NON NULL by scmi_protocol_register */
 		ret = pi->proto->init(handle);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-11-04 16:16   ` Thara Gopinath
  2020-10-28 20:29 ` [PATCH v2 4/8] firmware: arm_scmi: make notifications aware of protocol usage Cristian Marussi
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Expose a new generic get/put protocols API based on protocol handles;
provide also a devres managed version also for notifications.
All SCMI drivers still keep using the old handle based interface.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 126 +++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/notify.c | 123 ++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  34 +++++++-
 3 files changed, 282 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 8ca04acb6abb..4d86aafbf465 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -15,6 +15,7 @@
  */
 
 #include <linux/bitmap.h>
+#include <linux/device.h>
 #include <linux/export.h>
 #include <linux/idr.h>
 #include <linux/io.h>
@@ -728,6 +729,38 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
 	mutex_unlock(&info->protocols_mtx);
 }
 
+/**
+ * scmi_get_protocol_operations  - Get protocol operations
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ * @ph: A pointer reference used to pass back the associated protocol handle.
+ *
+ * Get hold of a protocol accounting for its usage, eventually triggering its
+ * initialization, and returning the protocol specific operations and related
+ * protocol handle which will be used as first argument in most of the protocols
+ * operations methods.
+ *
+ * Return: A reference to the requested protocol operations or error.
+ *	   Must be checked for errors by caller.
+ */
+static const void __must_check *
+scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
+			     struct scmi_protocol_handle **ph)
+{
+	struct scmi_protocol_instance *pi;
+
+	if (!ph)
+		return ERR_PTR(-EINVAL);
+
+	pi = scmi_get_protocol_instance(handle, protocol_id);
+	if (IS_ERR(pi))
+		return pi;
+
+	*ph = &pi->ph;
+
+	return pi->proto->ops;
+}
+
 void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
 				     u8 *prot_imp)
 {
@@ -751,6 +784,95 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
 	return false;
 }
 
+struct scmi_protocol_devres {
+	struct scmi_handle *handle;
+	u8 protocol_id;
+};
+
+static void scmi_devm_release_protocol(struct device *dev, void *res)
+{
+	struct scmi_protocol_devres *dres = res;
+
+	scmi_release_protocol(dres->handle, dres->protocol_id);
+}
+
+/**
+ * scmi_devm_get_protocol_ops  - Devres managed get protocol operations
+ * @sdev: A reference to an scmi_device whose embedded struct device is to
+ *	  be used for devres accounting.
+ * @protocol_id: The protocol being requested.
+ * @ph: A pointer reference used to pass back the associated protocol handle.
+ *
+ * Get hold of a protocol accounting for its usage, eventually triggering its
+ * initialization, and returning the protocol specific operations and related
+ * protocol handle which will be used as first argument in most of the
+ * protocols operations methods.
+ * Being a devres based managed method, protocol hold will be automatically
+ * released, and possibly de-initialized on last user, once the SCMI driver
+ * owning the scmi_device is unbound from it.
+ *
+ * Return: A reference to the requested protocol operations or error.
+ *	   Must be checked for errors by caller.
+ */
+static const void __must_check *
+scmi_devm_get_protocol_ops(struct scmi_device *sdev, u8 protocol_id,
+			   struct scmi_protocol_handle **ph)
+{
+	struct scmi_protocol_instance *pi;
+	struct scmi_protocol_devres *dres;
+	struct scmi_handle *handle = sdev->handle;
+
+	if (!ph)
+		return ERR_PTR(-EINVAL);
+
+	dres = devres_alloc(scmi_devm_release_protocol,
+			    sizeof(*dres), GFP_KERNEL);
+	if (!dres)
+		return ERR_PTR(-ENOMEM);
+
+	pi = scmi_get_protocol_instance(handle, protocol_id);
+	if (IS_ERR(pi)) {
+		devres_free(dres);
+		return pi;
+	}
+
+	dres->handle = handle;
+	dres->protocol_id = protocol_id;
+	devres_add(&sdev->dev, dres);
+
+	*ph = &pi->ph;
+
+	return pi->proto->ops;
+}
+
+static int scmi_devm_protocol_match(struct device *dev, void *res, void *data)
+{
+	struct scmi_protocol_devres *dres = res;
+
+	if (WARN_ON(!dres || !data))
+		return 0;
+
+	return dres->protocol_id == *((u8 *)data);
+}
+
+/**
+ * scmi_devm_put_protocol_ops  - Devres managed put protocol operations
+ * @sdev: A reference to an scmi_device whose embedded struct device is to
+ *	  be used for devres accounting.
+ * @protocol_id: The protocol being requested.
+ *
+ * Explicitly release a protocol hold previously obtained calling the above
+ * @scmi_devm_get_protocol_ops.
+ */
+static void scmi_devm_put_protocol_ops(struct scmi_device *sdev, u8 protocol_id)
+{
+	int ret;
+
+	ret = devres_release(&sdev->dev, scmi_devm_release_protocol,
+			     scmi_devm_protocol_match, &protocol_id);
+	WARN_ON(ret);
+}
+
 /**
  * scmi_handle_get() - Get the SCMI handle for a device
  *
@@ -1004,6 +1126,10 @@ static int scmi_probe(struct platform_device *pdev)
 	handle = &info->handle;
 	handle->dev = info->dev;
 	handle->version = &info->version;
+	handle->devm_get_ops = scmi_devm_get_protocol_ops;
+	handle->devm_put_ops = scmi_devm_put_protocol_ops;
+	handle->get_ops = scmi_get_protocol_operations;
+	handle->put_ops = scmi_release_protocol;
 
 	ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
 	if (ret)
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index eae58b2a92cc..64d43e425644 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -1370,6 +1370,127 @@ static int scmi_unregister_notifier(const struct scmi_handle *handle,
 	return 0;
 }
 
+struct scmi_notifier_devres {
+	const struct scmi_handle *handle;
+	u8 proto_id;
+	u8 evt_id;
+	u32 __src_id;
+	u32 *src_id;
+	struct notifier_block *nb;
+};
+
+static void scmi_devm_release_notifier(struct device *dev, void *res)
+{
+	struct scmi_notifier_devres *dres = res;
+
+	scmi_unregister_notifier(dres->handle, dres->proto_id, dres->evt_id,
+				 dres->src_id, dres->nb);
+}
+
+/**
+ * scmi_devm_register_notifier()  - Managed registration of a notifier_block
+ * for an event
+ * @sdev: A reference to an scmi_device whose embedded struct device is to
+ *	  be used for devres accounting.
+ * @proto_id: Protocol ID
+ * @evt_id: Event ID
+ * @src_id: Source ID, when NULL register for events coming form ALL possible
+ *	    sources
+ * @nb: A standard notifier block to register for the specified event
+ *
+ * Generic devres managed helper to register a notifier_block against a
+ * protocol event.
+ */
+static int scmi_devm_register_notifier(struct scmi_device *sdev,
+				       u8 proto_id, u8 evt_id, u32 *src_id,
+				       struct notifier_block *nb)
+{
+	int ret;
+	struct scmi_notifier_devres *dres;
+
+	dres = devres_alloc(scmi_devm_release_notifier,
+			    sizeof(*dres), GFP_KERNEL);
+	if (!dres)
+		return -ENOMEM;
+
+	ret = scmi_register_notifier(sdev->handle, proto_id,
+				     evt_id, src_id, nb);
+	if (ret) {
+		devres_free(dres);
+		return ret;
+	}
+
+	dres->handle = sdev->handle;
+	dres->proto_id = proto_id;
+	dres->evt_id = evt_id;
+	dres->nb = nb;
+	if (src_id) {
+		dres->__src_id = *src_id;
+		dres->src_id = &dres->__src_id;
+	} else {
+		dres->src_id = NULL;
+	}
+	devres_add(&sdev->dev, dres);
+
+	return ret;
+}
+
+static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
+{
+	struct scmi_notifier_devres *dres = res;
+	struct scmi_notifier_devres *xres = data;
+
+	if (WARN_ON(!dres || !xres))
+		return 0;
+
+	return dres->proto_id == xres->proto_id &&
+		dres->evt_id == xres->evt_id &&
+		dres->nb == xres->nb &&
+		((!dres->src_id && !xres->src_id) ||
+		  (dres->src_id && xres->src_id &&
+		   dres->__src_id == xres->__src_id));
+}
+
+/**
+ * scmi_devm_unregister_notifier()  - Managed un-registration of a
+ * notifier_block for an event
+ * @sdev: A reference to an scmi_device whose embedded struct device is to
+ *	  be used for devres accounting.
+ * @proto_id: Protocol ID
+ * @evt_id: Event ID
+ * @src_id: Source ID, when NULL register for events coming form ALL possible
+ *	    sources
+ * @nb: A standard notifier block to register for the specified event
+ *
+ * Generic devres managed helper to explicitly un-register a notifier_block
+ * against a protocol event, which was previously registered using the above
+ * @scmi_devm_register_notifier.
+ */
+static int scmi_devm_unregister_notifier(struct scmi_device *sdev,
+					 u8 proto_id, u8 evt_id, u32 *src_id,
+					 struct notifier_block *nb)
+{
+	int ret;
+	struct scmi_notifier_devres dres;
+
+	dres.handle = sdev->handle;
+	dres.proto_id = proto_id;
+	dres.evt_id = evt_id;
+	if (src_id) {
+		dres.__src_id = *src_id;
+		dres.src_id = &dres.__src_id;
+	} else {
+		dres.src_id = NULL;
+	}
+
+	ret = devres_release(&sdev->dev, scmi_devm_release_notifier,
+			     scmi_devm_notifier_match, &dres);
+
+	WARN_ON(ret);
+
+	return ret;
+}
+
 /**
  * scmi_protocols_late_init()  - Worker for late initialization
  * @work: The work item to use associated to the proper SCMI instance
@@ -1427,6 +1548,8 @@ static void scmi_protocols_late_init(struct work_struct *work)
  * directly from an scmi_driver to register its own notifiers.
  */
 static const struct scmi_notify_ops notify_ops = {
+	.devm_register_event_notifier = scmi_devm_register_notifier,
+	.devm_unregister_event_notifier = scmi_devm_unregister_notifier,
 	.register_event_notifier = scmi_register_notifier,
 	.unregister_event_notifier = scmi_unregister_notifier,
 };
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index d726ddc3dcc0..d95e43c3ea08 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -56,6 +56,8 @@ struct scmi_clock_info {
 };
 
 struct scmi_handle;
+struct scmi_device;
+struct scmi_protocol_handle;
 
 /**
  * struct scmi_clk_ops - represents the various operations provided
@@ -212,6 +214,10 @@ struct scmi_reset_ops {
 /**
  * struct scmi_notify_ops  - represents notifications' operations provided by
  * SCMI core
+ * @devm_register_event_notifier: Managed registration of a notifier_block for
+ *				  the requested event
+ * @devm_unregister_event_notifier: Managed unregistration of a notifier_block
+ *				    for the requested event
  * @register_event_notifier: Register a notifier_block for the requested event
  * @unregister_event_notifier: Unregister a notifier_block for the requested
  *			       event
@@ -221,7 +227,9 @@ struct scmi_reset_ops {
  * tuple: (proto_id, evt_id, src_id) using the provided register/unregister
  * interface where:
  *
- * @handle: The handle identifying the platform instance to use
+ * @sdev: The scmi_device to use when calling the devres managed ops devm_
+ * @handle: The handle identifying the platform instance to use, when not
+ *	    calling the managed ops devm_
  * @proto_id: The protocol ID as in SCMI Specification
  * @evt_id: The message ID of the desired event as in SCMI Specification
  * @src_id: A pointer to the desired source ID if different sources are
@@ -244,6 +252,13 @@ struct scmi_reset_ops {
  * @report: A custom struct describing the specific event delivered
  */
 struct scmi_notify_ops {
+	int (*devm_register_event_notifier)(struct scmi_device *sdev,
+					    u8 proto_id, u8 evt_id, u32 *src_id,
+					    struct notifier_block *nb);
+	int (*devm_unregister_event_notifier)(struct scmi_device *sdev,
+					      u8 proto_id, u8 evt_id,
+					      u32 *src_id,
+					      struct notifier_block *nb);
 	int (*register_event_notifier)(const struct scmi_handle *handle,
 				       u8 proto_id, u8 evt_id, u32 *src_id,
 				       struct notifier_block *nb);
@@ -262,6 +277,12 @@ struct scmi_notify_ops {
  * @clk_ops: pointer to set of clock protocol operations
  * @sensor_ops: pointer to set of sensor protocol operations
  * @reset_ops: pointer to set of reset protocol operations
+ * @devm_get_ops: devres managed method to acquire a protocol and get specific
+ *		  operations and a dedicated protocol handler
+ * @devm_put_ops: devres managed method to release a protocol
+ * @get_ops: method to acquire a protocol and get specific operations and a
+ *	     dedicated protocol handler
+ * @put_ops: method to release a protocol
  * @notify_ops: pointer to set of notifications related operations
  * @perf_priv: pointer to private data structure specific to performance
  *	protocol(for internal use only)
@@ -284,6 +305,17 @@ struct scmi_handle {
 	const struct scmi_power_ops *power_ops;
 	const struct scmi_sensor_ops *sensor_ops;
 	const struct scmi_reset_ops *reset_ops;
+
+	const void __must_check *
+		(*devm_get_ops)(struct scmi_device *sdev, u8 proto,
+				struct scmi_protocol_handle **ph);
+	void (*devm_put_ops)(struct scmi_device *sdev, u8 proto);
+
+	const void __must_check *
+		(*get_ops)(struct scmi_handle *handle, u8 proto,
+			   struct scmi_protocol_handle **ph);
+	void (*put_ops)(struct scmi_handle *handle, u8 proto);
+
 	const struct scmi_notify_ops *notify_ops;
 	/* for protocol internal use */
 	void *perf_priv;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 4/8] firmware: arm_scmi: make notifications aware of protocol usage
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (2 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 5/8] firmware: arm_scmi: refactor events registration Cristian Marussi
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Account any active notifications registered notifier against the proper
related protocol; do not consider pending venet handlers, only active event
handlers will concur to protocol usage accounting.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/notify.c | 50 +++++++++++++++++++++++++-----
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 64d43e425644..1ac73f51374b 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -367,7 +367,7 @@ static struct scmi_event_handler *
 scmi_get_active_handler(struct scmi_notify_instance *ni, u32 evt_key);
 static void scmi_put_active_handler(struct scmi_notify_instance *ni,
 				    struct scmi_event_handler *hndl);
-static void scmi_put_handler_unlocked(struct scmi_notify_instance *ni,
+static bool scmi_put_handler_unlocked(struct scmi_notify_instance *ni,
 				      struct scmi_event_handler *hndl);
 
 /**
@@ -899,9 +899,21 @@ static inline int scmi_bind_event_handler(struct scmi_notify_instance *ni,
 	if (!r_evt)
 		return -EINVAL;
 
-	/* Remove from pending and insert into registered */
+	/*
+	 * Remove from pending and insert into registered while getting hold
+	 * of protocol instance.
+	 */
 	hash_del(&hndl->hash);
+	/*
+	 * Acquire protocols only for NON pending handlers, so as NOT to trigger
+	 * protocol initialization when a notifier is registered against a still
+	 * not registered protocol, since it would make little sense to force init
+	 * protocols for which still no SCMI driver user exists: they wouldn't
+	 * emit any event anyway till some SCMI driver starts using it.
+	 */
+	scmi_acquire_protocol(ni->handle, KEY_XTRACT_PROTO_ID(hndl->key));
 	hndl->r_evt = r_evt;
+
 	mutex_lock(&r_evt->proto->registered_mtx);
 	hash_add(r_evt->proto->registered_events_handlers,
 		 &hndl->hash, hndl->key);
@@ -1192,41 +1204,65 @@ static int scmi_disable_events(struct scmi_event_handler *hndl)
  * * unregister and free the handler itself
  *
  * Context: Assumes all the proper locking has been managed by the caller.
+ *
+ * Return: True if handler was freed (users dropped to zero)
  */
-static void scmi_put_handler_unlocked(struct scmi_notify_instance *ni,
+static bool scmi_put_handler_unlocked(struct scmi_notify_instance *ni,
 				      struct scmi_event_handler *hndl)
 {
+	bool freed = false;
+
 	if (refcount_dec_and_test(&hndl->users)) {
 		if (!IS_HNDL_PENDING(hndl))
 			scmi_disable_events(hndl);
 		scmi_free_event_handler(hndl);
+		freed = true;
 	}
+
+	return freed;
 }
 
 static void scmi_put_handler(struct scmi_notify_instance *ni,
 			     struct scmi_event_handler *hndl)
 {
+	bool freed;
+	u8 protocol_id;
 	struct scmi_registered_event *r_evt = hndl->r_evt;
 
 	mutex_lock(&ni->pending_mtx);
-	if (r_evt)
+	if (r_evt) {
+		protocol_id = r_evt->proto->id;
 		mutex_lock(&r_evt->proto->registered_mtx);
+	}
 
-	scmi_put_handler_unlocked(ni, hndl);
+	freed = scmi_put_handler_unlocked(ni, hndl);
 
-	if (r_evt)
+	if (r_evt) {
 		mutex_unlock(&r_evt->proto->registered_mtx);
+		/*
+		 * Only registered handler acquired protocol; must be here
+		 * released only AFTER unlocking registered_mtx, since
+		 * releasing a protocol can trigger its de-initialization
+		 * (ie. including r_evt and registered_mtx)
+		 */
+		if (freed)
+			scmi_release_protocol(ni->handle, protocol_id);
+	}
 	mutex_unlock(&ni->pending_mtx);
 }
 
 static void scmi_put_active_handler(struct scmi_notify_instance *ni,
 				    struct scmi_event_handler *hndl)
 {
+	bool freed;
 	struct scmi_registered_event *r_evt = hndl->r_evt;
+	u8 protocol_id = r_evt->proto->id;
 
 	mutex_lock(&r_evt->proto->registered_mtx);
-	scmi_put_handler_unlocked(ni, hndl);
+	freed = scmi_put_handler_unlocked(ni, hndl);
 	mutex_unlock(&r_evt->proto->registered_mtx);
+	if (freed)
+		scmi_release_protocol(ni->handle, protocol_id);
 }
 
 /**
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 5/8] firmware: arm_scmi: refactor events registration
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (3 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 4/8] firmware: arm_scmi: make notifications aware of protocol usage Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 6/8] firmware: arm_scmi: port all protocols and drivers to the new API Cristian Marussi
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Refactor scmi_register_protocol_events() and move its invocation inside
the centralized initialization process triggered by get_ops() and friends.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/base.c    | 15 +++---
 drivers/firmware/arm_scmi/common.h  |  2 +
 drivers/firmware/arm_scmi/driver.c  |  7 +++
 drivers/firmware/arm_scmi/notify.c  | 77 ++++++++++++++++++++---------
 drivers/firmware/arm_scmi/notify.h  | 33 +++++++++++--
 drivers/firmware/arm_scmi/perf.c    | 25 +++++++---
 drivers/firmware/arm_scmi/power.c   | 25 +++++++---
 drivers/firmware/arm_scmi/reset.c   | 25 +++++++---
 drivers/firmware/arm_scmi/sensors.c | 22 ++++++---
 drivers/firmware/arm_scmi/system.c  | 16 +++---
 10 files changed, 181 insertions(+), 66 deletions(-)

diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index d3b309e34ae5..634a6ec99464 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -318,6 +318,14 @@ static const struct scmi_event_ops base_event_ops = {
 	.fill_custom_report = scmi_base_fill_custom_report,
 };
 
+static const struct scmi_protocol_events base_protocol_events = {
+	.queue_sz = 4 * SCMI_PROTO_QUEUE_SZ,
+	.ops = &base_event_ops,
+	.evts = base_events,
+	.num_events = ARRAY_SIZE(base_events),
+	.num_sources = SCMI_BASE_NUM_SOURCES,
+};
+
 static int scmi_base_protocol_init(struct scmi_handle *h)
 {
 	int id, ret;
@@ -352,12 +360,6 @@ static int scmi_base_protocol_init(struct scmi_handle *h)
 	dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
 		rev->num_agents);
 
-	scmi_register_protocol_events(handle, SCMI_PROTOCOL_BASE,
-				      (4 * SCMI_PROTO_QUEUE_SZ),
-				      &base_event_ops, base_events,
-				      ARRAY_SIZE(base_events),
-				      SCMI_BASE_NUM_SOURCES);
-
 	for (id = 0; id < rev->num_agents; id++) {
 		scmi_base_discover_agent_get(handle, id, name);
 		dev_dbg(dev, "Agent %d: %s\n", id, name);
@@ -370,6 +372,7 @@ static const struct scmi_protocol scmi_base = {
 	.id = SCMI_PROTOCOL_BASE,
 	.init = &scmi_base_protocol_init,
 	.ops = NULL,
+	.events = &base_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index f0678be02a09..60ef6cfb81bf 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -228,12 +228,14 @@ typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
  * @deinit: Optional protocol de-initialization function.
  * @ops: Optional reference to the operations provided by the protocol and
  *	 exposed in scmi_protocol.h.
+ * @events: An optional reference to the events supported by this protocol.
  */
 struct scmi_protocol {
 	const u8				id;
 	const scmi_prot_init_fn_t		init;
 	const scmi_prot_init_fn_t		deinit;
 	const void				*ops;
+	const struct scmi_protocol_events	*events;
 };
 
 int __init scmi_bus_init(void);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 4d86aafbf465..97f22278c5b6 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -664,6 +664,10 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
 		if (ret != protocol_id)
 			goto clean;
 
+		if (pi->proto->events)
+			scmi_register_protocol_events(handle, pi->proto->id,
+						      pi->proto->events);
+
 		devres_close_group(handle->dev, pi->gid);
 		dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
 			protocol_id);
@@ -715,6 +719,9 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
 	if (refcount_dec_and_test(&pi->users)) {
 		void *gid = pi->gid;
 
+		if (pi->proto->events)
+			scmi_deregister_protocol_events(handle, protocol_id);
+
 		if (pi->proto->deinit)
 			pi->proto->deinit(handle);
 
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 1ac73f51374b..504f5b5d557e 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -731,14 +731,9 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
 /**
  * scmi_register_protocol_events()  - Register Protocol Events with the core
  * @handle: The handle identifying the platform instance against which the
- *	    the protocol's events are registered
+ *	    protocol's events are registered
  * @proto_id: Protocol ID
- * @queue_sz: Size in bytes of the associated queue to be allocated
- * @ops: Protocol specific event-related operations
- * @evt: Event descriptor array
- * @num_events: Number of events in @evt array
- * @num_sources: Number of possible sources for this protocol on this
- *		 platform.
+ * @ee: A structure describing the events supported by this protocol.
  *
  * Used by SCMI Protocols initialization code to register with the notification
  * core the list of supported events and their descriptors: takes care to
@@ -747,18 +742,18 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
  *
  * Return: 0 on Success
  */
-int scmi_register_protocol_events(const struct scmi_handle *handle,
-				  u8 proto_id, size_t queue_sz,
-				  const struct scmi_event_ops *ops,
-				  const struct scmi_event *evt, int num_events,
-				  int num_sources)
+int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
+				  const struct scmi_protocol_events *ee)
 {
 	int i;
+	unsigned int num_sources;
 	size_t payld_sz = 0;
 	struct scmi_registered_events_desc *pd;
 	struct scmi_notify_instance *ni;
+	const struct scmi_event *evt;
 
-	if (!ops || !evt)
+	if (!ee || !ee->ops || !ee->evts ||
+	    (!ee->num_sources && !ee->ops->get_num_sources))
 		return -EINVAL;
 
 	/* Ensure notify_priv is updated */
@@ -767,20 +762,29 @@ int scmi_register_protocol_events(const struct scmi_handle *handle,
 		return -ENOMEM;
 	ni = handle->notify_priv;
 
-	/* Attach to the notification main devres group */
-	if (!devres_open_group(ni->handle->dev, ni->gid, GFP_KERNEL))
-		return -ENOMEM;
+	/* num_sources cannot be <= 0 */
+	if (ee->num_sources) {
+		num_sources = ee->num_sources;
+	} else {
+		int nsrc = ee->ops->get_num_sources(handle);
+
+		if (nsrc <= 0)
+			return -EINVAL;
+		num_sources = nsrc;
+	}
 
-	for (i = 0; i < num_events; i++)
+	evt = ee->evts;
+	for (i = 0; i < ee->num_events; i++)
 		payld_sz = max_t(size_t, payld_sz, evt[i].max_payld_sz);
 	payld_sz += sizeof(struct scmi_event_header);
 
-	pd = scmi_allocate_registered_events_desc(ni, proto_id, queue_sz,
-						  payld_sz, num_events, ops);
+	pd = scmi_allocate_registered_events_desc(ni, proto_id, ee->queue_sz,
+						  payld_sz, ee->num_events,
+						  ee->ops);
 	if (IS_ERR(pd))
 		goto err;
 
-	for (i = 0; i < num_events; i++, evt++) {
+	for (i = 0; i < ee->num_events; i++, evt++) {
 		struct scmi_registered_event *r_evt;
 
 		r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt),
@@ -814,8 +818,6 @@ int scmi_register_protocol_events(const struct scmi_handle *handle,
 	/* Ensure protocols are updated */
 	smp_wmb();
 
-	devres_close_group(ni->handle->dev, ni->gid);
-
 	/*
 	 * Finalize any pending events' handler which could have been waiting
 	 * for this protocol's events registration.
@@ -826,12 +828,39 @@ int scmi_register_protocol_events(const struct scmi_handle *handle,
 
 err:
 	dev_warn(handle->dev, "Proto:%X - Registration Failed !\n", proto_id);
-	/* A failing protocol registration does not trigger full failure */
-	devres_close_group(ni->handle->dev, ni->gid);
 
 	return -ENOMEM;
 }
 
+/**
+ * scmi_deregister_protocol_events  - Deregister protocol events with the core
+ * @handle: The handle identifying the platform instance against which the
+ *	    protocol's events are registered
+ * @proto_id: Protocol ID
+ */
+void scmi_deregister_protocol_events(const struct scmi_handle *handle,
+				     u8 proto_id)
+{
+	struct scmi_notify_instance *ni;
+	struct scmi_registered_events_desc *pd;
+
+	/* Ensure notify_priv is updated */
+	smp_rmb();
+	if (!handle->notify_priv)
+		return;
+
+	ni = handle->notify_priv;
+	pd = ni->registered_protocols[proto_id];
+	if (!pd)
+		return;
+
+	ni->registered_protocols[proto_id] = NULL;
+	/* Ensure protocols are updated */
+	smp_wmb();
+
+	cancel_work_sync(&pd->equeue.notify_work);
+}
+
 /**
  * scmi_allocate_event_handler()  - Allocate Event handler
  * @ni: A reference to the notification instance to use
diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h
index 3485f20fa70e..97ddfe55d773 100644
--- a/drivers/firmware/arm_scmi/notify.h
+++ b/drivers/firmware/arm_scmi/notify.h
@@ -31,8 +31,12 @@ struct scmi_event {
 	size_t	max_report_sz;
 };
 
+struct scmi_protocol_handle;
+
 /**
  * struct scmi_event_ops  - Protocol helpers called by the notification core.
+ * @get_num_sources: Returns the number of possible events' sources for this
+ *		     protocol
  * @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
  *			using the proper custom protocol commands.
  *			Return 0 on Success
@@ -46,6 +50,7 @@ struct scmi_event {
  *	    process context.
  */
 struct scmi_event_ops {
+	int (*get_num_sources)(const struct scmi_handle *handle);
 	int (*set_notify_enabled)(const struct scmi_handle *handle,
 				  u8 evt_id, u32 src_id, bool enabled);
 	void *(*fill_custom_report)(const struct scmi_handle *handle,
@@ -54,14 +59,32 @@ struct scmi_event_ops {
 				    void *report, u32 *src_id);
 };
 
+/**
+ * struct scmi_protocol_events  - Per-protocol description of available events
+ * @queue_sz: Size in bytes of the per-protocol queue to use.
+ * @ops: Array of protocol-specific events operations.
+ * @evts: Array of supported protocol's events.
+ * @num_events: Number of supported protocol's events described in @evts.
+ * @num_sources: Number of protocol's sources, should be greater than 0; if not
+ *		 available at compile time, it will be provided at run-time via
+ *		 @get_num_sources.
+ */
+struct scmi_protocol_events {
+	size_t				queue_sz;
+	const struct scmi_event_ops	*ops;
+	const struct scmi_event		*evts;
+	unsigned int			num_events;
+	unsigned int			num_sources;
+};
+
 int scmi_notification_init(struct scmi_handle *handle);
 void scmi_notification_exit(struct scmi_handle *handle);
 
-int scmi_register_protocol_events(const struct scmi_handle *handle,
-				  u8 proto_id, size_t queue_sz,
-				  const struct scmi_event_ops *ops,
-				  const struct scmi_event *evt, int num_events,
-				  int num_sources);
+struct scmi_protocol_handle;
+int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
+				  const struct scmi_protocol_events *ee);
+void scmi_deregister_protocol_events(const struct scmi_handle *handle,
+				     u8 proto_id);
 int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
 		const void *buf, size_t len, ktime_t ts);
 
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index bba43f1a2ede..298565cd4d6d 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -829,6 +829,16 @@ static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle,
 	return rep;
 }
 
+static int scmi_perf_get_num_sources(const struct scmi_handle *handle)
+{
+	struct scmi_perf_info *pi = handle->perf_priv;
+
+	if (!pi)
+		return -EINVAL;
+
+	return pi->num_domains;
+}
+
 static const struct scmi_event perf_events[] = {
 	{
 		.id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
@@ -843,10 +853,18 @@ static const struct scmi_event perf_events[] = {
 };
 
 static const struct scmi_event_ops perf_event_ops = {
+	.get_num_sources = scmi_perf_get_num_sources,
 	.set_notify_enabled = scmi_perf_set_notify_enabled,
 	.fill_custom_report = scmi_perf_fill_custom_report,
 };
 
+static const struct scmi_protocol_events perf_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &perf_event_ops,
+	.evts = perf_events,
+	.num_events = ARRAY_SIZE(perf_events),
+};
+
 static int scmi_perf_protocol_init(struct scmi_handle *handle)
 {
 	int domain;
@@ -879,12 +897,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
 			scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
 	}
 
-	scmi_register_protocol_events(handle,
-				      SCMI_PROTOCOL_PERF, SCMI_PROTO_QUEUE_SZ,
-				      &perf_event_ops, perf_events,
-				      ARRAY_SIZE(perf_events),
-				      pinfo->num_domains);
-
 	pinfo->version = version;
 	handle->perf_ops = &perf_ops;
 	handle->perf_priv = pinfo;
@@ -896,6 +908,7 @@ static const struct scmi_protocol scmi_perf = {
 	.id = SCMI_PROTOCOL_PERF,
 	.init = &scmi_perf_protocol_init,
 	.ops = &perf_ops,
+	.events = &perf_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(perf, scmi_perf)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index b620c5fec855..6db02dc547a7 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -246,6 +246,16 @@ static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
+static int scmi_power_get_num_sources(const struct scmi_handle *handle)
+{
+	struct scmi_power_info *pinfo = handle->power_priv;
+
+	if (!pinfo)
+		return -EINVAL;
+
+	return pinfo->num_domains;
+}
+
 static const struct scmi_event power_events[] = {
 	{
 		.id = SCMI_EVENT_POWER_STATE_CHANGED,
@@ -256,10 +266,18 @@ static const struct scmi_event power_events[] = {
 };
 
 static const struct scmi_event_ops power_event_ops = {
+	.get_num_sources = scmi_power_get_num_sources,
 	.set_notify_enabled = scmi_power_set_notify_enabled,
 	.fill_custom_report = scmi_power_fill_custom_report,
 };
 
+static const struct scmi_protocol_events power_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &power_event_ops,
+	.evts = power_events,
+	.num_events = ARRAY_SIZE(power_events),
+};
+
 static int scmi_power_protocol_init(struct scmi_handle *handle)
 {
 	int domain;
@@ -288,12 +306,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
 		scmi_power_domain_attributes_get(handle, domain, dom);
 	}
 
-	scmi_register_protocol_events(handle,
-				      SCMI_PROTOCOL_POWER, SCMI_PROTO_QUEUE_SZ,
-				      &power_event_ops, power_events,
-				      ARRAY_SIZE(power_events),
-				      pinfo->num_domains);
-
 	pinfo->version = version;
 	handle->power_ops = &power_ops;
 	handle->power_priv = pinfo;
@@ -305,6 +317,7 @@ static const struct scmi_protocol scmi_power = {
 	.id = SCMI_PROTOCOL_POWER,
 	.init = &scmi_power_protocol_init,
 	.ops = &power_ops,
+	.events = &power_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index 3283dde30641..7102cfeb2397 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -257,6 +257,16 @@ static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
+static int scmi_reset_get_num_sources(const struct scmi_handle *handle)
+{
+	struct scmi_reset_info *pinfo = handle->reset_priv;
+
+	if (!pinfo)
+		return -EINVAL;
+
+	return pinfo->num_domains;
+}
+
 static const struct scmi_event reset_events[] = {
 	{
 		.id = SCMI_EVENT_RESET_ISSUED,
@@ -266,10 +276,18 @@ static const struct scmi_event reset_events[] = {
 };
 
 static const struct scmi_event_ops reset_event_ops = {
+	.get_num_sources = scmi_reset_get_num_sources,
 	.set_notify_enabled = scmi_reset_set_notify_enabled,
 	.fill_custom_report = scmi_reset_fill_custom_report,
 };
 
+static const struct scmi_protocol_events reset_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &reset_event_ops,
+	.evts = reset_events,
+	.num_events = ARRAY_SIZE(reset_events),
+};
+
 static int scmi_reset_protocol_init(struct scmi_handle *handle)
 {
 	int domain;
@@ -298,12 +316,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
 		scmi_reset_domain_attributes_get(handle, domain, dom);
 	}
 
-	scmi_register_protocol_events(handle,
-				      SCMI_PROTOCOL_RESET, SCMI_PROTO_QUEUE_SZ,
-				      &reset_event_ops, reset_events,
-				      ARRAY_SIZE(reset_events),
-				      pinfo->num_domains);
-
 	pinfo->version = version;
 	handle->reset_ops = &reset_ops;
 	handle->reset_priv = pinfo;
@@ -315,6 +327,7 @@ static const struct scmi_protocol scmi_reset = {
 	.id = SCMI_PROTOCOL_RESET,
 	.init = &scmi_reset_protocol_init,
 	.ops = &reset_ops,
+	.events = &reset_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 7ed1c4c6ec50..827dc0aa519c 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -318,6 +318,13 @@ static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
+static int scmi_sensor_get_num_sources(const struct scmi_handle *handle)
+{
+	struct sensors_info *si = handle->sensor_priv;
+
+	return si->num_sensors;
+}
+
 static const struct scmi_event sensor_events[] = {
 	{
 		.id = SCMI_EVENT_SENSOR_TRIP_POINT_EVENT,
@@ -327,10 +334,18 @@ static const struct scmi_event sensor_events[] = {
 };
 
 static const struct scmi_event_ops sensor_event_ops = {
+	.get_num_sources = scmi_sensor_get_num_sources,
 	.set_notify_enabled = scmi_sensor_set_notify_enabled,
 	.fill_custom_report = scmi_sensor_fill_custom_report,
 };
 
+static const struct scmi_protocol_events sensor_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &sensor_event_ops,
+	.evts = sensor_events,
+	.num_events = ARRAY_SIZE(sensor_events),
+};
+
 static int scmi_sensors_protocol_init(struct scmi_handle *handle)
 {
 	u32 version;
@@ -354,12 +369,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
 
 	scmi_sensor_description_get(handle, sinfo);
 
-	scmi_register_protocol_events(handle,
-				      SCMI_PROTOCOL_SENSOR, SCMI_PROTO_QUEUE_SZ,
-				      &sensor_event_ops, sensor_events,
-				      ARRAY_SIZE(sensor_events),
-				      sinfo->num_sensors);
-
 	sinfo->version = version;
 	handle->sensor_ops = &sensor_ops;
 	handle->sensor_priv = sinfo;
@@ -371,6 +380,7 @@ static const struct scmi_protocol scmi_sensors = {
 	.id = SCMI_PROTOCOL_SENSOR,
 	.init = &scmi_sensors_protocol_init,
 	.ops = &sensor_ops,
+	.events = &sensor_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(sensors, scmi_sensors)
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 94a68bfc79d6..167f539f7d95 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -101,6 +101,14 @@ static const struct scmi_event_ops system_event_ops = {
 	.fill_custom_report = scmi_system_fill_custom_report,
 };
 
+static const struct scmi_protocol_events system_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &system_event_ops,
+	.evts = system_events,
+	.num_events = ARRAY_SIZE(system_events),
+	.num_sources = SCMI_SYSTEM_NUM_SOURCES,
+};
+
 static int scmi_system_protocol_init(struct scmi_handle *handle)
 {
 	u32 version;
@@ -115,13 +123,6 @@ static int scmi_system_protocol_init(struct scmi_handle *handle)
 	if (!pinfo)
 		return -ENOMEM;
 
-	scmi_register_protocol_events(handle,
-				      SCMI_PROTOCOL_SYSTEM, SCMI_PROTO_QUEUE_SZ,
-				      &system_event_ops,
-				      system_events,
-				      ARRAY_SIZE(system_events),
-				      SCMI_SYSTEM_NUM_SOURCES);
-
 	pinfo->version = version;
 	handle->system_priv = pinfo;
 
@@ -132,6 +133,7 @@ static const struct scmi_protocol scmi_system = {
 	.id = SCMI_PROTOCOL_SYSTEM,
 	.init = &scmi_system_protocol_init,
 	.ops = NULL,
+	.events = &system_protocol_events,
 };
 
 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 6/8] firmware: arm_scmi: port all protocols and drivers to the new API
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (4 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 5/8] firmware: arm_scmi: refactor events registration Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 7/8] firmware: arm_scmi: make notify_priv really private Cristian Marussi
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Convert core SCMI xfer operations to use protocol handles and expose them
via protocol handle to the protocol code itself so that it can be used to
generate at will all the related SCMI messages: using protocol handles
forbids protocol code from forging messages belonging to other protocols.

Port all protocols initialization routines, notifications core mechanisms
and SCMI drivers to the new protocol handle interface; protocols are now
initialized on first usage when an SCMI driver gets hold of the related
protocols operations and protocol handle: ported SCMI drivers use the
devres managed API to simplify their usage.

Finally, remove the old protocol-specific _ops and _priv references from
the handle structure: private data are now accessible only by protocol
code using the protocol handle methods.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/clk/clk-scmi.c                     |  27 ++-
 drivers/cpufreq/scmi-cpufreq.c             |  38 ++--
 drivers/firmware/arm_scmi/base.c           | 115 +++++------
 drivers/firmware/arm_scmi/bus.c            |  24 ---
 drivers/firmware/arm_scmi/clock.c          | 115 ++++++-----
 drivers/firmware/arm_scmi/common.h         |  20 +-
 drivers/firmware/arm_scmi/driver.c         | 101 ++++++----
 drivers/firmware/arm_scmi/notify.c         |  13 +-
 drivers/firmware/arm_scmi/notify.h         |   7 +-
 drivers/firmware/arm_scmi/perf.c           | 224 ++++++++++-----------
 drivers/firmware/arm_scmi/power.c          |  99 +++++----
 drivers/firmware/arm_scmi/reset.c          | 111 +++++-----
 drivers/firmware/arm_scmi/scmi_pm_domain.c |  26 ++-
 drivers/firmware/arm_scmi/sensors.c        | 105 +++++-----
 drivers/firmware/arm_scmi/system.c         |  34 ++--
 drivers/hwmon/scmi-hwmon.c                 |  24 ++-
 drivers/reset/reset-scmi.c                 |  33 +--
 include/linux/scmi_protocol.h              |  91 +++------
 18 files changed, 596 insertions(+), 611 deletions(-)

diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index c754dfbb73fd..54b2043fa3fb 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Power Interface (SCMI) Protocol based clock driver
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #include <linux/clk-provider.h>
@@ -13,11 +13,13 @@
 #include <linux/scmi_protocol.h>
 #include <asm/div64.h>
 
+static const struct scmi_clk_ops *clk_ops;
+
 struct scmi_clk {
 	u32 id;
 	struct clk_hw hw;
 	const struct scmi_clock_info *info;
-	const struct scmi_handle *handle;
+	const struct scmi_protocol_handle *ph;
 };
 
 #define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
@@ -29,7 +31,7 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
 	u64 rate;
 	struct scmi_clk *clk = to_scmi_clk(hw);
 
-	ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
+	ret = clk_ops->rate_get(clk->ph, clk->id, &rate);
 	if (ret)
 		return 0;
 	return rate;
@@ -69,21 +71,21 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct scmi_clk *clk = to_scmi_clk(hw);
 
-	return clk->handle->clk_ops->rate_set(clk->handle, clk->id, rate);
+	return clk_ops->rate_set(clk->ph, clk->id, rate);
 }
 
 static int scmi_clk_enable(struct clk_hw *hw)
 {
 	struct scmi_clk *clk = to_scmi_clk(hw);
 
-	return clk->handle->clk_ops->enable(clk->handle, clk->id);
+	return clk_ops->enable(clk->ph, clk->id);
 }
 
 static void scmi_clk_disable(struct clk_hw *hw)
 {
 	struct scmi_clk *clk = to_scmi_clk(hw);
 
-	clk->handle->clk_ops->disable(clk->handle, clk->id);
+	clk_ops->disable(clk->ph, clk->id);
 }
 
 static const struct clk_ops scmi_clk_ops = {
@@ -142,11 +144,16 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
 	struct device *dev = &sdev->dev;
 	struct device_node *np = dev->of_node;
 	const struct scmi_handle *handle = sdev->handle;
+	struct scmi_protocol_handle *ph;
 
-	if (!handle || !handle->clk_ops)
+	if (!handle)
 		return -ENODEV;
 
-	count = handle->clk_ops->count_get(handle);
+	clk_ops = handle->devm_get_ops(sdev, SCMI_PROTOCOL_CLOCK, &ph);
+	if (IS_ERR(clk_ops))
+		return PTR_ERR(clk_ops);
+
+	count = clk_ops->count_get(ph);
 	if (count < 0) {
 		dev_err(dev, "%pOFn: invalid clock output count\n", np);
 		return -EINVAL;
@@ -167,14 +174,14 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
 		if (!sclk)
 			return -ENOMEM;
 
-		sclk->info = handle->clk_ops->info_get(handle, idx);
+		sclk->info = clk_ops->info_get(ph, idx);
 		if (!sclk->info) {
 			dev_dbg(dev, "invalid clock info for idx %d\n", idx);
 			continue;
 		}
 
 		sclk->id = idx;
-		sclk->handle = handle;
+		sclk->ph = ph;
 
 		err = scmi_clk_ops_init(dev, sclk);
 		if (err) {
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
index 46b268095421..89719dce973b 100644
--- a/drivers/cpufreq/scmi-cpufreq.c
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -24,17 +24,17 @@ struct scmi_data {
 	struct device *cpu_dev;
 };
 
-static const struct scmi_handle *handle;
+static struct scmi_protocol_handle *ph;
+static const struct scmi_perf_ops *perf_ops;
 
 static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
 {
 	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
-	const struct scmi_perf_ops *perf_ops = handle->perf_ops;
 	struct scmi_data *priv = policy->driver_data;
 	unsigned long rate;
 	int ret;
 
-	ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
+	ret = perf_ops->freq_get(ph, priv->domain_id, &rate, false);
 	if (ret)
 		return 0;
 	return rate / 1000;
@@ -50,10 +50,9 @@ scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
 {
 	int ret;
 	struct scmi_data *priv = policy->driver_data;
-	const struct scmi_perf_ops *perf_ops = handle->perf_ops;
 	u64 freq = policy->freq_table[index].frequency;
 
-	ret = perf_ops->freq_set(handle, priv->domain_id, freq * 1000, false);
+	ret = perf_ops->freq_set(ph, priv->domain_id, freq * 1000, false);
 	if (!ret)
 		arch_set_freq_scale(policy->related_cpus, freq,
 				    policy->cpuinfo.max_freq);
@@ -64,9 +63,8 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
 					     unsigned int target_freq)
 {
 	struct scmi_data *priv = policy->driver_data;
-	const struct scmi_perf_ops *perf_ops = handle->perf_ops;
 
-	if (!perf_ops->freq_set(handle, priv->domain_id,
+	if (!perf_ops->freq_set(ph, priv->domain_id,
 				target_freq * 1000, true)) {
 		arch_set_freq_scale(policy->related_cpus, target_freq,
 				    policy->cpuinfo.max_freq);
@@ -82,7 +80,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
 	int cpu, domain, tdomain;
 	struct device *tcpu_dev;
 
-	domain = handle->perf_ops->device_domain_id(cpu_dev);
+	domain = perf_ops->device_domain_id(cpu_dev);
 	if (domain < 0)
 		return domain;
 
@@ -94,7 +92,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
 		if (!tcpu_dev)
 			continue;
 
-		tdomain = handle->perf_ops->device_domain_id(tcpu_dev);
+		tdomain = perf_ops->device_domain_id(tcpu_dev);
 		if (tdomain == domain)
 			cpumask_set_cpu(cpu, cpumask);
 	}
@@ -109,13 +107,13 @@ scmi_get_cpu_power(unsigned long *power, unsigned long *KHz,
 	unsigned long Hz;
 	int ret, domain;
 
-	domain = handle->perf_ops->device_domain_id(cpu_dev);
+	domain = perf_ops->device_domain_id(cpu_dev);
 	if (domain < 0)
 		return domain;
 
 	/* Get the power cost of the performance domain. */
 	Hz = *KHz * 1000;
-	ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power);
+	ret = perf_ops->est_power_get(ph, domain, &Hz, power);
 	if (ret)
 		return ret;
 
@@ -140,7 +138,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
 		return -ENODEV;
 	}
 
-	ret = handle->perf_ops->device_opps_add(handle, cpu_dev);
+	ret = perf_ops->device_opps_add(ph, cpu_dev);
 	if (ret) {
 		dev_warn(cpu_dev, "failed to add opps to the device\n");
 		return ret;
@@ -179,7 +177,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
 	}
 
 	priv->cpu_dev = cpu_dev;
-	priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev);
+	priv->domain_id = perf_ops->device_domain_id(cpu_dev);
 
 	policy->driver_data = priv;
 	policy->freq_table = freq_table;
@@ -187,14 +185,14 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
 	/* SCMI allows DVFS request for any domain from any CPU */
 	policy->dvfs_possible_from_any_cpu = true;
 
-	latency = handle->perf_ops->transition_latency_get(handle, cpu_dev);
+	latency = perf_ops->transition_latency_get(ph, cpu_dev);
 	if (!latency)
 		latency = CPUFREQ_ETERNAL;
 
 	policy->cpuinfo.transition_latency = latency;
 
 	policy->fast_switch_possible =
-		handle->perf_ops->fast_switch_possible(handle, cpu_dev);
+		perf_ops->fast_switch_possible(ph, cpu_dev);
 
 	em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
 
@@ -236,17 +234,21 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
 static int scmi_cpufreq_probe(struct scmi_device *sdev)
 {
 	int ret;
+	const struct scmi_handle *handle;
 
 	handle = sdev->handle;
 
-	if (!handle || !handle->perf_ops)
+	if (!handle)
 		return -ENODEV;
 
+	perf_ops = handle->devm_get_ops(sdev, SCMI_PROTOCOL_PERF, &ph);
+	if (IS_ERR(perf_ops))
+		return PTR_ERR(perf_ops);
+
 	ret = cpufreq_register_driver(&scmi_cpufreq_driver);
-	if (ret) {
+	if (ret)
 		dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n",
 			__func__, ret);
-	}
 
 	return ret;
 }
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 634a6ec99464..745e475e6cc4 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -50,30 +50,30 @@ struct scmi_base_error_notify_payld {
  * scmi_base_attributes_get() - gets the implementation details
  *	that are associated with the base protocol.
  *
- * @handle: SCMI entity handle
+ * @ph: SCMI protocol handle
  *
  * Return: 0 on success, else appropriate SCMI error.
  */
-static int scmi_base_attributes_get(const struct scmi_handle *handle)
+static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_base_attributes *attr_info;
-	struct scmi_revision_info *rev = handle->version;
+	struct scmi_revision_info *rev = ph->get_priv(ph);
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(*attr_info), &t);
 	if (ret)
 		return ret;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		attr_info = t->rx.buf;
 		rev->num_protocols = attr_info->num_protocols;
 		rev->num_agents = attr_info->num_agents;
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	return ret;
 }
@@ -81,19 +81,20 @@ static int scmi_base_attributes_get(const struct scmi_handle *handle)
 /**
  * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
  *
- * @handle: SCMI entity handle
+ * @ph: SCMI protocol handle
  * @sub_vendor: specify true if sub-vendor ID is needed
  *
  * Return: 0 on success, else appropriate SCMI error.
  */
 static int
-scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
+scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
 {
 	u8 cmd;
 	int ret, size;
 	char *vendor_id;
 	struct scmi_xfer *t;
-	struct scmi_revision_info *rev = handle->version;
+	struct scmi_revision_info *rev = ph->get_priv(ph);
+
 
 	if (sub_vendor) {
 		cmd = BASE_DISCOVER_SUB_VENDOR;
@@ -105,15 +106,15 @@ scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
 		size = ARRAY_SIZE(rev->vendor_id);
 	}
 
-	ret = scmi_xfer_get_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
+	ret = ph->xops->xfer_get_init(ph, cmd, 0, size, &t);
 	if (ret)
 		return ret;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		memcpy(vendor_id, t->rx.buf, size);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	return ret;
 }
@@ -123,30 +124,30 @@ scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
  *	implementation 32-bit version. The format of the version number is
  *	vendor-specific
  *
- * @handle: SCMI entity handle
+ * @ph: SCMI protocol handle
  *
  * Return: 0 on success, else appropriate SCMI error.
  */
 static int
-scmi_base_implementation_version_get(const struct scmi_handle *handle)
+scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph)
 {
 	int ret;
 	__le32 *impl_ver;
 	struct scmi_xfer *t;
-	struct scmi_revision_info *rev = handle->version;
+	struct scmi_revision_info *rev = ph->get_priv(ph);
 
-	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
-				 SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
+	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION,
+				      0, sizeof(*impl_ver), &t);
 	if (ret)
 		return ret;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		impl_ver = t->rx.buf;
 		rev->impl_ver = le32_to_cpu(*impl_ver);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	return ret;
 }
@@ -155,23 +156,24 @@ scmi_base_implementation_version_get(const struct scmi_handle *handle)
  * scmi_base_implementation_list_get() - gets the list of protocols it is
  *	OSPM is allowed to access
  *
- * @handle: SCMI entity handle
+ * @ph: SCMI protocol handle
  * @protocols_imp: pointer to hold the list of protocol identifiers
  *
  * Return: 0 on success, else appropriate SCMI error.
  */
-static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
-					     u8 *protocols_imp)
+static int
+scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
+				  u8 *protocols_imp)
 {
 	u8 *list;
 	int ret, loop;
 	struct scmi_xfer *t;
 	__le32 *num_skip, *num_ret;
 	u32 tot_num_ret = 0, loop_num_ret;
-	struct device *dev = handle->dev;
+	struct device *dev = ph->dev;
 
-	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
-				 SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
+				      sizeof(*num_skip), 0, &t);
 	if (ret)
 		return ret;
 
@@ -183,7 +185,7 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
 		/* Set the number of protocols to be skipped/already read */
 		*num_skip = cpu_to_le32(tot_num_ret);
 
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 		if (ret)
 			break;
 
@@ -198,10 +200,10 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
 
 		tot_num_ret += loop_num_ret;
 
-		scmi_reset_rx_to_maxsz(handle, t);
+		ph->xops->reset_rx_to_maxsz(ph, t);
 	} while (loop_num_ret);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	return ret;
 }
@@ -209,7 +211,7 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
 /**
  * scmi_base_discover_agent_get() - discover the name of an agent
  *
- * @handle: SCMI entity handle
+ * @ph: SCMI protocol handle
  * @id: Agent identifier
  * @name: Agent identifier ASCII string
  *
@@ -218,63 +220,63 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
  *
  * Return: 0 on success, else appropriate SCMI error.
  */
-static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
+static int scmi_base_discover_agent_get(const struct scmi_protocol_handle *ph,
 					int id, char *name)
 {
 	int ret;
 	struct scmi_xfer *t;
 
-	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_AGENT,
-				 SCMI_PROTOCOL_BASE, sizeof(__le32),
-				 SCMI_MAX_STR_SIZE, &t);
+	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_AGENT,
+				      sizeof(__le32), SCMI_MAX_STR_SIZE, &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(id, t->tx.buf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	return ret;
 }
 
-static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
+static int scmi_base_error_notify(const struct scmi_protocol_handle *ph,
+				  bool enable)
 {
 	int ret;
 	u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0;
 	struct scmi_xfer *t;
 	struct scmi_msg_base_error_notify *cfg;
 
-	ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS,
-				 SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, BASE_NOTIFY_ERRORS,
+				      sizeof(*cfg), 0, &t);
 	if (ret)
 		return ret;
 
 	cfg = t->tx.buf;
 	cfg->event_control = cpu_to_le32(evt_cntl);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_base_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_base_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					u8 evt_id, u32 src_id, bool enable)
 {
 	int ret;
 
-	ret = scmi_base_error_notify(handle, enable);
+	ret = scmi_base_error_notify(ph, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret);
 
 	return ret;
 }
 
-static void *scmi_base_fill_custom_report(const struct scmi_handle *handle,
+static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
 					  u8 evt_id, ktime_t timestamp,
 					  const void *payld, size_t payld_sz,
 					  void *report, u32 *src_id)
@@ -326,17 +328,16 @@ static const struct scmi_protocol_events base_protocol_events = {
 	.num_sources = SCMI_BASE_NUM_SOURCES,
 };
 
-static int scmi_base_protocol_init(struct scmi_handle *h)
+static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	int id, ret;
 	u8 *prot_imp;
 	u32 version;
 	char name[SCMI_MAX_STR_SIZE];
-	const struct scmi_handle *handle = h;
-	struct device *dev = handle->dev;
-	struct scmi_revision_info *rev = handle->version;
+	struct device *dev = ph->dev;
+	struct scmi_revision_info *rev = scmi_get_revision_area(ph);
 
-	ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
+	ret = ph->xops->version_get(ph, &version);
 	if (ret)
 		return ret;
 
@@ -346,13 +347,15 @@ static int scmi_base_protocol_init(struct scmi_handle *h)
 
 	rev->major_ver = PROTOCOL_REV_MAJOR(version),
 	rev->minor_ver = PROTOCOL_REV_MINOR(version);
+	ph->set_priv(ph, rev);
+
+	scmi_base_attributes_get(ph);
+	scmi_base_vendor_id_get(ph, false);
+	scmi_base_vendor_id_get(ph, true);
+	scmi_base_implementation_version_get(ph);
+	scmi_base_implementation_list_get(ph, prot_imp);
 
-	scmi_base_attributes_get(handle);
-	scmi_base_vendor_id_get(handle, false);
-	scmi_base_vendor_id_get(handle, true);
-	scmi_base_implementation_version_get(handle);
-	scmi_base_implementation_list_get(handle, prot_imp);
-	scmi_setup_protocol_implemented(handle, prot_imp);
+	scmi_setup_protocol_implemented(ph, prot_imp);
 
 	dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
 		 rev->major_ver, rev->minor_ver, rev->vendor_id,
@@ -361,7 +364,7 @@ static int scmi_base_protocol_init(struct scmi_handle *h)
 		rev->num_agents);
 
 	for (id = 0; id < rev->num_agents; id++) {
-		scmi_base_discover_agent_get(handle, id, name);
+		scmi_base_discover_agent_get(ph, id, name);
 		dev_dbg(dev, "Agent %d: %s\n", id, name);
 	}
 
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 0410cf77233c..25b191cfefc1 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -66,27 +66,11 @@ const struct scmi_protocol *scmi_get_protocol(int protocol_id)
 	return proto;
 }
 
-static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
-{
-	const struct scmi_protocol *proto;
-
-	proto = scmi_get_protocol(protocol_id);
-	if (!proto)
-		return -EINVAL;
-	return proto->init(handle);
-}
-
-static int scmi_protocol_dummy_init(struct scmi_handle *handle)
-{
-	return 0;
-}
-
 static int scmi_dev_probe(struct device *dev)
 {
 	struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
 	struct scmi_device *scmi_dev = to_scmi_dev(dev);
 	const struct scmi_device_id *id;
-	int ret;
 
 	id = scmi_dev_match_id(scmi_dev, scmi_drv);
 	if (!id)
@@ -95,14 +79,6 @@ static int scmi_dev_probe(struct device *dev)
 	if (!scmi_dev->handle)
 		return -EPROBE_DEFER;
 
-	ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle);
-	if (ret)
-		return ret;
-
-	/* Skip protocol initialisation for additional devices */
-	idr_replace(&scmi_available_protocols, &scmi_protocol_dummy_init,
-		    scmi_dev->protocol_id);
-
 	return scmi_drv->probe(scmi_dev);
 }
 
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index e8c84cff9922..00041dcb4d06 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -74,52 +74,53 @@ struct clock_info {
 	struct scmi_clock_info *clk;
 };
 
-static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle,
-					      struct clock_info *ci)
+static int
+scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
+				   struct clock_info *ci)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_clock_protocol_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		ci->num_clocks = le16_to_cpu(attr->num_clocks);
 		ci->max_async_req = attr->max_async_req;
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_clock_attributes_get(const struct scmi_handle *handle,
+static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
 				     u32 clk_id, struct scmi_clock_info *clk)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_clock_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
-				 sizeof(clk_id), sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
+				      sizeof(clk_id), sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(clk_id, t->tx.buf);
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
 	else
 		clk->name[0] = '\0';
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
@@ -136,7 +137,7 @@ static int rate_cmp_func(const void *_r1, const void *_r2)
 }
 
 static int
-scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
+scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
 			      struct scmi_clock_info *clk)
 {
 	u64 *rate = NULL;
@@ -148,8 +149,8 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 	struct scmi_msg_clock_describe_rates *clk_desc;
 	struct scmi_msg_resp_clock_describe_rates *rlist;
 
-	ret = scmi_xfer_get_init(handle, CLOCK_DESCRIBE_RATES,
-				 SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES,
+				      sizeof(*clk_desc), 0, &t);
 	if (ret)
 		return ret;
 
@@ -161,7 +162,7 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 		/* Set the number of rates to be skipped/already read */
 		clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
 
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 		if (ret)
 			goto err;
 
@@ -171,7 +172,7 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 		num_returned = NUM_RETURNED(rates_flag);
 
 		if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
-			dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
+			dev_err(ph->dev, "No. of rates > MAX_NUM_RATES");
 			break;
 		}
 
@@ -179,7 +180,7 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 			clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
 			clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
 			clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
-			dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
+			dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
 				clk->range.min_rate, clk->range.max_rate,
 				clk->range.step_size);
 			break;
@@ -188,12 +189,12 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 		rate = &clk->list.rates[tot_rate_cnt];
 		for (cnt = 0; cnt < num_returned; cnt++, rate++) {
 			*rate = RATE_TO_U64(rlist->rate[cnt]);
-			dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
+			dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
 		}
 
 		tot_rate_cnt += num_returned;
 
-		scmi_reset_rx_to_maxsz(handle, t);
+		ph->xops->reset_rx_to_maxsz(ph, t);
 		/*
 		 * check for both returned and remaining to avoid infinite
 		 * loop due to buggy firmware
@@ -208,42 +209,42 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
 	clk->rate_discrete = rate_discrete;
 
 err:
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
+scmi_clock_rate_get(const struct scmi_protocol_handle *ph,
+		    u32 clk_id, u64 *value)
 {
 	int ret;
 	struct scmi_xfer *t;
 
-	ret = scmi_xfer_get_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
-				 sizeof(__le32), sizeof(u64), &t);
+	ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_GET,
+				      sizeof(__le32), sizeof(u64), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(clk_id, t->tx.buf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		*value = get_unaligned_le64(t->rx.buf);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
-			       u64 rate)
+static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
+			       u32 clk_id, u64 rate)
 {
 	int ret;
 	u32 flags = 0;
 	struct scmi_xfer *t;
 	struct scmi_clock_set_rate *cfg;
-	struct clock_info *ci = handle->clk_priv;
+	struct clock_info *ci = ph->get_priv(ph);
 
-	ret = scmi_xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
-				 sizeof(*cfg), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
 	if (ret)
 		return ret;
 
@@ -258,26 +259,27 @@ static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
 	cfg->value_high = cpu_to_le32(rate >> 32);
 
 	if (flags & CLOCK_SET_ASYNC)
-		ret = scmi_do_xfer_with_response(handle, t);
+		ret = ph->xops->do_xfer_with_response(ph, t);
 	else
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 
 	if (ci->max_async_req)
 		atomic_dec(&ci->cur_async_req);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
+scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
+		      u32 config)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_clock_set_config *cfg;
 
-	ret = scmi_xfer_get_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
-				 sizeof(*cfg), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET,
+				      sizeof(*cfg), 0, &t);
 	if (ret)
 		return ret;
 
@@ -285,33 +287,33 @@ scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
 	cfg->id = cpu_to_le32(clk_id);
 	cfg->attributes = cpu_to_le32(config);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_clock_enable(const struct scmi_handle *handle, u32 clk_id)
+static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id)
 {
-	return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
+	return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE);
 }
 
-static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
+static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id)
 {
-	return scmi_clock_config_set(handle, clk_id, 0);
+	return scmi_clock_config_set(ph, clk_id, 0);
 }
 
-static int scmi_clock_count_get(const struct scmi_handle *handle)
+static int scmi_clock_count_get(const struct scmi_protocol_handle *ph)
 {
-	struct clock_info *ci = handle->clk_priv;
+	struct clock_info *ci = ph->get_priv(ph);
 
 	return ci->num_clocks;
 }
 
 static const struct scmi_clock_info *
-scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
+scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
 {
-	struct clock_info *ci = handle->clk_priv;
+	struct clock_info *ci = ph->get_priv(ph);
 	struct scmi_clock_info *clk = ci->clk + clk_id;
 
 	if (!clk->name[0])
@@ -329,24 +331,24 @@ static const struct scmi_clk_ops clk_ops = {
 	.disable = scmi_clock_disable,
 };
 
-static int scmi_clock_protocol_init(struct scmi_handle *handle)
+static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	u32 version;
 	int clkid, ret;
 	struct clock_info *cinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "Clock Version %d.%d\n",
+	dev_dbg(ph->dev, "Clock Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	cinfo = devm_kzalloc(handle->dev, sizeof(*cinfo), GFP_KERNEL);
+	cinfo = devm_kzalloc(ph->dev, sizeof(*cinfo), GFP_KERNEL);
 	if (!cinfo)
 		return -ENOMEM;
 
-	scmi_clock_protocol_attributes_get(handle, cinfo);
+	scmi_clock_protocol_attributes_get(ph, cinfo);
 
-	cinfo->clk = devm_kcalloc(handle->dev, cinfo->num_clocks,
+	cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
 				  sizeof(*cinfo->clk), GFP_KERNEL);
 	if (!cinfo->clk)
 		return -ENOMEM;
@@ -354,16 +356,13 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
 	for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
 		struct scmi_clock_info *clk = cinfo->clk + clkid;
 
-		ret = scmi_clock_attributes_get(handle, clkid, clk);
+		ret = scmi_clock_attributes_get(ph, clkid, clk);
 		if (!ret)
-			scmi_clock_describe_rates_get(handle, clkid, clk);
+			scmi_clock_describe_rates_get(ph, clkid, clk);
 	}
 
 	cinfo->version = version;
-	handle->clk_ops = &clk_ops;
-	handle->clk_priv = cinfo;
-
-	return 0;
+	return ph->set_priv(ph, cinfo);
 }
 
 static const struct scmi_protocol scmi_clock = {
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 60ef6cfb81bf..995f19a07b5e 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -19,6 +19,8 @@
 
 #include <asm/unaligned.h>
 
+#include "notify.h"
+
 #define SCMI_MAX_PROTO		256
 
 #define PROTOCOL_REV_MINOR_MASK	GENMASK(15, 0)
@@ -143,15 +145,6 @@ struct scmi_xfer {
 	struct completion *async_done;
 };
 
-void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer);
-int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer);
-int scmi_do_xfer_with_response(const struct scmi_handle *h,
-			       struct scmi_xfer *xfer);
-int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
-		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
-void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
-			    struct scmi_xfer *xfer);
-
 struct scmi_xfer_ops;
 
 /**
@@ -215,11 +208,10 @@ scmi_get_revision_area(const struct scmi_protocol_handle *ph);
 int scmi_handle_put(const struct scmi_handle *handle);
 struct scmi_handle *scmi_handle_get(struct device *dev);
 void scmi_set_handle(struct scmi_device *scmi_dev);
-int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
-void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
+void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
 				     u8 *prot_imp);
 
-typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
+typedef int (*scmi_prot_init_fn_t)(const struct scmi_protocol_handle *);
 
 /**
  * struct scmi_protocol  - Protocol descriptor
@@ -265,8 +257,8 @@ void __exit scmi_##name##_unregister(void) \
 
 const struct scmi_protocol *scmi_get_protocol(int protocol_id);
 
-int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id);
-void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id);
+int scmi_acquire_protocol(const struct scmi_handle *handle, u8 protocol_id);
+void scmi_release_protocol(const struct scmi_handle *handle, u8 protocol_id);
 
 /* SCMI Transport */
 /**
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 97f22278c5b6..87ceb235194b 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -352,12 +352,14 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
 /**
  * scmi_xfer_put() - Release a transmit message
  *
- * @handle: Pointer to SCMI entity handle
+ * @ph: Pointer to SCMI protocol handle
  * @xfer: message that was reserved by scmi_xfer_get
  */
-void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+static void scmi_xfer_put(const struct scmi_protocol_handle *ph,
+			  struct scmi_xfer *xfer)
 {
-	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+	struct scmi_info *info = handle_to_scmi_info(pi->handle);
 
 	__scmi_xfer_put(&info->tx_minfo, xfer);
 }
@@ -376,21 +378,30 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
 /**
  * scmi_do_xfer() - Do one transfer
  *
- * @handle: Pointer to SCMI entity handle
+ * @ph: Pointer to SCMI protocol handle
  * @xfer: Transfer to initiate and wait for response
  *
  * Return: -ETIMEDOUT in case of no response, if transmit error,
  *	return corresponding error, else if all goes well,
  *	return 0.
  */
-int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+static int scmi_do_xfer(const struct scmi_protocol_handle *ph,
+			struct scmi_xfer *xfer)
 {
 	int ret;
 	int timeout;
-	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+	struct scmi_info *info = handle_to_scmi_info(pi->handle);
 	struct device *dev = info->dev;
 	struct scmi_chan_info *cinfo;
 
+	/*
+	 * Re-instate protocol id here from protocol handle so that cannot be
+	 * overridden by mistake (or malice) by the protocol code mangling with
+	 * the scmi_xfer structure.
+	 */
+	xfer->hdr.protocol_id = pi->proto->id;
+
 	cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
 	if (unlikely(!cinfo))
 		return -EINVAL;
@@ -436,10 +447,11 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	return ret;
 }
 
-void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
-			    struct scmi_xfer *xfer)
+static void scmi_reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
+				   struct scmi_xfer *xfer)
 {
-	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+	struct scmi_info *info = handle_to_scmi_info(pi->handle);
 
 	xfer->rx.len = info->desc->max_msg_size;
 }
@@ -450,21 +462,24 @@ void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
  * scmi_do_xfer_with_response() - Do one transfer and wait until the delayed
  *	response is received
  *
- * @handle: Pointer to SCMI entity handle
+ * @ph: Pointer to SCMI protocol handle
  * @xfer: Transfer to initiate and wait for response
  *
  * Return: -ETIMEDOUT in case of no delayed response, if transmit error,
  *	return corresponding error, else if all goes well, return 0.
  */
-int scmi_do_xfer_with_response(const struct scmi_handle *handle,
-			       struct scmi_xfer *xfer)
+static int scmi_do_xfer_with_response(const struct scmi_protocol_handle *ph,
+				      struct scmi_xfer *xfer)
 {
 	int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
 	DECLARE_COMPLETION_ONSTACK(async_response);
 
+	xfer->hdr.protocol_id = pi->proto->id;
+
 	xfer->async_done = &async_response;
 
-	ret = scmi_do_xfer(handle, xfer);
+	ret = scmi_do_xfer(ph, xfer);
 	if (!ret && !wait_for_completion_timeout(xfer->async_done, timeout))
 		ret = -ETIMEDOUT;
 
@@ -475,9 +490,8 @@ int scmi_do_xfer_with_response(const struct scmi_handle *handle,
 /**
  * scmi_xfer_get_init() - Allocate and initialise one message for transmit
  *
- * @handle: Pointer to SCMI entity handle
+ * @ph: Pointer to SCMI protocol handle
  * @msg_id: Message identifier
- * @prot_id: Protocol identifier for the message
  * @tx_size: transmit message size
  * @rx_size: receive message size
  * @p: pointer to the allocated and initialised message
@@ -488,12 +502,14 @@ int scmi_do_xfer_with_response(const struct scmi_handle *handle,
  * Return: 0 if all went fine with @p pointing to message, else
  *	corresponding error.
  */
-int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
-		       size_t tx_size, size_t rx_size, struct scmi_xfer **p)
+static int scmi_xfer_get_init(const struct scmi_protocol_handle *ph, u8 msg_id,
+			      size_t tx_size, size_t rx_size,
+			      struct scmi_xfer **p)
 {
 	int ret;
 	struct scmi_xfer *xfer;
-	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+	struct scmi_info *info = handle_to_scmi_info(pi->handle);
 	struct scmi_xfers_info *minfo = &info->tx_minfo;
 	struct device *dev = info->dev;
 
@@ -502,7 +518,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
 	    tx_size > info->desc->max_msg_size)
 		return -ERANGE;
 
-	xfer = scmi_xfer_get(handle, minfo);
+	xfer = scmi_xfer_get(pi->handle, minfo);
 	if (IS_ERR(xfer)) {
 		ret = PTR_ERR(xfer);
 		dev_err(dev, "failed to get free message slot(%d)\n", ret);
@@ -512,7 +528,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
 	xfer->tx.len = tx_size;
 	xfer->rx.len = rx_size ? : info->desc->max_msg_size;
 	xfer->hdr.id = msg_id;
-	xfer->hdr.protocol_id = prot_id;
+	xfer->hdr.protocol_id = pi->proto->id;
 	xfer->hdr.poll_completion = false;
 
 	*p = xfer;
@@ -523,33 +539,30 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
 /**
  * scmi_version_get() - command to get the revision of the SCMI entity
  *
- * @handle: Pointer to SCMI entity handle
- * @protocol: Protocol identifier for the message
+ * @ph: Pointer to SCMI protocol handle
  * @version: Holds returned version of protocol.
  *
  * Updates the SCMI information in the internal data structure.
  *
  * Return: 0 if all went fine, else return appropriate error.
  */
-int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
-		     u32 *version)
+static int scmi_version_get(const struct scmi_protocol_handle *ph, u32 *version)
 {
 	int ret;
 	__le32 *rev_info;
 	struct scmi_xfer *t;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_VERSION, protocol, 0,
-				 sizeof(*version), &t);
+	ret = scmi_xfer_get_init(ph, PROTOCOL_VERSION, 0, sizeof(*version), &t);
 	if (ret)
 		return ret;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = scmi_do_xfer(ph, t);
 	if (!ret) {
 		rev_info = t->rx.buf;
 		*version = le32_to_cpu(*rev_info);
 	}
 
-	scmi_xfer_put(handle, t);
+	scmi_xfer_put(ph, t);
 	return ret;
 }
 
@@ -604,6 +617,15 @@ scmi_get_revision_area(const struct scmi_protocol_handle *ph)
 	return pi->handle->version;
 }
 
+static const struct scmi_xfer_ops xfer_ops = {
+	.version_get = scmi_version_get,
+	.xfer_get_init = scmi_xfer_get_init,
+	.reset_rx_to_maxsz = scmi_reset_rx_to_maxsz,
+	.do_xfer = scmi_do_xfer,
+	.do_xfer_with_response = scmi_do_xfer_with_response,
+	.xfer_put = scmi_xfer_put,
+};
+
 /**
  * scmi_get_protocol_instance  - Protocol initialization helper.
  * @handle: A reference to the SCMI platform instance.
@@ -616,7 +638,7 @@ scmi_get_revision_area(const struct scmi_protocol_handle *ph)
  * Return: A reference to an initialized protocol instance or error on failure.
  */
 static struct scmi_protocol_instance * __must_check
-scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
+scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
 {
 	int ret = -ENOMEM;
 	void *gid;
@@ -651,11 +673,12 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
 		pi->proto = proto;
 		pi->handle = handle;
 		pi->ph.dev = handle->dev;
+		pi->ph.xops = &xfer_ops;
 		pi->ph.set_priv = scmi_set_protocol_priv;
 		pi->ph.get_priv = scmi_get_protocol_priv;
 		refcount_set(&pi->users, 1);
 		/* proto->init is assured NON NULL by scmi_protocol_register */
-		ret = pi->proto->init(handle);
+		ret = pi->proto->init(&pi->ph);
 		if (ret)
 			goto clean;
 
@@ -666,6 +689,7 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
 
 		if (pi->proto->events)
 			scmi_register_protocol_events(handle, pi->proto->id,
+						      &pi->ph,
 						      pi->proto->events);
 
 		devres_close_group(handle->dev, pi->gid);
@@ -693,7 +717,7 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
  *
  * Return: 0 if protocol was acquired successfully.
  */
-int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id)
+int scmi_acquire_protocol(const struct scmi_handle *handle, u8 protocol_id)
 {
 	return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
 }
@@ -706,7 +730,7 @@ int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id)
  * Remove one user for the specified protocol and triggers de-initialization
  * and resources de-allocation once the last user has gone.
  */
-void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
+void scmi_release_protocol(const struct scmi_handle *handle, u8 protocol_id)
 {
 	struct scmi_info *info = handle_to_scmi_info(handle);
 	struct scmi_protocol_instance *pi;
@@ -723,7 +747,7 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
 			scmi_deregister_protocol_events(handle, protocol_id);
 
 		if (pi->proto->deinit)
-			pi->proto->deinit(handle);
+			pi->proto->deinit(&pi->ph);
 
 		idr_remove(&info->protocols, protocol_id);
 
@@ -751,7 +775,7 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
  *	   Must be checked for errors by caller.
  */
 static const void __must_check *
-scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
+scmi_get_protocol_operations(const struct scmi_handle *handle, u8 protocol_id,
 			     struct scmi_protocol_handle **ph)
 {
 	struct scmi_protocol_instance *pi;
@@ -768,10 +792,11 @@ scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
 	return pi->proto->ops;
 }
 
-void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
+void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
 				     u8 *prot_imp)
 {
-	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+	struct scmi_info *info = handle_to_scmi_info(pi->handle);
 
 	info->protocols_imp = prot_imp;
 }
@@ -792,7 +817,7 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
 }
 
 struct scmi_protocol_devres {
-	struct scmi_handle *handle;
+	const struct scmi_handle *handle;
 	u8 protocol_id;
 };
 
@@ -827,7 +852,7 @@ scmi_devm_get_protocol_ops(struct scmi_device *sdev, u8 protocol_id,
 {
 	struct scmi_protocol_instance *pi;
 	struct scmi_protocol_devres *dres;
-	struct scmi_handle *handle = sdev->handle;
+	const struct scmi_handle *handle = sdev->handle;
 
 	if (!ph)
 		return ERR_PTR(-EINVAL);
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 504f5b5d557e..6d4a1d78c22e 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -176,7 +176,7 @@
 #define REVT_NOTIFY_SET_STATUS(revt, eid, sid, state)		\
 ({								\
 	typeof(revt) r = revt;					\
-	r->proto->ops->set_notify_enabled(r->proto->ni->handle,	\
+	r->proto->ops->set_notify_enabled(r->proto->ph,		\
 					(eid), (sid), (state));	\
 })
 
@@ -189,7 +189,7 @@
 #define REVT_FILL_REPORT(revt, ...)				\
 ({								\
 	typeof(revt) r = revt;					\
-	r->proto->ops->fill_custom_report(r->proto->ni->handle,	\
+	r->proto->ops->fill_custom_report(r->proto->ph,		\
 					  __VA_ARGS__);		\
 })
 
@@ -277,6 +277,7 @@ struct scmi_registered_event;
  *		       events' descriptors, whose fixed-size is determined at
  *		       compile time.
  * @registered_mtx: A mutex to protect @registered_events_handlers
+ * @ph: SCMI protocol handle reference
  * @registered_events_handlers: An hashtable containing all events' handlers
  *				descriptors registered for this protocol
  *
@@ -301,6 +302,7 @@ struct scmi_registered_events_desc {
 	struct scmi_registered_event	**registered_events;
 	/* mutex to protect registered_events_handlers */
 	struct mutex			registered_mtx;
+	const struct scmi_protocol_handle	*ph;
 	DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ);
 };
 
@@ -733,6 +735,7 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
  * @handle: The handle identifying the platform instance against which the
  *	    protocol's events are registered
  * @proto_id: Protocol ID
+ * @ph: SCMI protocol handle.
  * @ee: A structure describing the events supported by this protocol.
  *
  * Used by SCMI Protocols initialization code to register with the notification
@@ -743,6 +746,7 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
  * Return: 0 on Success
  */
 int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
+				  const struct scmi_protocol_handle *ph,
 				  const struct scmi_protocol_events *ee)
 {
 	int i;
@@ -752,7 +756,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	struct scmi_notify_instance *ni;
 	const struct scmi_event *evt;
 
-	if (!ee || !ee->ops || !ee->evts ||
+	if (!ee || !ee->ops || !ee->evts || !ph ||
 	    (!ee->num_sources && !ee->ops->get_num_sources))
 		return -EINVAL;
 
@@ -766,7 +770,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	if (ee->num_sources) {
 		num_sources = ee->num_sources;
 	} else {
-		int nsrc = ee->ops->get_num_sources(handle);
+		int nsrc = ee->ops->get_num_sources(ph);
 
 		if (nsrc <= 0)
 			return -EINVAL;
@@ -784,6 +788,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	if (IS_ERR(pd))
 		goto err;
 
+	pd->ph = ph;
 	for (i = 0; i < ee->num_events; i++, evt++) {
 		struct scmi_registered_event *r_evt;
 
diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h
index 97ddfe55d773..3915bcd76242 100644
--- a/drivers/firmware/arm_scmi/notify.h
+++ b/drivers/firmware/arm_scmi/notify.h
@@ -50,10 +50,10 @@ struct scmi_protocol_handle;
  *	    process context.
  */
 struct scmi_event_ops {
-	int (*get_num_sources)(const struct scmi_handle *handle);
-	int (*set_notify_enabled)(const struct scmi_handle *handle,
+	int (*get_num_sources)(const struct scmi_protocol_handle *ph);
+	int (*set_notify_enabled)(const struct scmi_protocol_handle *ph,
 				  u8 evt_id, u32 src_id, bool enabled);
-	void *(*fill_custom_report)(const struct scmi_handle *handle,
+	void *(*fill_custom_report)(const struct scmi_protocol_handle *ph,
 				    u8 evt_id, ktime_t timestamp,
 				    const void *payld, size_t payld_sz,
 				    void *report, u32 *src_id);
@@ -82,6 +82,7 @@ void scmi_notification_exit(struct scmi_handle *handle);
 
 struct scmi_protocol_handle;
 int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
+				  const struct scmi_protocol_handle *ph,
 				  const struct scmi_protocol_events *ee);
 void scmi_deregister_protocol_events(const struct scmi_handle *handle,
 				     u8 proto_id);
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 298565cd4d6d..c1d0664aaf4a 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -175,21 +175,21 @@ static enum scmi_performance_protocol_cmd evt_2_cmd[] = {
 	PERF_NOTIFY_LEVEL,
 };
 
-static int scmi_perf_attributes_get(const struct scmi_handle *handle,
+static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
 				    struct scmi_perf_info *pi)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_perf_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_PERF, 0, sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
+				      sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		u16 flags = le16_to_cpu(attr->flags);
 
@@ -200,28 +200,27 @@ static int scmi_perf_attributes_get(const struct scmi_handle *handle,
 		pi->stats_size = le32_to_cpu(attr->stats_size);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
-				struct perf_dom_info *dom_info)
+scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
+				u32 domain, struct perf_dom_info *dom_info)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_perf_domain_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, PERF_DOMAIN_ATTRIBUTES,
-				 SCMI_PROTOCOL_PERF, sizeof(domain),
-				 sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_DOMAIN_ATTRIBUTES,
+				     sizeof(domain), sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(domain, t->tx.buf);
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		u32 flags = le32_to_cpu(attr->flags);
 
@@ -245,7 +244,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
 		strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
@@ -257,7 +256,7 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
 }
 
 static int
-scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
+scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain,
 			      struct perf_dom_info *perf_dom)
 {
 	int ret, cnt;
@@ -268,8 +267,8 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
 	struct scmi_msg_perf_describe_levels *dom_info;
 	struct scmi_msg_resp_perf_describe_levels *level_info;
 
-	ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_LEVELS,
-				 SCMI_PROTOCOL_PERF, sizeof(*dom_info), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_LEVELS,
+				      sizeof(*dom_info), 0, &t);
 	if (ret)
 		return ret;
 
@@ -281,14 +280,14 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
 		/* Set the number of OPPs to be skipped/already read */
 		dom_info->level_index = cpu_to_le32(tot_opp_cnt);
 
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 		if (ret)
 			break;
 
 		num_returned = le16_to_cpu(level_info->num_returned);
 		num_remaining = le16_to_cpu(level_info->num_remaining);
 		if (tot_opp_cnt + num_returned > MAX_OPPS) {
-			dev_err(handle->dev, "No. of OPPs exceeded MAX_OPPS");
+			dev_err(ph->dev, "No. of OPPs exceeded MAX_OPPS");
 			break;
 		}
 
@@ -299,13 +298,13 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
 			opp->trans_latency_us = le16_to_cpu
 				(level_info->opp[cnt].transition_latency_us);
 
-			dev_dbg(handle->dev, "Level %d Power %d Latency %dus\n",
+			dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n",
 				opp->perf, opp->power, opp->trans_latency_us);
 		}
 
 		tot_opp_cnt += num_returned;
 
-		scmi_reset_rx_to_maxsz(handle, t);
+		ph->xops->reset_rx_to_maxsz(ph, t);
 		/*
 		 * check for both returned and remaining to avoid infinite
 		 * loop due to buggy firmware
@@ -313,7 +312,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
 	} while (num_returned && num_remaining);
 
 	perf_dom->opp_count = tot_opp_cnt;
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 
 	sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
 	return ret;
@@ -353,15 +352,15 @@ static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db)
 #endif
 }
 
-static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain,
-				   u32 max_perf, u32 min_perf)
+static int scmi_perf_mb_limits_set(const struct scmi_protocol_handle *ph,
+				   u32 domain, u32 max_perf, u32 min_perf)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_perf_set_limits *limits;
 
-	ret = scmi_xfer_get_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
-				 sizeof(*limits), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_LIMITS_SET,
+				      sizeof(*limits), 0, &t);
 	if (ret)
 		return ret;
 
@@ -370,16 +369,16 @@ static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain,
 	limits->max_level = cpu_to_le32(max_perf);
 	limits->min_level = cpu_to_le32(min_perf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
-				u32 max_perf, u32 min_perf)
+static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
+				u32 domain, u32 max_perf, u32 min_perf)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
 	if (dom->fc_info && dom->fc_info->limit_set_addr) {
@@ -389,24 +388,24 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
 		return 0;
 	}
 
-	return scmi_perf_mb_limits_set(handle, domain, max_perf, min_perf);
+	return scmi_perf_mb_limits_set(ph, domain, max_perf, min_perf);
 }
 
-static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain,
-				   u32 *max_perf, u32 *min_perf)
+static int scmi_perf_mb_limits_get(const struct scmi_protocol_handle *ph,
+				   u32 domain, u32 *max_perf, u32 *min_perf)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_perf_get_limits *limits;
 
-	ret = scmi_xfer_get_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
-				 sizeof(__le32), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_LIMITS_GET,
+				      sizeof(__le32), 0, &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(domain, t->tx.buf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		limits = t->rx.buf;
 
@@ -414,14 +413,14 @@ static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain,
 		*min_perf = le32_to_cpu(limits->min_level);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
-				u32 *max_perf, u32 *min_perf)
+static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph,
+				u32 domain, u32 *max_perf, u32 *min_perf)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
 	if (dom->fc_info && dom->fc_info->limit_get_addr) {
@@ -430,18 +429,17 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
 		return 0;
 	}
 
-	return scmi_perf_mb_limits_get(handle, domain, max_perf, min_perf);
+	return scmi_perf_mb_limits_get(ph, domain, max_perf, min_perf);
 }
 
-static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain,
-				  u32 level, bool poll)
+static int scmi_perf_mb_level_set(const struct scmi_protocol_handle *ph,
+				  u32 domain, u32 level, bool poll)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_perf_set_level *lvl;
 
-	ret = scmi_xfer_get_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
-				 sizeof(*lvl), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_LEVEL_SET, sizeof(*lvl), 0, &t);
 	if (ret)
 		return ret;
 
@@ -450,16 +448,16 @@ static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain,
 	lvl->domain = cpu_to_le32(domain);
 	lvl->level = cpu_to_le32(level);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
-			       u32 level, bool poll)
+static int scmi_perf_level_set(const struct scmi_protocol_handle *ph,
+			       u32 domain, u32 level, bool poll)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
 	if (dom->fc_info && dom->fc_info->level_set_addr) {
@@ -468,35 +466,35 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
 		return 0;
 	}
 
-	return scmi_perf_mb_level_set(handle, domain, level, poll);
+	return scmi_perf_mb_level_set(ph, domain, level, poll);
 }
 
-static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain,
-				  u32 *level, bool poll)
+static int scmi_perf_mb_level_get(const struct scmi_protocol_handle *ph,
+				  u32 domain, u32 *level, bool poll)
 {
 	int ret;
 	struct scmi_xfer *t;
 
-	ret = scmi_xfer_get_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
-				 sizeof(u32), sizeof(u32), &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_LEVEL_GET,
+				     sizeof(u32), sizeof(u32), &t);
 	if (ret)
 		return ret;
 
 	t->hdr.poll_completion = poll;
 	put_unaligned_le32(domain, t->tx.buf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		*level = get_unaligned_le32(t->rx.buf);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
-			       u32 *level, bool poll)
+static int scmi_perf_level_get(const struct scmi_protocol_handle *ph,
+			       u32 domain, u32 *level, bool poll)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
 	if (dom->fc_info && dom->fc_info->level_get_addr) {
@@ -504,10 +502,10 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
 		return 0;
 	}
 
-	return scmi_perf_mb_level_get(handle, domain, level, poll);
+	return scmi_perf_mb_level_get(ph, domain, level, poll);
 }
 
-static int scmi_perf_level_limits_notify(const struct scmi_handle *handle,
+static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle *ph,
 					 u32 domain, int message_id,
 					 bool enable)
 {
@@ -515,8 +513,7 @@ static int scmi_perf_level_limits_notify(const struct scmi_handle *handle,
 	struct scmi_xfer *t;
 	struct scmi_perf_notify_level_or_limits *notify;
 
-	ret = scmi_xfer_get_init(handle, message_id, SCMI_PROTOCOL_PERF,
-				 sizeof(*notify), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*notify), 0, &t);
 	if (ret)
 		return ret;
 
@@ -524,9 +521,9 @@ static int scmi_perf_level_limits_notify(const struct scmi_handle *handle,
 	notify->domain = cpu_to_le32(domain);
 	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
@@ -540,7 +537,7 @@ static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
 }
 
 static void
-scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
+scmi_perf_domain_desc_fc(const struct scmi_protocol_handle *ph, u32 domain,
 			 u32 message_id, void __iomem **p_addr,
 			 struct scmi_fc_db_info **p_db)
 {
@@ -557,9 +554,8 @@ scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
 	if (!p_addr)
 		return;
 
-	ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL,
-				 SCMI_PROTOCOL_PERF,
-				 sizeof(*info), sizeof(*resp), &t);
+	ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_FASTCHANNEL,
+				      sizeof(*info), sizeof(*resp), &t);
 	if (ret)
 		return;
 
@@ -567,7 +563,7 @@ scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
 	info->domain = cpu_to_le32(domain);
 	info->message_id = cpu_to_le32(message_id);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (ret)
 		goto err_xfer;
 
@@ -579,20 +575,20 @@ scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
 
 	phys_addr = le32_to_cpu(resp->chan_addr_low);
 	phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
-	addr = devm_ioremap(handle->dev, phys_addr, size);
+	addr = devm_ioremap(ph->dev, phys_addr, size);
 	if (!addr)
 		goto err_xfer;
 	*p_addr = addr;
 
 	if (p_db && SUPPORTS_DOORBELL(flags)) {
-		db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL);
+		db = devm_kzalloc(ph->dev, sizeof(*db), GFP_KERNEL);
 		if (!db)
 			goto err_xfer;
 
 		size = 1 << DOORBELL_REG_WIDTH(flags);
 		phys_addr = le32_to_cpu(resp->db_addr_low);
 		phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
-		addr = devm_ioremap(handle->dev, phys_addr, size);
+		addr = devm_ioremap(ph->dev, phys_addr, size);
 		if (!addr)
 			goto err_xfer;
 
@@ -605,25 +601,25 @@ scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
 		*p_db = db;
 	}
 err_xfer:
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 }
 
-static void scmi_perf_domain_init_fc(const struct scmi_handle *handle,
+static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
 				     u32 domain, struct scmi_fc_info **p_fc)
 {
 	struct scmi_fc_info *fc;
 
-	fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL);
+	fc = devm_kzalloc(ph->dev, sizeof(*fc), GFP_KERNEL);
 	if (!fc)
 		return;
 
-	scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET,
+	scmi_perf_domain_desc_fc(ph, domain, PERF_LEVEL_SET,
 				 &fc->level_set_addr, &fc->level_set_db);
-	scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET,
+	scmi_perf_domain_desc_fc(ph, domain, PERF_LEVEL_GET,
 				 &fc->level_get_addr, NULL);
-	scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET,
+	scmi_perf_domain_desc_fc(ph, domain, PERF_LIMITS_SET,
 				 &fc->limit_set_addr, &fc->limit_set_db);
-	scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET,
+	scmi_perf_domain_desc_fc(ph, domain, PERF_LIMITS_GET,
 				 &fc->limit_get_addr, NULL);
 	*p_fc = fc;
 }
@@ -640,14 +636,14 @@ static int scmi_dev_domain_id(struct device *dev)
 	return clkspec.args[0];
 }
 
-static int scmi_dvfs_device_opps_add(const struct scmi_handle *handle,
+static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph,
 				     struct device *dev)
 {
 	int idx, ret, domain;
 	unsigned long freq;
 	struct scmi_opp *opp;
 	struct perf_dom_info *dom;
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 
 	domain = scmi_dev_domain_id(dev);
 	if (domain < 0)
@@ -672,11 +668,12 @@ static int scmi_dvfs_device_opps_add(const struct scmi_handle *handle,
 	return 0;
 }
 
-static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle,
-					    struct device *dev)
+static int
+scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph,
+				 struct device *dev)
 {
 	struct perf_dom_info *dom;
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	int domain = scmi_dev_domain_id(dev);
 
 	if (domain < 0)
@@ -687,35 +684,35 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle,
 	return dom->opp[dom->opp_count - 1].trans_latency_us * 1000;
 }
 
-static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
+static int scmi_dvfs_freq_set(const struct scmi_protocol_handle *ph, u32 domain,
 			      unsigned long freq, bool poll)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
-	return scmi_perf_level_set(handle, domain, freq / dom->mult_factor,
-				   poll);
+	return scmi_perf_level_set(ph, domain, freq / dom->mult_factor, poll);
 }
 
-static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
+static int scmi_dvfs_freq_get(const struct scmi_protocol_handle *ph, u32 domain,
 			      unsigned long *freq, bool poll)
 {
 	int ret;
 	u32 level;
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
-	ret = scmi_perf_level_get(handle, domain, &level, poll);
+	ret = scmi_perf_level_get(ph, domain, &level, poll);
 	if (!ret)
 		*freq = level * dom->mult_factor;
 
 	return ret;
 }
 
-static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
-				   unsigned long *freq, unsigned long *power)
+static int scmi_dvfs_est_power_get(const struct scmi_protocol_handle *ph,
+				   u32 domain, unsigned long *freq,
+				   unsigned long *power)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 	struct perf_dom_info *dom;
 	unsigned long opp_freq;
 	int idx, ret = -EINVAL;
@@ -739,11 +736,11 @@ static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
 	return ret;
 }
 
-static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
+static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
 				      struct device *dev)
 {
 	struct perf_dom_info *dom;
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 
 	dom = pi->dom_info + scmi_dev_domain_id(dev);
 
@@ -764,7 +761,7 @@ static const struct scmi_perf_ops perf_ops = {
 	.fast_switch_possible = scmi_fast_switch_possible,
 };
 
-static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					u8 evt_id, u32 src_id, bool enable)
 {
 	int ret, cmd_id;
@@ -773,7 +770,7 @@ static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
 		return -EINVAL;
 
 	cmd_id = evt_2_cmd[evt_id];
-	ret = scmi_perf_level_limits_notify(handle, src_id, cmd_id, enable);
+	ret = scmi_perf_level_limits_notify(ph, src_id, cmd_id, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
 			 evt_id, src_id, ret);
@@ -781,7 +778,7 @@ static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
 	return ret;
 }
 
-static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle,
+static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
 					  u8 evt_id, ktime_t timestamp,
 					  const void *payld, size_t payld_sz,
 					  void *report, u32 *src_id)
@@ -829,9 +826,9 @@ static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle,
 	return rep;
 }
 
-static int scmi_perf_get_num_sources(const struct scmi_handle *handle)
+static int scmi_perf_get_num_sources(const struct scmi_protocol_handle *ph)
 {
-	struct scmi_perf_info *pi = handle->perf_priv;
+	struct scmi_perf_info *pi = ph->get_priv(ph);
 
 	if (!pi)
 		return -EINVAL;
@@ -865,24 +862,24 @@ static const struct scmi_protocol_events perf_protocol_events = {
 	.num_events = ARRAY_SIZE(perf_events),
 };
 
-static int scmi_perf_protocol_init(struct scmi_handle *handle)
+static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	int domain;
 	u32 version;
 	struct scmi_perf_info *pinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_PERF, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "Performance Version %d.%d\n",
+	dev_dbg(ph->dev, "Performance Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
 	if (!pinfo)
 		return -ENOMEM;
 
-	scmi_perf_attributes_get(handle, pinfo);
+	scmi_perf_attributes_get(ph, pinfo);
 
-	pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
 				       sizeof(*pinfo->dom_info), GFP_KERNEL);
 	if (!pinfo->dom_info)
 		return -ENOMEM;
@@ -890,18 +887,15 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
 	for (domain = 0; domain < pinfo->num_domains; domain++) {
 		struct perf_dom_info *dom = pinfo->dom_info + domain;
 
-		scmi_perf_domain_attributes_get(handle, domain, dom);
-		scmi_perf_describe_levels_get(handle, domain, dom);
+		scmi_perf_domain_attributes_get(ph, domain, dom);
+		scmi_perf_describe_levels_get(ph, domain, dom);
 
 		if (dom->perf_fastchannels)
-			scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
+			scmi_perf_domain_init_fc(ph, domain, &dom->fc_info);
 	}
 
 	pinfo->version = version;
-	handle->perf_ops = &perf_ops;
-	handle->perf_priv = pinfo;
-
-	return 0;
+	return ph->set_priv(ph, pinfo);
 }
 
 static const struct scmi_protocol scmi_perf = {
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 6db02dc547a7..d9ce794d0299 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -68,21 +68,21 @@ struct scmi_power_info {
 	struct power_dom_info *dom_info;
 };
 
-static int scmi_power_attributes_get(const struct scmi_handle *handle,
+static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
 				     struct scmi_power_info *pi)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_power_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		pi->num_domains = le16_to_cpu(attr->num_domains);
 		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
@@ -90,28 +90,27 @@ static int scmi_power_attributes_get(const struct scmi_handle *handle,
 		pi->stats_size = le32_to_cpu(attr->stats_size);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
-				 struct power_dom_info *dom_info)
+scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
+				 u32 domain, struct power_dom_info *dom_info)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_power_domain_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES,
-				 SCMI_PROTOCOL_POWER, sizeof(domain),
-				 sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,
+				      sizeof(domain), sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(domain, t->tx.buf);
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		u32 flags = le32_to_cpu(attr->flags);
 
@@ -121,19 +120,18 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
 		strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int
-scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state)
+static int scmi_power_state_set(const struct scmi_protocol_handle *ph,
+				u32 domain, u32 state)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_power_set_state *st;
 
-	ret = scmi_xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
-				 sizeof(*st), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);
 	if (ret)
 		return ret;
 
@@ -142,43 +140,43 @@ scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state)
 	st->domain = cpu_to_le32(domain);
 	st->state = cpu_to_le32(state);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int
-scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
+static int scmi_power_state_get(const struct scmi_protocol_handle *ph,
+				u32 domain, u32 *state)
 {
 	int ret;
 	struct scmi_xfer *t;
 
-	ret = scmi_xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
-				 sizeof(u32), sizeof(u32), &t);
+	ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(domain, t->tx.buf);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret)
 		*state = get_unaligned_le32(t->rx.buf);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_power_num_domains_get(const struct scmi_handle *handle)
+static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
 {
-	struct scmi_power_info *pi = handle->power_priv;
+	struct scmi_power_info *pi = ph->get_priv(ph);
 
 	return pi->num_domains;
 }
 
-static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain)
+static char *scmi_power_name_get(const struct scmi_protocol_handle *ph,
+				 u32 domain)
 {
-	struct scmi_power_info *pi = handle->power_priv;
+	struct scmi_power_info *pi = ph->get_priv(ph);
 	struct power_dom_info *dom = pi->dom_info + domain;
 
 	return dom->name;
@@ -191,15 +189,15 @@ static const struct scmi_power_ops power_ops = {
 	.state_get = scmi_power_state_get,
 };
 
-static int scmi_power_request_notify(const struct scmi_handle *handle,
+static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
 				     u32 domain, bool enable)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_power_state_notify *notify;
 
-	ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY,
-				 SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY,
+				      sizeof(*notify), 0, &t);
 	if (ret)
 		return ret;
 
@@ -207,18 +205,18 @@ static int scmi_power_request_notify(const struct scmi_handle *handle,
 	notify->domain = cpu_to_le32(domain);
 	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_power_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					 u8 evt_id, u32 src_id, bool enable)
 {
 	int ret;
 
-	ret = scmi_power_request_notify(handle, src_id, enable);
+	ret = scmi_power_request_notify(ph, src_id, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
 			 evt_id, src_id, ret);
@@ -226,10 +224,10 @@ static int scmi_power_set_notify_enabled(const struct scmi_handle *handle,
 	return ret;
 }
 
-static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
-					   u8 evt_id, ktime_t timestamp,
-					   const void *payld, size_t payld_sz,
-					   void *report, u32 *src_id)
+static void *
+scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph, u8 evt_id,
+			      ktime_t timestamp, const void *payld,
+			      size_t payld_sz, void *report, u32 *src_id)
 {
 	const struct scmi_power_state_notify_payld *p = payld;
 	struct scmi_power_state_changed_report *r = report;
@@ -246,9 +244,9 @@ static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
-static int scmi_power_get_num_sources(const struct scmi_handle *handle)
+static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph)
 {
-	struct scmi_power_info *pinfo = handle->power_priv;
+	struct scmi_power_info *pinfo = ph->get_priv(ph);
 
 	if (!pinfo)
 		return -EINVAL;
@@ -278,24 +276,24 @@ static const struct scmi_protocol_events power_protocol_events = {
 	.num_events = ARRAY_SIZE(power_events),
 };
 
-static int scmi_power_protocol_init(struct scmi_handle *handle)
+static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	int domain;
 	u32 version;
 	struct scmi_power_info *pinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "Power Version %d.%d\n",
+	dev_dbg(ph->dev, "Power Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
 	if (!pinfo)
 		return -ENOMEM;
 
-	scmi_power_attributes_get(handle, pinfo);
+	scmi_power_attributes_get(ph, pinfo);
 
-	pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
 				       sizeof(*pinfo->dom_info), GFP_KERNEL);
 	if (!pinfo->dom_info)
 		return -ENOMEM;
@@ -303,14 +301,11 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
 	for (domain = 0; domain < pinfo->num_domains; domain++) {
 		struct power_dom_info *dom = pinfo->dom_info + domain;
 
-		scmi_power_domain_attributes_get(handle, domain, dom);
+		scmi_power_domain_attributes_get(ph, domain, dom);
 	}
 
 	pinfo->version = version;
-	handle->power_ops = &power_ops;
-	handle->power_priv = pinfo;
-
-	return 0;
+	return ph->set_priv(ph, pinfo);
 }
 
 static const struct scmi_protocol scmi_power = {
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index 7102cfeb2397..a183518baf18 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -64,46 +64,45 @@ struct scmi_reset_info {
 	struct reset_dom_info *dom_info;
 };
 
-static int scmi_reset_attributes_get(const struct scmi_handle *handle,
+static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
 				     struct scmi_reset_info *pi)
 {
 	int ret;
 	struct scmi_xfer *t;
 	u32 attr;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(attr), &t);
 	if (ret)
 		return ret;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		attr = get_unaligned_le32(t->rx.buf);
 		pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
-				 struct reset_dom_info *dom_info)
+scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
+				 u32 domain, struct reset_dom_info *dom_info)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_reset_domain_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES,
-				 SCMI_PROTOCOL_RESET, sizeof(domain),
-				 sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
+				      sizeof(domain), sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	put_unaligned_le32(domain, t->tx.buf);
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		u32 attributes = le32_to_cpu(attr->attributes);
 
@@ -115,47 +114,49 @@ scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
 		strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_reset_num_domains_get(const struct scmi_handle *handle)
+static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
 {
-	struct scmi_reset_info *pi = handle->reset_priv;
+	struct scmi_reset_info *pi = ph->get_priv(ph);
 
 	return pi->num_domains;
 }
 
-static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain)
+static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph,
+				 u32 domain)
 {
-	struct scmi_reset_info *pi = handle->reset_priv;
+	struct scmi_reset_info *pi = ph->get_priv(ph);
+
 	struct reset_dom_info *dom = pi->dom_info + domain;
 
 	return dom->name;
 }
 
-static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain)
+static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,
+				  u32 domain)
 {
-	struct scmi_reset_info *pi = handle->reset_priv;
+	struct scmi_reset_info *pi = ph->get_priv(ph);
 	struct reset_dom_info *dom = pi->dom_info + domain;
 
 	return dom->latency_us;
 }
 
-static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain,
+static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
 			     u32 flags, u32 state)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_reset_domain_reset *dom;
-	struct scmi_reset_info *pi = handle->reset_priv;
+	struct scmi_reset_info *pi = ph->get_priv(ph);
 	struct reset_dom_info *rdom = pi->dom_info + domain;
 
 	if (rdom->async_reset)
 		flags |= ASYNCHRONOUS_RESET;
 
-	ret = scmi_xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET,
-				 sizeof(*dom), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
 	if (ret)
 		return ret;
 
@@ -165,31 +166,32 @@ static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain,
 	dom->reset_state = cpu_to_le32(state);
 
 	if (rdom->async_reset)
-		ret = scmi_do_xfer_with_response(handle, t);
+		ret = ph->xops->do_xfer_with_response(ph, t);
 	else
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_reset_domain_reset(const struct scmi_handle *handle, u32 domain)
+static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph,
+				   u32 domain)
 {
-	return scmi_domain_reset(handle, domain, AUTONOMOUS_RESET,
+	return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET,
 				 ARCH_COLD_RESET);
 }
 
 static int
-scmi_reset_domain_assert(const struct scmi_handle *handle, u32 domain)
+scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain)
 {
-	return scmi_domain_reset(handle, domain, EXPLICIT_RESET_ASSERT,
+	return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT,
 				 ARCH_COLD_RESET);
 }
 
 static int
-scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain)
+scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain)
 {
-	return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET);
+	return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET);
 }
 
 static const struct scmi_reset_ops reset_ops = {
@@ -201,16 +203,15 @@ static const struct scmi_reset_ops reset_ops = {
 	.deassert = scmi_reset_domain_deassert,
 };
 
-static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id,
-			     bool enable)
+static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
+			     u32 domain_id, bool enable)
 {
 	int ret;
 	u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;
 	struct scmi_xfer *t;
 	struct scmi_msg_reset_notify *cfg;
 
-	ret = scmi_xfer_get_init(handle, RESET_NOTIFY,
-				 SCMI_PROTOCOL_RESET, sizeof(*cfg), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, RESET_NOTIFY, sizeof(*cfg), 0, &t);
 	if (ret)
 		return ret;
 
@@ -218,18 +219,18 @@ static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id,
 	cfg->id = cpu_to_le32(domain_id);
 	cfg->event_control = cpu_to_le32(evt_cntl);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_reset_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_reset_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					 u8 evt_id, u32 src_id, bool enable)
 {
 	int ret;
 
-	ret = scmi_reset_notify(handle, src_id, enable);
+	ret = scmi_reset_notify(ph, src_id, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
 			 evt_id, src_id, ret);
@@ -237,10 +238,11 @@ static int scmi_reset_set_notify_enabled(const struct scmi_handle *handle,
 	return ret;
 }
 
-static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle,
-					   u8 evt_id, ktime_t timestamp,
-					   const void *payld, size_t payld_sz,
-					   void *report, u32 *src_id)
+static void *
+scmi_reset_fill_custom_report(const struct scmi_protocol_handle *ph,
+			      u8 evt_id, ktime_t timestamp,
+			      const void *payld, size_t payld_sz,
+			      void *report, u32 *src_id)
 {
 	const struct scmi_reset_issued_notify_payld *p = payld;
 	struct scmi_reset_issued_report *r = report;
@@ -257,9 +259,9 @@ static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
-static int scmi_reset_get_num_sources(const struct scmi_handle *handle)
+static int scmi_reset_get_num_sources(const struct scmi_protocol_handle *ph)
 {
-	struct scmi_reset_info *pinfo = handle->reset_priv;
+	struct scmi_reset_info *pinfo = ph->get_priv(ph);
 
 	if (!pinfo)
 		return -EINVAL;
@@ -288,24 +290,24 @@ static const struct scmi_protocol_events reset_protocol_events = {
 	.num_events = ARRAY_SIZE(reset_events),
 };
 
-static int scmi_reset_protocol_init(struct scmi_handle *handle)
+static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	int domain;
 	u32 version;
 	struct scmi_reset_info *pinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "Reset Version %d.%d\n",
+	dev_dbg(ph->dev, "Reset Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
 	if (!pinfo)
 		return -ENOMEM;
 
-	scmi_reset_attributes_get(handle, pinfo);
+	scmi_reset_attributes_get(ph, pinfo);
 
-	pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
 				       sizeof(*pinfo->dom_info), GFP_KERNEL);
 	if (!pinfo->dom_info)
 		return -ENOMEM;
@@ -313,14 +315,11 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
 	for (domain = 0; domain < pinfo->num_domains; domain++) {
 		struct reset_dom_info *dom = pinfo->dom_info + domain;
 
-		scmi_reset_domain_attributes_get(handle, domain, dom);
+		scmi_reset_domain_attributes_get(ph, domain, dom);
 	}
 
 	pinfo->version = version;
-	handle->reset_ops = &reset_ops;
-	handle->reset_priv = pinfo;
-
-	return 0;
+	return ph->set_priv(ph, pinfo);
 }
 
 static const struct scmi_protocol scmi_reset = {
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
index 9e44479f0284..2866db5d7cf9 100644
--- a/drivers/firmware/arm_scmi/scmi_pm_domain.c
+++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c
@@ -2,7 +2,7 @@
 /*
  * SCMI Generic power domain support.
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  */
 
 #include <linux/err.h>
@@ -11,9 +11,11 @@
 #include <linux/pm_domain.h>
 #include <linux/scmi_protocol.h>
 
+static const struct scmi_power_ops *power_ops;
+
 struct scmi_pm_domain {
 	struct generic_pm_domain genpd;
-	const struct scmi_handle *handle;
+	const struct scmi_protocol_handle *ph;
 	const char *name;
 	u32 domain;
 };
@@ -25,16 +27,15 @@ static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
 	int ret;
 	u32 state, ret_state;
 	struct scmi_pm_domain *pd = to_scmi_pd(domain);
-	const struct scmi_power_ops *ops = pd->handle->power_ops;
 
 	if (power_on)
 		state = SCMI_POWER_STATE_GENERIC_ON;
 	else
 		state = SCMI_POWER_STATE_GENERIC_OFF;
 
-	ret = ops->state_set(pd->handle, pd->domain, state);
+	ret = power_ops->state_set(pd->ph, pd->domain, state);
 	if (!ret)
-		ret = ops->state_get(pd->handle, pd->domain, &ret_state);
+		ret = power_ops->state_get(pd->ph, pd->domain, &ret_state);
 	if (!ret && state != ret_state)
 		return -EIO;
 
@@ -60,11 +61,16 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 	struct genpd_onecell_data *scmi_pd_data;
 	struct generic_pm_domain **domains;
 	const struct scmi_handle *handle = sdev->handle;
+	struct scmi_protocol_handle *ph;
 
-	if (!handle || !handle->power_ops)
+	if (!handle)
 		return -ENODEV;
 
-	num_domains = handle->power_ops->num_domains_get(handle);
+	power_ops = handle->devm_get_ops(sdev, SCMI_PROTOCOL_POWER, &ph);
+	if (IS_ERR(power_ops))
+		return PTR_ERR(power_ops);
+
+	num_domains = power_ops->num_domains_get(ph);
 	if (num_domains < 0) {
 		dev_err(dev, "number of domains not found\n");
 		return num_domains;
@@ -85,14 +91,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 	for (i = 0; i < num_domains; i++, scmi_pd++) {
 		u32 state;
 
-		if (handle->power_ops->state_get(handle, i, &state)) {
+		if (power_ops->state_get(ph, i, &state)) {
 			dev_warn(dev, "failed to get state for domain %d\n", i);
 			continue;
 		}
 
 		scmi_pd->domain = i;
-		scmi_pd->handle = handle;
-		scmi_pd->name = handle->power_ops->name_get(handle, i);
+		scmi_pd->ph = ph;
+		scmi_pd->name = power_ops->name_get(ph, i);
 		scmi_pd->genpd.name = scmi_pd->name;
 		scmi_pd->genpd.power_off = scmi_pd_power_off;
 		scmi_pd->genpd.power_on = scmi_pd_power_on;
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 827dc0aa519c..a74a7938cd78 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -87,21 +87,21 @@ struct sensors_info {
 	struct scmi_sensor_info *sensors;
 };
 
-static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
+static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
 				      struct sensors_info *si)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_sensor_attributes *attr;
 
-	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
-				 SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(*attr), &t);
 	if (ret)
 		return ret;
 
 	attr = t->rx.buf;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 	if (!ret) {
 		si->num_sensors = le16_to_cpu(attr->num_sensors);
 		si->max_requests = attr->max_requests;
@@ -110,11 +110,11 @@ static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
 		si->reg_size = le32_to_cpu(attr->reg_size);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_sensor_description_get(const struct scmi_handle *handle,
+static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph,
 				       struct sensors_info *si)
 {
 	int ret, cnt;
@@ -123,8 +123,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
 	struct scmi_xfer *t;
 	struct scmi_msg_resp_sensor_description *buf;
 
-	ret = scmi_xfer_get_init(handle, SENSOR_DESCRIPTION_GET,
-				 SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, SENSOR_DESCRIPTION_GET,
+				      sizeof(__le32), 0, &t);
 	if (ret)
 		return ret;
 
@@ -134,7 +134,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
 		/* Set the number of sensors to be skipped/already read */
 		put_unaligned_le32(desc_index, t->tx.buf);
 
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 		if (ret)
 			break;
 
@@ -142,7 +142,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
 		num_remaining = le16_to_cpu(buf->num_remaining);
 
 		if (desc_index + num_returned > si->num_sensors) {
-			dev_err(handle->dev, "No. of sensors can't exceed %d",
+			dev_err(ph->dev, "No. of sensors can't exceed %d",
 				si->num_sensors);
 			break;
 		}
@@ -167,18 +167,18 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
 
 		desc_index += num_returned;
 
-		scmi_reset_rx_to_maxsz(handle, t);
+		ph->xops->reset_rx_to_maxsz(ph, t);
 		/*
 		 * check for both returned and remaining to avoid infinite
 		 * loop due to buggy firmware
 		 */
 	} while (num_returned && num_remaining);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle,
+static int scmi_sensor_trip_point_notify(const struct scmi_protocol_handle *ph,
 					 u32 sensor_id, bool enable)
 {
 	int ret;
@@ -186,8 +186,8 @@ static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle,
 	struct scmi_xfer *t;
 	struct scmi_msg_sensor_trip_point_notify *cfg;
 
-	ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY,
-				 SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, SENSOR_TRIP_POINT_NOTIFY,
+				      sizeof(*cfg), 0, &t);
 	if (ret)
 		return ret;
 
@@ -195,23 +195,23 @@ static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle,
 	cfg->id = cpu_to_le32(sensor_id);
 	cfg->event_control = cpu_to_le32(evt_cntl);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static int
-scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id,
-			      u8 trip_id, u64 trip_value)
+scmi_sensor_trip_point_config(const struct scmi_protocol_handle *ph,
+			      u32 sensor_id, u8 trip_id, u64 trip_value)
 {
 	int ret;
 	u32 evt_cntl = SENSOR_TP_BOTH;
 	struct scmi_xfer *t;
 	struct scmi_msg_set_sensor_trip_point *trip;
 
-	ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG,
-				 SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, SENSOR_TRIP_POINT_CONFIG,
+				      sizeof(*trip), 0, &t);
 	if (ret)
 		return ret;
 
@@ -221,24 +221,23 @@ scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id,
 	trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
 	trip->value_high = cpu_to_le32(trip_value >> 32);
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_sensor_reading_get(const struct scmi_handle *handle,
+static int scmi_sensor_reading_get(const struct scmi_protocol_handle *ph,
 				   u32 sensor_id, u64 *value)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_msg_sensor_reading_get *sensor;
-	struct sensors_info *si = handle->sensor_priv;
+	struct sensors_info *si = ph->get_priv(ph);
 	struct scmi_sensor_info *s = si->sensors + sensor_id;
 
-	ret = scmi_xfer_get_init(handle, SENSOR_READING_GET,
-				 SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
-				 sizeof(u64), &t);
+	ret = ph->xops->xfer_get_init(ph, SENSOR_READING_GET,
+				      sizeof(*sensor), sizeof(u64), &t);
 	if (ret)
 		return ret;
 
@@ -247,32 +246,32 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle,
 
 	if (s->async) {
 		sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC);
-		ret = scmi_do_xfer_with_response(handle, t);
+		ret = ph->xops->do_xfer_with_response(ph, t);
 		if (!ret)
 			*value = get_unaligned_le64((void *)
 						    ((__le32 *)t->rx.buf + 1));
 	} else {
 		sensor->flags = cpu_to_le32(0);
-		ret = scmi_do_xfer(handle, t);
+		ret = ph->xops->do_xfer(ph, t);
 		if (!ret)
 			*value = get_unaligned_le64(t->rx.buf);
 	}
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
 static const struct scmi_sensor_info *
-scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
+scmi_sensor_info_get(const struct scmi_protocol_handle *ph, u32 sensor_id)
 {
-	struct sensors_info *si = handle->sensor_priv;
+	struct sensors_info *si = ph->get_priv(ph);
 
 	return si->sensors + sensor_id;
 }
 
-static int scmi_sensor_count_get(const struct scmi_handle *handle)
+static int scmi_sensor_count_get(const struct scmi_protocol_handle *ph)
 {
-	struct sensors_info *si = handle->sensor_priv;
+	struct sensors_info *si = ph->get_priv(ph);
 
 	return si->num_sensors;
 }
@@ -284,12 +283,12 @@ static const struct scmi_sensor_ops sensor_ops = {
 	.reading_get = scmi_sensor_reading_get,
 };
 
-static int scmi_sensor_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					  u8 evt_id, u32 src_id, bool enable)
 {
 	int ret;
 
-	ret = scmi_sensor_trip_point_notify(handle, src_id, enable);
+	ret = scmi_sensor_trip_point_notify(ph, src_id, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
 			 evt_id, src_id, ret);
@@ -297,10 +296,11 @@ static int scmi_sensor_set_notify_enabled(const struct scmi_handle *handle,
 	return ret;
 }
 
-static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle,
-					    u8 evt_id, ktime_t timestamp,
-					    const void *payld, size_t payld_sz,
-					    void *report, u32 *src_id)
+static void *
+scmi_sensor_fill_custom_report(const struct scmi_protocol_handle *ph,
+			       u8 evt_id, ktime_t timestamp,
+			       const void *payld, size_t payld_sz,
+			       void *report, u32 *src_id)
 {
 	const struct scmi_sensor_trip_notify_payld *p = payld;
 	struct scmi_sensor_trip_point_report *r = report;
@@ -318,9 +318,9 @@ static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle,
 	return r;
 }
 
-static int scmi_sensor_get_num_sources(const struct scmi_handle *handle)
+static int scmi_sensor_get_num_sources(const struct scmi_protocol_handle *ph)
 {
-	struct sensors_info *si = handle->sensor_priv;
+	struct sensors_info *si = ph->get_priv(ph);
 
 	return si->num_sensors;
 }
@@ -346,34 +346,31 @@ static const struct scmi_protocol_events sensor_protocol_events = {
 	.num_events = ARRAY_SIZE(sensor_events),
 };
 
-static int scmi_sensors_protocol_init(struct scmi_handle *handle)
+static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	u32 version;
 	struct sensors_info *sinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "Sensor Version %d.%d\n",
+	dev_dbg(ph->dev, "Sensor Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	sinfo = devm_kzalloc(handle->dev, sizeof(*sinfo), GFP_KERNEL);
+	sinfo = devm_kzalloc(ph->dev, sizeof(*sinfo), GFP_KERNEL);
 	if (!sinfo)
 		return -ENOMEM;
 
-	scmi_sensor_attributes_get(handle, sinfo);
+	scmi_sensor_attributes_get(ph, sinfo);
 
-	sinfo->sensors = devm_kcalloc(handle->dev, sinfo->num_sensors,
+	sinfo->sensors = devm_kcalloc(ph->dev, sinfo->num_sensors,
 				      sizeof(*sinfo->sensors), GFP_KERNEL);
 	if (!sinfo->sensors)
 		return -ENOMEM;
 
-	scmi_sensor_description_get(handle, sinfo);
+	scmi_sensor_description_get(ph, sinfo);
 
 	sinfo->version = version;
-	handle->sensor_ops = &sensor_ops;
-	handle->sensor_priv = sinfo;
-
-	return 0;
+	return ph->set_priv(ph, sinfo);
 }
 
 static const struct scmi_protocol scmi_sensors = {
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 167f539f7d95..23365ef334bf 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -32,43 +32,43 @@ struct scmi_system_info {
 	u32 version;
 };
 
-static int scmi_system_request_notify(const struct scmi_handle *handle,
+static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
 				      bool enable)
 {
 	int ret;
 	struct scmi_xfer *t;
 	struct scmi_system_power_state_notify *notify;
 
-	ret = scmi_xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY,
-				 SCMI_PROTOCOL_SYSTEM, sizeof(*notify), 0, &t);
+	ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY,
+				      sizeof(*notify), 0, &t);
 	if (ret)
 		return ret;
 
 	notify = t->tx.buf;
 	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
 
-	ret = scmi_do_xfer(handle, t);
+	ret = ph->xops->do_xfer(ph, t);
 
-	scmi_xfer_put(handle, t);
+	ph->xops->xfer_put(ph, t);
 	return ret;
 }
 
-static int scmi_system_set_notify_enabled(const struct scmi_handle *handle,
+static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph,
 					  u8 evt_id, u32 src_id, bool enable)
 {
 	int ret;
 
-	ret = scmi_system_request_notify(handle, enable);
+	ret = scmi_system_request_notify(ph, enable);
 	if (ret)
 		pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
 
 	return ret;
 }
 
-static void *scmi_system_fill_custom_report(const struct scmi_handle *handle,
-					    u8 evt_id, ktime_t timestamp,
-					    const void *payld, size_t payld_sz,
-					    void *report, u32 *src_id)
+static void *
+scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph, u8 evt_id,
+			       ktime_t timestamp, const void *payld,
+			       size_t payld_sz, void *report, u32 *src_id)
 {
 	const struct scmi_system_power_state_notifier_payld *p = payld;
 	struct scmi_system_power_state_notifier_report *r = report;
@@ -109,24 +109,22 @@ static const struct scmi_protocol_events system_protocol_events = {
 	.num_sources = SCMI_SYSTEM_NUM_SOURCES,
 };
 
-static int scmi_system_protocol_init(struct scmi_handle *handle)
+static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	u32 version;
 	struct scmi_system_info *pinfo;
 
-	scmi_version_get(handle, SCMI_PROTOCOL_SYSTEM, &version);
+	ph->xops->version_get(ph, &version);
 
-	dev_dbg(handle->dev, "System Power Version %d.%d\n",
+	dev_dbg(ph->dev, "System Power Version %d.%d\n",
 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 
-	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
 	if (!pinfo)
 		return -ENOMEM;
 
 	pinfo->version = version;
-	handle->system_priv = pinfo;
-
-	return 0;
+	return ph->set_priv(ph, pinfo);
 }
 
 static const struct scmi_protocol scmi_system = {
diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
index d421e691318b..3cb9a5c3e560 100644
--- a/drivers/hwmon/scmi-hwmon.c
+++ b/drivers/hwmon/scmi-hwmon.c
@@ -2,7 +2,7 @@
 /*
  * System Control and Management Interface(SCMI) based hwmon sensor driver
  *
- * Copyright (C) 2018 ARM Ltd.
+ * Copyright (C) 2018-2020 ARM Ltd.
  * Sudeep Holla <sudeep.holla@arm.com>
  */
 
@@ -13,8 +13,10 @@
 #include <linux/sysfs.h>
 #include <linux/thermal.h>
 
+static const struct scmi_sensor_ops *sensor_ops;
+
 struct scmi_sensors {
-	const struct scmi_handle *handle;
+	const struct scmi_protocol_handle *ph;
 	const struct scmi_sensor_info **info[hwmon_max];
 };
 
@@ -69,10 +71,9 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 	u64 value;
 	const struct scmi_sensor_info *sensor;
 	struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
-	const struct scmi_handle *h = scmi_sensors->handle;
 
 	sensor = *(scmi_sensors->info[type] + channel);
-	ret = h->sensor_ops->reading_get(h, sensor->id, &value);
+	ret = sensor_ops->reading_get(scmi_sensors->ph, sensor->id, &value);
 	if (ret)
 		return ret;
 
@@ -169,11 +170,16 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
 	struct hwmon_channel_info *scmi_hwmon_chan;
 	const struct hwmon_channel_info **ptr_scmi_ci;
 	const struct scmi_handle *handle = sdev->handle;
+	struct scmi_protocol_handle *ph;
 
-	if (!handle || !handle->sensor_ops)
+	if (!handle)
 		return -ENODEV;
 
-	nr_sensors = handle->sensor_ops->count_get(handle);
+	sensor_ops = handle->devm_get_ops(sdev, SCMI_PROTOCOL_SENSOR, &ph);
+	if (IS_ERR(sensor_ops))
+		return PTR_ERR(sensor_ops);
+
+	nr_sensors = sensor_ops->count_get(ph);
 	if (!nr_sensors)
 		return -EIO;
 
@@ -181,10 +187,10 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
 	if (!scmi_sensors)
 		return -ENOMEM;
 
-	scmi_sensors->handle = handle;
+	scmi_sensors->ph = ph;
 
 	for (i = 0; i < nr_sensors; i++) {
-		sensor = handle->sensor_ops->info_get(handle, i);
+		sensor = sensor_ops->info_get(ph, i);
 		if (!sensor)
 			return -EINVAL;
 
@@ -234,7 +240,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
 	}
 
 	for (i = nr_sensors - 1; i >= 0 ; i--) {
-		sensor = handle->sensor_ops->info_get(handle, i);
+		sensor = sensor_ops->info_get(ph, i);
 		if (!sensor)
 			continue;
 
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index 8d3a858e3b19..c3d97dafdda4 100644
--- a/drivers/reset/reset-scmi.c
+++ b/drivers/reset/reset-scmi.c
@@ -2,7 +2,7 @@
 /*
  * ARM System Control and Management Interface (ARM SCMI) reset driver
  *
- * Copyright (C) 2019 ARM Ltd.
+ * Copyright (C) 2019-2020 ARM Ltd.
  */
 
 #include <linux/module.h>
@@ -11,18 +11,20 @@
 #include <linux/reset-controller.h>
 #include <linux/scmi_protocol.h>
 
+static const struct scmi_reset_ops *reset_ops;
+
 /**
  * struct scmi_reset_data - reset controller information structure
  * @rcdev: reset controller entity
- * @handle: ARM SCMI handle used for communication with system controller
+ * @ph: ARM SCMI protocol handle used for communication with system controller
  */
 struct scmi_reset_data {
 	struct reset_controller_dev rcdev;
-	const struct scmi_handle *handle;
+	const struct scmi_protocol_handle *ph;
 };
 
 #define to_scmi_reset_data(p)	container_of((p), struct scmi_reset_data, rcdev)
-#define to_scmi_handle(p)	(to_scmi_reset_data(p)->handle)
+#define to_scmi_handle(p)	(to_scmi_reset_data(p)->ph)
 
 /**
  * scmi_reset_assert() - assert device reset
@@ -37,9 +39,9 @@ struct scmi_reset_data {
 static int
 scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
 {
-	const struct scmi_handle *handle = to_scmi_handle(rcdev);
+	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
 
-	return handle->reset_ops->assert(handle, id);
+	return reset_ops->assert(ph, id);
 }
 
 /**
@@ -55,9 +57,9 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
 static int
 scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
 {
-	const struct scmi_handle *handle = to_scmi_handle(rcdev);
+	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
 
-	return handle->reset_ops->deassert(handle, id);
+	return reset_ops->deassert(ph, id);
 }
 
 /**
@@ -73,9 +75,9 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
 static int
 scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
 {
-	const struct scmi_handle *handle = to_scmi_handle(rcdev);
+	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
 
-	return handle->reset_ops->reset(handle, id);
+	return reset_ops->reset(ph, id);
 }
 
 static const struct reset_control_ops scmi_reset_ops = {
@@ -90,10 +92,15 @@ static int scmi_reset_probe(struct scmi_device *sdev)
 	struct device *dev = &sdev->dev;
 	struct device_node *np = dev->of_node;
 	const struct scmi_handle *handle = sdev->handle;
+	struct scmi_protocol_handle *ph;
 
-	if (!handle || !handle->reset_ops)
+	if (!handle)
 		return -ENODEV;
 
+	reset_ops = handle->devm_get_ops(sdev, SCMI_PROTOCOL_RESET, &ph);
+	if (IS_ERR(reset_ops))
+		return PTR_ERR(reset_ops);
+
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -101,8 +108,8 @@ static int scmi_reset_probe(struct scmi_device *sdev)
 	data->rcdev.ops = &scmi_reset_ops;
 	data->rcdev.owner = THIS_MODULE;
 	data->rcdev.of_node = np;
-	data->rcdev.nr_resets = handle->reset_ops->num_domains_get(handle);
-	data->handle = handle;
+	data->rcdev.nr_resets = reset_ops->num_domains_get(ph);
+	data->ph = ph;
 
 	return devm_reset_controller_register(dev, &data->rcdev);
 }
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index d95e43c3ea08..383b3d0ca383 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -71,16 +71,16 @@ struct scmi_protocol_handle;
  * @disable: disables the specified clock
  */
 struct scmi_clk_ops {
-	int (*count_get)(const struct scmi_handle *handle);
+	int (*count_get)(const struct scmi_protocol_handle *ph);
 
 	const struct scmi_clock_info *(*info_get)
-		(const struct scmi_handle *handle, u32 clk_id);
-	int (*rate_get)(const struct scmi_handle *handle, u32 clk_id,
+		(const struct scmi_protocol_handle *ph, u32 clk_id);
+	int (*rate_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
 			u64 *rate);
-	int (*rate_set)(const struct scmi_handle *handle, u32 clk_id,
+	int (*rate_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
 			u64 rate);
-	int (*enable)(const struct scmi_handle *handle, u32 clk_id);
-	int (*disable)(const struct scmi_handle *handle, u32 clk_id);
+	int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id);
+	int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id);
 };
 
 /**
@@ -102,26 +102,26 @@ struct scmi_clk_ops {
  *	at a given frequency
  */
 struct scmi_perf_ops {
-	int (*limits_set)(const struct scmi_handle *handle, u32 domain,
+	int (*limits_set)(const struct scmi_protocol_handle *ph, u32 domain,
 			  u32 max_perf, u32 min_perf);
-	int (*limits_get)(const struct scmi_handle *handle, u32 domain,
+	int (*limits_get)(const struct scmi_protocol_handle *ph, u32 domain,
 			  u32 *max_perf, u32 *min_perf);
-	int (*level_set)(const struct scmi_handle *handle, u32 domain,
+	int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain,
 			 u32 level, bool poll);
-	int (*level_get)(const struct scmi_handle *handle, u32 domain,
+	int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain,
 			 u32 *level, bool poll);
 	int (*device_domain_id)(struct device *dev);
-	int (*transition_latency_get)(const struct scmi_handle *handle,
+	int (*transition_latency_get)(const struct scmi_protocol_handle *ph,
 				      struct device *dev);
-	int (*device_opps_add)(const struct scmi_handle *handle,
+	int (*device_opps_add)(const struct scmi_protocol_handle *ph,
 			       struct device *dev);
-	int (*freq_set)(const struct scmi_handle *handle, u32 domain,
+	int (*freq_set)(const struct scmi_protocol_handle *ph, u32 domain,
 			unsigned long rate, bool poll);
-	int (*freq_get)(const struct scmi_handle *handle, u32 domain,
+	int (*freq_get)(const struct scmi_protocol_handle *ph, u32 domain,
 			unsigned long *rate, bool poll);
-	int (*est_power_get)(const struct scmi_handle *handle, u32 domain,
+	int (*est_power_get)(const struct scmi_protocol_handle *ph, u32 domain,
 			     unsigned long *rate, unsigned long *power);
-	bool (*fast_switch_possible)(const struct scmi_handle *handle,
+	bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph,
 				     struct device *dev);
 };
 
@@ -135,8 +135,8 @@ struct scmi_perf_ops {
  * @state_get: gets the power state of a power domain
  */
 struct scmi_power_ops {
-	int (*num_domains_get)(const struct scmi_handle *handle);
-	char *(*name_get)(const struct scmi_handle *handle, u32 domain);
+	int (*num_domains_get)(const struct scmi_protocol_handle *ph);
+	char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain);
 #define SCMI_POWER_STATE_TYPE_SHIFT	30
 #define SCMI_POWER_STATE_ID_MASK	(BIT(28) - 1)
 #define SCMI_POWER_STATE_PARAM(type, id) \
@@ -144,9 +144,9 @@ struct scmi_power_ops {
 		((id) & SCMI_POWER_STATE_ID_MASK))
 #define SCMI_POWER_STATE_GENERIC_ON	SCMI_POWER_STATE_PARAM(0, 0)
 #define SCMI_POWER_STATE_GENERIC_OFF	SCMI_POWER_STATE_PARAM(1, 0)
-	int (*state_set)(const struct scmi_handle *handle, u32 domain,
+	int (*state_set)(const struct scmi_protocol_handle *ph, u32 domain,
 			 u32 state);
-	int (*state_get)(const struct scmi_handle *handle, u32 domain,
+	int (*state_get)(const struct scmi_protocol_handle *ph, u32 domain,
 			 u32 *state);
 };
 
@@ -182,12 +182,12 @@ enum scmi_sensor_class {
  * @reading_get: gets the current value of the sensor
  */
 struct scmi_sensor_ops {
-	int (*count_get)(const struct scmi_handle *handle);
+	int (*count_get)(const struct scmi_protocol_handle *ph);
 	const struct scmi_sensor_info *(*info_get)
-		(const struct scmi_handle *handle, u32 sensor_id);
-	int (*trip_point_config)(const struct scmi_handle *handle,
+		(const struct scmi_protocol_handle *ph, u32 sensor_id);
+	int (*trip_point_config)(const struct scmi_protocol_handle *ph,
 				 u32 sensor_id, u8 trip_id, u64 trip_value);
-	int (*reading_get)(const struct scmi_handle *handle, u32 sensor_id,
+	int (*reading_get)(const struct scmi_protocol_handle *ph, u32 sensor_id,
 			   u64 *value);
 };
 
@@ -203,12 +203,12 @@ struct scmi_sensor_ops {
  * @deassert: explicitly deassert reset signal of the specified reset domain
  */
 struct scmi_reset_ops {
-	int (*num_domains_get)(const struct scmi_handle *handle);
-	char *(*name_get)(const struct scmi_handle *handle, u32 domain);
-	int (*latency_get)(const struct scmi_handle *handle, u32 domain);
-	int (*reset)(const struct scmi_handle *handle, u32 domain);
-	int (*assert)(const struct scmi_handle *handle, u32 domain);
-	int (*deassert)(const struct scmi_handle *handle, u32 domain);
+	int (*num_domains_get)(const struct scmi_protocol_handle *ph);
+	char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain);
+	int (*latency_get)(const struct scmi_protocol_handle *ph, u32 domain);
+	int (*reset)(const struct scmi_protocol_handle *ph, u32 domain);
+	int (*assert)(const struct scmi_protocol_handle *ph, u32 domain);
+	int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain);
 };
 
 /**
@@ -272,11 +272,6 @@ struct scmi_notify_ops {
  *
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
- * @power_ops: pointer to set of power protocol operations
- * @perf_ops: pointer to set of performance protocol operations
- * @clk_ops: pointer to set of clock protocol operations
- * @sensor_ops: pointer to set of sensor protocol operations
- * @reset_ops: pointer to set of reset protocol operations
  * @devm_get_ops: devres managed method to acquire a protocol and get specific
  *		  operations and a dedicated protocol handler
  * @devm_put_ops: devres managed method to release a protocol
@@ -284,27 +279,12 @@ struct scmi_notify_ops {
  *	     dedicated protocol handler
  * @put_ops: method to release a protocol
  * @notify_ops: pointer to set of notifications related operations
- * @perf_priv: pointer to private data structure specific to performance
- *	protocol(for internal use only)
- * @clk_priv: pointer to private data structure specific to clock
- *	protocol(for internal use only)
- * @power_priv: pointer to private data structure specific to power
- *	protocol(for internal use only)
- * @sensor_priv: pointer to private data structure specific to sensors
- *	protocol(for internal use only)
- * @reset_priv: pointer to private data structure specific to reset
- *	protocol(for internal use only)
  * @notify_priv: pointer to private data structure specific to notifications
  *	(for internal use only)
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
-	const struct scmi_perf_ops *perf_ops;
-	const struct scmi_clk_ops *clk_ops;
-	const struct scmi_power_ops *power_ops;
-	const struct scmi_sensor_ops *sensor_ops;
-	const struct scmi_reset_ops *reset_ops;
 
 	const void __must_check *
 		(*devm_get_ops)(struct scmi_device *sdev, u8 proto,
@@ -312,19 +292,12 @@ struct scmi_handle {
 	void (*devm_put_ops)(struct scmi_device *sdev, u8 proto);
 
 	const void __must_check *
-		(*get_ops)(struct scmi_handle *handle, u8 proto,
+		(*get_ops)(const struct scmi_handle *handle, u8 proto,
 			   struct scmi_protocol_handle **ph);
-	void (*put_ops)(struct scmi_handle *handle, u8 proto);
+	void (*put_ops)(const struct scmi_handle *handle, u8 proto);
 
 	const struct scmi_notify_ops *notify_ops;
-	/* for protocol internal use */
-	void *perf_priv;
-	void *clk_priv;
-	void *power_priv;
-	void *sensor_priv;
-	void *reset_priv;
 	void *notify_priv;
-	void *system_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 7/8] firmware: arm_scmi: make notify_priv really private
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (5 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 6/8] firmware: arm_scmi: port all protocols and drivers to the new API Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-10-28 20:29 ` [PATCH v2 8/8] firmware: arm_scmi: add protocol modularization support Cristian Marussi
  2020-11-04 16:15 ` [PATCH v2 0/8] SCMI vendor protocols and modularization Thara Gopinath
  8 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Notification private data is currently accessible via handle->notify_priv;
this data was indeed meant to be private to the notification core support
and not to be accessible to SCMI drivers: make it private hiding it inside
instance descriptor struct scmi_info.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h |  4 +++
 drivers/firmware/arm_scmi/driver.c | 21 +++++++++++++
 drivers/firmware/arm_scmi/notify.c | 49 +++++++++++-------------------
 include/linux/scmi_protocol.h      |  3 --
 4 files changed, 43 insertions(+), 34 deletions(-)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 995f19a07b5e..9ae0e4133f31 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -342,4 +342,8 @@ void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
 bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
 		     struct scmi_xfer *xfer);
 
+void scmi_set_notification_instance_data(const struct scmi_handle *handle,
+					 void *priv);
+void *scmi_get_notification_instance_data(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 87ceb235194b..d6a975992136 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -113,6 +113,7 @@ struct scmi_protocol_instance {
  * @protocols_mtx: A mutex to protect protocols instances initialization.
  * @protocols_imp: List of protocols implemented, currently maximum of
  *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
+ * @notify_priv: Pointer to private data structure specific to notifications.
  * @node: List head
  * @users: Number of users of this instance
  */
@@ -129,6 +130,7 @@ struct scmi_info {
 	/* Ensure mutual exclusive access to protocols instance array */
 	struct mutex protocols_mtx;
 	u8 *protocols_imp;
+	void *notify_priv;
 	struct list_head node;
 	int users;
 };
@@ -170,6 +172,25 @@ static inline void scmi_dump_header_dbg(struct device *dev,
 		hdr->id, hdr->seq, hdr->protocol_id);
 }
 
+void scmi_set_notification_instance_data(const struct scmi_handle *handle,
+					 void *priv)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	info->notify_priv = priv;
+	/* Ensure updated protocol private date are visible */
+	smp_wmb();
+}
+
+void *scmi_get_notification_instance_data(const struct scmi_handle *handle)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	/* Ensure protocols_private_data has been updated */
+	smp_rmb();
+	return info->notify_priv;
+}
+
 /**
  * scmi_xfer_get() - Allocate one message
  *
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 6d4a1d78c22e..1c0126f41756 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -580,11 +580,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
 	struct scmi_event_header eh;
 	struct scmi_notify_instance *ni;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return 0;
-	ni = handle->notify_priv;
 
 	r_evt = SCMI_GET_REVT(ni, proto_id, evt_id);
 	if (!r_evt)
@@ -760,11 +758,9 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	    (!ee->num_sources && !ee->ops->get_num_sources))
 		return -EINVAL;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return -ENOMEM;
-	ni = handle->notify_priv;
 
 	/* num_sources cannot be <= 0 */
 	if (ee->num_sources) {
@@ -849,12 +845,10 @@ void scmi_deregister_protocol_events(const struct scmi_handle *handle,
 	struct scmi_notify_instance *ni;
 	struct scmi_registered_events_desc *pd;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return;
 
-	ni = handle->notify_priv;
 	pd = ni->registered_protocols[proto_id];
 	if (!pd)
 		return;
@@ -1357,11 +1351,9 @@ static int scmi_register_notifier(const struct scmi_handle *handle,
 	struct scmi_event_handler *hndl;
 	struct scmi_notify_instance *ni;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return -ENODEV;
-	ni = handle->notify_priv;
 
 	evt_key = MAKE_HASH_KEY(proto_id, evt_id,
 				src_id ? *src_id : SRC_ID_MASK);
@@ -1405,11 +1397,9 @@ static int scmi_unregister_notifier(const struct scmi_handle *handle,
 	struct scmi_event_handler *hndl;
 	struct scmi_notify_instance *ni;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return -ENODEV;
-	ni = handle->notify_priv;
 
 	evt_key = MAKE_HASH_KEY(proto_id, evt_id,
 				src_id ? *src_id : SRC_ID_MASK);
@@ -1682,8 +1672,8 @@ int scmi_notification_init(struct scmi_handle *handle)
 
 	INIT_WORK(&ni->init_work, scmi_protocols_late_init);
 
+	scmi_set_notification_instance_data(handle, ni);
 	handle->notify_ops = &notify_ops;
-	handle->notify_priv = ni;
 	/* Ensure handle is up to date */
 	smp_wmb();
 
@@ -1695,7 +1685,7 @@ int scmi_notification_init(struct scmi_handle *handle)
 
 err:
 	dev_warn(handle->dev, "Initialization Failed.\n");
-	devres_release_group(handle->dev, NULL);
+	devres_release_group(handle->dev, gid);
 	return -ENOMEM;
 }
 
@@ -1706,19 +1696,16 @@ int scmi_notification_init(struct scmi_handle *handle)
 void scmi_notification_exit(struct scmi_handle *handle)
 {
 	struct scmi_notify_instance *ni;
+	void *gid;
 
-	/* Ensure notify_priv is updated */
-	smp_rmb();
-	if (!handle->notify_priv)
+	ni = scmi_get_notification_instance_data(handle);
+	if (!ni)
 		return;
-	ni = handle->notify_priv;
-
-	handle->notify_priv = NULL;
-	/* Ensure handle is up to date */
-	smp_wmb();
+	scmi_set_notification_instance_data(handle, NULL);
 
 	/* Destroy while letting pending work complete */
 	destroy_workqueue(ni->notify_wq);
 
-	devres_release_group(ni->handle->dev, ni->gid);
+	gid = ni->gid;
+	devres_release_group(ni->handle->dev, gid);
 }
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 383b3d0ca383..66625aedd3a7 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -279,8 +279,6 @@ struct scmi_notify_ops {
  *	     dedicated protocol handler
  * @put_ops: method to release a protocol
  * @notify_ops: pointer to set of notifications related operations
- * @notify_priv: pointer to private data structure specific to notifications
- *	(for internal use only)
  */
 struct scmi_handle {
 	struct device *dev;
@@ -297,7 +295,6 @@ struct scmi_handle {
 	void (*put_ops)(const struct scmi_handle *handle, u8 proto);
 
 	const struct scmi_notify_ops *notify_ops;
-	void *notify_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 8/8] firmware: arm_scmi: add protocol modularization support
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (6 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 7/8] firmware: arm_scmi: make notify_priv really private Cristian Marussi
@ 2020-10-28 20:29 ` Cristian Marussi
  2020-11-04 16:15 ` [PATCH v2 0/8] SCMI vendor protocols and modularization Thara Gopinath
  8 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-10-28 20:29 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, thara.gopinath, vincent.guittot,
	souvik.chakravarty, cristian.marussi

Extend SCMI protocols accounting to address possible module usage and add
the support to possibly define new protocols as loadable modules.
Keep Standard protocols built into the SCMI core.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/base.c    |  2 ++
 drivers/firmware/arm_scmi/bus.c     | 11 ++++++++++-
 drivers/firmware/arm_scmi/clock.c   |  2 ++
 drivers/firmware/arm_scmi/common.h  |  4 ++++
 drivers/firmware/arm_scmi/driver.c  |  5 ++++-
 drivers/firmware/arm_scmi/perf.c    |  2 ++
 drivers/firmware/arm_scmi/power.c   |  2 ++
 drivers/firmware/arm_scmi/reset.c   |  2 ++
 drivers/firmware/arm_scmi/sensors.c |  2 ++
 drivers/firmware/arm_scmi/system.c  |  2 ++
 include/linux/scmi_protocol.h       | 12 ++++++++++++
 11 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 745e475e6cc4..e66e23d07d82 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt) "SCMI Notifications BASE - " fmt
 
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 
 #include "common.h"
@@ -373,6 +374,7 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_base = {
 	.id = SCMI_PROTOCOL_BASE,
+	.owner = NULL,
 	.init = &scmi_base_protocol_init,
 	.ops = NULL,
 	.events = &base_protocol_events,
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 25b191cfefc1..b152860f6c99 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -56,7 +56,7 @@ const struct scmi_protocol *scmi_get_protocol(int protocol_id)
 	const struct scmi_protocol *proto;
 
 	proto = idr_find(&scmi_available_protocols, protocol_id);
-	if (!proto) {
+	if (!proto || !try_module_get(proto->owner)) {
 		pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
 		return NULL;
 	}
@@ -66,6 +66,15 @@ const struct scmi_protocol *scmi_get_protocol(int protocol_id)
 	return proto;
 }
 
+void scmi_put_protocol(int protocol_id)
+{
+	const struct scmi_protocol *proto;
+
+	proto = idr_find(&scmi_available_protocols, protocol_id);
+	if (proto)
+		module_put(proto->owner);
+}
+
 static int scmi_dev_probe(struct device *dev)
 {
 	struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 00041dcb4d06..06ed45c0002a 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2018-2020 ARM Ltd.
  */
 
+#include <linux/module.h>
 #include <linux/sort.h>
 
 #include "common.h"
@@ -367,6 +368,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_clock = {
 	.id = SCMI_PROTOCOL_CLOCK,
+	.owner = THIS_MODULE,
 	.init = &scmi_clock_protocol_init,
 	.ops = &clk_ops,
 };
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 9ae0e4133f31..d3f62f85ea18 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -14,6 +14,7 @@
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 #include <linux/types.h>
 
@@ -216,6 +217,7 @@ typedef int (*scmi_prot_init_fn_t)(const struct scmi_protocol_handle *);
 /**
  * struct scmi_protocol  - Protocol descriptor
  * @id: Protocol ID.
+ * @owner: Module reference if any.
  * @init: Mandatory protocol initialization function.
  * @deinit: Optional protocol de-initialization function.
  * @ops: Optional reference to the operations provided by the protocol and
@@ -224,6 +226,7 @@ typedef int (*scmi_prot_init_fn_t)(const struct scmi_protocol_handle *);
  */
 struct scmi_protocol {
 	const u8				id;
+	struct module				*owner;
 	const scmi_prot_init_fn_t		init;
 	const scmi_prot_init_fn_t		deinit;
 	const void				*ops;
@@ -256,6 +259,7 @@ void __exit scmi_##name##_unregister(void) \
 }
 
 const struct scmi_protocol *scmi_get_protocol(int protocol_id);
+void scmi_put_protocol(int protocol_id);
 
 int scmi_acquire_protocol(const struct scmi_handle *handle, u8 protocol_id);
 void scmi_release_protocol(const struct scmi_handle *handle, u8 protocol_id);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index d6a975992136..077152fd5fc3 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -677,7 +677,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
 		/* Fail if protocol not registered on bus */
 		proto = scmi_get_protocol(protocol_id);
 		if (!proto) {
-			ret = -ENODEV;
+			ret = -EPROBE_DEFER;
 			goto out;
 		}
 
@@ -722,6 +722,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
 	return pi;
 
 clean:
+	scmi_put_protocol(protocol_id);
 	devres_release_group(handle->dev, gid);
 out:
 	mutex_unlock(&info->protocols_mtx);
@@ -772,6 +773,8 @@ void scmi_release_protocol(const struct scmi_handle *handle, u8 protocol_id)
 
 		idr_remove(&info->protocols, protocol_id);
 
+		scmi_put_protocol(protocol_id);
+
 		devres_release_group(handle->dev, gid);
 		dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
 			protocol_id);
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index c1d0664aaf4a..66e0d2364ea6 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 #include <linux/io.h>
 #include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
 #include <linux/scmi_protocol.h>
@@ -900,6 +901,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_perf = {
 	.id = SCMI_PROTOCOL_PERF,
+	.owner = THIS_MODULE,
 	.init = &scmi_perf_protocol_init,
 	.ops = &perf_ops,
 	.events = &perf_protocol_events,
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index d9ce794d0299..819d948eb034 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
 
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 
 #include "common.h"
@@ -310,6 +311,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_power = {
 	.id = SCMI_PROTOCOL_POWER,
+	.owner = THIS_MODULE,
 	.init = &scmi_power_protocol_init,
 	.ops = &power_ops,
 	.events = &power_protocol_events,
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index a183518baf18..9f408070b70d 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
 
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 
 #include "common.h"
@@ -324,6 +325,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_reset = {
 	.id = SCMI_PROTOCOL_RESET,
+	.owner = THIS_MODULE,
 	.init = &scmi_reset_protocol_init,
 	.ops = &reset_ops,
 	.events = &reset_protocol_events,
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index a74a7938cd78..170d97f09d91 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
 
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 
 #include "common.h"
@@ -375,6 +376,7 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_sensors = {
 	.id = SCMI_PROTOCOL_SENSOR,
+	.owner = THIS_MODULE,
 	.init = &scmi_sensors_protocol_init,
 	.ops = &sensor_ops,
 	.events = &sensor_protocol_events,
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 23365ef334bf..13bd223feb84 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
 
+#include <linux/module.h>
 #include <linux/scmi_protocol.h>
 
 #include "common.h"
@@ -129,6 +130,7 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
 
 static const struct scmi_protocol scmi_system = {
 	.id = SCMI_PROTOCOL_SYSTEM,
+	.owner = THIS_MODULE,
 	.init = &scmi_system_protocol_init,
 	.ops = NULL,
 	.events = &system_protocol_events,
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 66625aedd3a7..7d6a12baa254 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -378,6 +378,18 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
 #define module_scmi_driver(__scmi_driver)	\
 	module_driver(__scmi_driver, scmi_register, scmi_unregister)
 
+/**
+ * module_scmi_protocol() - Helper macro for registering a scmi protocol
+ * @__scmi_protocol: scmi_protocol structure
+ *
+ * Helper macro for scmi drivers to set up proper module init / exit
+ * functions.  Replaces module_init() and module_exit() and keeps people from
+ * printing pointless things to the kernel log when their driver is loaded.
+ */
+#define module_scmi_protocol(__scmi_protocol)	\
+	module_driver(__scmi_protocol,		\
+		      scmi_protocol_register, scmi_protocol_unregister)
+
 struct scmi_protocol;
 int scmi_protocol_register(const struct scmi_protocol *proto);
 void scmi_protocol_unregister(const struct scmi_protocol *proto);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/8] SCMI vendor protocols and modularization
  2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
                   ` (7 preceding siblings ...)
  2020-10-28 20:29 ` [PATCH v2 8/8] firmware: arm_scmi: add protocol modularization support Cristian Marussi
@ 2020-11-04 16:15 ` Thara Gopinath
  8 siblings, 0 replies; 21+ messages in thread
From: Thara Gopinath @ 2020-11-04 16:15 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty



On 10/28/20 4:29 PM, Cristian Marussi wrote:
> The current revision of this series still does not address the possibility
> of creating dynamically the SCMI devices: any new protocols must be added
> to the SCMI embedded module device table as of now, while it could be
> desirable to have such devices created dynamically whenever a new protocol
> is added and loaded into the system.

Hi Cristian,

Thanks for v2. I have raised my concerns previously as well on having a 
static table in the main scmi framework for managing the client devices.
This will be a bottleneck not just for vendor protocol addition but also 
to extend the currently supported list of scmi protocols.
I will wait for your v3 (as you mentioned previously) for fixes  around 
this.

-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface
  2020-10-28 20:29 ` [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface Cristian Marussi
@ 2020-11-04 16:16   ` Thara Gopinath
  2020-11-04 16:56     ` Cristian Marussi
  0 siblings, 1 reply; 21+ messages in thread
From: Thara Gopinath @ 2020-11-04 16:16 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty

Hi Cristian,

On 10/28/20 4:29 PM, Cristian Marussi wrote:
> Extend common protocol registration routines and provide some new generic
> protocols' init/deinit helpers that tracks protocols' users and automatically
> perform the proper initialization/de-initialization on demand.
> 
> Convert all protocols to use new registration schema while modifying only Base
> protocol to use also the new initialization helpers.
> 
> All other standard protocols' initialization is still umodified and bound to
> SCMI devices probing.

minor nit : umodified->unmodified.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
> v1 --> v2
> - made all scmi_protocol refs const
> - introduced IDR to handle protocols instead of static array
> - refactored code around fast path
> ---
>   drivers/firmware/arm_scmi/base.c    |  10 +-
>   drivers/firmware/arm_scmi/bus.c     |  61 +++++++---
>   drivers/firmware/arm_scmi/clock.c   |  10 +-
>   drivers/firmware/arm_scmi/common.h  |  31 ++++-
>   drivers/firmware/arm_scmi/driver.c  | 168 +++++++++++++++++++++++++++-
>   drivers/firmware/arm_scmi/notify.c  |   3 +-
>   drivers/firmware/arm_scmi/perf.c    |  10 +-
>   drivers/firmware/arm_scmi/power.c   |  10 +-
>   drivers/firmware/arm_scmi/reset.c   |  10 +-
>   drivers/firmware/arm_scmi/sensors.c |  10 +-
>   drivers/firmware/arm_scmi/system.c  |   8 +-
>   include/linux/scmi_protocol.h       |   6 +-
>   12 files changed, 299 insertions(+), 38 deletions(-)
> 

[...]


> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 3dfd8b6a0ebf..beae8991422d 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -11,11 +11,12 @@
>    * various power domain DVFS including the core/cluster, certain system
>    * clocks configuration, thermal sensors and many others.
>    *
> - * Copyright (C) 2018 ARM Ltd.
> + * Copyright (C) 2018-2020 ARM Ltd.
>    */
>   
>   #include <linux/bitmap.h>
>   #include <linux/export.h>
> +#include <linux/idr.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
>   #include <linux/ktime.h>
> @@ -23,6 +24,7 @@
>   #include <linux/of_address.h>
>   #include <linux/of_device.h>
>   #include <linux/processor.h>
> +#include <linux/refcount.h>
>   #include <linux/slab.h>
>   
>   #include "common.h"
> @@ -68,6 +70,21 @@ struct scmi_xfers_info {
>   	spinlock_t xfer_lock;
>   };
>   
> +/**
> + * struct scmi_protocol_instance  - Describe an initialized protocol instance.
> + * @proto: A reference to the protocol descriptor.
> + * @gid: A reference for per-protocol devres management.
> + * @users: A refcount to track effective users of this protocol.
> + *
> + * Each protocol is initialized independently once for each SCMI platform in
> + * which is defined by DT and implemented by the SCMI server fw.
> + */
> +struct scmi_protocol_instance {
> +	const struct scmi_protocol	*proto;
> +	void				*gid;
> +	refcount_t			users;
> +};

Why do you need a separate protocol_instance? Will there be two 
instances of the same protocol for a single scmi device/instance?
Else everything that has been defined in this struct in this patch the
following ones can be rolled into scmi_protocol struct, right ?

> +
>   /**
>    * struct scmi_info - Structure representing a SCMI instance
>    *
> @@ -80,6 +97,10 @@ struct scmi_xfers_info {
>    * @rx_minfo: Universal Receive Message management info
>    * @tx_idr: IDR object to map protocol id to Tx channel info pointer
>    * @rx_idr: IDR object to map protocol id to Rx channel info pointer
> + * @protocols: IDR for protocols' instance descriptors initialized for
> + *	       this SCMI instance: populated on protocol's first attempted
> + *	       usage.
> + * @protocols_mtx: A mutex to protect protocols instances initialization.
>    * @protocols_imp: List of protocols implemented, currently maximum of
>    *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
>    * @node: List head
> @@ -94,6 +115,9 @@ struct scmi_info {
>   	struct scmi_xfers_info rx_minfo;
>   	struct idr tx_idr;
>   	struct idr rx_idr;
> +	struct idr protocols;
> +	/* Ensure mutual exclusive access to protocols instance array */
> +	struct mutex protocols_mtx;
>   	u8 *protocols_imp;
>   	struct list_head node;
>   	int users;
> @@ -519,6 +543,127 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
>   	return ret;
>   }
>   
> +/**
> + * scmi_get_protocol_instance  - Protocol initialization helper.
> + * @handle: A reference to the SCMI platform instance.
> + * @protocol_id: The protocol being requested.
> + *
> + * In case the required protocol has never been requested before for this
> + * instance, allocate and initialize all the needed structures while handling
> + * resource allocation with a dedicated per-protocol devres subgroup.
> + *
> + * Return: A reference to an initialized protocol instance or error on failure.
> + */
> +static struct scmi_protocol_instance * __must_check
> +scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
> +{
> +	int ret = -ENOMEM;
> +	void *gid;
> +	struct scmi_protocol_instance *pi;
> +	struct scmi_info *info = handle_to_scmi_info(handle);
> +
> +	mutex_lock(&info->protocols_mtx);
> +	pi = idr_find(&info->protocols, protocol_id);
> +
> +	if (pi) {
> +		refcount_inc(&pi->users);
> +	} else {
> +		const struct scmi_protocol *proto;
> +
> +		/* Fail if protocol not registered on bus */
> +		proto = scmi_get_protocol(protocol_id);
> +		if (!proto) {
> +			ret = -ENODEV;
> +			goto out;
> +		}
> +
> +		/* Protocol specific devres group */
> +		gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
> +		if (!gid)
> +			goto out;
> +
> +		pi = devm_kzalloc(handle->dev, sizeof(*pi), GFP_KERNEL);
> +		if (!pi)
> +			goto clean;
> +
> +		pi->gid = gid;
> +		pi->proto = proto;
> +		refcount_set(&pi->users, 1);
> +		/* proto->init is assured NON NULL by scmi_protocol_register */
> +		ret = pi->proto->init(handle);

So init for a protocol will be called twice. Is this intentional ? Once 
during the device probe via scmi_dev_probe and scmi_protocol_init. And
once via scmi_get_protocol_instance which gets called in get_ops apis 
defined and used in the later patches.

> +		if (ret)
> +			goto clean;
> +
> +		ret = idr_alloc(&info->protocols, pi,
> +				protocol_id, protocol_id + 1, GFP_KERNEL);
> +		if (ret != protocol_id)
> +			goto clean;
> +
> +		devres_close_group(handle->dev, pi->gid);
> +		dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
> +			protocol_id);
> +	}
> +	mutex_unlock(&info->protocols_mtx);
> +
> +	return pi;
> +
> +clean:
> +	devres_release_group(handle->dev, gid);
> +out:
> +	mutex_unlock(&info->protocols_mtx);
> +	return ERR_PTR(ret);
> +}
> +
> +/**
> + * scmi_acquire_protocol  - Protocol acquire
> + * @handle: A reference to the SCMI platform instance.
> + * @protocol_id: The protocol being requested.
> + *
> + * Register a new user for the requested protocol on the specified SCMI
> + * platform instance, possibly triggering its initialization on first user.
> + *
> + * Return: 0 if protocol was acquired successfully.
> + */
> +int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id)
> +{
> +	return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
> +}
> +
> +/**
> + * scmi_release_protocol  - Protocol de-initialization helper.
> + * @handle: A reference to the SCMI platform instance.
> + * @protocol_id: The protocol being requested.
> + *
> + * Remove one user for the specified protocol and triggers de-initialization
> + * and resources de-allocation once the last user has gone.
> + */
> +void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
> +{
> +	struct scmi_info *info = handle_to_scmi_info(handle);
> +	struct scmi_protocol_instance *pi;
> +
> +	mutex_lock(&info->protocols_mtx);
> +	pi = idr_find(&info->protocols, protocol_id);
> +	if (WARN_ON(!pi))
> +		goto out;
> +
> +	if (refcount_dec_and_test(&pi->users)) {
> +		void *gid = pi->gid;
> +
> +		if (pi->proto->deinit)
> +			pi->proto->deinit(handle);
> +
> +		idr_remove(&info->protocols, protocol_id);
> +
> +		devres_release_group(handle->dev, gid);
> +		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_handle *handle,
>   				     u8 *prot_imp)
>   {
> @@ -785,6 +930,8 @@ static int scmi_probe(struct platform_device *pdev)
>   	info->dev = dev;
>   	info->desc = desc;
>   	INIT_LIST_HEAD(&info->node);
> +	idr_init(&info->protocols);
> +	mutex_init(&info->protocols_mtx);
>   
>   	platform_set_drvdata(pdev, info);
>   	idr_init(&info->tx_idr);
> @@ -805,9 +952,14 @@ static int scmi_probe(struct platform_device *pdev)
>   	if (scmi_notification_init(handle))
>   		dev_err(dev, "SCMI Notifications NOT available.\n");
>   
> -	ret = scmi_base_protocol_init(handle);
> +	/*
> +	 * Trigger SCMI Base protocol initialization.
> +	 * It's mandatory and won't be ever released/deinit until the
> +	 * SCMI stack is shutdown/unloaded as a whole.
> +	 */
> +	ret = scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE);
>   	if (ret) {
> -		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> +		dev_err(dev, "unable to communicate with SCMI\n");
>   		return ret;
>   	}
>   
> @@ -859,6 +1011,10 @@ static int scmi_remove(struct platform_device *pdev)
>   	if (ret)
>   		return ret;
>   
> +	mutex_lock(&info->protocols_mtx);
> +	idr_destroy(&info->protocols);
> +	mutex_unlock(&info->protocols_mtx);
> +
>   	/* Safe to free channels since no more users */
>   	ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
>   	idr_destroy(&info->tx_idr);
> @@ -941,6 +1097,8 @@ static int __init scmi_driver_init(void)
>   {
>   	scmi_bus_init();
>   
> +	scmi_base_register();
> +

minor nit. extra line addeed ?

-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-10-28 20:29 ` [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles Cristian Marussi
@ 2020-11-04 16:16   ` Thara Gopinath
  2020-11-04 16:58     ` Etienne Carriere
  2020-11-04 17:44     ` Cristian Marussi
  0 siblings, 2 replies; 21+ messages in thread
From: Thara Gopinath @ 2020-11-04 16:16 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty


Hi Cristian,

On 10/28/20 4:29 PM, Cristian Marussi wrote:
> Add basic protocol handles definitions and helpers support.
> All protocols initialization code and SCMI drivers probing is still
> performed using the handle based interface.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
>   drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
>   drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
>   2 files changed, 125 insertions(+)
> 
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> index b08a8ddbc22a..f0678be02a09 100644
> --- a/drivers/firmware/arm_scmi/common.h
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
>   		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
>   void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
>   			    struct scmi_xfer *xfer);
> +
> +struct scmi_xfer_ops;
> +
> +/**
> + * struct scmi_protocol_handle  - Reference to an initialized protocol instance
> + *
> + * @dev: A reference to the associated SCMI instance device (handle->dev).
> + * @xops: A reference to a struct holding refs to the core xfer operations that
> + *	  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.
> + *
> + * This structure represents a protocol initialized against specific SCMI
> + * instance and it will be used as follows:
> + * - as a parameter fed from the core to the protocol initialization code so
> + *   that it can access the core xfer operations to build and generate SCMI
> + *   messages exclusively for the specific underlying protocol instance.
> + * - as an opaque handle fed by an SCMI driver user when it tries to access
> + *   this protocol through its own protocol operations.
> + *   In this case this handle will be returned as an opaque object together
> + *   with the related protocol operations when the SCMI driver tries to access
> + *   the protocol.
> + */
> +struct scmi_protocol_handle {
> +	struct device *dev;
> +	const struct scmi_xfer_ops *xops;
> +	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
> +	void *(*get_priv)(const struct scmi_protocol_handle *ph);
> +};

So scmi_xfer_ops are the ops that actually talks with the scmi firmware 
on the other end , right ? IIUC, these ops are the same for all the 
protocols of a scmi instance. Imho, this struct is not the right place 
for these ops to reside.You are inadvertently exposing scmi internal 
details to the client drivers. There is no reason why this should be 
part of scmi_handle. The protocols can extract it from the handle during 
protocol_reigster, right?

So, now to the second part, why do you need a scmi_protocol_handle? 
Again IIUC, if you have set_priv and get_priv hooks and get_ops and 
put_ops hooks, there is nothing that scmi_protocol_handle is providing 
extra, right? As mentioned in the comments for last patch any reason all 
of this cannot be rolled into scmi_protocol?

As long as you are not supporting multiple scmi_protocol_instance and 
scmi_protocol_handle for a protocol, I don't think having separate 
structs make sense. And you need to do this only if you think there can 
be multiple versions/instances of a protocol in the same scmi instance.
Or am I missing something here ?

> +
> +/**
> + * struct scmi_xfer_ops  - References to the core SCMI xfer operations.
> + * @version_get: Get this version protocol.
> + * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
> + * @reset_rx_to_maxsz: Reset rx size to max transport size.
> + * @do_xfer: Do the SCMI transfer.
> + * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
> + * @xfer_put: Free the xfer slot.
> + *
> + * Note that all this operations expect a protocol handle as first parameter;
> + * they then internally use it to infer the underlying protocol number: this
> + * way is not possible for a protocol implementation to forge messages for
> + * another protocol.
> + */
> +struct scmi_xfer_ops {
> +	int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
> +	int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
> +			     size_t tx_size, size_t rx_size,
> +			     struct scmi_xfer **p);
> +	void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
> +				  struct scmi_xfer *xfer);
> +	int (*do_xfer)(const struct scmi_protocol_handle *ph,
> +		       struct scmi_xfer *xfer);
> +	int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
> +				     struct scmi_xfer *xfer);
> +	void (*xfer_put)(const struct scmi_protocol_handle *ph,
> +			 struct scmi_xfer *xfer);
> +};
> +
> +struct scmi_revision_info *
> +scmi_get_revision_area(const struct scmi_protocol_handle *ph);
>   int scmi_handle_put(const struct scmi_handle *handle);
>   struct scmi_handle *scmi_handle_get(struct device *dev);
>   void scmi_set_handle(struct scmi_device *scmi_dev);
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index beae8991422d..8ca04acb6abb 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -72,19 +72,28 @@ struct scmi_xfers_info {
>   
>   /**
>    * struct scmi_protocol_instance  - Describe an initialized protocol instance.
> + * @handle: Reference to the SCMI handle associated to this protocol instance.
>    * @proto: A reference to the protocol descriptor.
>    * @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.
> + * @ph: An embedded protocol handle that will be passed down to protocol
> + *	initialization code to identify this instance.
>    *
>    * Each protocol is initialized independently once for each SCMI platform in
>    * which is defined by DT and implemented by the SCMI server fw.
>    */
>   struct scmi_protocol_instance {
> +	const struct scmi_handle	*handle;
>   	const struct scmi_protocol	*proto;
>   	void				*gid;
>   	refcount_t			users;
> +	void				*priv;
> +	struct scmi_protocol_handle	ph;
>   };
>   
> +#define ph_to_pi(h)	container_of(h, struct scmi_protocol_instance, ph)
> +
>   /**
>    * struct scmi_info - Structure representing a SCMI instance
>    *
> @@ -543,6 +552,57 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
>   	return ret;
>   }
>   
> +/**
> + * scmi_set_protocol_priv  - Set protocol specific data at init time
> + *
> + * @ph: A reference to the protocol handle.
> + * @priv: The private data to set.
> + *
> + * Return: 0 on Success
> + */
> +static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
> +				  void *priv)
> +{
> +	struct scmi_protocol_instance *pi = ph_to_pi(ph);
> +
> +	pi->priv = priv;
> +
> +	return 0;
> +}
> +
> +/**
> + * scmi_get_protocol_priv  - Set protocol specific data at init time
> + *
> + * @ph: A reference to the protocol handle.
> + *
> + * Return: Protocol private data if any was set.
> + */
> +static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
> +{
> +	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> +
> +	return pi->priv;
> +}
> +
> +/**
> + * scmi_get_revision_area  - Retrieve version memory area.
> + *
> + * @ph: A reference to the protocol handle.
> + *
> + * A helper to grab the version memory area reference during SCMI Base protocol
> + * initialization.
> + *
> + * Return: A reference to the version memory area associated to the SCMI
> + *	   instance underlying this protocol handle.
> + */
> +struct scmi_revision_info *
> +scmi_get_revision_area(const struct scmi_protocol_handle *ph)
> +{
> +	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> +
> +	return pi->handle->version;
> +}
> +
>   /**
>    * scmi_get_protocol_instance  - Protocol initialization helper.
>    * @handle: A reference to the SCMI platform instance.
> @@ -588,6 +648,10 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
>   
>   		pi->gid = gid;
>   		pi->proto = proto;
> +		pi->handle = handle;
> +		pi->ph.dev = handle->dev;
> +		pi->ph.set_priv = scmi_set_protocol_priv;
> +		pi->ph.get_priv = scmi_get_protocol_priv;
>   		refcount_set(&pi->users, 1);
>   		/* proto->init is assured NON NULL by scmi_protocol_register */
>   		ret = pi->proto->init(handle);
> 

-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support
  2020-10-28 20:29 ` [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support Cristian Marussi
@ 2020-11-04 16:16   ` Thara Gopinath
  2020-11-04 18:08     ` Cristian Marussi
  0 siblings, 1 reply; 21+ messages in thread
From: Thara Gopinath @ 2020-11-04 16:16 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, lukasz.luba, james.quinlan, Jonathan.Cameron,
	f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty

Hi Cristian,

On 10/28/20 4:29 PM, Cristian Marussi wrote:
> Expose a new generic get/put protocols API based on protocol handles;
> provide also a devres managed version also for notifications.

minor nit.. Maybe yous should reword this! Kind of confusing to understand!

Also, if it was me, I will separate the notifications and get/put hooks
into two separate patches. Not an issue though if you want to keep it
in the same patch.

> All SCMI drivers still keep using the old handle based interface.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
>   drivers/firmware/arm_scmi/driver.c | 126 +++++++++++++++++++++++++++++
>   drivers/firmware/arm_scmi/notify.c | 123 ++++++++++++++++++++++++++++
>   include/linux/scmi_protocol.h      |  34 +++++++-
>   3 files changed, 282 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 8ca04acb6abb..4d86aafbf465 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -15,6 +15,7 @@
>    */
>   
>   #include <linux/bitmap.h>
> +#include <linux/device.h>
>   #include <linux/export.h>
>   #include <linux/idr.h>
>   #include <linux/io.h>
> @@ -728,6 +729,38 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
>   	mutex_unlock(&info->protocols_mtx);
>   }
>   
> +/**
> + * scmi_get_protocol_operations  - Get protocol operations
> + * @handle: A reference to the SCMI platform instance.
> + * @protocol_id: The protocol being requested.
> + * @ph: A pointer reference used to pass back the associated protocol handle.
> + *
> + * Get hold of a protocol accounting for its usage, eventually triggering its
> + * initialization, and returning the protocol specific operations and related
> + * protocol handle which will be used as first argument in most of the protocols
> + * operations methods.
> + *
> + * Return: A reference to the requested protocol operations or error.
> + *	   Must be checked for errors by caller.
> + */
> +static const void __must_check *
> +scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
> +			     struct scmi_protocol_handle **ph)
> +{
> +	struct scmi_protocol_instance *pi;
> +
> +	if (!ph)
> +		return ERR_PTR(-EINVAL);
> +
> +	pi = scmi_get_protocol_instance(handle, protocol_id);
> +	if (IS_ERR(pi))
> +		return pi;
> +
> +	*ph = &pi->ph;
> +
> +	return pi->proto->ops;
> +}
> +
>   void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
>   				     u8 *prot_imp)
>   {
> @@ -751,6 +784,95 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
>   	return false;
>   }
>   
> +struct scmi_protocol_devres {
> +	struct scmi_handle *handle;
> +	u8 protocol_id;
> +};
> +
> +static void scmi_devm_release_protocol(struct device *dev, void *res)
> +{
> +	struct scmi_protocol_devres *dres = res;
> +
> +	scmi_release_protocol(dres->handle, dres->protocol_id);
> +}
> +
> +/**
> + * scmi_devm_get_protocol_ops  - Devres managed get protocol operations
> + * @sdev: A reference to an scmi_device whose embedded struct device is to
> + *	  be used for devres accounting.
> + * @protocol_id: The protocol being requested.
> + * @ph: A pointer reference used to pass back the associated protocol handle.
> + *
> + * Get hold of a protocol accounting for its usage, eventually triggering its
> + * initialization, and returning the protocol specific operations and related
> + * protocol handle which will be used as first argument in most of the
> + * protocols operations methods.
> + * Being a devres based managed method, protocol hold will be automatically
> + * released, and possibly de-initialized on last user, once the SCMI driver
> + * owning the scmi_device is unbound from it.
> + *
> + * Return: A reference to the requested protocol operations or error.
> + *	   Must be checked for errors by caller.
> + */
> +static const void __must_check *
> +scmi_devm_get_protocol_ops(struct scmi_device *sdev, u8 protocol_id,
> +			   struct scmi_protocol_handle **ph)
> +{
> +	struct scmi_protocol_instance *pi;
> +	struct scmi_protocol_devres *dres;
> +	struct scmi_handle *handle = sdev->handle;
> +
> +	if (!ph)
> +		return ERR_PTR(-EINVAL);
> +
> +	dres = devres_alloc(scmi_devm_release_protocol,
> +			    sizeof(*dres), GFP_KERNEL);
> +	if (!dres)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pi = scmi_get_protocol_instance(handle, protocol_id);
> +	if (IS_ERR(pi)) {
> +		devres_free(dres);
> +		return pi;
> +	}
> +
> +	dres->handle = handle;
> +	dres->protocol_id = protocol_id;
> +	devres_add(&sdev->dev, dres);
> +
> +	*ph = &pi->ph;
> +
> +	return pi->proto->ops;
> +}
> +
> +static int scmi_devm_protocol_match(struct device *dev, void *res, void *data)
> +{
> +	struct scmi_protocol_devres *dres = res;
> +
> +	if (WARN_ON(!dres || !data))
> +		return 0;
> +
> +	return dres->protocol_id == *((u8 *)data);
> +}
> +
> +/**
> + * scmi_devm_put_protocol_ops  - Devres managed put protocol operations
> + * @sdev: A reference to an scmi_device whose embedded struct device is to
> + *	  be used for devres accounting.
> + * @protocol_id: The protocol being requested.
> + *
> + * Explicitly release a protocol hold previously obtained calling the above
> + * @scmi_devm_get_protocol_ops.
> + */
> +static void scmi_devm_put_protocol_ops(struct scmi_device *sdev, u8 protocol_id)
> +{
> +	int ret;
> +
> +	ret = devres_release(&sdev->dev, scmi_devm_release_protocol,
> +			     scmi_devm_protocol_match, &protocol_id);
> +	WARN_ON(ret);
> +}
> +
>   /**
>    * scmi_handle_get() - Get the SCMI handle for a device
>    *
> @@ -1004,6 +1126,10 @@ static int scmi_probe(struct platform_device *pdev)
>   	handle = &info->handle;
>   	handle->dev = info->dev;
>   	handle->version = &info->version;
> +	handle->devm_get_ops = scmi_devm_get_protocol_ops;
> +	handle->devm_put_ops = scmi_devm_put_protocol_ops;
> +	handle->get_ops = scmi_get_protocol_operations;
> +	handle->put_ops = scmi_release_protocol;

Why do you need a dev_res version and a non dev_res version? I checked
patch 6 where you convert the drivers to use these hooks and all of them
are using the dev res apis.

>   
>   	ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
>   	if (ret)
> diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
> index eae58b2a92cc..64d43e425644 100644
> --- a/drivers/firmware/arm_scmi/notify.c
> +++ b/drivers/firmware/arm_scmi/notify.c
> @@ -1370,6 +1370,127 @@ static int scmi_unregister_notifier(const struct scmi_handle *handle,
>   	return 0;
>   }
>   
> +struct scmi_notifier_devres {
> +	const struct scmi_handle *handle;
> +	u8 proto_id;
> +	u8 evt_id;
> +	u32 __src_id;
> +	u32 *src_id;

Instead of maintaining two separate pointers for src id,
why not define a bool, something like is_src_id_valid?
Should simply this a bit and also don't have to maintain two 32 bit 
pointers. What do you think?

> +	struct notifier_block *nb;
> +};
> +
> +static void scmi_devm_release_notifier(struct device *dev, void *res)
> +{
> +	struct scmi_notifier_devres *dres = res;
> +
> +	scmi_unregister_notifier(dres->handle, dres->proto_id, dres->evt_id,
> +				 dres->src_id, dres->nb);
> +}
> +
> +/**
> + * scmi_devm_register_notifier()  - Managed registration of a notifier_block
> + * for an event
> + * @sdev: A reference to an scmi_device whose embedded struct device is to
> + *	  be used for devres accounting.
> + * @proto_id: Protocol ID
> + * @evt_id: Event ID
> + * @src_id: Source ID, when NULL register for events coming form ALL possible
> + *	    sources
> + * @nb: A standard notifier block to register for the specified event
> + *
> + * Generic devres managed helper to register a notifier_block against a
> + * protocol event.
> + */
> +static int scmi_devm_register_notifier(struct scmi_device *sdev,
> +				       u8 proto_id, u8 evt_id, u32 *src_id,
> +				       struct notifier_block *nb)
> +{
> +	int ret;
> +	struct scmi_notifier_devres *dres;
> +
> +	dres = devres_alloc(scmi_devm_release_notifier,
> +			    sizeof(*dres), GFP_KERNEL);
> +	if (!dres)
> +		return -ENOMEM;
> +
> +	ret = scmi_register_notifier(sdev->handle, proto_id,
> +				     evt_id, src_id, nb);
> +	if (ret) {
> +		devres_free(dres);
> +		return ret;
> +	}
> +
> +	dres->handle = sdev->handle;
> +	dres->proto_id = proto_id;
> +	dres->evt_id = evt_id;
> +	dres->nb = nb;
> +	if (src_id) {
> +		dres->__src_id = *src_id;
> +		dres->src_id = &dres->__src_id;
> +	} else {
> +		dres->src_id = NULL;
> +	}
> +	devres_add(&sdev->dev, dres);
> +
> +	return ret;
> +}
> +
> +static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
> +{
> +	struct scmi_notifier_devres *dres = res;
> +	struct scmi_notifier_devres *xres = data;
> +
> +	if (WARN_ON(!dres || !xres))
> +		return 0;
> +
> +	return dres->proto_id == xres->proto_id &&
> +		dres->evt_id == xres->evt_id &&
> +		dres->nb == xres->nb &&

Does the nb have to be compared as well ?

> +		((!dres->src_id && !xres->src_id) ||
> +		  (dres->src_id && xres->src_id &&
> +		   dres->__src_id == xres->__src_id));


-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface
  2020-11-04 16:16   ` Thara Gopinath
@ 2020-11-04 16:56     ` Cristian Marussi
  2020-11-06 16:22       ` Thara Gopinath
  0 siblings, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-11-04 16:56 UTC (permalink / raw)
  To: Thara Gopinath
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty

Hi Thara,

thanks for reviewing.

On Wed, Nov 04, 2020 at 11:16:06AM -0500, Thara Gopinath wrote:
> Hi Cristian,
> 
> On 10/28/20 4:29 PM, Cristian Marussi wrote:
> > Extend common protocol registration routines and provide some new generic
> > protocols' init/deinit helpers that tracks protocols' users and automatically
> > perform the proper initialization/de-initialization on demand.
> > 
> > Convert all protocols to use new registration schema while modifying only Base
> > protocol to use also the new initialization helpers.
> > 
> > All other standard protocols' initialization is still umodified and bound to
> > SCMI devices probing.
> 
> minor nit : umodified->unmodified.
> > 

Right.

> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> > v1 --> v2
> > - made all scmi_protocol refs const
> > - introduced IDR to handle protocols instead of static array
> > - refactored code around fast path
> > ---
> >   drivers/firmware/arm_scmi/base.c    |  10 +-
> >   drivers/firmware/arm_scmi/bus.c     |  61 +++++++---
> >   drivers/firmware/arm_scmi/clock.c   |  10 +-
> >   drivers/firmware/arm_scmi/common.h  |  31 ++++-
> >   drivers/firmware/arm_scmi/driver.c  | 168 +++++++++++++++++++++++++++-
> >   drivers/firmware/arm_scmi/notify.c  |   3 +-
> >   drivers/firmware/arm_scmi/perf.c    |  10 +-
> >   drivers/firmware/arm_scmi/power.c   |  10 +-
> >   drivers/firmware/arm_scmi/reset.c   |  10 +-
> >   drivers/firmware/arm_scmi/sensors.c |  10 +-
> >   drivers/firmware/arm_scmi/system.c  |   8 +-
> >   include/linux/scmi_protocol.h       |   6 +-
> >   12 files changed, 299 insertions(+), 38 deletions(-)
> > 
> 
> [...]
> 
> 
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index 3dfd8b6a0ebf..beae8991422d 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -11,11 +11,12 @@
> >    * various power domain DVFS including the core/cluster, certain system
> >    * clocks configuration, thermal sensors and many others.
> >    *
> > - * Copyright (C) 2018 ARM Ltd.
> > + * Copyright (C) 2018-2020 ARM Ltd.
> >    */
> >   #include <linux/bitmap.h>
> >   #include <linux/export.h>
> > +#include <linux/idr.h>
> >   #include <linux/io.h>
> >   #include <linux/kernel.h>
> >   #include <linux/ktime.h>
> > @@ -23,6 +24,7 @@
> >   #include <linux/of_address.h>
> >   #include <linux/of_device.h>
> >   #include <linux/processor.h>
> > +#include <linux/refcount.h>
> >   #include <linux/slab.h>
> >   #include "common.h"
> > @@ -68,6 +70,21 @@ struct scmi_xfers_info {
> >   	spinlock_t xfer_lock;
> >   };
> > +/**
> > + * struct scmi_protocol_instance  - Describe an initialized protocol instance.
> > + * @proto: A reference to the protocol descriptor.
> > + * @gid: A reference for per-protocol devres management.
> > + * @users: A refcount to track effective users of this protocol.
> > + *
> > + * Each protocol is initialized independently once for each SCMI platform in
> > + * which is defined by DT and implemented by the SCMI server fw.
> > + */
> > +struct scmi_protocol_instance {
> > +	const struct scmi_protocol	*proto;
> > +	void				*gid;
> > +	refcount_t			users;
> > +};
> 
> Why do you need a separate protocol_instance? Will there be two instances of
> the same protocol for a single scmi device/instance?
> Else everything that has been defined in this struct in this patch the
> following ones can be rolled into scmi_protocol struct, right ?
> 

Basically scm_protocol represents generically a protocol with all its ops,
events and stuff, and it is registered during core init or protocol module
loading with the core so that it appears into the available protocols IDR.

Each protocol then, if available, is later initialized only on its first usage
after a get/ops or notify reg by an SCMI driver; now, this SCMI driver operates
on an handle which represents in fact an SCMI instance (server) and you could
have more than one instance in theory on the system (multiple SCMI servers
identified by multiple DT nodes).

As an example you could have two distinct MCU handling two different set of
sensors, so you'd have two distinct DT nodes representing each of these SCMI
servers (with distinct transport channels of course) and you'll endup with two
instances of the core SCMI platform driver (so 2 distinct handles), creating 2
distinct devices each for the sensor protocol, so ending up at the end with two
different running instances of the SCMI Sensor driver (hwmon) using a distinct
handle to communicate with the correspondent sensors: as a consequence I
initialize a distinct protocol instance structure for each handle, and keep
distinct resource accounting (gid an users) while keeping a single reference
(proto) to the same undelrying protocol descriptor; you could see as the
scmi_protocol representing a class and the scmi_protocol_instance being an
instantiated object (for a specific SCMI handle) in our poor C-Based OO world :D

In this way the two possible SCMI instances stacks are completely separated from
the lower transport channels up to the SCMI driver users, including the
intermediate protocols implementation.


> > +
> >   /**
> >    * struct scmi_info - Structure representing a SCMI instance
> >    *
> > @@ -80,6 +97,10 @@ struct scmi_xfers_info {
> >    * @rx_minfo: Universal Receive Message management info
> >    * @tx_idr: IDR object to map protocol id to Tx channel info pointer
> >    * @rx_idr: IDR object to map protocol id to Rx channel info pointer
> > + * @protocols: IDR for protocols' instance descriptors initialized for
> > + *	       this SCMI instance: populated on protocol's first attempted
> > + *	       usage.
> > + * @protocols_mtx: A mutex to protect protocols instances initialization.
> >    * @protocols_imp: List of protocols implemented, currently maximum of
> >    *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
> >    * @node: List head
> > @@ -94,6 +115,9 @@ struct scmi_info {
> >   	struct scmi_xfers_info rx_minfo;
> >   	struct idr tx_idr;
> >   	struct idr rx_idr;
> > +	struct idr protocols;
> > +	/* Ensure mutual exclusive access to protocols instance array */
> > +	struct mutex protocols_mtx;
> >   	u8 *protocols_imp;
> >   	struct list_head node;
> >   	int users;
> > @@ -519,6 +543,127 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
> >   	return ret;
> >   }
> > +/**
> > + * scmi_get_protocol_instance  - Protocol initialization helper.
> > + * @handle: A reference to the SCMI platform instance.
> > + * @protocol_id: The protocol being requested.
> > + *
> > + * In case the required protocol has never been requested before for this
> > + * instance, allocate and initialize all the needed structures while handling
> > + * resource allocation with a dedicated per-protocol devres subgroup.
> > + *
> > + * Return: A reference to an initialized protocol instance or error on failure.
> > + */
> > +static struct scmi_protocol_instance * __must_check
> > +scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
> > +{
> > +	int ret = -ENOMEM;
> > +	void *gid;
> > +	struct scmi_protocol_instance *pi;
> > +	struct scmi_info *info = handle_to_scmi_info(handle);
> > +
> > +	mutex_lock(&info->protocols_mtx);
> > +	pi = idr_find(&info->protocols, protocol_id);
> > +
> > +	if (pi) {
> > +		refcount_inc(&pi->users);
> > +	} else {
> > +		const struct scmi_protocol *proto;
> > +
> > +		/* Fail if protocol not registered on bus */
> > +		proto = scmi_get_protocol(protocol_id);
> > +		if (!proto) {
> > +			ret = -ENODEV;
> > +			goto out;
> > +		}
> > +
> > +		/* Protocol specific devres group */
> > +		gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
> > +		if (!gid)
> > +			goto out;
> > +
> > +		pi = devm_kzalloc(handle->dev, sizeof(*pi), GFP_KERNEL);
> > +		if (!pi)
> > +			goto clean;
> > +
> > +		pi->gid = gid;
> > +		pi->proto = proto;
> > +		refcount_set(&pi->users, 1);
> > +		/* proto->init is assured NON NULL by scmi_protocol_register */
> > +		ret = pi->proto->init(handle);
> 
> So init for a protocol will be called twice. Is this intentional ? Once
> during the device probe via scmi_dev_probe and scmi_protocol_init. And
> once via scmi_get_protocol_instance which gets called in get_ops apis
> defined and used in the later patches.
> 

This is part of the desperate attempt to split this series as much as
possible :D ... so that in this patch I introduce this code path for
initialization (and all the related helpers) but I'm using it only for
the Base protocol (via acquire) ... all the other standard protocols here
still reliy on the probe based legacy initialization, then later in the
series I drop all the init@probe code and rely only on this path for
init and also events registrations.

> > +		if (ret)
> > +			goto clean;
> > +
> > +		ret = idr_alloc(&info->protocols, pi,
> > +				protocol_id, protocol_id + 1, GFP_KERNEL);
> > +		if (ret != protocol_id)
> > +			goto clean;
> > +
> > +		devres_close_group(handle->dev, pi->gid);
> > +		dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
> > +			protocol_id);
> > +	}
> > +	mutex_unlock(&info->protocols_mtx);
> > +
> > +	return pi;
> > +
> > +clean:
> > +	devres_release_group(handle->dev, gid);
> > +out:
> > +	mutex_unlock(&info->protocols_mtx);
> > +	return ERR_PTR(ret);
> > +}
> > +
> > +/**
> > + * scmi_acquire_protocol  - Protocol acquire
> > + * @handle: A reference to the SCMI platform instance.
> > + * @protocol_id: The protocol being requested.
> > + *
> > + * Register a new user for the requested protocol on the specified SCMI
> > + * platform instance, possibly triggering its initialization on first user.
> > + *
> > + * Return: 0 if protocol was acquired successfully.
> > + */
> > +int scmi_acquire_protocol(struct scmi_handle *handle, u8 protocol_id)
> > +{
> > +	return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
> > +}
> > +
> > +/**
> > + * scmi_release_protocol  - Protocol de-initialization helper.
> > + * @handle: A reference to the SCMI platform instance.
> > + * @protocol_id: The protocol being requested.
> > + *
> > + * Remove one user for the specified protocol and triggers de-initialization
> > + * and resources de-allocation once the last user has gone.
> > + */
> > +void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
> > +{
> > +	struct scmi_info *info = handle_to_scmi_info(handle);
> > +	struct scmi_protocol_instance *pi;
> > +
> > +	mutex_lock(&info->protocols_mtx);
> > +	pi = idr_find(&info->protocols, protocol_id);
> > +	if (WARN_ON(!pi))
> > +		goto out;
> > +
> > +	if (refcount_dec_and_test(&pi->users)) {
> > +		void *gid = pi->gid;
> > +
> > +		if (pi->proto->deinit)
> > +			pi->proto->deinit(handle);
> > +
> > +		idr_remove(&info->protocols, protocol_id);
> > +
> > +		devres_release_group(handle->dev, gid);
> > +		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_handle *handle,
> >   				     u8 *prot_imp)
> >   {
> > @@ -785,6 +930,8 @@ static int scmi_probe(struct platform_device *pdev)
> >   	info->dev = dev;
> >   	info->desc = desc;
> >   	INIT_LIST_HEAD(&info->node);
> > +	idr_init(&info->protocols);
> > +	mutex_init(&info->protocols_mtx);
> >   	platform_set_drvdata(pdev, info);
> >   	idr_init(&info->tx_idr);
> > @@ -805,9 +952,14 @@ static int scmi_probe(struct platform_device *pdev)
> >   	if (scmi_notification_init(handle))
> >   		dev_err(dev, "SCMI Notifications NOT available.\n");
> > -	ret = scmi_base_protocol_init(handle);
> > +	/*
> > +	 * Trigger SCMI Base protocol initialization.
> > +	 * It's mandatory and won't be ever released/deinit until the
> > +	 * SCMI stack is shutdown/unloaded as a whole.
> > +	 */
> > +	ret = scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE);
> >   	if (ret) {
> > -		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> > +		dev_err(dev, "unable to communicate with SCMI\n");
> >   		return ret;
> >   	}
> > @@ -859,6 +1011,10 @@ static int scmi_remove(struct platform_device *pdev)
> >   	if (ret)
> >   		return ret;
> > +	mutex_lock(&info->protocols_mtx);
> > +	idr_destroy(&info->protocols);
> > +	mutex_unlock(&info->protocols_mtx);
> > +
> >   	/* Safe to free channels since no more users */
> >   	ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
> >   	idr_destroy(&info->tx_idr);
> > @@ -941,6 +1097,8 @@ static int __init scmi_driver_init(void)
> >   {
> >   	scmi_bus_init();
> > +	scmi_base_register();
> > +
> 
> minor nit. extra line addeed ?
> 

Yep.

Thanks

Cristian

> -- 
> Warm Regards
> Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-11-04 16:16   ` Thara Gopinath
@ 2020-11-04 16:58     ` Etienne Carriere
  2020-11-04 17:44     ` Cristian Marussi
  1 sibling, 0 replies; 21+ messages in thread
From: Etienne Carriere @ 2020-11-04 16:58 UTC (permalink / raw)
  To: Thara Gopinath
  Cc: Cristian Marussi, linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	Sudeep Holla, lukasz.luba, Jim Quinlan, Jonathan.Cameron,
	f.fainelli, Vincent Guittot, Souvik Chakravarty

Hello Thara,

On Wed, 4 Nov 2020 at 17:16, Thara Gopinath <thara.gopinath@linaro.org> wrote:
>
>
> Hi Cristian,
>
> On 10/28/20 4:29 PM, Cristian Marussi wrote:
> > Add basic protocol handles definitions and helpers support.
> > All protocols initialization code and SCMI drivers probing is still
> > performed using the handle based interface.
> >
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> >   drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
> >   drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
> >   2 files changed, 125 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> > index b08a8ddbc22a..f0678be02a09 100644
> > --- a/drivers/firmware/arm_scmi/common.h
> > +++ b/drivers/firmware/arm_scmi/common.h
> > @@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
> >                      size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> >   void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
> >                           struct scmi_xfer *xfer);
> > +
> > +struct scmi_xfer_ops;
> > +
> > +/**
> > + * struct scmi_protocol_handle  - Reference to an initialized protocol instance
> > + *
> > + * @dev: A reference to the associated SCMI instance device (handle->dev).
> > + * @xops: A reference to a struct holding refs to the core xfer operations that
> > + *     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.
> > + *
> > + * This structure represents a protocol initialized against specific SCMI
> > + * instance and it will be used as follows:
> > + * - as a parameter fed from the core to the protocol initialization code so
> > + *   that it can access the core xfer operations to build and generate SCMI
> > + *   messages exclusively for the specific underlying protocol instance.
> > + * - as an opaque handle fed by an SCMI driver user when it tries to access
> > + *   this protocol through its own protocol operations.
> > + *   In this case this handle will be returned as an opaque object together
> > + *   with the related protocol operations when the SCMI driver tries to access
> > + *   the protocol.
> > + */
> > +struct scmi_protocol_handle {
> > +     struct device *dev;
> > +     const struct scmi_xfer_ops *xops;
> > +     int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
> > +     void *(*get_priv)(const struct scmi_protocol_handle *ph);
> > +};
>
> So scmi_xfer_ops are the ops that actually talks with the scmi firmware
> on the other end , right ? IIUC, these ops are the same for all the
> protocols of a scmi instance. Imho, this struct is not the right place
> for these ops to reside.You are inadvertently exposing scmi internal
> details to the client drivers. There is no reason why this should be
> part of scmi_handle. The protocols can extract it from the handle during
> protocol_reigster, right?
>
> So, now to the second part, why do you need a scmi_protocol_handle?
> Again IIUC, if you have set_priv and get_priv hooks and get_ops and
> put_ops hooks, there is nothing that scmi_protocol_handle is providing
> extra, right? As mentioned in the comments for last patch any reason all
> of this cannot be rolled into scmi_protocol?
>
> As long as you are not supporting multiple scmi_protocol_instance and
> scmi_protocol_handle for a protocol, I don't think having separate
> structs make sense.

This is a scenario SCMI can face. Consider system with several
contained firmwares
(multi-coprocs) each can expose SCMI services hence multi protocol instances.
I.e 2 coproc each exposing some of their clocks to Linux that registers them.

Regards,
Etienne

> And you need to do this only if you think there can
> be multiple versions/instances of a protocol in the same scmi instance.
> Or am I missing something here ?
>
> > +
> > +/**
> > + * struct scmi_xfer_ops  - References to the core SCMI xfer operations.
> > + * @version_get: Get this version protocol.
> > + * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
> > + * @reset_rx_to_maxsz: Reset rx size to max transport size.
> > + * @do_xfer: Do the SCMI transfer.
> > + * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
> > + * @xfer_put: Free the xfer slot.
> > + *
> > + * Note that all this operations expect a protocol handle as first parameter;
> > + * they then internally use it to infer the underlying protocol number: this
> > + * way is not possible for a protocol implementation to forge messages for
> > + * another protocol.
> > + */
> > +struct scmi_xfer_ops {
> > +     int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
> > +     int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
> > +                          size_t tx_size, size_t rx_size,
> > +                          struct scmi_xfer **p);
> > +     void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
> > +                               struct scmi_xfer *xfer);
> > +     int (*do_xfer)(const struct scmi_protocol_handle *ph,
> > +                    struct scmi_xfer *xfer);
> > +     int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
> > +                                  struct scmi_xfer *xfer);
> > +     void (*xfer_put)(const struct scmi_protocol_handle *ph,
> > +                      struct scmi_xfer *xfer);
> > +};
> > +
> > +struct scmi_revision_info *
> > +scmi_get_revision_area(const struct scmi_protocol_handle *ph);
> >   int scmi_handle_put(const struct scmi_handle *handle);
> >   struct scmi_handle *scmi_handle_get(struct device *dev);
> >   void scmi_set_handle(struct scmi_device *scmi_dev);
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index beae8991422d..8ca04acb6abb 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -72,19 +72,28 @@ struct scmi_xfers_info {
> >
> >   /**
> >    * struct scmi_protocol_instance  - Describe an initialized protocol instance.
> > + * @handle: Reference to the SCMI handle associated to this protocol instance.
> >    * @proto: A reference to the protocol descriptor.
> >    * @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.
> > + * @ph: An embedded protocol handle that will be passed down to protocol
> > + *   initialization code to identify this instance.
> >    *
> >    * Each protocol is initialized independently once for each SCMI platform in
> >    * which is defined by DT and implemented by the SCMI server fw.
> >    */
> >   struct scmi_protocol_instance {
> > +     const struct scmi_handle        *handle;
> >       const struct scmi_protocol      *proto;
> >       void                            *gid;
> >       refcount_t                      users;
> > +     void                            *priv;
> > +     struct scmi_protocol_handle     ph;
> >   };
> >
> > +#define ph_to_pi(h)  container_of(h, struct scmi_protocol_instance, ph)
> > +
> >   /**
> >    * struct scmi_info - Structure representing a SCMI instance
> >    *
> > @@ -543,6 +552,57 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
> >       return ret;
> >   }
> >
> > +/**
> > + * scmi_set_protocol_priv  - Set protocol specific data at init time
> > + *
> > + * @ph: A reference to the protocol handle.
> > + * @priv: The private data to set.
> > + *
> > + * Return: 0 on Success
> > + */
> > +static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
> > +                               void *priv)
> > +{
> > +     struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +     pi->priv = priv;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * scmi_get_protocol_priv  - Set protocol specific data at init time
> > + *
> > + * @ph: A reference to the protocol handle.
> > + *
> > + * Return: Protocol private data if any was set.
> > + */
> > +static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
> > +{
> > +     const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +     return pi->priv;
> > +}
> > +
> > +/**
> > + * scmi_get_revision_area  - Retrieve version memory area.
> > + *
> > + * @ph: A reference to the protocol handle.
> > + *
> > + * A helper to grab the version memory area reference during SCMI Base protocol
> > + * initialization.
> > + *
> > + * Return: A reference to the version memory area associated to the SCMI
> > + *      instance underlying this protocol handle.
> > + */
> > +struct scmi_revision_info *
> > +scmi_get_revision_area(const struct scmi_protocol_handle *ph)
> > +{
> > +     const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +     return pi->handle->version;
> > +}
> > +
> >   /**
> >    * scmi_get_protocol_instance  - Protocol initialization helper.
> >    * @handle: A reference to the SCMI platform instance.
> > @@ -588,6 +648,10 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
> >
> >               pi->gid = gid;
> >               pi->proto = proto;
> > +             pi->handle = handle;
> > +             pi->ph.dev = handle->dev;
> > +             pi->ph.set_priv = scmi_set_protocol_priv;
> > +             pi->ph.get_priv = scmi_get_protocol_priv;
> >               refcount_set(&pi->users, 1);
> >               /* proto->init is assured NON NULL by scmi_protocol_register */
> >               ret = pi->proto->init(handle);
> >
>
> --
> Warm Regards
> Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-11-04 16:16   ` Thara Gopinath
  2020-11-04 16:58     ` Etienne Carriere
@ 2020-11-04 17:44     ` Cristian Marussi
  2020-11-06 16:26       ` Thara Gopinath
  1 sibling, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-11-04 17:44 UTC (permalink / raw)
  To: Thara Gopinath
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty

Hi

On Wed, Nov 04, 2020 at 11:16:18AM -0500, Thara Gopinath wrote:
> 
> Hi Cristian,
> 
> On 10/28/20 4:29 PM, Cristian Marussi wrote:
> > Add basic protocol handles definitions and helpers support.
> > All protocols initialization code and SCMI drivers probing is still
> > performed using the handle based interface.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> >   drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
> >   drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
> >   2 files changed, 125 insertions(+)
> > 
> > diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> > index b08a8ddbc22a..f0678be02a09 100644
> > --- a/drivers/firmware/arm_scmi/common.h
> > +++ b/drivers/firmware/arm_scmi/common.h
> > @@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
> >   		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> >   void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
> >   			    struct scmi_xfer *xfer);
> > +
> > +struct scmi_xfer_ops;
> > +
> > +/**
> > + * struct scmi_protocol_handle  - Reference to an initialized protocol instance
> > + *
> > + * @dev: A reference to the associated SCMI instance device (handle->dev).
> > + * @xops: A reference to a struct holding refs to the core xfer operations that
> > + *	  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.
> > + *
> > + * This structure represents a protocol initialized against specific SCMI
> > + * instance and it will be used as follows:
> > + * - as a parameter fed from the core to the protocol initialization code so
> > + *   that it can access the core xfer operations to build and generate SCMI
> > + *   messages exclusively for the specific underlying protocol instance.
> > + * - as an opaque handle fed by an SCMI driver user when it tries to access
> > + *   this protocol through its own protocol operations.
> > + *   In this case this handle will be returned as an opaque object together
> > + *   with the related protocol operations when the SCMI driver tries to access
> > + *   the protocol.
> > + */
> > +struct scmi_protocol_handle {
> > +	struct device *dev;
> > +	const struct scmi_xfer_ops *xops;
> > +	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
> > +	void *(*get_priv)(const struct scmi_protocol_handle *ph);
> > +};
> 
> So scmi_xfer_ops are the ops that actually talks with the scmi firmware on
> the other end , right ? IIUC, these ops are the same for all the protocols
> of a scmi instance. Imho, this struct is not the right place for these ops
> to reside.You are inadvertently exposing scmi internal details to the client
> drivers. There is no reason why this should be part of scmi_handle. The
> protocols can extract it from the handle during protocol_reigster, right?
> 
> So, now to the second part, why do you need a scmi_protocol_handle? Again
> IIUC, if you have set_priv and get_priv hooks and get_ops and put_ops hooks,
> there is nothing that scmi_protocol_handle is providing extra, right? As
> mentioned in the comments for last patch any reason all of this cannot be
> rolled into scmi_protocol?

The basic idea for protocol_hande existence is that the protocol code
should be able to access the core xfer ops (without EXPORTing all
scmi_xfer ops) but protoX should NOT be allowed to mistakenly or
maliciously build and send protoY messages: since the protocol_handle
for protoX is embedded in a specific protocol_instance in this way you
can call from your protocol code something like:

ph->xops->xfer_get_init(ph, ...)

and the core will transparently (and forcibly) call for you an xfer_get_init()
for protcolX.

Same goes for the get_priv/set_priv you can set and get only your protoX
private data.

This also avoids error prone repetitions of the protocol ID params all
over the protocol code (as pointed out previously by Florian).

At this point the problem is that I have to pass such protocol_handle
also as param into all the protocols_ops in order to avoid to reintroduce
some form of repetition inside the protocol code to derive the ph from the
handle with some helper like

	ph = scmi_map_protocol_handle(handle, SCMI_PERF_PROTOCOL)

which also indeed would have re-introduce the capability for protocolX
to maliciosuly or mistakenly build/send protoY messages.

At the same time, you're right, the xops should NOT be exposed instead
to the SCMI drivers, which must not be able to build tehir own rogue
messaes, and here the trick is indeed that scmi_protocol_handle is an
opaque object for SCMI drivers: it is NOT defined in scmi_protocol.h BUT
only declared so the SCMI drivers are not able to dereference it and
access the xops, nor anything else in fact.

In fact I could also have declared it as a void * all across the
protocols ops in scmi_protocol.h, but I thought some sort of type
checking was better (maybe I'm wrong)

So basically now, when an SCMI driver wants to use protoX, it issues a
	get_ops(handle, X)
	
and it gets back:

- a void * to be cast back to protoX_ops
- and an scmi_protocol_handle *ph which represents protoX for that specific
  SCMI instance (handle) which it has use all over the protoX_ops
  invocations:
  	
	protoX_ops->oper1->(ph, ...)

  with such ph being completely opaque for the SCMI driver calling oper1(ph,...),
  but acting as a well defined protocol_handle inside protocolX code.

> 
> As long as you are not supporting multiple scmi_protocol_instance and
> scmi_protocol_handle for a protocol, I don't think having separate structs
> make sense. And you need to do this only if you think there can be multiple
> versions/instances of a protocol in the same scmi instance.
> Or am I missing something here ?
> 

What really protocol_handle brings in is the fact that is embedded in a
specific protocol_instance which in turn is associated to a specific
SCMI server instance: so the SCMI drivers can refer to a very specific
SCMI instance when using that protocol, and at the same time the protocol
code instead can use the xops ONLY to build/send messages for the
protocol number for which they are supposed to send them.

So the separation of the structures is indeed wanted so that the
protocol code receive only the bare minimum access to the data it needs,
while the core processing the protocol xops requests can derive the
containing specific protocol_instance to act upon, so that
 - protocolX can send ONLY protoX messages
 - messageX are sennt to the proper protocol instance associated to that
   handle, which identifies one specific SCMI server amomgst possible
   multiple choices: an SCMI driver instance indeed operates on a very
   specific handle and use that to get hold a specific protocol_handle
   and proto_ops when calling something like:
   
      perf_ops = get_ops(handle, SCMI_PROTOCOL_PERF, &ph)
      perf_ops->ferq_get(ph, ...);

Regards

Cristian

> > +
> > +/**
> > + * struct scmi_xfer_ops  - References to the core SCMI xfer operations.
> > + * @version_get: Get this version protocol.
> > + * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
> > + * @reset_rx_to_maxsz: Reset rx size to max transport size.
> > + * @do_xfer: Do the SCMI transfer.
> > + * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
> > + * @xfer_put: Free the xfer slot.
> > + *
> > + * Note that all this operations expect a protocol handle as first parameter;
> > + * they then internally use it to infer the underlying protocol number: this
> > + * way is not possible for a protocol implementation to forge messages for
> > + * another protocol.
> > + */
> > +struct scmi_xfer_ops {
> > +	int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
> > +	int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
> > +			     size_t tx_size, size_t rx_size,
> > +			     struct scmi_xfer **p);
> > +	void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
> > +				  struct scmi_xfer *xfer);
> > +	int (*do_xfer)(const struct scmi_protocol_handle *ph,
> > +		       struct scmi_xfer *xfer);
> > +	int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
> > +				     struct scmi_xfer *xfer);
> > +	void (*xfer_put)(const struct scmi_protocol_handle *ph,
> > +			 struct scmi_xfer *xfer);
> > +};
> > +
> > +struct scmi_revision_info *
> > +scmi_get_revision_area(const struct scmi_protocol_handle *ph);
> >   int scmi_handle_put(const struct scmi_handle *handle);
> >   struct scmi_handle *scmi_handle_get(struct device *dev);
> >   void scmi_set_handle(struct scmi_device *scmi_dev);
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index beae8991422d..8ca04acb6abb 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -72,19 +72,28 @@ struct scmi_xfers_info {
> >   /**
> >    * struct scmi_protocol_instance  - Describe an initialized protocol instance.
> > + * @handle: Reference to the SCMI handle associated to this protocol instance.
> >    * @proto: A reference to the protocol descriptor.
> >    * @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.
> > + * @ph: An embedded protocol handle that will be passed down to protocol
> > + *	initialization code to identify this instance.
> >    *
> >    * Each protocol is initialized independently once for each SCMI platform in
> >    * which is defined by DT and implemented by the SCMI server fw.
> >    */
> >   struct scmi_protocol_instance {
> > +	const struct scmi_handle	*handle;
> >   	const struct scmi_protocol	*proto;
> >   	void				*gid;
> >   	refcount_t			users;
> > +	void				*priv;
> > +	struct scmi_protocol_handle	ph;
> >   };
> > +#define ph_to_pi(h)	container_of(h, struct scmi_protocol_instance, ph)
> > +
> >   /**
> >    * struct scmi_info - Structure representing a SCMI instance
> >    *
> > @@ -543,6 +552,57 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
> >   	return ret;
> >   }
> > +/**
> > + * scmi_set_protocol_priv  - Set protocol specific data at init time
> > + *
> > + * @ph: A reference to the protocol handle.
> > + * @priv: The private data to set.
> > + *
> > + * Return: 0 on Success
> > + */
> > +static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
> > +				  void *priv)
> > +{
> > +	struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +	pi->priv = priv;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scmi_get_protocol_priv  - Set protocol specific data at init time
> > + *
> > + * @ph: A reference to the protocol handle.
> > + *
> > + * Return: Protocol private data if any was set.
> > + */
> > +static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
> > +{
> > +	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +	return pi->priv;
> > +}
> > +
> > +/**
> > + * scmi_get_revision_area  - Retrieve version memory area.
> > + *
> > + * @ph: A reference to the protocol handle.
> > + *
> > + * A helper to grab the version memory area reference during SCMI Base protocol
> > + * initialization.
> > + *
> > + * Return: A reference to the version memory area associated to the SCMI
> > + *	   instance underlying this protocol handle.
> > + */
> > +struct scmi_revision_info *
> > +scmi_get_revision_area(const struct scmi_protocol_handle *ph)
> > +{
> > +	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
> > +
> > +	return pi->handle->version;
> > +}
> > +
> >   /**
> >    * scmi_get_protocol_instance  - Protocol initialization helper.
> >    * @handle: A reference to the SCMI platform instance.
> > @@ -588,6 +648,10 @@ scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
> >   		pi->gid = gid;
> >   		pi->proto = proto;
> > +		pi->handle = handle;
> > +		pi->ph.dev = handle->dev;
> > +		pi->ph.set_priv = scmi_set_protocol_priv;
> > +		pi->ph.get_priv = scmi_get_protocol_priv;
> >   		refcount_set(&pi->users, 1);
> >   		/* proto->init is assured NON NULL by scmi_protocol_register */
> >   		ret = pi->proto->init(handle);
> > 
> 
> -- 
> Warm Regards
> Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support
  2020-11-04 16:16   ` Thara Gopinath
@ 2020-11-04 18:08     ` Cristian Marussi
  2020-11-06 16:07       ` Thara Gopinath
  0 siblings, 1 reply; 21+ messages in thread
From: Cristian Marussi @ 2020-11-04 18:08 UTC (permalink / raw)
  To: Thara Gopinath
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty

Hi,

On Wed, Nov 04, 2020 at 11:16:31AM -0500, Thara Gopinath wrote:
> Hi Cristian,
> 
> On 10/28/20 4:29 PM, Cristian Marussi wrote:
> > Expose a new generic get/put protocols API based on protocol handles;
> > provide also a devres managed version also for notifications.
> 
> minor nit.. Maybe yous should reword this! Kind of confusing to understand!
> 
> Also, if it was me, I will separate the notifications and get/put hooks
> into two separate patches. Not an issue though if you want to keep it
> in the same patch.

You're right I have to reword the commit message, and I'll review the
possibility of a further split, even though many parts of this series
are tightly bound together given the kind of changes so sometimes to
avoid breaking bisectability I had to push painfully long patches (like
6/8 :< )...but maybe I've got it wrong and this is not the case here.

> 
> > All SCMI drivers still keep using the old handle based interface.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> >   drivers/firmware/arm_scmi/driver.c | 126 +++++++++++++++++++++++++++++
> >   drivers/firmware/arm_scmi/notify.c | 123 ++++++++++++++++++++++++++++
> >   include/linux/scmi_protocol.h      |  34 +++++++-
> >   3 files changed, 282 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index 8ca04acb6abb..4d86aafbf465 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -15,6 +15,7 @@
> >    */
> >   #include <linux/bitmap.h>
> > +#include <linux/device.h>
> >   #include <linux/export.h>
> >   #include <linux/idr.h>
> >   #include <linux/io.h>
> > @@ -728,6 +729,38 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
> >   	mutex_unlock(&info->protocols_mtx);
> >   }
> > +/**
> > + * scmi_get_protocol_operations  - Get protocol operations
> > + * @handle: A reference to the SCMI platform instance.
> > + * @protocol_id: The protocol being requested.
> > + * @ph: A pointer reference used to pass back the associated protocol handle.
> > + *
> > + * Get hold of a protocol accounting for its usage, eventually triggering its
> > + * initialization, and returning the protocol specific operations and related
> > + * protocol handle which will be used as first argument in most of the protocols
> > + * operations methods.
> > + *
> > + * Return: A reference to the requested protocol operations or error.
> > + *	   Must be checked for errors by caller.
> > + */
> > +static const void __must_check *
> > +scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
> > +			     struct scmi_protocol_handle **ph)
> > +{
> > +	struct scmi_protocol_instance *pi;
> > +
> > +	if (!ph)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	pi = scmi_get_protocol_instance(handle, protocol_id);
> > +	if (IS_ERR(pi))
> > +		return pi;
> > +
> > +	*ph = &pi->ph;
> > +
> > +	return pi->proto->ops;
> > +}
> > +
> >   void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> >   				     u8 *prot_imp)
> >   {
> > @@ -751,6 +784,95 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
> >   	return false;
> >   }
> > +struct scmi_protocol_devres {
> > +	struct scmi_handle *handle;
> > +	u8 protocol_id;
> > +};
> > +
> > +static void scmi_devm_release_protocol(struct device *dev, void *res)
> > +{
> > +	struct scmi_protocol_devres *dres = res;
> > +
> > +	scmi_release_protocol(dres->handle, dres->protocol_id);
> > +}
> > +
> > +/**
> > + * scmi_devm_get_protocol_ops  - Devres managed get protocol operations
> > + * @sdev: A reference to an scmi_device whose embedded struct device is to
> > + *	  be used for devres accounting.
> > + * @protocol_id: The protocol being requested.
> > + * @ph: A pointer reference used to pass back the associated protocol handle.
> > + *
> > + * Get hold of a protocol accounting for its usage, eventually triggering its
> > + * initialization, and returning the protocol specific operations and related
> > + * protocol handle which will be used as first argument in most of the
> > + * protocols operations methods.
> > + * Being a devres based managed method, protocol hold will be automatically
> > + * released, and possibly de-initialized on last user, once the SCMI driver
> > + * owning the scmi_device is unbound from it.
> > + *
> > + * Return: A reference to the requested protocol operations or error.
> > + *	   Must be checked for errors by caller.
> > + */
> > +static const void __must_check *
> > +scmi_devm_get_protocol_ops(struct scmi_device *sdev, u8 protocol_id,
> > +			   struct scmi_protocol_handle **ph)
> > +{
> > +	struct scmi_protocol_instance *pi;
> > +	struct scmi_protocol_devres *dres;
> > +	struct scmi_handle *handle = sdev->handle;
> > +
> > +	if (!ph)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	dres = devres_alloc(scmi_devm_release_protocol,
> > +			    sizeof(*dres), GFP_KERNEL);
> > +	if (!dres)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	pi = scmi_get_protocol_instance(handle, protocol_id);
> > +	if (IS_ERR(pi)) {
> > +		devres_free(dres);
> > +		return pi;
> > +	}
> > +
> > +	dres->handle = handle;
> > +	dres->protocol_id = protocol_id;
> > +	devres_add(&sdev->dev, dres);
> > +
> > +	*ph = &pi->ph;
> > +
> > +	return pi->proto->ops;
> > +}
> > +
> > +static int scmi_devm_protocol_match(struct device *dev, void *res, void *data)
> > +{
> > +	struct scmi_protocol_devres *dres = res;
> > +
> > +	if (WARN_ON(!dres || !data))
> > +		return 0;
> > +
> > +	return dres->protocol_id == *((u8 *)data);
> > +}
> > +
> > +/**
> > + * scmi_devm_put_protocol_ops  - Devres managed put protocol operations
> > + * @sdev: A reference to an scmi_device whose embedded struct device is to
> > + *	  be used for devres accounting.
> > + * @protocol_id: The protocol being requested.
> > + *
> > + * Explicitly release a protocol hold previously obtained calling the above
> > + * @scmi_devm_get_protocol_ops.
> > + */
> > +static void scmi_devm_put_protocol_ops(struct scmi_device *sdev, u8 protocol_id)
> > +{
> > +	int ret;
> > +
> > +	ret = devres_release(&sdev->dev, scmi_devm_release_protocol,
> > +			     scmi_devm_protocol_match, &protocol_id);
> > +	WARN_ON(ret);
> > +}
> > +
> >   /**
> >    * scmi_handle_get() - Get the SCMI handle for a device
> >    *
> > @@ -1004,6 +1126,10 @@ static int scmi_probe(struct platform_device *pdev)
> >   	handle = &info->handle;
> >   	handle->dev = info->dev;
> >   	handle->version = &info->version;
> > +	handle->devm_get_ops = scmi_devm_get_protocol_ops;
> > +	handle->devm_put_ops = scmi_devm_put_protocol_ops;
> > +	handle->get_ops = scmi_get_protocol_operations;
> > +	handle->put_ops = scmi_release_protocol;
> 
> Why do you need a dev_res version and a non dev_res version? I checked
> patch 6 where you convert the drivers to use these hooks and all of them
> are using the dev res apis.
> 

Yes indeed, I wanted to drop the non devm_ version, but I was not sure
if for completeness I should have instead not provided...So I left it
to have it discussed on the list...I was hoping to be told to remove
those non devres :D

Also because in fact any current or future SCMI driver (that are the only
possible users of this interface) will certainly have an scmi_dev to use
with devm_() methods.

Probably the same is true for notify_ops, I could stick with the new
devres managed versions.

> >   	ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
> >   	if (ret)
> > diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
> > index eae58b2a92cc..64d43e425644 100644
> > --- a/drivers/firmware/arm_scmi/notify.c
> > +++ b/drivers/firmware/arm_scmi/notify.c
> > @@ -1370,6 +1370,127 @@ static int scmi_unregister_notifier(const struct scmi_handle *handle,
> >   	return 0;
> >   }
> > +struct scmi_notifier_devres {
> > +	const struct scmi_handle *handle;
> > +	u8 proto_id;
> > +	u8 evt_id;
> > +	u32 __src_id;
> > +	u32 *src_id;
> 
> Instead of maintaining two separate pointers for src id,
> why not define a bool, something like is_src_id_valid?
> Should simply this a bit and also don't have to maintain two 32 bit
> pointers. What do you think?
> 

Yes, I did not like so much this trick above, I'll review in V3.

> > +	struct notifier_block *nb;
> > +};
> > +
> > +static void scmi_devm_release_notifier(struct device *dev, void *res)
> > +{
> > +	struct scmi_notifier_devres *dres = res;
> > +
> > +	scmi_unregister_notifier(dres->handle, dres->proto_id, dres->evt_id,
> > +				 dres->src_id, dres->nb);
> > +}
> > +
> > +/**
> > + * scmi_devm_register_notifier()  - Managed registration of a notifier_block
> > + * for an event
> > + * @sdev: A reference to an scmi_device whose embedded struct device is to
> > + *	  be used for devres accounting.
> > + * @proto_id: Protocol ID
> > + * @evt_id: Event ID
> > + * @src_id: Source ID, when NULL register for events coming form ALL possible
> > + *	    sources
> > + * @nb: A standard notifier block to register for the specified event
> > + *
> > + * Generic devres managed helper to register a notifier_block against a
> > + * protocol event.
> > + */
> > +static int scmi_devm_register_notifier(struct scmi_device *sdev,
> > +				       u8 proto_id, u8 evt_id, u32 *src_id,
> > +				       struct notifier_block *nb)
> > +{
> > +	int ret;
> > +	struct scmi_notifier_devres *dres;
> > +
> > +	dres = devres_alloc(scmi_devm_release_notifier,
> > +			    sizeof(*dres), GFP_KERNEL);
> > +	if (!dres)
> > +		return -ENOMEM;
> > +
> > +	ret = scmi_register_notifier(sdev->handle, proto_id,
> > +				     evt_id, src_id, nb);
> > +	if (ret) {
> > +		devres_free(dres);
> > +		return ret;
> > +	}
> > +
> > +	dres->handle = sdev->handle;
> > +	dres->proto_id = proto_id;
> > +	dres->evt_id = evt_id;
> > +	dres->nb = nb;
> > +	if (src_id) {
> > +		dres->__src_id = *src_id;
> > +		dres->src_id = &dres->__src_id;
> > +	} else {
> > +		dres->src_id = NULL;
> > +	}
> > +	devres_add(&sdev->dev, dres);
> > +
> > +	return ret;
> > +}
> > +
> > +static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
> > +{
> > +	struct scmi_notifier_devres *dres = res;
> > +	struct scmi_notifier_devres *xres = data;
> > +
> > +	if (WARN_ON(!dres || !xres))
> > +		return 0;
> > +
> > +	return dres->proto_id == xres->proto_id &&
> > +		dres->evt_id == xres->evt_id &&
> > +		dres->nb == xres->nb &&
> 
> Does the nb have to be compared as well ?
> 

So the notify_ops API allows you to register multiple different callbacks
for exactly the same event tuple (proto_id, evt_id, src_id), and even though
on driver detach you'll automatically just unregister all the notifications
registered for the specific scmi_dev, you should be able to explicitly
devm_unregister() just a very specific callback if you want.

> > +		((!dres->src_id && !xres->src_id) ||
> > +		  (dres->src_id && xres->src_id &&
> > +		   dres->__src_id == xres->__src_id));
> 
> 
> -- 
> Warm Regards
> Thara

Cheers

Cristian

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support
  2020-11-04 18:08     ` Cristian Marussi
@ 2020-11-06 16:07       ` Thara Gopinath
  0 siblings, 0 replies; 21+ messages in thread
From: Thara Gopinath @ 2020-11-06 16:07 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty



On 11/4/20 1:08 PM, Cristian Marussi wrote:
> Hi,
> 
> On Wed, Nov 04, 2020 at 11:16:31AM -0500, Thara Gopinath wrote:
>> Hi Cristian,
>>
>> On 10/28/20 4:29 PM, Cristian Marussi wrote:
>>> Expose a new generic get/put protocols API based on protocol handles;
>>> provide also a devres managed version also for notifications.
>>
>> minor nit.. Maybe yous should reword this! Kind of confusing to understand!
>>
>> Also, if it was me, I will separate the notifications and get/put hooks
>> into two separate patches. Not an issue though if you want to keep it
>> in the same patch.
> 
> You're right I have to reword the commit message, and I'll review the
> possibility of a further split, even though many parts of this series
> are tightly bound together given the kind of changes so sometimes to
> avoid breaking bisectability I had to push painfully long patches (like
> 6/8 :< )...but maybe I've got it wrong and this is not the case here.
> 
>>
>>> All SCMI drivers still keep using the old handle based interface.
>>>
>>> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
>>> ---
>>>    drivers/firmware/arm_scmi/driver.c | 126 +++++++++++++++++++++++++++++
>>>    drivers/firmware/arm_scmi/notify.c | 123 ++++++++++++++++++++++++++++
>>>    include/linux/scmi_protocol.h      |  34 +++++++-
>>>    3 files changed, 282 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
>>> index 8ca04acb6abb..4d86aafbf465 100644
>>> --- a/drivers/firmware/arm_scmi/driver.c
>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>> @@ -15,6 +15,7 @@
>>>     */
>>>    #include <linux/bitmap.h>
>>> +#include <linux/device.h>
>>>    #include <linux/export.h>
>>>    #include <linux/idr.h>
>>>    #include <linux/io.h>
>>> @@ -728,6 +729,38 @@ void scmi_release_protocol(struct scmi_handle *handle, u8 protocol_id)
>>>    	mutex_unlock(&info->protocols_mtx);
>>>    }
>>> +/**
>>> + * scmi_get_protocol_operations  - Get protocol operations
>>> + * @handle: A reference to the SCMI platform instance.
>>> + * @protocol_id: The protocol being requested.
>>> + * @ph: A pointer reference used to pass back the associated protocol handle.
>>> + *
>>> + * Get hold of a protocol accounting for its usage, eventually triggering its
>>> + * initialization, and returning the protocol specific operations and related
>>> + * protocol handle which will be used as first argument in most of the protocols
>>> + * operations methods.
>>> + *
>>> + * Return: A reference to the requested protocol operations or error.
>>> + *	   Must be checked for errors by caller.
>>> + */
>>> +static const void __must_check *
>>> +scmi_get_protocol_operations(struct scmi_handle *handle, u8 protocol_id,
>>> +			     struct scmi_protocol_handle **ph)
>>> +{
>>> +	struct scmi_protocol_instance *pi;
>>> +
>>> +	if (!ph)
>>> +		return ERR_PTR(-EINVAL);
>>> +
>>> +	pi = scmi_get_protocol_instance(handle, protocol_id);
>>> +	if (IS_ERR(pi))
>>> +		return pi;
>>> +
>>> +	*ph = &pi->ph;
>>> +
>>> +	return pi->proto->ops;
>>> +}
>>> +
>>>    void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
>>>    				     u8 *prot_imp)
>>>    {
>>> @@ -751,6 +784,95 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
>>>    	return false;
>>>    }
>>> +struct scmi_protocol_devres {
>>> +	struct scmi_handle *handle;
>>> +	u8 protocol_id;
>>> +};
>>> +
>>> +static void scmi_devm_release_protocol(struct device *dev, void *res)
>>> +{
>>> +	struct scmi_protocol_devres *dres = res;
>>> +
>>> +	scmi_release_protocol(dres->handle, dres->protocol_id);
>>> +}
>>> +
>>> +/**
>>> + * scmi_devm_get_protocol_ops  - Devres managed get protocol operations
>>> + * @sdev: A reference to an scmi_device whose embedded struct device is to
>>> + *	  be used for devres accounting.
>>> + * @protocol_id: The protocol being requested.
>>> + * @ph: A pointer reference used to pass back the associated protocol handle.
>>> + *
>>> + * Get hold of a protocol accounting for its usage, eventually triggering its
>>> + * initialization, and returning the protocol specific operations and related
>>> + * protocol handle which will be used as first argument in most of the
>>> + * protocols operations methods.
>>> + * Being a devres based managed method, protocol hold will be automatically
>>> + * released, and possibly de-initialized on last user, once the SCMI driver
>>> + * owning the scmi_device is unbound from it.
>>> + *
>>> + * Return: A reference to the requested protocol operations or error.
>>> + *	   Must be checked for errors by caller.
>>> + */
>>> +static const void __must_check *
>>> +scmi_devm_get_protocol_ops(struct scmi_device *sdev, u8 protocol_id,
>>> +			   struct scmi_protocol_handle **ph)
>>> +{
>>> +	struct scmi_protocol_instance *pi;
>>> +	struct scmi_protocol_devres *dres;
>>> +	struct scmi_handle *handle = sdev->handle;
>>> +
>>> +	if (!ph)
>>> +		return ERR_PTR(-EINVAL);
>>> +
>>> +	dres = devres_alloc(scmi_devm_release_protocol,
>>> +			    sizeof(*dres), GFP_KERNEL);
>>> +	if (!dres)
>>> +		return ERR_PTR(-ENOMEM);
>>> +
>>> +	pi = scmi_get_protocol_instance(handle, protocol_id);
>>> +	if (IS_ERR(pi)) {
>>> +		devres_free(dres);
>>> +		return pi;
>>> +	}
>>> +
>>> +	dres->handle = handle;
>>> +	dres->protocol_id = protocol_id;
>>> +	devres_add(&sdev->dev, dres);
>>> +
>>> +	*ph = &pi->ph;
>>> +
>>> +	return pi->proto->ops;
>>> +}
>>> +
>>> +static int scmi_devm_protocol_match(struct device *dev, void *res, void *data)
>>> +{
>>> +	struct scmi_protocol_devres *dres = res;
>>> +
>>> +	if (WARN_ON(!dres || !data))
>>> +		return 0;
>>> +
>>> +	return dres->protocol_id == *((u8 *)data);
>>> +}
>>> +
>>> +/**
>>> + * scmi_devm_put_protocol_ops  - Devres managed put protocol operations
>>> + * @sdev: A reference to an scmi_device whose embedded struct device is to
>>> + *	  be used for devres accounting.
>>> + * @protocol_id: The protocol being requested.
>>> + *
>>> + * Explicitly release a protocol hold previously obtained calling the above
>>> + * @scmi_devm_get_protocol_ops.
>>> + */
>>> +static void scmi_devm_put_protocol_ops(struct scmi_device *sdev, u8 protocol_id)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = devres_release(&sdev->dev, scmi_devm_release_protocol,
>>> +			     scmi_devm_protocol_match, &protocol_id);
>>> +	WARN_ON(ret);
>>> +}
>>> +
>>>    /**
>>>     * scmi_handle_get() - Get the SCMI handle for a device
>>>     *
>>> @@ -1004,6 +1126,10 @@ static int scmi_probe(struct platform_device *pdev)
>>>    	handle = &info->handle;
>>>    	handle->dev = info->dev;
>>>    	handle->version = &info->version;
>>> +	handle->devm_get_ops = scmi_devm_get_protocol_ops;
>>> +	handle->devm_put_ops = scmi_devm_put_protocol_ops;
>>> +	handle->get_ops = scmi_get_protocol_operations;
>>> +	handle->put_ops = scmi_release_protocol;
>>
>> Why do you need a dev_res version and a non dev_res version? I checked
>> patch 6 where you convert the drivers to use these hooks and all of them
>> are using the dev res apis.
>>
> 
> Yes indeed, I wanted to drop the non devm_ version, but I was not sure
> if for completeness I should have instead not provided...So I left it
> to have it discussed on the list...I was hoping to be told to remove
> those non devres :D
> 
> Also because in fact any current or future SCMI driver (that are the only
> possible users of this interface) will certainly have an scmi_dev to use
> with devm_() methods.
> 
> Probably the same is true for notify_ops, I could stick with the new
> devres managed versions.

I think you should drop it, unless any one else has issues not having it.

-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface
  2020-11-04 16:56     ` Cristian Marussi
@ 2020-11-06 16:22       ` Thara Gopinath
  0 siblings, 0 replies; 21+ messages in thread
From: Thara Gopinath @ 2020-11-06 16:22 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty



On 11/4/20 11:56 AM, Cristian Marussi wrote:
> Hi Thara,
> 
> thanks for reviewing.
> 
> On Wed, Nov 04, 2020 at 11:16:06AM -0500, Thara Gopinath wrote:
>> Hi Cristian,
>>
>> On 10/28/20 4:29 PM, Cristian Marussi wrote:
>>> Extend common protocol registration routines and provide some new generic
>>> protocols' init/deinit helpers that tracks protocols' users and automatically
>>> perform the proper initialization/de-initialization on demand.
>>>
>>> Convert all protocols to use new registration schema while modifying only Base
>>> protocol to use also the new initialization helpers.
>>>
>>> All other standard protocols' initialization is still umodified and bound to
>>> SCMI devices probing.
>>
>> minor nit : umodified->unmodified.
>>>
> 
> Right.
> 
>>> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
>>> ---
>>> v1 --> v2
>>> - made all scmi_protocol refs const
>>> - introduced IDR to handle protocols instead of static array
>>> - refactored code around fast path
>>> ---
>>>    drivers/firmware/arm_scmi/base.c    |  10 +-
>>>    drivers/firmware/arm_scmi/bus.c     |  61 +++++++---
>>>    drivers/firmware/arm_scmi/clock.c   |  10 +-
>>>    drivers/firmware/arm_scmi/common.h  |  31 ++++-
>>>    drivers/firmware/arm_scmi/driver.c  | 168 +++++++++++++++++++++++++++-
>>>    drivers/firmware/arm_scmi/notify.c  |   3 +-
>>>    drivers/firmware/arm_scmi/perf.c    |  10 +-
>>>    drivers/firmware/arm_scmi/power.c   |  10 +-
>>>    drivers/firmware/arm_scmi/reset.c   |  10 +-
>>>    drivers/firmware/arm_scmi/sensors.c |  10 +-
>>>    drivers/firmware/arm_scmi/system.c  |   8 +-
>>>    include/linux/scmi_protocol.h       |   6 +-
>>>    12 files changed, 299 insertions(+), 38 deletions(-)
>>>
>>
>> [...]
>>
>>
>>> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
>>> index 3dfd8b6a0ebf..beae8991422d 100644
>>> --- a/drivers/firmware/arm_scmi/driver.c
>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>> @@ -11,11 +11,12 @@
>>>     * various power domain DVFS including the core/cluster, certain system
>>>     * clocks configuration, thermal sensors and many others.
>>>     *
>>> - * Copyright (C) 2018 ARM Ltd.
>>> + * Copyright (C) 2018-2020 ARM Ltd.
>>>     */
>>>    #include <linux/bitmap.h>
>>>    #include <linux/export.h>
>>> +#include <linux/idr.h>
>>>    #include <linux/io.h>
>>>    #include <linux/kernel.h>
>>>    #include <linux/ktime.h>
>>> @@ -23,6 +24,7 @@
>>>    #include <linux/of_address.h>
>>>    #include <linux/of_device.h>
>>>    #include <linux/processor.h>
>>> +#include <linux/refcount.h>
>>>    #include <linux/slab.h>
>>>    #include "common.h"
>>> @@ -68,6 +70,21 @@ struct scmi_xfers_info {
>>>    	spinlock_t xfer_lock;
>>>    };
>>> +/**
>>> + * struct scmi_protocol_instance  - Describe an initialized protocol instance.
>>> + * @proto: A reference to the protocol descriptor.
>>> + * @gid: A reference for per-protocol devres management.
>>> + * @users: A refcount to track effective users of this protocol.
>>> + *
>>> + * Each protocol is initialized independently once for each SCMI platform in
>>> + * which is defined by DT and implemented by the SCMI server fw.
>>> + */
>>> +struct scmi_protocol_instance {
>>> +	const struct scmi_protocol	*proto;
>>> +	void				*gid;
>>> +	refcount_t			users;
>>> +};
>>
>> Why do you need a separate protocol_instance? Will there be two instances of
>> the same protocol for a single scmi device/instance?
>> Else everything that has been defined in this struct in this patch the
>> following ones can be rolled into scmi_protocol struct, right ?
>>
> 
> Basically scm_protocol represents generically a protocol with all its ops,
> events and stuff, and it is registered during core init or protocol module
> loading with the core so that it appears into the available protocols IDR.
> 
> Each protocol then, if available, is later initialized only on its first usage
> after a get/ops or notify reg by an SCMI driver; now, this SCMI driver operates
> on an handle which represents in fact an SCMI instance (server) and you could
> have more than one instance in theory on the system (multiple SCMI servers
> identified by multiple DT nodes).
> 
> As an example you could have two distinct MCU handling two different set of
> sensors, so you'd have two distinct DT nodes representing each of these SCMI
> servers (with distinct transport channels of course) and you'll endup with two
> instances of the core SCMI platform driver (so 2 distinct handles), creating 2
> distinct devices each for the sensor protocol, so ending up at the end with two
> different running instances of the SCMI Sensor driver (hwmon) using a distinct
> handle to communicate with the correspondent sensors: as a consequence I
> initialize a distinct protocol instance structure for each handle, and keep
> distinct resource accounting (gid an users) while keeping a single reference
> (proto) to the same undelrying protocol descriptor; you could see as the
> scmi_protocol representing a class and the scmi_protocol_instance being an
> instantiated object (for a specific SCMI handle) in our poor C-Based OO world :D
> 
> In this way the two possible SCMI instances stacks are completely separated from
> the lower transport channels up to the SCMI driver users, including the
> intermediate protocols implementation.

Thanks for the explanation. I understand that this is to maintain two 
separate SCMI instances with the same underlying protocol ops. I think 
the confusing part here is why this struct does not have pointer to 
scmi_handle and I see that you have added it as part of patch 2. May be 
you can move that up here to indicate that same protocol can be used by 
two separate scmi server instances.

> 
> 
>>> +
>>>    /**
>>>     * struct scmi_info - Structure representing a SCMI instance
>>>     *
>>> @@ -80,6 +97,10 @@ struct scmi_xfers_info {
>>>     * @rx_minfo: Universal Receive Message management info
>>>     * @tx_idr: IDR object to map protocol id to Tx channel info pointer
>>>     * @rx_idr: IDR object to map protocol id to Rx channel info pointer
>>> + * @protocols: IDR for protocols' instance descriptors initialized for
>>> + *	       this SCMI instance: populated on protocol's first attempted
>>> + *	       usage.
>>> + * @protocols_mtx: A mutex to protect protocols instances initialization.
>>>     * @protocols_imp: List of protocols implemented, currently maximum of
>>>     *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
>>>     * @node: List head
>>> @@ -94,6 +115,9 @@ struct scmi_info {
>>>    	struct scmi_xfers_info rx_minfo;
>>>    	struct idr tx_idr;
>>>    	struct idr rx_idr;
>>> +	struct idr protocols;
>>> +	/* Ensure mutual exclusive access to protocols instance array */
>>> +	struct mutex protocols_mtx;
>>>    	u8 *protocols_imp;
>>>    	struct list_head node;
>>>    	int users;
>>> @@ -519,6 +543,127 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
>>>    	return ret;
>>>    }
>>> +/**
>>> + * scmi_get_protocol_instance  - Protocol initialization helper.
>>> + * @handle: A reference to the SCMI platform instance.
>>> + * @protocol_id: The protocol being requested.
>>> + *
>>> + * In case the required protocol has never been requested before for this
>>> + * instance, allocate and initialize all the needed structures while handling
>>> + * resource allocation with a dedicated per-protocol devres subgroup.
>>> + *
>>> + * Return: A reference to an initialized protocol instance or error on failure.
>>> + */
>>> +static struct scmi_protocol_instance * __must_check
>>> +scmi_get_protocol_instance(struct scmi_handle *handle, u8 protocol_id)
>>> +{
>>> +	int ret = -ENOMEM;
>>> +	void *gid;
>>> +	struct scmi_protocol_instance *pi;
>>> +	struct scmi_info *info = handle_to_scmi_info(handle);
>>> +
>>> +	mutex_lock(&info->protocols_mtx);
>>> +	pi = idr_find(&info->protocols, protocol_id);
>>> +
>>> +	if (pi) {
>>> +		refcount_inc(&pi->users);
>>> +	} else {
>>> +		const struct scmi_protocol *proto;
>>> +
>>> +		/* Fail if protocol not registered on bus */
>>> +		proto = scmi_get_protocol(protocol_id);
>>> +		if (!proto) {
>>> +			ret = -ENODEV;
>>> +			goto out;
>>> +		}
>>> +
>>> +		/* Protocol specific devres group */
>>> +		gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
>>> +		if (!gid)
>>> +			goto out;
>>> +
>>> +		pi = devm_kzalloc(handle->dev, sizeof(*pi), GFP_KERNEL);
>>> +		if (!pi)
>>> +			goto clean;
>>> +
>>> +		pi->gid = gid;
>>> +		pi->proto = proto;
>>> +		refcount_set(&pi->users, 1);
>>> +		/* proto->init is assured NON NULL by scmi_protocol_register */
>>> +		ret = pi->proto->init(handle);
>>
>> So init for a protocol will be called twice. Is this intentional ? Once
>> during the device probe via scmi_dev_probe and scmi_protocol_init. And
>> once via scmi_get_protocol_instance which gets called in get_ops apis
>> defined and used in the later patches.
>>
> 
> This is part of the desperate attempt to split this series as much as
> possible :D ... so that in this patch I introduce this code path for
> initialization (and all the related helpers) but I'm using it only for
> the Base protocol (via acquire) ... all the other standard protocols here
> still reliy on the probe based legacy initialization, then later in the
> series I drop all the init@probe code and rely only on this path for
> init and also events registrations.

Ok.. I did not closely check this part in patch 6. If you are removing 
the double init, It should be ok.



-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-11-04 17:44     ` Cristian Marussi
@ 2020-11-06 16:26       ` Thara Gopinath
  2020-11-07 12:53         ` Cristian Marussi
  0 siblings, 1 reply; 21+ messages in thread
From: Thara Gopinath @ 2020-11-06 16:26 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty



On 11/4/20 12:44 PM, Cristian Marussi wrote:
> Hi
> 
> On Wed, Nov 04, 2020 at 11:16:18AM -0500, Thara Gopinath wrote:
>>
>> Hi Cristian,
>>
>> On 10/28/20 4:29 PM, Cristian Marussi wrote:
>>> Add basic protocol handles definitions and helpers support.
>>> All protocols initialization code and SCMI drivers probing is still
>>> performed using the handle based interface.
>>>
>>> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
>>> ---
>>>    drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
>>>    drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
>>>    2 files changed, 125 insertions(+)
>>>
>>> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
>>> index b08a8ddbc22a..f0678be02a09 100644
>>> --- a/drivers/firmware/arm_scmi/common.h
>>> +++ b/drivers/firmware/arm_scmi/common.h
>>> @@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
>>>    		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
>>>    void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
>>>    			    struct scmi_xfer *xfer);
>>> +
>>> +struct scmi_xfer_ops;
>>> +
>>> +/**
>>> + * struct scmi_protocol_handle  - Reference to an initialized protocol instance
>>> + *
>>> + * @dev: A reference to the associated SCMI instance device (handle->dev).
>>> + * @xops: A reference to a struct holding refs to the core xfer operations that
>>> + *	  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.
>>> + *
>>> + * This structure represents a protocol initialized against specific SCMI
>>> + * instance and it will be used as follows:
>>> + * - as a parameter fed from the core to the protocol initialization code so
>>> + *   that it can access the core xfer operations to build and generate SCMI
>>> + *   messages exclusively for the specific underlying protocol instance.
>>> + * - as an opaque handle fed by an SCMI driver user when it tries to access
>>> + *   this protocol through its own protocol operations.
>>> + *   In this case this handle will be returned as an opaque object together
>>> + *   with the related protocol operations when the SCMI driver tries to access
>>> + *   the protocol.
>>> + */
>>> +struct scmi_protocol_handle {
>>> +	struct device *dev;
>>> +	const struct scmi_xfer_ops *xops;
>>> +	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
>>> +	void *(*get_priv)(const struct scmi_protocol_handle *ph);
>>> +};
>>
>> So scmi_xfer_ops are the ops that actually talks with the scmi firmware on
>> the other end , right ? IIUC, these ops are the same for all the protocols
>> of a scmi instance. Imho, this struct is not the right place for these ops
>> to reside.You are inadvertently exposing scmi internal details to the client
>> drivers. There is no reason why this should be part of scmi_handle. The
>> protocols can extract it from the handle during protocol_reigster, right?
>>
>> So, now to the second part, why do you need a scmi_protocol_handle? Again
>> IIUC, if you have set_priv and get_priv hooks and get_ops and put_ops hooks,
>> there is nothing that scmi_protocol_handle is providing extra, right? As
>> mentioned in the comments for last patch any reason all of this cannot be
>> rolled into scmi_protocol?
> 
> The basic idea for protocol_hande existence is that the protocol code
> should be able to access the core xfer ops (without EXPORTing all
> scmi_xfer ops) but protoX should NOT be allowed to mistakenly or
> maliciously build and send protoY messages: since the protocol_handle
> for protoX is embedded in a specific protocol_instance in this way you
> can call from your protocol code something like:
> 
> ph->xops->xfer_get_init(ph, ...)

I am still confused by this one... scmi_protocol_instance has a pointer 
to scmi_handle. So why not handle->xops->xfer_get_init(pi, ....). Here 
also protoX will not be allowed to send protoY messages, right? And then 
again set_priv and get_priv can be moved to scmi_protocol_instance right ?


-- 
Warm Regards
Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles
  2020-11-06 16:26       ` Thara Gopinath
@ 2020-11-07 12:53         ` Cristian Marussi
  0 siblings, 0 replies; 21+ messages in thread
From: Cristian Marussi @ 2020-11-07 12:53 UTC (permalink / raw)
  To: Thara Gopinath
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, lukasz.luba,
	james.quinlan, Jonathan.Cameron, f.fainelli, etienne.carriere,
	vincent.guittot, souvik.chakravarty

Hi Thara

thanks for the feedback.

On Fri, Nov 06, 2020 at 11:26:44AM -0500, Thara Gopinath wrote:
> 
> 
> On 11/4/20 12:44 PM, Cristian Marussi wrote:
> > Hi
> > 
> > On Wed, Nov 04, 2020 at 11:16:18AM -0500, Thara Gopinath wrote:
> > > 
> > > Hi Cristian,
> > > 
> > > On 10/28/20 4:29 PM, Cristian Marussi wrote:
> > > > Add basic protocol handles definitions and helpers support.
> > > > All protocols initialization code and SCMI drivers probing is still
> > > > performed using the handle based interface.
> > > > 
> > > > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > > > ---
> > > >    drivers/firmware/arm_scmi/common.h | 61 ++++++++++++++++++++++++++++
> > > >    drivers/firmware/arm_scmi/driver.c | 64 ++++++++++++++++++++++++++++++
> > > >    2 files changed, 125 insertions(+)
> > > > 
> > > > diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> > > > index b08a8ddbc22a..f0678be02a09 100644
> > > > --- a/drivers/firmware/arm_scmi/common.h
> > > > +++ b/drivers/firmware/arm_scmi/common.h
> > > > @@ -151,6 +151,67 @@ int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
> > > >    		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> > > >    void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
> > > >    			    struct scmi_xfer *xfer);
> > > > +
> > > > +struct scmi_xfer_ops;
> > > > +
> > > > +/**
> > > > + * struct scmi_protocol_handle  - Reference to an initialized protocol instance
> > > > + *
> > > > + * @dev: A reference to the associated SCMI instance device (handle->dev).
> > > > + * @xops: A reference to a struct holding refs to the core xfer operations that
> > > > + *	  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.
> > > > + *
> > > > + * This structure represents a protocol initialized against specific SCMI
> > > > + * instance and it will be used as follows:
> > > > + * - as a parameter fed from the core to the protocol initialization code so
> > > > + *   that it can access the core xfer operations to build and generate SCMI
> > > > + *   messages exclusively for the specific underlying protocol instance.
> > > > + * - as an opaque handle fed by an SCMI driver user when it tries to access
> > > > + *   this protocol through its own protocol operations.
> > > > + *   In this case this handle will be returned as an opaque object together
> > > > + *   with the related protocol operations when the SCMI driver tries to access
> > > > + *   the protocol.
> > > > + */
> > > > +struct scmi_protocol_handle {
> > > > +	struct device *dev;
> > > > +	const struct scmi_xfer_ops *xops;
> > > > +	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
> > > > +	void *(*get_priv)(const struct scmi_protocol_handle *ph);
> > > > +};
> > > 
> > > So scmi_xfer_ops are the ops that actually talks with the scmi firmware on
> > > the other end , right ? IIUC, these ops are the same for all the protocols
> > > of a scmi instance. Imho, this struct is not the right place for these ops
> > > to reside.You are inadvertently exposing scmi internal details to the client
> > > drivers. There is no reason why this should be part of scmi_handle. The
> > > protocols can extract it from the handle during protocol_reigster, right?
> > > 
> > > So, now to the second part, why do you need a scmi_protocol_handle? Again
> > > IIUC, if you have set_priv and get_priv hooks and get_ops and put_ops hooks,
> > > there is nothing that scmi_protocol_handle is providing extra, right? As
> > > mentioned in the comments for last patch any reason all of this cannot be
> > > rolled into scmi_protocol?
> > 
> > The basic idea for protocol_hande existence is that the protocol code
> > should be able to access the core xfer ops (without EXPORTing all
> > scmi_xfer ops) but protoX should NOT be allowed to mistakenly or
> > maliciously build and send protoY messages: since the protocol_handle
> > for protoX is embedded in a specific protocol_instance in this way you
> > can call from your protocol code something like:
> > 
> > ph->xops->xfer_get_init(ph, ...)
> 
> I am still confused by this one... scmi_protocol_instance has a pointer to
> scmi_handle. So why not handle->xops->xfer_get_init(pi, ....). Here also
> protoX will not be allowed to send protoY messages, right? And then again
> set_priv and get_priv can be moved to scmi_protocol_instance right ?
> 

So given that there a number of different 'actors' playing in the SCMI
stack, the basic idea/attempt in general was to expose to each single actor
just the bare minimum needed data structs with the bare minimum needed level
of access (read-only const when no write-access is needed).

We can consider 3 main SCMI actors in these regards:

- SCMI core which takes care to keep track of all the available/loadable
  protocols and to provide access to such protocols via get_ops while
  allocating proper instances and tracking the lifecycle of all the needed
  resources: to do that the core needs all the data in protocol_instance.

- SCMI Protocols that basically just need to be able to build and send
  messages related to their implemented protocols, and also storing any
  needed per-protocol private data somewhere; to do that a protocol
  implementation really needs only:
   
   + xfer_ops possibly restricted to its own protocol ID
   + a dev to use optionally for its own devres
   + a way to store private data specific to the SCMI instance if needed

  In order to realize this the core indeed passes down the protocols'
  init code a const protocol_handle, so that the protocol has available
  all of the above and nothing more, while the core, with the usual
  containmr_of trick, can retrieve the containing protocol_instance
  needed to process properly the xfer_ops.
  What I really don't like are the bulky set_priv/get_priv ops instead
  of a much simpler ph->priv kind-of-access: but this bulky accessors are
  in fact needed because ph is passed as const to protocol init.
  Not sure if I can play some trick here to avoid this without making
  the code even more complex.

  Note also that, the usage of the protocol_handle in the init code
  enforces as a consequence its usage also in the exposed protocol
  operations, because if I had kept the old perf_ops(handle, ) style
  interface that would have meant that the protocol core should have
  been able to somehow ask the core for the proper handle to use like:
   
   ph = scmi_map_protocol(handle, SCMI_PROTOCOL_X)

  which would have defeated all of the above attempt to effectively restrict
  the access of protoX to proto ID=X, since any protocol could have
  grabbed any other protocol_handle of choice with the above map interface.
  (beside being an unneeded repetition)

- at last, there are SCMI drivers that basically just need to able to grab
  access to their desired protocols, if available, and use those specific
  protocol operations related to the SCMI handle instance they refer: here
  the trick to limit the visibility of the data to the bare minimum needs,
  as said, is that the protocol_handle is just an opaque pointer for the
  SCMI driver, it cannot be dereferenced and accessed by the SCMI driver.


Regards

Cristian


> 
> -- 
> Warm Regards
> Thara

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2020-11-07 12:53 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-28 20:29 [PATCH v2 0/8] SCMI vendor protocols and modularization Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 1/8] firmware: arm_scmi: review protocol registration interface Cristian Marussi
2020-11-04 16:16   ` Thara Gopinath
2020-11-04 16:56     ` Cristian Marussi
2020-11-06 16:22       ` Thara Gopinath
2020-10-28 20:29 ` [PATCH v2 2/8] firmware: arm_scmi: introduce protocol handles Cristian Marussi
2020-11-04 16:16   ` Thara Gopinath
2020-11-04 16:58     ` Etienne Carriere
2020-11-04 17:44     ` Cristian Marussi
2020-11-06 16:26       ` Thara Gopinath
2020-11-07 12:53         ` Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 3/8] firmware: arm_scmi: introduce new protocol operations support Cristian Marussi
2020-11-04 16:16   ` Thara Gopinath
2020-11-04 18:08     ` Cristian Marussi
2020-11-06 16:07       ` Thara Gopinath
2020-10-28 20:29 ` [PATCH v2 4/8] firmware: arm_scmi: make notifications aware of protocol usage Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 5/8] firmware: arm_scmi: refactor events registration Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 6/8] firmware: arm_scmi: port all protocols and drivers to the new API Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 7/8] firmware: arm_scmi: make notify_priv really private Cristian Marussi
2020-10-28 20:29 ` [PATCH v2 8/8] firmware: arm_scmi: add protocol modularization support Cristian Marussi
2020-11-04 16:15 ` [PATCH v2 0/8] SCMI vendor protocols and modularization Thara Gopinath

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).