blob: fe2f330d4bef3830aac1d63e470eb454d9dcf69d [file] [log] [blame]
Ricardo Salveti9d033292016-07-25 23:34:52 -03001/*
Vinayak Kariappa Chettimada4c123a52018-01-09 15:03:33 +01002 * Copyright (c) 2017-2018 Nordic Semiconductor ASA
Ricardo Salveti9d033292016-07-25 23:34:52 -03003 * Copyright (c) 2016 Linaro Limited
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +02004 * Copyright (c) 2016 Intel Corporation
Ricardo Salveti9d033292016-07-25 23:34:52 -03005 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08006 * SPDX-License-Identifier: Apache-2.0
Ricardo Salveti9d033292016-07-25 23:34:52 -03007 */
8
9#include <errno.h>
10
Flavio Santesb04cdcd2016-12-04 14:59:37 -060011#include <kernel.h>
Ricardo Salveti9d033292016-07-25 23:34:52 -030012#include <device.h>
13#include <init.h>
14#include <soc.h>
Anas Nashiffe051a92019-06-25 15:53:50 -040015#include <drivers/flash.h>
Ricardo Salveti9d033292016-07-25 23:34:52 -030016#include <string.h>
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +020017#include <nrfx_nvmc.h>
Ricardo Salveti9d033292016-07-25 23:34:52 -030018
Carles Cufi8aa9a372018-03-20 18:41:39 +010019#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Anas Nashif5eb90ec2019-06-26 10:33:39 -040020#include <sys/__assert.h>
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020021#include <bluetooth/hci.h>
Vinayak Kariappa Chettimada87fe4402018-12-17 10:46:13 +010022#include "controller/hal/ticker.h"
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020023#include "controller/ticker/ticker.h"
24#include "controller/include/ll.h"
25
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +020026#define FLASH_SLOT_ERASE FLASH_PAGE_ERASE_MAX_TIME_US
27#define FLASH_INTERVAL_ERASE FLASH_SLOT_ERASE
28#define FLASH_SLOT_WRITE 7500
29#define FLASH_INTERVAL_WRITE FLASH_SLOT_WRITE
30
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020031#define FLASH_RADIO_ABORT_DELAY_US 500
32#define FLASH_TIMEOUT_MS ((FLASH_PAGE_ERASE_MAX_TIME_US)\
33 * (FLASH_PAGE_MAX_CNT) / 1000)
Carles Cufi8aa9a372018-03-20 18:41:39 +010034#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020035
36#define FLASH_OP_DONE (0) /* 0 for compliance with the driver API. */
37#define FLASH_OP_ONGOING (-1)
38
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +020039struct flash_context {
40 u32_t data_addr; /* Address of data to write. */
41 u32_t flash_addr; /* Address of flash to write or erase. */
42 u32_t len; /* Size off data to write or erase [B]. */
Carles Cufi8aa9a372018-03-20 18:41:39 +010043#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +020044 u8_t enable_time_limit; /* execution limited to timeslot. */
45 u32_t interval; /* timeslot interval. */
46 u32_t slot; /* timeslot length. */
Carles Cufi8aa9a372018-03-20 18:41:39 +010047#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +020048}; /*< Context type for f. @ref write_op @ref erase_op */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020049
Carles Cufi8aa9a372018-03-20 18:41:39 +010050#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020051typedef int (*flash_op_handler_t) (void *context);
52
53struct flash_op_desc {
54 flash_op_handler_t handler;
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +020055 struct flash_context *context; /* [in,out] */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020056 int result;
57};
58
59/* semaphore for synchronization of flash operations */
60static struct k_sem sem_sync;
61
62static int write_op(void *context); /* instance of flash_op_handler_t */
63static int write_in_timeslice(off_t addr, const void *data, size_t len);
64
65static int erase_op(void *context); /* instance of flash_op_handler_t */
66static int erase_in_timeslice(u32_t addr, u32_t size);
Carles Cufi8aa9a372018-03-20 18:41:39 +010067#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020068
Carles Cufi408ea142018-06-16 11:27:36 +020069#if defined(CONFIG_MULTITHREADING)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020070/* semaphore for locking flash resources (tickers) */
71static struct k_sem sem_lock;
Carles Cufi408ea142018-06-16 11:27:36 +020072#define SYNC_INIT() k_sem_init(&sem_lock, 1, 1)
73#define SYNC_LOCK() k_sem_take(&sem_lock, K_FOREVER)
74#define SYNC_UNLOCK() k_sem_give(&sem_lock)
75#else
76#define SYNC_INIT()
77#define SYNC_LOCK()
78#define SYNC_UNLOCK()
79#endif
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +020080
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +020081static bool write_protect;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +020082
83static int write(off_t addr, const void *data, size_t len);
84static int erase(u32_t addr, u32_t size);
85
Kumar Galaccad5bf2017-04-21 10:03:20 -050086static inline bool is_aligned_32(u32_t data)
Ricardo Salveti9d033292016-07-25 23:34:52 -030087{
88 return (data & 0x3) ? false : true;
89}
90
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +010091static inline bool is_regular_addr_valid(off_t addr, size_t len)
Ricardo Salveti9d033292016-07-25 23:34:52 -030092{
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +020093 size_t flash_size = nrfx_nvmc_flash_size_get();
94
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +010095 if (addr >= flash_size ||
96 addr < 0 ||
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +020097 len > flash_size ||
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +010098 (addr) + len > flash_size) {
Ricardo Salveti9d033292016-07-25 23:34:52 -030099 return false;
100 }
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +0100101
Ricardo Salveti9d033292016-07-25 23:34:52 -0300102 return true;
103}
104
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100105
106static inline bool is_uicr_addr_valid(off_t addr, size_t len)
107{
108#ifdef CONFIG_SOC_FLASH_NRF_UICR
109 if (addr >= (off_t)NRF_UICR + sizeof(*NRF_UICR) ||
110 addr < (off_t)NRF_UICR ||
111 len > sizeof(*NRF_UICR) ||
112 addr + len > (off_t)NRF_UICR + sizeof(*NRF_UICR)) {
113 return false;
114 }
115
116 return true;
117#else
118 return false;
119#endif /* CONFIG_SOC_FLASH_NRF_UICR */
120}
121
Ricardo Salveti9d033292016-07-25 23:34:52 -0300122static void nvmc_wait_ready(void)
123{
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200124 while (!nrfx_nvmc_write_done_check()) {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300125 }
126}
127
Carles Cufi8aa9a372018-03-20 18:41:39 +0100128static int flash_nrf_read(struct device *dev, off_t addr,
Ricardo Salveti9d033292016-07-25 23:34:52 -0300129 void *data, size_t len)
130{
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +0100131 if (is_regular_addr_valid(addr, len)) {
132 addr += DT_FLASH_BASE_ADDRESS;
133 } else if (!is_uicr_addr_valid(addr, len)) {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300134 return -EINVAL;
135 }
136
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +0100137 if (!len) {
138 return 0;
139 }
140
Ricardo Salveti9d033292016-07-25 23:34:52 -0300141 memcpy(data, (void *)addr, len);
142
143 return 0;
144}
145
Carles Cufi8aa9a372018-03-20 18:41:39 +0100146static int flash_nrf_write(struct device *dev, off_t addr,
Ricardo Salveti9d033292016-07-25 23:34:52 -0300147 const void *data, size_t len)
148{
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200149 int ret;
Ricardo Salveti9d033292016-07-25 23:34:52 -0300150
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200151 if (write_protect) {
152 return -EACCES;
153 }
154
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +0100155 if (is_regular_addr_valid(addr, len)) {
156 addr += DT_FLASH_BASE_ADDRESS;
157 } else if (!is_uicr_addr_valid(addr, len)) {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300158 return -EINVAL;
159 }
160
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +0100161 if (!len) {
162 return 0;
163 }
164
Carles Cufi408ea142018-06-16 11:27:36 +0200165 SYNC_LOCK();
Ricardo Salvetic7e60762016-09-13 13:32:55 -0300166
Carles Cufi8aa9a372018-03-20 18:41:39 +0100167#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200168 if (ticker_is_initialized(0)) {
169 ret = write_in_timeslice(addr, data, len);
170 } else
Carles Cufi8aa9a372018-03-20 18:41:39 +0100171#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200172 {
173 ret = write(addr, data, len);
Ricardo Salvetic7e60762016-09-13 13:32:55 -0300174 }
175
Carles Cufi408ea142018-06-16 11:27:36 +0200176 SYNC_UNLOCK();
Ricardo Salvetic7e60762016-09-13 13:32:55 -0300177
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200178 return ret;
Ricardo Salveti9d033292016-07-25 23:34:52 -0300179}
180
Carles Cufi8aa9a372018-03-20 18:41:39 +0100181static int flash_nrf_erase(struct device *dev, off_t addr, size_t size)
Ricardo Salveti9d033292016-07-25 23:34:52 -0300182{
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +0200183 u32_t pg_size = nrfx_nvmc_flash_page_size_get();
Kumar Galaccad5bf2017-04-21 10:03:20 -0500184 u32_t n_pages = size / pg_size;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200185 int ret;
Ricardo Salveti9d033292016-07-25 23:34:52 -0300186
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200187 if (write_protect) {
188 return -EACCES;
189 }
190
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100191 if (is_regular_addr_valid(addr, size)) {
192 /* Erase can only be done per page */
193 if (((addr % pg_size) != 0) || ((size % pg_size) != 0)) {
194 return -EINVAL;
195 }
196
197 if (!n_pages) {
198 return 0;
199 }
Andrzej Puzdrowskidbe56332019-11-08 15:53:49 +0100200
201 addr += DT_FLASH_BASE_ADDRESS;
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100202#ifdef CONFIG_SOC_FLASH_NRF_UICR
203 } else if (addr != (off_t)NRF_UICR || size != sizeof(*NRF_UICR)) {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300204 return -EINVAL;
205 }
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100206#else
207 } else {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300208 return -EINVAL;
209 }
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100210#endif /* CONFIG_SOC_FLASH_NRF_UICR */
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +0100211
Carles Cufi408ea142018-06-16 11:27:36 +0200212 SYNC_LOCK();
Ricardo Salveti9d033292016-07-25 23:34:52 -0300213
Carles Cufi8aa9a372018-03-20 18:41:39 +0100214#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200215 if (ticker_is_initialized(0)) {
216 ret = erase_in_timeslice(addr, size);
217 } else
Carles Cufi8aa9a372018-03-20 18:41:39 +0100218#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200219 {
220 ret = erase(addr, size);
Ricardo Salveti9d033292016-07-25 23:34:52 -0300221 }
222
Carles Cufi408ea142018-06-16 11:27:36 +0200223 SYNC_UNLOCK();
Ricardo Salveti9d033292016-07-25 23:34:52 -0300224
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200225 return ret;
Ricardo Salveti9d033292016-07-25 23:34:52 -0300226}
227
Carles Cufi8aa9a372018-03-20 18:41:39 +0100228static int flash_nrf_write_protection(struct device *dev, bool enable)
Ricardo Salveti9d033292016-07-25 23:34:52 -0300229{
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200230 /* virtual write-erase protection */
231 write_protect = enable;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200232
Ricardo Salveti9d033292016-07-25 23:34:52 -0300233 return 0;
234}
235
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200236#if defined(CONFIG_FLASH_PAGE_LAYOUT)
237static struct flash_pages_layout dev_layout;
238
Carles Cufi8aa9a372018-03-20 18:41:39 +0100239static void flash_nrf_pages_layout(struct device *dev,
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200240 const struct flash_pages_layout **layout,
241 size_t *layout_size)
242{
243 *layout = &dev_layout;
244 *layout_size = 1;
245}
246#endif /* CONFIG_FLASH_PAGE_LAYOUT */
247
Carles Cufi8aa9a372018-03-20 18:41:39 +0100248static const struct flash_driver_api flash_nrf_api = {
249 .read = flash_nrf_read,
250 .write = flash_nrf_write,
251 .erase = flash_nrf_erase,
252 .write_protection = flash_nrf_write_protection,
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200253#if defined(CONFIG_FLASH_PAGE_LAYOUT)
Carles Cufi8aa9a372018-03-20 18:41:39 +0100254 .page_layout = flash_nrf_pages_layout,
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200255#endif
Andrzej Puzdrowski296a3552017-09-29 13:03:40 +0200256 .write_block_size = 1,
Ricardo Salveti9d033292016-07-25 23:34:52 -0300257};
258
Carles Cufi8aa9a372018-03-20 18:41:39 +0100259static int nrf_flash_init(struct device *dev)
Ricardo Salveti9d033292016-07-25 23:34:52 -0300260{
Carles Cufi408ea142018-06-16 11:27:36 +0200261 SYNC_INIT();
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200262
Carles Cufi8aa9a372018-03-20 18:41:39 +0100263#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200264 k_sem_init(&sem_sync, 0, 1);
Carles Cufi8aa9a372018-03-20 18:41:39 +0100265#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200266
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200267#if defined(CONFIG_FLASH_PAGE_LAYOUT)
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +0200268 dev_layout.pages_count = nrfx_nvmc_flash_page_count_get();
269 dev_layout.pages_size = nrfx_nvmc_flash_page_size_get();
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200270#endif
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200271 write_protect = true;
Andrzej Puzdrowskia7863f22017-08-17 11:25:27 +0200272
Ricardo Salveti9d033292016-07-25 23:34:52 -0300273 return 0;
274}
275
Varun Sharma77c643a2018-12-09 23:17:34 +0530276DEVICE_AND_API_INIT(nrf_flash, DT_FLASH_DEV_NAME, nrf_flash_init,
277 NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
278 &flash_nrf_api);
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200279
Carles Cufi8aa9a372018-03-20 18:41:39 +0100280#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200281
282static void time_slot_callback_work(u32_t ticks_at_expire, u32_t remainder,
283 u16_t lazy, void *context)
284{
285 struct flash_op_desc *op_desc;
286 u8_t instance_index;
287 u8_t ticker_id;
288 int result;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200289
Andrzej Kaczmarek246f4ee2017-08-11 12:15:38 +0200290 __ASSERT(ll_radio_state_is_idle(),
291 "Radio is on during flash operation.\n");
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200292
293 op_desc = context;
294 if (op_desc->handler(op_desc->context) == FLASH_OP_DONE) {
295 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
296
297 /* Stop the time slot ticker */
298 result = ticker_stop(instance_index,
299 0,
300 ticker_id,
301 NULL,
302 NULL);
303
304 if (result != TICKER_STATUS_SUCCESS &&
305 result != TICKER_STATUS_BUSY) {
306 __ASSERT(0, "Failed to stop ticker.\n");
307 }
308
309 ((struct flash_op_desc *)context)->result = 0;
310
311 /* notify thread that data is available */
312 k_sem_give(&sem_sync);
313 }
314}
315
316static void time_slot_callback_helper(u32_t ticks_at_expire, u32_t remainder,
317 u16_t lazy, void *context)
318{
319 u8_t instance_index;
320 u8_t ticker_id;
321 int err;
322
323 ll_radio_state_abort();
324
325 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
326
327 /* start a secondary one-shot ticker after ~ 500 us, */
328 /* this will let any radio role to gracefully release the Radio h/w */
329
330 err = ticker_start(instance_index, /* Radio instance ticker */
331 0, /* user_id */
332 0, /* ticker_id */
333 ticks_at_expire, /* current tick */
Vinayak Kariappa Chettimada4c123a52018-01-09 15:03:33 +0100334 HAL_TICKER_US_TO_TICKS(FLASH_RADIO_ABORT_DELAY_US),
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200335 0, /* periodic (on-shot) */
336 0, /* per. remaind. (on-shot) */
337 0, /* lazy, voluntary skips */
338 0,
339 time_slot_callback_work, /* handler for executing */
340 /* the flash operation */
341 context, /* the context for the flash operation */
342 NULL, /* no op callback */
343 NULL);
344
345 if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) {
346 ((struct flash_op_desc *)context)->result = -ECANCELED;
347
348 /* abort flash timeslots */
349 err = ticker_stop(instance_index, 0, ticker_id, NULL, NULL);
350 if (err != TICKER_STATUS_SUCCESS &&
351 err != TICKER_STATUS_BUSY) {
352 __ASSERT(0, "Failed to stop ticker.\n");
353 }
354
355 /* notify thread that data is available */
356 k_sem_give(&sem_sync);
357 }
358}
359
360static int work_in_time_slice(struct flash_op_desc *p_flash_op_desc)
361{
362 u8_t instance_index;
363 u8_t ticker_id;
364 int result;
365 u32_t err;
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200366 struct flash_context *context = p_flash_op_desc->context;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200367
368 ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
369
370 err = ticker_start(instance_index,
371 3, /* user id for thread mode */
372 /* (MAYFLY_CALL_ID_PROGRAM) */
373 ticker_id, /* flash ticker id */
374 ticker_ticks_now_get(), /* current tick */
375 0, /* first int. immediately */
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200376 /* period */
377 HAL_TICKER_US_TO_TICKS(context->interval),
378 /* period remainder */
379 HAL_TICKER_REMAINDER(context->interval),
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200380 0, /* lazy, voluntary skips */
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200381 HAL_TICKER_US_TO_TICKS(context->slot),
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200382 time_slot_callback_helper,
383 p_flash_op_desc,
384 NULL, /* no op callback */
385 NULL);
386
387 if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) {
388 result = -ECANCELED;
389 } else if (k_sem_take(&sem_sync, K_MSEC(FLASH_TIMEOUT_MS)) != 0) {
390 /* wait for operation's complete overrun*/
391 result = -ETIMEDOUT;
392 } else {
393 result = p_flash_op_desc->result;
394 }
395
396 return result;
397}
398
399static int erase_in_timeslice(u32_t addr, u32_t size)
400{
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200401 struct flash_context context = {
402 .flash_addr = addr,
403 .len = size,
404 .enable_time_limit = 1, /* enable time limit */
405 .interval = FLASH_INTERVAL_ERASE,
406 .slot = FLASH_SLOT_ERASE
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200407 };
408
409 struct flash_op_desc flash_op_desc = {
410 .handler = erase_op,
411 .context = &context
412 };
413
414 return work_in_time_slice(&flash_op_desc);
415}
416
417static int write_in_timeslice(off_t addr, const void *data, size_t len)
418{
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200419 struct flash_context context = {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200420 .data_addr = (u32_t) data,
421 .flash_addr = addr,
422 .len = len,
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200423 .enable_time_limit = 1, /* enable time limit */
424 .interval = FLASH_INTERVAL_WRITE,
425 .slot = FLASH_SLOT_WRITE
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200426 };
427
428 struct flash_op_desc flash_op_desc = {
429 .handler = write_op,
430 .context = &context
431 };
432
433 return work_in_time_slice(&flash_op_desc);
434}
435
Carles Cufi8aa9a372018-03-20 18:41:39 +0100436#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200437
438static int erase_op(void *context)
439{
Andrzej Puzdrowski279115e2019-04-01 18:00:42 +0200440 u32_t pg_size = nrfx_nvmc_flash_page_size_get();
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200441 struct flash_context *e_ctx = context;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200442
Carles Cufi8aa9a372018-03-20 18:41:39 +0100443#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800444 u32_t ticks_begin = 0U;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200445 u32_t ticks_diff;
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800446 u32_t i = 0U;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200447
448 if (e_ctx->enable_time_limit) {
449 ticks_begin = ticker_ticks_now_get();
450 }
Carles Cufi8aa9a372018-03-20 18:41:39 +0100451#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200452
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100453#ifdef CONFIG_SOC_FLASH_NRF_UICR
454 if (e_ctx->flash_addr == (off_t)NRF_UICR) {
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200455 (void)nrfx_nvmc_uicr_erase();
Andrzej Puzdrowskicd4fbde2018-11-15 15:50:03 +0100456 return FLASH_OP_DONE;
457 }
458#endif
459
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200460 do {
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200461 (void)nrfx_nvmc_page_erase(e_ctx->flash_addr);
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200462
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200463 e_ctx->len -= pg_size;
464 e_ctx->flash_addr += pg_size;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200465
Carles Cufi8aa9a372018-03-20 18:41:39 +0100466#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200467 i++;
468
469 if (e_ctx->enable_time_limit) {
Vinayak Kariappa Chettimada3637d502018-02-16 11:22:21 +0100470 ticks_diff =
471 ticker_ticks_diff_get(ticker_ticks_now_get(),
472 ticks_begin);
473 if (ticks_diff + ticks_diff/i >
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200474 HAL_TICKER_US_TO_TICKS(e_ctx->slot)) {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200475 break;
476 }
477 }
Carles Cufi8aa9a372018-03-20 18:41:39 +0100478#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200479
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200480 } while (e_ctx->len > 0);
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200481
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200482 return (e_ctx->len > 0) ? FLASH_OP_ONGOING : FLASH_OP_DONE;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200483}
484
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200485static void shift_write_context(u32_t shift, struct flash_context *w_ctx)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200486{
487 w_ctx->flash_addr += shift;
488 w_ctx->data_addr += shift;
489 w_ctx->len -= shift;
490}
491
492static int write_op(void *context)
493{
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200494 struct flash_context *w_ctx = context;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200495 u32_t count;
496
Carles Cufi8aa9a372018-03-20 18:41:39 +0100497#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800498 u32_t ticks_begin = 0U;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200499 u32_t ticks_diff;
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800500 u32_t i = 1U;
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200501
502 if (w_ctx->enable_time_limit) {
503 ticks_begin = ticker_ticks_now_get();
504 }
Carles Cufi8aa9a372018-03-20 18:41:39 +0100505#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200506
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200507 /* If not aligned, write unaligned beginning */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200508 if (!is_aligned_32(w_ctx->flash_addr)) {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200509 count = sizeof(u32_t) - (w_ctx->flash_addr & 0x3);
510 if (count > w_ctx->len) {
511 count = w_ctx->len;
512 }
513
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200514 nrfx_nvmc_bytes_write(w_ctx->flash_addr,
515 (const void *)w_ctx->data_addr,
516 count);
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200517
518 shift_write_context(count, w_ctx);
519
Carles Cufi8aa9a372018-03-20 18:41:39 +0100520#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200521 if (w_ctx->enable_time_limit) {
Vinayak Kariappa Chettimada3637d502018-02-16 11:22:21 +0100522 ticks_diff =
523 ticker_ticks_diff_get(ticker_ticks_now_get(),
524 ticks_begin);
Patrik Flykt21358ba2019-03-28 14:57:54 -0600525 if (ticks_diff * 2U >
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200526 HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200527 nvmc_wait_ready();
528 return FLASH_OP_ONGOING;
529 }
530 }
Carles Cufi8aa9a372018-03-20 18:41:39 +0100531#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200532 }
533
534 /* Write all the 4-byte aligned data */
535 while (w_ctx->len >= sizeof(u32_t)) {
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200536 nrfx_nvmc_word_write(w_ctx->flash_addr,
537 UNALIGNED_GET((u32_t *)w_ctx->data_addr));
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200538
539 shift_write_context(sizeof(u32_t), w_ctx);
540
Carles Cufi8aa9a372018-03-20 18:41:39 +0100541#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200542 i++;
543
544 if (w_ctx->enable_time_limit) {
Vinayak Kariappa Chettimada3637d502018-02-16 11:22:21 +0100545 ticks_diff =
546 ticker_ticks_diff_get(ticker_ticks_now_get(),
547 ticks_begin);
548 if (ticks_diff + ticks_diff/i >
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200549 HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200550 nvmc_wait_ready();
551 return FLASH_OP_ONGOING;
552 }
553 }
Carles Cufi8aa9a372018-03-20 18:41:39 +0100554#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200555 }
556
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200557 /* Write remaining unaligned data */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200558 if (w_ctx->len) {
Andrzej Puzdrowski560b4582019-04-01 17:26:05 +0200559 nrfx_nvmc_bytes_write(w_ctx->flash_addr,
560 (const void *)w_ctx->data_addr,
561 w_ctx->len);
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200562
563 shift_write_context(w_ctx->len, w_ctx);
564 }
565
566 nvmc_wait_ready();
567
568 return FLASH_OP_DONE;
569}
570
571static int erase(u32_t addr, u32_t size)
572{
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200573 struct flash_context context = {
574 .flash_addr = addr,
575 .len = size,
Carles Cufi8aa9a372018-03-20 18:41:39 +0100576#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200577 .enable_time_limit = 0 /* disable time limit */
Carles Cufi8aa9a372018-03-20 18:41:39 +0100578#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200579 };
580
581 return erase_op(&context);
582}
583
584static int write(off_t addr, const void *data, size_t len)
585{
Vinayak Kariappa Chettimada9da6a852018-09-19 15:40:35 +0200586 struct flash_context context = {
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200587 .data_addr = (u32_t) data,
588 .flash_addr = addr,
589 .len = len,
Carles Cufi8aa9a372018-03-20 18:41:39 +0100590#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200591 .enable_time_limit = 0 /* disable time limit */
Carles Cufi8aa9a372018-03-20 18:41:39 +0100592#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
Andrzej Puzdrowski8b7a8442017-06-22 09:49:12 +0200593 };
594
595 return write_op(&context);
596}