#include <common.h>
#include <watchdog.h>
#include <jffs2/jffs2.h>
#include <asm/addrspace.h>
#include <asm/types.h>
#include "ar7240_soc.h"
#include "ar7240_flash.h"

/*
 * globals
 */
flash_info_t flash_info[CFG_MAX_FLASH_BANKS];

#undef display
#define display(x)  ;

#define SIZE_INBYTES_4MBYTES    (4 * 1024 * 1024)
#define SIZE_INBYTES_8MBYTES    (2 * SIZE_INBYTES_4MBYTES)
#define SIZE_INBYTES_16MBYTES   (2 * SIZE_INBYTES_8MBYTES)

#define SIZE_INBYTES_4KBYTES    (4 * 1024)
#define SIZE_INBYTES_64KBYTES   (16 * SIZE_INBYTES_4KBYTES)

#ifndef DEFAULT_FLASH_SIZE_IN_MB
#error "DEFAULT_FLASH_SIZE_IN_MB not defined!"
#endif


/*
 * statics
 */
static void ar7240_spi_write_enable(void);
static void ar7240_spi_poll(void);
#if !defined(ATH_SST_FLASH)
static void ar7240_spi_write_page(uint32_t addr, uint8_t * data, int len);
#endif
static void ar7240_spi_sector_erase(uint32_t addr);

static ulong read_id(void)
{
	unsigned int flashid = 0;

	ar7240_reg_wr_nf(AR7240_SPI_FS, 1);
	ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);

	ar7240_spi_bit_banger(0x9F);

	ar7240_spi_delay_8();
	ar7240_spi_delay_8();
	ar7240_spi_delay_8();
	ar7240_spi_delay_8();

	flashid = ar7240_reg_rd(AR7240_SPI_RD_STATUS);

	/*
	 * We have 3 bytes:
	 * - manufacture ID (1b)
	 * - product ID (2b)
	 */
	flashid = flashid >> 8;

	ar7240_spi_done();

	return((ulong)flashid);
}

#ifdef ATH_SST_FLASH
void ar7240_spi_flash_unblock(void)
{
	ar7240_spi_write_enable();
	ar7240_spi_bit_banger(AR7240_SPI_CMD_WRITE_SR);
	ar7240_spi_bit_banger(0x0);
	ar7240_spi_go();
	ar7240_spi_poll();
}
#endif

static void flash_set_geom(int size, int sector_count, int sector_size){
	int i;
	flash_info_t *info = &flash_info[0];

	info->size = size;
	info->sector_count = sector_count;
	info->sector_size = sector_size;

	for(i = 0; i < info->sector_count; i++){
		info->start[i] = CFG_FLASH_BASE + (i * info->sector_size);
	}
}

unsigned long flash_init(void)
{
	flash_info_t *info;

	info = &flash_info[0];

	// spi flash clock
	ar7240_reg_wr(AR7240_SPI_FS,	0x01);

	ar7240_reg_wr(AR7240_SPI_CLOCK,	0x43);

	ar7240_reg_wr(AR7240_SPI_FS,	0x0);

	// get flash id
	info->flash_id = read_id();

	puts("FLASH:  ");

	// fill flash info based on JEDEC ID
	switch(info->flash_id) {
		/*
		 * 4M flash chips
		 */
		case 0x010215:	// tested
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Spansion S25FL032P (4 MB)");
			break;

		case 0x1F4700:
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Atmel AT25DF321 (4 MB)");
			break;

		case 0x1C3016:	// tested
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("EON EN25Q32 (4 MB)");
			break;

		case 0x1C3116:	// tested
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("EON EN25F32 (4 MB)");
			break;

		case 0x202016:
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Micron M25P32 (4 MB)");
			break;

		case 0xEF4016:
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Winbond W25Q32 (4 MB)");
			break;

		case 0xC22016:
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Macronix MX25L320 (4 MB)");
			break;

			/*
			 * 8M flash chips
			 */
		case 0x010216:
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Spansion S25FL064P (8 MB)");
			break;

		case 0x1F4800:
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Atmel AT25DF641 (8 MB)");
			break;

		case 0x1C3017:	// tested
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("EON EN25Q64 (8 MB)");
			break;

		case 0x202017:
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Micron M25P64 (8 MB)");
			break;

		case 0xEF4017:	// tested
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Winbond W25Q64 (8 MB)");
			break;

		case 0xC22017:	// tested
		case 0xC22617:
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Macronix MX25L64 (8 MB)");
			break;

			/*
			 * 16M flash chips
			 */
		case 0xEF4018:	// tested
			flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
			puts("Winbond W25Q128 (16 MB)");
			break;

		case 0xC22018:
		case 0xC22618:
			flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
			puts("Macronix MX25L128 (16 MB)");
			break;

		case 0x012018:
			flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
			puts("Spansion S25FL127S (16 MB)");
			break;

		case 0x20BA18:
			flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
			puts("Micron N25Q128 (16 MB)");
			break;

			/*
			 * Unknown flash
			 */
		default:
#if (DEFAULT_FLASH_SIZE_IN_MB == 4)
			flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
			puts("Unknown type (using only 4 MB)\n");
#elif (DEFAULT_FLASH_SIZE_IN_MB == 8)
			flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
			puts("Unknown type (using only 8 MB)\n");
#elif (DEFAULT_FLASH_SIZE_IN_MB == 16)
			flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
			puts("Unknown type (using only 16 MB)\n");
#endif
			printf("\nPlease, send request to add support\nfor your FLASH - JEDEC ID: 0x%06lX\n", info->flash_id);
			info->flash_id = FLASH_CUSTOM;
			break;
	}

	puts("\n");

	return(info->size);
}

void
flash_print_info(flash_info_t *info)
{
	printf("The hell do you want flinfo for??\n");
}

int
flash_erase(flash_info_t *info, int s_first, int s_last)
{
	int i, sector_size = info->size / info->sector_count;

#ifdef FLASH_DEBUG
	printf("\nFirst %#x last %#x sector size %#x\n",
	       s_first, s_last, sector_size);
#endif

	for (i = s_first; i <= s_last; i++) {
#ifdef FLASH_DEBUG
		printf("\b\b\b\b%4d", i);
#else
		puts(".");

#ifdef CONFIG_SHOW_ACTIVITY
		extern void show_activity(int arg);
		show_activity(3);
#endif
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
		WATCHDOG_RESET();
#endif

#endif
		ar7240_spi_sector_erase(i * sector_size);
	}
	ar7240_spi_done();
	printf("\n");

	return 0;
}

/*
 * Write a buffer from memory to flash:
 * 0. Assumption: Caller has already erased the appropriate sectors.
 * 1. call page programming for every 256 bytes
 */
#ifdef ATH_SST_FLASH
void
ar7240_spi_flash_chip_erase(void)
{
	ar7240_spi_write_enable();
	ar7240_spi_bit_banger(AR7240_SPI_CMD_CHIP_ERASE);
	ar7240_spi_go();
	ar7240_spi_poll();
}

int
write_buff(flash_info_t *info, uchar *src, ulong dst, ulong len)
{
	uint32_t val;

	dst = dst - CFG_FLASH_BASE;
	printf("write len: %lu dst: 0x%x src: %p\n", len, dst, src);

	for (; len; len--, dst++, src++) {
		ar7240_spi_write_enable();	// dont move this above 'for'
		ar7240_spi_bit_banger(AR7240_SPI_CMD_PAGE_PROG);
		ar7240_spi_send_addr(dst);

		val = *src & 0xff;
		ar7240_spi_bit_banger(val);

		ar7240_spi_go();
		ar7240_spi_poll();
	}
	/*
	 * Disable the Function Select
	 * Without this we can't read from the chip again
	 */
	ar7240_reg_wr(AR7240_SPI_FS, 0);

	if (len) {
		// how to differentiate errors ??
		return ERR_PROG_ERROR;
	} else {
		return ERR_OK;
	}
}
#else
int
write_buff(flash_info_t *info, uchar *source, ulong addr, ulong len)
{
	int total = 0, len_this_lp, bytes_this_page, counter = 0;
	ulong dst;
	uchar *src;

#ifdef FLASH_DEBUG
	printf("write addr: %x\n", addr);
#endif
	addr = addr - CFG_FLASH_BASE;

	while (total < len) {
		src = source + total;
		dst = addr + total;
		bytes_this_page =
		    AR7240_SPI_PAGE_SIZE - (addr & AR7240_SPI_PAGE_SIZE-1);
		len_this_lp =
		    ((len - total) >
		     bytes_this_page) ? bytes_this_page : (len - total);
		ar7240_spi_write_page(dst, src, len_this_lp);
		total += len_this_lp;
		if(counter>=255)
		{
			puts(".");
			counter = 0;
#ifdef CONFIG_SHOW_ACTIVITY
			extern void show_activity(int arg);
			show_activity(3);
#endif
		}
		else
		{
			counter++;
		}
	}

	ar7240_spi_done();

	return 0;
}
#endif

static void
ar7240_spi_write_enable()
{
	ar7240_reg_wr_nf(AR7240_SPI_FS, 1);
	ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);
	ar7240_spi_bit_banger(AR7240_SPI_CMD_WREN);
	ar7240_spi_go();
}

static void
ar7240_spi_poll()
{
	int rd;

	do {
		ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);
		ar7240_spi_bit_banger(AR7240_SPI_CMD_RD_STATUS);
		ar7240_spi_delay_8();
		rd = (ar7240_reg_rd(AR7240_SPI_RD_STATUS) & 1);
	} while (rd);
}

#if !defined(ATH_SST_FLASH)
static void
ar7240_spi_write_page(uint32_t addr, uint8_t *data, int len)
{
	int i;
	uint8_t ch;

	display(0x77);
	ar7240_spi_write_enable();
	ar7240_spi_bit_banger(AR7240_SPI_CMD_PAGE_PROG);
	ar7240_spi_send_addr(addr);

	for (i = 0; i < len; i++) {
		ch = *(data + i);
		ar7240_spi_bit_banger(ch);
	}

	ar7240_spi_go();
	display(0x66);
	ar7240_spi_poll();
	display(0x6d);
}
#endif

static void
ar7240_spi_sector_erase(uint32_t addr)
{
	ar7240_spi_write_enable();
	ar7240_spi_bit_banger(AR7240_SPI_CMD_SECTOR_ERASE);
	ar7240_spi_send_addr(addr);
	ar7240_spi_go();
	display(0x7d);
	ar7240_spi_poll();
}
