GNU Linux-libre 4.14.259-gnu1
[releases.git] / drivers / soc / tegra / powergate-bpmp.c
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
23 struct tegra_powergate_info {
24         unsigned int id;
25         char *name;
26 };
27
28 struct tegra_powergate {
29         struct generic_pm_domain genpd;
30         struct tegra_bpmp *bpmp;
31         unsigned int id;
32 };
33
34 static inline struct tegra_powergate *
35 to_tegra_powergate(struct generic_pm_domain *genpd)
36 {
37         return container_of(genpd, struct tegra_powergate, genpd);
38 }
39
40 static 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
59 static 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
87 static 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
113 static 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
141 static 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
147 static 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
156 static 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
165 static struct tegra_powergate *
166 tegra_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
195 static 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
209 static int
210 tegra_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
250 static 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
281 remove:
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
291 static 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
305 static struct generic_pm_domain *
306 tegra_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
325 int 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
353 free:
354         for (i = 0; i < count; i++)
355                 kfree(powergates[i].name);
356
357         kfree(powergates);
358         return err;
359 }