static int flash_nrf5_erase(struct device *dev, off_t addr, size_t size) { uint32_t pg_size = NRF_FICR->CODEPAGESIZE; uint32_t n_pages = size / pg_size; /* Erase can only be done per page */ if (((addr % pg_size) != 0) || ((size % pg_size) != 0) || (n_pages == 0)) { return -EINVAL; } if (!is_addr_valid(addr, size)) { return -EINVAL; } /* Erase uses a specific configuration register */ NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos; nvmc_wait_ready(); for (uint32_t i = 0; i < n_pages; i++) { NRF_NVMC->ERASEPAGE = (uint32_t)addr + (i * pg_size); nvmc_wait_ready(); } NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; nvmc_wait_ready(); return 0; }
static int flash_nrf5_write(struct device *dev, off_t addr, const void *data, size_t len) { uint32_t addr_word; uint32_t tmp_word; void *data_word; uint32_t remaining = len; uint32_t count = 0; if (!is_addr_valid(addr, len)) { return -EINVAL; } /* Start with a word-aligned address and handle the offset */ addr_word = addr & ~0x3; /* If not aligned, read first word, update and write it back */ if (!is_aligned_32(addr)) { tmp_word = *(uint32_t *)(addr_word); count = sizeof(uint32_t) - (addr & 0x3); if (count > len) { count = len; } memcpy((uint8_t *)&tmp_word + (addr & 0x3), data, count); nvmc_wait_ready(); *(uint32_t *)addr_word = tmp_word; addr_word = addr + count; remaining -= count; } /* Write all the 4-byte aligned data */ data_word = (void *) data + count; while (remaining >= sizeof(uint32_t)) { nvmc_wait_ready(); *(uint32_t *)addr_word = *(uint32_t *)data_word; addr_word += sizeof(uint32_t); data_word += sizeof(uint32_t); remaining -= sizeof(uint32_t); } /* Write remaining data */ if (remaining) { tmp_word = *(uint32_t *)(addr_word); memcpy((uint8_t *)&tmp_word, data_word, remaining); nvmc_wait_ready(); *(uint32_t *)addr_word = tmp_word; } nvmc_wait_ready(); return 0; }
static int flash_nrf5_write_protection(struct device *dev, bool enable) { if (enable) { NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; } else { NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; } nvmc_wait_ready(); return 0; }
static int write_op(void *context) { struct flash_context *w_ctx = context; u32_t addr_word; u32_t tmp_word; u32_t count; #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) u32_t ticks_begin = 0; u32_t ticks_diff; u32_t i = 1; if (w_ctx->enable_time_limit) { ticks_begin = ticker_ticks_now_get(); } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ /* Start with a word-aligned address and handle the offset */ addr_word = (u32_t)w_ctx->flash_addr & ~0x3; /* If not aligned, read first word, update and write it back */ if (!is_aligned_32(w_ctx->flash_addr)) { tmp_word = *(u32_t *)(addr_word); count = sizeof(u32_t) - (w_ctx->flash_addr & 0x3); if (count > w_ctx->len) { count = w_ctx->len; } memcpy((u8_t *)&tmp_word + (w_ctx->flash_addr & 0x3), (void *)w_ctx->data_addr, count); nvmc_wait_ready(); *(u32_t *)addr_word = tmp_word; shift_write_context(count, w_ctx); #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) if (w_ctx->enable_time_limit) { ticks_diff = ticker_ticks_diff_get(ticker_ticks_now_get(), ticks_begin); if (2 * ticks_diff > HAL_TICKER_US_TO_TICKS(w_ctx->slot)) { nvmc_wait_ready(); return FLASH_OP_ONGOING; } } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ } /* Write all the 4-byte aligned data */ while (w_ctx->len >= sizeof(u32_t)) { nvmc_wait_ready(); *(u32_t *)w_ctx->flash_addr = UNALIGNED_GET((u32_t *)w_ctx->data_addr); shift_write_context(sizeof(u32_t), w_ctx); #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) i++; if (w_ctx->enable_time_limit) { ticks_diff = ticker_ticks_diff_get(ticker_ticks_now_get(), ticks_begin); if (ticks_diff + ticks_diff/i > HAL_TICKER_US_TO_TICKS(w_ctx->slot)) { nvmc_wait_ready(); return FLASH_OP_ONGOING; } } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ } /* Write remaining data */ if (w_ctx->len) { tmp_word = *(u32_t *)(w_ctx->flash_addr); memcpy((u8_t *)&tmp_word, (void *)w_ctx->data_addr, w_ctx->len); nvmc_wait_ready(); *(u32_t *)w_ctx->flash_addr = tmp_word; shift_write_context(w_ctx->len, w_ctx); } nvmc_wait_ready(); return FLASH_OP_DONE; }
static int erase_in_timeslice(u32_t addr, u32_t size) { struct flash_context context = { .flash_addr = addr, .len = size, .enable_time_limit = 1, /* enable time limit */ .interval = FLASH_INTERVAL_ERASE, .slot = FLASH_SLOT_ERASE }; struct flash_op_desc flash_op_desc = { .handler = erase_op, .context = &context }; return work_in_time_slice(&flash_op_desc); } static int write_in_timeslice(off_t addr, const void *data, size_t len) { struct flash_context context = { .data_addr = (u32_t) data, .flash_addr = addr, .len = len, .enable_time_limit = 1, /* enable time limit */ .interval = FLASH_INTERVAL_WRITE, .slot = FLASH_SLOT_WRITE }; struct flash_op_desc flash_op_desc = { .handler = write_op, .context = &context }; return work_in_time_slice(&flash_op_desc); } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ static int erase_op(void *context) { u32_t prev_nvmc_cfg = NRF_NVMC->CONFIG; u32_t pg_size = NRF_FICR->CODEPAGESIZE; struct flash_context *e_ctx = context; #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) u32_t ticks_begin = 0; u32_t ticks_diff; u32_t i = 0; if (e_ctx->enable_time_limit) { ticks_begin = ticker_ticks_now_get(); } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ /* Erase uses a specific configuration register */ NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos; nvmc_wait_ready(); do { NRF_NVMC->ERASEPAGE = e_ctx->flash_addr; nvmc_wait_ready(); e_ctx->len -= pg_size; e_ctx->flash_addr += pg_size; #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) i++; if (e_ctx->enable_time_limit) { ticks_diff = ticker_ticks_diff_get(ticker_ticks_now_get(), ticks_begin); if (ticks_diff + ticks_diff/i > HAL_TICKER_US_TO_TICKS(e_ctx->slot)) { break; } } #endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */ } while (e_ctx->len > 0); NRF_NVMC->CONFIG = prev_nvmc_cfg; nvmc_wait_ready(); return (e_ctx->len > 0) ? FLASH_OP_ONGOING : FLASH_OP_DONE; } static void shift_write_context(u32_t shift, struct flash_context *w_ctx) { w_ctx->flash_addr += shift; w_ctx->data_addr += shift; w_ctx->len -= shift; }