• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "hpm_pllctl_drv.h"
9 
10 #define PLLCTL_INT_PLL_MAX_FBDIV (2400U)
11 #define PLLCTL_INT_PLL_MIN_FBDIV (16U)
12 
13 #define PLLCTL_FRAC_PLL_MAX_FBDIV (240U)
14 #define PLLCTL_FRAC_PLL_MIN_FBDIV (20U)
15 
16 #define PLLCTL_PLL_MAX_REFDIV (63U)
17 #define PLLCTL_PLL_MIN_REFDIV (1U)
18 
19 #define PLLCTL_PLL_MAX_POSTDIV1 (7U)
20 #define PLLCTL_PLL_MIN_POSTDIV1 (1U)
21 
22 #define PLLCTL_FRAC_PLL_MIN_REF (10000000U)
23 #define PLLCTL_INT_PLL_MIN_REF (1000000U)
24 
25 
pllctl_set_pll_work_mode(PLLCTL_Type * ptr,uint8_t pll,bool int_mode)26 hpm_stat_t pllctl_set_pll_work_mode(PLLCTL_Type *ptr, uint8_t pll, bool int_mode)
27 {
28     if (int_mode) {
29         if (!(ptr->PLL[pll].CFG0 & PLLCTL_PLL_CFG0_DSMPD_MASK)) {
30             /* it was at frac mode, then it needs to be power down */
31             pllctl_pll_powerdown(ptr, pll);
32             ptr->PLL[pll].CFG0 |= PLLCTL_PLL_CFG0_DSMPD_MASK;
33             pllctl_pll_poweron(ptr, pll);
34         }
35     } else {
36         if (ptr->PLL[pll].CFG0 & PLLCTL_PLL_CFG0_DSMPD_MASK) {
37             /* pll has to be powered down to configure frac mode */
38             pllctl_pll_powerdown(ptr, pll);
39             ptr->PLL[pll].CFG0 &= ~PLLCTL_PLL_CFG0_DSMPD_MASK;
40             pllctl_pll_poweron(ptr, pll);
41         }
42     }
43 
44     return status_success;
45 }
46 
pllctl_set_refdiv(PLLCTL_Type * ptr,uint8_t pll,uint8_t div)47 hpm_stat_t pllctl_set_refdiv(PLLCTL_Type *ptr, uint8_t pll, uint8_t div)
48 {
49     uint32_t min_ref;
50 
51     if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
52             || (!div)
53             || (div > (PLLCTL_PLL_CFG0_REFDIV_MASK >> PLLCTL_PLL_CFG0_REFDIV_SHIFT))) {
54         return status_invalid_argument;
55     }
56 
57     if (ptr->PLL[pll].CFG0 & PLLCTL_PLL_CFG0_DSMPD_MASK) {
58         min_ref = PLLCTL_INT_PLL_MIN_REF;
59     } else {
60         min_ref = PLLCTL_FRAC_PLL_MIN_REF;
61     }
62 
63     if ((PLLCTL_SOC_PLL_REFCLK_FREQ / div) < min_ref) {
64         return status_pllctl_out_of_range;
65     }
66 
67     if (PLLCTL_PLL_CFG0_REFDIV_GET(ptr->PLL[pll].CFG0) != div) {
68         /* if div is different, it needs to be power down */
69         pllctl_pll_powerdown(ptr, pll);
70         ptr->PLL[pll].CFG0 = (ptr->PLL[pll].CFG0 & ~PLLCTL_PLL_CFG0_REFDIV_MASK)
71             | PLLCTL_PLL_CFG0_REFDIV_SET(div);
72         pllctl_pll_poweron(ptr, pll);
73     }
74     return status_success;
75 }
76 
pllctl_init_int_pll_with_freq(PLLCTL_Type * ptr,uint8_t pll,uint32_t freq_in_hz)77 hpm_stat_t pllctl_init_int_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
78                                     uint32_t freq_in_hz)
79 {
80     uint32_t freq, fbdiv, refdiv, postdiv;
81     if ((freq_in_hz < PLLCTL_PLL_VCO_FREQ_MIN)
82             || (freq_in_hz > PLLCTL_PLL_VCO_FREQ_MAX)) {
83         return status_invalid_argument;
84     }
85 
86     freq = freq_in_hz;
87     refdiv = PLLCTL_PLL_CFG0_REFDIV_GET(ptr->PLL[pll].CFG0);
88     postdiv = PLLCTL_PLL_CFG0_POSTDIV1_GET(ptr->PLL[pll].CFG0);
89     fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
90     if (fbdiv > PLLCTL_INT_PLL_MAX_FBDIV) {
91         /* current refdiv can't be used for the given frequency */
92         refdiv--;
93         do {
94             fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
95             if (fbdiv > PLLCTL_INT_PLL_MAX_FBDIV) {
96                 refdiv--;
97             } else {
98                 break;
99             }
100         } while (refdiv > PLLCTL_PLL_MIN_REFDIV);
101     } else if (fbdiv < PLLCTL_INT_PLL_MIN_FBDIV) {
102         /* current refdiv can't be used for the given frequency */
103         refdiv++;
104         do {
105             fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
106             if (fbdiv < PLLCTL_INT_PLL_MIN_FBDIV) {
107                 refdiv++;
108             } else {
109                 break;
110             }
111         } while (refdiv < PLLCTL_PLL_MAX_REFDIV);
112     }
113 
114     if ((refdiv > PLLCTL_PLL_MAX_REFDIV)
115             || (refdiv < PLLCTL_PLL_MIN_REFDIV)
116             || (fbdiv > PLLCTL_INT_PLL_MAX_FBDIV)
117             || (fbdiv < PLLCTL_INT_PLL_MIN_FBDIV)
118             || (((PLLCTL_SOC_PLL_REFCLK_FREQ / refdiv) < PLLCTL_INT_PLL_MIN_REF))) {
119         return status_pllctl_out_of_range;
120     }
121 
122     if (!(ptr->PLL[pll].CFG0 & PLLCTL_PLL_CFG0_DSMPD_MASK)) {
123         /* it was at frac mode, then it needs to be power down */
124         pllctl_pll_powerdown(ptr, pll);
125         ptr->PLL[pll].CFG0 |= PLLCTL_PLL_CFG0_DSMPD_MASK;
126     }
127 
128     if (PLLCTL_PLL_CFG0_REFDIV_GET(ptr->PLL[pll].CFG0) != refdiv) {
129         /* if refdiv is different, it needs to be power down */
130         pllctl_pll_powerdown(ptr, pll);
131         ptr->PLL[pll].CFG0 = (ptr->PLL[pll].CFG0 & ~PLLCTL_PLL_CFG0_REFDIV_MASK)
132             | PLLCTL_PLL_CFG0_REFDIV_SET(refdiv);
133     }
134 
135     ptr->PLL[pll].CFG2 = (ptr->PLL[pll].CFG2 & ~(PLLCTL_PLL_CFG2_FBDIV_INT_MASK)) | PLLCTL_PLL_CFG2_FBDIV_INT_SET(fbdiv);
136 
137     pllctl_pll_poweron(ptr, pll);
138     return status_success;
139 }
140 
pllctl_init_frac_pll_with_freq(PLLCTL_Type * ptr,uint8_t pll,uint32_t freq_in_hz)141 hpm_stat_t pllctl_init_frac_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
142                                     uint32_t freq_in_hz)
143 {
144     uint32_t frac, refdiv, fbdiv, freq, postdiv;
145     double div;
146     if ((freq_in_hz < PLLCTL_PLL_VCO_FREQ_MIN)
147             || (freq_in_hz > PLLCTL_PLL_VCO_FREQ_MAX)) {
148         return status_invalid_argument;
149     }
150 
151     freq = freq_in_hz;
152     refdiv = PLLCTL_PLL_CFG0_REFDIV_GET(ptr->PLL[pll].CFG0);
153     postdiv = PLLCTL_PLL_CFG0_POSTDIV1_GET(ptr->PLL[pll].CFG0);
154     fbdiv = (freq / postdiv) / (PLLCTL_SOC_PLL_REFCLK_FREQ / refdiv);
155 
156     if (fbdiv > PLLCTL_FRAC_PLL_MAX_FBDIV) {
157         /* current refdiv can't be used for the given frequency */
158         refdiv--;
159         do {
160             fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
161             if (fbdiv > PLLCTL_FRAC_PLL_MAX_FBDIV) {
162                 refdiv--;
163             } else {
164                 break;
165             }
166         } while (refdiv > PLLCTL_PLL_MIN_REFDIV);
167     } else if (fbdiv < PLLCTL_FRAC_PLL_MIN_FBDIV) {
168         /* current refdiv can't be used for the given frequency */
169         refdiv++;
170         do {
171             fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
172             if (fbdiv < PLLCTL_FRAC_PLL_MIN_FBDIV) {
173                 refdiv++;
174             } else {
175                 break;
176             }
177         } while (refdiv < PLLCTL_PLL_MAX_REFDIV);
178     }
179 
180     if ((refdiv > PLLCTL_PLL_MAX_REFDIV)
181             || (refdiv < PLLCTL_PLL_MIN_REFDIV)
182             || (fbdiv > PLLCTL_FRAC_PLL_MAX_FBDIV)
183             || (fbdiv < PLLCTL_FRAC_PLL_MIN_FBDIV)
184             || (((PLLCTL_SOC_PLL_REFCLK_FREQ / refdiv) < PLLCTL_FRAC_PLL_MIN_REF))) {
185         return status_pllctl_out_of_range;
186     }
187 
188     div = (double) freq / PLLCTL_SOC_PLL_REFCLK_FREQ * (refdiv * postdiv);
189     fbdiv = freq / (PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv));
190     frac = (div - fbdiv) * (1 << 24);
191 
192     /*
193      * pll has to be powered down to configure frac mode
194      */
195     pllctl_pll_powerdown(ptr, pll);
196 
197     ptr->PLL[pll].CFG0 = (ptr->PLL[pll].CFG0
198             & ~(PLLCTL_PLL_CFG0_REFDIV_MASK | PLLCTL_PLL_CFG0_DSMPD_MASK))
199         | PLLCTL_PLL_CFG0_REFDIV_SET(refdiv);
200 
201     pllctl_pll_ss_disable(ptr, pll);
202     ptr->PLL[pll].FREQ = (ptr->PLL[pll].FREQ
203             & ~(PLLCTL_PLL_FREQ_FRAC_MASK | PLLCTL_PLL_FREQ_FBDIV_FRAC_MASK))
204         | PLLCTL_PLL_FREQ_FBDIV_FRAC_SET(fbdiv) | PLLCTL_PLL_FREQ_FRAC_SET(frac);
205 
206     pllctl_pll_poweron(ptr, pll);
207     return status_success;
208 }
209 
pllctl_get_pll_freq_in_hz(PLLCTL_Type * ptr,uint8_t pll)210 uint32_t pllctl_get_pll_freq_in_hz(PLLCTL_Type *ptr, uint8_t pll)
211 {
212     uint32_t fbdiv, frac, refdiv, postdiv, refclk, freq;
213     if (ptr->PLL[pll].CFG1 & PLLCTL_PLL_CFG1_PLLPD_SW_MASK) {
214         /* pll is powered down */
215         return 0;
216     }
217 
218     refdiv = PLLCTL_PLL_CFG0_REFDIV_GET(ptr->PLL[pll].CFG0);
219     postdiv = PLLCTL_PLL_CFG0_POSTDIV1_GET(ptr->PLL[pll].CFG0);
220     refclk = PLLCTL_SOC_PLL_REFCLK_FREQ / (refdiv * postdiv);
221 
222     if (ptr->PLL[pll].CFG0 & PLLCTL_PLL_CFG0_DSMPD_MASK) {
223         /* pll int mode */
224         fbdiv = PLLCTL_PLL_CFG2_FBDIV_INT_GET(ptr->PLL[pll].CFG2);
225         freq = refclk * fbdiv;
226     } else {
227         /* pll frac mode */
228         fbdiv = PLLCTL_PLL_FREQ_FBDIV_FRAC_GET(ptr->PLL[pll].FREQ);
229         frac = PLLCTL_PLL_FREQ_FRAC_GET(ptr->PLL[pll].FREQ);
230         freq = (refclk * (fbdiv + ((double) frac / (1 << 24)))) + 0.5;
231     }
232     return freq;
233 }
234 
235