Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/cpuidle/dt_idle_genpd.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* PM domains for CPUs via genpd.
4
*
5
* Copyright (C) 2019 Linaro Ltd.
6
* Author: Ulf Hansson <[email protected]>
7
*
8
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
9
* Copyright (c) 2022 Ventana Micro Systems Inc.
10
*/
11
12
#define pr_fmt(fmt) "dt-idle-genpd: " fmt
13
14
#include <linux/cpu.h>
15
#include <linux/device.h>
16
#include <linux/kernel.h>
17
#include <linux/pm_domain.h>
18
#include <linux/pm_runtime.h>
19
#include <linux/slab.h>
20
#include <linux/string.h>
21
22
#include "dt_idle_genpd.h"
23
24
static int pd_parse_state_nodes(
25
int (*parse_state)(struct device_node *, u32 *),
26
struct genpd_power_state *states, int state_count)
27
{
28
int i, ret;
29
u32 state, *state_buf;
30
31
for (i = 0; i < state_count; i++) {
32
ret = parse_state(to_of_node(states[i].fwnode), &state);
33
if (ret)
34
goto free_state;
35
36
state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
37
if (!state_buf) {
38
ret = -ENOMEM;
39
goto free_state;
40
}
41
*state_buf = state;
42
states[i].data = state_buf;
43
}
44
45
return 0;
46
47
free_state:
48
i--;
49
for (; i >= 0; i--)
50
kfree(states[i].data);
51
return ret;
52
}
53
54
static int pd_parse_states(struct device_node *np,
55
int (*parse_state)(struct device_node *, u32 *),
56
struct genpd_power_state **states,
57
int *state_count)
58
{
59
int ret;
60
61
/* Parse the domain idle states. */
62
ret = of_genpd_parse_idle_states(np, states, state_count);
63
if (ret)
64
return ret;
65
66
/* Fill out the dt specifics for each found state. */
67
ret = pd_parse_state_nodes(parse_state, *states, *state_count);
68
if (ret)
69
kfree(*states);
70
71
return ret;
72
}
73
74
static void pd_free_states(struct genpd_power_state *states,
75
unsigned int state_count)
76
{
77
int i;
78
79
for (i = 0; i < state_count; i++)
80
kfree(states[i].data);
81
kfree(states);
82
}
83
84
void dt_idle_pd_free(struct generic_pm_domain *pd)
85
{
86
pd_free_states(pd->states, pd->state_count);
87
kfree(pd->name);
88
kfree(pd);
89
}
90
91
struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
92
int (*parse_state)(struct device_node *, u32 *))
93
{
94
struct generic_pm_domain *pd;
95
struct genpd_power_state *states = NULL;
96
int ret, state_count = 0;
97
98
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
99
if (!pd)
100
goto out;
101
102
pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
103
if (!pd->name)
104
goto free_pd;
105
106
/*
107
* Parse the domain idle states and let genpd manage the state selection
108
* for those being compatible with "domain-idle-state".
109
*/
110
ret = pd_parse_states(np, parse_state, &states, &state_count);
111
if (ret)
112
goto free_name;
113
114
pd->free_states = pd_free_states;
115
pd->name = kbasename(pd->name);
116
pd->states = states;
117
pd->state_count = state_count;
118
119
pr_debug("alloc PM domain %s\n", pd->name);
120
return pd;
121
122
free_name:
123
kfree(pd->name);
124
free_pd:
125
kfree(pd);
126
out:
127
pr_err("failed to alloc PM domain %pOF\n", np);
128
return NULL;
129
}
130
131
int dt_idle_pd_init_topology(struct device_node *np)
132
{
133
struct of_phandle_args child, parent;
134
int ret;
135
136
for_each_child_of_node_scoped(np, node) {
137
if (of_parse_phandle_with_args(node, "power-domains",
138
"#power-domain-cells", 0, &parent))
139
continue;
140
141
child.np = node;
142
child.args_count = 0;
143
ret = of_genpd_add_subdomain(&parent, &child);
144
of_node_put(parent.np);
145
if (ret)
146
return ret;
147
}
148
149
return 0;
150
}
151
152
int dt_idle_pd_remove_topology(struct device_node *np)
153
{
154
struct of_phandle_args child, parent;
155
int ret;
156
157
for_each_child_of_node_scoped(np, node) {
158
if (of_parse_phandle_with_args(node, "power-domains",
159
"#power-domain-cells", 0, &parent))
160
continue;
161
162
child.np = node;
163
child.args_count = 0;
164
ret = of_genpd_remove_subdomain(&parent, &child);
165
of_node_put(parent.np);
166
if (ret)
167
return ret;
168
}
169
170
return 0;
171
}
172
173
struct device *dt_idle_attach_cpu(int cpu, const char *name)
174
{
175
struct device *dev;
176
177
dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), name);
178
if (IS_ERR_OR_NULL(dev))
179
return dev;
180
181
pm_runtime_irq_safe(dev);
182
if (cpu_online(cpu))
183
pm_runtime_get_sync(dev);
184
185
dev_pm_syscore_device(dev, true);
186
187
return dev;
188
}
189
190
void dt_idle_detach_cpu(struct device *dev)
191
{
192
if (IS_ERR_OR_NULL(dev))
193
return;
194
195
dev_pm_domain_detach(dev, false);
196
}
197
198