aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2017-03-29 12:34:52 -0400
committerThierry Reding <treding@nvidia.com>2017-06-13 09:23:29 -0400
commite7149a7a3fc4ee6785f17961738f40ce1266d8d0 (patch)
tree5d5576d2113537bf01fd81b0db75e1d8b77fb3ee
parent52b8b80395835c3012bf79fc9d5a1dd82a2d922a (diff)
soc/tegra: bpmp: Implement generic PM domains
The BPMP firmware, found on Tegra186 and later, provides an ABI that can be used to enable and disable power to several power partitions in Tegra SoCs. The ABI allows for enumeration of the available power partitions, so the driver can be reused on future generations, provided the BPMP ABI remains stable. Based on work by Stefan Kristiansson <stefank@nvidia.com> and Mikko Perttunen <mperttunen@nvidia.com>. Signed-off-by: Thierry Reding <treding@nvidia.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/firmware/tegra/bpmp.c4
-rw-r--r--drivers/soc/tegra/Kconfig5
-rw-r--r--drivers/soc/tegra/Makefile1
-rw-r--r--drivers/soc/tegra/powergate-bpmp.c359
-rw-r--r--include/soc/tegra/bpmp.h12
5 files changed, 381 insertions, 0 deletions
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index 84e4c9a58a0c..f11c7025b4a1 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -810,6 +810,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
810 if (err < 0) 810 if (err < 0)
811 goto free_mrq; 811 goto free_mrq;
812 812
813 err = tegra_bpmp_init_powergates(bpmp);
814 if (err < 0)
815 goto free_mrq;
816
813 platform_set_drvdata(pdev, bpmp); 817 platform_set_drvdata(pdev, bpmp);
814 818
815 return 0; 819 return 0;
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index dcf088db40b6..1beb7c347344 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -115,3 +115,8 @@ config SOC_TEGRA_PMC
115 115
116config SOC_TEGRA_PMC_TEGRA186 116config SOC_TEGRA_PMC_TEGRA186
117 bool 117 bool
118
119config SOC_TEGRA_POWERGATE_BPMP
120 def_bool y
121 depends on PM_GENERIC_DOMAINS
122 depends on TEGRA_BPMP
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 4f81dd55e5d1..0e52b45721ac 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -4,3 +4,4 @@ obj-y += common.o
4obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o 4obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
5obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o 5obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
6obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o 6obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
7obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
diff --git a/drivers/soc/tegra/powergate-bpmp.c b/drivers/soc/tegra/powergate-bpmp.c
new file mode 100644
index 000000000000..8fc356039401
--- /dev/null
+++ b/drivers/soc/tegra/powergate-bpmp.c
@@ -0,0 +1,359 @@
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;
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}
diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
index 13dcd44e91bb..9ba65222bd3f 100644
--- a/include/soc/tegra/bpmp.h
+++ b/include/soc/tegra/bpmp.h
@@ -15,6 +15,7 @@
15#define __SOC_TEGRA_BPMP_H 15#define __SOC_TEGRA_BPMP_H
16 16
17#include <linux/mailbox_client.h> 17#include <linux/mailbox_client.h>
18#include <linux/pm_domain.h>
18#include <linux/reset-controller.h> 19#include <linux/reset-controller.h>
19#include <linux/semaphore.h> 20#include <linux/semaphore.h>
20#include <linux/types.h> 21#include <linux/types.h>
@@ -91,6 +92,8 @@ struct tegra_bpmp {
91 unsigned int num_clocks; 92 unsigned int num_clocks;
92 93
93 struct reset_controller_dev rstc; 94 struct reset_controller_dev rstc;
95
96 struct genpd_onecell_data genpd;
94}; 97};
95 98
96struct tegra_bpmp *tegra_bpmp_get(struct device *dev); 99struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
@@ -138,4 +141,13 @@ static inline int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
138} 141}
139#endif 142#endif
140 143
144#if IS_ENABLED(CONFIG_SOC_TEGRA_POWERGATE_BPMP)
145int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp);
146#else
147static inline int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
148{
149 return 0;
150}
151#endif
152
141#endif /* __SOC_TEGRA_BPMP_H */ 153#endif /* __SOC_TEGRA_BPMP_H */