From ff862764e5dda8a0e19b20375063e7908d0de858 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 14 Jul 2020 08:57:27 +0200 Subject: [PATCH] cpu/sam0_common: flashpage: introduce functions to write to user page --- cpu/sam0_common/include/periph_cpu_common.h | 43 ++++++++++++++ cpu/sam0_common/periph/flashpage.c | 65 +++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index a79010a369f38..db34cba5270f1 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -1036,6 +1036,49 @@ void dma_wait(dma_t dma); void dma_cancel(dma_t dma); /** @} */ +/** + * @name sam0 User Configuration + * + * The MCUs of this family contain a region of memory that is used to store + * CPU configuration & calibration data. + * It can be used to set persistent settings and has some additional space + * to store user configuration data. + * @{ + */ + +/** + * @brief MCU configuration applied on start. The contents of this struct differ + * between families. + */ +typedef struct sam0_aux_cfg_mapping nvm_user_page_t; + +/** + * @brief Reset the configuration area, apply a new configuration. + * + * + * @param cfg New MCU configuration, may be NULL. + * In that case, this will clear the remaining user area + * and apply the old configuration again. + */ +void sam0_flashpage_aux_reset(nvm_user_page_t *cfg); + +/** + * @brief Write data to the user configuration area. + * This will write data to the remaining space after @see nvm_user_page_t + * The size of this area depends on the MCU family used. + * + * Will only write bits 1 -> 0. To reset bits to 1, call @see sam0_flashpage_aux_reset + * This will reset the whole user area configuration. + * + * Arbitrary data lengths and offsets are supported. + * + * @param offset Byte offset after @see nvm_user_page_t + * @param data The data to write + * @param len Size of the data + */ +void sam0_flashpage_aux_write_raw(uint32_t offset, const void *data, size_t len); +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index 92685aed364e1..b560381bfd925 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -27,6 +27,7 @@ */ #include +#include #include "cpu.h" #include "periph/flashpage.h" @@ -36,6 +37,15 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) +/* Write Quad Word is the only allowed operation on AUX pages */ +#if defined(NVMCTRL_CTRLB_CMD_WQW) +#define AUX_CHUNK_SIZE (4 * sizeof(uint32_t)) +#elif defined(AUX_PAGE_SIZE) +#define AUX_CHUNK_SIZE AUX_PAGE_SIZE +#else +#define AUX_CHUNK_SIZE FLASH_USER_PAGE_SIZE +#endif + /** * @brief NVMCTRL selection macros */ @@ -87,6 +97,21 @@ static void _cmd_clear_page_buffer(void) #endif } +static void _cmd_erase_aux(void) +{ + wait_nvm_is_ready(); + + /* send Erase Page/Auxiliary Row command */ +#if defined(NVMCTRL_CTRLB_CMD_EP) + _NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EP); +#elif defined(NVMCTRL_CTRLA_CMD_EAR) + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_EAR); +#else + /* SAML1x uses same command for all areas */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); +#endif +} + static void _cmd_erase_row(void) { wait_nvm_is_ready(); @@ -99,6 +124,21 @@ static void _cmd_erase_row(void) #endif } +static void _cmd_write_aux(void) +{ + wait_nvm_is_ready(); + + /* write auxiliary page */ +#if defined(NVMCTRL_CTRLA_CMD_WAP) + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WAP); +#elif defined(NVMCTRL_CTRLB_CMD_WQW) + _NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WQW); +#else + /* SAML1x uses same command for all areas */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); +#endif +} + static void _cmd_write_page(void) { wait_nvm_is_ready(); @@ -202,7 +242,32 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); _write_page(target_addr, data, len, _cmd_write_page); +} + +void sam0_flashpage_aux_write_raw(uint32_t offset, const void *data, size_t len) +{ + uintptr_t dst = NVMCTRL_USER + sizeof(nvm_user_page_t) + offset; + +#ifdef FLASH_USER_PAGE_SIZE + assert(dst + len < NVMCTRL_USER + FLASH_USER_PAGE_SIZE); +#else + assert(dst + len < NVMCTRL_USER + AUX_PAGE_SIZE * AUX_NB_OF_PAGES); +#endif + + _write_row((void*)dst, data, len, AUX_CHUNK_SIZE, _cmd_write_aux); +} + +void sam0_flashpage_aux_reset(nvm_user_page_t *cfg) +{ + nvm_user_page_t old_cfg; + + if (cfg == NULL) { + cfg = &old_cfg; + memcpy(cfg, (void*)NVMCTRL_USER, sizeof(*cfg)); + } + _erase_page((void*)NVMCTRL_USER, _cmd_erase_aux); + _write_row((void*)NVMCTRL_USER, cfg, sizeof(*cfg), AUX_CHUNK_SIZE, _cmd_write_aux); } #ifdef FLASHPAGE_RWWEE_NUMOF