diff --git a/cpu/cc2538/Kconfig b/cpu/cc2538/Kconfig index 2386cd68d8..5b1491e3ff 100644 --- a/cpu/cc2538/Kconfig +++ b/cpu/cc2538/Kconfig @@ -10,6 +10,8 @@ config CPU_FAM_CC2538 select CPU_CORE_CORTEX_M3 select HAS_CPU_CC2538 select HAS_PERIPH_CPUID + select HAS_PERIPH_FLASHPAGE + select HAS_PERIPH_FLASHPAGE_RAW select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_HWRNG diff --git a/cpu/cc2538/Makefile.features b/cpu/cc2538/Makefile.features index c4e1935607..2b6665241f 100644 --- a/cpu/cc2538/Makefile.features +++ b/cpu/cc2538/Makefile.features @@ -2,6 +2,8 @@ CPU_CORE = cortex-m3 CPU_FAM = cc2538 FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_flashpage +FEATURES_PROVIDED += periph_flashpage_raw FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_uart_modecfg diff --git a/cpu/cc2538/Makefile.include b/cpu/cc2538/Makefile.include index ea1a0f256b..5c22f7a64d 100644 --- a/cpu/cc2538/Makefile.include +++ b/cpu/cc2538/Makefile.include @@ -1 +1,26 @@ + +# Set ROM and RAM lengths according to CPU model +ifneq (,$(filter cc2538nf11,$(CPU_MODEL))) + ROM_LEN ?= 128K + RAM_LEN ?= 16K +endif +ifneq (,$(filter cc2538nf23,$(CPU_MODEL))) + ROM_LEN ?= 256K + RAM_LEN ?= 32K +endif +ifneq (,$(filter cc2538nf53 cc2538sf53,$(CPU_MODEL))) + ROM_LEN ?= 512K + RAM_LEN ?= 32K +endif + +ROM_START_ADDR ?= 0x00200000 +RAM_START_ADDR ?= 0x20000000 + +KB := 1024 +ROM_LEN_K := $(shell echo $(ROM_LEN) | sed 's/K//') +FLASHSIZE := $(shell echo $$(( $(ROM_LEN_K) * $(KB) )) ) + +# Set CFLAGS +CFLAGS += -DCC2538_FLASHSIZE=$(FLASHSIZE)U + include $(RIOTMAKE)/arch/cortexm.inc.mk diff --git a/cpu/cc2538/include/cpu_conf.h b/cpu/cc2538/include/cpu_conf.h index 74a927ac42..48af9a8b97 100644 --- a/cpu/cc2538/include/cpu_conf.h +++ b/cpu/cc2538/include/cpu_conf.h @@ -43,6 +43,34 @@ extern "C" { #define CPU_HAS_BITBAND (1) /** @} */ + +/** + * @brief Flash page configuration + * @{ + */ +#define FLASHPAGE_SIZE (2048U) +/* Last page holds the Customer Configuration Area (CCA), this holds + the Bootloader Backdoor Configuration, Application Entry Point, + flashpage lock bits. For safety disable writing to that page by + default */ +#ifndef FLASHPAGE_CC2538_USE_CCA_PAGE +#define FLASHPAGE_CC2538_USE_CCA_PAGE (0) +#endif +#if FLASHPAGE_CC2538_USE_CCA_PAGE +#define FLASHPAGE_NUMOF ((CC2538_FLASHSIZE / FLASHPAGE_SIZE)) +#else +#define FLASHPAGE_NUMOF ((CC2538_FLASHSIZE / FLASHPAGE_SIZE) -1) +#endif +#define FLASH_ERASE_STATE (0x1) + +/* The minimum block size which can be written is 4B. However, the erase + * block is always FLASHPAGE_SIZE. + */ +#define FLASHPAGE_RAW_BLOCKSIZE (4U) +/* Writing should be always 4 bytes aligned */ +#define FLASHPAGE_RAW_ALIGNMENT (4U) +/** @} */ + /** * @name OpenWSN timing constants * diff --git a/cpu/cc2538/periph/flashpage.c b/cpu/cc2538/periph/flashpage.c new file mode 100644 index 0000000000..2278e9a5b0 --- /dev/null +++ b/cpu/cc2538/periph/flashpage.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_cc2538 + * @ingroup drivers_periph_flashpage + * @{ + * + * @file + * @brief Implementation of the peripheral flashpage interface + * + * @author Francisco Molina + * + * @} + */ + +#include + +#include "cpu.h" +#include "irq.h" +#include "periph/flashpage.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define FLASH_CTRL_FCTL_BUSY 0x00000080 +#define FLASH_CTRL_FCTL_FULL 0x00000040 +#define FLASH_CTRL_FCTL_WRITE 0x00000002 +#define FLASH_CTRL_FCTL_ERASE 0x00000001 +#define FLASH_CTRL_FCTL_CM_MASK 0x0000000C + +__attribute__ ((section (".ramfunc"))) +static inline void _erase(uint32_t *page_addr) +{ + /* wait for ongoing operations*/ + DEBUG("[flashpage] erase: wait for ongoing operations\n"); + while (FLASH_CTRL_FCTL & FLASH_CTRL_FCTL_BUSY) {} + + /* disable interrupts */ + int state = irq_disable(); + + /* Initialize Flash control register without changing the cache mode.*/ + FLASH_CTRL_FCTL &= FLASH_CTRL_FCTL_CM_MASK; + + /* set page to erase*/ + FLASH_CTRL_FADDR = (uint32_t)page_addr; + + /* starts the write-sequence state machine */ + DEBUG("[flashpage] erase: start erase sequence at %p\n", page_addr); + FLASH_CTRL_FCTL |= FLASH_CTRL_FCTL_ERASE; + + /* wait erase to complete */ + while (FLASH_CTRL_FCTL & FLASH_CTRL_FCTL_BUSY) {} + + /* re-enable interrupts */ + irq_restore(state); +} + +__attribute__ ((section (".ramfunc"))) +void flashpage_write_raw(void *target_addr, const void *data, size_t len) +{ + /* assert multiples of FLASHPAGE_RAW_BLOCKSIZE are written and no less of + that length. */ + assert(!(len % FLASHPAGE_RAW_BLOCKSIZE)); + + /* ensure writes are aligned */ + 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))); + + uint32_t *dst = target_addr; + const uint32_t *data_addr = data; + + /* disable interrupts and unlock flash */ + int state = irq_disable(); + + DEBUG("[flashpage_raw] write: to %p \n", dst); + /* Initialize Flash control register without changing the cache mode.*/ + FLASH_CTRL_FCTL &= FLASH_CTRL_FCTL_CM_MASK; + /* set start address*/ + FLASH_CTRL_FADDR = (uint32_t) dst; + /* starts the write-sequence state machine */ + DEBUG("[flashpage_raw] write: now writing the data\n"); + FLASH_CTRL_FCTL |= FLASH_CTRL_FCTL_WRITE; + for (unsigned i = 0; i < (len / FLASHPAGE_RAW_BLOCKSIZE); i++) { + FLASH_CTRL_FWDATA = (uint32_t) *(data_addr++); + /* wait for flash operation to complete */ + while (FLASH_CTRL_FCTL & FLASH_CTRL_FCTL_FULL) {} + } + /* re-enable interrupts */ + irq_restore(state); +} + +void flashpage_write(int page, const void *data) +{ + assert((unsigned) page < FLASHPAGE_NUMOF); + + uint32_t *page_addr = (uint32_t *)flashpage_addr(page); + + /* erase page */ + _erase(page_addr); + + /* write page */ + if (data != NULL) { + flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE); + } +}