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