1 /******************************************************************************
2  * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *****************************************************************************/
18 
19 /********************************************************************************************************
20  * @file	clock.c
21  *
22  * @brief	This is the source file for B91
23  *
24  * @author	Driver Group
25  *
26  *******************************************************************************************************/
27 #include "clock.h"
28 #include "mspi.h"
29 #include "sys.h"
30 #include "stimer.h"
31 /**********************************************************************************************************************
32  *                                			  local constants                                                       *
33  *********************************************************************************************************************/
34 
35 
36 /**********************************************************************************************************************
37  *                                           	local macro                                                        *
38  *********************************************************************************************************************/
39 
40 
41 /**********************************************************************************************************************
42  *                                             local data type                                                     *
43  *********************************************************************************************************************/
44 
45 
46 /**********************************************************************************************************************
47  *                                              global variable                                                       *
48  *********************************************************************************************************************/
49 sys_clk_t sys_clk = {
50 	.pll_clk = 192,
51 	.cclk = 24,
52 	.hclk = 24,
53 	.pclk = 24,
54 	.mspi_clk = 24,
55 };
56 _attribute_data_retention_sec_ unsigned char tl_24mrc_cal;
57 clk_32k_type_e g_clk_32k_src;
58 /**********************************************************************************************************************
59  *                                              local variable                                                     *
60  *********************************************************************************************************************/
61 
62 /**********************************************************************************************************************
63  *                                          local function prototype                                               *
64  *********************************************************************************************************************/
65 
66 /**********************************************************************************************************************
67  *                                         global function implementation                                             *
68  *********************************************************************************************************************/
69 
70 
71 /**
72  * @brief   	This function serves to set 32k clock source.
73  * @param[in]   src - variable of 32k type.
74  * @return  	none.
75  */
clock_32k_init(clk_32k_type_e src)76 void clock_32k_init(clk_32k_type_e src)
77 {
78 	unsigned char sel_32k   = analog_read_reg8(0x4e)&0x7f;
79 	unsigned char power_32k = analog_read_reg8(0x05)&0xfc;
80 	analog_write_reg8(0x4e, sel_32k|(src<<7));
81 	if(src)
82 	{
83 		analog_write_reg8(0x05, power_32k|0x1);//32k xtal
84 	}
85 	else
86 	{
87 		analog_write_reg8(0x05, power_32k|0x2);//32k rc
88 	}
89 	g_clk_32k_src = src;
90 }
91 
92 /**
93  * @brief   	This function serves to kick 32k xtal.
94  * @param[in]   xtal_times - kick times.
95  * @return  	1 success, 0 error.
96  */
clock_kick_32k_xtal(unsigned char xtal_times)97 unsigned char clock_kick_32k_xtal(unsigned char xtal_times)
98 {
99 	int last_32k_tick;
100 	int curr_32k_tick;
101 	for(unsigned char i = 0; i< xtal_times; i++)
102 	{
103 		if(0xff == g_chip_version)
104 		{
105 			delay_ms(1000);
106 		}
107 		else		//**Note that the clock is 24M crystal oscillator. PCLK is 24MHZ
108 		{
109 			//2.set PD0 as pwm output
110 			unsigned char pwm_clk = read_reg8(0x1401d8);//**condition: PCLK is 24MHZ,PCLK = HCLK
111 			write_reg8(0x1401d8,((pwm_clk & 0xfc) | 0x01));//PCLK = 12M
112 
113 			unsigned char reg_31e = read_reg8(0x14031e);	//PD0 -> pwm0
114 			write_reg8(0x14031e, reg_31e & 0xfe);
115 			unsigned char reg_336 = read_reg8(0x140336);
116 			write_reg8(0x140336, (reg_336 & 0xfc) | 0x02);
117 			unsigned char reg_355 = read_reg8(0x140355);
118 			write_reg8(0x140355, reg_355 | 0x01);
119 
120 			unsigned short reg_414 = read_reg16(0x140414);	//pwm0 cmp
121 			write_reg16(0x140414, 0x01);
122 			unsigned short reg_416 = read_reg16(0x140416);	//pwm0 max
123 			write_reg16(0x140416, 0x02);
124 
125 			write_reg8(0x140402, 0xb6);						//12M/(0xb6 + 1)/2 = 32k
126 			unsigned char reg_401 = read_reg8(0x140401);	//pwm_en  pwm0 enable
127 			write_reg8(0x140401, 0x01);
128 
129 			//3.wait for PWM wake up Xtal
130 			delay_ms(10);
131 
132 			//4.Xtal 32k output
133 			analog_write_reg8(0x03,0x4f); //<7:6>current select
134 
135 			//5.Recover PD0 as Xtal pin
136 			write_reg8(0x1401d8,pwm_clk);
137 			write_reg8(0x14031e,reg_31e);
138 			write_reg8(0x140336,reg_336);
139 			write_reg8(0x140355,reg_355);
140 			write_reg16(0x140414,reg_414);
141 			write_reg16(0x140416,reg_416);
142 			write_reg8(0x140401,reg_401);
143 		}
144 
145 		last_32k_tick = clock_get_32k_tick();
146 		delay_us(305);		//for 32k tick accumulator, tick period: 30.5us, dly 10 ticks
147 		curr_32k_tick = clock_get_32k_tick();
148 		if((curr_32k_tick - last_32k_tick) > 3)
149 		{
150 			return 1;		//pwm kick 32k pad success
151 		}
152 	}
153 	return 0;
154 }
155 
156 /**
157  * @brief     This function performs to select 24M as the system clock source.
158  * 			  24M RC is inaccurate, and it is greatly affected by temperature, if need use it so real-time calibration is required
159  *			  The 24M RC needs to be calibrated before the pm_sleep_wakeup function,
160  *			  because this clock will be used to kick 24m xtal start after wake up,
161  *	          The more accurate this time, the faster the crystal will start.Calibration cycle depends on usage
162  * @return    none.
163  */
clock_cal_24m_rc(void)164 void clock_cal_24m_rc(void)
165 {
166 	analog_write_reg8(0xc8, 0x80);
167 
168 
169 	analog_write_reg8(0x4f, analog_read_reg8(0x4f) | BIT(7) );
170 
171 	analog_write_reg8(0xc7, 0x0e);
172 	analog_write_reg8(0xc7, 0x0f);
173     while((analog_read_reg8(0xcf) & 0x80) == 0);
174     unsigned char cap = analog_read_reg8(0xcb);
175     analog_write_reg8(0x52, cap);		//write 24m cap into manual register
176 
177     analog_write_reg8(0x4f, analog_read_reg8(0x4f) & (~BIT(7)) );
178 
179     analog_write_reg8(0xc7, 0x0e);
180 	tl_24mrc_cal = analog_read_reg8(0x52);
181 }
182 
183 /**
184  * @brief     This function performs to select 32K as the system clock source.
185  * @return    none.
186  */
clock_cal_32k_rc(void)187 void clock_cal_32k_rc(void)
188 {
189 	analog_write_reg8(0x4f, ((analog_read_reg8(0x4f) & 0x3f) | 0x40));
190 	analog_write_reg8(0xc6, 0xf6);
191 	analog_write_reg8(0xc6, 0xf7);
192     while(0 == (analog_read_reg8(0xcf) & BIT(6))){};
193 	unsigned char res1 = analog_read_reg8(0xc9);	//read 32k res[13:6]
194 	analog_write_reg8(0x51, res1);		//write 32k res[13:6] into manual register
195 	unsigned char res2 = analog_read_reg8(0xca);	//read 32k res[5:0]
196 	analog_write_reg8(0x4f, (res2 | (analog_read_reg8(0x4f) & 0xc0)));		//write 32k res[5:0] into manual register
197 	analog_write_reg8(0xc6, 0xf6);
198 	analog_write_reg8(0x4f, ((analog_read_reg8(0x4f) & 0x3f) | 0x00));//manual on
199 }
200 
201 /**
202  * @brief  This function serves to set the 32k tick.
203  * @param  tick - the value of to be set to 32k.
204  * @return none.
205  */
clock_set_32k_tick(unsigned int tick)206 void clock_set_32k_tick(unsigned int tick)
207 {
208 	reg_system_ctrl |= FLD_SYSTEM_32K_WR_EN;//r_32k_wr = 1;
209 	while(reg_system_st & FLD_SYSTEM_RD_BUSY);
210 	reg_system_timer_set_32k = tick;
211 
212 	reg_system_st = FLD_SYSTEM_CMD_SYNC;//cmd_sync = 1,trig write
213 	//delay 10us
214 	__asm__("nop");__asm__("nop");__asm__("nop");__asm__("nop");
215 	__asm__("nop");__asm__("nop");__asm__("nop");__asm__("nop");
216 	__asm__("nop");__asm__("nop");__asm__("nop");__asm__("nop");
217 	__asm__("nop");__asm__("nop");__asm__("nop");__asm__("nop");
218 	while(reg_system_st & FLD_SYSTEM_CMD_SYNC);//wait wr_busy = 0
219 
220 }
221 
222 /**
223  * @brief  This function serves to get the 32k tick.
224  * @return none.
225  */
226 #if 0
227 unsigned int clock_get_32k_tick(void)
228 {
229 	unsigned int timer_32k_tick;
230 	reg_system_st = FLD_SYSTEM_CLR_RD_DONE;//clr rd_done
231 	while((reg_system_st & FLD_SYSTEM_CLR_RD_DONE) != 0);//wait rd_done = 0;
232 	reg_system_ctrl &= ~FLD_SYSTEM_32K_WR_EN;	//1:32k write mode; 0:32k read mode
233 	while((reg_system_st & FLD_SYSTEM_CLR_RD_DONE) == 0);//wait rd_done = 1;
234 	timer_32k_tick = reg_system_timer_read_32k;
235 	reg_system_ctrl |= FLD_SYSTEM_32K_WR_EN;	//1:32k write mode; 0:32k read mode
236 	return timer_32k_tick;
237 }
238 #else
239 /*
240  * modify by yi.bao,confirmed by guangjun at 20210105
241  * Use digital register way to get 32k tick may read error tick,cause the wakeup time is
242  * incorrect with the setting time,the sleep time will very little or very big,will not wakeup ontime.
243  */
clock_get_32k_tick(void)244 unsigned int clock_get_32k_tick(void)
245 {
246     unsigned int t0 = 0;
247     unsigned int t1 = 0;
248 
249     //In the system timer auto mode, when writing a tick value to the system tick, if the writing operation overlaps
250     //with the 32k rising edge, the writing operation will be unsuccessful. When reading the 32k tick value,
251     //first wait for the rising edge to pass to avoid overlap with the subsequent write tick value operation.
252     //modify by weihua.zhang, confirmed by jianzhi at 20210126
253 	t0 = analog_read_reg32(0x60);
254 	while(1)
255 	{
256 		t1 = analog_read_reg32(0x60);
257 		if((t1-t0) == 1)
258 		{
259 			return t1;
260 		}
261 		else if(t1-t0)
262 		{
263 			t0 = t1;
264 		}
265 	}
266 }
267 #endif
268 
269 /**
270  * @brief       This function use to select the system clock source.
271  * @param[in]   pll - pll clock.
272  * @param[in]	src - cclk source.
273  * @param[in]	cclk_div - the cclk divide from pll.it is useless if src is not PAD_PLL_DIV. cclk max is 96M
274  * @param[in]	hclk_div - the hclk divide from cclk.hclk max is 48M.
275  * @param[in]	pclk_div - the pclk divide from hclk.pclk max is 24M.if hclk = 1/2 * cclk, the pclk can not be 1/4 of hclk.
276  * @param[in]	mspi_clk_div - mspi_clk has two source. pll div and hclk.mspi max is 64M.
277  * @return      none
278  */
clock_init(sys_pll_clk_e pll,sys_clock_src_e src,sys_pll_div_to_cclk_e cclk_div,sys_cclk_div_to_hclk_e hclk_div,sys_hclk_div_to_pclk_e pclk_div,sys_pll_div_to_mspi_clk_e mspi_clk_div)279 void clock_init(sys_pll_clk_e pll,
280 		sys_clock_src_e src,
281 		sys_pll_div_to_cclk_e cclk_div,
282 		sys_cclk_div_to_hclk_e hclk_div,
283 		sys_hclk_div_to_pclk_e pclk_div,
284 		sys_pll_div_to_mspi_clk_e mspi_clk_div)
285 {
286 
287 	//pll clk
288 	analog_write_reg8(0x80, (analog_read_reg8(0x80) & 0xe0) | ((pll >> 2) & 0x1f));
289 	analog_write_reg8(0x09, (analog_read_reg8(0x09) & 0xf3) | ((pll&0x03) << 2));
290 	sys_clk.pll_clk = (pll >> 8);
291 
292 	//usb clock (192M/4 =48M) pll clock should be the multiple of 48, because USB clock is 48M.
293 	write_reg8(0x1401fb, sys_clk.pll_clk/48);
294 
295 	//wait for PLL stable
296 	analog_write_reg8(0x81, (analog_read_reg8(0x81) | BIT(6)));
297 	while(BIT(5) != (analog_read_reg8(0x88) & BIT(5)));
298 	analog_write_reg8(0x81, (analog_read_reg8(0x81) & ~BIT(6)));
299 
300 	//ensure mspi is not in busy status before change mspi clock
301 	mspi_stop_xip();
302 
303 	//change mspi clock should be ram code.
304 	if(CCLK_TO_MSPI_CLK == mspi_clk_div)
305 	{
306 		write_reg8(0x1401e8, read_reg8(0x1401e8) & 0x7f);  //bit7 0
307 	}
308 	else
309 	{
310 		write_reg8(0x1401e9, (read_reg8(0x1401e9) & 0x0f) | (mspi_clk_div<<4) );
311 		write_reg8(0x1401e8, read_reg8(0x1401e8) | BIT(7));  //if the div is odd, should set two times to ensure the correct sequence.
312 		write_reg8(0x1401e8, read_reg8(0x1401e8) | BIT(7));
313 		sys_clk.mspi_clk = sys_clk.pll_clk / mspi_clk_div;
314 	}
315 
316 	//hclk and pclk should be set ahead of cclk, ensure the hclk and pclk not exceed the max clk(cclk max 96M, hclk max 48M, pclk max 24M)
317 	if(CCLK_DIV1_TO_HCLK == hclk_div)
318 	{
319 		write_reg8(0x1401d8, read_reg8(0x1401d8) & ~BIT(2));
320 	}
321 	else
322 	{
323 		write_reg8(0x1401d8, read_reg8(0x1401d8) | BIT(2));
324 	}
325 
326 	//pclk can div1/div2/div4 from hclk.
327 	if(HCLK_DIV1_TO_PCLK == pclk_div)
328 	{
329 		write_reg8(0x1401d8, read_reg8(0x1401d8) & 0xfc);
330 	}
331 	else
332 	{
333 		write_reg8(0x1401d8, (read_reg8(0x1401d8) & 0xfc) | (pclk_div/2));
334 	}
335 
336 	//select cclk source(RC24M/PAD24M/PAD_PLL_DIV/PAD_PLL)
337 	if(PAD_PLL_DIV == src)
338 	{
339 		write_reg8(0x1401e8, (read_reg8(0x1401e8) & 0xf0) | cclk_div);
340 		sys_clk.cclk = sys_clk.pll_clk / cclk_div;
341 	}
342 	else if(PAD_PLL == src)
343 	{
344 		sys_clk.cclk = sys_clk.pll_clk;
345 	}
346 	else
347 	{
348 		sys_clk.cclk = 24;
349 	}
350 	write_reg8(0x1401e8, (read_reg8(0x1401e8) & 0x8f) | (src << 4));
351 
352 	//clk record.
353 	sys_clk.hclk = sys_clk.cclk/hclk_div;
354 	sys_clk.pclk = sys_clk.hclk / pclk_div;
355 	if(CCLK_TO_MSPI_CLK == mspi_clk_div){
356 		sys_clk.mspi_clk = sys_clk.cclk;
357 	}
358 }
359 
360 
361 /**********************************************************************************************************************
362  *                    						local function implementation                                             *
363  *********************************************************************************************************************/
364