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