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