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