blob: a7dadf2eea775cccd0c2ba911e6ad061fbf8feb4 [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
19#define PSC_PTSTAT 0x128
20#define PSC_PDSTAT 0x200
21#define PSC_PDCTL 0x300
22#define PSC_MDSTAT 0x800
23#define PSC_MDCTL 0xa00
24
25#define PDCTL_STATE_MASK 0x1
26#define PDCTL_STATE_OFF 0x0
27#define PDCTL_STATE_ON 0x1
28
29#define MDSTAT_STATE_MASK 0x3f
30#define MDSTAT_BUSY_MASK 0x30
31#define MDSTAT_STATE_SWRSTDISABLE 0x0
32#define MDSTAT_STATE_ENABLE 0x3
33
34#define LPSC_TIMEOUT 1000
35#define PD_TIMEOUT 1000
36
37static u32 psc_read(struct ti_psc *psc, u32 reg)
38{
39 u32 val;
40
41 val = readl(psc->base + reg);
42 debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
43 return val;
44}
45
46static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
47{
48 debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
49 writel(val, psc->base + reg);
50}
51
52static u32 pd_read(struct ti_pd *pd, u32 reg)
53{
54 return psc_read(pd->psc, reg + 4 * pd->id);
55}
56
57static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
58{
59 psc_write(val, pd->psc, reg + 4 * pd->id);
60}
61
62static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
63{
64 return psc_read(lpsc->psc, reg + 4 * lpsc->id);
65}
66
67static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
68{
69 psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
70}
71
72static const struct soc_attr ti_k3_soc_pd_data[] = {
73#if IS_ENABLED(CONFIG_SOC_K3_J721E)
74 {
75 .family = "J721E",
76 .data = &j721e_pd_platdata,
77 },
78 {
79 .family = "J7200",
80 .data = &j7200_pd_platdata,
81 },
David Huang7078f352022-01-25 20:56:34 +053082#elif CONFIG_SOC_K3_J721S2
83 {
84 .family = "J721S2",
85 .data = &j721s2_pd_platdata,
86 },
Tero Kristo682c1282021-06-11 11:45:15 +030087#endif
88 { /* sentinel */ }
89};
90
91static int ti_power_domain_probe(struct udevice *dev)
92{
93 struct ti_k3_pd_platdata *data = dev_get_priv(dev);
94 const struct soc_attr *soc_match_data;
95 const struct ti_k3_pd_platdata *pdata;
96
97 printf("%s(dev=%p)\n", __func__, dev);
98
99 if (!data)
100 return -ENOMEM;
101
102 soc_match_data = soc_device_match(ti_k3_soc_pd_data);
103 if (!soc_match_data)
104 return -ENODEV;
105
106 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
107
108 data->psc = pdata->psc;
109 data->pd = pdata->pd;
110 data->lpsc = pdata->lpsc;
111 data->devs = pdata->devs;
112 data->num_psc = pdata->num_psc;
113 data->num_pd = pdata->num_pd;
114 data->num_lpsc = pdata->num_lpsc;
115 data->num_devs = pdata->num_devs;
116
117 return 0;
118}
119
120static int ti_pd_wait(struct ti_pd *pd)
121{
122 u32 ptstat;
123 int ret;
124
125 ret = readl_poll_timeout(pd->psc->base + PSC_PTSTAT, ptstat,
126 !(ptstat & BIT(pd->id)), PD_TIMEOUT);
127
128 if (ret)
129 printf("%s: psc%d, pd%d failed to transition.\n", __func__,
130 pd->psc->id, pd->id);
131
132 return ret;
133}
134
135static void ti_pd_transition(struct ti_pd *pd)
136{
137 psc_write(BIT(pd->id), pd->psc, PSC_PTCMD);
138}
139
Tero Kristof95d3be2021-06-11 11:45:16 +0300140u8 ti_pd_state(struct ti_pd *pd)
Tero Kristo682c1282021-06-11 11:45:15 +0300141{
142 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
143}
144
145static int ti_pd_get(struct ti_pd *pd)
146{
147 u32 pdctl;
148 int ret;
149
150 pd->usecount++;
151
152 if (pd->usecount > 1)
153 return 0;
154
155 if (pd->depend) {
156 ret = ti_pd_get(pd->depend);
157 if (ret)
158 return ret;
159 ti_pd_transition(pd->depend);
160 ret = ti_pd_wait(pd->depend);
161 if (ret)
162 return ret;
163 }
164
165 pdctl = pd_read(pd, PSC_PDCTL);
166
167 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
168 return 0;
169
170 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
171
172 pdctl &= ~PDCTL_STATE_MASK;
173 pdctl |= PDCTL_STATE_ON;
174
175 pd_write(pdctl, pd, PSC_PDCTL);
176
177 return 0;
178}
179
180static int ti_pd_put(struct ti_pd *pd)
181{
182 u32 pdctl;
183 int ret;
184
185 pd->usecount--;
186
187 if (pd->usecount > 0)
188 return 0;
189
190 pdctl = pd_read(pd, PSC_PDCTL);
191 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
192 return 0;
193
194 pdctl &= ~PDCTL_STATE_MASK;
195 pdctl |= PDCTL_STATE_OFF;
196
197 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
198
199 pd_write(pdctl, pd, PSC_PDCTL);
200
201 if (pd->depend) {
202 ti_pd_transition(pd);
203 ret = ti_pd_wait(pd);
204 if (ret)
205 return ret;
206
207 ret = ti_pd_put(pd->depend);
208 if (ret)
209 return ret;
210 ti_pd_transition(pd->depend);
211 ret = ti_pd_wait(pd->depend);
212 if (ret)
213 return ret;
214 }
215
216 return 0;
217}
218
219static int ti_lpsc_wait(struct ti_lpsc *lpsc)
220{
221 u32 mdstat;
222 int ret;
223
224 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4,
225 mdstat,
226 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT);
227
228 if (ret)
229 printf("%s: module %d failed to transition.\n", __func__,
230 lpsc->id);
231
232 return ret;
233}
234
Tero Kristof95d3be2021-06-11 11:45:16 +0300235u8 lpsc_get_state(struct ti_lpsc *lpsc)
Tero Kristo682c1282021-06-11 11:45:15 +0300236{
237 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK;
238}
239
Tero Kristof95d3be2021-06-11 11:45:16 +0300240int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
Tero Kristo682c1282021-06-11 11:45:15 +0300241{
242 struct ti_pd *psc_pd;
243 int ret;
244 u32 mdctl;
245
246 psc_pd = lpsc->pd;
247
248 if (state == MDSTAT_STATE_ENABLE) {
249 lpsc->usecount++;
250 if (lpsc->usecount > 1)
251 return 0;
252 } else {
253 lpsc->usecount--;
254 if (lpsc->usecount >= 1)
255 return 0;
256 }
257
258 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
259 lpsc->psc->id, lpsc->id, state);
260
261 if (lpsc->depend)
262 ti_lpsc_transition(lpsc->depend, state);
263
264 mdctl = lpsc_read(lpsc, PSC_MDCTL);
265 if ((mdctl & MDSTAT_STATE_MASK) == state)
266 return 0;
267
268 if (state == MDSTAT_STATE_ENABLE)
269 ti_pd_get(psc_pd);
270 else
271 ti_pd_put(psc_pd);
272
273 mdctl &= ~MDSTAT_STATE_MASK;
274 mdctl |= state;
275
276 lpsc_write(mdctl, lpsc, PSC_MDCTL);
277
278 ti_pd_transition(psc_pd);
279 ret = ti_pd_wait(psc_pd);
280 if (ret)
281 return ret;
282
283 return ti_lpsc_wait(lpsc);
284}
285
286static int ti_power_domain_transition(struct power_domain *pd, u8 state)
287{
288 struct ti_lpsc *lpsc = pd->priv;
289
290 return ti_lpsc_transition(lpsc, state);
291}
292
293static int ti_power_domain_on(struct power_domain *pd)
294{
295 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
296
297 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
298}
299
300static int ti_power_domain_off(struct power_domain *pd)
301{
302 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
303
304 return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
305}
306
307static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
308{
309 int idx;
310
311 for (idx = 0; idx < data->num_devs; idx++)
312 if (data->devs[idx].id == id)
313 return data->devs[idx].lpsc;
314
315 return NULL;
316}
317
318static int ti_power_domain_of_xlate(struct power_domain *pd,
319 struct ofnode_phandle_args *args)
320{
321 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
322 struct ti_lpsc *lpsc;
323
324 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
325
326 if (args->args_count < 1) {
327 printf("Invalid args_count: %d\n", args->args_count);
328 return -EINVAL;
329 }
330
331 lpsc = lpsc_lookup(data, args->args[0]);
332 if (!lpsc) {
333 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
334 return -ENOENT;
335 }
336
337 pd->id = lpsc->id;
338 pd->priv = lpsc;
339
340 return 0;
341}
342
343static int ti_power_domain_request(struct power_domain *pd)
344{
345 return 0;
346}
347
348static int ti_power_domain_free(struct power_domain *pd)
349{
350 return 0;
351}
352
353static const struct udevice_id ti_power_domain_of_match[] = {
354 { .compatible = "ti,sci-pm-domain" },
355 { /* sentinel */ }
356};
357
358static struct power_domain_ops ti_power_domain_ops = {
359 .on = ti_power_domain_on,
360 .off = ti_power_domain_off,
361 .of_xlate = ti_power_domain_of_xlate,
362 .request = ti_power_domain_request,
363 .rfree = ti_power_domain_free,
364};
365
366U_BOOT_DRIVER(ti_pm_domains) = {
367 .name = "ti-pm-domains",
368 .id = UCLASS_POWER_DOMAIN,
369 .of_match = ti_power_domain_of_match,
370 .probe = ti_power_domain_probe,
371 .priv_auto = sizeof(struct ti_k3_pd_platdata),
372 .ops = &ti_power_domain_ops,
373};