/
flash_sync_mpsl.c
214 lines (177 loc) · 6.04 KB
/
flash_sync_mpsl.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
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include <errno.h>
#include <mpsl.h>
#include <mpsl_timeslot.h>
#include <hal/nrf_timer.h>
#include "multithreading_lock.h"
#include "soc_flash_nrf.h"
#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_sync_mpsl);
/* The request length specified by the upper layers is only time required to do
* the flash operations itself. Therefore we need to add some additional slack
* to each timeslot request.
*/
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
#define TIMESLOT_LENGTH_SLACK_US 1000
#else
#define TIMESLOT_LENGTH_SLACK_US 100
#endif
/* After this many us's, start using higher priority when requesting. */
#define TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US 30000
struct mpsl_context {
/* This semaphore is taken with a timeout when the flash operation starts. */
struct k_sem timeout_sem;
mpsl_timeslot_session_id_t session_id; /* Timeslot session ID. */
/* The flash operation may be split into multiple requests.
* This represents the length of such a request. */
uint32_t request_length_us;
/* Argument passed to nrf_flash_sync_exe(). */
struct flash_op_desc *op_desc;
mpsl_timeslot_request_t timeslot_request;
/* Return parameter for the timeslot session. */
mpsl_timeslot_signal_return_param_t return_param;
int status; /* Return value for nrf_flash_sync_exe(). */
/* Indicate timeout condition to the timeslot callback. */
atomic_t timeout_occured;
};
static struct mpsl_context _context;
/**
* Get time in microseconds since the beginning of the timeslot.
*
* This should only be called inside the timeslot.
*/
static uint32_t get_timeslot_time_us(void)
{
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_CAPTURE0);
return nrf_timer_cc_get(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0);
}
static void reschedule_next_timeslot(void)
{
_context.timeslot_request.params.earliest.priority =
MPSL_TIMESLOT_PRIORITY_HIGH;
_context.timeslot_request.params.earliest.timeout_us =
MPSL_TIMESLOT_EARLIEST_TIMEOUT_MAX_US;
int32_t ret = mpsl_timeslot_request(_context.session_id,
&_context.timeslot_request);
__ASSERT_EVAL((void)ret, (void)ret, ret == 0,
"mpsl_timeslot_request failed: %d", ret);
}
static mpsl_timeslot_signal_return_param_t *
timeslot_callback(mpsl_timeslot_session_id_t session_id, uint32_t signal)
{
int rc;
__ASSERT_NO_MSG(session_id == _context.session_id);
if (atomic_get(&_context.timeout_occured)) {
return NULL;
}
switch (signal) {
case MPSL_TIMESLOT_SIGNAL_START:
rc = _context.op_desc->handler(_context.op_desc->context);
if (rc != FLASH_OP_ONGOING) {
_context.status = (rc == FLASH_OP_DONE) ? 0 : rc;
_context.return_param.callback_action =
MPSL_TIMESLOT_SIGNAL_ACTION_END;
} else {
/* Reset the priority back to normal after a successful
* timeslot. */
_context.timeslot_request.params.earliest.priority =
MPSL_TIMESLOT_PRIORITY_NORMAL;
_context.timeslot_request.params.earliest.timeout_us =
TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US;
_context.return_param.callback_action =
MPSL_TIMESLOT_SIGNAL_ACTION_REQUEST;
_context.return_param.params.request.p_next =
&_context.timeslot_request;
}
break;
case MPSL_TIMESLOT_SIGNAL_SESSION_IDLE:
/* All requests are done, that means we are done. */
k_sem_give(&_context.timeout_sem);
return NULL;
case MPSL_TIMESLOT_SIGNAL_SESSION_CLOSED:
return NULL;
case MPSL_TIMESLOT_SIGNAL_CANCELLED:
case MPSL_TIMESLOT_SIGNAL_BLOCKED:
/* Retry the failed request. */
reschedule_next_timeslot();
return NULL;
default:
__ASSERT(false, "unexpected signal: %u", signal);
return NULL;
}
return &_context.return_param;
}
int nrf_flash_sync_init(void)
{
LOG_DBG("");
return k_sem_init(&_context.timeout_sem, 0, 1);
}
void nrf_flash_sync_set_context(uint32_t duration)
{
LOG_DBG("duration: %u", duration);
_context.request_length_us = duration;
}
bool nrf_flash_sync_is_required(void)
{
return mpsl_is_initialized();
}
int nrf_flash_sync_exe(struct flash_op_desc *op_desc)
{
LOG_DBG("");
int errcode = MULTITHREADING_LOCK_ACQUIRE();
__ASSERT_NO_MSG(errcode == 0);
int32_t ret = mpsl_timeslot_session_open(timeslot_callback,
&_context.session_id);
MULTITHREADING_LOCK_RELEASE();
if (ret < 0) {
LOG_ERR("mpsl_timeslot_session_open failed: %d", ret);
return -ENOMEM;
}
mpsl_timeslot_request_t *req = &_context.timeslot_request;
req->request_type = MPSL_TIMESLOT_REQ_TYPE_EARLIEST;
req->params.earliest.hfclk = MPSL_TIMESLOT_HFCLK_CFG_NO_GUARANTEE;
req->params.earliest.priority = MPSL_TIMESLOT_PRIORITY_NORMAL;
req->params.earliest.length_us =
_context.request_length_us + TIMESLOT_LENGTH_SLACK_US;
req->params.earliest.timeout_us = TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US;
_context.op_desc = op_desc;
_context.status = -ETIMEDOUT;
atomic_clear(&_context.timeout_occured);
__ASSERT_NO_MSG(k_sem_count_get(&_context.timeout_sem) == 0);
errcode = MULTITHREADING_LOCK_ACQUIRE();
__ASSERT_NO_MSG(errcode == 0);
ret = mpsl_timeslot_request(_context.session_id, req);
__ASSERT_EVAL((void)ret, (void)ret, ret == 0,
"mpsl_timeslot_request failed: %d", ret);
MULTITHREADING_LOCK_RELEASE();
if (k_sem_take(&_context.timeout_sem, K_MSEC(FLASH_TIMEOUT_MS)) < 0) {
LOG_ERR("timeout");
atomic_set(&_context.timeout_occured, 1);
}
/* This will cancel the timeslot if it is still in progress. */
errcode = MULTITHREADING_LOCK_ACQUIRE();
__ASSERT_NO_MSG(errcode == 0);
mpsl_timeslot_session_close(_context.session_id);
MULTITHREADING_LOCK_RELEASE();
/* Reset the semaphore after timeout, in case if the operation _did_
* complete before closing the session. */
if (atomic_get(&_context.timeout_occured)) {
k_sem_reset(&_context.timeout_sem);
}
return _context.status;
}
void nrf_flash_sync_get_timestamp_begin(void)
{
/* Not needed for this driver. */
}
bool nrf_flash_sync_check_time_limit(uint32_t iteration)
{
uint32_t now_us = get_timeslot_time_us();
uint32_t time_per_iteration_us = now_us / iteration;
return now_us + time_per_iteration_us >= _context.request_length_us;
}