mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
Merge pull request #11422 from fjmolinas/pr_kinetis_w_flashpage
cpu/kinetis: add flashpage for W & K series
This commit is contained in:
commit
e45dc52c7b
@ -19,4 +19,11 @@ else
|
|||||||
FEATURES_PROVIDED += periph_mcg
|
FEATURES_PROVIDED += periph_mcg
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Since KINETIS_SERIES isn't available in Makefile.features filter according to
|
||||||
|
# CPU_MODEL
|
||||||
|
ifneq (,$(filter mkw% mk%,$(CPU_MODEL)))
|
||||||
|
FEATURES_PROVIDED += periph_flashpage
|
||||||
|
FEATURES_PROVIDED += periph_flashpage_raw
|
||||||
|
endif
|
||||||
|
|
||||||
include $(RIOTCPU)/cortexm_common/Makefile.features
|
include $(RIOTCPU)/cortexm_common/Makefile.features
|
||||||
|
@ -136,6 +136,24 @@
|
|||||||
#endif /* (KINETIS_SUBFAMILY == y) */
|
#endif /* (KINETIS_SUBFAMILY == y) */
|
||||||
#endif /* (KINETIS_FAMILY == x) */
|
#endif /* (KINETIS_FAMILY == x) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Flashpage configuration
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define FLASHPAGE_SIZE (4096U)
|
||||||
|
#define FLASHPAGE_NUMOF ((KINETIS_ROMSIZE * 1024) / FLASHPAGE_SIZE)
|
||||||
|
|
||||||
|
/* The minimum block size which can be written is 8B (Phrase). However, the
|
||||||
|
* erase block is always FLASHPAGE_SIZE.
|
||||||
|
*/
|
||||||
|
#define FLASHPAGE_RAW_PHRASE (8U)
|
||||||
|
#define FLASHPAGE_RAW_BLOCKSIZE FLASHPAGE_RAW_PHRASE
|
||||||
|
/* Writing should be always 8 bytes aligned */
|
||||||
|
#define FLASHPAGE_RAW_ALIGNMENT FLASHPAGE_RAW_PHRASE
|
||||||
|
/* Section erase and programming must be 16 bytes aligned */
|
||||||
|
#define FLASHPAGE_RAW_SECTION_ALIGNMENT (16U)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -77,6 +77,23 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* KINETIS_CORE_x */
|
#endif /* KINETIS_CORE_x */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Flashpage configuration
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define FLASHPAGE_SIZE (2048U)
|
||||||
|
#define FLASHPAGE_NUMOF ((KINETIS_ROMSIZE * 1024) / FLASHPAGE_SIZE)
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
/* Section erase and programming must be 8 bytes aligned */
|
||||||
|
#define FLASHPAGE_RAW_SECTION_ALIGNMENT (8U)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
226
cpu/kinetis/periph/flashpage.c
Normal file
226
cpu/kinetis/periph/flashpage.c
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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_kinetis
|
||||||
|
* @ingroup drivers_periph_flashpage
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Low-level flashpage driver implementation
|
||||||
|
*
|
||||||
|
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "assert.h"
|
||||||
|
|
||||||
|
#define ENABLE_DEBUG (0)
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include "periph/flashpage.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Flash controller commands
|
||||||
|
*/
|
||||||
|
#define FTFx_VERIFY_SECTION (0x01U) /* RD1SEC */
|
||||||
|
#define FTFx_ERASE_SECTOR (0x09U) /* ERSSCR */
|
||||||
|
#define FTFx_PROGRAM_LONGWORD (0x06U) /* PGM4 */
|
||||||
|
#define FTFx_PROGRAM_PHRASE (0x07U) /* PGM8 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Kinetis Flash Memory Module common registers
|
||||||
|
*/
|
||||||
|
#if defined(FTFA)
|
||||||
|
#define FTFx FTFA
|
||||||
|
#define FTFx_BASE FTFA_BASE
|
||||||
|
#define FTFx_FSTAT_CCIF_MASK FTFA_FSTAT_CCIF_MASK
|
||||||
|
#define FTFx_FSTAT_RDCOLERR_MASK FTFA_FSTAT_RDCOLERR_MASK
|
||||||
|
#define FTFx_FSTAT_ACCERR_MASK FTFA_FSTAT_ACCERR_MASK
|
||||||
|
#define FTFx_FSTAT_FPVIOL_MASK FTFA_FSTAT_FPVIOL_MASK
|
||||||
|
#define FTFx_FSTAT_MGSTAT0_MASK FTFA_FSTAT_MGSTAT0_MASK
|
||||||
|
#define FTFx_FSEC_SEC_MASK FTFA_FSEC_SEC_MASK
|
||||||
|
#define FTFx_FSEC_KEYEN_MASK FTFA_FSEC_KEYEN_MASK
|
||||||
|
#elif defined(FTFE)
|
||||||
|
#define FTFx FTFE
|
||||||
|
#define FTFx_BASE FTFE_BASE
|
||||||
|
#define FTFx_FSTAT_CCIF_MASK FTFE_FSTAT_CCIF_MASK
|
||||||
|
#define FTFx_FSTAT_RDCOLERR_MASK FTFE_FSTAT_RDCOLERR_MASK
|
||||||
|
#define FTFx_FSTAT_ACCERR_MASK FTFE_FSTAT_ACCERR_MASK
|
||||||
|
#define FTFx_FSTAT_FPVIOL_MASK FTFE_FSTAT_FPVIOL_MASK
|
||||||
|
#define FTFx_FSTAT_MGSTAT0_MASK FTFE_FSTAT_MGSTAT0_MASK
|
||||||
|
#define FTFx_FSEC_SEC_MASK FTFE_FSEC_SEC_MASK
|
||||||
|
#define FTFx_FSEC_KEYEN_MASK FTFE_FSEC_KEYEN_MASK
|
||||||
|
#elif defined(FTFL)
|
||||||
|
#define FTFx FTFL
|
||||||
|
#define FTFx_BASE FTFL_BASE
|
||||||
|
#define FTFx_FSTAT_CCIF_MASK FTFL_FSTAT_CCIF_MASK
|
||||||
|
#define FTFx_FSTAT_RDCOLERR_MASK FTFL_FSTAT_RDCOLERR_MASK
|
||||||
|
#define FTFx_FSTAT_ACCERR_MASK FTFL_FSTAT_ACCERR_MASK
|
||||||
|
#define FTFx_FSTAT_FPVIOL_MASK FTFL_FSTAT_FPVIOL_MASK
|
||||||
|
#define FTFx_FSTAT_MGSTAT0_MASK FTFL_FSTAT_MGSTAT0_MASK
|
||||||
|
#define FTFx_FSEC_SEC_MASK FTFL_FSEC_SEC_MASK
|
||||||
|
#define FTFx_FSEC_KEYEN_MASK FTFL_FSEC_KEYEN_MASK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BYTES_JOIN_TO_WORD_1_3(x, y) ((((uint32_t)(x) & 0xFFU) << 24) | \
|
||||||
|
((uint32_t)(y) & 0xFFFFFFU))
|
||||||
|
#define BYTES_JOIN_TO_WORD_2_1_1(x, y, z) \
|
||||||
|
((((uint32_t)(x) & 0xFFFFU) << 16) | (((uint32_t)(y) & 0xFFU) << 8) | \
|
||||||
|
((uint32_t)(z) & 0xFFU))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Flash errors
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
NO_ERROR = 0,
|
||||||
|
PROTECTION_ERROR = -1,
|
||||||
|
ALIGN_ERROR = -2,
|
||||||
|
ACCESS_ERROR = -3,
|
||||||
|
COLLISION_ERROR = -4,
|
||||||
|
RUNTIME_ERROR = -5,
|
||||||
|
ERASE_ERROR = -6,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Pointer to FCCOB register */
|
||||||
|
volatile uint32_t *const FCCOBx = (volatile uint32_t *)&FTFx->FCCOB3;
|
||||||
|
|
||||||
|
/* Erasing/Programming flash is not allowed inside the same flash block
|
||||||
|
where the program is being read, for _run_command to be executed in
|
||||||
|
RAM to avoid this, more details in:
|
||||||
|
https://www.nxp.com/docs/en/application-note/AN4695.pdf */
|
||||||
|
__attribute__ ((section (".ramfunc")))
|
||||||
|
static void _run_command(void)
|
||||||
|
{
|
||||||
|
/* Clear previous errors */
|
||||||
|
FTFx->FSTAT = FTFx_FSTAT_FPVIOL_MASK | FTFx_FSTAT_ACCERR_MASK |
|
||||||
|
FTFx_FSTAT_RDCOLERR_MASK;
|
||||||
|
/* Start Operation */
|
||||||
|
FTFx->FSTAT = FTFx_FSTAT_CCIF_MASK;
|
||||||
|
/* Wait for CCIF to be set (operation finished) */
|
||||||
|
while (!(FTFx->FSTAT & FTFx_FSTAT_CCIF_MASK)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _check_errors(void)
|
||||||
|
{
|
||||||
|
if (FTFx->FSTAT & FTFx_FSTAT_FPVIOL_MASK) {
|
||||||
|
DEBUG("[flash_write]: flash protection violation\n");
|
||||||
|
return PROTECTION_ERROR;
|
||||||
|
}
|
||||||
|
if (FTFx->FSTAT & FTFx_FSTAT_ACCERR_MASK) {
|
||||||
|
DEBUG("[flash_write]: flash access error\n");
|
||||||
|
return ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
if (FTFx->FSTAT & FTFx_FSTAT_RDCOLERR_MASK) {
|
||||||
|
DEBUG("[flash_write]: collision error\n");
|
||||||
|
return COLLISION_ERROR;
|
||||||
|
}
|
||||||
|
if (FTFx->FSTAT & FTFx_FSTAT_MGSTAT0_MASK) {
|
||||||
|
DEBUG("[flash_write]: runtime error\\n");
|
||||||
|
return RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
DEBUG("[flash_write]: no error reported\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _verify_erased(uint32_t address, size_t len)
|
||||||
|
{
|
||||||
|
/* Ensures verifies are aligned */
|
||||||
|
assert(!(address % FLASHPAGE_RAW_SECTION_ALIGNMENT));
|
||||||
|
|
||||||
|
/* We verify FLASHPAGE_RAW_SECTION_ALIGNMENT Bytes at a time, we
|
||||||
|
round up before adjusting to FLASHPAGE_RAW_SECTION_ALIGNMENT */
|
||||||
|
uint32_t num = (len + (FLASHPAGE_RAW_SECTION_ALIGNMENT - 1)) /
|
||||||
|
FLASHPAGE_RAW_SECTION_ALIGNMENT;
|
||||||
|
|
||||||
|
/* Setup command and run */
|
||||||
|
FCCOBx[0] = BYTES_JOIN_TO_WORD_1_3(FTFx_VERIFY_SECTION, address);
|
||||||
|
FCCOBx[1] = BYTES_JOIN_TO_WORD_2_1_1(num, 0x00, 0xFF);
|
||||||
|
_run_command();
|
||||||
|
|
||||||
|
return _check_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _sector_erase(uint32_t address)
|
||||||
|
{
|
||||||
|
/* Ensures erases are aligned */
|
||||||
|
assert(!(address % FLASHPAGE_RAW_SECTION_ALIGNMENT));
|
||||||
|
|
||||||
|
/* Setup command and run */
|
||||||
|
FCCOBx[0] = BYTES_JOIN_TO_WORD_1_3(FTFx_ERASE_SECTOR, address);
|
||||||
|
_run_command();
|
||||||
|
|
||||||
|
return _check_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _flash_write(uint32_t address, uint32_t *data)
|
||||||
|
{
|
||||||
|
/* Setup command and run */
|
||||||
|
FCCOBx[1] = *data++;
|
||||||
|
if (FLASHPAGE_RAW_BLOCKSIZE == 8U) {
|
||||||
|
FCCOBx[2] = *data;
|
||||||
|
FCCOBx[0] = BYTES_JOIN_TO_WORD_1_3(FTFx_PROGRAM_PHRASE, address);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FCCOBx[0] = BYTES_JOIN_TO_WORD_1_3(FTFx_PROGRAM_LONGWORD, address);
|
||||||
|
}
|
||||||
|
_run_command();
|
||||||
|
|
||||||
|
return _check_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) + 1);
|
||||||
|
|
||||||
|
#ifdef FLASHPAGE_RAW_PHRASE
|
||||||
|
const uint64_t *data_addr = data;
|
||||||
|
uint64_t *dst = target_addr;
|
||||||
|
#else
|
||||||
|
const uint32_t *data_addr = data;
|
||||||
|
uint32_t *dst = target_addr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Verify sector was previously erased */
|
||||||
|
if (_verify_erased((uint32_t)dst, len) != NO_ERROR) {
|
||||||
|
DEBUG("[flash-write]: ERROR sector not in erased state\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write to flash FLASHPAGE_RAW_BLOCKSIZE bytes at a time */
|
||||||
|
for (size_t i = 0; i < (len / FLASHPAGE_RAW_BLOCKSIZE); i++) {
|
||||||
|
_flash_write((uint32_t) dst++, (uint32_t *) data_addr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void flashpage_write(int page, const void *data)
|
||||||
|
{
|
||||||
|
assert(page < (int)FLASHPAGE_NUMOF);
|
||||||
|
|
||||||
|
uint32_t *page_addr = flashpage_addr(page);
|
||||||
|
|
||||||
|
/* ERASE sector */
|
||||||
|
_sector_erase((uint32_t)page_addr);
|
||||||
|
|
||||||
|
/* WRITE sector */
|
||||||
|
if (data != NULL) {
|
||||||
|
flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user