Skip to content

Commit

Permalink
cpu/sam0_common: flashpage: split RWWEE and normal functions
Browse files Browse the repository at this point in the history
Move common code into helper functions and extract the commands
that differ between normal and RWWEE page reading / writing.
This cuts down on `#ifdef` use.
  • Loading branch information
benpicco committed Jul 12, 2020
1 parent 449c564 commit a1fb41f
Showing 1 changed file with 149 additions and 130 deletions.
279 changes: 149 additions & 130 deletions cpu/sam0_common/periph/flashpage.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@
* use in RIOT is actually the row size as specified in the datasheet.
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/

#include <assert.h>
#include <string.h>

#include "cpu.h"
#include "periph/flashpage.h"

#define NVMCTRL_PAC_BIT (0x00000002)

#define FLASH_MAIN 0
#define FLASH_RWWEE 1
#define ENABLE_DEBUG (0)
#include "debug.h"

/**
* @brief NVMCTRL selection macros
Expand All @@ -59,185 +59,204 @@ static void _unlock(void)
#ifdef REG_PAC_WRCTRL
PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_CLR | ID_NVMCTRL);
#else
PAC1->WPCLR.reg = NVMCTRL_PAC_BIT;
PAC1->WPCLR.reg = PAC1_WPROT_DEFAULT_VAL;
#endif
}

static void _lock(void)
{
wait_nvm_is_ready();

/* put peripheral access lock for the NVMCTRL peripheral */
#ifdef REG_PAC_WRCTRL
PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_SET | ID_NVMCTRL);
#else
PAC1->WPSET.reg = NVMCTRL_PAC_BIT;
PAC1->WPSET.reg = PAC1_WPROT_DEFAULT_VAL;
#endif
}

#ifdef FLASHPAGE_RWWEE_NUMOF
void flashpage_write_raw_internal(void *target_addr, const void *data, size_t len, int flash_type)
#else
void flashpage_write_raw(void *target_addr, const void *data, size_t len)
#endif
static void _cmd_clear_page_buffer(void)
{
/* The actual minimal block size for writing is 16B, thus we
* assert we write on multiples and no less of that length.
*/
assert(!(len % FLASHPAGE_RAW_BLOCKSIZE));

/* ensure 4 byte aligned writes */
assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) ||
((unsigned)data % FLASHPAGE_RAW_ALIGNMENT)));

/* ensure the length doesn't exceed the actual flash size */
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
assert(((unsigned)target_addr + len) <=
(CPU_FLASH_RWWEE_BASE + (FLASHPAGE_SIZE * FLASHPAGE_RWWEE_NUMOF)));
} else {
#endif
assert(((unsigned)target_addr + len) <=
(CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)));
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif

uint32_t *dst = (uint32_t *)target_addr;
const uint32_t *data_addr = data;

/* write 4 bytes in one go */
len /= 4;
wait_nvm_is_ready();

_unlock();
#ifdef NVMCTRL_CTRLB_CMDEX_KEY
_NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_PBC);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC);
#endif
}

static void _cmd_erase_row(void)
{
wait_nvm_is_ready();
for (unsigned i = 0; i < len; i++) {
*dst++ = *data_addr++;
}
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
#ifdef CPU_SAML1X
/* SAML1X use the same Write Page command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);

/* send Row/Block erase command */
#ifdef NVMCTRL_CTRLB_CMDEX_KEY
_NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEWP);
#endif
} else {
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#endif
}

static void _cmd_write_page(void)
{
wait_nvm_is_ready();

/* write page */
#ifdef NVMCTRL_CTRLB_CMDEX_KEY
_NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WP);
_NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WP);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#endif
#ifdef FLASHPAGE_RWWEE_NUMOF
}
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#endif
wait_nvm_is_ready();
}

static void _write_page(int page, uint32_t offset, const void *data, size_t len, void (*cmd_write)(void))
{
const uint32_t *src = data;
uint32_t *dst = flashpage_addr(page) + offset;

DEBUG("write %u bytes to page %d (%p)\n", len, page, dst);

_unlock();
_cmd_clear_page_buffer();

memcpy(dst, src, len);

cmd_write();
_lock();
}

#ifdef FLASHPAGE_RWWEE_NUMOF
void flashpage_write_internal(int page, const void *data, int flash_type)
#else
void flashpage_write(int page, const void *data)
#endif
static void _erase_page(int page, void (*cmd_erase)(void))
{
uint32_t *page_addr;
uintptr_t page_addr = (uintptr_t)flashpage_addr(page);

#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
page_addr = (uint32_t *)flashpage_rwwee_addr(page);
} else {
#endif
page_addr = (uint32_t *)flashpage_addr(page);
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif
DEBUG("erase page %d (%x)\n", page, page_addr);

/* erase given page (the ADDR register uses 16-bit addresses) */
_unlock();
#if defined(CPU_SAML1X) || defined(CPU_SAMD5X)
/* Ensure address alignment */
_NVMCTRL->ADDR.reg = (((uint32_t)page_addr) & 0xfffffffe);
#else
_NVMCTRL->ADDR.reg = (((uint32_t)page_addr) >> 1);
#endif
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
#ifdef CPU_SAML1X
/* SAML1X use the same Erase command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEER);
#endif
} else {
#endif
#ifdef NVMCTRL_CTRLB_CMDEX_KEY
_NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#endif
#ifdef FLASHPAGE_RWWEE_NUMOF
}

/* ??? */
#if defined(CPU_SAMD21) || defined(CPU_SAML21)
page_addr >>= 1;
#endif
wait_nvm_is_ready();

/* set Row/Block start address */
_NVMCTRL->ADDR.reg = page_addr;

cmd_erase();
_lock();
}

/* write data to page */
if (data != NULL) {
/* One RIOT page is FLASHPAGE_PAGES_PER_ROW SAM0 flash pages (a row) as
* defined in the file cpu/sam0_common/include/cpu_conf.h, therefore we
* have to split the write into FLASHPAGE_PAGES_PER_ROW raw calls
* underneath, each writing a physical page in chunks of 4 bytes (see
* flashpage_write_raw)
* The erasing is done once as a full row is always reased.
*/
for (unsigned curpage = 0; curpage < FLASHPAGE_PAGES_PER_ROW; curpage++) {
#ifdef FLASHPAGE_RWWEE_NUMOF
flashpage_write_raw_internal(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4),
(void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)),
NVMCTRL_PAGE_SIZE, flash_type);
#else
flashpage_write_raw(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4),
(void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)),
NVMCTRL_PAGE_SIZE);
#endif
}
static void _write_row(int page, const void *data, void (*write_cmd)(void))
{
/* One RIOT page is FLASHPAGE_PAGES_PER_ROW SAM0 flash pages (a row) as
* defined in the file cpu/sam0_common/include/cpu_conf.h, therefore we
* have to split the write into FLASHPAGE_PAGES_PER_ROW raw calls
* underneath, each writing a physical page in chunks of 4 bytes (see
* flashpage_write_raw)
* The erasing is done once as a full row is always erased.
*/
uint32_t offset = 0;
const void* end = data + FLASHPAGE_SIZE;
while (data != end) {
_write_page(page, offset, data, NVMCTRL_PAGE_SIZE, write_cmd);
data += NVMCTRL_PAGE_SIZE;
offset += NVMCTRL_PAGE_SIZE;
}
}

void flashpage_write(int page, const void *data)
{
assert((unsigned)page < FLASHPAGE_NUMOF);

_erase_page(page, _cmd_erase_row);

if (data == NULL) {
return;
}

_write_row(page, data, _cmd_write_page);
}

#ifdef FLASHPAGE_RWWEE_NUMOF
/*
* If RWWEE flash is present then we create an additional layer for the write functions
* so we can specify the type (either MAIN or RWWEE) we want to access, keeping the
* standard API unchanged and code for systems without RWWEE at a minimum at the cost
* of some more #defines in the code
*/
void flashpage_write_raw(void *target_addr, const void *data, size_t len)
{
flashpage_write_raw_internal(target_addr, data, len, FLASH_MAIN);
/* The actual minimal block size for writing is 16B, thus we
* assert we write on multiples and no less of that length.
*/
assert(!(len % FLASHPAGE_RAW_BLOCKSIZE));

/* ensure 4 byte aligned writes */
assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) ||
((unsigned)data % FLASHPAGE_RAW_ALIGNMENT)));

/* ensure the length doesn't exceed the actual flash size */
assert(((unsigned)target_addr + len) <=
(CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)));

int page = flashpage_page(target_addr);
uint32_t offset = (uintptr_t)target_addr & (FLASHPAGE_SIZE - 1);

_write_page(page, offset, data, len, _cmd_write_page);
}

void flashpage_write(int page, const void *data)
#ifdef FLASHPAGE_RWWEE_NUMOF

static void _cmd_erase_row_rwwee(void)
{
wait_nvm_is_ready();

/* send erase row command */
#ifdef NVMCTRL_CTRLA_CMD_RWWEEER
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEER);
#else
/* SAML1X use the same Erase command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#endif
}

static void _cmd_write_page_rwwee(void)
{
assert((uint32_t)page < FLASHPAGE_NUMOF);
wait_nvm_is_ready();

flashpage_write_internal(page, data, FLASH_MAIN);
/* write page */
#ifdef NVMCTRL_CTRLA_CMD_RWWEEWP
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEWP);
#else
/* SAML1X use the same Write Page command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#endif
}

void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len)
{
flashpage_write_raw_internal(target_addr, data, len, FLASH_RWWEE);
/* The actual minimal block size for writing is 16B, thus we
* assert we write on multiples and no less of that length.
*/
assert(!(len % FLASHPAGE_RAW_BLOCKSIZE));

/* ensure 4 byte aligned writes */
assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) ||
((unsigned)data % FLASHPAGE_RAW_ALIGNMENT)));

assert(((unsigned)target_addr + len) <=
(CPU_FLASH_RWWEE_BASE + (FLASHPAGE_SIZE * FLASHPAGE_RWWEE_NUMOF)));

int page = flashpage_rwwee_page(target_addr);
uint32_t offset = (uintptr_t)target_addr & (FLASHPAGE_SIZE - 1);

_write_page(page, offset, data, len, _cmd_write_page_rwwee);
}

void flashpage_rwwee_write(int page, const void *data)
{
assert((uint32_t)page < FLASHPAGE_RWWEE_NUMOF);
assert((unsigned)page < FLASHPAGE_RWWEE_NUMOF);

flashpage_write_internal(page, data, FLASH_RWWEE);
_erase_page(page, _cmd_erase_row_rwwee);

if (data == NULL) {
return;
}

_write_row(page, data, _cmd_write_page_rwwee);
}
#endif
#endif /* FLASHPAGE_RWWEE_NUMOF */

0 comments on commit a1fb41f

Please sign in to comment.