1 /*
2  * Copyright (C) 2019 ETH Zurich and University of Bologna
3  * Copyright (C) 2019 GreenWaves Technologies
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  * SPDX-License-Identifier: Apache-2.0
18  */
19 
20 /* FLL driver code for PULPissimo */
21 /* Author: Germain Haugou (germain.haugou@iis.ee.ethz.ch)
22  *         Eric Flamand (eric.flamand@greenwaves-technologies.com)
23  *         Robert Balas (balasr@iis.ee.ethz.ch)
24  */
25 
26 #include <pulp_io.h>
27 #include <stdint.h>
28 #include "system_metal.h"
29 #include "core-v-mcu-pulp-mem-map.h"
30 #include "hal_fll.h"
31 
32 #define __MAX(a, b)                                                            \
33 	({                                                                     \
34 		typeof(a) _a = (a);                                            \
35 		typeof(b) _b = (b);                                            \
36 		_a > _b ? _a : _b;                                             \
37 	})
38 
39 #define __FL1(x) (31 - __builtin_clz((x)))
40 
41 int __rt_fll_freq[ARCHI_NB_FLL];
42 static char __rt_fll_is_on[ARCHI_NB_FLL];
43 int __rt_freq_domains[RT_FREQ_NB_DOMAIN];
44 
pulp_fll_init(void)45 void pulp_fll_init(void)
46 {
47 	for (int i = 0; i < ARCHI_NB_FLL; i++) {
48 		__rt_fll_freq[i] = 0;
49 		__rt_fll_is_on[i] = 0;
50 	}
51 
52 	__rt_freq_domains[__RT_FREQ_DOMAIN_FC] = __fll_init(__RT_FLL_FC);
53 	__rt_freq_domains[__RT_FREQ_DOMAIN_PERIPH] =
54 		__fll_init(__RT_FLL_PERIPH);
55 }
56 
__rt_fll_get_mult_div_from_freq(unsigned int freq,unsigned int * mult,unsigned int * div)57 static unsigned int __rt_fll_get_mult_div_from_freq(unsigned int freq,
58 						    unsigned int *mult,
59 						    unsigned int *div)
60 {
61 	unsigned int fref = ARCHI_REF_CLOCK;
62 	unsigned int Log2M = __FL1(freq) - __FL1(fref);
63 	unsigned int D = __MAX(1, (FLL_LOG2_MAXM - Log2M) >> 1);
64 	unsigned int M = (freq << D) / fref;
65 	unsigned int fres;
66 
67 #if 0 /* PULP_CHIP == CHIP_QUENTIN */
68 
69 	/* TODO on quentin, the fll never converges (e.g. a mult factor of 15000 is
70 	 * not working), check what is the maximum value we can use
71 	 */
72 	while (M >= 10000) {
73 		M >>= 1;
74 		D--;
75 	};
76 
77 	if (D == 0) fres = fref*M;
78 	else fres = (fref*M + (1<<(D-1)))>>D;   /* Rounding */
79 
80 #else
81 	fres = (fref * M + (1 << (D - 1))) >> D; /* Rounding */
82 #endif
83 
84 	*mult = M;
85 	*div = D + 1;
86 	return fres;
87 }
88 
__rt_fll_get_freq_from_mult_div(unsigned int mult,unsigned int div)89 static unsigned int __rt_fll_get_freq_from_mult_div(unsigned int mult,
90 						    unsigned int div)
91 {
92 	// FreqOut = Fref * Mult/2^(Div-1)	With Mult on 16 bits and Div on 4 bits
93 	unsigned int fref = ARCHI_REF_CLOCK;
94 	unsigned int fres = (fref * mult) >> (div - 1);
95 	return fres;
96 }
97 
__fll_init(int fll)98 unsigned int __fll_init(int fll)
99 {
100 	//rt_trace(RT_TRACE_INIT, "Initializing FLL (fll: %d)\n", fll);
101 
102 	fll_reg_conf1_t reg1 = {
103 		.raw = readw((uintptr_t)(PULP_FLL_ADDR + FLL_CONF1_OFFSET +
104 					  fll * PULP_FLL_AREA_SIZE))
105 	};
106 
107 	/* Only lock the fll if it is not already done by the boot code */
108 	if (reg1.mode == 0) {
109 		/* Set Clock Ref lock assert count */
110 		fll_reg_conf2_t reg2 = {
111 			.raw = readw((uintptr_t)(PULP_FLL_ADDR +
112 						  FLL_CONF2_OFFSET +
113 						  fll * PULP_FLL_AREA_SIZE))
114 		};
115 
116 		reg2.assert_cycles = 6;
117 		reg2.lock_tolerance = 0x50;
118 		writew(reg2.raw, (uintptr_t)(PULP_FLL_ADDR + FLL_CONF2_OFFSET +
119 					      fll * PULP_FLL_AREA_SIZE));
120 		/*
121 		* In order to lock the fll faster, we first setup an approximated
122 		* frequency while we are in open loop as it is set immediately.
123 		* Then starting from this frequency, we should reach the target one
124 		* in lock mode faster.
125 		* We are in open loop, prime the fll forcing dco input, approx 70 MHz
126 		*/
127 		//rt_trace(RT_TRACE_INIT, "Priming FLL in open loop (fll: %d)\n",
128 		//	 fll);
129 		fll_reg_integrator_t reg_int = {
130 			.raw = readw((uintptr_t)(PULP_FLL_ADDR +
131 						  FLL_INTEGRATOR_OFFSET +
132 						  fll * PULP_FLL_AREA_SIZE))
133 		};
134 
135 /* TODO: quick hack to prevent 0 == 0 in next macro */
136 #ifndef PULP_CHIP
137 #define PULP_CHIP 1
138 #endif
139 
140 #if PULP_CHIP == CHIP_QUENTIN || PULP_CHIP == CHIP_KERBIN
141 #error "Touching bad FLL code path"
142 		/* TODO: don't know how to choose the right, 332 is too agressive for
143 		 * quentin and the fll is never converging
144 		 */
145 		reg_int.state_int_part = 100;
146 #else
147 		reg_int.state_int_part = 332;
148 #endif
149 
150 		writew(reg_int.raw,
151 		       (uintptr_t)(PULP_FLL_ADDR + FLL_INTEGRATOR_OFFSET +
152 				    fll * PULP_FLL_AREA_SIZE));
153 
154 		/* Lock Fll */
155 		reg1.output_lock_enable = 1;
156 		reg1.mode = 1;
157 		writew(reg1.raw, (uintptr_t)(PULP_FLL_ADDR + FLL_CONF1_OFFSET +
158 					      fll * PULP_FLL_AREA_SIZE));
159 	}
160 
161 	/* In case the FLL frequency was set while it was off, update it immediately */
162 	/* TODO */
163 	unsigned int freq = __rt_fll_freq[fll];
164 	if (freq) {
165 		__rt_fll_set_freq(fll, freq);
166 	} else {
167 		freq = __rt_fll_get_freq_from_mult_div(reg1.mult_factor,
168 						       reg1.clock_out_divider);
169 		__rt_fll_freq[fll] = freq;
170 	}
171 
172 	__rt_fll_is_on[fll] = 1;
173 
174 	//rt_trace(RT_TRACE_INIT, "FLL is locked (fll: %d, freq: %d)\n", fll,
175 	// freq);
176 
177 	return freq;
178 }
179 
__rt_fll_set_freq(int fll,unsigned int frequency)180 unsigned int __rt_fll_set_freq(int fll, unsigned int frequency)
181 {
182 	/* Synchronize with bridge so that it does not try to access the chip
183 	 * while we are changing the frequency
184 	 */
185 	// if (fll == __RT_FLL_FC)
186 	// 	__rt_bridge_req_shutdown();
187 
188 	unsigned int real_freq, mult, div;
189 	real_freq = __rt_fll_get_mult_div_from_freq(frequency, &mult, &div);
190 
191 	__rt_fll_freq[fll] = real_freq;
192 	if (__rt_fll_is_on[fll]) {
193 		fll_reg_conf1_t reg1 = {
194 			.raw = readw((uintptr_t)(PULP_FLL_ADDR +
195 						  FLL_CONF1_OFFSET +
196 						  fll * PULP_FLL_AREA_SIZE))
197 		};
198 
199 		reg1.mult_factor = mult;
200 		reg1.clock_out_divider = div;
201 
202 		writew(reg1.raw, (uintptr_t)(PULP_FLL_ADDR + FLL_CONF1_OFFSET +
203 					      fll * PULP_FLL_AREA_SIZE));
204 	}
205 
206 	return real_freq;
207 }
208