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