mirror of
https://github.com/thelsing/knx.git
synced 2025-11-07 01:20:20 +01:00
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
5715 lines
206 KiB
C
5715 lines
206 KiB
C
/*
|
|
* Copyright (c) 2015-2019, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <ti/drivers/dpl/ClockP.h>
|
|
#include <ti/drivers/dpl/DebugP.h>
|
|
#include <ti/drivers/dpl/HwiP.h>
|
|
#include <ti/drivers/dpl/SemaphoreP.h>
|
|
#include <ti/drivers/dpl/SwiP.h>
|
|
|
|
#include <ti/drivers/Power.h>
|
|
#include <ti/drivers/power/PowerCC26X2.h>
|
|
#include <ti/drivers/rf/RF.h>
|
|
#include <ti/drivers/utils/List.h>
|
|
|
|
#include <ti/devices/DeviceFamily.h>
|
|
#include DeviceFamily_constructPath(inc/hw_memmap.h)
|
|
#include DeviceFamily_constructPath(inc/hw_ints.h)
|
|
#include DeviceFamily_constructPath(inc/hw_types.h)
|
|
#include DeviceFamily_constructPath(inc/hw_rfc_rat.h)
|
|
#include DeviceFamily_constructPath(inc/hw_rfc_dbell.h)
|
|
#include DeviceFamily_constructPath(driverlib/rfc.h)
|
|
#include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
|
|
#include DeviceFamily_constructPath(driverlib/ioc.h)
|
|
#include DeviceFamily_constructPath(driverlib/aon_ioc.h)
|
|
#include DeviceFamily_constructPath(driverlib/rf_mailbox.h)
|
|
#include DeviceFamily_constructPath(driverlib/adi.h)
|
|
#include DeviceFamily_constructPath(driverlib/aon_rtc.h)
|
|
#include DeviceFamily_constructPath(driverlib/chipinfo.h)
|
|
#include DeviceFamily_constructPath(driverlib/aon_batmon.h)
|
|
#include DeviceFamily_constructPath(driverlib/osc.h)
|
|
|
|
#if defined(__IAR_SYSTEMS_ICC__)
|
|
#pragma diag_remark=Pa082
|
|
#endif
|
|
|
|
#if defined(RF_SINGLEMODE)
|
|
#error "An incompatible symbol (RF_SINGLEMODE) is defined in the project. \
|
|
To build with this driver, remove the RF_SINGLEMODE token definition."
|
|
#endif
|
|
|
|
/*-------------- Typedefs, structures & defines ---------------*/
|
|
|
|
/* Definition of internal state-machine events. */
|
|
typedef enum RF_FsmEvent_ {
|
|
RF_FsmEventLastCommandDone = (1UL << 1), /* Indicates that a radio command is finished. */
|
|
RF_FsmEventWakeup = (1UL << 2), /* Used to initiate the power up sequence of the RF core. */
|
|
RF_FsmEventPowerDown = (1UL << 3), /* Used to initiate the power down sequence of the RF core. */
|
|
RF_FsmEventInitChangePhy = (1UL << 10), /* Used to initiate the PHY change sequence. */
|
|
RF_FsmEventFinishChangePhy = (1UL << 11), /* Used to finalize the PHY change sequence. */
|
|
RF_FsmEventCpeInt = (1UL << 14), /* Generated during command execution. */
|
|
RF_FsmEventPowerStep = (1UL << 29), /* Generated during the power up sequence of RF core. */
|
|
RF_FsmEventRunScheduler = (1UL << 30) /* Used to invoke the scheduler again to check for conflicts. */
|
|
} RF_FsmEvent;
|
|
|
|
/* Definition of states of RF core. */
|
|
typedef enum RF_CoreStatus_ {
|
|
RF_CoreStatusIdle = 0, /* The RF core is OFF. */
|
|
RF_CoreStatusPoweringUp = 1, /* The RF core is being powered up. */
|
|
RF_CoreStatusActive = 2, /* The RF core is ON. */
|
|
RF_CoreStatusPoweringDown = 3, /* The RF core is being powered down. */
|
|
RF_CoreStatusPhySwitching = 4 /* The RF core is being reconfigured. */
|
|
} RF_CoreStatus;
|
|
|
|
/* Definition of internal power constraints. Note that the physical RAT channels in the RF core are
|
|
not a one-to-one map to the constraint values here. */
|
|
typedef enum RF_PowerConstraintSrc_ {
|
|
RF_PowerConstraintNone = 0,
|
|
RF_PowerConstraintRatCh0 = (1U << 0), /* Indicates that the Channel 0 of RAT timer is running. */
|
|
RF_PowerConstraintRatCh1 = (1U << 1), /* Indicates that the Channel 1 of RAT timer is running. */
|
|
RF_PowerConstraintRatCh2 = (1U << 2), /* Indicates that the Channel 2 of RAT timer is running. */
|
|
RF_PowerConstraintCmdQ = (1U << 3), /* Indicates that the RF core executing a radio command. */
|
|
RF_PowerConstraintDisallow = (1U << 7) /* Disable automatic power management. */
|
|
} RF_PowerConstraintSrc;
|
|
|
|
/* Definition of internal Radio Timer (RAT) modes. */
|
|
typedef enum RF_RatMode_ {
|
|
RF_RatModeUndefined = 0, /* Indicates that the RAT channel is not configured. */
|
|
RF_RatModeCompare = 1, /* Indicates that the RAT channel is configured to compare mode. */
|
|
RF_RatModeCapture = 2 /* Indicates that the RAT channel is configured to capture mode. */
|
|
} RF_RatMode;
|
|
|
|
/* Definition of internal Radio Timer (RAT) states. */
|
|
typedef enum RF_RatStatus_ {
|
|
RF_RatStatusIdle = 0, /* Indicates that the RAT channel is not used. */
|
|
RF_RatStatusPending = 1, /* Indicates that the RAT channel is configured, but the RAT timer is not running (i.e. RF core is OFF). */
|
|
RF_RatStatusRunning = 2 /* Indicates that the RAT channel is configured, and the RAT timer is running. */
|
|
} RF_RatStatus;
|
|
|
|
/* Definition of internal status codes of command shceduling. */
|
|
typedef enum RF_ScheduleCmdStatus_ {
|
|
RF_ScheduleCmdSuccess = 0, /* Schedule command success. */
|
|
RF_ScheduleCmdAllocError = 1, /* Schedule command allocation error (such as queue is full). */
|
|
RF_ScheduleCmdSchError = 2 /* SChedule command scheduler error (timing or priority conflict). */
|
|
} RF_ScheduleCmdStatus;
|
|
|
|
/*-------------- Macros ---------------*/
|
|
|
|
#define ABS(x) (((x) < 0) ? -(x) : (x))
|
|
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
|
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
|
#define UDIFF(x,y) (((y) > (x)) ? ((y) - (x)) : ((~0) + (y) - (x) + (1)))
|
|
#define ADD(x,y) ((x > ((~0) - (y))) ? (~0) : ((x) + (y)))
|
|
|
|
/*-------------- Defines ---------------*/
|
|
|
|
/* Max # of RF driver clients */
|
|
#define N_MAX_CLIENTS 2
|
|
/* 8 RF_Cmds in pool */
|
|
#define N_CMD_POOL 8
|
|
/* Modulus mask used for RF_CmdHandle calculations */
|
|
#define N_CMD_MODMASK 0xFFF
|
|
|
|
/*-------------- Internal RF constants ---------------*/
|
|
|
|
#define RF_CMD0 0x0607
|
|
#define RF_BOOT0 0xE0000011
|
|
#define RF_BOOT1 0x00000080
|
|
/* Accessible RF Core interrupts mask MSB 32 bits : RFHW int, LSB 32 bits : RF CPE int */
|
|
#define RF_INTERNAL_IFG_MASK 0xFFFFFFDF60001000
|
|
#define RF_TERMINATION_EVENT_MASK (RF_EventLastCmdDone | RF_EventLastFGCmdDone | RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled)
|
|
#define RF_CMD_FG_CMD_FLAG (1 << 4)
|
|
#define RF_CMD_ALLOC_FLAG (1 << 7)
|
|
#define RF_CMD_TERMINATED (DONE_OK | ERROR_PAST_START)
|
|
#define RF_HW_INT_RAT_CH_MASK (RFC_DBELL_RFHWIFG_RATCH7 | RFC_DBELL_RFHWIFG_RATCH6 | RFC_DBELL_RFHWIFG_RATCH5)
|
|
#define RF_RAT_CH_CNT 3
|
|
#define RF_HW_INT_CPE_MASK RFC_DBELL_RFHWIFG_MDMSOFT
|
|
#define RF_CPE0_INT_MASK 0xFFFFFFFF
|
|
/* Default value for power up duration (in us) used before first power cycle */
|
|
#define RF_DEFAULT_POWER_UP_TIME 2500
|
|
/* Default minimum power up duration (in us) */
|
|
#define RF_DEFAULT_MIN_POWER_UP_TIME 300
|
|
/* Default power-up margin (in us) to account for wake-up sequence outside the RF power state machine */
|
|
#define RF_DEFAULT_POWER_UP_MARGIN 314
|
|
/* Default phy-switching margin (in us) to account for overhead of processing time on the system MCU. */
|
|
#define RF_DEFAULT_PHY_SWITCHING_MARGIN 314
|
|
/* Default power down duration in us */
|
|
#define RF_DEFAULT_POWER_DOWN_TIME 1000
|
|
#define RF_MAX_CHAIN_CMD_LEN 8
|
|
/* RAT channel (0-4) are used by RF Core. Only 5,6,7 are available for application */
|
|
#define RF_RAT_CH_LOWEST 5
|
|
#define RF_SEND_RAT_STOP_RATIO 7
|
|
#define RF_RTC_CONV_TO_US_SHIFT 12
|
|
#define RF_SHIFT_4_BITS 4
|
|
#define RF_SHIFT_8_BITS 8
|
|
#define RF_SHIFT_16_BITS 16
|
|
#define RF_SHIFT_32_BITS 32
|
|
#define RF_RTC_TICK_INC (0x100000000LL/32768)
|
|
#define RF_SCALE_RTC_TO_4MHZ 4000000
|
|
#define RF_NUM_RAT_TICKS_IN_1_US 4
|
|
/* (3/4)th of a full RAT cycle, in us */
|
|
#define RF_DISPATCH_MAX_TIME_US (UINT32_MAX / RF_NUM_RAT_TICKS_IN_1_US * 3 / 4)
|
|
/* (1/4)th of a full RAT cycle, in us */
|
|
#define RF_DISPATCH_MAX_TIME_WRAPAROUND_US (int32_t)(RF_DISPATCH_MAX_TIME_US - UINT32_MAX / RF_NUM_RAT_TICKS_IN_1_US)
|
|
#define RF_DISPATCH_INFINIT_TIME (UINT32_MAX)
|
|
#define RF_XOSC_HF_SWITCH_CHECK_PERIOD_US 50
|
|
#define RF_DEFAULT_AVAILRATCH_VAL 0x7
|
|
#define RF_ABORT_FLUSH_ALL 0x2
|
|
#define RF_CMDSTA_REG_VAL_MASK 0xFF
|
|
#define RF_RAT_CAPTURE_REPEAT_MODE 0x10000000
|
|
#define RF_RAT_INTERRUPT_BASE_INDEX 0x01
|
|
#define RF_RAT_ERROR_BASE_INDEX 0x10
|
|
#define RF_RAT_COMPENSATION_TIME_US 25
|
|
#define RF_PHY_SWITCHING_MODE 1
|
|
#define RF_PHY_BOOTUP_MODE 0
|
|
#define RF_SCH_CMD_TIMING_INSERT 0x4
|
|
#define RF_REQ_ACCESS_MAX_DUR_US 1000000
|
|
/* Additional analog config time for setup command */
|
|
#define RF_ANALOG_CFG_TIME_US 96
|
|
/* Update analog configuration in setup */
|
|
#define RF_SETUP_ANALOGCFG_UPDATE 0
|
|
/* Don't update analog configuration in setup */
|
|
#define RF_SETUP_ANALOGCFG_NOUPDATE 0x2D
|
|
#define RF_SCH_CMD_STARTTIME_NOW 0
|
|
#define RF_SCH_CMD_ENDTIME_IGNORE 0
|
|
#define RF_DEFAULT_PHY_SWITCHING_TIME 500
|
|
#define RF_RADIOFREECB_PREEMPT_FLAG 0x1
|
|
#define RF_RADIOFREECB_REQACCESS_FLAG 0x2
|
|
#define RF_RADIOFREECB_CMDREJECT_FLAG 0x4
|
|
#define RF_DEFAULT_RAT_RTC_ERR_TOL_IN_US 5
|
|
/* Approx for 1e6 / 500. XTAL drift is 500 ppm */
|
|
#define RF_DEFAULT_COMB_XTAL_DRIFT_BITS_SHIFT 11
|
|
/* Window (in us) to decide if wakeup was from RF power up clock */
|
|
#define RF_WAKEUP_DETECTION_WINDOW_IN_US 300
|
|
/* Ieee context mask and background value */
|
|
#define RF_IEEE_ID_MASK 0xFC00
|
|
#define RF_IEEE_FG_CMD 0x2C00
|
|
/* Defines for to mask High-PA overrides. */
|
|
#define RF_TX20_ENABLED 0xFFFF
|
|
#define RF_TX20_PATYPE_ADDRESS 0x21000345
|
|
#define RF_TX20_PATYPE_MASK 0x04
|
|
#define RF_TX20_GAIN_ADDRESS 0x2100034C
|
|
#define RF_TX20_GAIN_MASK 0x003FFFFF
|
|
#define RF_TX20_PATTERN TX20_POWER_OVERRIDE(0)
|
|
#define RF_TXSTD_PATTERN TX_STD_POWER_OVERRIDE(0)
|
|
#define RF_TX_OVERRIDE_MASK 0x000003FF
|
|
#define RF_TX_OVERRIDE_SHIFT 10
|
|
#define RF_TX_OVERRIDE_INVALID_OFFSET 0xFF
|
|
/* Defines for update of the HPOSC override */
|
|
#define RF_HPOSC_OVERRIDE_PATTERN HPOSC_OVERRIDE(0)
|
|
#define RF_HPOSC_OVERRIDE_MASK 0xFFFF
|
|
/* Common defines for override handling*/
|
|
#define RF_OVERRIDE_SEARCH_DEPTH 80
|
|
|
|
/*-------------- Structures and definitions ---------------*/
|
|
|
|
/* FSM typedef. */
|
|
typedef void (*RF_FsmStateFxn)(RF_Object*, RF_FsmEvent const);
|
|
|
|
/* Rat channel configuration. */
|
|
typedef struct RF_RatChannel_s RF_RatChannel;
|
|
|
|
/* Rat channel configuration. */
|
|
struct RF_RatChannel_s {
|
|
RF_Handle pClient; /* Pointer to current client. NULL means the channel is free. */
|
|
RF_RatCallback pCb; /* Callback pointer of the channel. */
|
|
RF_RatMode mode; /* Mode of this RAT channel: RF_RatModeCompare, etc. */
|
|
RF_RatHandle handle; /* Channel number: 0,1,2. */
|
|
RF_RatStatus status; /* Status of the channel: RF_RatStatusIdle, RF_RatStatusPending, RF_RatStatusRunning */
|
|
uint64_t chCmd; /* Generic storage for the command structure itself. */
|
|
uint32_t ioCmd; /* Raw binary to be sent to the CM0 to set up the GPOs. This is optional. */
|
|
};
|
|
|
|
/* Rat module configuration. */
|
|
typedef struct RF_RatModule_s RF_RatModule;
|
|
|
|
/* Rat module configuration. */
|
|
struct RF_RatModule_s {
|
|
RF_RatChannel channel[RF_RAT_CH_CNT]; /* Container of channel configurations. */
|
|
uint8_t availableRatChannels; /* Storage of available RAT channels read from the RF core. */
|
|
uint8_t volatile pendingInt; /* Pending interrupt flags to be served. */
|
|
uint8_t numActiveChannels; /* Counter of active channels. This is used to compensate the
|
|
overhead of programming the channels.*/
|
|
};
|
|
|
|
/* RF core configuration. */
|
|
typedef struct RF_CoreState_s RF_CoreState;
|
|
|
|
/* RF core configuration. */
|
|
struct RF_CoreState_s
|
|
{
|
|
RF_CoreStatus volatile status;
|
|
RF_FsmStateFxn fxn;
|
|
uint32_t activeTimeUs;
|
|
bool init;
|
|
bool manualXoscHfSelect;
|
|
};
|
|
|
|
/* RAT synchronization. */
|
|
typedef union RF_RatSyncCmd_u RF_RatSyncCmd;
|
|
|
|
/* RAT synchronization. */
|
|
union RF_RatSyncCmd_u
|
|
{
|
|
rfc_CMD_SYNC_START_RAT_t start;
|
|
rfc_CMD_SYNC_STOP_RAT_t stop;
|
|
};
|
|
|
|
/* Reconfigure the PA settings. */
|
|
typedef union RF_ConfigurePaCmd_u RF_ConfigurePaCmd;
|
|
|
|
/* Reconfigure the PA settings. */
|
|
union RF_ConfigurePaCmd_u {
|
|
rfc_CMD_SET_TX_POWER_t tuneTxPower;
|
|
rfc_CMD_SET_TX20_POWER_t tuneTx20Power;
|
|
rfc_CMD_CHANGE_PA_t changePa;
|
|
};
|
|
|
|
/* Command queue. */
|
|
typedef struct RF_CmdQ_s RF_CmdQ;
|
|
|
|
/* Command queue. */
|
|
struct RF_CmdQ_s{
|
|
List_List pPend; /* List of pending commands to be dispatched. */
|
|
List_List pDone; /* List of executed commands to be served. */
|
|
RF_Cmd* volatile pCurrCmdBg; /* Currently running command. */
|
|
RF_Cmd* volatile pCurrCmdFg; /* Currently running foreground command. */
|
|
RF_Cmd* volatile pCurrCmdCb; /* Command which callback to be invoked. */
|
|
RF_CmdHandle volatile nSeqPost; /* Sequence # for previously posted command. */
|
|
RF_CmdHandle volatile nSeqDone; /* Sequence # for last done command. */
|
|
};
|
|
|
|
/* RF scheduler. */
|
|
typedef struct RF_Sch_s RF_Sch_t;
|
|
|
|
/* RF scheduler. */
|
|
struct RF_Sch_s {
|
|
RF_Handle clientHnd[N_MAX_CLIENTS]; /* Client handle for each registered client. */
|
|
RF_AccessParams accReq[N_MAX_CLIENTS]; /* Input parameters from any RF_requestAccess API calls. */
|
|
RF_Handle clientHndRadioFreeCb; /* Client handle for the radio callback. */
|
|
uint8_t issueRadioFreeCbFlags; /* Indicate if driver needs to issue RF_EventRadioFree callback {0:pre-emption, 1:requestAccess running, 2: reject command}. */
|
|
uint8_t cmdInsertFlags; /* Indicate if the command was inserted based on timing information. */
|
|
};
|
|
|
|
/*-------------- RTOS objects ---------------*/
|
|
|
|
/* RF core software interrupts */
|
|
static SwiP_Struct RF_swiFsmObj;
|
|
static void RF_swiFsm(uintptr_t a, uintptr_t b);
|
|
|
|
/* RF core hardware interrupts */
|
|
static HwiP_Struct RF_hwiCpe0Obj;
|
|
static void RF_hwiCpe0Active(uintptr_t a);
|
|
static void RF_hwiCpe0PowerFsm(uintptr_t a);
|
|
|
|
/* RF core HW software interrupts */
|
|
static SwiP_Struct RF_swiHwObj;
|
|
static void RF_swiHw(uintptr_t a, uintptr_t b);
|
|
|
|
/* RF core HW hardware interrupts */
|
|
static HwiP_Struct RF_hwiHwObj;
|
|
static void RF_hwiHw(uintptr_t a);
|
|
|
|
/* Clock used for triggering power-up sequences */
|
|
static ClockP_Struct RF_clkPowerUpObj;
|
|
static void RF_clkPowerUp(uintptr_t a);
|
|
|
|
/* Common inactivity timeout clock callback */
|
|
static ClockP_Struct RF_clkInactivityObj;
|
|
static void RF_clkInactivityCallback(uintptr_t a);
|
|
|
|
/* Common request access timeout clock callback */
|
|
static void RF_clkReqAccess(uintptr_t a);
|
|
|
|
|
|
/*-------------- Static structures ---------------*/
|
|
|
|
/* Default RF parameters structure */
|
|
static const RF_Params RF_defaultParams = {
|
|
.nInactivityTimeout = SemaphoreP_WAIT_FOREVER,
|
|
.nPowerUpDuration = 0,
|
|
.pPowerCb = NULL,
|
|
.pErrCb = NULL,
|
|
.nPowerUpDurationMargin = RF_DEFAULT_POWER_UP_MARGIN,
|
|
.nPhySwitchingDurationMargin = RF_DEFAULT_PHY_SWITCHING_MARGIN,
|
|
.pClientEventCb = NULL,
|
|
.nClientEventMask = 0,
|
|
};
|
|
|
|
/*-------------- Global variables ---------------*/
|
|
|
|
/* RF_Cmd container pool. Containers with extra information about RF commands. */
|
|
static RF_Cmd RF_cmdPool[N_CMD_POOL];
|
|
|
|
/* Command queue top level structure. It contains pointers to the different queues. */
|
|
static RF_CmdQ RF_cmdQ;
|
|
|
|
/* Static object used to subscribe from early notification in the power driver */
|
|
static Power_NotifyObj RF_wakeupNotifyObj;
|
|
|
|
/* Power constraints set by the RF driver */
|
|
static volatile uint8_t RF_powerConstraint;
|
|
|
|
/* Pointer to current radio client (indicates also whether the radio is powered) */
|
|
static RF_Object* RF_currClient;
|
|
|
|
/* Current state of the RF core. */
|
|
static RF_CoreState RF_core;
|
|
|
|
/* Static container of a direct/immediate commands */
|
|
static RF_RatModule RF_ratModule;
|
|
|
|
/* Commands used to synchronize the RTC and the RAT timer. */
|
|
static volatile RF_RatSyncCmd RF_ratSyncCmd;
|
|
|
|
/* Top level structure of the shceduler unit. */
|
|
static RF_Sch_t RF_Sch;
|
|
|
|
/* Variables used for powerUpDuration, phySwitchingTime and RAT sync time calculation. */
|
|
static uint32_t RF_rtcTimestampA; /* RTC timer value power-up and active time calculation. */
|
|
static uint32_t RF_rtcBeginSequence; /* RTC timer value for switching time calculation. */
|
|
static uint32_t RF_errTolValInUs; /* max allowed error between RAT/RTC drift to enable resync at power-down (in us). */
|
|
|
|
/* Counter of radio clients */
|
|
static uint8_t RF_numClients;
|
|
|
|
/*-------------- Externs ---------------*/
|
|
|
|
/* Hardware attribute structure populated in board file. */
|
|
extern const RFCC26XX_HWAttrsV2 RFCC26XX_hwAttrs;
|
|
|
|
/* Software policy set in the board file and implements the distributed scheduling algorithm. */
|
|
__attribute__((weak)) const RFCC26XX_SchedulerPolicy RFCC26XX_schedulerPolicy = {
|
|
.submitHook = RF_defaultSubmitPolicy,
|
|
.conflictHook = RF_defaultConflictPolicy
|
|
};
|
|
|
|
/*-------------- Booleans ---------------*/
|
|
|
|
/* variable to indicate with the FLASH is disable during the power up */
|
|
static bool bDisableFlashInIdleConstraint;
|
|
|
|
/*-------------- State machine functions ---------------*/
|
|
|
|
/* FSM state functions */
|
|
static void RF_fsmPowerUpState(RF_Object *pObj, RF_FsmEvent e);
|
|
static void RF_fsmSetupState(RF_Object *pObj, RF_FsmEvent e);
|
|
static void RF_fsmActiveState(RF_Object *pObj, RF_FsmEvent e);
|
|
static void RF_fsmXOSCState(RF_Object *pObj, RF_FsmEvent e);
|
|
|
|
/*-------------- Helper functions ---------------*/
|
|
|
|
/* Command queue handling */
|
|
static RF_Cmd* RF_queueEnd(RF_Handle h, List_List* pHead);
|
|
|
|
/* Command handling*/
|
|
static bool RF_isClientOwner(RF_Handle h, RF_Cmd* pCmd);
|
|
static RF_Cmd* RF_cmdAlloc(void);
|
|
static RF_Cmd* RF_cmdGet(RF_Handle h, RF_CmdHandle ch, uint8_t mask);
|
|
static void RF_cmdStoreEvents(RF_Cmd* pCmd, RF_EventMask events);
|
|
static bool RF_cmdDispatchTime(uint32_t* dispatchTimeClockTicks, bool conflict, RF_Cmd** pAbsCmd);
|
|
static RF_Stat RF_abortCmd(RF_Handle h, RF_CmdHandle ch, bool graceful, bool flush, bool preempt);
|
|
static bool RF_checkCmdFsError(void);
|
|
static void RF_cacheFsCmd(RF_Cmd* pCmd);
|
|
static uint32_t RF_discardPendCmd(RF_Handle h, RF_Cmd* pCmd, bool bFlushAll, bool bPreempt);
|
|
|
|
/* Scheduler */
|
|
static void RF_issueRadioFreeCb(uint8_t src);
|
|
static bool RF_verifyGap(RF_Cmd* newCmd, RF_Cmd* prevCmd, RF_Cmd* nextCmd);
|
|
static RF_ScheduleStatus RF_howToSchedule(RF_Cmd* newCmd, RF_Cmd* pCmdBg, RF_Cmd* pCmdFg, List_List* pPendQueue, List_List* pDoneQueue, RF_Cmd** pInsertLocation);
|
|
|
|
/* RAT module */
|
|
static RF_RatChannel* RF_ratGetChannel(uint8_t ch);
|
|
static RF_RatChannel* RF_ratAllocChannel(RF_RatHandle ratChannel);
|
|
static uint32_t RF_ratGetChannelValue(RF_RatHandle ratHandle);
|
|
static uint32_t RF_ratGetValue(void);
|
|
static void RF_ratGenerateChCmd(RF_RatChannel* ratCh, void* ratConfig);
|
|
static void RF_ratGenerateIoCmd(RF_RatChannel* ratCh, RF_RatConfigOutput* ioConfig);
|
|
static RF_RatHandle RF_ratSetupChannel(RF_Handle ratClient, RF_RatMode ratMode, RF_RatCallback ratCallback, RF_RatHandle ratChannel, void* ratConfig, RF_RatConfigOutput* ioConfig);
|
|
static void RF_ratRestartChannels(void);
|
|
static RF_Stat RF_ratArmChannel(RF_RatChannel* ratCh);
|
|
static void RF_ratFreeChannel(RF_RatChannel* ratCh);
|
|
static void RF_ratSuspendChannels(void);
|
|
static bool RF_ratReleaseChannels(void);
|
|
static bool RF_ratDispatchTime(uint32_t* dispatchTimeClockTicks);
|
|
static bool RF_ratIsRunning(void);
|
|
|
|
/* Time management */
|
|
static uint32_t RF_calculateDeltaTimeUs(uint32_t absTime, uint32_t nTotalPowerUpDuration);
|
|
static bool RF_calculateDispatchTime(uint32_t* dispatchTimeClockTicks);
|
|
static void RF_dispatchNextEvent(void);
|
|
static void RF_dispatchNextCmd(void);
|
|
static void RF_restartClockTimeout(ClockP_Handle clock, uint32_t timeout);
|
|
|
|
/* Power management */
|
|
static void RF_corePowerDown(void);
|
|
void RF_powerConstraintRelease(RF_PowerConstraintSrc src);
|
|
void RF_powerConstraintSet(RF_PowerConstraintSrc src);
|
|
RF_PowerConstraintSrc RF_powerConstraintGet(RF_PowerConstraintSrc src);
|
|
static void RF_setInactivityTimeout(void);
|
|
|
|
/* Others */
|
|
static void RF_init(void);
|
|
static void RF_defaultCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
|
|
static RF_Stat RF_runDirectImmediateCmd(RF_Handle h, uint32_t pCmd, uint32_t* rawStatus);
|
|
static RF_Stat RF_executeDirectImmediateCmd(uint32_t pCmd, uint32_t* rawStatus);
|
|
static void RF_invokeGlobalCallback(RF_GlobalEvent event, void* arg);
|
|
static void RF_dbellSubmitCmdAsync(uint32_t rawCmd);
|
|
static void RF_dbellSyncOnAck(void);
|
|
static bool RF_isRadioSetup(RF_Op* pOp);
|
|
static void RF_initRadioSetup(RF_Handle handle);
|
|
static void RF_radioOpDoneCb(void);
|
|
static RF_Op* RF_findEndOfChain(RF_Op* pOp);
|
|
static void RF_applyRfCorePatch(bool mode);
|
|
static bool RF_isStateTransitionAllowed(void);
|
|
|
|
/* PA management */
|
|
static RF_Stat RF_updatePaConfiguration(RF_RadioSetup* radioSetup, RF_TxPowerTable_Value newValue, RF_ConfigurePaCmd* configurePaCmd);
|
|
static void RF_extractPaConfiguration(RF_Handle handle);
|
|
static bool RF_decodeOverridePointers(RF_RadioSetup* radioSetup, uint16_t** pTxPower, uint32_t** pRegOverride, uint32_t** pRegOverrideTxStd, uint32_t** pRegOverrideTx20);
|
|
static void RF_attachOverrides(uint32_t* baseOverride, uint32_t* newOverride);
|
|
static void RF_detachOverrides(uint32_t* baseOverride, uint32_t* newOverride);
|
|
|
|
/* HPOSC management */
|
|
static void RF_updateHpOscOverride(uint32_t *pRegOverride);
|
|
|
|
/*-------------- Command queue internal functions ---------------*/
|
|
|
|
/*
|
|
* Compares the client of a command.
|
|
*
|
|
* Input: h - Client to check against.
|
|
* pCmd - Command to check.
|
|
* Return: true - If the client owns the command.
|
|
* false - Otherwise.
|
|
*/
|
|
static bool RF_isClientOwner(RF_Handle h, RF_Cmd* pCmd)
|
|
{
|
|
if (pCmd && (pCmd->pClient == h))
|
|
{
|
|
return(true);
|
|
}
|
|
else
|
|
{
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Search last entry in simple queue for particular client.
|
|
*
|
|
* Input: h - Client handle.
|
|
* list - List to search within.
|
|
* Return: RF_Cmd if found any
|
|
*/
|
|
static RF_Cmd* RF_queueEnd(RF_Handle h, List_List* list)
|
|
{
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Local variables */
|
|
List_Elem* pTail = NULL;
|
|
List_Elem* pHead = List_head(list);
|
|
|
|
/* Start at the head of queue */
|
|
while (pHead)
|
|
{
|
|
if (RF_isClientOwner(h, (RF_Cmd*)pHead))
|
|
{
|
|
pTail = pHead;
|
|
}
|
|
|
|
pHead = List_next(pHead);
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the last entry belongs to the client */
|
|
return((RF_Cmd*)pTail);
|
|
}
|
|
|
|
/*
|
|
* Allocate a command buffer from the command pool.
|
|
*
|
|
* Input: none
|
|
* Return: RF command
|
|
*/
|
|
static RF_Cmd* RF_cmdAlloc(void)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < N_CMD_POOL; i++)
|
|
{
|
|
/* Find the first available entry in the command pool */
|
|
if (!(RF_cmdPool[i].flags & RF_CMD_ALLOC_FLAG))
|
|
{
|
|
return(&RF_cmdPool[i]);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Search command in the command pool.
|
|
*
|
|
* Input: h - Handle to the client which the command should belong to.
|
|
* ch - Handle to the command to search for.
|
|
* mask - Optional mask of flags to compare to.
|
|
* Return: RF command
|
|
*/
|
|
static RF_Cmd* RF_cmdGet(RF_Handle h, RF_CmdHandle ch, uint8_t mask)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < N_CMD_POOL; i++)
|
|
{
|
|
/* Find the allocated command pool entry corresponding to ch */
|
|
if (RF_cmdPool[i].ch == ch)
|
|
{
|
|
if (RF_isClientOwner(h, &RF_cmdPool[i]))
|
|
{
|
|
/* If a mask is provided, check the flags too */
|
|
if (!mask || (RF_cmdPool[i].flags & mask))
|
|
{
|
|
return(&RF_cmdPool[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Atomic storage of radio events happened during the execution of a command.
|
|
*
|
|
* Input: pCmd - Command the events belogn to.
|
|
* events - The radio events to be store within the context of the command.
|
|
* Return: none
|
|
*/
|
|
static void RF_cmdStoreEvents(RF_Cmd* pCmd, RF_EventMask events)
|
|
{
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Store the events within the context of the command. */
|
|
if (pCmd)
|
|
{
|
|
/* The field rfifg store the events for the next callback.
|
|
The field pastifg accumulates the events in case an
|
|
RF_pendCmd() API call happens. */
|
|
pCmd->rfifg |= events;
|
|
pCmd->pastifg |= events;
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/*
|
|
* Reconfigure and restart a particular clock object.
|
|
*
|
|
* Input: clockObj - A pointer to a clock object.
|
|
* timeoutClockTicks - The timeout to be set in unit of clock ticks.
|
|
* Return: none
|
|
*/
|
|
static void RF_restartClockTimeout(ClockP_Handle clockHandle, uint32_t timeoutClockTicks)
|
|
{
|
|
/* Ceil the value at minimum 1 clock tick. */
|
|
timeoutClockTicks = MAX(timeoutClockTicks, 1);
|
|
|
|
/* Reprogram the clock object. */
|
|
ClockP_setTimeout(clockHandle, timeoutClockTicks);
|
|
ClockP_start(clockHandle);
|
|
}
|
|
|
|
/*
|
|
* Calculate the delta time to an RF event including the overhead of powering up
|
|
* and down.
|
|
*
|
|
* Input: abstime - The timestamp the event will need to happen.
|
|
* nTotalPowerUpDuration - The duration we need to compensate with.
|
|
* Return: deltaTime - The time left until the RF core need to be trigged.
|
|
*/
|
|
static uint32_t RF_calculateDeltaTimeUs(uint32_t absTime, uint32_t nTotalPowerUpDuration)
|
|
{
|
|
/* Local variables. */
|
|
uint32_t deltaTimeUs;
|
|
|
|
/* Read the timestamp to calculate difference from. */
|
|
uint32_t currentTime = RF_getCurrentTime();
|
|
|
|
/* Calculate the difference with the current timestamp. */
|
|
deltaTimeUs = UDIFF(currentTime, absTime);
|
|
deltaTimeUs /= RF_NUM_RAT_TICKS_IN_1_US;
|
|
|
|
/* Check if delta time is greater than (powerup duration + power down duration) for a
|
|
power cycle, and is less than 3/4 of a RAT cycle (~17 minutes) */
|
|
if ((deltaTimeUs > (int32_t)(nTotalPowerUpDuration + RF_DEFAULT_POWER_DOWN_TIME)) &&
|
|
(deltaTimeUs <= RF_DISPATCH_MAX_TIME_US))
|
|
{
|
|
/* Dispatch command in the future */
|
|
return(MAX((deltaTimeUs - nTotalPowerUpDuration), 1));
|
|
}
|
|
else
|
|
{
|
|
/* Dispatch immediately */
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the wakeup time of next command in the queue.
|
|
*
|
|
* Input: dispatchTimeClockTicks - Location where the calculated time is being stored.
|
|
* conflict - true: compare to the first command which have TRIG_ABSTIME trigger type.
|
|
* false: compare to the first command in the queue
|
|
* pAbsCmd - Pointer to the first command which has an absolute start time.
|
|
* Return: validTime - Indicates if the returned time is valid.
|
|
*/
|
|
static bool RF_cmdDispatchTime(uint32_t* dispatchTimeClockTicks, bool conflict, RF_Cmd** pAbsCmd)
|
|
{
|
|
/* By default, there is no command in the queue. */
|
|
RF_Cmd* pCmd = NULL;
|
|
bool validTime = false;
|
|
|
|
/* The input argument determines which command to use as a reference. This is to be able to
|
|
reuse the calculation for both power management and conflict resolution. */
|
|
if (conflict == true)
|
|
{
|
|
/* Start from the beginning of queue. */
|
|
RF_Cmd* pTempCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
|
|
/* Walk the queue and find the first command contains an absolute start time. */
|
|
while (pTempCmd)
|
|
{
|
|
/* Finish the search upon the first match. This assumes that commands with
|
|
absolute start times are in order. */
|
|
if (pTempCmd->pOp->startTrigger.triggerType == TRIG_ABSTIME)
|
|
{
|
|
pCmd = pTempCmd;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Continue the search if no match. */
|
|
pTempCmd = (RF_Cmd*)List_next((List_Elem*)pTempCmd);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The next command in the queue independently of its type determines the timing. */
|
|
pCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
}
|
|
|
|
/* Only recognizes TRIG_ABSTIME triggers, everything else gets dispatched immediately. */
|
|
if (pCmd)
|
|
{
|
|
/* If there is at least one pending command, we can calculate a legit dispatch time. */
|
|
validTime = true;
|
|
|
|
/* Return with the command which we calculate the remained time against. */
|
|
if (pAbsCmd)
|
|
{
|
|
*pAbsCmd = pCmd;
|
|
}
|
|
|
|
/* If the start trigger is absolute, we can calculate the time difference. */
|
|
if (pCmd->pOp->startTrigger.triggerType == TRIG_ABSTIME)
|
|
{
|
|
uint32_t nTotalDuration = 0;
|
|
|
|
/* Calculate the sum of overhead it takes to start up and configure the RF core. */
|
|
if (conflict == true)
|
|
{
|
|
nTotalDuration = pCmd->pClient->clientConfig.nPhySwitchingDuration
|
|
+ pCmd->pClient->clientConfig.nPhySwitchingDurationMargin;
|
|
}
|
|
else
|
|
{
|
|
nTotalDuration = pCmd->pClient->clientConfig.nPowerUpDuration
|
|
+ pCmd->pClient->clientConfig.nPowerUpDurationMargin
|
|
+ (RF_RAT_COMPENSATION_TIME_US * RF_ratModule.numActiveChannels);
|
|
}
|
|
|
|
/* Calculate the remained time until this absolute event. The calculation takes
|
|
into account the minimum power cycle time. */
|
|
*dispatchTimeClockTicks = RF_calculateDeltaTimeUs(pCmd->pOp->startTime, nTotalDuration);
|
|
|
|
/* Scale the value to clock ticks*/
|
|
*dispatchTimeClockTicks /= ClockP_tickPeriod;
|
|
}
|
|
else
|
|
{
|
|
/* Dispatch immediately. */
|
|
*dispatchTimeClockTicks = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This value will not be used. */
|
|
*dispatchTimeClockTicks = 0;
|
|
}
|
|
|
|
/* If the returned timestamp represents a valid dispatch time, return with true. */
|
|
return(validTime);
|
|
}
|
|
|
|
/*
|
|
* Determines if the RAT timer is running (clock is not gated) or not.
|
|
* This is used to determine if any RAT related command can be execured.
|
|
*
|
|
* Input: none
|
|
* Return: PWMCLK_EN_RAT - RAT timer is running.
|
|
* 0 - RAT timer is not running.
|
|
*/
|
|
static bool RF_ratIsRunning(void)
|
|
{
|
|
/* Assume by default that the RAT is not available. */
|
|
bool status = false;
|
|
|
|
/* If the RF core power domain is ON, read the clock of the RAT. */
|
|
if (HWREG(PRCM_BASE + PRCM_O_PDSTAT0) & PRCM_PDSTAT0_RFC_ON)
|
|
{
|
|
status = (bool)(HWREG(RFC_PWR_BASE + RFC_PWR_O_PWMCLKEN) & RFC_PWR_PWMCLKEN_RAT_M);
|
|
}
|
|
|
|
/* Return with the status of RAT. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* Allocate a RAT channel from the three slots available
|
|
* for the user.
|
|
*
|
|
* Input: ratChannel - Pointer to a user provided RF_RatHandle.
|
|
* Return: RF_RatChannel* - Pointer to the allocated channel if success.
|
|
* NULL - If failure.
|
|
*/
|
|
static RF_RatChannel* RF_ratAllocChannel(RF_RatHandle ratChannel)
|
|
{
|
|
/* Walk the RAT channel indexes. */
|
|
uint32_t i;
|
|
for (i = 0; i < RF_RAT_CH_CNT; i++)
|
|
{
|
|
/* Calculate the bit representing this channel within the available channels. */
|
|
uint32_t bitMask = (1 << i);
|
|
|
|
/* Verify that no one is using this channel (from outside the scope of RF driver). */
|
|
if (RF_ratModule.availableRatChannels & bitMask)
|
|
{
|
|
/* Mask the possible channels if a user handle is provided, otherwise find the
|
|
the first available channel. */
|
|
if ((ratChannel == RF_RatChannelAny) || (ratChannel == i))
|
|
{
|
|
/* Decode the fields of a channel. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(i);
|
|
|
|
/* If an available channel is found. */
|
|
if (ratCh && (ratCh->status == RF_RatStatusIdle))
|
|
{
|
|
/* Mark the channel as occupied. */
|
|
RF_ratModule.availableRatChannels &= ~bitMask;
|
|
|
|
/* Put the channel into pending state. */
|
|
ratCh->status = RF_RatStatusPending;
|
|
ratCh->handle = i;
|
|
|
|
/* Increment the counter of active channels. This is used to compensate the
|
|
power up time with the overhead of programming these channels. */
|
|
RF_ratModule.numActiveChannels += 1;
|
|
|
|
/* Return with a pointer to the channel. */
|
|
return(ratCh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return with an invalid channel pointer in case of error. */
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Free a given RAT channel.
|
|
*
|
|
* Input: ratCh - Pointer to a RAT channel in RF_ratModule.
|
|
* Return: none
|
|
*/
|
|
static void RF_ratFreeChannel(RF_RatChannel* ratCh)
|
|
{
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* If a valid pointer is provided */
|
|
if (ratCh && ratCh->status)
|
|
{
|
|
/* Precalculate the contraint ID of this channel. */
|
|
RF_PowerConstraintSrc powerConstraint = (RF_PowerConstraintSrc)(1 << ratCh->handle);
|
|
|
|
/* If the RF core power domain is ON. */
|
|
if (RF_ratIsRunning())
|
|
{
|
|
/* Calculate the channel index based on the handle. */
|
|
uint32_t ratChIntFlag = (1 << (RFC_DBELL_RFHWIFG_RATCH5_BITN + ratCh->handle));
|
|
|
|
/* Disable the RAT channel interrupt source. */
|
|
RFCHwIntDisable(ratChIntFlag);
|
|
RFCHwIntClear(ratChIntFlag);
|
|
}
|
|
|
|
/* Reset the status of the channel. */
|
|
ratCh->status = RF_RatStatusIdle;
|
|
ratCh->mode = RF_RatModeUndefined;
|
|
ratCh->pClient = NULL;
|
|
ratCh->pCb = NULL;
|
|
ratCh->chCmd = 0;
|
|
ratCh->ioCmd = 0;
|
|
|
|
/* Mark the channel as available. */
|
|
RF_ratModule.availableRatChannels |= (1 << ratCh->handle);
|
|
|
|
/* Decrement the counter of active channels. To avoid underflow, check its value first. */
|
|
if (RF_ratModule.numActiveChannels)
|
|
{
|
|
RF_ratModule.numActiveChannels -= 1;
|
|
}
|
|
|
|
/* Notify the state machine that the RF core can be possibly powered down. */
|
|
RF_powerConstraintRelease(powerConstraint);
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/*
|
|
* Returns with a pointer to a RAT channel based on it's handle.
|
|
*
|
|
* Input: ch - Channel handle.
|
|
* Return: ratCh - Pointer to a RAT channel in RF_ratModule.
|
|
*/
|
|
static RF_RatChannel* RF_ratGetChannel(uint8_t ch)
|
|
{
|
|
/* Convert a valid index into a pointer of a RAT channel configuration. */
|
|
if (ch < RF_RAT_CH_CNT)
|
|
{
|
|
return((RF_RatChannel*)&RF_ratModule.channel[ch]);
|
|
}
|
|
|
|
/* Return with NULL in case of invalid input argument. */
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Suspend the running channels and potentially initiate a power down.
|
|
*
|
|
* Input: none
|
|
* Return: true - All RAT channel is suspended.
|
|
* false - Otherwise.
|
|
*/
|
|
static bool RF_ratReleaseChannels(void)
|
|
{
|
|
/* Only try to release the RAT channels if there is no other dependencies set. */
|
|
if (!RF_powerConstraintGet(RF_PowerConstraintCmdQ) &&
|
|
!RF_powerConstraintGet(RF_PowerConstraintDisallow))
|
|
{
|
|
/* Calculate if there is enough time to power down and up. */
|
|
uint32_t dispatchTimeClockTicks;
|
|
bool validTime = RF_calculateDispatchTime(&dispatchTimeClockTicks);
|
|
|
|
/* If the next event is sufficiently far into the future. */
|
|
if (!validTime || (validTime && dispatchTimeClockTicks))
|
|
{
|
|
/* Suspend all RAT channels. */
|
|
RF_ratSuspendChannels();
|
|
|
|
/* RAT channels were suspended. */
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
/* RAT channels were not suspended. */
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
* Calculate the timeout of closest RAT event.
|
|
*
|
|
* Input: dispatchTimeClockTicks - Location where the calculated time is being stored.
|
|
* Return: validTime - Indicates if the returned time is valid.
|
|
*/
|
|
static bool RF_ratDispatchTime(uint32_t* dispatchTimeClockTicks)
|
|
{
|
|
/* By default, there is no RAT running. */
|
|
bool validTime = false;
|
|
|
|
/* Initialize the return value. */
|
|
*dispatchTimeClockTicks = RF_DISPATCH_INFINIT_TIME;
|
|
|
|
/* Iterate through the RAT containers and calculate the remained time until
|
|
the closest RAT event. */
|
|
uint32_t i;
|
|
for(i = 0; i < RF_RAT_CH_CNT; i++)
|
|
{
|
|
/* Use a local pointer to have easier access to member fields. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(i);
|
|
|
|
/* If the channel is either in PENDING or RUNNING state, meaning it is in use. */
|
|
if (ratCh && ratCh->status)
|
|
{
|
|
/* There is at least one active channel, we can calculate a legit timestamp. */
|
|
validTime = true;
|
|
|
|
/* If there is at least one channel in Capture mode, we need to power
|
|
up immediately. */
|
|
if (ratCh->mode == RF_RatModeCapture)
|
|
{
|
|
/* Use immediate timeout orcing the RF core to be powered up. */
|
|
*dispatchTimeClockTicks = 0;
|
|
|
|
/* No point to look to the other RAT channels.*/
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Decode the compareTime field. */
|
|
uint32_t compareTime = ((rfc_CMD_SET_RAT_CMP_t*)&ratCh->chCmd)->compareTime;
|
|
|
|
/* Calculate the remained time until this RAT event. The calculation takes
|
|
into account the minimum power cycle time. */
|
|
uint32_t deltaTimeUs = RF_calculateDeltaTimeUs(compareTime,
|
|
RF_currClient->clientConfig.nPowerUpDuration +
|
|
RF_currClient->clientConfig.nPowerUpDurationMargin +
|
|
(RF_RAT_COMPENSATION_TIME_US * RF_ratModule.numActiveChannels));
|
|
|
|
/* Scale the value to clock ticks. */
|
|
uint32_t deltaTimeClockTicks = deltaTimeUs / ClockP_tickPeriod;
|
|
|
|
/* If this is the closest RAT event, update the timer. */
|
|
if (deltaTimeClockTicks < (*dispatchTimeClockTicks))
|
|
{
|
|
*dispatchTimeClockTicks = deltaTimeClockTicks;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return with true if the dispatchTime represents a valid timestamp. */
|
|
return(validTime);
|
|
}
|
|
|
|
/*
|
|
* Arms a given RAT channel. The mode of the channel will define which mode
|
|
* it is being configured to. The cmd variable contains the raw word to be
|
|
* sent to the RF core.
|
|
*
|
|
* Input: ratCh - Pointer to a RAT channel.
|
|
* Return: status - Status code based on the response of RF core.
|
|
*
|
|
*/
|
|
static RF_Stat RF_ratArmChannel(RF_RatChannel* ratCh)
|
|
{
|
|
/* Local variable */
|
|
RF_Stat status = RF_StatError;
|
|
|
|
/* Only those channels can be programmed which are in pending state. */
|
|
if (ratCh && (ratCh->status == RF_RatStatusPending))
|
|
{
|
|
/* Calculate the channel interrupt flag on the handle. */
|
|
uint32_t ratChIntFlag = (1 << (RFC_DBELL_RFHWIFG_RATCH5_BITN + ratCh->handle));
|
|
|
|
/* Clear and enable the interrupt source for that particular channel. */
|
|
RFCHwIntClear(ratChIntFlag);
|
|
RFCHwIntEnable(ratChIntFlag);
|
|
|
|
/* Set the power constraint on this channel to keep the RF core ON. */
|
|
RF_powerConstraintSet((RF_PowerConstraintSrc)(1 << ratCh->handle));
|
|
|
|
/* Send the command to the RF core. */
|
|
status = RF_executeDirectImmediateCmd((uint32_t)&ratCh->chCmd, NULL);
|
|
|
|
/* If the channel configuration is succesfull. */
|
|
if (status == RF_StatCmdDoneSuccess)
|
|
{
|
|
/* Send the IO command to the RF core if there is any configured. */
|
|
if (ratCh->ioCmd)
|
|
{
|
|
status = RF_executeDirectImmediateCmd((uint32_t)ratCh->ioCmd, NULL);
|
|
}
|
|
|
|
/* If both the channel and io configuration is succesfull. */
|
|
if (status == RF_StatCmdDoneSuccess)
|
|
{
|
|
ratCh->status = RF_RatStatusRunning;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return with the status code. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* Restarts any RAT channels which are in pending state at the moment of
|
|
* invoking this method. This is used to automatically restore the rat module
|
|
* right after the RF core is powered up. This is essential for power management.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*
|
|
*/
|
|
static void RF_ratRestartChannels(void)
|
|
{
|
|
/* Iterate through the RAT containers and restore the channels
|
|
which were in running state before we entered Standby mode. */
|
|
uint32_t i;
|
|
for(i = 0; i < RF_RAT_CH_CNT; i++)
|
|
{
|
|
/* Convert the index to a pointer. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(i);
|
|
|
|
/* If the channel is in pending state, program it. */
|
|
if (ratCh && (ratCh->status == RF_RatStatusPending))
|
|
{
|
|
/* Try to program the RAT channel. */
|
|
RF_Stat status = RF_ratArmChannel(ratCh);
|
|
|
|
/* Execute error handling if programming fails, i.e. due to past timestamp.
|
|
This is done in SWI context. */
|
|
if (status != RF_StatCmdDoneSuccess)
|
|
{
|
|
/* Mark the event as an error by setting also a shadow bit. */
|
|
RF_ratModule.pendingInt |= ((RF_RAT_INTERRUPT_BASE_INDEX | RF_RAT_ERROR_BASE_INDEX) << ratCh->handle);
|
|
|
|
/* Post the SWI handler to serve the callback. */
|
|
SwiP_or(&RF_swiHwObj, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Suspends any RAT channel which are in RUNNING state.
|
|
* This is used to force all RAT channels into pending state allowing the power
|
|
* management to power off the RF core power domain and resynchronize the RAT channels
|
|
* on next power up.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_ratSuspendChannels(void)
|
|
{
|
|
/* Iterate through the RAT containers and suspend the active channels. */
|
|
uint32_t i;
|
|
for(i = 0; i < RF_RAT_CH_CNT; i++)
|
|
{
|
|
/* Set a pointer to the channel. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(i);
|
|
|
|
/* Only actively running channles can be suspended. */
|
|
if (ratCh && ratCh->status)
|
|
{
|
|
/* Set the status to be suspended. */
|
|
ratCh->status = RF_RatStatusPending;
|
|
|
|
/* Clear the power constraint of this channel */
|
|
RF_powerConstraintRelease((RF_PowerConstraintSrc)(1 << ratCh->handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read the counter value from the RAT timer.
|
|
*
|
|
* Input: none
|
|
* Return: time - The value found in the RATCNT running register.
|
|
*/
|
|
static uint32_t RF_ratGetValue(void)
|
|
{
|
|
return(HWREG(RFC_RAT_BASE + RFC_RAT_O_RATCNT));
|
|
}
|
|
|
|
/*
|
|
* Read the channel value from the RAT timer.
|
|
*
|
|
* Input: ratHandle - The handle to the channel.
|
|
* Return: timeout - The value found in the RATCHxVAL register.
|
|
*/
|
|
static uint32_t RF_ratGetChannelValue(RF_RatHandle ratHandle)
|
|
{
|
|
/* Read the channel value. */
|
|
return(HWREG(RFC_RAT_BASE + RFC_RAT_O_RATCH5VAL + ratHandle * sizeof(uint32_t)));
|
|
}
|
|
|
|
/*
|
|
* Generate a command which can be used to configure a RAT channel into COMPARE mode.
|
|
*
|
|
* Input: ratCh - Pointer to the channel.
|
|
* ratConfig - Configuration structure holding the channel setup.
|
|
* Return: none
|
|
*/
|
|
static void RF_ratGenerateChCmd(RF_RatChannel* ratCh, void* ratConfig)
|
|
{
|
|
/* Generate a command based on the mode. */
|
|
if (ratCh->mode == RF_RatModeCompare)
|
|
{
|
|
/* Local pointer to cast the configuration to the proper type. */
|
|
RF_RatConfigCompare* ratCompareConfig = (RF_RatConfigCompare*) ratConfig;
|
|
|
|
/* Point a pointer to the generic command field within the channels context. */
|
|
rfc_CMD_SET_RAT_CMP_t* pCmd = (rfc_CMD_SET_RAT_CMP_t*)&ratCh->chCmd;
|
|
|
|
/* Populate the command structure properly. */
|
|
pCmd->commandNo = CMD_SET_RAT_CMP; /* Instruct the RF core to use COMPARE mode. */
|
|
pCmd->ratCh = RF_RAT_CH_LOWEST + ratCh->handle; /* Encode the selected channel number. */
|
|
pCmd->compareTime = ratCompareConfig->timeout; /* Select the compare timeout. */
|
|
}
|
|
else if (ratCh->mode == RF_RatModeCapture)
|
|
{
|
|
/* Local pointer to cast the configuration to the proper type. */
|
|
RF_RatConfigCapture* ratCaptureConfig = (RF_RatConfigCapture*) ratConfig;
|
|
|
|
/* Point a pointer to the generic command field within the channels context. */
|
|
rfc_CMD_SET_RAT_CPT_t* pCmd = (rfc_CMD_SET_RAT_CPT_t*)&ratCh->chCmd;
|
|
|
|
/* Calculate the direct command to be sent to the RF core.*/
|
|
pCmd->commandNo = CMD_SET_RAT_CPT; /* Instruct the RF core to use CAPTURE mode. */
|
|
pCmd->config.ratCh = RF_RAT_CH_LOWEST + ratCh->handle; /* Encode the selected channel number. */
|
|
pCmd->config.inputSrc = ratCaptureConfig->source; /* Select the source to be captured. */
|
|
pCmd->config.inputMode = ratCaptureConfig->captureMode; /* Select the mode of capture: raising, falling, etc*/
|
|
pCmd->config.bRepeated = ratCaptureConfig->repeat; /* Select if we should re-arm the channel after a capture event. */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate a command which can be used to configure an IO for a particular RAT channel.
|
|
*
|
|
* Input: ratCh - Pointer to the channel.
|
|
* ioConfig - Configuration channel for the IO.
|
|
* Return: cmdToDoorbell - Return with the command structure. It is casted to uint32_t as it is
|
|
* stored in a generic variable.
|
|
*/
|
|
static void RF_ratGenerateIoCmd(RF_RatChannel* ratCh, RF_RatConfigOutput* ioConfig)
|
|
{
|
|
/* Local variable. */
|
|
uint32_t cmdToDoorbell = 0;
|
|
|
|
/* If there is an IO configuration. */
|
|
if (ioConfig)
|
|
{
|
|
cmdToDoorbell |= ioConfig->select << 2;
|
|
cmdToDoorbell |= ioConfig->mode << 5;
|
|
cmdToDoorbell |= (uint32_t)(RF_RAT_CH_LOWEST + ratCh->handle) << 8;
|
|
|
|
cmdToDoorbell = (uint32_t)CMDR_DIR_CMD_2BYTE(CMD_SET_RAT_OUTPUT, cmdToDoorbell);
|
|
}
|
|
|
|
/* Return with the raw command to be sent to the doorbell. */
|
|
ratCh->ioCmd = cmdToDoorbell;
|
|
}
|
|
|
|
/*
|
|
* Wrapper function to setup a RAT channel into the selected mode.
|
|
*
|
|
* Input: ratClient - Handle previously returned by RF_open().
|
|
* ratMode - Identifies the mode the channel is being set up: RF_RatModeCompare or RF_RatModeCapture.
|
|
* ratCallback - Callback function to be registered to the RAT channel.
|
|
* ratChannel - Preferred channel to be allocated. If RF_RatChannelAny is provided, allocatethe first available channel.
|
|
* ratConfig - Configuration structure holding the setup of the particulare channel.
|
|
* ioConfig - Configuration strucutre of the assosiated GPO setup.
|
|
* Return: ratHandle - RF_RatHandle to the allocated channel. If allocation fails, RF_ALLOC_ERROR is returned.
|
|
*/
|
|
static RF_RatHandle RF_ratSetupChannel(RF_Handle ratClient, RF_RatMode ratMode, RF_RatCallback ratCallback, RF_RatHandle ratChannel, void* ratConfig, RF_RatConfigOutput* ioConfig)
|
|
{
|
|
/* Return with an error. Either we couldn't allocate any RAT
|
|
channel, or the RAT module declined our configuration. */
|
|
RF_RatHandle ratHandle = (RF_RatHandle)RF_ALLOC_ERROR;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Find and allocate a RAT channel (if any is available) */
|
|
RF_RatChannel* ratCh = RF_ratAllocChannel(ratChannel);
|
|
|
|
/* If we could allocate a RAT channel */
|
|
if (ratCh)
|
|
{
|
|
/* Populate the container. Use the default "do nothing" callback
|
|
if no user callback is provided and generate the command based
|
|
on the mode of the channel. */
|
|
ratCh->pClient = ratClient;
|
|
ratCh->mode = ratMode;
|
|
ratCh->pCb = (RF_RatCallback)RF_defaultCallback;
|
|
RF_ratGenerateChCmd(ratCh, ratConfig);
|
|
RF_ratGenerateIoCmd(ratCh, ioConfig);
|
|
|
|
/* If there is a user callback provided, override the default callback. */
|
|
if (ratCallback)
|
|
{
|
|
ratCh->pCb = ratCallback;
|
|
}
|
|
|
|
/* Decide which PHY should be used upon first start up. */
|
|
if (RF_currClient == NULL)
|
|
{
|
|
RF_currClient = ratCh->pClient;
|
|
}
|
|
|
|
/* Calculate the RAT/RTC timestamp to be used to wake the RF core. */
|
|
RF_dispatchNextEvent();
|
|
|
|
/* Return with the handle upon success. */
|
|
ratHandle = (RF_RatHandle)ratCh->handle;
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with either an error OR a handle to a RAT channel. */
|
|
return(ratHandle);
|
|
}
|
|
|
|
/*
|
|
* Poll the RFACKIFG and clear the flag afterwards. This is used during the power up sequence
|
|
* of the RF core where interlaying processing is implemented.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_dbellSyncOnAck(void)
|
|
{
|
|
while (!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
|
|
HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG) = 0;
|
|
}
|
|
|
|
/*
|
|
* Submit a command to the doorbell without blocking command execution. This is used during the
|
|
* power up sequence where the system CPU can continue with processing data while the RF core
|
|
* executes the submitted command.
|
|
*
|
|
* Input: rawCmd - The raw command to be written to the doorbell. This can be a pointer or a
|
|
* a direct/immediate command.
|
|
* Return: none
|
|
*/
|
|
static void RF_dbellSubmitCmdAsync(uint32_t rawCmd)
|
|
{
|
|
HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG) = 0;
|
|
HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = rawCmd;
|
|
}
|
|
|
|
/*
|
|
* Wake up notification callback from the power driver. If the callback is from RF wakeup
|
|
* set constraint to let RF Driver control the XOSC switching else do nothing in the
|
|
* callback.
|
|
*
|
|
* Input: eventType - The type of event when the notification is invoked
|
|
* eventArg - Not used.
|
|
* clientArg - Not used.
|
|
* Return: Power_NOTIFYDONE
|
|
*/
|
|
static uint8_t RF_wakeupNotification(uint8_t eventType, uint32_t *eventArg, uint32_t *clientArg)
|
|
{
|
|
/* Check if the callback is for wakeup from standby and if power up clock is running */
|
|
if ((eventType == PowerCC26XX_AWAKE_STANDBY) && (ClockP_isActive(&RF_clkPowerUpObj)))
|
|
{
|
|
/* Calculate time (in us) until next trigger (assume next trigger is max ~70 min away) */
|
|
uint32_t timeInUsUntilNextTrig = ClockP_tickPeriod * ClockP_getTimeout(&RF_clkPowerUpObj);
|
|
|
|
/* Check if the next trig time is close enough to the actual power up */
|
|
if (timeInUsUntilNextTrig < RF_WAKEUP_DETECTION_WINDOW_IN_US)
|
|
{
|
|
/* Stop power up clock */
|
|
ClockP_stop(&RF_clkPowerUpObj);
|
|
|
|
/* Setup RF Driver to do the XOSC_HF switching */
|
|
Power_setConstraint(PowerCC26XX_SWITCH_XOSC_HF_MANUALLY);
|
|
|
|
/* Set variable to indicate RF Driver will do the XOSC_HF switching */
|
|
RF_core.manualXoscHfSelect = true;
|
|
|
|
/* Start the RF Core power up */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventWakeup);
|
|
}
|
|
}
|
|
|
|
return(Power_NOTIFYDONE);
|
|
}
|
|
|
|
/*-------------- Scheduler internal functions --------------------------------*/
|
|
|
|
/*
|
|
* Issue RF_EventRadioFree callback to the client. The callback is issued -
|
|
* 1. After pre-emption is complete
|
|
* 2. Dedicated request access period expires or released
|
|
* 3. command reject because of other high priority command running
|
|
*
|
|
* Input: src - Flag indicating the source of callback request.
|
|
* Return: none
|
|
*/
|
|
static void RF_issueRadioFreeCb(uint8_t src)
|
|
{
|
|
/* Enter critical section*/
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Clear the reason why the callback is being invoked */
|
|
RF_Sch.issueRadioFreeCbFlags &= ~src;
|
|
|
|
/* Local variable */
|
|
bool isReqAccessActive = false;
|
|
|
|
/* If any of the clients has active request access, indicate it */
|
|
if (RF_Sch.clientHnd[0])
|
|
{
|
|
isReqAccessActive |= ClockP_isActive(&RF_Sch.clientHnd[0]->state.clkReqAccess);
|
|
}
|
|
if (RF_Sch.clientHnd[1])
|
|
{
|
|
isReqAccessActive |= ClockP_isActive(&RF_Sch.clientHnd[1]->state.clkReqAccess);
|
|
}
|
|
|
|
/* If we cleared all the potential sources and there is no request access*/
|
|
if ((RF_Sch.issueRadioFreeCbFlags == 0) && !isReqAccessActive)
|
|
{
|
|
/* If a valid client handle is provided through the global pointer */
|
|
if (RF_Sch.clientHndRadioFreeCb && (RF_Sch.clientHndRadioFreeCb->clientConfig.nClientEventMask & RF_ClientEventRadioFree))
|
|
{
|
|
/* Get a pointer to the client event callback */
|
|
RF_ClientCallback pClientEventCb = (RF_ClientCallback)RF_Sch.clientHndRadioFreeCb->clientConfig.pClientEventCb;
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Invoek the client event callback */
|
|
pClientEventCb(RF_Sch.clientHndRadioFreeCb, RF_ClientEventRadioFree, NULL);
|
|
|
|
/* Clear the client pointer in any case */
|
|
RF_Sch.clientHndRadioFreeCb = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decode how much time it will take to switch protocol/phy configuration.
|
|
*
|
|
* Input: prevCmd - The command switching from.
|
|
* nextCmd - The command switching to.
|
|
* Return: switchingTime - The time it takes to switch the PHY.
|
|
*/
|
|
static int32_t RF_getSwitchingTimeInUs(RF_Cmd* prevCmd, RF_Cmd* nextCmd)
|
|
{
|
|
int32_t switchingTime = 0;
|
|
|
|
/* If otherCmd and newCmd are from different client then there is a switching time
|
|
related to moving between the two commands. */
|
|
if (prevCmd->pClient != nextCmd->pClient)
|
|
{
|
|
switchingTime = nextCmd->pClient->clientConfig.nPhySwitchingDuration;
|
|
}
|
|
|
|
/* Return the switching time related to moving between the two clients. */
|
|
return(switchingTime);
|
|
}
|
|
|
|
/*
|
|
* Check if new request can inserted between the previous and next command in the
|
|
* current queue.
|
|
*
|
|
* Input: newCmd - RF_Cmd pointer for the new command request
|
|
* prevCmd - RF_Cmd pointer for the previous cmd in the queue
|
|
* nextCmd - RF_Cmd pointer for the next cmd in the queue
|
|
* Return: true - If command can be inserted in the queue else
|
|
* false - Otherwise.
|
|
*/
|
|
static bool RF_verifyGap(RF_Cmd* newCmd, RF_Cmd* prevCmd, RF_Cmd* nextCmd)
|
|
{
|
|
/* Initialize local variables. */
|
|
bool insertNewCmdAfterPrev = prevCmd ? false : true;
|
|
bool insertNewCmdBeforeNext = nextCmd ? false : true;
|
|
int32_t deltaInUs = 0;
|
|
|
|
/* Step 1 - The newCmd must have an endTime in order to be placed anywhere
|
|
else than the end. Or if there are no commands behind. */
|
|
if ((newCmd) && (insertNewCmdBeforeNext || (newCmd->endTime != RF_SCH_CMD_ENDTIME_IGNORE)))
|
|
{
|
|
/* If there is a prevCmd and it have an endTime, we could potentially
|
|
put the new command behind it. */
|
|
if ((prevCmd) && (prevCmd->endTime != RF_SCH_CMD_ENDTIME_IGNORE))
|
|
{
|
|
/* Take the start time of the command located later in the timeline. */
|
|
deltaInUs = (int32_t)RF_convertRatTicksToUs(newCmd->startTime);
|
|
|
|
/* Substract the time earlier in the timeline. The result is the gap in between. */
|
|
deltaInUs -= (int32_t)RF_convertRatTicksToUs(prevCmd->endTime);
|
|
|
|
/* Substract the switching time needed to move between prevCmd and newCmd. */
|
|
deltaInUs -= RF_getSwitchingTimeInUs(prevCmd, newCmd);
|
|
|
|
/* Handle timer overflow with the assumption that the difference between the startTime
|
|
and endTime is less than ~8 min. */
|
|
if ((deltaInUs < ((int32_t)RF_DISPATCH_MAX_TIME_WRAPAROUND_US)) || (deltaInUs > 0))
|
|
{
|
|
/* Allow insertion if startTime has wrapped around or no wrap around and we can insert the command */
|
|
insertNewCmdAfterPrev = true;
|
|
}
|
|
}
|
|
|
|
/* If there is a nextCmd, and it has an aboslute startTime, we could potentially put the new command in front of it.
|
|
If we already have evaluated that we can't be behind the prevCmd, there is no need to evalue this. */
|
|
if ((insertNewCmdAfterPrev) && (nextCmd) && (nextCmd->pOp->startTrigger.triggerType == TRIG_ABSTIME))
|
|
{
|
|
/* Take the start time of the command located later in the timeline. */
|
|
deltaInUs = (int32_t)RF_convertRatTicksToUs(nextCmd->startTime);
|
|
|
|
/* Substract the time earlier in the timeline. The result is the gap in between. */
|
|
deltaInUs -= (int32_t)RF_convertRatTicksToUs(newCmd->endTime);
|
|
|
|
/* Substract the switching time needed to move between newCmd and nextCmd. */
|
|
deltaInUs -= RF_getSwitchingTimeInUs(newCmd, nextCmd);
|
|
|
|
/* Handle timer overflow with the assumption that the difference between the startTime
|
|
and endTime is less than ~8 min. */
|
|
if ((deltaInUs < ((int32_t)RF_DISPATCH_MAX_TIME_WRAPAROUND_US)) || (deltaInUs > 0))
|
|
{
|
|
/* Allow insertion if startTime has wrapped around or no wrap around and we can insert the command. */
|
|
insertNewCmdBeforeNext = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return with true if the command can be inserted into the queue (both before or after criteria met). */
|
|
return(insertNewCmdBeforeNext & insertNewCmdAfterPrev);
|
|
}
|
|
|
|
/*
|
|
* Check what scheduling strategy that can be used to schedule the requesting command.
|
|
*
|
|
* Input: newCmd - Points to the newly submitted radio command,
|
|
* pCmdBg - Points to the active background command (if any).
|
|
* pCmdFg - Points to the active foreground command (if any).
|
|
* pPendQueue - Points to the queue holding the commands to be executed.
|
|
* pDoneQueue - Points to the queue holding the commands which has been exeuted.
|
|
* pInsertLocation - Reference to command which the newCmd shall be inserted behind.
|
|
*
|
|
* Return: RF_ScheduleStatus - Returning status containing the scheduling decision.
|
|
*/
|
|
static RF_ScheduleStatus RF_howToSchedule(RF_Cmd* newCmd, RF_Cmd* pCmdBg, RF_Cmd* pCmdFg, List_List* pPendQueue, List_List* pDoneQueue, RF_Cmd** pInsertLocation)
|
|
{
|
|
/* By default, reject any new request. */
|
|
volatile RF_ScheduleStatus status = RF_ScheduleStatusError;
|
|
|
|
/* Typecast the arguments to RF commands. */
|
|
RF_Cmd* pHead = (RF_Cmd*)List_head(pPendQueue);
|
|
|
|
/* Load list head as the start point of the iterator. */
|
|
RF_Cmd* it = pHead;
|
|
|
|
/* Step 1 - Check if new command can be inserted based on the timing information
|
|
at the top of the pending queue. */
|
|
if (RF_verifyGap(newCmd, pCmdBg, pHead))
|
|
{
|
|
/* Indicate that the command was put on the top of the queue.ss */
|
|
status = RF_ScheduleStatusTop;
|
|
}
|
|
|
|
/* Step 2 - Check if new command can be inserted based on the timing information
|
|
in the middle/end of the pending queue. This require the new command
|
|
to have an ABSOLUTE startTrigger type. */
|
|
if ((status == RF_ScheduleStatusError) && (newCmd->pOp->startTrigger.triggerType == TRIG_ABSTIME))
|
|
{
|
|
/* Walk the queue.*/
|
|
while (it)
|
|
{
|
|
/* Check if we can insert the new command in between. */
|
|
if (RF_verifyGap(newCmd, it, (RF_Cmd*)List_next((List_Elem*)it)))
|
|
{
|
|
/* Set the return value that the new command should be
|
|
inserted in between it and it->pNext. */
|
|
status = RF_ScheduleStatusMiddle;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
it = (RF_Cmd*)List_next((List_Elem*)it);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Step 3 - If step 1) or 2) fails, reject or append the command to the end of the queue
|
|
based on the allowDelay argument of RF_scheduleCmd() API call. */
|
|
if ((status == RF_ScheduleStatusError) && (newCmd->allowDelay))
|
|
{
|
|
status = RF_ScheduleStatusTail;
|
|
}
|
|
|
|
/* Set pInsertLocation to mark where to insert the new command. */
|
|
*pInsertLocation = it;
|
|
|
|
/* Return with the scheduling method. */
|
|
return(status);
|
|
}
|
|
|
|
/**
|
|
* Sorts and adds command to the RF driver internal command queue.
|
|
*
|
|
* Input: pCmdNew - Pointer to the command to be submitted.
|
|
* pCmdBg - Running background command.
|
|
* pCmdFg - Running foreground command.
|
|
* pPendQueue - Pointer to the head structure of pend queue.
|
|
* pDoneQueue - Pointer to the head structure of done queue.
|
|
* Return: RF_ScheduleStatus - Identifies the success or failure of enquing.
|
|
*/
|
|
RF_ScheduleStatus RF_defaultSubmitPolicy(RF_Cmd* pCmdNew, RF_Cmd* pCmdBg, RF_Cmd* pCmdFg, List_List* pPendQueue, List_List* pDoneQueue)
|
|
{
|
|
/* Local pointer to a command which will be used if insertition
|
|
is selected as a method. */
|
|
RF_Cmd* pInsertLocation = NULL;
|
|
|
|
/* Check the method how the new command should be scheduled. */
|
|
RF_ScheduleStatus status = RF_howToSchedule(pCmdNew, pCmdBg, pCmdFg, pPendQueue, pDoneQueue, &pInsertLocation);
|
|
|
|
/* Step 1 - If the new command is placed to the top of the pend queue. */
|
|
if (status == RF_ScheduleStatusTop)
|
|
{
|
|
/* Insert command at the beginning of the queue */
|
|
List_putHead(pPendQueue, (List_Elem*)pCmdNew);
|
|
}
|
|
|
|
/* Step 2 - If the new command is inserted behind a particular command. */
|
|
if (status == RF_ScheduleStatusMiddle)
|
|
{
|
|
/* Insert command between pInsertLocation and pInsertLocation->pNext. */
|
|
if (List_next((List_Elem*)pInsertLocation))
|
|
{
|
|
/* Insert command before pInsertLocation->next. */
|
|
List_insert(pPendQueue, (List_Elem*)pCmdNew, List_next((List_Elem*)pInsertLocation));
|
|
}
|
|
else
|
|
{
|
|
/* Append command to the end of the queue (if next does not exist). */
|
|
List_put(pPendQueue, (List_Elem*)pCmdNew);
|
|
}
|
|
}
|
|
|
|
/* Step 3 - Append command to the end of the queue. */
|
|
if (status == RF_ScheduleStatusTail)
|
|
{
|
|
List_put(pPendQueue, (List_Elem*)pCmdNew);
|
|
}
|
|
|
|
/* Return command with the method we used to schedule the command.
|
|
Might be RF_ScheduleStatusError if none of the above rules applied. */
|
|
return(status);
|
|
}
|
|
|
|
/**
|
|
* Sorts and adds command to the RF driver internal command queue.
|
|
*
|
|
* Input: pCmdBg - Running background command.
|
|
* pCmdFg - Running foreground command.
|
|
* pPendQueue - Pointer to the head structure of pend queue.
|
|
* pDoneQueue - Pointer to the head structure of done queue.
|
|
* Return: RF_ScheduleStatus - Identifies the success or failure of enquing.
|
|
*/
|
|
RF_Conflict RF_defaultConflictPolicy(RF_Cmd* pCmdBg, RF_Cmd* pCmdFg, List_List* pPendQueue, List_List* pDoneQueue)
|
|
{
|
|
return(RF_ConflictNone);
|
|
}
|
|
|
|
/*
|
|
* Execute RF power down sequence.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_corePowerDown(void)
|
|
{
|
|
/* Local variables to calculate active time in current window. */
|
|
uint32_t deltaTimeInUs = 0;
|
|
|
|
/* Disable all CPE and HW interrupts as we are about to power down the core.
|
|
Clearing is not important as content will be lost anyway. */
|
|
RFCCpeIntDisable(~0);
|
|
RFCHwIntDisable(~0);
|
|
|
|
/* Remap HWI to the startup function (preparing for next wake up) */
|
|
HwiP_setFunc(&RF_hwiCpe0Obj, RF_hwiCpe0PowerFsm, (uintptr_t)NULL);
|
|
|
|
/* Take wake up timestamp and the current timestamp */
|
|
uint32_t rtcTimestampB = (uint32_t) AONRTCCurrent64BitValueGet();
|
|
|
|
/* Find the radio core active delta time since the last power up. */
|
|
deltaTimeInUs = UDIFF(RF_rtcTimestampA, rtcTimestampB);
|
|
deltaTimeInUs >>= RF_RTC_CONV_TO_US_SHIFT;
|
|
|
|
/* Accumulate the delta time with the previous active time windows. Avoid overflow. */
|
|
RF_core.activeTimeUs = ADD(RF_core.activeTimeUs, deltaTimeInUs);
|
|
|
|
/* Decide whether to send the CMD_SYNC_STOP_RAT command. If this is first power down (.init) or active time (activeTimeInUs)
|
|
is longer than the time that can cause maximum allowed error between RAT and RTC clocks. Yielding will automatically fulfill
|
|
the latter. */
|
|
if (!(RF_core.init) ||
|
|
(RF_core.activeTimeUs > (RF_errTolValInUs << RF_DEFAULT_COMB_XTAL_DRIFT_BITS_SHIFT)))
|
|
{
|
|
/* Stop and synchronize the RAT if it is running */
|
|
if (RF_ratIsRunning())
|
|
{
|
|
/* Setup RAT_SYNC command to follow powerdown. */
|
|
RF_ratSyncCmd.stop.commandNo = CMD_SYNC_STOP_RAT;
|
|
RF_ratSyncCmd.stop.status = IDLE;
|
|
RF_ratSyncCmd.stop.condition.rule = COND_NEVER;
|
|
RF_ratSyncCmd.stop.startTrigger.triggerType = TRIG_NOW;
|
|
RF_ratSyncCmd.stop.pNextOp = NULL;
|
|
|
|
/* Send RAT Stop command and synchronously wait until it run. */
|
|
RF_dbellSubmitCmdAsync((uint32_t)&RF_ratSyncCmd.stop);
|
|
while (!(RF_ratSyncCmd.stop.status & RF_CMD_TERMINATED));
|
|
}
|
|
|
|
/* The RF core is now initialized and RAT is synchronized. */
|
|
RF_core.init = true;
|
|
RF_core.activeTimeUs = 0;
|
|
}
|
|
|
|
/* Turn off Synth */
|
|
RFCSynthPowerDown();
|
|
|
|
/* Turn off the RF core by gating its clock. This is a quick way to have it shut off. */
|
|
RFCClockDisable();
|
|
}
|
|
|
|
/*-------------- Power constraints internal functions ------------------------*/
|
|
|
|
/*
|
|
* Set RF power constraints.
|
|
*
|
|
* Input: src - RF_PowerConstraintSrc (Source: Queue or RAT)
|
|
* Return: none
|
|
*/
|
|
void RF_powerConstraintSet(RF_PowerConstraintSrc src)
|
|
{
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Set constraint based on source */
|
|
RF_powerConstraint |= src;
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/*
|
|
* Release RF power constraints.
|
|
*
|
|
* Input: src - RF_PowerConstraintSrc (Source: Queue or RAT)
|
|
* Return: none
|
|
*/
|
|
void RF_powerConstraintRelease(RF_PowerConstraintSrc src)
|
|
{
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Release this constraint. */
|
|
RF_powerConstraint &= ~src;
|
|
|
|
/* Check if all constraints are clear. */
|
|
if (!(RF_powerConstraint & RF_PowerConstraintCmdQ))
|
|
{
|
|
/* Initiate power down if the above criterion is met.
|
|
The RAT timer though might will prevent us to proceed. */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventPowerDown);
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/*
|
|
* Get RF power constraints.
|
|
*
|
|
* Input: src - Mask of constraints we requesting
|
|
* Return: Bitwise-OR of the power constraints set and the input argument
|
|
*/
|
|
RF_PowerConstraintSrc RF_powerConstraintGet(RF_PowerConstraintSrc src)
|
|
{
|
|
/* Set constraint based on source */
|
|
return((RF_PowerConstraintSrc)(RF_powerConstraint & (uint8_t)src));
|
|
}
|
|
|
|
/*
|
|
* It calculates and returns the closest RF event in time if any.
|
|
*
|
|
* Calling context: Hwi, Swi
|
|
*
|
|
* Input: dispatchTime - pointer to a container where the calculated time can be returned
|
|
* Return: ticks - If command is far away in future.
|
|
* 0 - If command is too close and should be scheduled now.
|
|
*/
|
|
static bool RF_calculateDispatchTime(uint32_t* dispatchTimeClockTicks)
|
|
{
|
|
/* Local variables. */
|
|
uint32_t deltaTimeCmdClockTicks;
|
|
uint32_t deltaTimeRatClockTicks;
|
|
|
|
/* Initialize return value. */
|
|
*dispatchTimeClockTicks = 0;
|
|
|
|
/* Calculate the timestamp of the next command in the command queue. */
|
|
bool validCmdTime = RF_cmdDispatchTime(&deltaTimeCmdClockTicks, false, NULL);
|
|
|
|
/* If any of the RAT timers expire before the command should be dispatched,
|
|
reprogram the power up clock to the RAT event instead. */
|
|
bool validRatTime = RF_ratDispatchTime(&deltaTimeRatClockTicks);
|
|
|
|
if (validCmdTime && validRatTime)
|
|
{
|
|
/* Determine if command execution or RAT event comes first. */
|
|
*dispatchTimeClockTicks = MIN(deltaTimeCmdClockTicks, deltaTimeRatClockTicks);
|
|
}
|
|
else if (validCmdTime)
|
|
{
|
|
/* Command queue determines the next event. */
|
|
*dispatchTimeClockTicks = deltaTimeCmdClockTicks;
|
|
}
|
|
else if (validRatTime)
|
|
{
|
|
/* RAT timer determines the next event. */
|
|
*dispatchTimeClockTicks = deltaTimeRatClockTicks;
|
|
}
|
|
|
|
/* If any of them valid, return with true indicating a valid dispatch time. */
|
|
return(validCmdTime || validRatTime);
|
|
}
|
|
|
|
/*
|
|
* Dispatch the closest event generated either by a command or the RAT timer.
|
|
* If the RF core is powered, it triggs the HWI to execute the dispatcher.
|
|
* If the RF core is not powered, it decides if it should be powered ON immediately, or
|
|
* the execution can be deferred to a later timestamp. In the latter case, the RTC is used to keep
|
|
* track of proper timing.
|
|
*
|
|
* Input: none
|
|
* Return: status - Status of the command execution.
|
|
*
|
|
*/
|
|
static void RF_dispatchNextEvent(void)
|
|
{
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
/* Kick the HWI to dispatch the next pending event. */
|
|
HwiP_post(INT_RFC_CPE_0);
|
|
}
|
|
else if ((RF_core.status == RF_CoreStatusPoweringUp) ||
|
|
(RF_core.status == RF_CoreStatusPhySwitching))
|
|
{
|
|
/* Do nothing. We will dispatch the next event at the end
|
|
of power-up/phy-switching sequence naturally. */
|
|
}
|
|
else
|
|
{
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Calculate dispatch time. */
|
|
uint32_t dispatchTimeClockTicks;
|
|
bool validTime = RF_calculateDispatchTime(&dispatchTimeClockTicks);
|
|
|
|
if (validTime)
|
|
{
|
|
/* Decide whether the command should be dispatched. */
|
|
if (dispatchTimeClockTicks)
|
|
{
|
|
/* Dispatch command in the future. */
|
|
RF_restartClockTimeout(&RF_clkPowerUpObj, dispatchTimeClockTicks);
|
|
}
|
|
else
|
|
{
|
|
/* Dispatch the event immediately. Clock is not needed anymore. */
|
|
ClockP_stop(&RF_clkPowerUpObj);
|
|
|
|
/* Initiate powering up the RF core. */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventWakeup);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* There is no event to be dispatched. */
|
|
ClockP_stop(&RF_clkPowerUpObj);
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the cached FS command within the client's context.
|
|
*
|
|
* Calling context: Hwi, Swi
|
|
*
|
|
* Input: pCmd - Pointer to radio operation command.
|
|
* Return: none
|
|
*/
|
|
static void RF_cacheFsCmd(RF_Cmd* pCmd)
|
|
{
|
|
/* Upper limit of the number of operations in a chain */
|
|
uint8_t nCmdChainMax = RF_MAX_CHAIN_CMD_LEN;
|
|
|
|
/* Traverse the chain */
|
|
RF_Op* pOp = pCmd->pOp;
|
|
while (pOp && nCmdChainMax)
|
|
{
|
|
/* If the operation is a CMD_FS or CMD_FS_OFF */
|
|
if ((pOp->commandNo == CMD_FS) || (pOp->commandNo == CMD_FS_OFF))
|
|
{
|
|
/* Create a copy of the first CMD_FS command (or CMD_FS_OFF) for later power up */
|
|
memcpy(&pCmd->pClient->state.mode_state.cmdFs, pOp, sizeof(pCmd->pClient->state.mode_state.cmdFs));
|
|
break;
|
|
}
|
|
|
|
/* Step the chain */
|
|
pOp = pOp->pNextOp;
|
|
|
|
/* Avoid infinit loop (in case of closed loops) */
|
|
--nCmdChainMax;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the last radio operation within a chain.
|
|
*
|
|
* Calling context: Task, Hwi, Swi
|
|
*
|
|
* Input: pOp - Pointer to the first radio operation.
|
|
* Return: RF_Op* - Pointer to the last radio operation.
|
|
*/
|
|
static RF_Op* RF_findEndOfChain(RF_Op* pOp)
|
|
{
|
|
/* Upper limit of the number of operations in a chain. */
|
|
uint8_t nCmdChainMax = RF_MAX_CHAIN_CMD_LEN;
|
|
|
|
/* Traverse the chain. */
|
|
while (pOp->pNextOp && nCmdChainMax)
|
|
{
|
|
/* Step the chain. */
|
|
pOp = pOp->pNextOp;
|
|
|
|
/* Avoid infinit loop (in case of closed loops). */
|
|
--nCmdChainMax;
|
|
}
|
|
|
|
/* Return with the last radio operation. */
|
|
return(pOp);
|
|
}
|
|
|
|
/*
|
|
* Verify if the given command is a setup command.
|
|
*
|
|
* Calling context: Hwi, Swi
|
|
*
|
|
* Input: pOp - Pointer to radio operation.
|
|
* Return: true - The given command is a setup command.
|
|
* false - The given command is not a setup command.
|
|
*/
|
|
static bool RF_isRadioSetup(RF_Op* pOp)
|
|
{
|
|
/* Verify the command ID against the known setup commands. */
|
|
switch(pOp->commandNo)
|
|
{
|
|
case (CMD_RADIO_SETUP):
|
|
case (CMD_BLE5_RADIO_SETUP):
|
|
case (CMD_PROP_RADIO_SETUP):
|
|
case (CMD_PROP_RADIO_DIV_SETUP):
|
|
/* The given radio operation is indead a setup command. */
|
|
return(true);
|
|
default:
|
|
/* Do nothing. */
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Ensure that the setup command is properly initialized.
|
|
*
|
|
* Input: handle - Pointer to the client.
|
|
* Return: None
|
|
*/
|
|
static void RF_initRadioSetup(RF_Handle handle)
|
|
{
|
|
/* Local variables. */
|
|
uint16_t* pTxPower = NULL;
|
|
uint32_t* pRegOverride = NULL;
|
|
uint32_t* pRegOverrideTxStd = NULL;
|
|
uint32_t* pRegOverrideTx20 = NULL;
|
|
bool tx20FeatureAvailable = false;
|
|
bool update = handle->clientConfig.bUpdateSetup;
|
|
bool hposcAvailable = OSC_IsHPOSCEnabled();
|
|
|
|
/* Decode the setup command. */
|
|
RF_RadioSetup* radioSetup = handle->clientConfig.pRadioSetup;
|
|
radioSetup->common.status = IDLE;
|
|
|
|
/* Adjust the setup command if needed. */
|
|
switch (radioSetup->common.commandNo)
|
|
{
|
|
case (CMD_RADIO_SETUP):
|
|
case (CMD_BLE5_RADIO_SETUP):
|
|
/* Configure that the frequency synthetizer should be powered up */
|
|
radioSetup->common.config.bNoFsPowerUp = 0;
|
|
|
|
/* If requested, also change the analog configuration during power up */
|
|
if (update)
|
|
{
|
|
radioSetup->common.config.analogCfgMode = RF_SETUP_ANALOGCFG_UPDATE;
|
|
}
|
|
else
|
|
{
|
|
radioSetup->common.config.analogCfgMode = RF_SETUP_ANALOGCFG_NOUPDATE;
|
|
}
|
|
break;
|
|
|
|
case (CMD_PROP_RADIO_SETUP):
|
|
case (CMD_PROP_RADIO_DIV_SETUP):
|
|
/* Configure that the frequency synthetizer should be powered ON */
|
|
radioSetup->prop.config.bNoFsPowerUp = 0;
|
|
|
|
/* If requested, also change the analog configuration during power up */
|
|
if (update)
|
|
{
|
|
radioSetup->prop.config.analogCfgMode = RF_SETUP_ANALOGCFG_UPDATE;
|
|
}
|
|
else
|
|
{
|
|
radioSetup->prop.config.analogCfgMode = RF_SETUP_ANALOGCFG_NOUPDATE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Clear the update request flag as it was handled by now. */
|
|
handle->clientConfig.bUpdateSetup = false;
|
|
|
|
/* Decode if High Gain PA is available. */
|
|
tx20FeatureAvailable = RF_decodeOverridePointers(radioSetup, &pTxPower, &pRegOverride, &pRegOverrideTxStd, &pRegOverrideTx20);
|
|
|
|
/* Ensure that overrides are in sync with the selected PA. */
|
|
if (tx20FeatureAvailable && (*pTxPower == RF_TX20_ENABLED))
|
|
{
|
|
/* Attach the High Gain overrides. It does nothing if the extra overrides are NULL. */
|
|
RF_attachOverrides(pRegOverride, pRegOverrideTx20);
|
|
}
|
|
else
|
|
{
|
|
/* Detach the High Gain overrides. It does nothing if it is not present. */
|
|
RF_detachOverrides(pRegOverride, pRegOverrideTx20);
|
|
}
|
|
|
|
/* Compensate HPOSC drift if HPOSC functionality is enabled. */
|
|
if(hposcAvailable==true)
|
|
{
|
|
RF_updateHpOscOverride(pRegOverride);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Submit the pending command to the RF Core.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_dispatchNextCmd(void)
|
|
{
|
|
/* First element in the pend queue */
|
|
bool doDispatchNow = false;
|
|
RF_Cmd* pNextCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
|
|
/* Decide whether to schedule the next command or not. */
|
|
if (pNextCmd)
|
|
{
|
|
if (RF_cmdQ.pCurrCmdFg)
|
|
{
|
|
; /* Do nothing. */
|
|
}
|
|
else if (RF_cmdQ.pCurrCmdBg)
|
|
{
|
|
if ((RF_cmdQ.pCurrCmdBg->pClient == pNextCmd->pClient)
|
|
&& (pNextCmd->flags & RF_CMD_FG_CMD_FLAG))
|
|
{
|
|
/* Be sure that the background command is started within the RF core.
|
|
This is to avoid race condition. */
|
|
while ((RF_cmdQ.pCurrCmdBg->pOp->status == IDLE) ||
|
|
(RF_cmdQ.pCurrCmdBg->pOp->status == PENDING));
|
|
|
|
/* Try to execute the foreground command. */
|
|
doDispatchNow = true;
|
|
}
|
|
else
|
|
{
|
|
/* The command which we calculated the remained timing upon. It is the
|
|
first command having an absolute start time, and can locate anywhere
|
|
in the queue. */
|
|
RF_Cmd* pAbsCmd = NULL;
|
|
|
|
/* Calculate the timestamp of the next command in the command queue. */
|
|
uint32_t dispatchTimeClockTicks;
|
|
bool validTime = RF_cmdDispatchTime(&dispatchTimeClockTicks, true, &pAbsCmd);
|
|
|
|
if (validTime)
|
|
{
|
|
/* There is a conflict identified with the running command. */
|
|
if (dispatchTimeClockTicks == 0)
|
|
{
|
|
/* Invoke the registered hook to resolve the conflict. */
|
|
if(RFCC26XX_schedulerPolicy.conflictHook)
|
|
{
|
|
/* Invoke the conflit hook to determine what action we shall take. */
|
|
RF_Conflict conflict =
|
|
RFCC26XX_schedulerPolicy.conflictHook(RF_cmdQ.pCurrCmdBg,
|
|
RF_cmdQ.pCurrCmdFg,
|
|
&RF_cmdQ.pPend,
|
|
&RF_cmdQ.pDone);
|
|
|
|
/* Handle the conflict. */
|
|
if (conflict == RF_ConflictAbort)
|
|
{
|
|
RF_abortCmd(RF_cmdQ.pCurrCmdBg->pClient, RF_cmdQ.pCurrCmdBg->ch, false, true, true);
|
|
}
|
|
else if (conflict == RF_ConflictReject)
|
|
{
|
|
RF_abortCmd(pAbsCmd->pClient, pAbsCmd->ch, false, false, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The conflict is in the future, and might resolve naturarly.
|
|
Revisit the issue again before the execution should start. */
|
|
RF_restartClockTimeout(&RF_clkPowerUpObj, dispatchTimeClockTicks);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The RF core is available, dispatch the next command. */
|
|
doDispatchNow = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* There is nothing to do, serve the last callbacks. */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventLastCommandDone);
|
|
}
|
|
|
|
/* We need to evaluate and handle the next command. */
|
|
if (doDispatchNow)
|
|
{
|
|
if (pNextCmd->pClient != RF_currClient)
|
|
{
|
|
/* We need to change radio client, signal to FSM. */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventInitChangePhy);
|
|
}
|
|
else
|
|
{
|
|
/* Calculate the timestamp of the next command in the command queue. */
|
|
uint32_t dispatchTimeClockTicks;
|
|
bool validTime = RF_cmdDispatchTime(&dispatchTimeClockTicks, false, NULL);
|
|
|
|
/* Dispatch command in the future */
|
|
if (validTime && dispatchTimeClockTicks && !RF_cmdQ.pCurrCmdBg && !RF_cmdQ.pCurrCmdFg)
|
|
{
|
|
/* Command sufficiently far into future that it shouldn't be dispatched yet
|
|
Release RF power constraint and potentially power down radio */
|
|
RF_powerConstraintRelease(RF_PowerConstraintCmdQ);
|
|
}
|
|
else
|
|
{
|
|
/* Set power constraint on the command queue, since there is now a running command. */
|
|
RF_powerConstraintSet(RF_PowerConstraintCmdQ);
|
|
|
|
/* Move the command from the pending queue to the current command. */
|
|
if (pNextCmd->flags & RF_CMD_FG_CMD_FLAG)
|
|
{
|
|
RF_cmdQ.pCurrCmdFg = (RF_Cmd*)List_get(&RF_cmdQ.pPend);
|
|
}
|
|
else
|
|
{
|
|
RF_cmdQ.pCurrCmdBg = (RF_Cmd*)List_get(&RF_cmdQ.pPend);
|
|
}
|
|
|
|
/* Clear and enable the requested interrupt sources of the command. */
|
|
RFCCpeIntClear((uint32_t) (pNextCmd->bmEvent));
|
|
RFCCpeIntEnable((uint32_t)(pNextCmd->bmEvent));
|
|
RFCHwIntClear((uint32_t) (pNextCmd->bmEvent >> RF_SHIFT_32_BITS));
|
|
RFCHwIntEnable((uint32_t) (pNextCmd->bmEvent >> RF_SHIFT_32_BITS));
|
|
|
|
/* Decode the radio operation itself. */
|
|
RF_Op* pOp = (RF_Op*)pNextCmd->pOp;
|
|
|
|
/* Send the radio operation to the RF core. */
|
|
RFCDoorbellSendTo((uint32_t)pOp);
|
|
|
|
/* If the command is a new setup command, notify the board file. */
|
|
if (RF_isRadioSetup(pOp))
|
|
{
|
|
/* Invoke the global callback if the setup command changed. This is needed to
|
|
adjust the front-end configuration according to the new PHY. */
|
|
RF_invokeGlobalCallback(RF_GlobalEventRadioSetup, (void*)pOp);
|
|
}
|
|
|
|
/* Check the pending queue for any foreground command (IEEE 15.4 mode) */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventRunScheduler);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if there was an error with the synth while running CMD_FS
|
|
* error callback is not issued in this function.
|
|
*
|
|
* Input: none
|
|
* Return: true - If there was an error.
|
|
* false - If there was no error.
|
|
*/
|
|
static bool RF_checkCmdFsError(void)
|
|
{
|
|
/* Take the handle of the current client */
|
|
RF_Handle pObj = RF_currClient;
|
|
|
|
/* Find the FS command stored in the context of the client */
|
|
RF_Op *tmp1 = (RF_Op*)&pObj->clientConfig.pRadioSetup->prop;
|
|
while (tmp1->pNextOp && tmp1->pNextOp != (RF_Op*)&pObj->state.mode_state.cmdFs)
|
|
{
|
|
tmp1 = tmp1->pNextOp;
|
|
}
|
|
|
|
/* Evaluate if the FS command succeeded */
|
|
if ((tmp1->condition.rule == COND_ALWAYS) &&
|
|
(pObj->state.mode_state.cmdFs.status == ERROR_SYNTH_PROG))
|
|
{
|
|
/* CMD_FS completed with error so return true */
|
|
return(true);
|
|
}
|
|
else
|
|
{
|
|
/* There is no synth error so return false */
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF HW ISR when radio is active.
|
|
*
|
|
* Input: a - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_hwiHw(uintptr_t a)
|
|
{
|
|
/* Prepare a direct command */
|
|
RF_Cmd* pCmd = RF_cmdQ.pCurrCmdBg;
|
|
|
|
/* Read and clear the interrupt flags */
|
|
uint32_t rfchwifg = RFCHwIntGetAndClear(RF_HW_INT_CPE_MASK | RF_HW_INT_RAT_CH_MASK);
|
|
uint32_t rfchwien = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFHWIEN) & RF_HW_INT_CPE_MASK;
|
|
uint32_t rathwien = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFHWIEN) & RF_HW_INT_RAT_CH_MASK;
|
|
|
|
if (rfchwifg & rfchwien)
|
|
{
|
|
/* Post SWI_FSM if MODEM_SOFT event occured and the interrupt was enabled */
|
|
if (pCmd)
|
|
{
|
|
/* Store the command which callback need to be served */
|
|
RF_cmdQ.pCurrCmdCb = pCmd;
|
|
|
|
/* Decode the event numeber. */
|
|
RF_EventMask events = ((RF_EventMask)(rfchwifg & rfchwien) << RF_SHIFT_32_BITS);
|
|
|
|
/* Store the events within the context of the command for the callback. */
|
|
RF_cmdStoreEvents(pCmd, events);
|
|
|
|
/* Trig the state machine to handle this event */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventCpeInt);
|
|
}
|
|
}
|
|
|
|
/* Post the SWI_HW if any RAT channel event occured */
|
|
if (rfchwifg & rathwien)
|
|
{
|
|
/* Store the channel which cause the interrupt */
|
|
RF_ratModule.pendingInt |= (rfchwifg & rathwien) >> RFC_DBELL_RFHWIFG_RATCH5_BITN;
|
|
|
|
/* Post the swi to handle its callback */
|
|
SwiP_or(&RF_swiHwObj, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Software interrupt handler which servers Radio Timer (RAT) related events.
|
|
*
|
|
* Input: a - Generic argument. Not used.
|
|
* b - Generic argument. Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_swiHw(uintptr_t a, uintptr_t b)
|
|
{
|
|
/* Local variable */
|
|
bool error = false;
|
|
|
|
/* If the interrupt was trigged due to one of the RAT channels. */
|
|
if (RF_ratModule.pendingInt)
|
|
{
|
|
/* Process lower channel first and allow multiple interrupt flags to be processed sequentially. */
|
|
uint32_t i;
|
|
for(i = 0; i < RF_RAT_CH_CNT; i++)
|
|
{
|
|
if (RF_ratModule.pendingInt & (RF_RAT_INTERRUPT_BASE_INDEX << i))
|
|
{
|
|
/* If there is also a bit indicating that the interrupt is due to an error. */
|
|
if (RF_ratModule.pendingInt & (RF_RAT_ERROR_BASE_INDEX << i))
|
|
{
|
|
error = true;
|
|
}
|
|
|
|
/* Enter critical section. */
|
|
uint32_t key= HwiP_disable();
|
|
|
|
/* Atomic read-modify-write instruction of the interrupt flags.
|
|
Knowing that this is the only place when such a flag can be cleared, it is safe to only guard this
|
|
operation. Additional flags (which have been raised in the meantime) will be reserved and served in the
|
|
next iteration. */
|
|
RF_ratModule.pendingInt &= ~((RF_RAT_INTERRUPT_BASE_INDEX | RF_RAT_ERROR_BASE_INDEX) << i);
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Convert the channel index to a pointer of rat configuration. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(i);
|
|
|
|
/* Serve the interrupt if it is from an active channel. This is to avoid decoding function
|
|
pointers from invalid containers due to fantom interrupts. */
|
|
if (ratCh && ratCh->status)
|
|
{
|
|
/* Read the channel counter from the RAT timer. In capture mode this is the captured value,
|
|
in compare mode this is the compare timestamp.*/
|
|
uint32_t compareCaptureValue = RF_ratGetChannelValue(ratCh->handle);
|
|
|
|
/* Temporarily store the callback handler and the channel offset.
|
|
This is necessary in order to be able to free and reallocate the
|
|
same channel within the context of the callback itself. */
|
|
RF_Handle ratClient = (RF_Handle) ratCh->pClient;
|
|
RF_RatHandle ratHandle = (RF_CmdHandle) ratCh->handle;
|
|
RF_RatCallback ratCallback = (RF_RatCallback) ratCh->pCb;
|
|
|
|
/* Only free the channel if it is NOT in repeated capture mode, or an error occured. */
|
|
if (error || !(ratCh->mode == RF_RatModeCapture) || !(ratCh->chCmd & RF_RAT_CAPTURE_REPEAT_MODE))
|
|
{
|
|
/* Free RAT channel. If this is the last channel, it might delay with 1 LF edge to
|
|
calculate the next wake up event. */
|
|
RF_ratFreeChannel(ratCh);
|
|
}
|
|
|
|
/* Serve the user callback with Error or Compare/Capture Event. */
|
|
if (error)
|
|
{
|
|
ratCallback(ratClient, ratHandle, RF_EventError, 0);
|
|
}
|
|
else
|
|
{
|
|
ratCallback(ratClient, ratHandle, RF_EventRatCh, compareCaptureValue);
|
|
}
|
|
}
|
|
|
|
/* Only serve one channel at a time. */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Repost the SWI again if multiple interrupt flags are still set. */
|
|
if (RF_ratModule.pendingInt)
|
|
{
|
|
SwiP_or(&RF_swiHwObj, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF CPE0 ISR when radio is active. Assume that all IRQs relevant to command
|
|
* dispatcher are mapped here. Furthermore, assume that there is no need for
|
|
* critical sections here (i.e. that this ISR has higher priority than
|
|
* any HWI calling a RF API function or that HWIs can't call the RF API).
|
|
*
|
|
* Input: a - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_hwiCpe0Active(uintptr_t a)
|
|
{
|
|
/* Local variables. */
|
|
RF_Cmd* volatile* ppActiveCmd = NULL;
|
|
RF_Cmd* volatile* activeCmd[2] = {&RF_cmdQ.pCurrCmdBg, &RF_cmdQ.pCurrCmdFg};
|
|
uint32_t rfcpeifgMask = 0;
|
|
uint32_t rfcpeifg = 0;
|
|
uint32_t nextEvent = 0;
|
|
|
|
/* Handle PA switching. */
|
|
if (RFCCpeIntGetAndClear(RF_EventPaChanged))
|
|
{
|
|
/* The PA was changed during a chain of radio operation. We need to extract the current configuration
|
|
and propagate it back to the setup command. This is to reserve the change after power cycle. */
|
|
RF_extractPaConfiguration(RF_currClient);
|
|
|
|
/* Invoke the board file to reconfigure the external front-end configuration. */
|
|
RF_invokeGlobalCallback(RF_GlobalEventRadioSetup, (void*) RF_currClient->clientConfig.pRadioSetup);
|
|
}
|
|
|
|
/* Iterate through the possible active commands. */
|
|
uint32_t i;
|
|
for(i = 0; i < sizeof(activeCmd)/sizeof(uint32_t); i++)
|
|
{
|
|
/* Decode the active command. */
|
|
ppActiveCmd = activeCmd[i];
|
|
|
|
/* If there was a command running (handles both foreground and background context). */
|
|
if (*ppActiveCmd)
|
|
{
|
|
/* Decode the events the active command subscribed to. */
|
|
rfcpeifgMask = (*ppActiveCmd)->bmEvent;
|
|
|
|
/* Read the interrupt flags which belong to the active command (including the mandatory termination events). */
|
|
rfcpeifg = RFCCpeIntGetAndClear(rfcpeifgMask);
|
|
|
|
/* Save the events happened and to be passed to the callback. */
|
|
RF_cmdStoreEvents((*ppActiveCmd), rfcpeifg);
|
|
|
|
/* Look for termination events. */
|
|
if (rfcpeifg & (RFC_DBELL_RFCPEIFG_LAST_FG_COMMAND_DONE_M | RFC_DBELL_RFCPEIFG_LAST_COMMAND_DONE_M))
|
|
{
|
|
/* Disable interrupt sources which were subsribed by the command. Since the LAST_CMD_DONE is
|
|
is shared with the state machine, it cannot be disabled. */
|
|
RFCCpeIntDisable((uint32_t)((*ppActiveCmd)->bmEvent & ~(RFC_DBELL_RFCPEIFG_LAST_COMMAND_DONE_M | RFC_DBELL_RFCPEIEN_IRQ14_M)));
|
|
RFCHwIntDisable((uint32_t) ((*ppActiveCmd)->bmEvent >> RF_SHIFT_32_BITS));
|
|
|
|
/* Move active command to done queue. */
|
|
List_put(&RF_cmdQ.pDone, (List_Elem*)(*ppActiveCmd));
|
|
|
|
/* Retire the command, it is not running anymore. */
|
|
(*ppActiveCmd) = NULL;
|
|
|
|
/* We will invoke the callback and deallocate the command. */
|
|
nextEvent |= RF_FsmEventLastCommandDone;
|
|
}
|
|
else if (rfcpeifg)
|
|
{
|
|
/* The interrupt is just an ordinary event without termination. */
|
|
RF_cmdQ.pCurrCmdCb = (*ppActiveCmd);
|
|
|
|
/* We will just invoke the callback. */
|
|
nextEvent |= RF_FsmEventCpeInt;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Post SWI to handle registered callbacks if there is any. */
|
|
if (nextEvent)
|
|
{
|
|
SwiP_or(&RF_swiFsmObj, nextEvent);
|
|
}
|
|
|
|
/* Restart pending rat channels. */
|
|
RF_ratRestartChannels();
|
|
|
|
/* Dispatch the next pending command if exists. */
|
|
RF_dispatchNextCmd();
|
|
}
|
|
|
|
/*
|
|
* Clock callback due to inactivity timeout.
|
|
*
|
|
* Input: pObj - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_clkInactivityCallback(uintptr_t a)
|
|
{
|
|
/* If there are no pending commands in the queue */
|
|
if (RF_cmdQ.nSeqPost == RF_cmdQ.nSeqDone)
|
|
{
|
|
/* Release the constraint on the command queue and if nothing prevents, power down the radio */
|
|
RF_powerConstraintRelease(RF_PowerConstraintCmdQ);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clock callback due to request access timeout.
|
|
*
|
|
* Input: a - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_clkReqAccess(uintptr_t a)
|
|
{
|
|
RF_issueRadioFreeCb(RF_RADIOFREECB_REQACCESS_FLAG |
|
|
RF_RADIOFREECB_PREEMPT_FLAG |
|
|
RF_RADIOFREECB_CMDREJECT_FLAG);
|
|
}
|
|
|
|
/*
|
|
* Callback used to post semaphore for runCmd() and pendCmd().
|
|
*
|
|
* Input: h - Handle to the client.
|
|
* ch - Handle to the command which callback to be invoked.
|
|
* e - Events causing the function call.
|
|
* Return: none
|
|
*/
|
|
static void RF_syncCb(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
|
|
{
|
|
/* Local variables */
|
|
RF_Cmd* pCmd;
|
|
|
|
/* If there is a user callback provided. */
|
|
if (h->state.pCbSync)
|
|
{
|
|
/* Invoke the user callback with the events fired. */
|
|
((RF_Callback)h->state.pCbSync)(h, ch, e);
|
|
}
|
|
|
|
/* Mask the possible causes of releasing the semaphore */
|
|
RF_EventMask maskedEvents = (e & h->state.eventSync);
|
|
|
|
/* Release the semaphore on any of the reasons: last command done,
|
|
subscribed event happened, last FG command is done in IEEE mode */
|
|
if (maskedEvents)
|
|
{
|
|
/* Find the command. We do it here within the SWI context. */
|
|
pCmd = RF_cmdGet(h, ch, RF_CMD_ALLOC_FLAG);
|
|
|
|
/* Store the events in the context of the client */
|
|
h->state.unpendCause = maskedEvents;
|
|
|
|
/* Find the command. We do it here within the SWI context. */
|
|
if (pCmd)
|
|
{
|
|
/* Clear the handled past events so it is possible to pend again */
|
|
pCmd->pastifg &= ~h->state.unpendCause;
|
|
|
|
/* Exhange the callback function: use the user callback from this point */
|
|
pCmd->pCb = (RF_Callback)h->state.pCbSync;
|
|
}
|
|
|
|
/* Clear temporary storage of user callback (it was restored and served at this point) */
|
|
h->state.pCbSync = NULL;
|
|
|
|
/* Post the semaphore to release the RF_pendCmd() */
|
|
SemaphoreP_post(&h->state.semSync);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Invoke the global callback registered through the RFCC26XX_hwAttrs.
|
|
*
|
|
* Input: e - Events causing the function call.
|
|
* Return: none
|
|
*/
|
|
static void RF_invokeGlobalCallback(RF_GlobalEvent event, void* arg)
|
|
{
|
|
/* Decode the global callback and it's mask from the board file. */
|
|
RF_GlobalCallback callback = RFCC26XX_hwAttrs.globalCallback;
|
|
RF_GlobalEventMask eventMask = RFCC26XX_hwAttrs.globalEventMask;
|
|
|
|
/* If the board has subscribed to this event, invoke the callback. */
|
|
if (callback && (eventMask & event))
|
|
{
|
|
callback(RF_currClient, event, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Default callback function.
|
|
*
|
|
* Input: h - Handle to the client.
|
|
* ch - Handle to the command which callback to be invoked.
|
|
* e - Events causing the function call.
|
|
* Return: none
|
|
*/
|
|
static void RF_defaultCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
|
|
{
|
|
/* Do nothing */;
|
|
}
|
|
|
|
/*-------------- RF powerup/powerdown FSM functions ---------------*/
|
|
|
|
/*
|
|
* The SWI handler for FSM events.
|
|
*
|
|
* Input: a0 - Not used.
|
|
* a1 - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_swiFsm(uintptr_t a0, uintptr_t a1)
|
|
{
|
|
RF_core.fxn(RF_currClient, (RF_FsmEvent)SwiP_getTrigger());
|
|
}
|
|
|
|
/*
|
|
* Clock callback called upon powerup.
|
|
*
|
|
* Input: a - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_clkPowerUp(uintptr_t a)
|
|
{
|
|
if (RF_core.fxn == RF_fsmActiveState)
|
|
{
|
|
/* Dispatch the next RF core event. */
|
|
RF_dispatchNextEvent();
|
|
}
|
|
else
|
|
{
|
|
/* Trigger FSM SWI to start the wake up sequence of the radio.
|
|
This is important when we poll the XOSC_HF. */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventWakeup);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF CPE0 ISR during FSM powerup/powerdown.
|
|
*
|
|
* Input: a0 - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_hwiCpe0PowerFsm(uintptr_t a0)
|
|
{
|
|
/* Read all IRQ flags in doorbell and then clear them */
|
|
uint32_t rfcpeifg = RFCCpeIntGetAndClear(RF_CPE0_INT_MASK);
|
|
|
|
/* If the radio is active */
|
|
if (RF_core.fxn == RF_fsmActiveState)
|
|
{
|
|
/* Change HWI handler to the correct one */
|
|
HwiP_setFunc(&RF_hwiCpe0Obj, RF_hwiCpe0Active, (uintptr_t)NULL);
|
|
|
|
/* Mark radio and client as being active */
|
|
RF_core.status = RF_CoreStatusActive;
|
|
|
|
/* No synth error */
|
|
if (!RF_checkCmdFsError())
|
|
{
|
|
/* Restart pending rat channels. */
|
|
RF_ratRestartChannels();
|
|
|
|
/* Dispatch the next command */
|
|
RF_dispatchNextCmd();
|
|
}
|
|
}
|
|
|
|
/* Handle special events as boot, etc */
|
|
if (rfcpeifg & (RFC_DBELL_RFCPEIFG_BOOT_DONE_M | RFC_DBELL_RFCPEIFG_LAST_COMMAND_DONE_M))
|
|
{
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventPowerStep);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF CPE0 ISR during Change PHY switching.
|
|
*
|
|
* Input: a0 - Not used.
|
|
* Return: none
|
|
*/
|
|
static void RF_hwiCpe0ChangePhy(uintptr_t a0)
|
|
{
|
|
/* Clear all IRQ flags in doorbell and then clear them */
|
|
uint32_t rfcpeifg = RFCCpeIntGetAndClear(RF_CPE0_INT_MASK);
|
|
|
|
if (rfcpeifg & IRQ_LAST_COMMAND_DONE)
|
|
{
|
|
/* Proceed to the second phase of the phy switching process */
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventFinishChangePhy);
|
|
}
|
|
}
|
|
|
|
/*-------------- Power management state functions ---------------*/
|
|
/*
|
|
* Handles RF Core patching for CPE, MCE, RFE (if required) in setup state during power-up.
|
|
*
|
|
* Input: mode - RF_PHY_BOOTUP_MODE: First boot of the RF core.
|
|
* - RF_PHY_SWITCHING_MODE: Switching between two phys.
|
|
* Return: none
|
|
*/
|
|
static void RF_applyRfCorePatch(bool mode)
|
|
{
|
|
/* Local reference to the patches. */
|
|
void (*cpePatchFxn)(void) = RF_currClient->clientConfig.pRfMode->cpePatchFxn;
|
|
void (*mcePatchFxn)(void) = RF_currClient->clientConfig.pRfMode->mcePatchFxn;
|
|
void (*rfePatchFxn)(void) = RF_currClient->clientConfig.pRfMode->rfePatchFxn;
|
|
|
|
if (mode == RF_PHY_SWITCHING_MODE)
|
|
{
|
|
/* If patches are provided, enable RFE and MCE clocks. */
|
|
if ((mcePatchFxn != NULL) || (rfePatchFxn != NULL))
|
|
{
|
|
RF_dbellSubmitCmdAsync((uint32_t)CMDR_DIR_CMD_2BYTE(RF_CMD0, RFC_PWR_PWMCLKEN_MDMRAM | RFC_PWR_PWMCLKEN_RFERAM));
|
|
}
|
|
|
|
/* Clear the previous patch. */
|
|
if (cpePatchFxn != NULL)
|
|
{
|
|
RFCCpePatchReset();
|
|
}
|
|
}
|
|
|
|
/* Load the patches if relevant for this phy. */
|
|
if (cpePatchFxn != NULL)
|
|
{
|
|
if (mode == RF_PHY_BOOTUP_MODE)
|
|
{
|
|
cpePatchFxn();
|
|
}
|
|
}
|
|
|
|
if ((mcePatchFxn != NULL) || (rfePatchFxn != NULL))
|
|
{
|
|
/* Wait for clocks to be turned ON */
|
|
RF_dbellSyncOnAck();
|
|
|
|
/* Patch MCE if relevant */
|
|
if (mcePatchFxn != NULL)
|
|
{
|
|
mcePatchFxn();
|
|
}
|
|
|
|
/* Patch RFE if relevant */
|
|
if (rfePatchFxn != NULL)
|
|
{
|
|
rfePatchFxn();
|
|
}
|
|
|
|
/* Turn off additional clocks */
|
|
RFCDoorbellSendTo(CMDR_DIR_CMD_2BYTE(RF_CMD0, 0));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Arms the inactivity timer and hence postpones the decision whether
|
|
* power management shall take place or not.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_setInactivityTimeout(void)
|
|
{
|
|
/* Local variables to be used to find the correct timeout value. */
|
|
uint32_t inactivityTimeUsA = 0;
|
|
uint32_t inactivityTimeUsB = 0;
|
|
RF_Handle handleA = RF_Sch.clientHnd[0];
|
|
RF_Handle handleB = RF_Sch.clientHnd[1];
|
|
|
|
/* Issue radio free callback after pre-emption if required */
|
|
uint8_t tmp = RF_RADIOFREECB_PREEMPT_FLAG | RF_RADIOFREECB_CMDREJECT_FLAG;
|
|
|
|
/* If the radio was yielded, add the flag */
|
|
if (RF_currClient->state.bYielded)
|
|
{
|
|
tmp |= RF_RADIOFREECB_REQACCESS_FLAG;
|
|
}
|
|
|
|
/* Call the radio free callback */
|
|
RF_issueRadioFreeCb(tmp);
|
|
|
|
if (handleA)
|
|
{
|
|
if (handleA->state.bYielded == false)
|
|
{
|
|
inactivityTimeUsA = handleA->clientConfig.nInactivityTimeout;
|
|
}
|
|
handleA->state.bYielded = false;
|
|
}
|
|
|
|
if (handleB)
|
|
{
|
|
if (handleB->state.bYielded == false)
|
|
{
|
|
inactivityTimeUsB = handleB->clientConfig.nInactivityTimeout;
|
|
}
|
|
handleB->state.bYielded = false;
|
|
}
|
|
|
|
/* Set the inactivity time to the max between the two clients */
|
|
uint32_t inactivityTimeUs = MAX(inactivityTimeUsA, inactivityTimeUsB);
|
|
|
|
/* If immediate power down is reuqested */
|
|
if (inactivityTimeUs == SemaphoreP_NO_WAIT)
|
|
{
|
|
/* We can powerdown immediately */
|
|
RF_clkInactivityCallback((uintptr_t)NULL);
|
|
}
|
|
else if (inactivityTimeUs != SemaphoreP_WAIT_FOREVER)
|
|
{
|
|
/* Reprogram and start inactivity timer */
|
|
RF_restartClockTimeout(&RF_clkInactivityObj, inactivityTimeUs/ClockP_tickPeriod);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle callback to client for RF_EventLastCmdDone and issue radio free callback if required.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_radioOpDoneCb(void)
|
|
{
|
|
/* Serve the first entry in the done queue */
|
|
RF_Cmd* pCmd = (RF_Cmd*)List_head(&RF_cmdQ.pDone);
|
|
|
|
/* Radio command done */
|
|
if (pCmd)
|
|
{
|
|
/* Update implicit radio state (chained FS command if any) */
|
|
RF_cacheFsCmd(pCmd);
|
|
|
|
/* Read and clear the events */
|
|
RF_EventMask events = pCmd->rfifg;
|
|
pCmd->rfifg = 0;
|
|
|
|
/* Issue callback, free container and dequeue */
|
|
if (pCmd->pCb)
|
|
{
|
|
/* If any of the cancel events are set, mask out the other events. */
|
|
RF_EventMask exclusiveEvents = (RF_EventCmdCancelled
|
|
| RF_EventCmdAborted
|
|
| RF_EventCmdStopped
|
|
| RF_EventCmdPreempted);
|
|
|
|
/* Mask out the other events if any of the above is set. */
|
|
if (events & exclusiveEvents)
|
|
{
|
|
events &= exclusiveEvents;
|
|
}
|
|
|
|
/* Invoke the use callback */
|
|
pCmd->pCb(pCmd->pClient, pCmd->ch, events);
|
|
}
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Update num of radio command done */
|
|
RF_cmdQ.nSeqDone = (RF_cmdQ.nSeqDone+1) & N_CMD_MODMASK;
|
|
|
|
/* Commmand completed reset command flags */
|
|
pCmd->flags = 0;
|
|
|
|
/* Invalidate the command handle. This is to avoid having duplicate
|
|
handles in the pool. */
|
|
pCmd->ch = RF_SCHEDULE_CMD_ERROR;
|
|
|
|
/* Command completed, free command queue container */
|
|
List_get(&RF_cmdQ.pDone);
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Check if there are any more pending commands */
|
|
if (RF_cmdQ.nSeqDone == RF_cmdQ.nSeqPost)
|
|
{
|
|
RF_setInactivityTimeout();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Verify if reconiguring or powering down the radio is allowed.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static bool RF_isStateTransitionAllowed(void)
|
|
{
|
|
/* Local variable. */
|
|
bool status = false;
|
|
|
|
/* If we are not performing RF core state changes. */
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
if(RF_cmdQ.pCurrCmdBg == NULL &&
|
|
RF_cmdQ.pCurrCmdFg == NULL)
|
|
{
|
|
status = true;
|
|
}
|
|
}
|
|
|
|
/* Return with the decision. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* RF state machine function during power up state.
|
|
*
|
|
* Input: pObj - Pointer to RF object.
|
|
* e - State machine event.
|
|
* Return: none
|
|
*/
|
|
static void RF_fsmPowerUpState(RF_Object *pObj, RF_FsmEvent e)
|
|
{
|
|
/* Note: pObj is NULL in this state */
|
|
if (e & RF_FsmEventLastCommandDone)
|
|
{
|
|
/* Invoke the user provided callback function */
|
|
RF_radioOpDoneCb();
|
|
|
|
/* Retrig the SWI if there are more commands in the done queue. */
|
|
if (List_head(&RF_cmdQ.pDone))
|
|
{
|
|
/* Trigger self if there are more commands in callback queue */
|
|
SwiP_or(&RF_swiFsmObj, (e | RF_FsmEventLastCommandDone));
|
|
}
|
|
else
|
|
{
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventLastCommandDone;
|
|
|
|
/* Schedule the next event based on the state of the command queue
|
|
and the RAT module. */
|
|
RF_dispatchNextEvent();
|
|
}
|
|
}
|
|
else if (e & RF_FsmEventWakeup)
|
|
{
|
|
/* Notify the power driver that FLASH is needed in IDLE */
|
|
bDisableFlashInIdleConstraint = true;
|
|
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
|
|
|
|
/* Store the current RTC tick for nPowerUpDuration calculation */
|
|
RF_rtcTimestampA = AONRTCCurrent64BitValueGet();
|
|
|
|
/* Set current client from first command in command queue */
|
|
RF_Cmd* pNextCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
if (pNextCmd)
|
|
{
|
|
RF_Object* pNextClient = pNextCmd->pClient;
|
|
|
|
/* If the next command belongs to another client, initiate PHY switching */
|
|
if ((RF_currClient) && (RF_currClient != pNextClient))
|
|
{
|
|
/* Invoke the client switch callback if it was provided */
|
|
if (pNextClient->clientConfig.nClientEventMask & RF_ClientEventSwitchClientEntered)
|
|
{
|
|
RF_ClientCallback pClientEventCb = (RF_ClientCallback)pNextClient->clientConfig.pClientEventCb;
|
|
pClientEventCb(pNextClient, RF_ClientEventSwitchClientEntered, NULL);
|
|
}
|
|
|
|
/* Due to client switching, update the analogCfg field of setup command. */
|
|
pNextClient->clientConfig.bUpdateSetup = true;
|
|
}
|
|
|
|
/* Set the current client to be the next client */
|
|
RF_currClient = pNextClient;
|
|
}
|
|
|
|
/* Set the RF mode in the PRCM register (RF_open already verified that it is valid) */
|
|
HWREG(PRCM_BASE + PRCM_O_RFCMODESEL) = RF_currClient->clientConfig.pRfMode->rfMode;
|
|
|
|
/* Notiy the power driver that Standby is not allowed and RF core need to be powered */
|
|
Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
|
|
Power_setDependency(PowerCC26XX_DOMAIN_RFCORE);
|
|
|
|
/* Indicate that the power-up sequence is being started */
|
|
RF_core.status = RF_CoreStatusPoweringUp;
|
|
|
|
/* If the configuration on board level requires to set the dependency every time. */
|
|
if (RFCC26XX_hwAttrs.xoscHfAlwaysNeeded == false)
|
|
{
|
|
Power_setDependency(PowerCC26XX_XOSC_HF);
|
|
}
|
|
|
|
/* If there are RFE and MCE patches, turn on their clocks */
|
|
if ((RF_currClient->clientConfig.pRfMode->mcePatchFxn != NULL) ||
|
|
(RF_currClient->clientConfig.pRfMode->rfePatchFxn != NULL))
|
|
{
|
|
RF_dbellSubmitCmdAsync((uint32_t)CMDR_DIR_CMD_2BYTE(RF_CMD0, RFC_PWR_PWMCLKEN_MDMRAM | RFC_PWR_PWMCLKEN_RFERAM));
|
|
}
|
|
|
|
/* Turn on the clock to the RF core. Registers can be accessed afterwards. */
|
|
RFCClockEnable();
|
|
|
|
/* Reconfigure the CPE interrupt lines to a start up value on a controlled way. */
|
|
RFCCpeIntDisable(RF_CPE0_INT_MASK);
|
|
RFCCpe0IntSelect(RF_CPE0_INT_MASK);
|
|
|
|
/* Enable some of the interrupt sources. */
|
|
RFCCpeIntEnable(RFC_DBELL_RFCPEIEN_BOOT_DONE_M
|
|
| RFC_DBELL_RFCPEIEN_LAST_COMMAND_DONE_M
|
|
| RFC_DBELL_RFCPEIEN_IRQ14_M);
|
|
|
|
/* Set the next state. */
|
|
RF_core.fxn = RF_fsmSetupState;
|
|
|
|
/* Enable interrupts: continue when boot is done */
|
|
HwiP_enableInterrupt(INT_RFC_HW_COMB);
|
|
HwiP_enableInterrupt(INT_RFC_CPE_0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF state machine function during setup state.
|
|
*
|
|
* Input: pObj - Pointer to RF object.
|
|
* e - State machine event.
|
|
* Return: none
|
|
*/
|
|
static void RF_fsmSetupState(RF_Object *pObj, RF_FsmEvent e)
|
|
{
|
|
if (e & RF_FsmEventPowerStep)
|
|
{
|
|
/* Apply RF Core patches (if required) */
|
|
RF_applyRfCorePatch(RF_PHY_BOOTUP_MODE);
|
|
|
|
/* Initialize system bus request */
|
|
RF_dbellSubmitCmdAsync((uint32_t)CMDR_DIR_CMD_1BYTE(CMD_BUS_REQUEST, 1));
|
|
|
|
/* Configure the RAT_SYNC command which will follow SETUP command */
|
|
RF_ratSyncCmd.start.commandNo = CMD_SYNC_START_RAT;
|
|
RF_ratSyncCmd.start.status = IDLE;
|
|
RF_ratSyncCmd.start.startTrigger.triggerType = TRIG_NOW;
|
|
RF_ratSyncCmd.start.pNextOp = NULL;
|
|
RF_ratSyncCmd.start.condition.rule = COND_NEVER;
|
|
|
|
/* Init the content of setup command. */
|
|
RF_initRadioSetup(pObj);
|
|
|
|
/* Configure the SETUP command. */
|
|
RF_RadioSetup* pRadioSetup = pObj->clientConfig.pRadioSetup;
|
|
|
|
/* Search for specific commands in the command chain. */
|
|
RF_Op* tmp = (RF_Op*)&pRadioSetup->prop;
|
|
while ((tmp->pNextOp) && (tmp->pNextOp->commandNo != CMD_SYNC_START_RAT) &&
|
|
(tmp->pNextOp->commandNo != CMD_FS) &&
|
|
(tmp->pNextOp->commandNo != CMD_FS_OFF))
|
|
{
|
|
/* Trace to the end of chain */
|
|
tmp = tmp->pNextOp;
|
|
}
|
|
|
|
/* Add the CMD_RAT_SYNC to the end of chain */
|
|
tmp->pNextOp = (RF_Op*)&RF_ratSyncCmd.start;
|
|
tmp->condition.rule = COND_ALWAYS;
|
|
|
|
/* Setup FS command to follow SETUP command */
|
|
RF_Cmd* pCmdFirstPend = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
if (pCmdFirstPend && ((pCmdFirstPend->pOp->commandNo == CMD_FS) || (pCmdFirstPend->pOp->commandNo == CMD_FS_OFF)))
|
|
{
|
|
/* First command is FS command so no need to chain an implicit FS command -> Reset nRtc1 */
|
|
RF_rtcTimestampA = 0;
|
|
}
|
|
else
|
|
{
|
|
if (pObj->state.mode_state.cmdFs.commandNo)
|
|
{
|
|
/* Chain in the implicit FS command */
|
|
rfc_CMD_FS_t* pOpFs = &pObj->state.mode_state.cmdFs;
|
|
pOpFs->status = IDLE;
|
|
pOpFs->pNextOp = NULL;
|
|
pOpFs->startTrigger.triggerType = TRIG_NOW;
|
|
pOpFs->condition.rule = COND_NEVER;
|
|
RF_ratSyncCmd.start.pNextOp = (RF_Op*)pOpFs;
|
|
RF_ratSyncCmd.start.condition.rule = COND_ALWAYS;
|
|
}
|
|
}
|
|
|
|
/* Make sure system bus request is done by now */
|
|
RF_dbellSyncOnAck();
|
|
|
|
/* Set the next state. */
|
|
RF_core.fxn = RF_fsmActiveState;
|
|
|
|
/* Run the XOSC_HF switching if the pre-notify function setup the power
|
|
constraint PowerCC26XX_SWITCH_XOSC_HF_MANUALLY */
|
|
if (RF_core.manualXoscHfSelect)
|
|
{
|
|
/* Wait until the XOSC_HF is stable */
|
|
while (!PowerCC26XX_isStableXOSC_HF());
|
|
|
|
/* Invoke the XOSC_HF switching */
|
|
PowerCC26XX_switchXOSC_HF();
|
|
}
|
|
else if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF)
|
|
{
|
|
/* If the XOSC_HF is not ready yet, only execute the first hal of the chain*/
|
|
tmp->condition.rule = COND_NEVER;
|
|
|
|
/* Next state: RF_fsmXOSCState (polling XOSC_HF)*/
|
|
RF_core.fxn = RF_fsmXOSCState;
|
|
}
|
|
|
|
/* Send the setup chain to the RF core */
|
|
RF_dbellSubmitCmdAsync((uint32_t)pRadioSetup);
|
|
|
|
/* Invoke the global callback. */
|
|
RF_invokeGlobalCallback(RF_GlobalEventRadioSetup, (void*)pRadioSetup);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF state machine function during XOSC state.
|
|
*
|
|
* Input: pObj - Pointer to RF object.
|
|
* e - State machine event.
|
|
* Return: none
|
|
*/
|
|
static void RF_fsmXOSCState(RF_Object *pObj, RF_FsmEvent e)
|
|
{
|
|
if ((e & RF_FsmEventPowerStep) || (e & RF_FsmEventWakeup))
|
|
{
|
|
/* If XOSC_HF is now ready */
|
|
if (OSCClockSourceGet(OSC_SRC_CLK_HF) == OSC_XOSC_HF)
|
|
{
|
|
/* Next state: RF_fsmActiveState */
|
|
RF_core.fxn = RF_fsmActiveState;
|
|
|
|
/* Continue with the CMD_RAT_SYNC and the rest of the chain. */
|
|
RF_dbellSubmitCmdAsync((uint32_t)&RF_ratSyncCmd.start);
|
|
}
|
|
else
|
|
{
|
|
/* Clock source not yet switched to XOSC_HF: schedule new polling */
|
|
RF_restartClockTimeout(&RF_clkPowerUpObj, RF_XOSC_HF_SWITCH_CHECK_PERIOD_US/ClockP_tickPeriod);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RF state machine function during active state.
|
|
*
|
|
* Input: pObj - Pointer to RF object.
|
|
* e - State machine event.
|
|
* Return: none
|
|
*/
|
|
static void RF_fsmActiveState(RF_Object *pObj, RF_FsmEvent e)
|
|
{
|
|
volatile RF_Cmd* pCmd;
|
|
uint32_t rtcValTmp1;
|
|
uint32_t rtcValTmp2;
|
|
RF_EventMask events;
|
|
bool transitionAllowed;
|
|
uint32_t key;
|
|
|
|
if (e & RF_FsmEventCpeInt)
|
|
{
|
|
/* Enter critical section */
|
|
key = HwiP_disable();
|
|
|
|
/* Dereference the command which requested the callback*/
|
|
pCmd = (RF_Cmd*)RF_cmdQ.pCurrCmdCb;
|
|
|
|
/* If this is due to other event than LastCmdDone */
|
|
if (pCmd && !(pCmd->rfifg & RF_TERMINATION_EVENT_MASK))
|
|
{
|
|
/* Temporarily store the reason of callback */
|
|
events = pCmd->rfifg;
|
|
|
|
/* Clear the events which are handled here */
|
|
pCmd->rfifg &= (~events);
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Invoke the user callback if it is provided */
|
|
if (pCmd->pCb && events)
|
|
{
|
|
pCmd->pCb(pCmd->pClient, pCmd->ch, events);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventCpeInt;
|
|
}
|
|
/* Coming from powerup states */
|
|
else if (e & RF_FsmEventPowerStep)
|
|
{
|
|
/* RF core boot process is now finished */
|
|
HWREG(PRCM_BASE + PRCM_O_RFCBITS) |= RF_BOOT1;
|
|
|
|
/* Release the constraint on the FLASH in IDLE */
|
|
if (bDisableFlashInIdleConstraint)
|
|
{
|
|
Power_releaseConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
|
|
bDisableFlashInIdleConstraint = false;
|
|
}
|
|
|
|
/* Enter critical section */
|
|
key = HwiP_disable();
|
|
|
|
/* Update power up duration if coming from the clkPowerUpFxn. Skip the calcualtion
|
|
if coming from boot, since the LF clock is derived from RCOSC_HF without calibration. */
|
|
if ((OSCClockSourceGet(OSC_SRC_CLK_LF) != OSC_RCOSC_HF)
|
|
&& pObj->clientConfig.bMeasurePowerUpDuration
|
|
&& RF_rtcTimestampA)
|
|
{
|
|
/* Temporary storage to be able to compare the new value to the old measurement */
|
|
uint32_t prevPowerUpDuration = pObj->clientConfig.nPowerUpDuration;
|
|
|
|
/* Take wake up timestamp and the current timestamp */
|
|
rtcValTmp1 = (uint32_t) RF_rtcTimestampA;
|
|
rtcValTmp2 = (uint32_t) AONRTCCurrent64BitValueGet();
|
|
|
|
/* Calculate the difference of the timestamps and convert it to us units */
|
|
pObj->clientConfig.nPowerUpDuration = UDIFF(rtcValTmp1, rtcValTmp2);
|
|
pObj->clientConfig.nPowerUpDuration >>= RF_RTC_CONV_TO_US_SHIFT;
|
|
|
|
/* Low pass filter on power up durations less than in the previous cycle */
|
|
if (prevPowerUpDuration > pObj->clientConfig.nPowerUpDuration)
|
|
{
|
|
/* Expect that the values are small and the calculation can be done in 32 bits */
|
|
pObj->clientConfig.nPowerUpDuration = (prevPowerUpDuration + pObj->clientConfig.nPowerUpDuration)/2;
|
|
}
|
|
|
|
/* Power up duration should be within certain upper and lower bounds */
|
|
if ((pObj->clientConfig.nPowerUpDuration > RF_DEFAULT_POWER_UP_TIME) ||
|
|
(pObj->clientConfig.nPowerUpDuration < RF_DEFAULT_MIN_POWER_UP_TIME))
|
|
{
|
|
pObj->clientConfig.nPowerUpDuration = RF_DEFAULT_POWER_UP_TIME;
|
|
}
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Check the status of the CMD_FS, if it was sent (chained) to the setup command.
|
|
If it failed, return an error callback to the client.
|
|
The client can either resend the CMD_FS or ignore the error as per Errata on PG2.1 */
|
|
if (RF_checkCmdFsError())
|
|
{
|
|
/* Invoke the error callback: deault is do nothing */
|
|
RF_Callback pErrCb = (RF_Callback)pObj->clientConfig.pErrCb;
|
|
pErrCb(pObj, RF_ERROR_CMDFS_SYNTH_PROG, RF_EventError);
|
|
|
|
/* Check if there is pending command */
|
|
if (List_head(&RF_cmdQ.pPend))
|
|
{
|
|
/* Make sure the next pending command gets dispatched by issuing CPE0 IRQ */
|
|
RF_dispatchNextEvent();
|
|
}
|
|
else
|
|
{
|
|
/* No pending command */
|
|
e |= RF_FsmEventLastCommandDone;
|
|
}
|
|
}
|
|
|
|
/* Issue power up callback: the RF core is active */
|
|
RF_Callback pPowerCb = (RF_Callback)pObj->clientConfig.pPowerCb;
|
|
pPowerCb(pObj, 0, RF_EventPowerUp);
|
|
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventPowerStep;
|
|
}
|
|
else if (e & RF_FsmEventLastCommandDone)
|
|
{
|
|
/* Issue radio operation done callback */
|
|
RF_radioOpDoneCb();
|
|
|
|
/* Take the next command in the done queue if any left */
|
|
if (List_empty(&RF_cmdQ.pDone))
|
|
{
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventLastCommandDone;
|
|
}
|
|
}
|
|
else if (e & RF_FsmEventInitChangePhy)
|
|
{
|
|
/* Enter critical section */
|
|
key = HwiP_disable();
|
|
|
|
/* We only continue with phy switching if the RF core is still available.
|
|
This check is important since the queues might have changed in the meantime
|
|
of servicing the SWI. */
|
|
transitionAllowed = RF_isStateTransitionAllowed();
|
|
|
|
/* Take the next command from the pend queue */
|
|
RF_Cmd* pNextCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
|
|
if ((transitionAllowed == true) && (pNextCmd != NULL))
|
|
{
|
|
/* Indicate that we are changing phy on the RF core. */
|
|
RF_core.status = RF_CoreStatusPhySwitching;
|
|
|
|
/* Change HWI handler while switching the phy */
|
|
HwiP_setFunc(&RF_hwiCpe0Obj, RF_hwiCpe0ChangePhy, (uintptr_t)NULL);
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Stop inactivity clock of the current client if running */
|
|
ClockP_stop(&RF_clkInactivityObj);
|
|
|
|
/* Store the timestamp or measurement of the switching time */
|
|
RF_rtcBeginSequence = AONRTCCurrent64BitValueGet();
|
|
|
|
/* Notify the power driver that FLASH is needed in IDLE */
|
|
bDisableFlashInIdleConstraint = true;
|
|
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
|
|
|
|
/* Switch the current client to the commands client */
|
|
RF_currClient = pNextCmd->pClient;
|
|
|
|
/* Do client switch callback if provided */
|
|
if (RF_currClient->clientConfig.nClientEventMask & RF_ClientEventSwitchClientEntered)
|
|
{
|
|
RF_ClientCallback pClientEventCb = (RF_ClientCallback)RF_currClient->clientConfig.pClientEventCb;
|
|
pClientEventCb(RF_currClient, RF_ClientEventSwitchClientEntered, NULL);
|
|
}
|
|
|
|
/* Apply the new RF Core patch */
|
|
RF_applyRfCorePatch(RF_PHY_SWITCHING_MODE);
|
|
|
|
/* Ensure that the analog domain is updated. */
|
|
RF_currClient->clientConfig.bUpdateSetup = true;
|
|
|
|
/* Ensure that the overrides are correct. */
|
|
RF_initRadioSetup(RF_currClient);
|
|
|
|
/* Configure the SETUP command */
|
|
RF_RadioSetup* pRadioSetup = RF_currClient->clientConfig.pRadioSetup;
|
|
|
|
/* Walk the chain and search or specific commands */
|
|
RF_Op* tmp = (RF_Op*)&pRadioSetup->prop;
|
|
while ((tmp->pNextOp) && (tmp->pNextOp->commandNo != CMD_SYNC_START_RAT) &&
|
|
(tmp->pNextOp->commandNo != CMD_FS) &&
|
|
(tmp->pNextOp->commandNo != CMD_FS_OFF))
|
|
{
|
|
tmp = tmp->pNextOp;
|
|
}
|
|
|
|
/* Clear any of the found specific command */
|
|
tmp->pNextOp = NULL;
|
|
tmp->condition.rule = COND_NEVER;
|
|
|
|
/* Setup FS command to follow SETUP command */
|
|
RF_Op* pOpFirstPend = pNextCmd->pOp;
|
|
if ((pOpFirstPend->commandNo == CMD_FS) || (pOpFirstPend->commandNo == CMD_FS_OFF))
|
|
{
|
|
/* First command is FS command so no need to chain an implicit FS command -> reset nRtc2 */
|
|
RF_rtcBeginSequence = 0;
|
|
}
|
|
else
|
|
{
|
|
if (RF_currClient->state.mode_state.cmdFs.commandNo)
|
|
{
|
|
/* Chain in the implicit FS command */
|
|
rfc_CMD_FS_t* pOpFs = &RF_currClient->state.mode_state.cmdFs;
|
|
pOpFs->status = IDLE;
|
|
pOpFs->pNextOp = NULL;
|
|
pOpFs->startTrigger.triggerType = TRIG_NOW;
|
|
pOpFs->condition.rule = COND_NEVER;
|
|
tmp->pNextOp = (RF_Op*)pOpFs;
|
|
tmp->condition.rule = COND_ALWAYS;
|
|
}
|
|
}
|
|
|
|
/* Send the command chain */
|
|
RF_dbellSubmitCmdAsync((uint32_t)pRadioSetup);
|
|
|
|
/* Invoke the global callback. */
|
|
RF_invokeGlobalCallback(RF_GlobalEventRadioSetup, (void*)pRadioSetup);
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventInitChangePhy;
|
|
}
|
|
else if (e & RF_FsmEventFinishChangePhy)
|
|
{
|
|
/* Release the constraint on the FLASH in IDLE */
|
|
if (bDisableFlashInIdleConstraint)
|
|
{
|
|
Power_releaseConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
|
|
bDisableFlashInIdleConstraint = false;
|
|
}
|
|
|
|
/* Check the status of the CMD_FS, if it was sent (chained) to the setup command.
|
|
If it failed, invoke the error callback of the client.
|
|
The client can either resend the CMD_FS or ignore the error. */
|
|
if (RF_checkCmdFsError())
|
|
{
|
|
RF_Callback pErrCb = (RF_Callback)RF_currClient->clientConfig.pErrCb;
|
|
pErrCb(RF_currClient, RF_ERROR_CMDFS_SYNTH_PROG, RF_EventError);
|
|
}
|
|
|
|
/* Only compute PHY switching time if rtcValTmp1 is not zero (was initialized) */
|
|
if (RF_rtcBeginSequence)
|
|
{
|
|
/* Record the timestamp for switching time measurement. */
|
|
rtcValTmp2 = (uint32_t) AONRTCCurrent64BitValueGet();
|
|
|
|
/* Calculate how long it took to reconfigure the radio to a new phy. */
|
|
RF_currClient->clientConfig.nPhySwitchingDuration = UDIFF(RF_rtcBeginSequence, rtcValTmp2);
|
|
RF_currClient->clientConfig.nPhySwitchingDuration >>= RF_RTC_CONV_TO_US_SHIFT;
|
|
|
|
/* Reset RF_rtcBeginSequence value at the end of phy switching sequence. */
|
|
RF_rtcBeginSequence = 0;
|
|
}
|
|
|
|
/* Change HWI handler */
|
|
HwiP_setFunc(&RF_hwiCpe0Obj, RF_hwiCpe0Active, (uintptr_t)NULL);
|
|
|
|
/* Mark radio and client as being active */
|
|
RF_core.status = RF_CoreStatusActive;
|
|
|
|
/* Serve the callbacks if the queue was rearranged while PHY switching was performed. */
|
|
if (List_head(&RF_cmdQ.pDone))
|
|
{
|
|
SwiP_or(&RF_swiFsmObj, RF_FsmEventLastCommandDone);
|
|
}
|
|
|
|
/* Run the scheduler again. */
|
|
RF_dispatchNextEvent();
|
|
|
|
/* We have handled this event now */
|
|
e &= ~RF_FsmEventFinishChangePhy;
|
|
}
|
|
else if (e & RF_FsmEventPowerDown)
|
|
{
|
|
/* Enter critical section. */
|
|
key = HwiP_disable();
|
|
|
|
/* Verify if the decision has not been reverted in the meantime. */
|
|
transitionAllowed = RF_isStateTransitionAllowed();
|
|
|
|
/* If possible, put the running RAT channels into pending state allowing to
|
|
power down the RF core. */
|
|
if (transitionAllowed)
|
|
{
|
|
transitionAllowed = RF_ratReleaseChannels();
|
|
}
|
|
|
|
/* If there is nothing prevent us to power down, proceed. */
|
|
if (transitionAllowed)
|
|
{
|
|
/* Indicate that the RF core is being powered down from now */
|
|
RF_core.status = RF_CoreStatusPoweringDown;
|
|
|
|
/* Stop inactivity timer. */
|
|
ClockP_stop(&RF_clkInactivityObj);
|
|
|
|
/* Exit ritical setion. */
|
|
HwiP_restore(key);
|
|
|
|
/* Execute power down sequence of the RF core */
|
|
RF_corePowerDown();
|
|
|
|
/* Invoke the global callback. At this point the clock of RF core is OFF, but the
|
|
power domain is still powered (hence the doorbell signals are still active.
|
|
We do the callback here to save some power. */
|
|
RF_invokeGlobalCallback(RF_GlobalEventRadioPowerDown, NULL);
|
|
|
|
/* Notify the power driver that Standby mode is allowed and the RF core can be powered down. */
|
|
Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
|
|
Power_releaseDependency(PowerCC26XX_DOMAIN_RFCORE);
|
|
|
|
/* Closing all handles */
|
|
if (!RF_numClients)
|
|
{
|
|
/* Release the semaphore to be sure no one is pending on it */
|
|
SemaphoreP_post(&RF_currClient->state.semSync);
|
|
}
|
|
|
|
/* If there is no specific client request or the XOSC, release the dependency */
|
|
if (RFCC26XX_hwAttrs.xoscHfAlwaysNeeded == false)
|
|
{
|
|
Power_releaseDependency(PowerCC26XX_XOSC_HF);
|
|
}
|
|
|
|
/* Release constraint of switching XOSC_HF from the RF driver itself */
|
|
if (RF_core.manualXoscHfSelect)
|
|
{
|
|
RF_core.manualXoscHfSelect = false;
|
|
Power_releaseConstraint(PowerCC26XX_SWITCH_XOSC_HF_MANUALLY);
|
|
}
|
|
|
|
/* Next state: RF_fsmPowerUpState */
|
|
RF_core.fxn = RF_fsmPowerUpState;
|
|
|
|
/* Indicate that the RF core is now powered down */
|
|
RF_core.status = RF_CoreStatusIdle;
|
|
|
|
/* Issue radio available callback if RF_yield was called with no
|
|
pending commands in the queue */
|
|
uint8_t tmp = RF_RADIOFREECB_REQACCESS_FLAG;
|
|
if (RF_cmdQ.nSeqDone == RF_cmdQ.nSeqPost)
|
|
{
|
|
tmp |= RF_RADIOFREECB_PREEMPT_FLAG | RF_RADIOFREECB_CMDREJECT_FLAG;
|
|
}
|
|
RF_issueRadioFreeCb(tmp);
|
|
}
|
|
else
|
|
{
|
|
/* Exit ritical setion. */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/* Reschedule the next event based on the state of the command queue
|
|
and the RAT module. We do it here as future commands need to work even if
|
|
power management is disabled manually. */
|
|
RF_dispatchNextEvent();
|
|
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventPowerDown;
|
|
}
|
|
else if (e & RF_FsmEventRunScheduler)
|
|
{
|
|
/* Run the scheduler again. */
|
|
RF_dispatchNextEvent();
|
|
|
|
/* We've handled this event now */
|
|
e &= ~RF_FsmEventRunScheduler;
|
|
}
|
|
|
|
/* Call self again if there are outstanding events to be processed */
|
|
if (e)
|
|
{
|
|
/* Trig the SWI with the remained/unhandled events */
|
|
SwiP_or(&RF_swiFsmObj, e);
|
|
}
|
|
}
|
|
|
|
/*-------------- Initialization & helper functions ---------------*/
|
|
|
|
/*
|
|
* Initialize RF driver.
|
|
*
|
|
* Input: none
|
|
* Return: none
|
|
*/
|
|
static void RF_init(void)
|
|
{
|
|
union {
|
|
HwiP_Params hp;
|
|
SwiP_Params sp;
|
|
} params;
|
|
|
|
/* Power init */
|
|
Power_init();
|
|
|
|
/* Enable output RTC clock for Radio Timer Synchronization */
|
|
HWREG(AON_RTC_BASE + AON_RTC_O_CTL) |= AON_RTC_CTL_RTC_UPD_EN_M;
|
|
|
|
/* Set the automatic bus request */
|
|
HWREG(PRCM_BASE + PRCM_O_RFCBITS) = RF_BOOT0;
|
|
|
|
/* Initialize SWI used by the RF driver. */
|
|
SwiP_Params_init(¶ms.sp);
|
|
params.sp.priority = RFCC26XX_hwAttrs.swiPriority;
|
|
SwiP_construct(&RF_swiFsmObj, RF_swiFsm, ¶ms.sp);
|
|
SwiP_construct(&RF_swiHwObj, RF_swiHw, ¶ms.sp);
|
|
|
|
/* Initialize HWI used by the RF driver. */
|
|
HwiP_Params_init(¶ms.hp);
|
|
params.hp.priority = RFCC26XX_hwAttrs.hwiPriority;
|
|
HwiP_construct(&RF_hwiCpe0Obj, INT_RFC_CPE_0, RF_hwiCpe0PowerFsm, ¶ms.hp);
|
|
HwiP_construct(&RF_hwiHwObj, INT_RFC_HW_COMB, RF_hwiHw, ¶ms.hp);
|
|
|
|
/* Initialize clock object used as power-up trigger */
|
|
ClockP_construct(&RF_clkPowerUpObj, &RF_clkPowerUp, 0, NULL);
|
|
ClockP_construct(&RF_clkInactivityObj, &RF_clkInactivityCallback, 0, NULL);
|
|
|
|
/* Subscribe to wakeup notification from the Power driver */
|
|
Power_registerNotify(&RF_wakeupNotifyObj, /* Object to register */
|
|
PowerCC26XX_AWAKE_STANDBY, /* Event the notification to be invoked upon */
|
|
(Power_NotifyFxn) RF_wakeupNotification, /* Function to be invoked */
|
|
(uintptr_t) NULL); /* Parameters */
|
|
|
|
/* Set the XOSC_HF dependency if the HW attributes say so. This will ensure
|
|
that the XOSC_HF is turned on by the power driver as soon as possible when
|
|
coming out of standby. */
|
|
if (RFCC26XX_hwAttrs.xoscHfAlwaysNeeded == true)
|
|
{
|
|
Power_setDependency(PowerCC26XX_XOSC_HF);
|
|
}
|
|
|
|
/* Initialized the queues. */
|
|
List_clearList(&RF_cmdQ.pDone);
|
|
List_clearList(&RF_cmdQ.pPend);
|
|
|
|
/* Initialize global variables */
|
|
RF_core.status = RF_CoreStatusIdle;
|
|
RF_core.init = false;
|
|
RF_core.activeTimeUs = 0;
|
|
RF_core.manualXoscHfSelect = false;
|
|
RF_ratModule.availableRatChannels = RF_DEFAULT_AVAILRATCH_VAL;
|
|
RF_rtcTimestampA = 0;
|
|
RF_rtcBeginSequence = 0;
|
|
RF_errTolValInUs = RF_DEFAULT_RAT_RTC_ERR_TOL_IN_US;
|
|
RF_powerConstraint = 0;
|
|
|
|
/* Set FSM state to power up */
|
|
RF_core.fxn = RF_fsmPowerUpState;
|
|
}
|
|
|
|
/*
|
|
* Trace through the pending queue and flush the command(s).
|
|
*
|
|
* Input: h - Handle to the client calling this function.
|
|
* pCmd - Pointer to the command where the cancelling should start with.
|
|
* bFlushAll - Decides weather one or more commands should be aborted.
|
|
* Return: Number of commands was terminated.
|
|
*/
|
|
static uint32_t RF_discardPendCmd(RF_Handle h, RF_Cmd* pCmd, bool bFlushAll, bool bPreempt)
|
|
{
|
|
/* Local variables, start from the head of queue. */
|
|
uint32_t numDiscardedCmd = 0;
|
|
RF_Cmd* pElem = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
|
|
/* Find the first command to be cancelled. */
|
|
while (pElem && (pElem != pCmd))
|
|
{
|
|
pElem = (RF_Cmd*)List_next((List_Elem*)pElem);
|
|
}
|
|
|
|
/* If we found the command to be cancelled. */
|
|
while (pElem)
|
|
{
|
|
/* Temporarly store the next element, since we will need
|
|
to continue from there. */
|
|
RF_Cmd* pNextElem = (RF_Cmd*)List_next((List_Elem*)pElem);
|
|
|
|
if (RF_isClientOwner(h, pElem))
|
|
{
|
|
/* Mark the command that it was cancelled. */
|
|
RF_cmdStoreEvents(pElem, RF_EventCmdCancelled);
|
|
|
|
if (bPreempt)
|
|
{
|
|
/* Mark the command as being preempted. */
|
|
RF_cmdStoreEvents(pElem, RF_EventCmdPreempted);
|
|
|
|
/* Subscribe the client for RadioFree callback. */
|
|
RF_Sch.clientHndRadioFreeCb = pCmd->pClient;
|
|
RF_Sch.issueRadioFreeCbFlags |= RF_RADIOFREECB_PREEMPT_FLAG;
|
|
}
|
|
|
|
/* Remove the command from the pend queue and place it to
|
|
the done queue. */
|
|
List_remove(&RF_cmdQ.pPend, (List_Elem*)pElem);
|
|
List_put(&RF_cmdQ.pDone, (List_Elem*)pElem);
|
|
|
|
/* Increment the counter of cancelled commands. */
|
|
numDiscardedCmd += 1;
|
|
}
|
|
|
|
/* Break the loop if only single cancel was requested.
|
|
Step the queue otherwise. */
|
|
if (bFlushAll)
|
|
{
|
|
pElem = pNextElem;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return with the number of cancelled commands. */
|
|
return(numDiscardedCmd);
|
|
}
|
|
|
|
/*
|
|
* Process cancel commands. It is used by RF_cancelCmd, RF_flushCmd API.
|
|
*
|
|
* Input: h - Handle to the client calling this function.
|
|
* ch - Handle to the command where the cancelling should start with.
|
|
* graceful - true: stop the command
|
|
* false: abort the command
|
|
* flush - true: flush all commands of this client
|
|
* false: only cancel the given command
|
|
* preempt - mark the command as the reason of aborting is preemption
|
|
* Return: status
|
|
*/
|
|
static RF_Stat RF_abortCmd(RF_Handle h, RF_CmdHandle ch, bool graceful, bool flush, bool preempt)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Initialize local variables */
|
|
RF_Cmd* pCmd = NULL;
|
|
RF_Stat status = RF_StatInvalidParamsError;
|
|
RF_EventMask event = graceful ? RF_EventCmdStopped : RF_EventCmdAborted;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Handle FLUSH_ALL request */
|
|
if (ch == RF_CMDHANDLE_FLUSH_ALL)
|
|
{
|
|
/* Start to cancel the commands from the actively running onces if it belongs to this client. */
|
|
if (RF_isClientOwner(h, RF_cmdQ.pCurrCmdBg))
|
|
{
|
|
pCmd = RF_cmdQ.pCurrCmdBg;
|
|
}
|
|
else if (RF_isClientOwner(h, RF_cmdQ.pCurrCmdFg))
|
|
{
|
|
pCmd = RF_cmdQ.pCurrCmdFg;
|
|
}
|
|
else
|
|
{
|
|
/* Start to walk the pending queue from its head. */
|
|
pCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Search for the command in the command pool based on its handle. The command can
|
|
locate on any of the queues at this point. */
|
|
pCmd = RF_cmdGet(h, ch, 0x00);
|
|
}
|
|
|
|
/* If command handle is valid, proceed to cancel. */
|
|
if (pCmd)
|
|
{
|
|
/* If the command is still allocated. */
|
|
if (pCmd->flags & RF_CMD_ALLOC_FLAG)
|
|
{
|
|
/* If the command we want to cancel is actively running. */
|
|
if ((pCmd == RF_cmdQ.pCurrCmdBg) || (pCmd == RF_cmdQ.pCurrCmdFg))
|
|
{
|
|
/* Flag that the command has been aborted. In IEEE 15.4 mode, this means
|
|
aborting both the background and foreground commands. */
|
|
RF_cmdStoreEvents(RF_cmdQ.pCurrCmdBg, event);
|
|
RF_cmdStoreEvents(RF_cmdQ.pCurrCmdFg, event);
|
|
|
|
/* Decode what method to use to terminate the ongoing radio operation. */
|
|
uint32_t directCmd = (graceful) ? CMDR_DIR_CMD(CMD_STOP) : CMDR_DIR_CMD(CMD_ABORT);
|
|
|
|
/* Send the abort/stop command through the doorbell to the RF core. */
|
|
RFCDoorbellSendTo(directCmd);
|
|
|
|
if (preempt)
|
|
{
|
|
/* Mark the command as being preempted. */
|
|
RF_cmdStoreEvents(RF_cmdQ.pCurrCmdBg, RF_EventCmdPreempted);
|
|
RF_cmdStoreEvents(RF_cmdQ.pCurrCmdFg, RF_EventCmdPreempted);
|
|
|
|
/* Subscribe the client for RadioFree callback. */
|
|
RF_Sch.clientHndRadioFreeCb = pCmd->pClient;
|
|
RF_Sch.issueRadioFreeCbFlags |= RF_RADIOFREECB_PREEMPT_FLAG;
|
|
}
|
|
|
|
/* Remove all commands from the pend queue belong to this client. Only do it
|
|
if it was explicitely requested through the flush argument. */
|
|
if (flush)
|
|
{
|
|
RF_discardPendCmd(h, (RF_Cmd*)List_head(&RF_cmdQ.pPend), flush, preempt);
|
|
}
|
|
|
|
/* Return with success as we cancelled at least the currently running command. */
|
|
status = RF_StatSuccess;
|
|
}
|
|
else
|
|
{
|
|
/* Remove one/all commands from the pend queue based on the flush argument.
|
|
If at least one command is cancelled the operation was succesful. Otherwise,
|
|
either the pend queue is empty or pCmd have terminated earlier */
|
|
if (RF_discardPendCmd(h, pCmd, flush, preempt))
|
|
{
|
|
/* Kick the state machine to handle the done queue and re-execute the scheduler.
|
|
This is not necessary when the RF is currently performing a power-up. */
|
|
if ((RF_core.status != RF_CoreStatusPoweringUp) &&
|
|
(RF_core.status != RF_CoreStatusPhySwitching))
|
|
{
|
|
SwiP_or(&RF_swiFsmObj, (RF_FsmEventLastCommandDone | RF_FsmEventRunScheduler));
|
|
}
|
|
|
|
/* At least one command was cancelled. */
|
|
status = RF_StatSuccess;
|
|
}
|
|
else
|
|
{
|
|
/* The command is not running and is not in the pend queue. It is located on the
|
|
done queue, hence return RF_StatCmdEnded. */
|
|
status = RF_StatCmdEnded;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /* If command is still in the pool but it is not allocated anymore, i.e. it was already served. */
|
|
status = RF_StatCmdEnded;
|
|
}
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the result:
|
|
- RF_StatSuccess if at least one command was cancelled.
|
|
- RF_StatCmdEnded, when the command already finished.
|
|
- RF_StatInvalidParamsError otherwise. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* Execute a direct or immediate command in the RF Core if possible.
|
|
*
|
|
* Input: pCmd - Pointer to the command which shall be sent to the RF core.
|
|
* rawStatus - Return address of the raw status byte read from the CMDSTA register.
|
|
* Return: The return value interprets and converts the result of command execution to and RF_Stat value.
|
|
* RF_StatCmdDoneSuccess - If the command was sent and accepted by the RF core.
|
|
* RF_StatCmdDoneError - Command was rejected by the RF core.
|
|
* RF_StatRadioInactiveError - The RF core is OFF.
|
|
*/
|
|
static RF_Stat RF_executeDirectImmediateCmd(uint32_t pCmd, uint32_t* rawStatus)
|
|
{
|
|
/* If the RF core is ON, we can send the command */
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
/* Submit the command to the doorbell */
|
|
uint32_t localStatus = RFCDoorbellSendTo(pCmd);
|
|
|
|
/* Pass the rawStatus to the callee if possible. */
|
|
if (rawStatus)
|
|
{
|
|
*rawStatus = localStatus;
|
|
}
|
|
|
|
/* Check the return value of the RF core through the CMDSTA register within the doorbell */
|
|
if ((localStatus & RF_CMDSTA_REG_VAL_MASK) == CMDSTA_Done)
|
|
{
|
|
/* The command was accepted */
|
|
return(RF_StatCmdDoneSuccess);
|
|
}
|
|
else
|
|
{
|
|
/* The command was rejected */
|
|
return(RF_StatCmdDoneError);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The RF core is not capable of receiving the command */
|
|
return(RF_StatRadioInactiveError);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a direct or immediate command to the RF core. The command is rejected
|
|
* if the RF core is configured to a different PHY (client).
|
|
*
|
|
* Input: h - Handle to the client calling this function.
|
|
* pCmd - Pointer to the command which shall be sent to the RF core.
|
|
* rawStatus - Return address of raw status byte read from CMDSTA register.
|
|
* Return: RF_StatCmdDoneSuccess - If the command was sent and accepted by the RF core.
|
|
* RF_StatCmdDoneError - Command was rejected by the RF core.
|
|
* RF_StatInvalidParamsError - Client do not have the right to send commands now.
|
|
* RF_StatRadioInactiveError - The RF core is OFF.
|
|
*/
|
|
static RF_Stat RF_runDirectImmediateCmd(RF_Handle h, uint32_t pCmd, uint32_t* rawStatus)
|
|
{
|
|
/* Local variable. */
|
|
RF_Stat status;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Only the current client is allowed to send direct commands */
|
|
if (h != RF_currClient)
|
|
{
|
|
/* Return with an error code it is a different client */
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
else
|
|
{
|
|
/* Execute the direct or immediate command. */
|
|
status = RF_executeDirectImmediateCmd(pCmd, rawStatus);
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the status information about the success of command execution. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* Helper function to find the first override representing a High PA value (CC13x2P devices).
|
|
*
|
|
* Input: pOverride - Pointer to an override list to be searched.
|
|
* overridePattern - Pattern o override to search for.
|
|
* currentValue - Reference where the current value can be returned.
|
|
* Return: paOffset - Offset of the High PA override.
|
|
* RF_TX_OVERRIDE_INVALID_OFFSET - No override was found in the list.
|
|
*/
|
|
static uint8_t RF_getPAOverrideOffsetAndValue(uint32_t* pOverride, uint32_t overridePattern, uint32_t* currentValue)
|
|
{
|
|
/* Search for the particular override. */
|
|
uint8_t paOffset = RFCOverrideSearch(pOverride, overridePattern, RF_TX_OVERRIDE_MASK, RF_OVERRIDE_SEARCH_DEPTH);
|
|
|
|
/* If the override was found. */
|
|
if (currentValue)
|
|
{
|
|
*currentValue = pOverride[paOffset] >> RF_TX_OVERRIDE_SHIFT;
|
|
}
|
|
|
|
/* Return with an invalid value. */
|
|
return(paOffset);
|
|
}
|
|
|
|
/*
|
|
* Helper function to find and replace the first override representing a High PA value.
|
|
*
|
|
* Input: pOverride - Pointer to an override list to be searched.
|
|
* overridePattern - Mask of override type to searh for.
|
|
* newValue - The new raw value the PA to be set to.
|
|
* Return: paOffset - Offset of the High PA override.
|
|
* RF_TX_OVERRIDE_INVALID_OFFSET - No override was found in the list. Hence nothing to replace.
|
|
*/
|
|
static uint8_t RF_searchAndReplacePAOverride(uint32_t* pOverride, uint32_t overridePattern, uint32_t newValue)
|
|
{
|
|
/* Search for the particular override. */
|
|
uint8_t paOffset = RF_getPAOverrideOffsetAndValue(pOverride, overridePattern, NULL);
|
|
|
|
/* If the override was found. */
|
|
if (paOffset != RF_TX_OVERRIDE_INVALID_OFFSET)
|
|
{
|
|
if (overridePattern == RF_TX20_PATTERN)
|
|
{
|
|
/* Replace the high PA gain with the new value. */
|
|
pOverride[paOffset] = TX20_POWER_OVERRIDE(newValue);
|
|
}
|
|
else
|
|
{
|
|
/* Replace the default PA gain with the new value. */
|
|
pOverride[paOffset] = TX_STD_POWER_OVERRIDE(newValue);
|
|
}
|
|
}
|
|
|
|
/* Return with the offset of the PA override. */
|
|
return(paOffset);
|
|
}
|
|
|
|
/*
|
|
* Appends the PA specific override list to the end of given overrides.
|
|
*
|
|
* Input: baseOverride - Override list to append the applicable segment to.
|
|
* newOverride - Override segment to be appended.
|
|
* Return: none
|
|
*/
|
|
static void RF_attachOverrides(uint32_t* baseOverride, uint32_t* newOverride)
|
|
{
|
|
if (newOverride != NULL)
|
|
{
|
|
/* Search for the attached override list. */
|
|
uint32_t maskOverride = NEW_OVERRIDE_SEGMENT(newOverride);
|
|
|
|
/* Search for the end of the base override list. We also look for new segment vectors. */
|
|
while ((*baseOverride != END_OVERRIDE) && (*baseOverride != maskOverride))
|
|
{
|
|
baseOverride++;
|
|
}
|
|
|
|
/* Append the second override list. */
|
|
*baseOverride = maskOverride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Terminate the override list at the first match of a jump to the given newOverride.
|
|
* The function assumes that there are no other jump vectors before.
|
|
*
|
|
* Input: baseOverride - Override list to append the applicable segment to.
|
|
* newOverride - Override segment to be appended.
|
|
* Return: none
|
|
*/
|
|
static void RF_detachOverrides(uint32_t* baseOverride, uint32_t* newOverride)
|
|
{
|
|
if (newOverride != NULL)
|
|
{
|
|
/* Search for the attached override list. */
|
|
uint32_t maskOverride = NEW_OVERRIDE_SEGMENT(newOverride);
|
|
|
|
/* Search for the end of the base override list. We also look for new segment vectors. */
|
|
while ((*baseOverride != END_OVERRIDE) && (*baseOverride != maskOverride))
|
|
{
|
|
baseOverride++;
|
|
}
|
|
|
|
/* Append the second override list if exists. */
|
|
*baseOverride = END_OVERRIDE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decode all the override pointers according to the type of the setup command.
|
|
*
|
|
* Input: radioSetup - Pointer to the setup command to be evaluated.
|
|
* Return: tx20FeatureAvailable - true if the High Gain PA is available.
|
|
* pTxPower - Pointer to the txPower field of setup command.
|
|
* pRegOverride - Pointer to the base override list.
|
|
* pRegOverrideTxStd - Pointer to the Default PA override list.
|
|
* pRegOverrideTx20 - Pointer to the High PA override list.
|
|
*/
|
|
static bool RF_decodeOverridePointers(RF_RadioSetup* radioSetup, uint16_t** pTxPower, uint32_t** pRegOverride, uint32_t** pRegOverrideTxStd, uint32_t** pRegOverrideTx20)
|
|
{
|
|
/* Decode if High Gain PA is even available. */
|
|
bool tx20FeatureAvailable = (ChipInfo_GetChipType() == CHIP_TYPE_CC1352P);
|
|
|
|
/* Only decode the offset of those fields which exist on this device. */
|
|
if (tx20FeatureAvailable)
|
|
{
|
|
/* Local variables. */
|
|
uint8_t loDivider;
|
|
uint8_t frontEndMode;
|
|
uint8_t index;
|
|
|
|
/* Decode the offset of txPower field and all the override pointers
|
|
available on the CC1352P device. */
|
|
switch (radioSetup->commandId.commandNo)
|
|
{
|
|
case (CMD_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->common_pa.txPower;
|
|
*pRegOverride = radioSetup->common_pa.pRegOverride;
|
|
*pRegOverrideTxStd = radioSetup->common_pa.pRegOverrideTxStd;
|
|
*pRegOverrideTx20 = radioSetup->common_pa.pRegOverrideTx20;
|
|
|
|
/* Input to recalculation of overrides. */
|
|
loDivider = radioSetup->common_pa.loDivider;
|
|
frontEndMode = radioSetup->common_pa.config.frontEndMode;
|
|
|
|
break;
|
|
case (CMD_BLE5_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->ble5_pa.txPower;
|
|
*pRegOverride = radioSetup->ble5_pa.pRegOverrideCommon;
|
|
*pRegOverrideTxStd = radioSetup->ble5_pa.pRegOverrideTxStd;
|
|
*pRegOverrideTx20 = radioSetup->ble5_pa.pRegOverrideTx20;
|
|
|
|
/* Input to recalculation of overrides. */
|
|
loDivider = radioSetup->ble5_pa.loDivider;
|
|
frontEndMode = radioSetup->ble5_pa.config.frontEndMode;
|
|
|
|
break;
|
|
case (CMD_PROP_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->prop_pa.txPower;
|
|
*pRegOverride = radioSetup->prop_pa.pRegOverride;
|
|
*pRegOverrideTxStd = radioSetup->prop_pa.pRegOverrideTxStd;
|
|
*pRegOverrideTx20 = radioSetup->prop_pa.pRegOverrideTx20;
|
|
|
|
/* Input to recalculation of overrides. */
|
|
loDivider = 0;
|
|
frontEndMode = radioSetup->prop_pa.config.frontEndMode;
|
|
break;
|
|
default:
|
|
*pTxPower = &radioSetup->prop_div_pa.txPower;
|
|
*pRegOverride = radioSetup->prop_div_pa.pRegOverride;
|
|
*pRegOverrideTxStd = radioSetup->prop_div_pa.pRegOverrideTxStd;
|
|
*pRegOverrideTx20 = radioSetup->prop_div_pa.pRegOverrideTx20;
|
|
|
|
/* Input to recalculation of overrides. */
|
|
loDivider = radioSetup->prop_div_pa.loDivider;
|
|
frontEndMode = radioSetup->prop_div_pa.config.frontEndMode;
|
|
break;
|
|
}
|
|
|
|
/* Modify the divider and front-end specific override. This is to keep the override
|
|
list and the setup command in sync, even if the setup command was changed runtime
|
|
due to the changing stack configuration. */
|
|
if (*pRegOverrideTxStd)
|
|
{
|
|
index = RFCOverrideSearch(*pRegOverrideTxStd, RFC_FE_OVERRIDE_ADDRESS, RFC_FE_OVERRIDE_MASK, RFC_MAX_SEARCH_DEPTH);
|
|
|
|
if (index < RFC_MAX_SEARCH_DEPTH)
|
|
{
|
|
(*pRegOverrideTxStd)[index] = RFCAnaDivTxOverride(loDivider, frontEndMode);
|
|
}
|
|
}
|
|
|
|
if (*pRegOverrideTx20)
|
|
{
|
|
index = RFCOverrideSearch(*pRegOverrideTx20, RFC_FE_OVERRIDE_ADDRESS, RFC_FE_OVERRIDE_MASK, RFC_MAX_SEARCH_DEPTH);
|
|
|
|
if (index < RFC_MAX_SEARCH_DEPTH)
|
|
{
|
|
(*pRegOverrideTx20)[index] = RFCAnaDivTxOverride(loDivider, RFC_FE_MODE_ESCAPE_VALUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Decode the offset of txPower field and the only relevant override pointer
|
|
available on all other devices. */
|
|
switch (radioSetup->commandId.commandNo)
|
|
{
|
|
case (CMD_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->common.txPower;
|
|
*pRegOverride = radioSetup->common.pRegOverride;
|
|
break;
|
|
case (CMD_BLE5_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->ble5.txPower;
|
|
*pRegOverride = radioSetup->ble5.pRegOverrideCommon;
|
|
break;
|
|
case (CMD_PROP_RADIO_SETUP):
|
|
*pTxPower = &radioSetup->prop.txPower;
|
|
*pRegOverride = radioSetup->prop.pRegOverride;
|
|
break;
|
|
default:
|
|
*pTxPower = &radioSetup->prop_div.txPower;
|
|
*pRegOverride = radioSetup->prop_div.pRegOverride;
|
|
break;
|
|
}
|
|
|
|
/* Force the value of non-existing pointers to be NULL. */
|
|
*pRegOverrideTxStd = NULL;
|
|
*pRegOverrideTx20 = NULL;
|
|
}
|
|
|
|
/* Return if the High Gain PA feature is available or not. */
|
|
return (tx20FeatureAvailable);
|
|
}
|
|
|
|
/*
|
|
* In case the PA configuration changes during the execution of a chain, this function
|
|
* propagates the change back to the setup command. This is to reserve the change even
|
|
* after a power cycle
|
|
*
|
|
* Input: handle - Radio handle the change should be stored within
|
|
* Return: none
|
|
*/
|
|
static void RF_extractPaConfiguration(RF_Handle handle)
|
|
{
|
|
/* Local variable to store the return value of function call. It is not used here. */
|
|
RF_ConfigurePaCmd configurePaCmd;
|
|
|
|
/* Retrieve the PA configuration from the RF core itself. */
|
|
RF_TxPowerTable_Value value;
|
|
value.rawValue = RFCGetPaGain();
|
|
value.paType = (RF_TxPowerTable_PAType) RFCGetPaType();
|
|
|
|
/* Update the setup command with the new settings. The change is now permanent
|
|
and will be kept even if the RF core is powered off. */
|
|
RF_updatePaConfiguration(handle->clientConfig.pRadioSetup, value, &configurePaCmd);
|
|
}
|
|
|
|
/*
|
|
* Helper function to find the HPOSC_OVERRIDE in provided override list and modify the HPOSC frequency offset.
|
|
*
|
|
* Input: pRegOverride - Pointer to override list.
|
|
* Return: None
|
|
*/
|
|
static void RF_updateHpOscOverride(uint32_t *pRegOverride)
|
|
{
|
|
/* Local variables. */
|
|
int32_t tempDegC;
|
|
int32_t relFreqOffset;
|
|
int16_t relFreqOffsetConverted;
|
|
|
|
/* Find override for HPOSC frequency offset. */
|
|
if (pRegOverride)
|
|
{
|
|
uint8_t index;
|
|
index = RFCOverrideSearch(pRegOverride, RF_HPOSC_OVERRIDE_PATTERN, RF_HPOSC_OVERRIDE_MASK, RF_OVERRIDE_SEARCH_DEPTH);
|
|
|
|
if (index < RF_OVERRIDE_SEARCH_DEPTH)
|
|
{
|
|
/* Get temperature dependent HPOSC frequency offset */
|
|
tempDegC = AONBatMonTemperatureGetDegC();
|
|
relFreqOffset = OSC_HPOSCRelativeFrequencyOffsetGet(tempDegC);
|
|
relFreqOffsetConverted = OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(relFreqOffset);
|
|
|
|
/* Update override with the HPOSC frequency offset */
|
|
pRegOverride[index] = HPOSC_OVERRIDE(relFreqOffsetConverted);
|
|
|
|
/* Adjust the RTC increment if the LF clock is derived from the HF clock of the HPOSC */
|
|
if(OSC_IsHPOSCEnabledWithHfDerivedLfClock())
|
|
{
|
|
OSC_HPOSCRtcCompensate(relFreqOffset);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Hange here if HPOSC_OVERRIDE override is not available. */
|
|
while(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function to find and modify the PA selection and gain of the provided setup command.
|
|
*
|
|
* Input: radioSetup - Setup command belong to the client.
|
|
* newValue - The new value the PA to be set to.
|
|
* configurePaCmd - The immediate command to be used to apply the changes if the RF core is active.
|
|
* Return: RF_StatSuccess - The setup command was reconfigured.
|
|
* Otherwise - An error occured.
|
|
*/
|
|
static RF_Stat RF_updatePaConfiguration(RF_RadioSetup* radioSetup, RF_TxPowerTable_Value newValue, RF_ConfigurePaCmd* configurePaCmd)
|
|
{
|
|
/* Set the default return value to indicate success. */
|
|
RF_Stat status = RF_StatSuccess;
|
|
|
|
/* Local variables. */
|
|
uint16_t* pTxPower = NULL;
|
|
uint32_t* pRegOverride = NULL;
|
|
uint32_t* pRegOverrideTxStd = NULL;
|
|
uint32_t* pRegOverrideTx20 = NULL;
|
|
|
|
/* Decode if High Gain PA is available. */
|
|
bool tx20FeatureAvailable = RF_decodeOverridePointers(radioSetup, &pTxPower, &pRegOverride, &pRegOverrideTxStd, &pRegOverrideTx20);
|
|
|
|
/* The new value requires the deault PA. */
|
|
if (newValue.paType == RF_TxPowerTable_DefaultPA)
|
|
{
|
|
/* On CC1352P devices with the correct override lists. */
|
|
if (tx20FeatureAvailable && pRegOverrideTxStd && pRegOverrideTx20)
|
|
{
|
|
/* Store the new value in the setup command. */
|
|
*pTxPower = (uint16_t) newValue.rawValue;
|
|
|
|
/* Ensure that the gain within the overrides are also updated. */
|
|
RF_searchAndReplacePAOverride(pRegOverrideTxStd, RF_TXSTD_PATTERN, newValue.rawValue);
|
|
|
|
/* Detach the High Gain overrides. It does nothing if the overrides are not attached. */
|
|
RF_detachOverrides(pRegOverride, pRegOverrideTx20);
|
|
|
|
/* Return with the immediate command in the argument. */
|
|
configurePaCmd->changePa.commandNo = CMD_CHANGE_PA;
|
|
configurePaCmd->changePa.pRegOverride = pRegOverrideTxStd;
|
|
}
|
|
else if (tx20FeatureAvailable)
|
|
{
|
|
/* Limited backward compatibility on CC1352P devices without the
|
|
proper override lists. Only gain tuning on the Default PA is available. */
|
|
if (*pTxPower != RF_TX20_ENABLED)
|
|
{
|
|
/* Store the new value in the setup command. */
|
|
*pTxPower = (uint16_t) newValue.rawValue;
|
|
|
|
/* Use the dedicated command to tune the gain */
|
|
configurePaCmd->tuneTxPower.commandNo = CMD_SET_TX_POWER;
|
|
configurePaCmd->tuneTxPower.txPower = newValue.rawValue;
|
|
}
|
|
else
|
|
{
|
|
/* PA swithing is not allowed due to the missing overrides. */
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* On any other devices, just accept the new gain. */
|
|
*pTxPower = (uint16_t) newValue.rawValue;
|
|
|
|
/* Use the dedicated command to tune the gain. */
|
|
configurePaCmd->tuneTxPower.commandNo = CMD_SET_TX_POWER;
|
|
configurePaCmd->tuneTxPower.txPower = newValue.rawValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* On CC1352P devices with the correct override lists. */
|
|
if (tx20FeatureAvailable && pRegOverrideTxStd && pRegOverrideTx20)
|
|
{
|
|
/* If the High Gain PA is available store the escape value in the setup
|
|
command and update the overrides. */
|
|
*pTxPower = (uint16_t) RF_TX20_ENABLED;
|
|
|
|
/* Change the gain to the new value. */
|
|
RF_searchAndReplacePAOverride(pRegOverrideTx20, RF_TX20_PATTERN, newValue.rawValue);
|
|
|
|
/* Attach the High Gain overrides. */
|
|
RF_attachOverrides(pRegOverride, pRegOverrideTx20);
|
|
|
|
/* Return with the command argument to be used. */
|
|
configurePaCmd->changePa.commandNo = CMD_CHANGE_PA;
|
|
configurePaCmd->changePa.pRegOverride = pRegOverrideTx20;
|
|
}
|
|
else if (tx20FeatureAvailable)
|
|
{
|
|
/* Limited backward compatibility on CC1352P devices without the
|
|
proper override lists. Only gain tuning on the High PA is available
|
|
if the gain override is present within the base override list.*/
|
|
if (RF_searchAndReplacePAOverride(pRegOverride, RF_TX20_PATTERN, newValue.rawValue) == RF_TX_OVERRIDE_INVALID_OFFSET)
|
|
{
|
|
/* Cannot use the high gain PA without a proper override list
|
|
that contains at least a placeholder gain entry. */
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
else
|
|
{
|
|
/* If updating the override list with the gain value was succesful,
|
|
set the escape value in the setup command. */
|
|
*pTxPower = (uint16_t) RF_TX20_ENABLED;
|
|
|
|
/* Use the dedicated command to tune the gain. */
|
|
configurePaCmd->tuneTx20Power.commandNo = CMD_SET_TX20_POWER;
|
|
configurePaCmd->tuneTx20Power.tx20Power = newValue.rawValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Do not accept any high gain PA values on devices which do not support it. */
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
}
|
|
|
|
/* Return with the status. */
|
|
return(status);
|
|
}
|
|
|
|
/*-------------- API functions ---------------*/
|
|
/*
|
|
* ======== RF_open ========
|
|
* Open an RF handle
|
|
*/
|
|
RF_Handle RF_open(RF_Object *pObj, RF_Mode* pRfMode, RF_RadioSetup* pRadioSetup, RF_Params *params)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(pObj != NULL);
|
|
|
|
/* Read available RF modes from the PRCM register */
|
|
uint32_t availableRfModes = HWREG(PRCM_BASE + PRCM_O_RFCMODEHWOPT);
|
|
|
|
/* Verify that the provided configuration is supported by this device.
|
|
Reject any request which is not compliant. */
|
|
if (pRfMode && pRadioSetup && (availableRfModes & (1 << pRfMode->rfMode)))
|
|
{
|
|
/* Trim the override list; The implementation of RFCOverrideUpdate is device specific */
|
|
RFCOverrideUpdate((RF_Op*)pRadioSetup, NULL);
|
|
|
|
/* Register the setup command to the client */
|
|
pObj->clientConfig.pRadioSetup = pRadioSetup;
|
|
|
|
/* Register the mode to the client */
|
|
pObj->clientConfig.pRfMode = pRfMode;
|
|
}
|
|
else
|
|
{
|
|
/* Return with null if the device do not support the requested configuration */
|
|
return(NULL);
|
|
}
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Check whether RF driver is accepting more clients */
|
|
if (RF_numClients < N_MAX_CLIENTS)
|
|
{
|
|
/* Initialize shared objects on first client opening */
|
|
if (RF_numClients == 0) RF_init();
|
|
|
|
/* Save the new RF_Handle */
|
|
RF_Sch.clientHnd[RF_numClients++] = pObj;
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Populate default RF parameters if not provided */
|
|
RF_Params rfParams;
|
|
if (params == NULL)
|
|
{
|
|
RF_Params_init(&rfParams);
|
|
params = &rfParams;
|
|
}
|
|
|
|
/* Initialize RF_Object configuration */
|
|
pObj->clientConfig.nInactivityTimeout = params->nInactivityTimeout;
|
|
pObj->clientConfig.nPhySwitchingDuration = RF_DEFAULT_PHY_SWITCHING_TIME;
|
|
pObj->clientConfig.nClientEventMask = params->nClientEventMask;
|
|
pObj->clientConfig.nPowerUpDurationMargin = params->nPowerUpDurationMargin;
|
|
pObj->clientConfig.bUpdateSetup = true;
|
|
|
|
/* Decide if automatic adjustment should be used. */
|
|
if (params->nPowerUpDuration)
|
|
{
|
|
pObj->clientConfig.nPowerUpDuration = params->nPowerUpDuration;
|
|
pObj->clientConfig.bMeasurePowerUpDuration = false;
|
|
}
|
|
else
|
|
{
|
|
pObj->clientConfig.nPowerUpDuration = RF_DEFAULT_POWER_UP_TIME;
|
|
pObj->clientConfig.bMeasurePowerUpDuration = true;
|
|
}
|
|
|
|
/* Set all the callbacks to the default (do nothing) callback */
|
|
pObj->clientConfig.pErrCb = (void*) RF_defaultCallback;
|
|
pObj->clientConfig.pClientEventCb = (void*) RF_defaultCallback;
|
|
pObj->clientConfig.pPowerCb = (void*) RF_defaultCallback;
|
|
|
|
/* If a user specified callback is provided, overwrite the default */
|
|
if (params->pErrCb)
|
|
{
|
|
pObj->clientConfig.pErrCb = (void *)params->pErrCb;
|
|
}
|
|
if (params->pClientEventCb)
|
|
{
|
|
pObj->clientConfig.pClientEventCb = (void *)params->pClientEventCb;
|
|
}
|
|
if (params->pPowerCb)
|
|
{
|
|
pObj->clientConfig.pPowerCb = (void *)params->pPowerCb;
|
|
}
|
|
|
|
/* Initialize client state & variables to zero */
|
|
memset((void*)&pObj->state, 0, sizeof(pObj->state));
|
|
|
|
/* Initialize client specific semaphore object */
|
|
SemaphoreP_constructBinary(&pObj->state.semSync, 0);
|
|
|
|
/* Initialize client specific clock objects */
|
|
ClockP_construct(&pObj->state.clkReqAccess, RF_clkReqAccess, 0, NULL);
|
|
|
|
/* Return with the RF handle. */
|
|
return(pObj);
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with null if no more clients are accepted */
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== RF_close ========
|
|
* Close an RF handle
|
|
*/
|
|
void RF_close(RF_Handle h)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* If there is at least one active client */
|
|
if (RF_numClients)
|
|
{
|
|
/* Wait for all issued commands to finish before freeing the resources */
|
|
if (RF_cmdQ.nSeqPost != RF_cmdQ.nSeqDone)
|
|
{
|
|
/* There are commands which not even dispatched yet. */
|
|
RF_Cmd* pCmd = RF_queueEnd(h, &RF_cmdQ.pPend);
|
|
|
|
/* There is no pending commmand, determine if there are items on the
|
|
other queues. */
|
|
if (!pCmd)
|
|
{
|
|
/* If the client is executing a command running. */
|
|
if (RF_isClientOwner(h, RF_cmdQ.pCurrCmdBg))
|
|
{
|
|
/* The currentlty running command is the last. */
|
|
pCmd = RF_cmdQ.pCurrCmdBg;
|
|
}
|
|
else
|
|
{
|
|
/* All commands has been dispatched, some just need to be served. This also
|
|
can return with NULL if nothing to be done. */
|
|
pCmd = RF_queueEnd(h, &RF_cmdQ.pDone);
|
|
}
|
|
}
|
|
|
|
/* Pend until the running command terminates */
|
|
if (pCmd)
|
|
{
|
|
RF_pendCmd(h, pCmd->ch, RF_TERMINATION_EVENT_MASK);
|
|
}
|
|
}
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Clear the RF_sch client handle */
|
|
if (h == RF_Sch.clientHnd[0])
|
|
{
|
|
RF_Sch.clientHnd[0] = NULL;
|
|
}
|
|
else
|
|
{
|
|
RF_Sch.clientHnd[1] = NULL;
|
|
}
|
|
|
|
/* Check whether this is the last client */
|
|
if (--RF_numClients == 0)
|
|
{
|
|
/* If this is the last client, set it to be the active client */
|
|
RF_currClient = h;
|
|
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
/* Release the constraint on the RF resources */
|
|
RF_powerConstraintRelease(RF_PowerConstraintCmdQ);
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Wait until the radio is powered down (outside critical section) */
|
|
SemaphoreP_pend(&h->state.semSync, SemaphoreP_WAIT_FOREVER);
|
|
|
|
/* Enter critical section */
|
|
key = HwiP_disable();
|
|
}
|
|
|
|
/* Unregister shared RTOS objects initalized during RF_init by the first client */
|
|
SwiP_destruct(&RF_swiFsmObj);
|
|
HwiP_destruct(&RF_hwiCpe0Obj);
|
|
SwiP_destruct(&RF_swiHwObj);
|
|
HwiP_destruct(&RF_hwiHwObj);
|
|
ClockP_destruct(&RF_clkPowerUpObj);
|
|
ClockP_destruct(&RF_clkInactivityObj);
|
|
|
|
/* Unregister the wakeup notify callback */
|
|
Power_unregisterNotify(&RF_wakeupNotifyObj);
|
|
|
|
/* Release XOSC_HF dependency if it was set on board level. */
|
|
if (RFCC26XX_hwAttrs.xoscHfAlwaysNeeded == true)
|
|
{
|
|
Power_releaseDependency(PowerCC26XX_XOSC_HF);
|
|
}
|
|
}
|
|
|
|
/* If we're the current RF client, stop being it */
|
|
if (RF_currClient == h)
|
|
{
|
|
RF_currClient = NULL;
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Unregister client specific RTOS objects (these are not shared between clients) */
|
|
SemaphoreP_destruct(&h->state.semSync);
|
|
ClockP_destruct(&h->state.clkReqAccess);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== RF_getCurrentTime ========
|
|
* Get current time in RAT ticks
|
|
*/
|
|
uint32_t RF_getCurrentTime(void)
|
|
{
|
|
/* Local variable */
|
|
uint64_t nCurrentTime = 0;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* If radio is active, read the RAT */
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
/* Read the RAT timer through register access */
|
|
nCurrentTime = RF_ratGetValue();
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* The radio is inactive, read the RTC instead */
|
|
nCurrentTime = AONRTCCurrent64BitValueGet();
|
|
|
|
/* Conservatively assume that we are just about to increment the RTC
|
|
Scale with the 4 MHz that the RAT is running
|
|
Add the RAT offset for RTC==0 */
|
|
nCurrentTime += RF_RTC_TICK_INC;
|
|
nCurrentTime *= RF_SCALE_RTC_TO_4MHZ;
|
|
nCurrentTime += ((uint64_t)RF_ratSyncCmd.start.rat0) << RF_SHIFT_32_BITS;
|
|
nCurrentTime >>= RF_SHIFT_32_BITS;
|
|
}
|
|
|
|
/* Return with the current value */
|
|
return((uint32_t) nCurrentTime);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_postCmd ========
|
|
* Post radio command
|
|
*/
|
|
RF_CmdHandle RF_postCmd(RF_Handle h, RF_Op* pOp, RF_Priority ePri, RF_Callback pCb, RF_EventMask bmEvent)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
DebugP_assert(pOp != NULL);
|
|
|
|
/* Local pointer to a radio commands */
|
|
RF_CmdHandle cmdHandle = (RF_CmdHandle)RF_ALLOC_ERROR;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Try to allocate container */
|
|
RF_Cmd* pCmd = RF_cmdAlloc();
|
|
|
|
/* If allocation failed */
|
|
if (pCmd)
|
|
{
|
|
/* Stop inactivity clock if running */
|
|
ClockP_stop(&RF_clkInactivityObj);
|
|
|
|
/* Increment the sequence number and mask the value */
|
|
RF_cmdQ.nSeqPost = (RF_cmdQ.nSeqPost + 1) & N_CMD_MODMASK;
|
|
|
|
/* Populate container with reset values */
|
|
pCmd->pOp = pOp;
|
|
pCmd->ePri = ePri;
|
|
pCmd->pCb = pCb;
|
|
pCmd->ch = RF_cmdQ.nSeqPost;
|
|
pCmd->pClient = h;
|
|
pCmd->bmEvent = (bmEvent | RFC_DBELL_RFCPEIFG_LAST_COMMAND_DONE_M) & ~RF_INTERNAL_IFG_MASK;
|
|
pCmd->pastifg = 0;
|
|
pCmd->flags = RF_CMD_ALLOC_FLAG;
|
|
|
|
/* Cancel ongoing yielding */
|
|
h->state.bYielded = false;
|
|
|
|
/* Submit to pending command to the queue. */
|
|
List_put(&RF_cmdQ.pPend, (List_Elem*)pCmd);
|
|
|
|
/* Trigger dispatcher if the timings need to be reconsidered. */
|
|
if (List_head(&RF_cmdQ.pPend) == (List_Elem*)pCmd)
|
|
{
|
|
RF_dispatchNextEvent();
|
|
}
|
|
|
|
/* Return with the command handle as success */
|
|
cmdHandle = pCmd->ch;
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with an error code */
|
|
return(cmdHandle);
|
|
}
|
|
|
|
/*
|
|
* ==================== RF_ScheduleCmdParams_init ============================
|
|
* Initialize the parameter structure to be used with RF_scheduleCmd().
|
|
*/
|
|
void RF_ScheduleCmdParams_init(RF_ScheduleCmdParams *pSchParams)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(pSchParams != NULL);
|
|
|
|
/* Set the configuration to use the default values. */
|
|
pSchParams->priority = RF_PriorityNormal;
|
|
pSchParams->endTime = 0;
|
|
pSchParams->allowDelay = RF_AllowDelayAny;
|
|
}
|
|
|
|
/*
|
|
* ==================== RF_scheduleCmd ============================
|
|
* Process request to schedule new command from a particular client
|
|
*/
|
|
RF_CmdHandle RF_scheduleCmd(RF_Handle h, RF_Op* pOp, RF_ScheduleCmdParams *pSchParams, RF_Callback pCb, RF_EventMask bmEvent)
|
|
{
|
|
/* Local variable declaration. */
|
|
RF_Cmd* pCmd;
|
|
RF_Handle h2;
|
|
RF_ScheduleStatus status;
|
|
|
|
/* Assert. */
|
|
DebugP_assert(h != NULL);
|
|
DebugP_assert(pOp != NULL);
|
|
|
|
/* Local pointer to a radio commands. */
|
|
RF_CmdHandle cmdHandle = (RF_CmdHandle)RF_ALLOC_ERROR;
|
|
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Assign h2 to client that is not issuing the new command.
|
|
The client h is issuing the new command. */
|
|
if (h == RF_Sch.clientHnd[0])
|
|
{
|
|
h2 = RF_Sch.clientHnd[1];
|
|
}
|
|
else
|
|
{
|
|
h2 = RF_Sch.clientHnd[0];
|
|
}
|
|
|
|
/* If client h2 already has, reject any new commands from h. */
|
|
if (h2 && (ClockP_isActive(&h2->state.clkReqAccess)))
|
|
{
|
|
/* Set the status value to schedule_error if we could not allocate space. */
|
|
cmdHandle = (RF_CmdHandle) RF_ScheduleStatusError;
|
|
|
|
/* Store the reason and the handle why the callback is being invoked. */
|
|
RF_Sch.issueRadioFreeCbFlags |= RF_RADIOFREECB_CMDREJECT_FLAG;
|
|
RF_Sch.clientHndRadioFreeCb = h;
|
|
}
|
|
else
|
|
{
|
|
/* Check if command queue has free entries and allocate RF_Op* container
|
|
if command queue is full reject the command. */
|
|
pCmd = RF_cmdAlloc();
|
|
|
|
/* If allocation was successful. */
|
|
if (pCmd)
|
|
{
|
|
/* Stop inactivity clock if running. */
|
|
ClockP_stop(&RF_clkInactivityObj);
|
|
|
|
/* Increment the sequence number and mask the value. */
|
|
RF_cmdQ.nSeqPost = (RF_cmdQ.nSeqPost + 1) & N_CMD_MODMASK;
|
|
|
|
/* Cache meta-data. */
|
|
pCmd->pOp = pOp;
|
|
pCmd->ePri = pSchParams->priority;
|
|
pCmd->pCb = pCb;
|
|
pCmd->ch = RF_cmdQ.nSeqPost;
|
|
pCmd->pClient = h;
|
|
pCmd->bmEvent = bmEvent & ~RF_INTERNAL_IFG_MASK;
|
|
pCmd->flags = 0;
|
|
pCmd->pastifg = 0;
|
|
pCmd->endTime = RF_SCH_CMD_ENDTIME_IGNORE;
|
|
pCmd->startTime = RF_SCH_CMD_STARTTIME_NOW;
|
|
pCmd->allowDelay = pSchParams->allowDelay;
|
|
|
|
/* Update the default endTime based on the scheduling parameters. */
|
|
if (pSchParams->endTime)
|
|
{
|
|
pCmd->endTime = pSchParams->endTime;
|
|
}
|
|
|
|
/* Update the default startTime based on the command parameters. */
|
|
if (pOp->startTrigger.triggerType == TRIG_ABSTIME)
|
|
{
|
|
pCmd->startTime = pOp->startTime;
|
|
}
|
|
|
|
/* Find the last radio operation within the chain. */
|
|
RF_Op* pEndOfChain = RF_findEndOfChain(pOp);
|
|
|
|
/* Mark the context of the command based on it's ID and subscribe it
|
|
to the expected termination event. */
|
|
if ((pEndOfChain->commandNo & RF_IEEE_ID_MASK) == RF_IEEE_FG_CMD)
|
|
{
|
|
pCmd->flags |= RF_CMD_FG_CMD_FLAG;
|
|
pCmd->bmEvent |= RFC_DBELL_RFCPEIFG_LAST_FG_COMMAND_DONE_M;
|
|
}
|
|
else
|
|
{
|
|
pCmd->bmEvent |= RFC_DBELL_RFCPEIFG_LAST_COMMAND_DONE_M;
|
|
}
|
|
|
|
/* Cancel the radio free callback if new command is from the same client. */
|
|
if ((RF_Sch.clientHndRadioFreeCb == h) &&
|
|
(RF_Sch.issueRadioFreeCbFlags & RF_RADIOFREECB_PREEMPT_FLAG))
|
|
{
|
|
RF_Sch.issueRadioFreeCbFlags &= ~RF_RADIOFREECB_PREEMPT_FLAG;
|
|
}
|
|
|
|
/* Invoke the submit policy which shall identify where exactly the new command is being
|
|
inserted based on the application level prioritization table. */
|
|
if (RFCC26XX_schedulerPolicy.submitHook == NULL)
|
|
{
|
|
status = RF_ScheduleStatusError;
|
|
}
|
|
else
|
|
{
|
|
/* Execute the scheduling logic and queue management. */
|
|
status = RFCC26XX_schedulerPolicy.submitHook(pCmd,
|
|
RF_cmdQ.pCurrCmdBg,
|
|
RF_cmdQ.pCurrCmdFg,
|
|
&RF_cmdQ.pPend,
|
|
&RF_cmdQ.pDone);
|
|
|
|
/* In case of rescheduling (re-entering the same command), the assigned handle will
|
|
not match and the counter need to be corrected. */
|
|
if ((status != RF_ScheduleStatusError) && (RF_cmdQ.nSeqPost != pCmd->ch))
|
|
{
|
|
/* Decrement the sequence number and mask the value. */
|
|
RF_cmdQ.nSeqPost = (RF_cmdQ.nSeqPost - 1) & N_CMD_MODMASK;
|
|
}
|
|
}
|
|
|
|
/* Command was rejected. Either there was no slot available, or the timing did not fit. */
|
|
if ((status == RF_ALLOC_ERROR) || (status == RF_ScheduleStatusError))
|
|
{
|
|
/* Decrement the sequence number and mask the value. */
|
|
RF_cmdQ.nSeqPost = (RF_cmdQ.nSeqPost - 1) & N_CMD_MODMASK;
|
|
|
|
/* Store the reason and the handle why the callback is being invoked. */
|
|
RF_Sch.issueRadioFreeCbFlags |= RF_RADIOFREECB_CMDREJECT_FLAG;
|
|
RF_Sch.clientHndRadioFreeCb = h;
|
|
|
|
/* Ensure that the error code reflects the reason of rejection. */
|
|
cmdHandle = (RF_CmdHandle) status;
|
|
}
|
|
else
|
|
{
|
|
/* Command was inserted. Return with the valid handle. */
|
|
cmdHandle = pCmd->ch;
|
|
|
|
/* Mark the command as being allocated. */
|
|
pCmd->flags |= RF_CMD_ALLOC_FLAG;
|
|
|
|
/* Cancel previous yielding. */
|
|
h->state.bYielded = false;
|
|
|
|
/* Trigger dispatcher if the timings need to be reconsidered. */
|
|
if (List_head(&RF_cmdQ.pPend) == (List_Elem*)pCmd)
|
|
{
|
|
RF_dispatchNextEvent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the command handle. */
|
|
return(cmdHandle);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_pendCmd ========
|
|
* Pend on radio command
|
|
*/
|
|
RF_EventMask RF_pendCmd(RF_Handle h, RF_CmdHandle ch, RF_EventMask bmEvent)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* If the command handle is invalid (i.e. RF_ALLOC_ERROR) */
|
|
if (ch < 0)
|
|
{
|
|
/* Return with zero means the command was rejected earlier */
|
|
return(0);
|
|
}
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = SwiP_disable();
|
|
|
|
/* Find the command based on its handle in the command pool */
|
|
RF_Cmd* pCmd = RF_cmdGet(h, ch, RF_CMD_ALLOC_FLAG);
|
|
|
|
/* If the command was already disposed */
|
|
if (!pCmd || !(pCmd->flags & RF_CMD_ALLOC_FLAG))
|
|
{
|
|
/* Exit critical section */
|
|
SwiP_restore(key);
|
|
|
|
/* Return with last command done event */
|
|
return(RF_EventLastCmdDone);
|
|
}
|
|
|
|
/* Expand the pend mask to accept RF_EventLastCmdDone and RF_EventLastFGCmdDone events even if it is not given explicitely */
|
|
bmEvent = (bmEvent | RF_TERMINATION_EVENT_MASK);
|
|
|
|
/* If the command is being executed, but the event we pending on has already happend (i.e. in a chain),
|
|
return the past events */
|
|
if (pCmd->pastifg & bmEvent)
|
|
{
|
|
/* Exit critical section */
|
|
SwiP_restore(key);
|
|
|
|
/* Store the cause of returning */
|
|
h->state.unpendCause = pCmd->pastifg & bmEvent;
|
|
|
|
/* Clear the handled past events so it is possible to pend again */
|
|
pCmd->pastifg &= ~h->state.unpendCause;
|
|
|
|
/* Return with the events */
|
|
return(h->state.unpendCause);
|
|
}
|
|
|
|
/* Command has still not finished, override user callback with one that calls the user callback then posts to semaphore */
|
|
if (pCmd->pCb != RF_syncCb)
|
|
{
|
|
/* Temporarily store the callback function */
|
|
h->state.pCbSync = (void*)pCmd->pCb;
|
|
|
|
/* Exhange the callback function: this will invoke the user callback and post to the semaphore if needed */
|
|
pCmd->pCb = RF_syncCb;
|
|
}
|
|
|
|
/* Store the event subscriptions in the clients context. This can only be one of the already enabled
|
|
interrupt sources by RF_postCmd (including RF_EventLastCmdDone) */
|
|
h->state.eventSync = bmEvent;
|
|
|
|
/* Exit critical section */
|
|
SwiP_restore(key);
|
|
|
|
/* Wait for semaphore */
|
|
SemaphoreP_pend(&h->state.semSync, SemaphoreP_WAIT_FOREVER);
|
|
|
|
/* Return the events that resulted in releasing the RF_pend() call */
|
|
return(h->state.unpendCause);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_runCmd ========
|
|
* Run to completion a posted command
|
|
*/
|
|
RF_EventMask RF_runCmd(RF_Handle h, RF_Op* pOp, RF_Priority ePri, RF_Callback pCb, RF_EventMask bmEvent)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Post the requested command */
|
|
RF_CmdHandle ch = RF_postCmd(h, pOp, ePri, pCb, bmEvent);
|
|
|
|
/* If the command was accepted, pend until one of the special events occur */
|
|
return(RF_pendCmd(h, ch, RF_TERMINATION_EVENT_MASK));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_runScheduleCmd ========
|
|
* Run to completion a scheduled command
|
|
*/
|
|
RF_EventMask RF_runScheduleCmd(RF_Handle h, RF_Op* pOp, RF_ScheduleCmdParams *pSchParams, RF_Callback pCb, RF_EventMask bmEvent)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Post the requested command */
|
|
RF_CmdHandle ch = RF_scheduleCmd(h, pOp, pSchParams, pCb, bmEvent);
|
|
|
|
/* If the command was accepted, pend until one of the special events occur */
|
|
return(RF_pendCmd(h, ch, RF_TERMINATION_EVENT_MASK));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_yieldCmd ========
|
|
* Release client access
|
|
*/
|
|
void RF_yield(RF_Handle h)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Request the synchronization of RTC and RAT at next power down. This is trigged
|
|
by ceiling the active time to the maximum value. */
|
|
RF_core.activeTimeUs = UINT32_MAX;
|
|
|
|
/* Stop ongoing request access and issue callback if the radio is off */
|
|
ClockP_stop((&h->state.clkReqAccess));
|
|
|
|
/* If all commands are done */
|
|
if (RF_cmdQ.nSeqDone == RF_cmdQ.nSeqPost)
|
|
{
|
|
if ((RF_core.status != RF_CoreStatusActive) && RF_Sch.issueRadioFreeCbFlags)
|
|
{
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Invoke the radio free callback provided by the user. */
|
|
RF_issueRadioFreeCb(RF_RADIOFREECB_REQACCESS_FLAG |
|
|
RF_RADIOFREECB_PREEMPT_FLAG |
|
|
RF_RADIOFREECB_CMDREJECT_FLAG);
|
|
|
|
/* Enter critical section. */
|
|
key = HwiP_disable();
|
|
}
|
|
}
|
|
|
|
/* If the radioFreeCb did not post new commands. */
|
|
if (RF_cmdQ.nSeqDone == RF_cmdQ.nSeqPost)
|
|
{
|
|
/* All commands are done. Stop inactivity timer. */
|
|
ClockP_stop(&RF_clkInactivityObj);
|
|
|
|
/* Potentially power down the RF core. */
|
|
RF_powerConstraintRelease(RF_PowerConstraintCmdQ);
|
|
}
|
|
else
|
|
{
|
|
/* There are still client commands that haven't finished.
|
|
Set flag to indicate immediate powerdown when last command is done. */
|
|
h->state.bYielded = true;
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_cancelCmd ========
|
|
* Cancel single radio command
|
|
*/
|
|
RF_Stat RF_cancelCmd(RF_Handle h, RF_CmdHandle ch, uint8_t mode)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Decode what method to be used for terminating the commands. */
|
|
bool graceful = (bool)(mode & RF_ABORT_GRACEFULLY);
|
|
bool flush = (bool)(mode & RF_ABORT_FLUSH_ALL);
|
|
bool preempt = (bool)(mode & RF_ABORT_PREEMPTION);
|
|
|
|
/* Invoke the aborting process with the input arguments on a single command */
|
|
return(RF_abortCmd(h, ch, graceful, flush, preempt));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_flushCmd ========
|
|
* Cancel multiple radio commands from a client
|
|
*/
|
|
RF_Stat RF_flushCmd(RF_Handle h, RF_CmdHandle ch, uint8_t mode)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Decode what method to be used for terminating the commands. */
|
|
bool graceful = (bool)(mode & RF_ABORT_GRACEFULLY);
|
|
bool flush = true;
|
|
bool preempt = (bool)(mode & RF_ABORT_PREEMPTION);
|
|
|
|
/* Abort multiple radio commands implicitly */
|
|
return(RF_abortCmd(h, ch, graceful, flush, preempt));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_Params_init ========
|
|
* Initialize the RF_params to default value
|
|
*/
|
|
void RF_Params_init(RF_Params *params)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(params != NULL);
|
|
|
|
/* Assign default values for RF_params */
|
|
*params = RF_defaultParams;
|
|
}
|
|
|
|
/*
|
|
* ======== RF_runImmediateCmd ========
|
|
* Run immediate command
|
|
*/
|
|
RF_Stat RF_runImmediateCmd(RF_Handle h, uint32_t* pCmd)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Submit the command to the RF core */
|
|
return(RF_runDirectImmediateCmd(h, (uint32_t)pCmd, NULL));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_runDirectCmd ========
|
|
* Run direct command
|
|
*/
|
|
RF_Stat RF_runDirectCmd(RF_Handle h, uint32_t cmd)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Submit the command to the RF core */
|
|
return(RF_runDirectImmediateCmd(h, cmd, NULL));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_getRssi ========
|
|
* Get RSSI value
|
|
*/
|
|
int8_t RF_getRssi(RF_Handle h)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Local variable. */
|
|
uint32_t rawRssi;
|
|
|
|
/* Read the RSSI value if possible. */
|
|
RF_Stat status = RF_runDirectImmediateCmd(h, CMDR_DIR_CMD(CMD_GET_RSSI), &rawRssi);
|
|
|
|
/* Decode the RSSI value if possible. */
|
|
if (status == RF_StatCmdDoneSuccess)
|
|
{
|
|
return((int8_t)((rawRssi >> RF_SHIFT_16_BITS) & RF_CMDSTA_REG_VAL_MASK));
|
|
}
|
|
else
|
|
{
|
|
return((int8_t)RF_GET_RSSI_ERROR_VAL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== RF_getInfo ========
|
|
* Get RF driver info
|
|
*/
|
|
RF_Stat RF_getInfo(RF_Handle h, RF_InfoType type, RF_InfoVal *pValue)
|
|
{
|
|
/* Local variables */
|
|
int8_t i = 0;
|
|
RF_Cmd* pCmd;
|
|
RF_ScheduleMapElement *pScheduleMap;
|
|
|
|
/* Prepare the default status value */
|
|
RF_Stat status = RF_StatSuccess;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Serve the different flavor of requests */
|
|
switch (type)
|
|
{
|
|
case RF_GET_CURR_CMD:
|
|
/* Get the handle of the currently running command. It can be conerted
|
|
to a pointer through the RF_getCmdOp() API. */
|
|
if (RF_cmdQ.pCurrCmdBg)
|
|
{
|
|
pValue->ch = RF_cmdQ.pCurrCmdBg->ch;
|
|
}
|
|
else
|
|
{
|
|
status = RF_StatError;
|
|
}
|
|
break;
|
|
|
|
case RF_GET_AVAIL_RAT_CH:
|
|
/* Get available user channels within the RAT timer.
|
|
These channels can be allocated and used by the application. */
|
|
pValue->availRatCh = RF_ratModule.availableRatChannels;
|
|
break;
|
|
|
|
case RF_GET_RADIO_STATE:
|
|
/* Get current radio state */
|
|
pValue->bRadioState = (RF_core.status == RF_CoreStatusActive) ? true : false;
|
|
break;
|
|
|
|
case RF_GET_CLIENT_LIST:
|
|
/* Copy the client pointer list ([0] -> client 1, [1] -> client 2) */
|
|
pValue->pClientList[0] = RF_Sch.clientHnd[0];
|
|
pValue->pClientList[1] = RF_Sch.clientHnd[1];
|
|
break;
|
|
|
|
case RF_GET_CLIENT_SWITCHING_TIME:
|
|
/* Copy the phy switching times to the RF_InfoVal structure */
|
|
pValue->phySwitchingTimeInUs[0] = RF_Sch.clientHnd[0] ? RF_Sch.clientHnd[0]->clientConfig.nPhySwitchingDuration : 0;
|
|
pValue->phySwitchingTimeInUs[1] = RF_Sch.clientHnd[1] ? RF_Sch.clientHnd[1]->clientConfig.nPhySwitchingDuration : 0;
|
|
break;
|
|
|
|
case RF_GET_SCHEDULE_MAP:
|
|
/* Get scheduler timing map. This can be used to determine the recent
|
|
time slots which are occupied by posted commands; and can help to
|
|
find out if a command can be inserted to the queue or not. In dual-mode
|
|
applications, this can help to sync the two protocol. */
|
|
pScheduleMap = (RF_ScheduleMapElement *)pValue->pScheduleMap;
|
|
memset(pScheduleMap, 0, sizeof(RF_ScheduleMapElement) * RF_NUM_SCHEDULE_MAP_ENTRIES);
|
|
|
|
for (i = 0; i < RF_NUM_SCHEDULE_ACCESS_ENTRIES; i++)
|
|
{
|
|
/* Copy access request info to schedule map */
|
|
pScheduleMap[i].pClient = RF_Sch.clientHnd[i];
|
|
pScheduleMap[i].priority = RF_Sch.accReq[i].priority;
|
|
uint32_t startTime = RF_Sch.accReq[i].startTime;
|
|
pScheduleMap[i].startTime = startTime;
|
|
pScheduleMap[i].endTime = startTime + RF_Sch.accReq[i].duration;
|
|
}
|
|
|
|
/* Check if there is current command running */
|
|
if (RF_cmdQ.pCurrCmdBg)
|
|
{
|
|
/* Copy current command info to schedule map */
|
|
pScheduleMap[i].pClient = RF_cmdQ.pCurrCmdBg->pClient;
|
|
pScheduleMap[i].ch = RF_cmdQ.pCurrCmdBg->ch;
|
|
pScheduleMap[i].priority = RF_cmdQ.pCurrCmdBg->ePri;
|
|
pScheduleMap[i].startTime = RF_cmdQ.pCurrCmdBg->startTime;
|
|
pScheduleMap[i].endTime = RF_cmdQ.pCurrCmdBg->endTime;
|
|
}
|
|
|
|
/* Increment the index to ensure a fixed location for the background command. */
|
|
i++;
|
|
|
|
/* Check if there is current command running */
|
|
if (RF_cmdQ.pCurrCmdFg)
|
|
{
|
|
/* Copy current command info to schedule map */
|
|
pScheduleMap[i].pClient = RF_cmdQ.pCurrCmdFg->pClient;
|
|
pScheduleMap[i].ch = RF_cmdQ.pCurrCmdFg->ch;
|
|
pScheduleMap[i].priority = RF_cmdQ.pCurrCmdFg->ePri;
|
|
pScheduleMap[i].startTime = RF_cmdQ.pCurrCmdFg->startTime;
|
|
pScheduleMap[i].endTime = RF_cmdQ.pCurrCmdFg->endTime;
|
|
}
|
|
|
|
/* Increment the index to ensure a fixed location for the foreground command. */
|
|
i++;
|
|
|
|
/* Check pending commands */
|
|
pCmd = (RF_Cmd*)List_head(&RF_cmdQ.pPend);
|
|
|
|
/* Loop until end of command queue or number of entries exceed */
|
|
while (pCmd)
|
|
{
|
|
if (i < RF_NUM_SCHEDULE_MAP_ENTRIES)
|
|
{
|
|
/* Copy pending command info to schedule map */
|
|
pScheduleMap[i].pClient = pCmd->pClient;
|
|
pScheduleMap[i].ch = pCmd->ch;
|
|
pScheduleMap[i].priority = pCmd->ePri;
|
|
pScheduleMap[i].startTime = pCmd->startTime;
|
|
pScheduleMap[i].endTime = pCmd->endTime;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* Number of entries exceeded, get out of loop */
|
|
break;
|
|
}
|
|
|
|
/* Walk the queue. */
|
|
pCmd = (RF_Cmd*)List_next((List_Elem*)pCmd);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
status = RF_StatInvalidParamsError;
|
|
break;
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with a status code */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_getCmdOp ========
|
|
* Get RF command
|
|
*/
|
|
RF_Op* RF_getCmdOp(RF_Handle h, RF_CmdHandle ch)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Find the command in the command pool based on its handle */
|
|
RF_Cmd* pCmd = RF_cmdGet(h, ch, RF_CMD_ALLOC_FLAG);
|
|
|
|
/* If the command is found */
|
|
if (pCmd)
|
|
{
|
|
/* Return with the first operation in the command */
|
|
return(pCmd->pOp);
|
|
}
|
|
else
|
|
{
|
|
/* Return with null in case of error */
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== RF_RatConfigCompare_init ========
|
|
* Initialize RAT compare configuration
|
|
*/
|
|
void RF_RatConfigCompare_init(RF_RatConfigCompare* channelConfig)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(channelConfig != NULL);
|
|
|
|
/* Set the values to default. */
|
|
memset((void*)channelConfig, 0, sizeof(RF_RatConfigCompare));
|
|
|
|
/* Set the default allocation method to use any channel. */
|
|
channelConfig->channel = RF_RatChannelAny;
|
|
}
|
|
|
|
/*
|
|
* ======== RF_RatConfigCapture_init ========
|
|
* Initialize RAT capture configuration
|
|
*/
|
|
void RF_RatConfigCapture_init(RF_RatConfigCapture* channelConfig)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(channelConfig != NULL);
|
|
|
|
/* Set the values to default. */
|
|
memset((void*)channelConfig, 0, sizeof(RF_RatConfigCapture));
|
|
|
|
/* Set the default allocation method to use any channel. */
|
|
channelConfig->channel = RF_RatChannelAny;
|
|
}
|
|
|
|
/*
|
|
* ======== RF_RatConfigOutput_init ========
|
|
* Initialize RAT IO configuration
|
|
*/
|
|
void RF_RatConfigOutput_init(RF_RatConfigOutput* ioConfig)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(ioConfig != NULL);
|
|
|
|
/* Set the values to default. */
|
|
memset((void*)ioConfig, 0, sizeof(RF_RatConfigOutput));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_ratCompare ========
|
|
* Set RAT compare
|
|
*/
|
|
RF_RatHandle RF_ratCompare(RF_Handle rfHandle, RF_RatConfigCompare* channelConfig, RF_RatConfigOutput* ioConfig)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Configure the RAT channel into COMPARE mode. */
|
|
return(RF_ratSetupChannel(rfHandle, RF_RatModeCompare, channelConfig->callback, channelConfig->channel, (void*) channelConfig, ioConfig));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_ratCapture ========
|
|
* Set RAT capture
|
|
*/
|
|
RF_RatHandle RF_ratCapture(RF_Handle rfHandle, RF_RatConfigCapture* channelConfig, RF_RatConfigOutput* ioConfig)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Configure the RAT channel into CAPTURE mode. */
|
|
return(RF_ratSetupChannel(rfHandle, RF_RatModeCapture, channelConfig->callback, channelConfig->channel, (void*) channelConfig, ioConfig));
|
|
}
|
|
|
|
/*
|
|
* ======== RF_ratDisableChannel ========
|
|
* Disable RAT channel
|
|
*/
|
|
RF_Stat RF_ratDisableChannel(RF_Handle h, RF_RatHandle ratHandle)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Default return value */
|
|
RF_Stat status = RF_StatError;
|
|
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Find the pointer to the RAT channel configuration. */
|
|
RF_RatChannel* ratCh = RF_ratGetChannel(ratHandle);
|
|
|
|
/* If the provided handler is valid. */
|
|
if (ratCh && ratCh->status)
|
|
{
|
|
/* If the RF core is active, abort the RAT event. */
|
|
if (RF_core.status == RF_CoreStatusActive)
|
|
{
|
|
/* Calculate the configuration field of command (the channel we disable). */
|
|
uint16_t config = (uint16_t)(RF_RAT_CH_LOWEST + ratCh->handle) << RF_SHIFT_8_BITS;
|
|
|
|
/* Disable the channel within the RF core. */
|
|
status = RF_runDirectImmediateCmd(h, ((uint32_t)CMDR_DIR_CMD_2BYTE(CMD_DISABLE_RAT_CH, config)), NULL);
|
|
|
|
/* Free the container for further use. We do it after the direct command to be sure it is not powered down.
|
|
This will implicitely schedule the next event and run the power management accordingly. */
|
|
RF_ratFreeChannel(ratCh);
|
|
}
|
|
else
|
|
{
|
|
/* Set status to be successful. */
|
|
status = RF_StatCmdDoneSuccess;
|
|
|
|
/* Free the container for further use. If possible, power down the radio. */
|
|
RF_ratFreeChannel(ratCh);
|
|
|
|
/* Recalculate the next wakeup event if the radio was off. */
|
|
RF_dispatchNextEvent();
|
|
}
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the status code */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_control ========
|
|
* RF control
|
|
*/
|
|
RF_Stat RF_control(RF_Handle h, int8_t ctrl, void *args)
|
|
{
|
|
/* Assert */
|
|
DebugP_assert(h != NULL);
|
|
|
|
/* Prepare the return value for worst case scenario */
|
|
RF_Stat status = RF_StatSuccess;
|
|
|
|
/* Enter critical section */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Serve the different requests */
|
|
switch (ctrl)
|
|
{
|
|
case RF_CTRL_SET_INACTIVITY_TIMEOUT:
|
|
/* Update the inactivity timeout of the client.
|
|
This can be used if the value given at RF_open
|
|
need to be updated */
|
|
h->clientConfig.nInactivityTimeout = *(uint32_t *)args;
|
|
break;
|
|
|
|
case RF_CTRL_UPDATE_SETUP_CMD:
|
|
/* Enable a special boot process which can be controlled
|
|
through the config field of the radio setup command.
|
|
This will influence only the next power up sequence
|
|
and will be reset automatically afterwards. The special
|
|
power up process will require longer power up time, hence
|
|
the nPowerUpDuration need to be increased */
|
|
h->clientConfig.bUpdateSetup = true;
|
|
h->clientConfig.nPowerUpDuration += RF_ANALOG_CFG_TIME_US;
|
|
break;
|
|
|
|
case RF_CTRL_SET_POWERUP_DURATION_MARGIN:
|
|
/* Configure the margin which is added to the measured
|
|
nPowerUpDuration. This can ensure that the commands
|
|
are executed on time, depending on the load of the
|
|
cpu */
|
|
h->clientConfig.nPowerUpDurationMargin = *(uint32_t *)args;
|
|
break;
|
|
|
|
case RF_CTRL_SET_PHYSWITCHING_DURATION_MARGIN:
|
|
/* Configure the margin which is added to the measured
|
|
nPowerUpDuration. This can ensure that the commands
|
|
are executed on time, depending on the load of the
|
|
cpu */
|
|
h->clientConfig.nPhySwitchingDurationMargin = *(uint32_t *)args;
|
|
break;
|
|
|
|
case RF_CTRL_SET_RAT_RTC_ERR_TOL_VAL:
|
|
/* Configure the tolerance value which is used to determine
|
|
the period when the RAT need to be syncronized to the RTC
|
|
due to the frequency offset */
|
|
RF_errTolValInUs = *(uint32_t*)args;
|
|
break;
|
|
|
|
case RF_CTRL_SET_POWER_MGMT:
|
|
/* The RF drivers power management can be enabled/disabled by
|
|
directly setting the power constraints from the application.
|
|
It is important that the order of actions align. */
|
|
if (*(uint32_t*)args == 0)
|
|
{
|
|
RF_powerConstraintSet(RF_PowerConstraintDisallow);
|
|
}
|
|
else if (*(uint32_t*)args == 1)
|
|
{
|
|
RF_powerConstraintRelease(RF_PowerConstraintDisallow);
|
|
}
|
|
else
|
|
{
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
break;
|
|
|
|
case RF_CTRL_SET_HWI_PRIORITY:
|
|
/* Changing priorities during run-time has constraints.
|
|
To not mess up with the RF driver, we require the RF
|
|
driver to be inactive. */
|
|
if (RF_core.status || (List_head(&RF_cmdQ.pPend)))
|
|
{
|
|
status = RF_StatBusyError;
|
|
}
|
|
else
|
|
{
|
|
HwiP_setPriority(INT_RFC_CPE_0, *(uint32_t *)args);
|
|
HwiP_setPriority(INT_RFC_HW_COMB, *(uint32_t *)args);
|
|
}
|
|
break;
|
|
|
|
case RF_CTRL_SET_SWI_PRIORITY:
|
|
/* Changing priorities during run-time has constraints.
|
|
To not mess up with the RF driver, we require the RF
|
|
driver to be inactive. */
|
|
if (RF_core.status || (List_head(&RF_cmdQ.pPend)))
|
|
{
|
|
status = RF_StatBusyError;
|
|
}
|
|
else
|
|
{
|
|
SwiP_setPriority(&RF_swiFsmObj, *(uint32_t *)args);
|
|
SwiP_setPriority(&RF_swiHwObj, *(uint32_t *)args);
|
|
}
|
|
break;
|
|
|
|
case RF_CTRL_SET_AVAILABLE_RAT_CHANNELS_MASK:
|
|
/* Mask the available RAT channels manually. This can be used when
|
|
a particular RAT channel is used through oridnary radio operations
|
|
instead of the dedicated RAT APIs. */
|
|
RF_ratModule.availableRatChannels = *(uint8_t *)args;
|
|
break;
|
|
|
|
default:
|
|
/* Request can not be served */
|
|
status = RF_StatInvalidParamsError;
|
|
break;
|
|
}
|
|
|
|
/* Exit critical section */
|
|
HwiP_restore(key);
|
|
|
|
/* Return with the status code */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_requestAccess ========
|
|
* RF request access
|
|
*/
|
|
RF_Stat RF_requestAccess(RF_Handle h, RF_AccessParams *pParams)
|
|
{
|
|
/* Assert. */
|
|
DebugP_assert(h != NULL);
|
|
DebugP_assert(pParams != NULL);
|
|
|
|
/* By default, the status is set to busy. */
|
|
RF_Stat status = RF_StatBusyError;
|
|
|
|
/* Convert the requested duration to us. */
|
|
uint32_t durationInUs = RF_convertRatTicksToUs(pParams->duration);
|
|
|
|
/* Check if the requested period is within the acceptable range. */
|
|
if (durationInUs > RF_REQ_ACCESS_MAX_DUR_US)
|
|
{
|
|
/* Reject the request if not. */
|
|
status = RF_StatInvalidParamsError;
|
|
}
|
|
|
|
/* Enter critical section. */
|
|
uint32_t key = HwiP_disable();
|
|
|
|
/* Determine the ID of the requesting client. */
|
|
uint8_t clientIdx = 0;
|
|
if (h == RF_Sch.clientHnd[1])
|
|
{
|
|
clientIdx = 1;
|
|
}
|
|
|
|
/* Get handle to the other client. */
|
|
RF_Handle h2 = RF_Sch.clientHnd[clientIdx ^ 0x1];
|
|
|
|
/* Check if the radio is free and if request can be served.
|
|
If possible update the RF_Sch structure and start the timer (RTC)
|
|
for the request access duration, else, return RF_StatBusyError. */
|
|
if (!(h && ClockP_isActive(&h->state.clkReqAccess)) &&
|
|
!(h2 && ClockP_isActive(&h2->state.clkReqAccess)))
|
|
{
|
|
/* Update the scheduler. */
|
|
RF_Sch.accReq[clientIdx].duration = pParams->duration;
|
|
RF_Sch.accReq[clientIdx].priority = pParams->priority;
|
|
|
|
/* Start timeout of the request. */
|
|
RF_restartClockTimeout(&h->state.clkReqAccess, durationInUs/ClockP_tickPeriod);
|
|
|
|
/* Set status to success after the access was granted. */
|
|
status = RF_StatSuccess;
|
|
}
|
|
else
|
|
{
|
|
/* In case the request can not be served, prepare for a notification
|
|
callback when the radio becomes available. */
|
|
RF_Sch.issueRadioFreeCbFlags |= RF_RADIOFREECB_REQACCESS_FLAG;
|
|
RF_Sch.clientHndRadioFreeCb = h;
|
|
}
|
|
|
|
/* Exit critical section. */
|
|
HwiP_restore(key);
|
|
|
|
/* Return the status. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_setTxPower ========
|
|
* Set the TX power of the client
|
|
*/
|
|
RF_Stat RF_setTxPower(RF_Handle handle, RF_TxPowerTable_Value value)
|
|
{
|
|
/* Local variable stores the return value. */
|
|
RF_Stat status;
|
|
|
|
/* Placeholder of the command to be used to update the PA configuration within the RF core immediately. */
|
|
RF_ConfigurePaCmd configurePaCmd;
|
|
|
|
/* Update the setup command to make the changes permanent. */
|
|
status = RF_updatePaConfiguration(handle->clientConfig.pRadioSetup, value, &configurePaCmd);
|
|
|
|
/* If we managed to decode and cache the changes in the setup command. */
|
|
if (status == RF_StatSuccess)
|
|
{
|
|
/* Execute the necessary command to apply the changes. It only takes effect if the RF core
|
|
is active and we configure the current client. The IO configuration can be re-evaluated when
|
|
the RF core issues the PA_CHANGED interrupt. */
|
|
RF_runDirectImmediateCmd(handle, (uint32_t)&configurePaCmd, NULL);
|
|
}
|
|
|
|
/* Return with the status. */
|
|
return(status);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_getTxPower ========
|
|
* Get the current TX power value
|
|
*/
|
|
RF_TxPowerTable_Value RF_getTxPower(RF_Handle handle)
|
|
{
|
|
/* Default return value. */
|
|
RF_TxPowerTable_Value value = { .rawValue = RF_TxPowerTable_INVALID_VALUE,
|
|
.paType = RF_TxPowerTable_DefaultPA};
|
|
|
|
/* Local variables. */
|
|
uint16_t* pTxPower = NULL;
|
|
uint32_t* pRegOverride = NULL;
|
|
uint32_t* pRegOverrideTxStd = NULL;
|
|
uint32_t* pRegOverrideTx20 = NULL;
|
|
|
|
/* Decode if High Gain PA is available. */
|
|
bool tx20FeatureAvailable = RF_decodeOverridePointers(handle->clientConfig.pRadioSetup, &pTxPower, &pRegOverride, &pRegOverrideTxStd, &pRegOverrideTx20);
|
|
|
|
/* Continue the search for the poper value if the High PA is used. */
|
|
if (*pTxPower == RF_TX20_ENABLED)
|
|
{
|
|
/* Local variable. */
|
|
uint32_t rawValue;
|
|
|
|
/* Returning the High Gain PA gain is only possible if the P device is in use. */
|
|
if (tx20FeatureAvailable && pRegOverrideTxStd && pRegOverrideTx20)
|
|
{
|
|
if (RF_getPAOverrideOffsetAndValue(pRegOverrideTx20, RF_TX20_PATTERN, &rawValue) != RF_TX_OVERRIDE_INVALID_OFFSET)
|
|
{
|
|
/* Return the value found in the gain related list. */
|
|
value.rawValue = rawValue;
|
|
value.paType = RF_TxPowerTable_HighPA;
|
|
}
|
|
}
|
|
else if (tx20FeatureAvailable)
|
|
{
|
|
if (RF_getPAOverrideOffsetAndValue(pRegOverride, RF_TX20_PATTERN, &rawValue) != RF_TX_OVERRIDE_INVALID_OFFSET)
|
|
{
|
|
/* As a backup option, parse the common list too. This is or backward compatibility
|
|
and new software shall not rely on this feature. */
|
|
value.rawValue = rawValue;
|
|
value.paType = RF_TxPowerTable_HighPA;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The value in the .txPower field represents the output power.*/
|
|
value.rawValue = *pTxPower;
|
|
}
|
|
|
|
/* Return with the decoded value. */
|
|
return(value);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_TxPowerTable_findPowerLevel ========
|
|
* Retrieves a power level in dBm for a given power configuration value.
|
|
*/
|
|
int8_t RF_TxPowerTable_findPowerLevel(RF_TxPowerTable_Entry table[], RF_TxPowerTable_Value value)
|
|
{
|
|
/* Iterate through the power table. We do not verify against nullptr. */
|
|
uint32_t i;
|
|
for (i=0; (table[i].power != RF_TxPowerTable_INVALID_DBM) &&
|
|
(table[i].value.rawValue != RF_TxPowerTable_INVALID_VALUE); i++)
|
|
{
|
|
if (((uint32_t)table[i].value.paType == (uint32_t)value.paType) &&
|
|
((uint32_t)table[i].value.rawValue == (uint32_t)value.rawValue))
|
|
{
|
|
/* Break the loop on the first entry which satisfies
|
|
the lower-or-equal criterion toward the input argument. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return with the power level in dBm or with the
|
|
termination value RF_TxPowerTable_INVALID_DBM. */
|
|
return(table[i].power);
|
|
}
|
|
|
|
/*
|
|
* ======== RF_TxPowerTable_findValue ========
|
|
* Retrieves a power configuration value for a given power level in dBm.
|
|
*/
|
|
RF_TxPowerTable_Value RF_TxPowerTable_findValue(RF_TxPowerTable_Entry table[], int8_t powerLevel)
|
|
{
|
|
/* Local variable stores an invalid value. */
|
|
RF_TxPowerTable_Value invalidValue = { .rawValue = RF_TxPowerTable_INVALID_VALUE,
|
|
.paType = RF_TxPowerTable_DefaultPA };
|
|
|
|
/* Handle special input argument. */
|
|
if (powerLevel == RF_TxPowerTable_MIN_DBM)
|
|
{
|
|
return(table[0].value);
|
|
}
|
|
else
|
|
{
|
|
/* Iterate through the power table. We do not verify against nullptr. */
|
|
uint32_t i;
|
|
for (i=0; ((int8_t)table[i].power != (int8_t)RF_TxPowerTable_INVALID_DBM) &&
|
|
((uint32_t)table[i].value.rawValue != (uint32_t)RF_TxPowerTable_INVALID_VALUE); i++)
|
|
{
|
|
if (table[i].power > powerLevel)
|
|
{
|
|
/* Break the loop on the first entry which satisfies
|
|
the lower-or-equal criterion toward the input argument. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
/* If the first entry is already larger, then the requested
|
|
power level is invalid. */
|
|
return(invalidValue);
|
|
}
|
|
else
|
|
{
|
|
/* Return with a valid RF_TxPowerTable_Value or with the
|
|
maximum value in the table. */
|
|
return(table[i-1].value);
|
|
}
|
|
}
|
|
}
|