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