micropython/ports/renesas-ra/ra/ra_flash.c

439 lines
14 KiB
C

/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Renesas Electronics Corporation
* Copyright (c) 2023 Vekatech Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdbool.h>
#include "py/mpconfig.h"
#include "hal_data.h"
#include "ra_config.h"
#include "ra_utils.h"
#include "ra_flash.h"
#if !defined(USE_FSP_FLASH)
#define USE_FSP_FLASH
#endif
#if defined(USE_FSP_FLASH)
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#endif
/* Flags, set from Callback function */
static volatile _Bool g_b_flash_event_not_blank = false;
static volatile _Bool g_b_flash_event_blank = false;
static volatile _Bool g_b_flash_event_erase_complete = false;
static volatile _Bool g_b_flash_event_write_complete = false;
static uint8_t flash_buf[FLASH_BUF_SIZE] __attribute__((aligned(2)));
#ifdef USE_FSP_QSPI
#if MICROPY_HW_HAS_QSPI_FLASH
static bool r_qspi_get_QE(void) {
/* Enter direct communication mode */
R_QSPI->SFMCMD = 1U;
R_QSPI->SFMCOM = 0x35;
bool sts = (R_QSPI->SFMCOM >> 1) & 1;
/* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
* in the RA6M3 manual R01UH0886EJ0100. */
R_QSPI->SFMCMD = 1U;
/* Exit direct communication mode */
R_QSPI->SFMCMD = 0U;
return sts;
}
static void r_qspi_set_QE(qspi_instance_ctrl_t *p_instance_ctrl) {
/* Enter direct communication mode */
R_QSPI->SFMCMD = 1U;
/* Enable write. */
R_QSPI->SFMCOM = p_instance_ctrl->p_cfg->write_enable_command;
/* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
* in the RA6M3 manual R01UH0886EJ0100. */
R_QSPI->SFMCMD = 1U;
R_QSPI->SFMCOM = 0x31;
R_QSPI->SFMCOM = 0x02;
/* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
* in the RA6M3 manual R01UH0886EJ0100. */
R_QSPI->SFMCMD = 1U;
/* Exit direct communication mode */
R_QSPI->SFMCMD = 0U;
}
static void r_qspi_wait_WIP(qspi_instance_ctrl_t *p_instance_ctrl) {
/* Enter direct communication mode */
R_QSPI->SFMCMD = 1U;
R_QSPI->SFMCOM = p_instance_ctrl->p_cfg->status_command;
while ((R_QSPI->SFMCOM >> p_instance_ctrl->p_cfg->write_status_bit) & 1) {
;
}
/* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
* in the RA6M3 manual R01UH0886EJ0100. */
R_QSPI->SFMCMD = 1U;
/* Exit direct communication mode */
R_QSPI->SFMCMD = 0U;
}
static fsp_err_t R_QSPI_QuadEnable(spi_flash_ctrl_t *p_ctrl) {
qspi_instance_ctrl_t *p_instance_ctrl = (qspi_instance_ctrl_t *)p_ctrl;
#if QSPI_CFG_PARAM_CHECKING_ENABLE
FSP_ASSERT(NULL != p_instance_ctrl);
FSP_ASSERT(NULL != p_status);
FSP_ERROR_RETURN(QSPI_PRV_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
/* Do not enter direct communication mode from XIP mode. Reference note in section 39.10.2 "Using Direct
* Communication Mode" in the RA6M3 manual R01UH0886EJ0100. */
FSP_ERROR_RETURN(0U == R_QSPI->SFMSDC_b.SFMXST, FSP_ERR_INVALID_MODE);
#endif
/* Read device status. */
if (!r_qspi_get_QE()) {
r_qspi_set_QE(p_instance_ctrl);
}
return FSP_SUCCESS;
}
static fsp_err_t R_QSPI_Wait_WIP(spi_flash_ctrl_t *p_ctrl) {
qspi_instance_ctrl_t *p_instance_ctrl = (qspi_instance_ctrl_t *)p_ctrl;
#if QSPI_CFG_PARAM_CHECKING_ENABLE
FSP_ASSERT(NULL != p_instance_ctrl);
FSP_ASSERT(NULL != p_status);
FSP_ERROR_RETURN(QSPI_PRV_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
/* Do not enter direct communication mode from XIP mode. Reference note in section 39.10.2 "Using Direct
* Communication Mode" in the RA6M3 manual R01UH0886EJ0100. */
FSP_ERROR_RETURN(0U == R_QSPI->SFMSDC_b.SFMXST, FSP_ERR_INVALID_MODE);
#endif
/* Wait WIP flag to go 0 */
r_qspi_wait_WIP(p_instance_ctrl);
return FSP_SUCCESS;
}
static bool lmemprob(void *dst, size_t len) {
uint8_t *p;
for (p = (uint8_t *)dst; ((len > 0) && (p[0] == 0xFF)); len--) {
p++;
}
return len? false : true;
}
#endif
#endif
void *FLASH_SECTION lmemset(void *dst, int c, size_t len) {
char *p;
for (p = (char *)dst; len > 0; len--) {
*(p++) = (char)c;
}
return (void *)dst;
}
void *FLASH_SECTION lmemcpy(void *dst, const void *src, size_t len) {
char *d = (char *)dst;
const char *s = (const char *)src;
for (; len > 0; len--) {
*(d++) = *(s++);
}
return (void *)dst;
}
int FLASH_SECTION lmemcmp(const void *p1, const void *p2, size_t len) {
unsigned char *a, *b;
size_t i;
a = (unsigned char *)p1;
b = (unsigned char *)p2;
for (i = 0; i < len; i++) {
if (*a != *b) {
return (*a < *b) ? -1 : 1;
}
a++;
b++;
}
return (int)0;
}
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1) | MICROPY_HW_HAS_QSPI_FLASH
uint32_t FLASH_SECTION sector_size(uint32_t addr) {
return FLASH_SECTOR_SIZE;
}
uint32_t FLASH_SECTION sector_start(uint32_t addr) {
return addr & ~(FLASH_SECTOR_SIZE - 1);
}
uint32_t FLASH_SECTION sector_index(uint32_t addr) {
return (addr - 0x00000000) / FLASH_SECTOR_SIZE;
}
#elif defined(RA6M1) | defined(RA6M2) | defined(RA6M3) | defined(RA6M5)
#define REGION1_SECTOR_SIZE 0x8000 // 32K
#define REGION1_SECTOR_MAX 14
#define REGION0_SECTOR_SIZE 0x2000 // 8K
#define REGION0_SECTOR_MAX 8
uint32_t FLASH_SECTION sector_size(uint32_t addr) {
if (addr < 0x00010000) {
return REGION0_SECTOR_SIZE;
} else {
return REGION1_SECTOR_SIZE;
}
}
uint32_t FLASH_SECTION sector_start(uint32_t addr) {
if (addr < 0x00010000) {
return addr & ~(REGION0_SECTOR_SIZE - 1);
} else {
return addr & ~(REGION1_SECTOR_SIZE - 1);
}
}
uint32_t FLASH_SECTION sector_index(uint32_t addr) {
if (addr < 0x00010000) {
return (addr - 0x00000000) / REGION0_SECTOR_SIZE;
} else {
return ((addr - 0x00010000) / REGION1_SECTOR_SIZE) + REGION0_SECTOR_SIZE;
}
}
#else
#error "CMSIS MCU Series is not specified."
#endif
bool internal_flash_read(uint8_t *addr, uint32_t NumBytes, uint8_t *pSectorBuff) {
CHIP_WORD *startaddr = (CHIP_WORD *)addr;
CHIP_WORD *endaddr = (CHIP_WORD *)(addr + NumBytes);
while (startaddr < endaddr) {
*pSectorBuff++ = *startaddr++;
}
return true;
}
bool internal_flash_write(uint8_t *addr, uint32_t NumBytes, uint8_t *pSectorBuff, bool ReadModifyWrite) {
return internal_flash_writex(addr, NumBytes, pSectorBuff, ReadModifyWrite, true);
}
bool internal_flash_writex(uint8_t *addr, uint32_t NumBytes, uint8_t *pSectorBuff, bool ReadModifyWrite, bool fIncrementDataPtr) {
fsp_err_t err = FSP_SUCCESS;
bool flag;
uint32_t count;
uint8_t *buf_addr = (uint8_t *)&flash_buf[0];
uint32_t startaddr = (uint32_t)addr & FLASH_BUF_ADDR_MASK;
uint32_t offset = (uint32_t)addr & FLASH_BUF_OFF_MASK;
uint32_t endaddr = (uint32_t)addr + NumBytes;
uint32_t error_code = 0;
while (startaddr < endaddr) {
// copy from dst rom addr to flash buffer to keep current data
lmemcpy(flash_buf, (void *)startaddr, FLASH_BUF_SIZE);
// memset(flash_buf, 0xff, FLASH_BUF_SIZE);
if (NumBytes + offset > FLASH_BUF_SIZE) {
count = FLASH_BUF_SIZE - offset;
NumBytes -= count;
} else {
count = NumBytes;
}
// overwrite data from src addr to flash buffer
if (fIncrementDataPtr) {
lmemcpy(flash_buf + offset, pSectorBuff, count);
} else {
lmemset(flash_buf + offset, (int)*pSectorBuff, count);
}
g_b_flash_event_write_complete = false;
uint8_t *flash_addr = (uint8_t *)((uint32_t)startaddr & FLASH_BUF_ADDR_MASK);
#if MICROPY_HW_HAS_QSPI_FLASH
for (uint16_t idx = 0; ((err == FSP_SUCCESS) && (idx < FLASH_SECTOR_SIZE)); idx += FLASH_PAGE_SIZE)
{
err = R_QSPI_Write(&g_qspi0_ctrl, &buf_addr[idx], &flash_addr[idx], FLASH_PAGE_SIZE);
err = R_QSPI_Wait_WIP(&g_qspi0_ctrl);
}
#else
uint32_t state = ra_disable_irq();
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1)
err = R_FLASH_LP_Write(&g_flash0_ctrl, (uint32_t const)buf_addr, (uint32_t)flash_addr, FLASH_SECTOR_SIZE);
#elif defined(RA6M1) | defined(RA6M2) | defined(RA6M3) | defined(RA6M5)
err = R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t const)buf_addr, (uint32_t)flash_addr, FLASH_SECTOR_SIZE);
#else
#error "CMSIS MCU Series is not specified."
#endif
ra_enable_irq(state);
#endif
if (FSP_SUCCESS != err) {
error_code = 1;
goto WriteX_exit;
}
if (fIncrementDataPtr) {
flag = (lmemcmp((void *)(startaddr + offset), flash_buf + offset, count) == 0);
if (!flag) {
error_code = 2;
break;
}
}
offset = 0;
startaddr += FLASH_BUF_SIZE;
pSectorBuff += count;
}
WriteX_exit:
if (error_code == 0) {
return true;
}
return false;
}
bool internal_flash_memset(uint8_t *addr, uint8_t Data, uint32_t NumBytes) {
return true;
}
bool internal_flash_isblockerased(uint8_t *addr, uint32_t BlockLength) {
g_b_flash_event_not_blank = false;
g_b_flash_event_blank = false;
#if MICROPY_HW_HAS_QSPI_FLASH
return lmemprob((uint8_t *)((uint32_t)addr & FLASH_BUF_ADDR_MASK), FLASH_SECTOR_SIZE);
#else
fsp_err_t err = FSP_SUCCESS;
flash_result_t blankCheck = FLASH_RESULT_BLANK;
uint32_t state = ra_disable_irq();
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1)
err = R_FLASH_LP_BlankCheck(&g_flash0_ctrl, (uint32_t const)((uint32_t)addr & FLASH_BUF_ADDR_MASK), FLASH_SECTOR_SIZE, &blankCheck);
#elif defined(RA6M1) | defined(RA6M2) | defined(RA6M3) | defined(RA6M5)
err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, (uint32_t const)((uint32_t)addr & FLASH_BUF_ADDR_MASK), FLASH_SECTOR_SIZE, &blankCheck);
#else
#error "CMSIS MCU Series is not specified."
#endif
ra_enable_irq(state);
if (err == FSP_SUCCESS) {
if (FLASH_RESULT_BLANK == blankCheck) {
return true;
} else {
return false;
}
} else {
return false;
}
#endif
}
bool internal_flash_eraseblock(uint8_t *addr) {
uint32_t error_code = 0;
fsp_err_t err = FSP_SUCCESS;
g_b_flash_event_erase_complete = false;
#if MICROPY_HW_HAS_QSPI_FLASH
if (!lmemprob((uint8_t *)((uint32_t)addr & FLASH_BUF_ADDR_MASK), FLASH_SECTOR_SIZE)) {
err = R_QSPI_Erase(&g_qspi0_ctrl, (uint8_t *const)addr, FLASH_SECTOR_SIZE);
err = R_QSPI_Wait_WIP(&g_qspi0_ctrl);
}
#else
uint32_t state = ra_disable_irq();
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1)
err = R_FLASH_LP_Erase(&g_flash0_ctrl, (uint32_t const)addr, 1);
#elif defined(RA6M1) | defined(RA6M2) | defined(RA6M3) | defined(RA6M5)
err = R_FLASH_HP_Erase(&g_flash0_ctrl, (uint32_t const)addr, 1);
#else
#error "CMSIS MCU Series is not specified."
#endif
ra_enable_irq(state);
#endif
if (err == FSP_SUCCESS) {
error_code = 0;
} else {
error_code = 1;
}
if (error_code == 0) {
return true;
}
return false;
}
/* Callback function */
void callback_flash(flash_callback_args_t *p_args) {
/* TODO: add your own code here */
switch (p_args->event) {
case FLASH_EVENT_NOT_BLANK:
g_b_flash_event_not_blank = true;
break;
case FLASH_EVENT_BLANK:
g_b_flash_event_blank = true;
break;
case FLASH_EVENT_ERASE_COMPLETE:
g_b_flash_event_erase_complete = true;
break;
case FLASH_EVENT_WRITE_COMPLETE:
g_b_flash_event_write_complete = true;
break;
default:
/* Do nothing */
break;
}
}
bool internal_flash_init(void) {
fsp_err_t err = FSP_SUCCESS;
#if MICROPY_HW_HAS_QSPI_FLASH
err = R_QSPI_Open(&g_qspi0_ctrl, &g_qspi0_cfg);
if (err == FSP_SUCCESS) {
R_QSPI_QuadEnable(&g_qspi0_ctrl);
return true;
} else {
return false;
}
#else
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1)
err = R_FLASH_LP_Open(&g_flash0_ctrl, &g_flash0_cfg);
#elif defined(RA6M1) | defined(RA6M2) | defined(RA6M3) | defined(RA6M5)
err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);
#else
#error "CMSIS MCU Series is not specified."
#endif
if (err == FSP_SUCCESS) {
return true;
} else {
return false;
}
#endif
}
#else
// ToDo: implementation
#endif