1 /*****************************************************************************
2 *
3 * \file
4 *
5 * \brief Power Manager clocks configuration helper.
6 *
7 * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
8 *
9 * \asf_license_start
10 *
11 * \page License
12 *
13 * Subject to your compliance with these terms, you may use Microchip
14 * software and any derivatives exclusively with Microchip products.
15 * It is your responsibility to comply with third party license terms applicable
16 * to your use of third party software (including open source software) that
17 * may accompany Microchip software.
18 *
19 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
21 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
22 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
23 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
24 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
25 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
26 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
27 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
28 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
29 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
30 *
31 * \asf_license_stop
32 *
33 *****************************************************************************/
34 /*
35 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
36 */
37
38
39 #include <string.h>
40 #include "compiler.h"
41 #include "pm.h"
42
43 extern void flashc_set_wait_state(unsigned int wait_state);
44 #if (defined AVR32_FLASHC_210_H_INCLUDED)
45 extern void flashc_issue_command(unsigned int command, int page_number);
46 #endif
47
48
49 #define PM_MAX_MUL ((1 << AVR32_PM_PLL0_PLLMUL_SIZE) - 1)
50
51
pm_configure_clocks(pm_freq_param_t * param)52 int pm_configure_clocks(pm_freq_param_t *param)
53 {
54 // Supported frequencies:
55 // Fosc0 mul div PLL div2_en cpu_f pba_f Comment
56 // 12 15 1 192 1 12 12
57 // 12 9 3 40 1 20 20 PLL out of spec
58 // 12 15 1 192 1 24 12
59 // 12 9 1 120 1 30 15
60 // 12 9 3 40 0 40 20 PLL out of spec
61 // 12 15 1 192 1 48 12
62 // 12 15 1 192 1 48 24
63 // 12 8 1 108 1 54 27
64 // 12 9 1 120 1 60 15
65 // 12 9 1 120 1 60 30
66 // 12 10 1 132 1 66 16.5
67 //
68 unsigned long in_cpu_f = param->cpu_f;
69 unsigned long in_osc0_f = param->osc0_f;
70 unsigned long mul, div, div2_en = 0, div2_cpu = 0, div2_pba = 0;
71 unsigned long pll_freq, rest;
72 bool b_div2_pba, b_div2_cpu;
73
74 // Switch to external Oscillator 0
75 pm_switch_to_osc0(&AVR32_PM, in_osc0_f, param->osc0_startup);
76
77 // Start with CPU freq config
78 if (in_cpu_f == in_osc0_f)
79 {
80 param->cpu_f = in_osc0_f;
81 param->pba_f = in_osc0_f;
82 return PM_FREQ_STATUS_OK;
83 }
84 else if (in_cpu_f < in_osc0_f)
85 {
86 // TBD
87 }
88
89 rest = in_cpu_f % in_osc0_f;
90
91 for (div = 1; div < 32; div++)
92 {
93 if ((div * rest) % in_osc0_f == 0)
94 break;
95 }
96 if (div == 32)
97 return PM_FREQ_STATUS_FAIL;
98
99 mul = (in_cpu_f * div) / in_osc0_f;
100
101 if (mul > PM_MAX_MUL)
102 return PM_FREQ_STATUS_FAIL;
103
104 // export 2power from PLL div to div2_cpu
105 while (!(div % 2))
106 {
107 div /= 2;
108 div2_cpu++;
109 }
110
111 // Here we know the mul and div parameter of the PLL config.
112 // . Check out if the PLL has a valid in_cpu_f.
113 // . Try to have for the PLL frequency (VCO output) the highest possible value
114 // to reduce jitter.
115 while (in_osc0_f * 2 * mul / div < AVR32_PM_PLL_VCO_RANGE0_MAX_FREQ)
116 {
117 if (2 * mul > PM_MAX_MUL)
118 break;
119 mul *= 2;
120 div2_cpu++;
121 }
122
123 if (div2_cpu != 0)
124 {
125 div2_cpu--;
126 div2_en = 1;
127 }
128
129 pll_freq = in_osc0_f * mul / (div * (1 << div2_en));
130
131 // Update real CPU Frequency
132 param->cpu_f = pll_freq / (1 << div2_cpu);
133 mul--;
134
135 pm_pll_setup(&AVR32_PM
136 , 0 // pll
137 , mul // mul
138 , div // div
139 , 0 // osc
140 , 16 // lockcount
141 );
142
143 pm_pll_set_option(&AVR32_PM
144 , 0 // pll
145 // PLL clock is lower than 160MHz: need to set pllopt.
146 , (pll_freq < AVR32_PM_PLL_VCO_RANGE0_MIN_FREQ) ? 1 : 0 // pll_freq
147 , div2_en // pll_div2
148 , 0 // pll_wbwdisable
149 );
150
151 rest = pll_freq;
152 while (rest > AVR32_PM_PBA_MAX_FREQ ||
153 rest != param->pba_f)
154 {
155 div2_pba++;
156 rest = pll_freq / (1 << div2_pba);
157 if (rest < param->pba_f)
158 break;
159 }
160
161 // Update real PBA Frequency
162 param->pba_f = pll_freq / (1 << div2_pba);
163
164 // Enable PLL0
165 pm_pll_enable(&AVR32_PM, 0);
166
167 // Wait for PLL0 locked
168 pm_wait_for_pll0_locked(&AVR32_PM);
169
170 if (div2_cpu)
171 {
172 b_div2_cpu = true;
173 div2_cpu--;
174 }
175 else
176 b_div2_cpu = false;
177
178 if (div2_pba)
179 {
180 b_div2_pba = true;
181 div2_pba--;
182 }
183 else
184 b_div2_pba = false;
185
186 pm_cksel(&AVR32_PM
187 , b_div2_pba, div2_pba // PBA
188 , b_div2_cpu, div2_cpu // PBB
189 , b_div2_cpu, div2_cpu // HSB
190 );
191
192 if (param->cpu_f > AVR32_FLASHC_FWS_0_MAX_FREQ)
193 {
194 flashc_set_wait_state(1);
195 #if (defined AVR32_FLASHC_210_H_INCLUDED)
196 if (param->cpu_f > AVR32_FLASHC_HSEN_FWS_1_MAX_FREQ)
197 flashc_issue_command(AVR32_FLASHC_FCMD_CMD_HSEN, -1);
198 else
199 flashc_issue_command(AVR32_FLASHC_FCMD_CMD_HSDIS, -1);
200 #endif
201 }
202 else
203 {
204 flashc_set_wait_state(0);
205 #if (defined AVR32_FLASHC_210_H_INCLUDED)
206 if (param->cpu_f > AVR32_FLASHC_HSEN_FWS_0_MAX_FREQ)
207 flashc_issue_command(AVR32_FLASHC_FCMD_CMD_HSEN, -1);
208 else
209 flashc_issue_command(AVR32_FLASHC_FCMD_CMD_HSDIS, -1);
210 #endif
211 }
212
213 pm_switch_to_clock(&AVR32_PM, AVR32_PM_MCCTRL_MCSEL_PLL0);
214
215 return PM_FREQ_STATUS_OK;
216 }
217
218
pm_configure_usb_clock(void)219 void pm_configure_usb_clock(void)
220 {
221 #if UC3A3
222
223 // Setup USB GCLK.
224 pm_gc_setup(&AVR32_PM, AVR32_PM_GCLK_USBB, // gc
225 0, // osc_or_pll: use Osc (if 0) or PLL (if 1)
226 0, // pll_osc: select Osc0/PLL0 or Osc1/PLL1
227 0, // diven
228 0); // div
229
230 // Enable USB GCLK.
231 pm_gc_enable(&AVR32_PM, AVR32_PM_GCLK_USBB);
232 #else
233 // Use 12MHz from OSC0 and generate 96 MHz
234 pm_pll_setup(&AVR32_PM, 1, // pll.
235 7, // mul.
236 1, // div.
237 0, // osc.
238 16); // lockcount.
239
240 pm_pll_set_option(&AVR32_PM, 1, // pll.
241 1, // pll_freq: choose the range 80-180MHz.
242 1, // pll_div2.
243 0); // pll_wbwdisable.
244
245 // start PLL1 and wait forl lock
246 pm_pll_enable(&AVR32_PM, 1);
247
248 // Wait for PLL1 locked.
249 pm_wait_for_pll1_locked(&AVR32_PM);
250
251 pm_gc_setup(&AVR32_PM, AVR32_PM_GCLK_USBB, // gc.
252 1, // osc_or_pll: use Osc (if 0) or PLL (if 1).
253 1, // pll_osc: select Osc0/PLL0 or Osc1/PLL1.
254 0, // diven.
255 0); // div.
256 pm_gc_enable(&AVR32_PM, AVR32_PM_GCLK_USBB);
257 #endif
258 }
259