/* * NAND controller on SAMSUNG S3C2410 * * bushi@mizi.com * * 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 #include #include #include #include #include #include #include #include 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 */