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