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