/*-----------------------------------------------------------------------------
//     $Date: 2004/08/19 17:01:31 $
//     $RCSfile: strataflash.c,v $
//-----------------------------------------------------------------------------
//
//     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
//     SOLELY FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR
//     XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION
//     AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION
//     OR STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS
//     IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
//     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
//     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
//     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
//     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
//     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
//     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//     FOR A PARTICULAR PURPOSE.
//     
//     (c) Copyright 2004 Xilinx, Inc.
//     All rights reserved.
//
//---------------------------------------------------------------------------*/

#include <errno.h>
#include "xio.h"
#include "strataflash.h"

#define STRATAFLASH_BASEADR        0x28000000
#define STRATAFLASH_SIZE           (2*32*1024*1024/8)
#define STRATAFLASH_BLOCK_SIZE     (2*128*1024)
#define STRATAFLASH_BLOCKS         (STRATAFLASH_SIZE / STRATAFLASH_BLOCK_SIZE)

#define CMD_ERASE_BLOCK            0x00200020
#define CMD_WRITE_WORD             0x00400040
#define CMD_CLEAR_STATUS           0x00500050
#define CMD_LOCK_BLOCK             0x00600060
#define CMD_LOCK_BLOCK_SET         0x00010001
#define CMD_COMMIT                 0x00d000d0
#define CMD_GO_READ                0x00ff00ff
#define STATUS_BUSY                0x00800080

unsigned int strataflash_BaseAdr   = STRATAFLASH_BASEADR;     
unsigned int strataflash_Size      = STRATAFLASH_SIZE;

static void sendCommand(unsigned int cmd)
{
  XIo_Out32(strataflash_BaseAdr, cmd);
}

static int waitReady(unsigned int adr)
{
  unsigned int val;

  do
  {
    val = XIo_In32(adr);
  } while ((val & STATUS_BUSY) != STATUS_BUSY);
  if (val & 0x1a001a)
  {
    /* programming error */
    sendCommand(CMD_CLEAR_STATUS);
    sendCommand(CMD_GO_READ);
    return -1;
  }
  return 0;
}

static int blockCommand(unsigned int block,
                        unsigned int firstCmd,
                        unsigned int lastCmd)
{
  int res;
  unsigned int adr;
  
  res = strataflash_BlockToAddress(block, &adr);
  if (res != 0)
    return res;
  
  XIo_Out32(adr, firstCmd);
  XIo_Out32(adr, lastCmd);
  XIo_Out32(adr+4, firstCmd);
  XIo_Out32(adr+4, lastCmd);
  
  res = waitReady(adr);
  if (res == 0)
    res = waitReady(adr+4);
  
  sendCommand(CMD_GO_READ);
  return res;
}

static int writeWord(unsigned int adr, unsigned char *buf)
{
  unsigned int val;
  unsigned int i;
  int ret;
  
  val = 0;
  for(i = 0; i < 4; i++)
    val = (val << 8) | ((unsigned int) buf[i]);
  
  XIo_Out32(adr, CMD_WRITE_WORD);
  XIo_Out32(adr, val);
  ret = waitReady(adr);

  return ret;
}                   

int strataflash_AddressToBlock(unsigned int adr, unsigned int *block)
{
  /* check adr is within flash */
  if ((adr < strataflash_BaseAdr) ||
      (adr >= strataflash_BaseAdr+strataflash_Size))
    return -1;

  adr -= strataflash_BaseAdr;
  *block = adr / STRATAFLASH_BLOCK_SIZE;

  return 0;
}

int strataflash_BlockToAddress(unsigned int block, unsigned int *adr)
{
  if (block >= STRATAFLASH_BLOCKS)
    return -1;

  *adr = block*STRATAFLASH_BLOCK_SIZE + strataflash_BaseAdr;
  return 0;
}

int strataflash_Write(unsigned int adr, unsigned char *buf, unsigned int len)
{
  unsigned int i;
  
  /* check whether buffer fits into flash */
  if ((adr < strataflash_BaseAdr) ||
      (adr+len >= strataflash_BaseAdr+strataflash_Size))
    return -1;

  /* implementation restrictions:
     - address must be aligned to a word
     - len must be a multiple of a word
  */
  if ((adr & 3) || (len & 3))
    return -1;

  sendCommand(CMD_CLEAR_STATUS);
  for (i = 0; i < len; i += 4)
  {
    if (writeWord(adr+i, &buf[i]) != 0)
      return i;
  }
  sendCommand(CMD_GO_READ);
    
  return len;
}

int strataflash_EraseBlock(unsigned int block)
{
  int res;

  res = blockCommand(block, CMD_ERASE_BLOCK, CMD_COMMIT);

  return res;
}

int strataflash_LockBlock(unsigned int block)
{
  int res;

  res = blockCommand(block, CMD_LOCK_BLOCK, CMD_LOCK_BLOCK_SET);

  return res;
}


int strataflash_UnlockBlock(unsigned int block)
{
  int res;

  res = blockCommand(block, CMD_LOCK_BLOCK, CMD_COMMIT);

  return res;
}
