diff --git a/drivers/flash/flash_sam0.c b/drivers/flash/flash_sam0.c index b5fc5567cb85286..383fc937284c082 100644 --- a/drivers/flash/flash_sam0.c +++ b/drivers/flash/flash_sam0.c @@ -5,6 +5,9 @@ */ #define DT_DRV_COMPAT atmel_sam0_nvmctrl +#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) + +#define FLASH_WRITE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, write_block_size) #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL #include @@ -67,7 +70,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, }; @@ -148,10 +151,9 @@ static int flash_sam0_check_status(off_t offset) } 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); uint32_t *dst = FLASH_MEM(offset); int err; @@ -163,10 +165,11 @@ static int flash_sam0_write_page(const struct device *dev, off_t offset, flash_sam0_wait_ready(); /* Ensure writes happen 32 bits at a time. */ - for (; src != end; src++, dst++) { - *dst = UNALIGNED_GET((uint32_t *)src); + for (; len > 0; len -= sizeof(*src)) { + *dst++ = UNALIGNED_GET((uint32_t *)src++); } + #ifdef NVMCTRL_CTRLA_CMD_WP NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_WP | NVMCTRL_CTRLA_CMDEX_KEY; #else @@ -178,7 +181,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 +216,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; } @@ -275,24 +278,25 @@ static int flash_sam0_write(const struct device *dev, off_t offset, #else /* CONFIG_SOC_FLASH_SAM0_EMULATE_BYTE_PAGES */ +BUILD_ASSERT((FLASH_WRITE_BLK_SZ % sizeof(uint32_t)) == 0, "unsupported write-block-size"); + static int flash_sam0_write(const struct device *dev, off_t offset, const void *data, size_t len) { 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 +305,21 @@ 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]); + while (len > 0) { + size_t eop_len, write_len; + + /* Maximum size without crossing a page */ + eop_len = -(offset | ~(FLASH_PAGE_SIZE - 1)); + write_len = MIN(len, eop_len); + + err = flash_sam0_write_page(dev, offset, pdata, write_len); if (err != 0) { break; } + + offset += write_len; + pdata += write_len; + len -= write_len; } }