summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/clk-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/clk-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
new file mode 100644
index 0000000000..390d9722ac
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -0,0 +1,258 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "system.h"
23#include "clk-x1000.h"
24#include "x1000/cpm.h"
25#include "x1000/msc.h"
26#include "x1000/aic.h"
27
28static uint32_t pll_get(uint32_t pllreg, uint32_t onbit)
29{
30 if((pllreg & (1 << onbit)) == 0)
31 return 0;
32
33 /* Both PLL registers share the same layout of N/M/OD bits.
34 * The max multiplier is 128 and max EXCLK is 26 MHz, so the
35 * multiplication should fit within 32 bits without overflow.
36 */
37 uint32_t rate = X1000_EXCLK_FREQ;
38 rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1;
39 rate /= jz_vreadf(pllreg, CPM_APCR, PLLN) + 1;
40 rate >>= jz_vreadf(pllreg, CPM_APCR, PLLOD);
41 return rate;
42}
43
44static uint32_t sclk_a_get(void)
45{
46 switch(jz_readf(CPM_CCR, SEL_SRC)) {
47 case 1: return X1000_EXCLK_FREQ;
48 case 2: return clk_get(X1000_CLK_APLL);
49 default: return 0;
50 }
51}
52
53static uint32_t ccr_get(uint32_t selbit, uint32_t divbit)
54{
55 uint32_t reg = REG_CPM_CCR;
56 uint32_t sel = (reg >> selbit) & 0x3;
57 uint32_t div = (reg >> divbit) & 0xf;
58
59 switch(sel) {
60 case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
61 case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
62 default: return 0;
63 }
64}
65
66static uint32_t ddr_get(void)
67{
68 uint32_t reg = REG_CPM_DDRCDR;
69 uint32_t div = jz_vreadf(reg, CPM_DDRCDR, CLKDIV);
70
71 switch(jz_vreadf(reg, CPM_DDRCDR, CLKSRC)) {
72 case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
73 case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
74 default: return 0;
75 }
76}
77
78static uint32_t lcd_get(void)
79{
80 if(jz_readf(CPM_CLKGR, LCD))
81 return 0;
82
83 uint32_t reg = REG_CPM_LPCDR;
84 uint32_t rate;
85 switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) {
86 case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
87 case 1: rate = clk_get(X1000_CLK_MPLL); break;
88 default: return 0;
89 }
90
91 rate /= jz_vreadf(reg, CPM_LPCDR, CLKDIV) + 1;
92 return rate;
93}
94
95static uint32_t msc_get(int msc)
96{
97 if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) ||
98 (msc == 1 && jz_readf(CPM_CLKGR, MSC1)))
99 return 0;
100
101 uint32_t reg = REG_CPM_MSC0CDR;
102 uint32_t rate;
103 switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) {
104 case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
105 case 1: rate = clk_get(X1000_CLK_MPLL); break;
106 default: return 0;
107 }
108
109 uint32_t div;
110 if(msc == 0)
111 div = jz_readf(CPM_MSC0CDR, CLKDIV);
112 else
113 div = jz_readf(CPM_MSC1CDR, CLKDIV);
114
115 rate /= 2 * (div + 1);
116 rate >>= REG_MSC_CLKRT(msc);
117 return rate;
118}
119
120static uint32_t i2s_mclk_get(void)
121{
122 if(jz_readf(CPM_CLKGR, AIC))
123 return 0;
124
125 uint32_t reg = REG_CPM_I2SCDR;
126 unsigned long long rate;
127 if(jz_vreadf(reg, CPM_I2SCDR, CS) == 0)
128 rate = X1000_EXCLK_FREQ;
129 else {
130 if(jz_vreadf(reg, CPM_I2SCDR, PCS) == 0)
131 rate = clk_get(X1000_CLK_SCLK_A);
132 else
133 rate = clk_get(X1000_CLK_MPLL);
134
135 rate *= jz_vreadf(reg, CPM_I2SCDR, DIV_M);
136 rate /= jz_vreadf(reg, CPM_I2SCDR, DIV_N);
137 }
138
139 /* Clamp invalid setting to 32 bits */
140 if(rate > 0xffffffffull)
141 rate = 0xffffffff;
142
143 return rate;
144}
145
146static uint32_t i2s_bclk_get(void)
147{
148 return i2s_mclk_get() / (REG_AIC_I2SDIV + 1);
149}
150
151static uint32_t sfc_get(void)
152{
153 if(jz_readf(CPM_CLKGR, SFC))
154 return 0;
155
156 uint32_t reg = REG_CPM_SSICDR;
157 uint32_t rate;
158 if(jz_vreadf(reg, CPM_SSICDR, SFC_CS) == 0)
159 rate = clk_get(X1000_CLK_SCLK_A);
160 else
161 rate = clk_get(X1000_CLK_MPLL);
162
163 rate /= jz_vreadf(reg, CPM_SSICDR, CLKDIV) + 1;
164 return rate;
165}
166
167uint32_t clk_get(x1000_clk_t clk)
168{
169 switch(clk) {
170 case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ;
171 case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON);
172 case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON);
173 case X1000_CLK_SCLK_A: return sclk_a_get();
174 case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV);
175 case X1000_CLK_L2CACHE: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_L2DIV);
176 case X1000_CLK_AHB0: return ccr_get(BP_CPM_CCR_SEL_H0PLL, BP_CPM_CCR_H0DIV);
177 case X1000_CLK_AHB2: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_H2DIV);
178 case X1000_CLK_PCLK: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_PDIV);
179 case X1000_CLK_DDR: return ddr_get();
180 case X1000_CLK_LCD: return lcd_get();
181 case X1000_CLK_MSC0: return msc_get(0);
182 case X1000_CLK_MSC1: return msc_get(1);
183 case X1000_CLK_I2S_MCLK: return i2s_mclk_get();
184 case X1000_CLK_I2S_BCLK: return i2s_bclk_get();
185 case X1000_CLK_SFC: return sfc_get();
186 default: return 0;
187 }
188}
189
190const char* clk_get_name(x1000_clk_t clk)
191{
192 switch(clk) {
193#define CASE(x) case X1000_CLK_##x: return #x
194 CASE(EXCLK);
195 CASE(APLL);
196 CASE(MPLL);
197 CASE(SCLK_A);
198 CASE(CPU);
199 CASE(L2CACHE);
200 CASE(AHB0);
201 CASE(AHB2);
202 CASE(PCLK);
203 CASE(DDR);
204 CASE(LCD);
205 CASE(MSC0);
206 CASE(MSC1);
207 CASE(I2S_MCLK);
208 CASE(I2S_BCLK);
209 CASE(SFC);
210#undef CASE
211 default:
212 return "NONE";
213 }
214}
215
216#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL)
217#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX)
218#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY)
219
220void clk_set_ccr_mux(uint32_t muxbits)
221{
222 /* Set new mux configuration */
223 uint32_t reg = REG_CPM_CCR;
224 reg &= ~CCR_MUX_BITS;
225 reg |= muxbits & CCR_MUX_BITS;
226 REG_CPM_CCR = reg;
227
228 /* Wait for mux change to complete */
229 while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS);
230}
231
232void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk)
233{
234 /* Set new divider configuration */
235 jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1),
236 H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1),
237 CE_CPU(1), CE_AHB0(1), CE_AHB2(1));
238
239 /* Wait until divider change completes */
240 while(REG_CPM_CSR & CSR_DIV_BITS);
241
242 /* Disable CE bits after change */
243 jz_writef(CPM_CCR, CE_CPU(0), CE_AHB0(0), CE_AHB2(0));
244}
245
246void clk_set_ddr(x1000_clk_t src, uint32_t div)
247{
248 /* Write new configuration */
249 jz_writef(CPM_DDRCDR, CE(1), CLKDIV(div - 1),
250 CLKSRC(src == X1000_CLK_MPLL ? 2 : 1));
251
252 /* Wait until mux and divider change are complete */
253 while(jz_readf(CPM_CSR, DDR_MUX) == 0);
254 while(jz_readf(CPM_DDRCDR, BUSY));
255
256 /* Disable CE bit after change */
257 jz_writef(CPM_DDRCDR, CE(0));
258}