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.
Patches 1,2,3 try to address this simplifying the SCMI protocols interface
and reducing it to these common generic operations:
handle->get_ops() / handle->put_ops() / handle->notify_ops()
All protocols' private data pointers are removed from handle too and made
accessible only to the protocols code through dedicated internal helpers.
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 in
patch 3.
Leveraging this new centralized and common initialization flow, patches 4,5
take care to refactor and simplify protocol-events registration and remove
also *notify_priv from the handle interface making it accessible only to
the notification core.
Finally, patch 6 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.
Moreover protocol initialization is further modified to receive dynamically
a pointer to core scmi_xfer_ops at init time, so avoiding to have to export
all those xfer symbols in order to make the core transfer methods available
to an SCMI protocol dynamically loaded.
Standard protocols are still kept as builtins, though.
Patches 7-11 are marked [DEBUG] and not meant for for upstreaming but just
to be used as an example of how to use all of the above madness to develop
a custom dummy vendor protocol module and a related SCMI custom dummy
driver. (with all related DT entry/devname).
The series is currently based on for-next/scmi [1] on top of:
commit fd7c58ee3026 ("firmware: arm_scmi: Fix locking in notifications")
Any feedback welcome.
Thanks,
Cristian
[1]:https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/log/?h=for-next/scmi
Cristian Marussi (11):
firmware: arm_scmi: review protocol registration interface
firmware: arm_scmi: hide protocols' private data
firmware: arm_scmi: introduce common protocol interface
firmware: arm_scmi: refactor events registration
firmware: arm_scmi: make notify_priv really private
firmware: arm_scmi: add support for protocol modularization
[DEBUG] firmware: arm_scmi: add example custom protocol
[DEBUG] arm64: dts: juno: add example custom protocol support
[DEBUG] firmware: arm_scmi: add example SCMI driver for custom
protocol
[DEBUG] firmware: arm_scmi: add custom_dummy SCMI devname
[DEBUG][HACK] firmware: arm_scmi: force implemented protocol 0x99
arch/arm64/boot/dts/arm/juno-base.dtsi | 4 +
drivers/clk/clk-scmi.c | 30 +-
drivers/cpufreq/scmi-cpufreq.c | 28 +-
drivers/firmware/Kconfig | 17 +-
drivers/firmware/arm_scmi/Makefile | 5 +
drivers/firmware/arm_scmi/base.c | 79 +++--
drivers/firmware/arm_scmi/bus.c | 71 +++--
drivers/firmware/arm_scmi/clock.c | 72 +++--
drivers/firmware/arm_scmi/common.h | 73 ++++-
drivers/firmware/arm_scmi/driver.c | 292 +++++++++++++++++-
drivers/firmware/arm_scmi/notify.c | 171 ++++++----
drivers/firmware/arm_scmi/notify.h | 30 +-
drivers/firmware/arm_scmi/perf.c | 135 ++++----
drivers/firmware/arm_scmi/power.c | 85 +++--
drivers/firmware/arm_scmi/reset.c | 87 ++++--
drivers/firmware/arm_scmi/scmi_custom.c | 170 ++++++++++
drivers/firmware/arm_scmi/scmi_custom_dummy.c | 126 ++++++++
drivers/firmware/arm_scmi/scmi_pm_domain.c | 29 +-
drivers/firmware/arm_scmi/sensors.c | 92 ++++--
drivers/firmware/arm_scmi/system.c | 42 ++-
drivers/hwmon/scmi-hwmon.c | 26 +-
drivers/reset/reset-scmi.c | 26 +-
include/linux/scmi_protocol.h | 78 +++--
23 files changed, 1354 insertions(+), 414 deletions(-)
create mode 100644 drivers/firmware/arm_scmi/scmi_custom.c
create mode 100644 drivers/firmware/arm_scmi/scmi_custom_dummy.c
--
2.17.1
Describe statically the protocol events as part of struct scmi_protocol
and move the explicit registraton/deregistration code out the per-protocol
specific code and into the common core initialization code.
Add .get_num_sources to scmi_event_ops, to resolve at run-time the number
of available sources where not statically definable in .num-sources.
Simplify scmi_register_protocol_events devres handling since the events
resources can now be handled by the per-protocol devres.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/arm_scmi/base.c | 15 +++---
drivers/firmware/arm_scmi/common.h | 4 ++
drivers/firmware/arm_scmi/driver.c | 7 +++
drivers/firmware/arm_scmi/notify.c | 77 ++++++++++++++++++++---------
drivers/firmware/arm_scmi/notify.h | 30 +++++++++--
drivers/firmware/arm_scmi/perf.c | 26 +++++++---
drivers/firmware/arm_scmi/power.c | 26 +++++++---
drivers/firmware/arm_scmi/reset.c | 26 +++++++---
drivers/firmware/arm_scmi/sensors.c | 26 +++++++---
drivers/firmware/arm_scmi/system.c | 16 +++---
10 files changed, 187 insertions(+), 66 deletions(-)
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 129633e6fff4..f40821eeb103 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(const struct scmi_handle *h)
{
int id, ret;
@@ -352,12 +360,6 @@ static int scmi_base_protocol_init(const 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 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 56ebb710ee84..66574f57e304 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)
@@ -167,12 +169,14 @@ typedef int (*scmi_prot_init_fn_t)(const 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 049220efd227..378749040162 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -628,6 +628,10 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
/* Ensure initialized protocol is visible */
smp_wmb();
+ 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);
@@ -685,6 +689,9 @@ void scmi_release_protocol(const 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 02b00af9b08f..7ba182d4f2b4 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..bc30167b1dd6 100644
--- a/drivers/firmware/arm_scmi/notify.h
+++ b/drivers/firmware/arm_scmi/notify.h
@@ -33,6 +33,8 @@ struct scmi_event {
/**
* 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 +48,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 +57,31 @@ 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);
+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 bd9cb2583557..b3038362f362 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -839,6 +839,17 @@ 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 *pinfo =
+ scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+
+ if (!pinfo)
+ return -EINVAL;
+
+ return pinfo->num_domains;
+}
+
static const struct scmi_event perf_events[] = {
{
.id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
@@ -853,10 +864,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(const struct scmi_handle *handle)
{
int domain;
@@ -889,12 +908,6 @@ static int scmi_perf_protocol_init(const 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;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
}
@@ -903,6 +916,7 @@ static 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 1e026b5530a7..cb9b1f6a56dd 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -248,6 +248,17 @@ 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 =
+ scmi_get_proto_priv(handle, SCMI_PROTOCOL_POWER);
+
+ if (!pinfo)
+ return -EINVAL;
+
+ return pinfo->num_domains;
+}
+
static const struct scmi_event power_events[] = {
{
.id = SCMI_EVENT_POWER_STATE_CHANGED,
@@ -258,10 +269,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(const struct scmi_handle *handle)
{
int domain;
@@ -290,12 +309,6 @@ static int scmi_power_protocol_init(const 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;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
}
@@ -304,6 +317,7 @@ static 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 b7da4de0e56e..83bfd0514d4d 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -261,6 +261,17 @@ 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 =
+ scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+
+ if (!pinfo)
+ return -EINVAL;
+
+ return pinfo->num_domains;
+}
+
static const struct scmi_event reset_events[] = {
{
.id = SCMI_EVENT_RESET_ISSUED,
@@ -270,10 +281,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(const struct scmi_handle *handle)
{
int domain;
@@ -302,12 +321,6 @@ static int scmi_reset_protocol_init(const 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;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
}
@@ -316,6 +329,7 @@ static 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 e0129dcd322f..79bdd53ab7ba 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -321,6 +321,17 @@ 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 *sinfo =
+ scmi_get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
+
+ if (!sinfo)
+ return -EINVAL;
+
+ return sinfo->num_sensors;
+}
+
static const struct scmi_event sensor_events[] = {
{
.id = SCMI_EVENT_SENSOR_TRIP_POINT_EVENT,
@@ -330,10 +341,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(const struct scmi_handle *handle)
{
u32 version;
@@ -357,12 +376,6 @@ static int scmi_sensors_protocol_init(const 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;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
}
@@ -371,6 +384,7 @@ static 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 30e3510c1f07..ae884fc669f5 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(const struct scmi_handle *handle)
{
u32 version;
@@ -115,13 +123,6 @@ static int scmi_system_protocol_init(const 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;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SYSTEM, pinfo);
}
@@ -130,6 +131,7 @@ static 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
Add an example custom protocol 0x99 support.
Signed-off-by: Cristian Marussi <[email protected]>
---
arch/arm64/boot/dts/arm/juno-base.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi
index dd20a5242c45..85a45a536bbf 100644
--- a/arch/arm64/boot/dts/arm/juno-base.dtsi
+++ b/arch/arm64/boot/dts/arm/juno-base.dtsi
@@ -598,6 +598,10 @@
reg = <0x15>;
#thermal-sensor-cells = <1>;
};
+
+ scmi_custom99: protocol@99 {
+ reg = <0x99>;
+ };
};
};
--
2.17.1
Force custom protocol 0x99 as implemented for testing purposes.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/arm_scmi/base.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 8d7214fd2187..b0036111e287 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -356,6 +356,8 @@ static int scmi_base_protocol_init(const struct scmi_handle *h,
scmi_base_vendor_id_get(handle, true);
scmi_base_implementation_version_get(handle);
scmi_base_implementation_list_get(handle, prot_imp);
+ handle->version->num_protocols++;
+ prot_imp[handle->version->num_protocols] = SCMI_PROTOCOL_CUSTOM_DUMMY;
scmi_setup_protocol_implemented(handle, prot_imp);
dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
--
2.17.1
Modify protocol initialization callback adding a new parameter representing
a reference to the available xfer core operations and introduce a macro to
simply register with the core new protocols as loadable drivers.
Keep standard protocols as builtin.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/arm_scmi/base.c | 56 ++++++++++--------
drivers/firmware/arm_scmi/bus.c | 14 ++++-
drivers/firmware/arm_scmi/clock.c | 56 +++++++++---------
drivers/firmware/arm_scmi/common.h | 42 +++++++++-----
drivers/firmware/arm_scmi/driver.c | 50 ++++++++++------
drivers/firmware/arm_scmi/perf.c | 88 +++++++++++++++--------------
drivers/firmware/arm_scmi/power.c | 46 ++++++++-------
drivers/firmware/arm_scmi/reset.c | 46 ++++++++-------
drivers/firmware/arm_scmi/sensors.c | 52 +++++++++--------
drivers/firmware/arm_scmi/system.c | 16 ++++--
include/linux/scmi_protocol.h | 18 +++++-
11 files changed, 288 insertions(+), 196 deletions(-)
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index f40821eeb103..8d7214fd2187 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -15,6 +15,8 @@
#define SCMI_BASE_NUM_SOURCES 1
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
+static const struct scmi_xfer_ops *ops;
+
enum scmi_base_protocol_cmd {
BASE_DISCOVER_VENDOR = 0x3,
BASE_DISCOVER_SUB_VENDOR = 0x4,
@@ -61,19 +63,19 @@ static int scmi_base_attributes_get(const struct scmi_handle *handle)
struct scmi_msg_resp_base_attributes *attr_info;
struct scmi_revision_info *rev = handle->version;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
if (ret)
return ret;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -105,15 +107,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 = ops->xfer_get_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
if (ret)
return ret;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
memcpy(vendor_id, t->rx.buf, size);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -135,18 +137,18 @@ scmi_base_implementation_version_get(const struct scmi_handle *handle)
struct scmi_xfer *t;
struct scmi_revision_info *rev = handle->version;
- ret = scmi_xfer_get_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
+ ret = ops->xfer_get_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
if (ret)
return ret;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
impl_ver = t->rx.buf;
rev->impl_ver = le32_to_cpu(*impl_ver);
}
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -170,7 +172,7 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
u32 tot_num_ret = 0, loop_num_ret;
struct device *dev = handle->dev;
- ret = scmi_xfer_get_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
+ ret = ops->xfer_get_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
SCMI_PROTOCOL_BASE, 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 = ops->do_xfer(handle, 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);
+ ops->reset_rx_to_maxsz(handle, t);
} while (loop_num_ret);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -224,7 +226,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
int ret;
struct scmi_xfer *t;
- ret = scmi_xfer_get_init(handle, BASE_DISCOVER_AGENT,
+ ret = ops->xfer_get_init(handle, BASE_DISCOVER_AGENT,
SCMI_PROTOCOL_BASE, sizeof(__le32),
SCMI_MAX_STR_SIZE, &t);
if (ret)
@@ -232,11 +234,11 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
put_unaligned_le32(id, t->tx.buf);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -248,7 +250,7 @@ static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
struct scmi_xfer *t;
struct scmi_msg_base_error_notify *cfg;
- ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS,
+ ret = ops->xfer_get_init(handle, BASE_NOTIFY_ERRORS,
SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t);
if (ret)
return ret;
@@ -256,9 +258,9 @@ static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
cfg = t->tx.buf;
cfg->event_control = cpu_to_le32(evt_cntl);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -326,7 +328,8 @@ static const struct scmi_protocol_events base_protocol_events = {
.num_sources = SCMI_BASE_NUM_SOURCES,
};
-static int scmi_base_protocol_init(const struct scmi_handle *h)
+static int scmi_base_protocol_init(const struct scmi_handle *h,
+ const struct scmi_xfer_ops *xops)
{
int id, ret;
u8 *prot_imp;
@@ -336,7 +339,8 @@ static int scmi_base_protocol_init(const struct scmi_handle *h)
struct device *dev = handle->dev;
struct scmi_revision_info *rev = handle->version;
- ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
+ ops = xops;
+ ret = ops->version_get(handle, SCMI_PROTOCOL_BASE, &version);
if (ret)
return ret;
@@ -375,4 +379,12 @@ static struct scmi_protocol scmi_base = {
.events = &base_protocol_events,
};
-DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)
+int __init scmi_base_register(void)
+{
+ return scmi_protocol_register(&scmi_base, NULL);
+}
+
+void __exit scmi_base_unregister(void)
+{
+ return scmi_protocol_unregister(&scmi_base);
+}
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 3a2be1193c85..2ce98fae56e3 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);
@@ -186,7 +195,7 @@ void scmi_set_handle(struct scmi_device *scmi_dev)
scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
}
-int scmi_protocol_register(struct scmi_protocol *proto)
+int scmi_protocol_register(struct scmi_protocol *proto, struct module *owner)
{
int ret;
@@ -200,6 +209,7 @@ int scmi_protocol_register(struct scmi_protocol *proto)
return -EINVAL;
}
+ proto->owner = owner;
spin_lock(&protocol_lock);
ret = idr_alloc(&scmi_available_protocols, proto,
proto->id, proto->id + 1, GFP_ATOMIC);
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 539c94860b8f..a2f552c87b3e 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -9,6 +9,8 @@
#include "common.h"
+static const struct scmi_xfer_ops *ops;
+
enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3,
CLOCK_DESCRIBE_RATES = 0x4,
@@ -81,20 +83,20 @@ static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_resp_clock_protocol_attributes *attr;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
if (ret)
return ret;
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -105,7 +107,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_resp_clock_attributes *attr;
- ret = scmi_xfer_get_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
+ ret = ops->xfer_get_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
sizeof(clk_id), sizeof(*attr), &t);
if (ret)
return ret;
@@ -113,13 +115,13 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
put_unaligned_le32(clk_id, t->tx.buf);
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
else
clk->name[0] = '\0';
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -148,7 +150,7 @@ 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,
+ ret = ops->xfer_get_init(handle, CLOCK_DESCRIBE_RATES,
SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
if (ret)
return ret;
@@ -161,7 +163,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 = ops->do_xfer(handle, t);
if (ret)
goto err;
@@ -193,7 +195,7 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
tot_rate_cnt += num_returned;
- scmi_reset_rx_to_maxsz(handle, t);
+ ops->reset_rx_to_maxsz(handle, t);
/*
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
@@ -208,7 +210,7 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
clk->rate_discrete = rate_discrete;
err:
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -218,18 +220,18 @@ scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
int ret;
struct scmi_xfer *t;
- ret = scmi_xfer_get_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
+ ret = ops->xfer_get_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
sizeof(__le32), sizeof(u64), &t);
if (ret)
return ret;
put_unaligned_le32(clk_id, t->tx.buf);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
*value = get_unaligned_le64(t->rx.buf);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -241,9 +243,9 @@ static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
struct scmi_xfer *t;
struct scmi_clock_set_rate *cfg;
struct clock_info *ci =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
- ret = scmi_xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
+ ret = ops->xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
sizeof(*cfg), 0, &t);
if (ret)
return ret;
@@ -259,14 +261,14 @@ 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 = ops->do_xfer_with_response(handle, t);
else
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (ci->max_async_req)
atomic_dec(&ci->cur_async_req);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -277,7 +279,7 @@ scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
struct scmi_xfer *t;
struct scmi_clock_set_config *cfg;
- ret = scmi_xfer_get_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
+ ret = ops->xfer_get_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
sizeof(*cfg), 0, &t);
if (ret)
return ret;
@@ -286,9 +288,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -305,7 +307,7 @@ static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
static int scmi_clock_count_get(const struct scmi_handle *handle)
{
struct clock_info *ci =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
return ci->num_clocks;
}
@@ -314,7 +316,7 @@ static const struct scmi_clock_info *
scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
{
struct clock_info *ci =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_CLOCK);
struct scmi_clock_info *clk = ci->clk + clk_id;
if (!clk->name[0])
@@ -332,13 +334,15 @@ static const struct scmi_clk_ops clk_ops = {
.disable = scmi_clock_disable,
};
-static int scmi_clock_protocol_init(const struct scmi_handle *handle)
+static int scmi_clock_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
u32 version;
int clkid, ret;
struct clock_info *cinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
dev_dbg(handle->dev, "Clock Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -363,7 +367,7 @@ static int scmi_clock_protocol_init(const struct scmi_handle *handle)
}
cinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_CLOCK, cinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_CLOCK, cinfo);
}
static struct scmi_protocol scmi_clock = {
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 5a91e3324697..ec81edc12ca5 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>
@@ -145,26 +146,39 @@ 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 {
+ int (*version_get)(const struct scmi_handle *handle, u8 protocol,
+ u32 *version);
+ int (*set_proto_priv)(const struct scmi_handle *handle, u8 protocol_id,
+ void *priv);
+ void *(*get_proto_priv)(const struct scmi_handle *handle,
+ u8 protocol_id);
+ int (*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);
+ void (*reset_rx_to_maxsz)(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+ int (*do_xfer)(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+ int (*do_xfer_with_response)(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+ void (*xfer_put)(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+};
+
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,
u8 *prot_imp);
-typedef int (*scmi_prot_init_fn_t)(const struct scmi_handle *);
+typedef int (*scmi_prot_init_fn_t)(const struct scmi_handle *,
+ const struct scmi_xfer_ops *);
/**
* 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
@@ -173,6 +187,7 @@ typedef int (*scmi_prot_init_fn_t)(const struct scmi_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;
@@ -196,7 +211,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(system);
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
int __init scmi_##name##_register(void) \
{ \
- return scmi_protocol_register(&(proto)); \
+ return scmi_protocol_register(&(proto), THIS_MODULE); \
} \
\
void __exit scmi_##name##_unregister(void) \
@@ -205,14 +220,11 @@ 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);
-void *scmi_get_proto_priv(const struct scmi_handle *h, u8 prot);
-int scmi_set_proto_priv(const struct scmi_handle *handle, const u8 proto,
- void *priv);
-
/* 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 25a4152537e6..55df134c2338 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -370,7 +370,8 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
* @handle: Pointer to SCMI entity 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_handle *handle,
+ struct scmi_xfer *xfer)
{
struct scmi_info *info = handle_to_scmi_info(handle);
@@ -398,7 +399,8 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
* 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_handle *handle,
+ struct scmi_xfer *xfer)
{
int ret;
int timeout;
@@ -451,8 +453,8 @@ 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_handle *handle,
+ struct scmi_xfer *xfer)
{
struct scmi_info *info = handle_to_scmi_info(handle);
@@ -471,8 +473,8 @@ void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle,
* 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_handle *handle,
+ struct scmi_xfer *xfer)
{
int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
DECLARE_COMPLETION_ONSTACK(async_response);
@@ -503,8 +505,9 @@ 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_handle *handle, u8 msg_id,
+ u8 prot_id, size_t tx_size, size_t rx_size,
+ struct scmi_xfer **p)
{
int ret;
struct scmi_xfer *xfer;
@@ -546,8 +549,8 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
*
* 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_handle *handle, u8 protocol,
+ u32 *version)
{
int ret;
__le32 *rev_info;
@@ -568,8 +571,8 @@ int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
return ret;
}
-int scmi_set_proto_priv(const struct scmi_handle *handle,
- u8 protocol_id, void *priv)
+static int scmi_set_proto_priv(const struct scmi_handle *handle,
+ u8 protocol_id, void *priv)
{
struct scmi_info *info = handle_to_scmi_info(handle);
@@ -585,7 +588,8 @@ int scmi_set_proto_priv(const struct scmi_handle *handle,
return 0;
}
-void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
+static void *scmi_get_proto_priv(const struct scmi_handle *handle,
+ u8 protocol_id)
{
struct scmi_info *info = handle_to_scmi_info(handle);
@@ -594,6 +598,17 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
return info->protocols_private_data[protocol_id];
}
+static const struct scmi_xfer_ops xfer_ops = {
+ .version_get = scmi_version_get,
+ .set_proto_priv = scmi_set_proto_priv,
+ .get_proto_priv = scmi_get_proto_priv,
+ .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.
@@ -624,7 +639,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 = -EINVAL;
+ ret = -EPROBE_DEFER;
goto out;
}
@@ -641,7 +656,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
pi->proto = proto;
refcount_set(&pi->users, 1);
/* proto->init is assured NON NULL by scmi_protocol_register */
- ret = pi->proto->init(handle);
+ ret = pi->proto->init(handle, &xfer_ops);
if (ret)
goto clean;
@@ -664,6 +679,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);
@@ -714,13 +730,15 @@ void scmi_release_protocol(const 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(handle, &xfer_ops);
info->protocols_private_data[protocol_id] = NULL;
info->protocols[protocol_id] = NULL;
/* Ensure deinitialized protocol is visible */
smp_wmb();
+ 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 b3038362f362..60a28ca39455 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -19,6 +19,8 @@
#include "common.h"
#include "notify.h"
+static const struct scmi_xfer_ops *ops;
+
enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3,
PERF_DESCRIBE_LEVELS = 0x4,
@@ -182,14 +184,14 @@ static int scmi_perf_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_resp_perf_attributes *attr;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_PERF, 0, sizeof(*attr), &t);
if (ret)
return ret;
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
u16 flags = le16_to_cpu(attr->flags);
@@ -200,7 +202,7 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -212,7 +214,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_msg_resp_perf_domain_attributes *attr;
- ret = scmi_xfer_get_init(handle, PERF_DOMAIN_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PERF_DOMAIN_ATTRIBUTES,
SCMI_PROTOCOL_PERF, sizeof(domain),
sizeof(*attr), &t);
if (ret)
@@ -221,7 +223,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
put_unaligned_le32(domain, t->tx.buf);
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
u32 flags = le32_to_cpu(attr->flags);
@@ -245,7 +247,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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -268,7 +270,7 @@ 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,
+ ret = ops->xfer_get_init(handle, PERF_DESCRIBE_LEVELS,
SCMI_PROTOCOL_PERF, sizeof(*dom_info), 0, &t);
if (ret)
return ret;
@@ -281,7 +283,7 @@ 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 = ops->do_xfer(handle, t);
if (ret)
break;
@@ -305,7 +307,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
tot_opp_cnt += num_returned;
- scmi_reset_rx_to_maxsz(handle, t);
+ ops->reset_rx_to_maxsz(handle, t);
/*
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
@@ -313,7 +315,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);
+ ops->xfer_put(handle, t);
sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
return ret;
@@ -360,7 +362,7 @@ static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_perf_set_limits *limits;
- ret = scmi_xfer_get_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
+ ret = ops->xfer_get_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
sizeof(*limits), 0, &t);
if (ret)
return ret;
@@ -370,16 +372,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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
u32 max_perf, u32 min_perf)
{
- struct scmi_perf_info *pi = scmi_get_proto_priv(handle,
+ struct scmi_perf_info *pi = ops->get_proto_priv(handle,
SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
@@ -400,14 +402,14 @@ static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_perf_get_limits *limits;
- ret = scmi_xfer_get_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
+ ret = ops->xfer_get_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
sizeof(__le32), 0, &t);
if (ret)
return ret;
put_unaligned_le32(domain, t->tx.buf);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
limits = t->rx.buf;
@@ -415,7 +417,7 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -423,7 +425,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
u32 *max_perf, u32 *min_perf)
{
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
if (dom->fc_info && dom->fc_info->limit_get_addr) {
@@ -442,7 +444,7 @@ static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_perf_set_level *lvl;
- ret = scmi_xfer_get_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
+ ret = ops->xfer_get_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
sizeof(*lvl), 0, &t);
if (ret)
return ret;
@@ -452,9 +454,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -462,7 +464,7 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
u32 level, bool poll)
{
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
if (dom->fc_info && dom->fc_info->level_set_addr) {
@@ -480,7 +482,7 @@ static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain,
int ret;
struct scmi_xfer *t;
- ret = scmi_xfer_get_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
+ ret = ops->xfer_get_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
sizeof(u32), sizeof(u32), &t);
if (ret)
return ret;
@@ -488,11 +490,11 @@ static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain,
t->hdr.poll_completion = poll;
put_unaligned_le32(domain, t->tx.buf);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
*level = get_unaligned_le32(t->rx.buf);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -500,7 +502,7 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
u32 *level, bool poll)
{
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
if (dom->fc_info && dom->fc_info->level_get_addr) {
@@ -519,7 +521,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,
+ ret = ops->xfer_get_init(handle, message_id, SCMI_PROTOCOL_PERF,
sizeof(*notify), 0, &t);
if (ret)
return ret;
@@ -528,9 +530,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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -561,7 +563,7 @@ 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,
+ ret = ops->xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL,
SCMI_PROTOCOL_PERF,
sizeof(*info), sizeof(*resp), &t);
if (ret)
@@ -571,7 +573,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 = ops->do_xfer(handle, t);
if (ret)
goto err_xfer;
@@ -609,7 +611,7 @@ scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
*p_db = db;
}
err_xfer:
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
}
static void scmi_perf_domain_init_fc(const struct scmi_handle *handle,
@@ -652,7 +654,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_handle *handle,
struct scmi_opp *opp;
struct perf_dom_info *dom;
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
domain = scmi_dev_domain_id(dev);
if (domain < 0)
@@ -682,7 +684,7 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle,
{
struct perf_dom_info *dom;
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
int domain = scmi_dev_domain_id(dev);
if (domain < 0)
@@ -697,7 +699,7 @@ static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
unsigned long freq, bool poll)
{
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
return scmi_perf_level_set(handle, domain, freq / dom->mult_factor,
@@ -710,7 +712,7 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
int ret;
u32 level;
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
ret = scmi_perf_level_get(handle, domain, &level, poll);
@@ -724,7 +726,7 @@ static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
unsigned long *freq, unsigned long *power)
{
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom;
unsigned long opp_freq;
int idx, ret = -EINVAL;
@@ -753,7 +755,7 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
{
struct perf_dom_info *dom;
struct scmi_perf_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
dom = pi->dom_info + scmi_dev_domain_id(dev);
@@ -842,7 +844,7 @@ static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle,
static int scmi_perf_get_num_sources(const struct scmi_handle *handle)
{
struct scmi_perf_info *pinfo =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_PERF);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_PERF);
if (!pinfo)
return -EINVAL;
@@ -876,13 +878,15 @@ static const struct scmi_protocol_events perf_protocol_events = {
.num_events = ARRAY_SIZE(perf_events),
};
-static int scmi_perf_protocol_init(const struct scmi_handle *handle)
+static int scmi_perf_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
int domain;
u32 version;
struct scmi_perf_info *pinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_PERF, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_PERF, &version);
dev_dbg(handle->dev, "Performance Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -909,7 +913,7 @@ static int scmi_perf_protocol_init(const struct scmi_handle *handle)
}
pinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
}
static struct scmi_protocol scmi_perf = {
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index cb9b1f6a56dd..766e1782c9ff 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -12,6 +12,8 @@
#include "common.h"
#include "notify.h"
+static const struct scmi_xfer_ops *ops;
+
enum scmi_power_protocol_cmd {
POWER_DOMAIN_ATTRIBUTES = 0x3,
POWER_STATE_SET = 0x4,
@@ -75,14 +77,14 @@ static int scmi_power_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_resp_power_attributes *attr;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t);
if (ret)
return ret;
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
pi->num_domains = le16_to_cpu(attr->num_domains);
pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
@@ -90,7 +92,7 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -102,7 +104,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_msg_resp_power_domain_attributes *attr;
- ret = scmi_xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES,
SCMI_PROTOCOL_POWER, sizeof(domain),
sizeof(*attr), &t);
if (ret)
@@ -111,7 +113,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
put_unaligned_le32(domain, t->tx.buf);
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
u32 flags = le32_to_cpu(attr->flags);
@@ -121,7 +123,7 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -132,7 +134,7 @@ scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state)
struct scmi_xfer *t;
struct scmi_power_set_state *st;
- ret = scmi_xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
+ ret = ops->xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
sizeof(*st), 0, &t);
if (ret)
return ret;
@@ -142,9 +144,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -154,25 +156,25 @@ scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
int ret;
struct scmi_xfer *t;
- ret = scmi_xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
+ ret = ops->xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
sizeof(u32), sizeof(u32), &t);
if (ret)
return ret;
put_unaligned_le32(domain, t->tx.buf);
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret)
*state = get_unaligned_le32(t->rx.buf);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
static int scmi_power_num_domains_get(const struct scmi_handle *handle)
{
struct scmi_power_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_POWER);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_POWER);
return pi->num_domains;
}
@@ -180,7 +182,7 @@ static int scmi_power_num_domains_get(const struct scmi_handle *handle)
static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain)
{
struct scmi_power_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_POWER);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_POWER);
struct power_dom_info *dom = pi->dom_info + domain;
return dom->name;
@@ -200,7 +202,7 @@ static int scmi_power_request_notify(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_power_state_notify *notify;
- ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY,
+ ret = ops->xfer_get_init(handle, POWER_STATE_NOTIFY,
SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
if (ret)
return ret;
@@ -209,9 +211,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -251,7 +253,7 @@ static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
static int scmi_power_get_num_sources(const struct scmi_handle *handle)
{
struct scmi_power_info *pinfo =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_POWER);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_POWER);
if (!pinfo)
return -EINVAL;
@@ -281,13 +283,15 @@ static const struct scmi_protocol_events power_protocol_events = {
.num_events = ARRAY_SIZE(power_events),
};
-static int scmi_power_protocol_init(const struct scmi_handle *handle)
+static int scmi_power_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
int domain;
u32 version;
struct scmi_power_info *pinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_POWER, &version);
dev_dbg(handle->dev, "Power Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -310,7 +314,7 @@ static int scmi_power_protocol_init(const struct scmi_handle *handle)
}
pinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
}
static struct scmi_protocol scmi_power = {
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index 83bfd0514d4d..9accad66e07e 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -12,6 +12,8 @@
#include "common.h"
#include "notify.h"
+static const struct scmi_xfer_ops *ops;
+
enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3,
RESET = 0x4,
@@ -71,18 +73,18 @@ static int scmi_reset_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
u32 attr;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t);
if (ret)
return ret;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
attr = get_unaligned_le32(t->rx.buf);
pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
}
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -94,7 +96,7 @@ scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_msg_resp_reset_domain_attributes *attr;
- ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES,
SCMI_PROTOCOL_RESET, sizeof(domain),
sizeof(*attr), &t);
if (ret)
@@ -103,7 +105,7 @@ scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
put_unaligned_le32(domain, t->tx.buf);
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
u32 attributes = le32_to_cpu(attr->attributes);
@@ -115,14 +117,14 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
static int scmi_reset_num_domains_get(const struct scmi_handle *handle)
{
struct scmi_reset_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_RESET);
return pi->num_domains;
}
@@ -130,7 +132,7 @@ static int scmi_reset_num_domains_get(const struct scmi_handle *handle)
static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain)
{
struct scmi_reset_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_RESET);
struct reset_dom_info *dom = pi->dom_info + domain;
return dom->name;
@@ -139,7 +141,7 @@ static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain)
static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain)
{
struct scmi_reset_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_RESET);
struct reset_dom_info *dom = pi->dom_info + domain;
return dom->latency_us;
@@ -152,13 +154,13 @@ static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain,
struct scmi_xfer *t;
struct scmi_msg_reset_domain_reset *dom;
struct scmi_reset_info *pi =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_RESET);
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,
+ ret = ops->xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET,
sizeof(*dom), 0, &t);
if (ret)
return ret;
@@ -169,11 +171,11 @@ 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 = ops->do_xfer_with_response(handle, t);
else
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -213,7 +215,7 @@ static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id,
struct scmi_xfer *t;
struct scmi_msg_reset_notify *cfg;
- ret = scmi_xfer_get_init(handle, RESET_NOTIFY,
+ ret = ops->xfer_get_init(handle, RESET_NOTIFY,
SCMI_PROTOCOL_RESET, sizeof(*cfg), 0, &t);
if (ret)
return ret;
@@ -222,9 +224,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -264,7 +266,7 @@ static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle,
static int scmi_reset_get_num_sources(const struct scmi_handle *handle)
{
struct scmi_reset_info *pinfo =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_RESET);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_RESET);
if (!pinfo)
return -EINVAL;
@@ -293,13 +295,15 @@ static const struct scmi_protocol_events reset_protocol_events = {
.num_events = ARRAY_SIZE(reset_events),
};
-static int scmi_reset_protocol_init(const struct scmi_handle *handle)
+static int scmi_reset_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
int domain;
u32 version;
struct scmi_reset_info *pinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_RESET, &version);
dev_dbg(handle->dev, "Reset Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -322,7 +326,7 @@ static int scmi_reset_protocol_init(const struct scmi_handle *handle)
}
pinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
}
static struct scmi_protocol scmi_reset = {
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 79bdd53ab7ba..3a58dbca2b70 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -12,6 +12,8 @@
#include "common.h"
#include "notify.h"
+static const struct scmi_xfer_ops *ops;
+
enum scmi_sensor_protocol_cmd {
SENSOR_DESCRIPTION_GET = 0x3,
SENSOR_TRIP_POINT_NOTIFY = 0x4,
@@ -94,14 +96,14 @@ static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_resp_sensor_attributes *attr;
- ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ ret = ops->xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
if (ret)
return ret;
attr = t->rx.buf;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
if (!ret) {
si->num_sensors = le16_to_cpu(attr->num_sensors);
si->max_requests = attr->max_requests;
@@ -110,7 +112,7 @@ 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -123,7 +125,7 @@ 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,
+ ret = ops->xfer_get_init(handle, SENSOR_DESCRIPTION_GET,
SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
if (ret)
return ret;
@@ -134,7 +136,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 = ops->do_xfer(handle, t);
if (ret)
break;
@@ -167,14 +169,14 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
desc_index += num_returned;
- scmi_reset_rx_to_maxsz(handle, t);
+ ops->reset_rx_to_maxsz(handle, 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);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -186,7 +188,7 @@ 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,
+ ret = ops->xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY,
SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
if (ret)
return ret;
@@ -195,9 +197,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -210,7 +212,7 @@ scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id,
struct scmi_xfer *t;
struct scmi_msg_set_sensor_trip_point *trip;
- ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG,
+ ret = ops->xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG,
SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
if (ret)
return ret;
@@ -221,9 +223,9 @@ 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 = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -234,10 +236,10 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_msg_sensor_reading_get *sensor;
struct sensors_info *si =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
struct scmi_sensor_info *s = si->sensors + sensor_id;
- ret = scmi_xfer_get_init(handle, SENSOR_READING_GET,
+ ret = ops->xfer_get_init(handle, SENSOR_READING_GET,
SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
sizeof(u64), &t);
if (ret)
@@ -248,18 +250,18 @@ 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 = ops->do_xfer_with_response(handle, 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 = ops->do_xfer(handle, t);
if (!ret)
*value = get_unaligned_le64(t->rx.buf);
}
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -267,7 +269,7 @@ static const struct scmi_sensor_info *
scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
{
struct sensors_info *si =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
return si->sensors + sensor_id;
}
@@ -275,7 +277,7 @@ scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
static int scmi_sensor_count_get(const struct scmi_handle *handle)
{
struct sensors_info *si =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
return si->num_sensors;
}
@@ -324,7 +326,7 @@ static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle,
static int scmi_sensor_get_num_sources(const struct scmi_handle *handle)
{
struct sensors_info *sinfo =
- scmi_get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_SENSOR);
if (!sinfo)
return -EINVAL;
@@ -353,12 +355,14 @@ static const struct scmi_protocol_events sensor_protocol_events = {
.num_events = ARRAY_SIZE(sensor_events),
};
-static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
+static int scmi_sensors_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
u32 version;
struct sensors_info *sinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
dev_dbg(handle->dev, "Sensor Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -377,7 +381,7 @@ static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
scmi_sensor_description_get(handle, sinfo);
sinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
}
static struct scmi_protocol scmi_sensors = {
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index ae884fc669f5..4db3cc9cea3b 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -14,6 +14,8 @@
#define SCMI_SYSTEM_NUM_SOURCES 1
+static const struct scmi_xfer_ops *ops;
+
enum scmi_system_protocol_cmd {
SYSTEM_POWER_STATE_NOTIFY = 0x5,
};
@@ -39,7 +41,7 @@ static int scmi_system_request_notify(const struct scmi_handle *handle,
struct scmi_xfer *t;
struct scmi_system_power_state_notify *notify;
- ret = scmi_xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY,
+ ret = ops->xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY,
SCMI_PROTOCOL_SYSTEM, sizeof(*notify), 0, &t);
if (ret)
return ret;
@@ -47,9 +49,9 @@ static int scmi_system_request_notify(const struct scmi_handle *handle,
notify = t->tx.buf;
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
- ret = scmi_do_xfer(handle, t);
+ ret = ops->do_xfer(handle, t);
- scmi_xfer_put(handle, t);
+ ops->xfer_put(handle, t);
return ret;
}
@@ -109,12 +111,14 @@ static const struct scmi_protocol_events system_protocol_events = {
.num_sources = SCMI_SYSTEM_NUM_SOURCES,
};
-static int scmi_system_protocol_init(const struct scmi_handle *handle)
+static int scmi_system_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
{
u32 version;
struct scmi_system_info *pinfo;
- scmi_version_get(handle, SCMI_PROTOCOL_SYSTEM, &version);
+ ops = xops;
+ ops->version_get(handle, SCMI_PROTOCOL_SYSTEM, &version);
dev_dbg(handle->dev, "System Power Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
@@ -124,7 +128,7 @@ static int scmi_system_protocol_init(const struct scmi_handle *handle)
return -ENOMEM;
pinfo->version = version;
- return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SYSTEM, pinfo);
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_SYSTEM, pinfo);
}
static struct scmi_protocol scmi_system = {
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 650d0877a5c8..da675d6f90c0 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -351,8 +351,24 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
#define module_scmi_driver(__scmi_driver) \
module_driver(__scmi_driver, scmi_register, scmi_unregister)
+#define scmi_load(proto) \
+ scmi_protocol_register(proto, THIS_MODULE)
+#define scmi_unload(proto) \
+ scmi_protocol_unregister(proto)
+
+/**
+ * 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_load, scmi_unload)
+
struct scmi_protocol;
-int scmi_protocol_register(struct scmi_protocol *proto);
+int scmi_protocol_register(struct scmi_protocol *proto, struct module *owner);
void scmi_protocol_unregister(const struct scmi_protocol *proto);
/* SCMI Notification API - Custom Event Reports */
--
2.17.1
Add custom_dummy SCMI devname.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/arm_scmi/driver.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 55df134c2338..5c39a738866a 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -993,6 +993,7 @@ static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },},
+ { SCMI_PROTOCOL_CUSTOM_DUMMY, { "custom_dummy" },},
};
static inline void
--
2.17.1
Add an example SCMI driver using custom vendor protocol 0x99 and also
registering for Performance protocol notifications.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/Kconfig | 7 +
drivers/firmware/arm_scmi/Makefile | 2 +
drivers/firmware/arm_scmi/scmi_custom_dummy.c | 126 ++++++++++++++++++
3 files changed, 135 insertions(+)
create mode 100644 drivers/firmware/arm_scmi/scmi_custom_dummy.c
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 75e2668a6490..d209df6fd3ee 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -48,6 +48,13 @@ config ARM_SCMI_POWER_DOMAIN
will be called scmi_pm_domain. Note this may needed early in boot
before rootfs may be available.
+config ARM_SCMI_CUSTOM_DUMMY
+ tristate "SCMI Custom Dummy driver"
+ depends on ARM_SCMI_PROTOCOL_CUSTOM || (COMPILE_TEST && OF)
+ default n
+ help
+ Custom Dummy driver
+
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 0a03b7432497..361ede03cdb5 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) += scmi_custom.o
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
+
+obj-$(CONFIG_ARM_SCMI_CUSTOM_DUMMY) += scmi_custom_dummy.o
diff --git a/drivers/firmware/arm_scmi/scmi_custom_dummy.c b/drivers/firmware/arm_scmi/scmi_custom_dummy.c
new file mode 100644
index 000000000000..28fd3595a690
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_custom_dummy.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *
+ * Copyright (C) 2018-2020 ARM Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_domain.h>
+#include <linux/scmi_protocol.h>
+
+static const struct scmi_dummy_ops *dummy_ops;
+
+static int dummy_custom_perf_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct scmi_perf_level_report *er = data;
+
+ pr_info("%s()::%d - EVENT:[%ld] - TS:%lld DOMAIN_ID:%d AGENT_ID:%d LEVEL:%d\n",
+ __func__, __LINE__, event, er->timestamp, er->domain_id,
+ er->agent_id, er->performance_level);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dummy_custom_perf_nb = {
+ .notifier_call = dummy_custom_perf_cb,
+ .priority = 10,
+};
+
+static int custom_dummy_scream_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct scmi_dummy_scream_report *er = data;
+
+ pr_info("%s()::%d - EVENT:[%ld] - TS:%lld DOMAIN_ID:%d AGENT_ID:%d LEVEL:%d\n",
+ __func__, __LINE__, event, er->timestamp, er->domain_id,
+ er->agent_id, er->scream);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block custom_dummy_nb = {
+ .notifier_call = custom_dummy_scream_cb,
+ .priority = 10,
+};
+
+static int scmi_dummy_probe(struct scmi_device *sdev)
+{
+ int num_domains;
+ struct device *dev = &sdev->dev;
+ const struct scmi_handle *handle = sdev->handle;
+ u32 src_id;
+
+ if (!handle)
+ return -ENODEV;
+
+ dummy_ops = handle->get_ops(handle, SCMI_PROTOCOL_CUSTOM_DUMMY);
+ if (IS_ERR(dummy_ops))
+ return PTR_ERR(dummy_ops);
+
+ num_domains = dummy_ops->num_domains_get(handle);
+ if (num_domains < 0) {
+ dev_err(dev, "number of domains not found\n");
+ return num_domains;
+ }
+
+ pr_info("Registering notify_callback as CUSTOM_DUMMY !!\n");
+
+ src_id = 0x02;
+ handle->notify_ops->register_event_notifier(handle,
+ SCMI_PROTOCOL_CUSTOM_DUMMY,
+ SCMI_EVENT_DUMMY_SCREAM,
+ &src_id, &custom_dummy_nb);
+
+ src_id = 0x01;
+ handle->notify_ops->register_event_notifier(handle,
+ SCMI_PROTOCOL_PERF,
+ SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED,
+ &src_id,
+ &dummy_custom_perf_nb);
+
+ return 0;
+}
+
+static void scmi_dummy_remove(struct scmi_device *sdev)
+{
+ const struct scmi_handle *handle = sdev->handle;
+ u32 src_id;
+
+ src_id = 0x02;
+ handle->notify_ops->unregister_event_notifier(handle,
+ SCMI_PROTOCOL_CUSTOM_DUMMY,
+ SCMI_EVENT_DUMMY_SCREAM,
+ &src_id, &custom_dummy_nb);
+
+ src_id = 0x01;
+ handle->notify_ops->unregister_event_notifier(handle,
+ SCMI_PROTOCOL_PERF,
+ SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED,
+ &src_id,
+ &dummy_custom_perf_nb);
+
+ handle->put_ops(handle, SCMI_PROTOCOL_CUSTOM_DUMMY);
+
+ return;
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_CUSTOM_DUMMY, "custom_dummy" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_custom_dummy_driver = {
+ .name = "scmi-custom-dummy",
+ .probe = scmi_dummy_probe,
+ .remove = scmi_dummy_remove,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_custom_dummy_driver);
+
+MODULE_AUTHOR("DummyMaster");
+MODULE_DESCRIPTION("ARM SCMI Custom Dummy driver");
+MODULE_LICENSE("GPL v2");
--
2.17.1
Hi,
On 10/14/20 8:05 AM, Cristian Marussi wrote:
> Add an example SCMI driver using custom vendor protocol 0x99 and also
> registering for Performance protocol notifications.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
> drivers/firmware/Kconfig | 7 +
> drivers/firmware/arm_scmi/Makefile | 2 +
> drivers/firmware/arm_scmi/scmi_custom_dummy.c | 126 ++++++++++++++++++
> 3 files changed, 135 insertions(+)
> create mode 100644 drivers/firmware/arm_scmi/scmi_custom_dummy.c
>
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index 75e2668a6490..d209df6fd3ee 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -48,6 +48,13 @@ config ARM_SCMI_POWER_DOMAIN
> will be called scmi_pm_domain. Note this may needed early in boot
> before rootfs may be available.
>
> +config ARM_SCMI_CUSTOM_DUMMY
> + tristate "SCMI Custom Dummy driver"
> + depends on ARM_SCMI_PROTOCOL_CUSTOM || (COMPILE_TEST && OF)
Just an info note here: <linux/of.h> has lots of stubs for when CONFIG_OF is not
set/enabled, so COMPILE_TEST is usually enough of a dependency without adding "OF".
> + default n
> + help
> + Custom Dummy driver
--
~Randy
Hi Randy,
On Wed, Oct 14, 2020 at 08:55:08AM -0700, Randy Dunlap wrote:
> Hi,
>
> On 10/14/20 8:05 AM, Cristian Marussi wrote:
> > Add an example SCMI driver using custom vendor protocol 0x99 and also
> > registering for Performance protocol notifications.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
> > drivers/firmware/Kconfig | 7 +
> > drivers/firmware/arm_scmi/Makefile | 2 +
> > drivers/firmware/arm_scmi/scmi_custom_dummy.c | 126 ++++++++++++++++++
> > 3 files changed, 135 insertions(+)
> > create mode 100644 drivers/firmware/arm_scmi/scmi_custom_dummy.c
> >
> > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> > index 75e2668a6490..d209df6fd3ee 100644
> > --- a/drivers/firmware/Kconfig
> > +++ b/drivers/firmware/Kconfig
> > @@ -48,6 +48,13 @@ config ARM_SCMI_POWER_DOMAIN
> > will be called scmi_pm_domain. Note this may needed early in boot
> > before rootfs may be available.
> >
> > +config ARM_SCMI_CUSTOM_DUMMY
> > + tristate "SCMI Custom Dummy driver"
> > + depends on ARM_SCMI_PROTOCOL_CUSTOM || (COMPILE_TEST && OF)
>
> Just an info note here: <linux/of.h> has lots of stubs for when CONFIG_OF is not
> set/enabled, so COMPILE_TEST is usually enough of a dependency without adding "OF".
>
That's good to know, because even though this specific patch is just
example code not for upstream, I think we mostly use the above kind of
'depends' in other places... so thanks for the heads up I'll investigate
those other usages across the SCMI stack if we really need '&& OF'.
Thanks
Cristian
> > + default n
> > + help
> > + Custom Dummy driver
>
>
> --
> ~Randy
>
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 <[email protected]>
---
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, 298 insertions(+), 39 deletions(-)
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 017e5d8bd869..f19e08ed4369 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 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..afa2e4818a2b 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 = idr_find(&scmi_available_protocols, 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(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, 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..94bcad9a7d19 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 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..7de994e49884 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -23,6 +23,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 +69,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 +96,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: An array of 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 +114,9 @@ struct scmi_info {
struct scmi_xfers_info rx_minfo;
struct idr tx_idr;
struct idr rx_idr;
+ struct scmi_protocol_instance *protocols[SCMI_MAX_PROTO];
+ /* Ensure mutual exclusive access to protocols instance array */
+ struct mutex protocols_mtx;
u8 *protocols_imp;
struct list_head node;
int users;
@@ -519,6 +542,132 @@ 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);
+ /* Ensure protocols has been updated */
+ smp_rmb();
+ pi = info->protocols[protocol_id];
+
+ if (!pi) {
+ const struct scmi_protocol *proto;
+
+ /* Fail if protocol not registered on bus */
+ proto = scmi_get_protocol(protocol_id);
+ if (!proto) {
+ ret = -EINVAL;
+ 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;
+
+ info->protocols[protocol_id] = pi;
+ /* Ensure initialized protocol is visible */
+ smp_wmb();
+
+ devres_close_group(handle->dev, pi->gid);
+ dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
+ protocol_id);
+ } else {
+ refcount_inc(&pi->users);
+ }
+ 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 IS_ERR(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);
+ /* Ensure protocols has been updated */
+ smp_rmb();
+ pi = info->protocols[protocol_id];
+ if (WARN_ON(!pi)) {
+ mutex_unlock(&info->protocols_mtx);
+ return;
+ }
+
+ if (refcount_dec_and_test(&pi->users)) {
+ void *gid = pi->gid;
+
+ if (pi->proto->deinit)
+ pi->proto->deinit(handle);
+
+ info->protocols[protocol_id] = NULL;
+ /* Ensure deinitialized protocol is visible */
+ smp_wmb();
+
+ devres_release_group(handle->dev, gid);
+ dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
+ protocol_id);
+ }
+ mutex_unlock(&info->protocols_mtx);
+}
+
void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
u8 *prot_imp)
{
@@ -785,6 +934,7 @@ static int scmi_probe(struct platform_device *pdev)
info->dev = dev;
info->desc = desc;
INIT_LIST_HEAD(&info->node);
+ mutex_init(&info->protocols_mtx);
platform_set_drvdata(pdev, info);
idr_init(&info->tx_idr);
@@ -805,10 +955,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);
- if (ret) {
- dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
- return ret;
+ /*
+ * 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.
+ */
+ if (scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE)) {
+ dev_err(dev, "unable to communicate with SCMI\n");
+ return -ENODEV;
}
mutex_lock(&scmi_list_mutex);
@@ -941,6 +1095,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 +1110,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 +1119,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..854460a051c2 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 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..42c9c88da07c 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 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..2caf0bdb6fdc 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 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..dfe3076d2093 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 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..bcea18bf54ab 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 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..ca23d682941e 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(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
Add support for a loadable module implementing a custom vendor protocol
just as an example.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/firmware/Kconfig | 10 +-
drivers/firmware/arm_scmi/Makefile | 3 +
drivers/firmware/arm_scmi/scmi_custom.c | 170 ++++++++++++++++++++++++
include/linux/scmi_protocol.h | 21 +++
4 files changed, 203 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/arm_scmi/scmi_custom.c
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index afdbebba628a..75e2668a6490 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -6,7 +6,7 @@
menu "Firmware Drivers"
-config ARM_SCMI_PROTOCOL
+menuconfig ARM_SCMI_PROTOCOL
tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
depends on MAILBOX
@@ -27,6 +27,14 @@ config ARM_SCMI_PROTOCOL
This protocol library provides interface for all the client drivers
making use of the features offered by the SCMI.
+if ARM_SCMI_PROTOCOL
+ config ARM_SCMI_PROTOCOL_CUSTOM
+ tristate "SCMI Custom Protocol Implementation"
+ default y
+ help
+ ARM SCMI System Custom Protocol implementation
+endif
+
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index bc0d54f8e861..0a03b7432497 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -7,5 +7,8 @@ scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
+
+obj-$(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) += scmi_custom.o
+
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
diff --git a/drivers/firmware/arm_scmi/scmi_custom.c b/drivers/firmware/arm_scmi/scmi_custom.c
new file mode 100644
index 000000000000..5dfdbd175043
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_custom.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Dummy Custom Protocol
+ *
+ * Copyright (C) 2018-2020 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications CUSTOM - " fmt
+
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+#include "notify.h"
+
+static const struct scmi_xfer_ops *ops;
+
+struct scmi_dummy_scream_notify_payld {
+ __le32 agent_id;
+ __le32 domain_id;
+ __le32 scream;
+};
+
+struct dummy_dom_info {
+ bool state_set_sync;
+ bool state_set_async;
+ bool state_set_notify;
+ char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_dummy_info {
+ u32 version;
+ int num_domains;
+ u64 stats_addr;
+ u32 stats_size;
+ struct dummy_dom_info *dom_info;
+};
+
+static int scmi_dummy_num_domains_get(const struct scmi_handle *handle)
+{
+ struct scmi_dummy_info *di =
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY);
+
+ return di->num_domains;
+}
+
+static const struct scmi_dummy_ops dummy_ops = {
+ .num_domains_get = scmi_dummy_num_domains_get,
+};
+
+static int scmi_dummy_request_notify(const struct scmi_handle *handle,
+ u32 domain, bool enable)
+{
+ return domain % 2;
+}
+
+static int scmi_dummy_set_notify_enabled(const struct scmi_handle *handle,
+ u8 evt_id, u32 src_id, bool enable)
+{
+ int ret;
+
+ ret = scmi_dummy_request_notify(handle, src_id, enable);
+ if (ret)
+ pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
+ evt_id, src_id, ret);
+ else
+ pr_warn("NOTIF %sABLED - evt[%X] dom[%d]\n",
+ enable ? "EN" : "DIS", evt_id, src_id);
+
+ return ret;
+}
+
+static void *scmi_dummy_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)
+{
+ const struct scmi_dummy_scream_notify_payld *p = payld;
+ struct scmi_dummy_scream_report *r = report;
+
+ if (evt_id != SCMI_EVENT_DUMMY_SCREAM || sizeof(*p) != payld_sz)
+ return NULL;
+
+ r->timestamp = timestamp;
+ r->agent_id = le32_to_cpu(p->agent_id);
+ r->domain_id = le32_to_cpu(p->domain_id);
+ r->scream = le32_to_cpu(p->scream);
+ *src_id = r->domain_id;
+
+ return r;
+}
+
+static int scmi_dummy_get_num_sources(const struct scmi_handle *handle)
+{
+ struct scmi_dummy_info *dinfo =
+ ops->get_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY);
+
+ if (!dinfo)
+ return -EINVAL;
+
+ return dinfo->num_domains;
+}
+
+static const struct scmi_event dummy_events[] = {
+ {
+ .id = SCMI_EVENT_DUMMY_SCREAM,
+ .max_payld_sz = sizeof(struct scmi_dummy_scream_notify_payld),
+ .max_report_sz =
+ sizeof(struct scmi_dummy_scream_report),
+ },
+};
+
+static const struct scmi_event_ops dummy_event_ops = {
+ .get_num_sources = scmi_dummy_get_num_sources,
+ .set_notify_enabled = scmi_dummy_set_notify_enabled,
+ .fill_custom_report = scmi_dummy_fill_custom_report,
+};
+
+static const struct scmi_protocol_events dummy_protocol_events = {
+ .queue_sz = SCMI_PROTO_QUEUE_SZ,
+ .ops = &dummy_event_ops,
+ .evts = dummy_events,
+ .num_events = ARRAY_SIZE(dummy_events),
+};
+
+static int scmi_dummy_protocol_init(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
+{
+ u32 version = 0x20000001;
+ struct scmi_dummy_info *dinfo;
+
+ ops = xops;
+ //ops->version_get(handle, SCMI_PROTOCOL_POWER, &version);
+
+ dev_dbg(handle->dev, "Dummy Custom Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ dinfo = devm_kzalloc(handle->dev, sizeof(*dinfo), GFP_KERNEL);
+ if (!dinfo)
+ return -ENOMEM;
+
+ dinfo->num_domains = 5;
+
+ dinfo->dom_info = devm_kcalloc(handle->dev, dinfo->num_domains,
+ sizeof(*dinfo->dom_info), GFP_KERNEL);
+ if (!dinfo->dom_info)
+ return -ENOMEM;
+
+ dinfo->version = version;
+ return ops->set_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY, dinfo);
+}
+
+static int scmi_dummy_protocol_deinit(const struct scmi_handle *handle,
+ const struct scmi_xfer_ops *xops)
+{
+ return 0;
+}
+
+static struct scmi_protocol scmi_custom = {
+ .id = SCMI_PROTOCOL_CUSTOM_DUMMY,
+ .init = &scmi_dummy_protocol_init,
+ .deinit = &scmi_dummy_protocol_deinit,
+ .ops = &dummy_ops,
+ .events = &dummy_protocol_events,
+};
+
+module_scmi_protocol(scmi_custom);
+
+MODULE_AUTHOR("DummyMaster");
+MODULE_DESCRIPTION("ARM SCMI Custom Dummy Protocol");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index da675d6f90c0..9a89ca68b27d 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -209,6 +209,12 @@ struct scmi_reset_ops {
int (*deassert)(const struct scmi_handle *handle, u32 domain);
};
+#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM)
+struct scmi_dummy_ops {
+ int (*num_domains_get)(const struct scmi_handle *handle);
+};
+#endif
+
/**
* struct scmi_notify_ops - represents notifications' operations provided by
* SCMI core
@@ -278,6 +284,9 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_CLOCK = 0x14,
SCMI_PROTOCOL_SENSOR = 0x15,
SCMI_PROTOCOL_RESET = 0x16,
+#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM)
+ SCMI_PROTOCOL_CUSTOM_DUMMY = 0x99,
+#endif
};
enum scmi_system_events {
@@ -380,6 +389,9 @@ enum scmi_notification_events {
SCMI_EVENT_RESET_ISSUED = 0x0,
SCMI_EVENT_BASE_ERROR_EVENT = 0x0,
SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER = 0x0,
+#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM)
+ SCMI_EVENT_DUMMY_SCREAM = 0x0,
+#endif
};
struct scmi_power_state_changed_report {
@@ -433,4 +445,13 @@ struct scmi_base_error_report {
unsigned long long reports[];
};
+#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM)
+struct scmi_dummy_scream_report {
+ ktime_t timestamp;
+ unsigned int agent_id;
+ unsigned int domain_id;
+ unsigned int scream;
+};
+#endif
+
#endif /* _LINUX_SCMI_PROTOCOL_H */
--
2.17.1
Introduce generic get_ops/put_ops handle operations: any protocol, both
standard or custom, now exposes its operations through this common
interface which internally takes care to account for protocols' usage:
protocols' initialization is now performed on demand as soon as the first
user shows up while deinitialization (if any) is performed once
the last user of a protocol has gone.
Registered events' notifier are tracked too against the related protocol.
Convert all SCMI drivers to the new interface too.
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 30 +++++++++----
drivers/cpufreq/scmi-cpufreq.c | 28 ++++++------
drivers/firmware/arm_scmi/base.c | 2 +-
drivers/firmware/arm_scmi/bus.c | 24 -----------
drivers/firmware/arm_scmi/clock.c | 4 +-
drivers/firmware/arm_scmi/common.h | 6 +--
drivers/firmware/arm_scmi/driver.c | 31 ++++++++++++--
drivers/firmware/arm_scmi/notify.c | 50 +++++++++++++++++++---
drivers/firmware/arm_scmi/perf.c | 3 +-
drivers/firmware/arm_scmi/power.c | 3 +-
drivers/firmware/arm_scmi/reset.c | 3 +-
drivers/firmware/arm_scmi/scmi_pm_domain.c | 29 +++++++++----
drivers/firmware/arm_scmi/sensors.c | 3 +-
drivers/firmware/arm_scmi/system.c | 2 +-
drivers/hwmon/scmi-hwmon.c | 26 ++++++++---
drivers/reset/reset-scmi.c | 26 ++++++++---
include/linux/scmi_protocol.h | 15 +++----
17 files changed, 185 insertions(+), 100 deletions(-)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index c754dfbb73fd..4801a2df044b 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,6 +13,8 @@
#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;
@@ -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->handle, 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->handle, 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->handle, 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->handle, clk->id);
}
static const struct clk_ops scmi_clk_ops = {
@@ -143,10 +145,14 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
- if (!handle || !handle->clk_ops)
+ if (!handle)
return -ENODEV;
- count = handle->clk_ops->count_get(handle);
+ clk_ops = handle->get_ops(handle, SCMI_PROTOCOL_CLOCK);
+ if (IS_ERR(clk_ops))
+ return PTR_ERR(clk_ops);
+
+ count = clk_ops->count_get(handle);
if (count < 0) {
dev_err(dev, "%pOFn: invalid clock output count\n", np);
return -EINVAL;
@@ -167,7 +173,7 @@ 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(handle, idx);
if (!sclk->info) {
dev_dbg(dev, "invalid clock info for idx %d\n", idx);
continue;
@@ -191,6 +197,13 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
clk_data);
}
+static void scmi_clocks_remove(struct scmi_device *sdev)
+{
+ const struct scmi_handle *handle = sdev->handle;
+
+ handle->put_ops(handle, SCMI_PROTOCOL_CLOCK);
+}
+
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_CLOCK, "clocks" },
{ },
@@ -200,6 +213,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_clocks_driver = {
.name = "scmi-clocks",
.probe = scmi_clocks_probe,
+ .remove = scmi_clocks_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_clocks_driver);
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
index 46b268095421..280597b3c6fd 100644
--- a/drivers/cpufreq/scmi-cpufreq.c
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -25,11 +25,11 @@ struct scmi_data {
};
static const struct scmi_handle *handle;
+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;
@@ -50,7 +50,6 @@ 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);
@@ -64,7 +63,6 @@ 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,
target_freq * 1000, true)) {
@@ -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(handle, 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(handle, 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(handle, 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(handle, cpu_dev);
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
@@ -239,9 +237,13 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
handle = sdev->handle;
- if (!handle || !handle->perf_ops)
+ if (!handle)
return -ENODEV;
+ perf_ops = handle->get_ops(handle, SCMI_PROTOCOL_PERF);
+ if (IS_ERR(perf_ops))
+ return PTR_ERR(perf_ops);
+
ret = cpufreq_register_driver(&scmi_cpufreq_driver);
if (ret) {
dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n",
@@ -254,6 +256,8 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
static void scmi_cpufreq_remove(struct scmi_device *sdev)
{
cpufreq_unregister_driver(&scmi_cpufreq_driver);
+
+ handle->put_ops(handle, SCMI_PROTOCOL_PERF);
}
static const struct scmi_device_id scmi_id_table[] = {
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index f19e08ed4369..129633e6fff4 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,
};
-static int scmi_base_protocol_init(struct scmi_handle *h)
+static int scmi_base_protocol_init(const struct scmi_handle *h)
{
int id, ret;
u8 *prot_imp;
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index afa2e4818a2b..3a2be1193c85 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 = idr_find(&scmi_available_protocols, 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 4e8dafc36d7e..539c94860b8f 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -332,7 +332,7 @@ 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_handle *handle)
{
u32 version;
int clkid, ret;
@@ -363,8 +363,6 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
}
cinfo->version = version;
- handle->clk_ops = &clk_ops;
-
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_CLOCK, cinfo);
}
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index de2f22032a57..56ebb710ee84 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -158,7 +158,7 @@ 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);
-typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
+typedef int (*scmi_prot_init_fn_t)(const struct scmi_handle *);
/**
* struct scmi_protocol - Protocol descriptor
@@ -202,8 +202,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);
void *scmi_get_proto_priv(const struct scmi_handle *h, u8 prot);
int scmi_set_proto_priv(const struct scmi_handle *handle, const u8 proto,
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index bad1d0130e96..049220efd227 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -585,7 +585,7 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
* 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;
@@ -655,7 +655,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 IS_ERR(scmi_get_protocol_instance(handle, protocol_id));
}
@@ -668,7 +668,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;
@@ -700,6 +700,29 @@ 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.
+ *
+ * Get hold of a protocol accounting for its usage, eventually triggering its
+ * initialization, and returning the protocol specific operations.
+ *
+ * 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(const struct scmi_handle *handle, u8 protocol_id)
+{
+ struct scmi_protocol_instance *pi;
+
+ pi = scmi_get_protocol_instance(handle, protocol_id);
+ if (IS_ERR(pi))
+ return pi;
+
+ return pi->proto->ops;
+}
+
void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
u8 *prot_imp)
{
@@ -975,6 +998,8 @@ static int scmi_probe(struct platform_device *pdev)
handle = &info->handle;
handle->dev = info->dev;
handle->version = &info->version;
+ 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..02b00af9b08f 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);
}
/**
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 13e215f359fb..bd9cb2583557 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -857,7 +857,7 @@ static const struct scmi_event_ops perf_event_ops = {
.fill_custom_report = scmi_perf_fill_custom_report,
};
-static int scmi_perf_protocol_init(struct scmi_handle *handle)
+static int scmi_perf_protocol_init(const struct scmi_handle *handle)
{
int domain;
u32 version;
@@ -896,7 +896,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
pinfo->num_domains);
pinfo->version = version;
- handle->perf_ops = &perf_ops;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
}
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index e0b29ed4e09a..1e026b5530a7 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -262,7 +262,7 @@ static const struct scmi_event_ops power_event_ops = {
.fill_custom_report = scmi_power_fill_custom_report,
};
-static int scmi_power_protocol_init(struct scmi_handle *handle)
+static int scmi_power_protocol_init(const struct scmi_handle *handle)
{
int domain;
u32 version;
@@ -297,7 +297,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
pinfo->num_domains);
pinfo->version = version;
- handle->power_ops = &power_ops;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
}
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index f70e9b5108d5..b7da4de0e56e 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -274,7 +274,7 @@ static const struct scmi_event_ops reset_event_ops = {
.fill_custom_report = scmi_reset_fill_custom_report,
};
-static int scmi_reset_protocol_init(struct scmi_handle *handle)
+static int scmi_reset_protocol_init(const struct scmi_handle *handle)
{
int domain;
u32 version;
@@ -309,7 +309,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
pinfo->num_domains);
pinfo->version = version;
- handle->reset_ops = &reset_ops;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
}
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
index 9e44479f0284..bfea56f77890 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,6 +11,8 @@
#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;
@@ -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->handle, pd->domain, state);
if (!ret)
- ret = ops->state_get(pd->handle, pd->domain, &ret_state);
+ ret = power_ops->state_get(pd->handle, pd->domain, &ret_state);
if (!ret && state != ret_state)
return -EIO;
@@ -61,10 +62,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
struct generic_pm_domain **domains;
const struct scmi_handle *handle = sdev->handle;
- if (!handle || !handle->power_ops)
+ if (!handle)
return -ENODEV;
- num_domains = handle->power_ops->num_domains_get(handle);
+ power_ops = handle->get_ops(handle, SCMI_PROTOCOL_POWER);
+ if (IS_ERR(power_ops))
+ return PTR_ERR(power_ops);
+
+ num_domains = power_ops->num_domains_get(handle);
if (num_domains < 0) {
dev_err(dev, "number of domains not found\n");
return num_domains;
@@ -85,14 +90,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(handle, 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->name = power_ops->name_get(handle, 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;
@@ -111,6 +116,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
return 0;
}
+static void scmi_pm_domain_remove(struct scmi_device *sdev)
+{
+ const struct scmi_handle *handle = sdev->handle;
+
+ handle->put_ops(handle, SCMI_PROTOCOL_POWER);
+}
+
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_POWER, "genpd" },
{ },
@@ -120,6 +132,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_power_domain_driver = {
.name = "scmi-power-domain",
.probe = scmi_pm_domain_probe,
+ .remove = scmi_pm_domain_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_power_domain_driver);
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 8a0a599558ba..e0129dcd322f 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -334,7 +334,7 @@ static const struct scmi_event_ops sensor_event_ops = {
.fill_custom_report = scmi_sensor_fill_custom_report,
};
-static int scmi_sensors_protocol_init(struct scmi_handle *handle)
+static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
{
u32 version;
struct sensors_info *sinfo;
@@ -364,7 +364,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
sinfo->num_sensors);
sinfo->version = version;
- handle->sensor_ops = &sensor_ops;
return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
}
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 8f53f93c63ca..30e3510c1f07 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -101,7 +101,7 @@ static const struct scmi_event_ops system_event_ops = {
.fill_custom_report = scmi_system_fill_custom_report,
};
-static int scmi_system_protocol_init(struct scmi_handle *handle)
+static int scmi_system_protocol_init(const struct scmi_handle *handle)
{
u32 version;
struct scmi_system_info *pinfo;
diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
index d421e691318b..27ef71996a15 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 <[email protected]>
*/
@@ -13,6 +13,8 @@
#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_sensor_info **info[hwmon_max];
@@ -72,7 +74,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
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(h, sensor->id, &value);
if (ret)
return ret;
@@ -170,10 +172,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
const struct hwmon_channel_info **ptr_scmi_ci;
const struct scmi_handle *handle = sdev->handle;
- if (!handle || !handle->sensor_ops)
+ if (!handle)
return -ENODEV;
- nr_sensors = handle->sensor_ops->count_get(handle);
+ sensor_ops = handle->get_ops(handle, SCMI_PROTOCOL_SENSOR);
+ if (IS_ERR(sensor_ops))
+ return PTR_ERR(sensor_ops);
+
+ nr_sensors = sensor_ops->count_get(handle);
if (!nr_sensors)
return -EIO;
@@ -184,7 +190,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
scmi_sensors->handle = handle;
for (i = 0; i < nr_sensors; i++) {
- sensor = handle->sensor_ops->info_get(handle, i);
+ sensor = sensor_ops->info_get(handle, 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(handle, i);
if (!sensor)
continue;
@@ -258,6 +264,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
return PTR_ERR_OR_ZERO(hwdev);
}
+static void scmi_hwmon_remove(struct scmi_device *sdev)
+{
+ const struct scmi_handle *handle = sdev->handle;
+
+ handle->put_ops(handle, SCMI_PROTOCOL_SENSOR);
+}
+
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_SENSOR, "hwmon" },
{ },
@@ -267,6 +280,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_hwmon_drv = {
.name = "scmi-hwmon",
.probe = scmi_hwmon_probe,
+ .remove = scmi_hwmon_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_hwmon_drv);
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index 8d3a858e3b19..e48220dedb35 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,6 +11,8 @@
#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
@@ -39,7 +41,7 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
const struct scmi_handle *handle = to_scmi_handle(rcdev);
- return handle->reset_ops->assert(handle, id);
+ return reset_ops->assert(handle, id);
}
/**
@@ -57,7 +59,7 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
const struct scmi_handle *handle = to_scmi_handle(rcdev);
- return handle->reset_ops->deassert(handle, id);
+ return reset_ops->deassert(handle, id);
}
/**
@@ -75,7 +77,7 @@ scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
const struct scmi_handle *handle = to_scmi_handle(rcdev);
- return handle->reset_ops->reset(handle, id);
+ return reset_ops->reset(handle, id);
}
static const struct reset_control_ops scmi_reset_ops = {
@@ -91,9 +93,13 @@ static int scmi_reset_probe(struct scmi_device *sdev)
struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
- if (!handle || !handle->reset_ops)
+ if (!handle)
return -ENODEV;
+ reset_ops = handle->get_ops(handle, SCMI_PROTOCOL_RESET);
+ if (IS_ERR(reset_ops))
+ return PTR_ERR(reset_ops);
+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -101,12 +107,19 @@ 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->rcdev.nr_resets = reset_ops->num_domains_get(handle);
data->handle = handle;
return devm_reset_controller_register(dev, &data->rcdev);
}
+static void scmi_reset_remove(struct scmi_device *sdev)
+{
+ const struct scmi_handle *handle = sdev->handle;
+
+ handle->put_ops(handle, SCMI_PROTOCOL_RESET);
+}
+
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_RESET, "reset" },
{ },
@@ -116,6 +129,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_reset_driver = {
.name = "scmi-reset",
.probe = scmi_reset_probe,
+ .remove = scmi_reset_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_reset_driver);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index bc4f06d46bfb..bfe7017cff19 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -257,11 +257,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
* @notify_ops: pointer to set of notifications related operations
* @notify_priv: pointer to private data structure specific to notifications
* (for internal use only)
@@ -269,11 +264,11 @@ struct scmi_notify_ops {
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 *(*get_ops)(const struct scmi_handle *handle,
+ u8 proto);
+ void (*put_ops)(const struct scmi_handle *handle, u8 proto);
+
const struct scmi_notify_ops *notify_ops;
void *notify_priv;
};
--
2.17.1
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 <[email protected]>
---
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 66574f57e304..5a91e3324697 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -295,4 +295,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 378749040162..25a4152537e6 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -106,6 +106,7 @@ struct scmi_protocol_instance {
* they have to sit on their own.
* @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
*/
@@ -123,6 +124,7 @@ struct scmi_info {
struct mutex protocols_mtx;
void *protocols_private_data[SCMI_MAX_PROTO];
u8 *protocols_imp;
+ void *notify_priv;
struct list_head node;
int users;
};
@@ -164,6 +166,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 7ba182d4f2b4..d89d9c08e660 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -578,11 +578,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)
@@ -756,11 +754,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) {
@@ -844,12 +840,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;
@@ -1352,11 +1346,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);
@@ -1400,11 +1392,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);
@@ -1554,8 +1544,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 = ¬ify_ops;
- handle->notify_priv = ni;
/* Ensure handle is up to date */
smp_wmb();
@@ -1567,7 +1557,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;
}
@@ -1578,19 +1568,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 bfe7017cff19..650d0877a5c8 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -258,8 +258,6 @@ struct scmi_notify_ops {
* @dev: pointer to the SCMI device
* @version: pointer to the structure containing SCMI version information
* @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;
@@ -270,7 +268,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
Hi Vincent,
thanks for the review.
On Thu, Oct 15, 2020 at 11:41:09AM +0200, Vincent Guittot wrote:
> On Wed, 14 Oct 2020 at 17:06, Cristian Marussi <[email protected]> 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.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
>
> [...]
>
> >
> > +/**
> > + * 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);
> > + /* Ensure protocols has been updated */
> > + smp_rmb();
>
> Why do you need this smp_rmb and the smp_wmb below ?
>
> Isn't the mutex not enough ?
>
You're right, it's a leftover I think, since initially there was no
mutex. I'll remove it in V2.
Thanks
Cristian
> > + pi = info->protocols[protocol_id];
> > +
> > + if (!pi) {
> > + const struct scmi_protocol *proto;
> > +
> > + /* Fail if protocol not registered on bus */
> > + proto = scmi_get_protocol(protocol_id);
> > + if (!proto) {
> > + ret = -EINVAL;
> > + 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;
> > +
> > + info->protocols[protocol_id] = pi;
> > + /* Ensure initialized protocol is visible */
> > + smp_wmb();
> > +
> > + devres_close_group(handle->dev, pi->gid);
> > + dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
> > + protocol_id);
> > + } else {
> > + refcount_inc(&pi->users);
> > + }
> > + 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 IS_ERR(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);
> > + /* Ensure protocols has been updated */
> > + smp_rmb();
> > + pi = info->protocols[protocol_id];
> > + if (WARN_ON(!pi)) {
> > + mutex_unlock(&info->protocols_mtx);
> > + return;
> > + }
> > +
> > + if (refcount_dec_and_test(&pi->users)) {
> > + void *gid = pi->gid;
> > +
> > + if (pi->proto->deinit)
> > + pi->proto->deinit(handle);
> > +
> > + info->protocols[protocol_id] = NULL;
> > + /* Ensure deinitialized protocol is visible */
> > + smp_wmb();
> > +
> > + devres_release_group(handle->dev, gid);
> > + dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
> > + protocol_id);
> > + }
> > + mutex_unlock(&info->protocols_mtx);
> > +}
> > +
> > void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> > u8 *prot_imp)
> > {
> > @@ -785,6 +934,7 @@ static int scmi_probe(struct platform_device *pdev)
> > info->dev = dev;
> > info->desc = desc;
> > INIT_LIST_HEAD(&info->node);
> > + mutex_init(&info->protocols_mtx);
> >
> > platform_set_drvdata(pdev, info);
> > idr_init(&info->tx_idr);
> > @@ -805,10 +955,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);
> > - if (ret) {
> > - dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> > - return ret;
> > + /*
> > + * 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.
> > + */
> > + if (scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE)) {
> > + dev_err(dev, "unable to communicate with SCMI\n");
> > + return -ENODEV;
> > }
> >
> > mutex_lock(&scmi_list_mutex);
> > @@ -941,6 +1095,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 +1110,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 +1119,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..854460a051c2 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 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..42c9c88da07c 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 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..2caf0bdb6fdc 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 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..dfe3076d2093 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 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..bcea18bf54ab 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 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..ca23d682941e 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(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
> >
On Wed, 14 Oct 2020 at 17:06, Cristian Marussi <[email protected]> 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.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
[...]
>
> +/**
> + * 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);
> + /* Ensure protocols has been updated */
> + smp_rmb();
Why do you need this smp_rmb and the smp_wmb below ?
Isn't the mutex not enough ?
> + pi = info->protocols[protocol_id];
> +
> + if (!pi) {
> + const struct scmi_protocol *proto;
> +
> + /* Fail if protocol not registered on bus */
> + proto = scmi_get_protocol(protocol_id);
> + if (!proto) {
> + ret = -EINVAL;
> + 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;
> +
> + info->protocols[protocol_id] = pi;
> + /* Ensure initialized protocol is visible */
> + smp_wmb();
> +
> + devres_close_group(handle->dev, pi->gid);
> + dev_dbg(handle->dev, "Initialized protocol: 0x%X\n",
> + protocol_id);
> + } else {
> + refcount_inc(&pi->users);
> + }
> + 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 IS_ERR(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);
> + /* Ensure protocols has been updated */
> + smp_rmb();
> + pi = info->protocols[protocol_id];
> + if (WARN_ON(!pi)) {
> + mutex_unlock(&info->protocols_mtx);
> + return;
> + }
> +
> + if (refcount_dec_and_test(&pi->users)) {
> + void *gid = pi->gid;
> +
> + if (pi->proto->deinit)
> + pi->proto->deinit(handle);
> +
> + info->protocols[protocol_id] = NULL;
> + /* Ensure deinitialized protocol is visible */
> + smp_wmb();
> +
> + devres_release_group(handle->dev, gid);
> + dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
> + protocol_id);
> + }
> + mutex_unlock(&info->protocols_mtx);
> +}
> +
> void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> u8 *prot_imp)
> {
> @@ -785,6 +934,7 @@ static int scmi_probe(struct platform_device *pdev)
> info->dev = dev;
> info->desc = desc;
> INIT_LIST_HEAD(&info->node);
> + mutex_init(&info->protocols_mtx);
>
> platform_set_drvdata(pdev, info);
> idr_init(&info->tx_idr);
> @@ -805,10 +955,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);
> - if (ret) {
> - dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> - return ret;
> + /*
> + * 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.
> + */
> + if (scmi_acquire_protocol(handle, SCMI_PROTOCOL_BASE)) {
> + dev_err(dev, "unable to communicate with SCMI\n");
> + return -ENODEV;
> }
>
> mutex_lock(&scmi_list_mutex);
> @@ -941,6 +1095,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 +1110,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 +1119,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..854460a051c2 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 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..42c9c88da07c 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 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..2caf0bdb6fdc 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 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..dfe3076d2093 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 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..bcea18bf54ab 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 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..ca23d682941e 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(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
>
On 10/14/20 11:05 AM, Cristian Marussi wrote:
> Add custom_dummy SCMI devname.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
> drivers/firmware/arm_scmi/driver.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 55df134c2338..5c39a738866a 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -993,6 +993,7 @@ static struct scmi_prot_devnames devnames[] = {
> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> { SCMI_PROTOCOL_RESET, { "reset" },},
> + { SCMI_PROTOCOL_CUSTOM_DUMMY, { "custom_dummy" },},
Hi Cristian,
Thanks for the sample dummy custom protocol and driver!
The problem with adding scmi devname into the array is that every time a
custom vendor protocol is added, the array has to be extended. Instead
since the scmi spec supports the range 0x80-0xff for custom protocols,
why not check for that range in scmi_create_protocol_devices and go
ahead with registering the creating the protocol device via
scmi_create_protocol_device?
> };
>
> static inline void
>
--
Warm Regards
Thara
On Tue, Oct 20, 2020 at 10:46:46PM -0400, Thara Gopinath wrote:
> Hi Cristian,
>
> Thanks for this series!
>
Hi Thara,
thanks for reviewing.
> On 10/14/20 11:05 AM, 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.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
> > 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, 298 insertions(+), 39 deletions(-)
> >
> > diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> > index 017e5d8bd869..f19e08ed4369 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 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..afa2e4818a2b 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 = idr_find(&scmi_available_protocols, protocol_id);
> > + if (!proto)
> > return -EINVAL;
>
>
> Any reason not to use the above scmi_get_protocol here ?
>
You're right I spotted this awkward thing but I left it because the
whole scmi_protocol_init() is removed later on in this series, moving
proto initialization to when the protocol is requested, so it seemed
silly to fix code that I'm going to remove as a whole in the same
series.
Probably I could split the whole series better to avoid this. (or fix
it anyway and later removed)
> > - 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(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, 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/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index 3dfd8b6a0ebf..7de994e49884 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -23,6 +23,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 +69,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 +96,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: An array of 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 +114,9 @@ struct scmi_info {
> > struct scmi_xfers_info rx_minfo;
> > struct idr tx_idr;
> > struct idr rx_idr;
> > + struct scmi_protocol_instance *protocols[SCMI_MAX_PROTO];
> > + /* Ensure mutual exclusive access to protocols instance array */
> > + struct mutex protocols_mtx;
> > u8 *protocols_imp;
> > struct list_head node;
> > int users;
> > @@ -519,6 +542,132 @@ 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);
> > + /* Ensure protocols has been updated */
> > + smp_rmb();
> > + pi = info->protocols[protocol_id];
> > +
> > + if (!pi) {
> > + const struct scmi_protocol *proto;
> > +
> > + /* Fail if protocol not registered on bus */
> > + proto = scmi_get_protocol(protocol_id);
> > + if (!proto) {
> > + ret = -EINVAL;
> > + 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);
>
> Isnt this already done in scmi_protocol_init? Or rather the init should
> remain here and there is no need for it to be called from
> scmi_protocol_init?
>
So, basically I introduced a new initialization flow triggered by
scmi_acquire_protocol (and later in the series by get_ops), so that
protocols won't be anymore initialized by scmi_protocol_init() during
dev prove but later on when used effectively (acquire/get_ops) so that
I can track their usage, but in this patch I ported only base protocol
to this new flow while keeping other protos the old way...so again maybe
I could split this series better in V2, maybe merging all these protos
changes in one go
Regards
Cristian
>
>
> --
> Warm Regards
> Thara
On 10/14/20 11:05 AM, Cristian Marussi wrote:
> Modify protocol initialization callback adding a new parameter representing
> a reference to the available xfer core operations and introduce a macro to
> simply register with the core new protocols as loadable drivers.
> Keep standard protocols as builtin.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
> drivers/firmware/arm_scmi/base.c | 56 ++++++++++--------
> drivers/firmware/arm_scmi/bus.c | 14 ++++-
> drivers/firmware/arm_scmi/clock.c | 56 +++++++++---------
> drivers/firmware/arm_scmi/common.h | 42 +++++++++-----
> drivers/firmware/arm_scmi/driver.c | 50 ++++++++++------
> drivers/firmware/arm_scmi/perf.c | 88 +++++++++++++++--------------
> drivers/firmware/arm_scmi/power.c | 46 ++++++++-------
> drivers/firmware/arm_scmi/reset.c | 46 ++++++++-------
> drivers/firmware/arm_scmi/sensors.c | 52 +++++++++--------
> drivers/firmware/arm_scmi/system.c | 16 ++++--
> include/linux/scmi_protocol.h | 18 +++++-
> 11 files changed, 288 insertions(+), 196 deletions(-)
>
> diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> index f40821eeb103..8d7214fd2187 100644
> --- a/drivers/firmware/arm_scmi/base.c
> +++ b/drivers/firmware/arm_scmi/base.c
> @@ -15,6 +15,8 @@
> #define SCMI_BASE_NUM_SOURCES 1
> #define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
>
> +static const struct scmi_xfer_ops *ops;
Minor nit. I would consider renaming ops to something more
meaningful like xfer_ops (or anything that makes sense). ops by
itself leads to confusion with ops in scmo_protocol and in
scmi_protocol_events.
Same suggestion for all other declarations of ops in this patch.
--
Warm Regards
Thara
Hi Cristian,
Thanks for this series!
On 10/14/20 11:05 AM, 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.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
> 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, 298 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> index 017e5d8bd869..f19e08ed4369 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 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..afa2e4818a2b 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 = idr_find(&scmi_available_protocols, protocol_id);
> + if (!proto)
> return -EINVAL;
Any reason not to use the above scmi_get_protocol here ?
> - 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(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, 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/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 3dfd8b6a0ebf..7de994e49884 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -23,6 +23,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 +69,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 +96,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: An array of 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 +114,9 @@ struct scmi_info {
> struct scmi_xfers_info rx_minfo;
> struct idr tx_idr;
> struct idr rx_idr;
> + struct scmi_protocol_instance *protocols[SCMI_MAX_PROTO];
> + /* Ensure mutual exclusive access to protocols instance array */
> + struct mutex protocols_mtx;
> u8 *protocols_imp;
> struct list_head node;
> int users;
> @@ -519,6 +542,132 @@ 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);
> + /* Ensure protocols has been updated */
> + smp_rmb();
> + pi = info->protocols[protocol_id];
> +
> + if (!pi) {
> + const struct scmi_protocol *proto;
> +
> + /* Fail if protocol not registered on bus */
> + proto = scmi_get_protocol(protocol_id);
> + if (!proto) {
> + ret = -EINVAL;
> + 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);
Isnt this already done in scmi_protocol_init? Or rather the init should
remain here and there is no need for it to be called from
scmi_protocol_init?
--
Warm Regards
Thara
On 10/14/20 11:05 AM, Cristian Marussi wrote:
> Introduce generic get_ops/put_ops handle operations: any protocol, both
> standard or custom, now exposes its operations through this common
> interface which internally takes care to account for protocols' usage:
> protocols' initialization is now performed on demand as soon as the first
> user shows up while deinitialization (if any) is performed once
> the last user of a protocol has gone.
> Registered events' notifier are tracked too against the related protocol.
> Convert all SCMI drivers to the new interface too.
>
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
[...]
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index bad1d0130e96..049220efd227 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -585,7 +585,7 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
> * 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;
> @@ -655,7 +655,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 IS_ERR(scmi_get_protocol_instance(handle, protocol_id));
> }
> @@ -668,7 +668,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;
> @@ -700,6 +700,29 @@ 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.
> + *
> + * Get hold of a protocol accounting for its usage, eventually triggering its
> + * initialization, and returning the protocol specific operations.
> + *
> + * 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(const struct scmi_handle *handle, u8 protocol_id)
> +{
> + struct scmi_protocol_instance *pi;
> +
> + pi = scmi_get_protocol_instance(handle, protocol_id);
> + if (IS_ERR(pi))
> + return pi;
> +
> + return pi->proto->ops;
> +} > +
> void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> u8 *prot_imp)
> {
> @@ -975,6 +998,8 @@ static int scmi_probe(struct platform_device *pdev)
> handle = &info->handle;
> handle->dev = info->dev;
> handle->version = &info->version;
> + handle->get_ops = scmi_get_protocol_operations;
> + handle->put_ops = scmi_release_protocol;
Why do you need get_ops and put_ops? Why not have the drivers call
scmi_acquire_protocol and scmi_release_protocol directly and get the ops
from retrieved scmi_get_protocol_instance ? IMHO, this makes it more
readable. Also, this will make the usage of scmi_acquire_protocol and
scmi_release_protocol more consistent. Right now, notify.c uses
scmi_acquire_protocol to acquire protocol because there is no need for
ops and other drivers use get_ops to acquire protocol. Kind of confusing..
--
Warm Regards
Thara
>
> 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..02b00af9b08f 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);
> }
>
> /**
> diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
> index 13e215f359fb..bd9cb2583557 100644
> --- a/drivers/firmware/arm_scmi/perf.c
> +++ b/drivers/firmware/arm_scmi/perf.c
> @@ -857,7 +857,7 @@ static const struct scmi_event_ops perf_event_ops = {
> .fill_custom_report = scmi_perf_fill_custom_report,
> };
>
> -static int scmi_perf_protocol_init(struct scmi_handle *handle)
> +static int scmi_perf_protocol_init(const struct scmi_handle *handle)
> {
> int domain;
> u32 version;
> @@ -896,7 +896,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
> pinfo->num_domains);
>
> pinfo->version = version;
> - handle->perf_ops = &perf_ops;
> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
> }
>
> diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
> index e0b29ed4e09a..1e026b5530a7 100644
> --- a/drivers/firmware/arm_scmi/power.c
> +++ b/drivers/firmware/arm_scmi/power.c
> @@ -262,7 +262,7 @@ static const struct scmi_event_ops power_event_ops = {
> .fill_custom_report = scmi_power_fill_custom_report,
> };
>
> -static int scmi_power_protocol_init(struct scmi_handle *handle)
> +static int scmi_power_protocol_init(const struct scmi_handle *handle)
> {
> int domain;
> u32 version;
> @@ -297,7 +297,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
> pinfo->num_domains);
>
> pinfo->version = version;
> - handle->power_ops = &power_ops;
> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
> }
>
> diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
> index f70e9b5108d5..b7da4de0e56e 100644
> --- a/drivers/firmware/arm_scmi/reset.c
> +++ b/drivers/firmware/arm_scmi/reset.c
> @@ -274,7 +274,7 @@ static const struct scmi_event_ops reset_event_ops = {
> .fill_custom_report = scmi_reset_fill_custom_report,
> };
>
> -static int scmi_reset_protocol_init(struct scmi_handle *handle)
> +static int scmi_reset_protocol_init(const struct scmi_handle *handle)
> {
> int domain;
> u32 version;
> @@ -309,7 +309,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
> pinfo->num_domains);
>
> pinfo->version = version;
> - handle->reset_ops = &reset_ops;
> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
> }
>
> diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
> index 9e44479f0284..bfea56f77890 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,6 +11,8 @@
> #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;
> @@ -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->handle, pd->domain, state);
> if (!ret)
> - ret = ops->state_get(pd->handle, pd->domain, &ret_state);
> + ret = power_ops->state_get(pd->handle, pd->domain, &ret_state);
> if (!ret && state != ret_state)
> return -EIO;
>
> @@ -61,10 +62,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> struct generic_pm_domain **domains;
> const struct scmi_handle *handle = sdev->handle;
>
> - if (!handle || !handle->power_ops)
> + if (!handle)
> return -ENODEV;
>
> - num_domains = handle->power_ops->num_domains_get(handle);
> + power_ops = handle->get_ops(handle, SCMI_PROTOCOL_POWER);
> + if (IS_ERR(power_ops))
> + return PTR_ERR(power_ops);
> +
> + num_domains = power_ops->num_domains_get(handle);
> if (num_domains < 0) {
> dev_err(dev, "number of domains not found\n");
> return num_domains;
> @@ -85,14 +90,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(handle, 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->name = power_ops->name_get(handle, 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;
> @@ -111,6 +116,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> return 0;
> }
>
> +static void scmi_pm_domain_remove(struct scmi_device *sdev)
> +{
> + const struct scmi_handle *handle = sdev->handle;
> +
> + handle->put_ops(handle, SCMI_PROTOCOL_POWER);
> +}
> +
> static const struct scmi_device_id scmi_id_table[] = {
> { SCMI_PROTOCOL_POWER, "genpd" },
> { },
> @@ -120,6 +132,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> static struct scmi_driver scmi_power_domain_driver = {
> .name = "scmi-power-domain",
> .probe = scmi_pm_domain_probe,
> + .remove = scmi_pm_domain_remove,
> .id_table = scmi_id_table,
> };
> module_scmi_driver(scmi_power_domain_driver);
> diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
> index 8a0a599558ba..e0129dcd322f 100644
> --- a/drivers/firmware/arm_scmi/sensors.c
> +++ b/drivers/firmware/arm_scmi/sensors.c
> @@ -334,7 +334,7 @@ static const struct scmi_event_ops sensor_event_ops = {
> .fill_custom_report = scmi_sensor_fill_custom_report,
> };
>
> -static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> +static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
> {
> u32 version;
> struct sensors_info *sinfo;
> @@ -364,7 +364,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> sinfo->num_sensors);
>
> sinfo->version = version;
> - handle->sensor_ops = &sensor_ops;
> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
> }
>
> diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
> index 8f53f93c63ca..30e3510c1f07 100644
> --- a/drivers/firmware/arm_scmi/system.c
> +++ b/drivers/firmware/arm_scmi/system.c
> @@ -101,7 +101,7 @@ static const struct scmi_event_ops system_event_ops = {
> .fill_custom_report = scmi_system_fill_custom_report,
> };
>
> -static int scmi_system_protocol_init(struct scmi_handle *handle)
> +static int scmi_system_protocol_init(const struct scmi_handle *handle)
> {
> u32 version;
> struct scmi_system_info *pinfo;
> diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
> index d421e691318b..27ef71996a15 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 <[email protected]>
> */
>
> @@ -13,6 +13,8 @@
> #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_sensor_info **info[hwmon_max];
> @@ -72,7 +74,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> 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(h, sensor->id, &value);
> if (ret)
> return ret;
>
> @@ -170,10 +172,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> const struct hwmon_channel_info **ptr_scmi_ci;
> const struct scmi_handle *handle = sdev->handle;
>
> - if (!handle || !handle->sensor_ops)
> + if (!handle)
> return -ENODEV;
>
> - nr_sensors = handle->sensor_ops->count_get(handle);
> + sensor_ops = handle->get_ops(handle, SCMI_PROTOCOL_SENSOR);
> + if (IS_ERR(sensor_ops))
> + return PTR_ERR(sensor_ops);
> +
> + nr_sensors = sensor_ops->count_get(handle);
> if (!nr_sensors)
> return -EIO;
>
> @@ -184,7 +190,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> scmi_sensors->handle = handle;
>
> for (i = 0; i < nr_sensors; i++) {
> - sensor = handle->sensor_ops->info_get(handle, i);
> + sensor = sensor_ops->info_get(handle, 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(handle, i);
> if (!sensor)
> continue;
>
> @@ -258,6 +264,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> return PTR_ERR_OR_ZERO(hwdev);
> }
>
> +static void scmi_hwmon_remove(struct scmi_device *sdev)
> +{
> + const struct scmi_handle *handle = sdev->handle;
> +
> + handle->put_ops(handle, SCMI_PROTOCOL_SENSOR);
> +}
> +
> static const struct scmi_device_id scmi_id_table[] = {
> { SCMI_PROTOCOL_SENSOR, "hwmon" },
> { },
> @@ -267,6 +280,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> static struct scmi_driver scmi_hwmon_drv = {
> .name = "scmi-hwmon",
> .probe = scmi_hwmon_probe,
> + .remove = scmi_hwmon_remove,
> .id_table = scmi_id_table,
> };
> module_scmi_driver(scmi_hwmon_drv);
> diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
> index 8d3a858e3b19..e48220dedb35 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,6 +11,8 @@
> #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
> @@ -39,7 +41,7 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
> {
> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>
> - return handle->reset_ops->assert(handle, id);
> + return reset_ops->assert(handle, id);
> }
>
> /**
> @@ -57,7 +59,7 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
> {
> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>
> - return handle->reset_ops->deassert(handle, id);
> + return reset_ops->deassert(handle, id);
> }
>
> /**
> @@ -75,7 +77,7 @@ scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
> {
> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>
> - return handle->reset_ops->reset(handle, id);
> + return reset_ops->reset(handle, id);
> }
>
> static const struct reset_control_ops scmi_reset_ops = {
> @@ -91,9 +93,13 @@ static int scmi_reset_probe(struct scmi_device *sdev)
> struct device_node *np = dev->of_node;
> const struct scmi_handle *handle = sdev->handle;
>
> - if (!handle || !handle->reset_ops)
> + if (!handle)
> return -ENODEV;
>
> + reset_ops = handle->get_ops(handle, SCMI_PROTOCOL_RESET);
> + if (IS_ERR(reset_ops))
> + return PTR_ERR(reset_ops);
> +
> data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> if (!data)
> return -ENOMEM;
> @@ -101,12 +107,19 @@ 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->rcdev.nr_resets = reset_ops->num_domains_get(handle);
> data->handle = handle;
>
> return devm_reset_controller_register(dev, &data->rcdev);
> }
>
> +static void scmi_reset_remove(struct scmi_device *sdev)
> +{
> + const struct scmi_handle *handle = sdev->handle;
> +
> + handle->put_ops(handle, SCMI_PROTOCOL_RESET);
> +}
> +
> static const struct scmi_device_id scmi_id_table[] = {
> { SCMI_PROTOCOL_RESET, "reset" },
> { },
> @@ -116,6 +129,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> static struct scmi_driver scmi_reset_driver = {
> .name = "scmi-reset",
> .probe = scmi_reset_probe,
> + .remove = scmi_reset_remove,
> .id_table = scmi_id_table,
> };
> module_scmi_driver(scmi_reset_driver);
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index bc4f06d46bfb..bfe7017cff19 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -257,11 +257,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
> * @notify_ops: pointer to set of notifications related operations
> * @notify_priv: pointer to private data structure specific to notifications
> * (for internal use only)
> @@ -269,11 +264,11 @@ struct scmi_notify_ops {
> 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 *(*get_ops)(const struct scmi_handle *handle,
> + u8 proto);
> + void (*put_ops)(const struct scmi_handle *handle, u8 proto);
> +
> const struct scmi_notify_ops *notify_ops;
> void *notify_priv;
> };
>
On Tue, Oct 20, 2020 at 10:47:10PM -0400, Thara Gopinath wrote:
>
>
> On 10/14/20 11:05 AM, Cristian Marussi wrote:
> > Introduce generic get_ops/put_ops handle operations: any protocol, both
> > standard or custom, now exposes its operations through this common
> > interface which internally takes care to account for protocols' usage:
> > protocols' initialization is now performed on demand as soon as the first
> > user shows up while deinitialization (if any) is performed once
> > the last user of a protocol has gone.
> > Registered events' notifier are tracked too against the related protocol.
> > Convert all SCMI drivers to the new interface too.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
>
> [...]
>
>
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index bad1d0130e96..049220efd227 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -585,7 +585,7 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
> > * 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;
> > @@ -655,7 +655,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 IS_ERR(scmi_get_protocol_instance(handle, protocol_id));
> > }
> > @@ -668,7 +668,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;
> > @@ -700,6 +700,29 @@ 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.
> > + *
> > + * Get hold of a protocol accounting for its usage, eventually triggering its
> > + * initialization, and returning the protocol specific operations.
> > + *
> > + * 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(const struct scmi_handle *handle, u8 protocol_id)
> > +{
> > + struct scmi_protocol_instance *pi;
> > +
> > + pi = scmi_get_protocol_instance(handle, protocol_id);
> > + if (IS_ERR(pi))
> > + return pi;
> > +
> > + return pi->proto->ops;
> > +} > +
> > void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> > u8 *prot_imp)
> > {
> > @@ -975,6 +998,8 @@ static int scmi_probe(struct platform_device *pdev)
> > handle = &info->handle;
> > handle->dev = info->dev;
> > handle->version = &info->version;
> > + handle->get_ops = scmi_get_protocol_operations;
> > + handle->put_ops = scmi_release_protocol;
>
>
> Why do you need get_ops and put_ops? Why not have the drivers call
> scmi_acquire_protocol and scmi_release_protocol directly and get the ops
> from retrieved scmi_get_protocol_instance ? IMHO, this makes it more
> readable. Also, this will make the usage of scmi_acquire_protocol and
> scmi_release_protocol more consistent. Right now, notify.c uses
> scmi_acquire_protocol to acquire protocol because there is no need for ops
> and other drivers use get_ops to acquire protocol. Kind of confusing..
>
Trying to avoid exporting new synbols if not strictly needed, I exposed
get_ops()/put_ops() via handle for the usage of the SCMI drivers users,
while keeping scmi_acquire/release as internal non-exported wrappers used
only by the SCMI core itself like notifications.
You cannot call acquire/release from a loadable module as of now.
Additionally I thougt to add these wrappers for cases in which like
notifications you don't need the ops really (like notif or base) nor
the related forced __must_check(like notif), but just to get hold of
the protocol to avoid it being possibly unloaded.
I would antyway keep the get_ops/put_ops and I could just drop
acquire/release if confusing and use the raw ops methods also internally,
properly checking for the result everytime: currently notifications core
takes care to acquire a protocol only once the requested event has been
registered by some protocol (i.e. event handler is NOT pending) so that
I do not trigger a protocol initialization when registering a notifier
against a still unknown event: as a consequence acquire/release when
called in the notif context cannot fail, so I don;t check.
I'll try to simplify this though.
Regards
Cristian
> --
> Warm Regards
> Thara
>
> > 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..02b00af9b08f 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);
> > }
> > /**
> > diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
> > index 13e215f359fb..bd9cb2583557 100644
> > --- a/drivers/firmware/arm_scmi/perf.c
> > +++ b/drivers/firmware/arm_scmi/perf.c
> > @@ -857,7 +857,7 @@ static const struct scmi_event_ops perf_event_ops = {
> > .fill_custom_report = scmi_perf_fill_custom_report,
> > };
> > -static int scmi_perf_protocol_init(struct scmi_handle *handle)
> > +static int scmi_perf_protocol_init(const struct scmi_handle *handle)
> > {
> > int domain;
> > u32 version;
> > @@ -896,7 +896,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
> > pinfo->num_domains);
> > pinfo->version = version;
> > - handle->perf_ops = &perf_ops;
> > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
> > }
> > diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
> > index e0b29ed4e09a..1e026b5530a7 100644
> > --- a/drivers/firmware/arm_scmi/power.c
> > +++ b/drivers/firmware/arm_scmi/power.c
> > @@ -262,7 +262,7 @@ static const struct scmi_event_ops power_event_ops = {
> > .fill_custom_report = scmi_power_fill_custom_report,
> > };
> > -static int scmi_power_protocol_init(struct scmi_handle *handle)
> > +static int scmi_power_protocol_init(const struct scmi_handle *handle)
> > {
> > int domain;
> > u32 version;
> > @@ -297,7 +297,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
> > pinfo->num_domains);
> > pinfo->version = version;
> > - handle->power_ops = &power_ops;
> > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
> > }
> > diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
> > index f70e9b5108d5..b7da4de0e56e 100644
> > --- a/drivers/firmware/arm_scmi/reset.c
> > +++ b/drivers/firmware/arm_scmi/reset.c
> > @@ -274,7 +274,7 @@ static const struct scmi_event_ops reset_event_ops = {
> > .fill_custom_report = scmi_reset_fill_custom_report,
> > };
> > -static int scmi_reset_protocol_init(struct scmi_handle *handle)
> > +static int scmi_reset_protocol_init(const struct scmi_handle *handle)
> > {
> > int domain;
> > u32 version;
> > @@ -309,7 +309,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
> > pinfo->num_domains);
> > pinfo->version = version;
> > - handle->reset_ops = &reset_ops;
> > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
> > }
> > diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
> > index 9e44479f0284..bfea56f77890 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,6 +11,8 @@
> > #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;
> > @@ -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->handle, pd->domain, state);
> > if (!ret)
> > - ret = ops->state_get(pd->handle, pd->domain, &ret_state);
> > + ret = power_ops->state_get(pd->handle, pd->domain, &ret_state);
> > if (!ret && state != ret_state)
> > return -EIO;
> > @@ -61,10 +62,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> > struct generic_pm_domain **domains;
> > const struct scmi_handle *handle = sdev->handle;
> > - if (!handle || !handle->power_ops)
> > + if (!handle)
> > return -ENODEV;
> > - num_domains = handle->power_ops->num_domains_get(handle);
> > + power_ops = handle->get_ops(handle, SCMI_PROTOCOL_POWER);
> > + if (IS_ERR(power_ops))
> > + return PTR_ERR(power_ops);
> > +
> > + num_domains = power_ops->num_domains_get(handle);
> > if (num_domains < 0) {
> > dev_err(dev, "number of domains not found\n");
> > return num_domains;
> > @@ -85,14 +90,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(handle, 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->name = power_ops->name_get(handle, 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;
> > @@ -111,6 +116,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> > return 0;
> > }
> > +static void scmi_pm_domain_remove(struct scmi_device *sdev)
> > +{
> > + const struct scmi_handle *handle = sdev->handle;
> > +
> > + handle->put_ops(handle, SCMI_PROTOCOL_POWER);
> > +}
> > +
> > static const struct scmi_device_id scmi_id_table[] = {
> > { SCMI_PROTOCOL_POWER, "genpd" },
> > { },
> > @@ -120,6 +132,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > static struct scmi_driver scmi_power_domain_driver = {
> > .name = "scmi-power-domain",
> > .probe = scmi_pm_domain_probe,
> > + .remove = scmi_pm_domain_remove,
> > .id_table = scmi_id_table,
> > };
> > module_scmi_driver(scmi_power_domain_driver);
> > diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
> > index 8a0a599558ba..e0129dcd322f 100644
> > --- a/drivers/firmware/arm_scmi/sensors.c
> > +++ b/drivers/firmware/arm_scmi/sensors.c
> > @@ -334,7 +334,7 @@ static const struct scmi_event_ops sensor_event_ops = {
> > .fill_custom_report = scmi_sensor_fill_custom_report,
> > };
> > -static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> > +static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
> > {
> > u32 version;
> > struct sensors_info *sinfo;
> > @@ -364,7 +364,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> > sinfo->num_sensors);
> > sinfo->version = version;
> > - handle->sensor_ops = &sensor_ops;
> > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
> > }
> > diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
> > index 8f53f93c63ca..30e3510c1f07 100644
> > --- a/drivers/firmware/arm_scmi/system.c
> > +++ b/drivers/firmware/arm_scmi/system.c
> > @@ -101,7 +101,7 @@ static const struct scmi_event_ops system_event_ops = {
> > .fill_custom_report = scmi_system_fill_custom_report,
> > };
> > -static int scmi_system_protocol_init(struct scmi_handle *handle)
> > +static int scmi_system_protocol_init(const struct scmi_handle *handle)
> > {
> > u32 version;
> > struct scmi_system_info *pinfo;
> > diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
> > index d421e691318b..27ef71996a15 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 <[email protected]>
> > */
> > @@ -13,6 +13,8 @@
> > #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_sensor_info **info[hwmon_max];
> > @@ -72,7 +74,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> > 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(h, sensor->id, &value);
> > if (ret)
> > return ret;
> > @@ -170,10 +172,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > const struct hwmon_channel_info **ptr_scmi_ci;
> > const struct scmi_handle *handle = sdev->handle;
> > - if (!handle || !handle->sensor_ops)
> > + if (!handle)
> > return -ENODEV;
> > - nr_sensors = handle->sensor_ops->count_get(handle);
> > + sensor_ops = handle->get_ops(handle, SCMI_PROTOCOL_SENSOR);
> > + if (IS_ERR(sensor_ops))
> > + return PTR_ERR(sensor_ops);
> > +
> > + nr_sensors = sensor_ops->count_get(handle);
> > if (!nr_sensors)
> > return -EIO;
> > @@ -184,7 +190,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > scmi_sensors->handle = handle;
> > for (i = 0; i < nr_sensors; i++) {
> > - sensor = handle->sensor_ops->info_get(handle, i);
> > + sensor = sensor_ops->info_get(handle, 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(handle, i);
> > if (!sensor)
> > continue;
> > @@ -258,6 +264,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > return PTR_ERR_OR_ZERO(hwdev);
> > }
> > +static void scmi_hwmon_remove(struct scmi_device *sdev)
> > +{
> > + const struct scmi_handle *handle = sdev->handle;
> > +
> > + handle->put_ops(handle, SCMI_PROTOCOL_SENSOR);
> > +}
> > +
> > static const struct scmi_device_id scmi_id_table[] = {
> > { SCMI_PROTOCOL_SENSOR, "hwmon" },
> > { },
> > @@ -267,6 +280,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > static struct scmi_driver scmi_hwmon_drv = {
> > .name = "scmi-hwmon",
> > .probe = scmi_hwmon_probe,
> > + .remove = scmi_hwmon_remove,
> > .id_table = scmi_id_table,
> > };
> > module_scmi_driver(scmi_hwmon_drv);
> > diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
> > index 8d3a858e3b19..e48220dedb35 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,6 +11,8 @@
> > #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
> > @@ -39,7 +41,7 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
> > {
> > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > - return handle->reset_ops->assert(handle, id);
> > + return reset_ops->assert(handle, id);
> > }
> > /**
> > @@ -57,7 +59,7 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
> > {
> > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > - return handle->reset_ops->deassert(handle, id);
> > + return reset_ops->deassert(handle, id);
> > }
> > /**
> > @@ -75,7 +77,7 @@ scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
> > {
> > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > - return handle->reset_ops->reset(handle, id);
> > + return reset_ops->reset(handle, id);
> > }
> > static const struct reset_control_ops scmi_reset_ops = {
> > @@ -91,9 +93,13 @@ static int scmi_reset_probe(struct scmi_device *sdev)
> > struct device_node *np = dev->of_node;
> > const struct scmi_handle *handle = sdev->handle;
> > - if (!handle || !handle->reset_ops)
> > + if (!handle)
> > return -ENODEV;
> > + reset_ops = handle->get_ops(handle, SCMI_PROTOCOL_RESET);
> > + if (IS_ERR(reset_ops))
> > + return PTR_ERR(reset_ops);
> > +
> > data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> > if (!data)
> > return -ENOMEM;
> > @@ -101,12 +107,19 @@ 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->rcdev.nr_resets = reset_ops->num_domains_get(handle);
> > data->handle = handle;
> > return devm_reset_controller_register(dev, &data->rcdev);
> > }
> > +static void scmi_reset_remove(struct scmi_device *sdev)
> > +{
> > + const struct scmi_handle *handle = sdev->handle;
> > +
> > + handle->put_ops(handle, SCMI_PROTOCOL_RESET);
> > +}
> > +
> > static const struct scmi_device_id scmi_id_table[] = {
> > { SCMI_PROTOCOL_RESET, "reset" },
> > { },
> > @@ -116,6 +129,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > static struct scmi_driver scmi_reset_driver = {
> > .name = "scmi-reset",
> > .probe = scmi_reset_probe,
> > + .remove = scmi_reset_remove,
> > .id_table = scmi_id_table,
> > };
> > module_scmi_driver(scmi_reset_driver);
> > diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> > index bc4f06d46bfb..bfe7017cff19 100644
> > --- a/include/linux/scmi_protocol.h
> > +++ b/include/linux/scmi_protocol.h
> > @@ -257,11 +257,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
> > * @notify_ops: pointer to set of notifications related operations
> > * @notify_priv: pointer to private data structure specific to notifications
> > * (for internal use only)
> > @@ -269,11 +264,11 @@ struct scmi_notify_ops {
> > 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 *(*get_ops)(const struct scmi_handle *handle,
> > + u8 proto);
> > + void (*put_ops)(const struct scmi_handle *handle, u8 proto);
> > +
> > const struct scmi_notify_ops *notify_ops;
> > void *notify_priv;
> > };
> >
>
>
On Tue, Oct 20, 2020 at 10:47:28PM -0400, Thara Gopinath wrote:
>
>
> On 10/14/20 11:05 AM, Cristian Marussi wrote:
> > Modify protocol initialization callback adding a new parameter representing
> > a reference to the available xfer core operations and introduce a macro to
> > simply register with the core new protocols as loadable drivers.
> > Keep standard protocols as builtin.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
> > drivers/firmware/arm_scmi/base.c | 56 ++++++++++--------
> > drivers/firmware/arm_scmi/bus.c | 14 ++++-
> > drivers/firmware/arm_scmi/clock.c | 56 +++++++++---------
> > drivers/firmware/arm_scmi/common.h | 42 +++++++++-----
> > drivers/firmware/arm_scmi/driver.c | 50 ++++++++++------
> > drivers/firmware/arm_scmi/perf.c | 88 +++++++++++++++--------------
> > drivers/firmware/arm_scmi/power.c | 46 ++++++++-------
> > drivers/firmware/arm_scmi/reset.c | 46 ++++++++-------
> > drivers/firmware/arm_scmi/sensors.c | 52 +++++++++--------
> > drivers/firmware/arm_scmi/system.c | 16 ++++--
> > include/linux/scmi_protocol.h | 18 +++++-
> > 11 files changed, 288 insertions(+), 196 deletions(-)
> >
> > diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> > index f40821eeb103..8d7214fd2187 100644
> > --- a/drivers/firmware/arm_scmi/base.c
> > +++ b/drivers/firmware/arm_scmi/base.c
> > @@ -15,6 +15,8 @@
> > #define SCMI_BASE_NUM_SOURCES 1
> > #define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
> > +static const struct scmi_xfer_ops *ops;
>
> Minor nit. I would consider renaming ops to something more
> meaningful like xfer_ops (or anything that makes sense). ops by
> itself leads to confusion with ops in scmo_protocol and in
> scmi_protocol_events.
>
> Same suggestion for all other declarations of ops in this patch.
>
I agree, but I'm in fact reworking deeply the interface for protocols
initialization (following some idea from Florian) in order to pass down
a protocol object including the xfer_ops instead of a handle+ops like
now, so this hopefully will solve also this concern.
Thanks
Cristian
> --
> Warm Regards
> Thara
On Tue, Oct 20, 2020 at 10:49:23PM -0400, Thara Gopinath wrote:
>
>
> On 10/14/20 11:05 AM, Cristian Marussi wrote:
> > Add custom_dummy SCMI devname.
> >
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
> > drivers/firmware/arm_scmi/driver.c | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index 55df134c2338..5c39a738866a 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -993,6 +993,7 @@ static struct scmi_prot_devnames devnames[] = {
> > { SCMI_PROTOCOL_CLOCK, { "clocks" },},
> > { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > { SCMI_PROTOCOL_RESET, { "reset" },},
> > + { SCMI_PROTOCOL_CUSTOM_DUMMY, { "custom_dummy" },},
>
> Hi Cristian,
>
> Thanks for the sample dummy custom protocol and driver!
> The problem with adding scmi devname into the array is that every time a
> custom vendor protocol is added, the array has to be extended. Instead since
> the scmi spec supports the range 0x80-0xff for custom protocols, why not
> check for that range in scmi_create_protocol_devices and go ahead with
> registering the creating the protocol device via
> scmi_create_protocol_device?
>
Hi,
so this is really a good point, and in fact in some earlier (non-public)
iterations I had a mechanism to just get rid of these device tables,
thinking that if you want to enable custom protocols loading, it seemed
better to let the related devices being created dynamically at will, so
that an SCMI driver can just 'declare' its own device name and that will
be created if the corresponding protocol is found in the DT and
implemented in fw.
Anyway this complicated the code a lot in some dubious ways.
In a built-in scenario you end up with your driver being probe before the
platform SCMI driver, so you cannot create the device straight away in
your driver (there's not even an SCMI bus still) and you anyway need the
platform SCMI driver to be up and running to check the DT and initialize
basic transport to talk to the fw and check the protocol is supported by
fw before creating the device itself: so I ended up basically having the
SCMI driver just 'requesting' some device name to the core and then having
the core creating the device later on when the SCMI platform was probed
iff the DT and the fw supported that protocol (or immediately if your
driver was a module and the SCMI platform was already initialized)
All of the above, even if working, led to a lot of machinery to track all
these requested devices properly and properly create/destroy them, and
also it does not seem the right thing to do, since it's basically
mimicing/duplicating all the usual probe deferring standard mechanism.
Maybe this could have been addressed in different ways but I've not
explored further.
So at the end I removed such dynamic device creation support from this
series.
Now you proposal would be, if I understood correctly, to just create
straight away a custom device whenever its protocol is defined in the DT
and supported by fw, so that the custom driver above would not have to
declare anything statically, and it will just be associated with some
"dev_proto_99" matching just on protocol number.
I'd like this option because it simplifies a lot the above issues, but
I don't think it is viable because in this way you are no more able to
define 2 distinct SCMI drivers for the same protocol (like you
can do now defining multiple names in the match table: as an example you
could not create a different "custom_dummy_2" SCMI driver using the
custom protocol 0x99, because there;s only one single "dev_proto_99"
device created and already probed for "custom_dummy".
So the problem is again that if you want to support multiple SCMI
drivers they have to be able to declare their own devname, against which
the platform SCMI driver can match and initialized if needed the
underlying device.
In short, I want certainly to explore the dynamic device creation
further, but for the moment I put it apart trying to consolidate
all the rest.
Maybe I could re-introduce something better later on in future versions
of this series, or maybe just address this a distinct series later on.
Sorry for the flood-style email :D
Thanks
Cristian
>
> > };
> > static inline void
> >
>
> --
> Warm Regards
> Thara
On 10/21/20 7:35 AM, Cristian Marussi wrote:
> On Tue, Oct 20, 2020 at 10:49:23PM -0400, Thara Gopinath wrote:
>>
>>
>> On 10/14/20 11:05 AM, Cristian Marussi wrote:
>>> Add custom_dummy SCMI devname.
>>>
>>> Signed-off-by: Cristian Marussi <[email protected]>
>>> ---
>>> drivers/firmware/arm_scmi/driver.c | 1 +
>>> 1 file changed, 1 insertion(+)
>>>
>>> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
>>> index 55df134c2338..5c39a738866a 100644
>>> --- a/drivers/firmware/arm_scmi/driver.c
>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>> @@ -993,6 +993,7 @@ static struct scmi_prot_devnames devnames[] = {
>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>> + { SCMI_PROTOCOL_CUSTOM_DUMMY, { "custom_dummy" },},
>>
>> Hi Cristian,
>>
>> Thanks for the sample dummy custom protocol and driver!
>> The problem with adding scmi devname into the array is that every time a
>> custom vendor protocol is added, the array has to be extended. Instead since
>> the scmi spec supports the range 0x80-0xff for custom protocols, why not
>> check for that range in scmi_create_protocol_devices and go ahead with
>> registering the creating the protocol device via
>> scmi_create_protocol_device?
>>
>
> Hi,
>
> so this is really a good point, and in fact in some earlier (non-public)
> iterations I had a mechanism to just get rid of these device tables,
> thinking that if you want to enable custom protocols loading, it seemed
> better to let the related devices being created dynamically at will, so
> that an SCMI driver can just 'declare' its own device name and that will
> be created if the corresponding protocol is found in the DT and
> implemented in fw.
>
> Anyway this complicated the code a lot in some dubious ways.
>
> In a built-in scenario you end up with your driver being probe before the
> platform SCMI driver, so you cannot create the device straight away in
> your driver (there's not even an SCMI bus still) and you anyway need the
> platform SCMI driver to be up and running to check the DT and initialize
> basic transport to talk to the fw and check the protocol is supported by
> fw before creating the device itself: so I ended up basically having the
> SCMI driver just 'requesting' some device name to the core and then having
> the core creating the device later on when the SCMI platform was probed
> iff the DT and the fw supported that protocol (or immediately if your
> driver was a module and the SCMI platform was already initialized)
>
> All of the above, even if working, led to a lot of machinery to track all
> these requested devices properly and properly create/destroy them, and
> also it does not seem the right thing to do, since it's basically
> mimicing/duplicating all the usual probe deferring standard mechanism.
>
> Maybe this could have been addressed in different ways but I've not
> explored further.
>
> So at the end I removed such dynamic device creation support from this
> series.
>
> Now you proposal would be, if I understood correctly, to just create
> straight away a custom device whenever its protocol is defined in the DT
> and supported by fw, so that the custom driver above would not have to
> declare anything statically, and it will just be associated with some
> "dev_proto_99" matching just on protocol number.
>
> I'd like this option because it simplifies a lot the above issues, but
> I don't think it is viable because in this way you are no more able to
> define 2 distinct SCMI drivers for the same protocol (like you
> can do now defining multiple names in the match table: as an example you
> could not create a different "custom_dummy_2" SCMI driver using the
> custom protocol 0x99, because there;s only one single "dev_proto_99"
> device created and already probed for "custom_dummy".
Hi,
Apologies for the delay in this reply as it took me a while to figure
out in the code what is happening around this. So if I understand you
correctly, the table is required to support multiple drivers (which is
today 2) for a protocol which in turn means creating devices for these
drivers. But drivers/firmware/arm_scmi/driver.c is the wrong place to
define these dev names. Like any other device/driver pair in the kernel
, why is this info not conveyed from DT ?
It should be fairly straightforward to have scmi_dev_match_id to match
the device compatible string passed via dt node with a driver that is
attached to scmi_bus.
Now, the questions is, is there any existing implementation that
requires 2 separate devices with 2 separate drivers for a protocol? I am
using 2 here because MAX_SCMI_DEV_PER_PROTOCOL is defined as 2 today in
the framework. If not, I will just drop this and just create a device
for every protocol that gets conveyed from DT. If yes, I will look at
passing that info from DT either as a compatible string or as a number
so that you can dynamically build the name when creating device.
The problem with this table is every time anyone wants to support a new
driver, the table has to be updated. Ideally, framework files should not
require any modification to support an extension protocol.
>
> So the problem is again that if you want to support multiple SCMI
> drivers they have to be able to declare their own devname, against which
> the platform SCMI driver can match and initialized if needed the
> underlying device.
>
> In short, I want certainly to explore the dynamic device creation
> further, but for the moment I put it apart trying to consolidate
> all the rest.
>
> Maybe I could re-introduce something better later on in future versions
> of this series, or maybe just address this a distinct series later on.
>
> Sorry for the flood-style email :D
No issues!
>
> Thanks
>
> Cristian
>
>>
>>> };
>>> static inline void
>>>
>>
>> --
>> Warm Regards
>> Thara
--
Warm Regards
Thara
On 10/21/20 6:27 AM, Cristian Marussi wrote:
> On Tue, Oct 20, 2020 at 10:47:10PM -0400, Thara Gopinath wrote:
>>
>>
>> On 10/14/20 11:05 AM, Cristian Marussi wrote:
>>> Introduce generic get_ops/put_ops handle operations: any protocol, both
>>> standard or custom, now exposes its operations through this common
>>> interface which internally takes care to account for protocols' usage:
>>> protocols' initialization is now performed on demand as soon as the first
>>> user shows up while deinitialization (if any) is performed once
>>> the last user of a protocol has gone.
>>> Registered events' notifier are tracked too against the related protocol.
>>> Convert all SCMI drivers to the new interface too.
>>>
>>> Signed-off-by: Cristian Marussi <[email protected]>
>>> ---
>>
>> [...]
>>
>>
>>> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
>>> index bad1d0130e96..049220efd227 100644
>>> --- a/drivers/firmware/arm_scmi/driver.c
>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>> @@ -585,7 +585,7 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
>>> * 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;
>>> @@ -655,7 +655,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 IS_ERR(scmi_get_protocol_instance(handle, protocol_id));
>>> }
>>> @@ -668,7 +668,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;
>>> @@ -700,6 +700,29 @@ 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.
>>> + *
>>> + * Get hold of a protocol accounting for its usage, eventually triggering its
>>> + * initialization, and returning the protocol specific operations.
>>> + *
>>> + * 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(const struct scmi_handle *handle, u8 protocol_id)
>>> +{
>>> + struct scmi_protocol_instance *pi;
>>> +
>>> + pi = scmi_get_protocol_instance(handle, protocol_id);
>>> + if (IS_ERR(pi))
>>> + return pi;
>>> +
>>> + return pi->proto->ops;
>>> +} > +
>>> void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
>>> u8 *prot_imp)
>>> {
>>> @@ -975,6 +998,8 @@ static int scmi_probe(struct platform_device *pdev)
>>> handle = &info->handle;
>>> handle->dev = info->dev;
>>> handle->version = &info->version;
>>> + handle->get_ops = scmi_get_protocol_operations;
>>> + handle->put_ops = scmi_release_protocol;
>>
>>
>> Why do you need get_ops and put_ops? Why not have the drivers call
>> scmi_acquire_protocol and scmi_release_protocol directly and get the ops
>> from retrieved scmi_get_protocol_instance ? IMHO, this makes it more
>> readable. Also, this will make the usage of scmi_acquire_protocol and
>> scmi_release_protocol more consistent. Right now, notify.c uses
>> scmi_acquire_protocol to acquire protocol because there is no need for ops
>> and other drivers use get_ops to acquire protocol. Kind of confusing..
>>
>
> Trying to avoid exporting new synbols if not strictly needed, I exposed
> get_ops()/put_ops() via handle for the usage of the SCMI drivers users,
> while keeping scmi_acquire/release as internal non-exported wrappers used
> only by the SCMI core itself like notifications.
> You cannot call acquire/release from a loadable module as of now.
>
> Additionally I thougt to add these wrappers for cases in which like
> notifications you don't need the ops really (like notif or base) nor
> the related forced __must_check(like notif), but just to get hold of
> the protocol to avoid it being possibly unloaded.
>
> I would antyway keep the get_ops/put_ops and I could just drop
> acquire/release if confusing and use the raw ops methods also internally,
> properly checking for the result everytime: currently notifications core
> takes care to acquire a protocol only once the requested event has been
> registered by some protocol (i.e. event handler is NOT pending) so that
> I do not trigger a protocol initialization when registering a notifier
> against a still unknown event: as a consequence acquire/release when
> called in the notif context cannot fail, so I don;t check.
>
> I'll try to simplify this though.
I think a standard interface is better. So either a get_ops/put_ops or
acquire_release that can be called by all users/drivers of a protocol
will standardize it. I will wait for your next version.
>
> Regards
>
> Cristian
>
>> --
>> Warm Regards
>> Thara
>>
>>> 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..02b00af9b08f 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);
>>> }
>>> /**
>>> diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
>>> index 13e215f359fb..bd9cb2583557 100644
>>> --- a/drivers/firmware/arm_scmi/perf.c
>>> +++ b/drivers/firmware/arm_scmi/perf.c
>>> @@ -857,7 +857,7 @@ static const struct scmi_event_ops perf_event_ops = {
>>> .fill_custom_report = scmi_perf_fill_custom_report,
>>> };
>>> -static int scmi_perf_protocol_init(struct scmi_handle *handle)
>>> +static int scmi_perf_protocol_init(const struct scmi_handle *handle)
>>> {
>>> int domain;
>>> u32 version;
>>> @@ -896,7 +896,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
>>> pinfo->num_domains);
>>> pinfo->version = version;
>>> - handle->perf_ops = &perf_ops;
>>> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
>>> }
>>> diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
>>> index e0b29ed4e09a..1e026b5530a7 100644
>>> --- a/drivers/firmware/arm_scmi/power.c
>>> +++ b/drivers/firmware/arm_scmi/power.c
>>> @@ -262,7 +262,7 @@ static const struct scmi_event_ops power_event_ops = {
>>> .fill_custom_report = scmi_power_fill_custom_report,
>>> };
>>> -static int scmi_power_protocol_init(struct scmi_handle *handle)
>>> +static int scmi_power_protocol_init(const struct scmi_handle *handle)
>>> {
>>> int domain;
>>> u32 version;
>>> @@ -297,7 +297,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
>>> pinfo->num_domains);
>>> pinfo->version = version;
>>> - handle->power_ops = &power_ops;
>>> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
>>> }
>>> diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
>>> index f70e9b5108d5..b7da4de0e56e 100644
>>> --- a/drivers/firmware/arm_scmi/reset.c
>>> +++ b/drivers/firmware/arm_scmi/reset.c
>>> @@ -274,7 +274,7 @@ static const struct scmi_event_ops reset_event_ops = {
>>> .fill_custom_report = scmi_reset_fill_custom_report,
>>> };
>>> -static int scmi_reset_protocol_init(struct scmi_handle *handle)
>>> +static int scmi_reset_protocol_init(const struct scmi_handle *handle)
>>> {
>>> int domain;
>>> u32 version;
>>> @@ -309,7 +309,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
>>> pinfo->num_domains);
>>> pinfo->version = version;
>>> - handle->reset_ops = &reset_ops;
>>> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
>>> }
>>> diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
>>> index 9e44479f0284..bfea56f77890 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,6 +11,8 @@
>>> #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;
>>> @@ -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->handle, pd->domain, state);
>>> if (!ret)
>>> - ret = ops->state_get(pd->handle, pd->domain, &ret_state);
>>> + ret = power_ops->state_get(pd->handle, pd->domain, &ret_state);
>>> if (!ret && state != ret_state)
>>> return -EIO;
>>> @@ -61,10 +62,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
>>> struct generic_pm_domain **domains;
>>> const struct scmi_handle *handle = sdev->handle;
>>> - if (!handle || !handle->power_ops)
>>> + if (!handle)
>>> return -ENODEV;
>>> - num_domains = handle->power_ops->num_domains_get(handle);
>>> + power_ops = handle->get_ops(handle, SCMI_PROTOCOL_POWER);
>>> + if (IS_ERR(power_ops))
>>> + return PTR_ERR(power_ops);
>>> +
>>> + num_domains = power_ops->num_domains_get(handle);
>>> if (num_domains < 0) {
>>> dev_err(dev, "number of domains not found\n");
>>> return num_domains;
>>> @@ -85,14 +90,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(handle, 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->name = power_ops->name_get(handle, 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;
>>> @@ -111,6 +116,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
>>> return 0;
>>> }
>>> +static void scmi_pm_domain_remove(struct scmi_device *sdev)
>>> +{
>>> + const struct scmi_handle *handle = sdev->handle;
>>> +
>>> + handle->put_ops(handle, SCMI_PROTOCOL_POWER);
>>> +}
>>> +
>>> static const struct scmi_device_id scmi_id_table[] = {
>>> { SCMI_PROTOCOL_POWER, "genpd" },
>>> { },
>>> @@ -120,6 +132,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
>>> static struct scmi_driver scmi_power_domain_driver = {
>>> .name = "scmi-power-domain",
>>> .probe = scmi_pm_domain_probe,
>>> + .remove = scmi_pm_domain_remove,
>>> .id_table = scmi_id_table,
>>> };
>>> module_scmi_driver(scmi_power_domain_driver);
>>> diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
>>> index 8a0a599558ba..e0129dcd322f 100644
>>> --- a/drivers/firmware/arm_scmi/sensors.c
>>> +++ b/drivers/firmware/arm_scmi/sensors.c
>>> @@ -334,7 +334,7 @@ static const struct scmi_event_ops sensor_event_ops = {
>>> .fill_custom_report = scmi_sensor_fill_custom_report,
>>> };
>>> -static int scmi_sensors_protocol_init(struct scmi_handle *handle)
>>> +static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
>>> {
>>> u32 version;
>>> struct sensors_info *sinfo;
>>> @@ -364,7 +364,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
>>> sinfo->num_sensors);
>>> sinfo->version = version;
>>> - handle->sensor_ops = &sensor_ops;
>>> return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
>>> }
>>> diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
>>> index 8f53f93c63ca..30e3510c1f07 100644
>>> --- a/drivers/firmware/arm_scmi/system.c
>>> +++ b/drivers/firmware/arm_scmi/system.c
>>> @@ -101,7 +101,7 @@ static const struct scmi_event_ops system_event_ops = {
>>> .fill_custom_report = scmi_system_fill_custom_report,
>>> };
>>> -static int scmi_system_protocol_init(struct scmi_handle *handle)
>>> +static int scmi_system_protocol_init(const struct scmi_handle *handle)
>>> {
>>> u32 version;
>>> struct scmi_system_info *pinfo;
>>> diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
>>> index d421e691318b..27ef71996a15 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 <[email protected]>
>>> */
>>> @@ -13,6 +13,8 @@
>>> #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_sensor_info **info[hwmon_max];
>>> @@ -72,7 +74,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
>>> 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(h, sensor->id, &value);
>>> if (ret)
>>> return ret;
>>> @@ -170,10 +172,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
>>> const struct hwmon_channel_info **ptr_scmi_ci;
>>> const struct scmi_handle *handle = sdev->handle;
>>> - if (!handle || !handle->sensor_ops)
>>> + if (!handle)
>>> return -ENODEV;
>>> - nr_sensors = handle->sensor_ops->count_get(handle);
>>> + sensor_ops = handle->get_ops(handle, SCMI_PROTOCOL_SENSOR);
>>> + if (IS_ERR(sensor_ops))
>>> + return PTR_ERR(sensor_ops);
>>> +
>>> + nr_sensors = sensor_ops->count_get(handle);
>>> if (!nr_sensors)
>>> return -EIO;
>>> @@ -184,7 +190,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
>>> scmi_sensors->handle = handle;
>>> for (i = 0; i < nr_sensors; i++) {
>>> - sensor = handle->sensor_ops->info_get(handle, i);
>>> + sensor = sensor_ops->info_get(handle, 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(handle, i);
>>> if (!sensor)
>>> continue;
>>> @@ -258,6 +264,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
>>> return PTR_ERR_OR_ZERO(hwdev);
>>> }
>>> +static void scmi_hwmon_remove(struct scmi_device *sdev)
>>> +{
>>> + const struct scmi_handle *handle = sdev->handle;
>>> +
>>> + handle->put_ops(handle, SCMI_PROTOCOL_SENSOR);
>>> +}
>>> +
>>> static const struct scmi_device_id scmi_id_table[] = {
>>> { SCMI_PROTOCOL_SENSOR, "hwmon" },
>>> { },
>>> @@ -267,6 +280,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
>>> static struct scmi_driver scmi_hwmon_drv = {
>>> .name = "scmi-hwmon",
>>> .probe = scmi_hwmon_probe,
>>> + .remove = scmi_hwmon_remove,
>>> .id_table = scmi_id_table,
>>> };
>>> module_scmi_driver(scmi_hwmon_drv);
>>> diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
>>> index 8d3a858e3b19..e48220dedb35 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,6 +11,8 @@
>>> #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
>>> @@ -39,7 +41,7 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
>>> {
>>> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>>> - return handle->reset_ops->assert(handle, id);
>>> + return reset_ops->assert(handle, id);
>>> }
>>> /**
>>> @@ -57,7 +59,7 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
>>> {
>>> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>>> - return handle->reset_ops->deassert(handle, id);
>>> + return reset_ops->deassert(handle, id);
>>> }
>>> /**
>>> @@ -75,7 +77,7 @@ scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
>>> {
>>> const struct scmi_handle *handle = to_scmi_handle(rcdev);
>>> - return handle->reset_ops->reset(handle, id);
>>> + return reset_ops->reset(handle, id);
>>> }
>>> static const struct reset_control_ops scmi_reset_ops = {
>>> @@ -91,9 +93,13 @@ static int scmi_reset_probe(struct scmi_device *sdev)
>>> struct device_node *np = dev->of_node;
>>> const struct scmi_handle *handle = sdev->handle;
>>> - if (!handle || !handle->reset_ops)
>>> + if (!handle)
>>> return -ENODEV;
>>> + reset_ops = handle->get_ops(handle, SCMI_PROTOCOL_RESET);
>>> + if (IS_ERR(reset_ops))
>>> + return PTR_ERR(reset_ops);
>>> +
>>> data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>> if (!data)
>>> return -ENOMEM;
>>> @@ -101,12 +107,19 @@ 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->rcdev.nr_resets = reset_ops->num_domains_get(handle);
>>> data->handle = handle;
>>> return devm_reset_controller_register(dev, &data->rcdev);
>>> }
>>> +static void scmi_reset_remove(struct scmi_device *sdev)
>>> +{
>>> + const struct scmi_handle *handle = sdev->handle;
>>> +
>>> + handle->put_ops(handle, SCMI_PROTOCOL_RESET);
>>> +}
>>> +
>>> static const struct scmi_device_id scmi_id_table[] = {
>>> { SCMI_PROTOCOL_RESET, "reset" },
>>> { },
>>> @@ -116,6 +129,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
>>> static struct scmi_driver scmi_reset_driver = {
>>> .name = "scmi-reset",
>>> .probe = scmi_reset_probe,
>>> + .remove = scmi_reset_remove,
>>> .id_table = scmi_id_table,
>>> };
>>> module_scmi_driver(scmi_reset_driver);
>>> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
>>> index bc4f06d46bfb..bfe7017cff19 100644
>>> --- a/include/linux/scmi_protocol.h
>>> +++ b/include/linux/scmi_protocol.h
>>> @@ -257,11 +257,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
>>> * @notify_ops: pointer to set of notifications related operations
>>> * @notify_priv: pointer to private data structure specific to notifications
>>> * (for internal use only)
>>> @@ -269,11 +264,11 @@ struct scmi_notify_ops {
>>> 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 *(*get_ops)(const struct scmi_handle *handle,
>>> + u8 proto);
>>> + void (*put_ops)(const struct scmi_handle *handle, u8 proto);
>>> +
>>> const struct scmi_notify_ops *notify_ops;
>>> void *notify_priv;
>>> };
>>>
>>
>>
--
Warm Regards
Thara
Hi
On Mon, Oct 26, 2020 at 08:37:46AM -0400, Thara Gopinath wrote:
>
>
> On 10/21/20 7:35 AM, Cristian Marussi wrote:
> > On Tue, Oct 20, 2020 at 10:49:23PM -0400, Thara Gopinath wrote:
> > >
> > >
> > > On 10/14/20 11:05 AM, Cristian Marussi wrote:
> > > > Add custom_dummy SCMI devname.
> > > >
> > > > Signed-off-by: Cristian Marussi <[email protected]>
> > > > ---
> > > > drivers/firmware/arm_scmi/driver.c | 1 +
> > > > 1 file changed, 1 insertion(+)
> > > >
> > > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > > index 55df134c2338..5c39a738866a 100644
> > > > --- a/drivers/firmware/arm_scmi/driver.c
> > > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > > @@ -993,6 +993,7 @@ static struct scmi_prot_devnames devnames[] = {
> > > > { SCMI_PROTOCOL_CLOCK, { "clocks" },},
> > > > { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > > > { SCMI_PROTOCOL_RESET, { "reset" },},
> > > > + { SCMI_PROTOCOL_CUSTOM_DUMMY, { "custom_dummy" },},
> > >
> > > Hi Cristian,
> > >
> > > Thanks for the sample dummy custom protocol and driver!
> > > The problem with adding scmi devname into the array is that every time a
> > > custom vendor protocol is added, the array has to be extended. Instead since
> > > the scmi spec supports the range 0x80-0xff for custom protocols, why not
> > > check for that range in scmi_create_protocol_devices and go ahead with
> > > registering the creating the protocol device via
> > > scmi_create_protocol_device?
> > >
> >
> > Hi,
> >
> > so this is really a good point, and in fact in some earlier (non-public)
> > iterations I had a mechanism to just get rid of these device tables,
> > thinking that if you want to enable custom protocols loading, it seemed
> > better to let the related devices being created dynamically at will, so
> > that an SCMI driver can just 'declare' its own device name and that will
> > be created if the corresponding protocol is found in the DT and
> > implemented in fw.
> >
> > Anyway this complicated the code a lot in some dubious ways.
> >
> > In a built-in scenario you end up with your driver being probe before the
> > platform SCMI driver, so you cannot create the device straight away in
> > your driver (there's not even an SCMI bus still) and you anyway need the
> > platform SCMI driver to be up and running to check the DT and initialize
> > basic transport to talk to the fw and check the protocol is supported by
> > fw before creating the device itself: so I ended up basically having the
> > SCMI driver just 'requesting' some device name to the core and then having
> > the core creating the device later on when the SCMI platform was probed
> > iff the DT and the fw supported that protocol (or immediately if your
> > driver was a module and the SCMI platform was already initialized)
> >
> > All of the above, even if working, led to a lot of machinery to track all
> > these requested devices properly and properly create/destroy them, and
> > also it does not seem the right thing to do, since it's basically
> > mimicing/duplicating all the usual probe deferring standard mechanism.
> >
> > Maybe this could have been addressed in different ways but I've not
> > explored further.
> >
> > So at the end I removed such dynamic device creation support from this
> > series.
> >
> > Now you proposal would be, if I understood correctly, to just create
> > straight away a custom device whenever its protocol is defined in the DT
> > and supported by fw, so that the custom driver above would not have to
> > declare anything statically, and it will just be associated with some
> > "dev_proto_99" matching just on protocol number.
> >
> > I'd like this option because it simplifies a lot the above issues, but
> > I don't think it is viable because in this way you are no more able to
> > define 2 distinct SCMI drivers for the same protocol (like you
> > can do now defining multiple names in the match table: as an example you
> > could not create a different "custom_dummy_2" SCMI driver using the
> > custom protocol 0x99, because there;s only one single "dev_proto_99"
> > device created and already probed for "custom_dummy".
>
> Hi,
>
> Apologies for the delay in this reply as it took me a while to figure out in
> the code what is happening around this. So if I understand you correctly,
> the table is required to support multiple drivers (which is today 2) for a
> protocol which in turn means creating devices for these drivers. But
> drivers/firmware/arm_scmi/driver.c is the wrong place to define these dev
> names. Like any other device/driver pair in the kernel , why is this info
> not conveyed from DT ?
>
I suppose the original implememtation did this because the devices have to
be created as child of the SCMI bus if and only if the related protocol
is defined in the DT and the fw is determined to support such protocol
at run-time: for these reasons the device creation is delegated to the
SCMI platform driver which takes care, once is up (matches on arm,scmi),
to parse the DT and after initializing the proper transport (as described
in the DT) to check with the fw if the protocol is support by that SCMI
instance.
So the module device table is a way for the drivers to tell the core SCMI
platform driver that they exist and need a device, but the final decision
as to create them 9and having them probed after attaching to the SCMI
bus) or not is up to the SCMI platform bus driver.
Consider also that you could have multiple instance of SCMI servers and
so multple instances of your driver/devices could be needed: every SCMI
driver instance currenly creates a distinct device for each of these possible
devices (even though not so much tested probably :D)
> It should be fairly straightforward to have scmi_dev_match_id to match the
> device compatible string passed via dt node with a driver that is attached
> to scmi_bus.
>
But maybe there's a simple way to do it as you said with the DT, while
keeping the above constraints..I'll look into this for V3 (unless some
more heavy rework would be needed following reviews...)
> Now, the questions is, is there any existing implementation that requires 2
> separate devices with 2 separate drivers for a protocol? I am using 2 here
> because MAX_SCMI_DEV_PER_PROTOCOL is defined as 2 today in the framework. If
> not, I will just drop this and just create a device for every protocol that
> gets conveyed from DT. If yes, I will look at passing that info from DT
> either as a compatible string or as a number so that you can dynamically
> build the name when creating device.
There is for sure a partner that is developing a sensor SCMI driver
which is going to work aside the existing scmi-hwmon...and I think there
should be also other cases, since Sudeep added such possibility of
multiple devices per protocol well before this partner project came
along. I'll check with him.
I'll explore further DT based device generation (and also check with
Sudeep if we want really to add more DT entries...)
One more interesting thing in these regards it's that from the reqs pnt
of view, we should really not care to associate a specific protocol to
an SCMI driver, because even though a specific SCMI driver will
certainly use mainly if not only one protocol (notifications apart), it
is really not bound as of now (nor was before this series) to use a
single protocol, but it could grab multiple operations anyway.
> The problem with this table is every time anyone wants to support a new
> driver, the table has to be updated. Ideally, framework files should not
> require any modification to support an extension protocol.
>
I agree...as said I had previously a clunky solution which did not seem right
but indeed was successfully generating devices dynamically.
> >
> > So the problem is again that if you want to support multiple SCMI
> > drivers they have to be able to declare their own devname, against which
> > the platform SCMI driver can match and initialized if needed the
> > underlying device.
> >
> > In short, I want certainly to explore the dynamic device creation
> > further, but for the moment I put it apart trying to consolidate
> > all the rest.
> >
> > Maybe I could re-introduce something better later on in future versions
> > of this series, or maybe just address this a distinct series later on.
> >
> > Sorry for the flood-style email :D
>
> No issues!
>
... so I'll keep spamming you :P
Thanks
Cristian
> >
> > Thanks
> >
> > Cristian
> >
> > >
> > > > };
> > > > static inline void
> > > >
> > >
> > > --
> > > Warm Regards
> > > Thara
>
> --
> Warm Regards
> Thara
On Mon, Oct 26, 2020 at 09:07:12AM -0400, Thara Gopinath wrote:
Hi Thara,
>
>
> On 10/21/20 6:27 AM, Cristian Marussi wrote:
> > On Tue, Oct 20, 2020 at 10:47:10PM -0400, Thara Gopinath wrote:
> > >
> > >
> > > On 10/14/20 11:05 AM, Cristian Marussi wrote:
> > > > Introduce generic get_ops/put_ops handle operations: any protocol, both
> > > > standard or custom, now exposes its operations through this common
> > > > interface which internally takes care to account for protocols' usage:
> > > > protocols' initialization is now performed on demand as soon as the first
> > > > user shows up while deinitialization (if any) is performed once
> > > > the last user of a protocol has gone.
> > > > Registered events' notifier are tracked too against the related protocol.
> > > > Convert all SCMI drivers to the new interface too.
> > > >
> > > > Signed-off-by: Cristian Marussi <[email protected]>
> > > > ---
> > >
> > > [...]
> > >
> > >
> > > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > > index bad1d0130e96..049220efd227 100644
> > > > --- a/drivers/firmware/arm_scmi/driver.c
> > > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > > @@ -585,7 +585,7 @@ void *scmi_get_proto_priv(const struct scmi_handle *handle, u8 protocol_id)
> > > > * 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;
> > > > @@ -655,7 +655,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 IS_ERR(scmi_get_protocol_instance(handle, protocol_id));
> > > > }
> > > > @@ -668,7 +668,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;
> > > > @@ -700,6 +700,29 @@ 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.
> > > > + *
> > > > + * Get hold of a protocol accounting for its usage, eventually triggering its
> > > > + * initialization, and returning the protocol specific operations.
> > > > + *
> > > > + * 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(const struct scmi_handle *handle, u8 protocol_id)
> > > > +{
> > > > + struct scmi_protocol_instance *pi;
> > > > +
> > > > + pi = scmi_get_protocol_instance(handle, protocol_id);
> > > > + if (IS_ERR(pi))
> > > > + return pi;
> > > > +
> > > > + return pi->proto->ops;
> > > > +} > +
> > > > void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> > > > u8 *prot_imp)
> > > > {
> > > > @@ -975,6 +998,8 @@ static int scmi_probe(struct platform_device *pdev)
> > > > handle = &info->handle;
> > > > handle->dev = info->dev;
> > > > handle->version = &info->version;
> > > > + handle->get_ops = scmi_get_protocol_operations;
> > > > + handle->put_ops = scmi_release_protocol;
> > >
> > >
> > > Why do you need get_ops and put_ops? Why not have the drivers call
> > > scmi_acquire_protocol and scmi_release_protocol directly and get the ops
> > > from retrieved scmi_get_protocol_instance ? IMHO, this makes it more
> > > readable. Also, this will make the usage of scmi_acquire_protocol and
> > > scmi_release_protocol more consistent. Right now, notify.c uses
> > > scmi_acquire_protocol to acquire protocol because there is no need for ops
> > > and other drivers use get_ops to acquire protocol. Kind of confusing..
> > >
> >
> > Trying to avoid exporting new synbols if not strictly needed, I exposed
> > get_ops()/put_ops() via handle for the usage of the SCMI drivers users,
> > while keeping scmi_acquire/release as internal non-exported wrappers used
> > only by the SCMI core itself like notifications.
> > You cannot call acquire/release from a loadable module as of now.
> >
> > Additionally I thougt to add these wrappers for cases in which like
> > notifications you don't need the ops really (like notif or base) nor
> > the related forced __must_check(like notif), but just to get hold of
> > the protocol to avoid it being possibly unloaded.
> >
> > I would antyway keep the get_ops/put_ops and I could just drop
> > acquire/release if confusing and use the raw ops methods also internally,
> > properly checking for the result everytime: currently notifications core
> > takes care to acquire a protocol only once the requested event has been
> > registered by some protocol (i.e. event handler is NOT pending) so that
> > I do not trigger a protocol initialization when registering a notifier
> > against a still unknown event: as a consequence acquire/release when
> > called in the notif context cannot fail, so I don;t check.
> >
> > I'll try to simplify this though.
>
> I think a standard interface is better. So either a get_ops/put_ops or
> acquire_release that can be called by all users/drivers of a protocol will
> standardize it. I will wait for your next version.
The idea was to minimize the number of needed exported symbols to
maintain while keeping loadable modules working: currently any SCMI driver
in order to work has anyway to grab the SCMI instance handle which identifies
the SCMI instance that you are addressing (in the hyp that you could have
multiple SCMI server instances running on different MC).
In order to do that you define your driver module_scmi_driver() which
registers it onto the SCMI bus using scmi_driver_register() which is
indeed EXPORT_SYMBOL_GPL().
On the other side you can write a new protocol now and define it with
module_scmi_protocol() which in turn registers it with the SCMI core
using scmi_protocol_register(), which is another EXPORT_SYMBOL_GPL.
But at this point you have exported the bare minimum you need since:
- all the generic get_ops/put_ops are attached to the handle that the
SCMI driver receives mandatorily at probe time (instead of the old
perf_ops, power_ops...)
- the new (in V2) protocol handle descriptor is passed along from the
core to the protocols code so that they can be loadable modules too
and accessing all the core xfer ops without the need to export also
all of those internal message handling methods.
The acquire/release interface are indeed just internal wrappers used by
the core to grab a protocol when you don't really need the ops, and as
such are currenly not accessible from the modules sitting outside the
core.
Regards
Cristian
> >
> > Regards
> >
> > Cristian
> >
> > > --
> > > Warm Regards
> > > Thara
> > >
> > > > 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..02b00af9b08f 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);
> > > > }
> > > > /**
> > > > diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
> > > > index 13e215f359fb..bd9cb2583557 100644
> > > > --- a/drivers/firmware/arm_scmi/perf.c
> > > > +++ b/drivers/firmware/arm_scmi/perf.c
> > > > @@ -857,7 +857,7 @@ static const struct scmi_event_ops perf_event_ops = {
> > > > .fill_custom_report = scmi_perf_fill_custom_report,
> > > > };
> > > > -static int scmi_perf_protocol_init(struct scmi_handle *handle)
> > > > +static int scmi_perf_protocol_init(const struct scmi_handle *handle)
> > > > {
> > > > int domain;
> > > > u32 version;
> > > > @@ -896,7 +896,6 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
> > > > pinfo->num_domains);
> > > > pinfo->version = version;
> > > > - handle->perf_ops = &perf_ops;
> > > > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_PERF, pinfo);
> > > > }
> > > > diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
> > > > index e0b29ed4e09a..1e026b5530a7 100644
> > > > --- a/drivers/firmware/arm_scmi/power.c
> > > > +++ b/drivers/firmware/arm_scmi/power.c
> > > > @@ -262,7 +262,7 @@ static const struct scmi_event_ops power_event_ops = {
> > > > .fill_custom_report = scmi_power_fill_custom_report,
> > > > };
> > > > -static int scmi_power_protocol_init(struct scmi_handle *handle)
> > > > +static int scmi_power_protocol_init(const struct scmi_handle *handle)
> > > > {
> > > > int domain;
> > > > u32 version;
> > > > @@ -297,7 +297,6 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
> > > > pinfo->num_domains);
> > > > pinfo->version = version;
> > > > - handle->power_ops = &power_ops;
> > > > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_POWER, pinfo);
> > > > }
> > > > diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
> > > > index f70e9b5108d5..b7da4de0e56e 100644
> > > > --- a/drivers/firmware/arm_scmi/reset.c
> > > > +++ b/drivers/firmware/arm_scmi/reset.c
> > > > @@ -274,7 +274,7 @@ static const struct scmi_event_ops reset_event_ops = {
> > > > .fill_custom_report = scmi_reset_fill_custom_report,
> > > > };
> > > > -static int scmi_reset_protocol_init(struct scmi_handle *handle)
> > > > +static int scmi_reset_protocol_init(const struct scmi_handle *handle)
> > > > {
> > > > int domain;
> > > > u32 version;
> > > > @@ -309,7 +309,6 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
> > > > pinfo->num_domains);
> > > > pinfo->version = version;
> > > > - handle->reset_ops = &reset_ops;
> > > > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_RESET, pinfo);
> > > > }
> > > > diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
> > > > index 9e44479f0284..bfea56f77890 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,6 +11,8 @@
> > > > #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;
> > > > @@ -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->handle, pd->domain, state);
> > > > if (!ret)
> > > > - ret = ops->state_get(pd->handle, pd->domain, &ret_state);
> > > > + ret = power_ops->state_get(pd->handle, pd->domain, &ret_state);
> > > > if (!ret && state != ret_state)
> > > > return -EIO;
> > > > @@ -61,10 +62,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> > > > struct generic_pm_domain **domains;
> > > > const struct scmi_handle *handle = sdev->handle;
> > > > - if (!handle || !handle->power_ops)
> > > > + if (!handle)
> > > > return -ENODEV;
> > > > - num_domains = handle->power_ops->num_domains_get(handle);
> > > > + power_ops = handle->get_ops(handle, SCMI_PROTOCOL_POWER);
> > > > + if (IS_ERR(power_ops))
> > > > + return PTR_ERR(power_ops);
> > > > +
> > > > + num_domains = power_ops->num_domains_get(handle);
> > > > if (num_domains < 0) {
> > > > dev_err(dev, "number of domains not found\n");
> > > > return num_domains;
> > > > @@ -85,14 +90,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(handle, 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->name = power_ops->name_get(handle, 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;
> > > > @@ -111,6 +116,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
> > > > return 0;
> > > > }
> > > > +static void scmi_pm_domain_remove(struct scmi_device *sdev)
> > > > +{
> > > > + const struct scmi_handle *handle = sdev->handle;
> > > > +
> > > > + handle->put_ops(handle, SCMI_PROTOCOL_POWER);
> > > > +}
> > > > +
> > > > static const struct scmi_device_id scmi_id_table[] = {
> > > > { SCMI_PROTOCOL_POWER, "genpd" },
> > > > { },
> > > > @@ -120,6 +132,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > static struct scmi_driver scmi_power_domain_driver = {
> > > > .name = "scmi-power-domain",
> > > > .probe = scmi_pm_domain_probe,
> > > > + .remove = scmi_pm_domain_remove,
> > > > .id_table = scmi_id_table,
> > > > };
> > > > module_scmi_driver(scmi_power_domain_driver);
> > > > diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
> > > > index 8a0a599558ba..e0129dcd322f 100644
> > > > --- a/drivers/firmware/arm_scmi/sensors.c
> > > > +++ b/drivers/firmware/arm_scmi/sensors.c
> > > > @@ -334,7 +334,7 @@ static const struct scmi_event_ops sensor_event_ops = {
> > > > .fill_custom_report = scmi_sensor_fill_custom_report,
> > > > };
> > > > -static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> > > > +static int scmi_sensors_protocol_init(const struct scmi_handle *handle)
> > > > {
> > > > u32 version;
> > > > struct sensors_info *sinfo;
> > > > @@ -364,7 +364,6 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
> > > > sinfo->num_sensors);
> > > > sinfo->version = version;
> > > > - handle->sensor_ops = &sensor_ops;
> > > > return scmi_set_proto_priv(handle, SCMI_PROTOCOL_SENSOR, sinfo);
> > > > }
> > > > diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
> > > > index 8f53f93c63ca..30e3510c1f07 100644
> > > > --- a/drivers/firmware/arm_scmi/system.c
> > > > +++ b/drivers/firmware/arm_scmi/system.c
> > > > @@ -101,7 +101,7 @@ static const struct scmi_event_ops system_event_ops = {
> > > > .fill_custom_report = scmi_system_fill_custom_report,
> > > > };
> > > > -static int scmi_system_protocol_init(struct scmi_handle *handle)
> > > > +static int scmi_system_protocol_init(const struct scmi_handle *handle)
> > > > {
> > > > u32 version;
> > > > struct scmi_system_info *pinfo;
> > > > diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
> > > > index d421e691318b..27ef71996a15 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 <[email protected]>
> > > > */
> > > > @@ -13,6 +13,8 @@
> > > > #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_sensor_info **info[hwmon_max];
> > > > @@ -72,7 +74,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> > > > 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(h, sensor->id, &value);
> > > > if (ret)
> > > > return ret;
> > > > @@ -170,10 +172,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > > > const struct hwmon_channel_info **ptr_scmi_ci;
> > > > const struct scmi_handle *handle = sdev->handle;
> > > > - if (!handle || !handle->sensor_ops)
> > > > + if (!handle)
> > > > return -ENODEV;
> > > > - nr_sensors = handle->sensor_ops->count_get(handle);
> > > > + sensor_ops = handle->get_ops(handle, SCMI_PROTOCOL_SENSOR);
> > > > + if (IS_ERR(sensor_ops))
> > > > + return PTR_ERR(sensor_ops);
> > > > +
> > > > + nr_sensors = sensor_ops->count_get(handle);
> > > > if (!nr_sensors)
> > > > return -EIO;
> > > > @@ -184,7 +190,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > > > scmi_sensors->handle = handle;
> > > > for (i = 0; i < nr_sensors; i++) {
> > > > - sensor = handle->sensor_ops->info_get(handle, i);
> > > > + sensor = sensor_ops->info_get(handle, 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(handle, i);
> > > > if (!sensor)
> > > > continue;
> > > > @@ -258,6 +264,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
> > > > return PTR_ERR_OR_ZERO(hwdev);
> > > > }
> > > > +static void scmi_hwmon_remove(struct scmi_device *sdev)
> > > > +{
> > > > + const struct scmi_handle *handle = sdev->handle;
> > > > +
> > > > + handle->put_ops(handle, SCMI_PROTOCOL_SENSOR);
> > > > +}
> > > > +
> > > > static const struct scmi_device_id scmi_id_table[] = {
> > > > { SCMI_PROTOCOL_SENSOR, "hwmon" },
> > > > { },
> > > > @@ -267,6 +280,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > static struct scmi_driver scmi_hwmon_drv = {
> > > > .name = "scmi-hwmon",
> > > > .probe = scmi_hwmon_probe,
> > > > + .remove = scmi_hwmon_remove,
> > > > .id_table = scmi_id_table,
> > > > };
> > > > module_scmi_driver(scmi_hwmon_drv);
> > > > diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
> > > > index 8d3a858e3b19..e48220dedb35 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,6 +11,8 @@
> > > > #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
> > > > @@ -39,7 +41,7 @@ scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
> > > > {
> > > > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > > > - return handle->reset_ops->assert(handle, id);
> > > > + return reset_ops->assert(handle, id);
> > > > }
> > > > /**
> > > > @@ -57,7 +59,7 @@ scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
> > > > {
> > > > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > > > - return handle->reset_ops->deassert(handle, id);
> > > > + return reset_ops->deassert(handle, id);
> > > > }
> > > > /**
> > > > @@ -75,7 +77,7 @@ scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
> > > > {
> > > > const struct scmi_handle *handle = to_scmi_handle(rcdev);
> > > > - return handle->reset_ops->reset(handle, id);
> > > > + return reset_ops->reset(handle, id);
> > > > }
> > > > static const struct reset_control_ops scmi_reset_ops = {
> > > > @@ -91,9 +93,13 @@ static int scmi_reset_probe(struct scmi_device *sdev)
> > > > struct device_node *np = dev->of_node;
> > > > const struct scmi_handle *handle = sdev->handle;
> > > > - if (!handle || !handle->reset_ops)
> > > > + if (!handle)
> > > > return -ENODEV;
> > > > + reset_ops = handle->get_ops(handle, SCMI_PROTOCOL_RESET);
> > > > + if (IS_ERR(reset_ops))
> > > > + return PTR_ERR(reset_ops);
> > > > +
> > > > data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> > > > if (!data)
> > > > return -ENOMEM;
> > > > @@ -101,12 +107,19 @@ 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->rcdev.nr_resets = reset_ops->num_domains_get(handle);
> > > > data->handle = handle;
> > > > return devm_reset_controller_register(dev, &data->rcdev);
> > > > }
> > > > +static void scmi_reset_remove(struct scmi_device *sdev)
> > > > +{
> > > > + const struct scmi_handle *handle = sdev->handle;
> > > > +
> > > > + handle->put_ops(handle, SCMI_PROTOCOL_RESET);
> > > > +}
> > > > +
> > > > static const struct scmi_device_id scmi_id_table[] = {
> > > > { SCMI_PROTOCOL_RESET, "reset" },
> > > > { },
> > > > @@ -116,6 +129,7 @@ MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > static struct scmi_driver scmi_reset_driver = {
> > > > .name = "scmi-reset",
> > > > .probe = scmi_reset_probe,
> > > > + .remove = scmi_reset_remove,
> > > > .id_table = scmi_id_table,
> > > > };
> > > > module_scmi_driver(scmi_reset_driver);
> > > > diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> > > > index bc4f06d46bfb..bfe7017cff19 100644
> > > > --- a/include/linux/scmi_protocol.h
> > > > +++ b/include/linux/scmi_protocol.h
> > > > @@ -257,11 +257,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
> > > > * @notify_ops: pointer to set of notifications related operations
> > > > * @notify_priv: pointer to private data structure specific to notifications
> > > > * (for internal use only)
> > > > @@ -269,11 +264,11 @@ struct scmi_notify_ops {
> > > > 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 *(*get_ops)(const struct scmi_handle *handle,
> > > > + u8 proto);
> > > > + void (*put_ops)(const struct scmi_handle *handle, u8 proto);
> > > > +
> > > > const struct scmi_notify_ops *notify_ops;
> > > > void *notify_priv;
> > > > };
> > > >
> > >
> > >
>
> --
> Warm Regards
> Thara