1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/gd32v/periph/flashpage.c
2023-01-27 06:46:51 +01:00

150 lines
4.1 KiB
C

/*
* Copyright (C) 2020 Inria
* Copyright (C) 2020 Koen Zandberg <koen@bergzand.net>
*
* 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_gd32v
* @{
*
* @file
* @brief Low-level flash lock/unlock implementation
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @author Oleg Artamonov <oleg@unwds.com>
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#include "cpu.h"
#include "periph_cpu.h"
#include "periph/flashpage.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define FLASHPAGE_DIV (2U)
#define FLASH_KEY1 ((uint32_t)0x45670123)
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)
static void _unlock_flash(void)
{
if (FMC->CTL0 & FMC_CTL0_LK_Msk) {
DEBUG("[flash-common] unlocking the flash module\n");
FMC->KEY0 = FLASH_KEY1;
FMC->KEY0 = FLASH_KEY2;
}
}
static void _lock_flash(void)
{
if (!(FMC->CTL0 & FMC_CTL0_LK_Msk)) {
DEBUG("[flash-common] locking the flash module\n");
FMC->CTL0 |= FMC_CTL0_LK_Msk;
}
}
static void _wait_for_pending_operations(void)
{
if (FMC->STAT0 & FMC_STAT0_BUSY_Msk) {
DEBUG("[flash-common] waiting for any pending operation to finish\n");
while (FMC->STAT0 & FMC_STAT0_BUSY_Msk) {}
}
/* Clear 'end of operation' bit in status register */
FMC->STAT0 |= FMC_STAT0_ENDF_Msk;
}
void flashpage_erase(unsigned page)
{
uint32_t irc8_state = (RCU->CTL & RCU_CTL_IRC8MEN_Msk);
uint16_t *dst = flashpage_addr(page);
/* the internal RC oscillator (HSI) must be enabled */
gd32v_enable_irc8();
/* unlock the flash module */
_unlock_flash();
/* make sure no flash operation is ongoing */
_wait_for_pending_operations();
/* set page erase bit and program page address */
DEBUG("[flashpage] erase: setting the erase bit\n");
FMC->CTL0 |= FMC_CTL0_PER_Msk;
DEBUG("address to erase: %p\n", dst);
DEBUG("[flashpage] erase: setting the page address\n");
FMC->ADDR0 = (uint32_t)dst;
/* trigger the page erase and wait for it to be finished */
DEBUG("[flashpage] erase: trigger the page erase\n");
FMC->CTL0 |= FMC_CTL0_START_Msk;
/* wait as long as device is busy */
_wait_for_pending_operations();
/* reset PER bit */
DEBUG("[flashpage] erase: resetting the page erase bit\n");
FMC->CTL0 &= ~(FMC_CTL0_PER_Msk);
/* lock the flash module again */
_lock_flash();
/* restore the HSI state */
if (!irc8_state) {
gd32v_disable_irc8();
}
}
void flashpage_write(void *target_addr, const void *data, size_t len)
{
/* assert multiples of FLASHPAGE_WRITE_BLOCK_SIZE are written and no less of
that length. */
assert(!(len % FLASHPAGE_WRITE_BLOCK_SIZE));
/* ensure writes are aligned */
assert(!(((unsigned)target_addr % FLASHPAGE_WRITE_BLOCK_ALIGNMENT) ||
((unsigned)data % FLASHPAGE_WRITE_BLOCK_ALIGNMENT)));
/* ensure the length doesn't exceed the actual flash size */
assert(((unsigned)target_addr + len) <
(CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)) + 1);
uint16_t *dst = (uint16_t *)target_addr;
const uint16_t *data_addr = data;
uint32_t irc8_state = (RCU->CTL & RCU_CTL_IRC8MEN_Msk);
/* the internal RC oscillator (HSI) must be enabled */
gd32v_enable_irc8();
/* unlock the flash module */
_unlock_flash();
/* make sure no flash operation is ongoing */
_wait_for_pending_operations();
/* set PG bit and program page to flash */
FMC->CTL0 |= FMC_CTL0_PG_Msk;
for (size_t i = 0; i < (len / FLASHPAGE_DIV); i++) {
DEBUG("[flashpage_write] writing %c to %p\n", (char)data_addr[i], dst);
*dst++ = data_addr[i];
/* wait as long as device is busy */
_wait_for_pending_operations();
}
FMC->CTL0 &= ~(FMC_CTL0_PG_Msk);
DEBUG("[flashpage_write] write: done writing data\n");
/* lock the flash module again */
_lock_flash();
/* restore the HSI state */
if (!irc8_state) {
gd32v_disable_irc8();
}
}