firmware: arm_scmi: Introduce a new lifecycle for protocol devices
authorCristian Marussi <cristian.marussi@arm.com>
Thu, 22 Dec 2022 18:50:48 +0000 (18:50 +0000)
committerSudeep Holla <sudeep.holla@arm.com>
Thu, 19 Jan 2023 09:51:09 +0000 (09:51 +0000)
Protocol devices are created or destroyed depending on the related device
request/unrequest events emitted on the scmi_requested_devices_nh
notification chain by the SCMI bus and served in the driver by the
scmi_device_request_notifier.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://lore.kernel.org/r/20221222185049.737625-9-cristian.marussi@arm.com
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/driver.c

index 83a43a5f7bb4b546d549e0b248466a6cd046a150..115baaa4aca94da3cd9bc9bfc99d5ebf065d19ee 100644 (file)
@@ -14,6 +14,8 @@
  * Copyright (C) 2018-2021 ARM Ltd.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/bitmap.h>
 #include <linux/device.h>
 #include <linux/export.h>
@@ -140,6 +142,7 @@ struct scmi_protocol_instance {
  * @bus_nb: A notifier to listen for device bind/unbind on the scmi bus
  * @dev_req_nb: A notifier to listen for device request/unrequest on the scmi
  *             bus
+ * @devreq_mtx: A mutex to serialize device creation for this SCMI instance
  */
 struct scmi_info {
        struct device *dev;
@@ -161,6 +164,8 @@ struct scmi_info {
        int users;
        struct notifier_block bus_nb;
        struct notifier_block dev_req_nb;
+       /* Serialize device creation process for this instance */
+       struct mutex devreq_mtx;
 };
 
 #define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
@@ -255,6 +260,40 @@ void scmi_protocol_unregister(const struct scmi_protocol *proto)
 }
 EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
 
+/**
+ * scmi_create_protocol_devices  - Create devices for all pending requests for
+ * this SCMI instance.
+ *
+ * @np: The device node describing the protocol
+ * @info: The SCMI instance descriptor
+ * @prot_id: The protocol ID
+ * @name: The optional name of the device to be created: if not provided this
+ *       call will lead to the creation of all the devices currently requested
+ *       for the specified protocol.
+ */
+static void scmi_create_protocol_devices(struct device_node *np,
+                                        struct scmi_info *info,
+                                        int prot_id, const char *name)
+{
+       struct scmi_device *sdev;
+
+       mutex_lock(&info->devreq_mtx);
+       sdev = scmi_device_create(np, info->dev, prot_id, name);
+       if (name && !sdev)
+               dev_err(info->dev,
+                       "failed to create device for protocol 0x%X (%s)\n",
+                       prot_id, name);
+       mutex_unlock(&info->devreq_mtx);
+}
+
+static void scmi_destroy_protocol_devices(struct scmi_info *info,
+                                         int prot_id, const char *name)
+{
+       mutex_lock(&info->devreq_mtx);
+       scmi_device_destroy(info->dev, prot_id, name);
+       mutex_unlock(&info->devreq_mtx);
+}
+
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
                                         void *priv)
 {
@@ -2283,8 +2322,12 @@ static int scmi_device_request_notifier(struct notifier_block *nb,
 
        switch (action) {
        case SCMI_BUS_NOTIFY_DEVICE_REQUEST:
+               scmi_create_protocol_devices(np, info, id_table->protocol_id,
+                                            id_table->name);
                break;
        case SCMI_BUS_NOTIFY_DEVICE_UNREQUEST:
+               scmi_destroy_protocol_devices(info, id_table->protocol_id,
+                                             id_table->name);
                break;
        default:
                return NOTIFY_DONE;
@@ -2318,6 +2361,7 @@ static int scmi_probe(struct platform_device *pdev)
        idr_init(&info->protocols);
        mutex_init(&info->protocols_mtx);
        idr_init(&info->active_protocols);
+       mutex_init(&info->devreq_mtx);
 
        platform_set_drvdata(pdev, info);
        idr_init(&info->tx_idr);
@@ -2412,6 +2456,7 @@ static int scmi_probe(struct platform_device *pdev)
                }
 
                of_node_get(child);
+               scmi_create_protocol_devices(child, info, prot_id, NULL);
        }
 
        return 0;