/*
 *  drivers/mtd/s3c2410_nand.c
 *
 *  Copyright (c) 2002 SAMSUNG ELECTRONICS 
 *                  SW.LEE <hitchcar@sec.samsung.com>
 *
 *  Derived from drivers/mtd/spia.c,autcpu12.c
 * 	 Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *  Overview:
 *   This is a device driver for the NAND flash device found on the
 *   autronix s3c2410 board, which is a SmartMediaCard. It supports 
 *   16MB, 32MB and 64MB cards.
 */

#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/sizes.h>

/*
 * MTD structure for S3C2410 board
 */
static struct mtd_info *s3c2410_mtd = NULL;

/*
 * Module stuff
 */
#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define s3c2410_nand_init init_module
#define s3c2410_cleanup cleanup_module
#endif


#ifdef MODULE
MODULE_PARM(s3c2410_temp, "i");
__setup("s3c2410_temp=",s3c2410_temp);
#endif


/*
 * Define partitions for flash devices
 */


/*
 * THIS IS ONLY PARTITION FOR S3C2410 NAND CONTROLLER
 *
 * This partition drives from IPAQ Linux Partition 
 *
 * HAVE THE BELOW PARTITION IN YOUR MIND 
 */ 

#define GPE	1 
#define TEST_WIN GPE 

#if	(TEST_WIN == XINWDOW)
static struct mtd_partition partition_info64k[] = {
	/* BOOTLOADER + KERNEL */
	{ name: "S3C2410 flash partition 0",
	  offset: 0,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 1",
	  offset: 2 * SZ_1M,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 2",
	  offset: 4 * SZ_1M,      /* Block Number  4 * 64 = 256  (0x100) */
	  size:   4 * SZ_1M},

	/* INIT CRAMFS           */
	{ name: "S3C2410 flash partition 3",
	  offset: 8 * SZ_1M,       /* Block Number 8 * 64 = 512 (0x200)*/
	  size:   2 * SZ_1M},

	/* ROOT CRAMFS           */	
	{ name: "S3C2410 flash partition 4",
	  offset: 10 * SZ_1M,     /* Block Number 10 * 64 = 640 (0x280) */
	  size:   6  * SZ_1M},
	
	/* USR CRAMFS            */
	{ name: "S3C2410 flash partition 5",
	  offset: 16 * SZ_1M,     /* Block number 16 * 64 = 1024 (0x400) */
	  size:   8  * SZ_1M},
	
	/* EXT2 FILE SYSTEM     */
	{ name: "S3C2410 flash partition 6",
	  offset: 24 * SZ_1M,     /* Block number 24 * 64 = 1536 */
	  size:   24 * SZ_1M},

	/*  JFFS FILE SYSTEM    */
	{ name: "S3C2410 flash partition 7",
	  offset: 48 * SZ_1M,    /* Block number 48 * 64 = 3072 */
	  size:   16 * SZ_1M},
};

#define NUM_PARTITIONS64K 8
#elif 	(TEST_WIN == OPIE)

static struct mtd_partition partition_info64k[] = {
	/* BOOTLOADER + KERNEL */
	{ name: "S3C2410 flash partition 0",
	  offset: 0,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 1",
	  offset: 2 * SZ_1M,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 2",
	  offset: 4 * SZ_1M,      /* Block Number  4 * 64 = 256  (0x100) */
	  size:   4 * SZ_1M},

	/* INIT CRAMFS           */
	{ name: "S3C2410 flash partition 3",
	  offset: 8 * SZ_1M,       /* Block Number 8 * 64 = 512 (0x200)*/
	  size:   8 * SZ_1M},

	/* ROOT CRAMFS           */	
	{ name: "S3C2410 flash partition 4",
	  offset: 18 * SZ_1M,     /* Block Number 18 * 64 = 960 (0x480) */
	  size:   6  * SZ_1M},
};
#define NUM_PARTITIONS64K 5
#elif	(TEST_WIN == GPE)
static struct mtd_partition partition_info64k[] = {
	/* BOOTLOADER + KERNEL */
	{ name: "S3C2410 flash partition 0",
	  offset: 0,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 1",
	  offset: 2 * SZ_1M,
	  size:   2 * SZ_1M},

	{ name: "S3C2410 flash partition 2",
	  offset: 4 * SZ_1M,      /* Block Number  4 * 64 = 256  (0x100) */
	  size:   4 * SZ_1M},

	{ name: "S3C2410 flash partition 3",
	  offset: 8 * SZ_1M,       /* Block Number 8 * 64 = 512 (0x200)*/
	  size:   8 * SZ_1M},

	/* ROOT CRAMFS           */	
	{ name: "S3C2410 flash partition 4",
	  offset: 18 * SZ_1M,     /* Block Number 18 * 64 = 960 (0x480) */
	  size:   18  * SZ_1M},
};
#define NUM_PARTITIONS64K 5
#endif


/* 
 *	hardware specific access to control-lines
 */

void s3c2410_hwcontrol(int cmd)
{
  switch(cmd){
                /* SAMSUNG S3C2410 NAND CONTROLLER HARDWARE AUTOMATIC */
        case NAND_CTL_SETCLE:break;
        case NAND_CTL_CLRCLE:break;
        case NAND_CTL_SETALE:break;
        case NAND_CTL_CLRALE:break;
	         /* NAND FLASH MEMORY CHIP ENABLE -- active low  */
        case NAND_CTL_SETNCE: rNFCONF= (rNFCONF&~(1<<11))|(0<<11);  break;
     	case NAND_CTL_CLRNCE: rNFCONF= (rNFCONF&~(1<<11))|(1<<11);  break;
     	case NAND_CTL_S3C_WAIT: NF_WAITRB();  break;
	}
}

/*
*	read device ready pin
*/
int s3c2410_device_ready(void)
{
	return (rNFSTAT&1) ? 1 : 0;
}


void NF_Reset(void)
{
    int i;
   
    
    NF_nFCE_L();

    NF_CMD(0xFF);	//reset command

    for(i=0;i<10;i++);  //tWB = 100ns. 

    NF_WAITRB();      //wait 200~500us;
     
    NF_nFCE_H();
}

#if 1 
#define TACLS   0
#define TWRPH0  3
#define TWRPH1  0
#endif


void NF_Init(void)
{
    rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);	
    // 1  1    1     1,   1      xxx,  r xxx,   r xxx        
    // En 512B 4step ECCR nFCE=H tACLS   tWRPH0   tWRPH1
    
    NF_Reset();
}


/*
 * Main initialization routine
 */
int __init s3c2410_nand_init (void)
{
	struct nand_chip *this;
	int err = 0;

	/* Allocate memory for MTD device structure and private data */
	s3c2410_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
				GFP_KERNEL);
	if (!s3c2410_mtd) {
		printk ("Unable to allocate S3C2410 NAND MTD device structure.\n");
		err = -ENOMEM;
		goto out;
	}

	/* map physical adress */
	/*
	s3c2410_fio_base=(unsigned long)ioremap(s3c2410_fio_pbase,SZ_1K);
	if(!s3c2410_fio_base){
	        printk("Ioremap s3c2410 SmartMedia Card failed\n");
	        err = -EIO;
	        goto out_mtd;
	}
	*/

	/* Get pointer to private data */
	this = (struct nand_chip *) (&s3c2410_mtd[1]);

	/* Initialize structures */
	memset((char *) s3c2410_mtd, 0, sizeof(struct mtd_info));
	memset((char *) this, 0, sizeof(struct nand_chip));

	/* Link the private data with the MTD structure */
	s3c2410_mtd->priv = this;

	/* Set address of NAND IO lines */
	this->hwcontrol = s3c2410_hwcontrol;
	this->dev_ready = s3c2410_device_ready;
	/* 200 us command delay time */
	this->chip_delay = 1;	

	NF_Init();		/* S3C2410 NAND FLASH INIT */
	/* Scan to find existance of the device */
	if (nand_scan (s3c2410_mtd)) {/* see include/linux/mtd/nand_ids.h  */
		err = -ENXIO;
		goto out_ior;
	}

	/* Allocate memory for internal data buffer */
	this->data_buf = kmalloc (sizeof(u_char) * (s3c2410_mtd->oobblock + s3c2410_mtd->oobsize), GFP_KERNEL);
	if (!this->data_buf) {
		printk ("Unable to allocate NAND data buffer for S3C2410.\n");
		err = -ENOMEM;
		goto out_ior;
	}

	/* Allocate memory for internal data buffer */
	this->data_cache = kmalloc (sizeof(u_char) * (s3c2410_mtd->oobblock + s3c2410_mtd->oobsize), GFP_KERNEL);
	if (!this->data_cache) {
		printk ("Unable to allocate NAND data cache for S3C2410.\n");
		err = -ENOMEM;
		goto out_buf;
	}
	this->cache_page = -1;

	/* Register the partitions */
	switch(s3c2410_mtd->size){
		case SZ_64M: add_mtd_partitions(s3c2410_mtd, partition_info64k, NUM_PARTITIONS64K); 				break; 
		default: 
			printk("--------------------------------------------  \n");
			printk("YOU MUST SEE nand_ids.h file \n");
			printk("and must register your NAND SMC Device        \n");
			printk("Unsupported SmartMedia device\n"); 
			printk ("           Only test in MAF 0xEC, DEV 0x76   \n");
			err = -ENXIO;
			goto out_cac;
		
	}
	goto out;

out_cac:
	kfree (this->data_cache);    
out_buf:
	kfree (this->data_buf);    
out_ior:
out_mtd:
	kfree (s3c2410_mtd);
out:
	return err;
}

module_init(s3c2410_nand_init);

/*
 * Clean up routine
 */
#ifdef MODULE
static void __exit s3c2410_cleanup (void)
{
	struct nand_chip *this = (struct nand_chip *) &s3c2410_mtd[1];

	/* Unregister partitions */
	del_mtd_partitions(s3c2410_mtd);
	
	/* Unregister the device */
	del_mtd_device (s3c2410_mtd);

	/* Free internal data buffers */
	kfree (this->data_buf);
	kfree (this->data_cache);

	/* Free the MTD device structure */
	kfree (s3c2410_mtd);
}
module_exit(s3c2410_cleanup);
#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SW.LEE <hitchcar@sec.samsung.com>");
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on  s3c2410");
