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