Arm SCMI updates for v6.17

1. A fix is introduced to correct turbo frequency marking for 64-bit
   devices with sustained frequencies over 4GHz, ensuring accurate turbo
   frequency identification.

2. Debug capabilities are being improved by introducing in-flight transfer
   tracking using debug counters, which help diagnose transfer congestion
   and behavior. Additional tracepoints are added to log in-flight counts
   at transfer begin and end, offering better runtime insight. The debug
   counters now support decrement operations using a newly added
   scmi_dec_count helper, making counter tracking symmetric and more robust.

3. A race condition in suspend-resume logic is being resolved by ensuring
   SCMI_SYSPOWER_IDLE state is set early during resume, improving suspend
   reliability under certain conditions. New suspend and resume operations
   are added to the scmi_bus_type to enable finer power management control
   for SCMI-based devices.

4. Finally enhancements are also made to avoid registering notifiers for
   events that a platform does not support, reducing unnecessary overhead
   by checking for unsupported event types during protocolinitialization.

* tag 'scmi-updates-6.17' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: Convert to SYSTEM_SLEEP_PM_OPS
  firmware: arm_scmi: Avoid notifier registration for unsupported events
  firmware: arm_scmi: power_control: Ensure SCMI_SYSPOWER_IDLE is set early during resume
  firmware: arm_scmi: Add power management operations to SCMI bus
  include: trace:  Add tracepoint support for inflight xfer count
  firmware: arm_scmi: Track number of inflight SCMI transfers
  firmware: arm_scmi: Add support for debug counter decrement
  firmware: arm_scmi: Fix up turbo frequencies selection

Link: https://lore.kernel.org/r/20250709122907.1171913-1-sudeep.holla@arm.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann
2025-07-21 16:41:38 +02:00
8 changed files with 126 additions and 29 deletions
+26
View File
@@ -323,6 +323,31 @@ static struct attribute *scmi_device_attributes_attrs[] = {
};
ATTRIBUTE_GROUPS(scmi_device_attributes);
static int scmi_pm_suspend(struct device *dev)
{
const struct device_driver *drv = dev->driver;
if (drv && drv->pm && drv->pm->suspend)
return drv->pm->suspend(dev);
return 0;
}
static int scmi_pm_resume(struct device *dev)
{
const struct device_driver *drv = dev->driver;
if (drv && drv->pm && drv->pm->resume)
return drv->pm->resume(dev);
return 0;
}
static const struct dev_pm_ops scmi_dev_pm_ops = {
.suspend = pm_sleep_ptr(scmi_pm_suspend),
.resume = pm_sleep_ptr(scmi_pm_resume),
};
const struct bus_type scmi_bus_type = {
.name = "scmi_protocol",
.match = scmi_dev_match,
@@ -330,6 +355,7 @@ const struct bus_type scmi_bus_type = {
.remove = scmi_dev_remove,
.uevent = scmi_device_uevent,
.dev_groups = scmi_device_attributes_groups,
.pm = &scmi_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(scmi_bus_type);
+8
View File
@@ -305,6 +305,7 @@ enum debug_counters {
ERR_MSG_INVALID,
ERR_MSG_NOMEM,
ERR_PROTOCOL,
XFERS_INFLIGHT,
SCMI_DEBUG_COUNTERS_LAST
};
@@ -314,6 +315,12 @@ static inline void scmi_inc_count(atomic_t *arr, int stat)
atomic_inc(&arr[stat]);
}
static inline void scmi_dec_count(atomic_t *arr, int stat)
{
if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS))
atomic_dec(&arr[stat]);
}
enum scmi_bad_msg {
MSG_UNEXPECTED = -1,
MSG_INVALID = -2,
@@ -498,4 +505,5 @@ static struct platform_driver __drv = { \
void scmi_notification_instance_data_set(const struct scmi_handle *handle,
void *priv);
void *scmi_notification_instance_data_get(const struct scmi_handle *handle);
int scmi_inflight_count(const struct scmi_handle *handle);
#endif /* _SCMI_COMMON_H */
+26 -2
View File
@@ -190,6 +190,7 @@ struct scmi_info {
};
#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
#define tx_minfo_to_scmi_info(h) container_of(h, struct scmi_info, tx_minfo)
#define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb)
#define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb)
@@ -603,9 +604,14 @@ static inline void
scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
struct scmi_xfers_info *minfo)
{
/* In this context minfo will be tx_minfo due to the xfer pending */
struct scmi_info *info = tx_minfo_to_scmi_info(minfo);
/* Set in-flight */
set_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq);
scmi_inc_count(info->dbg->counters, XFERS_INFLIGHT);
xfer->pending = true;
}
@@ -807,9 +813,13 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
spin_lock_irqsave(&minfo->xfer_lock, flags);
if (refcount_dec_and_test(&xfer->users)) {
if (xfer->pending) {
struct scmi_info *info = tx_minfo_to_scmi_info(minfo);
scmi_xfer_token_clear(minfo, xfer);
hash_del(&xfer->node);
xfer->pending = false;
scmi_dec_count(info->dbg->counters, XFERS_INFLIGHT);
}
hlist_add_head(&xfer->node, &minfo->free_xfers);
}
@@ -1433,7 +1443,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
xfer->hdr.poll_completion,
scmi_inflight_count(&info->handle));
/* Clear any stale status */
xfer->hdr.status = SCMI_SUCCESS;
@@ -1469,7 +1480,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
info->desc->ops->mark_txdone(cinfo, ret, xfer);
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq, ret);
xfer->hdr.protocol_id, xfer->hdr.seq, ret,
scmi_inflight_count(&info->handle));
return ret;
}
@@ -2912,6 +2924,7 @@ static const char * const dbg_counter_strs[] = {
"err_msg_invalid",
"err_msg_nomem",
"err_protocol",
"xfers_inflight",
};
static ssize_t reset_all_on_write(struct file *filp, const char __user *buf,
@@ -3405,6 +3418,17 @@ static struct dentry *scmi_debugfs_init(void)
return d;
}
int scmi_inflight_count(const struct scmi_handle *handle)
{
if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) {
struct scmi_info *info = handle_to_scmi_info(handle);
return atomic_read(&info->dbg->counters[XFERS_INFLIGHT]);
} else {
return 0;
}
}
static int __init scmi_driver_init(void)
{
scmi_quirks_initialize();
+30 -9
View File
@@ -318,6 +318,9 @@ struct scmi_registered_events_desc {
* customized event report
* @num_sources: The number of possible sources for this event as stated at
* events' registration time
* @not_supported_by_platform: A flag to indicate that not even one source was
* found to be supported by the platform for this
* event
* @sources: A reference to a dynamically allocated array used to refcount the
* events' enable requests for all the existing sources
* @sources_mtx: A mutex to serialize the access to @sources
@@ -334,6 +337,7 @@ struct scmi_registered_event {
const struct scmi_event *evt;
void *report;
u32 num_sources;
bool not_supported_by_platform;
refcount_t *sources;
/* locking to serialize the access to sources */
struct mutex sources_mtx;
@@ -811,10 +815,19 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
if (!r_evt->report)
return -ENOMEM;
for (id = 0; id < r_evt->num_sources; id++)
if (ee->ops->is_notify_supported &&
!ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
if (ee->ops->is_notify_supported) {
int supported = 0;
for (id = 0; id < r_evt->num_sources; id++) {
if (!ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
else
supported++;
}
/* Not even one source has been found to be supported */
r_evt->not_supported_by_platform = !supported;
}
pd->registered_events[i] = r_evt;
/* Ensure events are updated */
@@ -936,6 +949,11 @@ static inline int scmi_bind_event_handler(struct scmi_notify_instance *ni,
* of protocol instance.
*/
hash_del(&hndl->hash);
/* Bailout if event is not supported at all */
if (r_evt->not_supported_by_platform)
return -EOPNOTSUPP;
/*
* Acquire protocols only for NON pending handlers, so as NOT to trigger
* protocol initialization when a notifier is registered against a still
@@ -1060,6 +1078,9 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni,
r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key),
KEY_XTRACT_EVT_ID(evt_key));
if (r_evt && r_evt->not_supported_by_platform)
return ERR_PTR(-EOPNOTSUPP);
mutex_lock(&ni->pending_mtx);
/* Search registered events at first ... if possible at all */
if (r_evt) {
@@ -1087,7 +1108,7 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni,
hndl->key);
/* this hndl can be only a pending one */
scmi_put_handler_unlocked(ni, hndl);
hndl = NULL;
hndl = ERR_PTR(-EINVAL);
}
}
mutex_unlock(&ni->pending_mtx);
@@ -1370,8 +1391,8 @@ static int scmi_notifier_register(const struct scmi_handle *handle,
evt_key = MAKE_HASH_KEY(proto_id, evt_id,
src_id ? *src_id : SRC_ID_MASK);
hndl = scmi_get_or_create_handler(ni, evt_key);
if (!hndl)
return -EINVAL;
if (IS_ERR(hndl))
return PTR_ERR(hndl);
blocking_notifier_chain_register(&hndl->chain, nb);
@@ -1416,8 +1437,8 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle,
evt_key = MAKE_HASH_KEY(proto_id, evt_id,
src_id ? *src_id : SRC_ID_MASK);
hndl = scmi_get_handler(ni, evt_key);
if (!hndl)
return -EINVAL;
if (IS_ERR(hndl))
return PTR_ERR(hndl);
/*
* Note that this chain unregistration call is safe on its own
+1 -1
View File
@@ -892,7 +892,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph,
freq = dom->opp[idx].indicative_freq * dom->mult_factor;
/* All OPPs above the sustained frequency are treated as turbo */
data.turbo = freq > dom->sustained_freq_khz * 1000;
data.turbo = freq > dom->sustained_freq_khz * 1000UL;
data.level = dom->opp[idx].perf;
data.freq = freq;
+4 -2
View File
@@ -475,7 +475,8 @@ static void scmi_xfer_raw_worker(struct work_struct *work)
raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer);
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq, ret);
xfer->hdr.protocol_id, xfer->hdr.seq,
ret, scmi_inflight_count(raw->handle));
/* Wait also for an async delayed response if needed */
if (!ret && xfer->async_done) {
@@ -642,7 +643,8 @@ static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
xfer->hdr.poll_completion,
scmi_inflight_count(raw->handle));
ret = raw->desc->ops->send_message(rw->cinfo, xfer);
if (ret) {
+17 -5
View File
@@ -46,6 +46,7 @@
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/reboot.h>
#include <linux/scmi_protocol.h>
@@ -324,12 +325,7 @@ static int scmi_userspace_notifier(struct notifier_block *nb,
static void scmi_suspend_work_func(struct work_struct *work)
{
struct scmi_syspower_conf *sc =
container_of(work, struct scmi_syspower_conf, suspend_work);
pm_suspend(PM_SUSPEND_MEM);
sc->state = SCMI_SYSPOWER_IDLE;
}
static int scmi_syspower_probe(struct scmi_device *sdev)
@@ -354,6 +350,7 @@ static int scmi_syspower_probe(struct scmi_device *sdev)
sc->required_transition = SCMI_SYSTEM_MAX;
sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
sc->dev = &sdev->dev;
dev_set_drvdata(&sdev->dev, sc);
INIT_WORK(&sc->suspend_work, scmi_suspend_work_func);
@@ -363,6 +360,18 @@ static int scmi_syspower_probe(struct scmi_device *sdev)
NULL, &sc->userspace_nb);
}
static int scmi_system_power_resume(struct device *dev)
{
struct scmi_syspower_conf *sc = dev_get_drvdata(dev);
sc->state = SCMI_SYSPOWER_IDLE;
return 0;
}
static const struct dev_pm_ops scmi_system_power_pmops = {
SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume)
};
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_SYSTEM, "syspower" },
{ },
@@ -370,6 +379,9 @@ static const struct scmi_device_id scmi_id_table[] = {
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_system_power_driver = {
.driver = {
.pm = pm_sleep_ptr(&scmi_system_power_pmops),
},
.name = "scmi-system-power",
.probe = scmi_syspower_probe,
.id_table = scmi_id_table,
+14 -10
View File
@@ -36,8 +36,8 @@ TRACE_EVENT(scmi_fc_call,
TRACE_EVENT(scmi_xfer_begin,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
bool poll),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, poll),
bool poll, int inflight),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, poll, inflight),
TP_STRUCT__entry(
__field(int, transfer_id)
@@ -45,6 +45,7 @@ TRACE_EVENT(scmi_xfer_begin,
__field(u8, protocol_id)
__field(u16, seq)
__field(bool, poll)
__field(int, inflight)
),
TP_fast_assign(
@@ -53,11 +54,12 @@ TRACE_EVENT(scmi_xfer_begin,
__entry->protocol_id = protocol_id;
__entry->seq = seq;
__entry->poll = poll;
__entry->inflight = inflight;
),
TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X poll=%u",
__entry->protocol_id, __entry->msg_id, __entry->seq,
__entry->transfer_id, __entry->poll)
TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X poll=%u inflight=%d",
__entry->protocol_id, __entry->msg_id, __entry->seq,
__entry->transfer_id, __entry->poll, __entry->inflight)
);
TRACE_EVENT(scmi_xfer_response_wait,
@@ -90,8 +92,8 @@ TRACE_EVENT(scmi_xfer_response_wait,
TRACE_EVENT(scmi_xfer_end,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
int status),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, status),
int status, int inflight),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, status, inflight),
TP_STRUCT__entry(
__field(int, transfer_id)
@@ -99,6 +101,7 @@ TRACE_EVENT(scmi_xfer_end,
__field(u8, protocol_id)
__field(u16, seq)
__field(int, status)
__field(int, inflight)
),
TP_fast_assign(
@@ -107,11 +110,12 @@ TRACE_EVENT(scmi_xfer_end,
__entry->protocol_id = protocol_id;
__entry->seq = seq;
__entry->status = status;
__entry->inflight = inflight;
),
TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X s=%d",
__entry->protocol_id, __entry->msg_id, __entry->seq,
__entry->transfer_id, __entry->status)
TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X s=%d inflight=%d",
__entry->protocol_id, __entry->msg_id, __entry->seq,
__entry->transfer_id, __entry->status, __entry->inflight)
);
TRACE_EVENT(scmi_rx_done,