zephyr/drivers/ieee802154/ieee802154_dw1000.c
Johann Fischer d261de8960 drivers: dw1000: fix SPI GPIO CS
Since commit 5963ebaf33
("drivers: spi: CS configuration through devicetree")
the SPI GPIO CS flags are obtained from DT,
but the patch series has missed the necessary changes
for the ieee802154_dw1000 driver and decawave_dwm1001_dev board.

Signed-off-by: Johann Fischer <j.fischer@phytec.de>
2020-07-07 15:08:16 +02:00

1686 lines
45 KiB
C
Raw Permalink 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 <logging/log.h>
LOG_MODULE_REGISTER(dw1000, LOG_LEVEL_INF);
#include <errno.h>
#include <kernel.h>
#include <arch/cpu.h>
#include <debug/stack.h>
#include <device.h>
#include <init.h>
#include <net/net_if.h>
#include <net/net_pkt.h>
#include <sys/byteorder.h>
#include <string.h>
#include <random/rand32.h>
#include <debug/stack.h>
#include <math.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>
#include <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 1017.63
#define UWB_PHY_TPSYM_PRF16 993.59
#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_THREAD_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 {
const char *irq_port;
uint8_t irq_pin;
gpio_dt_flags_t irq_flags;
const char *rst_port;
uint8_t rst_pin;
gpio_dt_flags_t rst_flags;
const char *spi_port;
uint8_t spi_cs_pin;
gpio_dt_flags_t spi_cs_flags;
const char *spi_cs_port;
uint32_t spi_freq;
uint8_t spi_slave;
};
#define DWT_STATE_TX 0
#define DWT_STATE_CCA 1
#define DWT_STATE_RX_DEF_ON 2
struct dwt_context {
struct net_if *iface;
struct device *irq_gpio;
struct device *rst_gpio;
struct device *spi;
struct spi_cs_control spi_cs;
struct spi_config *spi_cfg;
struct spi_config spi_cfg_slow;
struct spi_config spi_cfg_fast;
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 = {
.irq_port = DT_INST_GPIO_LABEL(0, int_gpios),
.irq_pin = DT_INST_GPIO_PIN(0, int_gpios),
.irq_flags = DT_INST_GPIO_FLAGS(0, int_gpios),
.rst_port = DT_INST_GPIO_LABEL(0, reset_gpios),
.rst_pin = DT_INST_GPIO_PIN(0, reset_gpios),
.rst_flags = DT_INST_GPIO_FLAGS(0, reset_gpios),
.spi_port = DT_INST_BUS_LABEL(0),
.spi_freq = DT_INST_PROP(0, spi_max_frequency),
.spi_slave = DT_INST_REG_ADDR(0),
#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0)
.spi_cs_port = DT_INST_SPI_DEV_CS_GPIOS_LABEL(0),
.spi_cs_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(0),
.spi_cs_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0),
#endif
};
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 structer 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(struct dwt_context *ctx);
static int dwt_spi_read(struct dwt_context *ctx,
uint16_t hdr_len, const uint8_t *hdr_buf,
uint32_t data_len, uint8_t *data)
{
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(ctx->spi, 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(struct dwt_context *ctx,
uint16_t hdr_len, const uint8_t *hdr_buf,
uint32_t data_len, const uint8_t *data)
{
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(ctx->spi, 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(struct dwt_context *ctx,
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(ctx, hdr_len, hdr, buf_len, buf);
} else {
return dwt_spi_read(ctx, hdr_len, hdr, buf_len, buf);
}
}
static int dwt_register_read(struct dwt_context *ctx,
uint8_t reg, uint16_t offset, size_t buf_len, uint8_t *buf)
{
return dwt_spi_transfer(ctx, reg, offset, buf_len, buf, false);
}
static int dwt_register_write(struct dwt_context *ctx,
uint8_t reg, uint16_t offset, size_t buf_len, uint8_t *buf)
{
return dwt_spi_transfer(ctx, reg, offset, buf_len, buf, true);
}
static inline uint32_t dwt_reg_read_u32(struct dwt_context *ctx,
uint8_t reg, uint16_t offset)
{
uint8_t buf[sizeof(uint32_t)];
dwt_spi_transfer(ctx, reg, offset, sizeof(buf), buf, false);
return sys_get_le32(buf);
}
static inline uint16_t dwt_reg_read_u16(struct dwt_context *ctx,
uint8_t reg, uint16_t offset)
{
uint8_t buf[sizeof(uint16_t)];
dwt_spi_transfer(ctx, reg, offset, sizeof(buf), buf, false);
return sys_get_le16(buf);
}
static inline uint8_t dwt_reg_read_u8(struct dwt_context *ctx,
uint8_t reg, uint16_t offset)
{
uint8_t buf;
dwt_spi_transfer(ctx, reg, offset, sizeof(buf), &buf, false);
return buf;
}
static inline void dwt_reg_write_u32(struct dwt_context *ctx,
uint8_t reg, uint16_t offset, uint32_t val)
{
uint8_t buf[sizeof(uint32_t)];
sys_put_le32(val, buf);
dwt_spi_transfer(ctx, reg, offset, sizeof(buf), buf, true);
}
static inline void dwt_reg_write_u16(struct dwt_context *ctx,
uint8_t reg, uint16_t offset, uint16_t val)
{
uint8_t buf[sizeof(uint16_t)];
sys_put_le16(val, buf);
dwt_spi_transfer(ctx, reg, offset, sizeof(buf), buf, true);
}
static inline void dwt_reg_write_u8(struct dwt_context *ctx,
uint8_t reg, uint16_t offset, uint8_t val)
{
dwt_spi_transfer(ctx, reg, offset, sizeof(uint8_t), &val, true);
}
static ALWAYS_INLINE void dwt_setup_int(struct dwt_context *ctx,
bool enable)
{
const struct dwt_hi_cfg *hi_cfg = &dw1000_0_config;
unsigned int flags = enable
? GPIO_INT_EDGE_TO_ACTIVE
: GPIO_INT_DISABLE;
gpio_pin_interrupt_configure(ctx->irq_gpio,
hi_cfg->irq_pin,
flags);
}
static void dwt_reset_rfrx(struct dwt_context *ctx)
{
/*
* Apply a receiver-only soft reset,
* see SOFTRESET field description in DW1000 User Manual.
*/
dwt_reg_write_u8(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_RX);
dwt_reg_write_u8(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_CLEAR);
}
static void dwt_disable_txrx(struct dwt_context *ctx)
{
dwt_setup_int(ctx, false);
dwt_reg_write_u8(ctx, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET,
DWT_SYS_CTRL_TRXOFF);
dwt_reg_write_u32(ctx, 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(ctx, true);
}
/* timeout time in units of 1.026 microseconds */
static int dwt_enable_rx(struct dwt_context *ctx, uint16_t timeout)
{
uint32_t sys_cfg;
uint16_t sys_ctrl = DWT_SYS_CTRL_RXENAB;
sys_cfg = dwt_reg_read_u32(ctx, DWT_SYS_CFG_ID, 0);
if (timeout != 0) {
dwt_reg_write_u16(ctx, 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(ctx, DWT_SYS_CFG_ID, 0, sys_cfg);
dwt_reg_write_u16(ctx, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET, sys_ctrl);
return 0;
}
static inline void dwt_irq_handle_rx_cca(struct dwt_context *ctx)
{
k_sem_give(&ctx->phy_sem);
ctx->cca_busy = true;
/* Clear all RX event bits */
dwt_reg_write_u32(ctx, DWT_SYS_STATUS_ID, 0,
DWT_SYS_STATUS_ALL_RX_GOOD);
}
static inline void dwt_irq_handle_rx(struct dwt_context *ctx, uint32_t sys_stat)
{
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(ctx, 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_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(ctx, DWT_RX_BUFFER_ID, 0, pkt_len, pkt->buffer->data);
dwt_register_read(ctx, 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;
/* Traking 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};
struct net_ptp_time timestamp;
uint64_t ts_fsec;
memcpy(ts_buf, rx_inf_reg.rx_time, DWT_RX_TIME_RX_STAMP_LEN);
ts_fsec = sys_get_le64(ts_buf) * DWT_TS_TIME_UNITS_FS;
timestamp.second = (ts_fsec / 1000000) / NSEC_PER_SEC;
timestamp.nanosecond = (ts_fsec / 1000000) % NSEC_PER_SEC;
net_pkt_set_timestamp(pkt, &timestamp);
}
/* 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(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_radio_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, (int8_t)net_pkt_ieee802154_rssi(pkt));
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(ctx, 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(ctx, DWT_SYS_CTRL_ID, DWT_SYS_CTRL_OFFSET,
DWT_SYS_CTRL_RXENAB);
}
}
static void dwt_irq_handle_tx(struct dwt_context *ctx, uint32_t sys_stat)
{
/* Clear TX event bits */
dwt_reg_write_u32(ctx, 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(struct dwt_context *ctx, uint32_t sys_stat)
{
/* Clear RX timeout event bits */
dwt_reg_write_u32(ctx, DWT_SYS_STATUS_ID, 0,
DWT_SYS_STATUS_RXRFTO);
dwt_disable_txrx(ctx);
/* Receiver reset necessary, see 4.1.6 RX Message timestamp */
dwt_reset_rfrx(ctx);
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(struct dwt_context *ctx, uint32_t sys_stat)
{
/* Clear RX error event bits */
dwt_reg_write_u32(ctx, DWT_SYS_STATUS_ID, 0, DWT_SYS_STATUS_ALL_RX_ERR);
dwt_disable_txrx(ctx);
/* Receiver reset necessary, see 4.1.6 RX Message timestamp */
dwt_reset_rfrx(ctx);
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(ctx, 0);
}
}
static void dwt_irq_work_handler(struct k_work *item)
{
struct dwt_context *ctx = CONTAINER_OF(item, struct dwt_context,
irq_cb_work);
uint32_t sys_stat;
k_sem_take(&ctx->dev_lock, K_FOREVER);
sys_stat = dwt_reg_read_u32(ctx, 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(ctx);
} else {
dwt_irq_handle_rx(ctx, sys_stat);
}
}
if (sys_stat & DWT_SYS_STATUS_TXFRS) {
dwt_irq_handle_tx(ctx, sys_stat);
}
if (sys_stat & DWT_SYS_STATUS_ALL_RX_TO) {
dwt_irq_handle_rxto(ctx, sys_stat);
}
if (sys_stat & DWT_SYS_STATUS_ALL_RX_ERR) {
dwt_irq_handle_error(ctx, sys_stat);
}
k_sem_give(&ctx->dev_lock);
}
static void dwt_gpio_callback(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(&ctx->irq_cb_work);
}
static enum ieee802154_hw_caps dwt_get_capabilities(struct device *dev)
{
return IEEE802154_HW_FCS |
IEEE802154_HW_2_4_GHZ | /* FIXME: add IEEE802154_HW_UWB_PHY */
IEEE802154_HW_FILTER;
}
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(struct device *dev)
{
struct dwt_context *ctx = dev->driver_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(ctx);
LOG_DBG("CCA duration %u us", cca_dur);
dwt_enable_rx(ctx, 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(ctx, 0);
k_sem_give(&ctx->dev_lock);
}
return ctx->cca_busy ? -EBUSY : 0;
}
static int dwt_ed(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(struct device *dev, uint16_t channel)
{
struct dwt_context *ctx = dev->driver_data;
struct dwt_phy_config *rf_cfg = &ctx->rf_cfg;
rf_cfg->channel = channel;
LOG_INF("Set channel %u", channel);
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_disable_txrx(ctx);
dwt_configure_rf_phy(ctx);
if (atomic_test_bit(&ctx->state, DWT_STATE_RX_DEF_ON)) {
dwt_enable_rx(ctx, 0);
}
k_sem_give(&ctx->dev_lock);
return 0;
}
static int dwt_set_pan_id(struct device *dev, uint16_t pan_id)
{
struct dwt_context *ctx = dev->driver_data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_reg_write_u16(ctx, 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(struct device *dev, uint16_t short_addr)
{
struct dwt_context *ctx = dev->driver_data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_reg_write_u16(ctx, 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(struct device *dev, const uint8_t *ieee_addr)
{
struct dwt_context *ctx = dev->driver_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(ctx, 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(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(struct device *dev, int16_t dbm)
{
struct dwt_context *ctx = dev->driver_data;
LOG_INF("set_txpower not supported %p", ctx);
return 0;
}
static int dwt_tx(struct device *dev, enum ieee802154_tx_mode tx_mode,
struct net_pkt *pkt, struct net_buf *frag)
{
struct dwt_context *ctx = dev->driver_data;
size_t len = frag->len;
uint32_t tx_time = 0;
struct net_ptp_time *txts;
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.
*/
txts = net_pkt_timestamp(pkt);
tmp_fs = txts->second * NSEC_PER_SEC + txts->nanosecond;
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(ctx, DWT_DX_TIME_ID, 1, tx_time);
LOG_DBG("ntx hi32 %x", tx_time);
LOG_DBG("sys hi32 %x",
dwt_reg_read_u32(ctx, 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(ctx, DWT_TX_BUFFER_ID, 0, len, frag->data)) {
LOG_ERR("Failed to write TX data");
goto error;
}
tx_fctrl = dwt_reg_read_u32(ctx, 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(ctx, DWT_TX_FCTRL_ID, 0, tx_fctrl);
dwt_disable_txrx(ctx);
/* Begin transmission */
dwt_reg_write_u8(ctx, 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(ctx, 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};
struct net_ptp_time timestamp;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_register_read(ctx, 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(ctx, DWT_SYS_TIME_ID, 1));
k_sem_give(&ctx->dev_lock);
tmp_fs = sys_get_le64(ts_buf) * DWT_TS_TIME_UNITS_FS;
timestamp.second = (tmp_fs / 1000000) / NSEC_PER_SEC;
timestamp.nanosecond = (tmp_fs / 1000000) % NSEC_PER_SEC;
net_pkt_set_timestamp(pkt, &timestamp);
}
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(ctx, 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(struct dwt_context *ctx,
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(ctx, DWT_SYS_CFG_ID, 0, (uint8_t)sys_cfg_ff);
}
static int dwt_configure(struct device *dev, enum ieee802154_config_type type,
const struct ieee802154_config *config)
{
struct dwt_context *ctx = dev->driver_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;
}
/*
* Note, the DW_RESET pin should not be driven high externally.
*/
static int dwt_hw_reset(struct device *dev)
{
struct dwt_context *ctx = dev->driver_data;
const struct dwt_hi_cfg *hi_cfg = dev->config_info;
if (gpio_pin_configure(ctx->rst_gpio, hi_cfg->rst_pin,
GPIO_OUTPUT_ACTIVE | hi_cfg->rst_flags)) {
LOG_ERR("Failed to configure GPIO pin %u", hi_cfg->rst_pin);
return -EINVAL;
}
k_sleep(K_MSEC(1));
gpio_pin_set(ctx->rst_gpio, hi_cfg->rst_pin, 0);
k_sleep(K_MSEC(5));
if (gpio_pin_configure(ctx->rst_gpio, hi_cfg->rst_pin,
GPIO_INPUT | hi_cfg->rst_flags)) {
LOG_ERR("Failed to configure GPIO pin %u", hi_cfg->rst_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(struct dwt_context *ctx, const uint32_t freq)
{
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(struct dwt_context *ctx)
{
ctx->spi_cfg = &ctx->spi_cfg_fast;
}
static void dwt_set_rx_mode(struct dwt_context *ctx)
{
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(ctx, DWT_RX_SNIFF_ID, DWT_RX_SNIFF_OFFSET,
sizeof(rx_sniff), rx_sniff);
pmsc_ctrl0 = dwt_reg_read_u32(ctx, 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(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, pmsc_ctrl0);
}
static int dwt_start(struct device *dev)
{
struct dwt_context *ctx = dev->driver_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(ctx, DWT_SPI_CSWAKEUP_FREQ);
if (dwt_reg_read_u32(ctx, DWT_DEV_ID_ID, 0) != DWT_DEVICE_ID) {
/* Keep SPI CS line low for 500 microseconds */
dwt_register_read(ctx, 0, 0, sizeof(cswakeup_buf),
cswakeup_buf);
/* Give device time to initialize */
k_sleep(K_MSEC(5));
if (dwt_reg_read_u32(ctx, 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(ctx, DWT_SPI_SLOW_FREQ);
dwt_set_spi_fast(ctx);
dwt_setup_int(ctx, true);
dwt_disable_txrx(ctx);
dwt_reset_rfrx(ctx);
if (CONFIG_IEEE802154_DW1000_SNIFF_ONT != 0) {
dwt_set_rx_mode(ctx);
}
/* Re-enable RX after packet reception */
atomic_set_bit(&ctx->state, DWT_STATE_RX_DEF_ON);
dwt_enable_rx(ctx, 0);
k_sem_give(&ctx->dev_lock);
LOG_INF("Started %p", dev);
return 0;
}
static int dwt_stop(struct device *dev)
{
struct dwt_context *ctx = dev->driver_data;
k_sem_take(&ctx->dev_lock, K_FOREVER);
dwt_disable_txrx(ctx);
dwt_reset_rfrx(ctx);
dwt_setup_int(ctx, false);
/* Copy the user configuration and enter sleep mode */
dwt_reg_write_u8(ctx, 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(struct dwt_context *ctx, 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(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, clks);
}
static inline void dwt_set_sysclks_auto(struct dwt_context *ctx)
{
uint8_t sclks = DWT_PMSC_CTRL0_SYSCLKS_AUTO |
DWT_PMSC_CTRL0_RXCLKS_AUTO |
DWT_PMSC_CTRL0_TXCLKS_AUTO;
dwt_reg_write_u8(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_OFFSET, sclks);
}
static uint32_t dwt_otpmem_read(struct dwt_context *ctx, uint16_t otp_addr)
{
dwt_reg_write_u16(ctx, DWT_OTP_IF_ID, DWT_OTP_ADDR, otp_addr);
dwt_reg_write_u8(ctx, 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(ctx, DWT_OTP_IF_ID, DWT_OTP_CTRL, 0x00);
/* Read read data, available 40ns after rising edge of OTP_READ */
return dwt_reg_read_u32(ctx, DWT_OTP_IF_ID, DWT_OTP_RDAT);
}
static int dwt_initialise_dev(struct dwt_context *ctx)
{
uint32_t otp_val = 0;
uint8_t xtal_trim;
dwt_set_sysclks_xti(ctx, false);
ctx->sleep_mode = 0;
/* Disable PMSC control of analog RF subsystem */
dwt_reg_write_u16(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL1_OFFSET,
DWT_PMSC_CTRL1_PKTSEQ_DISABLE);
/* Clear all status flags */
dwt_reg_write_u32(ctx, 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(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_ALL);
k_sleep(K_MSEC(1));
dwt_reg_write_u8(ctx, DWT_PMSC_ID, DWT_PMSC_CTRL0_SOFTRESET_OFFSET,
DWT_PMSC_CTRL0_RESET_CLEAR);
dwt_set_sysclks_xti(ctx, false);
/*
* This bit (a.k.a PLLLDT) should be set to ensure reliable
* operation of the CPLOCK bit.
*/
dwt_reg_write_u8(ctx, 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(ctx, DWT_OTP_LDOTUNE_ADDR);
if ((otp_val & 0xFF) != 0) {
dwt_reg_write_u8(ctx, 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(ctx, 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(ctx, DWT_OTP_PARTID_ADDR));
LOG_DBG("LOT ID 0x%08x", dwt_otpmem_read(ctx, DWT_OTP_LOTID_ADDR));
LOG_DBG("Vbat 0x%02x", dwt_otpmem_read(ctx, DWT_OTP_VBAT_ADDR));
LOG_DBG("Vtemp 0x%02x", dwt_otpmem_read(ctx, 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(ctx, 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(ctx, true);
dwt_reg_write_u16(ctx, DWT_OTP_IF_ID, DWT_OTP_CTRL,
DWT_OTP_CTRL_LDELOAD);
k_sleep(K_MSEC(1));
dwt_set_sysclks_xti(ctx, false);
ctx->sleep_mode |= DWT_AON_WCFG_ONW_LLDE;
dwt_set_sysclks_auto(ctx);
if (!(dwt_reg_read_u8(ctx, DWT_SYS_STATUS_ID, 0) &
DWT_SYS_STATUS_CPLOCK)) {
LOG_WRN("PLL has not locked");
return -EIO;
}
dwt_set_spi_fast(ctx);
/* Setup antenna delay values */
dwt_reg_write_u16(ctx, DWT_LDE_IF_ID, DWT_LDE_RXANTD_OFFSET,
DW1000_RX_ANT_DLY);
dwt_reg_write_u16(ctx, DWT_TX_ANTD_ID, DWT_TX_ANTD_OFFSET,
DW1000_TX_ANT_DLY);
/* Clear AON_CFG1 register */
dwt_reg_write_u8(ctx, 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 avaiable, 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(ctx, 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(ctx, 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(struct dwt_context *ctx)
{
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(ctx, DWT_SYS_CFG_ID, 0);
tx_fctrl = dwt_reg_read_u32(ctx, 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(ctx, DWT_SYS_CFG_ID, 0, sys_cfg);
LOG_DBG("SYS_CFG: 0x%08x", sys_cfg);
dwt_reg_write_u16(ctx, DWT_LDE_IF_ID, DWT_LDE_REPC_OFFSET, lde_repc);
LOG_DBG("LDE_REPC: 0x%04x", lde_repc);
dwt_reg_write_u8(ctx, DWT_LDE_IF_ID, DWT_LDE_CFG1_OFFSET,
DWT_DEFAULT_LDE_CFG1);
if (rf_cfg->prf == DWT_PRF_64M) {
dwt_reg_write_u16(ctx, 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(ctx, 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(ctx, DWT_FS_CTRL_ID, DWT_FS_PLLCFG_OFFSET, pll_cfg);
LOG_DBG("PLLCFG: 0x%08x", pll_cfg);
dwt_reg_write_u8(ctx, 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(ctx, 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(ctx, DWT_RF_CONF_ID, DWT_RF_TXCTRL_OFFSET, txctrl);
LOG_DBG("TXCTRL: 0x%08x", txctrl);
/* Digital receiver configuration, DRX_CONF */
dwt_reg_write_u16(ctx, DWT_DRX_CONF_ID, DWT_DRX_TUNE0b_OFFSET, tune0b);
LOG_DBG("DRX_TUNE0b: 0x%04x", tune0b);
dwt_reg_write_u16(ctx, DWT_DRX_CONF_ID, DWT_DRX_TUNE1a_OFFSET, tune1a);
LOG_DBG("DRX_TUNE1a: 0x%04x", tune1a);
dwt_reg_write_u16(ctx, DWT_DRX_CONF_ID, DWT_DRX_TUNE1b_OFFSET, tune1b);
LOG_DBG("DRX_TUNE1b: 0x%04x", tune1b);
dwt_reg_write_u32(ctx, DWT_DRX_CONF_ID, DWT_DRX_TUNE2_OFFSET, tune2);
LOG_DBG("DRX_TUNE2: 0x%08x", tune2);
dwt_reg_write_u8(ctx, DWT_DRX_CONF_ID, DWT_DRX_TUNE4H_OFFSET, tune4h);
LOG_DBG("DRX_TUNE4H: 0x%02x", tune4h);
dwt_reg_write_u16(ctx, 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(ctx, DWT_AGC_CTRL_ID, DWT_AGC_TUNE1_OFFSET,
agc_tune1);
LOG_DBG("AGC_TUNE1: 0x%04x", agc_tune1);
dwt_reg_write_u32(ctx, 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(ctx, 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(ctx, 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(ctx, DWT_TX_FCTRL_ID, 0, tx_fctrl);
LOG_DBG("TX_FCTRL 0x%08x", tx_fctrl);
/* Set the Pulse Generator Delay */
dwt_reg_write_u8(ctx, DWT_TX_CAL_ID, DWT_TC_PGDELAY_OFFSET, pgdelay);
LOG_DBG("PGDELAY 0x%02x", pgdelay);
/* Set Transmit Power Control */
dwt_reg_write_u32(ctx, 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(ctx, 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(struct device *dev)
{
struct dwt_context *ctx = dev->driver_data;
const struct dwt_hi_cfg *hi_cfg = dev->config_info;
LOG_INF("Initialize DW1000 Transceiver");
k_sem_init(&ctx->phy_sem, 0, 1);
/* SPI config */
ctx->spi_cfg_slow.operation = SPI_WORD_SET(8);
ctx->spi_cfg_slow.frequency = DWT_SPI_SLOW_FREQ;
ctx->spi_cfg_slow.slave = hi_cfg->spi_slave;
ctx->spi_cfg_fast.operation = SPI_WORD_SET(8);
ctx->spi_cfg_fast.frequency = hi_cfg->spi_freq;
ctx->spi_cfg_fast.slave = hi_cfg->spi_slave;
ctx->spi = device_get_binding((char *)hi_cfg->spi_port);
if (!ctx->spi) {
LOG_ERR("SPI master port %s not found", hi_cfg->spi_port);
return -EINVAL;
}
#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0)
ctx->spi_cs.gpio_dev =
device_get_binding((char *)hi_cfg->spi_cs_port);
if (!ctx->spi_cs.gpio_dev) {
LOG_ERR("SPI CS port %s not found", hi_cfg->spi_cs_port);
return -EINVAL;
}
ctx->spi_cs.gpio_pin = hi_cfg->spi_cs_pin;
ctx->spi_cs.gpio_dt_flags = hi_cfg->spi_cs_flags;
ctx->spi_cfg_slow.cs = &ctx->spi_cs;
ctx->spi_cfg_fast.cs = &ctx->spi_cs;
#endif
dwt_set_spi_slow(ctx, DWT_SPI_SLOW_FREQ);
/* Initialize IRQ GPIO */
ctx->irq_gpio = device_get_binding((char *)hi_cfg->irq_port);
if (!ctx->irq_gpio) {
LOG_ERR("GPIO port %s not found", hi_cfg->irq_port);
return -EINVAL;
}
if (gpio_pin_configure(ctx->irq_gpio, hi_cfg->irq_pin,
GPIO_INPUT | hi_cfg->irq_flags)) {
LOG_ERR("Unable to configure GPIO pin %u", hi_cfg->irq_pin);
return -EINVAL;
}
gpio_init_callback(&(ctx->gpio_cb), dwt_gpio_callback,
BIT(hi_cfg->irq_pin));
if (gpio_add_callback(ctx->irq_gpio, &(ctx->gpio_cb))) {
LOG_ERR("Failed to add IRQ callback");
return -EINVAL;
}
/* Initialize RESET GPIO */
ctx->rst_gpio = device_get_binding(hi_cfg->rst_port);
if (ctx->rst_gpio == NULL) {
LOG_ERR("Could not get GPIO port for RESET");
return -EIO;
}
if (gpio_pin_configure(ctx->rst_gpio, hi_cfg->rst_pin,
GPIO_INPUT | hi_cfg->rst_flags)) {
LOG_ERR("Unable to configure GPIO pin %u", hi_cfg->rst_pin);
return -EINVAL;
}
LOG_INF("GPIO and SPI configured");
dwt_hw_reset(dev);
if (dwt_reg_read_u32(ctx, DWT_DEV_ID_ID, 0) != DWT_DEVICE_ID) {
LOG_ERR("Failed to read device ID %p", dev);
return -ENODEV;
}
if (dwt_initialise_dev(ctx)) {
LOG_ERR("Failed to initialize DW1000");
return -EIO;
}
if (dwt_configure_rf_phy(ctx)) {
LOG_ERR("Failed to configure RF PHY");
return -EIO;
}
/* Allow Beacon, Data, Acknowledgement, MAC command */
dwt_set_frame_filter(ctx, 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(ctx, 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 */
k_work_q_start(&dwt_work_queue,
dwt_work_queue_stack,
K_THREAD_STACK_SIZEOF(dwt_work_queue_stack),
CONFIG_SYSTEM_WORKQUEUE_PRIORITY);
k_work_init(&ctx->irq_cb_work, dwt_irq_work_handler);
dwt_setup_int(ctx, true);
LOG_INF("DW1000 device initialized and configured");
return 0;
}
static inline uint8_t *get_mac(struct device *dev)
{
struct dwt_context *dw1000 = dev->driver_data;
uint32_t *ptr = (uint32_t *)(dw1000->mac_addr);
UNALIGNED_PUT(sys_rand32_get(), ptr);
ptr = (uint32_t *)(dw1000->mac_addr + 4);
UNALIGNED_PUT(sys_rand32_get(), ptr);
dw1000->mac_addr[0] = (dw1000->mac_addr[0] & ~0x01) | 0x02;
return dw1000->mac_addr;
}
static void dwt_iface_api_init(struct net_if *iface)
{
struct device *dev = net_if_get_device(iface);
struct dwt_context *dw1000 = dev->driver_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 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,
};
#define DWT_PSDU_LENGTH (127 - DWT_FCS_LENGTH)
#if defined(CONFIG_IEEE802154_RAW_MODE)
DEVICE_AND_API_INIT(dw1000, DT_INST_LABEL(0),
dw1000_init, &dwt_0_context, &dw1000_0_config,
POST_KERNEL, CONFIG_IEEE802154_DW1000_INIT_PRIO,
&dwt_radio_api);
#else
NET_DEVICE_INIT(dw1000,
DT_INST_LABEL(0),
dw1000_init,
device_pm_control_nop,
&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