diff options
-rw-r--r-- | firmware/target/mips/ingenic_x1000/clk-x1000.c | 236 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/clk-x1000.h | 6 |
2 files changed, 128 insertions, 114 deletions
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c index 390d9722ac..6cc18fb113 100644 --- a/firmware/target/mips/ingenic_x1000/clk-x1000.c +++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c | |||
@@ -25,99 +25,129 @@ | |||
25 | #include "x1000/msc.h" | 25 | #include "x1000/msc.h" |
26 | #include "x1000/aic.h" | 26 | #include "x1000/aic.h" |
27 | 27 | ||
28 | static uint32_t pll_get(uint32_t pllreg, uint32_t onbit) | 28 | struct clk_info { |
29 | uint8_t mux_reg; /* offset from CPM_BASE for mux register */ | ||
30 | uint8_t mux_shift; /* shift to get mux */ | ||
31 | uint8_t mux_mask; /* mask to get mux after shifting */ | ||
32 | uint8_t mux_type; /* type of mux, maps register bits to clock source */ | ||
33 | uint8_t div_reg; /* offset from CPM_BASE for divider register */ | ||
34 | uint8_t div_shift; /* shift to get divider */ | ||
35 | uint8_t div_mask; /* mask to get divider after shifting */ | ||
36 | uint8_t miscbits; /* inclk shift, clkgr bit, and fake mux value */ | ||
37 | }; | ||
38 | |||
39 | /* Ugliness to pack/unpack stuff in clk_info->miscbits */ | ||
40 | #define INCLK_SHIFT(n) ((n) & 1) | ||
41 | #define CLKGR_BIT(n) (((n) & 0x1f) << 1) | ||
42 | #define FAKEMUX(n) (((n) & 3) << 6) | ||
43 | #define GET_INCLK_SHIFT(miscbits) ((miscbits) & 1) | ||
44 | #define GET_CLKGR_BIT(miscbits) (((miscbits) >> 1) & 0x1f) | ||
45 | #define GET_FAKEMUX(miscbits) (((miscbits) >> 6) & 3) | ||
46 | |||
47 | /* Clock sources -- the order here is important! */ | ||
48 | #define S_STOP 0 | ||
49 | #define S_EXCLK 1 | ||
50 | #define S_APLL 2 | ||
51 | #define S_MPLL 3 | ||
52 | #define S_SCLK_A 4 | ||
53 | |||
54 | /* Muxes */ | ||
55 | #define MUX_TWOBIT 0 | ||
56 | #define MUX_ONEBIT 1 | ||
57 | #define MUX_USB 2 | ||
58 | #define MUX_PHONY 3 | ||
59 | #define MUX_NUM_TYPES 4 | ||
60 | |||
61 | /* Ugliness to define muxes */ | ||
62 | #define MKSEL(x,i) (((x) & 0xf) << ((i)*4)) | ||
63 | #define GETSEL(x,i) (((x) >> ((i)*4)) & 0xf) | ||
64 | #define STOP(i) MKSEL(S_STOP, i) | ||
65 | #define EXCLK(i) MKSEL(S_EXCLK, i) | ||
66 | #define APLL(i) MKSEL(S_APLL, i) | ||
67 | #define MPLL(i) MKSEL(S_MPLL, i) | ||
68 | #define SCLK_A(i) MKSEL(S_SCLK_A, i) | ||
69 | #define MKMUX(a,b,c,d) (a(0)|b(1)|c(2)|d(3)) | ||
70 | |||
71 | /* Ugliness to shorten the clk_info table */ | ||
72 | #define JA(x) (JA_CPM_##x & 0xff) | ||
73 | #define BM(x) ((BM_CPM_##x) >> (BP_CPM_##x)) | ||
74 | #define BP(x) (BP_CPM_##x) | ||
75 | #define CG(x) CLKGR_BIT(BP_CPM_CLKGR_##x) | ||
76 | #define M(r,f,t) JA(r), BP(r##_##f), BM(r##_##f), t | ||
77 | #define D(r,f) JA(r), BP(r##_##f), BM(r##_##f) | ||
78 | |||
79 | static const uint16_t clk_mux[MUX_NUM_TYPES] = { | ||
80 | /* 00 01 10 11 */ | ||
81 | MKMUX(STOP, SCLK_A, MPLL, STOP), /* MUX_TWOBIT */ | ||
82 | MKMUX(SCLK_A, MPLL, STOP, STOP), /* MUX_ONEBIT */ | ||
83 | MKMUX(EXCLK, EXCLK, SCLK_A, MPLL), /* MUX_USB */ | ||
84 | MKMUX(EXCLK, APLL, MPLL, SCLK_A), /* MUX_PHONY */ | ||
85 | }; | ||
86 | |||
87 | /* Keep in order with enum x1000_clk_t */ | ||
88 | const struct clk_info clk_info[X1000_NUM_SIMPLE_CLKS] = { | ||
89 | {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(0)}, | ||
90 | {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(1)}, | ||
91 | {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(2)}, | ||
92 | {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(3)}, | ||
93 | {M(CCR, SEL_CPLL, MUX_TWOBIT), D(CCR, CDIV), CG(CPU_BIT)}, | ||
94 | {M(CCR, SEL_CPLL, MUX_TWOBIT), D(CCR, L2DIV), CG(CPU_BIT)}, | ||
95 | {M(CCR, SEL_H0PLL, MUX_TWOBIT), D(CCR, H0DIV), CG(AHB0)}, | ||
96 | {M(CCR, SEL_H2PLL, MUX_TWOBIT), D(CCR, H2DIV), CG(APB0)}, | ||
97 | {M(CCR, SEL_H2PLL, MUX_TWOBIT), D(CCR, PDIV), CG(APB0)}, | ||
98 | {M(DDRCDR, CLKSRC, MUX_TWOBIT), D(DDRCDR, CLKDIV), CG(DDR)}, | ||
99 | {M(LPCDR, CLKSRC, MUX_ONEBIT), D(LPCDR, CLKDIV), CG(LCD)}, | ||
100 | {M(MSC0CDR, CLKSRC, MUX_ONEBIT), D(MSC0CDR, CLKDIV), CG(MSC0)|INCLK_SHIFT(1)}, | ||
101 | {M(MSC0CDR, CLKSRC, MUX_ONEBIT), D(MSC1CDR, CLKDIV), CG(MSC1)|INCLK_SHIFT(1)}, | ||
102 | {M(SSICDR, SFC_CS, MUX_ONEBIT), D(SSICDR, CLKDIV), CG(SFC)}, | ||
103 | {M(USBCDR, CLKSRC, MUX_USB), D(USBCDR, CLKDIV), CG(OTG)}, | ||
104 | }; | ||
105 | |||
106 | static uint32_t clk_get_in_rate(uint8_t mux_type, uint32_t mux) | ||
29 | { | 107 | { |
30 | if((pllreg & (1 << onbit)) == 0) | 108 | uint32_t reg, onbit; |
31 | return 0; | 109 | uint32_t src = GETSEL(clk_mux[mux_type], mux); |
32 | 110 | again: | |
33 | /* Both PLL registers share the same layout of N/M/OD bits. | 111 | switch(src) { |
34 | * The max multiplier is 128 and max EXCLK is 26 MHz, so the | 112 | default: return 0; |
35 | * multiplication should fit within 32 bits without overflow. | 113 | case S_EXCLK: return X1000_EXCLK_FREQ; |
36 | */ | 114 | case S_APLL: reg = REG_CPM_APCR; onbit = BM_CPM_APCR_ON; break; |
37 | uint32_t rate = X1000_EXCLK_FREQ; | 115 | case S_MPLL: reg = REG_CPM_MPCR; onbit = BM_CPM_MPCR_ON; break; |
38 | rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1; | 116 | case S_SCLK_A: src = jz_readf(CPM_CCR, SEL_SRC); goto again; |
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 | } | 117 | } |
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 | 118 | ||
78 | static uint32_t lcd_get(void) | 119 | if(!(reg & onbit)) |
79 | { | ||
80 | if(jz_readf(CPM_CLKGR, LCD)) | ||
81 | return 0; | 120 | return 0; |
82 | 121 | ||
83 | uint32_t reg = REG_CPM_LPCDR; | 122 | uint32_t rate = X1000_EXCLK_FREQ; |
84 | uint32_t rate; | 123 | rate *= jz_vreadf(reg, CPM_APCR, PLLM) + 1; |
85 | switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) { | 124 | rate /= jz_vreadf(reg, CPM_APCR, PLLN) + 1; |
86 | case 0: rate = clk_get(X1000_CLK_SCLK_A); break; | 125 | rate >>= jz_vreadf(reg, CPM_APCR, PLLOD); |
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; | 126 | return rate; |
93 | } | 127 | } |
94 | 128 | ||
95 | static uint32_t msc_get(int msc) | 129 | static uint32_t clk_get_simple(const struct clk_info* info) |
96 | { | 130 | { |
97 | if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) || | 131 | if(REG_CPM_CLKGR & (1 << GET_CLKGR_BIT(info->miscbits))) |
98 | (msc == 1 && jz_readf(CPM_CLKGR, MSC1))) | ||
99 | return 0; | 132 | return 0; |
100 | 133 | ||
101 | uint32_t reg = REG_CPM_MSC0CDR; | 134 | uint32_t base = 0xb0000000; |
102 | uint32_t rate; | 135 | uint32_t mux_reg = *(const volatile uint32_t*)(base + info->mux_reg); |
103 | switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) { | 136 | uint32_t div_reg = *(const volatile uint32_t*)(base + info->div_reg); |
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 | 137 | ||
109 | uint32_t div; | 138 | uint32_t mux = (mux_reg >> info->mux_shift) & info->mux_mask; |
110 | if(msc == 0) | 139 | uint32_t div = (div_reg >> info->div_shift) & info->div_mask; |
111 | div = jz_readf(CPM_MSC0CDR, CLKDIV); | ||
112 | else | ||
113 | div = jz_readf(CPM_MSC1CDR, CLKDIV); | ||
114 | 140 | ||
115 | rate /= 2 * (div + 1); | 141 | mux |= GET_FAKEMUX(info->miscbits); |
116 | rate >>= REG_MSC_CLKRT(msc); | 142 | |
143 | uint32_t rate = clk_get_in_rate(info->mux_type, mux); | ||
144 | rate >>= GET_INCLK_SHIFT(info->miscbits); | ||
145 | rate /= (div + 1); | ||
117 | return rate; | 146 | return rate; |
118 | } | 147 | } |
119 | 148 | ||
120 | static uint32_t i2s_mclk_get(void) | 149 | #ifndef BOOTLOADER |
150 | static uint32_t clk_get_i2s_mclk(void) | ||
121 | { | 151 | { |
122 | if(jz_readf(CPM_CLKGR, AIC)) | 152 | if(jz_readf(CPM_CLKGR, AIC)) |
123 | return 0; | 153 | return 0; |
@@ -143,48 +173,29 @@ static uint32_t i2s_mclk_get(void) | |||
143 | return rate; | 173 | return rate; |
144 | } | 174 | } |
145 | 175 | ||
146 | static uint32_t i2s_bclk_get(void) | 176 | static uint32_t clk_get_i2s_bclk(void) |
147 | { | 177 | { |
148 | return i2s_mclk_get() / (REG_AIC_I2SDIV + 1); | 178 | return clk_get_i2s_mclk() / (REG_AIC_I2SDIV + 1); |
149 | } | 179 | } |
150 | 180 | ||
151 | static uint32_t sfc_get(void) | 181 | static uint32_t clk_get_decimal(x1000_clk_t clk) |
152 | { | 182 | { |
153 | if(jz_readf(CPM_CLKGR, SFC)) | 183 | switch(clk) { |
154 | return 0; | 184 | case X1000_CLK_I2S_MCLK: return clk_get_i2s_mclk(); |
155 | 185 | case X1000_CLK_I2S_BCLK: return clk_get_i2s_bclk(); | |
156 | uint32_t reg = REG_CPM_SSICDR; | 186 | default: return 0; |
157 | uint32_t rate; | 187 | } |
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 | } | 188 | } |
189 | #endif | ||
166 | 190 | ||
167 | uint32_t clk_get(x1000_clk_t clk) | 191 | uint32_t clk_get(x1000_clk_t clk) |
168 | { | 192 | { |
169 | switch(clk) { | 193 | #ifndef BOOTLOADER |
170 | case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ; | 194 | if(clk >= X1000_NUM_SIMPLE_CLKS) |
171 | case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON); | 195 | return clk_get_decimal(clk); |
172 | case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON); | 196 | #endif |
173 | case X1000_CLK_SCLK_A: return sclk_a_get(); | 197 | |
174 | case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV); | 198 | return clk_get_simple(&clk_info[clk]); |
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 | } | 199 | } |
189 | 200 | ||
190 | const char* clk_get_name(x1000_clk_t clk) | 201 | const char* clk_get_name(x1000_clk_t clk) |
@@ -204,9 +215,10 @@ const char* clk_get_name(x1000_clk_t clk) | |||
204 | CASE(LCD); | 215 | CASE(LCD); |
205 | CASE(MSC0); | 216 | CASE(MSC0); |
206 | CASE(MSC1); | 217 | CASE(MSC1); |
218 | CASE(SFC); | ||
219 | CASE(USB); | ||
207 | CASE(I2S_MCLK); | 220 | CASE(I2S_MCLK); |
208 | CASE(I2S_BCLK); | 221 | CASE(I2S_BCLK); |
209 | CASE(SFC); | ||
210 | #undef CASE | 222 | #undef CASE |
211 | default: | 223 | default: |
212 | return "NONE"; | 224 | return "NONE"; |
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h index 76413b90d2..2ff602db9f 100644 --- a/firmware/target/mips/ingenic_x1000/clk-x1000.h +++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h | |||
@@ -45,9 +45,11 @@ typedef enum x1000_clk_t { | |||
45 | X1000_CLK_LCD, | 45 | X1000_CLK_LCD, |
46 | X1000_CLK_MSC0, | 46 | X1000_CLK_MSC0, |
47 | X1000_CLK_MSC1, | 47 | X1000_CLK_MSC1, |
48 | X1000_CLK_I2S_MCLK, | ||
49 | X1000_CLK_I2S_BCLK, | ||
50 | X1000_CLK_SFC, | 48 | X1000_CLK_SFC, |
49 | X1000_CLK_USB, | ||
50 | X1000_NUM_SIMPLE_CLKS, | ||
51 | X1000_CLK_I2S_MCLK = X1000_NUM_SIMPLE_CLKS, | ||
52 | X1000_CLK_I2S_BCLK, | ||
51 | X1000_CLK_COUNT, | 53 | X1000_CLK_COUNT, |
52 | } x1000_clk_t; | 54 | } x1000_clk_t; |
53 | 55 | ||