//==========================================================================
//
//      flash_arm_lpc2xxx.c
//
//      Flash programming for LPC2xxx
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
// Contributors: 
// Date:         2007-07-12
// Purpose:      
// Description:  
//              
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <pkgconf/devs_flash_arm_lpc2xxx.h>
#include <pkgconf/hal.h>
#include <pkgconf/hal_arm.h>
#include <cyg/hal/hal_intr.h>

#include <cyg/io/flash.h>
#include <cyg/io/flash_dev.h>

#include "flash_arm_lpc2xxx.h"

/* gcc builtins */
extern void* memcpy(void *, const void *, size_t);
extern void* memset(void *, int, size_t);

/* wrapper for simpler IAP access */
static void
iap(struct iap_param *param, struct iap_param *result)
{
  static void (* const iap)(struct iap_param *, struct iap_param *)
    = (void (*)(struct iap_param *, struct iap_param *)) IAP_LOCATION;
  cyg_uint32 cpsr;
  
  HAL_DISABLE_INTERRUPTS(cpsr);
  iap(param, result);
  HAL_RESTORE_INTERRUPTS(cpsr);
}

void 
flash_query(void *data)
{
  /* nothing to do here */
}

/*
 * 248k in 31 blocks by 8k there actually less blocks since two of
 * them are 64k, but accessing anything but the last few 8k blocks is
 * not supported anyway
 */
int 
flash_hwr_init(void)
{
  flash_info.block_size = 8 * 1024;
  flash_info.blocks = 31;
  flash_info.start = (void *) 0;
  flash_info.end = (void *) (248 * 1024);
  
  return FLASH_ERR_OK;
}


static const cyg_uint8 flash_errors[12] = {
  FLASH_ERR_OK,          /* IAP_CMD_SUCCESS */
  FLASH_ERR_PROTOCOL,    /* IAP_INV_COMMAND */
  FLASH_ERR_INVALID,     /* IAP_SRC_ADDRERR */
  FLASH_ERR_INVALID,     /* IAP_DST_ADDRERR */
  FLASH_ERR_INVALID,     /* IAP_SRC_ADDRMAP */
  FLASH_ERR_INVALID,     /* IAP_DST_ADDRMAP */
  FLASH_ERR_INVALID,     /* IAP_CNT_INVALID */
  FLASH_ERR_INVALID,     /* IAP_SEC_INVALID */
  FLASH_ERR_PROTOCOL,    /* IAP_SEC_NOTBLNK */
  FLASH_ERR_PROTOCOL,    /* IAP_SEC_NOTPREP */
  FLASH_ERR_DRV_VERIFY,  /* IAP_CMP_INEQUAL */
  FLASH_ERR_DRV_TIMEOUT, /* IAP_BSY         */
};

int
flash_hwr_map_error(e)
{
  if(e > 11)
    return FLASH_ERR_PROTOCOL;
  return flash_errors[e];
}

/* this will not work for flash addresses < 0x30000 */
static int
block_by_addr(cyg_uint32 addr)
{
  int block;
  
  block = (addr >> 13) & 0x1f;
  block -= 14;
  
  return block;
}

int
flash_erase_block(void *block, unsigned int size)
{
  struct iap_param param, result;
  
  param.code = IAP_PREPARE;
  
  param.p[0] = param.p[1] = block_by_addr((cyg_uint32) block);
  if(param.p[0] < 10)
    return FLASH_ERR_INVALID;
  
  /* prepare sector(s) */
  iap(&param, &result);
  if(result.code != IAP_CMD_SUCCESS)
    return result.code;
  
  param.code = IAP_ERASE;
  param.p[2] = CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / 1000;
  
  /* erase sector(s) */
  iap(&param, &result);
  return result.code;
}

int
flash_program_buf(void *addr, void *data, int len)
{
  static const int size = CYGPKG_DEVS_FLASH_ARM_LPC2XXX_BUFSIZE;
  static const cyg_uint32 b = (0x40004000 - 32 - 
                               CYGPKG_DEVS_FLASH_ARM_LPC2XXX_BUFSIZE);
  static void * const buf = (void *) (0x40004000 - 32 - 
                                      CYGPKG_DEVS_FLASH_ARM_LPC2XXX_BUFSIZE);
  cyg_uint32 a = (cyg_uint32) addr;
  char *d = (char *) data;
  struct iap_param param, result;
  
  param.code = IAP_PREPARE;
  param.p[0] = block_by_addr(a);
  param.p[1] = block_by_addr(a + len - 1);
  if(param.p[0] < 10 || param.p[1] > 16)
    return FLASH_ERR_INVALID;
  
  do {
    /* prepare sector(s) */
    iap(&param, &result);
    if(result.code != IAP_CMD_SUCCESS)
      return result.code;
    
    if(len < size)
      memset(buf, 0xff, size);
    memcpy(buf, d, size);
    
    param.code = IAP_COPY;
    param.p[0] = a;
    param.p[1] = b;
    param.p[2] = size;
    param.p[3] = CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / 1000;
    
    /* copy ram to flash */
    iap(&param, &result);
    if(result.code != IAP_CMD_SUCCESS)
      return result.code;
    
    len -= size;
    a += size;
    d += size;
  } while(len > 0);
  
  return(result.code);
}

bool
flash_code_overlaps(void *start, void *end)
{
  extern unsigned char _stext[], _etext[];
  
  return ((((unsigned long)&_stext >= (unsigned long)start) &&
           ((unsigned long)&_stext < (unsigned long)end)) ||
          (((unsigned long)&_etext >= (unsigned long)start) &&
           ((unsigned long)&_etext < (unsigned long)end)));
}
