blob: 5e7a4c5648d8f94d0f9587213837b31bf7245219 [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 },
119#endif
Tero Kristo682c1282021-06-11 11:45:15 +0300120 { /* sentinel */ }
121};
122
123static int ti_power_domain_probe(struct udevice *dev)
124{
125 struct ti_k3_pd_platdata *data = dev_get_priv(dev);
126 const struct soc_attr *soc_match_data;
127 const struct ti_k3_pd_platdata *pdata;
128
129 printf("%s(dev=%p)\n", __func__, dev);
130
131 if (!data)
132 return -ENOMEM;
133
134 soc_match_data = soc_device_match(ti_k3_soc_pd_data);
135 if (!soc_match_data)
136 return -ENODEV;
137
138 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
139
140 data->psc = pdata->psc;
141 data->pd = pdata->pd;
142 data->lpsc = pdata->lpsc;
143 data->devs = pdata->devs;
144 data->num_psc = pdata->num_psc;
145 data->num_pd = pdata->num_pd;
146 data->num_lpsc = pdata->num_lpsc;
147 data->num_devs = pdata->num_devs;
148
149 return 0;
150}
151
152static int ti_pd_wait(struct ti_pd *pd)
153{
154 u32 ptstat;
Dave Gerlacha3621752022-04-01 20:02:48 -0500155 u32 pdoffset = 0;
156 u32 ptstatreg = PSC_PTSTAT;
Tero Kristo682c1282021-06-11 11:45:15 +0300157 int ret;
158
Dave Gerlacha3621752022-04-01 20:02:48 -0500159 if (pd->id > 31) {
160 pdoffset = 32;
161 ptstatreg = PSC_PTSTAT_H;
162 }
163
164 ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
165 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
Tero Kristo682c1282021-06-11 11:45:15 +0300166
167 if (ret)
168 printf("%s: psc%d, pd%d failed to transition.\n", __func__,
169 pd->psc->id, pd->id);
170
171 return ret;
172}
173
174static void ti_pd_transition(struct ti_pd *pd)
175{
Dave Gerlacha3621752022-04-01 20:02:48 -0500176 u32 pdoffset = 0;
177 u32 ptcmdreg = PSC_PTCMD;
178
179 if (pd->id > 31) {
180 pdoffset = 32;
181 ptcmdreg = PSC_PTCMD_H;
182 }
183
184 psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
Tero Kristo682c1282021-06-11 11:45:15 +0300185}
186
Tero Kristof95d3be2021-06-11 11:45:16 +0300187u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300188{
189 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
190}
191
192static int ti_pd_get(struct ti_pd *pd)
193{
194 u32 pdctl;
195 int ret;
196
197 pd->usecount++;
198
199 if (pd->usecount > 1)
200 return 0;
201
202 if (pd->depend) {
203 ret = ti_pd_get(pd->depend);
204 if (ret)
205 return ret;
206 ti_pd_transition(pd->depend);
207 ret = ti_pd_wait(pd->depend);
208 if (ret)
209 return ret;
210 }
211
212 pdctl = pd_read(pd, PSC_PDCTL);
213
214 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
215 return 0;
216
217 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
218
219 pdctl &= ~PDCTL_STATE_MASK;
220 pdctl |= PDCTL_STATE_ON;
221
222 pd_write(pdctl, pd, PSC_PDCTL);
223
224 return 0;
225}
226
227static int ti_pd_put(struct ti_pd *pd)
228{
229 u32 pdctl;
230 int ret;
231
232 pd->usecount--;
233
234 if (pd->usecount > 0)
235 return 0;
236
237 pdctl = pd_read(pd, PSC_PDCTL);
238 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
239 return 0;
240
241 pdctl &= ~PDCTL_STATE_MASK;
242 pdctl |= PDCTL_STATE_OFF;
243
244 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
245
246 pd_write(pdctl, pd, PSC_PDCTL);
247
248 if (pd->depend) {
249 ti_pd_transition(pd);
250 ret = ti_pd_wait(pd);
251 if (ret)
252 return ret;
253
254 ret = ti_pd_put(pd->depend);
255 if (ret)
256 return ret;
257 ti_pd_transition(pd->depend);
258 ret = ti_pd_wait(pd->depend);
259 if (ret)
260 return ret;
261 }
262
263 return 0;
264}
265
266static int ti_lpsc_wait(struct ti_lpsc *lpsc)
267{
268 u32 mdstat;
269 int ret;
270
271 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
272 mdstat,
273 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
274
275 if (ret)
276 printf("%s: module %d failed to transition.\n", __func__,
277 lpsc->id);
278
279 return ret;
280}
281
Tero Kristof95d3be2021-06-11 11:45:16 +0300282u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300283{
284 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
285}
286
Tero Kristof95d3be2021-06-11 11:45:16 +0300287int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300288{
289 struct ti_pd *psc_pd;
290 int ret;
291 u32 mdctl;
292
293 psc_pd = lpsc->pd;
294
295 if (state == MDSTAT_STATE_ENABLE) {
296 lpsc->usecount++;
297 if (lpsc->usecount > 1)
298 return 0;
299 } else {
300 lpsc->usecount--;
301 if (lpsc->usecount >= 1)
302 return 0;
303 }
304
305 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
306 lpsc->psc->id, lpsc->id, state);
307
308 if (lpsc->depend)
309 ti_lpsc_transition(lpsc->depend, state);
310
311 mdctl = lpsc_read(lpsc, PSC_MDCTL);
312 if ((mdctl & MDSTAT_STATE_MASK) == state)
313 return 0;
314
315 if (state == MDSTAT_STATE_ENABLE)
316 ti_pd_get(psc_pd);
317 else
318 ti_pd_put(psc_pd);
319
320 mdctl &= ~MDSTAT_STATE_MASK;
321 mdctl |= state;
322
323 lpsc_write(mdctl, lpsc, PSC_MDCTL);
324
325 ti_pd_transition(psc_pd);
326 ret = ti_pd_wait(psc_pd);
327 if (ret)
328 return ret;
329
330 return ti_lpsc_wait(lpsc);
331}
332
333static int ti_power_domain_transition(struct power_domain *pd, u8 state)
334{
335 struct ti_lpsc *lpsc = pd->priv;
336
337 return ti_lpsc_transition(lpsc, state);
338}
339
340static int ti_power_domain_on(struct power_domain *pd)
341{
342 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
343
344 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
345}
346
347static int ti_power_domain_off(struct power_domain *pd)
348{
349 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
350
351 return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
352}
353
354static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
355{
356 int idx;
357
358 for (idx = 0; idx < data->num_devs; idx++)
359 if (data->devs[idx].id == id)
360 return data->devs[idx].lpsc;
361
362 return NULL;
363}
364
365static int ti_power_domain_of_xlate(struct power_domain *pd,
366 struct ofnode_phandle_args *args)
367{
368 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
369 struct ti_lpsc *lpsc;
370
371 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
372
373 if (args->args_count < 1) {
374 printf("Invalid args_count: %d\n", args->args_count);
375 return -EINVAL;
376 }
377
378 lpsc = lpsc_lookup(data, args->args[0]);
379 if (!lpsc) {
380 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
381 return -ENOENT;
382 }
383
384 pd->id = lpsc->id;
385 pd->priv = lpsc;
386
387 return 0;
388}
Tero Kristo682c1282021-06-11 11:45:15 +0300389static const struct udevice_id ti_power_domain_of_match[] = {
390 { .compatible = "ti,sci-pm-domain" },
391 { /* sentinel */ }
392};
393
394static struct power_domain_ops ti_power_domain_ops = {
395 .on = ti_power_domain_on,
396 .off = ti_power_domain_off,
397 .of_xlate = ti_power_domain_of_xlate,
Tero Kristo682c1282021-06-11 11:45:15 +0300398};
399
400U_BOOT_DRIVER(ti_pm_domains) = {
401 .name = "ti-pm-domains",
402 .id = UCLASS_POWER_DOMAIN,
403 .of_match = ti_power_domain_of_match,
404 .probe = ti_power_domain_probe,
405 .priv_auto = sizeof(struct ti_k3_pd_platdata),
406 .ops = &ti_power_domain_ops,
407};