blob: fa5422f2e8ba57fa49fba21c0329a43349092608 [file] [log] [blame]
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -04001/*
2 * Keystone: PSC configuration module
3 *
4 * (C) Copyright 2012-2014
5 * Texas Instruments Incorporated, <www.ti.com>
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 */
9
10#include <common.h>
11#include <asm-generic/errno.h>
12#include <asm/io.h>
13#include <asm/processor.h>
14#include <asm/arch/psc_defs.h>
15
16#define DEVICE_REG32_R(addr) __raw_readl((u32 *)(addr))
17#define DEVICE_REG32_W(addr, val) __raw_writel(val, (u32 *)(addr))
18
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -040019int psc_delay(void)
20{
21 udelay(10);
22 return 10;
23}
24
25/*
26 * FUNCTION PURPOSE: Wait for end of transitional state
27 *
28 * DESCRIPTION: Polls pstat for the selected domain and waits for transitions
29 * to be complete.
30 *
31 * Since this is boot loader code it is *ASSUMED* that interrupts
32 * are disabled and no other core is mucking around with the psc
33 * at the same time.
34 *
35 * Returns 0 when the domain is free. Returns -1 if a timeout
36 * occurred waiting for the completion.
37 */
38int psc_wait(u32 domain_num)
39{
40 u32 retry;
41 u32 ptstat;
42
43 /*
44 * Do nothing if the power domain is in transition. This should never
45 * happen since the boot code is the only software accesses psc.
46 * It's still remotely possible that the hardware state machines
47 * initiate transitions.
48 * Don't trap if the domain (or a module in this domain) is
49 * stuck in transition.
50 */
51 retry = 0;
52
53 do {
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +030054 ptstat = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_PSTAT);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -040055 ptstat = ptstat & (1 << domain_num);
56 } while ((ptstat != 0) && ((retry += psc_delay()) <
57 PSC_PTSTAT_TIMEOUT_LIMIT));
58
59 if (retry >= PSC_PTSTAT_TIMEOUT_LIMIT)
60 return -1;
61
62 return 0;
63}
64
65u32 psc_get_domain_num(u32 mod_num)
66{
67 u32 domain_num;
68
69 /* Get the power domain associated with the module number */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +030070 domain_num = DEVICE_REG32_R(KS2_PSC_BASE +
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -040071 PSC_REG_MDCFG(mod_num));
72 domain_num = PSC_REG_MDCFG_GET_PD(domain_num);
73
74 return domain_num;
75}
76
77/*
78 * FUNCTION PURPOSE: Power up/down a module
79 *
80 * DESCRIPTION: Powers up/down the requested module and the associated power
81 * domain if required. No action is taken it the module is
82 * already powered up/down.
83 *
84 * This only controls modules. The domain in which the module
85 * resides will be left in the power on state. Multiple modules
86 * can exist in a power domain, so powering down the domain based
87 * on a single module is not done.
88 *
89 * Returns 0 on success, -1 if the module can't be powered up, or
90 * if there is a timeout waiting for the transition.
91 */
92int psc_set_state(u32 mod_num, u32 state)
93{
94 u32 domain_num;
95 u32 pdctl;
96 u32 mdctl;
97 u32 ptcmd;
98 u32 reset_iso;
99 u32 v;
100
101 /*
102 * Get the power domain associated with the module number, and reset
103 * isolation functionality
104 */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300105 v = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400106 domain_num = PSC_REG_MDCFG_GET_PD(v);
107 reset_iso = PSC_REG_MDCFG_GET_RESET_ISO(v);
108
109 /* Wait for the status of the domain/module to be non-transitional */
110 if (psc_wait(domain_num) != 0)
111 return -1;
112
113 /*
114 * Perform configuration even if the current status matches the
115 * existing state
116 *
117 * Set the next state of the power domain to on. It's OK if the domain
118 * is always on. This code will not ever power down a domain, so no
119 * change is made if the new state is power down.
120 */
121 if (state == PSC_REG_VAL_MDCTL_NEXT_ON) {
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300122 pdctl = DEVICE_REG32_R(KS2_PSC_BASE +
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400123 PSC_REG_PDCTL(domain_num));
124 pdctl = PSC_REG_PDCTL_SET_NEXT(pdctl,
125 PSC_REG_VAL_PDCTL_NEXT_ON);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300126 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num),
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400127 pdctl);
128 }
129
130 /* Set the next state for the module to enabled/disabled */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300131 mdctl = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400132 mdctl = PSC_REG_MDCTL_SET_NEXT(mdctl, state);
133 mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, reset_iso);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300134 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num), mdctl);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400135
136 /* Trigger the enable */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300137 ptcmd = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_PTCMD);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400138 ptcmd |= (u32)(1<<domain_num);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300139 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_PTCMD, ptcmd);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400140
141 /* Wait on the complete */
142 return psc_wait(domain_num);
143}
144
145/*
146 * FUNCTION PURPOSE: Power up a module
147 *
148 * DESCRIPTION: Powers up the requested module and the associated power domain
149 * if required. No action is taken it the module is already
150 * powered up.
151 *
152 * Returns 0 on success, -1 if the module can't be powered up, or
153 * if there is a timeout waiting for the transition.
154 */
155int psc_enable_module(u32 mod_num)
156{
157 u32 mdctl;
158
159 /* Set the bit to apply reset */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300160 mdctl = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400161 if ((mdctl & 0x3f) == PSC_REG_VAL_MDSTAT_STATE_ON)
162 return 0;
163
164 return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_ON);
165}
166
167/*
168 * FUNCTION PURPOSE: Power down a module
169 *
170 * DESCRIPTION: Powers down the requested module.
171 *
172 * Returns 0 on success, -1 on failure or timeout.
173 */
174int psc_disable_module(u32 mod_num)
175{
176 u32 mdctl;
177
178 /* Set the bit to apply reset */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300179 mdctl = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400180 if ((mdctl & 0x3f) == 0)
181 return 0;
182 mdctl = PSC_REG_MDCTL_SET_LRSTZ(mdctl, 0);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300183 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num), mdctl);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400184
185 return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_SWRSTDISABLE);
186}
187
188/*
189 * FUNCTION PURPOSE: Set the reset isolation bit in mdctl
190 *
191 * DESCRIPTION: The reset isolation enable bit is set. The state of the module
192 * is not changed. Returns 0 if the module config showed that
193 * reset isolation is supported. Returns 1 otherwise. This is not
194 * an error, but setting the bit in mdctl has no effect.
195 */
196int psc_set_reset_iso(u32 mod_num)
197{
198 u32 v;
199 u32 mdctl;
200
201 /* Set the reset isolation bit */
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300202 mdctl = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400203 mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, 1);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300204 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num), mdctl);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400205
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300206 v = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400207 if (PSC_REG_MDCFG_GET_RESET_ISO(v) == 1)
208 return 0;
209
210 return 1;
211}
212
213/*
214 * FUNCTION PURPOSE: Disable a power domain
215 *
216 * DESCRIPTION: The power domain is disabled
217 */
218int psc_disable_domain(u32 domain_num)
219{
220 u32 pdctl;
221 u32 ptcmd;
222
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300223 pdctl = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400224 pdctl = PSC_REG_PDCTL_SET_NEXT(pdctl, PSC_REG_VAL_PDCTL_NEXT_OFF);
225 pdctl = PSC_REG_PDCTL_SET_PDMODE(pdctl, PSC_REG_VAL_PDCTL_PDMODE_SLEEP);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300226 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num), pdctl);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400227
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300228 ptcmd = DEVICE_REG32_R(KS2_PSC_BASE + PSC_REG_PTCMD);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400229 ptcmd |= (u32)(1 << domain_num);
Khoronzhuk, Ivan2df64102014-07-09 19:48:39 +0300230 DEVICE_REG32_W(KS2_PSC_BASE + PSC_REG_PTCMD, ptcmd);
Vitaly Andrianov7bcf4d62014-04-04 13:16:53 -0400231
232 return psc_wait(domain_num);
233}