xref: /linux/drivers/cpuidle/cpuidle-exynos.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2712eddf7SBartlomiej Zolnierkiewicz /*
3712eddf7SBartlomiej Zolnierkiewicz  * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
43d739985SJaecheol Lee  *		http://www.samsung.com
53d739985SJaecheol Lee  *
6712eddf7SBartlomiej Zolnierkiewicz  * Coupled cpuidle support based on the work of:
7712eddf7SBartlomiej Zolnierkiewicz  *	Colin Cross <ccross@android.com>
8712eddf7SBartlomiej Zolnierkiewicz  *	Daniel Lezcano <daniel.lezcano@linaro.org>
93d739985SJaecheol Lee */
103d739985SJaecheol Lee 
113d739985SJaecheol Lee #include <linux/cpuidle.h>
1267173ca4SAmit Daniel Kachhap #include <linux/cpu_pm.h>
1376ee4557SKyungmin Park #include <linux/export.h>
1484599238SPaul Gortmaker #include <linux/init.h>
1535baa336SBartlomiej Zolnierkiewicz #include <linux/platform_device.h>
16712eddf7SBartlomiej Zolnierkiewicz #include <linux/of.h>
17712eddf7SBartlomiej Zolnierkiewicz #include <linux/platform_data/cpuidle-exynos.h>
183d739985SJaecheol Lee 
1967173ca4SAmit Daniel Kachhap #include <asm/suspend.h>
2006c77b3cSAmit Daniel Kachhap #include <asm/cpuidle.h>
2167173ca4SAmit Daniel Kachhap 
22712eddf7SBartlomiej Zolnierkiewicz static atomic_t exynos_idle_barrier;
23712eddf7SBartlomiej Zolnierkiewicz 
24712eddf7SBartlomiej Zolnierkiewicz static struct cpuidle_exynos_data *exynos_cpuidle_pdata;
25277f5046SDaniel Lezcano static void (*exynos_enter_aftr)(void);
26ccd458c1SKukjin Kim 
27712eddf7SBartlomiej Zolnierkiewicz static int exynos_enter_coupled_lowpower(struct cpuidle_device *dev,
28712eddf7SBartlomiej Zolnierkiewicz 					 struct cpuidle_driver *drv,
29712eddf7SBartlomiej Zolnierkiewicz 					 int index)
30712eddf7SBartlomiej Zolnierkiewicz {
31712eddf7SBartlomiej Zolnierkiewicz 	int ret;
32712eddf7SBartlomiej Zolnierkiewicz 
33712eddf7SBartlomiej Zolnierkiewicz 	exynos_cpuidle_pdata->pre_enter_aftr();
34712eddf7SBartlomiej Zolnierkiewicz 
35712eddf7SBartlomiej Zolnierkiewicz 	/*
36712eddf7SBartlomiej Zolnierkiewicz 	 * Waiting all cpus to reach this point at the same moment
37712eddf7SBartlomiej Zolnierkiewicz 	 */
38712eddf7SBartlomiej Zolnierkiewicz 	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);
39712eddf7SBartlomiej Zolnierkiewicz 
40712eddf7SBartlomiej Zolnierkiewicz 	/*
41712eddf7SBartlomiej Zolnierkiewicz 	 * Both cpus will reach this point at the same time
42712eddf7SBartlomiej Zolnierkiewicz 	 */
43712eddf7SBartlomiej Zolnierkiewicz 	ret = dev->cpu ? exynos_cpuidle_pdata->cpu1_powerdown()
44712eddf7SBartlomiej Zolnierkiewicz 		       : exynos_cpuidle_pdata->cpu0_enter_aftr();
45712eddf7SBartlomiej Zolnierkiewicz 	if (ret)
46712eddf7SBartlomiej Zolnierkiewicz 		index = ret;
47712eddf7SBartlomiej Zolnierkiewicz 
48712eddf7SBartlomiej Zolnierkiewicz 	/*
49712eddf7SBartlomiej Zolnierkiewicz 	 * Waiting all cpus to finish the power sequence before going further
50712eddf7SBartlomiej Zolnierkiewicz 	 */
51712eddf7SBartlomiej Zolnierkiewicz 	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);
52712eddf7SBartlomiej Zolnierkiewicz 
53712eddf7SBartlomiej Zolnierkiewicz 	exynos_cpuidle_pdata->post_enter_aftr();
54712eddf7SBartlomiej Zolnierkiewicz 
55712eddf7SBartlomiej Zolnierkiewicz 	return index;
56712eddf7SBartlomiej Zolnierkiewicz }
57712eddf7SBartlomiej Zolnierkiewicz 
587880e45eSDaniel Lezcano static int exynos_enter_lowpower(struct cpuidle_device *dev,
5967173ca4SAmit Daniel Kachhap 				struct cpuidle_driver *drv,
6067173ca4SAmit Daniel Kachhap 				int index)
6167173ca4SAmit Daniel Kachhap {
6267173ca4SAmit Daniel Kachhap 	int new_index = index;
6367173ca4SAmit Daniel Kachhap 
64118f5c1dSBartlomiej Zolnierkiewicz 	/* AFTR can only be entered when cores other than CPU0 are offline */
65118f5c1dSBartlomiej Zolnierkiewicz 	if (num_online_cpus() > 1 || dev->cpu != 0)
6667173ca4SAmit Daniel Kachhap 		new_index = drv->safe_state_index;
6767173ca4SAmit Daniel Kachhap 
6867173ca4SAmit Daniel Kachhap 	if (new_index == 0)
6906c77b3cSAmit Daniel Kachhap 		return arm_cpuidle_simple_enter(dev, drv, new_index);
7001601b34STomasz Figa 
7101601b34STomasz Figa 	exynos_enter_aftr();
7201601b34STomasz Figa 
7301601b34STomasz Figa 	return new_index;
7467173ca4SAmit Daniel Kachhap }
7567173ca4SAmit Daniel Kachhap 
767880e45eSDaniel Lezcano static struct cpuidle_driver exynos_idle_driver = {
777880e45eSDaniel Lezcano 	.name			= "exynos_idle",
7853af16a1SDaniel Lezcano 	.owner			= THIS_MODULE,
7953af16a1SDaniel Lezcano 	.states = {
8053af16a1SDaniel Lezcano 		[0] = ARM_CPUIDLE_WFI_STATE,
8153af16a1SDaniel Lezcano 		[1] = {
827880e45eSDaniel Lezcano 			.enter			= exynos_enter_lowpower,
8353af16a1SDaniel Lezcano 			.exit_latency		= 300,
84c324f43aSMarek Szyprowski 			.target_residency	= 10000,
8553af16a1SDaniel Lezcano 			.name			= "C1",
8653af16a1SDaniel Lezcano 			.desc			= "ARM power down",
8753af16a1SDaniel Lezcano 		},
8853af16a1SDaniel Lezcano 	},
8953af16a1SDaniel Lezcano 	.state_count = 2,
9053af16a1SDaniel Lezcano 	.safe_state_index = 0,
9153af16a1SDaniel Lezcano };
9253af16a1SDaniel Lezcano 
93712eddf7SBartlomiej Zolnierkiewicz static struct cpuidle_driver exynos_coupled_idle_driver = {
94712eddf7SBartlomiej Zolnierkiewicz 	.name			= "exynos_coupled_idle",
95712eddf7SBartlomiej Zolnierkiewicz 	.owner			= THIS_MODULE,
96712eddf7SBartlomiej Zolnierkiewicz 	.states = {
97712eddf7SBartlomiej Zolnierkiewicz 		[0] = ARM_CPUIDLE_WFI_STATE,
98712eddf7SBartlomiej Zolnierkiewicz 		[1] = {
99712eddf7SBartlomiej Zolnierkiewicz 			.enter			= exynos_enter_coupled_lowpower,
100712eddf7SBartlomiej Zolnierkiewicz 			.exit_latency		= 5000,
101712eddf7SBartlomiej Zolnierkiewicz 			.target_residency	= 10000,
102712eddf7SBartlomiej Zolnierkiewicz 			.flags			= CPUIDLE_FLAG_COUPLED |
103712eddf7SBartlomiej Zolnierkiewicz 						  CPUIDLE_FLAG_TIMER_STOP,
104712eddf7SBartlomiej Zolnierkiewicz 			.name			= "C1",
105712eddf7SBartlomiej Zolnierkiewicz 			.desc			= "ARM power down",
106712eddf7SBartlomiej Zolnierkiewicz 		},
107712eddf7SBartlomiej Zolnierkiewicz 	},
108712eddf7SBartlomiej Zolnierkiewicz 	.state_count = 2,
109712eddf7SBartlomiej Zolnierkiewicz 	.safe_state_index = 0,
110712eddf7SBartlomiej Zolnierkiewicz };
111712eddf7SBartlomiej Zolnierkiewicz 
112f612a4fbSJingoo Han static int exynos_cpuidle_probe(struct platform_device *pdev)
1133d739985SJaecheol Lee {
114043c86b6SDaniel Lezcano 	int ret;
1153d739985SJaecheol Lee 
116cfdda353SBartlomiej Zolnierkiewicz 	if (IS_ENABLED(CONFIG_SMP) &&
1178919d779SMarek Szyprowski 	    (of_machine_is_compatible("samsung,exynos4210") ||
1188919d779SMarek Szyprowski 	     of_machine_is_compatible("samsung,exynos3250"))) {
119712eddf7SBartlomiej Zolnierkiewicz 		exynos_cpuidle_pdata = pdev->dev.platform_data;
120712eddf7SBartlomiej Zolnierkiewicz 
121712eddf7SBartlomiej Zolnierkiewicz 		ret = cpuidle_register(&exynos_coupled_idle_driver,
122712eddf7SBartlomiej Zolnierkiewicz 				       cpu_possible_mask);
123712eddf7SBartlomiej Zolnierkiewicz 	} else {
124277f5046SDaniel Lezcano 		exynos_enter_aftr = (void *)(pdev->dev.platform_data);
125277f5046SDaniel Lezcano 
1267880e45eSDaniel Lezcano 		ret = cpuidle_register(&exynos_idle_driver, NULL);
127712eddf7SBartlomiej Zolnierkiewicz 	}
128712eddf7SBartlomiej Zolnierkiewicz 
1295db9f436SDaniel Lezcano 	if (ret) {
130ae7c4c87SJingoo Han 		dev_err(&pdev->dev, "failed to register cpuidle driver\n");
1315db9f436SDaniel Lezcano 		return ret;
13246bcfad7SDeepthi Dharwar 	}
1333d739985SJaecheol Lee 
1343d739985SJaecheol Lee 	return 0;
1353d739985SJaecheol Lee }
13635baa336SBartlomiej Zolnierkiewicz 
13735baa336SBartlomiej Zolnierkiewicz static struct platform_driver exynos_cpuidle_driver = {
13835baa336SBartlomiej Zolnierkiewicz 	.probe	= exynos_cpuidle_probe,
13935baa336SBartlomiej Zolnierkiewicz 	.driver = {
14035baa336SBartlomiej Zolnierkiewicz 		.name = "exynos_cpuidle",
14135baa336SBartlomiej Zolnierkiewicz 	},
14235baa336SBartlomiej Zolnierkiewicz };
14384599238SPaul Gortmaker builtin_platform_driver(exynos_cpuidle_driver);
144