firmware: arm_scmi: Add a generic transport supplier

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>
This commit is contained in:
Cristian Marussi 2026-05-10 17:05:25 +01:00 committed by Sudeep Holla
parent 9dfae7d2ed
commit ab6eb28a47

View 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) \
{ \