xref: /linux/drivers/cpuidle/cpuidle-s390.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
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