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