]> git.ipfire.org Git - thirdparty/linux.git/blame - drivers/soc/tegra/powergate-bpmp.c
lib/test_stackinit: Handle Clang auto-initialization pattern
[thirdparty/linux.git] / drivers / soc / tegra / powergate-bpmp.c
CommitLineData
e7149a7a
TR
1/*
2 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#include <linux/of.h>
15#include <linux/platform_device.h>
16#include <linux/pm_domain.h>
17#include <linux/slab.h>
18#include <linux/version.h>
19
20#include <soc/tegra/bpmp.h>
21#include <soc/tegra/bpmp-abi.h>
22
23struct tegra_powergate_info {
24 unsigned int id;
25 char *name;
26};
27
28struct tegra_powergate {
29 struct generic_pm_domain genpd;
30 struct tegra_bpmp *bpmp;
31 unsigned int id;
32};
33
34static inline struct tegra_powergate *
35to_tegra_powergate(struct generic_pm_domain *genpd)
36{
37 return container_of(genpd, struct tegra_powergate, genpd);
38}
39
40static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
41 unsigned int id, u32 state)
42{
43 struct mrq_pg_request request;
44 struct tegra_bpmp_message msg;
775dba87 45 int err;
e7149a7a
TR
46
47 memset(&request, 0, sizeof(request));
48 request.cmd = CMD_PG_SET_STATE;
49 request.id = id;
50 request.set_state.state = state;
51
52 memset(&msg, 0, sizeof(msg));
53 msg.mrq = MRQ_PG;
54 msg.tx.data = &request;
55 msg.tx.size = sizeof(request);
56
775dba87
TA
57 err = tegra_bpmp_transfer(bpmp, &msg);
58 if (err < 0)
59 return err;
60 else if (msg.rx.ret < 0)
61 return -EINVAL;
62
63 return 0;
e7149a7a
TR
64}
65
66static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
67 unsigned int id)
68{
69 struct mrq_pg_response response;
70 struct mrq_pg_request request;
71 struct tegra_bpmp_message msg;
72 int err;
73
74 memset(&request, 0, sizeof(request));
75 request.cmd = CMD_PG_GET_STATE;
76 request.id = id;
77
78 memset(&response, 0, sizeof(response));
79
80 memset(&msg, 0, sizeof(msg));
81 msg.mrq = MRQ_PG;
82 msg.tx.data = &request;
83 msg.tx.size = sizeof(request);
84 msg.rx.data = &response;
85 msg.rx.size = sizeof(response);
86
87 err = tegra_bpmp_transfer(bpmp, &msg);
88 if (err < 0)
89 return PG_STATE_OFF;
775dba87
TA
90 else if (msg.rx.ret < 0)
91 return -EINVAL;
e7149a7a
TR
92
93 return response.get_state.state;
94}
95
96static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
97{
98 struct mrq_pg_response response;
99 struct mrq_pg_request request;
100 struct tegra_bpmp_message msg;
101 int err;
102
103 memset(&request, 0, sizeof(request));
104 request.cmd = CMD_PG_GET_MAX_ID;
105
106 memset(&response, 0, sizeof(response));
107
108 memset(&msg, 0, sizeof(msg));
109 msg.mrq = MRQ_PG;
110 msg.tx.data = &request;
111 msg.tx.size = sizeof(request);
112 msg.rx.data = &response;
113 msg.rx.size = sizeof(response);
114
115 err = tegra_bpmp_transfer(bpmp, &msg);
116 if (err < 0)
117 return err;
775dba87
TA
118 else if (msg.rx.ret < 0)
119 return -EINVAL;
e7149a7a
TR
120
121 return response.get_max_id.max_id;
122}
123
124static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
125 unsigned int id)
126{
127 struct mrq_pg_response response;
128 struct mrq_pg_request request;
129 struct tegra_bpmp_message msg;
130 int err;
131
132 memset(&request, 0, sizeof(request));
133 request.cmd = CMD_PG_GET_NAME;
134 request.id = id;
135
136 memset(&response, 0, sizeof(response));
137
138 memset(&msg, 0, sizeof(msg));
139 msg.mrq = MRQ_PG;
140 msg.tx.data = &request;
141 msg.tx.size = sizeof(request);
142 msg.rx.data = &response;
143 msg.rx.size = sizeof(response);
144
145 err = tegra_bpmp_transfer(bpmp, &msg);
775dba87 146 if (err < 0 || msg.rx.ret < 0)
e7149a7a
TR
147 return NULL;
148
149 return kstrdup(response.get_name.name, GFP_KERNEL);
150}
151
152static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
153 unsigned int id)
154{
155 return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
156}
157
158static int tegra_powergate_power_on(struct generic_pm_domain *domain)
159{
160 struct tegra_powergate *powergate = to_tegra_powergate(domain);
161 struct tegra_bpmp *bpmp = powergate->bpmp;
162
163 return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
164 PG_STATE_ON);
165}
166
167static int tegra_powergate_power_off(struct generic_pm_domain *domain)
168{
169 struct tegra_powergate *powergate = to_tegra_powergate(domain);
170 struct tegra_bpmp *bpmp = powergate->bpmp;
171
172 return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
173 PG_STATE_OFF);
174}
175
176static struct tegra_powergate *
177tegra_powergate_add(struct tegra_bpmp *bpmp,
178 const struct tegra_powergate_info *info)
179{
180 struct tegra_powergate *powergate;
181 bool off;
182 int err;
183
184 off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
185
186 powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
187 if (!powergate)
188 return ERR_PTR(-ENOMEM);
189
190 powergate->id = info->id;
191 powergate->bpmp = bpmp;
192
193 powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
194 powergate->genpd.power_on = tegra_powergate_power_on;
195 powergate->genpd.power_off = tegra_powergate_power_off;
196
197 err = pm_genpd_init(&powergate->genpd, NULL, off);
198 if (err < 0) {
199 kfree(powergate->genpd.name);
200 return ERR_PTR(err);
201 }
202
203 return powergate;
204}
205
206static void tegra_powergate_remove(struct tegra_powergate *powergate)
207{
208 struct generic_pm_domain *genpd = &powergate->genpd;
209 struct tegra_bpmp *bpmp = powergate->bpmp;
210 int err;
211
212 err = pm_genpd_remove(genpd);
213 if (err < 0)
214 dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
215 genpd->name, err);
216
217 kfree(genpd->name);
218}
219
220static int
221tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
222 struct tegra_powergate_info **powergatesp)
223{
224 struct tegra_powergate_info *powergates;
225 unsigned int max_id, id, count = 0;
226 unsigned int num_holes = 0;
227 int err;
228
229 err = tegra_bpmp_powergate_get_max_id(bpmp);
230 if (err < 0)
231 return err;
232
233 max_id = err;
234
235 dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
236
237 powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
238 if (!powergates)
239 return -ENOMEM;
240
241 for (id = 0; id <= max_id; id++) {
242 struct tegra_powergate_info *info = &powergates[count];
243
244 info->name = tegra_bpmp_powergate_get_name(bpmp, id);
245 if (!info->name || info->name[0] == '\0') {
246 num_holes++;
247 continue;
248 }
249
250 info->id = id;
251 count++;
252 }
253
254 dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
255
256 *powergatesp = powergates;
257
258 return count;
259}
260
261static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
262 struct tegra_powergate_info *powergates,
263 unsigned int count)
264{
265 struct genpd_onecell_data *genpd = &bpmp->genpd;
266 struct generic_pm_domain **domains;
267 struct tegra_powergate *powergate;
268 unsigned int i;
269 int err;
270
271 domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
272 if (!domains)
273 return -ENOMEM;
274
275 for (i = 0; i < count; i++) {
276 powergate = tegra_powergate_add(bpmp, &powergates[i]);
277 if (IS_ERR(powergate)) {
278 err = PTR_ERR(powergate);
279 goto remove;
280 }
281
282 dev_dbg(bpmp->dev, "added power domain %s\n",
283 powergate->genpd.name);
284 domains[i] = &powergate->genpd;
285 }
286
287 genpd->num_domains = count;
288 genpd->domains = domains;
289
290 return 0;
291
292remove:
293 while (i--) {
294 powergate = to_tegra_powergate(domains[i]);
295 tegra_powergate_remove(powergate);
296 }
297
298 kfree(genpd->domains);
299 return err;
300}
301
302static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
303{
304 struct genpd_onecell_data *genpd = &bpmp->genpd;
305 unsigned int i = genpd->num_domains;
306 struct tegra_powergate *powergate;
307
308 while (i--) {
309 dev_dbg(bpmp->dev, "removing power domain %s\n",
310 genpd->domains[i]->name);
311 powergate = to_tegra_powergate(genpd->domains[i]);
312 tegra_powergate_remove(powergate);
313 }
314}
315
316static struct generic_pm_domain *
317tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
318{
319 struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
320 struct genpd_onecell_data *genpd = data;
321 unsigned int i;
322
323 for (i = 0; i < genpd->num_domains; i++) {
324 struct tegra_powergate *powergate;
325
326 powergate = to_tegra_powergate(genpd->domains[i]);
327 if (powergate->id == spec->args[0]) {
328 domain = &powergate->genpd;
329 break;
330 }
331 }
332
333 return domain;
334}
335
336int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
337{
338 struct device_node *np = bpmp->dev->of_node;
339 struct tegra_powergate_info *powergates;
340 struct device *dev = bpmp->dev;
341 unsigned int count, i;
342 int err;
343
344 err = tegra_bpmp_probe_powergates(bpmp, &powergates);
345 if (err < 0)
346 return err;
347
348 count = err;
349
350 dev_dbg(dev, "%u power domains probed\n", count);
351
352 err = tegra_bpmp_add_powergates(bpmp, powergates, count);
353 if (err < 0)
354 goto free;
355
356 bpmp->genpd.xlate = tegra_powergate_xlate;
357
358 err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
359 if (err < 0) {
360 dev_err(dev, "failed to add power domain provider: %d\n", err);
361 tegra_bpmp_remove_powergates(bpmp);
362 }
363
364free:
365 for (i = 0; i < count; i++)
366 kfree(powergates[i].name);
367
368 kfree(powergates);
369 return err;
370}