// SPDX-License-Identifier: GPL-2.0-only /* * s390 generic CPU idle driver. * * Copyright IBM Corp. 2026 */ #define pr_fmt(fmt) "CPUidle s390: " fmt #include #include #include #include static __cpuidle int s390_enter_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { arch_cpu_idle(); return index; } static struct cpuidle_driver s390_cpuidle_driver = { .cpumask = (struct cpumask *)cpu_present_mask, .name = "s390-idle", .states = { { /* entry 0 is for polling */}, { .enter = s390_enter_idle, .name = "IDLE", .desc = "ENABLED WAIT", }, }, .safe_state_index = 0, .state_count = 2, }; static int s390_cpuidle_cpu_online(unsigned int cpu) { struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); int rc; if (dev->registered) { cpuidle_pause_and_lock(); rc = cpuidle_enable_device(dev); cpuidle_resume_and_unlock(); if (rc) pr_err("Failed to enable cpuidle device on cpu %u\n", cpu); } else { dev->cpu = cpu; rc = cpuidle_register_device(dev); if (rc) pr_err("Failed to register cpuidle driver on cpu %u\n", cpu); } return rc; } static int s390_cpuidle_cpu_dead(unsigned int cpu) { struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); if (!dev->registered) return 0; cpuidle_pause_and_lock(); cpuidle_disable_device(dev); cpuidle_resume_and_unlock(); return 0; } /* * The target_residency and exit_latency values are benchmark-derived estimates * that remain non-deterministic due to s390's virtualized architecture. * * Configuration strategy: * - Poll idle state: Values derived from the next enabled idle state (EW) * - Enabled Wait state: Values selected based on idle behavior and empirical * measurement data * * Goal is to improve responsiveness for workloads with frequent sleep/wakeup * cycles while minimizing any side effects. */ static void __init s390_cpuidle_ew_tune(void) { struct cpuidle_state *state = &s390_cpuidle_driver.states[1]; if (machine_is_lpar()) { state->target_residency = 5; state->exit_latency = 5; } else { state->target_residency = 1; state->exit_latency = 1; } } static int __init s390_cpuidle_init(void) { int rc; s390_cpuidle_ew_tune(); cpuidle_poll_state_init(&s390_cpuidle_driver); rc = cpuidle_register(&s390_cpuidle_driver, NULL); if (rc) return rc; rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "cpuidle/s390:online", s390_cpuidle_cpu_online, s390_cpuidle_cpu_dead); if (rc < 0) { cpuidle_unregister(&s390_cpuidle_driver); pr_err("Failed to allocate hotplug state: cpuidle/s390:online\n"); return rc; } return 0; } device_initcall(s390_cpuidle_init);