Merge tag 'scmi-updates-6.17' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers
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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user