blob: c35193073400c2b92fe4bd0bed55f6a8d6ea4300 [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 },
Andrew Davisbb6cc992025-03-19 13:54:58 -050097#endif
98#if IS_ENABLED(CONFIG_SOC_K3_J7200)
Tero Kristo682c1282021-06-11 11:45:15 +030099 {
100 .family = "J7200",
101 .data = &j7200_pd_platdata,
102 },
Bryan Brattlof1c621052024-03-12 15:20:20 -0500103#endif
104#if IS_ENABLED(CONFIG_SOC_K3_J721S2)
David Huang7078f352022-01-25 20:56:34 +0530105 {
106 .family = "J721S2",
107 .data = &j721s2_pd_platdata,
108 },
Tero Kristo682c1282021-06-11 11:45:15 +0300109#endif
Jayesh Choudhary9332a6372024-06-12 14:41:16 +0530110#if IS_ENABLED(CONFIG_SOC_K3_J722S)
111 {
112 .family = "J722S",
113 .data = &j722s_pd_platdata,
114 },
115#endif
Bryan Brattlof9a83dcd2024-03-12 15:20:21 -0500116#if IS_ENABLED(CONFIG_SOC_K3_J784S4)
Apurva Nandanb93ab922024-02-24 01:51:44 +0530117 {
118 .family = "J784S4",
119 .data = &j784s4_pd_platdata,
120 },
Manorit Chawdhryb5a384e2025-03-17 10:24:24 +0530121 {
122 .family = "J742S2",
123 .data = &j784s4_pd_platdata,
124 },
Apurva Nandanb93ab922024-02-24 01:51:44 +0530125#endif
Tero Kristo682c1282021-06-11 11:45:15 +0300126 { /* sentinel */ }
127};
128
129static int ti_power_domain_probe(struct udevice *dev)
130{
131 struct ti_k3_pd_platdata *data = dev_get_priv(dev);
132 const struct soc_attr *soc_match_data;
133 const struct ti_k3_pd_platdata *pdata;
134
135 printf("%s(dev=%p)\n", __func__, dev);
136
137 if (!data)
138 return -ENOMEM;
139
140 soc_match_data = soc_device_match(ti_k3_soc_pd_data);
141 if (!soc_match_data)
142 return -ENODEV;
143
144 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
145
146 data->psc = pdata->psc;
147 data->pd = pdata->pd;
148 data->lpsc = pdata->lpsc;
149 data->devs = pdata->devs;
150 data->num_psc = pdata->num_psc;
151 data->num_pd = pdata->num_pd;
152 data->num_lpsc = pdata->num_lpsc;
153 data->num_devs = pdata->num_devs;
154
155 return 0;
156}
157
158static int ti_pd_wait(struct ti_pd *pd)
159{
160 u32 ptstat;
Dave Gerlacha3621752022-04-01 20:02:48 -0500161 u32 pdoffset = 0;
162 u32 ptstatreg = PSC_PTSTAT;
Tero Kristo682c1282021-06-11 11:45:15 +0300163 int ret;
164
Dave Gerlacha3621752022-04-01 20:02:48 -0500165 if (pd->id > 31) {
166 pdoffset = 32;
167 ptstatreg = PSC_PTSTAT_H;
168 }
169
170 ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
171 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
Tero Kristo682c1282021-06-11 11:45:15 +0300172
173 if (ret)
174 printf("%s: psc%d, pd%d failed to transition.\n", __func__,
175 pd->psc->id, pd->id);
176
177 return ret;
178}
179
180static void ti_pd_transition(struct ti_pd *pd)
181{
Dave Gerlacha3621752022-04-01 20:02:48 -0500182 u32 pdoffset = 0;
183 u32 ptcmdreg = PSC_PTCMD;
184
185 if (pd->id > 31) {
186 pdoffset = 32;
187 ptcmdreg = PSC_PTCMD_H;
188 }
189
190 psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
Tero Kristo682c1282021-06-11 11:45:15 +0300191}
192
Tero Kristof95d3be2021-06-11 11:45:16 +0300193u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300194{
195 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
196}
197
198static int ti_pd_get(struct ti_pd *pd)
199{
200 u32 pdctl;
201 int ret;
202
203 pd->usecount++;
204
205 if (pd->usecount > 1)
206 return 0;
207
208 if (pd->depend) {
209 ret = ti_pd_get(pd->depend);
210 if (ret)
211 return ret;
212 ti_pd_transition(pd->depend);
213 ret = ti_pd_wait(pd->depend);
214 if (ret)
215 return ret;
216 }
217
218 pdctl = pd_read(pd, PSC_PDCTL);
219
220 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
221 return 0;
222
223 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
224
225 pdctl &= ~PDCTL_STATE_MASK;
226 pdctl |= PDCTL_STATE_ON;
227
228 pd_write(pdctl, pd, PSC_PDCTL);
229
230 return 0;
231}
232
233static int ti_pd_put(struct ti_pd *pd)
234{
235 u32 pdctl;
236 int ret;
237
238 pd->usecount--;
239
240 if (pd->usecount > 0)
241 return 0;
242
243 pdctl = pd_read(pd, PSC_PDCTL);
244 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
245 return 0;
246
247 pdctl &= ~PDCTL_STATE_MASK;
248 pdctl |= PDCTL_STATE_OFF;
249
250 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
251
252 pd_write(pdctl, pd, PSC_PDCTL);
253
254 if (pd->depend) {
255 ti_pd_transition(pd);
256 ret = ti_pd_wait(pd);
257 if (ret)
258 return ret;
259
260 ret = ti_pd_put(pd->depend);
261 if (ret)
262 return ret;
263 ti_pd_transition(pd->depend);
264 ret = ti_pd_wait(pd->depend);
265 if (ret)
266 return ret;
267 }
268
269 return 0;
270}
271
272static int ti_lpsc_wait(struct ti_lpsc *lpsc)
273{
274 u32 mdstat;
275 int ret;
276
277 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
278 mdstat,
279 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
280
281 if (ret)
282 printf("%s: module %d failed to transition.\n", __func__,
283 lpsc->id);
284
285 return ret;
286}
287
Tero Kristof95d3be2021-06-11 11:45:16 +0300288u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300289{
290 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
291}
292
Tero Kristof95d3be2021-06-11 11:45:16 +0300293int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300294{
295 struct ti_pd *psc_pd;
296 int ret;
297 u32 mdctl;
298
299 psc_pd = lpsc->pd;
300
301 if (state == MDSTAT_STATE_ENABLE) {
302 lpsc->usecount++;
303 if (lpsc->usecount > 1)
304 return 0;
305 } else {
306 lpsc->usecount--;
307 if (lpsc->usecount >= 1)
308 return 0;
309 }
310
311 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
312 lpsc->psc->id, lpsc->id, state);
313
314 if (lpsc->depend)
315 ti_lpsc_transition(lpsc->depend, state);
316
317 mdctl = lpsc_read(lpsc, PSC_MDCTL);
318 if ((mdctl & MDSTAT_STATE_MASK) == state)
319 return 0;
320
321 if (state == MDSTAT_STATE_ENABLE)
322 ti_pd_get(psc_pd);
323 else
324 ti_pd_put(psc_pd);
325
326 mdctl &= ~MDSTAT_STATE_MASK;
327 mdctl |= state;
328
329 lpsc_write(mdctl, lpsc, PSC_MDCTL);
330
331 ti_pd_transition(psc_pd);
332 ret = ti_pd_wait(psc_pd);
333 if (ret)
334 return ret;
335
336 return ti_lpsc_wait(lpsc);
337}
338
339static int ti_power_domain_transition(struct power_domain *pd, u8 state)
340{
341 struct ti_lpsc *lpsc = pd->priv;
342
343 return ti_lpsc_transition(lpsc, state);
344}
345
346static int ti_power_domain_on(struct power_domain *pd)
347{
348 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
349
350 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
351}
352
353static int ti_power_domain_off(struct power_domain *pd)
354{
355 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
356
357 return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
358}
359
360static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
361{
362 int idx;
363
364 for (idx = 0; idx < data->num_devs; idx++)
365 if (data->devs[idx].id == id)
366 return data->devs[idx].lpsc;
367
368 return NULL;
369}
370
371static int ti_power_domain_of_xlate(struct power_domain *pd,
372 struct ofnode_phandle_args *args)
373{
374 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
375 struct ti_lpsc *lpsc;
376
377 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
378
379 if (args->args_count < 1) {
380 printf("Invalid args_count: %d\n", args->args_count);
381 return -EINVAL;
382 }
383
384 lpsc = lpsc_lookup(data, args->args[0]);
385 if (!lpsc) {
386 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
387 return -ENOENT;
388 }
389
390 pd->id = lpsc->id;
391 pd->priv = lpsc;
392
393 return 0;
394}
Tero Kristo682c1282021-06-11 11:45:15 +0300395static const struct udevice_id ti_power_domain_of_match[] = {
396 { .compatible = "ti,sci-pm-domain" },
397 { /* sentinel */ }
398};
399
400static struct power_domain_ops ti_power_domain_ops = {
401 .on = ti_power_domain_on,
402 .off = ti_power_domain_off,
403 .of_xlate = ti_power_domain_of_xlate,
Tero Kristo682c1282021-06-11 11:45:15 +0300404};
405
406U_BOOT_DRIVER(ti_pm_domains) = {
407 .name = "ti-pm-domains",
408 .id = UCLASS_POWER_DOMAIN,
409 .of_match = ti_power_domain_of_match,
410 .probe = ti_power_domain_probe,
411 .priv_auto = sizeof(struct ti_k3_pd_platdata),
412 .ops = &ti_power_domain_ops,
413};