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