GNU Linux-libre 4.19.264-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         int err;
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
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;
64 }
65
66 static 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;
90         else if (msg.rx.ret < 0)
91                 return -EINVAL;
92
93         return response.get_state.state;
94 }
95
96 static 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;
118         else if (msg.rx.ret < 0)
119                 return -EINVAL;
120
121         return response.get_max_id.max_id;
122 }
123
124 static 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);
146         if (err < 0 || msg.rx.ret < 0)
147                 return NULL;
148
149         return kstrdup(response.get_name.name, GFP_KERNEL);
150 }
151
152 static 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
158 static 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
167 static 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
176 static struct tegra_powergate *
177 tegra_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
206 static 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
220 static int
221 tegra_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
261 static 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
292 remove:
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
302 static 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
316 static struct generic_pm_domain *
317 tegra_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
336 int 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
364 free:
365         for (i = 0; i < count; i++)
366                 kfree(powergates[i].name);
367
368         kfree(powergates);
369         return err;
370 }