You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
11 KiB
C
323 lines
11 KiB
C
/***************************************************************************//**
|
|
* @file
|
|
* @brief This file implements a simple API for configuring FEM control signals
|
|
* via PRS.
|
|
*******************************************************************************
|
|
* # License
|
|
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
|
|
*******************************************************************************
|
|
*
|
|
* SPDX-License-Identifier: Zlib
|
|
*
|
|
* The licensor of this software is Silicon Laboratories Inc.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "sl_fem_util_config.h"
|
|
#include "sl_fem_util.h"
|
|
|
|
#define SL_FEM_UTIL_GLOBAL_ENABLE (SL_FEM_UTIL_RX_ENABLE \
|
|
|| SL_FEM_UTIL_TX_ENABLE \
|
|
|| SL_FEM_UTIL_BYPASS_ENABLE \
|
|
|| SL_FEM_UTIL_TX_HIGH_POWER_ENABLE)
|
|
|
|
#if SL_FEM_UTIL_GLOBAL_ENABLE == 1
|
|
#include "em_device.h"
|
|
#include "em_assert.h"
|
|
#include "em_gpio.h"
|
|
#include "em_bus.h"
|
|
#include "em_cmu.h"
|
|
#include "em_prs.h"
|
|
|
|
#ifdef _SILICON_LABS_32B_SERIES_1
|
|
#if (!defined(SL_FEM_UTIL_RX_CHANNEL) \
|
|
|| !defined(SL_FEM_UTIL_RX_PORT) \
|
|
|| !defined(SL_FEM_UTIL_RX_PIN) \
|
|
|| !defined(SL_FEM_UTIL_RX_LOC))
|
|
#error "SL_FEM_UTIL_RX_CHANNEL/PORT/PIN/LOC must be defined."
|
|
#endif
|
|
#else //!_SILICON_LABS_32B_SERIES_1
|
|
#if (!defined(SL_FEM_UTIL_RX_CHANNEL) \
|
|
|| !defined(SL_FEM_UTIL_RX_PORT) \
|
|
|| !defined(SL_FEM_UTIL_RX_PIN))
|
|
#error "SL_FEM_UTIL_RX_CHANNEL/PORT/PIN must be defined."
|
|
#endif
|
|
#endif //!_SILICON_LABS_32B_SERIES_1
|
|
|
|
// if no separate CTX pin is defined, CRX is a combined RX-TX pin
|
|
#ifndef SL_FEM_UTIL_TX_CHANNEL
|
|
#define SL_FEM_UTIL_TX_PORT SL_FEM_UTIL_RX_PORT
|
|
#define SL_FEM_UTIL_TX_PIN SL_FEM_UTIL_RX_PIN
|
|
#define SL_FEM_UTIL_TX_LOC SL_FEM_UTIL_RX_LOC
|
|
#define SL_FEM_UTIL_TX_CHANNEL SL_FEM_UTIL_RX_CHANNEL
|
|
#endif
|
|
|
|
#if SL_FEM_UTIL_TX_ENABLE == 1 && SL_FEM_UTIL_RX_ENABLE == 1
|
|
#if SL_FEM_UTIL_RX_CHANNEL == SL_FEM_UTIL_TX_CHANNEL
|
|
#error The RX and TX PRS channels cannot be equal.
|
|
#endif
|
|
#endif
|
|
|
|
#if SL_FEM_UTIL_RX_ENABLE && defined(SL_FEM_UTIL_SLEEP_CHANNEL)
|
|
#if (SL_FEM_UTIL_RX_CHANNEL + 1) != SL_FEM_UTIL_SLEEP_CHANNEL
|
|
#error "SL_FEM_UTIL_SLEEP_CHANNEL must immediately follow SL_FEM_UTIL_RX_CHANNEL"
|
|
#endif
|
|
#endif // SL_FEM_UTIL_RX_ENABLE
|
|
|
|
#ifndef PRS_CHAN_COUNT
|
|
#define PRS_CHAN_COUNT PRS_ASYNC_CHAN_COUNT
|
|
#endif
|
|
|
|
#if defined(SL_FEM_UTIL_SLEEP_CHANNEL) && (SL_FEM_UTIL_SLEEP_CHANNEL >= PRS_CHAN_COUNT)
|
|
#error "SL_FEM_UTIL_SLEEP_CHANNEL number higher than number of PRS channels"
|
|
#endif
|
|
|
|
#if SL_FEM_UTIL_TX_ENABLE
|
|
#if SL_FEM_UTIL_TX_CHANNEL >= PRS_CHAN_COUNT
|
|
#error "SL_FEM_UTIL_TX_CHANNEL number higher than number of PRS channels"
|
|
#endif
|
|
#endif
|
|
#endif // SL_FEM_UTIL_GLOBAL_ENABLE
|
|
|
|
void sl_fem_util_init(void)
|
|
{
|
|
#if SL_FEM_UTIL_GLOBAL_ENABLE == 1
|
|
// Turn on the GPIO clock so that we can turn on GPIOs
|
|
CMU_ClockEnable(cmuClock_GPIO, true);
|
|
CMU_ClockEnable(cmuClock_PRS, true);
|
|
|
|
#ifdef _SILICON_LABS_32B_SERIES_1
|
|
volatile uint32_t * routeRegister;
|
|
#endif //_SILICON_LABS_32B_SERIES_1
|
|
#if SL_FEM_UTIL_TX_ENABLE == 1
|
|
//Enable the output of TX based on a specific port and pin
|
|
//Configure the tx gpio to be an output (FEM pin CTX)
|
|
GPIO_PinModeSet(SL_FEM_UTIL_TX_PORT, SL_FEM_UTIL_TX_PIN, gpioModePushPull, 0);
|
|
//Setup the PRS to output TX on the channel and location chosen.
|
|
#ifdef _SILICON_LABS_32B_SERIES_1
|
|
PRS->CH[SL_FEM_UTIL_TX_CHANNEL].CTRL = PRS_RAC_PAEN;
|
|
|
|
// Configure TX/RX PRS output to selected channel and location
|
|
if (SL_FEM_UTIL_TX_CHANNEL < 4) {
|
|
routeRegister = &PRS->ROUTELOC0;
|
|
} else if (SL_FEM_UTIL_TX_CHANNEL < 8) {
|
|
routeRegister = &PRS->ROUTELOC1;
|
|
} else if (SL_FEM_UTIL_TX_CHANNEL < 12) {
|
|
routeRegister = &PRS->ROUTELOC2;
|
|
} else {
|
|
EFM_ASSERT(0);
|
|
return; // error
|
|
}
|
|
|
|
BUS_RegMaskedClear(routeRegister, 0xFF << ((SL_FEM_UTIL_TX_CHANNEL % 4) * 8));
|
|
BUS_RegMaskedSet(routeRegister, SL_FEM_UTIL_TX_LOC << ((SL_FEM_UTIL_TX_CHANNEL % 4) * 8));
|
|
|
|
BUS_RegMaskedSet(&PRS->ROUTEPEN, (1 << SL_FEM_UTIL_TX_CHANNEL));
|
|
#else //!_SILICON_LABS_32B_SERIES_1
|
|
PRS_SourceAsyncSignalSet(SL_FEM_UTIL_TX_CHANNEL,
|
|
0U,
|
|
#ifdef PRS_RACL_PAEN
|
|
PRS_RACL_PAEN);
|
|
#elif defined(PRS_RAC_PAEN)
|
|
PRS_RAC_PAEN);
|
|
#else
|
|
#error "No PRS setting defined for Source=RAC, Signal=PAEN"
|
|
#endif //PRS_RACL_PAEN
|
|
PRS_PinOutput(SL_FEM_UTIL_TX_CHANNEL, prsTypeAsync, SL_FEM_UTIL_TX_PORT, SL_FEM_UTIL_TX_PIN);
|
|
#endif //_SILICON_LABS_32B_SERIES_1
|
|
#endif //SL_FEM_UTIL_TX_ENABLE == 1
|
|
|
|
#if SL_FEM_UTIL_RX_ENABLE == 1
|
|
//Enable the output of RX based on a specific port and pin
|
|
//Configure the rx gpio to be an output (FEM pin CRX)
|
|
GPIO_PinModeSet(SL_FEM_UTIL_RX_PORT, SL_FEM_UTIL_RX_PIN, gpioModePushPull, 0);
|
|
|
|
#ifdef _SILICON_LABS_32B_SERIES_1
|
|
//Setup the PRS to output RX on the channel and location chosen.
|
|
PRS->CH[SL_FEM_UTIL_RX_CHANNEL].CTRL = PRS_RAC_LNAEN;
|
|
|
|
// Configure TX/RX PRS output to selected channel and location
|
|
if (SL_FEM_UTIL_RX_CHANNEL < 4) {
|
|
routeRegister = &PRS->ROUTELOC0;
|
|
} else if (SL_FEM_UTIL_RX_CHANNEL < 8) {
|
|
routeRegister = &PRS->ROUTELOC1;
|
|
} else if (SL_FEM_UTIL_RX_CHANNEL < 12) {
|
|
routeRegister = &PRS->ROUTELOC2;
|
|
} else {
|
|
EFM_ASSERT(0);
|
|
return; // error
|
|
}
|
|
|
|
BUS_RegMaskedClear(routeRegister, 0xFF << ((SL_FEM_UTIL_RX_CHANNEL % 4) * 8));
|
|
BUS_RegMaskedSet(routeRegister, SL_FEM_UTIL_RX_LOC << ((SL_FEM_UTIL_RX_CHANNEL % 4) * 8));
|
|
|
|
BUS_RegMaskedSet(&PRS->ROUTEPEN, (1 << SL_FEM_UTIL_RX_CHANNEL));
|
|
#else //!_SILICON_LABS_32B_SERIES_1
|
|
PRS_SourceAsyncSignalSet(SL_FEM_UTIL_RX_CHANNEL,
|
|
0U,
|
|
#ifdef PRS_RACL_LNAEN
|
|
PRS_RACL_LNAEN);
|
|
#elif defined(PRS_RAC_LNAEN)
|
|
PRS_RAC_LNAEN);
|
|
#else
|
|
#error "No PRS setting defined for Source=RAC, Signal=LNAEN"
|
|
#endif //PRS_RACL_LNAEN
|
|
PRS_PinOutput(SL_FEM_UTIL_RX_CHANNEL, prsTypeAsync, SL_FEM_UTIL_RX_PORT, SL_FEM_UTIL_RX_PIN);
|
|
#endif //_SILICON_LABS_32B_SERIES_1
|
|
#endif //SL_FEM_UTIL_RX_ENABLE == 1
|
|
|
|
#if defined(SL_FEM_UTIL_SLEEP_CHANNEL)
|
|
// initialize sleep as output (FEM pin CSD)
|
|
GPIO_PinModeSet(SL_FEM_UTIL_SLEEP_PORT, SL_FEM_UTIL_SLEEP_PIN, gpioModePushPull, 0);
|
|
|
|
#ifdef _SILICON_LABS_32B_SERIES_1
|
|
// set up the CSD to be active whenever the PA or LNA are enabled
|
|
// its signal is PA enable ORed with the RX channel's signal (LNA enable)
|
|
#if SL_FEM_UTIL_RX_ENABLE
|
|
PRS->CH[SL_FEM_UTIL_SLEEP_CHANNEL].CTRL = PRS_RAC_PAEN | PRS_CH_CTRL_ORPREV;
|
|
#else
|
|
PRS->CH[SL_FEM_UTIL_SLEEP_CHANNEL].CTRL = PRS_RAC_PAEN;
|
|
#endif
|
|
|
|
// Configure CSD PRS output to selected channel and location
|
|
if (SL_FEM_UTIL_SLEEP_CHANNEL < 4) {
|
|
routeRegister = &PRS->ROUTELOC0;
|
|
} else if (SL_FEM_UTIL_SLEEP_CHANNEL < 8) {
|
|
routeRegister = &PRS->ROUTELOC1;
|
|
} else if (SL_FEM_UTIL_SLEEP_CHANNEL < 12) {
|
|
routeRegister = &PRS->ROUTELOC2;
|
|
} else {
|
|
EFM_ASSERT(0);
|
|
return; // error
|
|
}
|
|
|
|
BUS_RegMaskedClear(routeRegister, 0xFF << ((SL_FEM_UTIL_SLEEP_CHANNEL % 4) * 8));
|
|
BUS_RegMaskedSet(routeRegister, SL_FEM_UTIL_SLEEP_LOC << ((SL_FEM_UTIL_SLEEP_CHANNEL % 4) * 8));
|
|
|
|
// Enable CSD PRS output on both output and input
|
|
BUS_RegMaskedSet(&PRS->ROUTEPEN, (1 << SL_FEM_UTIL_SLEEP_CHANNEL));
|
|
#else //!_SILICON_LABS_32B_SERIES_1
|
|
// set up the CSD to be active whenever the PA or LNA are enabled
|
|
// its signal is PA enable ORed with the RX channel's signal (LNA enable)
|
|
PRS_SourceAsyncSignalSet(SL_FEM_UTIL_SLEEP_CHANNEL,
|
|
0U,
|
|
#ifdef PRS_RACL_PAEN
|
|
PRS_RACL_PAEN);
|
|
#elif defined(PRS_RAC_PAEN)
|
|
PRS_RAC_PAEN);
|
|
#else
|
|
#error "No PRS setting defined for Source=RAC, Signal=PAEN"
|
|
#endif //PRS_RACL_PAEN
|
|
#if SL_FEM_UTIL_RX_ENABLE
|
|
PRS_Combine(SL_FEM_UTIL_SLEEP_CHANNEL,
|
|
SL_FEM_UTIL_RX_CHANNEL,
|
|
prsLogic_A_OR_B);
|
|
#endif
|
|
// Configure CSD PRS output to selected channel
|
|
PRS_PinOutput(SL_FEM_UTIL_SLEEP_CHANNEL, prsTypeAsync, SL_FEM_UTIL_SLEEP_PORT, SL_FEM_UTIL_SLEEP_PIN);
|
|
|
|
#endif //_SILICON_LABS_32B_SERIES_1
|
|
#endif // SL_FEM_UTIL_SLEEP_CHANNEL
|
|
|
|
// if fem has a bypass pin (FEM pin CPS)
|
|
#ifdef SL_FEM_UTIL_BYPASS_PORT
|
|
// set up bypass pin
|
|
#if SL_FEM_UTIL_BYPASS_ENABLE
|
|
GPIO_PinModeSet(SL_FEM_UTIL_BYPASS_PORT, SL_FEM_UTIL_BYPASS_PIN, gpioModePushPull, 1);
|
|
#else
|
|
GPIO_PinModeSet(SL_FEM_UTIL_BYPASS_PORT, SL_FEM_UTIL_BYPASS_PIN, gpioModePushPull, 0);
|
|
#endif
|
|
#endif
|
|
|
|
// if fem has a tx power pin (FEM pin CHL)
|
|
#ifdef SL_FEM_UTIL_TXPOWER_PORT
|
|
// set up tx power pin
|
|
#if SL_FEM_UTIL_TX_HIGH_POWER_ENABLE
|
|
GPIO_PinModeSet(SL_FEM_UTIL_TXPOWER_PORT, SL_FEM_UTIL_TXPOWER_PIN, gpioModePushPull, 1);
|
|
#else
|
|
GPIO_PinModeSet(SL_FEM_UTIL_TXPOWER_PORT, SL_FEM_UTIL_TXPOWER_PIN, gpioModePushPull, 0);
|
|
#endif
|
|
#endif
|
|
#endif // SL_FEM_UTIL_GLOBAL_ENABLE
|
|
}
|
|
|
|
void sl_fem_util_wakeup(void)
|
|
{
|
|
#if SL_FEM_UTIL_GLOBAL_ENABLE == 1
|
|
// if fem has a bypass pin (FEM pin CPS)
|
|
#ifdef SL_FEM_UTIL_BYPASS_PORT
|
|
#if SL_FEM_UTIL_BYPASS_ENABLE
|
|
GPIO_PinOutSet(SL_FEM_UTIL_BYPASS_PORT, SL_FEM_UTIL_BYPASS_PIN);
|
|
#else
|
|
GPIO_PinOutClear(SL_FEM_UTIL_BYPASS_PORT, SL_FEM_UTIL_BYPASS_PIN);
|
|
#endif
|
|
#endif
|
|
|
|
// if fem has a tx power pin (FEM pin CHL)
|
|
#ifdef SL_FEM_UTIL_TX_HIGH_POWER_PORT
|
|
#if SL_FEM_UTIL_TX_HIGH_POWER_ENABLE
|
|
GPIO_PinOutSet(SL_FEM_UTIL_TX_HIGH_POWER_PORT, SL_FEM_UTIL_TX_HIGH_POWER_PIN);
|
|
#else
|
|
GPIO_PinOutClear(SL_FEM_UTIL_TX_HIGH_POWER_PORT, SL_FEM_UTIL_TX_HIGH_POWER_PIN);
|
|
#endif
|
|
#endif
|
|
#endif // SL_FEM_UTIL_GLOBAL_ENABLE
|
|
}
|
|
|
|
void sl_fem_util_shutdown(void)
|
|
{
|
|
#if SL_FEM_UTIL_GLOBAL_ENABLE == 1
|
|
// if fem has a bypass pin (FEM pin CPS)
|
|
#ifdef SL_FEM_UTIL_BYPASS_PORT
|
|
GPIO_PinOutClear(SL_FEM_UTIL_BYPASS_PORT, SL_FEM_UTIL_BYPASS_PIN);
|
|
#endif
|
|
|
|
// if fem has a tx power pin (FEM pin CHL)
|
|
#ifdef SL_FEM_UTIL_TX_HIGH_POWER_PORT
|
|
GPIO_PinOutClear(SL_FEM_UTIL_TX_HIGH_POWER_PORT, SL_FEM_UTIL_TX_HIGH_POWER_PIN);
|
|
#endif
|
|
#endif // SL_FEM_UTIL_GLOBAL_ENABLE
|
|
}
|
|
|
|
#if SL_FEM_UTIL_RUNTIME_PHY_SELECT
|
|
#if SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
static volatile bool fem_util_phy_enabled = true;
|
|
#else //!SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
static volatile bool fem_util_phy_enabled = false;
|
|
#endif //SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
#elif SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
#define fem_util_phy_enabled (true)
|
|
#else //!SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
#define fem_util_phy_enabled (false)
|
|
#endif //SL_FEM_UTIL_OPTIMIZED_PHY_ENABLE
|
|
|
|
bool sl_fem_util_get_phy_select(void)
|
|
{
|
|
return fem_util_phy_enabled;
|
|
}
|
|
|
|
void sl_fem_util_enable_phy(bool enable)
|
|
{
|
|
#if SL_FEM_UTIL_RUNTIME_PHY_SELECT
|
|
fem_util_phy_enabled = enable;
|
|
#else //!SL_FEM_UTIL_RUNTIME_PHY_SELECT
|
|
(void)enable;
|
|
#endif //SL_FEM_UTIL_RUNTIME_PHY_SELECT
|
|
}
|