From 32bbba2fc50bd604e55289bcaaf4609ada0e0392 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sat, 9 Nov 2019 23:56:29 +0100 Subject: [PATCH] cpu/lpc2387: add support for backup RAM lpc23xx has 2k of battery RAM that is retained in Deep Power Down mode. To not overwrite that data it must only be initialized on Power On Reset. However, RSIR looks the same when waking up from Deep Power Down as it does on the power-on case. So use 4 bytes of the backup RAM to keep a signature that is only valid if memory was retained (no power-on Reset). A small change to the linker script is required so two sections can be placed into flash. --- cpu/arm7_common/arm7_init.c | 27 ++++++++++++++++++++++++++ cpu/lpc2387/Makefile.features | 3 ++- cpu/lpc2387/cpu.c | 33 ++++++++++++++++++++++++++++++++ cpu/lpc2387/include/cpu.h | 10 ++++++++++ cpu/lpc2387/ldscripts/lpc2387.ld | 23 ++++++++++++++++------ 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/cpu/arm7_common/arm7_init.c b/cpu/arm7_common/arm7_init.c index 0dfdf92733..47c0d79a9b 100644 --- a/cpu/arm7_common/arm7_init.c +++ b/cpu/arm7_common/arm7_init.c @@ -24,6 +24,7 @@ #include "puf_sram.h" #endif +#include "cpu.h" #include "log.h" static inline void @@ -35,6 +36,15 @@ _init_data(void) extern unsigned int __bss_start; extern unsigned int __bss_end; +/* Support for LPRAM. */ +#ifdef CPU_HAS_BACKUP_RAM + extern unsigned int _sbackup_data_load[]; + extern unsigned int _sbackup_data[]; + extern unsigned int _ebackup_data[]; + extern unsigned int _sbackup_bss[]; + extern unsigned int _ebackup_bss[]; +#endif /* CPU_HAS_BACKUP_RAM */ + register unsigned int *p1; register unsigned int *p2; register unsigned int *p3; @@ -57,6 +67,23 @@ _init_data(void) while (p1 < p2) { *p1++ = 0; } + +#ifdef CPU_HAS_BACKUP_RAM + if (cpu_woke_from_backup()) { + + /* load low-power data section. */ + for (p1 = _sbackup_data, p2 = _sbackup_data_load; + p1 < _ebackup_data; + p1++, p2++) { + *p1 = *p2; + } + + /* zero-out low-power bss. */ + for (p1 = _sbackup_bss; p1 < _ebackup_bss; p1++) { + *p1 = 0; + } + } +#endif /* CPU_HAS_BACKUP_RAM */ } void bootloader(void) diff --git a/cpu/lpc2387/Makefile.features b/cpu/lpc2387/Makefile.features index 58e25954b0..296fd508bc 100644 --- a/cpu/lpc2387/Makefile.features +++ b/cpu/lpc2387/Makefile.features @@ -1,5 +1,6 @@ # Put defined MCU peripherals here (in alphabetical order) -FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += backup_ram FEATURES_PROVIDED += periph_dac +FEATURES_PROVIDED += periph_gpio periph_gpio_irq -include $(RIOTCPU)/arm7_common/Makefile.features diff --git a/cpu/lpc2387/cpu.c b/cpu/lpc2387/cpu.c index d48e3f7c41..1f5cc7a3fc 100644 --- a/cpu/lpc2387/cpu.c +++ b/cpu/lpc2387/cpu.c @@ -136,4 +136,37 @@ void cpu_init(void) periph_init(); } +/* RSIR will only have POR bit set even when waking up from Deep Power Down + * Use signature in battery RAM to discriminate between Deep Power Down and POR + */ +bool cpu_woke_from_backup(void) +{ + static char signature[] __attribute__((section(".backup.data"))) = { + 'R', 'I', 'O', 'T' + }; + + /* external reset */ + if (RSIR & RSIR_EXTR) { + return false; + } + + if (signature[0] != 'R') { + return false; + } + + if (signature[1] != 'I') { + return false; + } + + if (signature[2] != 'O') { + return false; + } + + if (signature[3] != 'T') { + return false; + } + + return true; +} + /** @} */ diff --git a/cpu/lpc2387/include/cpu.h b/cpu/lpc2387/include/cpu.h index a56181d573..ecc188e823 100644 --- a/cpu/lpc2387/include/cpu.h +++ b/cpu/lpc2387/include/cpu.h @@ -57,6 +57,16 @@ static inline void cpu_print_last_instruction(void) printf("%p\n", (void*) lr_ptr); } +/** + * @brief Returns true if the CPU woke from Deep Sleep + */ +bool cpu_woke_from_backup(void); + +/** + * @brief The CPU has RAM that is retained in the deepest sleep mode. + */ +#define CPU_HAS_BACKUP_RAM (1) + #ifdef __cplusplus } #endif diff --git a/cpu/lpc2387/ldscripts/lpc2387.ld b/cpu/lpc2387/ldscripts/lpc2387.ld index 2cd0b15fe1..072e17774a 100644 --- a/cpu/lpc2387/ldscripts/lpc2387.ld +++ b/cpu/lpc2387/ldscripts/lpc2387.ld @@ -171,7 +171,7 @@ SECTIONS * collect all initialized .data sections that go into RAM * initial values get placed at the end of .text in flash */ - .data : AT (_etext) + .data : { . = ALIGN(4); /* ensure data is aligned so relocation can use 4-byte operations */ _data = .; /* create a global symbol marking the start of the .data section */ @@ -196,7 +196,7 @@ SECTIONS KEEP(*(SORT(.fini_array.*))) KEEP(*(.fini_array)) PROVIDE_HIDDEN (__fini_array_end = .); - } >ram /* put all the above into RAM (but load the LMA copy into FLASH) */ + } >ram AT > flash /* put all the above into RAM (but load the LMA copy into FLASH) */ . = ALIGN(4); /* ensure data is aligned so relocation can use 4-byte operations */ _edata = .; /* define a global symbol marking the end of the .data section */ @@ -287,9 +287,20 @@ SECTIONS } > ram_usb __heap_size = SIZEOF(.heap3); - - .batteryram (NOLOAD) : /* battery ram stays on during powerdown but needs to be handled specially */ - { - *(.batteryram) + .backup.bss (NOLOAD) : ALIGN(4) { + _sbackup_bss = .; + *(.backup.bss) + _ebackup_bss = .; + /* Round size so that we can use 4 byte copy in init */ + . = ALIGN(4); } > ram_battery + + _sbackup_data_load = LOADADDR(.backup.data); + .backup.data : ALIGN(4) { + _sbackup_data = .; + *(.backup.data) + _ebackup_data = .; + /* Round size so that we can use 4 byte copy in init */ + . = ALIGN(4); + } > ram_battery AT> flash }