In order to minimize SCMI platform fw-side complexity, only one platform should be in charge of SCMI SystemPower protocol communications with the OSPM: enforce the existence of one single unique device associated with SystemPower protocol across any possible number of SCMI platforms, and warn if a system tries to register different SystemPower devices from multiple platforms.
Signed-off-by: Cristian Marussi <cristian.maru...@arm.com> --- drivers/firmware/arm_scmi/bus.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 88149a46e6d9..e8542a7e8862 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -19,6 +19,9 @@ static DEFINE_IDA(scmi_bus_id); static DEFINE_IDR(scmi_available_protocols); static DEFINE_SPINLOCK(protocol_lock); +/* Track globally the creation of SCMI SystemPower related devices */ +static bool scmi_syspower_registered; + static const struct scmi_device_id * scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv) { @@ -175,6 +178,23 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol, int id, retval; struct scmi_device *scmi_dev; + /* + * Check if any SCMI SystemPower device was already created. + * + * Note that, shared global @scmi_syspower_registered is not protected + * by a mutex since: + * - scmi_device_create() is not concurrently invoked by the SCMI core + * - scmi_device_destroy() is effectively called only upon SCMI core + * unloading/unbinding, so no race is either possible between create + * and destroy. + */ + if (protocol == SCMI_PROTOCOL_SYSTEM && scmi_syspower_registered) { + dev_warn(parent, + "SCMI SystemPower protocol device must be unique !\n"); + dump_stack(); + return NULL; + } + scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL); if (!scmi_dev) return NULL; @@ -204,6 +224,9 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol, if (retval) goto put_dev; + if (protocol == SCMI_PROTOCOL_SYSTEM) + scmi_syspower_registered = true; + return scmi_dev; put_dev: kfree_const(scmi_dev->name); @@ -218,6 +241,8 @@ void scmi_device_destroy(struct scmi_device *scmi_dev) scmi_handle_put(scmi_dev->handle); ida_simple_remove(&scmi_bus_id, scmi_dev->id); device_unregister(&scmi_dev->dev); + if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) + scmi_syspower_registered = false; } void scmi_set_handle(struct scmi_device *scmi_dev) -- 2.17.1