diff options
author | John Crispin <john@openwrt.org> | 2015-12-10 19:06:32 +0000 |
---|---|---|
committer | John Crispin <john@openwrt.org> | 2015-12-10 19:06:32 +0000 |
commit | 6df4426245be87148d6aa75859951cb6351eb24c (patch) | |
tree | 57a361566f8a505f89b685125d9ab36bba289e19 /target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch | |
parent | f2b0ae86983705c98a50ae845403e67c9c412f4d (diff) | |
download | mtk-20170518-6df4426245be87148d6aa75859951cb6351eb24c.zip mtk-20170518-6df4426245be87148d6aa75859951cb6351eb24c.tar.gz mtk-20170518-6df4426245be87148d6aa75859951cb6351eb24c.tar.bz2 |
ralink: bump to the target to v4.3
Signed-off-by: John Crispin <blogic@openwrt.org>
SVN-Revision: 47831
Diffstat (limited to 'target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch')
-rw-r--r-- | target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch | 2421 |
1 files changed, 2421 insertions, 0 deletions
diff --git a/target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch b/target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch new file mode 100644 index 0000000..a381e21 --- /dev/null +++ b/target/linux/ramips/patches-4.3/0038-mtd-ralink-add-mt7620-nand-driver.patch @@ -0,0 +1,2421 @@ +From fb6e1578cd73d7d81f675e75247a676423f32412 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 17 Nov 2013 17:41:46 +0100 +Subject: [PATCH 38/53] mtd: ralink: add mt7620 nand driver + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/mtd/maps/Kconfig | 4 + + drivers/mtd/maps/Makefile | 2 + + drivers/mtd/maps/ralink_nand.c | 2136 ++++++++++++++++++++++++++++++++++++++++ + drivers/mtd/maps/ralink_nand.h | 232 +++++ + 4 files changed, 2374 insertions(+) + create mode 100644 drivers/mtd/maps/ralink_nand.c + create mode 100644 drivers/mtd/maps/ralink_nand.h + +diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig +index 7c95a65..c2739db 100644 +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -399,4 +399,8 @@ config MTD_LATCH_ADDR + + If compiled as a module, it will be called latch-addr-flash. + ++config MTD_NAND_MT7620 ++ tristate "Support for NAND on Mediatek MT7620" ++ depends on RALINK && SOC_MT7620 ++ + endmenu +diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile +index 141c91a..94d2aa0 100644 +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -43,3 +43,5 @@ obj-$(CONFIG_MTD_VMU) += vmu-flash.o + obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o + obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o + obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o ++obj-$(CONFIG_MTD_NAND_MT7620) += ralink_nand.o ++ +diff --git a/drivers/mtd/maps/ralink_nand.c b/drivers/mtd/maps/ralink_nand.c +new file mode 100644 +index 0000000..64f9119 +--- /dev/null ++++ b/drivers/mtd/maps/ralink_nand.c +@@ -0,0 +1,2136 @@ ++#define DEBUG ++#include <linux/device.h> ++#undef DEBUG ++#include <linux/slab.h> ++#include <linux/mtd/mtd.h> ++#include <linux/delay.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++ ++#include "ralink_nand.h" ++#ifdef RANDOM_GEN_BAD_BLOCK ++#include <linux/random.h> ++#endif ++ ++#define LARGE_MTD_BOOT_PART_SIZE (CFG_BLOCKSIZE<<2) ++#define LARGE_MTD_CONFIG_PART_SIZE (CFG_BLOCKSIZE<<2) ++#define LARGE_MTD_FACTORY_PART_SIZE (CFG_BLOCKSIZE<<1) ++ ++ ++#define BLOCK_ALIGNED(a) ((a) & (CFG_BLOCKSIZE - 1)) ++ ++#define READ_STATUS_RETRY 1000 ++ ++struct mtd_info *ranfc_mtd = NULL; ++ ++int skipbbt = 0; ++int ranfc_debug = 1; ++static int ranfc_bbt = 1; ++#if defined (WORKAROUND_RX_BUF_OV) ++static int ranfc_verify = 1; ++#endif ++static u32 nand_addrlen; ++ ++#if 0 ++module_param(ranfc_debug, int, 0644); ++module_param(ranfc_bbt, int, 0644); ++module_param(ranfc_verify, int, 0644); ++#endif ++ ++#if 0 ++#define ra_dbg(args...) do { if (ranfc_debug) printk(args); } while(0) ++#else ++#define ra_dbg(args...) ++#endif ++ ++#define CLEAR_INT_STATUS() ra_outl(NFC_INT_ST, ra_inl(NFC_INT_ST)) ++#define NFC_TRANS_DONE() (ra_inl(NFC_INT_ST) & INT_ST_ND_DONE) ++ ++int is_nand_page_2048 = 0; ++const unsigned int nand_size_map[2][3] = {{25, 30, 30}, {20, 27, 30}}; ++ ++static int nfc_wait_ready(int snooze_ms); ++ ++static const char * const mtk_probe_types[] = { "cmdlinepart", "ofpart", NULL }; ++ ++/** ++ * reset nand chip ++ */ ++static int nfc_chip_reset(void) ++{ ++ int status; ++ ++ //ra_dbg("%s:\n", __func__); ++ ++ // reset nand flash ++ ra_outl(NFC_CMD1, 0x0); ++ ra_outl(NFC_CMD2, 0xff); ++ ra_outl(NFC_ADDR, 0x0); ++ ra_outl(NFC_CONF, 0x0411); ++ ++ status = nfc_wait_ready(5); //erase wait 5us ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ } ++ ++ return (int)(status & NAND_STATUS_FAIL); ++ ++} ++ ++ ++ ++/** ++ * clear NFC and flash chip. ++ */ ++static int nfc_all_reset(void) ++{ ++ int retry; ++ ++ ra_dbg("%s: \n", __func__); ++ ++ // reset controller ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) | 0x02); //clear data buffer ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) & ~0x02); //clear data buffer ++ ++ CLEAR_INT_STATUS(); ++ ++ retry = READ_STATUS_RETRY; ++ while ((ra_inl(NFC_INT_ST) & 0x02) != 0x02 && retry--); ++ if (retry <= 0) { ++ printk("nfc_all_reset: clean buffer fail \n"); ++ return -1; ++ } ++ ++ retry = READ_STATUS_RETRY; ++ while ((ra_inl(NFC_STATUS) & 0x1) != 0x0 && retry--) { //fixme, controller is busy ? ++ udelay(1); ++ } ++ ++ nfc_chip_reset(); ++ ++ return 0; ++} ++ ++/** NOTICE: only called by nfc_wait_ready(). ++ * @return -1, nfc can not get transction done ++ * @return 0, ok. ++ */ ++static int _nfc_read_status(char *status) ++{ ++ unsigned long cmd1, conf; ++ int int_st, nfc_st; ++ int retry; ++ ++ cmd1 = 0x70; ++ conf = 0x000101 | (1 << 20); ++ ++ //fixme, should we check nfc status? ++ CLEAR_INT_STATUS(); ++ ++ ra_outl(NFC_CMD1, cmd1); ++ ra_outl(NFC_CONF, conf); ++ ++ /* FIXME, ++ * 1. since we have no wired ready signal, directly ++ * calling this function is not gurantee to read right status under ready state. ++ * 2. the other side, we can not determine how long to become ready, this timeout retry is nonsense. ++ * 3. SUGGESTION: call nfc_read_status() from nfc_wait_ready(), ++ * that is aware about caller (in sementics) and has snooze plused nfc ND_DONE. ++ */ ++ retry = READ_STATUS_RETRY; ++ do { ++ nfc_st = ra_inl(NFC_STATUS); ++ int_st = ra_inl(NFC_INT_ST); ++ ++ ndelay(10); ++ } while (!(int_st & INT_ST_RX_BUF_RDY) && retry--); ++ ++ if (!(int_st & INT_ST_RX_BUF_RDY)) { ++ printk("nfc_read_status: NFC fail, int_st(%x), retry:%x. nfc:%x, reset nfc and flash. \n", ++ int_st, retry, nfc_st); ++ nfc_all_reset(); ++ *status = NAND_STATUS_FAIL; ++ return -1; ++ } ++ ++ *status = (char)(le32_to_cpu(ra_inl(NFC_DATA)) & 0x0ff); ++ return 0; ++} ++ ++/** ++ * @return !0, chip protect. ++ * @return 0, chip not protected. ++ */ ++static int nfc_check_wp(void) ++{ ++ /* Check the WP bit */ ++#if !defined CONFIG_NOT_SUPPORT_WP ++ return !!(ra_inl(NFC_CTRL) & 0x01); ++#else ++ char result = 0; ++ int ret; ++ ++ ret = _nfc_read_status(&result); ++ //FIXME, if ret < 0 ++ ++ return !(result & NAND_STATUS_WP); ++#endif ++} ++ ++#if !defined CONFIG_NOT_SUPPORT_RB ++/* ++ * @return !0, chip ready. ++ * @return 0, chip busy. ++ */ ++static int nfc_device_ready(void) ++{ ++ /* Check the ready */ ++ return !!(ra_inl(NFC_STATUS) & 0x04); ++} ++#endif ++ ++ ++/** ++ * generic function to get data from flash. ++ * @return data length reading from flash. ++ */ ++static int _ra_nand_pull_data(char *buf, int len, int use_gdma) ++{ ++#ifdef RW_DATA_BY_BYTE ++ char *p = buf; ++#else ++ __u32 *p = (__u32 *)buf; ++#endif ++ int retry, int_st; ++ unsigned int ret_data; ++ int ret_size; ++ ++ // receive data by use_gdma ++ if (use_gdma) { ++ //if (_ra_nand_dma_pull((unsigned long)p, len)) { ++ if (1) { ++ printk("%s: fail \n", __func__); ++ len = -1; //return error ++ } ++ ++ return len; ++ } ++ ++ //fixme: retry count size? ++ retry = READ_STATUS_RETRY; ++ // no gdma ++ while (len > 0) { ++ int_st = ra_inl(NFC_INT_ST); ++ if (int_st & INT_ST_RX_BUF_RDY) { ++ ++ ret_data = ra_inl(NFC_DATA); ++ ra_outl(NFC_INT_ST, INT_ST_RX_BUF_RDY); ++#ifdef RW_DATA_BY_BYTE ++ ret_size = sizeof(unsigned int); ++ ret_size = min(ret_size, len); ++ len -= ret_size; ++ while (ret_size-- > 0) { ++ //nfc is little endian ++ *p++ = ret_data & 0x0ff; ++ ret_data >>= 8; ++ } ++#else ++ ret_size = min(len, 4); ++ len -= ret_size; ++ if (ret_size == 4) ++ *p++ = ret_data; ++ else { ++ __u8 *q = (__u8 *)p; ++ while (ret_size-- > 0) { ++ *q++ = ret_data & 0x0ff; ++ ret_data >>= 8; ++ } ++ p = (__u32 *)q; ++ } ++#endif ++ retry = READ_STATUS_RETRY; ++ } ++ else if (int_st & INT_ST_ND_DONE) { ++ break; ++ } ++ else { ++ udelay(1); ++ if (retry-- < 0) ++ break; ++ } ++ } ++ ++#ifdef RW_DATA_BY_BYTE ++ return (int)(p - buf); ++#else ++ return ((int)p - (int)buf); ++#endif ++} ++ ++/** ++ * generic function to put data into flash. ++ * @return data length writing into flash. ++ */ ++static int _ra_nand_push_data(char *buf, int len, int use_gdma) ++{ ++#ifdef RW_DATA_BY_BYTE ++ char *p = buf; ++#else ++ __u32 *p = (__u32 *)buf; ++#endif ++ int retry, int_st; ++ unsigned int tx_data = 0; ++ int tx_size, iter = 0; ++ ++ // receive data by use_gdma ++ if (use_gdma) { ++ //if (_ra_nand_dma_push((unsigned long)p, len)) ++ if (1) ++ len = 0; ++ printk("%s: fail \n", __func__); ++ return len; ++ } ++ ++ // no gdma ++ retry = READ_STATUS_RETRY; ++ while (len > 0) { ++ int_st = ra_inl(NFC_INT_ST); ++ if (int_st & INT_ST_TX_BUF_RDY) { ++#ifdef RW_DATA_BY_BYTE ++ tx_size = min(len, (int)sizeof(unsigned long)); ++ for (iter = 0; iter < tx_size; iter++) { ++ tx_data |= (*p++ << (8*iter)); ++ } ++#else ++ tx_size = min(len, 4); ++ if (tx_size == 4) ++ tx_data = (*p++); ++ else { ++ __u8 *q = (__u8 *)p; ++ for (iter = 0; iter < tx_size; iter++) ++ tx_data |= (*q++ << (8*iter)); ++ p = (__u32 *)q; ++ } ++#endif ++ ra_outl(NFC_INT_ST, INT_ST_TX_BUF_RDY); ++ ra_outl(NFC_DATA, tx_data); ++ len -= tx_size; ++ retry = READ_STATUS_RETRY; ++ } ++ else if (int_st & INT_ST_ND_DONE) { ++ break; ++ } ++ else { ++ udelay(1); ++ if (retry-- < 0) { ++ ra_dbg("%s p:%p buf:%p \n", __func__, p, buf); ++ break; ++ } ++ } ++ } ++ ++ ++#ifdef RW_DATA_BY_BYTE ++ return (int)(p - buf); ++#else ++ return ((int)p - (int)buf); ++#endif ++ ++} ++ ++static int nfc_select_chip(struct ra_nand_chip *ra, int chipnr) ++{ ++#if (CONFIG_NUMCHIPS == 1) ++ if (!(chipnr < CONFIG_NUMCHIPS)) ++ return -1; ++ return 0; ++#else ++ BUG(); ++#endif ++} ++ ++/** @return -1: chip_select fail ++ * 0 : both CE and WP==0 are OK ++ * 1 : CE OK and WP==1 ++ */ ++static int nfc_enable_chip(struct ra_nand_chip *ra, unsigned int offs, int read_only) ++{ ++ int chipnr = offs >> ra->chip_shift; ++ ++ ra_dbg("%s: offs:%x read_only:%x \n", __func__, offs, read_only); ++ ++ chipnr = nfc_select_chip(ra, chipnr); ++ if (chipnr < 0) { ++ printk("%s: chip select error, offs(%x)\n", __func__, offs); ++ return -1; ++ } ++ ++ if (!read_only) ++ return nfc_check_wp(); ++ ++ return 0; ++} ++ ++/** wait nand chip becomeing ready and return queried status. ++ * @param snooze: sleep time in ms unit before polling device ready. ++ * @return status of nand chip ++ * @return NAN_STATUS_FAIL if something unexpected. ++ */ ++static int nfc_wait_ready(int snooze_ms) ++{ ++ int retry; ++ char status; ++ ++ // wait nfc idle, ++ if (snooze_ms == 0) ++ snooze_ms = 1; ++ else ++ schedule_timeout(snooze_ms * HZ / 1000); ++ ++ snooze_ms = retry = snooze_ms *1000000 / 100 ; // ndelay(100) ++ ++ while (!NFC_TRANS_DONE() && retry--) { ++ if (!cond_resched()) ++ ndelay(100); ++ } ++ ++ if (!NFC_TRANS_DONE()) { ++ printk("nfc_wait_ready: no transaction done \n"); ++ return NAND_STATUS_FAIL; ++ } ++ ++#if !defined (CONFIG_NOT_SUPPORT_RB) ++ //fixme ++ while(!(status = nfc_device_ready()) && retry--) { ++ ndelay(100); ++ } ++ ++ if (status == 0) { ++ printk("nfc_wait_ready: no device ready. \n"); ++ return NAND_STATUS_FAIL; ++ } ++ ++ _nfc_read_status(&status); ++ return status; ++#else ++ ++ while(retry--) { ++ _nfc_read_status(&status); ++ if (status & NAND_STATUS_READY) ++ break; ++ ndelay(100); ++ } ++ if (retry<0) ++ printk("nfc_wait_ready 2: no device ready, status(%x). \n", status); ++ ++ return status; ++#endif ++} ++ ++/** ++ * return 0: erase OK ++ * return -EIO: fail ++ */ ++int nfc_erase_block(struct ra_nand_chip *ra, int row_addr) ++{ ++ unsigned long cmd1, cmd2, bus_addr, conf; ++ char status; ++ ++ cmd1 = 0x60; ++ cmd2 = 0xd0; ++ bus_addr = row_addr; ++ conf = 0x00511 | ((CFG_ROW_ADDR_CYCLE)<<16); ++ ++ // set NFC ++ ra_dbg("%s: cmd1: %lx, cmd2:%lx bus_addr: %lx, conf: %lx \n", ++ __func__, cmd1, cmd2, bus_addr, conf); ++ ++ //fixme, should we check nfc status? ++ CLEAR_INT_STATUS(); ++ ++ ra_outl(NFC_CMD1, cmd1); ++ ra_outl(NFC_CMD2, cmd2); ++ ra_outl(NFC_ADDR, bus_addr); ++ ra_outl(NFC_CONF, conf); ++ ++ status = nfc_wait_ready(3); //erase wait 3ms ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return -EIO; ++ } ++ ++ return 0; ++ ++} ++ ++static inline int _nfc_read_raw_data(int cmd1, int cmd2, int bus_addr, int bus_addr2, int conf, char *buf, int len, int flags) ++{ ++ int ret; ++ ++ CLEAR_INT_STATUS(); ++ ra_outl(NFC_CMD1, cmd1); ++ ra_outl(NFC_CMD2, cmd2); ++ ra_outl(NFC_ADDR, bus_addr); ++#if defined (CONFIG_RALINK_RT6855) || defined (CONFIG_RALINK_RT6855A) || \ ++ defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621) ++ ra_outl(NFC_ADDR2, bus_addr2); ++#endif ++ ra_outl(NFC_CONF, conf); ++ ++ ret = _ra_nand_pull_data(buf, len, 0); ++ if (ret != len) { ++ ra_dbg("%s: ret:%x (%x) \n", __func__, ret, len); ++ return NAND_STATUS_FAIL; ++ } ++ ++ //FIXME, this section is not necessary ++ ret = nfc_wait_ready(0); //wait ready ++ /* to prevent the DATA FIFO 's old data from next operation */ ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) | 0x02); //clear data buffer ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) & ~0x02); //clear data buffer ++ ++ if (ret & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return NAND_STATUS_FAIL; ++ } ++ ++ return 0; ++} ++ ++static inline int _nfc_write_raw_data(int cmd1, int cmd3, int bus_addr, int bus_addr2, int conf, char *buf, int len, int flags) ++{ ++ int ret; ++ ++ CLEAR_INT_STATUS(); ++ ra_outl(NFC_CMD1, cmd1); ++ ra_outl(NFC_CMD3, cmd3); ++ ra_outl(NFC_ADDR, bus_addr); ++#if defined (CONFIG_RALINK_RT6855) || defined (CONFIG_RALINK_RT6855A) || \ ++ defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621) ++ ra_outl(NFC_ADDR2, bus_addr2); ++#endif ++ ra_outl(NFC_CONF, conf); ++ ++ ret = _ra_nand_push_data(buf, len, 0); ++ if (ret != len) { ++ ra_dbg("%s: ret:%x (%x) \n", __func__, ret, len); ++ return NAND_STATUS_FAIL; ++ } ++ ++ ret = nfc_wait_ready(1); //write wait 1ms ++ /* to prevent the DATA FIFO 's old data from next operation */ ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) | 0x02); //clear data buffer ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) & ~0x02); //clear data buffer ++ ++ if (ret & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return NAND_STATUS_FAIL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @return !0: fail ++ * @return 0: OK ++ */ ++int nfc_read_oob(struct ra_nand_chip *ra, int page, unsigned int offs, char *buf, int len, int flags) ++{ ++ unsigned int cmd1 = 0, cmd2 = 0, conf = 0; ++ unsigned int bus_addr = 0, bus_addr2 = 0; ++ unsigned int ecc_en; ++ int use_gdma; ++ int status; ++ ++ int pages_perblock = 1<<(ra->erase_shift - ra->page_shift); ++ // constrain of nfc read function ++ ++#if defined (WORKAROUND_RX_BUF_OV) ++ BUG_ON (len > 60); //problem of rx-buffer overrun ++#endif ++ BUG_ON (offs >> ra->oob_shift); //page boundry ++ BUG_ON ((unsigned int)(((offs + len) >> ra->oob_shift) + page) > ++ ((page + pages_perblock) & ~(pages_perblock-1))); //block boundry ++ ++ use_gdma = flags & FLAG_USE_GDMA; ++ ecc_en = flags & FLAG_ECC_EN; ++ bus_addr = (page << (CFG_COLUMN_ADDR_CYCLE*8)) | (offs & ((1<<CFG_COLUMN_ADDR_CYCLE*8) - 1)); ++ ++ if (is_nand_page_2048) { ++ bus_addr += CFG_PAGESIZE; ++ bus_addr2 = page >> (CFG_COLUMN_ADDR_CYCLE*8); ++ cmd1 = 0x0; ++ cmd2 = 0x30; ++ conf = 0x000511| ((CFG_ADDR_CYCLE)<<16) | (len << 20); ++ } ++ else { ++ cmd1 = 0x50; ++ conf = 0x000141| ((CFG_ADDR_CYCLE)<<16) | (len << 20); ++ } ++ if (ecc_en) ++ conf |= (1<<3); ++ if (use_gdma) ++ conf |= (1<<2); ++ ++ ra_dbg("%s: cmd1:%x, bus_addr:%x, conf:%x, len:%x, flag:%x\n", ++ __func__, cmd1, bus_addr, conf, len, flags); ++ ++ status = _nfc_read_raw_data(cmd1, cmd2, bus_addr, bus_addr2, conf, buf, len, flags); ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail\n", __func__); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @return !0: fail ++ * @return 0: OK ++ */ ++int nfc_write_oob(struct ra_nand_chip *ra, int page, unsigned int offs, char *buf, int len, int flags) ++{ ++ unsigned int cmd1 = 0, cmd3=0, conf = 0; ++ unsigned int bus_addr = 0, bus_addr2 = 0; ++ int use_gdma; ++ int status; ++ ++ int pages_perblock = 1<<(ra->erase_shift - ra->page_shift); ++ // constrain of nfc read function ++ ++ BUG_ON (offs >> ra->oob_shift); //page boundry ++ BUG_ON ((unsigned int)(((offs + len) >> ra->oob_shift) + page) > ++ ((page + pages_perblock) & ~(pages_perblock-1))); //block boundry ++ ++ use_gdma = flags & FLAG_USE_GDMA; ++ bus_addr = (page << (CFG_COLUMN_ADDR_CYCLE*8)) | (offs & ((1<<CFG_COLUMN_ADDR_CYCLE*8) - 1)); ++ ++ if (is_nand_page_2048) { ++ cmd1 = 0x80; ++ cmd3 = 0x10; ++ bus_addr += CFG_PAGESIZE; ++ bus_addr2 = page >> (CFG_COLUMN_ADDR_CYCLE*8); ++ conf = 0x001123 | ((CFG_ADDR_CYCLE)<<16) | ((len) << 20); ++ } ++ else { ++ cmd1 = 0x08050; ++ cmd3 = 0x10; ++ conf = 0x001223 | ((CFG_ADDR_CYCLE)<<16) | ((len) << 20); ++ } ++ if (use_gdma) ++ conf |= (1<<2); ++ ++ // set NFC ++ ra_dbg("%s: cmd1: %x, cmd3: %x bus_addr: %x, conf: %x, len:%x\n", ++ __func__, cmd1, cmd3, bus_addr, conf, len); ++ ++ status = _nfc_write_raw_data(cmd1, cmd3, bus_addr, bus_addr2, conf, buf, len, flags); ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++int nfc_read_page(struct ra_nand_chip *ra, char *buf, int page, int flags); ++int nfc_write_page(struct ra_nand_chip *ra, char *buf, int page, int flags); ++ ++ ++#if !defined (WORKAROUND_RX_BUF_OV) ++static int one_bit_correction(char *ecc, char *expected, int *bytes, int *bits); ++int nfc_ecc_verify(struct ra_nand_chip *ra, char *buf, int page, int mode) ++{ ++ int ret, i; ++ char *p, *e; ++ int ecc; ++ ++ //ra_dbg("%s, page:%x mode:%d\n", __func__, page, mode); ++ ++ if (mode == FL_WRITING) { ++ int len = CFG_PAGESIZE + CFG_PAGE_OOBSIZE; ++ int conf = 0x000141| ((CFG_ADDR_CYCLE)<<16) | (len << 20); ++ conf |= (1<<3); //(ecc_en) ++ //conf |= (1<<2); // (use_gdma) ++ ++ p = ra->readback_buffers; ++ ret = nfc_read_page(ra, ra->readback_buffers, page, FLAG_ECC_EN); ++ if (ret == 0) ++ goto ecc_check; ++ ++ //FIXME, double comfirm ++ printk("%s: read back fail, try again \n",__func__); ++ ret = nfc_read_page(ra, ra->readback_buffers, page, FLAG_ECC_EN); ++ if (ret != 0) { ++ printk("\t%s: read back fail agian \n",__func__); ++ goto bad_block; ++ } ++ } ++ else if (mode == FL_READING) { ++ p = buf; ++ } ++ else ++ return -2; ++ ++ecc_check: ++ p += CFG_PAGESIZE; ++ if (!is_nand_page_2048) { ++ ecc = ra_inl(NFC_ECC); ++ if (ecc == 0) //clean page. ++ return 0; ++ e = (char*)&ecc; ++ for (i=0; i<CONFIG_ECC_BYTES; i++) { ++ int eccpos = CONFIG_ECC_OFFSET + i; ++ if (*(p + eccpos) != (char)0xff) ++ break; ++ if (i == CONFIG_ECC_BYTES - 1) { ++ printk("skip ecc 0xff at page %x\n", page); ++ return 0; ++ } ++ } ++ for (i=0; i<CONFIG_ECC_BYTES; i++) { ++ int eccpos = CONFIG_ECC_OFFSET + i; ++ if (*(p + eccpos) != *(e + i)) { ++ printk("%s mode:%s, invalid ecc, page: %x read:%x %x %x, ecc:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+ CONFIG_ECC_OFFSET), *(p+ CONFIG_ECC_OFFSET+1), *(p+ CONFIG_ECC_OFFSET +2), ecc); ++ return -1; ++ } ++ } ++ } ++#if defined (CONFIG_RALINK_RT6855) || defined (CONFIG_RALINK_RT6855A) || \ ++ defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621) ++ else { ++ int ecc2, ecc3, ecc4, qsz; ++ char *e2, *e3, *e4; ++ int correction_flag = 0; ++ ecc = ra_inl(NFC_ECC_P1); ++ ecc2 = ra_inl(NFC_ECC_P2); ++ ecc3 = ra_inl(NFC_ECC_P3); ++ ecc4 = ra_inl(NFC_ECC_P4); ++ e = (char*)&ecc; ++ e2 = (char*)&ecc2; ++ e3 = (char*)&ecc3; ++ e4 = (char*)&ecc4; ++ qsz = CFG_PAGE_OOBSIZE / 4; ++ if (ecc == 0 && ecc2 == 0 && ecc3 == 0 && ecc4 == 0) ++ return 0; ++ for (i=0; i<CONFIG_ECC_BYTES; i++) { ++ int eccpos = CONFIG_ECC_OFFSET + i; ++ if (*(p + eccpos) != (char)0xff) ++ break; ++ else if (*(p + eccpos + qsz) != (char)0xff) ++ break; ++ else if (*(p + eccpos + qsz*2) != (char)0xff) ++ break; ++ else if (*(p + eccpos + qsz*3) != (char)0xff) ++ break; ++ if (i == CONFIG_ECC_BYTES - 1) { ++ printk("skip ecc 0xff at page %x\n", page); ++ return 0; ++ } ++ } ++ for (i=0; i<CONFIG_ECC_BYTES; i++) { ++ int eccpos = CONFIG_ECC_OFFSET + i; ++ if (*(p + eccpos) != *(e + i)) { ++ printk("%s mode:%s, invalid ecc, page: %x read:%x %x %x, ecc:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+ CONFIG_ECC_OFFSET), *(p+ CONFIG_ECC_OFFSET+1), *(p+ CONFIG_ECC_OFFSET +2), ecc); ++ correction_flag |= 0x1; ++ } ++ if (*(p + eccpos + qsz) != *(e2 + i)) { ++ printk("%s mode:%s, invalid ecc2, page: %x read:%x %x %x, ecc2:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+CONFIG_ECC_OFFSET+qsz), *(p+ CONFIG_ECC_OFFSET+1+qsz), *(p+ CONFIG_ECC_OFFSET+2+qsz), ecc2); ++ correction_flag |= 0x2; ++ } ++ if (*(p + eccpos + qsz*2) != *(e3 + i)) { ++ printk("%s mode:%s, invalid ecc3, page: %x read:%x %x %x, ecc3:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+CONFIG_ECC_OFFSET+qsz*2), *(p+ CONFIG_ECC_OFFSET+1+qsz*2), *(p+ CONFIG_ECC_OFFSET+2+qsz*2), ecc3); ++ correction_flag |= 0x4; ++ } ++ if (*(p + eccpos + qsz*3) != *(e4 + i)) { ++ printk("%s mode:%s, invalid ecc4, page: %x read:%x %x %x, ecc4:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+CONFIG_ECC_OFFSET+qsz*3), *(p+ CONFIG_ECC_OFFSET+1+qsz*3), *(p+ CONFIG_ECC_OFFSET+2+qsz*3), ecc4); ++ correction_flag |= 0x8; ++ } ++ } ++ ++ if (correction_flag) ++ { ++ printk("trying to do correction!\n"); ++ if (correction_flag & 0x1) ++ { ++ int bytes, bits; ++ char *pBuf = p - CFG_PAGESIZE; ++ ++ if (one_bit_correction(p + CONFIG_ECC_OFFSET, e, &bytes, &bits) == 0) ++ { ++ pBuf[bytes] = pBuf[bytes] ^ (1 << bits); ++ printk("1. correct byte %d, bit %d!\n", bytes, bits); ++ } ++ else ++ { ++ printk("failed to correct!\n"); ++ return -1; ++ } ++ } ++ ++ if (correction_flag & 0x2) ++ { ++ int bytes, bits; ++ char *pBuf = (p - CFG_PAGESIZE) + CFG_PAGESIZE/4; ++ ++ if (one_bit_correction((p + CONFIG_ECC_OFFSET + qsz), e2, &bytes, &bits) == 0) ++ { ++ pBuf[bytes] = pBuf[bytes] ^ (1 << bits); ++ printk("2. correct byte %d, bit %d!\n", bytes, bits); ++ } ++ else ++ { ++ printk("failed to correct!\n"); ++ return -1; ++ } ++ } ++ if (correction_flag & 0x4) ++ { ++ int bytes, bits; ++ char *pBuf = (p - CFG_PAGESIZE) + CFG_PAGESIZE/2; ++ ++ if (one_bit_correction((p + CONFIG_ECC_OFFSET + qsz * 2), e3, &bytes, &bits) == 0) ++ { ++ pBuf[bytes] = pBuf[bytes] ^ (1 << bits); ++ printk("3. correct byte %d, bit %d!\n", bytes, bits); ++ } ++ else ++ { ++ printk("failed to correct!\n"); ++ return -1; ++ } ++ } ++ if (correction_flag & 0x8) ++ { ++ int bytes, bits; ++ char *pBuf = (p - CFG_PAGESIZE) + CFG_PAGESIZE*3/4; ++ ++ if (one_bit_correction((p + CONFIG_ECC_OFFSET + qsz * 3), e4, &bytes, &bits) == 0) ++ { ++ pBuf[bytes] = pBuf[bytes] ^ (1 << bits); ++ printk("4. correct byte %d, bit %d!\n", bytes, bits); ++ } ++ else ++ { ++ printk("failed to correct!\n"); ++ return -1; ++ } ++ } ++ } ++ ++ } ++#endif ++ return 0; ++ ++bad_block: ++ return -1; ++} ++ ++#else ++ ++void ranfc_dump(void) ++{ ++ int i; ++ for (i=0; i<11; i++) { ++ if (i==6) ++ continue; ++ printk("%x: %x \n", NFC_BASE + i*4, ra_inl(NFC_BASE + i*4)); ++ } ++} ++ ++/** ++ * @return 0, ecc OK or corrected. ++ * @return NAND_STATUS_FAIL, ecc fail. ++ */ ++ ++int nfc_ecc_verify(struct ra_nand_chip *ra, char *buf, int page, int mode) ++{ ++ int ret, i; ++ char *p, *e; ++ int ecc; ++ ++ if (ranfc_verify == 0) ++ return 0; ++ ++ ra_dbg("%s, page:%x mode:%d\n", __func__, page, mode); ++ ++ if (mode == FL_WRITING) { // read back and memcmp ++ ret = nfc_read_page(ra, ra->readback_buffers, page, FLAG_NONE); ++ if (ret != 0) //double comfirm ++ ret = nfc_read_page(ra, ra->readback_buffers, page, FLAG_NONE); ++ ++ if (ret != 0) { ++ printk("%s: mode:%x read back fail \n", __func__, mode); ++ return -1; ++ } ++ return memcmp(buf, ra->readback_buffers, 1<<ra->page_shift); ++ } ++ ++ if (mode == FL_READING) { ++#if 0 ++ if (ra->sandbox_page == 0) ++ return 0; ++ ++ ret = nfc_write_page(ra, buf, ra->sandbox_page, FLAG_USE_GDMA | FLAG_ECC_EN); ++ if (ret != 0) { ++ printk("%s, fail write sandbox_page \n", __func__); ++ return -1; ++ } ++#else ++ /** @note: ++ * The following command is actually not 'write' command to drive NFC to write flash. ++ * However, it can make NFC to calculate ECC, that will be used to compare with original ones. ++ * --YT ++ */ ++ unsigned int conf = 0x001223| (CFG_ADDR_CYCLE<<16) | (0x200 << 20) | (1<<3) | (1<<2); ++ _nfc_write_raw_data(0xff, 0xff, ra->sandbox_page<<ra->page_shift, conf, buf, 0x200, FLAG_USE_GDMA); ++#endif ++ ++ ecc = ra_inl(NFC_ECC); ++ if (ecc == 0) //clean page. ++ return 0; ++ e = (char*)&ecc; ++ p = buf + (1<<ra->page_shift); ++ for (i=0; i<CONFIG_ECC_BYTES; i++) { ++ int eccpos = CONFIG_ECC_OFFSET + i; ++ if (*(p + eccpos) != *(e + i)) { ++ printk("%s mode:%s, invalid ecc, page: %x read:%x %x %x, write:%x \n", ++ __func__, (mode == FL_READING)?"read":"write", page, ++ *(p+ CONFIG_ECC_OFFSET), *(p+ CONFIG_ECC_OFFSET+1), *(p+ CONFIG_ECC_OFFSET +2), ecc); ++ ++ for (i=0; i<528; i++) ++ printk("%-2x \n", *(buf + i)); ++ return -1; ++ } ++ } ++ return 0; ++ } ++ ++ return -1; ++ ++} ++ ++#endif ++ ++ ++/** ++ * @return -EIO, writing size is less than a page ++ * @return 0, OK ++ */ ++int nfc_read_page(struct ra_nand_chip *ra, char *buf, int page, int flags) ++{ ++ unsigned int cmd1 = 0, cmd2 = 0, conf = 0; ++ unsigned int bus_addr = 0, bus_addr2 = 0; ++ unsigned int ecc_en; ++ int use_gdma; ++ int size, offs; ++ int status = 0; ++ ++ use_gdma = flags & FLAG_USE_GDMA; ++ ecc_en = flags & FLAG_ECC_EN; ++ ++ page = page & (CFG_CHIPSIZE - 1); // chip boundary ++ size = CFG_PAGESIZE + CFG_PAGE_OOBSIZE; //add oobsize ++ offs = 0; ++ ++ while (size > 0) { ++ int len; ++#if defined (WORKAROUND_RX_BUF_OV) ++ len = min(60, size); ++#else ++ len = size; ++#endif ++ bus_addr = (page << (CFG_COLUMN_ADDR_CYCLE*8)) | (offs & ((1<<CFG_COLUMN_ADDR_CYCLE*8)-1)); ++ if (is_nand_page_2048) { ++ bus_addr2 = page >> (CFG_COLUMN_ADDR_CYCLE*8); ++ cmd1 = 0x0; ++ cmd2 = 0x30; ++ conf = 0x000511| ((CFG_ADDR_CYCLE)<<16) | (len << 20); ++ } ++ else { ++ if (offs & ~(CFG_PAGESIZE-1)) ++ cmd1 = 0x50; ++ else if (offs & ~((1<<CFG_COLUMN_ADDR_CYCLE*8)-1)) ++ cmd1 = 0x01; ++ else ++ cmd1 = 0; ++ ++ conf = 0x000141| ((CFG_ADDR_CYCLE)<<16) | (len << 20); ++ } ++#if !defined (WORKAROUND_RX_BUF_OV) ++ if (ecc_en) ++ conf |= (1<<3); ++#endif ++ if (use_gdma) ++ conf |= (1<<2); ++ ++ status = _nfc_read_raw_data(cmd1, cmd2, bus_addr, bus_addr2, conf, buf+offs, len, flags); ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return -EIO; ++ } ++ ++ offs += len; ++ size -= len; ++ } ++ ++ // verify and correct ecc ++ if ((flags & (FLAG_VERIFY | FLAG_ECC_EN)) == (FLAG_VERIFY | FLAG_ECC_EN)) { ++ status = nfc_ecc_verify(ra, buf, page, FL_READING); ++ if (status != 0) { ++ printk("%s: fail, buf:%x, page:%x, flag:%x\n", ++ __func__, (unsigned int)buf, page, flags); ++ return -EBADMSG; ++ } ++ } ++ else { ++ // fix,e not yet support ++ ra->buffers_page = -1; //cached ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * @return -EIO, fail to write ++ * @return 0, OK ++ */ ++int nfc_write_page(struct ra_nand_chip *ra, char *buf, int page, int flags) ++{ ++ unsigned int cmd1 = 0, cmd3, conf = 0; ++ unsigned int bus_addr = 0, bus_addr2 = 0; ++ unsigned int ecc_en; ++ int use_gdma; ++ int size; ++ char status; ++ uint8_t *oob = buf + (1<<ra->page_shift); ++ ++ use_gdma = flags & FLAG_USE_GDMA; ++ ecc_en = flags & FLAG_ECC_EN; ++ ++ oob[ra->badblockpos] = 0xff; //tag as good block. ++ ra->buffers_page = -1; //cached ++ ++ page = page & (CFG_CHIPSIZE-1); //chip boundary ++ size = CFG_PAGESIZE + CFG_PAGE_OOBSIZE; //add oobsize ++ bus_addr = (page << (CFG_COLUMN_ADDR_CYCLE*8)); //write_page always write from offset 0. ++ ++ if (is_nand_page_2048) { ++ bus_addr2 = page >> (CFG_COLUMN_ADDR_CYCLE*8); ++ cmd1 = 0x80; ++ cmd3 = 0x10; ++ conf = 0x001123| ((CFG_ADDR_CYCLE)<<16) | (size << 20); ++ } ++ else { ++ cmd1 = 0x8000; ++ cmd3 = 0x10; ++ conf = 0x001223| ((CFG_ADDR_CYCLE)<<16) | (size << 20); ++} ++ if (ecc_en) ++ conf |= (1<<3); //enable ecc ++ if (use_gdma) ++ conf |= (1<<2); ++ ++ // set NFC ++ ra_dbg("nfc_write_page: cmd1: %x, cmd3: %x bus_addr: %x, conf: %x, len:%x\n", ++ cmd1, cmd3, bus_addr, conf, size); ++ ++ status = _nfc_write_raw_data(cmd1, cmd3, bus_addr, bus_addr2, conf, buf, size, flags); ++ if (status & NAND_STATUS_FAIL) { ++ printk("%s: fail \n", __func__); ++ return -EIO; ++ } ++ ++ ++ if (flags & FLAG_VERIFY) { // verify and correct ecc ++ status = nfc_ecc_verify(ra, buf, page, FL_WRITING); ++ ++#ifdef RANDOM_GEN_BAD_BLOCK ++ if (((random32() & 0x1ff) == 0x0) && (page >= 0x100)) // randomly create bad block ++ { ++ printk("hmm... create a bad block at page %x\n", (bus_addr >> 16)); ++ status = -1; ++ } ++#endif ++ ++ if (status != 0) { ++ printk("%s: ecc_verify fail: ret:%x \n", __func__, status); ++ oob[ra->badblockpos] = 0x33; ++ page -= page % (CFG_BLOCKSIZE/CFG_PAGESIZE); ++ printk("create a bad block at page %x\n", page); ++ if (!is_nand_page_2048) ++ status = nfc_write_oob(ra, page, ra->badblockpos, oob+ra->badblockpos, 1, flags); ++ else ++ { ++ status = _nfc_write_raw_data(cmd1, cmd3, bus_addr, bus_addr2, conf, buf, size, flags); ++ nfc_write_oob(ra, page, 0, oob, 16, FLAG_NONE); ++ } ++ return -EBADMSG; ++ } ++ } ++ ++ ++ ra->buffers_page = page; //cached ++ return 0; ++} ++ ++ ++ ++/************************************************************* ++ * nand internal process ++ *************************************************************/ ++ ++/** ++ * nand_release_device - [GENERIC] release chip ++ * @mtd: MTD device structure ++ * ++ * Deselect, release chip lock and wake up anyone waiting on the device ++ */ ++static void nand_release_device(struct ra_nand_chip *ra) ++{ ++ /* De-select the NAND device */ ++ nfc_select_chip(ra, -1); ++ ++ /* Release the controller and the chip */ ++ ra->state = FL_READY; ++ ++ mutex_unlock(ra->controller); ++} ++ ++/** ++ * nand_get_device - [GENERIC] Get chip for selected access ++ * @chip: the nand chip descriptor ++ * @mtd: MTD device structure ++ * @new_state: the state which is requested ++ * ++ * Get the device and lock it for exclusive access ++ */ ++static int ++nand_get_device(struct ra_nand_chip *ra, int new_state) ++{ ++ int ret = 0; ++ ++ ret = mutex_lock_interruptible(ra->controller); ++ if (!ret) ++ ra->state = new_state; ++ ++ return ret; ++ ++} ++ ++ ++ ++/************************************************************* ++ * nand internal process ++ *************************************************************/ ++ ++int nand_bbt_get(struct ra_nand_chip *ra, int block) ++{ ++ int byte, bits; ++ bits = block * BBTTAG_BITS; ++ ++ byte = bits / 8; ++ bits = bits % 8; ++ ++ return (ra->bbt[byte] >> bits) & BBTTAG_BITS_MASK; ++} ++ ++int nand_bbt_set(struct ra_nand_chip *ra, int block, int tag) ++{ ++ int byte, bits; ++ bits = block * BBTTAG_BITS; ++ ++ byte = bits / 8; ++ bits = bits % 8; ++ ++ // If previous tag is bad, dont overwrite it ++ if (((ra->bbt[byte] >> bits) & BBTTAG_BITS_MASK) == BBT_TAG_BAD) ++ { ++ return BBT_TAG_BAD; ++ } ++ ++ ra->bbt[byte] = (ra->bbt[byte] & ~(BBTTAG_BITS_MASK << bits)) | ((tag & BBTTAG_BITS_MASK) << bits); ++ ++ return tag; ++} ++ ++/** ++ * nand_block_checkbad - [GENERIC] Check if a block is marked bad ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * Check, if the block is bad. Either by reading the bad block table or ++ * calling of the scan function. ++ */ ++int nand_block_checkbad(struct ra_nand_chip *ra, loff_t offs) ++{ ++ int page, block; ++ int ret = 4; ++ unsigned int tag; ++ char *str[]= {"UNK", "RES", "BAD", "GOOD"}; ++ ++ if (ranfc_bbt == 0) ++ return 0; ++ ++ { ++ // align with chip ++ ++ offs = offs & ((1<<ra->chip_shift) -1); ++ ++ page = offs >> ra->page_shift; ++ block = offs >> ra->erase_shift; ++ } ++ ++ tag = nand_bbt_get(ra, block); ++ ++ if (tag == BBT_TAG_UNKNOWN) { ++ ret = nfc_read_oob(ra, page, ra->badblockpos, (char*)&tag, 1, FLAG_NONE); ++ if (ret == 0) ++ tag = ((le32_to_cpu(tag) & 0x0ff) == 0x0ff) ? BBT_TAG_GOOD : BBT_TAG_BAD; ++ else ++ tag = BBT_TAG_BAD; ++ ++ nand_bbt_set(ra, block, tag); ++ } ++ ++ if (tag != BBT_TAG_GOOD) { ++ printk("%s: offs:%x tag: %s \n", __func__, (unsigned int)offs, str[tag]); ++ return 1; ++ } ++ else ++ return 0; ++ ++} ++ ++ ++ ++/** ++ * nand_block_markbad - ++ */ ++int nand_block_markbad(struct ra_nand_chip *ra, loff_t offs) ++{ ++ int page, block; ++ int ret = 4; ++ unsigned int tag; ++ char *ecc; ++ ++ // align with chip ++ ra_dbg("%s offs: %x \n", __func__, (int)offs); ++ ++ offs = offs & ((1<<ra->chip_shift) -1); ++ ++ page = offs >> ra->page_shift; ++ block = offs >> ra->erase_shift; ++ ++ tag = nand_bbt_get(ra, block); ++ ++ if (tag == BBT_TAG_BAD) { ++ printk("%s: mark repeatedly \n", __func__); ++ return 0; ++ } ++ ++ // new tag as bad ++ tag =BBT_TAG_BAD; ++ ret = nfc_read_page(ra, ra->buffers, page, FLAG_NONE); ++ if (ret != 0) { ++ printk("%s: fail to read bad block tag \n", __func__); ++ goto tag_bbt; ++ } ++ ++ ecc = &ra->buffers[(1<<ra->page_shift)+ra->badblockpos]; ++ if (*ecc == (char)0x0ff) { ++ //tag into flash ++ *ecc = (char)tag; ++ ret = nfc_write_page(ra, ra->buffers, page, FLAG_USE_GDMA); ++ if (ret) ++ printk("%s: fail to write bad block tag \n", __func__); ++ ++ } ++ ++tag_bbt: ++ //update bbt ++ nand_bbt_set(ra, block, tag); ++ ++ return 0; ++} ++ ++ ++#if defined (WORKAROUND_RX_BUF_OV) ++/** ++ * to find a bad block for ecc verify of read_page ++ */ ++unsigned int nand_bbt_find_sandbox(struct ra_nand_chip *ra) ++{ ++ loff_t offs = 0; ++ int chipsize = 1 << ra->chip_shift; ++ int blocksize = 1 << ra->erase_shift; ++ ++ ++ while (offs < chipsize) { ++ if (nand_block_checkbad(ra, offs)) //scan and verify the unknown tag ++ break; ++ offs += blocksize; ++ } ++ ++ if (offs >= chipsize) { ++ offs = chipsize - blocksize; ++ } ++ ++ nand_bbt_set(ra, (unsigned int)offs>>ra->erase_shift, BBT_TAG_RES); // tag bbt only, instead of update badblockpos of flash. ++ return (offs >> ra->page_shift); ++} ++#endif ++ ++ ++ ++/** ++ * nand_erase_nand - [Internal] erase block(s) ++ * @mtd: MTD device structure ++ * @instr: erase instruction ++ * @allowbbt: allow erasing the bbt area ++ * ++ * Erase one ore more blocks ++ */ ++int _nand_erase_nand(struct ra_nand_chip *ra, struct erase_info *instr) ++{ ++ int page, len, status, ret; ++ unsigned int addr, blocksize = 1<<ra->erase_shift; ++ ++ ra_dbg("%s: start:%x, len:%x \n", __func__, ++ (unsigned int)instr->addr, (unsigned int)instr->len); ++ ++//#define BLOCK_ALIGNED(a) ((a) & (blocksize - 1)) // already defined ++ ++ if (BLOCK_ALIGNED(instr->addr) || BLOCK_ALIGNED(instr->len)) { ++ ra_dbg("%s: erase block not aligned, addr:%x len:%x\n", __func__, instr->addr, instr->len); ++ return -EINVAL; ++ } ++ ++ instr->fail_addr = 0xffffffff; ++ ++ len = instr->len; ++ addr = instr->addr; ++ instr->state = MTD_ERASING; ++ ++ while (len) { ++ ++ page = (int)(addr >> ra->page_shift); ++ ++ /* select device and check wp */ ++ if (nfc_enable_chip(ra, addr, 0)) { ++ printk("%s: nand is write protected \n", __func__); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* if we have a bad block, we do not erase bad blocks */ ++ if (nand_block_checkbad(ra, addr)) { ++ printk(KERN_WARNING "nand_erase: attempt to erase a " ++ "bad block at 0x%08x\n", addr); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* ++ * Invalidate the page cache, if we erase the block which ++ * contains the current cached page ++ */ ++ if (BLOCK_ALIGNED(addr) == BLOCK_ALIGNED(ra->buffers_page << ra->page_shift)) ++ ra->buffers_page = -1; ++ ++ status = nfc_erase_block(ra, page); ++ /* See if block erase succeeded */ ++ if (status) { ++ printk("%s: failed erase, page 0x%08x\n", __func__, page); ++ instr->state = MTD_ERASE_FAILED; ++ instr->fail_addr = (page << ra->page_shift); ++ goto erase_exit; ++ } ++ ++ ++ /* Increment page address and decrement length */ ++ len -= blocksize; ++ addr += blocksize; ++ ++ } ++ instr->state = MTD_ERASE_DONE; ++ ++erase_exit: ++ ++ ret = ((instr->state == MTD_ERASE_DONE) ? 0 : -EIO); ++ /* Do call back function */ ++ if (!ret) ++ mtd_erase_callback(instr); ++ ++ if (ret) { ++ nand_bbt_set(ra, addr >> ra->erase_shift, BBT_TAG_BAD); ++ } ++ ++ /* Return more or less happy */ ++ return ret; ++} ++ ++static int ++nand_write_oob_buf(struct ra_nand_chip *ra, uint8_t *buf, uint8_t *oob, size_t size, ++ int mode, int ooboffs) ++{ ++ size_t oobsize = 1<<ra->oob_shift; ++ struct nand_oobfree *free; ++ uint32_t woffs = ooboffs; ++ int retsize = 0; ++ ++ ra_dbg("%s: size:%x, mode:%x, offs:%x \n", __func__, size, mode, ooboffs); ++ ++ switch(mode) { ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_RAW: ++ if (ooboffs > oobsize) ++ return -1; ++ ++ size = min(size, oobsize - ooboffs); ++ memcpy(buf + ooboffs, oob, size); ++ retsize = size; ++ break; ++ ++ case MTD_OPS_AUTO_OOB: ++ if (ooboffs > ra->oob->oobavail) ++ return -1; ++ ++ while (size) { ++ for(free = ra->oob->oobfree; free->length && size; free++) { ++ int wlen = free->length - woffs; ++ int bytes = 0; ++ ++ /* Write request not from offset 0 ? */ ++ if (wlen <= 0) { ++ woffs = -wlen; ++ continue; ++ } ++ ++ bytes = min_t(size_t, size, wlen); ++ memcpy (buf + free->offset + woffs, oob, bytes); ++ woffs = 0; ++ oob += bytes; ++ size -= bytes; ++ retsize += bytes; ++ } ++ buf += oobsize; ++ } ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ return retsize; ++} ++ ++static int nand_read_oob_buf(struct ra_nand_chip *ra, uint8_t *oob, size_t size, ++ int mode, int ooboffs) ++{ ++ size_t oobsize = 1<<ra->oob_shift; ++ uint8_t *buf = ra->buffers + (1<<ra->page_shift); ++ int retsize=0; ++ ++ ra_dbg("%s: size:%x, mode:%x, offs:%x \n", __func__, size, mode, ooboffs); ++ ++ switch(mode) { ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_RAW: ++ if (ooboffs > oobsize) ++ return -1; ++ ++ size = min(size, oobsize - ooboffs); ++ memcpy(oob, buf + ooboffs, size); ++ return size; ++ ++ case MTD_OPS_AUTO_OOB: { ++ struct nand_oobfree *free; ++ uint32_t woffs = ooboffs; ++ ++ if (ooboffs > ra->oob->oobavail) ++ return -1; ++ ++ size = min(size, ra->oob->oobavail - ooboffs); ++ for(free = ra->oob->oobfree; free->length && size; free++) { ++ int wlen = free->length - woffs; ++ int bytes = 0; ++ ++ /* Write request not from offset 0 ? */ ++ if (wlen <= 0) { ++ woffs = -wlen; ++ continue; ++ } ++ ++ bytes = min_t(size_t, size, wlen); ++ memcpy (oob, buf + free->offset + woffs, bytes); ++ woffs = 0; ++ oob += bytes; ++ size -= bytes; ++ retsize += bytes; ++ } ++ return retsize; ++ } ++ default: ++ BUG(); ++ } ++ ++ return -1; ++} ++ ++/** ++ * nand_do_write_ops - [Internal] NAND write with ECC ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @ops: oob operations description structure ++ * ++ * NAND write with ECC ++ */ ++static int nand_do_write_ops(struct ra_nand_chip *ra, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ int page; ++ uint32_t datalen = ops->len; ++ uint32_t ooblen = ops->ooblen; ++ uint8_t *oob = ops->oobbuf; ++ uint8_t *data = ops->datbuf; ++ int pagesize = (1<<ra->page_shift); ++ int pagemask = (pagesize -1); ++ int oobsize = 1<<ra->oob_shift; ++ loff_t addr = to; ++ //int i = 0; //for ra_dbg only ++ ++ ra_dbg("%s: to:%x, ops data:%p, oob:%p datalen:%x ooblen:%x, ooboffs:%x oobmode:%x \n", ++ __func__, (unsigned int)to, data, oob, datalen, ooblen, ops->ooboffs, ops->mode); ++ ++ ops->retlen = 0; ++ ops->oobretlen = 0; ++ ++ ++ /* Invalidate the page cache, when we write to the cached page */ ++ ra->buffers_page = -1; ++ ++ ++ if (data ==0) ++ datalen = 0; ++ ++ // oob sequential (burst) write ++ if (datalen == 0 && ooblen) { ++ int len = ((ooblen + ops->ooboffs) + (ra->oob->oobavail - 1)) / ra->oob->oobavail * oobsize; ++ ++ /* select chip, and check if it is write protected */ ++ if (nfc_enable_chip(ra, addr, 0)) ++ return -EIO; ++ ++ //FIXME, need sanity check of block boundary ++ page = (int)((to & ((1<<ra->chip_shift)-1)) >> ra->page_shift); //chip boundary ++ memset(ra->buffers, 0x0ff, pagesize); ++ //fixme, should we reserve the original content? ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ nfc_read_oob(ra, page, 0, ra->buffers, len, FLAG_NONE); ++ } ++ //prepare buffers ++ if (ooblen != 8) ++ { ++ nand_write_oob_buf(ra, ra->buffers, oob, ooblen, ops->mode, ops->ooboffs); ++ // write out buffer to chip ++ nfc_write_oob(ra, page, 0, ra->buffers, len, FLAG_USE_GDMA); ++ } ++ ++ ops->oobretlen = ooblen; ++ ooblen = 0; ++ } ++ ++ // data sequential (burst) write ++ if (datalen && ooblen == 0) { ++ // ranfc can not support write_data_burst, since hw-ecc and fifo constraints.. ++ } ++ ++ // page write ++ while(datalen || ooblen) { ++ int len; ++ int ret; ++ int offs; ++ int ecc_en = 0; ++ ++ ra_dbg("%s (%d): addr:%x, ops data:%p, oob:%p datalen:%x ooblen:%x, ooboffs:%x \n", ++ __func__, i++, (unsigned int)addr, data, oob, datalen, ooblen, ops->ooboffs); ++ ++ page = (int)((addr & ((1<<ra->chip_shift)-1)) >> ra->page_shift); //chip boundary ++ ++ /* select chip, and check if it is write protected */ ++ if (nfc_enable_chip(ra, addr, 0)) ++ return -EIO; ++ ++ // oob write ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ //fixme, this path is not yet varified ++ nfc_read_oob(ra, page, 0, ra->buffers + pagesize, oobsize, FLAG_NONE); ++ } ++ if (oob && ooblen > 0) { ++ len = nand_write_oob_buf(ra, ra->buffers + pagesize, oob, ooblen, ops->mode, ops->ooboffs); ++ if (len < 0) ++ return -EINVAL; ++ ++ oob += len; ++ ops->oobretlen += len; ++ ooblen -= len; ++ } ++ ++ // data write ++ offs = addr & pagemask; ++ len = min_t(size_t, datalen, pagesize - offs); ++ if (data && len > 0) { ++ memcpy(ra->buffers + offs, data, len); // we can not sure ops->buf wether is DMA-able. ++ ++ data += len; ++ datalen -= len; ++ ops->retlen += len; ++ ++ ecc_en = FLAG_ECC_EN; ++ } ++ ret = nfc_write_page(ra, ra->buffers, page, FLAG_USE_GDMA | FLAG_VERIFY | ++ ((ops->mode == MTD_OPS_RAW || ops->mode == MTD_OPS_PLACE_OOB) ? 0 : ecc_en )); ++ if (ret) { ++ nand_bbt_set(ra, addr >> ra->erase_shift, BBT_TAG_BAD); ++ return ret; ++ } ++ ++ nand_bbt_set(ra, addr >> ra->erase_shift, BBT_TAG_GOOD); ++ ++ addr = (page+1) << ra->page_shift; ++ ++ } ++ return 0; ++} ++ ++/** ++ * nand_do_read_ops - [Internal] Read data with ECC ++ * ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @ops: oob ops structure ++ * ++ * Internal function. Called with chip held. ++ */ ++static int nand_do_read_ops(struct ra_nand_chip *ra, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ int page; ++ uint32_t datalen = ops->len; ++ uint32_t ooblen = ops->ooblen; ++ uint8_t *oob = ops->oobbuf; ++ uint8_t *data = ops->datbuf; ++ int pagesize = (1<<ra->page_shift); ++ int pagemask = (pagesize -1); ++ loff_t addr = from; ++ //int i = 0; //for ra_dbg only ++ ++ ra_dbg("%s: addr:%x, ops data:%p, oob:%p datalen:%x ooblen:%x, ooboffs:%x \n", ++ __func__, (unsigned int)addr, data, oob, datalen, ooblen, ops->ooboffs); ++ ++ ops->retlen = 0; ++ ops->oobretlen = 0; ++ if (data == 0) ++ datalen = 0; ++ ++ ++ while(datalen || ooblen) { ++ int len; ++ int ret; ++ int offs; ++ ++ ra_dbg("%s (%d): addr:%x, ops data:%p, oob:%p datalen:%x ooblen:%x, ooboffs:%x \n", ++ __func__, i++, (unsigned int)addr, data, oob, datalen, ooblen, ops->ooboffs); ++ /* select chip */ ++ if (nfc_enable_chip(ra, addr, 1) < 0) ++ return -EIO; ++ ++ page = (int)((addr & ((1<<ra->chip_shift)-1)) >> ra->page_shift); ++ ++ ret = nfc_read_page(ra, ra->buffers, page, FLAG_VERIFY | ++ ((ops->mode == MTD_OPS_RAW || ops->mode == MTD_OPS_PLACE_OOB) ? 0: FLAG_ECC_EN )); ++ //FIXME, something strange here, some page needs 2 more tries to guarantee read success. ++ if (ret) { ++ printk("read again:\n"); ++ ret = nfc_read_page(ra, ra->buffers, page, FLAG_VERIFY | ++ ((ops->mode == MTD_OPS_RAW || ops->mode == MTD_OPS_PLACE_OOB) ? 0: FLAG_ECC_EN )); ++ ++ if (ret) { ++ printk("read again fail \n"); ++ nand_bbt_set(ra, addr >> ra->erase_shift, BBT_TAG_BAD); ++ if ((ret != -EUCLEAN) && (ret != -EBADMSG)) { ++ return ret; ++ } ++ else { ++ /* ecc verification fail, but data need to be returned. */ ++ } ++ } ++ else { ++ printk(" read agian susccess \n"); ++ } ++ } ++ ++ // oob read ++ if (oob && ooblen > 0) { ++ len = nand_read_oob_buf(ra, oob, ooblen, ops->mode, ops->ooboffs); ++ if (len < 0) { ++ printk("nand_read_oob_buf: fail return %x \n", len); ++ return -EINVAL; ++ } ++ ++ oob += len; ++ ops->oobretlen += len; ++ ooblen -= len; ++ } ++ ++ // data read ++ offs = addr & pagemask; ++ len = min_t(size_t, datalen, pagesize - offs); ++ if (data && len > 0) { ++ memcpy(data, ra->buffers + offs, len); // we can not sure ops->buf wether is DMA-able. ++ ++ data += len; ++ datalen -= len; ++ ops->retlen += len; ++ if (ret) ++ return ret; ++ } ++ ++ ++ nand_bbt_set(ra, addr >> ra->erase_shift, BBT_TAG_GOOD); ++ // address go further to next page, instead of increasing of length of write. This avoids some special cases wrong. ++ addr = (page+1) << ra->page_shift; ++ } ++ return 0; ++} ++ ++static int ++ramtd_nand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct ra_nand_chip *ra = (struct ra_nand_chip *)mtd->priv; ++ int ret; ++ ++ ra_dbg("%s: start:%x, len:%x \n", __func__, ++ (unsigned int)instr->addr, (unsigned int)instr->len); ++ ++ nand_get_device(ra, FL_ERASING); ++ ret = _nand_erase_nand((struct ra_nand_chip *)mtd->priv, instr); ++ nand_release_device(ra); ++ ++ return ret; ++} ++ ++static int ++ramtd_nand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const uint8_t *buf) ++{ ++ struct ra_nand_chip *ra = mtd->priv; ++ struct mtd_oob_ops ops; ++ int ret; ++ ++ ra_dbg("%s: to 0x%x len=0x%x\n", __func__, to, len); ++ ++ if ((to + len) > mtd->size) ++ return -EINVAL; ++ ++ if (!len) ++ return 0; ++ ++ nand_get_device(ra, FL_WRITING); ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.len = len; ++ ops.datbuf = (uint8_t *)buf; ++ ops.oobbuf = NULL; ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ++ ret = nand_do_write_ops(ra, to, &ops); ++ ++ *retlen = ops.retlen; ++ ++ nand_release_device(ra); ++ ++ return ret; ++} ++ ++static int ++ramtd_nand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, uint8_t *buf) ++{ ++ ++ struct ra_nand_chip *ra = mtd->priv; ++ int ret; ++ struct mtd_oob_ops ops; ++ ++ ra_dbg("%s: mtd:%p from:%x, len:%x, buf:%p \n", __func__, mtd, (unsigned int)from, len, buf); ++ ++ /* Do not allow reads past end of device */ ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ if (!len) ++ return 0; ++ ++ nand_get_device(ra, FL_READING); ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.len = len; ++ ops.datbuf = buf; ++ ops.oobbuf = NULL; ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ++ ret = nand_do_read_ops(ra, from, &ops); ++ ++ *retlen = ops.retlen; ++ ++ nand_release_device(ra); ++ ++ return ret; ++ ++} ++ ++static int ++ramtd_nand_readoob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct ra_nand_chip *ra = mtd->priv; ++ int ret; ++ ++ ra_dbg("%s: \n", __func__); ++ ++ nand_get_device(ra, FL_READING); ++ ++ ret = nand_do_read_ops(ra, from, ops); ++ ++ nand_release_device(ra); ++ ++ return ret; ++} ++ ++static int ++ramtd_nand_writeoob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct ra_nand_chip *ra = mtd->priv; ++ int ret; ++ ++ nand_get_device(ra, FL_READING); ++ ret = nand_do_write_ops(ra, to, ops); ++ nand_release_device(ra); ++ ++ return ret; ++} ++ ++static int ++ramtd_nand_block_isbad(struct mtd_info *mtd, loff_t offs) ++{ ++ if (offs > mtd->size) ++ return -EINVAL; ++ ++ return nand_block_checkbad((struct ra_nand_chip *)mtd->priv, offs); ++} ++ ++static int ++ramtd_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct ra_nand_chip *ra = mtd->priv; ++ int ret; ++ ++ ra_dbg("%s: \n", __func__); ++ nand_get_device(ra, FL_WRITING); ++ ret = nand_block_markbad(ra, ofs); ++ nand_release_device(ra); ++ ++ return ret; ++} ++ ++// 1-bit error detection ++static int one_bit_correction(char *ecc1, char *ecc2, int *bytes, int *bits) ++{ ++ // check if ecc and expected are all valid ++ char *p, nibble, crumb; ++ int i, xor, iecc1 = 0, iecc2 = 0; ++ ++ printk("correction : %x %x %x\n", ecc1[0], ecc1[1], ecc1[2]); ++ printk("correction : %x %x %x\n", ecc2[0], ecc2[1], ecc2[2]); ++ ++ p = (char *)ecc1; ++ for (i = 0; i < CONFIG_ECC_BYTES; i++) ++ { ++ nibble = *(p+i) & 0xf; ++ if ((nibble != 0x0) && (nibble != 0xf) && (nibble != 0x3) && (nibble != 0xc) && ++ (nibble != 0x5) && (nibble != 0xa) && (nibble != 0x6) && (nibble != 0x9)) ++ return -1; ++ nibble = ((*(p+i)) >> 4) & 0xf; ++ if ((nibble != 0x0) && (nibble != 0xf) && (nibble != 0x3) && (nibble != 0xc) && ++ (nibble != 0x5) && (nibble != 0xa) && (nibble != 0x6) && (nibble != 0x9)) ++ return -1; ++ } ++ ++ p = (char *)ecc2; ++ for (i = 0; i < CONFIG_ECC_BYTES; i++) ++ { ++ nibble = *(p+i) & 0xf; ++ if ((nibble != 0x0) && (nibble != 0xf) && (nibble != 0x3) && (nibble != 0xc) && ++ (nibble != 0x5) && (nibble != 0xa) && (nibble != 0x6) && (nibble != 0x9)) ++ return -1; ++ nibble = ((*(p+i)) >> 4) & 0xf; ++ if ((nibble != 0x0) && (nibble != 0xf) && (nibble != 0x3) && (nibble != 0xc) && ++ (nibble != 0x5) && (nibble != 0xa) && (nibble != 0x6) && (nibble != 0x9)) ++ return -1; ++ } ++ ++ memcpy(&iecc1, ecc1, 3); ++ memcpy(&iecc2, ecc2, 3); ++ ++ xor = iecc1 ^ iecc2; ++ printk("xor = %x (%x %x)\n", xor, iecc1, iecc2); ++ ++ *bytes = 0; ++ for (i = 0; i < 9; i++) ++ { ++ crumb = (xor >> (2*i)) & 0x3; ++ if ((crumb == 0x0) || (crumb == 0x3)) ++ return -1; ++ if (crumb == 0x2) ++ *bytes += (1 << i); ++ } ++ ++ *bits = 0; ++ for (i = 0; i < 3; i++) ++ { ++ crumb = (xor >> (18 + 2*i)) & 0x3; ++ if ((crumb == 0x0) || (crumb == 0x3)) ++ return -1; ++ if (crumb == 0x2) ++ *bits += (1 << i); ++ } ++ ++ return 0; ++} ++ ++ ++ ++/************************************************************ ++ * the init/exit section. ++ */ ++ ++static struct nand_ecclayout ra_oob_layout = { ++ .eccbytes = CONFIG_ECC_BYTES, ++ .eccpos = {5, 6, 7}, ++ .oobfree = { ++ {.offset = 0, .length = 4}, ++ {.offset = 8, .length = 8}, ++ {.offset = 0, .length = 0} ++ }, ++#define RA_CHIP_OOB_AVAIL (4+8) ++ .oobavail = RA_CHIP_OOB_AVAIL, ++ // 5th byte is bad-block flag. ++}; ++ ++static int ++mtk_nand_probe(struct platform_device *pdev) ++{ ++ struct mtd_part_parser_data ppdata; ++ struct ra_nand_chip *ra; ++ int alloc_size, bbt_size, buffers_size, reg, err; ++ unsigned char chip_mode = 12; ++ ++/* if(ra_check_flash_type()!=BOOT_FROM_NAND) { ++ return 0; ++ }*/ ++ ++ //FIXME: config 512 or 2048-byte page according to HWCONF ++#if defined (CONFIG_RALINK_RT6855A) ++ reg = ra_inl(RALINK_SYSCTL_BASE+0x8c); ++ chip_mode = ((reg>>28) & 0x3)|(((reg>>22) & 0x3)<<2); ++ if (chip_mode == 1) { ++ printk("! nand 2048\n"); ++ ra_or(NFC_CONF1, 1); ++ is_nand_page_2048 = 1; ++ nand_addrlen = 5; ++ } ++ else { ++ printk("! nand 512\n"); ++ ra_and(NFC_CONF1, ~1); ++ is_nand_page_2048 = 0; ++ nand_addrlen = 4; ++ } ++#elif (defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_RT6855)) ++ ra_outl(RALINK_SYSCTL_BASE+0x60, ra_inl(RALINK_SYSCTL_BASE+0x60) & ~(0x3<<18)); ++ reg = ra_inl(RALINK_SYSCTL_BASE+0x10); ++ chip_mode = (reg & 0x0F); ++ if((chip_mode==1)||(chip_mode==11)) { ++ ra_or(NFC_CONF1, 1); ++ is_nand_page_2048 = 1; ++ nand_addrlen = ((chip_mode!=11) ? 4 : 5); ++ printk("!!! nand page size = 2048, addr len=%d\n", nand_addrlen); ++ } ++ else { ++ ra_and(NFC_CONF1, ~1); ++ is_nand_page_2048 = 0; ++ nand_addrlen = ((chip_mode!=10) ? 3 : 4); ++ printk("!!! nand page size = 512, addr len=%d\n", nand_addrlen); ++ } ++#else ++ is_nand_page_2048 = 0; ++ nand_addrlen = 3; ++ printk("!!! nand page size = 512, addr len=%d\n", nand_addrlen); ++#endif ++ ++#if defined (CONFIG_RALINK_RT6855A) || defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_RT6855) ++ //config ECC location ++ ra_and(NFC_CONF1, 0xfff000ff); ++ ra_or(NFC_CONF1, ((CONFIG_ECC_OFFSET + 2) << 16) + ++ ((CONFIG_ECC_OFFSET + 1) << 12) + ++ (CONFIG_ECC_OFFSET << 8)); ++#endif ++ ++#define ALIGNE_16(a) (((unsigned long)(a)+15) & ~15) ++ buffers_size = ALIGNE_16((1<<CONFIG_PAGE_SIZE_BIT) + (1<<CONFIG_OOBSIZE_PER_PAGE_BIT)); //ra->buffers ++ bbt_size = BBTTAG_BITS * (1<<(CONFIG_CHIP_SIZE_BIT - (CONFIG_PAGE_SIZE_BIT + CONFIG_NUMPAGE_PER_BLOCK_BIT))) / 8; //ra->bbt ++ bbt_size = ALIGNE_16(bbt_size); ++ ++ alloc_size = buffers_size + bbt_size; ++ alloc_size += buffers_size; //for ra->readback_buffers ++ alloc_size += sizeof(*ra); ++ alloc_size += sizeof(*ranfc_mtd); ++ ++ //make sure gpio-0 is input ++ ra_outl(RALINK_PIO_BASE+0x24, ra_inl(RALINK_PIO_BASE+0x24) & ~0x01); ++ ++ ra = (struct ra_nand_chip *)kzalloc(alloc_size, GFP_KERNEL | GFP_DMA); ++ if (!ra) { ++ printk("%s: mem alloc fail \n", __func__); ++ return -ENOMEM; ++ } ++ memset(ra, 0, alloc_size); ++ ++ //dynamic ++ ra->buffers = (char *)((char *)ra + sizeof(*ra)); ++ ra->readback_buffers = ra->buffers + buffers_size; ++ ra->bbt = ra->readback_buffers + buffers_size; ++ ranfc_mtd = (struct mtd_info *)(ra->bbt + bbt_size); ++ ++ //static ++ ra->numchips = CONFIG_NUMCHIPS; ++ ra->chip_shift = CONFIG_CHIP_SIZE_BIT; ++ ra->page_shift = CONFIG_PAGE_SIZE_BIT; ++ ra->oob_shift = CONFIG_OOBSIZE_PER_PAGE_BIT; ++ ra->erase_shift = (CONFIG_PAGE_SIZE_BIT + CONFIG_NUMPAGE_PER_BLOCK_BIT); ++ ra->badblockpos = CONFIG_BAD_BLOCK_POS; ++ ra_oob_layout.eccpos[0] = CONFIG_ECC_OFFSET; ++ ra_oob_layout.eccpos[1] = CONFIG_ECC_OFFSET + 1; ++ ra_oob_layout.eccpos[2] = CONFIG_ECC_OFFSET + 2; ++ ra->oob = &ra_oob_layout; ++ ra->buffers_page = -1; ++ ++#if defined (WORKAROUND_RX_BUF_OV) ++ if (ranfc_verify) { ++ ra->sandbox_page = nand_bbt_find_sandbox(ra); ++ } ++#endif ++ ra_outl(NFC_CTRL, ra_inl(NFC_CTRL) | 0x01); //set wp to high ++ nfc_all_reset(); ++ ++ ranfc_mtd->type = MTD_NANDFLASH; ++ ranfc_mtd->flags = MTD_CAP_NANDFLASH; ++ ranfc_mtd->size = CONFIG_NUMCHIPS * CFG_CHIPSIZE; ++ ranfc_mtd->erasesize = CFG_BLOCKSIZE; ++ ranfc_mtd->writesize = CFG_PAGESIZE; ++ ranfc_mtd->oobsize = CFG_PAGE_OOBSIZE; ++ ranfc_mtd->oobavail = RA_CHIP_OOB_AVAIL; ++ ranfc_mtd->name = "ra_nfc"; ++ //ranfc_mtd->index ++ ranfc_mtd->ecclayout = &ra_oob_layout; ++ //ranfc_mtd->numberaseregions ++ //ranfc_mtd->eraseregions ++ //ranfc_mtd->bansize ++ ranfc_mtd->_erase = ramtd_nand_erase; ++ //ranfc_mtd->point ++ //ranfc_mtd->unpoint ++ ranfc_mtd->_read = ramtd_nand_read; ++ ranfc_mtd->_write = ramtd_nand_write; ++ ranfc_mtd->_read_oob = ramtd_nand_readoob; ++ ranfc_mtd->_write_oob = ramtd_nand_writeoob; ++ //ranfc_mtd->get_fact_prot_info; ranfc_mtd->read_fact_prot_reg; ++ //ranfc_mtd->get_user_prot_info; ranfc_mtd->read_user_prot_reg; ++ //ranfc_mtd->write_user_prot_reg; ranfc_mtd->lock_user_prot_reg; ++ //ranfc_mtd->writev; ranfc_mtd->sync; ranfc_mtd->lock; ranfc_mtd->unlock; ranfc_mtd->suspend; ranfc_mtd->resume; ++ ranfc_mtd->_block_isbad = ramtd_nand_block_isbad; ++ ranfc_mtd->_block_markbad = ramtd_nand_block_markbad; ++ //ranfc_mtd->reboot_notifier ++ //ranfc_mtd->ecc_stats; ++ // subpage_sht; ++ ++ //ranfc_mtd->get_device; ranfc_mtd->put_device ++ ranfc_mtd->priv = ra; ++ ++ ranfc_mtd->owner = THIS_MODULE; ++ ra->controller = &ra->hwcontrol; ++ mutex_init(ra->controller); ++ ++ printk("%s: alloc %x, at %p , btt(%p, %x), ranfc_mtd:%p\n", ++ __func__ , alloc_size, ra, ra->bbt, bbt_size, ranfc_mtd); ++ ++ ppdata.of_node = pdev->dev.of_node; ++ err = mtd_device_parse_register(ranfc_mtd, mtk_probe_types, ++ &ppdata, NULL, 0); ++ ++ return err; ++} ++ ++static int ++mtk_nand_remove(struct platform_device *pdev) ++{ ++ struct ra_nand_chip *ra; ++ ++ if (ranfc_mtd) { ++ ra = (struct ra_nand_chip *)ranfc_mtd->priv; ++ ++ /* Deregister partitions */ ++ //del_mtd_partitions(ranfc_mtd); ++ kfree(ra); ++ } ++ return 0; ++} ++ ++static const struct of_device_id mtk_nand_match[] = { ++ { .compatible = "mtk,mt7620-nand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mtk_nand_match); ++ ++static struct platform_driver mtk_nand_driver = { ++ .probe = mtk_nand_probe, ++ .remove = mtk_nand_remove, ++ .driver = { ++ .name = "mt7620_nand", ++ .owner = THIS_MODULE, ++ .of_match_table = mtk_nand_match, ++ }, ++}; ++ ++module_platform_driver(mtk_nand_driver); ++ ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/maps/ralink_nand.h b/drivers/mtd/maps/ralink_nand.h +new file mode 100644 +index 0000000..a408ae9 +--- /dev/null ++++ b/drivers/mtd/maps/ralink_nand.h +@@ -0,0 +1,232 @@ ++#ifndef RT2880_NAND_H ++#define RT2880_NAND_H ++ ++#include <linux/mtd/mtd.h> ++ ++//#include "gdma.h" ++ ++#define RALINK_SYSCTL_BASE 0xB0000000 ++#define RALINK_PIO_BASE 0xB0000600 ++#define RALINK_NAND_CTRL_BASE 0xB0000810 ++#define CONFIG_RALINK_MT7620 ++ ++#define SKIP_BAD_BLOCK ++//#define RANDOM_GEN_BAD_BLOCK ++ ++#define ra_inl(addr) (*(volatile unsigned int *)(addr)) ++#define ra_outl(addr, value) (*(volatile unsigned int *)(addr) = (value)) ++#define ra_aor(addr, a_mask, o_value) ra_outl(addr, (ra_inl(addr) & (a_mask)) | (o_value)) ++#define ra_and(addr, a_mask) ra_aor(addr, a_mask, 0) ++#define ra_or(addr, o_value) ra_aor(addr, -1, o_value) ++ ++ ++#define CONFIG_NUMCHIPS 1 ++#define CONFIG_NOT_SUPPORT_WP //rt3052 has no WP signal for chip. ++//#define CONFIG_NOT_SUPPORT_RB ++ ++extern int is_nand_page_2048; ++extern const unsigned int nand_size_map[2][3]; ++ ++//chip ++// chip geometry: SAMSUNG small size 32MB. ++#define CONFIG_CHIP_SIZE_BIT (nand_size_map[is_nand_page_2048][nand_addrlen-3]) //! (1<<NAND_SIZE_BYTE) MB ++//#define CONFIG_CHIP_SIZE_BIT (is_nand_page_2048? 29 : 25) //! (1<<NAND_SIZE_BYTE) MB ++#define CONFIG_PAGE_SIZE_BIT (is_nand_page_2048? 11 : 9) //! (1<<PAGE_SIZE) MB ++//#define CONFIG_SUBPAGE_BIT 1 //! these bits will be compensate by command cycle ++#define CONFIG_NUMPAGE_PER_BLOCK_BIT (is_nand_page_2048? 6 : 5) //! order of number of pages a block. ++#define CONFIG_OOBSIZE_PER_PAGE_BIT (is_nand_page_2048? 6 : 4) //! byte number of oob a page. ++#define CONFIG_BAD_BLOCK_POS (is_nand_page_2048? 0 : 4) //! offset of byte to denote bad block. ++#define CONFIG_ECC_BYTES 3 //! ecc has 3 bytes ++#define CONFIG_ECC_OFFSET (is_nand_page_2048? 6 : 5) //! ecc starts from offset 5. ++ ++//this section should not be modified. ++//#define CFG_COLUMN_ADDR_MASK ((1 << (CONFIG_PAGE_SIZE_BIT - CONFIG_SUBPAGE_BIT)) - 1) ++//#define CFG_COLUMN_ADDR_CYCLE (((CONFIG_PAGE_SIZE_BIT - CONFIG_SUBPAGE_BIT) + 7)/8) ++//#define CFG_ROW_ADDR_CYCLE ((CONFIG_CHIP_SIZE_BIT - CONFIG_PAGE_SIZE_BIT + 7)/8) ++//#define CFG_ADDR_CYCLE (CFG_COLUMN_ADDR_CYCLE + CFG_ROW_ADDR_CYCLE) ++ ++#define CFG_COLUMN_ADDR_CYCLE (is_nand_page_2048? 2 : 1) ++#define CFG_ROW_ADDR_CYCLE (nand_addrlen - CFG_COLUMN_ADDR_CYCLE) ++#define CFG_ADDR_CYCLE (CFG_COLUMN_ADDR_CYCLE + CFG_ROW_ADDR_CYCLE) ++ ++#define CFG_CHIPSIZE (1 << ((CONFIG_CHIP_SIZE_BIT>=32)? 31 : CONFIG_CHIP_SIZE_BIT)) ++//#define CFG_CHIPSIZE (1 << CONFIG_CHIP_SIZE_BIT) ++#define CFG_PAGESIZE (1 << CONFIG_PAGE_SIZE_BIT) ++#define CFG_BLOCKSIZE (CFG_PAGESIZE << CONFIG_NUMPAGE_PER_BLOCK_BIT) ++#define CFG_NUMPAGE (1 << (CONFIG_CHIP_SIZE_BIT - CONFIG_PAGE_SIZE_BIT)) ++#define CFG_NUMBLOCK (CFG_NUMPAGE >> CONFIG_NUMPAGE_PER_BLOCK_BIT) ++#define CFG_BLOCK_OOBSIZE (1 << (CONFIG_OOBSIZE_PER_PAGE_BIT + CONFIG_NUMPAGE_PER_BLOCK_BIT)) ++#define CFG_PAGE_OOBSIZE (1 << CONFIG_OOBSIZE_PER_PAGE_BIT) ++ ++#define NAND_BLOCK_ALIGN(addr) ((addr) & (CFG_BLOCKSIZE-1)) ++#define NAND_PAGE_ALIGN(addr) ((addr) & (CFG_PAGESIZE-1)) ++ ++ ++#define NFC_BASE RALINK_NAND_CTRL_BASE ++#define NFC_CTRL (NFC_BASE + 0x0) ++#define NFC_CONF (NFC_BASE + 0x4) ++#define NFC_CMD1 (NFC_BASE + 0x8) ++#define NFC_CMD2 (NFC_BASE + 0xc) ++#define NFC_CMD3 (NFC_BASE + 0x10) ++#define NFC_ADDR (NFC_BASE + 0x14) ++#define NFC_DATA (NFC_BASE + 0x18) ++#if defined (CONFIG_RALINK_RT6855) || defined (CONFIG_RALINK_RT6855A) || \ ++ defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621) ++#define NFC_ECC (NFC_BASE + 0x30) ++#else ++#define NFC_ECC (NFC_BASE + 0x1c) ++#endif ++#define NFC_STATUS (NFC_BASE + 0x20) ++#define NFC_INT_EN (NFC_BASE + 0x24) ++#define NFC_INT_ST (NFC_BASE + 0x28) ++#if defined (CONFIG_RALINK_RT6855) || defined (CONFIG_RALINK_RT6855A) || \ ++ defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621) ++#define NFC_CONF1 (NFC_BASE + 0x2c) ++#define NFC_ECC_P1 (NFC_BASE + 0x30) ++#define NFC_ECC_P2 (NFC_BASE + 0x34) ++#define NFC_ECC_P3 (NFC_BASE + 0x38) ++#define NFC_ECC_P4 (NFC_BASE + 0x3c) ++#define NFC_ECC_ERR1 (NFC_BASE + 0x40) ++#define NFC_ECC_ERR2 (NFC_BASE + 0x44) ++#define NFC_ECC_ERR3 (NFC_BASE + 0x48) ++#define NFC_ECC_ERR4 (NFC_BASE + 0x4c) ++#define NFC_ADDR2 (NFC_BASE + 0x50) ++#endif ++ ++enum _int_stat { ++ INT_ST_ND_DONE = 1<<0, ++ INT_ST_TX_BUF_RDY = 1<<1, ++ INT_ST_RX_BUF_RDY = 1<<2, ++ INT_ST_ECC_ERR = 1<<3, ++ INT_ST_TX_TRAS_ERR = 1<<4, ++ INT_ST_RX_TRAS_ERR = 1<<5, ++ INT_ST_TX_KICK_ERR = 1<<6, ++ INT_ST_RX_KICK_ERR = 1<<7 ++}; ++ ++ ++//#define WORKAROUND_RX_BUF_OV 1 ++ ++ ++/************************************************************* ++ * stolen from nand.h ++ *************************************************************/ ++ ++/* ++ * Standard NAND flash commands ++ */ ++#define NAND_CMD_READ0 0 ++#define NAND_CMD_READ1 1 ++#define NAND_CMD_RNDOUT 5 ++#define NAND_CMD_PAGEPROG 0x10 ++#define NAND_CMD_READOOB 0x50 ++#define NAND_CMD_ERASE1 0x60 ++#define NAND_CMD_STATUS 0x70 ++#define NAND_CMD_STATUS_MULTI 0x71 ++#define NAND_CMD_SEQIN 0x80 ++#define NAND_CMD_RNDIN 0x85 ++#define NAND_CMD_READID 0x90 ++#define NAND_CMD_ERASE2 0xd0 ++#define NAND_CMD_RESET 0xff ++ ++/* Extended commands for large page devices */ ++#define NAND_CMD_READSTART 0x30 ++#define NAND_CMD_RNDOUTSTART 0xE0 ++#define NAND_CMD_CACHEDPROG 0x15 ++ ++/* Extended commands for AG-AND device */ ++/* ++ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but ++ * there is no way to distinguish that from NAND_CMD_READ0 ++ * until the remaining sequence of commands has been completed ++ * so add a high order bit and mask it off in the command. ++ */ ++#define NAND_CMD_DEPLETE1 0x100 ++#define NAND_CMD_DEPLETE2 0x38 ++#define NAND_CMD_STATUS_MULTI 0x71 ++#define NAND_CMD_STATUS_ERROR 0x72 ++/* multi-bank error status (banks 0-3) */ ++#define NAND_CMD_STATUS_ERROR0 0x73 ++#define NAND_CMD_STATUS_ERROR1 0x74 ++#define NAND_CMD_STATUS_ERROR2 0x75 ++#define NAND_CMD_STATUS_ERROR3 0x76 ++#define NAND_CMD_STATUS_RESET 0x7f ++#define NAND_CMD_STATUS_CLEAR 0xff ++ ++#define NAND_CMD_NONE -1 ++ ++/* Status bits */ ++#define NAND_STATUS_FAIL 0x01 ++#define NAND_STATUS_FAIL_N1 0x02 ++#define NAND_STATUS_TRUE_READY 0x20 ++#define NAND_STATUS_READY 0x40 ++#define NAND_STATUS_WP 0x80 ++ ++typedef enum { ++ FL_READY, ++ FL_READING, ++ FL_WRITING, ++ FL_ERASING, ++ FL_SYNCING, ++ FL_CACHEDPRG, ++ FL_PM_SUSPENDED, ++} nand_state_t; ++ ++/*************************************************************/ ++ ++ ++ ++typedef enum _ra_flags { ++ FLAG_NONE = 0, ++ FLAG_ECC_EN = (1<<0), ++ FLAG_USE_GDMA = (1<<1), ++ FLAG_VERIFY = (1<<2), ++} RA_FLAGS; ++ ++ ++#define BBTTAG_BITS 2 ++#define BBTTAG_BITS_MASK ((1<<BBTTAG_BITS) -1) ++enum BBT_TAG { ++ BBT_TAG_UNKNOWN = 0, //2'b01 ++ BBT_TAG_GOOD = 3, //2'b11 ++ BBT_TAG_BAD = 2, //2'b10 ++ BBT_TAG_RES = 1, //2'b01 ++}; ++ ++struct ra_nand_chip { ++ int numchips; ++ int chip_shift; ++ int page_shift; ++ int erase_shift; ++ int oob_shift; ++ int badblockpos; ++#if !defined (__UBOOT__) ++ struct mutex hwcontrol; ++ struct mutex *controller; ++#endif ++ struct nand_ecclayout *oob; ++ int state; ++ unsigned int buffers_page; ++ char *buffers; //[CFG_PAGESIZE + CFG_PAGE_OOBSIZE]; ++ char *readback_buffers; ++ unsigned char *bbt; ++#if defined (WORKAROUND_RX_BUF_OV) ++ unsigned int sandbox_page; // steal a page (block) for read ECC verification ++#endif ++ ++}; ++ ++ ++ ++//fixme, gdma api ++int nand_dma_sync(void); ++void release_dma_buf(void); ++int set_gdma_ch(unsigned long dst, ++ unsigned long src, unsigned int len, int burst_size, ++ int soft_mode, int src_req_type, int dst_req_type, ++ int src_burst_mode, int dst_burst_mode); ++ ++ ++ ++ ++#endif +-- +1.7.10.4 + |