blob: 9e7151307c8933d51104218637c28b33dac1fc8b [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 *
5 * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/
6 * 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
Tero Kristo682c1282021-06-11 11:45:15 +0300102 { /* sentinel */ }
103};
104
105static int ti_power_domain_probe(struct udevice *dev)
106{
107 struct ti_k3_pd_platdata *data = dev_get_priv(dev);
108 const struct soc_attr *soc_match_data;
109 const struct ti_k3_pd_platdata *pdata;
110
111 printf("%s(dev=%p)\n", __func__, dev);
112
113 if (!data)
114 return -ENOMEM;
115
116 soc_match_data = soc_device_match(ti_k3_soc_pd_data);
117 if (!soc_match_data)
118 return -ENODEV;
119
120 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
121
122 data->psc = pdata->psc;
123 data->pd = pdata->pd;
124 data->lpsc = pdata->lpsc;
125 data->devs = pdata->devs;
126 data->num_psc = pdata->num_psc;
127 data->num_pd = pdata->num_pd;
128 data->num_lpsc = pdata->num_lpsc;
129 data->num_devs = pdata->num_devs;
130
131 return 0;
132}
133
134static int ti_pd_wait(struct ti_pd *pd)
135{
136 u32 ptstat;
Dave Gerlacha3621752022-04-01 20:02:48 -0500137 u32 pdoffset = 0;
138 u32 ptstatreg = PSC_PTSTAT;
Tero Kristo682c1282021-06-11 11:45:15 +0300139 int ret;
140
Dave Gerlacha3621752022-04-01 20:02:48 -0500141 if (pd->id > 31) {
142 pdoffset = 32;
143 ptstatreg = PSC_PTSTAT_H;
144 }
145
146 ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
147 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
Tero Kristo682c1282021-06-11 11:45:15 +0300148
149 if (ret)
150 printf("%s: psc%d, pd%d failed to transition.\n", __func__,
151 pd->psc->id, pd->id);
152
153 return ret;
154}
155
156static void ti_pd_transition(struct ti_pd *pd)
157{
Dave Gerlacha3621752022-04-01 20:02:48 -0500158 u32 pdoffset = 0;
159 u32 ptcmdreg = PSC_PTCMD;
160
161 if (pd->id > 31) {
162 pdoffset = 32;
163 ptcmdreg = PSC_PTCMD_H;
164 }
165
166 psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
Tero Kristo682c1282021-06-11 11:45:15 +0300167}
168
Tero Kristof95d3be2021-06-11 11:45:16 +0300169u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300170{
171 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
172}
173
174static int ti_pd_get(struct ti_pd *pd)
175{
176 u32 pdctl;
177 int ret;
178
179 pd->usecount++;
180
181 if (pd->usecount > 1)
182 return 0;
183
184 if (pd->depend) {
185 ret = ti_pd_get(pd->depend);
186 if (ret)
187 return ret;
188 ti_pd_transition(pd->depend);
189 ret = ti_pd_wait(pd->depend);
190 if (ret)
191 return ret;
192 }
193
194 pdctl = pd_read(pd, PSC_PDCTL);
195
196 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
197 return 0;
198
199 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
200
201 pdctl &= ~PDCTL_STATE_MASK;
202 pdctl |= PDCTL_STATE_ON;
203
204 pd_write(pdctl, pd, PSC_PDCTL);
205
206 return 0;
207}
208
209static int ti_pd_put(struct ti_pd *pd)
210{
211 u32 pdctl;
212 int ret;
213
214 pd->usecount--;
215
216 if (pd->usecount > 0)
217 return 0;
218
219 pdctl = pd_read(pd, PSC_PDCTL);
220 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
221 return 0;
222
223 pdctl &= ~PDCTL_STATE_MASK;
224 pdctl |= PDCTL_STATE_OFF;
225
226 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
227
228 pd_write(pdctl, pd, PSC_PDCTL);
229
230 if (pd->depend) {
231 ti_pd_transition(pd);
232 ret = ti_pd_wait(pd);
233 if (ret)
234 return ret;
235
236 ret = ti_pd_put(pd->depend);
237 if (ret)
238 return ret;
239 ti_pd_transition(pd->depend);
240 ret = ti_pd_wait(pd->depend);
241 if (ret)
242 return ret;
243 }
244
245 return 0;
246}
247
248static int ti_lpsc_wait(struct ti_lpsc *lpsc)
249{
250 u32 mdstat;
251 int ret;
252
253 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
254 mdstat,
255 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
256
257 if (ret)
258 printf("%s: module %d failed to transition.\n", __func__,
259 lpsc->id);
260
261 return ret;
262}
263
Tero Kristof95d3be2021-06-11 11:45:16 +0300264u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300265{
266 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
267}
268
Tero Kristof95d3be2021-06-11 11:45:16 +0300269int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300270{
271 struct ti_pd *psc_pd;
272 int ret;
273 u32 mdctl;
274
275 psc_pd = lpsc->pd;
276
277 if (state == MDSTAT_STATE_ENABLE) {
278 lpsc->usecount++;
279 if (lpsc->usecount > 1)
280 return 0;
281 } else {
282 lpsc->usecount--;
283 if (lpsc->usecount >= 1)
284 return 0;
285 }
286
287 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
288 lpsc->psc->id, lpsc->id, state);
289
290 if (lpsc->depend)
291 ti_lpsc_transition(lpsc->depend, state);
292
293 mdctl = lpsc_read(lpsc, PSC_MDCTL);
294 if ((mdctl & MDSTAT_STATE_MASK) == state)
295 return 0;
296
297 if (state == MDSTAT_STATE_ENABLE)
298 ti_pd_get(psc_pd);
299 else
300 ti_pd_put(psc_pd);
301
302 mdctl &= ~MDSTAT_STATE_MASK;
303 mdctl |= state;
304
305 lpsc_write(mdctl, lpsc, PSC_MDCTL);
306
307 ti_pd_transition(psc_pd);
308 ret = ti_pd_wait(psc_pd);
309 if (ret)
310 return ret;
311
312 return ti_lpsc_wait(lpsc);
313}
314
315static int ti_power_domain_transition(struct power_domain *pd, u8 state)
316{
317 struct ti_lpsc *lpsc = pd->priv;
318
319 return ti_lpsc_transition(lpsc, state);
320}
321
322static int ti_power_domain_on(struct power_domain *pd)
323{
324 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
325
326 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
327}
328
329static int ti_power_domain_off(struct power_domain *pd)
330{
331 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
332
333 return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
334}
335
336static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
337{
338 int idx;
339
340 for (idx = 0; idx < data->num_devs; idx++)
341 if (data->devs[idx].id == id)
342 return data->devs[idx].lpsc;
343
344 return NULL;
345}
346
347static int ti_power_domain_of_xlate(struct power_domain *pd,
348 struct ofnode_phandle_args *args)
349{
350 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
351 struct ti_lpsc *lpsc;
352
353 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
354
355 if (args->args_count < 1) {
356 printf("Invalid args_count: %d\n", args->args_count);
357 return -EINVAL;
358 }
359
360 lpsc = lpsc_lookup(data, args->args[0]);
361 if (!lpsc) {
362 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
363 return -ENOENT;
364 }
365
366 pd->id = lpsc->id;
367 pd->priv = lpsc;
368
369 return 0;
370}
Tero Kristo682c1282021-06-11 11:45:15 +0300371static const struct udevice_id ti_power_domain_of_match[] = {
372 { .compatible = "ti,sci-pm-domain" },
373 { /* sentinel */ }
374};
375
376static struct power_domain_ops ti_power_domain_ops = {
377 .on = ti_power_domain_on,
378 .off = ti_power_domain_off,
379 .of_xlate = ti_power_domain_of_xlate,
Tero Kristo682c1282021-06-11 11:45:15 +0300380};
381
382U_BOOT_DRIVER(ti_pm_domains) = {
383 .name = "ti-pm-domains",
384 .id = UCLASS_POWER_DOMAIN,
385 .of_match = ti_power_domain_of_match,
386 .probe = ti_power_domain_probe,
387 .priv_auto = sizeof(struct ti_k3_pd_platdata),
388 .ops = &ti_power_domain_ops,
389};