/*=================================< flash.c >===============================*/
//    
//    Title:      FLASH UNIT MAIN SOURCE FILE
//    Saved date: 02 Jun 2003 11:50:29
//    Written by: JAG
//    Project:    RMS101 SBC
//    Target:     LH79520
//    Compiler:   GNU GCC for ARM
//		Copyright:	Revely code subject to the Revely Open Source License 
//						Sharp code copyright Sharp 
//
// These flash driver functions are adapted from LH28F320BF.c by Sharp.
// To make things a little easier to use, a series of higher level functions
// have been added.
//
// Actually, this whole unit could be tidier. Update is planned soon.
//
// Unless remapped, the RMS101's Flash occupies the range (40000000-0x403ffffe)
//
//	Note : Intel Flash Devices do not support chip erase function, but Sharp
//			 devices do.
//
// V1.00	10-30-02 JAG 	Initial release
//	V1.01	05-02-03 JAG	Minor mods to support older 320BJE parts
// 
/*===========================================================================*/

#include <stddefs.h> 
#include <flash.h>

/*-----------------------< Block Addresses as an array >--------------------*/

/* Use these for LH28F320 */
const WORD FlashBlockAdd[] = 
{
	0x000000, 0x002000, 0x004000, 0x006000, 0x008000, 0x00a000, 	// 0-5
	0x00c000, 0x00e000, 0x010000, 0x020000, 0x030000, 0x040000,		// 6-11
	0x050000, 0x060000, 0x070000, 0x080000, 0x090000, 0x0a0000,  	// 12-17
	0x0b0000, 0x0c0000, 0x0d0000, 0x0e0000, 0x0f0000, 0x100000,  	// 18-23
	0x110000, 0x120000, 0x130000, 0x140000, 0x150000, 0x160000,  	// 24-29
	0x170000, 0x180000, 0x190000, 0x1a0000, 0x1b0000, 0x1c0000,  	// 30-35
	0x1d0000, 0x1e0000, 0x1f0000, 0x200000, 0x210000, 0x220000,  	// 36-41
	0x230000, 0x240000, 0x250000, 0x260000, 0x270000, 0x280000,  	// 42-47
	0x290000, 0x2a0000, 0x2b0000, 0x2c0000, 0x2d0000, 0x2e0000,  	// 48-53
	0x2f0000, 0x300000, 0x310000, 0x320000, 0x330000, 0x340000,  	// 54-59
	0x350000, 0x360000, 0x370000, 0x380000, 0x390000, 0x3a0000,  	// 60-65
	0x3b0000, 0x3c0000, 0x3d0000, 0x3e0000, 0x3f0000				  	// 66-70
};


void read_array(HALF *address);
HALF word_write(HALF *address, HALF data);
HALF block_erase(HALF *address);
HALF all_block_erase(HALF *address);
HALF set_block_lock(HALF *address);
HALF clear_block_lock(HALF *address);
HALF set_block_lockdown(HALF *address);
HALF multi_word_write(HALF *address, HALF *data, int num_of_data);
void multi_word_write_sub(HALF *address, HALF *data, int num_of_data);
void write_suspend_to_read(HALF *address, HALF *result);
void erase_suspend_to_read(HALF *address, HALF *result);
int CSR_full_status_check(HALF CSR);
void read_id_codes(HALF *base_add, HALF *Maker_code, HALF *Device_code);
int check_id_codes(HALF *base_add);
HALF read_block_info(HALF *block_add);
void set_pcr(HALF *base_add, int pcv);
int read_pcr_info(HALF *base_add);


/*------------------------------< FlashChipErase >---------------------------*/
void FlashChipErase(void) 
{
	all_block_erase((PHALF) FLASH_BASEADD);
	read_array((PHALF) FLASH_BASEADD);		// back to normal mode
}

/*-----------------------------< FlashBlockErase >---------------------------*/
void FlashBlockErase(BYTE bFirstBlock, BYTE bLastBlock)
{
	while (bFirstBlock <= bLastBlock)
	{
		block_erase((PHALF) (FLASH_BASEADD + FlashBlockAdd[bFirstBlock]));
		bFirstBlock++;
	}
	read_array((PHALF) FLASH_BASEADD);		// back to normal mode
}

/*-----------------------------< FlashWriteHalf >---------------------------*/
void FlashWriteHalf(WORD wAddr, HALF hData)
{
	word_write((PHALF)wAddr, hData);
	read_array((PHALF) FLASH_BASEADD);		// back to normal mode
}

/*------------------------------< FlashLockAll >----------------------------*/
void FlashLockAll(void)
{
	BYTE b;
	
	//for (b=0; b < FLASH_BLOCKS; b++)
	//	set_block_lock((PHALF) (FLASH_BASEADD + FlashBlockAdd[b]));
	read_array((PHALF) FLASH_BASEADD);		// back to normal mode	
}

/*----------------------------< FlashUnlockAll >----------------------------*/
void FlashUnlockAll(void)
{
	BYTE b;
	
	//for (b=0; b < FLASH_BLOCKS; b++)
	// In LH28F320BJE one command clear locks on all blocks
	clear_block_lock((PHALF) (FLASH_BASEADD + FlashBlockAdd[0]));	
	read_array((PHALF) FLASH_BASEADD);		// back to normal mode
}

	
/*=====================< Start of Code from Sharp >=======================*/

/***************************************************************************
*
*   Function : read_array
*
*   Input    : address      flash address
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Change read array mode
*
***************************************************************************/
void read_array(HALF *address)
{
	volatile PHALF ptr;
	
	ptr = address;     
   *ptr = CMD_READ_ARRAY;
}

/********************************************************************
*
*   Function : word_write
*
*   Input    : address      write address
*              data         write data
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Word write function
* 
*	       In this function, after Word/Byte write command 
*              executes, operation idles until WSM is ready.
*	       Then function returns CSR value.
*
********************************************************************/
HALF word_write(HALF *address, HALF data)
{
    HALF CSR, wOld;
    volatile unsigned short *ptr;

    ptr = address;
    wOld = ~*ptr ; 
    /* Actual data write to flash address */
    // Don't burn down bits if they're already zeros
    *ptr = CMD_DATA_WRITE;
    *ptr = data | wOld; 
     
    /* Read Status Registers command */
    *ptr = CMD_READ_STATUS;
    /* Poll CSR until CSR.7 = 1 (WSM ready) */
    while(!(*ptr & CSR_WSM_STATUS_READY))
    {
        /* System may issue an write suspend command (B0[B0])
           here to read data from a different address. */
    }

    /* Save CSR before clearing it */
    CSR = *ptr;
    /* Clear Status Registers command */
    *ptr = CMD_CLEAR_STATUS;

    /* Return CSR to be checked for status of operation. */
    return CSR;
}

/***************************************************************************
*
*   Function : block_erase
*
*   Input    : address       address of Erase Block
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Block erase function 
*
***************************************************************************/
HALF block_erase(HALF *address)
{
    HALF CSR;
    volatile PHALF pPtr;
    
    pPtr = address; 

    /* Single Block Erase command */
    *pPtr = CMD_BLOCK_ERASE;
    /* Confirm command */
    *pPtr = CMD_CONFIRM;

    /* Read CSR comamnd */
    *pPtr = CMD_READ_STATUS;
    /* Poll CSR until CSR.7 = 1 (WSM ready) */
    while(!(*pPtr & CSR_WSM_STATUS_READY))
    {
        /* System may issue an erase suspend command (B0[B0])
           here to read data from a different block. */
    }

    /* Save CSR before clearing it */
    CSR = *pPtr;
    /* Clear CSR command */
    *pPtr = CMD_CLEAR_STATUS;

    /* Return CSR to be checked for status of operation. */
    return CSR; 
}

/***************************************************************************
*
*   Function : all_block_erase
*
*   Input    : address      flash address
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 1998/08/26
*                   release
*  
*   Content  : Full chip erase function
*
*              All unlocked blocks are erased.
*
***************************************************************************/
HALF all_block_erase(HALF *address)
{ 
    HALF CSR;
    volatile PHALF pPtr;

	 pPtr = address;
	
    /* All block erase command */
    *pPtr = CMD_FULL_CHIP_ERASE;
    /* Confirm command */
    *pPtr = CMD_CONFIRM;

    /* Read CSR comamnd */
    *pPtr = CMD_READ_STATUS;
    /* Poll CSR until CSR.7 = 1 (WSM ready) */
    while(!(*pPtr & CSR_WSM_STATUS_READY))
    {
        /* System may issue an erase suspend command (B0[B0])
           here to read data from a different block. */
    }

    /* Save CSR before clearing it */
    CSR = *pPtr;
    /* Clear CSR command */
    *pPtr = CMD_CLEAR_STATUS;

    /* Return CSR to be checked for status of operation. */
    return CSR;
}

/***************************************************************************
*
*   Function : set_block_lock
*
*   Input    : address      lock address
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 2001/03/29
*                   release
*
*   Content  : Set block lock bit function
*
***************************************************************************/
HALF set_block_lock(HALF *address)
{
    HALF CSR;
    volatile PHALF pPtr;

	 pPtr = address;
	 
    /* Set block lock-bit setup command */
    *pPtr = CMD_BLOCK_LOCK;
    /* Set block lock-bit confirm command */
    *pPtr = CMD_SET_LOCKBIT;

    /* Read CSR comamnd */
    *pPtr = CMD_READ_STATUS;
    /* Save CSR before clearing it */
    CSR = *pPtr;

    /* Clear CSR command */
    *pPtr = CMD_CLEAR_STATUS;

    /* Return CSR to be checked for status of operation. */
    return CSR;
}

/***************************************************************************
*
*   Function : clear_block_lock
*
*   Input    : address      flash address
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 2001/03/29
*                   release 
*
*   Content  : Clear block lock bit function
*
***************************************************************************/
HALF clear_block_lock(HALF *address)
{
    HALF CSR;
    volatile unsigned short *ptr;

		ptr = address;  
	
   /* Set block lock-bit setup command */
   *ptr = CMD_BLOCK_LOCK;
    /* Clr block lock-bit confirm command */
 	*ptr = CMD_RESET_LOCKBIT;

   /* Read CSR comamnd */
   *ptr = CMD_READ_STATUS;
   
   /* Poll CSR until CSR.7 = 1 (WSM ready) */
   while(!(*ptr & CSR_WSM_STATUS_READY));
   
   /* Save CSR before clearing it */
   CSR = *ptr;

   /* Clear CSR command */
   *ptr = CMD_CLEAR_STATUS;

   /* Return CSR to be checked for status of operation. */
   return CSR;
}

/***************************************************************************
*
*   Function : set_block_lockdown
*
*   Input    : address      lockdown address
*
*   Output   : CSR to  be checked for status of operation
*
*   Revision : 1.0, 2001/03/29
*                   release
*
*   Content  : Set block lockdown bit
*
***************************************************************************/
HALF set_block_lockdown(HALF *address)
{
    HALF CSR;
    volatile unsigned short *ptr;

	ptr = address;  

   /* Set block lock-bit setup command */
   *ptr = CMD_BLOCK_LOCK;
   /* Set block lock-bit confirm command */
   *ptr = CMD_SET_LOCKDOWNBIT;

   /* Read CSR comamnd */
   *ptr = CMD_READ_STATUS;
    
   /* Poll CSR until CSR.7 = 1 (WSM ready) */
	while(!(*ptr & CSR_WSM_STATUS_READY));
    
   /* Save CSR before clearing it */
   CSR = *ptr;

   /* Clear CSR command */
   *ptr = CMD_CLEAR_STATUS;

   /* Return CSR to be checked for status of operation. */
    return CSR;
}

/***********************************************************************
*
*   Function : multi_word_write
*
*   Input    : address       write address
*              data          write data pointer
*              num_of_data   write data count
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 1999/12/29
*                   release
*
*   Content  : Write multi word data into the flash memory
*
************************************************************************/
HALF multi_word_write(HALF *address, HALF *data, int num_of_data)
{
   HALF CSR;              /* CSR data */
   HALF *tmp_add;         /* work address */
   int cnt_data;           /* data counter*/

   tmp_add = address;
   cnt_data = (MAX_MULTI_WRITE_NUM - ((int)tmp_add & MASK_MULTI_WRITE) / sizeof(HALF));

   if( 0 != cnt_data )
   {
       multi_word_write_sub(tmp_add, data, cnt_data);
       tmp_add += cnt_data;
       data += cnt_data;
   }
   while(cnt_data + MAX_MULTI_WRITE_NUM < num_of_data)
   {
       multi_word_write_sub(tmp_add, data, MAX_MULTI_WRITE_NUM);
       tmp_add += MAX_MULTI_WRITE_NUM;
       data += MAX_MULTI_WRITE_NUM;
       cnt_data += MAX_MULTI_WRITE_NUM;
   }
   cnt_data = num_of_data - cnt_data;
   
   if(0 < cnt_data)
       multi_word_write_sub(tmp_add, data, cnt_data);

   /* Read CSR comamnd */
   *address = CMD_READ_STATUS;
   /* Poll CSR until CSR.7 = 1 (WSM ready) */
   while(!(*address & CSR_WSM_STATUS_READY));

   /* Store CSR data */
   CSR = *address;
   /* Clear CSR command */
   *address = CMD_CLEAR_STATUS;

	/* Return CSR to  be checked for status of operation. */
 	return CSR;
}

/***********************************************************************
*
*   Function : multi_word_write_sub
*
*   Input    : address      write address
*              data         write data pointer
*              word_count   write data count
*
*   Output   :
*
*   Revision : 1.0, 1999/11/29
*                   release
*
*   Content  : Write multi word data into the flash memory
*
************************************************************************/
void multi_word_write_sub(HALF *address, HALF *data, int num_of_data)
{
    HALF *end_max_add;

    /* Multi write command setup */
    *address = CMD_MULTI_DATA_WRITE;

    /* Poll XSR until CSR.7 = 1 (PageBuffer is not full) */
    while(!(*address & XSR_MULTI_WRITE_READY)) {
        *address = CMD_MULTI_DATA_WRITE;
    }

    /* Data size setup */
    *address = num_of_data - 1;

    /* Data setup */
    end_max_add = address + num_of_data;
    while(address < end_max_add) {
        *(address++) = *(data++);
    }

    /* Confirm command */
    *address = CMD_CONFIRM;
}

/********************************************************************
*
*   Function : write_suspend_to_read
*
*   Input    : address      read address
*              result       read data pointer
*
*   Output   : None
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Word read while write suspend mode
*
*              In case of executing write suspend or resume command,
*	           that will be ignored when the write operation is completed.
*     	       Status register keeps status since write operation is executed.
*              Word write function of this driver does not 
*	           execute another task until function completes.
*              Therefore, when using this function, function call operation 
*	           must be described separately at WSM poling of erase function
*              part in word write function.
*
********************************************************************/
void write_suspend_to_read(HALF *address, HALF *result)
{
    /* Word Write suspend command */
    *address = CMD_SUSPEND;

    *address = CMD_READ_STATUS;
    /* Poll CSR until CSR.7 = 1 (WSM ready) */
    while(!(*address & CSR_WSM_STATUS_READY))
        ;

    /* if CSR.2 = 1 (Word Write incomplete) */
    if(*address & CSR_WRITE_SUSPEND)
    {
        /* SUSPEND MODE */
        /* Read Flash Array command */
        *address = CMD_READ_ARRAY;
        /* Do the actual read */
        *result = *address;
        /* Word Write resume command */
        *address = CMD_RESUME;
    } else {
        /* WORD WRITE COMPLETED */
        /* Read Flash Array command */
        *address = CMD_READ_ARRAY;
        /* Do the actual read */
        *result = *address;
    }
}

/***********************************************************
*
*   Function : erase_suspend_to_read
*
*   Input    : address          read address
*              result           read data pointer
*
*   Output   : CSR to be checked for status of operation
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Word read while Erase suspend mode
*
*              In case of executing erase suspend or resume command,
*	           that will be ignored when the erase operation is completed.
*     	       Status register keeps status since erase operation is executed.
*              Word write function of this driver does not 
*	           execute another task until function completes.
*              Therefore, when using this function, function call operation 
*	           must be described separately at WSM poling of erase function
*              part in word write function.
*
**********************************************************/
void erase_suspend_to_read(HALF *address, HALF *result)
{
    /* Erase Suspend cmmand */
    *address = CMD_SUSPEND;

    *address = CMD_READ_STATUS;
    /* Poll CSR until CSR.7 = 1 (WSM ready) */
    while(!(*address & CSR_WSM_STATUS_READY))
        ;

    /* if CSR.6 = 1 (erase incomplete) */
    if(*address & CSR_ERASE_SUSPEND)
    {
        /* SUSPEND MODE */
        /* Read Flash Array command */
        *address = CMD_READ_ARRAY;
        /* Do the actual read */
        *result = *address;
        /* Erase Resume command */
        *address = CMD_RESUME;
        /* Read CSR comamnd */
        *address = CMD_READ_STATUS;
    } else {
        /* ERASE COMPLETED */
        /* Read Flash Array command */
        *address = CMD_READ_ARRAY;
        /* Do the actual read */
        *result = *address;
        /* Read CSR comamnd */
        *address = CMD_READ_STATUS;
    }
}

/***************************************************************************
*
*   Function : CSR_full_status_check
*
*   Input    : CSR           value of status register
*
*   Output   : Error code
*               0 ERR_NO_ERROR
*               1 ERR_LOW_VPP
*               2 ERR_BLOCK_LOCKED
*               3 ERR_COMMAND_SEQ_ERROR
*               4 ERR_WP_LOW
*               5 ERR_WRITE
*               6 ERR_ERASE
*               7 ERR_WRITE_ERASING_BLOCK
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Exchange CSR value to Error Codes function
*
***************************************************************************/
int CSR_full_status_check(HALF CSR)
{
    int errcode;

    if(CSR & CSR_LOW_VPP)
    /* Vpp Low detect */
        errcode = ERR_LOW_VPP;
    else if(CSR & CSR_DEVICE_PROTECT)
    /* Master Lock-bit, Block Lock-bit and/or RP# Lock Detected */
        errcode = ERR_BLOCK_LOCKED;
    else if((CSR & CSR_WRITE_ERROR) && (CSR & CSR_ERASE_ERROR))
    /* Mistake Command Sequence */ /* Program error check */
        errcode = ERR_COMMAND_SEQ_ERROR;
    else if((CSR & CSR_WRITE_ERROR) && (CSR & CSR_ERASE_SUSPEND))
    /* write error or Write data on Erasing Block */
        errcode = ERR_WRITE_ERASING_BLOCK;
    else if(CSR & CSR_WRITE_ERROR)
    /* Write error */
        errcode = ERR_WRITE;
    else if(CSR & CSR_ERASE_ERROR)
    /* Block erase error */
        errcode = ERR_ERASE;
    else
        errcode = ERR_NO_ERROR;

    return errcode;
}

/***************************************************************************
*
*   Function : read_id_codes
*
*   Input    : base_add         flash base address
*            : Maker_code       pointer of maker code
*            : Device_code      pointer of device code
*  
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Read device code and manufacturer code function
*
*	Note : We can't access the device code on a 16 bit bus since 
*
***************************************************************************/
void read_id_codes(HALF *base_add, HALF *Maker_code, HALF *Device_code)
{
    /* Read identifier codes command */
    *base_add = CMD_READ_ID_CODE;

    /* Store Maker code */
    *Maker_code = *base_add;

	 /* Store Device code */
    *Device_code = *(base_add + ID_DEVICE_OFFSET);
}

/***************************************************************************
*
*   Function : check_id_codes
*
*   Input    : base_add         flash base address
*
*   Output   : OK(0),fail(-1)
*
*   Revision : 1.0, 1998/08/26
*                   release
*
*   Content  : Check device code and manufacturer code function
*
*              If Both values confirm defines, then return "0",
*	           else return "-1".
*
***************************************************************************/
int check_id_codes(HALF *base_add)
{

    /* Read identifier codes command */
    *base_add = CMD_READ_ID_CODE;

    /* check identifier codes */
    if( (ID_MAKER_CODE  == *base_add)
      &&(ID_DEVICE_CODE == *(base_add + ID_DEVICE_OFFSET)) )
    {
        return 0;
    } else {
        return -1;
    }
}

/***************************************************************************
*
*   Function : read_block_info
*
*   Input    : block_add         flash block base address
*
*   Output   : status bit
*
*   Revision : 1.0, 2001/03/29
*                   release
*
*   Content  : Read block lock bit and block lockdown bit
*
***************************************************************************/
HALF read_block_info(HALF *block_add)
{

    /* Read identifier codes command */
    *block_add = CMD_READ_ID_CODE;

    /* check block lock bit & block lockdown bit */
    return *(block_add + ID_BLOCK_OFFSET); 
}

/***************************************************************************
*
*   Function : set_pcr
*
*   Input    : base_add      flash base address
*              pcv           partition configuration value [0-7]
*
*   Output   :
*
*   Revision : 1.0, 2001/03/29
*                   release
*
*   Content  : Set partition configuration register
*
***************************************************************************/
void set_pcr(HALF *base_add, int pcv)
{
    volatile HALF *pcrc;

    pcrc = base_add + (pcv<<8);		// Fixed bug in this line - JAG
	
    /* Set partition configuration register setup command */
    *pcrc = CMD_SET_PCR;
    /* Set confirm command block_add*/
    *pcrc = CMD_PCR_CONFIRM;
}

/***************************************************************************
*
*   Function : read_pcr_info
*
*   Input    : base_add      flash base address
*
*   Output   : partition configuration value [0-7]
*
*   Revision : 1.0, 2001/03/29
*                   release
*
*   Content  : Read partition configuration register
*
***************************************************************************/
int read_pcr_info(HALF *base_add)
{
    int pcv;

    /* Read identifier codes command */
    *base_add = CMD_READ_ID_CODE;

    pcv = ((*(base_add + ID_PCR_OFFSET))>>8) & 0x07;

    /* check partition configuration register */
    return pcv;
}
