1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8
9 #include <soc.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/init.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/bluetooth/hci_types.h>
15
16 #include "hal/ticker.h"
17 #include "ticker/ticker.h"
18 #include "ll.h"
19
20 #include "soc_flash_nrf.h"
21
22 #define FLASH_RADIO_ABORT_DELAY_US 1500
23 #define FLASH_RADIO_WORK_DELAY_US 200
24
25 /* delay needed for start execution-window */
26 #define FLASH_SYNC_SWITCHING_TIME (FLASH_RADIO_ABORT_DELAY_US +\
27 FLASH_RADIO_WORK_DELAY_US)
28
29 struct ticker_sync_context {
30 uint32_t interval; /* timeslot interval. */
31 uint32_t slot; /* timeslot length. */
32 uint32_t ticks_begin; /* timeslot begin timestamp */
33 int result;
34 };
35
36 static struct ticker_sync_context _ticker_sync_context;
37
38 /* semaphore for synchronization of flash operations */
39 static struct k_sem sem_sync;
40
ticker_stop_work_cb(uint32_t status,void * param)41 static void ticker_stop_work_cb(uint32_t status, void *param)
42 {
43 __ASSERT((status == TICKER_STATUS_SUCCESS ||
44 status == TICKER_STATUS_FAILURE),
45 "Failed to stop work ticker, ticker job busy.\n");
46
47 /* notify thread that data is available */
48 k_sem_give(&sem_sync);
49 }
50
ticker_stop_prepare_cb(uint32_t status,void * param)51 static void ticker_stop_prepare_cb(uint32_t status, void *param)
52 {
53 uint8_t instance_index;
54 uint8_t ticker_id;
55 uint32_t ret;
56
57 __ASSERT(status == TICKER_STATUS_SUCCESS,
58 "Failed to stop prepare ticker.\n");
59
60 /* Get the ticker instance and ticker id for flash operations */
61 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
62
63 /* Stop the work ticker, from ULL_LOW context */
64 ret = ticker_stop(instance_index, 2U, (ticker_id + 1U),
65 ticker_stop_work_cb, NULL);
66 __ASSERT((ret == TICKER_STATUS_SUCCESS ||
67 ret == TICKER_STATUS_BUSY),
68 "Failed to request the work ticker to stop.\n");
69 }
70
time_slot_callback_work(uint32_t ticks_at_expire,uint32_t ticks_drift,uint32_t remainder,uint16_t lazy,uint8_t force,void * context)71 static void time_slot_callback_work(uint32_t ticks_at_expire,
72 uint32_t ticks_drift,
73 uint32_t remainder,
74 uint16_t lazy, uint8_t force,
75 void *context)
76 {
77 struct flash_op_desc *op_desc;
78 int rc;
79
80 __ASSERT(ll_radio_state_is_idle(),
81 "Radio is on during flash operation.\n");
82
83 op_desc = context;
84 rc = op_desc->handler(op_desc->context);
85 if (rc != FLASH_OP_ONGOING) {
86 uint8_t instance_index;
87 uint8_t ticker_id;
88 uint32_t ret;
89
90 /* Get the ticker instance and ticker id for flash operations */
91 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
92
93 /* Stop the prepare ticker, from ULL_HIGH context */
94 ret = ticker_stop(instance_index, 1U, ticker_id,
95 ticker_stop_prepare_cb, NULL);
96 __ASSERT((ret == TICKER_STATUS_SUCCESS ||
97 ret == TICKER_STATUS_BUSY),
98 "Failed to stop ticker.\n");
99
100 _ticker_sync_context.result = (rc == FLASH_OP_DONE) ? 0 : rc;
101 }
102 }
103
time_slot_delay(uint32_t ticks_at_expire,uint32_t ticks_delay,ticker_timeout_func callback,void * context)104 static void time_slot_delay(uint32_t ticks_at_expire, uint32_t ticks_delay,
105 ticker_timeout_func callback, void *context)
106 {
107 uint8_t instance_index;
108 uint8_t ticker_id;
109 uint32_t ret;
110
111 /* Get the ticker instance and ticker id for flash operations */
112 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
113
114 /* Start a secondary one-shot ticker after ticks_delay,
115 * this will let any radio role to gracefully abort and release the
116 * Radio h/w.
117 */
118 ret = ticker_start(instance_index, /* Radio instance ticker */
119 1, /* user id for link layer ULL_HIGH */
120 /* (MAYFLY_CALL_ID_WORKER) */
121 (ticker_id + 1), /* ticker_id */
122 ticks_at_expire, /* current tick */
123 ticks_delay, /* one-shot delayed timeout */
124 0, /* periodic timeout */
125 0, /* periodic remainder */
126 0, /* lazy, voluntary skips */
127 0,
128 callback, /* handler for executing radio abort or */
129 /* flash work */
130 context, /* the context for the flash operation */
131 NULL, /* no op callback */
132 NULL);
133
134 if (ret != TICKER_STATUS_SUCCESS && ret != TICKER_STATUS_BUSY) {
135 /* Failed to enqueue the ticker start operation request */
136 _ticker_sync_context.result = 0;
137
138 /* Abort flash prepare ticker, from ULL_HIGH context */
139 ret = ticker_stop(instance_index, 1U, ticker_id,
140 ticker_stop_prepare_cb, NULL);
141 __ASSERT((ret == TICKER_STATUS_SUCCESS ||
142 ret == TICKER_STATUS_BUSY),
143 "Failed to stop ticker.\n");
144 }
145 }
146
time_slot_callback_abort(uint32_t ticks_at_expire,uint32_t ticks_drift,uint32_t remainder,uint16_t lazy,uint8_t force,void * context)147 static void time_slot_callback_abort(uint32_t ticks_at_expire,
148 uint32_t ticks_drift,
149 uint32_t remainder,
150 uint16_t lazy, uint8_t force,
151 void *context)
152 {
153 ll_radio_state_abort();
154 time_slot_delay(ticks_at_expire,
155 HAL_TICKER_US_TO_TICKS(FLASH_RADIO_WORK_DELAY_US),
156 time_slot_callback_work,
157 context);
158 }
159
time_slot_callback_prepare(uint32_t ticks_at_expire,uint32_t ticks_drift,uint32_t remainder,uint16_t lazy,uint8_t force,void * context)160 static void time_slot_callback_prepare(uint32_t ticks_at_expire,
161 uint32_t ticks_drift,
162 uint32_t remainder,
163 uint16_t lazy, uint8_t force,
164 void *context)
165 {
166 #if defined(CONFIG_BT_CTLR_LOW_LAT)
167 time_slot_callback_abort(ticks_at_expire, ticks_drift, remainder, lazy,
168 force, context);
169 #else /* !CONFIG_BT_CTLR_LOW_LAT */
170 time_slot_delay(ticks_at_expire,
171 HAL_TICKER_US_TO_TICKS(FLASH_RADIO_ABORT_DELAY_US),
172 time_slot_callback_abort,
173 context);
174 #endif /* CONFIG_BT_CTLR_LOW_LAT */
175 }
176
177
nrf_flash_sync_init(void)178 int nrf_flash_sync_init(void)
179 {
180 k_sem_init(&sem_sync, 0, 1);
181 return 0;
182 }
183
nrf_flash_sync_set_context(uint32_t duration)184 void nrf_flash_sync_set_context(uint32_t duration)
185 {
186 /* FLASH_SYNC_SWITCHING_TIME is delay which is always added by
187 * the slot calling mechanism
188 */
189 _ticker_sync_context.interval = duration - FLASH_SYNC_SWITCHING_TIME;
190 _ticker_sync_context.slot = duration;
191 }
192
nrf_flash_sync_exe(struct flash_op_desc * op_desc)193 int nrf_flash_sync_exe(struct flash_op_desc *op_desc)
194 {
195 uint8_t instance_index;
196 uint8_t ticker_id;
197 uint32_t ret;
198 int result;
199
200 /* Get the ticker instance and ticker id for flash operations */
201 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
202
203 /* Start periodic flash operation prepare time slots */
204 ret = ticker_start(instance_index,
205 3, /* user id for thread mode */
206 /* (MAYFLY_CALL_ID_PROGRAM) */
207 ticker_id, /* flash ticker id */
208 ticker_ticks_now_get(), /* current tick */
209 0, /* first int. immediately */
210 /* period */
211 HAL_TICKER_US_TO_TICKS(
212 _ticker_sync_context.interval),
213 /* period remainder */
214 HAL_TICKER_REMAINDER(_ticker_sync_context.interval),
215 0, /* lazy, voluntary skips */
216 HAL_TICKER_US_TO_TICKS(_ticker_sync_context.slot),
217 time_slot_callback_prepare,
218 op_desc,
219 NULL, /* no op callback */
220 NULL);
221
222 if (ret != TICKER_STATUS_SUCCESS && ret != TICKER_STATUS_BUSY) {
223 /* Failed to enqueue the ticker start operation request */
224 result = -ECANCELED;
225 } else if (k_sem_take(&sem_sync, K_MSEC(FLASH_TIMEOUT_MS)) != 0) {
226 /* Stop any scheduled jobs, from thread context */
227 ret = ticker_stop(instance_index, 3U, ticker_id, NULL, NULL);
228 __ASSERT((ret == TICKER_STATUS_SUCCESS ||
229 ret == TICKER_STATUS_BUSY),
230 "Failed to stop ticker.\n");
231
232 /* wait for operation's complete overrun*/
233 result = -ETIMEDOUT;
234 } else {
235 result = _ticker_sync_context.result;
236 }
237
238 return result;
239 }
240
nrf_flash_sync_is_required(void)241 bool nrf_flash_sync_is_required(void)
242 {
243 return ticker_is_initialized(0);
244 }
245
nrf_flash_sync_get_timestamp_begin(void)246 void nrf_flash_sync_get_timestamp_begin(void)
247 {
248 _ticker_sync_context.ticks_begin = ticker_ticks_now_get();
249 }
250
nrf_flash_sync_check_time_limit(uint32_t iteration)251 bool nrf_flash_sync_check_time_limit(uint32_t iteration)
252 {
253 uint32_t ticks_diff;
254
255 ticks_diff = ticker_ticks_diff_get(ticker_ticks_now_get(),
256 _ticker_sync_context.ticks_begin);
257 if (ticks_diff + ticks_diff/iteration >
258 HAL_TICKER_US_TO_TICKS(_ticker_sync_context.slot)) {
259 return true;
260 }
261
262 return false;
263 }
264