#include "bsp_fmc_flash.h"
#include <stdio.h>
#include <string.h>

/* 这个文件默认是根据GD32F303 实现的flash 操作接口函数，所以移植的时候，需要根据项目自行实现芯片平台相关的fmc 外设操作函数，只需要替换实现以下函数的函数体：
	bsp_flash_read 
	bsp_flash_write 
	bsp_flash_is_erased
	bsp_flash_erased
	其他不需要动
*/

#include "at32a423.h"                   // Device header


#define BSP_FLASH_BANK_FLAGS   ((fmc_flag_enum)(FMC_FLAG_PGAERR | FMC_FLAG_WPERR | \
                                                FMC_FLAG_END    | FMC_FLAG_PGERR ))
#define BSP_FLASH_SN_SIZE      16U

static const bsp_flash_info_t s_bsp_flash_info = 
{
    BSP_FLASH_PAGE_SIZE,
    BSP_FLASH_PROGRAM_UNIT,
    BSP_FLASH_ERASE_VALUE,
};

static uint32_t bsp_flash_align_down(uint32_t value, uint32_t align)
{
    return value & ~(align - 1U);
}

static uint32_t bsp_flash_align_up(uint32_t value, uint32_t align)
{
    return (value + align - 1U) & ~(align - 1U);
}

static void bsp_flash_clear_flags(void)
{
    fmc_flag_clear(BSP_FLASH_BANK_FLAGS); 
}

static uint8_t bsp_flash_pack_word(const uint8_t *src, uint32_t size, uint32_t *word_out)
{
    uint8_t bytes[BSP_FLASH_PROGRAM_UNIT] = { BSP_FLASH_ERASE_VALUE, BSP_FLASH_ERASE_VALUE,
                                              BSP_FLASH_ERASE_VALUE, BSP_FLASH_ERASE_VALUE };

    if ((word_out == 0) || ((src == 0) && (size > 0U))) {
        return 0;
    }

    if (size > BSP_FLASH_PROGRAM_UNIT) {
        size = BSP_FLASH_PROGRAM_UNIT;
    }

    if (size > 0U) {
        memcpy(bytes, src, size);
    }

    *word_out = ((uint32_t)bytes[0]) |
                ((uint32_t)bytes[1] << 8) |
                ((uint32_t)bytes[2] << 16) |
                ((uint32_t)bytes[3] << 24);
    return 1;
}

const bsp_flash_info_t *bsp_flash_get_info(void)
{
    return &s_bsp_flash_info;
}

uint8_t bsp_flash_read(uint32_t addr, void *buffer, uint32_t size)
{
    if ((buffer == 0) && (size > 0U)) {
        return 0;
    }

    if (size > 0U) {
        memcpy(buffer, (const void *)addr, size);
    }

    return 1;
}

uint8_t bsp_flash_is_erased(uint32_t addr, uint32_t size)
{
    const uint8_t *flash_ptr = (const uint8_t *)addr;
    uint32_t i;

    for (i = 0U; i < size; i++) {
        if (flash_ptr[i] != BSP_FLASH_ERASE_VALUE) {
            return 0;
        }
    }

    return 1;
}

uint8_t bsp_flash_erased(uint32_t addr, uint32_t size)
{
    uint32_t erase_addr;
    uint32_t erase_end;

    if (size == 0U) {
        return 1;
    }

    erase_addr = bsp_flash_align_down(addr, BSP_FLASH_PAGE_SIZE);
    erase_end = bsp_flash_align_up(addr + size, BSP_FLASH_PAGE_SIZE);

    fmc_unlock();
    bsp_flash_clear_flags();

    while (erase_addr < erase_end) {
        if (fmc_page_erase(erase_addr) != FMC_READY) {
            fmc_lock();
            return 0;
        }
        erase_addr += BSP_FLASH_PAGE_SIZE;
        bsp_flash_clear_flags();
    }

    fmc_lock();
    return 1;
}

uint8_t bsp_flash_write(uint32_t addr, const void *data, uint32_t size)
{
    const uint8_t *src = (const uint8_t *)data;
    uint32_t offset = 0U;
    uint32_t word = 0U;
    uint32_t chunk_size;

    if ((addr % BSP_FLASH_PROGRAM_UNIT) != 0U) {
        return 0;
    }

    if ((data == 0) && (size > 0U)) {
        return 0;
    }

    if (size == 0U) {
        return 1;
    }

    fmc_unlock();
    bsp_flash_clear_flags();

    while (offset < size) 
    {
        chunk_size = size - offset;
        if (chunk_size > BSP_FLASH_PROGRAM_UNIT) {
            chunk_size = BSP_FLASH_PROGRAM_UNIT;
        }

        if (!bsp_flash_pack_word(src + offset, chunk_size, &word)) {
            fmc_lock();
            return 0;
        }

        if (fmc_word_program(addr + offset, word) != FMC_READY) {
            fmc_lock();
            return 0;
        }

        offset += BSP_FLASH_PROGRAM_UNIT;
        bsp_flash_clear_flags();
    }

    fmc_lock();
    return 1;
}

