• 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 
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 
tegra_bpmp_powergate_get_state(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_bpmp_powergate_get_max_id(struct tegra_bpmp * bpmp)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 
tegra_bpmp_powergate_get_name(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_bpmp_powergate_is_powered(struct tegra_bpmp * bpmp,unsigned int id)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 
tegra_powergate_power_on(struct generic_pm_domain * domain)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 
tegra_powergate_power_off(struct generic_pm_domain * domain)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 *
tegra_powergate_add(struct tegra_bpmp * bpmp,const struct tegra_powergate_info * info)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 
tegra_powergate_remove(struct tegra_powergate * powergate)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
tegra_bpmp_probe_powergates(struct tegra_bpmp * bpmp,struct tegra_powergate_info ** powergatesp)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 
tegra_bpmp_add_powergates(struct tegra_bpmp * bpmp,struct tegra_powergate_info * powergates,unsigned int count)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 
tegra_bpmp_remove_powergates(struct tegra_bpmp * bpmp)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 *
tegra_powergate_xlate(struct of_phandle_args * spec,void * data)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 
tegra_bpmp_init_powergates(struct tegra_bpmp * bpmp)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 }
360