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, ¤tTxPowerConfig);
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