Merge pull request #2664 from utzig/kinetis-trng-refactor

Updates to the kinetis TRNG driver
diff --git a/hw/drivers/trng/trng_kinetis/src/trng_kinetis.c b/hw/drivers/trng/trng_kinetis/src/trng_kinetis.c
index 7f38cdf..dfab99d 100644
--- a/hw/drivers/trng/trng_kinetis/src/trng_kinetis.c
+++ b/hw/drivers/trng/trng_kinetis/src/trng_kinetis.c
@@ -35,6 +35,22 @@
     do {                                                              \
         (base)->MCTL |= (TRNG_MCTL_PRGM_MASK | TRNG_MCTL_ERR_MASK);   \
     } while (0)
+#define TRNG_CLEAR_AND_ENABLE_INTS(base)                              \
+    do {                                                              \
+        (base)->INT_CTRL &= ~(TRNG_INT_CTRL_HW_ERR_MASK |             \
+                              TRNG_INT_CTRL_ENT_VAL_MASK);            \
+        (base)->INT_MASK |= TRNG_INT_MASK_HW_ERR_MASK |               \
+                            TRNG_INT_MASK_ENT_VAL_MASK;               \
+    } while (0)
+#define TRNG_CLEAR_AND_ENABLE_ENTROPY_INT(base)                       \
+    do {                                                              \
+        (base)->INT_CTRL &= ~TRNG_INT_CTRL_ENT_VAL_MASK;              \
+        (base)->INT_MASK |= TRNG_INT_MASK_ENT_VAL_MASK;               \
+    } while (0)
+#define TRNG_DISABLE_ENTROPY_INT(base)                                \
+    do {                                                              \
+        (base)->INT_MASK &= ~TRNG_INT_MASK_ENT_VAL_MASK;              \
+    } while (0)
 #else
 #error "Unsupported TRNG interface"
 #endif
@@ -42,33 +58,80 @@
 #include "trng/trng.h"
 #include "trng_kinetis/trng_kinetis.h"
 
-static uint8_t rng_cache[ MYNEWT_VAL(KINETIS_TRNG_CACHE_LEN) ];
-static uint16_t rng_cache_out;
-static uint16_t rng_cache_in;
-static struct os_mutex rng_cache_mu;
-static os_stack_t *pstack;
+#define TRNG_CACHE_LEN MYNEWT_VAL(KINETIS_TRNG_CACHE_LEN)
+static struct {
+    uint16_t out;
+    uint16_t in;
+    uint16_t used;
+    struct os_mutex mu;
+    uint8_t cache[TRNG_CACHE_LEN];
+} rng_state;
+static_assert(sizeof(rng_state.cache) == TRNG_CACHE_LEN,
+              "Must fix TRNG_CACHE_LEN usage");
+#define CACHE_OUT(x)                                               \
+    do {                                                           \
+        (x) = rng_state.cache[rng_state.out];                      \
+        rng_state.out = (rng_state.out + 1) % TRNG_CACHE_LEN;      \
+        rng_state.used--;                                          \
+    } while (0)
+#define CACHE_IN(x)                                                \
+    do {                                                           \
+        rng_state.cache[rng_state.in] = (x);                       \
+        rng_state.in = (rng_state.in + 1) % TRNG_CACHE_LEN;        \
+        rng_state.used++;                                          \
+    } while (0)
+#define IS_CACHE_FULL() (rng_state.used == TRNG_CACHE_LEN)
+#define IS_CACHE_EMPTY() (rng_state.used == 0)
+#define CACHE_INIT()                                               \
+    do {                                                           \
+        rng_state.out = 0;                                         \
+        rng_state.in = 0;                                          \
+        rng_state.used = 0;                                        \
+    } while (0)
+#define CACHE_LOCK() os_mutex_pend(&rng_state.mu, OS_TIMEOUT_NEVER)
+#define CACHE_UNLOCK() os_mutex_release(&rng_state.mu)
+#define CACHE_INIT_LOCK() os_mutex_init(&rng_state.mu)
+
+#if USE_RNGA
 static bool running;
+#endif
 static struct os_eventq rng_evtq;
+static struct os_event evt = {0};
 
 #define TRNG_POLLER_PRIO (8)
 #define TRNG_POLLER_STACK_SIZE OS_STACK_ALIGN(64)
-static struct os_task poller_task;
+static struct os_task trng_poller_task;
+OS_TASK_STACK_DEFINE(trng_poller_stack, TRNG_POLLER_STACK_SIZE);
 
-static inline void
+#if USE_TRNG
+static void
+trng_irq_handler(void)
+{
+    if (TRNG0->MCTL & TRNG_MCTL_ERR_MASK) {
+        TRNG0->MCTL |= TRNG_MCTL_ERR_MASK;
+    }
+
+    if (TRNG0->INT_CTRL & TRNG_INT_CTRL_HW_ERR_MASK) {
+        TRNG0->INT_CTRL &= ~TRNG_INT_CTRL_HW_ERR_MASK;
+    }
+
+    if (TRNG0->INT_CTRL & TRNG_INT_CTRL_ENT_VAL_MASK) {
+        TRNG_DISABLE_ENTROPY_INT(TRNG0);
+        (void)os_eventq_put(&rng_evtq, &evt);
+    }
+}
+#endif
+
+static void
 kinetis_trng_start(void)
 {
-    struct os_event evt;
-
 #if USE_RNGA
     RNGA_SetMode(RNG, kRNGA_ModeNormal);
+    running = true;
+    (void)os_eventq_put(&rng_evtq, &evt);
 #elif USE_TRNG
     TRNG_START(TRNG0);
 #endif
-    running = true;
-
-    evt.ev_queued = 0;
-    evt.ev_arg = NULL;
-    (void)os_eventq_put(&rng_evtq, &evt);
 }
 
 static inline void
@@ -76,47 +139,35 @@
 {
 #if USE_RNGA
     RNGA_SetMode(RNG, kRNGA_ModeSleep);
+    running = false;
 #elif USE_TRNG
     TRNG_STOP(TRNG0);
 #endif
-
-   running = false;
 }
 
 static size_t
 kinetis_trng_read(struct trng_dev *trng, void *ptr, size_t size)
 {
     size_t num_read;
+    uint8_t *u8p;
 
-    os_mutex_pend(&rng_cache_mu, OS_TIMEOUT_NEVER);
+    num_read = 0;
+    u8p = (uint8_t *)ptr;
 
-    if (rng_cache_out <= rng_cache_in) {
-        size = min(size, rng_cache_in - rng_cache_out);
-        memcpy(ptr, &rng_cache[rng_cache_out], size);
-        num_read = size;
-    } else if (rng_cache_out + size <= sizeof(rng_cache)) {
-        memcpy(ptr, &rng_cache[rng_cache_out], size);
-        num_read = size;
-    } else {
-        num_read = sizeof(rng_cache) - rng_cache_out;
-        memcpy(ptr, &rng_cache[rng_cache_out], num_read);
+    CACHE_LOCK();
 
-        size -= num_read;
-        ptr += num_read;
-
-        size = min(size, rng_cache_in);
-        memcpy(ptr, rng_cache, size);
-        num_read += size;
+    while (!IS_CACHE_EMPTY() && size) {
+        CACHE_OUT(*u8p++);
+        num_read++;
+        size--;
     }
 
-    rng_cache_out = (rng_cache_out + num_read) % sizeof(rng_cache);
+    CACHE_UNLOCK();
 
     if (num_read > 0) {
         kinetis_trng_start();
     }
 
-    os_mutex_release(&rng_cache_mu);
-
     return num_read;
 }
 
@@ -146,31 +197,39 @@
     int rc;
 
     while (1) {
-        if (running) {
+#if USE_TRNG
+        (void)os_eventq_get(&rng_evtq);
+#endif
+
 #if USE_RNGA
+        if (running) {
             rc = RNGA_GetRandomData(RNG, data, sizeof(uint32_t));
 #else
             rc = TRNG_GetRandomData(TRNG0, data, sizeof(uint32_t));
 #endif
             if (rc == 0) {
-                os_mutex_pend(&rng_cache_mu, OS_TIMEOUT_NEVER);
+                CACHE_LOCK();
+
                 for (i = 0; i < 4; i++) {
-                    rng_cache[rng_cache_in++] = data[i];
-
-                    if (rng_cache_in >= sizeof(rng_cache)) {
-                        rng_cache_in = 0;
-                    }
-
-                    if ((rng_cache_in + 1) % sizeof(rng_cache) == rng_cache_out) {
+                    if (IS_CACHE_FULL()) {
                         kinetis_trng_stop();
                         break;
                     }
+
+                    CACHE_IN(data[i]);
                 }
-                os_mutex_release(&rng_cache_mu);
+
+                CACHE_UNLOCK();
+
+#if USE_TRNG
+                TRNG_CLEAR_AND_ENABLE_ENTROPY_INT(TRNG0);
+#endif
             }
+#if USE_RNGA
         } else {
             (void)os_eventq_get(&rng_evtq);
         }
+#endif
     }
 }
 
@@ -179,22 +238,28 @@
 {
     struct trng_dev *trng;
 #if USE_TRNG
-    trng_config_t default_config;
+    trng_config_t trng_config;
 #endif
 
     trng = (struct trng_dev *)dev;
     assert(trng);
 
     if (!(dev->od_flags & OS_DEV_F_STATUS_OPEN)) {
-        rng_cache_out = 0;
-        rng_cache_in = 0;
+        CACHE_INIT();
 
 #if USE_RNGA
         RNGA_Init(RNG);
         RNGA_Seed(RNG, SIM->UIDL);
+
 #elif USE_TRNG
-        (void)TRNG_GetDefaultConfig(&default_config);
-        TRNG_Init(TRNG0, &default_config);
+        NVIC_SetVector(TRNG0_IRQn, (uint32_t)trng_irq_handler);
+        NVIC_EnableIRQ(TRNG0_IRQn);
+
+        (void)TRNG_GetDefaultConfig(&trng_config);
+        trng_config.entropyDelay = MYNEWT_VAL(KINETIS_TRNG_ENTROPY_DELAY);
+        TRNG_Init(TRNG0, &trng_config);
+
+        TRNG_CLEAR_AND_ENABLE_INTS(TRNG0);
 #endif
 
         kinetis_trng_start();
@@ -218,13 +283,11 @@
     trng->interface.read = kinetis_trng_read;
 
     os_eventq_init(&rng_evtq);
-    os_mutex_init(&rng_cache_mu);
+    CACHE_INIT_LOCK();
 
-    pstack = malloc(sizeof(os_stack_t) * TRNG_POLLER_STACK_SIZE);
-    assert(pstack);
-
-    rc = os_task_init(&poller_task, "trng_poller", trng_poller_handler, NULL,
-            TRNG_POLLER_PRIO, OS_WAIT_FOREVER, pstack, TRNG_POLLER_STACK_SIZE);
+    rc = os_task_init(&trng_poller_task, "trng_poller", trng_poller_handler, NULL,
+            TRNG_POLLER_PRIO, OS_WAIT_FOREVER, trng_poller_stack,
+            TRNG_POLLER_STACK_SIZE);
     if (rc) {
         return rc;
     }
diff --git a/hw/drivers/trng/trng_kinetis/syscfg.yml b/hw/drivers/trng/trng_kinetis/syscfg.yml
index 7c72a7b..dc01492 100644
--- a/hw/drivers/trng/trng_kinetis/syscfg.yml
+++ b/hw/drivers/trng/trng_kinetis/syscfg.yml
@@ -30,3 +30,10 @@
         value: 0
         restrictions:
             - '!KINETIS_TRNG_USE_RNGA'
+    KINETIS_TRNG_ENTROPY_DELAY:
+        description: >
+            The configured number is the length, in system clocks, of each
+            entropy sample taken. The  default value is 3200, but it can be
+            reduced for faster sample generation, given the compromise of
+            generating worse entropy.
+        value: 3200