zephyr/drivers/ieee802154/ieee802154_dw1000.c

1694 lines
45 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020 PHYTEC Messtechnik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(dw1000, LOG_LEVEL_INF);
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/debug/stack.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/sys/byteorder.h>
#include <string.h>
#include <zephyr/random/random.h>
#include <zephyr/debug/stack.h>
#include <math.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/net/ieee802154_radio.h>
#include "ieee802154_dw1000_regs.h"
#define DT_DRV_COMPAT decawave_dw1000
#define DWT_FCS_LENGTH 2U
#define DWT_SPI_CSWAKEUP_FREQ 500000U
#define DWT_SPI_SLOW_FREQ 2000000U
#define DWT_SPI_TRANS_MAX_HDR_LEN 3
#define DWT_SPI_TRANS_REG_MAX_RANGE 0x3F
#define DWT_SPI_TRANS_SHORT_MAX_OFFSET 0x7F
#define DWT_SPI_TRANS_WRITE_OP BIT(7)
#define DWT_SPI_TRANS_SUB_ADDR BIT(6)
#define DWT_SPI_TRANS_EXTEND_ADDR BIT(7)
#define DWT_TS_TIME_UNITS_FS 15650U /* DWT_TIME_UNITS in fs */
#define DW1000_TX_ANT_DLY 16450
#define DW1000_RX_ANT_DLY 16450
/* SHR Symbol Duration in ns */
#define UWB_PHY_TPSYM_PRF64 IEEE802154_PHY_HRP_UWB_PRF64_TPSYM_SYMBOL_PERIOD_NS
#define UWB_PHY_TPSYM_PRF16 IEEE802154_PHY_HRP_UWB_PRF16_TPSYM_SYMBOL_PERIOD_NS
#define UWB_PHY_NUMOF_SYM_SHR_SFD 8
/* PHR Symbol Duration Tdsym in ns */
#define UWB_PHY_TDSYM_PHR_110K 8205.13
#define UWB_PHY_TDSYM_PHR_850K 1025.64
#define UWB_PHY_TDSYM_PHR_6M8 1025.64
#define UWB_PHY_NUMOF_SYM_PHR 18
/* Data Symbol Duration Tdsym in ns */
#define UWB_PHY_TDSYM_DATA_110K 8205.13
#define UWB_PHY_TDSYM_DATA_850K 1025.64
#define UWB_PHY_TDSYM_DATA_6M8 128.21
#define DWT_WORK_QUEUE_STACK_SIZE 512
static struct k_work_q dwt_work_queue;
static K_KERNEL_STACK_DEFINE(dwt_work_queue_stack,
DWT_WORK_QUEUE_STACK_SIZE);
struct dwt_phy_config {
uint8_t channel; /* Channel 1, 2, 3, 4, 5, 7 */
uint8_t dr; /* Data rate DWT_BR_110K, DWT_BR_850K, DWT_BR_6M8 */
uint8_t prf; /* PRF DWT_PRF_16M or DWT_PRF_64M */
uint8_t rx_pac_l; /* DWT_PAC8..DWT_PAC64 */
uint8_t rx_shr_code; /* RX SHR preamble code */
uint8_t rx_ns_sfd; /* non-standard SFD */
uint16_t rx_sfd_to; /* SFD timeout value (in symbols)
* (tx_shr_nsync + 1 + SFD_length - rx_pac_l)
*/
uint8_t tx_shr_code; /* TX SHR preamble code */
uint32_t tx_shr_nsync; /* PLEN index, e.g. DWT_PLEN_64 */
float t_shr;
float t_phr;
float t_dsym;
};
struct dwt_hi_cfg {
struct spi_dt_spec bus;
struct gpio_dt_spec irq_gpio;
struct gpio_dt_spec rst_gpio;
};
#define DWT_STATE_TX 0
#define DWT_STATE_CCA 1
#define DWT_STATE_RX_DEF_ON 2
struct dwt_context {
const struct device *dev;
struct net_if *iface;
const struct spi_config *spi_cfg;
struct spi_config spi_cfg_slow;
struct gpio_callback gpio_cb;
struct k_sem dev_lock;
struct k_sem phy_sem;
struct k_work irq_cb_work;
struct k_thread thread;
struct dwt_phy_config rf_cfg;
atomic_t state;
bool cca_busy;
uint16_t sleep_mode;
uint8_t mac_addr[8];
};
static const struct dwt_hi_cfg dw1000_0_config = {
.bus = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8), 0),
.irq_gpio = GPIO_DT_SPEC_INST_GET(0, int_gpios),
.rst_gpio = GPIO_DT_SPEC_INST_GET(0, reset_gpios),
};
static struct dwt_context dwt_0_context = {
.dev_lock = Z_SEM_INITIALIZER(dwt_0_context.dev_lock, 1, 1),
.phy_sem = Z_SEM_INITIALIZER(dwt_0_context.phy_sem, 0, 1),
.rf_cfg = {
.channel = 5,
.dr = DWT_BR_6M8,
.prf = DWT_PRF_64M,
.rx_pac_l = DWT_PAC8,
.rx_shr_code = 10,
.rx_ns_sfd = 0,
.rx_sfd_to = (129 + 8 - 8),
.tx_shr_code = 10,
.tx_shr_nsync = DWT_PLEN_128,
},
};
/* This struct is used to read all additional RX frame info at one push */
struct dwt_rx_info_regs {
uint8_t rx_fqual[DWT_RX_FQUAL_LEN];
uint8_t rx_ttcki[DWT_RX_TTCKI_LEN];
uint8_t rx_ttcko[DWT_RX_TTCKO_LEN];
/* RX_TIME without RX_RAWST */
uint8_t rx_time[DWT_RX_TIME_FP_RAWST_OFFSET];
} _packed;
static int dwt_configure_rf_phy(const struct device *dev);
static int dwt_spi_read(const struct device *dev,
uint16_t hdr_len, const uint8_t *hdr_buf,
uint32_t data_len, uint8_t *data)
{
struct dwt_context *ctx = dev->data;
const struct dwt_hi_cfg *hi_cfg = dev->config;
const struct spi_buf tx_buf = {
.buf = (uint8_t *)hdr_buf,
.len = hdr_len
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1
};
struct spi_buf rx_buf[2] = {
{
.buf = NULL,
.len = hdr_len,
},
{
.buf = (uint8_t *)data,
.len = data_len,
},
};
const struct spi_buf_set rx = {
.buffers = rx_buf,
.count = 2
};
LOG_DBG("spi read, header length %u, data length %u",
(uint16_t)hdr_len, (uint32_t)data_len);
LOG_HEXDUMP_DBG(hdr_buf, (uint16_t)hdr_len, "rd: header");
if (spi_transceive(hi_cfg->bus.bus, ctx->spi_cfg, &tx, &rx)) {
LOG_ERR("SPI transfer failed");
return -EIO;
}
LOG_HEXDUMP_DBG(data, (uint32_t)data_len, "rd: data");
return 0;
}
static int dwt_spi_write(const struct device *dev,
uint16_t hdr_len, const uint8_t *hdr_buf,
uint32_t data_len, const uint8_t *data)
{
struct dwt_context *ctx = dev->data;
const struct dwt_hi_cfg *hi_cfg = dev->config;
struct spi_buf buf[2] = {
{.buf = (uint8_t *)hdr_buf, .len = hdr_len},
{.buf = (uint8_t *)data, .len = data_len}
};
struct spi_buf_set buf_set = {.buffers = buf, .count = 2};
LOG_DBG("spi write, header length %u, data length %u",
(uint16_t)hdr_len, (uint32_t)data_len);
LOG_HEXDUMP_DBG(hdr_buf, (uint16_t)hdr_len, "wr: header");
LOG_HEXDUMP_DBG(data, (uint32_t)data_len, "wr: data");
if (spi_write(hi_cfg->bus.bus, ctx->spi_cfg, &buf_set)) {
LOG_ERR("SPI read failed");
return -EIO;
}
return 0;
}
/* See 2.2.1.2 Transaction formats of the SPI interface */
static int dwt_spi_transfer(const struct device *dev,
uint8_t reg, uint16_t offset,
size_t buf_len, uint8_t *buf, bool write)
{
uint8_t hdr[DWT_SPI_TRANS_MAX_HDR_LEN] = {0};
size_t hdr_len = 0;
hdr[0] = reg & DWT_SPI_TRANS_REG_MAX_RANGE;
hdr_len += 1;
if (offset != 0) {
hdr[0] |= DWT_SPI_TRANS_SUB_ADDR;
hdr[1] = (uint8_t)offset & DWT_SPI_TRANS_SHORT_MAX_OFFSET;
hdr_len += 1;
if (offset > DWT_SPI_TRANS_SHORT_MAX_OFFSET) {
hdr[1] |= DWT_SPI_TRANS_EXTEND_ADDR;
hdr[2] = (uint8_t)(offset >> 7);
hdr_len += 1;
}
}
if (write) {
hdr[0] |= DWT_SPI_TRANS_WRITE_OP;
return dwt_spi_write(dev, hdr_len, hdr, buf_len, buf);
} else {
return dwt_spi_read(dev, hdr_len, hdr, buf_len, buf);
}
}
static int dwt_register_read(const struct device *dev,
uint8_t reg, uint16_t offset, size_t buf_len, uint8_t *buf)
{
return dwt_spi_transfer(dev, reg, offset, buf_len, buf, false);
}
static int dwt_register_write(const struct device *dev,
uint8_t reg, uint16_t offset, size_t buf_len, uint8_t *buf)
{
return dwt_spi_transfer(dev, reg, offset, buf_len, buf, true);
}
static inline uint32_t dwt_reg_read_u32(const struct device *dev,
uint8_t reg, uint16_t offset)
{
uint8_t buf[sizeof(uint32_t)];
dwt_spi_transfer(dev, reg, offset, sizeof(buf), buf, false);
return sys_get_le32(buf);
}
static inline uint16_t dwt_reg_read_u16(const struct device *dev,
uint8_t reg, uint16_t offset)
{
uint8_t buf[sizeof(uint16_t)];
dwt_spi_transfer(dev, reg, offset, sizeof(buf), buf, false);
return sys_get_le16(buf);
}
static inline uint8_t dwt_reg_read_u8(const struct device *dev,
uint8_t reg, uint16_t offset)
{
uint8_t buf;
dwt_spi_transfer(dev, reg, offset, sizeof(buf), &buf, false);
return buf;
}
static inline void dwt_reg_write_u32(const struct device *dev,
uint8_t reg, uint16_t offset, uint32_t val)
{
uint8_t buf[sizeof(uint32_t)];
sys_put_le32(val, buf);
dwt_spi_transfer(dev, reg, offset, sizeof(buf), buf, true);
}
static inline void dwt_reg_write_u16(const struct device *dev,
uint8_t reg, uint16_t offset, uint16_t val)
{
uint8_t buf[sizeof(uint16_t)];
sys_put_le16(val, buf);
dwt_spi_transfer(dev, reg, offset, sizeof(buf), buf, true);
}
static inline void dwt_reg_write_u8(const struct device *dev,
uint8_t reg, uint16_t offset, uint8_t val)
{
dwt_spi_transfer(dev, reg, offset, sizeof(uint8_t), &val, true);
}
static ALWAYS_INLINE void dwt_setup_int(const struct device *dev,
bool enable)
{
const struct dwt_hi_cfg *hi_cfg = dev->config;
unsigned int flags = enable
? GPIO_INT_EDGE_TO_ACTIVE
: GPIO_INT_DISABLE;
gpio_pin_interrupt_configure_dt(&hi_cfg->irq_gpio, flags);
}
static void dwt_reset_rfrx(const struct device *dev)
{
/*
* Apply a receiver-only soft reset,
* see SOFTRESET field description in DW1000 User Manual.
*/
dwt_reg_write_u8(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_RX);
dwt_reg_write_u8(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_CLEAR);
}
static void dwt_disable_txrx(const struct device *dev)
{
dwt_setup_int(dev, false);
dwt_reg_write_u8(dev, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET,
DWT_SYS_CTRL_TRXOFF);
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, DWT_SYS_STATUS_OFFSET,
(DWT_SYS_STATUS_ALL_RX_GOOD |
DWT_SYS_STATUS_ALL_RX_TO |
DWT_SYS_STATUS_ALL_RX_ERR |
DWT_SYS_STATUS_ALL_TX));
dwt_setup_int(dev, true);
}
/* timeout time in units of 1.026 microseconds */
static int dwt_enable_rx(const struct device *dev, uint16_t timeout)
{
uint32_t sys_cfg;
uint16_t sys_ctrl = DWT_SYS_CTRL_RXENAB;
sys_cfg = dwt_reg_read_u32(dev, DWT_SYS_CFG_ID, 0);
if (timeout != 0) {
dwt_reg_write_u16(dev, DWT_RX_FWTO_ID, DWT_RX_FWTO_OFFSET,
timeout);
sys_cfg |= DWT_SYS_CFG_RXWTOE;
} else {
sys_cfg &= ~DWT_SYS_CFG_RXWTOE;
}
dwt_reg_write_u32(dev, DWT_SYS_CFG_ID, 0, sys_cfg);
dwt_reg_write_u16(dev, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET, sys_ctrl);
return 0;
}
static inline void dwt_irq_handle_rx_cca(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
k_sem_give(&ctx->phy_sem);
ctx->cca_busy = true;
/* Clear all RX event bits */
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0,
DWT_SYS_STATUS_ALL_RX_GOOD);
}
static inline void dwt_irq_handle_rx(const struct device *dev, uint32_t sys_stat)
{
struct dwt_context *ctx = dev->data;
struct net_pkt *pkt = NULL;
struct dwt_rx_info_regs rx_inf_reg;
float a_const;
uint32_t rx_finfo;
uint32_t ttcki;
uint32_t rx_pacc;
uint32_t cir_pwr;
uint32_t flags_to_clear;
int32_t ttcko;
uint16_t pkt_len;
uint8_t *fctrl;
int8_t rx_level = INT8_MIN;
LOG_DBG("RX OK event, SYS_STATUS 0x%08x", sys_stat);
flags_to_clear = sys_stat & DWT_SYS_STATUS_ALL_RX_GOOD;
rx_finfo = dwt_reg_read_u32(dev, DWT_RX_FINFO_ID, DWT_RX_FINFO_OFFSET);
pkt_len = rx_finfo & DWT_RX_FINFO_RXFLEN_MASK;
rx_pacc = (rx_finfo & DWT_RX_FINFO_RXPACC_MASK) >>
DWT_RX_FINFO_RXPACC_SHIFT;
if (!(IS_ENABLED(CONFIG_IEEE802154_RAW_MODE))) {
pkt_len -= DWT_FCS_LENGTH;
}
pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, pkt_len,
AF_UNSPEC, 0, K_NO_WAIT);
if (!pkt) {
LOG_ERR("No buf available");
goto rx_out_enable_rx;
}
dwt_register_read(dev, DWT_RX_BUFFER_ID, 0, pkt_len, pkt->buffer->data);
dwt_register_read(dev, DWT_RX_FQUAL_ID, 0, sizeof(rx_inf_reg),
(uint8_t *)&rx_inf_reg);
net_buf_add(pkt->buffer, pkt_len);
fctrl = pkt->buffer->data;
/*
* Get Ranging tracking offset and tracking interval
* for Crystal characterization
*/
ttcki = sys_get_le32(rx_inf_reg.rx_ttcki);
ttcko = sys_get_le32(rx_inf_reg.rx_ttcko) & DWT_RX_TTCKO_RXTOFS_MASK;
/* Tracking offset value is a 19-bit signed integer */
if (ttcko & BIT(18)) {
ttcko |= ~DWT_RX_TTCKO_RXTOFS_MASK;
}
/* TODO add:
* net_pkt_set_ieee802154_tcki(pkt, ttcki);
* net_pkt_set_ieee802154_tcko(pkt, ttcko);
*/
LOG_DBG("ttcko %d ttcki: 0x%08x", ttcko, ttcki);
if (IS_ENABLED(CONFIG_NET_PKT_TIMESTAMP)) {
uint8_t ts_buf[sizeof(uint64_t)] = {0};
uint64_t ts_nsec;
memcpy(ts_buf, rx_inf_reg.rx_time, DWT_RX_TIME_RX_STAMP_LEN);
ts_nsec = (sys_get_le64(ts_buf) * DWT_TS_TIME_UNITS_FS) / 1000000U;
net_pkt_set_timestamp_ns(pkt, ts_nsec);
}
/* See 4.7.2 Estimating the receive signal power */
cir_pwr = sys_get_le16(&rx_inf_reg.rx_fqual[6]);
if (ctx->rf_cfg.prf == DWT_PRF_16M) {
a_const = DWT_RX_SIG_PWR_A_CONST_PRF16;
} else {
a_const = DWT_RX_SIG_PWR_A_CONST_PRF64;
}
if (rx_pacc != 0) {
#if defined(CONFIG_NEWLIB_LIBC)
/* From 4.7.2 Estimating the receive signal power */
rx_level = 10.0 * log10f(cir_pwr * BIT(17) /
(rx_pacc * rx_pacc)) - a_const;
#endif
}
net_pkt_set_ieee802154_rssi_dbm(pkt, rx_level);
/*
* Workaround for AAT status bit issue,
* From 5.3.5 Host Notification in DW1000 User Manual:
* "Note: there is a situation that can result in the AAT bit being set
* for the current frame as a result of a previous frame that was
* received and rejected due to frame filtering."
*/
if ((sys_stat & DWT_SYS_STATUS_AAT) && ((fctrl[0] & 0x20) == 0)) {
flags_to_clear |= DWT_SYS_STATUS_AAT;
}
if (ieee802154_handle_ack(ctx->iface, pkt) == NET_OK) {
LOG_INF("ACK packet handled");
goto rx_out_unref_pkt;
}
/* LQI not implemented */
LOG_DBG("Caught a packet (%u) (RSSI: %d)", pkt_len, rx_level);
LOG_HEXDUMP_DBG(pkt->buffer->data, pkt_len, "RX buffer:");
if (net_recv_data(ctx->iface, pkt) == NET_OK) {
goto rx_out_enable_rx;
} else {
LOG_DBG("Packet dropped by NET stack");
}
rx_out_unref_pkt:
if (pkt) {
net_pkt_unref(pkt);
}
rx_out_enable_rx:
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0, flags_to_clear);
LOG_DBG("Cleared SYS_STATUS flags 0x%08x", flags_to_clear);
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
/*
* Re-enable reception but in contrast to dwt_enable_rx()
* without to read SYS_STATUS and set delayed option.
*/
dwt_reg_write_u16(dev, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET,
DWT_SYS_CTRL_RXENAB);
}
}
static void dwt_irq_handle_tx(const struct device *dev, uint32_t sys_stat)
{
struct dwt_context *ctx = dev->data;
/* Clear TX event bits */
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0,
DWT_SYS_STATUS_ALL_TX);
LOG_DBG("TX confirmed event");
k_sem_give(&ctx->phy_sem);
}
static void dwt_irq_handle_rxto(const struct device *dev, uint32_t sys_stat)
{
struct dwt_context *ctx = dev->data;
/* Clear RX timeout event bits */
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0,
DWT_SYS_STATUS_RXRFTO);
dwt_disable_txrx(dev);
/* Receiver reset necessary, see 4.1.6 RX Message timestamp */
dwt_reset_rfrx(dev);
LOG_DBG("RX timeout event");
if (atomic_test_bit(&ctx->state, DWT_STATE_CCA)) {
k_sem_give(&ctx->phy_sem);
ctx->cca_busy = false;
}
}
static void dwt_irq_handle_error(const struct device *dev, uint32_t sys_stat)
{
struct dwt_context *ctx = dev->data;
/* Clear RX error event bits */
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0, DWT_SYS_STATUS_ALL_RX_ERR);
dwt_disable_txrx(dev);
/* Receiver reset necessary, see 4.1.6 RX Message timestamp */
dwt_reset_rfrx(dev);
LOG_INF("RX error event");
if (atomic_test_bit(&ctx->state, DWT_STATE_CCA)) {
k_sem_give(&ctx->phy_sem);
ctx->cca_busy = true;
return;
}
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
dwt_enable_rx(dev, 0);
}
}
static void dwt_irq_work_handler(struct k_work *item)
{
struct dwt_context *ctx = CONTAINER_OF(item, struct dwt_context,
irq_cb_work);
const struct device *dev = ctx->dev;
uint32_t sys_stat;
k_sem_take(&ctx->dev_lock, K_FOREVER);
sys_stat = dwt_reg_read_u32(dev, DWT_SYS_STATUS_ID, 0);
if (sys_stat & DWT_SYS_STATUS_RXFCG) {
if (atomic_test_bit(&ctx->state, DWT_STATE_CCA)) {
dwt_irq_handle_rx_cca(dev);
} else {
dwt_irq_handle_rx(dev, sys_stat);
}
}
if (sys_stat & DWT_SYS_STATUS_TXFRS) {
dwt_irq_handle_tx(dev, sys_stat);
}
if (sys_stat & DWT_SYS_STATUS_ALL_RX_TO) {
dwt_irq_handle_rxto(dev, sys_stat);
}
if (sys_stat & DWT_SYS_STATUS_ALL_RX_ERR) {
dwt_irq_handle_error(dev, sys_stat);
}
k_sem_give(&ctx->dev_lock);
}
static void dwt_gpio_callback(const struct device *dev,
struct gpio_callback *cb, uint32_t pins)
{
struct dwt_context *ctx = CONTAINER_OF(cb, struct dwt_context, gpio_cb);
LOG_DBG("IRQ callback triggered %p", ctx);
k_work_submit_to_queue(&dwt_work_queue, &ctx->irq_cb_work);
}
static enum ieee802154_hw_caps dwt_get_capabilities(const struct device *dev)
{
/* TODO: Implement HW-supported AUTOACK + frame pending bit handling. */
return IEEE802154_HW_FCS | IEEE802154_HW_FILTER |
IEEE802154_HW_TXTIME;
}
static uint32_t dwt_get_pkt_duration_ns(struct dwt_context *ctx, uint8_t psdu_len)
{
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
float t_psdu = rf_cfg->t_dsym * psdu_len * 8;
return (rf_cfg->t_shr + rf_cfg->t_phr + t_psdu);
}
static int dwt_cca(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
uint32_t cca_dur = (dwt_get_pkt_duration_ns(ctx, 127) +
dwt_get_pkt_duration_ns(ctx, 5)) /
UWB_PHY_TDSYM_PHR_6M8;
if (atomic_test_and_set_bit(&ctx->state, DWT_STATE_CCA)) {
LOG_ERR("Transceiver busy");
return -EBUSY;
}
/* Perform CCA Mode 5 */
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_disable_txrx(dev);
LOG_DBG("CCA duration %u us", cca_dur);
dwt_enable_rx(dev, cca_dur);
k_sem_give(&ctx->dev_lock);
k_sem_take(&ctx->phy_sem, K_FOREVER);
LOG_DBG("CCA finished %p", ctx);
atomic_clear_bit(&ctx->state, DWT_STATE_CCA);
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_enable_rx(dev, 0);
k_sem_give(&ctx->dev_lock);
}
return ctx->cca_busy ? -EBUSY : 0;
}
static int dwt_ed(const struct device *dev, uint16_t duration,
energy_scan_done_cb_t done_cb)
{
/* TODO: see description Sub-Register 0x23:02 AGC_CTRL1 */
return -ENOTSUP;
}
static int dwt_set_channel(const struct device *dev, uint16_t channel)
{
struct dwt_context *ctx = dev->data;
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
if (channel > 15) {
return -EINVAL;
}
if (channel == 0 || channel == 6 || channel > 7) {
return -ENOTSUP;
}
rf_cfg->channel = channel;
LOG_INF("Set channel %u", channel);
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_disable_txrx(dev);
dwt_configure_rf_phy(dev);
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
dwt_enable_rx(dev, 0);
}
k_sem_give(&ctx->dev_lock);
return 0;
}
static int dwt_set_pan_id(const struct device *dev, uint16_t pan_id)
{
struct dwt_context *ctx = dev->data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_reg_write_u16(dev, DWT_PANADR_ID, DWT_PANADR_PAN_ID_OFFSET, pan_id);
k_sem_give(&ctx->dev_lock);
LOG_INF("Set PAN ID 0x%04x %p", pan_id, ctx);
return 0;
}
static int dwt_set_short_addr(const struct device *dev, uint16_t short_addr)
{
struct dwt_context *ctx = dev->data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_reg_write_u16(dev, DWT_PANADR_ID, DWT_PANADR_SHORT_ADDR_OFFSET,
short_addr);
k_sem_give(&ctx->dev_lock);
LOG_INF("Set short 0x%x %p", short_addr, ctx);
return 0;
}
static int dwt_set_ieee_addr(const struct device *dev,
const uint8_t *ieee_addr)
{
struct dwt_context *ctx = dev->data;
LOG_INF("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_register_write(dev, DWT_EUI_64_ID, DWT_EUI_64_OFFSET,
DWT_EUI_64_LEN, (uint8_t *)ieee_addr);
k_sem_give(&ctx->dev_lock);
return 0;
}
static int dwt_filter(const struct device *dev,
bool set,
enum ieee802154_filter_type type,
const struct ieee802154_filter *filter)
{
if (!set) {
return -ENOTSUP;
}
if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) {
return dwt_set_ieee_addr(dev, filter->ieee_addr);
} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
return dwt_set_short_addr(dev, filter->short_addr);
} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
return dwt_set_pan_id(dev, filter->pan_id);
}
return -ENOTSUP;
}
static int dwt_set_power(const struct device *dev, int16_t dbm)
{
struct dwt_context *ctx = dev->data;
LOG_INF("set_txpower not supported %p", ctx);
return 0;
}
static int dwt_tx(const struct device *dev, enum ieee802154_tx_mode tx_mode,
struct net_pkt *pkt, struct net_buf *frag)
{
struct dwt_context *ctx = dev->data;
size_t len = frag->len;
uint32_t tx_time = 0;
uint64_t tmp_fs;
uint32_t tx_fctrl;
uint8_t sys_ctrl = DWT_SYS_CTRL_TXSTRT;
if (atomic_test_and_set_bit(&ctx->state, DWT_STATE_TX)) {
LOG_ERR("Transceiver busy");
return -EBUSY;
}
k_sem_reset(&ctx->phy_sem);
k_sem_take(&ctx->dev_lock, K_FOREVER);
switch (tx_mode) {
case IEEE802154_TX_MODE_DIRECT:
break;
case IEEE802154_TX_MODE_TXTIME:
/*
* tx_time is the high 32-bit of the 40-bit system
* time value at which to send the message.
*/
tmp_fs = net_pkt_timestamp_ns(pkt);
tmp_fs *= 1000U * 1000U;
tx_time = (tmp_fs / DWT_TS_TIME_UNITS_FS) >> 8;
sys_ctrl |= DWT_SYS_CTRL_TXDLYS;
/* DX_TIME is 40-bit register */
dwt_reg_write_u32(dev, DWT_DX_TIME_ID, 1, tx_time);
LOG_DBG("ntx hi32 %x", tx_time);
LOG_DBG("sys hi32 %x",
dwt_reg_read_u32(dev, DWT_SYS_TIME_ID, 1));
break;
default:
LOG_ERR("TX mode %d not supported", tx_mode);
goto error;
}
LOG_HEXDUMP_DBG(frag->data, len, "TX buffer:");
/*
* See "3 Message Transmission" in DW1000 User Manual for
* more details about transmission configuration.
*/
if (dwt_register_write(dev, DWT_TX_BUFFER_ID, 0, len, frag->data)) {
LOG_ERR("Failed to write TX data");
goto error;
}
tx_fctrl = dwt_reg_read_u32(dev, DWT_TX_FCTRL_ID, 0);
/* Clear TX buffer index offset, frame length, and length extension */
tx_fctrl &= ~(DWT_TX_FCTRL_TFLEN_MASK | DWT_TX_FCTRL_TFLE_MASK |
DWT_TX_FCTRL_TXBOFFS_MASK);
/* Set frame length and ranging flag */
tx_fctrl |= (len + DWT_FCS_LENGTH) & DWT_TX_FCTRL_TFLEN_MASK;
tx_fctrl |= DWT_TX_FCTRL_TR;
/* Update Transmit Frame Control register */
dwt_reg_write_u32(dev, DWT_TX_FCTRL_ID, 0, tx_fctrl);
dwt_disable_txrx(dev);
/* Begin transmission */
dwt_reg_write_u8(dev, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET, sys_ctrl);
if (sys_ctrl & DWT_SYS_CTRL_TXDLYS) {
uint32_t sys_stat = dwt_reg_read_u32(dev, DWT_SYS_STATUS_ID, 0);
if (sys_stat & DWT_SYS_STATUS_HPDWARN) {
LOG_WRN("Half Period Delay Warning");
}
}
k_sem_give(&ctx->dev_lock);
/* Wait for the TX confirmed event */
k_sem_take(&ctx->phy_sem, K_FOREVER);
if (IS_ENABLED(CONFIG_NET_PKT_TIMESTAMP)) {
uint8_t ts_buf[sizeof(uint64_t)] = {0};
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_register_read(dev, DWT_TX_TIME_ID,
DWT_TX_TIME_TX_STAMP_OFFSET,
DWT_TX_TIME_TX_STAMP_LEN,
ts_buf);
LOG_DBG("ts hi32 %x", (uint32_t)(sys_get_le64(ts_buf) >> 8));
LOG_DBG("sys hi32 %x",
dwt_reg_read_u32(dev, DWT_SYS_TIME_ID, 1));
k_sem_give(&ctx->dev_lock);
tmp_fs = sys_get_le64(ts_buf) * DWT_TS_TIME_UNITS_FS;
net_pkt_set_timestamp_ns(pkt, tmp_fs / 1000000U);
}
atomic_clear_bit(&ctx->state, DWT_STATE_TX);
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_enable_rx(dev, 0);
k_sem_give(&ctx->dev_lock);
}
return 0;
error:
atomic_clear_bit(&ctx->state, DWT_STATE_TX);
k_sem_give(&ctx->dev_lock);
return -EIO;
}
static void dwt_set_frame_filter(const struct device *dev,
bool ff_enable, uint8_t ff_type)
{
uint32_t sys_cfg_ff = ff_enable ? DWT_SYS_CFG_FFE : 0;
sys_cfg_ff |= ff_type & DWT_SYS_CFG_FF_ALL_EN;
dwt_reg_write_u8(dev, DWT_SYS_CFG_ID, 0, (uint8_t)sys_cfg_ff);
}
static int dwt_configure(const struct device *dev,
enum ieee802154_config_type type,
const struct ieee802154_config *config)
{
struct dwt_context *ctx = dev->data;
LOG_DBG("API configure %p", ctx);
switch (type) {
case IEEE802154_CONFIG_AUTO_ACK_FPB:
LOG_DBG("IEEE802154_CONFIG_AUTO_ACK_FPB");
break;
case IEEE802154_CONFIG_ACK_FPB:
LOG_DBG("IEEE802154_CONFIG_ACK_FPB");
break;
case IEEE802154_CONFIG_PAN_COORDINATOR:
LOG_DBG("IEEE802154_CONFIG_PAN_COORDINATOR");
break;
case IEEE802154_CONFIG_PROMISCUOUS:
LOG_DBG("IEEE802154_CONFIG_PROMISCUOUS");
break;
case IEEE802154_CONFIG_EVENT_HANDLER:
LOG_DBG("IEEE802154_CONFIG_EVENT_HANDLER");
break;
default:
return -EINVAL;
}
return -ENOTSUP;
}
/* driver-allocated attribute memory - constant across all driver instances */
static const struct {
const struct ieee802154_phy_channel_range phy_channel_range[2];
const struct ieee802154_phy_supported_channels phy_supported_channels;
} drv_attr = {
.phy_channel_range = {
{ .from_channel = 1, .to_channel = 5 },
{ .from_channel = 7, .to_channel = 7 },
},
.phy_supported_channels = {
.ranges = drv_attr.phy_channel_range,
.num_ranges = 2U,
},
};
static int dwt_attr_get(const struct device *dev, enum ieee802154_attr attr,
struct ieee802154_attr_value *value)
{
if (ieee802154_attr_get_channel_page_and_range(
attr, IEEE802154_ATTR_PHY_CHANNEL_PAGE_FOUR_HRP_UWB,
&drv_attr.phy_supported_channels, value) == 0) {
return 0;
}
switch (attr) {
case IEEE802154_ATTR_PHY_HRP_UWB_SUPPORTED_PRFS: {
struct dwt_context *ctx = dev->data;
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
value->phy_hrp_uwb_supported_nominal_prfs =
rf_cfg->prf == DWT_PRF_64M ? IEEE802154_PHY_HRP_UWB_NOMINAL_64_M
: IEEE802154_PHY_HRP_UWB_NOMINAL_16_M;
return 0;
}
default:
return -ENOENT;
}
}
/*
* Note, the DW_RESET pin should not be driven high externally.
*/
static int dwt_hw_reset(const struct device *dev)
{
const struct dwt_hi_cfg *hi_cfg = dev->config;
if (gpio_pin_configure_dt(&hi_cfg->rst_gpio, GPIO_OUTPUT_ACTIVE)) {
LOG_ERR("Failed to configure GPIO pin %u", hi_cfg->rst_gpio.pin);
return -EINVAL;
}
k_sleep(K_MSEC(1));
gpio_pin_set_dt(&hi_cfg->rst_gpio, 0);
k_sleep(K_MSEC(5));
if (gpio_pin_configure_dt(&hi_cfg->rst_gpio, GPIO_INPUT)) {
LOG_ERR("Failed to configure GPIO pin %u", hi_cfg->rst_gpio.pin);
return -EINVAL;
}
return 0;
}
/*
* SPI speed in INIT state or for wake-up sequence,
* see 2.3.2 Overview of main operational states
*/
static void dwt_set_spi_slow(const struct device *dev, const uint32_t freq)
{
struct dwt_context *ctx = dev->data;
ctx->spi_cfg_slow.frequency = freq;
ctx->spi_cfg = &ctx->spi_cfg_slow;
}
/* SPI speed in IDLE, RX, and TX state */
static void dwt_set_spi_fast(const struct device *dev)
{
const struct dwt_hi_cfg *hi_cfg = dev->config;
struct dwt_context *ctx = dev->data;
ctx->spi_cfg = &hi_cfg->bus.config;
}
static void dwt_set_rx_mode(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
uint32_t pmsc_ctrl0;
uint32_t t_on_us;
uint8_t rx_sniff[2];
/* SNIFF Mode ON time in units of PAC */
rx_sniff[0] = CONFIG_IEEE802154_DW1000_SNIFF_ONT &
DWT_RX_SNIFF_SNIFF_ONT_MASK;
/* SNIFF Mode OFF time in microseconds */
rx_sniff[1] = CONFIG_IEEE802154_DW1000_SNIFF_OFFT;
t_on_us = (rx_sniff[0] + 1) * (BIT(3) << rf_cfg->rx_pac_l);
LOG_INF("RX duty cycle %u%%", t_on_us * 100 / (t_on_us + rx_sniff[1]));
dwt_register_write(dev, DWT_RX_SNIFF_ID, DWT_RX_SNIFF_OFFSET,
sizeof(rx_sniff), rx_sniff);
pmsc_ctrl0 = dwt_reg_read_u32(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET);
/* Enable PLL2 on/off sequencing for SNIFF mode */
pmsc_ctrl0 |= DWT_PMSC_CTRL0_PLL2_SEQ_EN;
dwt_reg_write_u32(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, pmsc_ctrl0);
}
static int dwt_start(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
uint8_t cswakeup_buf[32] = {0};
k_sem_take(&ctx->dev_lock, K_FOREVER);
/* Set SPI clock to lowest frequency */
dwt_set_spi_slow(dev, DWT_SPI_CSWAKEUP_FREQ);
if (dwt_reg_read_u32(dev, DWT_DEV_ID_ID, 0) != DWT_DEVICE_ID) {
/* Keep SPI CS line low for 500 microseconds */
dwt_register_read(dev, 0, 0, sizeof(cswakeup_buf),
cswakeup_buf);
/* Give device time to initialize */
k_sleep(K_MSEC(5));
if (dwt_reg_read_u32(dev, DWT_DEV_ID_ID, 0) != DWT_DEVICE_ID) {
LOG_ERR("Failed to wake-up %p", dev);
k_sem_give(&ctx->dev_lock);
return -1;
}
} else {
LOG_WRN("Device not in a sleep mode");
}
/* Restore SPI clock settings */
dwt_set_spi_slow(dev, DWT_SPI_SLOW_FREQ);
dwt_set_spi_fast(dev);
dwt_setup_int(dev, true);
dwt_disable_txrx(dev);
dwt_reset_rfrx(dev);
if (CONFIG_IEEE802154_DW1000_SNIFF_ONT != 0) {
dwt_set_rx_mode(dev);
}
/* Re-enable RX after packet reception */
atomic_set_bit(&ctx->state, DWT_STATE_RX_DEF_ON);
dwt_enable_rx(dev, 0);
k_sem_give(&ctx->dev_lock);
LOG_INF("Started %p", dev);
return 0;
}
static int dwt_stop(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_disable_txrx(dev);
dwt_reset_rfrx(dev);
dwt_setup_int(dev, false);
/* Copy the user configuration and enter sleep mode */
dwt_reg_write_u8(dev, DWT_AON_ID, DWT_AON_CTRL_OFFSET,
DWT_AON_CTRL_SAVE);
k_sem_give(&ctx->dev_lock);
LOG_INF("Stopped %p", dev);
return 0;
}
static inline void dwt_set_sysclks_xti(const struct device *dev, bool ldeload)
{
uint16_t clks = BIT(9) | DWT_PMSC_CTRL0_SYSCLKS_19M;
/*
* See Table 4: Register accesses required to load LDE microcode,
* set PMSC_CTRL0 0x0301, load LDE, set PMSC_CTRL0 0x0200.
*/
if (ldeload) {
clks |= BIT(8);
}
/* Force system clock to be the 19.2 MHz XTI clock */
dwt_reg_write_u16(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, clks);
}
static inline void dwt_set_sysclks_auto(const struct device *dev)
{
uint8_t sclks = DWT_PMSC_CTRL0_SYSCLKS_AUTO |
DWT_PMSC_CTRL0_RXCLKS_AUTO |
DWT_PMSC_CTRL0_TXCLKS_AUTO;
dwt_reg_write_u8(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, sclks);
}
static uint32_t dwt_otpmem_read(const struct device *dev, uint16_t otp_addr)
{
dwt_reg_write_u16(dev, DWT_OTP_IF_ID, DWT_OTP_ADDR, otp_addr);
dwt_reg_write_u8(dev, DWT_OTP_IF_ID, DWT_OTP_CTRL,
DWT_OTP_CTRL_OTPREAD | DWT_OTP_CTRL_OTPRDEN);
/* OTPREAD is self clearing but OTPRDEN is not */
dwt_reg_write_u8(dev, DWT_OTP_IF_ID, DWT_OTP_CTRL, 0x00);
/* Read read data, available 40ns after rising edge of OTP_READ */
return dwt_reg_read_u32(dev, DWT_OTP_IF_ID, DWT_OTP_RDAT);
}
static int dwt_initialise_dev(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
uint32_t otp_val = 0;
uint8_t xtal_trim;
dwt_set_sysclks_xti(dev, false);
ctx->sleep_mode = 0;
/* Disable PMSC control of analog RF subsystem */
dwt_reg_write_u16(dev, DWT_PMSC_ID, DWT_PMSC_CTRL1_OFFSET,
DWT_PMSC_CTRL1_PKTSEQ_DISABLE);
/* Clear all status flags */
dwt_reg_write_u32(dev, DWT_SYS_STATUS_ID, 0, DWT_SYS_STATUS_MASK_32);
/*
* Apply soft reset,
* see SOFTRESET field description in DW1000 User Manual.
*/
dwt_reg_write_u8(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_ALL);
k_sleep(K_MSEC(1));
dwt_reg_write_u8(dev, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_CLEAR);
dwt_set_sysclks_xti(dev, false);
/*
* This bit (a.k.a PLLLDT) should be set to ensure reliable
* operation of the CPLOCK bit.
*/
dwt_reg_write_u8(dev, DWT_EXT_SYNC_ID, DWT_EC_CTRL_OFFSET,
DWT_EC_CTRL_PLLLCK);
/* Kick LDO if there is a value programmed. */
otp_val = dwt_otpmem_read(dev, DWT_OTP_LDOTUNE_ADDR);
if ((otp_val & 0xFF) != 0) {
dwt_reg_write_u8(dev, DWT_OTP_IF_ID, DWT_OTP_SF,
DWT_OTP_SF_LDO_KICK);
ctx->sleep_mode |= DWT_AON_WCFG_ONW_LLDO;
LOG_INF("Load LDOTUNE_CAL parameter");
}
otp_val = dwt_otpmem_read(dev, DWT_OTP_XTRIM_ADDR);
xtal_trim = otp_val & DWT_FS_XTALT_MASK;
LOG_INF("OTP Revision 0x%02x, XTAL Trim 0x%02x",
(uint8_t)(otp_val >> 8), xtal_trim);
LOG_DBG("CHIP ID 0x%08x", dwt_otpmem_read(dev, DWT_OTP_PARTID_ADDR));
LOG_DBG("LOT ID 0x%08x", dwt_otpmem_read(dev, DWT_OTP_LOTID_ADDR));
LOG_DBG("Vbat 0x%02x", dwt_otpmem_read(dev, DWT_OTP_VBAT_ADDR));
LOG_DBG("Vtemp 0x%02x", dwt_otpmem_read(dev, DWT_OTP_VTEMP_ADDR));
if (xtal_trim == 0) {
/* Set to default */
xtal_trim = DWT_FS_XTALT_MIDRANGE;
}
/* For FS_XTALT bits 7:5 must always be set to binary “011” */
xtal_trim |= BIT(6) | BIT(5);
dwt_reg_write_u8(dev, DWT_FS_CTRL_ID, DWT_FS_XTALT_OFFSET, xtal_trim);
/* Load LDE microcode into RAM, see 2.5.5.10 LDELOAD */
dwt_set_sysclks_xti(dev, true);
dwt_reg_write_u16(dev, DWT_OTP_IF_ID, DWT_OTP_CTRL,
DWT_OTP_CTRL_LDELOAD);
k_sleep(K_MSEC(1));
dwt_set_sysclks_xti(dev, false);
ctx->sleep_mode |= DWT_AON_WCFG_ONW_LLDE;
dwt_set_sysclks_auto(dev);
if (!(dwt_reg_read_u8(dev, DWT_SYS_STATUS_ID, 0) &
DWT_SYS_STATUS_CPLOCK)) {
LOG_WRN("PLL has not locked");
return -EIO;
}
dwt_set_spi_fast(dev);
/* Setup antenna delay values */
dwt_reg_write_u16(dev, DWT_LDE_IF_ID, DWT_LDE_RXANTD_OFFSET,
DW1000_RX_ANT_DLY);
dwt_reg_write_u16(dev, DWT_TX_ANTD_ID, DWT_TX_ANTD_OFFSET,
DW1000_TX_ANT_DLY);
/* Clear AON_CFG1 register */
dwt_reg_write_u8(dev, DWT_AON_ID, DWT_AON_CFG1_OFFSET, 0);
/*
* Configure sleep mode:
* - On wake-up load configurations from the AON memory
* - preserve sleep mode configuration
* - On Wake-up load the LDE microcode
* - When available, on wake-up load the LDO tune value
*/
ctx->sleep_mode |= DWT_AON_WCFG_ONW_LDC |
DWT_AON_WCFG_PRES_SLEEP;
dwt_reg_write_u16(dev, DWT_AON_ID, DWT_AON_WCFG_OFFSET,
ctx->sleep_mode);
LOG_DBG("sleep mode 0x%04x", ctx->sleep_mode);
/* Enable sleep and wake using SPI CSn */
dwt_reg_write_u8(dev, DWT_AON_ID, DWT_AON_CFG0_OFFSET,
DWT_AON_CFG0_WAKE_SPI | DWT_AON_CFG0_SLEEP_EN);
return 0;
}
/*
* RF PHY configuration. Must be carried out as part of initialization and
* for every channel change. See also 2.5 Default Configuration on Power Up.
*/
static int dwt_configure_rf_phy(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
uint8_t chan = rf_cfg->channel;
uint8_t prf_idx = rf_cfg->prf;
uint32_t chan_ctrl = 0;
uint8_t rxctrlh;
uint8_t pll_tune;
uint8_t tune4h;
uint8_t pgdelay;
uint16_t lde_repc;
uint16_t agc_tune1;
uint16_t sfdto;
uint16_t tune1a;
uint16_t tune0b;
uint16_t tune1b;
uint32_t txctrl;
uint32_t pll_cfg;
uint32_t tune2;
uint32_t sys_cfg;
uint32_t tx_fctrl;
uint32_t power;
if ((chan < 1) || (chan > 7) || (chan == 6)) {
LOG_ERR("Channel not supported %u", chan);
return -ENOTSUP;
}
if (rf_cfg->rx_shr_code >= ARRAY_SIZE(dwt_lde_repc_defs)) {
LOG_ERR("Preamble code not supported %u",
rf_cfg->rx_shr_code);
return -ENOTSUP;
}
if (prf_idx >= DWT_NUMOF_PRFS) {
LOG_ERR("PRF not supported %u", prf_idx);
return -ENOTSUP;
}
if (rf_cfg->rx_pac_l >= DWT_NUMOF_PACS) {
LOG_ERR("RX PAC not supported %u", rf_cfg->rx_pac_l);
return -ENOTSUP;
}
if (rf_cfg->rx_ns_sfd > 1) {
LOG_ERR("Wrong NS SFD configuration");
return -ENOTSUP;
}
if (rf_cfg->tx_shr_nsync >= DWT_NUM_OF_PLEN) {
LOG_ERR("Wrong SHR configuration");
return -ENOTSUP;
}
lde_repc = dwt_lde_repc_defs[rf_cfg->rx_shr_code];
agc_tune1 = dwt_agc_tune1_defs[prf_idx];
sfdto = rf_cfg->rx_sfd_to;
rxctrlh = dwt_rxctrlh_defs[dwt_ch_to_cfg[chan]];
txctrl = dwt_txctrl_defs[dwt_ch_to_cfg[chan]];
pll_tune = dwt_plltune_defs[dwt_ch_to_cfg[chan]];
pll_cfg = dwt_pllcfg_defs[dwt_ch_to_cfg[chan]];
tune2 = dwt_tune2_defs[prf_idx][rf_cfg->rx_pac_l];
tune1a = dwt_tune1a_defs[prf_idx];
tune0b = dwt_tune0b_defs[rf_cfg->dr][rf_cfg->rx_ns_sfd];
pgdelay = dwt_pgdelay_defs[dwt_ch_to_cfg[chan]];
sys_cfg = dwt_reg_read_u32(dev, DWT_SYS_CFG_ID, 0);
tx_fctrl = dwt_reg_read_u32(dev, DWT_TX_FCTRL_ID, 0);
/* Don't allow 0 - SFD timeout will always be enabled */
if (sfdto == 0) {
sfdto = DWT_SFDTOC_DEF;
}
/* Set IEEE 802.15.4 compliant mode */
sys_cfg &= ~DWT_SYS_CFG_PHR_MODE_11;
if (rf_cfg->dr == DWT_BR_110K) {
/* Set Receiver Mode 110 kbps data rate */
sys_cfg |= DWT_SYS_CFG_RXM110K;
lde_repc = lde_repc >> 3;
tune1b = DWT_DRX_TUNE1b_110K;
tune4h = DWT_DRX_TUNE4H_PRE64;
} else {
sys_cfg &= ~DWT_SYS_CFG_RXM110K;
if (rf_cfg->tx_shr_nsync == DWT_PLEN_64) {
tune1b = DWT_DRX_TUNE1b_6M8_PRE64;
tune4h = DWT_DRX_TUNE4H_PRE64;
} else {
tune1b = DWT_DRX_TUNE1b_850K_6M8;
tune4h = DWT_DRX_TUNE4H_PRE128PLUS;
}
}
if (sys_cfg & DWT_SYS_CFG_DIS_STXP) {
if (rf_cfg->prf == DWT_PRF_64M) {
power = dwt_txpwr_stxp1_64[dwt_ch_to_cfg[chan]];
} else {
power = dwt_txpwr_stxp1_16[dwt_ch_to_cfg[chan]];
}
} else {
if (rf_cfg->prf == DWT_PRF_64M) {
power = dwt_txpwr_stxp0_64[dwt_ch_to_cfg[chan]];
} else {
power = dwt_txpwr_stxp0_16[dwt_ch_to_cfg[chan]];
}
}
dwt_reg_write_u32(dev, DWT_SYS_CFG_ID, 0, sys_cfg);
LOG_DBG("SYS_CFG: 0x%08x", sys_cfg);
dwt_reg_write_u16(dev, DWT_LDE_IF_ID, DWT_LDE_REPC_OFFSET, lde_repc);
LOG_DBG("LDE_REPC: 0x%04x", lde_repc);
dwt_reg_write_u8(dev, DWT_LDE_IF_ID, DWT_LDE_CFG1_OFFSET,
DWT_DEFAULT_LDE_CFG1);
if (rf_cfg->prf == DWT_PRF_64M) {
dwt_reg_write_u16(dev, DWT_LDE_IF_ID, DWT_LDE_CFG2_OFFSET,
DWT_DEFAULT_LDE_CFG2_PRF64);
LOG_DBG("LDE_CFG2: 0x%04x", DWT_DEFAULT_LDE_CFG2_PRF64);
} else {
dwt_reg_write_u16(dev, DWT_LDE_IF_ID, DWT_LDE_CFG2_OFFSET,
DWT_DEFAULT_LDE_CFG2_PRF16);
LOG_DBG("LDE_CFG2: 0x%04x", DWT_DEFAULT_LDE_CFG2_PRF16);
}
/* Configure PLL2/RF PLL block CFG/TUNE (for a given channel) */
dwt_reg_write_u32(dev, DWT_FS_CTRL_ID, DWT_FS_PLLCFG_OFFSET, pll_cfg);
LOG_DBG("PLLCFG: 0x%08x", pll_cfg);
dwt_reg_write_u8(dev, DWT_FS_CTRL_ID, DWT_FS_PLLTUNE_OFFSET, pll_tune);
LOG_DBG("PLLTUNE: 0x%02x", pll_tune);
/* Configure RF RX blocks (for specified channel/bandwidth) */
dwt_reg_write_u8(dev, DWT_RF_CONF_ID, DWT_RF_RXCTRLH_OFFSET, rxctrlh);
LOG_DBG("RXCTRLH: 0x%02x", rxctrlh);
/* Configure RF/TX blocks for specified channel and PRF */
dwt_reg_write_u32(dev, DWT_RF_CONF_ID, DWT_RF_TXCTRL_OFFSET, txctrl);
LOG_DBG("TXCTRL: 0x%08x", txctrl);
/* Digital receiver configuration, DRX_CONF */
dwt_reg_write_u16(dev, DWT_DRX_CONF_ID, DWT_DRX_TUNE0b_OFFSET, tune0b);
LOG_DBG("DRX_TUNE0b: 0x%04x", tune0b);
dwt_reg_write_u16(dev, DWT_DRX_CONF_ID, DWT_DRX_TUNE1a_OFFSET, tune1a);
LOG_DBG("DRX_TUNE1a: 0x%04x", tune1a);
dwt_reg_write_u16(dev, DWT_DRX_CONF_ID, DWT_DRX_TUNE1b_OFFSET, tune1b);
LOG_DBG("DRX_TUNE1b: 0x%04x", tune1b);
dwt_reg_write_u32(dev, DWT_DRX_CONF_ID, DWT_DRX_TUNE2_OFFSET, tune2);
LOG_DBG("DRX_TUNE2: 0x%08x", tune2);
dwt_reg_write_u8(dev, DWT_DRX_CONF_ID, DWT_DRX_TUNE4H_OFFSET, tune4h);
LOG_DBG("DRX_TUNE4H: 0x%02x", tune4h);
dwt_reg_write_u16(dev, DWT_DRX_CONF_ID, DWT_DRX_SFDTOC_OFFSET, sfdto);
LOG_DBG("DRX_SFDTOC: 0x%04x", sfdto);
/* Automatic Gain Control configuration and control, AGC_CTRL */
dwt_reg_write_u16(dev, DWT_AGC_CTRL_ID, DWT_AGC_TUNE1_OFFSET,
agc_tune1);
LOG_DBG("AGC_TUNE1: 0x%04x", agc_tune1);
dwt_reg_write_u32(dev, DWT_AGC_CTRL_ID, DWT_AGC_TUNE2_OFFSET,
DWT_AGC_TUNE2_VAL);
if (rf_cfg->rx_ns_sfd) {
/*
* SFD_LENGTH, length of the SFD sequence used when
* the data rate is 850 kbps or 6.8 Mbps,
* must be set to either 8 or 16.
*/
dwt_reg_write_u8(dev, DWT_USR_SFD_ID, 0x00,
dwt_ns_sfdlen[rf_cfg->dr]);
LOG_DBG("USR_SFDLEN: 0x%02x", dwt_ns_sfdlen[rf_cfg->dr]);
chan_ctrl |= DWT_CHAN_CTRL_DWSFD;
}
/* Set RX_CHAN and TX CHAN */
chan_ctrl |= (chan & DWT_CHAN_CTRL_TX_CHAN_MASK) |
((chan << DWT_CHAN_CTRL_RX_CHAN_SHIFT) &
DWT_CHAN_CTRL_RX_CHAN_MASK);
/* Set RXPRF */
chan_ctrl |= (BIT(rf_cfg->prf) << DWT_CHAN_CTRL_RXFPRF_SHIFT) &
DWT_CHAN_CTRL_RXFPRF_MASK;
/* Set TX_PCOD */
chan_ctrl |= (rf_cfg->tx_shr_code << DWT_CHAN_CTRL_TX_PCOD_SHIFT) &
DWT_CHAN_CTRL_TX_PCOD_MASK;
/* Set RX_PCOD */
chan_ctrl |= (rf_cfg->rx_shr_code << DWT_CHAN_CTRL_RX_PCOD_SHIFT) &
DWT_CHAN_CTRL_RX_PCOD_MASK;
/* Set Channel Control */
dwt_reg_write_u32(dev, DWT_CHAN_CTRL_ID, 0, chan_ctrl);
LOG_DBG("CHAN_CTRL 0x%08x", chan_ctrl);
/* Set up TX Preamble Size, PRF and Data Rate */
tx_fctrl = dwt_plen_cfg[rf_cfg->tx_shr_nsync] |
(BIT(rf_cfg->prf) << DWT_TX_FCTRL_TXPRF_SHFT) |
(rf_cfg->dr << DWT_TX_FCTRL_TXBR_SHFT);
dwt_reg_write_u32(dev, DWT_TX_FCTRL_ID, 0, tx_fctrl);
LOG_DBG("TX_FCTRL 0x%08x", tx_fctrl);
/* Set the Pulse Generator Delay */
dwt_reg_write_u8(dev, DWT_TX_CAL_ID, DWT_TC_PGDELAY_OFFSET, pgdelay);
LOG_DBG("PGDELAY 0x%02x", pgdelay);
/* Set Transmit Power Control */
dwt_reg_write_u32(dev, DWT_TX_POWER_ID, 0, power);
LOG_DBG("TX_POWER 0x%08x", power);
/*
* From 5.3.1.2 SFD Initialisation,
* SFD sequence initialisation for Auto ACK frame.
*/
dwt_reg_write_u8(dev, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET,
DWT_SYS_CTRL_TXSTRT | DWT_SYS_CTRL_TRXOFF);
/*
* Calculate PHY timing parameters
*
* From (9.4) Std 802.15.4-2011
* Tshr = Tpsym * (NSYNC + NSFD )
* Tphr = NPHR * Tdsym1m
* Tpsdu = Tdsym * NPSDU * NSYMPEROCTET / Rfec
*
* PRF: pulse repetition frequency
* PSR: preamble symbol repetitions
* SFD: start of frame delimiter
* SHR: synchronisation header (SYNC + SFD)
* PHR: PHY header
*/
uint16_t nsync = BIT(rf_cfg->tx_shr_nsync + 6);
if (rf_cfg->prf == DWT_PRF_64M) {
rf_cfg->t_shr = UWB_PHY_TPSYM_PRF64 *
(nsync + UWB_PHY_NUMOF_SYM_SHR_SFD);
} else {
rf_cfg->t_shr = UWB_PHY_TPSYM_PRF16 *
(nsync + UWB_PHY_NUMOF_SYM_SHR_SFD);
}
if (rf_cfg->dr == DWT_BR_6M8) {
rf_cfg->t_phr = UWB_PHY_NUMOF_SYM_PHR * UWB_PHY_TDSYM_PHR_6M8;
rf_cfg->t_dsym = UWB_PHY_TDSYM_DATA_6M8 / 0.44;
} else if (rf_cfg->dr == DWT_BR_850K) {
rf_cfg->t_phr = UWB_PHY_NUMOF_SYM_PHR * UWB_PHY_TDSYM_PHR_850K;
rf_cfg->t_dsym = UWB_PHY_TDSYM_DATA_850K / 0.44;
} else {
rf_cfg->t_phr = UWB_PHY_NUMOF_SYM_PHR * UWB_PHY_TDSYM_PHR_110K;
rf_cfg->t_dsym = UWB_PHY_TDSYM_DATA_110K / 0.44;
}
return 0;
}
static int dw1000_init(const struct device *dev)
{
struct dwt_context *ctx = dev->data;
const struct dwt_hi_cfg *hi_cfg = dev->config;
LOG_INF("Initialize DW1000 Transceiver");
k_sem_init(&ctx->phy_sem, 0, 1);
/* slow SPI config */
memcpy(&ctx->spi_cfg_slow, &hi_cfg->bus.config, sizeof(ctx->spi_cfg_slow));
ctx->spi_cfg_slow.frequency = DWT_SPI_SLOW_FREQ;
if (!spi_is_ready_dt(&hi_cfg->bus)) {
LOG_ERR("SPI device not ready");
return -ENODEV;
}
dwt_set_spi_slow(dev, DWT_SPI_SLOW_FREQ);
/* Initialize IRQ GPIO */
if (!gpio_is_ready_dt(&hi_cfg->irq_gpio)) {
LOG_ERR("IRQ GPIO device not ready");
return -ENODEV;
}
if (gpio_pin_configure_dt(&hi_cfg->irq_gpio, GPIO_INPUT)) {
LOG_ERR("Unable to configure GPIO pin %u", hi_cfg->irq_gpio.pin);
return -EINVAL;
}
gpio_init_callback(&(ctx->gpio_cb), dwt_gpio_callback,
BIT(hi_cfg->irq_gpio.pin));
if (gpio_add_callback(hi_cfg->irq_gpio.port, &(ctx->gpio_cb))) {
LOG_ERR("Failed to add IRQ callback");
return -EINVAL;
}
/* Initialize RESET GPIO */
if (!gpio_is_ready_dt(&hi_cfg->rst_gpio)) {
LOG_ERR("Reset GPIO device not ready");
return -ENODEV;
}
if (gpio_pin_configure_dt(&hi_cfg->rst_gpio, GPIO_INPUT)) {
LOG_ERR("Unable to configure GPIO pin %u", hi_cfg->rst_gpio.pin);
return -EINVAL;
}
LOG_INF("GPIO and SPI configured");
dwt_hw_reset(dev);
if (dwt_reg_read_u32(dev, DWT_DEV_ID_ID, 0) != DWT_DEVICE_ID) {
LOG_ERR("Failed to read device ID %p", dev);
return -ENODEV;
}
if (dwt_initialise_dev(dev)) {
LOG_ERR("Failed to initialize DW1000");
return -EIO;
}
if (dwt_configure_rf_phy(dev)) {
LOG_ERR("Failed to configure RF PHY");
return -EIO;
}
/* Allow Beacon, Data, Acknowledgement, MAC command */
dwt_set_frame_filter(dev, true, DWT_SYS_CFG_FFAB | DWT_SYS_CFG_FFAD |
DWT_SYS_CFG_FFAA | DWT_SYS_CFG_FFAM);
/*
* Enable system events:
* - transmit frame sent,
* - receiver FCS good,
* - receiver PHY header error,
* - receiver FCS error,
* - receiver Reed Solomon Frame Sync Loss,
* - receive Frame Wait Timeout,
* - preamble detection timeout,
* - receive SFD timeout
*/
dwt_reg_write_u32(dev, DWT_SYS_MASK_ID, 0,
DWT_SYS_MASK_MTXFRS |
DWT_SYS_MASK_MRXFCG |
DWT_SYS_MASK_MRXPHE |
DWT_SYS_MASK_MRXFCE |
DWT_SYS_MASK_MRXRFSL |
DWT_SYS_MASK_MRXRFTO |
DWT_SYS_MASK_MRXPTO |
DWT_SYS_MASK_MRXSFDTO);
/* Initialize IRQ event work queue */
ctx->dev = dev;
k_work_queue_start(&dwt_work_queue, dwt_work_queue_stack,
K_KERNEL_STACK_SIZEOF(dwt_work_queue_stack),
CONFIG_SYSTEM_WORKQUEUE_PRIORITY, NULL);
k_work_init(&ctx->irq_cb_work, dwt_irq_work_handler);
dwt_setup_int(dev, true);
LOG_INF("DW1000 device initialized and configured");
return 0;
}
static inline uint8_t *get_mac(const struct device *dev)
{
struct dwt_context *dw1000 = dev->data;
sys_rand_get(dw1000->mac_addr, sizeof(dw1000->mac_addr));
dw1000->mac_addr[0] = (dw1000->mac_addr[0] & ~0x01) | 0x02;
return dw1000->mac_addr;
}
static void dwt_iface_api_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
struct dwt_context *dw1000 = dev->data;
uint8_t *mac = get_mac(dev);
net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);
dw1000->iface = iface;
ieee802154_init(iface);
LOG_INF("Iface initialized");
}
static const struct ieee802154_radio_api dwt_radio_api = {
.iface_api.init = dwt_iface_api_init,
.get_capabilities = dwt_get_capabilities,
.cca = dwt_cca,
.set_channel = dwt_set_channel,
.filter = dwt_filter,
.set_txpower = dwt_set_power,
.start = dwt_start,
.stop = dwt_stop,
.configure = dwt_configure,
.ed_scan = dwt_ed,
.tx = dwt_tx,
.attr_get = dwt_attr_get,
};
#define DWT_PSDU_LENGTH (127 - DWT_FCS_LENGTH)
#if defined(CONFIG_IEEE802154_RAW_MODE)
DEVICE_DT_INST_DEFINE(0, dw1000_init, NULL,
&dwt_0_context, &dw1000_0_config,
POST_KERNEL, CONFIG_IEEE802154_DW1000_INIT_PRIO,
&dwt_radio_api);
#else
NET_DEVICE_DT_INST_DEFINE(0,
dw1000_init,
NULL,
&dwt_0_context,
&dw1000_0_config,
CONFIG_IEEE802154_DW1000_INIT_PRIO,
&dwt_radio_api,
IEEE802154_L2,
NET_L2_GET_CTX_TYPE(IEEE802154_L2),
DWT_PSDU_LENGTH);
#endif