blob: 8996c40ddc03101be1d40ac250b024c97a7f1983 [file] [log] [blame]
Tero Kristo682c1282021-06-11 11:45:15 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments power domain driver
4 *
Nishanth Menoneaa39c62023-11-01 15:56:03 -05005 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/
Tero Kristo682c1282021-06-11 11:45:15 +03006 * 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
Dave Gerlacha3621752022-04-01 20:02:48 -050019#define PSC_PTCMD_H 0x124
Tero Kristo682c1282021-06-11 11:45:15 +030020#define PSC_PTSTAT 0x128
Dave Gerlacha3621752022-04-01 20:02:48 -050021#define PSC_PTSTAT_H 0x12C
Tero Kristo682c1282021-06-11 11:45:15 +030022#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 },
Bryan Brattlof1c621052024-03-12 15:20:20 -050084#endif
85#if IS_ENABLED(CONFIG_SOC_K3_J721S2)
David Huang7078f352022-01-25 20:56:34 +053086 {
87 .family = "J721S2",
88 .data = &j721s2_pd_platdata,
89 },
Tero Kristo682c1282021-06-11 11:45:15 +030090#endif
Bryan Brattlof1c621052024-03-12 15:20:20 -050091#if IS_ENABLED(CONFIG_SOC_K3_AM625)
Suman Annaa9768c92022-05-25 13:38:43 +053092 {
93 .family = "AM62X",
94 .data = &am62x_pd_platdata,
95 },
96#endif
Bryan Brattlof1c621052024-03-12 15:20:20 -050097#if IS_ENABLED(CONFIG_SOC_K3_AM62A7)
Bryan Brattlof2d982b72022-11-03 19:13:56 -050098 {
99 .family = "AM62AX",
100 .data = &am62ax_pd_platdata,
101 },
102#endif
Bryan Brattlof9a83dcd2024-03-12 15:20:21 -0500103#if IS_ENABLED(CONFIG_SOC_K3_J784S4)
Apurva Nandanb93ab922024-02-24 01:51:44 +0530104 {
105 .family = "J784S4",
106 .data = &j784s4_pd_platdata,
107 },
108#endif
Bryan Brattlof9a83dcd2024-03-12 15:20:21 -0500109#if IS_ENABLED(CONFIG_SOC_K3_AM62P5)
110 {
111 .family = "AM62PX",
112 .data = &am62px_pd_platdata,
113 },
114#endif
Tero Kristo682c1282021-06-11 11:45:15 +0300115 { /* 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;
Dave Gerlacha3621752022-04-01 20:02:48 -0500150 u32 pdoffset = 0;
151 u32 ptstatreg = PSC_PTSTAT;
Tero Kristo682c1282021-06-11 11:45:15 +0300152 int ret;
153
Dave Gerlacha3621752022-04-01 20:02:48 -0500154 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);
Tero Kristo682c1282021-06-11 11:45:15 +0300161
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{
Dave Gerlacha3621752022-04-01 20:02:48 -0500171 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);
Tero Kristo682c1282021-06-11 11:45:15 +0300180}
181
Tero Kristof95d3be2021-06-11 11:45:16 +0300182u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300183{
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
Tero Kristof95d3be2021-06-11 11:45:16 +0300277u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300278{
279 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
280}
281
Tero Kristof95d3be2021-06-11 11:45:16 +0300282int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300283{
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}
Tero Kristo682c1282021-06-11 11:45:15 +0300384static 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,
Tero Kristo682c1282021-06-11 11:45:15 +0300393};
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};