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
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
197static 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
211static int
212tegra_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
252static 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
283remove:
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
293static 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
307static struct generic_pm_domain *
308tegra_powergate_xlate(const 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
327int 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
355free:
356	for (i = 0; i < count; i++)
357		kfree(powergates[i].name);
358
359	kfree(powergates);
360	return err;
361}
362