/**
******************************************************************************************************************************************************************************************
*@file                       cpu_flash.c
*@author         
*@data                       
*@version                    v1.0
*@par Copyright(c):
*
*@par History:
*     version:author,date,desc
*     v1.0   :create this file
*
*******************************************************************************************************************************************************************************************
*/
#include "stdint.h"
#include "cpu.h"
#include "cpu_flash.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "app_config.h"
#include "app_trace.h"
const bootrom_func_t *pboot_func = (const bootrom_func_t *)0x00001000;
void flash_burst_disable(void) __attribute__((section("config_extflash")));

#define PROTECTION_VOLTAGE		300

static void flash_voltage_protection_check(void)
{
	uint32_t r0,r1,r2,r3,r4,r5,r6;
	uint32_t gate;
	uint32_t vol;
	
	/* save gate and enable gpadc */
	gate = CRM->CG_CTRL & CRM_PeriphClock_QPADC;
	crm_cmd_periphclock(CRM_PeriphClock_QPADC,ENABLE);
	
	/* save gpadc config */
	r0 = *(volatile uint32_t *)0x5000F000;
	r1 = *(volatile uint32_t *)0x5000F004;
	r2 = *(volatile uint32_t *)0x5000F008;
	r3 = *(volatile uint32_t *)0x5000F00C;
	r4 = *(volatile uint32_t *)0x5000F01C;
	r5 = *(volatile uint32_t *)0x5000F024;
	r6 = *(volatile uint32_t *)0x5000F028;
	
	/* reset gpadc */
	CRM->SOFT_RST |= CRM_PeriphReset_GPADC;
	
	/* set to temp */
	*(volatile uint32_t *)0x5000F000 = 0x00008054;
	
	do
	{
		*(volatile uint32_t *)0x5000F004 = 1;
		
		/* get temp */
		while(*(volatile uint32_t *)0x5000F004 & BIT0);
		vol = *(volatile uint32_t *)0x5000F020;
	}while(vol < PROTECTION_VOLTAGE);	/* ´ËÖµ´óÔ¼2.6 ~ 2.8v×óÓÒ */
	
	/* restore gpadc config */
	*(volatile uint32_t *)0x5000F000 = r0;
	*(volatile uint32_t *)0x5000F004 = r1;
	*(volatile uint32_t *)0x5000F008 = r2;
	*(volatile uint32_t *)0x5000F00C = r3;
	*(volatile uint32_t *)0x5000F01C = r4;
	*(volatile uint32_t *)0x5000F024 = r5;
	*(volatile uint32_t *)0x5000F028 = r6;
	
	/* restore gate */
	CRM->CG_CTRL &= ~CRM_PeriphClock_QPADC;
	CRM->CG_CTRL |= (CRM_PeriphClock_QPADC & gate);
}

void flash_burst_disable(void)
{
    uint8_t  manu_id;
    
    manu_id = (pboot_func->flash_read_id() & 0x00FF0000) >> 16;
        
    if (manu_id != QSPI_SUPPORT_CHIP_JEDEC_ID_EON)
    {
        CACHE->CACHE_BACK_DOOR &= ~0x00FF0000; // ??cache wrap, ? burst??
        pboot_func->flash_set_burst_len(0x10);
    }
}

void flash_burst_enable(void) __attribute__((section("config_extflash")));
void flash_burst_enable(void)
{   
    uint8_t  manu_id;

    manu_id = (pboot_func->flash_read_id() & 0x00FF0000) >> 16;
        
    if (manu_id != QSPI_SUPPORT_CHIP_JEDEC_ID_EON)
    {
        pboot_func->flash_unlock();
        pboot_func->flash_set_burst_len(0x40);
        CACHE->CACHE_BACK_DOOR &= ~0x00FF0000;
        CACHE->CACHE_BACK_DOOR = 0x00A50000; // open cache wrap, ? burst??
        CACHE->CACHE_REF = 0xC0000000;//clean all cache
        while (CACHE->CACHE_CS & 1);
        pboot_func->flash_lock();
    }
}
static void flash_cache_clear()
{
    CACHE->CACHE_REF = 0x40000000; 
	CACHE->CACHE_REF |= 0x80000000;
	while ((CACHE->CACHE_REF & 0x80000000));
}

uint32_t flash_read(uint32_t address, uint32_t size, uint8_t *buf)
{
    uint32_t status = FLASH_SUCCEED;
	if(address < FLASH_VALID_ADDR || address >= FLASH_END_ADDR)
        return FLASH_ERROR_ADDR_INVALID;
	address |= 0x01000000;
	memcpy(buf,(uint8_t*)address,size);
    return (status);
}

uint32_t flash_write(uint32_t address, uint32_t size, uint8_t *buf)
{
    uint32_t status             = FLASH_SUCCEED;
    uint32_t current_page_end   = 0;
	uint32_t wr_size            = 0;
	if(address < FLASH_VALID_ADDR || address >= FLASH_END_ADDR)
        return FLASH_ERROR_ADDR_INVALID;

    __set_faultmask(1);
	flash_voltage_protection_check();
    pboot_func->flash_unlock();
    current_page_end   = (address/FALSH_PAGE_SIZE+1) * FALSH_PAGE_SIZE;
    while(size > 0)
    {
        if(address + size > current_page_end)
        {
            wr_size = current_page_end-address;
            status  = pboot_func->flash_program(address, wr_size, buf);
			if(status)
            {
                address += wr_size;
                buf     += wr_size;
                size    -= wr_size;
                current_page_end += FALSH_PAGE_SIZE;
            }
        }
        else
        {
            status = pboot_func->flash_program(address, size, buf);
            if(status)
            {
                size = 0;
            }
        }
    }
    pboot_func->flash_lock();
    flash_cache_clear();
    __set_faultmask(0);
    return (status);
}

uint32_t flash_erase(uint32_t address, uint32_t size)
{
    uint16_t i          = 0;
    uint16_t num_sector = 0;
    uint32_t status     = FLASH_SUCCEED;
    if(address < FLASH_VALID_ADDR || address >= FLASH_END_ADDR)
        return FLASH_ERROR_ADDR_INVALID;
    if((address - FLASH_ADDR_BASE)%(FLASH_SECTOR_SIZE))
        return FLASH_ERROR_ADDR_INVALID;
    if((address + size) > FLASH_END_ADDR)
        return FLASH_ERROR_ADDR_OUT;
    
    num_sector = size/(FLASH_SECTOR_SIZE);
    __set_faultmask(1);
	flash_voltage_protection_check();
    pboot_func->flash_unlock();
    for(i=0; i<num_sector; i++)
        status = pboot_func->flash_erase_sector(address+(i*FLASH_SECTOR_SIZE));
    pboot_func->flash_lock();
    flash_cache_clear();
    __set_faultmask(0);
    return (status);
}

uint32_t flash_otp_read(uint32_t page,uint32_t addr, uint32_t size, uint8_t *buf)
{
   uint32_t result = 0;
   __set_faultmask(1);
   result = pboot_func->flash_read_otp(page,addr,size,buf);
   __set_faultmask(0);
   return result;
}

bool Flash_set_data_long(uint32_t addr, uint32_t len, uint32_t *arr) {
	uint32_t i;
	uint32_t longlen = 4*len;
	uint8_t readdata[longlen];
	if(FLASH_SUCCEED == flash_write(addr, longlen, (uint8_t *)arr))
	{
		if(FLASH_SUCCEED == flash_read(addr, longlen, (uint8_t *)readdata))
		{
			for(i = 0; i < len; i++){
				if(readdata[i] != arr[i])
					return FALSE;
			}
			return TRUE;
		}else
		{
			return FALSE;
		}			
	}
	else
	{
		return FALSE;
	}
}

bool Flash_get_data_long(uint32_t addr, uint32_t len, uint32_t *arr) {
	uint32_t result;
	uint32_t longlen = 4*len;
	result = flash_read(addr, longlen, (uint8_t *)arr);
	if(result == FLASH_SUCCEED)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}

}		 

bool Flash_set_data_byte(uint32_t addr, uint32_t len, uint8_t *arr) {
	uint8_t readdata[len];
	uint32_t i;
	if(FLASH_SUCCEED == flash_write(addr, len, arr))
	{
		if(FLASH_SUCCEED == flash_read(addr, len, readdata))
		{
			for(i = 0; i < len; i++){
				if(readdata[i] != arr[i])
					return FALSE;
			}
			return TRUE;
		}else
		{
			return FALSE;
		}			
	}
	else
	{
		return FALSE;
	}
}

bool Flash_get_data_byte(uint32_t addr, uint32_t len, uint8_t *arr) {
	uint32_t result;
	result = flash_read(addr, len, arr);
	if(result == FLASH_SUCCEED)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

#define MIN_STORAGE 0x1000
static uint8_t cpybuf[MIN_STORAGE];
bool Flash_clean_long(uint32_t addr, uint32_t len){
	uint32_t bgn = addr / 0x1000 * 0x1000;
	uint32_t n1, n2, mid, longlen;
	longlen = 4*len;
	n1 = addr - bgn;
	mid = bgn + n1 + longlen;
	n2 = MIN_STORAGE - n1 - longlen;
	Flash_get_data_byte(bgn, MIN_STORAGE, cpybuf); // copy
	flash_erase(bgn,0x1000); // clean
	Flash_set_data_byte(bgn, n1, cpybuf);
	Flash_set_data_byte(mid, n2, cpybuf + n1 + longlen);
	return TRUE;
}

bool Flash_clean_bytes(uint32_t addr, uint32_t len){
	uint32_t bgn = addr / 0x1000 * 0x1000;
	uint32_t n1, n2, mid;
	n1 = addr - bgn;
	mid = bgn + n1 + len;
	n2 = MIN_STORAGE - n1 - len;
	Flash_get_data_byte(bgn, MIN_STORAGE, cpybuf); // copy
	flash_erase(bgn,0x1000); // clean
	Flash_set_data_byte(bgn, n1, cpybuf);
	Flash_set_data_byte(mid, n2, cpybuf+n1+len);
	return TRUE;
}



