drivers: flash: atmel SAM0 fix flash_write to handle smaller length.

Fix flash_sam0_write to handle byte lengths smaller than FLASH_PAGE_SIZE

Signed-off-by: Daniel Evans <photonthunder@gmail.com>
This commit is contained in:
Daniel Evans 2023-06-27 13:07:42 -06:00 committed by Carles Cufí
parent d3a73cdb0e
commit debc65fa63

View file

@ -5,6 +5,7 @@
*/
#define DT_DRV_COMPAT atmel_sam0_nvmctrl
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
#include <zephyr/logging/log.h>
@ -17,6 +18,9 @@ LOG_MODULE_REGISTER(flash_sam0);
#include <soc.h>
#include <string.h>
#define FLASH_WRITE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, write_block_size)
BUILD_ASSERT((FLASH_WRITE_BLK_SZ % sizeof(uint32_t)) == 0, "unsupported write-block-size");
/*
* Zephyr and the SAM0 series use different and conflicting names for
* the erasable units and programmable units:
@ -67,7 +71,7 @@ static const struct flash_parameters flash_sam0_parameters = {
#if CONFIG_SOC_FLASH_SAM0_EMULATE_BYTE_PAGES
.write_block_size = 1,
#else
.write_block_size = DT_PROP(DT_INST(0, soc_nv_flash), write_block_size),
.write_block_size = FLASH_WRITE_BLK_SZ,
#endif
.erase_value = 0xff,
};
@ -147,11 +151,18 @@ static int flash_sam0_check_status(off_t offset)
return 0;
}
/*
* Data to be written to the NVM block are first written to and stored
* in an internal buffer called the page buffer. The page buffer contains
* the same number of bytes as an NVM page. Writes to the page buffer must
* be 16 or 32 bits. 8-bit writes to the page buffer are not allowed and
* will cause a system exception
*/
static int flash_sam0_write_page(const struct device *dev, off_t offset,
const void *data)
const void *data, size_t len)
{
const uint32_t *src = data;
const uint32_t *end = src + FLASH_PAGE_SIZE / sizeof(*src);
const uint32_t *end = src + (len / sizeof(*src));
uint32_t *dst = FLASH_MEM(offset);
int err;
@ -178,7 +189,7 @@ static int flash_sam0_write_page(const struct device *dev, off_t offset,
return err;
}
if (memcmp(data, FLASH_MEM(offset), FLASH_PAGE_SIZE) != 0) {
if (memcmp(data, FLASH_MEM(offset), len) != 0) {
LOG_ERR("verify error at offset 0x%lx", (long)offset);
return -EIO;
}
@ -213,7 +224,7 @@ static int flash_sam0_commit(const struct device *dev, off_t base)
for (page = 0; page < PAGES_PER_ROW; page++) {
err = flash_sam0_write_page(
dev, base + page * FLASH_PAGE_SIZE,
&ctx->buf[page * FLASH_PAGE_SIZE]);
&ctx->buf[page * FLASH_PAGE_SIZE], ROW_SIZE);
if (err != 0) {
return err;
}
@ -280,19 +291,18 @@ static int flash_sam0_write(const struct device *dev, off_t offset,
{
const uint8_t *pdata = data;
int err;
size_t idx;
err = flash_sam0_valid_range(offset, len);
if (err != 0) {
return err;
}
if ((offset % FLASH_PAGE_SIZE) != 0) {
if ((offset % FLASH_WRITE_BLK_SZ) != 0) {
LOG_WRN("0x%lx: not on a write block boundary", (long)offset);
return -EINVAL;
}
if ((len % FLASH_PAGE_SIZE) != 0) {
if ((len % FLASH_WRITE_BLK_SZ) != 0) {
LOG_WRN("%zu: not a integer number of write blocks", len);
return -EINVAL;
}
@ -301,12 +311,20 @@ static int flash_sam0_write(const struct device *dev, off_t offset,
err = flash_sam0_write_protection(dev, false);
if (err == 0) {
for (idx = 0; idx < len; idx += FLASH_PAGE_SIZE) {
err = flash_sam0_write_page(dev, offset + idx,
&pdata[idx]);
/* Maximum size without crossing a page */
size_t eop_len = FLASH_PAGE_SIZE - (offset % FLASH_PAGE_SIZE);
size_t write_len = MIN(len, eop_len);
while (len > 0) {
err = flash_sam0_write_page(dev, offset, pdata, write_len);
if (err != 0) {
break;
}
offset += write_len;
pdata += write_len;
len -= write_len;
write_len = MIN(len, FLASH_PAGE_SIZE);
}
}
@ -454,6 +472,9 @@ static int flash_sam0_init(const struct device *dev)
#ifdef NVMCTRL_CTRLB_MANW
/* Require an explicit write command */
NVMCTRL->CTRLB.bit.MANW = 1;
#elif NVMCTRL_CTRLA_WMODE
/* Set manual write mode */
NVMCTRL->CTRLA.bit.WMODE = NVMCTRL_CTRLA_WMODE_MAN_Val;
#endif
return flash_sam0_write_protection(dev, false);