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 */
15enum 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
23static 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
69static 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
102static 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
115static 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;
174err_out:
175	cps_cpuidle_unregister();
176	return err;
177}
178device_initcall(cps_cpuidle_init);
179