Merge tag 'for-linus' of git://git.armlinux.org.uk/~rmk/linux-arm
[linux-2.6-microblaze.git] / drivers / cpuidle / cpuidle-cps.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2014 Imagination Technologies
4  * Author: Paul Burton <paul.burton@mips.com>
5  */
6
7 #include <linux/cpu_pm.h>
8 #include <linux/cpuidle.h>
9 #include <linux/init.h>
10
11 #include <asm/idle.h>
12 #include <asm/pm-cps.h>
13
14 /* Enumeration of the various idle states this driver may enter */
15 enum cps_idle_state {
16         STATE_WAIT = 0,         /* MIPS wait instruction, coherent */
17         STATE_NC_WAIT,          /* MIPS wait instruction, non-coherent */
18         STATE_CLOCK_GATED,      /* Core clock gated */
19         STATE_POWER_GATED,      /* Core power gated */
20         STATE_COUNT
21 };
22
23 static int cps_nc_enter(struct cpuidle_device *dev,
24                         struct cpuidle_driver *drv, int index)
25 {
26         enum cps_pm_state pm_state;
27         int err;
28
29         /*
30          * At least one core must remain powered up & clocked in order for the
31          * system to have any hope of functioning.
32          *
33          * TODO: don't treat core 0 specially, just prevent the final core
34          * TODO: remap interrupt affinity temporarily
35          */
36         if (cpus_are_siblings(0, dev->cpu) && (index > STATE_NC_WAIT))
37                 index = STATE_NC_WAIT;
38
39         /* Select the appropriate cps_pm_state */
40         switch (index) {
41         case STATE_NC_WAIT:
42                 pm_state = CPS_PM_NC_WAIT;
43                 break;
44         case STATE_CLOCK_GATED:
45                 pm_state = CPS_PM_CLOCK_GATED;
46                 break;
47         case STATE_POWER_GATED:
48                 pm_state = CPS_PM_POWER_GATED;
49                 break;
50         default:
51                 BUG();
52                 return -EINVAL;
53         }
54
55         /* Notify listeners the CPU is about to power down */
56         if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
57                 return -EINTR;
58
59         /* Enter that state */
60         err = cps_pm_enter_state(pm_state);
61
62         /* Notify listeners the CPU is back up */
63         if (pm_state == CPS_PM_POWER_GATED)
64                 cpu_pm_exit();
65
66         return err ?: index;
67 }
68
69 static struct cpuidle_driver cps_driver = {
70         .name                   = "cpc_cpuidle",
71         .owner                  = THIS_MODULE,
72         .states = {
73                 [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
74                 [STATE_NC_WAIT] = {
75                         .enter  = cps_nc_enter,
76                         .exit_latency           = 200,
77                         .target_residency       = 450,
78                         .name   = "nc-wait",
79                         .desc   = "non-coherent MIPS wait",
80                 },
81                 [STATE_CLOCK_GATED] = {
82                         .enter  = cps_nc_enter,
83                         .exit_latency           = 300,
84                         .target_residency       = 700,
85                         .flags  = CPUIDLE_FLAG_TIMER_STOP,
86                         .name   = "clock-gated",
87                         .desc   = "core clock gated",
88                 },
89                 [STATE_POWER_GATED] = {
90                         .enter  = cps_nc_enter,
91                         .exit_latency           = 600,
92                         .target_residency       = 1000,
93                         .flags  = CPUIDLE_FLAG_TIMER_STOP,
94                         .name   = "power-gated",
95                         .desc   = "core power gated",
96                 },
97         },
98         .state_count            = STATE_COUNT,
99         .safe_state_index       = 0,
100 };
101
102 static void __init cps_cpuidle_unregister(void)
103 {
104         int cpu;
105         struct cpuidle_device *device;
106
107         for_each_possible_cpu(cpu) {
108                 device = &per_cpu(cpuidle_dev, cpu);
109                 cpuidle_unregister_device(device);
110         }
111
112         cpuidle_unregister_driver(&cps_driver);
113 }
114
115 static int __init cps_cpuidle_init(void)
116 {
117         int err, cpu, i;
118         struct cpuidle_device *device;
119
120         /* Detect supported states */
121         if (!cps_pm_support_state(CPS_PM_POWER_GATED))
122                 cps_driver.state_count = STATE_CLOCK_GATED + 1;
123         if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
124                 cps_driver.state_count = STATE_NC_WAIT + 1;
125         if (!cps_pm_support_state(CPS_PM_NC_WAIT))
126                 cps_driver.state_count = STATE_WAIT + 1;
127
128         /* Inform the user if some states are unavailable */
129         if (cps_driver.state_count < STATE_COUNT) {
130                 pr_info("cpuidle-cps: limited to ");
131                 switch (cps_driver.state_count - 1) {
132                 case STATE_WAIT:
133                         pr_cont("coherent wait\n");
134                         break;
135                 case STATE_NC_WAIT:
136                         pr_cont("non-coherent wait\n");
137                         break;
138                 case STATE_CLOCK_GATED:
139                         pr_cont("clock gating\n");
140                         break;
141                 }
142         }
143
144         /*
145          * Set the coupled flag on the appropriate states if this system
146          * requires it.
147          */
148         if (coupled_coherence)
149                 for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
150                         cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
151
152         err = cpuidle_register_driver(&cps_driver);
153         if (err) {
154                 pr_err("Failed to register CPS cpuidle driver\n");
155                 return err;
156         }
157
158         for_each_possible_cpu(cpu) {
159                 device = &per_cpu(cpuidle_dev, cpu);
160                 device->cpu = cpu;
161 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
162                 cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
163 #endif
164
165                 err = cpuidle_register_device(device);
166                 if (err) {
167                         pr_err("Failed to register CPU%d cpuidle device\n",
168                                cpu);
169                         goto err_out;
170                 }
171         }
172
173         return 0;
174 err_out:
175         cps_cpuidle_unregister();
176         return err;
177 }
178 device_initcall(cps_cpuidle_init);