1*01c162e2SMete Durlu // SPDX-License-Identifier: GPL-2.0-only 2*01c162e2SMete Durlu /* 3*01c162e2SMete Durlu * s390 generic CPU idle driver. 4*01c162e2SMete Durlu * 5*01c162e2SMete Durlu * Copyright IBM Corp. 2026 6*01c162e2SMete Durlu */ 7*01c162e2SMete Durlu 8*01c162e2SMete Durlu #define pr_fmt(fmt) "CPUidle s390: " fmt 9*01c162e2SMete Durlu 10*01c162e2SMete Durlu #include <linux/init.h> 11*01c162e2SMete Durlu #include <linux/cpuidle.h> 12*01c162e2SMete Durlu #include <linux/cpu.h> 13*01c162e2SMete Durlu #include <linux/sched/clock.h> 14*01c162e2SMete Durlu 15*01c162e2SMete Durlu static __cpuidle int s390_enter_idle(struct cpuidle_device *dev, 16*01c162e2SMete Durlu struct cpuidle_driver *drv, 17*01c162e2SMete Durlu int index) 18*01c162e2SMete Durlu { 19*01c162e2SMete Durlu arch_cpu_idle(); 20*01c162e2SMete Durlu return index; 21*01c162e2SMete Durlu } 22*01c162e2SMete Durlu 23*01c162e2SMete Durlu static struct cpuidle_driver s390_cpuidle_driver = { 24*01c162e2SMete Durlu .cpumask = (struct cpumask *)cpu_present_mask, 25*01c162e2SMete Durlu .name = "s390-idle", 26*01c162e2SMete Durlu .states = { 27*01c162e2SMete Durlu { /* entry 0 is for polling */}, 28*01c162e2SMete Durlu { 29*01c162e2SMete Durlu .enter = s390_enter_idle, 30*01c162e2SMete Durlu .name = "IDLE", 31*01c162e2SMete Durlu .desc = "ENABLED WAIT", 32*01c162e2SMete Durlu }, 33*01c162e2SMete Durlu }, 34*01c162e2SMete Durlu .safe_state_index = 0, 35*01c162e2SMete Durlu .state_count = 2, 36*01c162e2SMete Durlu }; 37*01c162e2SMete Durlu 38*01c162e2SMete Durlu static int s390_cpuidle_cpu_online(unsigned int cpu) 39*01c162e2SMete Durlu { 40*01c162e2SMete Durlu struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); 41*01c162e2SMete Durlu int rc; 42*01c162e2SMete Durlu 43*01c162e2SMete Durlu if (dev->registered) { 44*01c162e2SMete Durlu cpuidle_pause_and_lock(); 45*01c162e2SMete Durlu rc = cpuidle_enable_device(dev); 46*01c162e2SMete Durlu cpuidle_resume_and_unlock(); 47*01c162e2SMete Durlu if (rc) 48*01c162e2SMete Durlu pr_err("Failed to enable cpuidle device on cpu %u\n", cpu); 49*01c162e2SMete Durlu } else { 50*01c162e2SMete Durlu dev->cpu = cpu; 51*01c162e2SMete Durlu rc = cpuidle_register_device(dev); 52*01c162e2SMete Durlu if (rc) 53*01c162e2SMete Durlu pr_err("Failed to register cpuidle driver on cpu %u\n", cpu); 54*01c162e2SMete Durlu } 55*01c162e2SMete Durlu return rc; 56*01c162e2SMete Durlu } 57*01c162e2SMete Durlu 58*01c162e2SMete Durlu static int s390_cpuidle_cpu_dead(unsigned int cpu) 59*01c162e2SMete Durlu { 60*01c162e2SMete Durlu struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); 61*01c162e2SMete Durlu 62*01c162e2SMete Durlu if (!dev->registered) 63*01c162e2SMete Durlu return 0; 64*01c162e2SMete Durlu cpuidle_pause_and_lock(); 65*01c162e2SMete Durlu cpuidle_disable_device(dev); 66*01c162e2SMete Durlu cpuidle_resume_and_unlock(); 67*01c162e2SMete Durlu return 0; 68*01c162e2SMete Durlu } 69*01c162e2SMete Durlu 70*01c162e2SMete Durlu /* 71*01c162e2SMete Durlu * The target_residency and exit_latency values are benchmark-derived estimates 72*01c162e2SMete Durlu * that remain non-deterministic due to s390's virtualized architecture. 73*01c162e2SMete Durlu * 74*01c162e2SMete Durlu * Configuration strategy: 75*01c162e2SMete Durlu * - Poll idle state: Values derived from the next enabled idle state (EW) 76*01c162e2SMete Durlu * - Enabled Wait state: Values selected based on idle behavior and empirical 77*01c162e2SMete Durlu * measurement data 78*01c162e2SMete Durlu * 79*01c162e2SMete Durlu * Goal is to improve responsiveness for workloads with frequent sleep/wakeup 80*01c162e2SMete Durlu * cycles while minimizing any side effects. 81*01c162e2SMete Durlu */ 82*01c162e2SMete Durlu static void __init s390_cpuidle_ew_tune(void) 83*01c162e2SMete Durlu { 84*01c162e2SMete Durlu struct cpuidle_state *state = &s390_cpuidle_driver.states[1]; 85*01c162e2SMete Durlu 86*01c162e2SMete Durlu if (machine_is_lpar()) { 87*01c162e2SMete Durlu state->target_residency = 5; 88*01c162e2SMete Durlu state->exit_latency = 5; 89*01c162e2SMete Durlu } else { 90*01c162e2SMete Durlu state->target_residency = 1; 91*01c162e2SMete Durlu state->exit_latency = 1; 92*01c162e2SMete Durlu } 93*01c162e2SMete Durlu } 94*01c162e2SMete Durlu 95*01c162e2SMete Durlu static int __init s390_cpuidle_init(void) 96*01c162e2SMete Durlu { 97*01c162e2SMete Durlu int rc; 98*01c162e2SMete Durlu 99*01c162e2SMete Durlu s390_cpuidle_ew_tune(); 100*01c162e2SMete Durlu cpuidle_poll_state_init(&s390_cpuidle_driver); 101*01c162e2SMete Durlu rc = cpuidle_register(&s390_cpuidle_driver, NULL); 102*01c162e2SMete Durlu if (rc) 103*01c162e2SMete Durlu return rc; 104*01c162e2SMete Durlu rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, 105*01c162e2SMete Durlu "cpuidle/s390:online", 106*01c162e2SMete Durlu s390_cpuidle_cpu_online, 107*01c162e2SMete Durlu s390_cpuidle_cpu_dead); 108*01c162e2SMete Durlu if (rc < 0) { 109*01c162e2SMete Durlu cpuidle_unregister(&s390_cpuidle_driver); 110*01c162e2SMete Durlu pr_err("Failed to allocate hotplug state: cpuidle/s390:online\n"); 111*01c162e2SMete Durlu return rc; 112*01c162e2SMete Durlu } 113*01c162e2SMete Durlu return 0; 114*01c162e2SMete Durlu } 115*01c162e2SMete Durlu device_initcall(s390_cpuidle_init); 116