1 /***************************************************************************//**
2  * @file
3  * @brief PA power conversion functions provided to the customer as source for
4  *   highest level of customization.
5  * @details This file contains the curves and logic that convert PA power
6  *   levels to dBm powers.
7  *******************************************************************************
8  * # License
9  * <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
10  *******************************************************************************
11  *
12  * SPDX-License-Identifier: Zlib
13  *
14  * The licensor of this software is Silicon Laboratories Inc.
15  *
16  * This software is provided 'as-is', without any express or implied
17  * warranty. In no event will the authors be held liable for any damages
18  * arising from the use of this software.
19  *
20  * Permission is granted to anyone to use this software for any purpose,
21  * including commercial applications, and to alter it and redistribute it
22  * freely, subject to the following restrictions:
23  *
24  * 1. The origin of this software must not be misrepresented; you must not
25  *    claim that you wrote the original software. If you use this software
26  *    in a product, an acknowledgment in the product documentation would be
27  *    appreciated but is not required.
28  * 2. Altered source versions must be plainly marked as such, and must not be
29  *    misrepresented as being the original software.
30  * 3. This notice may not be removed or altered from any source distribution.
31  *
32  ******************************************************************************/
33 
34 #include "em_device.h"
35 #include "em_cmu.h"
36 #include "pa_conversions_efr32.h"
37 #include "rail.h"
38 
39 #define MAX(a, b) ((a) > (b) ? (a) : (b))
40 
41 static RAIL_TxPowerCurvesConfigAlt_t powerCurvesState;
42 
43 #if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LAS_32B_SERIES_2_CONFIG_1)
44   #define PA_CONVERSION_MINIMUM_PWRLVL 1U
45 #else
46   #define PA_CONVERSION_MINIMUM_PWRLVL 0U
47 #endif
48 // For details on how to use this plugin, see
49 //   https://www.silabs.com/documents/public/application-notes/an1127-power-amplifier-power-conversion-functions.pdf
50 
51 //   This macro is defined when Silicon Labs builds this into the library as WEAK
52 //   to ensure it can be overriden by customer versions of these functions. The macro
53 //   should *not* be defined in a customer build.
54 #ifdef RAIL_PA_CONVERSIONS_WEAK
55 __WEAK
56 #endif
RAIL_GetTxPowerCurve(RAIL_TxPowerMode_t mode)57 const RAIL_TxPowerCurves_t *RAIL_GetTxPowerCurve(RAIL_TxPowerMode_t mode)
58 {
59   static RAIL_TxPowerCurves_t powerCurves;
60 
61   // Check for an invalid Tx power mode
62   if (mode >= RAIL_TX_POWER_MODE_NONE) {
63     return NULL;
64   }
65   RAIL_Handle_t railHandle = RAIL_EFR32_HANDLE;
66   RAIL_TxPowerLevel_t maxPowerLevel, minPowerLevel;
67   if (RAIL_SupportsTxPowerModeAlt(railHandle,
68                                   &mode,
69                                   &maxPowerLevel,
70                                   &minPowerLevel) == false) {
71     return NULL;
72   }
73 
74   RAIL_TxPowerCurveAlt_t const *curve =
75     powerCurvesState.curves[mode].conversion.powerCurve;
76 
77   // Check for an invalid power curve
78   if (curve == NULL) {
79     return NULL;
80   }
81 
82 #if RAIL_SUPPORTS_DBM_POWERSETTING_MAPPING_TABLE
83   RAIL_PaDescriptor_t const *modeInfo = &powerCurvesState.curves[mode];
84   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_DBM_POWERSETTING_MAPPING_TABLE) {
85     powerCurves.maxPower = modeInfo->maxPowerDbm;
86     powerCurves.minPower = modeInfo->minPowerDbm;
87     // Mapping table does not have RAIL_TxPowerCurveSegment_t segments
88     powerCurves.powerParams = NULL;
89   } else
90 #endif
91   {
92     powerCurves.maxPower = curve->maxPower;
93     powerCurves.minPower = curve->minPower;
94     powerCurves.powerParams = &curve->powerParams[0];
95   }
96   return &powerCurves;
97 }
98 
99 // This function will not be supported for any parts after efr32xg1x
100 #ifdef RAIL_PA_CONVERSIONS_WEAK
101 __WEAK
102 #endif
RAIL_InitTxPowerCurves(const RAIL_TxPowerCurvesConfig_t * config)103 RAIL_Status_t RAIL_InitTxPowerCurves(const RAIL_TxPowerCurvesConfig_t *config)
104 {
105 #ifdef _SILICON_LABS_32B_SERIES_1
106   // First PA is 2.4 GHz high power, using a piecewise fit
107   RAIL_PaDescriptor_t *current =
108     &powerCurvesState.curves[RAIL_TX_POWER_MODE_2P4_HP];
109   current->algorithm = RAIL_PA_ALGORITHM_PIECEWISE_LINEAR;
110   current->segments = config->piecewiseSegments;
111   current->min = RAIL_TX_POWER_LEVEL_2P4_HP_MIN;
112   current->max = RAIL_TX_POWER_LEVEL_2P4_HP_MAX;
113   static RAIL_TxPowerCurveAlt_t txPower2p4 = {
114     .minPower = 0U,
115     .maxPower = 0U,
116     .powerParams = { // The current max number of piecewise segments is 8
117       { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U },
118       { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U },
119     }
120   };
121   txPower2p4.maxPower = config->txPowerSgCurves->maxPower;
122   txPower2p4.minPower = config->txPowerSgCurves->minPower;
123   (void) memcpy(&txPower2p4.powerParams[0],
124                 config->txPowerSgCurves->powerParams,
125                 config->piecewiseSegments * sizeof(RAIL_TxPowerCurveSegment_t));
126   current->conversion.powerCurve = &txPower2p4;
127 
128   // Second PA is 2.4 GHz low power, using a mapping table
129   current = &powerCurvesState.curves[RAIL_TX_POWER_MODE_2P4_LP];
130   current->algorithm = RAIL_PA_ALGORITHM_MAPPING_TABLE;
131   current->segments = 0U;
132   current->min = RAIL_TX_POWER_LEVEL_2P4_LP_MIN;
133   current->max = RAIL_TX_POWER_LEVEL_2P4_LP_MAX;
134   current->conversion.mappingTable = config->txPower24LpCurves;
135 
136   // Third and final PA is Sub-GHz, using a piecewise fit
137   current = &powerCurvesState.curves[RAIL_TX_POWER_MODE_SUBGIG];
138   current->algorithm = RAIL_PA_ALGORITHM_PIECEWISE_LINEAR;
139   current->segments = config->piecewiseSegments;
140   current->min = RAIL_TX_POWER_LEVEL_SUBGIG_MIN;
141   current->max = RAIL_TX_POWER_LEVEL_SUBGIG_HP_MAX;
142   static RAIL_TxPowerCurveAlt_t txPowerSubGig = {
143     .minPower = 0U,
144     .maxPower = 0U,
145     .powerParams = { // The current max number of piecewise segments is 8
146       { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U },
147       { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U }, { 0U, 0U, 0U },
148     }
149   };
150   txPowerSubGig.maxPower = config->txPowerSgCurves->maxPower;
151   txPowerSubGig.minPower = config->txPowerSgCurves->minPower;
152   (void) memcpy(&txPowerSubGig.powerParams[0],
153                 config->txPowerSgCurves->powerParams,
154                 config->piecewiseSegments * sizeof(RAIL_TxPowerCurveSegment_t));
155   current->conversion.powerCurve = &txPowerSubGig;
156 
157   return RAIL_STATUS_NO_ERROR;
158 #else
159   (void) config;
160   return RAIL_STATUS_INVALID_CALL;
161 #endif
162 }
163 
164 #ifdef RAIL_PA_CONVERSIONS_WEAK
165 __WEAK
166 #endif
RAIL_InitTxPowerCurvesAlt(const RAIL_TxPowerCurvesConfigAlt_t * config)167 RAIL_Status_t RAIL_InitTxPowerCurvesAlt(const RAIL_TxPowerCurvesConfigAlt_t *config)
168 {
169   RAIL_VerifyTxPowerCurves(config);
170 
171   powerCurvesState = *config;
172 
173   return RAIL_STATUS_NO_ERROR;
174 }
175 
176 #ifdef RAIL_PA_CONVERSIONS_WEAK
177 __WEAK
178 #endif
RAIL_GetPowerSettingTable(RAIL_Handle_t railHandle,RAIL_TxPowerMode_t mode,RAIL_TxPower_t * minPower,RAIL_TxPower_t * maxPower,RAIL_TxPowerLevel_t * step)179 const RAIL_PaPowerSetting_t *RAIL_GetPowerSettingTable(RAIL_Handle_t railHandle, RAIL_TxPowerMode_t mode,
180                                                        RAIL_TxPower_t *minPower, RAIL_TxPower_t *maxPower,
181                                                        RAIL_TxPowerLevel_t *step)
182 {
183   (void)railHandle;
184 #if RAIL_SUPPORTS_DBM_POWERSETTING_MAPPING_TABLE
185   // Check for an invalid Tx power mode
186   if ((mode >= RAIL_NUM_PA)
187    #ifdef RAIL_TX_POWER_MODE_2P4GIG_HIGHEST
188       || (mode == RAIL_TX_POWER_MODE_2P4GIG_HIGHEST)
189    #endif
190    #ifdef RAIL_TX_POWER_MODE_SUBGIG_HIGHEST
191       || (mode == RAIL_TX_POWER_MODE_SUBGIG_HIGHEST)
192    #endif
193       ) {
194     return NULL;
195   }
196   RAIL_PaDescriptor_t *modeInfo = &powerCurvesState.curves[mode];
197   *minPower = modeInfo->minPowerDbm;
198   *maxPower = modeInfo->maxPowerDbm;
199   *step = modeInfo->step;
200   return (RAIL_PaPowerSetting_t*)(modeInfo->conversion.mappingTable);
201 #else
202   (void)mode;
203   (void)minPower;
204   (void)maxPower;
205   (void)step;
206   return NULL;
207 #endif
208 }
209 
210 #ifdef RAIL_PA_CONVERSIONS_WEAK
211 __WEAK
212 #endif
RAIL_ConvertDbmToRaw(RAIL_Handle_t railHandle,RAIL_TxPowerMode_t mode,RAIL_TxPower_t power)213 RAIL_TxPowerLevel_t RAIL_ConvertDbmToRaw(RAIL_Handle_t railHandle,
214                                          RAIL_TxPowerMode_t mode,
215                                          RAIL_TxPower_t power)
216 {
217   uint32_t powerLevel;
218   uint32_t minPowerLevel;
219 
220   (void)railHandle;
221   // This function is called internally from the RAIL library,
222   // so if the user never calls RAIL_InitTxPowerCurves - even
223   // if they never intend to use dBm values in their code -
224   // they'll always hit the assert below. Give the user a way
225   // to not have to call RAIL_InitTxPowerCurves if they don't
226   // care about dBm values by picking a dBm value that returns the
227   // highest RAIL_TxPowerLevel_t possible. In other words, when
228   // a channel dBm limitation greater than or equal to \ref RAIL_TX_POWER_MAX
229   // is converted to raw units, the max RAIL_TxPowerLevel_t will be
230   // returned. When compared to the current power level of the PA,
231   // it will always be greater, indicating that no power coercion
232   // is necessary to comply with channel limitations.
233   if (power >= RAIL_TX_POWER_MAX) {
234     return 255U;
235   }
236 
237   // Check for an invalid Tx power mode
238   if ((mode >= RAIL_NUM_PA)
239    #ifdef RAIL_TX_POWER_MODE_2P4GIG_HIGHEST
240       || (mode == RAIL_TX_POWER_MODE_2P4GIG_HIGHEST)
241    #endif
242    #ifdef RAIL_TX_POWER_MODE_SUBGIG_HIGHEST
243       || (mode == RAIL_TX_POWER_MODE_SUBGIG_HIGHEST)
244    #endif
245       ) {
246     return 0U;
247   }
248 
249   RAIL_PaDescriptor_t const *modeInfo = &powerCurvesState.curves[mode];
250   minPowerLevel = MAX(modeInfo->min, PA_CONVERSION_MINIMUM_PWRLVL);
251 
252 #if RAIL_SUPPORTS_DBM_POWERSETTING_MAPPING_TABLE
253   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_DBM_POWERSETTING_MAPPING_TABLE) {
254     RAIL_TxPower_t minPower = modeInfo->minPowerDbm;
255     RAIL_TxPower_t maxPower = modeInfo->maxPowerDbm;
256     RAIL_TxPowerLevel_t step = modeInfo->step;
257 
258     // Cap the power to within the range of the mapping table
259     if (power < minPower) {
260       power = minPower;
261     } else if (power > maxPower) {
262       power = maxPower;
263     } else {
264       // Power level is within bounds (MISRA required else)
265     }
266 
267     uint32_t powerIndex = (power - minPower) / step;
268     RAIL_SetPaPowerSetting(railHandle, modeInfo->conversion.mappingTable[powerIndex], minPower, maxPower, power);
269     return 0U;
270   }
271 #endif
272 
273   // If we're in low power mode, just use the simple lookup table
274   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_MAPPING_TABLE) {
275     // Binary search through the lookup table to find the closest power level
276     // without going over.
277     uint32_t lower = 0U;
278     // Track the high side of the estimate
279     uint32_t powerIndex = modeInfo->max - minPowerLevel;
280 
281     while (lower < powerIndex) {
282       // Calculate the midpoint of the current range
283       uint32_t index = powerIndex - (powerIndex - lower) / 2U;
284       if (power < modeInfo->conversion.mappingTable[index]) {
285         powerIndex = index - 1U;
286       } else {
287         lower = index;
288       }
289     }
290     return (RAIL_TxPowerLevel_t)(powerIndex + minPowerLevel);
291   }
292 
293   // Here we know we're using the piecewise linear conversion
294   RAIL_TxPowerCurveAlt_t const *paParams = modeInfo->conversion.powerCurve;
295   // Check for valid paParams before using them
296   if (paParams == NULL) {
297     return 0U;
298   }
299 
300   // Cap the power based on the PA settings.
301   if (power > paParams->maxPower) {
302     // If we go above the maximum dbm the chip supports
303     // Then provide maximum powerLevel
304     power = paParams->maxPower;
305   } else if (power < paParams->minPower) {
306     // If we go below the minimum we want included in the curve fit, force it.
307     power = paParams->minPower;
308   } else {
309     // Do nothing, power is OK
310   }
311   // Map the power value to a 0 - 7 curveIndex value
312   //There are 8 segments of step size of RAIL_TX_POWER_CURVE_INCREMENT in deci dBm
313   //starting from maximum RAIL_TX_POWER_CURVE_MAX in deci dBm
314   // These are just starting points to give the code
315   // a rough idea of which segment to use, based on
316   // how they were fit. Adjustments are made later on
317   // if this turns out to be incorrect.
318   RAIL_TxPower_t txPowerMax = RAIL_TX_POWER_CURVE_DEFAULT_MAX;
319   RAIL_TxPower_t txPowerIncrement = RAIL_TX_POWER_CURVE_DEFAULT_INCREMENT;
320   int16_t curveIndex = 0;
321   // if the first curve segment starts with RAIL_TX_POWER_LEVEL_INVALID
322   //It is an extra curve segment to depict the maxpower and increment
323   // (in deci-dBm) used while generating the curves.
324   // The extra segment is only present when curve segment is generated by
325   //using values different than the default - RAIL_TX_POWER_CURVE_DEFAULT_MAX
326   // and RAIL_TX_POWER_CURVE_DEFAULT_INCREMENT.
327   if ((paParams->powerParams[0].maxPowerLevel) == RAIL_TX_POWER_LEVEL_INVALID) {
328     curveIndex += 1;
329     txPowerMax = (RAIL_TxPower_t) paParams->powerParams[0].slope;
330     txPowerIncrement = (RAIL_TxPower_t) paParams->powerParams[0].intercept;
331   }
332 
333   curveIndex += (txPowerMax - power) / txPowerIncrement;
334   if ((curveIndex > ((int16_t)modeInfo->segments - 1))
335       || (curveIndex < 0)) {
336     curveIndex = ((int16_t)modeInfo->segments - 1);
337   }
338 
339   do {
340     // Select the correct piecewise segment to use for conversion.
341     RAIL_TxPowerCurveSegment_t const *powerParams =
342       &paParams->powerParams[curveIndex];
343 
344     // powerLevel can only go down to 0.
345     int32_t powerLevelInt = powerParams->intercept + ((int32_t)powerParams->slope * (int32_t)power);
346     if (powerLevelInt < 0) {
347       powerLevel = 0U;
348     } else {
349       powerLevel = (uint32_t) powerLevelInt;
350     }
351     // Add 500 to do rounding correctly, as opposed to just rounding towards 0
352     powerLevel = ((powerLevel + 500U) / 1000U);
353 
354     // In case it turns out the resultant power level was too low and we have
355     // to recalculate with the next curve...
356     curveIndex++;
357   } while ((curveIndex < (int16_t)modeInfo->segments)
358            && (powerLevel <= paParams->powerParams[curveIndex].maxPowerLevel));
359 
360 // We already know that curveIndex is at most modeInfo->segments
361   if (powerLevel > paParams->powerParams[curveIndex - 1].maxPowerLevel) {
362     powerLevel = paParams->powerParams[curveIndex - 1].maxPowerLevel;
363   }
364 
365 // If we go below the minimum we want included in the curve fit, force it.
366   if (powerLevel < minPowerLevel) {
367     powerLevel = minPowerLevel;
368   }
369 
370   return (RAIL_TxPowerLevel_t)powerLevel;
371 }
372 
373 #ifdef RAIL_PA_CONVERSIONS_WEAK
374 __WEAK
375 #endif
RAIL_ConvertRawToDbm(RAIL_Handle_t railHandle,RAIL_TxPowerMode_t mode,RAIL_TxPowerLevel_t powerLevel)376 RAIL_TxPower_t RAIL_ConvertRawToDbm(RAIL_Handle_t railHandle,
377                                     RAIL_TxPowerMode_t mode,
378                                     RAIL_TxPowerLevel_t powerLevel)
379 {
380   (void)railHandle;
381 
382   // Check for an invalid Tx power mode
383   if ((mode >= RAIL_NUM_PA)
384    #ifdef RAIL_TX_POWER_MODE_2P4GIG_HIGHEST
385       || (mode == RAIL_TX_POWER_MODE_2P4GIG_HIGHEST)
386    #endif
387    #ifdef RAIL_TX_POWER_MODE_SUBGIG_HIGHEST
388       || (mode == RAIL_TX_POWER_MODE_SUBGIG_HIGHEST)
389    #endif
390       ) {
391     return RAIL_TX_POWER_MIN;
392   }
393 
394   RAIL_PaDescriptor_t const *modeInfo = &powerCurvesState.curves[mode];
395 
396   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_MAPPING_TABLE) {
397     // Limit the max power level
398     if (powerLevel > modeInfo->max) {
399       powerLevel = modeInfo->max;
400     }
401 
402     // We 1-index low power PA power levels, but of course arrays are 0 indexed
403     powerLevel -= MAX(modeInfo->min, PA_CONVERSION_MINIMUM_PWRLVL);
404 
405     //If the index calculation above underflowed, then provide the lowest array index.
406     if (powerLevel > (modeInfo->max - modeInfo->min)) {
407       powerLevel = 0U;
408     }
409     return modeInfo->conversion.mappingTable[powerLevel];
410   } else {
411 #if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LAS_32B_SERIES_2_CONFIG_1)
412     // Although 0 is a legitimate power on non-2.4 LP PA's and can be set via
413     // "RAIL_SetTxPower(railHandle, 0)" it is MUCH lower than power
414     // level 1 (approximately -50 dBm). Including it in the piecewise
415     // linear fit would skew the curve substantially, so we exclude it
416     // from the conversion.
417     if (powerLevel == 0U) {
418       return -500;
419     }
420 #endif
421 
422     RAIL_TxPowerCurveAlt_t const *powerCurve = modeInfo->conversion.powerCurve;
423     // Check for a valid powerCurve pointer before using it
424     if (powerCurve == NULL) {
425       return RAIL_TX_POWER_MIN;
426     }
427 
428     RAIL_TxPowerCurveSegment_t const *powerParams = powerCurve->powerParams;
429 
430     // Hard code the extremes (i.e. don't use the curve fit) in order
431     // to make it clear that we are reaching the extent of the chip's
432     // capabilities
433     if (powerLevel <= modeInfo->min) {
434       return powerCurve->minPower;
435     } else if (powerLevel >= modeInfo->max) {
436       return powerCurve->maxPower;
437     } else {
438       // Power level is within bounds (MISRA required else)
439     }
440 
441     // Figure out which parameter to use based on the power level
442     uint8_t x = 0;
443     uint8_t upperBound = modeInfo->segments - 1U;
444 
445     // If the first curve segment starts with RAIL_TX_POWER_LEVEL_INVALID,
446     // then it is an additional curve segment that stores maxpower and increment
447     // (in deci-dBm) used to generate the curves.
448     // The extra info segment is present only if the curves were generated using
449     // values other than default - RAIL_TX_POWER_CURVE_DEFAULT_MAX and
450     // RAIL_TX_POWER_CURVE_DEFAULT_INCREMENT.
451     if ((powerParams[0].maxPowerLevel) == RAIL_TX_POWER_LEVEL_INVALID) {
452       x = 1U; // skip over the first entry
453     }
454 
455     for (; x < upperBound; x++) {
456       if (powerParams[x + 1U].maxPowerLevel < powerLevel) {
457         break;
458       }
459     }
460     int32_t power;
461     power = ((1000 * (int32_t)(powerLevel)) - powerParams[x].intercept);
462     power = ((power + ((int32_t)powerParams[x].slope / 2)) / (int32_t)powerParams[x].slope);
463 
464     if (power > powerCurve->maxPower) {
465       return powerCurve->maxPower;
466     } else if (power < powerCurve->minPower) {
467       return powerCurve->minPower;
468     } else {
469       return (RAIL_TxPower_t)power;
470     }
471   }
472 }
473 
474 #ifdef RAIL_PA_CONVERSIONS_WEAK
475 __WEAK
476 #endif
RAIL_GetTxPowerCurveLimits(RAIL_Handle_t railHandle,RAIL_TxPowerMode_t mode,RAIL_TxPower_t * maxPower,RAIL_TxPower_t * increment)477 RAIL_Status_t RAIL_GetTxPowerCurveLimits(RAIL_Handle_t railHandle,
478                                          RAIL_TxPowerMode_t mode,
479                                          RAIL_TxPower_t *maxPower,
480                                          RAIL_TxPower_t *increment)
481 {
482   (void)railHandle;
483   // Check for an invalid Tx power mode
484   if ((mode >= RAIL_NUM_PA)
485    #ifdef RAIL_TX_POWER_MODE_2P4GIG_HIGHEST
486       || (mode == RAIL_TX_POWER_MODE_2P4GIG_HIGHEST)
487    #endif
488    #ifdef RAIL_TX_POWER_MODE_SUBGIG_HIGHEST
489       || (mode == RAIL_TX_POWER_MODE_SUBGIG_HIGHEST)
490    #endif
491       ) {
492     return RAIL_STATUS_INVALID_PARAMETER;
493   }
494   RAIL_PaDescriptor_t const *modeInfo = &powerCurvesState.curves[mode];
495 
496 #if RAIL_SUPPORTS_DBM_POWERSETTING_MAPPING_TABLE
497   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_DBM_POWERSETTING_MAPPING_TABLE) {
498     *maxPower = modeInfo->maxPowerDbm;
499     *increment = modeInfo->step;
500     return RAIL_STATUS_NO_ERROR;
501   }
502 #endif
503 
504   //The power max info only for available Linear fit
505   if (modeInfo->algorithm == RAIL_PA_ALGORITHM_MAPPING_TABLE) {
506     return RAIL_STATUS_INVALID_CALL;
507   }
508   *maxPower = RAIL_TX_POWER_CURVE_DEFAULT_MAX;
509   *increment = RAIL_TX_POWER_CURVE_DEFAULT_INCREMENT;
510   RAIL_TxPowerCurveAlt_t const *paParams = modeInfo->conversion.powerCurve;
511   if ((paParams->powerParams[0].maxPowerLevel) == RAIL_TX_POWER_LEVEL_INVALID) {
512     *maxPower = paParams->powerParams[0].slope;
513     *increment = (RAIL_TxPower_t)paParams->powerParams[0].intercept;
514   }
515   return RAIL_STATUS_NO_ERROR;
516 }
517 
518 // This macro is defined when Silicon Labs builds curves into the library as WEAK
519 // to ensure it can be overriden by customer versions of these functions. It
520 // should *not* be defined in a customer build.
521 #if !defined(RAIL_PA_CONVERSIONS_WEAK) && !defined(HAL_CONFIG)
522 
523 #include "sl_rail_util_pa_config.h"
524 
sl_rail_util_pa_init(void)525 void sl_rail_util_pa_init(void)
526 {
527 #if SL_RAIL_UTIL_PA_VOLTAGE_MV > 1800
528   (void)RAIL_InitTxPowerCurvesAlt(&RAIL_TxPowerCurvesVbat);
529 #else
530   (void)RAIL_InitTxPowerCurvesAlt(&RAIL_TxPowerCurvesDcdc);
531 #endif
532 #if SL_RAIL_UTIL_PA_CALIBRATION_ENABLE
533   RAIL_EnablePaCal(true);
534 #endif
535 }
536 
537 #if RAIL_SUPPORTS_2P4GHZ_BAND
538 static RAIL_TxPowerConfig_t txPowerConfig2p4Ghz = {
539   .mode = SL_RAIL_UTIL_PA_SELECTION_2P4GHZ,
540   .voltage = SL_RAIL_UTIL_PA_VOLTAGE_MV,
541   .rampTime = SL_RAIL_UTIL_PA_RAMP_TIME_US,
542 };
543 #endif
sl_rail_util_pa_get_tx_power_config_2p4ghz(void)544 RAIL_TxPowerConfig_t *sl_rail_util_pa_get_tx_power_config_2p4ghz(void)
545 {
546 #if RAIL_SUPPORTS_2P4GHZ_BAND
547   return &txPowerConfig2p4Ghz;
548 #else
549   return NULL;
550 #endif
551 }
552 
553 #if RAIL_SUPPORTS_SUBGHZ_BAND
554 static RAIL_TxPowerConfig_t txPowerConfigSubGhz = {
555   .mode = SL_RAIL_UTIL_PA_SELECTION_SUBGHZ,
556   .voltage = SL_RAIL_UTIL_PA_VOLTAGE_MV,
557   .rampTime = SL_RAIL_UTIL_PA_RAMP_TIME_US,
558 };
559 #endif
sl_rail_util_pa_get_tx_power_config_subghz(void)560 RAIL_TxPowerConfig_t *sl_rail_util_pa_get_tx_power_config_subghz(void)
561 {
562 #if RAIL_SUPPORTS_SUBGHZ_BAND
563   return &txPowerConfigSubGhz;
564 #else
565   return NULL;
566 #endif
567 }
568 
569 #if RAIL_SUPPORTS_OFDM_PA
570 #ifndef SL_RAIL_UTIL_PA_SELECTION_OFDM
571 #define SL_RAIL_UTIL_PA_SELECTION_OFDM RAIL_TX_POWER_MODE_OFDM_PA_POWERSETTING_TABLE
572 #endif
573 static RAIL_TxPowerConfig_t txPowerConfigOFDM = {
574   .mode = SL_RAIL_UTIL_PA_SELECTION_OFDM,
575   .voltage = SL_RAIL_UTIL_PA_VOLTAGE_MV,
576 };
577 #endif // RAIL_SUPPORTS_OFDM_PA
sl_rail_util_pa_get_tx_power_config_ofdm(void)578 RAIL_TxPowerConfig_t *sl_rail_util_pa_get_tx_power_config_ofdm(void)
579 {
580 #if RAIL_SUPPORTS_OFDM_PA
581   return &txPowerConfigOFDM;
582 #else
583   return NULL;
584 #endif // RAIL_SUPPORTS_OFDM_PA
585 }
586 
sl_rail_util_pa_on_channel_config_change(RAIL_Handle_t rail_handle,const RAIL_ChannelConfigEntry_t * entry)587 void sl_rail_util_pa_on_channel_config_change(RAIL_Handle_t rail_handle,
588                                               const RAIL_ChannelConfigEntry_t *entry)
589 {
590   if (!RAIL_IsPaAutoModeEnabled(rail_handle)) {
591     RAIL_TxPowerConfig_t currentTxPowerConfig;
592     RAIL_TxPowerConfig_t *newTxPowerConfigPtr;
593     RAIL_Status_t status;
594 
595     // Get current TX Power Config.
596     status = RAIL_GetTxPowerConfig(rail_handle, &currentTxPowerConfig);
597     if (status != RAIL_STATUS_NO_ERROR) {
598       while (true) {
599       } // Error: Can't get TX Power Config
600     }
601 
602 #if RAIL_SUPPORTS_DUAL_BAND
603     // Determine new TX Power Config.
604     if (entry->baseFrequency < 1000000000UL) {
605       newTxPowerConfigPtr = &txPowerConfigSubGhz;
606     } else {
607       newTxPowerConfigPtr = &txPowerConfig2p4Ghz;
608     }
609 #else
610     (void) entry;
611 #if RAIL_SUPPORTS_2P4GHZ_BAND
612     newTxPowerConfigPtr = &txPowerConfig2p4Ghz;
613 #else
614     newTxPowerConfigPtr = &txPowerConfigSubGhz;
615 #endif
616 #endif
617 
618 #if RAIL_IEEE802154_SUPPORTS_DUAL_PA_CONFIG
619     if (currentTxPowerConfig.mode == RAIL_TX_POWER_MODE_NONE) {
620 #if RAIL_SUPPORTS_OFDM_PA
621       if (RAIL_SupportsTxPowerMode(rail_handle,
622                                    txPowerConfigOFDM.mode,
623                                    NULL)) {
624         // Apply OFDM Power Config.
625         status = RAIL_ConfigTxPower(rail_handle, &txPowerConfigOFDM);
626         if (status != RAIL_STATUS_NO_ERROR) {
627           while (true) {
628           } // Error: Can't set TX Power Config
629         }
630         // Set default TX power after RAIL_ConfigTxPower.
631         status = RAIL_SetTxPowerDbm(rail_handle, SL_RAIL_UTIL_PA_POWER_DECI_DBM);
632         if (status != RAIL_STATUS_NO_ERROR) {
633           while (true) {
634           } // Error: Can't set TX Power
635         }
636       }
637 #endif // RAIL_SUPPORTS_OFDM_PA
638       // Apply FSK Power Config.
639       status = RAIL_ConfigTxPower(rail_handle, newTxPowerConfigPtr);
640       if (status != RAIL_STATUS_NO_ERROR) {
641         while (true) {
642         } // Error: Can't set TX Power Config
643       }
644       // Set default TX power after RAIL_ConfigTxPower.
645       status = RAIL_SetTxPowerDbm(rail_handle, SL_RAIL_UTIL_PA_POWER_DECI_DBM);
646       if (status != RAIL_STATUS_NO_ERROR) {
647         while (true) {
648         } // Error: Can't set TX Power
649       }
650     }
651 #else
652     // Call RAIL_ConfigTxPower only if TX Power Config mode has changed.
653     if (currentTxPowerConfig.mode != newTxPowerConfigPtr->mode) {
654       // Save current TX power before RAIL_ConfigTxPower (because not preserved).
655       RAIL_TxPower_t txPowerDeciDbm;
656       if (currentTxPowerConfig.mode == RAIL_TX_POWER_MODE_NONE) {
657         txPowerDeciDbm = SL_RAIL_UTIL_PA_POWER_DECI_DBM;
658       } else {
659         txPowerDeciDbm = RAIL_GetTxPowerDbm(rail_handle);
660       }
661 
662       // Apply new TX Power Config.
663       status = RAIL_ConfigTxPower(rail_handle, newTxPowerConfigPtr);
664       if (status != RAIL_STATUS_NO_ERROR) {
665         while (true) {
666         } // Error: Can't set TX Power Config
667       }
668       // Restore TX power after RAIL_ConfigTxPower.
669       status = RAIL_SetTxPowerDbm(rail_handle, txPowerDeciDbm);
670       if (status != RAIL_STATUS_NO_ERROR) {
671         while (true) {
672         } // Error: Can't set TX Power
673       }
674     }
675 #endif
676   } // !RAIL_IsPaAutoModeEnabled
677 }
678 #endif // !RAIL_PA_CONVERSIONS_WEAK
679