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