/***********************************************************************/
/*  This file is part of the ARM Toolchain package                     */
/*  Copyright KEIL ELEKTRONIK GmbH 2003 - 2006                         */
/***********************************************************************/
/*                                                                     */
/*  FlashDev.C:  Adapted by NXP                                        */
/*                                                                     */
/*               Programming functions adapted for SST39x3201/3201B    */
/*               16-bit flash on the LPC1800/4300 board from Hitex.    */
/*               The flash ID is used to select the correct commands.  */
/*               This applies to Rev A2/A3/A4 boards.                  */
/*                                                                     */
/***********************************************************************/

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


// Inlcude them only if you intend to do LPC18xx/43xx specific things
//#include "lpc43xx.h"             
//#include "config.h"


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



/* Parameter definitions */
union fsreg 	// Flash Status Register
{                  
  struct b  
  {
    unsigned int q0:1;
    unsigned int q1:1;
    unsigned int q2:1;
    unsigned int q3:1;
    unsigned int q4:1;
    unsigned int q5:1;
    unsigned int q6:1;
    unsigned int q7:1;
  } b;
  unsigned int v;
} fsr;


unsigned long base_adr;
unsigned short flash_id;

#define SST39VF3201     0
#define SST39VF3201B    1




/************************************************************************
 * Check if Program/Erase completed
 *    Parameter:      adr:  Block Start Address
 *    Return Value:   0 - OK,  1 - Failed
 ************************************************************************/

int Polling (unsigned long adr) 
{
  unsigned int q6;

  // Check Toggle Bit
  do 
  {
    fsr.v = M8(adr);
    q6 = fsr.b.q6;
    fsr.v = M8(adr);
  } 
  while (fsr.b.q6 != q6);

  return (0);              // Done

}


/************************************************************************
 *  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) 
{	
	unsigned short device_id;
    unsigned short manufacturer_id;

    volatile int i;
    
    base_adr = adr;

	// The bootloader inside the LPC1800 and LPC4300 starts the PLL for all
    // possible bootmodes with a frequency of 96 MHz.
    // So nothing must be done here. 
    
    // Disable interrupts on Cortex-M3 level
	//__disable_irq();

    // The configuration of the pins for the external memory interface is done in the
    // script file (LPC18xx_43xx_ExtFlashProg.ini).
    // This more in line with the philosophy of the KEIL flash tool.

    /*
    // Disable the external memory controller before changing pin control configuration
    LPC_EMC->CONTROL = 0x00000000;

	// Set all EMI related pins to the correct function	
    // Data bus (16 bits wide)
	scu_pinmux(1,7,MD_PLN_FAST,FUNC3);  // D0
	scu_pinmux(1,8,MD_PLN_FAST,FUNC3);  // D1
	scu_pinmux(1,9,MD_PLN_FAST,FUNC3);  // D2
	scu_pinmux(1,10,MD_PLN_FAST,FUNC3); // D3
	scu_pinmux(1,11,MD_PLN_FAST,FUNC3); // D4
	scu_pinmux(1,12,MD_PLN_FAST,FUNC3); // D5
	scu_pinmux(1,13,MD_PLN_FAST,FUNC3); // D6
	scu_pinmux(1,14,MD_PLN_FAST,FUNC3); // D7
	scu_pinmux(5,4,MD_PLN_FAST,FUNC2);  // D8
	scu_pinmux(5,5,MD_PLN_FAST,FUNC2);  // D9
	scu_pinmux(5,6,MD_PLN_FAST,FUNC2);  // D10
	scu_pinmux(5,7,MD_PLN_FAST,FUNC2);  // D11
	scu_pinmux(5,0,MD_PLN_FAST,FUNC2);  // D12
	scu_pinmux(5,1,MD_PLN_FAST,FUNC2);  // D13
	scu_pinmux(5,2,MD_PLN_FAST,FUNC2);  // D14
	scu_pinmux(5,3,MD_PLN_FAST,FUNC2);  // D15

	// Address bus
	scu_pinmux(2,9,MD_PLN_FAST,FUNC3);  // A0 (not used)
	scu_pinmux(2,10,MD_PLN_FAST,FUNC3); // A1 connected to A0 on flash
	scu_pinmux(2,11,MD_PLN_FAST,FUNC3); // A2
	scu_pinmux(2,12,MD_PLN_FAST,FUNC3); // A3
	scu_pinmux(2,13,MD_PLN_FAST,FUNC3); // A4
	scu_pinmux(1,0,MD_PLN_FAST,FUNC2);  // A5
	scu_pinmux(1,1,MD_PLN_FAST,FUNC2);  // A6
	scu_pinmux(1,2,MD_PLN_FAST,FUNC2);  // A7
	scu_pinmux(2,8,MD_PLN_FAST,FUNC3);  // A8
	scu_pinmux(2,7,MD_PLN_FAST,FUNC3);  // A9
	scu_pinmux(2,6,MD_PLN_FAST,FUNC2);  // A10
	scu_pinmux(2,2,MD_PLN_FAST,FUNC2);  // A11
	scu_pinmux(2,1,MD_PLN_FAST,FUNC2);  // A12
	scu_pinmux(2,0,MD_PLN_FAST,FUNC2);  // A13
	scu_pinmux(6,8,MD_PLN_FAST,FUNC1);  // A14
	scu_pinmux(6,7,MD_PLN_FAST,FUNC1);  // A15
	scu_pinmux(0xD,16,MD_PLN_FAST,FUNC2);  // A16
	scu_pinmux(0xD,15,MD_PLN_FAST,FUNC2);  // A17
	scu_pinmux(0xE,0,MD_PLN_FAST,FUNC3);   // A18
	scu_pinmux(0xE,1,MD_PLN_FAST,FUNC3);   // A19
	scu_pinmux(0xE,2,MD_PLN_FAST,FUNC3);   // A20
    scu_pinmux(0xE,3,MD_PLN_FAST,FUNC3);   // A21


	// Control signals for the flash on CS0 and the SRAM on CS2
	scu_pinmux(1,6,MD_PLN_FAST,FUNC3);     // WE - Write Enable
	scu_pinmux(1,3,MD_PLN_FAST,FUNC3);     // OE - Output Enable
	scu_pinmux(1,5,MD_PLN_FAST,FUNC3);     // CS0 - Chip Select 0
	//scu_pinmux(0xD,12,MD_PLN_FAST,FUNC2);  // CS2 - Chip Select 2
    //scu_pinmux(1,4,MD_PLN_FAST,FUNC3);     // BLS0 - Byte Lane Select 0	
    //scu_pinmux(6,6,MD_PLN_FAST,FUNC1);     // BLS1 - Byte Lane Select 1
    
    LPC_EMC->CONTROL = 0x00000001;            // Re-enable the external memory controller 
                                              // before the configuration of the waitstates
     
    // Configure CS0 for 70ns flash memory
    // @96MHz there should be 7 waitstates for the 70ns flash, let's be conservative and work with 9
    LPC_EMC->STATICCONFIG0 |= 0x00080081;     // CS0: 16 bit = WE
    LPC_EMC->STATICWAITOEN0 = 0;              // CS0: WAITOEN = 0
    LPC_EMC->STATICWAITRD0 = 9;               // CS0: WAITRD = 9 

    // The Hitex board has external SRAM on CS2
    // @72MHz there should be 6 waitstates, let's be conservative and set 7
    //LPC_EMC->STATICWAITOEN2 = 0;              // CS2: WAITOEN = 0
    //LPC_EMC->STATICWAITRD2 = 7;               // CS2: WAITRD = 7
	*/

    // ===========  Check for the external flash type  ============
    // On the Hitex Rev. A2 and newer the following two flash types are used:
    //  SST39VF3201  - flash_id = 0x235B 
    //  SST39VF3201B - flash_id = 0x235D 

    // Try it with the commands for the 3201 first: enter the software ID mode
    // The soft-loops between the memory writes are required, to be investigated why.
    // Thew sift-by-1 is required because A1 from the LPC4300 is the first address line
    // which is connected to the 16-bit flash.
    M16(base_adr + (0x5555 << 1)) = 0x00AA;
    for( i = 0; i < 10; i++);
    M16(base_adr + (0x2AAA << 1)) = 0x0055;
    for( i = 0; i < 10; i++);
    M16(base_adr + (0x5555 << 1)) = 0x0090;
    for( i = 0; i < 10; i++);

    // Read manufacturer ID on flash address 0
    manufacturer_id = M16(base_adr);

    if (manufacturer_id == 0x00BF)
    {
        // Getting the correct manufacturer ID would in principle be enough,
        // however, read the device ID as well
        device_id = M16(base_adr + (1 << 1));
        if (device_id == 0x235B)
        {
            flash_id = SST39VF3201;

            // Exit the software Id mode
            M16(base_adr) = 0xF0;
            // Return with an OK
            return(0);
        }
    }

    // Try it next with the commands for the 3201B: enter the software ID mode
    // The soft-loops between the memory writes are required, to be investigated why 
    M16(base_adr + (0x555 << 1)) = 0x00AA;
    for( i = 0; i < 10; i++);
    M16(base_adr + (0x2AA << 1)) = 0x0055;
    for( i = 0; i < 10; i++);
    M16(base_adr + (0x555 << 1)) = 0x0090;
    for( i = 0; i < 10; i++);

    // Read manufacturer ID on flash address 0
    manufacturer_id = M16(base_adr);

    if (manufacturer_id == 0x00BF)
    {
        // Getting the correct manufacturer ID would in principle be enough,
        // however, read the device ID as well
        device_id = M16(base_adr + (1 << 1));

        if (device_id == 0x235D)
        {
            flash_id = SST39VF3201B;

            // Exit the software Id mode
            M16(base_adr) = 0xF0;
            // Return with an OK
            return(0);
        }
    }
    
    // If both attempts failed exit the software Id mode ...
    M16(base_adr) = 0xF0;

    // ... and return with error
    return(1);
}



/************************************************************************
 *  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) 
{
  	// Nothing implemented
    return (0);
}



/************************************************************************
 *  Erase complete Flash Memory
 *    Return Value:   0 - OK,  1 - Failed
 ************************************************************************/

int EraseChip (void) 
{

  if (flash_id == SST39VF3201B)
  {
      // Start Chip Erase Command
      M16(base_adr + (0x555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AA << 1)) = 0x0055;
      M16(base_adr + (0x555 << 1)) = 0x0080;
      M16(base_adr + (0x555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AA << 1)) = 0x0055;
      M16(base_adr + (0x555 << 1)) = 0x0010;
  }
  else     // SST39VF3201
  {
      // Start Chip Erase Command
      M16(base_adr + (0x5555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AAA << 1)) = 0x0055;
      M16(base_adr + (0x5555 << 1)) = 0x0080;
      M16(base_adr + (0x5555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AAA << 1)) = 0x0055;
      M16(base_adr + (0x5555 << 1)) = 0x0010;
  }

  return (Polling(base_adr));  // Wait until Erase completed
}


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

int EraseSector (unsigned long adr) 
{

  if (flash_id == SST39VF3201B)
  {
      // Start Erase Sector Command
      M16(base_adr + (0x555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AA << 1)) = 0x0055;
      M16(base_adr + (0x555 << 1)) = 0x0080;
      M16(base_adr + (0x555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AA << 1)) = 0x0055;
      M16(adr) = 0x0050;
  }
  else  // SST39VF3201
  {
      // Start Erase Sector Command
      M16(base_adr + (0x5555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AAA << 1)) = 0x0055;
      M16(base_adr + (0x5555 << 1)) = 0x0080;
      M16(base_adr + (0x5555 << 1)) = 0x00AA;
      M16(base_adr + (0x2AAA << 1)) = 0x0055;
      M16(adr) = 0x0030;
  }

  return (Polling(adr));       // Wait until Erase completed
}


/************************************************************************
 *  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) 
{
  int i;


  if (flash_id == SST39VF3201B)
  {
      for (i = 0; i < ((sz+1)/2); i++)  
      {
	      // Start Program Command
          M16(base_adr + (0x555 << 1)) = 0x00AA;
          M16(base_adr + (0x2AA << 1)) = 0x0055;
          M16(base_adr + (0x555 << 1)) = 0x00A0;
          M16(adr) = *((unsigned short *) buf);

          if (Polling(adr) != 0) return (1);
          buf += 2;
          adr += 2;
      }
  }
  else    // SST39VF3201
  {
      for (i = 0; i < ((sz+1)/2); i++)  
      {
	      // Start Program Command
          M16(base_adr + (0x5555 << 1)) = 0x00AA;
          M16(base_adr + (0x2AAA << 1)) = 0x0055;
          M16(base_adr + (0x5555 << 1)) = 0x00A0;
          M16(adr) = *((unsigned short *) buf);
          if (Polling(adr) != 0) return (1);
          buf += 2;
          adr += 2;
      }
  }

  return (0);
}

