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