/***********************************************************************/
/*  This file is part of the ARM Toolchain package                     */
/*  Copyright KEIL ELEKTRONIK GmbH 2003 - 2007                         */
/***********************************************************************/
/*                                                                     */
/*  FlashDev.C:  Flash Programming Functions adapted for               */
/*               Sharp LH28F128SPH (16-bit Bus) on the                 */
/*               LPC1800/LPC4300 Eval Board v2.3			    	   */
/*                                                                     */
/***********************************************************************/

#include "..\FlashOS.H"         // FlashOS Structures

#define M8(adr)  (*((volatile unsigned char  *) (adr)))
#define M16(adr) (*((volatile unsigned short *) (adr)))
#define M32(adr) (*((volatile unsigned long  *) (adr)))

#define STACK_SIZE   64         // Stack Size


int lastBlock;


/*
 *  Initialize Flash Programming Functions
 *    Parameter:      adr:  Device Base Address
 *                    clk:  Clock Frequency (Hz)
 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
{
    M32(0x40086400) = 3;
    M32(0x40086404) = 3;
    M32(0x40086408) = 3;
    M32(0x4008640C) = 3;
    M32(0x40086410) = 3;
    M32(0x40086414) = 3;
    M32(0x40086418) = 3;
    M32(0x4008641C) = 3;

    M8(0x400F0080) = 0xFF;
    M8(0x400F009C) = 0xFF;

    return 0;
}


/*
 *  De-Initialize Flash Programming Functions
 *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int UnInit (unsigned long fnc)
{
    M8(0x400F0080) = 0xFF;

    return 0;
}



int GetBlockFromAddress (unsigned long adr)
{
    /* We only need to know the offset inside the flash */
    adr = adr & 0x1FFFFF;

    
    if (adr < 0x1E0000) {
        // first case is true if block is in the first 15@64Kwords
        return (adr / 0x20000);
    }
    else if (adr < 0x1F0000) {
        // this is the only 32 Kwords block in the flash (block #15)
        return 15;
    }
    else {
        // else belongs to the last 8@8Kwords
        return (((adr - 0x1F0000) / 0x2000) + 16);  
    }
}



/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseSector (unsigned long adr) {
  unsigned char sr;

  M16(adr) = 0x60;              // Start Unlock Block Command
  M16(adr) = 0xD0;              // Confirm Command
  do {
    sr = M16(adr);              // Read Status Register
  } while (!(sr & (1 << 7)));   // until Flash is Ready
  M16(adr) = 0xFF;              // Revert to Read Mode

  M16(adr) = 0x20;              // Start Erase Block Command
  M16(adr) = 0xD0;              // Confirm Command
  do {
    sr = M16(adr);              // Read Status Register
  } while (!(sr & (1 << 7)));   // until Flash is Ready
  M16(adr) = 0x50;              // Clear Status Register
  M16(adr) = 0xFF;              // Revert to Read Mode
  if (sr & 0x3A) {              // Check for Errors
    return (1);                 // Failed
  } else {
    return (0);                 // OK
  }
}


/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */

int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{
    unsigned char sr;
    int block;


    lastBlock = -1;

    while (sz >= 2) {
        /* New block number? */
        block = GetBlockFromAddress(adr);
        if (block != lastBlock) {
            lastBlock = block;

            /* Unlock the new block */
            M16(adr) = 0x60;                    /* Start Unlock Block Command */
            M16(adr) = 0xD0;                    /* Confirm Command */
            do {
                sr = M16(adr);              
            } while (!(sr & (1 << 7)));         /* Wait until Flash is ready */
            M16(adr) = 0xFF;                    /* Revert to Read Mode */
        }

        M16(adr) = 0x40;                        /* Write command */
        M16(adr) = *((unsigned short *) buf);   /* Write 16-bit word */
        do {
          sr = M16(adr);
        } while (!(sr & (1 << 7)));             /* Wait until flash is ready */

        M16(adr) = 0x50;                        /* Clear Status Register */
        M16(adr) = 0xFF;                        /* Revert to Read Mode */

        if (sr & 0x3A)                          /* Stop in case of error */
        {
            M8(0x400F0094) = sr;
            break;
        }

        adr += 2;
        buf += 2;
        sz -= 2;
    }

    if (sr & 0x3A) {
        return 1;       /* Error */
    } else {
        return 0;       /* OK */
    }
}
