#include "flash.h"
#include "fpga.h"


#define FLASH_ADDRESS       (0x04000000UL)
#define FLASH_SIZE          (16 * 1024 * 1024)
#define ERASE_BLOCK_SIZE    (64 * 1024)


bool flash_program (uint32_t src, uint32_t dst, uint32_t length) {
    if ((dst < FLASH_ADDRESS) || ((dst + length) > (FLASH_ADDRESS + FLASH_SIZE))) {
        return true;
    }
    if ((src <= dst) && ((src + length) > dst)) {
        return true;
    }
    if ((dst <= src) && ((dst + length) > src)) {
        return true;
    }
    while (length > 0) {
        uint32_t block = (length > FPGA_MAX_MEM_TRANSFER) ? FPGA_MAX_MEM_TRANSFER : length;
        fpga_mem_copy(src, dst, block);
        src += block;
        dst += block;
        length -= block;
    }
    return false;
}

void flash_wait_busy (void) {
    uint8_t dummy[2];
    fpga_mem_read(FLASH_ADDRESS, 2, dummy);
}

bool flash_erase_block (uint32_t address) {
    if ((address % FLASH_ERASE_BLOCK_SIZE) != 0) {
        return true;
    }
    if ((address < FLASH_ADDRESS) || (address >= (FLASH_ADDRESS + FLASH_SIZE))) {
        return true;
    }
    address &= (FLASH_SIZE - 1);
    for (int i = 0; i < (FLASH_ERASE_BLOCK_SIZE / ERASE_BLOCK_SIZE); i++) {
        fpga_reg_set(REG_FLASH_SCR, address);
        while (fpga_reg_get(REG_FLASH_SCR) & FLASH_SCR_BUSY);
        address += ERASE_BLOCK_SIZE;
    }
    flash_wait_busy();
    return false;
}
