1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments power domain driver
4 *
5 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/
6 *	Tero Kristo <t-kristo@ti.com>
7 */
8
9#include <asm/io.h>
10#include <common.h>
11#include <dm.h>
12#include <errno.h>
13#include <power-domain-uclass.h>
14#include <soc.h>
15#include <k3-dev.h>
16#include <linux/iopoll.h>
17
18#define PSC_PTCMD		0x120
19#define PSC_PTCMD_H		0x124
20#define PSC_PTSTAT		0x128
21#define PSC_PTSTAT_H		0x12C
22#define PSC_PDSTAT		0x200
23#define PSC_PDCTL		0x300
24#define PSC_MDSTAT		0x800
25#define PSC_MDCTL		0xa00
26
27#define PDCTL_STATE_MASK		0x1
28#define PDCTL_STATE_OFF			0x0
29#define PDCTL_STATE_ON			0x1
30
31#define MDSTAT_STATE_MASK		0x3f
32#define MDSTAT_BUSY_MASK		0x30
33#define MDSTAT_STATE_SWRSTDISABLE	0x0
34#define MDSTAT_STATE_ENABLE		0x3
35
36#define LPSC_TIMEOUT		1000
37#define PD_TIMEOUT		1000
38
39static u32 psc_read(struct ti_psc *psc, u32 reg)
40{
41	u32 val;
42
43	val = readl(psc->base + reg);
44	debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
45	return val;
46}
47
48static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
49{
50	debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
51	writel(val, psc->base + reg);
52}
53
54static u32 pd_read(struct ti_pd *pd, u32 reg)
55{
56	return psc_read(pd->psc, reg + 4 * pd->id);
57}
58
59static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
60{
61	psc_write(val, pd->psc, reg + 4 * pd->id);
62}
63
64static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
65{
66	return psc_read(lpsc->psc, reg + 4 * lpsc->id);
67}
68
69static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
70{
71	psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
72}
73
74static const struct soc_attr ti_k3_soc_pd_data[] = {
75#if IS_ENABLED(CONFIG_SOC_K3_J721E)
76	{
77		.family = "J721E",
78		.data = &j721e_pd_platdata,
79	},
80	{
81		.family = "J7200",
82		.data = &j7200_pd_platdata,
83	},
84#endif
85#if IS_ENABLED(CONFIG_SOC_K3_J721S2)
86	{
87		.family = "J721S2",
88		.data = &j721s2_pd_platdata,
89	},
90#endif
91#if IS_ENABLED(CONFIG_SOC_K3_AM625)
92	{
93		.family = "AM62X",
94		.data = &am62x_pd_platdata,
95	},
96#endif
97#if IS_ENABLED(CONFIG_SOC_K3_AM62A7)
98	{
99		.family = "AM62AX",
100		.data = &am62ax_pd_platdata,
101	},
102#endif
103#if IS_ENABLED(CONFIG_SOC_K3_J784S4)
104	{
105		.family = "J784S4",
106		.data = &j784s4_pd_platdata,
107	},
108#endif
109#if IS_ENABLED(CONFIG_SOC_K3_AM62P5)
110	{
111		.family = "AM62PX",
112		.data = &am62px_pd_platdata,
113	},
114#endif
115	{ /* sentinel */ }
116};
117
118static int ti_power_domain_probe(struct udevice *dev)
119{
120	struct ti_k3_pd_platdata *data = dev_get_priv(dev);
121	const struct soc_attr *soc_match_data;
122	const struct ti_k3_pd_platdata *pdata;
123
124	printf("%s(dev=%p)\n", __func__, dev);
125
126	if (!data)
127		return -ENOMEM;
128
129	soc_match_data = soc_device_match(ti_k3_soc_pd_data);
130	if (!soc_match_data)
131		return -ENODEV;
132
133	pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
134
135	data->psc = pdata->psc;
136	data->pd = pdata->pd;
137	data->lpsc = pdata->lpsc;
138	data->devs = pdata->devs;
139	data->num_psc = pdata->num_psc;
140	data->num_pd = pdata->num_pd;
141	data->num_lpsc = pdata->num_lpsc;
142	data->num_devs = pdata->num_devs;
143
144	return 0;
145}
146
147static int ti_pd_wait(struct ti_pd *pd)
148{
149	u32 ptstat;
150	u32 pdoffset = 0;
151	u32 ptstatreg = PSC_PTSTAT;
152	int ret;
153
154	if (pd->id > 31) {
155		pdoffset = 32;
156		ptstatreg = PSC_PTSTAT_H;
157	}
158
159	ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
160				 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
161
162	if (ret)
163		printf("%s: psc%d, pd%d failed to transition.\n", __func__,
164		       pd->psc->id, pd->id);
165
166	return ret;
167}
168
169static void ti_pd_transition(struct ti_pd *pd)
170{
171	u32 pdoffset = 0;
172	u32 ptcmdreg = PSC_PTCMD;
173
174	if (pd->id > 31) {
175		pdoffset = 32;
176		ptcmdreg = PSC_PTCMD_H;
177	}
178
179	psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
180}
181
182u8 ti_pd_state(struct ti_pd *pd)
183{
184	return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
185}
186
187static int ti_pd_get(struct ti_pd *pd)
188{
189	u32 pdctl;
190	int ret;
191
192	pd->usecount++;
193
194	if (pd->usecount > 1)
195		return 0;
196
197	if (pd->depend) {
198		ret = ti_pd_get(pd->depend);
199		if (ret)
200			return ret;
201		ti_pd_transition(pd->depend);
202		ret = ti_pd_wait(pd->depend);
203		if (ret)
204			return ret;
205	}
206
207	pdctl = pd_read(pd, PSC_PDCTL);
208
209	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
210		return 0;
211
212	debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
213
214	pdctl &= ~PDCTL_STATE_MASK;
215	pdctl |= PDCTL_STATE_ON;
216
217	pd_write(pdctl, pd, PSC_PDCTL);
218
219	return 0;
220}
221
222static int ti_pd_put(struct ti_pd *pd)
223{
224	u32 pdctl;
225	int ret;
226
227	pd->usecount--;
228
229	if (pd->usecount > 0)
230		return 0;
231
232	pdctl = pd_read(pd, PSC_PDCTL);
233	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
234		return 0;
235
236	pdctl &= ~PDCTL_STATE_MASK;
237	pdctl |= PDCTL_STATE_OFF;
238
239	debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
240
241	pd_write(pdctl, pd, PSC_PDCTL);
242
243	if (pd->depend) {
244		ti_pd_transition(pd);
245		ret = ti_pd_wait(pd);
246		if (ret)
247			return ret;
248
249		ret = ti_pd_put(pd->depend);
250		if (ret)
251			return ret;
252		ti_pd_transition(pd->depend);
253		ret = ti_pd_wait(pd->depend);
254		if (ret)
255			return ret;
256	}
257
258	return 0;
259}
260
261static int ti_lpsc_wait(struct ti_lpsc *lpsc)
262{
263	u32 mdstat;
264	int ret;
265
266	ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
267				 mdstat,
268				 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
269
270	if (ret)
271		printf("%s: module %d failed to transition.\n", __func__,
272		       lpsc->id);
273
274	return ret;
275}
276
277u8 lpsc_get_state(struct ti_lpsc *lpsc)
278{
279	return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
280}
281
282int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
283{
284	struct ti_pd *psc_pd;
285	int ret;
286	u32 mdctl;
287
288	psc_pd = lpsc->pd;
289
290	if (state == MDSTAT_STATE_ENABLE) {
291		lpsc->usecount++;
292		if (lpsc->usecount > 1)
293			return 0;
294	} else {
295		lpsc->usecount--;
296		if (lpsc->usecount >= 1)
297			return 0;
298	}
299
300	debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
301	      lpsc->psc->id, lpsc->id, state);
302
303	if (lpsc->depend)
304		ti_lpsc_transition(lpsc->depend, state);
305
306	mdctl = lpsc_read(lpsc, PSC_MDCTL);
307	if ((mdctl & MDSTAT_STATE_MASK) == state)
308		return 0;
309
310	if (state == MDSTAT_STATE_ENABLE)
311		ti_pd_get(psc_pd);
312	else
313		ti_pd_put(psc_pd);
314
315	mdctl &= ~MDSTAT_STATE_MASK;
316	mdctl |= state;
317
318	lpsc_write(mdctl, lpsc, PSC_MDCTL);
319
320	ti_pd_transition(psc_pd);
321	ret = ti_pd_wait(psc_pd);
322	if (ret)
323		return ret;
324
325	return ti_lpsc_wait(lpsc);
326}
327
328static int ti_power_domain_transition(struct power_domain *pd, u8 state)
329{
330	struct ti_lpsc *lpsc = pd->priv;
331
332	return ti_lpsc_transition(lpsc, state);
333}
334
335static int ti_power_domain_on(struct power_domain *pd)
336{
337	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
338
339	return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
340}
341
342static int ti_power_domain_off(struct power_domain *pd)
343{
344	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
345
346	return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
347}
348
349static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
350{
351	int idx;
352
353	for (idx = 0; idx < data->num_devs; idx++)
354		if (data->devs[idx].id == id)
355			return data->devs[idx].lpsc;
356
357	return NULL;
358}
359
360static int ti_power_domain_of_xlate(struct power_domain *pd,
361				    struct ofnode_phandle_args *args)
362{
363	struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
364	struct ti_lpsc *lpsc;
365
366	debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
367
368	if (args->args_count < 1) {
369		printf("Invalid args_count: %d\n", args->args_count);
370		return -EINVAL;
371	}
372
373	lpsc = lpsc_lookup(data, args->args[0]);
374	if (!lpsc) {
375		printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
376		return -ENOENT;
377	}
378
379	pd->id = lpsc->id;
380	pd->priv = lpsc;
381
382	return 0;
383}
384static const struct udevice_id ti_power_domain_of_match[] = {
385	{ .compatible = "ti,sci-pm-domain" },
386	{ /* sentinel */ }
387};
388
389static struct power_domain_ops ti_power_domain_ops = {
390	.on = ti_power_domain_on,
391	.off = ti_power_domain_off,
392	.of_xlate = ti_power_domain_of_xlate,
393};
394
395U_BOOT_DRIVER(ti_pm_domains) = {
396	.name = "ti-pm-domains",
397	.id = UCLASS_POWER_DOMAIN,
398	.of_match = ti_power_domain_of_match,
399	.probe = ti_power_domain_probe,
400	.priv_auto = sizeof(struct ti_k3_pd_platdata),
401	.ops = &ti_power_domain_ops,
402};
403