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