Skip to content

Commit e09b256

Browse files
PavelVPVnordicjm
authored andcommitted
emds: Write emds data without using the flash driver
This commit allows EMDS to bypass the flash driver when writing emds data to flash to ensure deterministic write time without being blocked by mpsl. It also allows to call emds_store from ISR, which was not possible with the flash driver. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
1 parent def1e84 commit e09b256

File tree

4 files changed

+182
-68
lines changed

4 files changed

+182
-68
lines changed

doc/nrf/libraries/others/emds.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ When changing the :kconfig:option:`CONFIG_EMDS_FLASH_TIME_BASE_OVERHEAD_US` opti
4848

4949
The application must call the :c:func:`emds_store` function to store all entries.
5050
This can only be done once, before the :c:func:`emds_prepare` function must be called again.
51-
When invoked, the :c:func:`emds_store` function triggers the emergency data store process in a separate thread, and then stores all the registered entries.
51+
When invoked, the :c:func:`emds_store` function stores all the registered entries.
5252
Invocation of this call should be performed when the application detects loss of power, or when a reboot is triggered.
5353

5454
.. note::
@@ -82,7 +82,6 @@ The above described process is summarized in a message sequence diagram.
8282
...;
8383
Application->Application [ label = "Interrupt calling emds_store()" ];
8484
Application=>EMDS [ label = "emds_store()" ];
85-
EMDS box EMDS [ label = "Thread storing data executing" ];
8685
Application<<=EMDS [ label = "emds_store_cb_t callback" ];
8786
Application->Application [ label = "Reboot/halt" ];
8887

include/emds/emds.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ struct emds_dynamic_entry {
7171
* @brief Callback for application commands when storing has been executed.
7272
*
7373
* This can be used to perform other actions or execute busy-waiting until the
74-
* power supply is discharged. It will run in the thread of emds. Scheduler and
75-
* interrupts are unlocked before triggering this callback. Therefore, this
74+
* power supply is discharged. It will be run by @ref emds_store,
75+
* and called from the same context the @ref emds_store is called from.
76+
* Interrupts are unlocked before triggering this callback. Therefore, this
7677
* callback may not be reached before available backup power runs out.
78+
* If the application needs interrupts to be locked during this callback,
79+
* application can do so by calling irq_lock before calling @ref emds_store.
7780
*/
7881
typedef void (*emds_store_cb_t)(void);
7982

@@ -112,9 +115,20 @@ int emds_entry_add(struct emds_dynamic_entry *entry);
112115
*
113116
* Triggers the process of storing all data registered to be stored. All data
114117
* registered either through @ref emds_entry_add function or the
115-
* @ref EMDS_STATIC_ENTRY_DEFINE macro is stored. Once the data storage is
116-
* completed, the data should not be changed, and the device should be halted.
118+
* @ref EMDS_STATIC_ENTRY_DEFINE macro is stored. It locks all interrupts until
119+
* the write is finished. Once the data storage is completed, the data should
120+
* not be changed, and the device should be halted. The device must not be
121+
* allowed to reboot when operating on a backup supply, since reboot will
122+
* trigger data load from the EMDS storage and clear the storage area. The reboot
123+
* should only be allowed when the main power supply is available.
124+
*
117125
* This is a time-critical operation and will be processed as fast as possible.
126+
* To achieve better time predictability, this function must be called from an
127+
* interrupt context with the highest priority.
128+
*
129+
* When writing to flash, it bypasses the flash driver. Therefore, when running
130+
* with MPSL, make sure to uninitialize the MPSL before this function is called.
131+
* Otherwise, an assertion may be triggered by the exit of the function.
118132
*
119133
* @retval 0 Success
120134
* @retval -ERRNO errno code if error

subsys/emds/emds.c

+39-56
Original file line numberDiff line numberDiff line change
@@ -14,68 +14,13 @@
1414
#include <zephyr/logging/log.h>
1515
LOG_MODULE_REGISTER(emds, CONFIG_EMDS_LOG_LEVEL);
1616

17-
K_SEM_DEFINE(emds_sem, 0, 1);
1817
static bool emds_ready;
1918
static bool emds_initialized;
20-
static uint32_t store_key;
2119

2220
static sys_slist_t emds_dynamic_entries;
2321
static struct emds_fs emds_flash;
2422
static emds_store_cb_t app_store_cb;
2523

26-
static void emds_handler(void)
27-
{
28-
while (true) {
29-
k_sem_reset(&emds_sem);
30-
k_sem_take(&emds_sem, K_FOREVER);
31-
32-
k_sched_lock();
33-
34-
LOG_DBG("Emergency Data Storeage released");
35-
36-
STRUCT_SECTION_FOREACH(emds_entry, ch) {
37-
ssize_t len = emds_flash_write(&emds_flash,
38-
ch->id, ch->data, ch->len);
39-
if (len < 0) {
40-
LOG_ERR("Write static entry: (%d) error (%d)",
41-
ch->id, len);
42-
} else if (len != ch->len) {
43-
LOG_ERR("Write static entry: (%d) failed (%d:%d)",
44-
ch->id, ch->len, len);
45-
}
46-
}
47-
48-
struct emds_dynamic_entry *ch;
49-
50-
SYS_SLIST_FOR_EACH_CONTAINER(&emds_dynamic_entries, ch, node) {
51-
ssize_t len = emds_flash_write(&emds_flash,
52-
ch->entry.id, ch->entry.data, ch->entry.len);
53-
if (len < 0) {
54-
LOG_ERR("Write dynamic entry: (%d) error (%d).",
55-
ch->entry.id, len);
56-
}
57-
if (len != ch->entry.len) {
58-
LOG_ERR("Write dynamic entry: (%d) failed (%d:%d).",
59-
ch->entry.id, ch->entry.len, len);
60-
}
61-
}
62-
63-
emds_ready = false;
64-
65-
k_sched_unlock();
66-
67-
/* Unlock all interrupts */
68-
irq_unlock(store_key);
69-
70-
if (app_store_cb) {
71-
app_store_cb();
72-
}
73-
}
74-
}
75-
76-
K_THREAD_DEFINE(emds_tid, CONFIG_EMDS_THREAD_STACK_SIZE, emds_handler, NULL,
77-
NULL, NULL, K_PRIO_COOP(CONFIG_EMDS_THREAD_PRIORITY), 0, 0);
78-
7924
static int emds_fs_init(void)
8025
{
8126
int rc;
@@ -194,6 +139,8 @@ int emds_entry_add(struct emds_dynamic_entry *entry)
194139

195140
int emds_store(void)
196141
{
142+
uint32_t store_key;
143+
197144
if (!emds_ready) {
198145
return -ECANCELED;
199146
}
@@ -202,7 +149,43 @@ int emds_store(void)
202149
store_key = irq_lock();
203150

204151
/* Start the emergency data storage process. */
205-
k_sem_give(&emds_sem);
152+
LOG_DBG("Emergency Data Storeage released");
153+
154+
STRUCT_SECTION_FOREACH(emds_entry, ch) {
155+
ssize_t len = emds_flash_write(&emds_flash,
156+
ch->id, ch->data, ch->len);
157+
if (len < 0) {
158+
LOG_ERR("Write static entry: (%d) error (%d)",
159+
ch->id, len);
160+
} else if (len != ch->len) {
161+
LOG_ERR("Write static entry: (%d) failed (%d:%d)",
162+
ch->id, ch->len, len);
163+
}
164+
}
165+
166+
struct emds_dynamic_entry *ch;
167+
168+
SYS_SLIST_FOR_EACH_CONTAINER(&emds_dynamic_entries, ch, node) {
169+
ssize_t len = emds_flash_write(&emds_flash,
170+
ch->entry.id, ch->entry.data, ch->entry.len);
171+
if (len < 0) {
172+
LOG_ERR("Write dynamic entry: (%d) error (%d).",
173+
ch->entry.id, len);
174+
}
175+
if (len != ch->entry.len) {
176+
LOG_ERR("Write dynamic entry: (%d) failed (%d:%d).",
177+
ch->entry.id, ch->entry.len, len);
178+
}
179+
}
180+
181+
emds_ready = false;
182+
183+
/* Unlock all interrupts */
184+
irq_unlock(store_key);
185+
186+
if (app_store_cb) {
187+
app_store_cb();
188+
}
206189

207190
return 0;
208191
}

subsys/emds/emds_flash.c

+124-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <zephyr/sys/crc.h>
1212
#include <zephyr/logging/log.h>
1313
#include "emds_flash.h"
14+
#include <nrfx_nvmc.h>
15+
#include <nrf_erratas.h>
1416

1517
LOG_MODULE_REGISTER(emds_flash, CONFIG_EMDS_LOG_LEVEL);
1618

@@ -36,6 +38,124 @@ enum ate_type {
3638
BUILD_ASSERT(offsetof(struct emds_ate, crc8) == sizeof(struct emds_ate) - sizeof(uint8_t),
3739
"crc8 must be the last member");
3840

41+
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
42+
43+
#if NRF52_ERRATA_242_PRESENT
44+
#include <hal/nrf_power.h>
45+
/* Disable POFWARN by writing POFCON before a write or erase operation.
46+
* Do not attempt to write or erase if EVENTS_POFWARN is already asserted.
47+
*/
48+
static bool pofcon_enabled;
49+
50+
static int suspend_pofwarn(void)
51+
{
52+
if (!nrf52_errata_242()) {
53+
return 0;
54+
}
55+
56+
bool enabled;
57+
nrf_power_pof_thr_t pof_thr;
58+
59+
pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled);
60+
61+
if (enabled) {
62+
nrf_power_pofcon_set(NRF_POWER, false, pof_thr);
63+
64+
/* This check need to be reworked once POFWARN event will be
65+
* served by zephyr.
66+
*/
67+
if (nrf_power_event_check(NRF_POWER, NRF_POWER_EVENT_POFWARN)) {
68+
nrf_power_pofcon_set(NRF_POWER, true, pof_thr);
69+
return -ECANCELED;
70+
}
71+
}
72+
73+
return 0;
74+
}
75+
76+
static void restore_pofwarn(void)
77+
{
78+
nrf_power_pof_thr_t pof_thr;
79+
80+
if (pofcon_enabled) {
81+
pof_thr = nrf_power_pofcon_get(NRF_POWER, NULL);
82+
83+
nrf_power_pofcon_set(NRF_POWER, true, pof_thr);
84+
pofcon_enabled = false;
85+
}
86+
}
87+
88+
#define SUSPEND_POFWARN() suspend_pofwarn()
89+
#define RESUME_POFWARN() restore_pofwarn()
90+
#else
91+
#define SUSPEND_POFWARN() 0
92+
#define RESUME_POFWARN()
93+
#endif /* NRF52_ERRATA_242_PRESENT */
94+
95+
static inline bool is_aligned_32(uint32_t data)
96+
{
97+
return (data & 0x3) ? false : true;
98+
}
99+
100+
static inline bool is_within_bounds(off_t addr, size_t len, off_t boundary_start,
101+
size_t boundary_size)
102+
{
103+
return (addr >= boundary_start &&
104+
(addr < (boundary_start + boundary_size)) &&
105+
(len <= (boundary_start + boundary_size - addr)));
106+
}
107+
108+
static inline bool is_regular_addr_valid(off_t addr, size_t len)
109+
{
110+
return is_within_bounds(addr, len, 0, nrfx_nvmc_flash_size_get());
111+
}
112+
113+
static void nvmc_wait_ready(void)
114+
{
115+
while (!nrfx_nvmc_write_done_check()) {
116+
}
117+
}
118+
119+
static int flash_direct_write(const struct device *dev, off_t offset, const void *data, size_t len)
120+
{
121+
uint32_t flash_addr = offset;
122+
uint32_t data_addr = (uint32_t)data;
123+
124+
ARG_UNUSED(dev);
125+
126+
if (!is_regular_addr_valid(flash_addr, len)) {
127+
return -EINVAL;
128+
}
129+
130+
if (!is_aligned_32(flash_addr)) {
131+
return -EINVAL;
132+
}
133+
134+
if (len % sizeof(uint32_t)) {
135+
return -EINVAL;
136+
}
137+
138+
flash_addr += DT_REG_ADDR(SOC_NV_FLASH_NODE);
139+
140+
if (SUSPEND_POFWARN()) {
141+
return -ECANCELED;
142+
}
143+
144+
while (len >= sizeof(uint32_t)) {
145+
nrfx_nvmc_word_write(flash_addr, UNALIGNED_GET((uint32_t *)data_addr));
146+
147+
flash_addr += sizeof(uint32_t);
148+
data_addr += sizeof(uint32_t);
149+
len -= sizeof(uint32_t);
150+
}
151+
152+
RESUME_POFWARN();
153+
154+
nvmc_wait_ready();
155+
156+
return 0;
157+
}
158+
39159
static size_t align_size(struct emds_fs *fs, size_t len)
40160
{
41161
uint8_t write_block_size = fs->flash_params->write_block_size;
@@ -52,7 +172,7 @@ static int ate_wrt(struct emds_fs *fs, const struct emds_ate *entry)
52172
return -EINVAL;
53173
}
54174

55-
int rc = flash_write(fs->flash_dev, fs->ate_wra, entry, sizeof(struct emds_ate));
175+
int rc = flash_direct_write(fs->flash_dev, fs->ate_wra, entry, sizeof(struct emds_ate));
56176

57177
if (rc) {
58178
return rc;
@@ -82,7 +202,7 @@ static int data_wrt(struct emds_fs *fs, const void *data, size_t len)
82202
blen = temp_len & ~(fs->flash_params->write_block_size - 1U);
83203
/* Writes multiples of 4 bytes to flash */
84204
if (blen > 0) {
85-
rc = flash_write(fs->flash_dev, offset, data8, blen);
205+
rc = flash_direct_write(fs->flash_dev, offset, data8, blen);
86206
if (rc) {
87207
return rc;
88208
}
@@ -96,7 +216,8 @@ static int data_wrt(struct emds_fs *fs, const void *data, size_t len)
96216
(void)memcpy(buf, data8, temp_len);
97217
(void)memset(buf + temp_len, fs->flash_params->erase_value,
98218
fs->flash_params->write_block_size - temp_len);
99-
rc = flash_write(fs->flash_dev, offset, buf, fs->flash_params->write_block_size);
219+
rc = flash_direct_write(fs->flash_dev, offset, buf,
220+
fs->flash_params->write_block_size);
100221
if (rc) {
101222
return rc;
102223
}
@@ -344,15 +465,12 @@ ssize_t emds_flash_write(struct emds_fs *fs, uint16_t id, const void *data, size
344465
return 0;
345466
}
346467

347-
k_mutex_lock(&fs->emds_lock, K_FOREVER);
348-
349468
int rc = entry_wrt(fs, id, data, len);
350469

351470
if (rc) {
352471
return rc;
353472
}
354473

355-
k_mutex_unlock(&fs->emds_lock);
356474
return len;
357475
}
358476

0 commit comments

Comments
 (0)