#include <common.h>
#include <asm/io.h>
#include "rx64mflash.h"

static int wait_frdy(void) __attribute__((section(".iram.text")));
static int wait_frdy(void)
{
	int i;
	volatile int j;

	for (i = 0; i < 10; i++) {
		if (__raw_readl(FSTATR) & FRDY)
			return 0;
		for(j = 0; j < 10000; j++);
	}
	return -1;
}
	
static void normal_mode(void) __attribute__((section(".iram.text")));
static void normal_mode(void)
{
	__raw_writew(FENTRYR_KEY | 0x00, FENTRYR);
	while(__raw_readw(FENTRYR));
}

static int pe_mode(unsigned int fentryd)
	__attribute__((section(".iram.text")));
static int pe_mode(unsigned int fentryd)
{
	/* Enter P/E mode */
	__raw_writew(FENTRYR_KEY | fentryd, FENTRYR);
	while(__raw_readw(FENTRYR) != fentryd);
	/* clock setting */
	__raw_writew(FPCKAR_KEY | (CONFIG_SYS_CLK_FREQ / 1000000), FPCKAR);
	return 0;
}

static void cmd_abort(void)
	__attribute__((section(".iram.text")));
static void cmd_abort(void)
{
	__raw_writeb(ABORT, FACICMD);
	while((__raw_readw(FSTATR) & FRDY) == 0);
}

static int protect_c(unsigned long addr, int prot)
	__attribute__((section(".iram.text")));
static int protect_c(unsigned long addr, int prot)
{
	pe_mode(0x0001);
	if (prot) {
		__raw_writel(addr, FSADDR);
		__raw_writeb(0x77, FACICMD);
		__raw_writeb(0xd0, FACICMD);
		if (wait_frdy() < 0) {
			cmd_abort();
			return ERR_TIMOUT;
		}
	} else {
		__raw_writel(addr, FSADDR);
		__raw_writeb(0x71, FACICMD);
		__raw_writeb(0xd0, FACICMD);
		if (wait_frdy() < 0) {
			cmd_abort();
			return ERR_TIMOUT;
		}
		if (__raw_readb(FLKSTAT) == 0) {
			__raw_writew(0x5501, FPROTR);
			__raw_writel(addr, FSADDR);
			__raw_writeb(0x20, FACICMD);
			__raw_writeb(0xd0, FACICMD);
			if (wait_frdy() < 0) {
				cmd_abort();
				return ERR_TIMOUT;
			}
		}
	}
	normal_mode();
	return 0;
}

static int write_c(uchar *src, ulong addr, ulong cnt)
	__attribute__((section(".iram.text")));
static int write_c(uchar *src, ulong addr, ulong cnt)
{
	ulong dst;
	int i;

	pe_mode(0x0001);
	dst = addr & ~0x00000ff;
	while(cnt > 0) {
		__raw_writel(addr, FSADDR);
		__raw_writeb(FCU_PROG, FACICMD);
		__raw_writeb(256 / 2, FACICMD);
		for(i = 0; i < 256; dst += 2, i += 2) {
			if (dst < addr || cnt == 0) {
				__raw_writeb(0xff, FACICMD);
				__raw_writeb(0xff, FACICMD);
		} else {
				__raw_writeb(*(unsigned char *)src, FACICMD);
				__raw_writel(*(unsigned char *)(src + 1), FACICMD);
				cnt -= 2;
				src += 2;
			}
			while(__raw_readw(FSTATR) & DBFULL);
		}
		__raw_writeb(FCU_PROG_DONE, dst);
		if (wait_frdy() < 0) {
			cmd_abort();
			return ERR_TIMOUT;
		}
	}
	normal_mode();
	return 0;
}

static int erase_page_c(unsigned int addr)
	__attribute__((section(".iram.text")));
static int erase_page_c(unsigned int addr)
{
	pe_mode(0x0001);
	__raw_writel(addr, FSADDR);
	__raw_writeb(0x20, FACICMD);
	__raw_writeb(0xd0, FACICMD);
	if (wait_frdy() < 0) {
		cmd_abort();
		return ERR_TIMOUT;
	}
	normal_mode();
	return 0;
}

static void fcu_init(void)
	__attribute__((section(".iram.text")));
static void fcu_init(void)
{
	unsigned long *src = (unsigned long *)FCUFIRM_TOP;
	unsigned long *dst = (unsigned long *)FCURAM_TOP;
	int i;

	__raw_writeb(0x01, FWEPROR);
	/* setup FCU RAM */
	__raw_writew(FENTRYR_KEY | 0x00, FENTRYR);
	__raw_writew(FCURAME_KEY | FCURAME_FCRME, FCURAME);
	for (i = 0; i < FCUFIRM_SIZE; i += 4)
		*dst++ = *src++;
	__raw_writew(FCURAME_KEY, FCURAME);
	pe_mode(0x0001);
	__raw_writeb(0xb3, FACICMD);
	while ((__raw_readl(FSTATR) & FRDY) == 0);
	normal_mode();
}

int (*rx_flash_wait_frdy)(void) = wait_frdy;
int (*rx_flash_pe_mode)(unsigned int) = pe_mode;
void (*rx_flash_normal_mode)(void) = normal_mode;
int (*rx_flash_write_c)(uchar *, ulong, ulong) = write_c;
int (*rx_flash_protect_c)(unsigned long, int) = protect_c;
int (*rx_flash_erase_page_c)(unsigned int) = erase_page_c;
void (*rx_flash_abort)(void) = cmd_abort;
void (*rx_fcu_init)(void) = fcu_init;
