#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.32.9
-# Wed Oct 20 15:12:34 2010
+# Sat Nov 6 11:33:18 2010
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
# CONFIG_RELAY is not set
# CONFIG_NAMESPACES is not set
CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE="../initramfs"
-CONFIG_INITRAMFS_ROOT_UID=0
-CONFIG_INITRAMFS_ROOT_GID=0
-# CONFIG_RD_GZIP is not set
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
# CONFIG_RD_BZIP2 is not set
# CONFIG_RD_LZMA is not set
-CONFIG_INITRAMFS_COMPRESSION_NONE=y
-# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
-# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
-# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_ANON_INODES=y
#
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
-CONFIG_CPU_ICACHE_DISABLE=y
-CONFIG_CPU_DCACHE_DISABLE=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
# CONFIG_CPU_BPREDICT_DISABLE is not set
CONFIG_HAS_TLS_REG=y
CONFIG_ARM_L1_CACHE_SHIFT=5
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
-# CONFIG_MTD is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_RK29=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
CONFIG_MISC_DEVICES=y
# CONFIG_ANDROID_PMEM is not set
# CONFIG_ENCLOSURE_SERVICES is not set
#
# CONFIG_RK2818_POWER is not set
+#
+# GPU Vivante
+#
+CONFIG_VIVANTE=y
+
#
# CMMB
#
#
# File systems
#
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-# CONFIG_EXT2_FS_XIP is not set
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_XATTR=y
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
# CONFIG_EXT4_FS is not set
-CONFIG_JBD=y
-CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
-CONFIG_FS_POSIX_ACL=y
+# CONFIG_FS_POSIX_ACL is not set
# CONFIG_XFS_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
+CONFIG_YAFFS_FS=y
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
+# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set
+# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_DECOMPRESS_GZIP=y
CONFIG_REED_SOLOMON=y
CONFIG_REED_SOLOMON_ENC8=y
CONFIG_REED_SOLOMON_DEC8=y
--- /dev/null
+
+/*
+ * drivers/mtd/nand/rk29_nand.c
+ *
+ * Copyright (C) 2010 RockChip, Inc.
+ * Author: hxy@rock-chips.com
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/rk29_nand.h>
+#include <mach/rk29_iomap.h>
+#include <mach/iomux.h>
+
+#define PROGRAM_BUSY_COUNT 10000
+#define ERASE_BUSY_COUNT 20000
+#define READ_BUSY_COUNT 5000
+#define RESET_BUSY_COUNT 20000
+
+/* Define delays in microsec for NAND device operations */
+#define TROP_US_DELAY 2000
+
+
+#ifdef CONFIG_DM9000_USE_NAND_CONTROL
+static DEFINE_MUTEX(rknand_mutex);
+#define RKNAND_LOCK() do { int panic = in_interrupt() | in_atomic(); if (!panic) mutex_lock(&rknand_mutex); } while (0)
+#define RKNAND_UNLOCK() do { int panic = in_interrupt() | in_atomic(); if (!panic) mutex_unlock(&rknand_mutex); } while (0)
+#else
+#define RKNAND_LOCK() do {} while (0)
+#define RKNAND_UNLOCK() do {} while (0)
+#endif
+
+struct rk29_nand_mtd {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+ const struct rk29_nand_flash *flash_info;
+
+ struct clk *clk;
+ unsigned long clk_rate;
+ void __iomem *regs;
+ int cs; // support muliple nand chip,record current chip select
+ u_char accesstime;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
+
+};
+
+/* OOB placement block for use with software ecc generation */
+static struct nand_ecclayout nand_sw_eccoob_8 = {
+ .eccbytes = 48,
+ .eccpos = { 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55},
+ .oobfree = {{0,8},{56, 72}}
+};
+
+/* OOB placement block for use with hardware ecc generation */
+static struct nand_ecclayout nand_hw_eccoob_16 = {
+ .eccbytes = 28,
+ .eccpos = { 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,
+ 18,19,20,21,22,23,24,25,26,27,28,29,30,31},
+ .oobfree = {{0, 4}}
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+
+static void rk29_nand_wait_busy(struct mtd_info *mtd, uint32_t timeout)
+{
+
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ while (timeout > 0)
+ {
+ timeout--;
+ udelay(10);
+ if ( pRK29NC->FMCTL& FMC_FRDY)
+ break;
+
+ }
+
+ return;
+}
+
+static void rk29_nand_wait_bchdone(struct mtd_info *mtd, uint32_t timeout)
+{
+
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ while (timeout > 0)
+ {
+ timeout--;
+ udelay(1);
+ if(pRK29NC->BCHST &(1<<1))
+ break;
+ }
+
+ return;
+}
+
+// only for dma mode
+static void wait_op_done(struct mtd_info *mtd, int max_retries, uint16_t param)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ while (max_retries-- > 0) {
+ udelay(1);
+ if (pRK29NC->FLCTL & FL_RDY)
+ break;
+ }
+}
+
+static int rk29_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ if(pRK29NC->FMCTL& FMC_FRDY)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+* ÉèÖÃƬѡ
+*/
+static void rk29_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+
+ if( chip<0 )
+ pRK29NC->FMCTL &=0xffffff00; // release chip select
+ else
+ {
+ master->cs = chip;
+ pRK29NC->FMCTL &=0xffffff00;
+ pRK29NC ->FMCTL |= 0x1<<chip; // select chip
+ }
+
+}
+
+/*
+ * ¶ÁÒ»¸ö×Ö½ÚÊý¾Ý
+*/
+static u_char rk29_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ u_char ret = 0;
+
+ ret = (u_char)(pRK29NC ->chip[master->cs].data);
+
+ return ret;
+}
+
+/*
+ * ¶ÁÒ»¸öword ³¤¶ÈÊý¾Ý
+*/
+static u16 rk29_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+
+ u_char tmp1 = 0,tmp2=0;
+ u16 ret=0;
+
+ tmp1 = (u_char)(pRK29NC ->chip[master->cs].data);
+ tmp2 = (u_char)(pRK29NC ->chip[master->cs].data);
+
+ ret = (tmp2 <<8)|tmp1;
+
+ return ret;
+}
+
+static void rk29_nand_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+ uint32_t i, chipnr;
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+
+
+ if ( len < mtd->writesize ) // read oob
+ {
+ pRK29NC ->BCHCTL = BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ rk29_nand_wait_bchdone(mtd,TROP_US_DELAY) ;
+ memcpy(buf,(u_char *)(pRK29NC->spare),4); // only use nandc sram0
+ }
+ else
+ {
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ pRK29NC ->BCHCTL = BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ rk29_nand_wait_bchdone(mtd,TROP_US_DELAY) ;
+ memcpy(buf+i*0x400,(u_char *)(pRK29NC->buf),0x400); // only use nandc sram0
+ }
+ }
+
+
+
+ rk29_nand_select_chip(mtd,-1);
+
+ RKNAND_UNLOCK();
+
+
+ return;
+
+}
+
+static void rk29_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ uint32_t i = 0, chipnr;
+
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+
+
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ memcpy((u_char *)(pRK29NC->buf),buf+i*0x400,0x400); // only use nandc sram0
+ pRK29NC ->BCHCTL =BCH_WR|BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|0x1<<5|FL_RDN|FL_BYPASS|FL_START;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ }
+
+
+
+ rk29_nand_select_chip(mtd,-1);
+
+ RKNAND_UNLOCK();
+
+
+}
+
+
+static void rk29_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ uint32_t timeout = 1000;
+ char status,ret;
+
+
+ switch (command) {
+
+ case NAND_CMD_READID:
+ pRK29NC ->chip[master->cs].cmd = command;
+ pRK29NC ->chip[master->cs].addr = 0x0;
+ while (timeout>0)
+ {
+ timeout --;
+ udelay(1);
+ if(pRK29NC->FLCTL&FL_INTCLR)
+ break;
+
+ }
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+ break;
+
+ case NAND_CMD_READ0:
+ pRK29NC ->chip[master->cs].cmd = command;
+ if ( column>= 0 )
+ {
+ pRK29NC ->chip[master->cs].addr = column & 0xff;
+ if( mtd->writesize > 512)
+ pRK29NC ->chip[master->cs].addr = (column >> 8) & 0xff;
+ }
+ if ( page_addr>=0 )
+ {
+ pRK29NC ->chip[master->cs].addr = page_addr & 0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr >> 8) & 0xFF;
+ pRK29NC ->chip[master->cs].addr = (page_addr >> 16) & 0xff;
+ }
+ if( mtd->writesize > 512)
+ pRK29NC ->chip[0].cmd = NAND_CMD_READSTART;
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+
+ break;
+
+ case NAND_CMD_READ1:
+ pRK29NC ->chip[master->cs].cmd = command;
+ break;
+
+ case NAND_CMD_READOOB:
+ pRK29NC ->BCHCTL = 0x0;
+ if( mtd->writesize > 512 )
+ command = NAND_CMD_READ0; // È«²¿¶Á£¬°üÀ¨¶Áoob
+
+ pRK29NC ->chip[master->cs].cmd = command;
+
+ if ( mtd->writesize >512 )
+ {
+ if ( column>= 0 )
+ {
+ pRK29NC ->chip[master->cs].addr = column & 0xff;
+ pRK29NC ->chip[master->cs].addr = ( column >> 8) & 0xff;
+ }
+ if ( page_addr>=0 )
+ {
+ pRK29NC ->chip[master->cs].addr = page_addr & 0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr >> 8) & 0xFF;
+ pRK29NC ->chip[master->cs].addr = (page_addr >> 16) & 0xff;
+ }
+ pRK29NC ->chip[master->cs].cmd = NAND_CMD_READSTART;
+ }
+ else
+ {
+ pRK29NC ->chip[master->cs].addr = column;
+ }
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+
+
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ pRK29NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤
+ pRK29NC ->chip[master->cs].cmd = command;
+ rk29_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT);
+
+ pRK29NC ->chip[master->cs].cmd = NAND_CMD_STATUS;
+ status = pRK29NC ->chip[master->cs].data;
+
+ if(status&0x1)
+ ret = -1;
+ else
+ ret =0;
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ pRK29NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤
+ pRK29NC ->BCHCTL = 0x0;
+ pRK29NC ->chip[master->cs].cmd = command;
+ if ( page_addr>=0 )
+ {
+ pRK29NC ->chip[master->cs].addr = page_addr & 0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr>>8)&0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr>>16)&0xff;
+ }
+ break;
+
+ case NAND_CMD_ERASE2:
+ pRK29NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤
+ pRK29NC ->chip[master->cs].cmd = command;
+ rk29_nand_wait_busy(mtd,ERASE_BUSY_COUNT);
+ pRK29NC ->chip[master->cs].cmd = NAND_CMD_STATUS;
+ status = pRK29NC ->chip[master->cs].data;
+
+ if(status&0x1)
+ ret = -1;
+ else
+ ret =0;
+
+ break;
+
+ case NAND_CMD_SEQIN:
+ pRK29NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤
+ pRK29NC ->chip[master->cs].cmd = command;
+ udelay(1);
+ if ( column>= 0 )
+ {
+ pRK29NC ->chip[master->cs].addr = column;
+ if( mtd->writesize > 512)
+ pRK29NC ->chip[master->cs].addr = (column >> 8) & 0xff;
+ }
+ if( page_addr>=0 )
+ {
+ pRK29NC ->chip[master->cs].addr = page_addr & 0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr>>8)&0xff;
+ pRK29NC ->chip[master->cs].addr = (page_addr>>16)&0xff;
+ }
+
+ break;
+
+ case NAND_CMD_STATUS:
+ pRK29NC ->BCHCTL = 0x0;
+ pRK29NC ->chip[master->cs].cmd = command;
+ while (timeout>0)
+ {
+ timeout --;
+ udelay(1);
+ if(pRK29NC->FLCTL&FL_INTCLR)
+ break;
+
+ }
+ break;
+
+ case NAND_CMD_RESET:
+ pRK29NC ->chip[master->cs].cmd = command;
+ while (timeout>0)
+ {
+ timeout --;
+ udelay(1);
+ if(pRK29NC->FLCTL&FL_INTCLR)
+ break;
+
+ }
+ rk29_nand_wait_busy(mtd,RESET_BUSY_COUNT);
+ break;
+
+ /* This applies to read commands */
+ default:
+ pRK29NC ->chip[master->cs].cmd = command;
+ break;
+ }
+
+ udelay (1);
+
+}
+
+int rk29_nand_calculate_ecc(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ int eccdata[7],i;
+
+ for(i=0;i<7;i++)
+ {
+ eccdata[i] = pRK29NC->spare[i+1];
+
+
+ ecc_code[i*4] = eccdata[i]& 0xff;
+ ecc_code[i*4+1] = (eccdata[i]>> 8)& 0xff;
+ ecc_code[i*4+2] = (eccdata[i]>>16)& 0xff;
+ ecc_code[i*4+3] = (eccdata[i]>>24)& 0xff;
+
+ }
+
+ return 0;
+}
+
+ void rk29_nand_hwctl_ecc(struct mtd_info *mtd, int mode)
+ {
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ pRK29NC->BCHCTL = 1; // reset bch and enable hw ecc
+
+ return;
+ }
+
+ int rk29_nand_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,uint8_t *calc_ecc)
+ {
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ // hw correct data
+ if( pRK29NC->BCHST & (1<<2) )
+ {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "rk2818 nand :hw ecc uncorrectable error\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ int rk29_nand_read_page(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf, int page)
+ {
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ int i,chipnr;
+
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ pRK29NC ->BCHCTL = BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ rk29_nand_wait_bchdone(mtd,TROP_US_DELAY) ;
+
+ memcpy(buf+i*0x400,(u_char *)(pRK29NC->buf),0x400); // only use nandc sram0
+ }
+
+
+ rk29_nand_select_chip(mtd,-1);
+
+ RKNAND_UNLOCK();
+
+ return 0;
+
+ }
+
+void rk29_nand_write_page(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf)
+ {
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+ uint32_t i = 0, chipnr;
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+
+
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ memcpy((u_char *)(pRK29NC->buf),(buf+i*0x400),0x400); // only use nandc sram0
+ if(i==0)
+ memcpy((u_char *)(pRK29NC->spare),(u_char *)(chip->oob_poi + chip->ops.ooboffs),4);
+
+ pRK29NC ->BCHCTL = BCH_WR|BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_RDN|FL_BYPASS|FL_START;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ }
+
+ pRK29NC ->chip[0].cmd = NAND_CMD_PAGEPROG;
+
+
+
+ rk29_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT);
+
+ rk29_nand_select_chip(mtd,-1);
+
+ RKNAND_UNLOCK();
+
+ return;
+
+ }
+
+int rk29_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+ int i,chipnr;
+
+
+ if (sndcmd) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ sndcmd = 0;
+ }
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+
+
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+
+
+
+
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ pRK29NC ->BCHCTL = BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ rk29_nand_wait_bchdone(mtd,TROP_US_DELAY) ;
+ if(i==0)
+ memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK29NC->spare),4);
+ }
+
+
+ rk29_nand_select_chip(mtd,-1);
+
+ RKNAND_UNLOCK();
+
+
+ return sndcmd;
+}
+
+int rk29_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct rk29_nand_mtd *master = nand_chip->priv;
+ pNANDC pRK29NC= (pNANDC)(master->regs);
+
+ int i,chipnr;
+
+
+ RKNAND_LOCK();
+
+ chipnr = master->cs ;
+
+ rk29_nand_select_chip(mtd,chipnr);
+
+ rk29_nand_wait_busy(mtd,READ_BUSY_COUNT);
+
+ pRK29NC->FLCTL |= FL_BYPASS; // dma mode
+
+
+
+ for(i=0;i<mtd->writesize/0x400;i++)
+ {
+ pRK29NC ->BCHCTL = BCH_RST;
+ pRK29NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;
+ wait_op_done(mtd,TROP_US_DELAY,0);
+ rk29_nand_wait_bchdone(mtd,TROP_US_DELAY) ;
+ memcpy(buf+i*0x400,(u_char *)(pRK29NC->buf),0x400); // only use nandc sram0
+ if(i==0)
+ memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK29NC->spare),4);
+ }
+
+
+ rk29_nand_select_chip(mtd,-1);
+ RKNAND_UNLOCK();
+
+
+ return 0;
+}
+
+static int rk29_nand_setrate(struct rk29_nand_mtd *info)
+{
+ pNANDC pRK29NC= (pNANDC)(info->regs);
+
+ unsigned long clkrate = clk_get_rate(info->clk);
+
+ u_char accesstime,rwpw,csrw,rwcs;
+
+ unsigned int ns=0,timingcfg;
+
+ unsigned long flags;
+
+ //scan nand flash access time
+ if ( info->accesstime ==0x00 )
+ accesstime=50;
+ else if ( info->accesstime==0x80)
+ accesstime=25;
+ else if ( info->accesstime==0x08)
+ accesstime=20;
+ else
+ accesstime=60; //60ns
+#if 0
+ info->clk_rate = clkrate;
+ clkrate /= 1000000; /* turn clock into MHz for ease of use */
+
+ if(clkrate>0 && clkrate<200)
+ ns= 1000/clkrate; // ns
+ else
+ return -1;
+
+ timingcfg = (accesstime + ns -1)/ns;
+
+ timingcfg = (timingcfg>=3) ? (timingcfg-2) : timingcfg; //csrw+1, rwcs+1
+
+ rwpw = timingcfg-timingcfg/4;
+ csrw = timingcfg/4;
+ rwcs = (timingcfg/4 >=1)?(timingcfg/4):1;
+#else
+ rwpw = 4;
+ csrw = 1;
+ rwcs = 2;
+#endif
+
+ RKNAND_LOCK();
+
+ pRK29NC ->FMWAIT |= (rwcs<<FMW_RWCS_OFFSET)|(rwpw<<FMW_RWPW_OFFSET)|(csrw<<FMW_CSRW_OFFSET);
+
+ RKNAND_UNLOCK();
+
+
+ return 0;
+}
+
+/* cpufreq driver support */
+
+#ifdef CONFIG_CPU_FREQ
+
+static int rk29_nand_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
+{
+ struct rk29_nand_mtd *info;
+ unsigned long newclk;
+
+ info = container_of(nb, struct rk29_nand_mtd, freq_transition);
+ newclk = clk_get_rate(info->clk);
+
+ if (val == CPUFREQ_POSTCHANGE && newclk != info->clk_rate)
+ {
+ rk29_nand_setrate(info);
+ }
+
+ return 0;
+}
+
+static inline int rk29_nand_cpufreq_register(struct rk29_nand_mtd *info)
+{
+ info->freq_transition.notifier_call = rk29_nand_cpufreq_transition;
+
+ return cpufreq_register_notifier(&info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void rk29_nand_cpufreq_deregister(struct rk29_nand_mtd *info)
+{
+ cpufreq_unregister_notifier(&info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int rk29_nand_cpufreq_register(struct rk29_nand_mtd *info)
+{
+ return 0;
+}
+
+static inline void rk29_nand_cpufreq_deregister(struct rk29_nand_mtd *info)
+{
+}
+#endif
+
+
+static int rk29_nand_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct rk29_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct rk29_nand_mtd *master;
+ struct resource *res;
+ int err = 0;
+ pNANDC pRK29NC;
+ u_char maf_id,dev_id,ext_id3,ext_id4;
+ struct nand_chip *chip;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *partitions = NULL;
+ int num_partitions = 0;
+#endif
+
+ /* Allocate memory for MTD device structure and private data */
+ master = kzalloc(sizeof(struct rk29_nand_mtd), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &master->nand;
+ mtd = &master->mtd;
+ mtd->priv = this;
+ mtd->owner = THIS_MODULE;
+ mtd->name = dev_name(&pdev->dev);
+
+ /* 50 us command delay time */
+ this->chip_delay = 5;
+
+ this->priv = master;
+ this->dev_ready = rk29_nand_dev_ready;
+ this->cmdfunc = rk29_nand_cmdfunc;
+ this->select_chip = rk29_nand_select_chip;
+ this->read_byte = rk29_nand_read_byte;
+ this->read_word = rk29_nand_read_word;
+ this->write_buf = rk29_nand_write_buf;
+ this->read_buf = rk29_nand_read_buf;
+ this->options |= NAND_USE_FLASH_BBT; // open bbt options
+
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto outres;
+ }
+
+ master->regs = ioremap(res->start, res->end - res->start + 1);
+ if (!master->regs) {
+ err = -EIO;
+ goto outres;
+ }
+
+ if (pdata->hw_ecc) {
+ this->ecc.calculate = rk29_nand_calculate_ecc;
+ this->ecc.hwctl = rk29_nand_hwctl_ecc;
+ this->ecc.correct = rk29_nand_correct_data;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.read_page = rk29_nand_read_page;
+ this->ecc.write_page = rk29_nand_write_page;
+ this->ecc.read_oob = rk29_nand_read_oob;
+ this->ecc.read_page_raw = rk29_nand_read_page_raw;
+ this->ecc.size = 1024;
+ this->ecc.bytes = 28;
+ this->ecc.layout = &nand_hw_eccoob_16;
+ } else {
+ this->ecc.size = 256;
+ this->ecc.bytes = 3;
+ this->ecc.layout = &nand_sw_eccoob_8;
+ this->ecc.mode = NAND_ECC_SOFT;
+ }
+
+
+
+ master->clk = clk_get(NULL, "nandc");
+
+ clk_enable(master->clk);
+
+ pRK29NC = (pNANDC)(master->regs);
+ pRK29NC ->FMCTL = FMC_WP|FMC_FRDY;
+ pRK29NC ->FMWAIT |= (1<<FMW_RWCS_OFFSET)|(4<<FMW_RWPW_OFFSET)|(1<<FMW_CSRW_OFFSET);
+ pRK29NC ->BCHCTL = 0x1;
+
+ this->select_chip(mtd, 0);
+ this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ maf_id = this->read_byte(mtd);
+ dev_id = this->read_byte(mtd);
+ ext_id3 = this->read_byte(mtd);
+ ext_id4 = this->read_byte(mtd);
+
+ master->accesstime = ext_id4&0x88;
+
+ rk29_nand_setrate(master);
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ /* NAND bus width determines access funtions used by upper layer */
+ if (pdata->width == 2) {
+ this->options |= NAND_BUSWIDTH_16;
+ this->ecc.layout = &nand_hw_eccoob_16;
+ }
+ // iomux flash cs1~cs7
+ if (pdata && pdata->io_init) {
+ pdata->io_init();
+ }
+
+ /* Scan to find existence of the device */
+#if 0
+ if (nand_scan(mtd, 8)) { // rk2818 nandc support max 8 cs
+#else
+ if (nand_scan(mtd, 1)) { // test for fpga board nand
+#endif
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "RK2818 NAND: Unable to find any NAND device.\n");
+ err = -ENXIO;
+ goto outscan;
+ }
+
+ //¸ù¾ÝƬѡÇé¿ö»Ö¸´IO MUXÔʼֵ
+#if 0
+ chip = mtd->priv;
+ switch(chip->numchips)
+ {
+ case 1:
+ rk2818_mux_api_mode_resume(GPIOA5_FLASHCS1_SEL_NAME);
+ case 2:
+ rk2818_mux_api_mode_resume(GPIOA6_FLASHCS2_SEL_NAME);
+ case 3:
+ rk2818_mux_api_mode_resume(GPIOA7_FLASHCS3_SEL_NAME);
+ case 4:
+ rk2818_mux_api_mode_resume(GPIOE_SPI1_FLASH_SEL1_NAME);
+ case 5:
+ case 6:
+ rk2818_mux_api_mode_resume(GPIOE_SPI1_FLASH_SEL_NAME);
+ case 7:
+ case 8:
+ break;
+ default:
+ DEBUG(MTD_DEBUG_LEVEL0, "RK2818 NAND: numchips error!!!\n");
+ }
+#endif
+#if 0
+ // rk281x dma mode bch must (1k data + 32 oob) bytes align , so cheat system writesize =1024,oobsize=32
+ mtd->writesize = 1024;
+ mtd->oobsize = 32;
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+ num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
+ if (num_partitions > 0) {
+ printk(KERN_INFO "Using commandline partition definition\n");
+ add_mtd_partitions(mtd, partitions, num_partitions);
+ if(partitions)
+ kfree(partitions);
+ } else if (pdata->nr_parts) {
+ printk(KERN_INFO "Using board partition definition\n");
+ add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts);
+ } else
+#endif
+ {
+ printk(KERN_INFO "no partition info available, registering whole flash at once\n");
+ add_mtd_device(mtd);
+ }
+
+ platform_set_drvdata(pdev, master);
+
+ err =rk29_nand_cpufreq_register(master);
+ if (err < 0) {
+ printk(KERN_ERR"rk2818 nand failed to init cpufreq support\n");
+ goto outscan;
+ }
+
+ return 0;
+
+outres:
+outscan:
+ iounmap(master->regs);
+ kfree(master);
+
+ return err;
+
+}
+
+static int rk29_nand_remove(struct platform_device *pdev)
+{
+ struct rk29_nand_mtd *master = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if(master == NULL)
+ return 0;
+
+ rk29_nand_cpufreq_deregister(master);
+
+
+ nand_release(&master->mtd);
+
+ if(master->regs!=NULL){
+ iounmap(master->regs);
+ master->regs = NULL;
+ }
+
+ if (master->clk != NULL && !IS_ERR(master->clk)) {
+ clk_disable(master->clk);
+ clk_put(master->clk);
+ }
+
+ kfree(master);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rk29_nand_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "RK2818_NAND : NAND suspend\n");
+ if (info)
+ ret = info->suspend(info);
+ return ret;
+}
+
+static int rk29_nand_resume(struct platform_device *pdev)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "RK2818_NAND : NAND resume\n");
+ /* Enable the NFC clock */
+
+ if (info)
+ info->resume(info);
+
+ return ret;
+}
+#else
+#define rk29_nand_suspend NULL
+#define rk29_nand_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver rk29_nand_driver = {
+ .driver = {
+ .name = "rk29-nand",
+ },
+ .probe = rk29_nand_probe,
+ .remove = rk29_nand_remove,
+ .suspend = rk29_nand_suspend,
+ .resume = rk29_nand_resume,
+};
+
+static int __init rk29_nand_init(void)
+{
+ /* Register the device driver structure. */
+ printk("rk29_nand_init\n");
+ return platform_driver_register(&rk29_nand_driver);;
+}
+
+static void __exit rk29_nand_exit(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&rk29_nand_driver);
+}
+
+#ifdef CONFIG_DM9000_USE_NAND_CONTROL
+// nandc dma cs mutex for dm9000 interface
+void rk29_nand_status_mutex_lock(void)
+{
+ pNANDC pRK29NC= (pNANDC)RK2818_NANDC_BASE;
+ mutex_lock(&rknand_mutex);
+ pRK29NC->FMCTL &=0xffffff00; // release chip select
+
+}
+
+int rk29_nand_status_mutex_trylock(void)
+{
+ pNANDC pRK29NC= (pNANDC)RK2818_NANDC_BASE;
+ if( mutex_trylock(&rknand_mutex))
+ {
+ pRK29NC->FMCTL &=0xffffff00; // release chip select
+ return 1; // ready
+ }
+ else
+ return 0; // busy
+}
+
+void rk29_nand_status_mutex_unlock(void)
+{
+ mutex_unlock(&rknand_mutex);
+ return;
+}
+#endif
+
+module_init(rk29_nand_init);
+module_exit(rk29_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("hxy <hxy@rock-chips.com>");
+MODULE_DESCRIPTION("MTD NAND driver for rk29 device");
+