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