blob: 54957c1ccbd2630e0b3e49cdf0a53e5356c7f53a [file] [log] [blame]
/* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of Nordic Semiconductor ASA 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 HOLDER 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.
*
*/
/**
* @file
* This file implements common function for Front End Module control of the nRF 802.15.4 radio driver.
*
*/
#include "nrf_fem_control_api.h"
#include <assert.h>
#include "compiler_abstraction.h"
#include "nrf_fem_control_config.h"
#include "nrf_802154_config.h"
#include "nrf.h"
#include "nrf_gpio.h"
#include "nrf_gpiote.h"
#include "nrf_ppi.h"
#include "nrf_radio.h"
#include "nrf_timer.h"
#define NRF_FEM_TIMER_INSTANCE NRF_802154_TIMER_INSTANCE
#if ENABLE_FEM
static nrf_fem_control_cfg_t m_nrf_fem_control_cfg; /**< FEM controller configuration. */
/** Check whether pin is valid and enabled. */
static bool pin_is_enabled(nrf_fem_control_pin_t pin)
{
switch (pin)
{
case NRF_FEM_CONTROL_LNA_PIN:
return m_nrf_fem_control_cfg.lna_cfg.enable;
case NRF_FEM_CONTROL_PA_PIN:
return m_nrf_fem_control_cfg.pa_cfg.enable;
case NRF_FEM_CONTROL_ANY_PIN:
return m_nrf_fem_control_cfg.lna_cfg.enable || m_nrf_fem_control_cfg.pa_cfg.enable;
default:
assert(false);
return false;
}
}
/**
* @section GPIO control.
*/
/** Initialize GPIO according to configuration provided. */
static void gpio_init(void)
{
if (m_nrf_fem_control_cfg.pa_cfg.enable)
{
nrf_gpio_cfg_output(m_nrf_fem_control_cfg.pa_cfg.gpio_pin);
nrf_gpio_pin_write(m_nrf_fem_control_cfg.pa_cfg.gpio_pin,
!m_nrf_fem_control_cfg.pa_cfg.active_high);
}
if (m_nrf_fem_control_cfg.lna_cfg.enable)
{
nrf_gpio_cfg_output(m_nrf_fem_control_cfg.lna_cfg.gpio_pin);
nrf_gpio_pin_write(m_nrf_fem_control_cfg.lna_cfg.gpio_pin,
!m_nrf_fem_control_cfg.lna_cfg.active_high);
}
}
/** Configure GPIOTE module. */
static void gpiote_configure(void)
{
if (m_nrf_fem_control_cfg.lna_cfg.enable)
{
nrf_gpiote_task_configure(m_nrf_fem_control_cfg.lna_gpiote_ch_id,
m_nrf_fem_control_cfg.lna_cfg.gpio_pin,
(nrf_gpiote_polarity_t)GPIOTE_CONFIG_POLARITY_None,
(nrf_gpiote_outinit_t) !m_nrf_fem_control_cfg.lna_cfg.active_high);
nrf_gpiote_task_enable(m_nrf_fem_control_cfg.lna_gpiote_ch_id);
}
if (m_nrf_fem_control_cfg.pa_cfg.enable)
{
nrf_gpiote_task_configure(m_nrf_fem_control_cfg.pa_gpiote_ch_id,
m_nrf_fem_control_cfg.pa_cfg.gpio_pin,
(nrf_gpiote_polarity_t)GPIOTE_CONFIG_POLARITY_None,
(nrf_gpiote_outinit_t) !m_nrf_fem_control_cfg.pa_cfg.active_high);
nrf_gpiote_task_enable(m_nrf_fem_control_cfg.pa_gpiote_ch_id);
}
}
/**
* @section PPI control.
*/
/** Initialize PPI according to configuration provided. */
static void ppi_init(void)
{
/* RADIO DISABLED --> clr LNA & clr PA PPI */
if (m_nrf_fem_control_cfg.lna_cfg.enable && m_nrf_fem_control_cfg.pa_cfg.enable)
{
nrf_ppi_channel_and_fork_endpoint_setup(
(nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr,
(uint32_t)(&NRF_RADIO->EVENTS_DISABLED),
(m_nrf_fem_control_cfg.lna_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.lna_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.lna_gpiote_ch_id])),
(m_nrf_fem_control_cfg.pa_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.pa_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.pa_gpiote_ch_id])));
}
else if (m_nrf_fem_control_cfg.lna_cfg.enable)
{
nrf_ppi_channel_endpoint_setup(
(nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr,
(uint32_t)(&NRF_RADIO->EVENTS_DISABLED),
(m_nrf_fem_control_cfg.lna_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.lna_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.lna_gpiote_ch_id])));
}
else if (m_nrf_fem_control_cfg.pa_cfg.enable)
{
nrf_ppi_channel_endpoint_setup(
(nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr,
(uint32_t)(&NRF_RADIO->EVENTS_DISABLED),
(m_nrf_fem_control_cfg.pa_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.pa_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.pa_gpiote_ch_id])));
}
else
{
// Intentionally empty
}
}
/** Setup PPI to set LNA pin on a timer event. */
static void ppi_lna_enable_setup(nrf_timer_cc_channel_t timer_cc_channel)
{
/* TIMER0->COMPARE --> set LNA PPI */
nrf_ppi_channel_endpoint_setup(
(nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set,
(uint32_t)(&NRF_FEM_TIMER_INSTANCE->EVENTS_COMPARE[timer_cc_channel]),
(m_nrf_fem_control_cfg.lna_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.lna_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.lna_gpiote_ch_id])));
}
/** Setup PPI to set PA pin on a timer event. */
static void ppi_pa_enable_setup(nrf_timer_cc_channel_t timer_cc_channel)
{
/* TIMER2->COMPARE --> set PA PPI */
nrf_ppi_channel_endpoint_setup(
(nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set,
(uint32_t)(&NRF_FEM_TIMER_INSTANCE->EVENTS_COMPARE[timer_cc_channel]),
(m_nrf_fem_control_cfg.pa_cfg.active_high ?
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.pa_gpiote_ch_id]) :
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.pa_gpiote_ch_id])));
}
/**
* @section FEM API functions.
*/
void nrf_fem_control_cfg_set(const nrf_fem_control_cfg_t * p_cfg)
{
m_nrf_fem_control_cfg = *p_cfg;
if (m_nrf_fem_control_cfg.pa_cfg.enable || m_nrf_fem_control_cfg.lna_cfg.enable)
{
gpio_init();
gpiote_configure();
ppi_init();
nrf_ppi_channel_enable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr);
}
}
void nrf_fem_control_cfg_get(nrf_fem_control_cfg_t * p_cfg)
{
*p_cfg = m_nrf_fem_control_cfg;
}
void nrf_fem_control_activate(void)
{
if (m_nrf_fem_control_cfg.pa_cfg.enable || m_nrf_fem_control_cfg.lna_cfg.enable)
{
nrf_ppi_channel_enable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr);
}
}
void nrf_fem_control_deactivate(void)
{
if (m_nrf_fem_control_cfg.pa_cfg.enable || m_nrf_fem_control_cfg.lna_cfg.enable)
{
nrf_ppi_channel_disable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_clr);
nrf_ppi_channel_disable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set);
}
}
void nrf_fem_control_ppi_enable(nrf_fem_control_pin_t pin, nrf_timer_cc_channel_t timer_cc_channel)
{
if (pin_is_enabled(pin))
{
switch (pin)
{
case NRF_FEM_CONTROL_LNA_PIN:
ppi_lna_enable_setup(timer_cc_channel);
nrf_ppi_channel_enable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set);
break;
case NRF_FEM_CONTROL_PA_PIN:
ppi_pa_enable_setup(timer_cc_channel);
nrf_ppi_channel_enable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set);
break;
default:
assert(false);
break;
}
}
}
void nrf_fem_control_ppi_disable(nrf_fem_control_pin_t pin)
{
if (pin_is_enabled(pin))
{
nrf_ppi_channel_disable((nrf_ppi_channel_t)m_nrf_fem_control_cfg.ppi_ch_id_set);
}
}
uint32_t nrf_fem_control_delay_get(nrf_fem_control_pin_t pin)
{
uint32_t target_time = 1;
if (pin_is_enabled(pin))
{
switch (pin)
{
case NRF_FEM_CONTROL_LNA_PIN:
target_time = NRF_FEM_RADIO_RX_STARTUP_LATENCY_US - NRF_FEM_LNA_TIME_IN_ADVANCE;
break;
case NRF_FEM_CONTROL_PA_PIN:
target_time = NRF_FEM_RADIO_TX_STARTUP_LATENCY_US - NRF_FEM_PA_TIME_IN_ADVANCE;
break;
default:
assert(false);
break;
}
}
return target_time;
}
void nrf_fem_control_pin_clear(void)
{
if (pin_is_enabled(NRF_FEM_CONTROL_PA_PIN))
{
nrf_gpiote_task_force(m_nrf_fem_control_cfg.pa_gpiote_ch_id,
(nrf_gpiote_outinit_t) !m_nrf_fem_control_cfg.pa_cfg.active_high);
}
if (pin_is_enabled(NRF_FEM_CONTROL_LNA_PIN))
{
nrf_gpiote_task_force(m_nrf_fem_control_cfg.lna_gpiote_ch_id,
(nrf_gpiote_outinit_t) !m_nrf_fem_control_cfg.lna_cfg.active_high);
}
}
void nrf_fem_control_timer_set(nrf_fem_control_pin_t pin,
nrf_timer_cc_channel_t timer_cc_channel,
nrf_timer_short_mask_t short_mask)
{
if (pin_is_enabled(pin))
{
uint32_t target_time = nrf_fem_control_delay_get(pin);
nrf_timer_shorts_enable(NRF_FEM_TIMER_INSTANCE, short_mask);
nrf_timer_cc_write(NRF_FEM_TIMER_INSTANCE, timer_cc_channel, target_time);
}
}
void nrf_fem_control_timer_reset(nrf_fem_control_pin_t pin, nrf_timer_short_mask_t short_mask)
{
if (pin_is_enabled(pin))
{
// Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
nrf_timer_task_trigger(NRF_FEM_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_timer_shorts_disable(NRF_FEM_TIMER_INSTANCE, short_mask);
}
}
void nrf_fem_control_ppi_fork_setup(nrf_fem_control_pin_t pin,
nrf_ppi_channel_t ppi_channel,
uint32_t task_addr)
{
if (pin_is_enabled(pin))
{
nrf_ppi_fork_endpoint_setup(ppi_channel, task_addr);
}
}
void nrf_fem_control_ppi_task_setup(nrf_fem_control_pin_t pin,
nrf_ppi_channel_t ppi_channel,
uint32_t event_addr,
uint32_t task_addr)
{
if (pin_is_enabled(pin))
{
nrf_ppi_channel_endpoint_setup(ppi_channel, event_addr, task_addr);
nrf_ppi_channel_enable(ppi_channel);
}
}
void nrf_fem_control_ppi_fork_clear(nrf_fem_control_pin_t pin, nrf_ppi_channel_t ppi_channel)
{
if (pin_is_enabled(pin))
{
nrf_ppi_fork_endpoint_setup(ppi_channel, 0);
}
}
void nrf_fem_control_ppi_pin_task_setup(nrf_ppi_channel_t ppi_channel,
uint32_t event_addr,
bool lna_pin_set,
bool pa_pin_set)
{
uint32_t lna_task_addr = 0;
uint32_t pa_task_addr = 0;
if (m_nrf_fem_control_cfg.lna_cfg.enable)
{
if ((lna_pin_set && m_nrf_fem_control_cfg.lna_cfg.active_high) ||
(!lna_pin_set && !m_nrf_fem_control_cfg.lna_cfg.active_high))
{
lna_task_addr =
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.lna_gpiote_ch_id]);
}
else
{
lna_task_addr =
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.lna_gpiote_ch_id]);
}
}
if (m_nrf_fem_control_cfg.pa_cfg.enable)
{
if ((pa_pin_set && m_nrf_fem_control_cfg.pa_cfg.active_high) ||
(!pa_pin_set && !m_nrf_fem_control_cfg.pa_cfg.active_high))
{
pa_task_addr =
(uint32_t)(&NRF_GPIOTE->TASKS_SET[m_nrf_fem_control_cfg.pa_gpiote_ch_id]);
}
else
{
pa_task_addr =
(uint32_t)(&NRF_GPIOTE->TASKS_CLR[m_nrf_fem_control_cfg.pa_gpiote_ch_id]);
}
}
if (lna_task_addr != 0 || pa_task_addr != 0)
{
nrf_ppi_channel_and_fork_endpoint_setup(ppi_channel, event_addr, lna_task_addr,
pa_task_addr);
nrf_ppi_channel_enable(ppi_channel);
}
}
#endif // ENABLE_FEM