1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/kw2xrf/kw2xrf_getset.c
Jose Alamos 4ebcd7c055
drivers/kw2xrf: add IEEE 802.15.4 Radio HAL support
Co-authored-by: Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
2022-08-15 12:11:03 +02:00

475 lines
13 KiB
C

/*
* Copyright (C) 2016 PHYTEC Messtechnik GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup drivers_kw2xrf
* @{
* @file
* @brief get/set functionality of kw2xrf driver
*
* @author Johann Fischer <j.fischer@phytec.de>
* @author Jonas Remmert <j.remmert@phytec.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Sebastian Meiling <s@mlng.net>
* @}
*/
#include "log.h"
#include "kw2xrf.h"
#include "kw2xrf_spi.h"
#include "kw2xrf_reg.h"
#include "kw2xrf_getset.h"
#include "kw2xrf_intern.h"
#include "byteorder.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define KW2XRF_LQI_HW_MAX 230 /**< LQI Saturation Level */
/* Modem_PA_PWR Register (PA Power Control) has a valid range from 3-31 */
#define KW2XRF_PA_RANGE_MAX 31 /**< Maximum value of PA Power Control Register */
#define KW2XRF_PA_RANGE_MIN 3 /**< Minimum value of PA Power Control Register */
#define KW2XRF_NUM_CHANNEL (KW2XRF_MAX_CHANNEL - KW2XRF_MIN_CHANNEL + 1)
/* PLL integer and fractional lookup tables
*
* Fc = 2405 + 5(k - 11) , k = 11,12,...,26
*
* Equation for PLL frequency, MKW2xD Reference Manual, p.255 :
* F = ((PLL_INT0 + 64) + (PLL_FRAC0/65536))32MHz
*
*/
static const uint8_t pll_int_lt[16] = {
11, 11, 11, 11,
11, 11, 12, 12,
12, 12, 12, 12,
13, 13, 13, 13
};
static const uint16_t pll_frac_lt[16] = {
10240, 20480, 30720, 40960,
51200, 61440, 6144, 16384,
26624, 36864, 47104, 57344,
2048, 12288, 22528, 32768
};
static const uint8_t pow_lt[44] = {
3, 4, 4, 5,
6, 6, 7, 7,
8, 9, 9, 10,
11, 11, 12, 13,
13, 14, 14, 15,
16, 16, 17, 18,
18, 19, 20, 20,
21, 21, 22, 23,
23, 24, 25, 25,
26, 27, 27, 28,
28, 29, 30, 31
};
void kw2xrf_set_tx_power(kw2xrf_t *dev, int16_t txpower)
{
if (txpower > KW2XDRF_OUTPUT_POWER_MAX) {
txpower = KW2XDRF_OUTPUT_POWER_MAX;
}
if (txpower < KW2XDRF_OUTPUT_POWER_MIN) {
txpower = KW2XDRF_OUTPUT_POWER_MIN;
}
uint8_t level = pow_lt[txpower - KW2XDRF_OUTPUT_POWER_MIN];
kw2xrf_write_dreg(dev, MKW2XDM_PA_PWR, MKW2XDM_PA_PWR(level));
LOG_DEBUG("[kw2xrf] set txpower to: %d\n", txpower);
dev->tx_power = txpower;
}
uint16_t kw2xrf_get_txpower(kw2xrf_t *dev)
{
return dev->tx_power;
}
uint8_t kw2xrf_get_channel(kw2xrf_t *dev)
{
uint8_t pll_int = kw2xrf_read_dreg(dev, MKW2XDM_PLL_INT0);
uint16_t pll_frac = kw2xrf_read_dreg(dev, MKW2XDM_PLL_FRAC0_LSB);
pll_frac |= ((uint16_t)kw2xrf_read_dreg(dev, MKW2XDM_PLL_FRAC0_MSB) << 8);
for (unsigned i = 0; i < KW2XRF_NUM_CHANNEL; i++) {
if ((pll_frac_lt[i] == pll_frac) && (pll_int_lt[i] == pll_int)) {
return i + 11;
}
}
return 0;
}
static int kw2xrf_get_sequence(kw2xrf_t *dev)
{
int reg = 0;
reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK;
return reg;
}
int kw2xrf_set_channel(kw2xrf_t *dev, uint8_t channel)
{
/* Save old sequence to restore this state later */
uint8_t old_seq = kw2xrf_get_sequence(dev);
if (channel < KW2XRF_MIN_CHANNEL || channel > KW2XRF_MAX_CHANNEL) {
LOG_ERROR("[kw2xrf] Invalid channel %u\n", channel);
return -1;
}
if (old_seq) {
kw2xrf_abort_sequence(dev);
}
uint8_t tmp = channel - 11;
kw2xrf_write_dreg(dev, MKW2XDM_PLL_INT0, MKW2XDM_PLL_INT0_VAL(pll_int_lt[tmp]));
kw2xrf_write_dreg(dev, MKW2XDM_PLL_FRAC0_LSB, (uint8_t)pll_frac_lt[tmp]);
kw2xrf_write_dreg(dev, MKW2XDM_PLL_FRAC0_MSB, (uint8_t)(pll_frac_lt[tmp] >> 8));
if (old_seq) {
kw2xrf_set_sequence(dev, old_seq);
}
LOG_DEBUG("[kw2xrf] set channel to %u\n", channel);
return 0;
}
void kw2xrf_abort_sequence(kw2xrf_t *dev)
{
uint8_t regs[MKW2XDM_PHY_CTRL4 + 1];
kw2xrf_mask_irq_b(dev);
kw2xrf_read_dregs(dev, MKW2XDM_IRQSTS1, regs, (MKW2XDM_PHY_CTRL4 + 1));
if ((regs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) != XCVSEQ_IDLE) {
/* abort any ongoing sequence */
regs[MKW2XDM_PHY_CTRL1] &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, regs[MKW2XDM_PHY_CTRL1]);
}
uint8_t state;
do {
state = kw2xrf_read_dreg(dev, MKW2XDM_SEQ_STATE);
DEBUG("[kw2xrf] abort SEQ_STATE: %x\n", state);
} while ((state & 0x1F) != 0);
/* clear all IRQ bits */
regs[MKW2XDM_IRQSTS1] = 0x7f;
regs[MKW2XDM_IRQSTS2] = 0x03;
regs[MKW2XDM_IRQSTS3] |= 0x0f;
kw2xrf_write_dregs(dev, MKW2XDM_IRQSTS1, regs, 3);
kw2xrf_enable_irq_b(dev);
}
/*
* Simplified version for irq handling where the state of
* the sequenz manager is known.
*/
void kw2xrf_set_idle_sequence(kw2xrf_t *dev)
{
kw2xrf_mask_irq_b(dev);
uint8_t reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
/* reset sequenz manager */
reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);
if (dev->pending_tx) {
DEBUG("[kw2xrf] pending tx, cannot set idle sequenz\n");
return;
}
/* start new sequenz */
reg |= MKW2XDM_PHY_CTRL1_XCVSEQ(dev->idle_state);
kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);
switch (dev->idle_state) {
case XCVSEQ_IDLE:
/* for inexplicable reasons, the receive mode is also idle mode */
case XCVSEQ_RECEIVE:
dev->state = NETOPT_STATE_IDLE;
break;
case XCVSEQ_CONTINUOUS_CCA:
case XCVSEQ_CCA:
dev->state = NETOPT_STATE_RX;
break;
case XCVSEQ_TRANSMIT:
case XCVSEQ_TX_RX:
dev->state = NETOPT_STATE_TX;
break;
default:
dev->state = NETOPT_STATE_IDLE;
}
kw2xrf_enable_irq_b(dev);
}
void kw2xrf_set_sequence(kw2xrf_t *dev, kw2xrf_physeq_t seq)
{
uint8_t reg = 0;
kw2xrf_abort_sequence(dev);
switch (seq) {
case XCVSEQ_IDLE:
/* for inexplicable reasons, the receive mode is also idle mode */
case XCVSEQ_RECEIVE:
dev->state = NETOPT_STATE_IDLE;
break;
case XCVSEQ_CONTINUOUS_CCA:
case XCVSEQ_CCA:
dev->state = NETOPT_STATE_RX;
break;
case XCVSEQ_TRANSMIT:
case XCVSEQ_TX_RX:
dev->state = NETOPT_STATE_TX;
break;
default:
DEBUG("[kw2xrf] undefined state assigned to phy\n");
dev->state = NETOPT_STATE_IDLE;
}
DEBUG("[kw2xrf] set sequence to %i\n", seq);
reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
reg |= MKW2XDM_PHY_CTRL1_XCVSEQ(seq);
kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);
}
void kw2xrf_set_pan(kw2xrf_t *dev, uint16_t pan)
{
uint8_t val_ar[2];
val_ar[1] = (pan >> 8);
val_ar[0] = (uint8_t)pan;
kw2xrf_write_iregs(dev, MKW2XDMI_MACPANID0_LSB, val_ar, 2);
LOG_DEBUG("[kw2xrf] set pan to: 0x%x\n", pan);
}
void kw2xrf_set_addr_short(kw2xrf_t *dev, uint16_t addr)
{
uint8_t val_ar[2];
val_ar[0] = (addr >> 8);
val_ar[1] = (uint8_t)addr;
#ifdef MODULE_SIXLOWPAN
/* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to
* 0 for unicast addresses */
val_ar[0] &= 0x7F;
#endif
kw2xrf_write_iregs(dev, MKW2XDMI_MACSHORTADDRS0_LSB, val_ar,
IEEE802154_SHORT_ADDRESS_LEN);
}
void kw2xrf_set_addr_long(kw2xrf_t *dev, uint64_t addr)
{
uint64_t tmp;
uint8_t *ap = (uint8_t *)(&tmp);
for (unsigned i = 0; i < IEEE802154_LONG_ADDRESS_LEN; i++) {
ap[i] = (addr >> ((IEEE802154_LONG_ADDRESS_LEN - 1 - i) * 8));
}
kw2xrf_write_iregs(dev, MKW2XDMI_MACLONGADDRS0_0, ap,
IEEE802154_LONG_ADDRESS_LEN);
}
uint16_t kw2xrf_get_addr_short(kw2xrf_t *dev)
{
uint16_t addr;
uint8_t *ap = (uint8_t *)(&addr);
kw2xrf_read_iregs(dev, MKW2XDMI_MACSHORTADDRS0_LSB, ap,
IEEE802154_SHORT_ADDRESS_LEN);
return byteorder_swaps(addr);
}
uint64_t kw2xrf_get_addr_long(kw2xrf_t *dev)
{
uint64_t addr;
uint8_t *ap = (uint8_t *)(&addr);
kw2xrf_read_iregs(dev, MKW2XDMI_MACLONGADDRS0_0, ap,
IEEE802154_LONG_ADDRESS_LEN);
/* Address is always read as little endian and API specifies big endian */
return byteorder_swapll(addr);
}
int8_t kw2xrf_get_cca_threshold(kw2xrf_t *dev)
{
uint8_t tmp;
kw2xrf_read_iregs(dev, MKW2XDMI_CCA1_THRESH, &tmp, 1);
/* KW2x register value represents absolute value in dBm
* default value: -75 dBm
*/
return (-tmp);
}
void kw2xrf_set_cca_threshold(kw2xrf_t *dev, int8_t value)
{
/* normalize to absolute value */
if (value < 0) {
value = -value;
}
kw2xrf_write_iregs(dev, MKW2XDMI_CCA1_THRESH, (uint8_t*)&value, 1);
}
void kw2xrf_set_cca_mode(kw2xrf_t *dev, uint8_t mode)
{
uint8_t tmp;
tmp = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL4);
tmp &= ~MKW2XDM_PHY_CTRL4_CCATYPE_MASK;
tmp |= MKW2XDM_PHY_CTRL4_CCATYPE(mode);
kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL4, tmp);
}
uint8_t kw2xrf_get_cca_mode(kw2xrf_t *dev)
{
uint8_t tmp;
tmp = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL4);
return (tmp & MKW2XDM_PHY_CTRL4_CCATYPE_MASK) >> MKW2XDM_PHY_CTRL4_CCATYPE_SHIFT;
}
int16_t kw2xrf_get_rssi(uint32_t value)
{
/* Get rssi (Received Signal Strength Indicator, unit is dBm)
* from lqi (Link Quality Indicator) value.
* There are two different equations for RSSI:
* RF = (LQI - 286.6) / 2.69333 (MKW2xD Reference Manual)
* RF = (LQI - 295.4) / 2.84 (MCR20A Reference Manual)
* The last appears more to match the graphic (Figure 3-10).
* Since RSSI value is always positive and we want to
* avoid the floating point computation:
* -RF * 65536 = (LQI / 2.84 - 295.4 / 2.84) * 65536
* RF * 65536 = (295.4 * 65536 / 2.84) - (LQI * 65536 / 2.84)
*/
uint32_t a = (uint32_t)(295.4 * 65536 / 2.84);
uint32_t b = (uint32_t)(65536 / 2.84);
return ((b * value) - a) >> 16;
}
void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state)
{
DEBUG("[kw2xrf] set option %i to %i\n", option, state);
/* set option field */
if (state) {
/* trigger option specific actions */
switch (option) {
case KW2XRF_OPT_AUTOCCA:
LOG_DEBUG("[kw2xrf] opt: enabling CCA before TX mode\n");
kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_CCABFRTX);
break;
case KW2XRF_OPT_PROMISCUOUS:
LOG_DEBUG("[kw2xrf] opt: enabling PROMISCUOUS mode\n");
/* disable auto ACKs in promiscuous mode */
kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_AUTOACK | MKW2XDM_PHY_CTRL1_RXACKRQD);
/* enable promiscuous mode */
kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL4,
MKW2XDM_PHY_CTRL4_PROMISCUOUS);
break;
case KW2XRF_OPT_AUTOACK:
kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_AUTOACK);
break;
case KW2XRF_OPT_ACK_REQ:
kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_RXACKRQD);
break;
default:
/* do nothing */
break;
}
}
else {
/* trigger option specific actions */
switch (option) {
case KW2XRF_OPT_AUTOCCA:
kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_CCABFRTX);
break;
case KW2XRF_OPT_PROMISCUOUS:
/* disable promiscuous mode */
kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL4,
MKW2XDM_PHY_CTRL4_PROMISCUOUS);
break;
case KW2XRF_OPT_AUTOACK:
kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_AUTOACK);
break;
case KW2XRF_OPT_ACK_REQ:
kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
MKW2XDM_PHY_CTRL1_RXACKRQD);
break;
default:
/* do nothing */
break;
}
}
}
netopt_state_t kw2xrf_get_status(kw2xrf_t *dev)
{
uint8_t reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
switch (reg & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) {
case XCVSEQ_RECEIVE:
return NETOPT_STATE_RX;
case XCVSEQ_TRANSMIT:
return NETOPT_STATE_TX;
case XCVSEQ_CCA:
return NETOPT_STATE_RX;
case XCVSEQ_TX_RX:
return NETOPT_STATE_TX;
case XCVSEQ_CONTINUOUS_CCA:
return NETOPT_STATE_RX;
case XCVSEQ_IDLE:
return NETOPT_STATE_IDLE;
default:
break;
}
return NETOPT_STATE_IDLE;
}
int kw2xrf_cca(kw2xrf_t *dev)
{
/* TODO: add Standalone CCA here */
kw2xrf_seq_timeout_on(dev, 0x3ffff);
kw2xrf_set_sequence(dev, XCVSEQ_CONTINUOUS_CCA);
return 0;
}
void kw2xrf_set_rx_watermark(kw2xrf_t *dev, uint8_t value)
{
kw2xrf_write_iregs(dev, MKW2XDMI_RX_WTR_MARK, &value, 1);
}