xref: /OK3568_Linux_fs/kernel/drivers/cpuidle/cpuidle-exynos.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
4*4882a593Smuzhiyun  *		http://www.samsung.com
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Coupled cpuidle support based on the work of:
7*4882a593Smuzhiyun  *	Colin Cross <ccross@android.com>
8*4882a593Smuzhiyun  *	Daniel Lezcano <daniel.lezcano@linaro.org>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/cpuidle.h>
12*4882a593Smuzhiyun #include <linux/cpu_pm.h>
13*4882a593Smuzhiyun #include <linux/export.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/of.h>
17*4882a593Smuzhiyun #include <linux/platform_data/cpuidle-exynos.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <asm/suspend.h>
20*4882a593Smuzhiyun #include <asm/cpuidle.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun static atomic_t exynos_idle_barrier;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun static struct cpuidle_exynos_data *exynos_cpuidle_pdata;
25*4882a593Smuzhiyun static void (*exynos_enter_aftr)(void);
26*4882a593Smuzhiyun 
exynos_enter_coupled_lowpower(struct cpuidle_device * dev,struct cpuidle_driver * drv,int index)27*4882a593Smuzhiyun static int exynos_enter_coupled_lowpower(struct cpuidle_device *dev,
28*4882a593Smuzhiyun 					 struct cpuidle_driver *drv,
29*4882a593Smuzhiyun 					 int index)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun 	int ret;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	exynos_cpuidle_pdata->pre_enter_aftr();
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	/*
36*4882a593Smuzhiyun 	 * Waiting all cpus to reach this point at the same moment
37*4882a593Smuzhiyun 	 */
38*4882a593Smuzhiyun 	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	/*
41*4882a593Smuzhiyun 	 * Both cpus will reach this point at the same time
42*4882a593Smuzhiyun 	 */
43*4882a593Smuzhiyun 	ret = dev->cpu ? exynos_cpuidle_pdata->cpu1_powerdown()
44*4882a593Smuzhiyun 		       : exynos_cpuidle_pdata->cpu0_enter_aftr();
45*4882a593Smuzhiyun 	if (ret)
46*4882a593Smuzhiyun 		index = ret;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	/*
49*4882a593Smuzhiyun 	 * Waiting all cpus to finish the power sequence before going further
50*4882a593Smuzhiyun 	 */
51*4882a593Smuzhiyun 	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	exynos_cpuidle_pdata->post_enter_aftr();
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	return index;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
exynos_enter_lowpower(struct cpuidle_device * dev,struct cpuidle_driver * drv,int index)58*4882a593Smuzhiyun static int exynos_enter_lowpower(struct cpuidle_device *dev,
59*4882a593Smuzhiyun 				struct cpuidle_driver *drv,
60*4882a593Smuzhiyun 				int index)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	int new_index = index;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	/* AFTR can only be entered when cores other than CPU0 are offline */
65*4882a593Smuzhiyun 	if (num_online_cpus() > 1 || dev->cpu != 0)
66*4882a593Smuzhiyun 		new_index = drv->safe_state_index;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (new_index == 0)
69*4882a593Smuzhiyun 		return arm_cpuidle_simple_enter(dev, drv, new_index);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	exynos_enter_aftr();
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	return new_index;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun static struct cpuidle_driver exynos_idle_driver = {
77*4882a593Smuzhiyun 	.name			= "exynos_idle",
78*4882a593Smuzhiyun 	.owner			= THIS_MODULE,
79*4882a593Smuzhiyun 	.states = {
80*4882a593Smuzhiyun 		[0] = ARM_CPUIDLE_WFI_STATE,
81*4882a593Smuzhiyun 		[1] = {
82*4882a593Smuzhiyun 			.enter			= exynos_enter_lowpower,
83*4882a593Smuzhiyun 			.exit_latency		= 300,
84*4882a593Smuzhiyun 			.target_residency	= 10000,
85*4882a593Smuzhiyun 			.name			= "C1",
86*4882a593Smuzhiyun 			.desc			= "ARM power down",
87*4882a593Smuzhiyun 		},
88*4882a593Smuzhiyun 	},
89*4882a593Smuzhiyun 	.state_count = 2,
90*4882a593Smuzhiyun 	.safe_state_index = 0,
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun static struct cpuidle_driver exynos_coupled_idle_driver = {
94*4882a593Smuzhiyun 	.name			= "exynos_coupled_idle",
95*4882a593Smuzhiyun 	.owner			= THIS_MODULE,
96*4882a593Smuzhiyun 	.states = {
97*4882a593Smuzhiyun 		[0] = ARM_CPUIDLE_WFI_STATE,
98*4882a593Smuzhiyun 		[1] = {
99*4882a593Smuzhiyun 			.enter			= exynos_enter_coupled_lowpower,
100*4882a593Smuzhiyun 			.exit_latency		= 5000,
101*4882a593Smuzhiyun 			.target_residency	= 10000,
102*4882a593Smuzhiyun 			.flags			= CPUIDLE_FLAG_COUPLED |
103*4882a593Smuzhiyun 						  CPUIDLE_FLAG_TIMER_STOP,
104*4882a593Smuzhiyun 			.name			= "C1",
105*4882a593Smuzhiyun 			.desc			= "ARM power down",
106*4882a593Smuzhiyun 		},
107*4882a593Smuzhiyun 	},
108*4882a593Smuzhiyun 	.state_count = 2,
109*4882a593Smuzhiyun 	.safe_state_index = 0,
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun 
exynos_cpuidle_probe(struct platform_device * pdev)112*4882a593Smuzhiyun static int exynos_cpuidle_probe(struct platform_device *pdev)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	int ret;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SMP) &&
117*4882a593Smuzhiyun 	    (of_machine_is_compatible("samsung,exynos4210") ||
118*4882a593Smuzhiyun 	     of_machine_is_compatible("samsung,exynos3250"))) {
119*4882a593Smuzhiyun 		exynos_cpuidle_pdata = pdev->dev.platform_data;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 		ret = cpuidle_register(&exynos_coupled_idle_driver,
122*4882a593Smuzhiyun 				       cpu_possible_mask);
123*4882a593Smuzhiyun 	} else {
124*4882a593Smuzhiyun 		exynos_enter_aftr = (void *)(pdev->dev.platform_data);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 		ret = cpuidle_register(&exynos_idle_driver, NULL);
127*4882a593Smuzhiyun 	}
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	if (ret) {
130*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to register cpuidle driver\n");
131*4882a593Smuzhiyun 		return ret;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun static struct platform_driver exynos_cpuidle_driver = {
138*4882a593Smuzhiyun 	.probe	= exynos_cpuidle_probe,
139*4882a593Smuzhiyun 	.driver = {
140*4882a593Smuzhiyun 		.name = "exynos_cpuidle",
141*4882a593Smuzhiyun 	},
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun builtin_platform_driver(exynos_cpuidle_driver);
144