/**********************************************************************/
/*                                                                    */
/* Project Name:  flash.c                                             */
/* Last modified: 13/02/2005                                          */
/* By:            r62780                                              */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
/*                                                                    */
/* Description: MC9S12DP256B_FLASH_DOONSTACK - demo                     */
/*                                                                    */
/*                                                                    */
/* Documentation: AN2720/D                                            */
/*                                                                    */
/*                                                                    */
/* This software is classified as Engineering Sample Software.        */
/*                                                                    */
/**********************************************************************/
/*                                                                    */
/* Services performed by FREESCALE in this matter are performed AS IS */
/* and without any warranty. CUSTOMER retains the final decision      */
/* relative to the total design and functionality of the end product. */ 
/* FREESCALE neither guarantees nor will be held liable by CUSTOMER   */
/* for the success of this project. FREESCALE DISCLAIMS ALL           */
/* WARRANTIES, EXPRESSED, IMPLIED OR STATUTORY INCLUDING, BUT NOT     */
/* LIMITED TO, IMPLIED WARRANTY OF MERCHANTABILITY OR  FITNESS FOR A  */
/* PARTICULAR PURPOSE ON ANY HARDWARE, SOFTWARE ORE ADVISE SUPPLIED   */
/* TO THE PROJECT BY FREESCALE, AND OR NAY PRODUCT RESULTING FROM     */
/* FREESCALE SERVICES . IN NO EVENT SHALL FREESCALE BE LIABLE FOR     */
/* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT. */
/*                                                                    */
/* CUSTOMER agrees to hold FREESCALE harmless against any and all     */
/* claims demands or actions by anyone on account of any damage, or   */
/* injury, whether commercial, contractual, or tortuous, rising       */
/* directly or indirectly as a result of the advise or assistance     */
/* supplied CUSTOMER in connection with product, services or goods    */
/* supplied under this Agreement.                                     */
/*                                                                    */
/**********************************************************************/
#include <mc9s12c32.h>     // derivative information
#include "flash.h"
//------------------------------------------------------------------------------
#define PUIF  unsigned int *far
//------------------------------------------------------------------------------
extern DoOnStack(unsigned int *far address);
//------------------------------------------------------------------------------
void Set_Flash_Block(PUIF far_address);
//------------------------------------------------------------------------------
// Function Name: Flash_Init
// Description : Initialize Flash NVM for HCS12 by programming
// FCLKDIV based on passed oscillator frequency, then
// uprotect the array, and finally ensure PVIOL and
// ACCERR are cleared by writing to them.
//
//------------------------------------------------------------------------------
signed char Flash_Init(unsigned long oscclk, unsigned long tbus) // [kHz,ns]
{
  unsigned long fclk_val;
  unsigned long temp;

  if(tbus > 1000L) return PROGRAM_ERASE_IMPOSSIBLE; // tbus > 1us (1000ns) (page 35 S12FTS256KV2.pdf )
  temp = (oscclk*1000) % 8;
  fclk_val = (oscclk  * (5000UL+tbus)) / 1000000UL;

  if (oscclk >= 12800UL)
    {
      fclk_val = fclk_val / 8;
      if(temp)
       { // oscclk % 8 is not an integer
         fclk_val = fclk_val - 1;
       }
      FCLKDIV = (unsigned char) (fclk_val | 0x40);
    }
  else
    {
      if(temp)
       { // oscclk % 8 is not an integer
         fclk_val = (fclk_val-1);
       }
      FCLKDIV = (unsigned char) fclk_val;
    }  

  // test computed parameters
  if (oscclk >= 12800L)
    {
      temp = 8UL*1000000UL*(1+FCLKDIV&0x3F) / oscclk + tbus;
    }
  else
    {
      temp = 1000000UL*(1+FCLKDIV&0x3F) / oscclk + tbus;
    }  
  temp = temp / 1000;
  
  if(temp < 5UL) return PROGRAM_ERASE_IMPOSSIBLE; 


  FSTAT= 0x30;
  return 1;
}
//------------------------------------------------------------------------------
// Function Name: Set_Flash_Block
// Description : Sets flash block by means of BKSEL1 and BKSEL0
// author: R62780
//------------------------------------------------------------------------------
void Set_Flash_Block(PUIF far_address)
{
  unsigned int* address;
  unsigned char bank;
  																			// set new block from required adress
  address = (unsigned int*)far_address; // strip page off
  if(address > ((unsigned int*)0x7FFF)) // is accessed flash addressed by bank?
    {
      bank = (unsigned char) (((unsigned long)far_address)>>16);
      // flash bank (BKSEL1,BKSEL0)
      bank = (0x3F - bank)>>2;  // calculate block = (0x3F - bank)/4;  // block size is 64 kB (DP256, A256, C32)
      //bank = (0x3F - bank)>>3;  // calculate block = (0x3F - bank)/4; // block size is 128 kB (A512)
      FCNFG = FCNFG & 0xFC;
      FCNFG = FCNFG | bank;
    }
  else
    {
      FCNFG = FCNFG & 0xFC;
    }  
}
//------------------------------------------------------------------------------
// Function Name: Flash_Write_Word
// Description : Program a given Flash location using address and data
// passed from calling function.
//
//------------------------------------------------------------------------------
signed char Flash_Write_Word(PUIF far_address,unsigned int data)
{
  unsigned int* address;
  unsigned char block_old,err;

  address = (unsigned int*)far_address; // strip page off
  if((unsigned int)address & 0x0001) {return Flash_Odd_Access;} // Aligned word?
  if(*far_address != 0xFFFF) {return Flash_Not_Erased;} // Is it erased?

  block_old = FCNFG & 0x03;             // get flash bank (block) R62780
  Set_Flash_Block(far_address);         // R62780
  FSTAT = 0x30;
  (*far_address) = data;                // Store desired data to address being programmed
  FCMD  = PROG;                         // Store programming command in FCMD
  (void)DoOnStack(far_address);         // just passed for PPAGE

  err = FSTAT;
  FCNFG = FCNFG & 0xFC;									// restore flash bank (block) R62780
  FCNFG = FCNFG | block_old;						// R62780
  if(err & FSTAT_ACCERR_MASK) {return Access_Error;}
  if(err & FSTAT_PVIOL_MASK)  {return Protection_Error;}
  return 1;
}
//------------------------------------------------------------------------------
// Function Name: Flash_Erase_Sector
// Description : Erases a given Flash sector using address
// passed from calling function.
//------------------------------------------------------------------------------
signed char Flash_Erase_Sector(PUIF far_address)
{
  unsigned int* address;
  unsigned char block_old,err;

  address = (unsigned int*)far_address; // strip page off
  if((unsigned int)address & 0x0001) {return Flash_Odd_Access;} // Aligned word?
  if((unsigned int)address % Flash_Sector_Size !=0) {return Not_StartofSector_Error;}

  block_old = FCNFG & 0x03;             // get flash bank (block) R62780
  Set_Flash_Block(far_address);         // R62780
  FSTAT = 0x30;
  (*far_address) = 0xFFFF;              // Dummy store to page to be erased
  FCMD = ERASE;
  (void)DoOnStack(far_address);
  err =  FSTAT;
  FCNFG = FCNFG & 0xFC;									// restore flash bank (block) R62780
  FCNFG = FCNFG | block_old;						// R62780
  if(err & FSTAT_ACCERR_MASK) {return Access_Error;}
  if(err & FSTAT_PVIOL_MASK)  {return Protection_Error;}
  return 1;
}
//------------------------------------------------------------------------------
// Function Name: Flash_Write_Block
// Description : Program a range of Flash location using address and data
// pointers passed from calling function.
//
//------------------------------------------------------------------------------
signed char Flash_Write_Block(PUIF address_source,PUIF far_address_destination,unsigned int count)
{
  unsigned long i; // long supports > 64K words
  signed char ret_val;
  for (i = 0;i<count;i++)
    {
      ret_val = Flash_Write_Word(far_address_destination++, *address_source++);
      if (ret_val == Access_Error) {return Access_Error;}
      if (ret_val == Protection_Error) {return Protection_Error;}
    }
   return 1;
}
//------------------------------------------------------------------------------
// Function Name: Flash_Erase_Block
// Description : Erases a range of Flash sectors using address
// pointers passed from calling function.
//
// S12 have max 64 page and max of 32 sectors per page (800 total sectors)
//------------------------------------------------------------------------------
signed char Flash_Erase_Block(PUIF start_address,PUIF end_address)
{
  unsigned int i;
  unsigned int count;
  unsigned long address;
  signed char ret_val;
  count = ((((unsigned int)end_address)-((unsigned int)start_address))/Flash_Sector_Size)+1;
  address = (unsigned long)start_address;
  for (i = 0;i < count;i++)
    {
      ret_val = Flash_Erase_Sector((unsigned int *far)address);
      if (ret_val == Access_Error) {return Access_Error;}
      if (ret_val == Protection_Error) {return Protection_Error;}
      address = address+Flash_Sector_Size;
  }
  return 1;
}