1 /***************************************************************************//**
2 * @file
3 * @brief DCDC Coulomb Counter (DCDC_COULOMB_COUNTER) peripheral API
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
7 *******************************************************************************
8 *
9 * SPDX-License-Identifier: Zlib
10 *
11 * The licensor of this software is Silicon Laboratories Inc.
12 *
13 * This software is provided 'as-is', without any express or implied
14 * warranty. In no event will the authors be held liable for any damages
15 * arising from the use of this software.
16 *
17 * Permission is granted to anyone to use this software for any purpose,
18 * including commercial applications, and to alter it and redistribute it
19 * freely, subject to the following restrictions:
20 *
21 * 1. The origin of this software must not be misrepresented; you must not
22 * claim that you wrote the original software. If you use this software
23 * in a product, an acknowledgment in the product documentation would be
24 * appreciated but is not required.
25 * 2. Altered source versions must be plainly marked as such, and must not be
26 * misrepresented as being the original software.
27 * 3. This notice may not be removed or altered from any source distribution.
28 *
29 ******************************************************************************/
30
31 #include "sl_hal_dcdc_coulomb_counter.h"
32 #if defined(DCDC_COUNT) && (DCDC_COUNT > 0) && defined(DCDC_CCCTRL_CCEN)
33 #include "sl_assert.h"
34 #include "em_bus.h"
35 #include "em_prs.h"
36
37 /***************************************************************************//**
38 * @addtogroup dcdccoulombcounter DCDC - DCDC Coulomb Counter Unit
39 * @brief DCDC Coulomb Counter (DCDC_COULOMB_COUNTER) Peripheral API
40 * @details
41 * This module contains functions to control the DCDC_COULOMB_COUNTER peripheral
42 * of Silicon Labs 32-bit MCUs and SoCs. The DCDC_COULOMB_COUNTER module
43 * measures the charge delivered by the DC-DC.
44 * @{
45 ******************************************************************************/
46
47 /* Global variable to store the channel assigned for calibrating */
48 /* the DCDC Coulomb Counter module. */
49 static int prs_channel = -1;
50
51 /***************************************************************************//**
52 * Initializes DCDC_COULOMB_COUNTER module.
53 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_init(const sl_hal_dcdc_coulomb_counter_config_t * p_config)54 void sl_hal_dcdc_coulomb_counter_init(const sl_hal_dcdc_coulomb_counter_config_t *p_config)
55 {
56 if (DCDC->CCCTRL & _DCDC_CCCTRL_CCEN_MASK) {
57 /* Disable COULOMB_COUNTER_INTERNAL module. */
58 sl_hal_dcdc_coulomb_counter_disable();
59 }
60
61 /* Set configuration. */
62 /* The counter thresholds can be used be used to establish a service */
63 /* interval for the coulomb counter hardware. */
64 DCDC->CCTHR = ((uint32_t)(p_config->counter_threshold_em0) << _DCDC_CCTHR_EM0CNT_SHIFT)
65 | ((uint32_t)(p_config->counter_threshold_em2) << _DCDC_CCTHR_EM2CNT_SHIFT);
66 }
67
68 /***************************************************************************//**
69 * Disables DCDC_COULOMB_COUNTER module.
70 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_disable(void)71 void sl_hal_dcdc_coulomb_counter_disable(void)
72 {
73 /* Quick exit if the DCDC_COULOMB_COUNTER and is already disabled. */
74 if ((DCDC->CCCTRL & _DCDC_CCCTRL_CCEN_MASK) == 0U) {
75 return;
76 }
77
78 /* Stop counting if running. */
79 if (DCDC->CCSTATUS & _DCDC_CCSTATUS_CCRUNNING_MASK) {
80 sl_hal_dcdc_coulomb_counter_stop();
81 }
82
83 /* Disable module. */
84 EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
85 DCDC->CCCTRL_CLR = DCDC_CCCTRL_CCEN;
86 }
87
88 /***************************************************************************//**
89 * Gets the DCDC_COULOMB_COUNTER count for the selected energy mode.
90 *
91 * @note As a guideline, using the default DC-DC settings, the DC-DC may
92 * produce up to 60,000-70,000 pulses per second for every mA of load current.
93 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_get_count(sl_hal_dcdc_coulomb_counter_emode_t emode)94 uint32_t sl_hal_dcdc_coulomb_counter_get_count(sl_hal_dcdc_coulomb_counter_emode_t emode)
95 {
96 if (emode == SL_HAL_DCDC_COULOMB_COUNTER_EM0) {
97 return DCDC->CCEM0CNT;
98 } else {
99 return DCDC->CCEM2CNT;
100 }
101 }
102
103 /***************************************************************************//**
104 * Initializes the calibration of the DCDC Coulomb Counter.
105 *
106 * @note The coulomb counter should be calibrated to determine the
107 * charge per pulse, both for EM0/1 and for EM2/3 settings of the DC-DC.
108 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_cal_init(sl_hal_dcdc_coulomb_counter_calibration_config_t config)109 void sl_hal_dcdc_coulomb_counter_cal_init(sl_hal_dcdc_coulomb_counter_calibration_config_t config)
110 {
111 CMU_ClockEnable(cmuClock_PRS, true);
112
113 if (prs_channel == -1) {
114 prs_channel = PRS_GetFreeChannel(prsTypeAsync);
115
116 /* Channel number >= 0 if an unused PRS channel was found. */
117 /* If no free PRS channel was found then -1 is returned. */
118 if (prs_channel == -1) {
119 EFM_ASSERT(false);
120 }
121
122 /* Configure an asynchronous PRS channel */
123 /* to route the DCDC MONO70NSANA producer to the CMU CALDN consumer. */
124 PRS_SourceAsyncSignalSet(prs_channel, PRS_ASYNC_CH_CTRL_SOURCESEL_DCDC, PRS_ASYNC_CH_CTRL_SIGSEL_DCDCMONO70NSANA);
125 PRS_ConnectConsumer(prs_channel, prsTypeAsync, prsConsumerCMU_CALDN);
126 }
127
128 /* Setup the calibration circuit: */
129 /* - HFXO is clocking up-counter. */
130 /* - PRS CMU_CALDN consumer is clocking down-counter. */
131 CMU_CalibrateConfig(config.cal_count, cmuSelect_PRS, config.reference_clk);
132 CMU_CalibrateCont(false);
133
134 /* Enable the desired calibration load. */
135 sl_hal_dcdc_coulomb_counter_set_cal_load_level(config.cal_emode, config.cal_load_level);
136 sl_hal_dcdc_coulomb_counter_enable_cal_load();
137
138 /* Wait for at least one DC-DC pulse to settle DC-DC. */
139 DCDC->IF_CLR = DCDC_IF_REGULATION;
140 while ((DCDC->IF & DCDC_IF_REGULATION) == 0U) {
141 /* Wait for DCDC to complete it's startup. */
142 }
143
144 /* Clear any pending calibration flags. */
145 CMU->IF_CLR = CMU_IF_CALRDY;
146 DCDC->CCCALCTRL_CLR = DCDC_CCCALCTRL_CCCALHALT;
147 }
148
149 /***************************************************************************//**
150 * Starts DCDC_COULOMB_COUNTER calibration sequence.
151 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_cal_start(void)152 void sl_hal_dcdc_coulomb_counter_cal_start(void)
153 {
154 /* Start the up counter. */
155 CMU_CalibrateStart();
156 }
157
158 /***************************************************************************//**
159 * Stops DCDC_COULOMB_COUNTER calibration sequence.
160 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_cal_stop(void)161 void sl_hal_dcdc_coulomb_counter_cal_stop(void)
162 {
163 CMU_CalibrateStop();
164
165 sl_hal_dcdc_coulomb_counter_disable_cal_load();
166 }
167
168 /***************************************************************************//**
169 * Sets the calibration load level.
170 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_set_cal_load_level(sl_hal_dcdc_coulomb_counter_emode_t emode,sl_hal_dcdc_coulomb_counter_calibration_load_level_t load_level)171 void sl_hal_dcdc_coulomb_counter_set_cal_load_level(sl_hal_dcdc_coulomb_counter_emode_t emode,
172 sl_hal_dcdc_coulomb_counter_calibration_load_level_t load_level)
173 {
174 /* Set load level. */
175 DCDC->CCCALCTRL = (DCDC->CCCALCTRL & ~(_DCDC_CCCALCTRL_CCLVL_MASK))
176 | ((load_level << _DCDC_CCCALCTRL_CCLVL_SHIFT) & _DCDC_CCCALCTRL_CCLVL_MASK);
177
178 EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
179 /* Adjust DCDC when calibrating for EM2. */
180 if (emode == SL_HAL_DCDC_COULOMB_COUNTER_EM2) {
181 /* Forces DCDC to use EM23CTRL0.IPKVAL peak current settings in EM0. */
182 DCDC->CCCALCTRL_SET = DCDC_CCCALCTRL_CCCALEM2;
183 } else {
184 DCDC->CCCALCTRL_CLR = DCDC_CCCALCTRL_CCCALEM2;
185 }
186 }
187
188 /***************************************************************************//**
189 * Enables the calibration load.
190 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_enable_cal_load(void)191 void sl_hal_dcdc_coulomb_counter_enable_cal_load(void)
192 {
193 DCDC->CCCALCTRL_SET = DCDC_CCCALCTRL_CCLOADEN;
194 }
195
196 /***************************************************************************//**
197 * Disables the calibration load.
198 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_disable_cal_load(void)199 void sl_hal_dcdc_coulomb_counter_disable_cal_load(void)
200 {
201 DCDC->CCCALCTRL_CLR = DCDC_CCCALCTRL_CCLOADEN;
202 }
203
204 /***************************************************************************//**
205 * Gets the calibration load current from the stored value
206 * in DEVINFO as measured during production testing.
207 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_get_cal_load_current(sl_hal_dcdc_coulomb_counter_calibration_load_level_t load_level)208 uint16_t sl_hal_dcdc_coulomb_counter_get_cal_load_current(sl_hal_dcdc_coulomb_counter_calibration_load_level_t load_level)
209 {
210 uint16_t ccload = 0U;
211
212 switch (load_level) {
213 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD0:
214 ccload = (DEVINFO->CCLOAD10 & _DEVINFO_CCLOAD10_CCLOAD0_MASK)
215 >> _DEVINFO_CCLOAD10_CCLOAD0_SHIFT;
216 break;
217
218 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD1:
219 ccload = (DEVINFO->CCLOAD10 & _DEVINFO_CCLOAD10_CCLOAD1_MASK)
220 >> _DEVINFO_CCLOAD10_CCLOAD1_SHIFT;
221 break;
222
223 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD2:
224 ccload = (DEVINFO->CCLOAD32 & _DEVINFO_CCLOAD32_CCLOAD2_MASK)
225 >> _DEVINFO_CCLOAD32_CCLOAD2_SHIFT;
226 break;
227
228 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD3:
229 ccload = (DEVINFO->CCLOAD32 & _DEVINFO_CCLOAD32_CCLOAD3_MASK)
230 >> _DEVINFO_CCLOAD32_CCLOAD3_SHIFT;
231 break;
232
233 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD4:
234 ccload = (DEVINFO->CCLOAD54 & _DEVINFO_CCLOAD54_CCLOAD4_MASK)
235 >> _DEVINFO_CCLOAD54_CCLOAD4_SHIFT;
236 break;
237
238 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD5:
239 ccload = (DEVINFO->CCLOAD54 & _DEVINFO_CCLOAD54_CCLOAD5_MASK)
240 >> _DEVINFO_CCLOAD54_CCLOAD5_SHIFT;
241 break;
242
243 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD6:
244 ccload = (DEVINFO->CCLOAD76 & _DEVINFO_CCLOAD76_CCLOAD6_MASK)
245 >> _DEVINFO_CCLOAD76_CCLOAD6_SHIFT;
246 break;
247
248 case SL_HAL_DCDC_COULOMB_COUNTER_CAL_LOAD7:
249 ccload = (DEVINFO->CCLOAD76 & _DEVINFO_CCLOAD76_CCLOAD7_MASK)
250 >> _DEVINFO_CCLOAD76_CCLOAD7_SHIFT;
251 break;
252
253 default:
254 EFM_ASSERT(false);
255 }
256
257 /* Make sure calibration load setting was measured during production test. */
258 EFM_ASSERT(ccload < (_DEVINFO_CCLOAD10_CCLOAD0_MASK >> _DEVINFO_CCLOAD10_CCLOAD0_SHIFT));
259
260 return ccload;
261 }
262
263 /***************************************************************************//**
264 * Gets the frequency (in Hz) of the CMU Calibration Up-Counter source.
265 *
266 * @note A high-frequency oscillator source of known frequency (fHF)
267 * is used to time pulses from the DC-DC and establish the charge per pulse.
268 ******************************************************************************/
sl_hal_dcdc_coulomb_counter_get_cal_reference_freq(void)269 uint32_t sl_hal_dcdc_coulomb_counter_get_cal_reference_freq(void)
270 {
271 uint32_t freq = 0U;
272 uint8_t upsel = _CMU_CALCTRL_UPSEL_DISABLED;
273
274 upsel = (CMU->CALCTRL & _CMU_CALCTRL_UPSEL_MASK) >> _CMU_CALCTRL_UPSEL_SHIFT;
275
276 switch (upsel) {
277 case _CMU_CALCTRL_UPSEL_PRS:
278 freq = CMU_ClockFreqGet(cmuClock_PRS);
279 break;
280
281 case _CMU_CALCTRL_UPSEL_HFXO:
282 freq = SystemHFXOClockGet();
283 break;
284
285 case _CMU_CALCTRL_UPSEL_LFXO:
286 freq = SystemLFXOClockGet();
287 break;
288
289 case _CMU_CALCTRL_UPSEL_HFRCODPLL:
290 freq = SystemHFRCODPLLClockGet();
291 break;
292
293 case _CMU_CALCTRL_UPSEL_FSRCO:
294 freq = SystemFSRCOClockGet();
295 break;
296
297 case _CMU_CALCTRL_UPSEL_LFRCO:
298 freq = SystemLFRCOClockGet();
299 break;
300
301 case _CMU_CALCTRL_UPSEL_ULFRCO:
302 freq = SystemULFRCOClockGet();
303 break;
304
305 case _CMU_CALCTRL_UPSEL_DISABLED:
306 default:
307 EFM_ASSERT(false);
308 }
309
310 return freq;
311 }
312
313 /** @} (end addtogroup dcdccoulombcounter) */
314 #endif /* defined(DCDC_COUNT) && (DCDC_COUNT > 0) && defined(DCDC_CCCTRL_CCEN) */
315