diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-02-27 22:08:58 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-03-28 00:01:37 +0000 |
commit | 3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch) | |
tree | b647717f83ad56b15dc42cfdef5d04d68cd9bd6b /firmware/target/mips/ingenic_x1000/clk-x1000.c | |
parent | 83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff) | |
download | rockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.tar.gz rockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.zip |
New port: FiiO M3K on bare metal
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/clk-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/clk-x1000.c | 258 |
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 | |||
28 | static 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 | |||
44 | static 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 | |||
53 | static 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 | |||
66 | static 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 | |||
78 | static 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 | |||
95 | static 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 | |||
120 | static 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 | |||
146 | static uint32_t i2s_bclk_get(void) | ||
147 | { | ||
148 | return i2s_mclk_get() / (REG_AIC_I2SDIV + 1); | ||
149 | } | ||
150 | |||
151 | static 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 | |||
167 | uint32_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 | |||
190 | const 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 | |||
220 | void 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 | |||
232 | void 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 | |||
246 | void 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 | } | ||