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