1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2020, NXP
4 * All rights reserved.
5 *
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 */
9
10 #include "fsl_spm.h"
11 #include "math.h" /* Using floor() function to convert float variable to int. */
12
13 /* Component ID definition, used by tools. */
14 #ifndef FSL_COMPONENT_ID
15 #define FSL_COMPONENT_ID "platform.drivers.spm"
16 #endif
17
18 /*!
19 * brief Gets the regulators Status.
20 *
21 * param base SPM peripheral base address.
22 * param info Pointer to status structure, see to #spm_regulator_status_t.
23 */
SPM_GetRegulatorStatus(SPM_Type * base,spm_regulator_status_t * info)24 void SPM_GetRegulatorStatus(SPM_Type *base, spm_regulator_status_t *info)
25 {
26 assert(NULL != info);
27
28 volatile uint32_t tmp32 = base->RSR; /* volatile here is to make sure this value is actually from the hardware. */
29
30 info->mcuLowPowerModeStatus =
31 (spm_mcu_low_power_mode_status_t)(uint32_t)((tmp32 & SPM_RSR_MCUPMSTAT_MASK) >> SPM_RSR_MCUPMSTAT_SHIFT);
32 info->isDcdcLdoOn =
33 (0x4UL == (0x4UL & ((tmp32 & SPM_RSR_REGSEL_MASK) >> SPM_RSR_REGSEL_SHIFT))); /* 1<<2 responses DCDC LDO. */
34 info->isAuxLdoOn =
35 (0x2UL == (0x2UL & ((tmp32 & SPM_RSR_REGSEL_MASK) >> SPM_RSR_REGSEL_SHIFT))); /* 1<<1 responses AUX LDO. */
36 info->isCoreLdoOn =
37 (0x1UL == (0x1UL & ((tmp32 & SPM_RSR_REGSEL_MASK) >> SPM_RSR_REGSEL_SHIFT))); /* 1<<0 responses CORE LDO. */
38 }
39
40 /*!
41 * brief Configures the low-voltage detect setting.
42 *
43 * This function configures the low-voltage detect setting, including the trip
44 * point voltage setting, enables or disables the interrupt, enables or disables the system reset.
45 *
46 * param base SPM peripheral base address.
47 * param config Pointer to low-voltage detect configuration structure, see to #spm_low_volt_detect_config_t.
48 */
SPM_SetLowVoltDetectConfig(SPM_Type * base,const spm_low_volt_detect_config_t * config)49 void SPM_SetLowVoltDetectConfig(SPM_Type *base, const spm_low_volt_detect_config_t *config)
50 {
51 uint32_t tmp32 = base->LVDSC1 & ~(SPM_LVDSC1_VDD_LVDIE_MASK | SPM_LVDSC1_VDD_LVDRE_MASK | SPM_LVDSC1_VDD_LVDV_MASK |
52 SPM_LVDSC1_COREVDD_LVDIE_MASK | SPM_LVDSC1_COREVDD_LVDRE_MASK);
53
54 /* VDD voltage detection. */
55 tmp32 |= SPM_LVDSC1_VDD_LVDV(config->vddLowVoltDetectSelect);
56 if (true == config->enableIntOnVddLowVolt)
57 {
58 tmp32 |= SPM_LVDSC1_VDD_LVDIE_MASK;
59 }
60 if (true == config->enableResetOnVddLowVolt)
61 {
62 tmp32 |= SPM_LVDSC1_VDD_LVDRE_MASK;
63 }
64 /* Clear the Low Voltage Detect Flag with previous power detect setting. */
65 tmp32 |= SPM_LVDSC1_VDD_LVDACK_MASK;
66
67 /* COREVDD voltage detection. */
68 if (true == config->enableIntOnCoreLowVolt)
69 {
70 tmp32 |= SPM_LVDSC1_COREVDD_LVDIE_MASK;
71 }
72 if (true == config->enableResetOnCoreLowVolt)
73 {
74 tmp32 |= SPM_LVDSC1_COREVDD_LVDRE_MASK;
75 }
76 tmp32 |= SPM_LVDSC1_COREVDD_LVDACK_MASK; /* Clear previous error flag. */
77
78 base->LVDSC1 = tmp32;
79 }
80
81 /*!
82 * brief Configures the low-voltage warning setting.
83 *
84 * This function configures the low-voltage warning setting, including the trip
85 * point voltage setting and enabling or disabling the interrupt.
86 *
87 * param base SPM peripheral base address.
88 * param config Pointer to Low-voltage warning configuration structure, see to #spm_low_volt_warning_config_t.
89 */
SPM_SetLowVoltWarningConfig(SPM_Type * base,const spm_low_volt_warning_config_t * config)90 void SPM_SetLowVoltWarningConfig(SPM_Type *base, const spm_low_volt_warning_config_t *config)
91 {
92 uint32_t tmp32 = base->LVDSC2 & ~(SPM_LVDSC2_VDD_LVWV_MASK | SPM_LVDSC2_VDD_LVWIE_MASK);
93
94 tmp32 |= SPM_LVDSC2_VDD_LVWV(config->vddLowVoltDetectSelect);
95 if (true == config->enableIntOnVddLowVolt)
96 {
97 tmp32 |= SPM_LVDSC2_VDD_LVWIE_MASK;
98 }
99 tmp32 |= SPM_LVDSC2_VDD_LVWACK_MASK; /* Clear previous error flag. */
100
101 base->LVDSC2 = tmp32;
102 }
103
104 /*!
105 * brief Configures the high-voltage detect setting.
106 *
107 * This function configures the high-voltage detect setting, including the trip
108 * point voltage setting, enabling or disabling the interrupt, enabling or disabling the system reset.
109 *
110 * param base SPM peripheral base address.
111 * param config High-voltage detect configuration structure, see to #spm_high_volt_detect_config_t.
112 */
SPM_SetHighVoltDetectConfig(SPM_Type * base,const spm_high_volt_detect_config_t * config)113 void SPM_SetHighVoltDetectConfig(SPM_Type *base, const spm_high_volt_detect_config_t *config)
114 {
115 uint32_t tmp32;
116
117 tmp32 = base->HVDSC1 & ~(SPM_HVDSC1_VDD_HVDIE_MASK | SPM_HVDSC1_VDD_HVDRE_MASK | SPM_HVDSC1_VDD_HVDV_MASK);
118 tmp32 |= SPM_HVDSC1_VDD_HVDV(config->vddHighVoltDetectSelect);
119 if (true == config->enableIntOnVddHighVolt)
120 {
121 tmp32 |= SPM_HVDSC1_VDD_HVDIE_MASK;
122 }
123 if (true == config->enableResetOnVddHighVolt)
124 {
125 tmp32 |= SPM_HVDSC1_VDD_HVDRE_MASK;
126 }
127 tmp32 |= SPM_HVDSC1_VDD_HVDACK_MASK; /* Clear previous error flag. */
128
129 base->HVDSC1 = tmp32;
130 }
131
132 /*!
133 * brief Configures the Aux LDO.
134 *
135 * param base SPM peripheral base address.
136 * param config Pointer to configuration structure, see to #spm_aux_ldo_config_t.
137 */
SPM_SetAuxLdoConfig(SPM_Type * base,const spm_aux_ldo_config_t * config)138 void SPM_SetAuxLdoConfig(SPM_Type *base, const spm_aux_ldo_config_t *config)
139 {
140 uint32_t tmp32 = 0U;
141
142 switch (config->lowPowerMode)
143 {
144 case kSPM_AuxLdoRemainInHighPowerInLowPowerModes:
145 tmp32 |= SPM_AUXLDOLPCNFG_LPSEL_MASK;
146 break;
147 default: /* kSPM_RfLdoEnterLowPowerInLowPowerModes. */
148 assert(false);
149 break;
150 }
151 base->AUXLDOLPCNFG = tmp32;
152
153 tmp32 = SPM_AUXLDOSC_IOSSSEL(config->ioSoftStartDuration) | SPM_AUXLDOSC_AUXREGVSEL(config->ioRegulatorVolt);
154 base->AUXLDOSC = tmp32;
155 }
156
157 /*!
158 * brief Sets DCDC battery monitor with its ADC value.
159 *
160 * For better accuracy, software would call this function to set the battery voltage value into DCDC
161 * measured by ADC.
162 *
163 * param base SPM peripheral base address.
164 * param batAdcVal ADC measured battery value with an 8mV LSB resolution.
165 * Value 0 would disable the battery monitor.
166 */
SPM_SetDcdcBattMonitor(SPM_Type * base,uint32_t batAdcVal)167 void SPM_SetDcdcBattMonitor(SPM_Type *base, uint32_t batAdcVal)
168 {
169 /* Clear the value and disable it at first. */
170 base->DCDCC2 &= ~(SPM_DCDCC2_DCDC_BATTMONITOR_BATT_VAL_MASK | SPM_DCDCC2_DCDC_BATTMONITOR_EN_BATADJ_MASK);
171 if (0U != batAdcVal)
172 {
173 /* When setting the value to BATT_VAL field, it should be zero before. */
174 base->DCDCC2 |= SPM_DCDCC2_DCDC_BATTMONITOR_BATT_VAL(batAdcVal);
175 base->DCDCC2 |= SPM_DCDCC2_DCDC_BATTMONITOR_EN_BATADJ_MASK;
176 }
177 }
178
179 /*!
180 * brief Set DCDC loop control config.
181 *
182 * param base SPM peripheral base address.
183 * param config The Pointer to the structure @ref spm_dcdc_loop_control_config_t.
184 */
SPM_SetDcdcLoopControlConfig(SPM_Type * base,const spm_dcdc_loop_control_config_t * config)185 void SPM_SetDcdcLoopControlConfig(SPM_Type *base, const spm_dcdc_loop_control_config_t *config)
186 {
187 assert(config != NULL);
188
189 uint32_t temp32;
190
191 temp32 = base->DCDCC1;
192 temp32 &= ~(SPM_DCDCC1_DCDC_LOOPCTRL_EN_CM_HYST_MASK | SPM_DCDCC1_DCDC_LOOPCTRL_EN_DF_HYST_MASK);
193 temp32 |= SPM_DCDCC1_DCDC_LOOPCTRL_EN_CM_HYST(config->enableCommonHysteresis) |
194 SPM_DCDCC1_DCDC_LOOPCTRL_EN_DF_HYST(config->enableDifferentialHysteresis);
195 base->DCDCC1 = temp32;
196
197 temp32 = base->DCDCC2;
198 temp32 &= ~SPM_DCDCC2_DCDC_LOOPCTRL_HYST_SIGN_MASK;
199 temp32 |= SPM_DCDCC2_DCDC_LOOPCTRL_HYST_SIGN(config->invertHysteresisSign);
200 base->DCDCC2 = temp32;
201 }
202
203 /*!
204 * brief Bypasses the ADC measure value
205 *
206 * Forces DCDC to bypass the adc measuring state and loads the user-defined value in this function.
207 *
208 * param base SPM peripheral base address.
209 * param enable Enable the bypass or not.
210 * param value User-setting value to be available instead of ADC measured value.
211 */
SPM_BypassDcdcBattMonitor(SPM_Type * base,bool enable,uint32_t value)212 void SPM_BypassDcdcBattMonitor(SPM_Type *base, bool enable, uint32_t value)
213 {
214 if (true == enable)
215 {
216 /* Set the user-defined value before enable the bypass. */
217 base->DCDCC3 = (base->DCDCC3 & ~SPM_DCDCC3_DCDC_VBAT_VALUE_MASK) | SPM_DCDCC3_DCDC_VBAT_VALUE(value);
218 /* Enable the bypass and load the user-defined value. */
219 base->DCDCC3 |= SPM_DCDCC3_DCDC_BYPASS_ADC_MEAS_MASK;
220 }
221 else
222 {
223 base->DCDCC3 &= ~SPM_DCDCC3_DCDC_BYPASS_ADC_MEAS_MASK;
224 }
225 }
226
227 /*!
228 * brief Configure the DCDC integrator value.
229 *
230 * Integrator value can be loaded in pulsed mode. Software can program this value according to
231 * battery voltage and VDD_CORE output target value before goes to the pulsed mode.
232 *
233 code
234 spm_dcdc_integrator_config_t SpmDcdcIntegratorConfigStruct =
235 {
236 .vddCoreValue = 1.25f,
237 .vBatValue = 3.34f
238 };
239 endcode
240 *
241 * param base SPM peripheral base address.
242 * param config Pointer to configuration structure, see to #spm_dcdc_integrator_config_t.
243 * Passing NULL would clear all user-defined setting and use hardware default setting.
244 */
SPM_SetDcdcIntegratorConfig(SPM_Type * base,const spm_dcdc_integrator_config_t * config)245 void SPM_SetDcdcIntegratorConfig(SPM_Type *base, const spm_dcdc_integrator_config_t *config)
246 {
247 int32_t tmp32;
248
249 assert(NULL != config);
250
251 tmp32 = (int32_t)(double)(config->vddCoreValue / config->vBatValue * 32.0 - 16.0) *
252 8192L; /* Target value = ((VDD_CORE/Vbat)*32 - 16) * 2^13 */
253 base->DCDCC4 = SPM_DCDCC4_PULSE_RUN_SPEEDUP_MASK | SPM_DCDCC4_INTEGRATOR_VALUE_SELECT_MASK |
254 SPM_DCDCC4_INTEGRATOR_VALUE(tmp32);
255 }
256