Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Peng Fan and committed by
Sudeep Holla
9a0658d3 76e65f7a

+17 -5
+17 -5
drivers/firmware/arm_scmi/scmi_power_control.c
··· 46 46 #include <linux/math.h> 47 47 #include <linux/module.h> 48 48 #include <linux/mutex.h> 49 + #include <linux/pm.h> 49 50 #include <linux/printk.h> 50 51 #include <linux/reboot.h> 51 52 #include <linux/scmi_protocol.h> ··· 325 324 326 325 static void scmi_suspend_work_func(struct work_struct *work) 327 326 { 328 - struct scmi_syspower_conf *sc = 329 - container_of(work, struct scmi_syspower_conf, suspend_work); 330 - 331 327 pm_suspend(PM_SUSPEND_MEM); 332 - 333 - sc->state = SCMI_SYSPOWER_IDLE; 334 328 } 335 329 336 330 static int scmi_syspower_probe(struct scmi_device *sdev) ··· 350 354 sc->required_transition = SCMI_SYSTEM_MAX; 351 355 sc->userspace_nb.notifier_call = &scmi_userspace_notifier; 352 356 sc->dev = &sdev->dev; 357 + dev_set_drvdata(&sdev->dev, sc); 353 358 354 359 INIT_WORK(&sc->suspend_work, scmi_suspend_work_func); 355 360 ··· 360 363 NULL, &sc->userspace_nb); 361 364 } 362 365 366 + static int scmi_system_power_resume(struct device *dev) 367 + { 368 + struct scmi_syspower_conf *sc = dev_get_drvdata(dev); 369 + 370 + sc->state = SCMI_SYSPOWER_IDLE; 371 + return 0; 372 + } 373 + 374 + static const struct dev_pm_ops scmi_system_power_pmops = { 375 + SET_SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume) 376 + }; 377 + 363 378 static const struct scmi_device_id scmi_id_table[] = { 364 379 { SCMI_PROTOCOL_SYSTEM, "syspower" }, 365 380 { }, ··· 379 370 MODULE_DEVICE_TABLE(scmi, scmi_id_table); 380 371 381 372 static struct scmi_driver scmi_system_power_driver = { 373 + .driver = { 374 + .pm = &scmi_system_power_pmops, 375 + }, 382 376 .name = "scmi-system-power", 383 377 .probe = scmi_syspower_probe, 384 378 .id_table = scmi_id_table,