]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_scmi: Add a generic transport supplier
authorCristian Marussi <cristian.marussi@arm.com>
Sun, 10 May 2026 16:05:25 +0000 (17:05 +0100)
committerSudeep Holla <sudeep.holla@kernel.org>
Thu, 21 May 2026 16:31:51 +0000 (17:31 +0100)
Add the capability to define a common generic transport supplier which
embeds the logic needed to support one single unique instance of transport
supplier.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://patch.msgid.link/20260510160527.3537474-3-cristian.marussi@arm.com
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
drivers/firmware/arm_scmi/common.h

index 9de8e4eb719ad2e1806674c9273f579812d11fbd..b9723c105fc1b562f66f7a165f1db3dc9c94a27d 100644 (file)
@@ -502,6 +502,117 @@ struct scmi_transport {
        const struct scmi_transport_handle *th;
 };
 
+/**
+ * struct scmi_transport_supplier  - Transport descriptor
+ * @mtx: A mutex to protect @available
+ * @available: A reference to an initialized transport device, when available.
+ *            This reference is implicitly used to track the status of the
+ *            supplier and it can cycle through the following 3 states:
+ *            1. NOT_READY - PTR_ERR(-EPROBE_DEFER): no supplier available;
+ *               this is the transport initial state.
+ *            2. AVAILABLE - <supplier_dev>: a transport supplier has been
+ *               initialized and it is available, ready to use.
+ *            3. BUSY _ PTR_ERR(-EBUSY): transport supplier is currently in use.
+ * @th: An embedded transport handle object that embeds the helpers
+ *     implementing the above mentioned logic
+ *
+ * Note that this transport driver enforces single instance probing.
+ */
+struct scmi_transport_supplier {
+       /* Protect @available */
+       struct mutex mtx;
+       struct device *available;
+       const struct scmi_transport_handle th;
+};
+
+#define to_sup(t)      container_of(t, struct scmi_transport_supplier, th)
+
+/**
+ * scmi_transport_supplier_put  - A helper to dispose of a supplier
+ * @th: A reference to the transport handle to use
+ * @supplier: A reference to the device supplier to manage, cannot be NULL
+ *           or ERR_PTR.
+ *
+ * Note that putting a supplier will have different effect based on the
+ * current state of scmi_transport_supplier.available:
+ *  - NOT_READY/BUSY: @supplier will be set as the new available device: this
+ *                   can be used to made available a supplier OR stop using one.
+ *  - AVAILABLE: if the @supplier we are disposing of matches the currently
+ *              available one, roll back to NOT_READY state.
+ *              Any other attempt to override an available supplier with a
+ *              new one is rejected, effectively enforcing one single supplier.
+ *
+ * Return: 0 on Success, errno otherwise.
+ */
+static inline int
+scmi_transport_supplier_put(const struct scmi_transport_handle *th,
+                           struct device *supplier)
+{
+       struct scmi_transport_supplier *sup = to_sup(th);
+
+       /* Nothing to do when the provided supplier was never real */
+       if (IS_ERR_OR_NULL(supplier))
+               return 0;
+
+       guard(mutex)(&sup->mtx);
+       switch (PTR_ERR_OR_ZERO(sup->available)) {
+       case -EPROBE_DEFER:
+       case -EBUSY:
+               sup->available = supplier;
+               break;
+       case 0:
+               /* Putting a supplier when in the AVAILABLE state causes a
+                * transition back to the NOT_READY state, BUT only if the
+                * supplier we are disposing of was exactly the device that was
+                * previously made readily available.
+                */
+               if (supplier != sup->available)
+                       return -EINVAL;
+               sup->available = ERR_PTR(-EPROBE_DEFER);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * scmi_transport_supplier_get  - A helper to get hold of a supplier
+ * @th: A reference to the transport handle to use
+ *
+ * Note that, trying to get a supplier device can return:
+ *  - a ready to use supplier device, (subsequently made unavailable)
+ *  - PTR_ERR(-EPROBE_DEFER): no supplier is available
+ *  - PTR_ERR(-BUSY): supplier was already taken by a previous get
+ *
+ *  This allows the probe to defer and wait when a possible device can
+ *  be reasonably expected to appear.
+ *
+ * Return: a usable supplier device on Success or PTR_ERR on Failure.
+ */
+static inline struct device *
+scmi_transport_supplier_get(const struct scmi_transport_handle *th)
+{
+       struct scmi_transport_supplier *sup = to_sup(th);
+       struct device *supplier;
+
+       guard(mutex)(&sup->mtx);
+       supplier = sup->available;
+       if (!IS_ERR(sup->available))
+               sup->available = ERR_PTR(-EBUSY);
+
+       return supplier;
+}
+
+#define DEFINE_SCMI_TRANSPORT_SUPPLIER(__supplier)             \
+struct scmi_transport_supplier __supplier = {                  \
+       .mtx = __MUTEX_INITIALIZER(__supplier.mtx),             \
+       .available = INIT_ERR_PTR(-EPROBE_DEFER),               \
+       .th.supplier_get = scmi_transport_supplier_get,         \
+       .th.supplier_put = scmi_transport_supplier_put,         \
+}
+
 #define DEFINE_SCMI_TRANSPORT_DRIVER(__tag, __drv, __desc, __match, __core_ops)\
 static void __tag##_dev_free(void *data)                                      \
 {                                                                             \