From a92b577bc1e85072f8b5e00c198e4583671fe8ae Mon Sep 17 00:00:00 2001
From: Hauke Petersen <hauke.petersen@fu-berlin.de>
Date: Thu, 5 Apr 2018 10:58:52 +0200
Subject: [PATCH] cpu/nrf5x: added nrfble radio driver

---
 cpu/nrf5x_common/Makefile              |   3 +
 cpu/nrf5x_common/include/nrfble.h      |  55 ++++
 cpu/nrf5x_common/radio/nrfble/Makefile |   3 +
 cpu/nrf5x_common/radio/nrfble/nrfble.c | 360 +++++++++++++++++++++++++
 4 files changed, 421 insertions(+)
 create mode 100644 cpu/nrf5x_common/include/nrfble.h
 create mode 100644 cpu/nrf5x_common/radio/nrfble/Makefile
 create mode 100644 cpu/nrf5x_common/radio/nrfble/nrfble.c

diff --git a/cpu/nrf5x_common/Makefile b/cpu/nrf5x_common/Makefile
index ef599b387d..41f24c0a2d 100644
--- a/cpu/nrf5x_common/Makefile
+++ b/cpu/nrf5x_common/Makefile
@@ -2,6 +2,9 @@ MODULE = cpu_common
 DIRS = periph
 
 # build one of the radio drivers, if enabled
+ifneq (,$(filter nrfble,$(USEMODULE)))
+  DIRS += radio/nrfble
+endif
 ifneq (,$(filter nrfmin,$(USEMODULE)))
   DIRS += radio/nrfmin
 endif
diff --git a/cpu/nrf5x_common/include/nrfble.h b/cpu/nrf5x_common/include/nrfble.h
new file mode 100644
index 0000000000..9c41b3ee44
--- /dev/null
+++ b/cpu/nrf5x_common/include/nrfble.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    drivers_nrf5x_nrfble NRF BLE radio driver
+ * @ingroup     drivers_netdev
+ * @brief       Radio driver for nRF5x SoCs for using the radio in BLE mode
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for the nrfble radio driver
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NRFBLE_H
+#define NRFBLE_H
+
+#include "net/netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   nrfble channel configuration
+ * @{
+ */
+#define NRFBLE_CHAN_MIN             (0U)
+#define NRFBLE_CHAN_MAX             (39U)
+/** @} */
+
+/**
+ * @brief   Default transmission power used
+ */
+#define NRFBLE_TXPOWER_DEFAULT      (0)                 /* 0dBm */
+
+/**
+ * @brief   Setup the device driver's data structures
+ *
+ * @return  pointer to the device's netdev struct
+ */
+netdev_t *nrfble_setup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NRFBLE_H */
+/** @} */
diff --git a/cpu/nrf5x_common/radio/nrfble/Makefile b/cpu/nrf5x_common/radio/nrfble/Makefile
new file mode 100644
index 0000000000..1f6a482e4f
--- /dev/null
+++ b/cpu/nrf5x_common/radio/nrfble/Makefile
@@ -0,0 +1,3 @@
+MODULE = nrfble
+
+include $(RIOTBASE)/Makefile.base
diff --git a/cpu/nrf5x_common/radio/nrfble/nrfble.c b/cpu/nrf5x_common/radio/nrfble/nrfble.c
new file mode 100644
index 0000000000..dd500cbde3
--- /dev/null
+++ b/cpu/nrf5x_common/radio/nrfble/nrfble.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_nrf5x_nrfble
+ *
+ * @note        This driver is not thread safe and should only be used by a
+ *              single thread at once!
+ * @{
+ *
+ * @file
+ * @brief       Bluetooth low energy radio driver for nRF5x SoCs
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <errno.h>
+
+#include "cpu.h"
+#include "assert.h"
+
+#include "nrfble.h"
+#include "net/netdev/ble.h"
+
+#define ENABLE_DEBUG            (0)
+#include "debug.h"
+
+/* driver specific device configuration */
+#define CONF_MODE               RADIO_MODE_MODE_Ble_1Mbit
+#define CONF_LEN                (8U)
+#define CONF_S0                 (1U)
+#define CONF_S1                 (0U)
+#define CONF_STATLEN            (0U)
+#define CONF_BASE_ADDR_LEN      (3U)
+#define CONF_ENDIAN             RADIO_PCNF1_ENDIAN_Little
+#define CONF_WHITENING          RADIO_PCNF1_WHITEEN_Enabled
+#define CONF_TIFS               (150U)
+#define CONF_CRC_LEN            (0X3 | RADIO_CRCCNF_SKIPADDR_Msk)
+#define CONF_CRC_POLY           (0x00065b)
+
+/* used shortcuts */
+#define SHORTS_BASE             (RADIO_SHORTS_READY_START_Msk | \
+                                 RADIO_SHORTS_END_DISABLE_Msk)
+#define SHORTS_RX               (SHORTS_BASE | RADIO_SHORTS_DISABLED_TXEN_Msk)
+#define SHORTS_TX               (SHORTS_BASE | RADIO_SHORTS_DISABLED_RXEN_Msk)
+
+/* interrupt masks */
+#define INT_DIS                 (RADIO_INTENCLR_DISABLED_Msk | \
+                                 RADIO_INTENCLR_ADDRESS_Msk)
+#define INT_EN                  (RADIO_INTENSET_DISABLED_Msk | \
+                                 RADIO_INTENSET_ADDRESS_Msk)
+
+/* driver internal radio states */
+#define STATE_IDLE              (0x00)
+#define STATE_RX                (0x01)
+#define STATE_TX                (0x02)
+#define STATE_BUSY              (0x80)
+
+/* forward declaration of the netdev driver struct */
+static const netdev_driver_t netdev_driver;
+
+/* current radio state */
+static volatile uint8_t _state = STATE_IDLE;
+
+/* active radio context */
+static netdev_ble_ctx_t *_ctx = NULL;
+
+/* allocate the netdev device descriptor */
+netdev_t _nrfble_dev;
+
+/* map logical BLE channel values to actual radio frequencies */
+static const uint8_t _ble_chan_map[40] = {
+    [ 0] = 4,
+    [ 1] = 6,
+    [ 2] = 8,
+    [ 3] = 10,
+    [ 4] = 12,
+    [ 5] = 14,
+    [ 6] = 16,
+    [ 7] = 18,
+    [ 8] = 20,
+    [ 9] = 22,
+    [10] = 24,
+    [11] = 28,
+    [12] = 30,
+    [13] = 32,
+    [14] = 34,
+    [15] = 36,
+    [16] = 38,
+    [17] = 40,
+    [18] = 42,
+    [19] = 44,
+    [20] = 46,
+    [21] = 48,
+    [22] = 50,
+    [23] = 52,
+    [24] = 54,
+    [25] = 56,
+    [26] = 58,
+    [27] = 60,
+    [28] = 62,
+    [29] = 64,
+    [30] = 66,
+    [31] = 68,
+    [32] = 70,
+    [33] = 72,
+    [34] = 74,
+    [35] = 76,
+    [36] = 78,
+    [37] = 2,
+    [38] = 26,
+    [39] = 80,
+};
+
+/**
+ * @brief   Set radio into idle (DISABLED) state
+ */
+static void _go_idle(void)
+{
+    if (!(_state & STATE_BUSY)) {
+        NRF_RADIO->INTENCLR = INT_DIS;
+        NRF_RADIO->SHORTS = 0;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->TASKS_DISABLE = 1;
+        while (NRF_RADIO->EVENTS_DISABLED == 0) {}
+        _state = STATE_IDLE;
+    }
+}
+
+/**
+ * @brief   Set radio context (channel, CRC, whitening, and access address)
+ */
+static void _set_context(netdev_ble_ctx_t *ctx)
+{
+    if (ctx) {
+        assert(ctx->chan <= NRFBLE_CHAN_MAX);
+
+        _ctx = ctx;
+        NRF_RADIO->FREQUENCY = _ble_chan_map[ctx->chan];
+        NRF_RADIO->PREFIX0 = ctx->aa.raw[3];
+        NRF_RADIO->BASE0   = (uint32_t)((ctx->aa.raw[0] << 8) |
+                                        (ctx->aa.raw[1] << 16) |
+                                        (ctx->aa.raw[2] << 24));
+        NRF_RADIO->DATAWHITEIV = ctx->chan;
+        NRF_RADIO->CRCINIT = (ctx->crc & NETDEV_BLE_CRC_MASK);
+    }
+    else {
+        _go_idle();
+    }
+}
+
+static int16_t _nrfble_get_txpower(void)
+{
+    int8_t p = (int8_t)NRF_RADIO->TXPOWER;
+    if (p < 0) {
+        return (int16_t)(0xff00 | p);
+    }
+    return (int16_t)p;
+}
+
+static void _nrfble_set_txpower(int16_t power)
+{
+    if (power > 2) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
+    }
+    else if (power > -2) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
+    }
+    else if (power > -6) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
+    }
+    else if (power > -10) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
+    }
+    else if (power > -14) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
+    }
+    else if (power > -18) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
+    }
+    else if (power > -25) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;
+    }
+    else {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm;
+    }
+}
+
+/**
+ * @brief   Radio interrupt routine
+ */
+void isr_radio(void)
+{
+    if (NRF_RADIO->EVENTS_ADDRESS) {
+        NRF_RADIO->EVENTS_ADDRESS = 0;
+        _state |= STATE_BUSY;
+    }
+
+    if (NRF_RADIO->EVENTS_DISABLED) {
+        NRF_RADIO->EVENTS_DISABLED = 0;
+
+        if (_state == (STATE_BUSY | STATE_RX)) {
+            _state = STATE_TX;
+            if (NRF_RADIO->CRCSTATUS) {
+                _ctx->crc |= NETDEV_BLE_CRC_OK;
+            }
+            else {
+                _ctx->crc &= ~(NETDEV_BLE_CRC_OK);
+            }
+            _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_RX_COMPLETE);
+        }
+        else {  /* on TX done */
+            _state = STATE_RX;
+            _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_TX_COMPLETE);
+        }
+    }
+
+    cortexm_isr_end();
+}
+
+netdev_t *nrfble_setup(void)
+{
+    _nrfble_dev.driver = &netdev_driver;
+    _nrfble_dev.event_callback = NULL;
+    _nrfble_dev.context = NULL;
+#ifdef MODULE_NETSTATS_L2
+    memset(&_nrfble_dev.stats, 0, sizeof(netstats_t));;
+#endif
+    return &_nrfble_dev;
+}
+
+static int _nrfble_init(netdev_t *dev)
+{
+    (void)dev;
+    assert(_nrfble_dev.driver && _nrfble_dev.event_callback);
+
+    /* power on the NRFs radio */
+    NRF_RADIO->POWER = 1;
+    /* configure variable parameters to default values */
+    NRF_RADIO->TXPOWER = NRFBLE_TXPOWER_DEFAULT;
+    /* always send from and listen to logical address 0 */
+    NRF_RADIO->TXADDRESS = 0x00UL;
+    NRF_RADIO->RXADDRESSES = 0x01UL;
+    /* load driver specific configuration */
+    NRF_RADIO->MODE = CONF_MODE;
+    /* configure data fields and packet length whitening and endianess */
+    NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) |
+                        (CONF_S0 << RADIO_PCNF0_S0LEN_Pos) |
+                        (CONF_LEN << RADIO_PCNF0_LFLEN_Pos));
+    NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) |
+                        (CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) |
+                        (CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) |
+                        (CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) |
+                        (NETDEV_BLE_PDU_MAXLEN << RADIO_PCNF1_MAXLEN_Pos));
+    /* set inter frame spacing to 150us */
+    NRF_RADIO->TIFS = CONF_TIFS;
+    /* configure CRC length and polynomial */
+    NRF_RADIO->CRCCNF = CONF_CRC_LEN;
+    NRF_RADIO->CRCPOLY = CONF_CRC_POLY;
+    /* enable global interrupts, but mask all local interrupts for now */
+    NRF_RADIO->INTENCLR = 0xffffffff;
+    NVIC_EnableIRQ(RADIO_IRQn);
+
+    DEBUG("[nrfble] initialization successful\n");
+    return 0;
+}
+
+static int _nrfble_send(netdev_t *dev, const iolist_t *data)
+{
+    (void)dev;
+    assert(data);
+
+    NRF_RADIO->PACKETPTR = (uint32_t)data->iol_base;
+    NRF_RADIO->SHORTS = SHORTS_TX;
+
+    /* in case no trx sequence is active, we start a new one now */
+    if (_state == STATE_IDLE) {
+        _state = STATE_TX;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->INTENSET = INT_EN;
+        NRF_RADIO->TASKS_TXEN = 1;
+    }
+
+    return 0;
+}
+
+static int _nrfble_recv(netdev_t *dev, void *buf, size_t len, void *info)
+{
+    (void)dev;
+    (void)len;
+    (void)info;
+    assert(buf && (len == sizeof(netdev_ble_pkt_t)));
+
+    NRF_RADIO->PACKETPTR = (uint32_t)buf;
+    NRF_RADIO->SHORTS = SHORTS_RX;
+
+    /* in case no trx sequence is active, we start a new one now */
+    if (_state == STATE_IDLE) {
+        _state = STATE_RX;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->INTENSET = INT_EN;
+        NRF_RADIO->TASKS_RXEN = 1;
+    }
+
+    return 0;
+}
+
+static int _nrfble_get(netdev_t *dev, netopt_t opt, void *val, size_t max_len)
+{
+    (void)dev;
+    (void)max_len;
+
+    switch (opt) {
+        case NETOPT_TX_POWER:
+            assert(max_len >= sizeof(int16_t));
+            *((int16_t *)val) = _nrfble_get_txpower();
+            return sizeof(int16_t);
+        case NETOPT_DEVICE_TYPE:
+            assert(max_len >= sizeof(uint16_t));
+            *((uint16_t *)val) = NETDEV_TYPE_BLE;
+            return sizeof(uint16_t);
+        default:
+            return -ENOTSUP;
+    }
+}
+
+static int _nrfble_set(netdev_t *dev, netopt_t opt, const void *val, size_t len)
+{
+    (void)dev;
+    (void)len;
+
+    switch (opt) {
+        case NETOPT_BLE_CTX:
+            _set_context((netdev_ble_ctx_t *)val);
+            return sizeof(netdev_ble_ctx_t);
+        case NETOPT_TX_POWER:
+            assert(len == sizeof(int16_t));
+            _nrfble_set_txpower(*((const int16_t *)val));
+            return sizeof(int16_t);
+        default:
+            return -ENOTSUP;
+    }
+}
+
+/* export of the netdev interface */
+static const netdev_driver_t netdev_driver = {
+    .send = _nrfble_send,
+    .recv = _nrfble_recv,
+    .init = _nrfble_init,
+    .isr  = NULL,
+    .get  = _nrfble_get,
+    .set  = _nrfble_set
+};
-- 
GitLab