/*****************************************************************************
 * Copyright (c) 2004 Rowley Associates Limited.                             *
 *                                                                           *
 * This file may be distributed under the terms of the License Agreement     *
 * provided with this software.                                              *
 *                                                                           *
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE   *
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
 *****************************************************************************
 *                                                                           *
 *                    Flash Eraser for Revely RMS101 SBC                     *
 *                                                                           *
 * Description                                                               *
 * -----------                                                               *
 * This program erases the first FLASH sector and writes a "b -2"            *
 * instruction at the reset vector. This is effect replaces an existing      *
 * FLASH resident program with the simplest ARM application possible.        *
 *****************************************************************************/

#include <targets/LH79520.h>

#define FLASH_START_ADDRESS 0x40000000
#define FLASH_LENGTH 0x400000
#define FLASH_END_ADDRESS FLASH_START_ADDRESS + FLASH_LENGTH

#define ADDRESS_IN_FLASH(a) ADDRESS_IN_RANGE((unsigned long)a, FLASH_START_ADDRESS, FLASH_END_ADDRESS)

#define CMD_READ_ARRAY       0xFFFF
#define CMD_DATA_WRITE       0x4040
#define CMD_BLOCK_ERASE      0x2020
#define CMD_FULL_CHIP_ERASE  0x3030
#define CMD_SUSPEND          0xB0B0
#define CMD_READ_STATUS      0x7070
#define CMD_CLEAR_STATUS     0x5050
#define CMD_CONFIRM          0xD0D0
#define CMD_RESUME           0xD0D0
#define CMD_READ_ID_CODE     0x9090
#define CMD_BLOCK_LOCK       0x6060
#define CMD_SET_LOCKBIT      0x0101
#define CMD_RESET_LOCKBIT    0xD0D0
#define CMD_SET_LOCKDOWNBIT  0x2F2F
#define CMD_SET_PCR          0x6060
#define CMD_PCR_CONFIRM      0x0404
#define CMD_OTP_WRITE        0xC0C0
#define CMD_MULTI_DATA_WRITE 0xE8E8

#define CSR_WSM_STATUS_READY 0x0080
#define CSR_ERASE_SUSPEND    0x0040
#define CSR_ERASE_ERROR      0x0020
#define CSR_WRITE_ERROR      0x0010
#define CSR_LOW_VPP          0x0008
#define CSR_WRITE_SUSPEND    0x0004
#define CSR_DEVICE_PROTECT   0x0002

#define SDRAM_BASE 0x20000000
#define SDRAM_INIT_NOP  0x03
#define SDRAM_INIT_MODE 0x02
#define SDRAM_INIT_PALL 0x01
#define SDRAM_INIT_NORMAL 0x0c
// Other bit fields
#define SDRAM_STATUS    0x20

static int
flash_write_uint16(volatile unsigned short *address, unsigned short data)
{
  unsigned short old = *address;
  *address = CMD_DATA_WRITE;
  *address = data | ~old;
  *address = CMD_READ_STATUS;
  while (!(*address & CSR_WSM_STATUS_READY));
  *address = CMD_CLEAR_STATUS;
  return 1;
}

static int
flash_write_byte(volatile unsigned char *address, unsigned char data)
{
  int result = 1;
  unsigned char tmp[2];
  unsigned short *alignedAddress = (unsigned short *)((unsigned int)address & ~1);
  *(unsigned short *)tmp = *alignedAddress;
  tmp[(unsigned int)address & 1] = data;
  if (*(unsigned short *)tmp != *alignedAddress)
    result = flash_write_uint16((unsigned short *)alignedAddress, *(unsigned short *)tmp);
  return result;
}

static int
flash_erase_sector(volatile unsigned short *address)
{
  *address = CMD_BLOCK_ERASE;
  *address = CMD_CONFIRM;
  *address = CMD_READ_STATUS;
  while (!(*address & CSR_WSM_STATUS_READY));
  *address = CMD_CLEAR_STATUS;
  return 1;
}

static int
flash_erase_all(unsigned short *address)
{
  *address = CMD_FULL_CHIP_ERASE;
  *address = CMD_CONFIRM;
  *address = CMD_READ_STATUS;
  while (!(*address & CSR_WSM_STATUS_READY));
  *address = CMD_CLEAR_STATUS;
  return 1;
}

void
C_Entry(void)
{
  SMCBCR0 = (6 << 5) + 0x400 + (6 << 11) + 0x10000000; // Config Flash SMC for normal operation
  debug_printf("Erasing FLASH\n");
  flash_erase_sector((volatile unsigned short *)FLASH_START_ADDRESS);
  flash_write_uint16((volatile unsigned short *)FLASH_START_ADDRESS, 0xFFFE);
  flash_write_uint16((volatile unsigned short *)FLASH_START_ADDRESS + 1, 0xeaff);
  debug_printf("FLASH Erase Complete\n");
}

