Skip to contentSkip to conversation

Tips and Tricks for S3C2410 NAND controller?

DISCUSSION David Müller (ELSOFT AG) 3 participants 3 messages Aug 8, 2003 – Aug 9, 2003 Memory technology devices (MTD)

lore
Share & tools
lore
#0 lore raw

Hello

I have written a hardware driver for the NAND controller built in Samsung's S3C2410 SoC. I followed the instruction on
http://linux-mtd.infradead.org/tech/nand.html to implement it but it doesn't seem to work correctly.

Does anybody have some tricks or ideas what needs to be specially observed when using this controller?

TIA

Dave

#1 lore raw
Quoted 6 lines · David Müller (ELSOFT AG)

Hello

I have written a hardware driver for the NAND controller built in Samsung's S3C2410 SoC. I followed the instruction on
http://linux-mtd.infradead.org/tech/nand.html to implement it but it doesn't seem to work correctly.

Show code and describe symptoms. I've changed the interface to the core NAND code recently -- the docs will be slightly out of date.

Signature · 1 line
dwmw2
#2 lore raw
In reply to David Müller (ELSOFT AG)
Quoted 15 lines

Hello

I have written a hardware driver for the NAND controller built in Samsung's S3C2410 SoC. I followed the instruction on
http://linux-mtd.infradead.org/tech/nand.html to implement it but it doesn't seem to work correctly.

Does anybody have some tricks or ideas what needs to be specially observed when using this controller?

TIA

Quoted 7 lines

Dave

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

Hi,

attached file is my implementation against with MTD CVS Jul.06.2003.

(
ignore the BONFS dependency in source code. it's a read-only
block-device layer that support the bad-block-management on NAND flash for general filesystem such as cramfs, ...
)

my boss says,
"do not any technical-support about these. just open the source."

Bushi

/*
* NAND controller on SAMSUNG S3C2410
*
* [email protected]
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/

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

static struct mtd_info *s3c2410_nand_mtd = NULL;

/*
* if you use the BON,
* the partition table for MTD blkdev will be generated by "bon_check_mtd()". */
#ifdef CONFIG_MTD_NAND_BONFS
static int mtd_num = 0;
static struct mtd_partition *partition_info = NULL;

extern struct nand_oobinfo bonfs_oob;
extern int bon_check_mtd(struct mtd_info *mtd, struct mtd_partition **mtd_table); #endif /* CONFIG_MTD_NAND_BONFS */

static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)

{
	if (chip == 0)
		NFCONF &= ~NFCONF_nFCE_HIGH;
	else
		NFCONF |= NFCONF_nFCE_HIGH;
}

static int s3c2410_nand_device_ready(struct mtd_info *mtd)

{
	return (NFSTAT & NFSTAT_RnB) ? 1:0;
}

static void s3c2410_nand_cmdfunc(struct mtd_info *mtd, unsigned command, int column, int page_addr) {

	register struct nand_chip *this = mtd->priv;

	/*
	 * Write out the command to the device.
	 */
	if (command == NAND_CMD_SEQIN) {
		int readcmd;
		if (column >= mtd->oobblock) {
			/* OOB area */
			column -= mtd->oobblock;
			readcmd = NAND_CMD_READOOB;
		} else if (column < 256) {
			/* First 256 bytes --> READ0 */
			readcmd = NAND_CMD_READ0;
		} else {
			column -= 256;
			readcmd = NAND_CMD_READ1;
		}
		NFCMD = readcmd;
	}
	NFCMD = (command & 0xFF);

	if (column != -1 || page_addr != -1) {
		/* Serially input address */
		if (column != -1)
			NFADDR = (column & 0xFF);
		if (page_addr != -1) {
			NFADDR = (unsigned char)(page_addr & 0xff);
			NFADDR = (unsigned char)((page_addr >> 8) & 0xff);
			/* One more address cycle for higher density devices */
			if (mtd->size & 0x0c000000) 
				NFADDR = (unsigned char)((page_addr >> 16) & 0x0f);
		}
		/* Latch in address */
	}
	
	/* 
	 * program and erase have their own busy handlers 
	 * status and sequential in needs no delay
	 */
	switch (command) {
			
	case NAND_CMD_PAGEPROG:
	case NAND_CMD_ERASE1:
	case NAND_CMD_ERASE2:
	case NAND_CMD_SEQIN:
	case NAND_CMD_STATUS:
		return;

	case NAND_CMD_RESET:
		if (this->dev_ready)	
			break;
		NFCMD = NAND_CMD_STATUS;
		while ( !(NFDATA & 0x40));
		return;

	/* This applies to read commands */	
	default:
		/* 
		 * If we don't have access to the busy pin, we apply the given
		 * command delay
		*/
		if (!this->dev_ready) {
			udelay (this->chip_delay);
			return;
		}	
	}
	
	/* wait until command is processed */
	while (!this->dev_ready(mtd));
}

/*
* Main initialization routine
*/
int __init s3c2410_nand_init (void)

{
	struct nand_chip *this;
    u_int16_t nfconf;
	int err = 0;

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

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

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

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

    /* set NAND Flash controller */
    {
      nfconf = NFCONF;
      /* NAND Flash controller enable */
      nfconf |= NFCONF_FCTRL_EN;

      /* Set flash memory timing */
      nfconf &= ~NFCONF_TWRPH1;   /* 0x0 */
      nfconf |= NFCONF_TWRPH0_3;  /* 0x3 */
      nfconf &= ~NFCONF_TACLS;    /* 0x0 */

      NFCONF = nfconf;
    }

	/* Set address of NAND IO lines */
	this->IO_ADDR_R = io_p2v(0x4e00000c);
	this->IO_ADDR_W = io_p2v(0x4e00000c);
	this->select_chip = s3c2410_nand_select_chip;
	this->dev_ready = s3c2410_nand_device_ready;
	this->cmdfunc = s3c2410_nand_cmdfunc;

	this->eccmode = NAND_ECC_SOFT;

	/* 20 us command delay time */
	this->chip_delay = 20;		

	/* Scan to find existance of the device */
	if (nand_scan (s3c2410_nand_mtd, 1)) {
		err = -ENXIO;
		goto out_mtd;
	}

	/* Allocate memory for internal data buffer */
	this->data_buf = kmalloc (sizeof(u_char) * (s3c2410_nand_mtd->oobblock + s3c2410_nand_mtd->oobsize), GFP_KERNEL);
	if (!this->data_buf) {
		printk ("Unable to allocate NAND data buffer\n");
		err = -ENOMEM;
		goto out_mtd;
	}
#ifdef CONFIG_MTD_NAND_BONFS
	s3c2410_nand_mtd->oobinfo = bonfs_oob;
	mtd_num = bon_check_mtd(s3c2410_nand_mtd, &partition_info);
	if (mtd_num > 1)
		add_mtd_partitions(s3c2410_nand_mtd, partition_info, mtd_num);
	else
		add_mtd_device(s3c2410_nand_mtd);
#else
	add_mtd_device(s3c2410_nand_mtd);

#endif

	goto out;

out_cac:

	kfree (this->data_buf);    
out_mtd:
	kfree (s3c2410_nand_mtd);
out:
	return err;
}

module_init(s3c2410_nand_init);

/*
* Clean up routine
*/
#ifdef MODULE
static void __exit s3c2410_nand_cleanup (void)

{
	struct nand_chip *this = (struct nand_chip *) &s3c2410_nand_mtd[1];

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

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

	/* Free the MTD device structure */
	kfree (s3c2410_nand_mtd);
}

module_exit(s3c2410_nand_cleanup);
#endif

MODULE_LICENSE("GPL");

/*
* NAND Flash Controller (Page 6-1 ~ 6-8)
*
* Register

   NFCONF   NAND Flash Configuration    [word, R/W, 0x00000000]
   NFCMD    NAND Flash Command Set      [word, R/W, 0x00000000]
   NFADDR   NAND Flash Address Set      [word, R/W, 0x00000000]
   NFDATA   NAND Flash Data             [word, R/W, 0x00000000]
   NFSTAT   NAND Flash Status           [word, R, 0x00000000]
   NFECC    NAND Flash ECC              [3 bytes, R, 0x00000000]

*
*/

#if 0

#define bNAND_CTL(Nb) __REG(0x4e000000 + (Nb))
#define NFCONF bNAND_CTL(0x00)

#define NFCMD       bNAND_CTL(0x04)
#define NFADDR      bNAND_CTL(0x08)
#define NFDATA      bNAND_CTL(0x0c)
#define NFSTAT      bNAND_CTL(0x10)
#define NFECC       bNAND_CTL(0x14)
#define fNFCONF_TWRPH1   Fld(3,0)
#define NFCONF_TWRPH1    FMsk(fNFCONF_TWRPH1)
#define NFCONF_TWRPH1_0  FInsrt(0x0, fNFCONF_TWRPH1) /* 0 */
#define fNFCONF_TWRPH0   Fld(3,4)
#define NFCONF_TWRPH0    FMsk(fNFCONF_TWRPH0)
#define NFCONF_TWRPH0_3  FInsrt(0x3, fNFCONF_TWRPH0) /* 3 */
#define fNFCONF_TACLS    Fld(3,8)
#define NFCONF_TACLS     FMsk(fNFCONF_TACLS)
#define NFCONF_TACLS_0   FInsrt(0x0, fNFCONF_TACLS) /* 0 */
#define fNFCONF_nFCE     Fld(1,11)
#define NFCONF_nFCE      FMsk(fNFCONF_nFCE)
#define NFCONF_nFCE_LOW  FInsrt(0x0, fNFCONF_nFCE) /* active */

#define NFCONF_nFCE_HIGH FInsrt(0x1, fNFCONF_nFCE) /* inactive */
#define fNFCONF_ECC Fld(1,12)
#define NFCONF_ECC FMsk(fNFCONF_ECC)
#define NFCONF_ECC_NINIT FInsrt(0x0, fNFCONF_ECC) /* not initialize */
#define NFCONF_ECC_INIT FInsrt(0x1, fNFCONF_ECC) /* initialize */
#define fNFCONF_ADDRSTEP Fld(1,13) /* Addressing Step */
#define NFCONF_ADDRSTEP FMsk(fNFCONF_ADDRSTEP)
#define fNFCONF_PAGESIZE Fld(1,14)
#define NFCONF_PAGESIZE FMsk(fNFCONF_PAGESIZE)
#define NFCONF_PAGESIZE_256 FInsrt(0x0, fNFCONF_PAGESIZE) /* 256 bytes */ #define NFCONF_PAGESIZE_512 FInsrt(0x1, fNFCONF_PAGESIZE) /* 512 bytes */
#define fNFCONF_FCTRL Fld(1,15) /* Flash controller enable/disable */
#define NFCONF_FCTRL FMsk(fNFCONF_FCTRL)
#define NFCONF_FCTRL_DIS FInsrt(0x0, fNFCONF_FCTRL) /* Disable */ #define NFCONF_FCTRL_EN FInsrt(0x1, fNFCONF_FCTRL) /* Enable */

#define NFSTAT_RnB      (1 << 0)
#define NFSTAT_nFWE     (1 << 8)
#define NFSTAT_nFRE     (1 << 9)
#define NFSTAT_ALE      (1 << 10)
#define NFSTAT_CLE      (1 << 11)

#define NFSTAT_AUTOBOOT (1 << 15)

#endif /* 0 */