You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1683 lines
45 KiB
1683 lines
45 KiB
/* |
|
* 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 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_L2_PKT_INCL_FCS)) { |
|
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
|
|
|