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