Skip to content

Commit 9a0658d

Browse files
MrVansudeep-holla
authored andcommitted
firmware: arm_scmi: power_control: Ensure SCMI_SYSPOWER_IDLE is set early during resume
Fix a race condition where a second suspend notification from another SCMI agent wakes the system before SCMI_SYSPOWER_IDLE is set, leading to ignored suspend requests. This is due to interrupts triggering early execution of `scmi_userspace_notifier()` before the SCMI state is updated. To resolve this, set SCMI_SYSPOWER_IDLE earlier in the device resume path, prior to `thaw_processes()`. This ensures the SCMI state is correct when the notifier runs, allowing the system to suspend again as expected. On some platforms using SCMI, SCP cannot distinguish between CPU idle and suspend since both result in cluster power-off. By explicitly setting the idle state early, the Linux SCMI agent can correctly re-suspend in response to external notifications. Signed-off-by: Peng Fan <peng.fan@nxp.com> Message-Id: <20250704-scmi-pm-v2-2-9316cec2f9cc@nxp.com> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
1 parent 76e65f7 commit 9a0658d

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

drivers/firmware/arm_scmi/scmi_power_control.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/math.h>
4747
#include <linux/module.h>
4848
#include <linux/mutex.h>
49+
#include <linux/pm.h>
4950
#include <linux/printk.h>
5051
#include <linux/reboot.h>
5152
#include <linux/scmi_protocol.h>
@@ -324,12 +325,7 @@ static int scmi_userspace_notifier(struct notifier_block *nb,
324325

325326
static void scmi_suspend_work_func(struct work_struct *work)
326327
{
327-
struct scmi_syspower_conf *sc =
328-
container_of(work, struct scmi_syspower_conf, suspend_work);
329-
330328
pm_suspend(PM_SUSPEND_MEM);
331-
332-
sc->state = SCMI_SYSPOWER_IDLE;
333329
}
334330

335331
static int scmi_syspower_probe(struct scmi_device *sdev)
@@ -354,6 +350,7 @@ static int scmi_syspower_probe(struct scmi_device *sdev)
354350
sc->required_transition = SCMI_SYSTEM_MAX;
355351
sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
356352
sc->dev = &sdev->dev;
353+
dev_set_drvdata(&sdev->dev, sc);
357354

358355
INIT_WORK(&sc->suspend_work, scmi_suspend_work_func);
359356

@@ -363,13 +360,28 @@ static int scmi_syspower_probe(struct scmi_device *sdev)
363360
NULL, &sc->userspace_nb);
364361
}
365362

363+
static int scmi_system_power_resume(struct device *dev)
364+
{
365+
struct scmi_syspower_conf *sc = dev_get_drvdata(dev);
366+
367+
sc->state = SCMI_SYSPOWER_IDLE;
368+
return 0;
369+
}
370+
371+
static const struct dev_pm_ops scmi_system_power_pmops = {
372+
SET_SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume)
373+
};
374+
366375
static const struct scmi_device_id scmi_id_table[] = {
367376
{ SCMI_PROTOCOL_SYSTEM, "syspower" },
368377
{ },
369378
};
370379
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
371380

372381
static struct scmi_driver scmi_system_power_driver = {
382+
.driver = {
383+
.pm = &scmi_system_power_pmops,
384+
},
373385
.name = "scmi-system-power",
374386
.probe = scmi_syspower_probe,
375387
.id_table = scmi_id_table,

0 commit comments

Comments
 (0)