1 /*
2 *********************************************************************************************************
3 * AR100 SYSTEM
4 * AR100 Software System Develop Kits
5 * clock control unit module
6 *
7 * (c) Copyright 2012-2016, Sunny China
8 * All Rights Reserved
9 *
10 * File : ccu.c
11 * By : Sunny
12 * Version : v1.0
13 * Date : 2012-5-7
14 * Descript: clock control unit module.
15 * Update : date auther ver notes
16 * 2012-5-7 8:43:10 Sunny 1.0 Create this file.
17 *********************************************************************************************************
18 */
19
20 #include "ccu_i.h"
21 #include "platform.h"
22 #include "cpucfg_regs.h"
23 #include "hal_prcm.h"
24 #include "compiler_attributes.h"
25 #include "aw_io.h"
26
27 #define DO_NOT_CALIBRATION
28 /* ccu module registers base address */
29 struct ccu_reg_list *ccu_reg_addr;
30 struct ccu_pll_c0_cpux_reg0000 *ccu_pll_c0_cpux_reg_addr;
31 struct ccu_pll_ddr0_reg0010 *ccu_pll_ddr0_reg_addr;
32 struct ccu_pll_periph_reg0010 *ccu_pll_periph0_reg_addr;
33 struct ccu_pll_audio0_reg0020 *ccu_pll_audio0_reg_addr;
34 /* struct ccu_pll_periph1_reg0028 *ccu_pll_periph1_reg_addr; */
35
36 /* apb clock change notifier list */
37 struct notifier *apbs2_notifier_head;
38 u32 iosc_freq = 16000000;
39 u32 losc_freq = 32768;
40
41 #ifndef DO_NOT_CALIBRATION
42 static u32 filter_channel[10] = {0};
43 static u32 filter_count;
44 #endif
45
dcxo_cali_start(u32 __maybe_unused * bk)46 void dcxo_cali_start(u32 __maybe_unused *bk)
47 {
48 #ifndef DO_NOT_CALIBRATION
49 u32 calibration_status, xo_ctrl;
50
51 calibration_status = readl(IOSC_CLK_AUTO_CALI);
52 xo_ctrl = readl(XO_CTRL);
53 writel(readl(XO_CTRL) | (0xa), XO_CTRL);
54 writel(0x7, IOSC_CLK_AUTO_CALI);
55
56 bk[0] = calibration_status;
57 bk[1] = xo_ctrl;
58 #endif
59 }
60
dcxo_cali_end(__maybe_unused u32 * bk)61 void dcxo_cali_end(__maybe_unused u32 *bk)
62 {
63 #ifndef DO_NOT_CALIBRATION
64 u32 calibration_status, xo_ctrl;
65
66 calibration_status = bk[0];
67 xo_ctrl = bk[1];
68
69 writel(xo_ctrl, XO_CTRL);
70 writel(calibration_status, IOSC_CLK_AUTO_CALI);
71 #endif
72 }
73
osc_freq_init(void)74 void osc_freq_init(void)
75 {
76 #ifndef DO_NOT_CALIBRATION
77 u32 count = 0;
78 u32 value, sum = 0;
79 u32 integer, decimal;
80 u32 dcxo_status_bk[2] = {0};
81
82 filter_count = 0;
83 dcxo_cali_start(dcxo_status_bk);
84 time_mdelay(50);
85
86 while (1) {
87 count++;
88 if (count > 20)
89 break;
90 value = readl(IOSC_CLK_AUTO_CALI);
91 time_mdelay(16);
92 integer = (value >> DCXO_CALI_INTEGER_OFFSET);
93 decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
94 value = integer * losc_freq + (losc_freq * 65535 / decimal);
95 if (value > 24000000 || value < 8000000)
96 continue;
97 sum = value + sum;
98 filter_channel[filter_count] = value;
99 filter_count++;
100 if (filter_count == 5)
101 break;
102
103 }
104 if (filter_count == 0)
105 iosc_freq = 16000000;
106 else
107 iosc_freq = sum / filter_count;
108
109 dcxo_cali_end(dcxo_status_bk);
110 #endif
111 }
112
filter_sliding(u32 * channel,u32 value,u32 max_count)113 static u32 __maybe_unused filter_sliding(u32 *channel, u32 value, u32 max_count)
114 {
115 u32 *bk, *ch = channel + max_count - 1;
116 u32 sum = 0;
117
118 do {
119 bk = ch;
120 ch--;
121 *bk = *ch;
122 sum += *bk;
123 } while (ch >= (channel + 1));
124
125 *ch = value;
126 sum = sum + *ch;
127
128 return sum / max_count;
129 }
130
osc_freq_filter(void)131 void osc_freq_filter(void)
132 {
133 #ifndef DO_NOT_CALIBRATION
134 u32 integer, decimal;
135 u32 value, sum;
136
137 time_mdelay(16);
138 value = readl(IOSC_CLK_AUTO_CALI);
139 integer = (value >> DCXO_CALI_INTEGER_OFFSET);
140 decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
141 value = integer * losc_freq + (losc_freq * 65535 / decimal);
142
143 if (value > 24000000 || value < 8000000)
144 return ;
145
146 if (filter_count < 10) {
147 sum = iosc_freq * filter_count;
148 filter_channel[filter_count] = value;
149 sum = sum + value;
150 filter_count++;
151 iosc_freq = sum / filter_count;
152 } else {
153 iosc_freq = filter_sliding(&filter_channel[0], value, filter_count);
154 }
155 #endif
156 }
157
158
159 /*
160 *********************************************************************************************************
161 * INITIALIZE CCU
162 *
163 * Description: initialize clock control unit.
164 *
165 * Arguments : none.
166 *
167 * Returns : OK if initialize ccu succeeded, others if failed.
168 *********************************************************************************************************
169 */
ccu_init(void)170 s32 ccu_init(void)
171 {
172
173 /* initialize ccu register address */
174 ccu_reg_addr = (struct ccu_reg_list *)R_PRCM_REG_BASE;
175 ccu_pll_c0_cpux_reg_addr = (struct ccu_pll_c0_cpux_reg0000 *)CCU_PLL_C0_REG;
176 ccu_pll_ddr0_reg_addr = (struct ccu_pll_ddr0_reg0010 *)CCU_PLL_DDR0_REG;
177 ccu_pll_periph0_reg_addr = (struct ccu_pll_periph_reg0010 *)CCU_PLL_PERIPH0_REG;
178 ccu_pll_audio0_reg_addr = (struct ccu_pll_audio0_reg0020 *)CCU_PLL_AUDIO0_REG;
179 /* ccu_pll_periph1_reg_addr = (struct ccu_pll_periph1_reg0028 *)CCU_PLL_PERIPH1_REG; */
180 #ifndef CFG_FPGA_PLATFORM
181 /* setup cpus post div source to 200M(CCU_CPUS_POST_DIV) */
182 /* FIXME: board in fix */
183 /* u32 value;
184 value = (ccu_get_sclk_freq(CCU_SYS_CLK_PLL3)) / CCU_CPUS_POST_DIV;
185 if (value < 1) {
186 [>to avoid PLL5 freq less than CCU_CPUS_POST_DIV<]
187 value = 1;
188 }
189 ccu_reg_addr->cpus_clk_cfg.factor_m = value - 1;
190 [>set ar100 clock source to PLL5<]
191 ccu_set_mclk_src(CCU_MOD_CLK_CPUS, CCU_SYS_CLK_PLL3); */
192 #endif
193
194 /* initialize apb notifier list */
195 apbs2_notifier_head = NULL;
196
197 /* ccu initialize succeeded */
198 return OK;
199 }
200
201 /*
202 *********************************************************************************************************
203 * EXIT CCU
204 *
205 * Description: exit clock control unit.
206 *
207 * Arguments : none.
208 *
209 * Returns : OK if exit ccu succeeded, others if failed.
210 *********************************************************************************************************
211 */
ccu_exit(void)212 s32 ccu_exit(void)
213 {
214 ccu_pll_c0_cpux_reg_addr = NULL;
215 ccu_reg_addr = NULL;
216 ccu_pll_periph0_reg_addr = NULL;
217 ccu_pll_audio0_reg_addr = NULL;
218
219 return OK;
220 }
221
write_rtc_domain_reg(u32 reg,u32 value)222 void write_rtc_domain_reg(u32 reg, u32 value)
223 {
224 writel((unsigned long)value, (unsigned long)reg);
225 }
226
read_rtc_domain_reg(u32 reg)227 u32 read_rtc_domain_reg(u32 reg)
228 {
229 return readl((unsigned long)reg);
230 }
231
232