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