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