You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by we...@apache.org on 2017/03/30 21:45:05 UTC

[2/5] incubator-mynewt-core git commit: Low power timer. Only nrf52 right now.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/507f493a/net/nimble/controller/src/ble_ll_sched.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll_sched.c b/net/nimble/controller/src/ble_ll_sched.c
index dd42c3f..ffe8ef3 100644
--- a/net/nimble/controller/src/ble_ll_sched.c
+++ b/net/nimble/controller/src/ble_ll_sched.c
@@ -21,24 +21,35 @@
 #include <string.h>
 #include "os/os.h"
 #include "os/os_cputime.h"
+#include "bsp.h"
 #include "ble/xcvr.h"
 #include "controller/ble_phy.h"
 #include "controller/ble_ll.h"
 #include "controller/ble_ll_sched.h"
 #include "controller/ble_ll_adv.h"
 #include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_xcvr.h"
 #include "ble_ll_conn_priv.h"
 
 /* XXX: this is temporary. Not sure what I want to do here */
 struct hal_timer g_ble_ll_sched_timer;
 
+#ifdef BLE_XCVR_RFCLK
+/* Settling time of crystal, in ticks */
+uint8_t g_ble_ll_sched_xtal_ticks;
+#endif
+
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+uint8_t g_ble_ll_sched_offset_ticks;
+#endif
+
 #define BLE_LL_SCHED_ADV_WORST_CASE_USECS       \
     (BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + BLE_LL_SCHED_ADV_MAX_USECS \
      + XCVR_TX_SCHED_DELAY_USECS)
 
-
 #if (BLE_LL_SCHED_DEBUG == 1)
 int32_t g_ble_ll_sched_max_late;
+int32_t g_ble_ll_sched_max_early;
 #endif
 
 /* XXX: TODO:
@@ -155,6 +166,17 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
     /* Get schedule element from connection */
     sch = &connsm->conn_sch;
 
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+    /* Set schedule start and end times */
+    sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks;
+    if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+        usecs = connsm->slave_cur_window_widening;
+        sch->start_time -= (os_cputime_usecs_to_ticks(usecs) + 1);
+        sch->remainder = 0;
+    } else {
+        sch->remainder = connsm->anchor_point_usecs;
+    }
+#else
     /* Set schedule start and end times */
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         usecs = XCVR_RX_SCHED_DELAY_USECS;
@@ -163,6 +185,7 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         usecs = XCVR_TX_SCHED_DELAY_USECS;
     }
     sch->start_time = connsm->anchor_point - os_cputime_usecs_to_ticks(usecs);
+#endif
     sch->end_time = connsm->ce_end_time;
 
     /* Better be past current time or we just leave */
@@ -236,8 +259,17 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         entry = start_overlap;
     }
 
+#ifdef BLE_XCVR_RFCLK
+    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (entry == sch) {
+        ble_ll_xcvr_rfclk_timer_start(sch->start_time);
+    } else {
+        sch = entry;
+    }
+#else
     /* Get first on list */
     sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+#endif
 
     OS_EXIT_CRITICAL(sr);
 
@@ -247,67 +279,108 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
     return rc;
 }
 
+/**
+ * Called to schedule a connection when the current role is master.
+ *
+ * Context: Interrupt
+ *
+ * @param connsm
+ * @param ble_hdr
+ * @param pyld_len
+ *
+ * @return int
+ */
 int
-ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
-                        uint8_t req_slots)
+ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
+                        struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len)
 {
     int rc;
     os_sr_t sr;
-    uint32_t tps;
+    uint8_t req_slots;
     uint32_t initial_start;
     uint32_t earliest_start;
     uint32_t earliest_end;
     uint32_t dur;
     uint32_t itvl_t;
-    uint32_t ce_end_time;
+    uint32_t adv_rxend;
     struct ble_ll_sched_item *entry;
     struct ble_ll_sched_item *sch;
 
-    /* Better have a connsm */
-    assert(connsm != NULL);
-
     /* Get schedule element from connection */
     rc = -1;
     sch = &connsm->conn_sch;
-
+    req_slots = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS);
+
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+    /* XXX:
+     * The calculations for the 32kHz crystal bear alot of explanation. The
+     * earliest possible time that the master can start the connection with a
+     * slave is 1.25 msecs from the end of the connection request. The
+     * connection request is sent an IFS time from the end of the advertising
+     * packet that was received plus the time it takes to send the connection
+     * request. At 1 Mbps, this is 1752 usecs, or 57.41 ticks. Using 57 ticks
+     * makes us off ~13 usecs. Since we dont want to actually calculate the
+     * receive end time tick (this would take too long), we assume the end of
+     * the advertising PDU is 'now' (we call os_cputime_get32). We dont know
+     * how much time it will take to service the ISR but if we are more than the
+     * rx to tx time of the chip we will not be successful transmitting the
+     * connect request. All this means is that we presume that the slave will
+     * receive the connect request later than we expect but no earlier than
+     * 13 usecs before (this is important).
+     *
+     * The code then attempts to schedule the connection at the
+     * earliest time although this may not be possible. When the actual
+     * schedule start time is determined, the master has to determine if this
+     * time is more than a transmit window offset interval (1.25 msecs). The
+     * master has to tell the slave how many transmit window offsets there are
+     * from the earliest possible time to when the actual transmit start will
+     * occur. Later in this function you will see the calculation. The actual
+     * transmission start has to occur within the transmit window. The transmit
+     * window interval is in units of 1.25 msecs and has to be at least 1. To
+     * make things a bit easier (but less power efficient for the slave), we
+     * use a transmit window of 2. We do this because we dont quite know the
+     * exact start of the transmission and if we are too early or too late we
+     * could miss the transmit window. A final note: the actual transmission
+     * start (the anchor point) is sched offset ticks from the schedule start
+     * time. We dont add this to the calculation when calculating the window
+     * offset. The reason we dont do this is we want to insure we transmit
+     * after the window offset we tell the slave. For example, say we think
+     * we are transmitting 1253 usecs from the earliest start. This would cause
+     * us to send a transmit window offset of 1. Since we are actually
+     * transmitting earlier than the slave thinks we could end up transmitting
+     * before the window offset. Transmitting later is fine since we have the
+     * transmit window to do so. Transmitting before is bad, since the slave
+     * wont be listening. We could do better calculation if we wanted to use
+     * a transmit window of 1 as opposed to 2, but for now we dont care.
+     */
+    dur = req_slots * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
+    adv_rxend = os_cputime_get32();
+    earliest_start = adv_rxend + 57;    /* XXX: only works for 1 Mbps */
+    earliest_end = earliest_start + dur;
+    itvl_t = connsm->conn_itvl_ticks;
+#else
+    adv_rxend = ble_hdr->beg_cputime +
+        os_cputime_usecs_to_ticks(BLE_TX_DUR_USECS_M(pyld_len));
     /*
      * The earliest start time is 1.25 msecs from the end of the connect
      * request transmission. Note that adv_rxend is the end of the received
      * advertisement, so we need to add an IFS plus the time it takes to send
-     * the connection request
+     * the connection request. The 1.25 msecs starts from the end of the conn
+     * request.
      */
     dur = os_cputime_usecs_to_ticks(req_slots * BLE_LL_SCHED_USECS_PER_SLOT);
     earliest_start = adv_rxend +
         os_cputime_usecs_to_ticks(BLE_LL_IFS + BLE_LL_CONN_REQ_DURATION +
                                   BLE_LL_CONN_INITIAL_OFFSET);
     earliest_end = earliest_start + dur;
-
     itvl_t = os_cputime_usecs_to_ticks(connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS);
+#endif
 
     /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
     /* The schedule item must occur after current running item (if any) */
     sch->start_time = earliest_start;
-
-    /*
-     * If we are currently in a connection, we add one slot time to the
-     * earliest start so we can end the connection reasonably.
-     */
-    if (ble_ll_state_get() == BLE_LL_STATE_CONNECTION) {
-        tps = os_cputime_usecs_to_ticks(BLE_LL_SCHED_USECS_PER_SLOT);
-        ce_end_time = ble_ll_conn_get_ce_end_time();
-        while ((int32_t)(ce_end_time - os_cputime_get32()) < 0) {
-            ce_end_time += tps;
-        }
-
-        /* Start at next slot boundary past earliest */
-        while ((int32_t)(ce_end_time - earliest_start) < 0) {
-            ce_end_time += tps;
-        }
-        earliest_start = ce_end_time;
-        earliest_end = earliest_start + dur;
-    }
     initial_start = earliest_start;
 
     if (!ble_ll_sched_insert_if_empty(sch)) {
@@ -338,6 +411,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
             }
         }
 
+        /* Must be able to schedule within one connection interval */
         if (!entry) {
             if ((earliest_start - initial_start) <= itvl_t) {
                 rc = 0;
@@ -346,18 +420,38 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
         }
 
         if (!rc) {
-            /* calculate number of connection intervals before start */
+            /* calculate number of window offsets. Each offset is 1.25 ms */
             sch->enqueued = 1;
-            connsm->tx_win_off = (earliest_start - initial_start) /
-                os_cputime_usecs_to_ticks(BLE_LL_CONN_ITVL_USECS);
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+            /*
+             * NOTE: we dont add sched offset ticks as we want to under-estimate
+             * the transmit window slightly since the window size is currently
+             * 2 when using a 32768 crystal.
+             */
+            dur = os_cputime_ticks_to_usecs(earliest_start - initial_start);
+            connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS;
+#else
+            dur = os_cputime_ticks_to_usecs(earliest_start - initial_start);
+            dur += XCVR_TX_SCHED_DELAY_USECS;
+            connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS;
+#endif
         }
     }
 
     if (!rc) {
         sch->start_time = earliest_start;
         sch->end_time = earliest_end;
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+        /*
+         * Since we have the transmit window to transmit in, we dont need
+         * to set the anchor point usecs; just transmit to the nearest tick.
+         */
+        connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks;
+        connsm->anchor_point_usecs = 0;
+#else
         connsm->anchor_point = earliest_start +
             os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS);
+#endif
         connsm->ce_end_time = earliest_end;
     }
 
@@ -371,6 +465,15 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
     return rc;
 }
 
+/**
+ * Schedules a slave connection for the first time.
+ *
+ * Context: Link Layer
+ *
+ * @param connsm
+ *
+ * @return int
+ */
 int
 ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
 {
@@ -380,15 +483,32 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
     struct ble_ll_sched_item *next_sch;
     struct ble_ll_sched_item *sch;
 
+#ifdef BLE_XCVR_RFCLK
+    int first;
+    first = 0;
+#endif
+
     /* Get schedule element from connection */
     rc = -1;
     sch = &connsm->conn_sch;
 
     /* Set schedule start and end times */
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+    /*
+     * XXX: for now, we dont care about anchor point usecs for the slave. It
+     * does not matter if we turn on the receiver up to one tick before w
+     * need to. We also subtract one extra tick since the conversion from
+     * usecs to ticks could be off by up to 1 tick.
+     */
+    sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks -
+        os_cputime_usecs_to_ticks(connsm->slave_cur_window_widening) - 1;
+#else
     sch->start_time = connsm->anchor_point -
         os_cputime_usecs_to_ticks(XCVR_RX_SCHED_DELAY_USECS +
                                   connsm->slave_cur_window_widening);
+#endif
     sch->end_time = connsm->ce_end_time;
+    sch->remainder = 0;
 
     /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
@@ -403,6 +523,9 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
     if (!entry) {
         /* Nothing in schedule. Schedule as soon as possible */
         rc = 0;
+#ifdef BLE_XCVR_RFCLK
+        first = 1;
+#endif
     } else {
         os_cputime_timer_stop(&g_ble_ll_sched_timer);
         while (1) {
@@ -435,9 +558,24 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
         if (!rc) {
             sch->enqueued = 1;
         }
+#ifdef BLE_XCVR_RFCLK
+        next_sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+        if (next_sch == sch) {
+            first = 1;
+        } else {
+            sch = next_sch;
+        }
+#else
         sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+#endif
     }
 
+#ifdef BLE_XCVR_RFCLK
+    if (first) {
+        ble_ll_xcvr_rfclk_timer_start(sch->start_time);
+    }
+#endif
+
     OS_EXIT_CRITICAL(sr);
 
     os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
@@ -450,9 +588,6 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch)
 {
     int rc;
     os_sr_t sr;
-    uint8_t ll_state;
-    int32_t ticks;
-    uint32_t ce_end_time;
     uint32_t adv_start;
     uint32_t duration;
     struct ble_ll_sched_item *entry;
@@ -463,39 +598,12 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch)
     orig = sch;
 
     OS_ENTER_CRITICAL(sr);
-
-    /*
-     * If we are currently in a connection, we add one slot time to the
-     * earliest start so we can end the connection reasonably.
-     */
-    ll_state = ble_ll_state_get();
-    if (ll_state == BLE_LL_STATE_CONNECTION) {
-        ticks = (int32_t)os_cputime_usecs_to_ticks(BLE_LL_SCHED_MAX_TXRX_SLOT);
-        ce_end_time = ble_ll_conn_get_ce_end_time();
-        if ((int32_t)(ce_end_time - sch->start_time) < ticks) {
-            ce_end_time += ticks;
-        }
-        sch->start_time = ce_end_time;
-        sch->end_time = ce_end_time + duration;
-    }
-#if MYNEWT_VAL(BLE_MULTI_ADV_SUPPORT)
-    else if ((ll_state == BLE_LL_STATE_ADV) && (BLE_LL_ADV_INSTANCES > 1)) {
-        /*
-         * Since we currently dont know how long this item might be scheduled
-         * for we add what we think the worst-case time for the advertising
-         * scheduled item to be over. We add in a IFS for good measure.
-         */
-        sch->start_time += BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS +
-            BLE_LL_SCHED_ADV_MAX_USECS + XCVR_TX_SCHED_DELAY_USECS;
-        sch->end_time = sch->start_time + duration;
-    }
-#endif
-
     entry = ble_ll_sched_insert_if_empty(sch);
     if (!entry) {
         rc = 0;
         adv_start = sch->start_time;
     } else {
+        /* XXX: no need to stop timer if not first on list. Modify code? */
         os_cputime_timer_stop(&g_ble_ll_sched_timer);
         TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
             /* We can insert if before entry in list */
@@ -529,13 +637,13 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch)
 
     ble_ll_adv_scheduled((struct ble_ll_adv_sm *)orig->cb_arg, adv_start);
 
-    OS_EXIT_CRITICAL(sr);
+#ifdef BLE_XCVR_RFCLK
+    if (orig == sch) {
+        ble_ll_xcvr_rfclk_timer_start(sch->start_time);
+    }
+#endif
 
-    /* XXX: some things to test. I am not sure that if we are passed the
-       output compare that we actually get the interrupt. */
-    /* XXX: I am not sure that if we receive a packet while scanning
-     * that we actually go back to scanning. I need to make sure
-       we re-enable the receive. Put an event in the log! */
+    OS_EXIT_CRITICAL(sr);
 
     os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
 
@@ -653,6 +761,12 @@ ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start,
         }
         sch->end_time = sch->start_time + duration;
         *start = sch->start_time;
+
+#ifdef BLE_XCVR_RFCLK
+        if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) {
+            ble_ll_xcvr_rfclk_timer_start(sch->start_time);
+        }
+#endif
     }
 
     OS_EXIT_CRITICAL(sr);
@@ -784,26 +898,33 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch)
 void
 ble_ll_sched_run(void *arg)
 {
-    int32_t dt;
     struct ble_ll_sched_item *sch;
 
     /* Look through schedule queue */
-    while ((sch = TAILQ_FIRST(&g_ble_ll_sched_q)) != NULL) {
+    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (sch) {
+#if (BLE_LL_SCHED_DEBUG == 1)
+        int32_t dt;
+
         /* Make sure we have passed the start time of the first event */
         dt = (int32_t)(os_cputime_get32() - sch->start_time);
-        if (dt >= 0) {
-#if (BLE_LL_SCHED_DEBUG == 1)
-            if (dt > g_ble_ll_sched_max_late) {
-                g_ble_ll_sched_max_late = dt;
-            }
+        if (dt > g_ble_ll_sched_max_late) {
+            g_ble_ll_sched_max_late = dt;
+        }
+        if (dt < g_ble_ll_sched_max_early) {
+            g_ble_ll_sched_max_early = dt;
+        }
 #endif
-            /* Remove schedule item and execute the callback */
-            TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
-            sch->enqueued = 0;
-            ble_ll_sched_execute_item(sch);
-        } else {
+
+        /* Remove schedule item and execute the callback */
+        TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
+        sch->enqueued = 0;
+        ble_ll_sched_execute_item(sch);
+
+        /* Restart if there is an item on the schedule */
+        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+        if (sch) {
             os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-            break;
         }
     }
 }
@@ -837,6 +958,55 @@ ble_ll_sched_next_time(uint32_t *next_event_time)
     return rc;
 }
 
+#ifdef BLE_XCVR_RFCLK
+/**
+ * Checks to see if we need to restart the cputime timer which starts the
+ * rf clock settling.
+ *
+ * NOTE: Should only be called from the Link Layer task!
+ *
+ * Context: Link-Layer task.
+ *
+ */
+void
+ble_ll_sched_rfclk_chk_restart(void)
+{
+    int stop;
+    os_sr_t sr;
+    uint8_t ll_state;
+    int32_t time_till_next;
+    uint32_t next_time;
+
+    stop = 0;
+    OS_ENTER_CRITICAL(sr);
+    ll_state = ble_ll_state_get();
+    if (ble_ll_sched_next_time(&next_time)) {
+        /*
+         * If the time until the next event is too close, no need to start
+         * the timer. Leave clock on.
+         */
+        time_till_next = (int32_t)(next_time - os_cputime_get32());
+        if (time_till_next > g_ble_ll_data.ll_xtal_ticks) {
+            /* Stop the clock */
+            stop = 1;
+            ble_ll_xcvr_rfclk_timer_start(next_time);
+        }
+    } else {
+        stop = 1;
+    }
+
+    /* Only disable the rfclk if doing nothing */
+    if (stop && (ll_state == BLE_LL_STATE_STANDBY)) {
+        ble_ll_log(BLE_LL_LOG_ID_RFCLK_SCHED_DIS, g_ble_ll_data.ll_rfclk_state,
+                   0, 0);
+        ble_ll_xcvr_rfclk_disable();
+    }
+    OS_EXIT_CRITICAL(sr);
+}
+
+#endif
+
+
 /**
  * Stop the scheduler
  *
@@ -857,6 +1027,24 @@ ble_ll_sched_stop(void)
 int
 ble_ll_sched_init(void)
 {
+    /*
+     * Initialize max early to large negative number. This is used
+     * to determine the worst-case "early" time the schedule was called. Dont
+     * expect this to be less than -3 or -4.
+     */
+#if (BLE_LL_SCHED_DEBUG == 1)
+    g_ble_ll_sched_max_early = -50000;
+#endif
+
+    /*
+     * This is the offset from the start of the scheduled item until the actual
+     * tx/rx should occur, in ticks. We also "round up" to the nearest tick.
+     */
+#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768
+    g_ble_ll_sched_offset_ticks =
+        os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS + 30);
+#endif
+
     /* Initialize cputimer for the scheduler */
     os_cputime_timer_init(&g_ble_ll_sched_timer, ble_ll_sched_run, NULL);
     return 0;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/507f493a/net/nimble/controller/src/ble_ll_xcvr.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll_xcvr.c b/net/nimble/controller/src/ble_ll_xcvr.c
new file mode 100644
index 0000000..50ab32e
--- /dev/null
+++ b/net/nimble/controller/src/ble_ll_xcvr.c
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdint.h>
+#include <assert.h>
+#include "syscfg/syscfg.h"
+#include "os/os_cputime.h"
+#include "controller/ble_phy.h"
+#include "controller/ble_ll.h"
+#include "controller/ble_ll_xcvr.h"
+
+#ifdef BLE_XCVR_RFCLK
+int
+ble_ll_xcvr_rfclk_state(void)
+{
+    uint32_t expiry;
+
+    if (g_ble_ll_data.ll_rfclk_state == BLE_RFCLK_STATE_ON) {
+        expiry = g_ble_ll_data.ll_rfclk_start_time;
+        if ((int32_t)(os_cputime_get32() - expiry) >
+                g_ble_ll_data.ll_xtal_ticks) {
+            g_ble_ll_data.ll_rfclk_state = BLE_RFCLK_STATE_SETTLED;
+        }
+    }
+    return g_ble_ll_data.ll_rfclk_state;
+}
+
+void
+ble_ll_xcvr_rfclk_enable(void)
+{
+    g_ble_ll_data.ll_rfclk_state = BLE_RFCLK_STATE_ON;
+    ble_phy_rfclk_enable();
+}
+
+void
+ble_ll_xcvr_rfclk_disable(void)
+{
+    ble_phy_rfclk_disable();
+    g_ble_ll_data.ll_rfclk_state = BLE_RFCLK_STATE_OFF;
+}
+
+void
+ble_ll_xcvr_rfclk_stop(void)
+{
+    ble_ll_log(BLE_LL_LOG_ID_RFCLK_STOP, g_ble_ll_data.ll_rfclk_state, 0,0);
+    os_cputime_timer_stop(&g_ble_ll_data.ll_rfclk_timer);
+    ble_ll_xcvr_rfclk_disable();
+}
+
+uint32_t
+ble_ll_xcvr_rfclk_time_till_settled(void)
+{
+    int32_t dt;
+    uint32_t rc;
+
+    rc = 0;
+    if (g_ble_ll_data.ll_rfclk_state == BLE_RFCLK_STATE_ON) {
+        dt = (int32_t)(os_cputime_get32() - g_ble_ll_data.ll_rfclk_start_time);
+        assert(dt >= 0);
+        if (dt < g_ble_ll_data.ll_xtal_ticks) {
+            rc = g_ble_ll_data.ll_xtal_ticks - (uint32_t)dt;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Called when the timer to turn on the RF CLOCK expires. This function checks
+ * the state of the clock. If the clock is off, the clock is turned on.
+ * Otherwise, we just exit.
+ *
+ * Context: Interrupt
+ *
+ * @param arg
+ */
+void
+ble_ll_xcvr_rfclk_timer_exp(void *arg)
+{
+    if (g_ble_ll_data.ll_rfclk_state == BLE_RFCLK_STATE_OFF) {
+        ble_ll_xcvr_rfclk_start_now(os_cputime_get32());
+    }
+}
+
+/**
+ * This API is used to turn on the rfclock without setting the cputime timer to
+ * start the clock at some later point.
+ *
+ * NOTE: presumes that the state of the rf clock was checked prior to calling.
+ *
+ * @param now
+ */
+void
+ble_ll_xcvr_rfclk_start_now(uint32_t now)
+{
+    ble_ll_xcvr_rfclk_enable();
+    g_ble_ll_data.ll_rfclk_start_time = now;
+    ble_ll_log(BLE_LL_LOG_ID_RFCLK_ENABLE, 0, 0, now);
+}
+
+/**
+ * Starts the timer that will turn the rf clock on. The 'cputime' is
+ * the time at which the clock needs to be settled.
+ *
+ * @param cputime   Time at which rfclock should be on and settled.
+ */
+void
+ble_ll_xcvr_rfclk_timer_start(uint32_t cputime)
+{
+    /*
+     * If we are currently in an advertising event or a connection event,
+     * no need to start the cputime timer
+     */
+    if ((g_ble_ll_data.ll_state == BLE_LL_STATE_ADV) ||
+        (g_ble_ll_data.ll_state == BLE_LL_STATE_CONNECTION)) {
+        return;
+    }
+
+    /* Account for the settling time */
+    cputime -= g_ble_ll_data.ll_xtal_ticks;
+
+    /*
+     * If the timer is on the list, we need to see if its expiry is before
+     * 'cputime'. If the expiry is before, no need to do anything. If it
+     * is after, we need to stop the timer and start at new time.
+     */
+    if (g_ble_ll_data.ll_rfclk_timer.link.tqe_prev != NULL) {
+        if ((int32_t)(cputime - g_ble_ll_data.ll_rfclk_timer.expiry) >= 0) {
+            return;
+        }
+        os_cputime_timer_stop(&g_ble_ll_data.ll_rfclk_timer);
+    }
+    os_cputime_timer_start(&g_ble_ll_data.ll_rfclk_timer, cputime);
+    ble_ll_log(BLE_LL_LOG_ID_RFCLK_START, g_ble_ll_data.ll_rfclk_state, 0,
+               g_ble_ll_data.ll_rfclk_timer.expiry);
+}
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/507f493a/net/nimble/controller/syscfg.yml
----------------------------------------------------------------------
diff --git a/net/nimble/controller/syscfg.yml b/net/nimble/controller/syscfg.yml
index 357decc..569010f 100644
--- a/net/nimble/controller/syscfg.yml
+++ b/net/nimble/controller/syscfg.yml
@@ -137,6 +137,14 @@ syscfg.defs:
             material often.
         value: '32'
 
+    # Crystal setting time
+    BLE_XTAL_SETTLE_TIME:
+        description: >
+            The settling time of the high-frequency oscillator. This is
+            used to turn on/off the clock used for the radio (assuming
+            the HW supports this). This value is in microseconds.
+        value: '0'
+
     # Configuration for LL supported features.
     #
     # There are a total 8 features that the LL can support. These can be found

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/507f493a/net/nimble/host/src/ble_hs_hci_evt.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_hci_evt.c b/net/nimble/host/src/ble_hs_hci_evt.c
index cd98f7f..a9655e3 100644
--- a/net/nimble/host/src/ble_hs_hci_evt.c
+++ b/net/nimble/host/src/ble_hs_hci_evt.c
@@ -6,7 +6,7 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
@@ -624,8 +624,8 @@ ble_hs_hci_evt_acl_process(struct os_mbuf *om)
 #if (BLETEST_THROUGHPUT_TEST == 0)
     BLE_HS_LOG(DEBUG, "ble_hs_hci_evt_acl_process(): conn_handle=%u pb=%x "
                       "len=%u data=",
-               BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc), 
-               BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc), 
+               BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc),
+               BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc),
                hci_hdr.hdh_len);
     ble_hs_log_mbuf(om);
     BLE_HS_LOG(DEBUG, "\n");

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/507f493a/net/nimble/include/nimble/ble.h
----------------------------------------------------------------------
diff --git a/net/nimble/include/nimble/ble.h b/net/nimble/include/nimble/ble.h
index b514131..6f29fd4 100644
--- a/net/nimble/include/nimble/ble.h
+++ b/net/nimble/include/nimble/ble.h
@@ -96,6 +96,9 @@ struct ble_mbuf_hdr
         struct ble_mbuf_hdr_txinfo txinfo;
     };
     uint32_t beg_cputime;
+#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768)
+    uint32_t rem_usecs;
+#endif
 };
 
 #define BLE_MBUF_HDR_CRC_OK(hdr)        \