summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c236
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h6
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
28static uint32_t pll_get(uint32_t pllreg, uint32_t onbit) 28struct 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
79static 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 */
88const 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
106static 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
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 } 117 }
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 118
78static 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
95static uint32_t msc_get(int msc) 129static 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
120static uint32_t i2s_mclk_get(void) 149#ifndef BOOTLOADER
150static 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
146static uint32_t i2s_bclk_get(void) 176static 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
151static uint32_t sfc_get(void) 181static 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
167uint32_t clk_get(x1000_clk_t clk) 191uint32_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
190const char* clk_get_name(x1000_clk_t clk) 201const 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