From c3ca2dc23e78922fc3359e6380c3301da6e7909a Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E5=BF=83=E5=AE=87?= Date: Fri, 21 May 2010 08:15:40 +0000 Subject: [PATCH] add rk2818 nand flash driver and yaffs2 support --- .config | 24 +- arch/arm/Makefile | 2 +- arch/arm/mach-rk2818/Makefile.boot | 4 +- arch/arm/mach-rk2818/board-midsdk.c | 3 +- arch/arm/mach-rk2818/devices.c | 25 + arch/arm/mach-rk2818/devices.h | 1 + .../mach-rk2818/include/mach/rk2818_nand.h | 121 +++ drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_bbt.c | 8 +- drivers/mtd/nand/rk2818_nand.c | 800 ++++++++++++++++++ fs/yaffs2/yaffs_fs.c | 3 + fs/yaffs2/yaffs_mtdif.c | 4 +- 13 files changed, 989 insertions(+), 13 deletions(-) create mode 100644 arch/arm/mach-rk2818/include/mach/rk2818_nand.h create mode 100644 drivers/mtd/nand/rk2818_nand.c diff --git a/.config b/.config index 93dd88c4b0ba..699ea77f69f0 100644 --- a/.config +++ b/.config @@ -512,8 +512,16 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set -# CONFIG_MTD_NAND_IDS is not set -# CONFIG_MTD_NAND 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_RK2818=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set # CONFIG_MTD_ONENAND is not set # @@ -1182,7 +1190,17 @@ CONFIG_MISC_FILESYSTEMS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_YAFFS_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 diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 49d1e52b8576..1442b3bebaee 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -108,7 +108,7 @@ CHECKFLAGS += -D__arm__ #Default value head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o -textofs-y := 0x00008000 +textofs-y := 0x00408000 textofs-$(CONFIG_ARCH_CLPS711X) := 0x00028000 # SA1111 DMA bug: we don't want the kernel to live in precious DMA-able memory ifeq ($(CONFIG_ARCH_SA1100),y) diff --git a/arch/arm/mach-rk2818/Makefile.boot b/arch/arm/mach-rk2818/Makefile.boot index 0706dc3c698a..b1c79179d4ee 100644 --- a/arch/arm/mach-rk2818/Makefile.boot +++ b/arch/arm/mach-rk2818/Makefile.boot @@ -1,3 +1,3 @@ - zreladdr-y := 0x60008000 -params_phys-y := 0x60000100 + zreladdr-y := 0x60408000 +params_phys-y := 0x600f8000 initrd_phys-y := 0x60800000 diff --git a/arch/arm/mach-rk2818/board-midsdk.c b/arch/arm/mach-rk2818/board-midsdk.c index 85fe4c1d8e42..e6757d08dd25 100644 --- a/arch/arm/mach-rk2818/board-midsdk.c +++ b/arch/arm/mach-rk2818/board-midsdk.c @@ -295,6 +295,7 @@ static struct platform_device *devices[] __initdata = { &rk2818_device_fb, &rk2818_device_backlight, &rk2818_device_dsp, + &rk2818_nand_device, }; extern struct sys_timer rk2818_timer; @@ -335,7 +336,7 @@ MACHINE_START(RK2818, "RK28board") /* UART for LL DEBUG */ .phys_io = 0x18002000, .io_pg_offst = ((0xFF100000) >> 18) & 0xfffc, - .boot_params = RK2818_SDRAM_PHYS + 0x100, + .boot_params = RK2818_SDRAM_PHYS + 0xf8000, .map_io = machine_rk2818_mapio, .init_irq = machine_rk2818_init_irq, .init_machine = machine_rk2818_board_init, diff --git a/arch/arm/mach-rk2818/devices.c b/arch/arm/mach-rk2818/devices.c index d5f70bbeb380..63041036a687 100644 --- a/arch/arm/mach-rk2818/devices.c +++ b/arch/arm/mach-rk2818/devices.c @@ -27,6 +27,7 @@ #include #include +#include static struct resource resources_i2c0[] = { { @@ -352,4 +353,28 @@ struct platform_device rk2818_device_pmem_dsp = { #endif +#if defined(CONFIG_MTD_NAND_RK2818) +static struct resource nand_resources[] = { + { + .start = RK2818_NANDC_PHYS, + .end = RK2818_NANDC_PHYS+RK2818_NANDC_SIZE -1, + .flags = IORESOURCE_MEM, + } +}; +static struct rk2818_nand_platform_data rk2818_nand_data = { + .width = 1, /* data bus width in bytes */ + .hw_ecc = 1, /* hw ecc 0: soft ecc */ + .num_flash = 1, +}; +struct platform_device rk2818_nand_device = { + .name = "rk2818-nand", + .id = -1, + .resource = nand_resources, + .num_resources= ARRAY_SIZE(nand_resources), + .dev = { + .platform_data= &rk2818_nand_data, + }, + +}; +#endif diff --git a/arch/arm/mach-rk2818/devices.h b/arch/arm/mach-rk2818/devices.h index fdfda9e564c4..9ab2063e8233 100644 --- a/arch/arm/mach-rk2818/devices.h +++ b/arch/arm/mach-rk2818/devices.h @@ -33,4 +33,5 @@ extern struct platform_device rk2818_device_adckey; extern struct platform_device rk2818_device_battery; extern struct platform_device rk2818_device_backlight; extern struct platform_device rk2818_device_dsp; +extern struct platform_device rk2818_nand_device; #endif diff --git a/arch/arm/mach-rk2818/include/mach/rk2818_nand.h b/arch/arm/mach-rk2818/include/mach/rk2818_nand.h new file mode 100644 index 000000000000..e61d28433323 --- /dev/null +++ b/arch/arm/mach-rk2818/include/mach/rk2818_nand.h @@ -0,0 +1,121 @@ + +/* + * arch/arm/mach-rk2818/include/mach/rk2818_nand.h + * + * Copyright (C) 2010 RockChip, Inc. + * Author: + * + * 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. + */ + +#ifndef __ASM_ARCH_RK2818_NAND_H +#define __ASM_ARCH_RK2818_NAND_H + + +//BCHCTL¼Ä´æÆ÷ +#define BCH_WR 0x0002 +#define BCH_RST 0x0001 +//FLCTL¼Ä´æÆ÷ +#define FL_RDY (0x1<<20) +#define FL_LBA_EN (0x1<<11) +#define FL_COR_EN (0x1<<10) +#define FL_INT_EN (0x1<<9) +#define FL_INTCLR (0x1<<8) +#define FL_STMOD (0x1<<7) +#define FL_TRCNT (0x3<<5) +#define FL_STADDR (0x1<<4) +#define FL_BYPASS (0x1<<3) +#define FL_START (0x1<<2) +#define FL_RDN (0x1<<1) +#define FL_RST (0x1<<0) +//FMCTL¼Ä´æÆ÷ +#define FMC_WP (0x1<<8) +#define FMC_FRDY (0x1<<9) +#define FMC_FRDY_INT_EN (0x1<<10) +#define FMC_FRDY_INT_CLR (0x1<<11) +//FMWAIT¼Ä´æÆ÷ +#define FMW_RWCS_OFFSET 0 +#define FMW_RWPW_OFFSET 5 +#define FMW_RDY (0x1<<11) +#define FMW_CSRW_OFFSET 12 +#define FMW_DLY_OFFSET 16 +#define FMW_DLY_DBG (0x1<<23) + +struct rk2818_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct rk2818_nand_cmdset { + uint16_t read1; + uint16_t read2; + uint16_t program; + uint16_t read_status; + uint16_t read_id; + uint16_t erase; + uint16_t reset; + uint16_t lock; + uint16_t unlock; + uint16_t lock_status; +}; + +typedef volatile struct tagCHIP_IF +{ + uint32_t data; + uint32_t addr; + uint32_t cmd; + uint32_t RESERVED[0x3d]; +}CHIP_IF, *pCHIP_IF; + +//NANDC Registers +typedef volatile struct tagNANDC +{ + volatile uint32_t FMCTL; + volatile uint32_t FMWAIT; + volatile uint32_t FLCTL; + volatile uint32_t BCHCTL; + volatile uint32_t BCHST; + volatile uint32_t RESERVED1[(0x200-0x14)/4]; //FLR + volatile uint32_t spare[0x200/4]; + volatile uint32_t RESERVED2[0x400/4]; + volatile CHIP_IF chip[8]; + volatile uint32_t buf[0x800/4]; +}NANDC, *pNANDC; + +struct rk2818_nand_flash { + const struct rk2818_nand_timing *timing; /* NAND Flash timing */ + const struct rk2818_nand_cmdset *cmdset; + + uint32_t page_per_block; /* Pages per block (PG_PER_BLK) */ + uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ + uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ + uint32_t num_blocks; /* Number of physical blocks in Flash */ + uint32_t chip_id; +}; + +struct rk2818_nand_platform_data { + + int width; /* data bus width in bytes */ + int hw_ecc; /* 1:hw ecc, 0: soft ecc */ + struct mtd_partition *parts; + unsigned int nr_parts; + size_t num_flash; +}; + + +#endif /* __ASM_ARCH_RK2818_NAND_H */ + diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index a8dbad3a286e..6e999604337e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -360,6 +360,12 @@ config MTD_NAND_ATMEL_ECC_NONE endchoice +config MTD_NAND_RK2818 + tristate "NAND Flash support for RK2818" + depends on ARCH_RK2818 + help + This enables the NAND flash controller on the RK2818 SoC + config MTD_NAND_PXA3xx tristate "Support for NAND flash devices on PXA3xx" depends on MTD_NAND && PXA3xx diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6950d3dabf10..063518202051 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o +obj-$(CONFIG_MTD_NAND_RK2818) += rk2818_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 55c23e5cd210..bd977a5a904c 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1115,8 +1115,8 @@ static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; static struct nand_bbt_descr bbt_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, + | NAND_BBT_2BIT /*| NAND_BBT_VERSION */| NAND_BBT_PERCHIP, + .offs =0, //8, // meet to rk2818 nandc spare .len = 4, .veroffs = 12, .maxblocks = 4, @@ -1125,8 +1125,8 @@ static struct nand_bbt_descr bbt_main_descr = { static struct nand_bbt_descr bbt_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, + | NAND_BBT_2BIT /*| NAND_BBT_VERSION */| NAND_BBT_PERCHIP, + .offs =0, //8, // meet to rk2818 nandc spare .len = 4, .veroffs = 12, .maxblocks = 4, diff --git a/drivers/mtd/nand/rk2818_nand.c b/drivers/mtd/nand/rk2818_nand.c new file mode 100644 index 000000000000..9247ce9d51e3 --- /dev/null +++ b/drivers/mtd/nand/rk2818_nand.c @@ -0,0 +1,800 @@ + +/* + * drivers/mtd/nand/rk2818_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PROGRAM_BUSY_COUNT 10000 +#define ERASE_BUSY_COUNT 20000 +#define READ_BUSY_COUNT 5000 + +/* Define delays in microsec for NAND device operations */ +#define TROP_US_DELAY 2000 + +struct rk2818_nand_mtd { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; + const struct rk2818_nand_flash *flash_info; + + struct clk *clk; + void __iomem *regs; + uint16_t col_addr; +}; + +/* 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 rk2818_nand_wait_busy(struct mtd_info *mtd, uint32_t timeout) +{ + + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + while (timeout > 0) + { + timeout--; + udelay(10); + if ( pRK28NC->FMCTL& FMC_FRDY) + break; + + } + + return; +} + +static void rk2818_nand_wait_bchdone(struct mtd_info *mtd, uint32_t timeout) +{ + + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + while (timeout > 0) + { + timeout--; + udelay(1); + if(pRK28NC->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 rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + while (max_retries-- > 0) { + udelay(1); + if (pRK28NC->FLCTL & FL_RDY) + break; + } +} + +static int rk2818_nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + if(pRK28NC->FMCTL& FMC_FRDY) + return 1; + else + return 0; +} + +/* +* ÉèÖÃƬѡ +*/ +static void rk2818_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + pRK28NC ->FMCTL |= 0x1<priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + u_char ret = 0; + pRK28NC->FLCTL &= ~FL_BYPASS; // bypass mode + ret = (u_char)(pRK28NC ->chip[0].data); + return ret; +} + +/* + * ¶ÁÒ»¸öword ³¤¶ÈÊý¾Ý +*/ +static u16 rk2818_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + u_char tmp1 = 0,tmp2=0; + u16 ret=0; + + pRK28NC->FLCTL &= ~FL_BYPASS; // bypass mode + + tmp1 = (u_char)(pRK28NC ->chip[0].data); + tmp2 = (u_char)(pRK28NC ->chip[0].data); + + ret = (tmp2 <<8)|tmp1; + + return ret; +} + +static void rk2818_nand_read_buf(struct mtd_info *mtd, u_char* const buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + uint32_t i; + + + if ( len < mtd->writesize ) // read oob + { + pRK28NC ->BCHCTL = BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ; + wait_op_done(mtd,TROP_US_DELAY,0); + rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ; + memcpy(buf,(u_char *)(pRK28NC->spare),4); // only use nandc sram0 + } + else + { + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + for(i=0;iwritesize/0x400;i++) + { + pRK28NC ->BCHCTL = BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ; + wait_op_done(mtd,TROP_US_DELAY,0); + rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ; + memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400); // only use nandc sram0 + } + } + + return; + +} + +static void rk2818_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + uint32_t i = 0; + + #if 0 + if(len < mtd->writesize) + { + pRK28NC->FLCTL &= ~FL_BYPASS; //bypass mode + // bypass write + if (len >0) + { + for( i = 0; ichip[0].data = buf[i] ; + } + } + } + else + #endif + { + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + for(i=0;iwritesize/0x400;i++) + { + memcpy((u_char *)(pRK28NC->buf),buf+i*0x400,0x400); // only use nandc sram0 + pRK28NC ->BCHCTL =BCH_WR|BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|0x1<<5|FL_RDN|FL_BYPASS|FL_START; + wait_op_done(mtd,TROP_US_DELAY,0); + } + } + +} + + +static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + uint32_t timeout = 1000; + char status,ret; + + // pRK28NC->FLCTL &= ~FL_BYPASS; // bypass mode to send command + + switch (command) { + + case NAND_CMD_READID: + pRK28NC ->chip[0].cmd = command; + pRK28NC ->chip[0].addr = 0x0; + while (timeout>0) + { + timeout --; + udelay(1); + if(pRK28NC->FLCTL&FL_INTCLR) + break; + + } + + break; + + case NAND_CMD_READ0: + pRK28NC ->chip[0].cmd = command; + if ( column>= 0 ) + { + pRK28NC ->chip[0].addr = column & 0xff; + if( mtd->writesize > 512) + pRK28NC ->chip[0].addr = (column >> 8) & 0xff; + } + if ( page_addr>=0 ) + { + pRK28NC ->chip[0].addr = page_addr & 0xff; + pRK28NC ->chip[0].addr = (page_addr >> 8) & 0xFF; + pRK28NC ->chip[0].addr = (page_addr >> 16) & 0xff; + } + if( mtd->writesize > 512) + pRK28NC ->chip[0].cmd = NAND_CMD_READSTART; + + rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT); + + break; + + case NAND_CMD_READ1: + //pRK28NC ->BCHCTL = 0x0; + pRK28NC ->chip[0].cmd = command; + break; + + case NAND_CMD_READOOB: + pRK28NC ->BCHCTL = 0x0; + if( mtd->writesize > 512 ) + command = NAND_CMD_READ0; // È«²¿¶Á£¬°üÀ¨¶Áoob + + pRK28NC ->chip[0].cmd = command; + + if ( mtd->writesize >512 ) + { + if ( column>= 0 ) + { + pRK28NC ->chip[0].addr = (column + mtd->writesize) & 0xff; + pRK28NC ->chip[0].addr = ( (column + mtd->writesize) >> 8) & 0xff; + } + if ( page_addr>=0 ) + { + pRK28NC ->chip[0].addr = page_addr & 0xff; + pRK28NC ->chip[0].addr = (page_addr >> 8) & 0xFF; + pRK28NC ->chip[0].addr = (page_addr >> 16) & 0xff; + } + pRK28NC ->chip[0].cmd = NAND_CMD_READSTART; + } + else + { + pRK28NC ->chip[0].addr = column; + } + + rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT); + + + break; + + case NAND_CMD_PAGEPROG: + pRK28NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤ + pRK28NC ->chip[0].cmd = command; + rk2818_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT); + + pRK28NC ->chip[0].cmd = NAND_CMD_STATUS; + status = pRK28NC ->chip[0].data; + + if(status&0x1) + ret = -1; + else + ret =0; + + break; + + case NAND_CMD_ERASE1: + pRK28NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤ + pRK28NC ->BCHCTL = 0x0; + pRK28NC ->chip[0].cmd = command; + if ( page_addr>=0 ) + { + pRK28NC ->chip[0].addr = page_addr & 0xff; + pRK28NC ->chip[0].addr = (page_addr>>8)&0xff; + pRK28NC ->chip[0].addr = (page_addr>>16)&0xff; + } + break; + + case NAND_CMD_ERASE2: + pRK28NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤ + pRK28NC ->chip[0].cmd = command; + rk2818_nand_wait_busy(mtd,ERASE_BUSY_COUNT); + pRK28NC ->chip[0].cmd = NAND_CMD_STATUS; + status = pRK28NC ->chip[0].data; + + if(status&0x1) + ret = -1; + else + ret =0; + + break; + + case NAND_CMD_SEQIN: + pRK28NC ->FMCTL |= FMC_WP; //½â³ýд±£»¤ + pRK28NC ->chip[0].cmd = command; + udelay(1); + if ( column>= 0 ) + { + pRK28NC ->chip[0].addr = column; + if( mtd->writesize > 512) + pRK28NC ->chip[0].addr = (column >> 8) & 0xff; + } + if( page_addr>=0 ) + { + pRK28NC ->chip[0].addr = page_addr & 0xff; + pRK28NC ->chip[0].addr = (page_addr>>8)&0xff; + pRK28NC ->chip[0].addr = (page_addr>>16)&0xff; + } + + break; + + case NAND_CMD_STATUS: + pRK28NC ->BCHCTL = 0x0; + // pRK28NC ->FLCTL =0x1; + pRK28NC ->chip[0].cmd = command; + break; + + case NAND_CMD_RESET: + pRK28NC ->chip[0].cmd = command; + break; + + /* This applies to read commands */ + default: + pRK28NC ->chip[0].cmd = command; + break; + } + + udelay (1); + +} + +int rk2818_nand_calculate_ecc(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + int eccdata[7],i; + + for(i=0;i<7;i++) + { + eccdata[i] = pRK28NC->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 rk2818_nand_hwctl_ecc(struct mtd_info *mtd, int mode) + { + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + pRK28NC->BCHCTL = 1; // reset bch and enable hw ecc + + return; + } + + int rk2818_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 rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + // hw correct data + if( pRK28NC->BCHST & (1<<2) ) + { + DEBUG(MTD_DEBUG_LEVEL0, + "rk2818 nand :hw ecc uncorrectable error\n"); + return -1; + } + + return 0; + } + + int rk2818_nand_read_page(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf, int page) + { + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + + int i; + + + rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT); + + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + + for(i=0;iwritesize/0x400;i++) + { + pRK28NC ->BCHCTL = BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ; + wait_op_done(mtd,TROP_US_DELAY,0); + rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ; + + memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400); // only use nandc sram0 + } + + return 0; + + } + +void rk2818_nand_write_page(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf) + { + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + uint32_t i = 0; + + + + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + for(i=0;iwritesize/0x400;i++) + { + memcpy((u_char *)(pRK28NC->buf),(buf+i*0x400),0x400); // only use nandc sram0 + if(i==0) + memcpy((u_char *)(pRK28NC->spare),(u_char *)(chip->oob_poi + chip->ops.ooboffs),4); + + pRK28NC ->BCHCTL = BCH_WR|BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_RDN|FL_BYPASS|FL_START; + wait_op_done(mtd,TROP_US_DELAY,0); + } + + pRK28NC ->chip[0].cmd = NAND_CMD_PAGEPROG; + rk2818_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT); + + + return; + + } + +int rk2818_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + int i; + //uint8_t buf[0x400]; + + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + + rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT); + + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + + for(i=0;iwritesize/0x400;i++) + { + pRK28NC ->BCHCTL = BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ; + wait_op_done(mtd,TROP_US_DELAY,0); + rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ; + // memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400); // only use nandc sram0 + if(i==0) + memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK28NC->spare),4); + } + + + return sndcmd; +} + +int rk2818_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 rk2818_nand_mtd *master = nand_chip->priv; + pNANDC pRK28NC= (pNANDC)(master->regs); + + + int i; + + + rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT); + + pRK28NC->FLCTL |= FL_BYPASS; // dma mode + + for(i=0;iwritesize/0x400;i++) + { + pRK28NC ->BCHCTL = BCH_RST; + pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ; + wait_op_done(mtd,TROP_US_DELAY,0); + rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ; + memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400); // only use nandc sram0 + if(i==0) + memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK28NC->spare),4); + } + + return 0; +} + + +static int rk2818_nand_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct rk2818_nand_platform_data *pdata = pdev->dev.platform_data; + struct rk2818_nand_mtd *master; + struct resource *res; + int err = 0, nr_parts = 0; + pNANDC pRK28NC; + +#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 rk2818_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 = rk2818_nand_dev_ready; + this->cmdfunc = rk2818_nand_cmdfunc; + this->select_chip = rk2818_nand_select_chip; + this->read_byte = rk2818_nand_read_byte; + this->read_word = rk2818_nand_read_word; + this->write_buf = rk2818_nand_write_buf; + this->read_buf = rk2818_nand_read_buf; + this->options |= NAND_USE_FLASH_BBT; // open bbt options + + // this->block_bad = rk2818_nand_block_bad; + // this->block_markbad = rk2818_nand_block_markbad; + + 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 = rk2818_nand_calculate_ecc; + this->ecc.hwctl = rk2818_nand_hwctl_ecc; + this->ecc.correct = rk2818_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.read_page = rk2818_nand_read_page; + this->ecc.write_page = rk2818_nand_write_page; + this->ecc.read_oob = rk2818_nand_read_oob; + this->ecc.read_page_raw = rk2818_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; + } + + pRK28NC = (pNANDC)(master->regs); + pRK28NC ->FMCTL = FMC_WP|FMC_FRDY|(0x1<<0); + pRK28NC ->FMWAIT |= (1<BCHCTL = 0x1; + + /* 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; + } + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + DEBUG(MTD_DEBUG_LEVEL0, + "RK2818 NAND: Unable to find any NAND device.\n"); + err = -ENXIO; + goto outscan; + } + +#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); + } +#if 0 + nr_parts = pdata->nr_parts; + master->parts = pdata->parts; + add_mtd_partitions(mtd, master->parts, nr_parts); +#endif + + platform_set_drvdata(pdev, master); + + return 0; + +outres: +outscan: + iounmap(master->regs); + kfree(master); + + return err; + +} + +static int rk2818_nand_remove(struct platform_device *pdev) +{ + struct rk2818_nand_mtd *master = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + nand_release(&master->mtd); + iounmap(master->regs); + kfree(master); + + return 0; +} + +#ifdef CONFIG_PM +static int rk2818_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 rk2818_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 rk2818_nand_suspend NULL +#define rk2818_nand_resume NULL +#endif /* CONFIG_PM */ + + +static struct platform_driver rk2818_nand_driver = { + .driver = { + .name = "rk2818-nand", + }, + .probe = rk2818_nand_probe, + .remove = rk2818_nand_remove, + .suspend = rk2818_nand_suspend, + .resume = rk2818_nand_resume, +}; + +static int __init rk2818_nand_init(void) +{ + /* Register the device driver structure. */ + printk("rk2818_nand_init\n"); + return platform_driver_register(&rk2818_nand_driver);; +} + +static void __exit rk2818_nand_exit(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&rk2818_nand_driver); +} + +module_init(rk2818_nand_init); +module_exit(rk2818_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("hxy "); +MODULE_DESCRIPTION("MTD NAND driver for rk2818 device"); + diff --git a/fs/yaffs2/yaffs_fs.c b/fs/yaffs2/yaffs_fs.c index 4c155aef0a35..b3f4f0312760 100644 --- a/fs/yaffs2/yaffs_fs.c +++ b/fs/yaffs2/yaffs_fs.c @@ -2189,6 +2189,9 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, dev->nReservedBlocks = 5; dev->nShortOpCaches = (options.no_cache) ? 0 : 10; dev->inbandTags = options.inband_tags; +#ifdef CONFIG_ARCH_RK2818 + dev->inbandTags = 1; +#endif /* ... and the functions. */ if (yaffsVersion == 2) { diff --git a/fs/yaffs2/yaffs_mtdif.c b/fs/yaffs2/yaffs_mtdif.c index 72f64203f31f..ad18fd59eb10 100644 --- a/fs/yaffs2/yaffs_mtdif.c +++ b/fs/yaffs2/yaffs_mtdif.c @@ -209,14 +209,14 @@ int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); __u32 addr = - ((loff_t) blockNumber) * dev->nDataBytesPerChunk + ((loff_t) blockNumber) * dev->totalBytesPerChunk * dev->nChunksPerBlock; struct erase_info ei; int retval = 0; ei.mtd = mtd; ei.addr = addr; - ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock; + ei.len = dev->totalBytesPerChunk * dev->nChunksPerBlock; ei.time = 1000; ei.retries = 2; ei.callback = NULL; -- 2.34.1