//===============================================================================
// EEProm Programming
// Pretty much per Freescale docs.
//
//===============================================================================
#ifndef byte
typedef unsigned char byte;
#endif
#ifndef word
typedef unsigned int word;
#endif

#ifndef GCC_TOOLCHAIN
#include <mc9s12dp256.h>
#endif

#define CBEIF     0x80    // Command Buffer Empty Interrupt Flag.
#define CCIF      0x40    // Command Complete Interrupt Flag.
#define PVIOL     0x20    // Protection Violation.
#define ACCERR    0x10    // EEPROM Access Error.
#define SectorEraseCmd 0x40
#define WordProgramCmd 0x20

//===============================================================================
// YOU MUST CALL THIS FIRST!!!
//===============================================================================
void InitEEProm() 
{
// note: This are derived from OSCCLK so PLL has no effect.  
 #ifdef miniDragon
 FCLKDIV = ((16000/200/8)-1)+0x40; // Flash CLK = 200 kHz
 ECLKDIV = ((16000/200/8)-1)+0x40; // Eeprom CLK = 200 kHz
#else
 FCLKDIV = ((4000/200)-1); // Flash CLK = 200 kHz
 ECLKDIV = ((4000/200)-1); // Eeprom CLK = 200 kHz
#endif
}
//===============================================================================
//===============================================================================
byte __ExecuteEECmd(word* address, word data, byte CMD) 
{
    *(address ) = data; 
    ECMD = CMD;                       // Set command
    ESTAT = CBEIF + PVIOL + ACCERR;   // clear flags and start command
    asm("nop ; nop ; nop");           // From freescale example: let cmd get started.
    
    // Loop until ESTAT status changes
    for(;;)
    {
      if( ESTAT &  (CCIF + PVIOL + ACCERR ) ) // Errors or Done ?
          break;    
    }
     
    return ESTAT & ( PVIOL + ACCERR);
}
//===============================================================================
// Address is d-word algined (bits 0 & 1 are 0)
// as we erase 4 bytes (that's minimum erase size)
//===============================================================================
byte EraseEESector(word* address ) 
{
   return __ExecuteEECmd(address, (word) 0xfff,SectorEraseCmd);
}
//===============================================================================
// Address is even meaning word algined (bit 0 is 0)
// - minimum burn size for part.
//===============================================================================
byte burnEEWord(word* address, word data ) 
{
   return __ExecuteEECmd(address,data, WordProgramCmd);
}
//===============================================================================
// Get the data we are not changing and save it.
// erase both words and burn them back.
//===============================================================================
byte WriteEEWord(word* address, word data ) 
{
    word temp[2];
    static volatile word index;
    temp[0] = *((word*)((word) address & 0xfffc));
    temp[1] =  *((word*)(((word) address & 0xfffc) + 2));
    index = ((word) address & 3) >> 1;
    temp[index] = data;
    
    EraseEESector((word*) ((word) address & 0xfffc)); 
    
    burnEEWord((word*)((word) address & 0xfffc), *((word*)(&temp[0])) );
    return burnEEWord((word*)(((word) address & 0xfffc)+2), *((word*)(&temp[1])) );
}

//===============================================================================
// Get 4 bytes of data - 3 that we are not burning.
// erase 4 bytes and burn them back.
//===============================================================================
byte WriteEEByte(byte* address, byte data )
{
    byte temp[4];
    int i;
    byte rc;
    // Read in all the data in this sector.
    char* p = (char*) ((word) address & 0xfffc);
    for( i = 0 ; i < 4 ; ++i )
       temp[i] = p[i];
    // Replace the byte we want to write to.   
    temp[((word) address & 3)] = data;
    // Erase the whole sector.
    rc = EraseEESector((word*)((word) address & 0xfffc) );   

    if( rc != 0 )
       return rc;
    
    // Write back the sector.
    burnEEWord((word*)((word) address & 0xfffc), *((word*)(&temp[0])) );
    return burnEEWord((word*)(((word) address & 0xfffc)+2), *((word*)(&temp[2])) );
}
//===============================================================================
// Do a byte by byte serialization of the type.
// This leaves the data right order for the "C" compiler.
// You can use a pointer to read and write it,
// or as a pointer to an array.
//===============================================================================
byte WriteEEType(byte* address,void* type_to_write, int size_in_bytes )
{
  byte* p = (byte*) address;
  byte* d = (byte*) type_to_write;
  int i;
  for( i = 0 ; i < size_in_bytes; ++i,++p)
     WriteEEByte( p, d[i]);       
  return 0;     
}

byte WriteEEFloat(float* address, float data )
{
  return WriteEEType((byte *) address, &data, sizeof(float) );
}
void TestEEProm()
{
//
// EEProm Sample Code.
static float floattest;
typedef struct _tstruct {
  byte b;
  word w;
  float f;
  char tag[5];
} tstruct;

// This is really just to copy into EEProm
tstruct structtest={0xaa,0xa5de, 45.5e1,"TEST"};

// Note that the real memory for this is "allocated" in EE.
tstruct *structtestP = (tstruct*)  0x430;

// A float array in EE.
static float* floatarray=(float*) 0x440;
   int i; 
//
// Examples of EEProm usage. 
//
 EraseEESector((word*)0x400);
 WriteEEWord((word*)0x400,0x5a5a);
 WriteEEWord((word*)0x402,0xdead);
 EraseEESector((word*)0x404);
 WriteEEWord((word*)0x404,0xbeef);
 WriteEEWord((word*)0x406,0xfeed);
 
 EraseEESector((word*)0x408);
 WriteEEByte((byte*)0x408,8); 
 WriteEEByte((byte*)0x409,9);
 WriteEEByte((byte*)0x40a,0xdd);
 WriteEEByte((byte*)0x40b,0xee);
 
 EraseEESector((word*)0x410);
 WriteEEFloat((float*)0x410, 45.6778F); 
 floattest = *((float*) 0x410);
 
 EraseEESector((word*)&floatarray[0]);
 WriteEEFloat(&floatarray[0], 1.234e2); 
 floattest = floatarray[0];

 EraseEESector((word*)&floatarray[0]);
 WriteEEFloat(&floatarray[1], 5.678e-2); 
 floattest = floatarray[1];

 EraseEESector((word*)structtestP);
 WriteEEType((byte *)structtestP, &structtest, sizeof(tstruct) ); 
 
 for(  i = 0; i < 8 ; ++i) {
   //  floatarray[i] = (float) i * 3.1415926F;
   WriteEEFloat(&floatarray[i], (float) i * 3.1415926F); 
 }
 for(  i = 0; i < 8 ; ++i) {
  floattest = floatarray[i]; 
 }
}
