File Index
Files and directories
Sign in
FILE

powergate-bpmp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pmdomain/tegra/powergate-bpmp.c C
  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
 14struct tegra_powergate_info {
 15	unsigned int id;
 16	char *name;
 17};
 18
 19struct tegra_powergate {
 20	struct generic_pm_domain genpd;
 21	struct tegra_bpmp *bpmp;
 22	unsigned int id;
 23};
 24
 25static inline struct tegra_powergate *
 26to_tegra_powergate(struct generic_pm_domain *genpd)
 27{
 28	return container_of(genpd, struct tegra_powergate, genpd);
 29}
 30
 31static 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
 57static 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
 87static 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
115static 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
143static 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
149static 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
158static 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
167static struct tegra_powergate *
168tegra_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	powergate->genpd.flags = GENPD_FLAG_NO_STAY_ON;
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
198static 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
212static int
213tegra_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
253static 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
284remove:
285	while (i--) {
286		powergate = to_tegra_powergate(domains[i]);
287		tegra_powergate_remove(powergate);
288	}
289
290	kfree(domains);
291	return err;
292}
293
294static 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
308static struct generic_pm_domain *
309tegra_powergate_xlate(const 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
328int 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
356free:
357	for (i = 0; i < count; i++)
358		kfree(powergates[i].name);
359
360	kfree(powergates);
361	return err;
362}
~/ Read-only file preview.