490 lines
15 KiB
C
490 lines
15 KiB
C
/*
|
|
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @file hal_efl_lld.c
|
|
* @brief GD32VF103 Embedded Flash subsystem low level driver source.
|
|
*
|
|
* @addtogroup HAL_EFL
|
|
* @{
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "hal.h"
|
|
|
|
#if (HAL_USE_EFL == TRUE) || defined(__DOXYGEN__)
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local definitions. */
|
|
/*===========================================================================*/
|
|
|
|
#define GD32_FLASH_LINE_SIZE 2U
|
|
#define GD32_FLASH_LINE_MASK (GD32_FLASH_LINE_SIZE - 1U)
|
|
|
|
/*===========================================================================*/
|
|
/* Driver exported variables. */
|
|
/*===========================================================================*/
|
|
|
|
/**
|
|
* @brief EFL1 driver identifier.
|
|
*/
|
|
EFlashDriver EFLD1;
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local variables and types. */
|
|
/*===========================================================================*/
|
|
|
|
static const flash_descriptor_t efl_lld_descriptor = {
|
|
.attributes = FLASH_ATTR_ERASED_IS_ONE |
|
|
FLASH_ATTR_MEMORY_MAPPED,
|
|
.page_size = GD32_FLASH_LINE_SIZE,
|
|
.sectors_count = GD32_FLASH_NUMBER_OF_BANKS *
|
|
GD32_FLASH_SECTORS_PER_BANK,
|
|
.sectors = NULL,
|
|
.sectors_size = GD32_FLASH_SECTOR_SIZE,
|
|
.address = (uint8_t *)FLASH_BASE,
|
|
.size = GD32_FLASH_NUMBER_OF_BANKS *
|
|
GD32_FLASH_SECTORS_PER_BANK *
|
|
GD32_FLASH_SECTOR_SIZE
|
|
};
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local functions. */
|
|
/*===========================================================================*/
|
|
|
|
static inline void gd32_flash_lock(EFlashDriver *eflp) {
|
|
|
|
eflp->flash->CTL |= FLASH_CTL_LK;
|
|
}
|
|
|
|
static inline void gd32_flash_unlock(EFlashDriver *eflp) {
|
|
|
|
eflp->flash->KEY |= FLASH_KEY1;
|
|
eflp->flash->KEY |= FLASH_KEY2;
|
|
}
|
|
|
|
static inline void gd32_flash_enable_pgm(EFlashDriver *eflp) {
|
|
|
|
eflp->flash->CTL |= FLASH_CTL_PG;
|
|
}
|
|
|
|
static inline void gd32_flash_disable_pgm(EFlashDriver *eflp) {
|
|
|
|
eflp->flash->CTL &= ~FLASH_CTL_PG;
|
|
}
|
|
|
|
static inline void gd32_flash_clear_status(EFlashDriver *eflp) {
|
|
|
|
eflp->flash->STAT = 0x0000001FU;
|
|
}
|
|
|
|
static inline uint32_t gd32_flash_is_busy(EFlashDriver *eflp) {
|
|
|
|
return (eflp->flash->STAT & FLASH_STAT_BUSY);
|
|
}
|
|
|
|
static inline void gd32_flash_wait_busy(EFlashDriver *eflp) {
|
|
|
|
/* Wait for busy bit clear.*/
|
|
while (gd32_flash_is_busy(eflp) != 0U) {
|
|
}
|
|
}
|
|
|
|
static inline flash_error_t gd32_flash_check_errors(EFlashDriver *eflp) {
|
|
uint32_t sr = eflp->flash->STAT;
|
|
|
|
/* Clearing error conditions.*/
|
|
eflp->flash->STAT = sr & 0x0000001FU;
|
|
|
|
/* Decoding relevant errors.*/
|
|
if ((sr & FLASH_STAT_WPERR) != 0U) {
|
|
return FLASH_ERROR_HW_FAILURE;
|
|
}
|
|
|
|
if ((sr & FLASH_STAT_PGERR) != 0U) {
|
|
return FLASH_ERROR_PROGRAM; /* There is no error on erase.*/
|
|
}
|
|
|
|
return FLASH_NO_ERROR;
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
/* Driver interrupt handlers. */
|
|
/*===========================================================================*/
|
|
|
|
/*===========================================================================*/
|
|
/* Driver exported functions. */
|
|
/*===========================================================================*/
|
|
|
|
/**
|
|
* @brief Low level Embedded Flash driver initialization.
|
|
*
|
|
* @notapi
|
|
*/
|
|
void efl_lld_init(void) {
|
|
|
|
/* Driver initialization.*/
|
|
eflObjectInit(&EFLD1);
|
|
EFLD1.flash = FLASH;
|
|
}
|
|
|
|
/**
|
|
* @brief Configures and activates the Embedded Flash peripheral.
|
|
*
|
|
* @param[in] eflp pointer to a @p EFlashDriver structure
|
|
*
|
|
* @notapi
|
|
*/
|
|
void efl_lld_start(EFlashDriver *eflp) {
|
|
|
|
gd32_flash_unlock(eflp);
|
|
FLASH->CTL = 0x00000000U;
|
|
}
|
|
|
|
/**
|
|
* @brief Deactivates the Embedded Flash peripheral.
|
|
*
|
|
* @param[in] eflp pointer to a @p EFlashDriver structure
|
|
*
|
|
* @notapi
|
|
*/
|
|
void efl_lld_stop(EFlashDriver *eflp) {
|
|
|
|
gd32_flash_lock(eflp);
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the flash descriptor structure.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @return A flash device descriptor.
|
|
*
|
|
* @notapi
|
|
*/
|
|
const flash_descriptor_t *efl_lld_get_descriptor(void *instance) {
|
|
|
|
(void)instance;
|
|
|
|
return &efl_lld_descriptor;
|
|
}
|
|
|
|
/**
|
|
* @brief Read operation.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @param[in] offset flash offset
|
|
* @param[in] n number of bytes to be read
|
|
* @param[out] rp pointer to the data buffer
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if there is no erase operation in progress.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_READ if the read operation failed.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @notapi
|
|
*/
|
|
flash_error_t efl_lld_read(void *instance, flash_offset_t offset,
|
|
size_t n, uint8_t *rp) {
|
|
EFlashDriver *devp = (EFlashDriver *)instance;
|
|
flash_error_t err = FLASH_NO_ERROR;
|
|
|
|
osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U));
|
|
osalDbgCheck((size_t)offset + n <= (size_t)efl_lld_descriptor.size);
|
|
osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
|
|
"invalid state");
|
|
|
|
/* No reading while erasing.*/
|
|
if (devp->state == FLASH_ERASE) {
|
|
return FLASH_BUSY_ERASING;
|
|
}
|
|
|
|
/* FLASH_READY state while the operation is performed.*/
|
|
devp->state = FLASH_READ;
|
|
|
|
/* Clearing error status bits.*/
|
|
gd32_flash_clear_status(devp);
|
|
|
|
/* Actual read implementation.*/
|
|
memcpy((void *)rp, (const void *)efl_lld_descriptor.address + offset, n);
|
|
|
|
/* Ready state again.*/
|
|
devp->state = FLASH_READY;
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Program operation.
|
|
* @note It is only possible to write erased pages once except
|
|
* when writing all zeroes.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @param[in] offset flash offset
|
|
* @param[in] n number of bytes to be programmed
|
|
* @param[in] pp pointer to the data buffer
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if there is no erase operation in progress.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_PROGRAM if the program operation failed.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @notapi
|
|
*/
|
|
flash_error_t efl_lld_program(void *instance, flash_offset_t offset,
|
|
size_t n, const uint8_t *pp) {
|
|
EFlashDriver *devp = (EFlashDriver *)instance;
|
|
flash_error_t err = FLASH_NO_ERROR;
|
|
|
|
osalDbgCheck((instance != NULL) && (pp != NULL) && (n > 0U));
|
|
osalDbgCheck((size_t)offset + n <= (size_t)efl_lld_descriptor.size);
|
|
|
|
osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
|
|
"invalid state");
|
|
|
|
/* No programming while erasing.*/
|
|
if (devp->state == FLASH_ERASE) {
|
|
return FLASH_BUSY_ERASING;
|
|
}
|
|
|
|
/* FLASH_PGM state while the operation is performed.*/
|
|
devp->state = FLASH_PGM;
|
|
|
|
/* Clearing error status bits.*/
|
|
gd32_flash_clear_status(devp);
|
|
|
|
/* Enabling PGM mode in the controller.*/
|
|
gd32_flash_enable_pgm(devp);
|
|
|
|
/* Actual program implementation.*/
|
|
while (n > 0U) {
|
|
volatile uint16_t *address;
|
|
|
|
union {
|
|
uint16_t hw[GD32_FLASH_LINE_SIZE / sizeof (uint16_t)];
|
|
uint8_t b[GD32_FLASH_LINE_SIZE / sizeof (uint8_t)];
|
|
} line;
|
|
|
|
/* Unwritten bytes are initialized to all ones.*/
|
|
line.hw[0] = 0xFFFFU;
|
|
|
|
/* Programming address aligned to flash lines.*/
|
|
address = (volatile uint16_t *)(efl_lld_descriptor.address +
|
|
(offset & ~GD32_FLASH_LINE_MASK));
|
|
|
|
/* Copying data inside the prepared line.*/
|
|
do {
|
|
line.b[offset & GD32_FLASH_LINE_MASK] = *pp;
|
|
offset++;
|
|
n--;
|
|
pp++;
|
|
}
|
|
while ((n > 0U) & ((offset & GD32_FLASH_LINE_MASK) != 0U));
|
|
|
|
/* Programming line.*/
|
|
address[0] = line.hw[0];
|
|
gd32_flash_wait_busy(devp);
|
|
|
|
err = gd32_flash_check_errors(devp);
|
|
if (err != FLASH_NO_ERROR) {
|
|
break;
|
|
}
|
|
/* Check for flash error.*/
|
|
if (address[0] != line.hw[0]) {
|
|
err = FLASH_ERROR_PROGRAM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Disabling PGM mode in the controller.*/
|
|
gd32_flash_disable_pgm(devp);
|
|
|
|
/* Ready state again.*/
|
|
devp->state = FLASH_READY;
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Starts a whole-device erase operation.
|
|
* @note This function does nothing, the flash memory is where the program
|
|
* is running on.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if there is no erase operation in progress.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @notapi
|
|
*/
|
|
flash_error_t efl_lld_start_erase_all(void *instance) {
|
|
(void) instance;
|
|
|
|
return FLASH_ERROR_UNIMPLEMENTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Starts an sector erase operation.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @param[in] sector sector to be erased
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if there is no erase operation in progress.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @notapi
|
|
*/
|
|
flash_error_t efl_lld_start_erase_sector(void *instance,
|
|
flash_sector_t sector) {
|
|
EFlashDriver *devp = (EFlashDriver *)instance;
|
|
|
|
osalDbgCheck(instance != NULL);
|
|
osalDbgCheck(sector < efl_lld_descriptor.sectors_count);
|
|
osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
|
|
"invalid state");
|
|
|
|
/* No erasing while erasing.*/
|
|
if (devp->state == FLASH_ERASE) {
|
|
return FLASH_BUSY_ERASING;
|
|
}
|
|
|
|
/* FLASH_PGM state while the operation is performed.*/
|
|
devp->state = FLASH_ERASE;
|
|
|
|
/* Clearing error status bits.*/
|
|
gd32_flash_clear_status(devp);
|
|
|
|
/* Enable page erase.*/
|
|
devp->flash->CTL |= FLASH_CTL_PER;
|
|
|
|
/* Set the page.*/
|
|
devp->flash->ADDR = (uint32_t)(efl_lld_descriptor.address +
|
|
flashGetSectorOffset(getBaseFlash(devp), sector));
|
|
|
|
/* Start the erase.*/
|
|
devp->flash->CTL |= FLASH_CTL_START;
|
|
|
|
return FLASH_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Queries the driver for erase operation progress.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @param[out] wait_time recommended time, in milliseconds, that
|
|
* should be spent before calling this
|
|
* function again, can be @p NULL
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if there is no erase operation in progress.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_ERASE if the erase operation failed.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @api
|
|
*/
|
|
flash_error_t efl_lld_query_erase(void *instance, uint32_t *wait_time) {
|
|
EFlashDriver *devp = (EFlashDriver *)instance;
|
|
flash_error_t err;
|
|
|
|
/* If there is an erase in progress then the device must be checked.*/
|
|
if (devp->state == FLASH_ERASE) {
|
|
|
|
/* Checking for operation in progress.*/
|
|
if (gd32_flash_is_busy(devp) == 0U) {
|
|
|
|
/* Disabling the various erase control bits.*/
|
|
devp->flash->CTL &= ~(FLASH_CTL_OBER | FLASH_CTL_OBPG |
|
|
FLASH_CTL_MER | FLASH_CTL_PER);
|
|
|
|
/* Back to ready state.*/
|
|
devp->state = FLASH_READY;
|
|
|
|
err = FLASH_NO_ERROR;
|
|
}
|
|
else {
|
|
/* Recommended time before polling again, this is a simplified
|
|
implementation.*/
|
|
if (wait_time != NULL) {
|
|
*wait_time = (uint32_t)GD32_FLASH_WAIT_TIME_MS;
|
|
}
|
|
|
|
err = FLASH_BUSY_ERASING;
|
|
}
|
|
}
|
|
else {
|
|
err = FLASH_NO_ERROR;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the erase state of a sector.
|
|
*
|
|
* @param[in] ip pointer to a @p EFlashDriver instance
|
|
* @param[in] sector sector to be verified
|
|
* @return An error code.
|
|
* @retval FLASH_NO_ERROR if the sector is erased.
|
|
* @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
|
|
* @retval FLASH_ERROR_VERIFY if the verify operation failed.
|
|
* @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
|
|
*
|
|
* @notapi
|
|
*/
|
|
flash_error_t efl_lld_verify_erase(void *instance, flash_sector_t sector) {
|
|
EFlashDriver *devp = (EFlashDriver *)instance;
|
|
uint32_t *address;
|
|
flash_error_t err = FLASH_NO_ERROR;
|
|
unsigned i;
|
|
|
|
osalDbgCheck(instance != NULL);
|
|
osalDbgCheck(sector < efl_lld_descriptor.sectors_count);
|
|
osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
|
|
"invalid state");
|
|
|
|
/* No verifying while erasing.*/
|
|
if (devp->state == FLASH_ERASE) {
|
|
return FLASH_BUSY_ERASING;
|
|
}
|
|
|
|
/* Address of the sector.*/
|
|
address = (uint32_t *)(efl_lld_descriptor.address +
|
|
flashGetSectorOffset(getBaseFlash(devp), sector));
|
|
|
|
/* FLASH_READY state while the operation is performed.*/
|
|
devp->state = FLASH_READ;
|
|
|
|
/* Scanning the sector space.*/
|
|
for (i = 0U; i < GD32_FLASH_SECTOR_SIZE / sizeof(uint32_t); i++) {
|
|
if (*address != 0xFFFFFFFFU) {
|
|
err = FLASH_ERROR_VERIFY;
|
|
break;
|
|
}
|
|
address++;
|
|
}
|
|
|
|
/* Ready state again.*/
|
|
devp->state = FLASH_READY;
|
|
|
|
return err;
|
|
}
|
|
|
|
#endif /* HAL_USE_EFL == TRUE */
|
|
|
|
/** @} */
|