blob: fb4ca2dd6b45fc7f9588648fb1343d94f2201863 [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 },
David Huang7078f352022-01-25 20:56:34 +053084#elif CONFIG_SOC_K3_J721S2
85 {
86 .family = "J721S2",
87 .data = &j721s2_pd_platdata,
88 },
Tero Kristo682c1282021-06-11 11:45:15 +030089#endif
Suman Annaa9768c92022-05-25 13:38:43 +053090#ifdef CONFIG_SOC_K3_AM625
91 {
92 .family = "AM62X",
93 .data = &am62x_pd_platdata,
94 },
95#endif
Bryan Brattlof2d982b72022-11-03 19:13:56 -050096#ifdef CONFIG_SOC_K3_AM62A7
97 {
98 .family = "AM62AX",
99 .data = &am62ax_pd_platdata,
100 },
101#endif
Apurva Nandanb93ab922024-02-24 01:51:44 +0530102#ifdef CONFIG_SOC_K3_J784S4
103 {
104 .family = "J784S4",
105 .data = &j784s4_pd_platdata,
106 },
107#endif
Tero Kristo682c1282021-06-11 11:45:15 +0300108 { /* sentinel */ }
109};
110
111static int ti_power_domain_probe(struct udevice *dev)
112{
113 struct ti_k3_pd_platdata *data = dev_get_priv(dev);
114 const struct soc_attr *soc_match_data;
115 const struct ti_k3_pd_platdata *pdata;
116
117 printf("%s(dev=%p)\n", __func__, dev);
118
119 if (!data)
120 return -ENOMEM;
121
122 soc_match_data = soc_device_match(ti_k3_soc_pd_data);
123 if (!soc_match_data)
124 return -ENODEV;
125
126 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
127
128 data->psc = pdata->psc;
129 data->pd = pdata->pd;
130 data->lpsc = pdata->lpsc;
131 data->devs = pdata->devs;
132 data->num_psc = pdata->num_psc;
133 data->num_pd = pdata->num_pd;
134 data->num_lpsc = pdata->num_lpsc;
135 data->num_devs = pdata->num_devs;
136
137 return 0;
138}
139
140static int ti_pd_wait(struct ti_pd *pd)
141{
142 u32 ptstat;
Dave Gerlacha3621752022-04-01 20:02:48 -0500143 u32 pdoffset = 0;
144 u32 ptstatreg = PSC_PTSTAT;
Tero Kristo682c1282021-06-11 11:45:15 +0300145 int ret;
146
Dave Gerlacha3621752022-04-01 20:02:48 -0500147 if (pd->id > 31) {
148 pdoffset = 32;
149 ptstatreg = PSC_PTSTAT_H;
150 }
151
152 ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
153 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
Tero Kristo682c1282021-06-11 11:45:15 +0300154
155 if (ret)
156 printf("%s: psc%d, pd%d failed to transition.\n", __func__,
157 pd->psc->id, pd->id);
158
159 return ret;
160}
161
162static void ti_pd_transition(struct ti_pd *pd)
163{
Dave Gerlacha3621752022-04-01 20:02:48 -0500164 u32 pdoffset = 0;
165 u32 ptcmdreg = PSC_PTCMD;
166
167 if (pd->id > 31) {
168 pdoffset = 32;
169 ptcmdreg = PSC_PTCMD_H;
170 }
171
172 psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
Tero Kristo682c1282021-06-11 11:45:15 +0300173}
174
Tero Kristof95d3be2021-06-11 11:45:16 +0300175u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300176{
177 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
178}
179
180static int ti_pd_get(struct ti_pd *pd)
181{
182 u32 pdctl;
183 int ret;
184
185 pd->usecount++;
186
187 if (pd->usecount > 1)
188 return 0;
189
190 if (pd->depend) {
191 ret = ti_pd_get(pd->depend);
192 if (ret)
193 return ret;
194 ti_pd_transition(pd->depend);
195 ret = ti_pd_wait(pd->depend);
196 if (ret)
197 return ret;
198 }
199
200 pdctl = pd_read(pd, PSC_PDCTL);
201
202 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
203 return 0;
204
205 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
206
207 pdctl &= ~PDCTL_STATE_MASK;
208 pdctl |= PDCTL_STATE_ON;
209
210 pd_write(pdctl, pd, PSC_PDCTL);
211
212 return 0;
213}
214
215static int ti_pd_put(struct ti_pd *pd)
216{
217 u32 pdctl;
218 int ret;
219
220 pd->usecount--;
221
222 if (pd->usecount > 0)
223 return 0;
224
225 pdctl = pd_read(pd, PSC_PDCTL);
226 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
227 return 0;
228
229 pdctl &= ~PDCTL_STATE_MASK;
230 pdctl |= PDCTL_STATE_OFF;
231
232 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
233
234 pd_write(pdctl, pd, PSC_PDCTL);
235
236 if (pd->depend) {
237 ti_pd_transition(pd);
238 ret = ti_pd_wait(pd);
239 if (ret)
240 return ret;
241
242 ret = ti_pd_put(pd->depend);
243 if (ret)
244 return ret;
245 ti_pd_transition(pd->depend);
246 ret = ti_pd_wait(pd->depend);
247 if (ret)
248 return ret;
249 }
250
251 return 0;
252}
253
254static int ti_lpsc_wait(struct ti_lpsc *lpsc)
255{
256 u32 mdstat;
257 int ret;
258
259 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
260 mdstat,
261 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
262
263 if (ret)
264 printf("%s: module %d failed to transition.\n", __func__,
265 lpsc->id);
266
267 return ret;
268}
269
Tero Kristof95d3be2021-06-11 11:45:16 +0300270u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300271{
272 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
273}
274
Tero Kristof95d3be2021-06-11 11:45:16 +0300275int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300276{
277 struct ti_pd *psc_pd;
278 int ret;
279 u32 mdctl;
280
281 psc_pd = lpsc->pd;
282
283 if (state == MDSTAT_STATE_ENABLE) {
284 lpsc->usecount++;
285 if (lpsc->usecount > 1)
286 return 0;
287 } else {
288 lpsc->usecount--;
289 if (lpsc->usecount >= 1)
290 return 0;
291 }
292
293 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
294 lpsc->psc->id, lpsc->id, state);
295
296 if (lpsc->depend)
297 ti_lpsc_transition(lpsc->depend, state);
298
299 mdctl = lpsc_read(lpsc, PSC_MDCTL);
300 if ((mdctl & MDSTAT_STATE_MASK) == state)
301 return 0;
302
303 if (state == MDSTAT_STATE_ENABLE)
304 ti_pd_get(psc_pd);
305 else
306 ti_pd_put(psc_pd);
307
308 mdctl &= ~MDSTAT_STATE_MASK;
309 mdctl |= state;
310
311 lpsc_write(mdctl, lpsc, PSC_MDCTL);
312
313 ti_pd_transition(psc_pd);
314 ret = ti_pd_wait(psc_pd);
315 if (ret)
316 return ret;
317
318 return ti_lpsc_wait(lpsc);
319}
320
321static int ti_power_domain_transition(struct power_domain *pd, u8 state)
322{
323 struct ti_lpsc *lpsc = pd->priv;
324
325 return ti_lpsc_transition(lpsc, state);
326}
327
328static int ti_power_domain_on(struct power_domain *pd)
329{
330 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
331
332 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
333}
334
335static int ti_power_domain_off(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_SWRSTDISABLE);
340}
341
342static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
343{
344 int idx;
345
346 for (idx = 0; idx < data->num_devs; idx++)
347 if (data->devs[idx].id == id)
348 return data->devs[idx].lpsc;
349
350 return NULL;
351}
352
353static int ti_power_domain_of_xlate(struct power_domain *pd,
354 struct ofnode_phandle_args *args)
355{
356 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
357 struct ti_lpsc *lpsc;
358
359 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
360
361 if (args->args_count < 1) {
362 printf("Invalid args_count: %d\n", args->args_count);
363 return -EINVAL;
364 }
365
366 lpsc = lpsc_lookup(data, args->args[0]);
367 if (!lpsc) {
368 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
369 return -ENOENT;
370 }
371
372 pd->id = lpsc->id;
373 pd->priv = lpsc;
374
375 return 0;
376}
Tero Kristo682c1282021-06-11 11:45:15 +0300377static const struct udevice_id ti_power_domain_of_match[] = {
378 { .compatible = "ti,sci-pm-domain" },
379 { /* sentinel */ }
380};
381
382static struct power_domain_ops ti_power_domain_ops = {
383 .on = ti_power_domain_on,
384 .off = ti_power_domain_off,
385 .of_xlate = ti_power_domain_of_xlate,
Tero Kristo682c1282021-06-11 11:45:15 +0300386};
387
388U_BOOT_DRIVER(ti_pm_domains) = {
389 .name = "ti-pm-domains",
390 .id = UCLASS_POWER_DOMAIN,
391 .of_match = ti_power_domain_of_match,
392 .probe = ti_power_domain_probe,
393 .priv_auto = sizeof(struct ti_k3_pd_platdata),
394 .ops = &ti_power_domain_ops,
395};