1694 lines
45 KiB
C
1694 lines
45 KiB
C
/*
|
||
* 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
|