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