Commit 4e324723 authored by Alexander Potashev's avatar Alexander Potashev

RT74765. twr-k60n512: implement `envm_write()` for the `cptf` command

Write the necessary data into flash by flash sectors (2 Kbytes).
Use the "Program Section" command to write a complete flash sector.

If only a part of the sector should be updated, firstly load the
existing data that should not be changed into the write buffer (flash
programming acceleration RAM), and then write from this buffer into the
flash sector.
This technique makes it possible to update even single bytes without
damaging other data in the same flash sector.

Before writing at the flash address 0x40C, a check is performed to make
sure the MCU will not switch to the secure state.

All functions that may be called from `envm_write()` are put into the
`.ramcode` section, because the internal flash is not a safe place
when self-upgrade is in progress.

Like in the FNET project, we disable Single Entry Buffer and Data Cache
via FMC registers. This must be a workaround for an errata.
parent aa4e8b78
......@@ -20,6 +20,7 @@
*/
#include <common.h>
#include <errno.h>
#include "envm.h"
......@@ -81,6 +82,8 @@ struct kinetis_flash_conf {
(3 << KINETIS_FLASH_CONF_FSEC_FSLACC_BITS)
/* Flash Security */
#define KINETIS_FLASH_CONF_FSEC_SEC_BITS 0
#define KINETIS_FLASH_CONF_FSEC_SEC_MSK \
(3 << KINETIS_FLASH_CONF_FSEC_SEC_BITS)
#define KINETIS_FLASH_CONF_FSEC_SEC_UNSECURE \
(2 << KINETIS_FLASH_CONF_FSEC_SEC_BITS)
......@@ -137,15 +140,347 @@ __attribute__((section(".kinetis_flash_conf"), used)) = {
};
/*
* Initialize internal Flash interface
* Address of the flash programming acceleration RAM
*/
void envm_init(void)
#define FLASH_PROG_ACCEL_BASE 0x14000000
#define FLASH_PROG_ACCEL_PTR ((volatile u8 *)FLASH_PROG_ACCEL_BASE)
/*
* Size in bytes of the flash programming acceleration RAM
*/
#define FLASH_PROG_ACCEL_SIZE (4 * 1024)
/*
* Size of the bottom half of the flash programming acceleration RAM
* that may be used for flash programming.
*/
#define FLASH_PROG_ACCEL_HALF (FLASH_PROG_ACCEL_SIZE / 2)
/*
* Size of a sector in the MCU internal flash is 2 Kbytes
* on the program flash only MCUs.
*/
#define FLASH_SECTOR_SIZE (2 * 1024)
/*
* Offset of the byte in flash that loads into the FSEC register on reset
*/
#define KINETIS_FLASH_FSEC_OFFSET 0x40C
/*
* Pointer to the beginning of flash
*/
#define KINETIS_NVM_PTR ((u8 *)CONFIG_MEM_NVM_BASE)
/*
* Check that the data for a sector fits the flash programming acceleration RAM
*
* If on some other MCU the flash sector size is larger than
* `FLASH_PROG_ACCEL_HALF`, the code in `kinetis_flash_program()` must be
* fixed accordingly.
*/
#if FLASH_SECTOR_SIZE > FLASH_PROG_ACCEL_HALF
#error Flash sector size exceeds the maximum flash programming block size
#endif
/*
* FTFL commands
*/
#define FTFL_CMD_ERASE_SECTOR 0x09
#define FTFL_CMD_PROGRAM_SECTION 0x0B
/*
* Flash Memory Controller (FMC) register map
*/
struct kinetis_fmc_regs {
u32 pfapr; /* Flash Access Protection Register */
u32 pfb0cr; /* Flash Bank 0 Control Register */
u32 pfb1cr; /* Flash Bank 1 Control Register */
};
/*
* FMC registers base
*/
#define KINETIS_FMC_BASE (KINETIS_AIPS0PERIPH_BASE + 0x0001F000)
#define KINETIS_FMC ((volatile struct kinetis_fmc_regs *) \
KINETIS_FMC_BASE)
/*
* Flash Bank Control Registers (FMC_PFB0CR, FMC_PFB1CR)
*/
/* Bank Data Cache Enable */
#define KINETIS_FMC_PFBCR_BDCE_MSK (1 << 4)
/* Bank Single Entry Buffer Enable */
#define KINETIS_FMC_PFBCR_BSEBE_MSK (1 << 0)
/*
* Flash Memory Module (FTFL) register map
*/
struct kinetis_ftfl_regs {
u8 fstat; /* Flash Status Register */
u8 fcnfg; /* Flash Configuration Register */
u8 fsec; /* Flash Security Register */
u8 fopt; /* Flash Option Register */
/* Flash Common Command Object Registers (3:0) */
u8 fccob3;
u8 fccob2;
u8 fccob1;
u8 fccob0;
/* Flash Common Command Object Registers (7:4) */
u8 fccob7;
u8 fccob6;
u8 fccob5;
u8 fccob4;
/* Flash Common Command Object Registers (0xB:8) */
u8 fccobB;
u8 fccobA;
u8 fccob9;
u8 fccob8;
};
/*
* FTFL registers base
*/
#define KINETIS_FTFL_BASE (KINETIS_AIPS0PERIPH_BASE + 0x00020000)
#define KINETIS_FTFL ((volatile struct kinetis_ftfl_regs *) \
KINETIS_FTFL_BASE)
/*
* Flash Status Register (FTFL_FSTAT)
*/
/* Command Complete Interrupt Flag */
#define KINETIS_FTFL_FSTAT_CCIF_MSK (1 << 7)
/* FTFL Read Collision Error Flag */
#define KINETIS_FTFL_FSTAT_RDCOLERR_MSK (1 << 6)
/* Flash Access Error Flag */
#define KINETIS_FTFL_FSTAT_ACCERR_MSK (1 << 5)
/* Flash Protection Violation Flag */
#define KINETIS_FTFL_FSTAT_FPVIOL_MSK (1 << 4)
/*
* Execute an FTFL command
*
* `flash_addr` goes to FCCOB[3:1], converted to big-endian.
* `data0` goes FCCOB[7:4], converted to big-endian.
* `data1` goes FCCOB[0xB:8], converted to big-endian.
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
kinetis_ftfl_command(u8 command, u32 flash_addr, u32 data0, u32 data1)
{
int rv;
/*
* TBD
* This problem exists only in first released product version (mask 0M33Z)
*
* Single entry buffer disable; Data Cache disable.
*/
KINETIS_FMC->pfb0cr &=
~(KINETIS_FMC_PFBCR_BDCE_MSK | KINETIS_FMC_PFBCR_BSEBE_MSK);
KINETIS_FMC->pfb1cr &=
~(KINETIS_FMC_PFBCR_BDCE_MSK | KINETIS_FMC_PFBCR_BSEBE_MSK);
return;
/*
* Wait until the previous command is finished
*/
while (!(KINETIS_FTFL->fstat & KINETIS_FTFL_FSTAT_CCIF_MSK));
/*
* Clear the error bits before starting the next command
*/
KINETIS_FTFL->fstat = (KINETIS_FTFL_FSTAT_ACCERR_MSK | KINETIS_FTFL_FSTAT_FPVIOL_MSK);
/* Write the command code to FCCOB[0] */
KINETIS_FTFL->fccob0 = command;
/* Write the flash address to FCCOB[3:1] */
KINETIS_FTFL->fccob1 = (u8)(flash_addr >> 16); /* flash_addr[23:16] */
KINETIS_FTFL->fccob2 = (u8)(flash_addr >> 8); /* flash_addr[15:8] */
KINETIS_FTFL->fccob3 = (u8)flash_addr; /* flash_addr[7:0] */
/* Write the data word 0 to FCCOB[7:4] */
KINETIS_FTFL->fccob4 = (u8)(data0 >> 24); /* data0[31:24] */
KINETIS_FTFL->fccob5 = (u8)(data0 >> 16); /* data0[23:16] */
KINETIS_FTFL->fccob6 = (u8)(data0 >> 8); /* data0[15:8] */
KINETIS_FTFL->fccob7 = (u8)data0; /* data0[7:0] */
/* Write the data word 1 to FCCOB[7:4] */
KINETIS_FTFL->fccob8 = (u8)(data1 >> 24); /* data1[31:24] */
KINETIS_FTFL->fccob9 = (u8)(data1 >> 16); /* data1[23:16] */
KINETIS_FTFL->fccobA = (u8)(data1 >> 8); /* data1[15:8] */
KINETIS_FTFL->fccobB = (u8)data1; /* data1[7:0] */
/*
* Start command execution
*/
KINETIS_FTFL->fstat = KINETIS_FTFL_FSTAT_CCIF_MSK;
while (!(KINETIS_FTFL->fstat & KINETIS_FTFL_FSTAT_CCIF_MSK));
rv = 0;
if (KINETIS_FTFL->fstat &
(KINETIS_FTFL_FSTAT_ACCERR_MSK | KINETIS_FTFL_FSTAT_FPVIOL_MSK |
KINETIS_FTFL_FSTAT_RDCOLERR_MSK))
rv = -EIO;
return rv;
}
/*
* Run the "Erase Sector" command
*
* `sect` is the sector number.
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
kinetis_ftfl_erase_sector(u32 sect)
{
return kinetis_ftfl_command(FTFL_CMD_ERASE_SECTOR,
sect * FLASH_SECTOR_SIZE, 0, 0);
}
/*
* Run the "Program Section" command
*
* `size` is the length of the data to program.
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
kinetis_ftfl_program_section(u32 dest_addr, u32 size)
{
/*
* ">> 3": We divide `size` by 8 to convert the size in bytes into
* the size in phrases (64 bits of data).
*
* "<< 16": Then we move the two bytes inside the `data0` word,
* so that they go into the FCCOB[5:4] registers.
*/
return kinetis_ftfl_command(FTFL_CMD_PROGRAM_SECTION,
dest_addr, size << (16 - 3), 0);
}
/*
* A copy of the `memcpy()` function in the `.ramcode` section
*/
void __attribute__((section(".ramcode")))
__attribute__((long_call))
memcpy_ramcode(volatile u8 *dest, const volatile u8 *src, u32 count)
{
while (count--)
*dest++ = *src++;
}
/*
* Copy memory buffer to a sector in flash
*
* This function also erases the sector before programming it.
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
kinetis_flash_program_sector(u32 sect, u32 sect_offset, u8 *src, u32 size)
{
int rv;
if (sect >= CONFIG_MEM_NVM_LEN / FLASH_SECTOR_SIZE) {
/* The sector number is too big */
rv = -EINVAL;
goto out;
}
if (sect_offset + size > FLASH_SECTOR_SIZE) {
/* Attempt to write into more than one sector */
rv = -EINVAL;
goto out;
}
/*
* Save the existing data before the requested region
*/
if (sect_offset > 0) {
memcpy_ramcode(FLASH_PROG_ACCEL_PTR,
KINETIS_NVM_PTR + sect * FLASH_SECTOR_SIZE,
sect_offset);
}
/*
* Save the existing data after the requested region
*/
if (sect_offset + size < FLASH_SECTOR_SIZE) {
memcpy_ramcode(FLASH_PROG_ACCEL_PTR + sect_offset + size,
KINETIS_NVM_PTR + sect * FLASH_SECTOR_SIZE +
sect_offset + size,
FLASH_SECTOR_SIZE - (sect_offset + size));
}
/*
* Copy the input data into the flash programming acceleration RAM
*/
memcpy_ramcode(FLASH_PROG_ACCEL_PTR + sect_offset, src, size);
/*
* Erase the sector
*/
rv = kinetis_ftfl_erase_sector(sect);
if (rv != 0)
goto out;
/*
* Program the sector
*/
rv = kinetis_ftfl_program_section(sect * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
if (rv != 0)
goto out;
rv = 0;
out:
return rv;
}
/*
* Copy memory buffer to flash
*
* This function also erases the necessary flash sectors before programming
* them.
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
kinetis_flash_program(u32 dest_addr, u8 *src, u32 size)
{
int rv;
u32 sect, first_sect, last_sect;
u32 sect_offset, sect_len;
first_sect = dest_addr / FLASH_SECTOR_SIZE;
last_sect = (dest_addr + size - 1) / FLASH_SECTOR_SIZE;
for (sect = first_sect; sect <= last_sect; sect ++) {
if (first_sect == last_sect) {
sect_offset = dest_addr - first_sect * FLASH_SECTOR_SIZE;
sect_len = size;
} else if (sect == first_sect) {
sect_offset = dest_addr - first_sect * FLASH_SECTOR_SIZE;
sect_len = FLASH_SECTOR_SIZE - sect_offset;
} else if (sect == last_sect) {
sect_offset = 0;
sect_len = dest_addr + size - last_sect * FLASH_SECTOR_SIZE;
} else { /* a sector in the middle */
sect_offset = 0;
sect_len = FLASH_SECTOR_SIZE;
}
rv = kinetis_flash_program_sector(sect, sect_offset,
src + (sect * FLASH_SECTOR_SIZE + sect_offset - dest_addr),
sect_len);
if (rv != 0)
goto out;
}
rv = 0;
out:
return rv;
}
/*
* Initialize internal Flash interface
*
* This function should not be in .ramcode, because it will be called only once
* before self-upgrade.
*/
void envm_init(void)
{
}
/*
......@@ -153,13 +488,38 @@ void envm_init(void)
* Note that we need for this function to reside in RAM since it
* will be used to self-upgrade U-boot in internal Flash.
*/
unsigned int __attribute__((section(".ramcode")))
__attribute__ ((long_call))
envm_write(unsigned int offset, void * buf, unsigned int size)
u32 __attribute__((section(".ramcode")))
__attribute__((long_call))
envm_write(u32 offset, void *buf, u32 size)
{
/*
* TBD
*/
u32 rv = 0;
if (offset < CONFIG_MEM_NVM_BASE ||
offset + size > CONFIG_MEM_NVM_BASE + CONFIG_MEM_NVM_LEN) {
printf("%s: Address %#x is not in flash or "
"size 0x%x is too big\n",
__func__, offset, size);
goto out;
}
if (KINETIS_FLASH_FSEC_OFFSET >= offset &&
KINETIS_FLASH_FSEC_OFFSET < offset + size &&
(((u8 *)buf)[KINETIS_FLASH_FSEC_OFFSET - offset] &
KINETIS_FLASH_CONF_FSEC_SEC_MSK) !=
KINETIS_FLASH_CONF_FSEC_SEC_UNSECURE) {
printf("%s: You have attempted to secure\n"
" the MCU internal flash by writing the relevant\n"
" value at the address 0x40C of the flash. This\n"
" operation will not continue because it may lead\n"
" to lock-up of the MCU.\n",
__func__);
goto out;
}
if (kinetis_flash_program(offset, buf, size) < 0)
goto out;
return 0;
rv = size;
out:
return rv;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment