• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
to_tegra_powergate(struct generic_pm_domain * genpd)27 to_tegra_powergate(struct generic_pm_domain *genpd)
28 {
29 	return container_of(genpd, struct tegra_powergate, genpd);
30 }
31 
tegra_bpmp_powergate_set_state(struct tegra_bpmp * bpmp,unsigned int id,u32 state)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 
tegra_bpmp_powergate_get_state(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_bpmp_powergate_get_max_id(struct tegra_bpmp * bpmp)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 
tegra_bpmp_powergate_get_name(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_bpmp_powergate_is_powered(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_powergate_power_on(struct generic_pm_domain * domain)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 
tegra_powergate_power_off(struct generic_pm_domain * domain)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 *
tegra_powergate_add(struct tegra_bpmp * bpmp,const struct tegra_powergate_info * info)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 
tegra_powergate_remove(struct tegra_powergate * powergate)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
tegra_bpmp_probe_powergates(struct tegra_bpmp * bpmp,struct tegra_powergate_info ** powergatesp)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 
tegra_bpmp_add_powergates(struct tegra_bpmp * bpmp,struct tegra_powergate_info * powergates,unsigned int count)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 
tegra_bpmp_remove_powergates(struct tegra_bpmp * bpmp)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 *
tegra_powergate_xlate(struct of_phandle_args * spec,void * data)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 
tegra_bpmp_init_powergates(struct tegra_bpmp * bpmp)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 }
363