blob: 8fc3560394013dd051e8bda5c0b5ac455c1646a3 [file] [log] [blame]
Thierry Redinge7149a72017-03-29 18:34:52 +02001/*
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;
45
46 memset(&request, 0, sizeof(request));
47 request.cmd = CMD_PG_SET_STATE;
48 request.id = id;
49 request.set_state.state = state;
50
51 memset(&msg, 0, sizeof(msg));
52 msg.mrq = MRQ_PG;
53 msg.tx.data = &request;
54 msg.tx.size = sizeof(request);
55
56 return tegra_bpmp_transfer(bpmp, &msg);
57}
58
59static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
60 unsigned int id)
61{
62 struct mrq_pg_response response;
63 struct mrq_pg_request request;
64 struct tegra_bpmp_message msg;
65 int err;
66
67 memset(&request, 0, sizeof(request));
68 request.cmd = CMD_PG_GET_STATE;
69 request.id = id;
70
71 memset(&response, 0, sizeof(response));
72
73 memset(&msg, 0, sizeof(msg));
74 msg.mrq = MRQ_PG;
75 msg.tx.data = &request;
76 msg.tx.size = sizeof(request);
77 msg.rx.data = &response;
78 msg.rx.size = sizeof(response);
79
80 err = tegra_bpmp_transfer(bpmp, &msg);
81 if (err < 0)
82 return PG_STATE_OFF;
83
84 return response.get_state.state;
85}
86
87static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
88{
89 struct mrq_pg_response response;
90 struct mrq_pg_request request;
91 struct tegra_bpmp_message msg;
92 int err;
93
94 memset(&request, 0, sizeof(request));
95 request.cmd = CMD_PG_GET_MAX_ID;
96
97 memset(&response, 0, sizeof(response));
98
99 memset(&msg, 0, sizeof(msg));
100 msg.mrq = MRQ_PG;
101 msg.tx.data = &request;
102 msg.tx.size = sizeof(request);
103 msg.rx.data = &response;
104 msg.rx.size = sizeof(response);
105
106 err = tegra_bpmp_transfer(bpmp, &msg);
107 if (err < 0)
108 return err;
109
110 return response.get_max_id.max_id;
111}
112
113static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
114 unsigned int id)
115{
116 struct mrq_pg_response response;
117 struct mrq_pg_request request;
118 struct tegra_bpmp_message msg;
119 int err;
120
121 memset(&request, 0, sizeof(request));
122 request.cmd = CMD_PG_GET_NAME;
123 request.id = id;
124
125 memset(&response, 0, sizeof(response));
126
127 memset(&msg, 0, sizeof(msg));
128 msg.mrq = MRQ_PG;
129 msg.tx.data = &request;
130 msg.tx.size = sizeof(request);
131 msg.rx.data = &response;
132 msg.rx.size = sizeof(response);
133
134 err = tegra_bpmp_transfer(bpmp, &msg);
135 if (err < 0)
136 return NULL;
137
138 return kstrdup(response.get_name.name, GFP_KERNEL);
139}
140
141static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
142 unsigned int id)
143{
144 return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
145}
146
147static int tegra_powergate_power_on(struct generic_pm_domain *domain)
148{
149 struct tegra_powergate *powergate = to_tegra_powergate(domain);
150 struct tegra_bpmp *bpmp = powergate->bpmp;
151
152 return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
153 PG_STATE_ON);
154}
155
156static int tegra_powergate_power_off(struct generic_pm_domain *domain)
157{
158 struct tegra_powergate *powergate = to_tegra_powergate(domain);
159 struct tegra_bpmp *bpmp = powergate->bpmp;
160
161 return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
162 PG_STATE_OFF);
163}
164
165static struct tegra_powergate *
166tegra_powergate_add(struct tegra_bpmp *bpmp,
167 const struct tegra_powergate_info *info)
168{
169 struct tegra_powergate *powergate;
170 bool off;
171 int err;
172
173 off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
174
175 powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
176 if (!powergate)
177 return ERR_PTR(-ENOMEM);
178
179 powergate->id = info->id;
180 powergate->bpmp = bpmp;
181
182 powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
183 powergate->genpd.power_on = tegra_powergate_power_on;
184 powergate->genpd.power_off = tegra_powergate_power_off;
185
186 err = pm_genpd_init(&powergate->genpd, NULL, off);
187 if (err < 0) {
188 kfree(powergate->genpd.name);
189 return ERR_PTR(err);
190 }
191
192 return powergate;
193}
194
195static void tegra_powergate_remove(struct tegra_powergate *powergate)
196{
197 struct generic_pm_domain *genpd = &powergate->genpd;
198 struct tegra_bpmp *bpmp = powergate->bpmp;
199 int err;
200
201 err = pm_genpd_remove(genpd);
202 if (err < 0)
203 dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
204 genpd->name, err);
205
206 kfree(genpd->name);
207}
208
209static int
210tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
211 struct tegra_powergate_info **powergatesp)
212{
213 struct tegra_powergate_info *powergates;
214 unsigned int max_id, id, count = 0;
215 unsigned int num_holes = 0;
216 int err;
217
218 err = tegra_bpmp_powergate_get_max_id(bpmp);
219 if (err < 0)
220 return err;
221
222 max_id = err;
223
224 dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
225
226 powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
227 if (!powergates)
228 return -ENOMEM;
229
230 for (id = 0; id <= max_id; id++) {
231 struct tegra_powergate_info *info = &powergates[count];
232
233 info->name = tegra_bpmp_powergate_get_name(bpmp, id);
234 if (!info->name || info->name[0] == '\0') {
235 num_holes++;
236 continue;
237 }
238
239 info->id = id;
240 count++;
241 }
242
243 dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
244
245 *powergatesp = powergates;
246
247 return count;
248}
249
250static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
251 struct tegra_powergate_info *powergates,
252 unsigned int count)
253{
254 struct genpd_onecell_data *genpd = &bpmp->genpd;
255 struct generic_pm_domain **domains;
256 struct tegra_powergate *powergate;
257 unsigned int i;
258 int err;
259
260 domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
261 if (!domains)
262 return -ENOMEM;
263
264 for (i = 0; i < count; i++) {
265 powergate = tegra_powergate_add(bpmp, &powergates[i]);
266 if (IS_ERR(powergate)) {
267 err = PTR_ERR(powergate);
268 goto remove;
269 }
270
271 dev_dbg(bpmp->dev, "added power domain %s\n",
272 powergate->genpd.name);
273 domains[i] = &powergate->genpd;
274 }
275
276 genpd->num_domains = count;
277 genpd->domains = domains;
278
279 return 0;
280
281remove:
282 while (i--) {
283 powergate = to_tegra_powergate(domains[i]);
284 tegra_powergate_remove(powergate);
285 }
286
287 kfree(genpd->domains);
288 return err;
289}
290
291static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
292{
293 struct genpd_onecell_data *genpd = &bpmp->genpd;
294 unsigned int i = genpd->num_domains;
295 struct tegra_powergate *powergate;
296
297 while (i--) {
298 dev_dbg(bpmp->dev, "removing power domain %s\n",
299 genpd->domains[i]->name);
300 powergate = to_tegra_powergate(genpd->domains[i]);
301 tegra_powergate_remove(powergate);
302 }
303}
304
305static struct generic_pm_domain *
306tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
307{
308 struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
309 struct genpd_onecell_data *genpd = data;
310 unsigned int i;
311
312 for (i = 0; i < genpd->num_domains; i++) {
313 struct tegra_powergate *powergate;
314
315 powergate = to_tegra_powergate(genpd->domains[i]);
316 if (powergate->id == spec->args[0]) {
317 domain = &powergate->genpd;
318 break;
319 }
320 }
321
322 return domain;
323}
324
325int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
326{
327 struct device_node *np = bpmp->dev->of_node;
328 struct tegra_powergate_info *powergates;
329 struct device *dev = bpmp->dev;
330 unsigned int count, i;
331 int err;
332
333 err = tegra_bpmp_probe_powergates(bpmp, &powergates);
334 if (err < 0)
335 return err;
336
337 count = err;
338
339 dev_dbg(dev, "%u power domains probed\n", count);
340
341 err = tegra_bpmp_add_powergates(bpmp, powergates, count);
342 if (err < 0)
343 goto free;
344
345 bpmp->genpd.xlate = tegra_powergate_xlate;
346
347 err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
348 if (err < 0) {
349 dev_err(dev, "failed to add power domain provider: %d\n", err);
350 tegra_bpmp_remove_powergates(bpmp);
351 }
352
353free:
354 for (i = 0; i < count; i++)
355 kfree(powergates[i].name);
356
357 kfree(powergates);
358 return err;
359}