diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/flash_api.c b/targets/TARGET_NORDIC/TARGET_NRF5/flash_api.c index 8329a2c9be..be592b8b36 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/flash_api.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5/flash_api.c @@ -36,56 +36,134 @@ * */ -#include "flash_api.h" +#if DEVICE_FLASH + +#include "hal/flash_api.h" +#include "hal/lp_ticker_api.h" + +#include "nrf_drv_common.h" #include "nrf_nvmc.h" #include "nrf_soc.h" -#include "nrf_sdm.h" -#if DEVICE_FLASH +#define WORD_WRITE_TIMEOUT_US (1 * 1000) // Max. value from datasheet: 338 us +#define PAGE_ERASE_TIMEOUT_US (200 * 1000) // Max. value from datasheet: 89.7 ms + +/* Macro for testing if the SoftDevice is active, regardless of whether the + * application is build with the SoftDevice or not. + */ +#if defined(SOFTDEVICE_PRESENT) +#include "nrf_sdm.h" +static uint8_t wrapper(void) { + uint8_t softdevice_is_enabled; + ret_code_t result = sd_softdevice_is_enabled(&softdevice_is_enabled); + return ((result == NRF_SUCCESS) && (softdevice_is_enabled == 1)); +} +#define NRF_HAL_SD_IS_ENABLED() wrapper() +#else +#define NRF_HAL_SD_IS_ENABLED() 0 +#endif int32_t flash_init(flash_t *obj) { (void)(obj); - uint8_t sd_enabled; - if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) { - return -1; + + /* Initialize low power ticker. Used for timeouts. */ + static bool first_init = true; + + if (first_init) { + first_init = false; + lp_ticker_init(); } + return 0; } int32_t flash_free(flash_t *obj) { (void)(obj); + return 0; } int32_t flash_erase_sector(flash_t *obj, uint32_t address) { (void)(obj); - uint8_t sd_enabled; - if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) { - return -1; + + /* Return value defaults to error. */ + uint32_t result = NRF_ERROR_BUSY; + + if (NRF_HAL_SD_IS_ENABLED()) { + + /* Convert address to page number. */ + uint32_t page_number = address / NRF_FICR->CODEPAGESIZE; + + /* Setup stop watch for timeout. */ + uint32_t start_us = lp_ticker_read(); + uint32_t now_us = start_us; + + /* Retry if flash is busy until timeout is reached. */ + while (((now_us - start_us) < PAGE_ERASE_TIMEOUT_US) && + (result == NRF_ERROR_BUSY)) { + + result = sd_flash_page_erase(page_number); + + /* Read timeout timer. */ + now_us = lp_ticker_read(); + } + + } else { + + /* Raw API doesn't return error code, assume success. */ + nrf_nvmc_page_erase(address); + result = NRF_SUCCESS; } - nrf_nvmc_page_erase(address); - return 0; + + /* Convert Nordic error code to mbed HAL error code. */ + return (result == NRF_SUCCESS) ? 0 : -1; } int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size) { - uint8_t sd_enabled; - if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) { - return -1; + (void)(obj); + + /* Return value defaults to error. */ + uint32_t result = NRF_ERROR_BUSY; + + /* Convert size to words. */ + uint32_t words = size / sizeof(uint32_t); + + if (NRF_HAL_SD_IS_ENABLED()) { + + /* Setup stop watch for timeout. */ + uint32_t start_us = lp_ticker_read(); + uint32_t now_us = start_us; + + /* Retry if flash is busy until timeout is reached. */ + while (((now_us - start_us) < (words * WORD_WRITE_TIMEOUT_US)) && + (result == NRF_ERROR_BUSY)) { + + result = sd_flash_write((uint32_t *) address, (const uint32_t *) data, words); + + /* Read timeout timer. */ + now_us = lp_ticker_read(); + } + + } else { + /* We will use *_words function to speed up flashing code. Word means 32bit -> 4B + * or sizeof(uint32_t). + */ + nrf_nvmc_write_words(address, (const uint32_t *) data, words); + result = NRF_SUCCESS; } - /* We will use *_words function to speed up flashing code. Word means 32bit -> 4B - * or sizeof(uint32_t). - */ - nrf_nvmc_write_words(address, (const uint32_t *) data, (size / sizeof(uint32_t))); - return 0; + + /* Convert Nordic error code to mbed HAL error code. */ + return (result == NRF_SUCCESS) ? 0 : -1; } uint32_t flash_get_size(const flash_t *obj) { (void)(obj); + /* Just count flash size. */ return NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE; } @@ -93,10 +171,12 @@ uint32_t flash_get_size(const flash_t *obj) uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address) { (void)(obj); + /* Test if passed address is in flash space. */ if (address < flash_get_size(obj)) { return NRF_FICR->CODEPAGESIZE; } + /* Something goes wrong, return invalid size error code. */ return MBED_FLASH_INVALID_SIZE; } @@ -104,11 +184,15 @@ uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address) uint32_t flash_get_page_size(const flash_t *obj) { (void)(obj); - return NRF_FICR->CODEPAGESIZE; + + /* Return minimum writeable size. Note that this is different from the erase page size. */ + return 4; } uint32_t flash_get_start_address(const flash_t *obj) { + (void)(obj); + return 0; } diff --git a/targets/targets.json b/targets/targets.json index a22cfa2861..911a4246e9 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -3481,7 +3481,7 @@ "supported_form_factors": ["ARDUINO"], "inherits": ["MCU_NRF52"], "macros_add": ["BOARD_PCA10040", "NRF52_PAN_12", "NRF52_PAN_15", "NRF52_PAN_58", "NRF52_PAN_55", "NRF52_PAN_54", "NRF52_PAN_31", "NRF52_PAN_30", "NRF52_PAN_51", "NRF52_PAN_36", "NRF52_PAN_53", "S132", "CONFIG_GPIO_AS_PINRESET", "BLE_STACK_SUPPORT_REQD", "SWI_DISABLE0", "NRF52_PAN_20", "NRF52_PAN_64", "NRF52_PAN_62", "NRF52_PAN_63"], - "device_has_add": ["ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"], + "device_has_add": ["ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE", "FLASH"], "release_versions": ["2", "5"], "device_name": "nRF52832_xxAA" },