aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMingjun Zhang <zhang.mingjun@linaro.org>2013-03-18 14:20:47 +0800
committerMingjun Zhang <zhang.mingjun@linaro.org>2013-03-18 14:20:47 +0800
commit160c3bf4e56e347621eacf41ea1537b5eda9f15f (patch)
tree9bc2e4e76ed3d21856e17b6645a00c695214207c /drivers
parent8ee72010119fadefab0d73c4b99ba61b1b0e4f27 (diff)
mtd: nand: hs nfc: support hisilicon nand flash controller
Hisilicon nfc is a powerful nand flash controller with dma and hardware ecc functions. This driver supports some non-standard nand chips, included in the driver's nand ids table. Signed-off-by: Mingjun Zhang <zhang.mingjun@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/Kconfig15
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/hs_nfc/Makefile7
-rw-r--r--drivers/mtd/nand/hs_nfc/debugfs.c274
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc504.h299
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc504_hw.c845
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc504_os.c339
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc504_read_retry.c353
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc_common.h128
-rw-r--r--drivers/mtd/nand/hs_nfc/hinfc_spl_ids.c195
10 files changed, 2456 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5819eb57521..1ac202fe967 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -575,4 +575,19 @@ config MTD_NAND_XWAY
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).
+config MTD_NAND_HINFC504
+ tristate "support hisilicon nand flash controller v504"
+ depends on ARCH_HS
+ default y
+ help
+ Enables support for hisilicon's NAND Flash Controller v504.
+ The NFC can do hardware ECC, it also has a dma engine.
+
+config MTD_NAND_HINFC504_DEBUG
+ bool "Hisilicon NAND Flash Controller driver debug"
+ depends on MTD_NAND_HINFC504
+ help
+ Enable debugging of the NFC driver, use debugfs to log NFC's
+ operation.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index d76d9120569..a7f49bd1456 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -53,5 +53,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_HINFC504) += hs_nfc/
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/hs_nfc/Makefile b/drivers/mtd/nand/hs_nfc/Makefile
new file mode 100644
index 00000000000..97d791af7f8
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/Makefile
@@ -0,0 +1,7 @@
+#
+#linux/drivers/mtd/nand/hs_nfc/Makefile
+#
+obj-$(CONFIG_MTD_NAND_HINFC504) += hinfc504_os.o hinfc504_hw.o hinfc_spl_ids.o \
+ hinfc504_read_retry.o
+
+obj-$(CONFIG_MTD_NAND_HINFC504_DEBUG) += debugfs.o
diff --git a/drivers/mtd/nand/hs_nfc/debugfs.c b/drivers/mtd/nand/hs_nfc/debugfs.c
new file mode 100644
index 00000000000..d317f471ba5
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/debugfs.c
@@ -0,0 +1,274 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include "hinfc504.h"
+
+#define get_page_index(host) \
+ ((host->addr_value[0] >> 16) | (host->addr_value[1] << 16))
+
+struct nand_logs_t {
+ unsigned short hour;
+ unsigned short min;
+ unsigned short sec;
+ unsigned short msec;
+
+ unsigned short addr_cycle;
+
+ unsigned long page;
+ unsigned long offset;
+ unsigned long data_len;
+
+ char error;
+ char ops;
+
+#define MAX_LOG_DATA_LEN 40
+ unsigned char data[MAX_LOG_DATA_LEN];
+};
+
+struct nand_logs_root_t {
+ struct dentry *entry;
+
+ int page_oob_size;/* page_oob_size = page size + oob size */
+ int data_offset;/* user config */
+ int data_len;/* user config */
+
+ int log_index;
+#define MAX_LOG_NUMS 100
+ struct nand_logs_t logs[MAX_LOG_NUMS];
+};
+
+static struct nand_logs_root_t *nand_logs_root;
+
+static void do_gettime(unsigned short *hour, unsigned short *min,
+ unsigned short *sec, unsigned short *msec)
+{
+ long val;
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ val = tv.tv_sec % 86400; /* 86400sec = 1 day */
+
+ if (hour)
+ *hour = val / 3600;
+ val %= 3600;
+ if (min)
+ *min = val / 60;
+ if (sec)
+ *sec = val % 60;
+ if (msec)
+ *msec = tv.tv_usec / 1000;
+}
+/*
+ * statistics for nand flash's operations (read/write/erase)
+ */
+void nand_log_ops(struct hinfc_host *host, char ops)
+{
+ struct nand_logs_t *logs;
+ int page_oob_size = nand_logs_root->page_oob_size;
+
+ logs = &nand_logs_root->logs[nand_logs_root->log_index];
+ do_gettime(&logs->hour, &logs->min, &logs->sec, &logs->msec);
+
+ logs->ops = ops;
+ logs->addr_cycle = host->addr_cycle;
+
+ logs->offset = host->addr_value[0] & 0xFFFF;
+ if (logs->offset > page_oob_size || !logs->offset)
+ logs->offset = nand_logs_root->data_offset;
+
+ logs->data_len = nand_logs_root->data_len;
+ if (logs->offset + logs->data_len > page_oob_size)
+ logs->data_len = page_oob_size - logs->offset;
+
+ switch (ops) {
+ case 'R':/* read */
+ case 'W':/* write */
+ logs->page = get_page_index(host);
+ memcpy(logs->data, host->buffer + logs->offset, logs->data_len);
+ break;
+ case 'E':/* erase */
+ logs->page = host->addr_value[0];
+ logs->offset = 0;
+ logs->data_len = 0;
+ break;
+ default:
+ break;
+ }
+
+ nand_logs_root->log_index++;
+ if (nand_logs_root->log_index >= MAX_LOG_NUMS)
+ nand_logs_root->log_index = 0;
+}
+
+static int nand_log_show(struct seq_file *m, void *v)
+{
+ int i, index;
+ struct nand_logs_t *logs;
+
+ seq_printf(m, "nand log parameters: offset=%d len=%d\n",
+ nand_logs_root->data_offset,
+ nand_logs_root->data_len);
+
+ seq_printf(m, " UTC Clock op cylce page-offset data\n");
+
+ /* list by time */
+ for (i = 0; i < MAX_LOG_NUMS; i++) {
+ index = (nand_logs_root->log_index + i) % MAX_LOG_NUMS;
+
+ logs = &nand_logs_root->logs[index];
+
+ seq_printf(m, "%02d:%02d:%02d.%04d %c %-2u 0x%08lX-%04lX",
+ logs->hour, logs->min,
+ logs->sec, logs->msec, logs->ops,
+ logs->addr_cycle, logs->page, logs->offset);
+
+ if (logs->ops == 'E')
+ seq_printf(m, " ---");
+ else {
+ int ix;
+ seq_printf(m, " %c ", logs->error);
+
+ for (ix = 0; ix < logs->data_len; ix++) {
+ if ((ix % 16) == 15)
+ seq_printf(m, "%02X-", logs->data[ix]);
+ else
+ seq_printf(m, "%02X ", logs->data[ix]);
+ }
+ }
+ seq_printf(m, "\n");
+ }
+
+ return 0;
+}
+/*
+ * echo "offset=2048 length=8" > /sys/kernel/debug/hs_nfc/nand_logs
+ */
+static ssize_t nand_log_config(struct file *file, const char __user *userbuf,
+ size_t len, loff_t *ppos)
+{
+#define LEN 50
+ char tmpbuf[LEN], *s, *r, *tok;
+ int ret;
+ unsigned long value = 0;
+
+ if (len == 0)
+ return 0;
+ if (len >= LEN)
+ len = LEN - 1;
+
+ if (copy_from_user(tmpbuf, userbuf, len))
+ return -EFAULT;
+
+ tmpbuf[len] = '\0';
+
+ tok = "offset=";
+ s = strstr(&tmpbuf[0], tok);
+ if (s) {
+ r = strsep(&s, " ");
+ r += strlen(tok);
+ ret = kstrtoul(r, 0, &value);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (value > nand_logs_root->page_oob_size)
+ value = nand_logs_root->page_oob_size;
+
+ nand_logs_root->data_offset = value;
+
+ tok = "length=";
+ s = strstr(s, tok);
+ if (s) {
+ s += strlen(tok);
+
+ ret = kstrtoul(s, 0, &value);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (value > MAX_LOG_DATA_LEN)
+ value = MAX_LOG_DATA_LEN;
+
+ nand_logs_root->data_len = value;
+ }
+ }
+
+ *ppos += len;
+ return len;
+}
+
+static int nand_log_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, nand_log_show, inode->i_private);
+}
+
+static const struct file_operations fops_nand_logs = {
+ .owner = THIS_MODULE,
+ .open = nand_log_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = nand_log_config,
+ .release = single_release,
+};
+
+int nand_log_init(struct hinfc_host *host)
+{
+ struct dentry *dir, *file = NULL;
+
+ dir = debugfs_create_dir("hs_nfc", NULL);
+ if (dir)
+ file = debugfs_create_file("nand_logs", 0644, dir, host,
+ &fops_nand_logs);
+
+ if (!file)
+ goto err_state;
+
+ nand_logs_root = kzalloc(sizeof(*nand_logs_root), GFP_KERNEL);
+ if (!nand_logs_root) {
+ pr_err("mem alloc failed!\n");
+ goto err_state;
+ }
+
+ nand_logs_root->entry = dir;
+ nand_logs_root->page_oob_size = host->pagesize + host->oobsize;
+ nand_logs_root->data_len = 8;/* default data len 8 */
+
+ pr_info("nand log enable\n");
+ return 0;
+
+err_state:
+ debugfs_remove_recursive(dir);
+ nand_logs_root = NULL;
+ return -ENODEV;
+}
+
+void nand_log_exit(void)
+{
+ if (!nand_logs_root)
+ return;
+
+ debugfs_remove_recursive(nand_logs_root->entry);
+ kfree(nand_logs_root);
+}
diff --git a/drivers/mtd/nand/hs_nfc/hinfc504.h b/drivers/mtd/nand/hs_nfc/hinfc504.h
new file mode 100644
index 00000000000..43037cc25c7
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc504.h
@@ -0,0 +1,299 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 HINFCV504H
+#define HINFCV504H
+/******************************************************************************/
+#include <linux/interrupt.h>
+#include "hinfc_common.h"
+
+/* see comments in this driver's probe func */
+#define CONFIG_HINFC504_MAX_CHIP (2)
+
+/* TODO: nfc default timing value, move it to dts file if needed */
+#define CONFIG_HINFC504_W_LATCH (5)
+#define CONFIG_HINFC504_R_LATCH (7)
+#define CONFIG_HINFC504_RW_LATCH (3)
+
+#define HINFC504_NFC_TIMEOUT (2 * HZ)
+#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ)
+#define HINFC504_CHIP_DELAY (25)
+
+/*****************************************************************************/
+#define HINFC504_REG_BASE_ADDRESS_LEN (0x100)
+#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128)
+
+#define HINFC504_ADDR_CYCLE_MASK 0x4
+#define HINFC504_DMA_ADDR_OFFSET 4096
+/*****************************************************************************/
+#define HINFC504_CON 0x00
+#define HINFC600_CON 0x00
+#define HINFC504_CON_OP_MODE_NORMAL (1U << 0)
+#define HINFC504_CON_PAGEISZE_SHIFT (1)
+#define HINFC504_CON_PAGESIZE_MASK (0x07)
+#define HINFC504_CON_BUS_WIDTH (1U << 4)
+#define HINFC504_CON_READY_BUSY_SEL (1U << 8)
+#define HINFC504_CON_ECCTYPE_SHIFT (9)
+#define HINFC504_CON_ECCTYPE_MASK (0x07)
+#define HINFC600_CON_RANDOMIZER_EN (1<<13)
+/*****************************************************************************/
+#define HINFC504_PWIDTH 0x04
+#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
+ ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
+
+#define HINFC504_CMD 0x0C
+#define HINFC504_ADDRL 0x10
+#define HINFC504_ADDRH 0x14
+#define HINFC504_DATA_NUM 0x18
+
+#define HINFC504_OP 0x1C
+#define HINFC504_OP_READ_STATUS_EN (1U << 0)
+#define HINFC504_OP_READ_DATA_EN (1U << 1)
+#define HINFC504_OP_WAIT_READY_EN (1U << 2)
+#define HINFC504_OP_CMD2_EN (1U << 3)
+#define HINFC504_OP_WRITE_DATA_EN (1U << 4)
+#define HINFC504_OP_ADDR_EN (1U << 5)
+#define HINFC504_OP_CMD1_EN (1U << 6)
+#define HINFC504_OP_NF_CS_SHIFT (7)
+#define HINFC504_OP_NF_CS_MASK (3)
+#define HINFC504_OP_ADDR_CYCLE_SHIFT (9)
+#define HINFC504_OP_ADDR_CYCLE_MASK (7)
+
+#define HINFC504_STATUS 0x20
+#define HINFC504_READY (1U << 0)
+
+#define HINFC504_INTEN 0x24
+#define HINFC504_INTEN_DMA (1U << 9)
+
+#define HINFC504_INTS 0x28
+#define HINFC504_INTS_UE (1U << 6)
+#define HINFC504_INTS_DMA (1U << 9)
+
+#define HINFC504_INTCLR 0x2C
+#define HINFC504_INTCLR_CE (1U << 5)
+#define HINFC504_INTCLR_UE (1U << 6)
+#define HINFC504_INTCLR_DMA (1U << 9)
+
+#define HINFC504_DMA_CTRL 0x60
+#define HINFC504_DMA_CTRL_DMA_START (1U << 0)
+#define HINFC504_DMA_CTRL_WE (1U << 1)
+#define HINFC504_DMA_CTRL_DATA_AREA_EN (1U << 2)
+#define HINFC504_DMA_CTRL_OOB_AREA_EN (1U << 3)
+#define HINFC504_DMA_CTRL_BURST4_EN (1U << 4)
+#define HINFC504_DMA_CTRL_BURST8_EN (1U << 5)
+#define HINFC504_DMA_CTRL_BURST16_EN (1U << 6)
+#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7)
+#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1)
+#define HINFC504_DMA_CTRL_CS_SHIFT (8)
+#define HINFC504_DMA_CTRL_CS_MASK (0x03)
+
+#define HINFC504_DMA_ADDR_DATA 0x64
+#define HINFC504_DMA_ADDR_OOB 0x68
+#define HINFC504_DMA_ADDR_DATA1 0xB4
+#define HINFC504_DMA_ADDR_DATA2 0xB8
+#define HINFC504_DMA_ADDR_DATA3 0xBC
+
+#define HINFC504_DMA_LEN 0x6C
+#define HINFC504_DMA_LEN_OOB_SHIFT (16)
+#define HINFC504_DMA_LEN_OOB_MASK (0xFFF)
+
+#define HINFC504_DMA_PARA 0x70
+#define HINFC504_DMA_PARA_DATA_RW_EN (1U << 0)
+#define HINFC504_DMA_PARA_OOB_RW_EN (1U << 1)
+#define HINFC504_DMA_PARA_DATA_EDC_EN (1U << 2)
+#define HINFC504_DMA_PARA_OOB_EDC_EN (1U << 3)
+#define HINFC504_DMA_PARA_DATA_ECC_EN (1U << 4)
+#define HINFC504_DMA_PARA_OOB_ECC_EN (1U << 5)
+#define HINFC504_DMA_PARA_EXT_LEN_SHIFT (6)
+#define HINFC504_DMA_PARA_EXT_LEN_MASK (0x03)
+
+#define HINFC504_VERSION 0x74
+#define HINFC504_LOG_READ_ADDR 0x7C
+#define HINFC504_LOG_READ_LEN 0x80
+
+#define HINFC504_ECC_REG0 0xA0
+#define HINFC504_ECC_REG1 0xA4
+#define HINFC504_ECC_REG2 0xA8
+#define HINFC504_ECC_REG3 0xAC
+
+#define HINFC504_RANDOMIZER 0xC0
+#define HINFC504_RANDOMIZER_PAD 0x02
+#define HINFC504_RANDOMIZER_ENABLE 0x01
+/* read nand id or nand status, return from nand data length */
+#define HINFC504_NANDINFO_LEN 0x10
+
+#define HINFC600_BOOT_CFG 0xC4
+#define HINFC600_BOOT_CFG_RANDOMIZER_PAD 0x01
+
+/* DMA address align with 32 bytes. */
+#define HINFC504_DMA_ALIGN 64
+/*****************************************************************************/
+#define ENABLE 1
+#define DISABLE 0
+/*****************************************************************************/
+
+struct hinfc_host {
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ void __iomem *iobase;
+ struct completion cmd_complete;
+
+ int is_randomizer;
+ unsigned int offset;
+ unsigned int command;
+
+ int chipselect;
+
+ int ecctype;
+ unsigned int n24bit_ext_len;
+
+ unsigned int NFC_CON;
+ unsigned int NFC_CON_ECC_NONE;
+
+ unsigned int addr_cycle;
+ unsigned int addr_value[2];
+ unsigned int cache_addr_value[2];
+ unsigned int block_page_mask;
+
+ char *buffer;
+ dma_addr_t dma_buffer;
+ dma_addr_t dma_oob;
+
+ unsigned int pagesize;
+ unsigned int oobsize;
+
+ int version; /* controller version */
+
+ int need_rr_data;
+#define HINFC_READ_RETRY_DATA_LEN 128
+ char rr_data[HINFC_READ_RETRY_DATA_LEN];
+
+#define HINFC_BAD_BLOCK_POS 0
+ unsigned char *bbm; /* nand bad block mark */
+ unsigned short *epm; /* nand empty page mark */
+
+ unsigned int uc_er;
+
+ int (*send_cmd_pageprog)(struct hinfc_host *host);
+ int (*send_cmd_status)(struct hinfc_host *host);
+ int (*send_cmd_readstart)(struct hinfc_host *host);
+ int (*send_cmd_erase)(struct hinfc_host *host);
+ int (*send_cmd_readid)(struct hinfc_host *host);
+ int (*send_cmd_reset)(struct hinfc_host *host, int chipselect);
+
+ int (*enable_ecc_randomizer)(struct hinfc_host *host, int ecc_en,
+ int randomizer_en);
+
+ void (*diagnose_ecc)(struct hinfc_host *host);
+
+ struct read_retry_t *read_retry;
+};
+/*****************************************************************************/
+#define HINFC504_READ_1CMD_0ADD_NODATA \
+ (HINFC504_OP_CMD1_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT))
+
+#define HINFC504_READ_1CMD_1ADD_DATA \
+ (HINFC504_OP_CMD1_EN \
+ | HINFC504_OP_ADDR_EN \
+ | HINFC504_OP_READ_DATA_EN \
+ | HINFC504_OP_WAIT_READY_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT) \
+ | (1 << HINFC504_OP_ADDR_CYCLE_SHIFT))
+
+#define HINFC504_READ_2CMD_5ADD \
+ (HINFC504_OP_CMD1_EN \
+ | HINFC504_OP_CMD2_EN \
+ | HINFC504_OP_ADDR_EN \
+ | HINFC504_OP_READ_DATA_EN \
+ | HINFC504_OP_WAIT_READY_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT) \
+ | (5 << HINFC504_OP_ADDR_CYCLE_SHIFT))
+
+#define HINFC504_WRITE_0CMD_1ADD_DATA \
+ (HINFC504_OP_ADDR_EN \
+ | HINFC504_OP_WRITE_DATA_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT) \
+ | (1 << HINFC504_OP_ADDR_CYCLE_SHIFT))
+
+#define HINFC504_WRITE_1CMD_1ADD_DATA \
+ (HINFC504_OP_CMD1_EN \
+ | HINFC504_OP_ADDR_EN \
+ | HINFC504_OP_WRITE_DATA_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT) \
+ | (1 << HINFC504_OP_ADDR_CYCLE_SHIFT))
+
+#define HINFC504_WRITE_1CMD_2ADD_DATA \
+ (HINFC504_OP_CMD1_EN \
+ | HINFC504_OP_ADDR_EN \
+ | HINFC504_OP_WRITE_DATA_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT) \
+ | (2 << HINFC504_OP_ADDR_CYCLE_SHIFT))
+
+#define HINFC504_WRITE_2CMD_0ADD_NODATA \
+ (HINFC504_OP_CMD1_EN \
+ | HINFC504_OP_CMD2_EN \
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) \
+ << HINFC504_OP_NF_CS_SHIFT))
+
+/*****************************************************************************/
+#define hinfc_read(_host, _reg) \
+ readl(_host->iobase + (_reg))
+
+#define hinfc_write(_host, _value, _reg) \
+ writel((_value), _host->iobase + (_reg))
+
+#define HINFC_CMD_SEQ(_cmd0, _cmd1) \
+ (((_cmd0) & 0xFF) | ((_cmd1) & 0xFF) << 8)
+/*****************************************************************************/
+void wait_controller_finished(struct hinfc_host *host);
+void hinfc504_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
+ int page_addr);
+int hinfc504_dev_ready(struct mtd_info *mtd);
+void hinfc504_select_chip(struct mtd_info *mtd, int chipselect);
+uint8_t hinfc504_read_byte(struct mtd_info *mtd);
+u16 hinfc504_read_word(struct mtd_info *mtd);
+void hinfc504_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
+void hinfc504_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
+void hinfc504_host_init(struct hinfc_host *host);
+int hinfc504_ecc_probe(struct mtd_info *mtd, struct nand_chip *chip,
+ struct nand_flash_dev_ex *flash_dev_ex);
+irqreturn_t hinfc_irq_handle(int irq, void *devid);
+
+#ifdef CONFIG_MTD_NAND_HINFC504_DEBUG
+int nand_log_init(struct hinfc_host *host);
+void nand_log_exit(void);
+void nand_log_ops(struct hinfc_host *host, char ops);
+#else
+static inline int nand_log_init(struct hinfc_host *host) { return 0; }
+static inline void nand_log_ops(struct hinfc_host *host, char ops) {};
+static inline void nand_log_exit(void) {};
+#endif
+/******************************************************************************/
+#endif /* HINFCV504H */
diff --git a/drivers/mtd/nand/hs_nfc/hinfc504_hw.c b/drivers/mtd/nand/hs_nfc/hinfc504_hw.c
new file mode 100644
index 00000000000..d1238f15050
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc504_hw.c
@@ -0,0 +1,845 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include "hinfc504.h"
+
+/*****************************************************************************/
+void wait_controller_finished(struct hinfc_host *host)
+{
+ unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
+ int val;
+
+ while (time_before(jiffies, timeout)) {
+ val = hinfc_read(host, HINFC504_STATUS);
+ if (val & HINFC504_READY)
+ return;/* nfc is ready */
+ }
+
+ /* wait cmd timeout */
+ pr_err("Wait NAND controller exec cmd timeout.\n");
+}
+
+static void hinfc504_dma_transfer(struct hinfc_host *host, int todev)
+{
+ unsigned int dma_addr = (unsigned int)host->dma_buffer;
+ unsigned long val;
+ int ret;
+
+ hinfc_write(host, dma_addr, HINFC504_DMA_ADDR_DATA);
+
+ dma_addr += HINFC504_DMA_ADDR_OFFSET;
+ hinfc_write(host, dma_addr, HINFC504_DMA_ADDR_DATA1);
+
+ dma_addr += HINFC504_DMA_ADDR_OFFSET;
+ hinfc_write(host, dma_addr, HINFC504_DMA_ADDR_DATA2);
+
+ dma_addr += HINFC504_DMA_ADDR_OFFSET;
+ hinfc_write(host, dma_addr, HINFC504_DMA_ADDR_DATA3);
+
+ hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
+
+ if (host->ecctype == et_ecc_none) {
+ hinfc_write(host,
+ ((host->oobsize & HINFC504_DMA_LEN_OOB_MASK)
+ << HINFC504_DMA_LEN_OOB_SHIFT),
+ HINFC504_DMA_LEN);
+
+ hinfc_write(host,
+ HINFC504_DMA_PARA_DATA_RW_EN
+ | HINFC504_DMA_PARA_OOB_RW_EN
+ | ((host->n24bit_ext_len
+ & HINFC504_DMA_PARA_EXT_LEN_MASK)
+ << HINFC504_DMA_PARA_EXT_LEN_SHIFT),
+ HINFC504_DMA_PARA);
+ } else
+ hinfc_write(host,
+ HINFC504_DMA_PARA_DATA_RW_EN
+ | HINFC504_DMA_PARA_OOB_RW_EN
+ | HINFC504_DMA_PARA_DATA_EDC_EN
+ | HINFC504_DMA_PARA_OOB_EDC_EN
+ | HINFC504_DMA_PARA_DATA_ECC_EN
+ | HINFC504_DMA_PARA_OOB_ECC_EN
+ | ((host->n24bit_ext_len
+ & HINFC504_DMA_PARA_EXT_LEN_MASK)
+ << HINFC504_DMA_PARA_EXT_LEN_SHIFT),
+ HINFC504_DMA_PARA);
+
+ val = (HINFC504_DMA_CTRL_DMA_START
+ | HINFC504_DMA_CTRL_BURST4_EN
+ | HINFC504_DMA_CTRL_BURST8_EN
+ | HINFC504_DMA_CTRL_BURST16_EN
+ | HINFC504_DMA_CTRL_DATA_AREA_EN
+ | HINFC504_DMA_CTRL_OOB_AREA_EN
+ | ((host->addr_cycle == 4 ? 1 : 0)
+ << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
+ | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
+ << HINFC504_DMA_CTRL_CS_SHIFT));
+
+ if (todev)
+ val |= HINFC504_DMA_CTRL_WE;
+
+ hinfc_write(host, val, HINFC504_DMA_CTRL);
+
+ init_completion(&host->cmd_complete);
+ ret = wait_for_completion_timeout(&host->cmd_complete,
+ HINFC504_NFC_DMA_TIMEOUT);
+ if (!ret) {
+ pr_err("DMA operation(irq) timeout!\n");
+ /* sanity check */
+ val = hinfc_read(host, HINFC504_DMA_CTRL);
+ if (!(val & HINFC504_DMA_CTRL_DMA_START))
+ pr_err("dma is already done but without irq ACK!");
+ else
+ pr_err("dma is really timeout!");
+ }
+}
+/*****************************************************************************/
+static int hinfc504_send_cmd_pageprog(struct hinfc_host *host)
+{
+
+ WARN_ON(host->addr_cycle != 4 && host->addr_cycle != 5);
+ host->addr_value[0] &= 0xffff0000;
+
+ host->enable_ecc_randomizer(host, ENABLE, ENABLE);
+
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+ hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
+ HINFC504_CMD);
+
+ if (host->is_randomizer)
+ *host->epm = 0xFFFF;
+
+ hinfc504_dma_transfer(host, 1);
+
+ return 0;
+}
+/*****************************************************************************/
+#define NAND_BAD_BLOCK 1
+#define NAND_EMPTY_PAGE 2
+#define NAND_VALID_DATA 3
+
+static int hinfc504_get_data_status(struct hinfc_host *host)
+{
+ /* this is block start address */
+ if (!((host->addr_value[0] >> 16) & host->block_page_mask)) {
+
+ /* it is a bad block */
+ if (*host->bbm == 0)
+ return NAND_BAD_BLOCK;
+ /*
+ * if there are more than 2 bits flipping, it is
+ * maybe a bad block
+ */
+ if (host->uc_er && *host->bbm != 0xFFFF
+ && hweight8(*host->bbm) <= 6)
+ return NAND_BAD_BLOCK;
+ }
+
+ /* it is an empty page */
+ if (*host->epm != 0xFFFF && host->is_randomizer)
+ return NAND_EMPTY_PAGE;
+
+ return NAND_VALID_DATA;
+}
+/*****************************************************************************/
+
+static int hinfc504_do_read_retry(struct hinfc_host *host)
+{
+ int ix;
+
+ for (ix = 1; host->uc_er && ix < host->read_retry->count; ix++) {
+
+ hinfc_write(host, HINFC504_INTCLR_UE | HINFC504_INTCLR_CE,
+ HINFC504_INTCLR);
+
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+
+ /* check whether it is toshiba chip */
+ if (host->read_retry->type == NAND_RR_TOSHIBA_24nm) {
+
+ hinfc_write(host, HINFC_CMD_SEQ(0x5C, 0xC5),
+ HINFC504_CMD);
+ hinfc_write(host,
+ HINFC504_WRITE_2CMD_0ADD_NODATA,
+ HINFC504_OP);
+ wait_controller_finished(host);
+ }
+
+ host->read_retry->set_rr_param(host, ix);
+
+ /* check whether it is toshiba chip */
+ if (host->read_retry->type == NAND_RR_TOSHIBA_24nm) {
+ hinfc_write(host, HINFC_CMD_SEQ(0x26, 0x5D),
+ HINFC504_CMD);
+ hinfc_write(host,
+ HINFC504_WRITE_2CMD_0ADD_NODATA,
+ HINFC504_OP);
+ wait_controller_finished(host);
+ }
+
+ host->enable_ecc_randomizer(host, DISABLE, ENABLE);
+
+ hinfc_write(host, HINFC504_INTCLR_UE | HINFC504_INTCLR_CE,
+ HINFC504_INTCLR);
+ hinfc_write(host, host->NFC_CON, HINFC504_CON);
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+ hinfc_write(host,
+ HINFC_CMD_SEQ(NAND_CMD_READ0, NAND_CMD_READSTART),
+ HINFC504_CMD);
+
+ hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
+ hinfc_write(host, (host->pagesize + host->oobsize),
+ HINFC504_LOG_READ_LEN);
+
+ hinfc504_dma_transfer(host, 0);
+ host->uc_er = (hinfc_read(host, HINFC504_INTS)
+ & HINFC504_INTS_UE) ? 1 : 0;
+
+ if (host->uc_er && host->diagnose_ecc)
+ host->diagnose_ecc(host);
+ }
+
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+
+ host->read_retry->set_rr_param(host, 0);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_send_cmd_readstart(struct hinfc_host *host)
+{
+ if ((host->addr_value[0] == host->cache_addr_value[0])
+ && (host->addr_value[1] == host->cache_addr_value[1]))
+ return 0;
+
+ WARN_ON(host->addr_cycle != 4 && host->addr_cycle != 5);
+ host->addr_value[0] &= 0xffff0000;
+
+ host->enable_ecc_randomizer(host, ENABLE, ENABLE);
+
+ hinfc_write(host, HINFC504_INTCLR_UE | HINFC504_INTCLR_CE,
+ HINFC504_INTCLR);
+ hinfc_write(host, host->NFC_CON, HINFC504_CON);
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+ hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
+ HINFC504_CMD);
+
+ hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
+ hinfc_write(host, host->pagesize + host->oobsize,
+ HINFC504_LOG_READ_LEN);
+
+ hinfc504_dma_transfer(host, 0);
+ host->uc_er = !!(hinfc_read(host, HINFC504_INTS) & HINFC504_INTS_UE);
+
+ if (host->read_retry || host->is_randomizer) {
+ int status = hinfc504_get_data_status(host);
+
+ if (status == NAND_EMPTY_PAGE) {
+ /*
+ * oob area used by yaffs2 only 32 bytes,
+ * so we only fill 32 bytes.
+ */
+ if (host->is_randomizer)
+ memset(host->buffer, 0xFF,
+ host->pagesize + host->oobsize);
+ host->uc_er = 2;
+
+ } else if (status == NAND_VALID_DATA) {
+
+ /* if NAND chip support read retry */
+ if (host->uc_er && host->read_retry)
+ hinfc504_do_read_retry(host);
+
+ } /* 'else' NAND have a bad block, do nothing. */
+ }
+
+ host->cache_addr_value[0] = host->addr_value[0];
+ host->cache_addr_value[1] = host->addr_value[1];
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_send_cmd_erase(struct hinfc_host *host)
+{
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
+ HINFC504_CMD);
+
+ hinfc_write(host, HINFC504_OP_WAIT_READY_EN
+ | HINFC504_OP_CMD2_EN
+ | HINFC504_OP_CMD1_EN
+ | HINFC504_OP_ADDR_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
+ << HINFC504_OP_ADDR_CYCLE_SHIFT),
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_send_cmd_readid(struct hinfc_host *host)
+{
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+
+ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+ hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
+ hinfc_write(host, 0, HINFC504_ADDRL);
+
+ /* does not affect R/B line */
+ hinfc_write(host, HINFC504_OP_CMD1_EN
+ | HINFC504_OP_ADDR_EN
+ | HINFC504_OP_READ_DATA_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT,
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_enable_ecc_randomizer(struct hinfc_host *host,
+ int ecc_en, int randomizer_en)
+{
+ unsigned int regval;
+
+ if (host->is_randomizer) {
+
+ regval = hinfc_read(host, HINFC504_RANDOMIZER);
+ if (randomizer_en)
+ regval |= HINFC504_RANDOMIZER_ENABLE;
+ else
+ regval &= ~HINFC504_RANDOMIZER_ENABLE;
+ hinfc_write(host, regval, HINFC504_RANDOMIZER);
+ }
+
+ if (ecc_en)
+ hinfc_write(host, host->NFC_CON, HINFC504_CON);
+ else
+ hinfc_write(host, host->NFC_CON_ECC_NONE, HINFC504_CON);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc600_enable_ecc_randomizer(struct hinfc_host *host,
+ int ecc_en, int randomizer_en)
+{
+ unsigned int nfc_con = ecc_en ?
+ host->NFC_CON : host->NFC_CON_ECC_NONE;
+
+ if (host->is_randomizer) {
+ if (randomizer_en)
+ nfc_con |= HINFC600_CON_RANDOMIZER_EN;
+ else
+ nfc_con &= ~HINFC600_CON_RANDOMIZER_EN;
+ }
+
+ hinfc_write(host, nfc_con, HINFC600_CON);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_send_cmd_status(struct hinfc_host *host)
+{
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+
+ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+ hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
+ /* no need to set HINFC504_OP_WAIT_READY_EN,
+ * cause READ STATUS CMD do not affect or depend on the R/B line
+ */
+ hinfc_write(host, HINFC504_OP_CMD1_EN
+ | HINFC504_OP_READ_DATA_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT),
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_send_cmd_reset(struct hinfc_host *host, int chipselect)
+{
+ hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
+
+ hinfc_write(host, HINFC504_OP_CMD1_EN
+ | ((chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | HINFC504_OP_WAIT_READY_EN,
+ HINFC504_OP);
+
+ /* tRST <= 500us */
+ wait_controller_finished(host);
+
+ return 0;
+}
+/*****************************************************************************/
+/*
+ * nfc will monitor the R/B line
+ */
+int hinfc504_dev_ready(struct mtd_info *mtd)
+{
+ return 0x1;
+}
+/*****************************************************************************/
+
+void hinfc504_select_chip(struct mtd_info *mtd, int chipselect)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ if (chipselect < 0)
+ return;
+
+ BUG_ON(chipselect > CONFIG_HINFC504_MAX_CHIP);
+
+ host->chipselect = chipselect;
+}
+/*****************************************************************************/
+
+uint8_t hinfc504_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ if (host->command == NAND_CMD_STATUS)
+ return readb(chip->IO_ADDR_R);
+
+ host->offset++;
+
+ if (host->command == NAND_CMD_READID)
+ return readb(chip->IO_ADDR_R + host->offset - 1);
+
+ return readb(host->buffer + host->offset - 1);
+}
+/*****************************************************************************/
+
+u16 hinfc504_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ host->offset += 2;
+ return readw(host->buffer + host->offset - 2);
+}
+/*****************************************************************************/
+
+void hinfc504_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ memcpy(host->buffer + host->offset, buf, len);
+ host->offset += len;
+}
+/*****************************************************************************/
+
+void hinfc504_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ memcpy(buf, host->buffer + host->offset, len);
+ host->offset += len;
+}
+
+static void set_addr(struct mtd_info *mtd, int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+
+ host->addr_cycle = 0;
+ host->addr_value[0] = 0;
+ host->addr_value[1] = 0;
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+
+ host->addr_value[0] = column & 0xffff;
+ host->addr_cycle = 2;/* FIXME */
+ }
+ if (page_addr != -1) {
+ host->addr_value[0] |= (page_addr & 0xffff)
+ << (host->addr_cycle * 8);
+ host->addr_cycle += 2;
+ /* One more address cycle for devices > 128MiB */
+ if (chip->chipsize > (128 << 20)) {
+ host->addr_cycle += 1;
+ host->addr_value[1] |= (page_addr >> 16) & 0xff;
+ }
+ }
+}
+
+void hinfc504_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
+ int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hinfc_host *host = chip->priv;
+ int is_cache_invalid = 1;
+
+ host->command = command;
+
+ switch (command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ if (command == NAND_CMD_READ0)
+ host->offset = column;
+ else
+ host->offset = column + mtd->writesize;
+
+ is_cache_invalid = 0;
+ set_addr(mtd, column, page_addr);
+ host->send_cmd_readstart(host);
+ host->uc_er = 0;
+ nand_log_ops(host, 'R');
+ break;
+
+ case NAND_CMD_SEQIN:
+ host->offset = column;
+
+ case NAND_CMD_ERASE1:
+ set_addr(mtd, column, page_addr);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ host->send_cmd_pageprog(host);
+ nand_log_ops(host, 'W');
+ break;
+
+ case NAND_CMD_ERASE2:
+ host->send_cmd_erase(host);
+ nand_log_ops(host, 'E');
+ break;
+
+ case NAND_CMD_READID:
+ host->offset = column;
+ memset(chip->IO_ADDR_R, 0, 0x10);
+ host->send_cmd_readid(host);
+ break;
+
+ case NAND_CMD_STATUS:
+ host->offset = 0;
+ memset(chip->IO_ADDR_R, 0, 0x10);
+ host->send_cmd_status(host);
+ break;
+
+ case NAND_CMD_RESET:
+ host->send_cmd_reset(host, host->chipselect);
+ break;
+
+ default:
+ pr_err("Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
+ command, column, page_addr);
+ }
+
+ if (is_cache_invalid) {
+ host->cache_addr_value[0] = ~0;
+ host->cache_addr_value[1] = ~0;
+ }
+}
+
+/*****************************************************************************/
+static struct nand_ecclayout nand_ecc_default = {
+ .oobfree = { {2, 30} }
+};
+
+static struct nand_ecclayout nand_ecc_2K_1bit = {
+ .oobfree = { {22, 30} }
+};
+/*****************************************************************************/
+
+static struct page_page_ecc_info page_page_ecc_info[] = {
+ {pt_pagesize_16K, et_ecc_40bit1k, 1200/*1152*/, &nand_ecc_default},
+ {pt_pagesize_8K, et_ecc_40bit1k, 600/*592*/, &nand_ecc_default},
+ {pt_pagesize_8K, et_ecc_24bit1k, 368, &nand_ecc_default},
+ {pt_pagesize_8K, et_ecc_none, 32, &nand_ecc_default},
+
+ {pt_pagesize_4K, et_ecc_24bit1k, 200, &nand_ecc_default},
+ {pt_pagesize_4K, et_ecc_4bit, 128/*104*/, &nand_ecc_default},
+ {pt_pagesize_4K, et_ecc_1bit, 128, &nand_ecc_default},
+ {pt_pagesize_4K, et_ecc_none, 32, &nand_ecc_default},
+
+ {pt_pagesize_2K, et_ecc_24bit1k, 128/*116*/, &nand_ecc_default},
+ {pt_pagesize_2K, et_ecc_4bit, 64, &nand_ecc_default},
+ {pt_pagesize_2K, et_ecc_1bit, 64, &nand_ecc_2K_1bit},
+ {pt_pagesize_2K, et_ecc_none, 32, &nand_ecc_default},
+
+ {},
+};
+/*****************************************************************************/
+struct page_page_ecc_info *hinfc504_select_ecc(struct mtd_info *mtd,
+ enum page_type pagetype, enum ecc_type ecctype,
+ char *cfgmsg, int allow_pagediv)
+{
+ struct page_page_ecc_info *info = page_page_ecc_info, *fit = NULL;
+ int pagesize;
+
+ for (; info->layout; info++) {
+ if (info->pagetype == pagetype && info->ecctype == ecctype) {
+ fit = info;
+ break;
+ }
+ }
+
+ if (!fit) {
+ pr_err("Driver(%s mode) does not support this Nand Flash\n"
+ "pagesize:%s, ecctype:%s\n",
+ cfgmsg,
+ get_pagesize_str(pagetype),
+ get_ecctype_str(ecctype));
+ return NULL;
+ }
+
+ /* TODO: add comments here */
+ pagesize = get_pagesize(pagetype);
+ if ((pagesize != mtd->writesize) &&
+ (pagesize > mtd->writesize || !allow_pagediv)) {
+ pr_err("Hardware (%s mode) configure pagesize is %d\n"
+ "But the Nand Flash pageszie is %d\n",
+ cfgmsg, pagesize, mtd->writesize);
+ return NULL;
+ }
+
+ if (fit->oobsize > mtd->oobsize) {
+ pr_err("(%s mode)the oob size of the nand flash is %d bytes\n"
+ "but the controller request %d bytes for ecc %s\n",
+ cfgmsg, mtd->oobsize, fit->oobsize,
+ get_ecctype_str(ecctype));
+ return NULL;
+ }
+
+ return fit;
+}
+/*****************************************************************************/
+
+int hinfc504_ecc_probe(struct mtd_info *mtd, struct nand_chip *chip,
+ struct nand_flash_dev_ex *flash_dev_ex)
+{
+ struct page_page_ecc_info *best = NULL;
+ struct hinfc_host *host = chip->priv;
+
+ best = hinfc504_select_ecc(mtd,
+ ((host->NFC_CON >> HINFC504_CON_PAGEISZE_SHIFT)
+ & HINFC504_CON_PAGESIZE_MASK),
+ ((host->NFC_CON >> HINFC504_CON_ECCTYPE_SHIFT)
+ & HINFC504_CON_ECCTYPE_MASK),
+ "hardware config", 0);
+
+ BUG_ON(!best);
+
+ if (best->ecctype != et_ecc_none)
+ mtd->oobsize = best->oobsize;
+
+ chip->ecc.layout = best->layout;
+
+ host->ecctype = best->ecctype;
+ host->pagesize = get_pagesize(best->pagetype);
+ host->oobsize = mtd->oobsize;
+ host->block_page_mask = (mtd->erasesize / mtd->writesize) - 1;
+
+ if (host->ecctype == et_ecc_24bit1k) {
+ if (host->pagesize == SZ_4K)
+ host->n24bit_ext_len = 0x03; /* 8bytes; */
+ else if (host->pagesize == SZ_8K)
+ host->n24bit_ext_len = 0x01; /* 4bytes; */
+ }
+ host->dma_oob = host->dma_buffer + host->pagesize;
+ host->bbm = (unsigned char *)(host->dma_oob + HINFC_BAD_BLOCK_POS);
+
+ host->epm = (unsigned short *)(host->dma_oob
+ + chip->ecc.layout->oobfree[0].offset + 28);
+
+ host->NFC_CON = HINFC504_CON_OP_MODE_NORMAL
+ | ((best->pagetype & HINFC504_CON_PAGESIZE_MASK)
+ << HINFC504_CON_PAGEISZE_SHIFT)
+ | HINFC504_CON_READY_BUSY_SEL
+ | ((best->ecctype & HINFC504_CON_ECCTYPE_MASK)
+ << HINFC504_CON_ECCTYPE_SHIFT);
+
+ host->NFC_CON_ECC_NONE = HINFC504_CON_OP_MODE_NORMAL
+ | ((best->pagetype & HINFC504_CON_PAGESIZE_MASK)
+ << HINFC504_CON_PAGEISZE_SHIFT)
+ | HINFC504_CON_READY_BUSY_SEL;
+
+ if (mtd->writesize > NAND_MAX_PAGESIZE
+ || mtd->oobsize > NAND_MAX_OOBSIZE) {
+ pr_err("Driver does not support this Nand Flash.\n"
+ "Please check NAND_MAX_PAGESIZE and NAND_MAX_OOBSIZE\n");
+ }
+
+ if (mtd->writesize != host->pagesize) {
+ unsigned int shift = 0;
+ unsigned int writesize = mtd->writesize;
+ while (writesize > host->pagesize) {
+ writesize >>= 1;
+ shift++;
+ }
+ chip->chipsize = chip->chipsize >> shift;
+ mtd->erasesize = mtd->erasesize >> shift;
+ mtd->writesize = host->pagesize;
+ pr_info("Nand divide into 1/%u\n", (1 << shift));
+ }
+
+ flash_dev_ex->ecctype = host->ecctype;
+ flash_dev_ex->is_randomizer = host->is_randomizer;
+
+ if (flash_dev_ex->read_retry_type != NAND_RR_NONE) {
+ struct read_retry_t *rr = read_retry_list;
+ for ( ; rr->type != NAND_RR_NONE; rr++) {
+ if (rr->type == flash_dev_ex->read_retry_type) {
+ host->read_retry = rr;
+ break;
+ }
+ }
+
+ if (!host->read_retry)
+ pr_err("Unsupported 'read retry' type: %d\n",
+ flash_dev_ex->read_retry_type);
+ }
+
+ /*
+ * If it want to support the 'read retry' feature,
+ * the 'randomizer' feature must be enabed first.
+ */
+ if (host->read_retry && !host->is_randomizer) {
+ pr_err("This Nand flash need to enable 'randomizer' feature.\n"
+ "Please configure hardware randomizer PIN.");
+ }
+
+ return 0;
+}
+/*****************************************************************************/
+
+static int hinfc504_get_randomizer(struct hinfc_host *host)
+{
+ int val = hinfc_read(host, HINFC504_RANDOMIZER);
+
+ host->is_randomizer = !!(val & HINFC504_RANDOMIZER_PAD);
+
+ val |= (host->is_randomizer ? HINFC504_RANDOMIZER_ENABLE : 0);
+ hinfc_write(host, val, HINFC504_RANDOMIZER);
+
+ return host->is_randomizer;
+}
+/*****************************************************************************/
+
+static int hinfc600_get_randomizer(struct hinfc_host *host)
+{
+ int val = hinfc_read(host, HINFC600_BOOT_CFG);
+ host->is_randomizer = !!(val & HINFC600_BOOT_CFG_RANDOMIZER_PAD);
+
+ return host->is_randomizer;
+}
+/*****************************************************************************/
+
+void hinfc504_diagnose_ecc(struct hinfc_host *host)
+{
+ /* add debug code here */
+}
+/*****************************************************************************/
+irqreturn_t hinfc_irq_handle(int irq, void *devid)
+{
+ struct hinfc_host *host = devid;
+ unsigned int status;
+
+ status = hinfc_read(host, HINFC504_INTS);
+
+ if (status & HINFC504_INTS_DMA) {
+ /* clear DMA irq */
+ hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
+ complete(&host->cmd_complete);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void hinfc504_host_init(struct hinfc_host *host)
+{
+ host->version = hinfc_read(host, HINFC504_VERSION);
+
+ host->addr_cycle = 0;
+ host->addr_value[0] = 0;
+ host->addr_value[1] = 0;
+ host->cache_addr_value[0] = ~0;
+ host->cache_addr_value[1] = ~0;
+ host->chipselect = 0;
+
+ host->send_cmd_pageprog = hinfc504_send_cmd_pageprog;
+ host->send_cmd_readstart = hinfc504_send_cmd_readstart;
+ host->send_cmd_erase = hinfc504_send_cmd_erase;
+ host->send_cmd_readid = hinfc504_send_cmd_readid;
+ host->send_cmd_status = hinfc504_send_cmd_status;
+ host->send_cmd_reset = hinfc504_send_cmd_reset;
+ host->diagnose_ecc = hinfc504_diagnose_ecc;
+
+ host->NFC_CON = hinfc_read(host, HINFC504_CON)
+ | HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL;
+
+ host->NFC_CON_ECC_NONE = host->NFC_CON
+ & (~(HINFC504_CON_ECCTYPE_MASK << HINFC504_CON_ECCTYPE_SHIFT))
+ & (~HINFC600_CON_RANDOMIZER_EN);
+
+ memset(host->chip->IO_ADDR_R, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
+ memset(host->buffer, 0xff, NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE);
+
+ hinfc_write(host,
+ SET_HINFC504_PWIDTH(CONFIG_HINFC504_W_LATCH,
+ CONFIG_HINFC504_R_LATCH, CONFIG_HINFC504_RW_LATCH),
+ HINFC504_PWIDTH);
+
+ if (HINFC_VER_600 == host->version) {
+ hinfc600_get_randomizer(host);
+ host->enable_ecc_randomizer = hinfc600_enable_ecc_randomizer;
+ } else {
+ hinfc504_get_randomizer(host);
+ host->enable_ecc_randomizer = hinfc504_enable_ecc_randomizer;
+ }
+
+ /* enable dma irq */
+ hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
+}
diff --git a/drivers/mtd/nand/hs_nfc/hinfc504_os.c b/drivers/mtd/nand/hs_nfc/hinfc504_os.c
new file mode 100644
index 00000000000..555e53d7dff
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc504_os.c
@@ -0,0 +1,339 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include "hinfc504.h"
+
+/*****************************************************************************/
+#define DRIVER_NAME "hinand"
+
+/*****************************************************************************/
+static int hinfc504_os_version_check(struct hinfc_host *host)
+{
+ int val = readl(host->iobase + HINFC504_VERSION);
+ int support;
+
+ if (val == HINFC_VER_600 || val == HINFC_VER_505)
+ support = true;
+ else
+ support = false;
+
+ pr_info("Nand Flash Controller Version: 0x%x. %sSupported\n",
+ val, support ? "" : "Not ");
+
+ return support;
+}
+#ifdef CONFIG_OF
+static int hinfc504_probe_dt(struct platform_device *pdev,
+ struct hinfc_host *host)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ of_property_read_u8_array(np, "hisilicon,nand-read-retry",
+ (u8 *)&host->rr_data, HINFC_READ_RETRY_DATA_LEN);
+
+ return 0;
+}
+#else
+static int hinfc504_probe_dt(struct platform_device *pdev,
+ struct hinfc_host *host)
+{
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * there are several chipselects in the nfc controller, that means
+ * each of the cs can be connected with the different chip, but
+ * the driver does not support this scenario. However the driver
+ * support each of cs hook with the same kind of nand chip.
+ */
+static int hinfc504_os_probe(struct platform_device *pdev)
+{
+ int i, ret, irq, max_chips = CONFIG_HINFC504_MAX_CHIP;
+ struct device *dev = &pdev->dev;
+ struct hinfc_host *host;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ struct resource *res;
+ struct nand_flash_dev_ex flash_dev_ex;
+ struct nand_flash_dev *def;
+
+ memset(&flash_dev_ex, 0, sizeof(flash_dev_ex));
+
+ host = kzalloc(sizeof(*host) + sizeof(*chip) + sizeof(*mtd),
+ GFP_KERNEL);
+ if (!host) {
+ dev_err(dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, host);
+ ret = hinfc504_probe_dt(pdev, host);
+ if (ret) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ chip = (struct nand_chip *)&host[1];
+ mtd = (struct mtd_info *)&chip[1];
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource defined\n");
+ ret = -ENXIO;
+ goto err_res;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_res;
+ }
+ dev_dbg(dev, "res.start=0x%x, res.end=0x%x\n", res->start, res->end);
+
+ host->iobase = devm_request_and_ioremap(dev, res);
+ if (!host->iobase) {
+ ret = -EIO;
+ dev_err(dev, "devm_request_and_ioremap[0] fail\n");
+ goto err_res;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_res;
+ }
+ dev_dbg(dev, "res.start=0x%x, res.end=0x%x\n", res->start, res->end);
+
+ chip->IO_ADDR_R = chip->IO_ADDR_W = devm_request_and_ioremap(dev, res);
+ if (!chip->IO_ADDR_R) {
+ ret = -EIO;
+ dev_err(dev, "devm_request_and_ioremap[1] fail\n");
+ goto err_res;
+ }
+
+ ret = hinfc504_os_version_check(host);
+ if (!ret)
+ goto err_res;
+
+#if 0
+ host->clk = clk_get(dev, NULL);
+ if (IS_ERR(host->clk)) {
+ dev_err(dev, "failed to get nand clock\n");
+ ret = PTR_ERR(info->clk);
+ goto err_res;
+ }
+ clk_prepare_enable(host->clk);
+#endif
+ host->chip = chip;
+ host->mtd = mtd;
+
+ mtd->priv = chip;
+ mtd->owner = THIS_MODULE;
+ /* use driver's unique name instead of device's dts name */
+ mtd->name = DRIVER_NAME;
+
+ chip->priv = host;
+ chip->cmdfunc = hinfc504_cmdfunc;
+ chip->dev_ready = hinfc504_dev_ready;
+ chip->select_chip = hinfc504_select_chip;
+ chip->read_byte = hinfc504_read_byte;
+ chip->read_word = hinfc504_read_word;
+ chip->write_buf = hinfc504_write_buf;
+ chip->read_buf = hinfc504_read_buf;
+
+ chip->chip_delay = HINFC504_CHIP_DELAY;
+ chip->options = NAND_SKIP_BBTSCAN;
+ chip->ecc.layout = NULL;
+ chip->ecc.mode = NAND_ECC_NONE;
+
+ host->buffer = dma_alloc_coherent(dev,
+ NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE,
+ &host->dma_buffer, GFP_KERNEL);
+ if (!host->buffer) {
+ dev_err(dev, "Can't malloc memory for NAND driver.\n");
+ goto err_buf;
+ }
+
+ hinfc504_host_init(host);
+
+ ret = request_irq(irq, hinfc_irq_handle, IRQF_DISABLED, "nandc", host);
+ if (ret < 0) {
+ dev_err(dev, "failed to request IRQ\n");
+ goto err_irq;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0, -1);
+ /* Read entire ID string */
+ for (i = 0; i < 8; i++)
+ flash_dev_ex.ids[i] = chip->read_byte(mtd);
+ /*
+ * we support some non-standard nand chips in nand_flash_special_dev
+ * if the id matches, then use the info in the table, otherwise,
+ * def is NULL, use the normal way to probe the nand info
+ */
+ def = nand_get_flash_type_ex(&flash_dev_ex);
+
+ ret = nand_scan_ident(mtd, max_chips, def);
+ if (ret) {
+ ret = -ENODEV;
+ goto err_ident;
+ }
+
+ flash_dev_ex.oobsize = mtd->oobsize;
+ hinfc504_ecc_probe(mtd, chip, &flash_dev_ex);
+ nand_scan_tail(mtd);
+
+ nand_log_init(host);
+
+ ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
+ if (ret) {
+ dev_err(dev, "Err MTD partition=%d\n", ret);
+ goto err_mtd;
+ }
+
+ return 0;
+
+err_mtd:
+ nand_release(mtd);
+err_ident:
+err_irq:
+err_buf:
+ if (host->buffer)
+ dma_free_coherent(dev,
+ NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE,
+ host->buffer, host->dma_buffer);
+ kfree(host);
+err_res:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+/*****************************************************************************/
+int hinfc504_os_remove(struct platform_device *pdev)
+{
+ struct hinfc_host *host = platform_get_drvdata(pdev);
+ int irq;
+
+ //clk_unprepare_disable(host->clk);
+
+ nand_release(host->mtd);
+ dma_free_coherent(&pdev->dev,
+ (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE),
+ host->buffer,
+ host->dma_buffer);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ free_irq(irq, host);
+
+ kfree(host);
+ platform_set_drvdata(pdev, NULL);
+
+ nand_log_exit();
+
+ return 0;
+}
+/*****************************************************************************/
+#ifdef CONFIG_PM
+static int hinfc504_os_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct hinfc_host *host = platform_get_drvdata(pdev);
+
+ while ((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0)
+ ;
+
+ while ((hinfc_read(host, HINFC504_DMA_CTRL))
+ & HINFC504_DMA_CTRL_DMA_START)
+ _cond_resched();
+
+ //clk_unprepare_disable(host->clk);
+
+ return 0;
+}
+/*****************************************************************************/
+static int hinfc504_os_resume(struct platform_device *pdev)
+{
+ int cs;
+ struct hinfc_host *host = platform_get_drvdata(pdev);
+ struct nand_chip *chip = host->chip;
+
+ //clk_prepare_enable(host->clk);
+
+ for (cs = 0; cs < chip->numchips; cs++)
+ host->send_cmd_reset(host, cs);
+ hinfc_write(host,
+ SET_HINFC504_PWIDTH(CONFIG_HINFC504_W_LATCH,
+ CONFIG_HINFC504_R_LATCH, CONFIG_HINFC504_RW_LATCH),
+ HINFC504_PWIDTH);
+
+ return 0;
+}
+#else
+#define hinfc504_os_suspend NULL
+#define hinfc504_os_resume NULL
+#endif /* CONFIG_PM */
+
+/*****************************************************************************/
+#ifdef CONFIG_OF
+static const struct of_device_id nfc_id_table[] = {
+ { .compatible = "hisilicon,nfc504" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nfc_id_table);
+#endif
+
+static struct platform_driver hinfc504_os_pltdrv = {
+ .driver = {
+ .name = DRIVER_NAME,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(nfc_id_table),
+#endif
+ },
+ .probe = hinfc504_os_probe,
+ .remove = hinfc504_os_remove,
+ .suspend = hinfc504_os_suspend,
+ .resume = hinfc504_os_resume,
+};
+
+module_platform_driver(hinfc504_os_pltdrv);
+/*****************************************************************************/
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhiyong Cai");
+MODULE_AUTHOR("Mingjun Zhang");
+MODULE_DESCRIPTION("Hisilicon Nand Flash Controller V504 Driver");
+/*****************************************************************************/
diff --git a/drivers/mtd/nand/hs_nfc/hinfc504_read_retry.c b/drivers/mtd/nand/hs_nfc/hinfc504_read_retry.c
new file mode 100644
index 00000000000..f357f657970
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc504_read_retry.c
@@ -0,0 +1,353 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include "hinfc504.h"
+#include "hinfc_common.h"
+
+/* hynix */
+static char *hynix_otp_check(const char otp[8 * 128])
+{
+ int index = 0;
+ int ix, jx;
+ char *ptr = NULL;
+ int min, cur;
+ char *otp_origin, *otp_inverse;
+
+ min = 64;
+ for (ix = 0; ix < 8; ix++) {
+
+ otp_origin = (char *)otp + ix * 128;
+ otp_inverse = otp_origin + 64;
+ cur = 0;
+
+ for (jx = 0; jx < 64; jx++, otp_origin++, otp_inverse++) {
+ if (((*otp_origin) ^ (*otp_inverse)) == 0xFF)
+ continue;
+ cur++;
+ }
+ if (cur < min) {
+ min = cur;
+ index = ix;
+ ptr = (char *)otp + ix * 128;
+ if (!cur)
+ break;
+ }
+ }
+
+ pr_info("RR select parameter %d from %d, error %d\n", index, ix, min);
+
+ return ptr;
+}
+
+static int hinfc504_hynix_get_rr_param(struct hinfc_host *host)
+{
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+ /* step1: reset the chip */
+ host->send_cmd_reset(host, host->chipselect);
+
+ /* step2: cmd: 0x36, address: 0xAE, data: 0x00 */
+ hinfc_write(host, 1, HINFC504_DATA_NUM); /* data length 1 */
+ writel(0x0, host->chip->IO_ADDR_R); /* data: 0x00 */
+ hinfc_write(host, 0xAE, HINFC504_ADDRL); /* address: 0xAE */
+ hinfc_write(host, 0x36, HINFC504_CMD); /* cmd: 0x36 */
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ /* step3: address: 0xB0, data: 0x4D */
+ writel(0x4D, host->chip->IO_ADDR_R); /* data: 0x4d */
+ hinfc_write(host, 1, HINFC504_DATA_NUM); /* data length 1 */
+ hinfc_write(host, 0xB0, HINFC504_ADDRL); /* address: 0xB0 */
+ /* only address and data, without cmd */
+ hinfc_write(host, HINFC504_WRITE_0CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ /* step4: cmd: 0x16, 0x17, 0x04, 0x19 */
+ hinfc_write(host, 0x17 << 8 | 0x16, HINFC504_CMD);
+ hinfc_write(host, 0, HINFC504_OP);
+ hinfc_write(host, HINFC504_WRITE_2CMD_0ADD_NODATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ hinfc_write(host, 0x19 << 8 | 0x04, HINFC504_CMD);
+ hinfc_write(host, 0, HINFC504_OP);
+ hinfc_write(host, HINFC504_WRITE_2CMD_0ADD_NODATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ /* step5: cmd: 0x00 0x30, address: 0x02 00 00 00 */
+ hinfc_write(host, 0x2000000, HINFC504_ADDRL);
+ hinfc_write(host, 0x30 << 8 | 0x00, HINFC504_CMD);
+ hinfc_write(host, 0, HINFC504_OP);
+ hinfc_write(host, 0x80, HINFC504_DATA_NUM);
+ hinfc_write(host, HINFC504_READ_2CMD_5ADD, HINFC504_OP);
+ wait_controller_finished(host);
+
+ /*step6 save otp read retry table to mem*/
+ {
+ char *otp = hynix_otp_check(host->chip->IO_ADDR_R + 2);
+ memcpy(host->rr_data, otp, 64);
+ host->need_rr_data = 1;
+ }
+
+ /* step7: reset the chip */
+ host->send_cmd_reset(host, host->chipselect);
+
+ /* step8: cmd: 0x38 */
+ hinfc_write(host, 0x38, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_2CMD_0ADD_NODATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ host->enable_ecc_randomizer(host, ENABLE, ENABLE);
+ /* get hynix otp table finish */
+ return 0;
+}
+
+#define HINFC504_HYNIX_RR_BASEADDR 0xB0
+static int hinfc504_hynix_set_rr_reg(struct hinfc_host *host, char *val)
+{
+ int i;
+ host->enable_ecc_randomizer(host, DISABLE, DISABLE);
+ hinfc_write(host, 1, HINFC504_DATA_NUM);
+
+ for (i = 0; i < 8; i++) {
+ switch (i) {
+ case 0:
+ writel(val[i], host->chip->IO_ADDR_R);
+ hinfc_write(host, HINFC504_HYNIX_RR_BASEADDR,
+ HINFC504_ADDRL);
+ hinfc_write(host, 0x36, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA,
+ HINFC504_OP);
+ break;
+ case 7:
+ hinfc_write(host, 0x16, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_2CMD_0ADD_NODATA,
+ HINFC504_OP);
+ break;
+ default:
+ writel(val[i], host->chip->IO_ADDR_R);
+ hinfc_write(host, HINFC504_HYNIX_RR_BASEADDR + i,
+ HINFC504_ADDRL);
+ hinfc_write(host, HINFC504_WRITE_0CMD_1ADD_DATA,
+ HINFC504_OP);
+ break;
+
+ }
+ wait_controller_finished(host);
+ }
+ host->enable_ecc_randomizer(host, ENABLE, ENABLE);
+ return 0;
+}
+
+static int hinfc504_hynix_set_rr_param(struct hinfc_host *host, int param)
+{
+ unsigned char *rr;
+
+ if (!(host->rr_data[0] | host->rr_data[1]
+ | host->rr_data[2] | host->rr_data[3])
+ || !param)
+ return -1;
+
+ rr = (unsigned char *)&host->rr_data[((param & 0x07) << 3)];
+
+ /* set the read retry regs to adjust reading level */
+ return hinfc504_hynix_set_rr_reg(host, (char *)rr);
+}
+
+/* samsung */
+static int hinfc504_samsung_set_rr_reg(struct hinfc_host *host, int param)
+{
+#define SAMSUNG_RR_CMD 0xA1
+
+ unsigned char samsung_rr_params[15][4] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x05, 0x0A, 0x00, 0x00},
+ {0x28, 0x00, 0xEC, 0xD8},
+ {0xED, 0xF5, 0xED, 0xE6},
+ {0x0A, 0x0F, 0x05, 0x00},
+ {0x0F, 0x0A, 0xFB, 0xEC},
+ {0xE8, 0xEF, 0xE8, 0xDC},
+ {0xF1, 0xFB, 0xFE, 0xF0},
+ {0x0A, 0x00, 0xFB, 0xEC},
+ {0xD0, 0xE2, 0xD0, 0xC2},
+ {0x14, 0x0F, 0xFB, 0xEC},
+ {0xE8, 0xFB, 0xE8, 0xDC},
+ {0x1E, 0x14, 0xFB, 0xEC},
+ {0xFB, 0xFF, 0xFB, 0xF8},
+ {0x07, 0x0C, 0x02, 0x00}
+ };
+
+ if (param >= 15)
+ param = (param % 15);
+
+ hinfc_write(host, 1, HINFC504_DATA_NUM);
+
+ writel(samsung_rr_params[param][0], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0xA700, HINFC504_ADDRL);
+ hinfc_write(host, SAMSUNG_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_2ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(samsung_rr_params[param][1], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0xA400, HINFC504_ADDRL);
+ hinfc_write(host, SAMSUNG_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_2ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(samsung_rr_params[param][2], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0xA500, HINFC504_ADDRL);
+ hinfc_write(host, SAMSUNG_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_2ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(samsung_rr_params[param][3], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0xA600, HINFC504_ADDRL);
+ hinfc_write(host, SAMSUNG_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_2ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ return 0;
+
+#undef SAMSUNG_RR_CMD
+}
+
+static int hinfc504_samsung_set_rr_param(struct hinfc_host *host, int param)
+{
+ return hinfc504_samsung_set_rr_reg(host, param);
+}
+
+/* micron */
+#define MICRON_RR_ADDR 0x89
+
+static int hinfc504_micron_set_rr_reg(struct hinfc_host *host, int rr)
+{
+#define MICRON_SET_RR 0xEF
+ hinfc_write(host, 1, HINFC504_DATA_NUM);
+
+ writel(rr, host->chip->IO_ADDR_W);
+ hinfc_write(host, MICRON_RR_ADDR, HINFC504_ADDRL);
+ hinfc_write(host, MICRON_SET_RR, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+#undef MICRON_SET_RR
+ return 0;
+}
+
+static int hinfc504_micron_get_rr_param(struct hinfc_host *host)
+{
+#define MICRON_GET_RR 0xEE
+ hinfc_write(host, 1, HINFC504_DATA_NUM);
+
+ hinfc_write(host, MICRON_RR_ADDR, HINFC504_ADDRL);
+ hinfc_write(host, MICRON_GET_RR, HINFC504_CMD);
+ hinfc_write(host, HINFC504_READ_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+ hinfc_write(host, NAND_CMD_READ0, HINFC504_CMD);
+ hinfc_write(host, HINFC504_READ_1CMD_0ADD_NODATA, HINFC504_OP);
+#undef MICRON_GET_RR
+ return readl(host->chip->IO_ADDR_R) & 0xff;
+}
+
+static int hinfc504_micron_set_rr_param(struct hinfc_host *host, int rr_option)
+{
+ hinfc504_micron_set_rr_reg(host, rr_option);
+ return 0;
+}
+
+/* toshiba */
+static int hinfc504_toshiba_24nm_set_rr_reg(struct hinfc_host *host, int param)
+{
+#define TOSHIBA_RR_CMD 0x55
+
+ static char toshiba_rr_param[] = {0x00, 0x04, 0x7c, 0x78, 0x74, 0x08};
+
+ if (!param) {
+ host->send_cmd_reset(host, host->chipselect);
+ return 0;
+ }
+
+ if (param >= 6)
+ param = (param % 6);
+
+ hinfc_write(host, 1, HINFC504_DATA_NUM);
+
+ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0x4, HINFC504_ADDRL);
+ hinfc_write(host, TOSHIBA_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0x5, HINFC504_ADDRL);
+ hinfc_write(host, TOSHIBA_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0x6, HINFC504_ADDRL);
+ hinfc_write(host, TOSHIBA_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R);
+ hinfc_write(host, 0x7, HINFC504_ADDRL);
+ hinfc_write(host, TOSHIBA_RR_CMD, HINFC504_CMD);
+ hinfc_write(host, HINFC504_WRITE_1CMD_1ADD_DATA, HINFC504_OP);
+ wait_controller_finished(host);
+
+ return 0;
+
+#undef TOSHIBA_RR_CMD
+}
+
+static int hinfc504_toshiba_24nm_set_rr_param(struct hinfc_host *host,
+ int param)
+{
+ return hinfc504_toshiba_24nm_set_rr_reg(host, param);
+}
+
+struct read_retry_t read_retry_list[] = {
+ {
+ .type = NAND_RR_HYNIX,
+ .count = 7,
+ .set_rr_param = hinfc504_hynix_set_rr_param,
+ .get_rr_param = hinfc504_hynix_get_rr_param,
+ },
+ {
+ .type = NAND_RR_MICRON,
+ .count = 8,
+ .set_rr_param = hinfc504_micron_set_rr_param,
+ .get_rr_param = hinfc504_micron_get_rr_param,
+ },
+ {
+ .type = NAND_RR_SAMSUNG,
+ .count = 15,
+ .set_rr_param = hinfc504_samsung_set_rr_param,
+ .get_rr_param = NULL,
+ },
+ {
+ .type = NAND_RR_TOSHIBA_24nm,
+ .count = 6,
+ .set_rr_param = hinfc504_toshiba_24nm_set_rr_param,
+ .get_rr_param = NULL,
+ },
+ { NAND_RR_NONE, 0, NULL, NULL},
+};
diff --git a/drivers/mtd/nand/hs_nfc/hinfc_common.h b/drivers/mtd/nand/hs_nfc/hinfc_common.h
new file mode 100644
index 00000000000..414d8fa2a3f
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc_common.h
@@ -0,0 +1,128 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 HINFC_COMMONH
+#define HINFC_COMMONH
+/******************************************************************************/
+#include <linux/mtd/nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sizes.h>
+
+#define HINFC_VER_300 (0x300)
+#define HINFC_VER_301 (0x301)
+#define HINFC_VER_310 (0x310)
+#define HINFC_VER_504 (0x504)
+#define HINFC_VER_505 (0x505)
+#define HINFC_VER_600 (0x600)
+
+/*****************************************************************************/
+
+enum ecc_type {
+ et_ecc_none = 0x00,
+ et_ecc_1bit = 0x01,
+ et_ecc_4bit = 0x02,
+ et_ecc_4bytes = 0x02,
+ et_ecc_8bytes = 0x03,
+ et_ecc_24bit1k = 0x04,
+ et_ecc_40bit1k = 0x05,
+};
+
+enum page_type {
+ pt_pagesize_512,
+ pt_pagesize_2K,
+ pt_pagesize_4K,
+ pt_pagesize_8K,
+ pt_pagesize_16K,
+};
+
+struct page_page_ecc_info {
+ enum page_type pagetype;
+ enum ecc_type ecctype;
+ int oobsize;
+ struct nand_ecclayout *layout;
+};
+
+struct hinfc_host;
+
+struct read_retry_t {
+ int type;
+ int count;
+ int (*set_rr_param)(struct hinfc_host *host, int param);
+ int (*get_rr_param)(struct hinfc_host *host);
+};
+
+enum read_retry_type {
+ NAND_RR_NONE,
+ NAND_RR_HYNIX,
+ NAND_RR_MICRON,
+ NAND_RR_SAMSUNG,
+ NAND_RR_TOSHIBA_24nm,
+};
+
+struct nand_flash_dev_ex {
+ struct nand_flash_dev flash_dev;
+
+ unsigned char ids[8];
+ int oobsize;
+ int ecctype;
+ int is_randomizer;
+ int read_retry_type;
+};
+
+/*****************************************************************************/
+static inline char *get_ecctype_str(enum ecc_type ecctype)
+{
+ static char *ecctype_string[] = {
+ "None", "1bit", "4bits", "8bits", "24bits/1K",
+ "40bits/1K", "unknown", "unknown"};
+ return ecctype_string[(ecctype & 0x07)];
+}
+/*****************************************************************************/
+
+static inline char *get_pagesize_str(enum page_type pagetype)
+{
+ static char *pagesize_str[] = {
+ "512", "2K", "4K", "8K", "16K", "unknown",
+ "unknown", "unknown"};
+ return pagesize_str[pagetype & 0x07];
+}
+/*****************************************************************************/
+
+static inline unsigned int get_pagesize(enum page_type pagetype)
+{
+ unsigned int pagesize[] = { SZ_512, SZ_2K, SZ_4K,
+ SZ_8K, SZ_16K, 0, 0, 0 };
+ return pagesize[pagetype & 0x07];
+}
+
+struct nand_flash_dev *nand_get_flash_type_ex(struct nand_flash_dev_ex
+ *flash_dev_ex);
+
+void nand_show_info(struct nand_flash_dev_ex *flash_dev_ex,
+ struct mtd_info *mtd, char *vendor, char *chipname);
+
+void nand_show_chip(struct nand_chip *chip);
+
+extern struct read_retry_t read_retry_list[];
+/******************************************************************************/
+#endif /* HINFC_COMMONH */
diff --git a/drivers/mtd/nand/hs_nfc/hinfc_spl_ids.c b/drivers/mtd/nand/hs_nfc/hinfc_spl_ids.c
new file mode 100644
index 00000000000..bb1b1709ce3
--- /dev/null
+++ b/drivers/mtd/nand/hs_nfc/hinfc_spl_ids.c
@@ -0,0 +1,195 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2013 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright © 2012-2013 Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Mingjun Zhang <zhang.mingjun@linaro.org>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/mtd/nand.h>
+#include <linux/sizes.h>
+#include "hinfc_common.h"
+
+/*****************************************************************************/
+
+#define SZ_768K (SZ_256K + SZ_512K)
+#define SZ_4G (0x100000000ULL)
+#define SZ_8G (0x200000000ULL)
+#define SZ_16G (0x400000000ULL)
+#define SZ_64G (0x1000000000ULL)
+
+/*****************************************************************************/
+struct nand_flash_special_dev {
+ unsigned char id[8];
+ int length; /* length of id. */
+ unsigned long long chipsize;
+ char *name;
+
+ unsigned long pagesize;
+ unsigned long erasesize;
+ unsigned long oobsize;
+ unsigned long options;
+ int read_retry_type;
+};
+
+/*****************************************************************************/
+/*
+ * samsung: 27nm need randomizer, 21nm need read retry;
+ * micron: 25nm need read retry, datasheet will explain read retry.
+ * toshaba 32nm need randomizer, 24nm need read retry.
+ * hynix: 2xnm need read retry.
+ */
+static struct nand_flash_special_dev nand_flash_special_dev[] = {
+ /* 1st 2nd 3rd 4th 5th 6th 7th 8th len chipsize(B)
+ device pagesize erasesize oobsize options */
+ /* Micron */
+ { {0x2C, 0x64, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, 8, SZ_8G,
+ "MT29F64G08CBABA", SZ_8K, SZ_2M, 744, 0, NAND_RR_MICRON},
+ { {0x2C, 0x38, 0x00, 0x26, 0x85, 0x00, 0x00, 0x00}, 8, SZ_1G,
+ "MT29F8G08ABxBA", SZ_4K, SZ_512K, 224, 0, NAND_RR_NONE},
+ { {0x2C, 0x48, 0x04, 0x46, 0x85, 0x00, 0x00, 0x00}, 8, SZ_2G,
+ "MT29F16G08CBABx", SZ_4K, SZ_1M, 224, 0, NAND_RR_NONE},
+ { {0x2C, 0x48, 0x04, 0x4A, 0xA5, 0x00, 0x00, 0x00}, 8, SZ_2G,
+ "MT29F16G08CBACA", SZ_4K, SZ_1M, 224, 0, NAND_RR_NONE},
+ { {0x2C, 0x68, 0x04, 0x4A, 0xA9, 0x00, 0x00, 0x00}, 8, SZ_4G,
+ "MT29F32G08CBACA", SZ_4K, SZ_1M, 224, 0, NAND_RR_NONE},
+ { {0x2C, 0x88, 0x04, 0x4B, 0xA9, 0x00, 0x00, 0x00}, 8, SZ_8G,
+ "MT29F64G08CxxAA", SZ_8K, SZ_2M, 448, 0, NAND_RR_NONE},
+ { {0x2C, 0xA8, 0x05, 0xCB, 0xA9, 0x00, 0x00, 0x00}, 8, SZ_16G,
+ "MT29F256G08CJAAA", SZ_8K, SZ_2M, 448, 0, NAND_RR_NONE},
+ { {0x2C, 0x48, 0x00, 0x26, 0xA9, 0x00, 0x00, 0x00}, 5, SZ_2G,
+ "MT29F16G08ABACA", SZ_4K, SZ_512K, 224, 0, NAND_RR_NONE},
+
+ /* Toshiba */
+ { {0x98, 0xD5, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, 6, SZ_2G,
+ "TC58NVG4D2FTA00", SZ_8K, SZ_1M, 448, 0, NAND_RR_NONE},
+ { {0x98, 0xD7, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, 6, SZ_8G,
+ "TH58NVG6D2FTA20", SZ_8K, SZ_1M, 448, 0, NAND_RR_NONE},
+ { {0x98, 0xD7, 0x94, 0x32, 0x76, 0x56, 0x08, 0x00}, 6, SZ_4G,
+ "TC58NVG5D2HTA00 24nm", SZ_8K, SZ_1M, 640, 0,
+ NAND_RR_TOSHIBA_24nm},
+ { {0x98, 0xDE, 0x94, 0x82, 0x76, 0x00, 0x00, 0x00}, 5, SZ_8G,
+ "TC58NVG6D2GTA00", SZ_8K, SZ_2M, 640, 0,
+ NAND_RR_NONE},
+ { {0x98, 0xDE, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, 8, SZ_8G,
+ "TC58NVG6DCJTA00 19nm", SZ_16K, SZ_4M, 1280, 0,
+ NAND_RR_TOSHIBA_24nm},
+ { {0x98, 0xD7, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, 6, SZ_4G,
+ "TC58TEG5DCJTA00 19nm", SZ_16K, SZ_4M, 1280, 0,
+ NAND_RR_TOSHIBA_24nm},
+ { {0x98, 0xF1, 0x80, 0x15, 0x72, 0x00, 0x00, 0x00}, 5, SZ_128M,
+ "TC58NVG0S3HTA00", SZ_2K, SZ_128K, 128, 0, NAND_RR_NONE},
+ { {0x98, 0xD7, 0x98, 0x92, 0x72, 0x57, 0x08, 0x10}, 6, SZ_4G,
+ "TC58NVG5T2JTA00 19nm TLC", SZ_8K, SZ_4M, 1024, 0,
+ NAND_RR_TOSHIBA_24nm}, /* 60bit/1K */
+
+ /* Samsung K9xxG08UxD: K9LBG08U0D / K9HCG08U1D / K9XDG08U5D */
+ { {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43, 0x00, 0x00}, 6, SZ_4G,
+ "K9GBG08U0A 20nm", SZ_8K, SZ_1M, 640, 0,
+ NAND_RR_SAMSUNG},/* 24bit1k */
+ { {0xEC, 0xD7, 0x94, 0x7E, 0x64, 0x44, 0x00, 0x00}, 6, SZ_4G,
+ "K9GBG08U0B", SZ_8K, SZ_1M, 1024, 0, NAND_RR_SAMSUNG},
+
+ /* Hynix */
+ { {0xAD, 0xD7, 0x94, 0x91, 0x60, 0x44, 0x00, 0x00}, 6, SZ_4G,
+ "H27UBG8T2C", SZ_8K, SZ_2M, 640, 0, NAND_RR_HYNIX},
+
+ /* MIRA */
+ { {0xC8, 0xD5, 0x14, 0x29, 0x34, 0x01, 0x00, 0x00}, 6, SZ_2G,
+ "P1UAGA30AT-GCA", SZ_4K, SZ_512K, 218, 0, NAND_RR_NONE},
+ /* PowerFlash ASU8GA30IT-G30CA and MIRA PSU8GA30AT-GIA
+ have the same ID */
+ { {0xC8, 0xD3, 0x90, 0x19, 0x34, 0x01, 0x00, 0x00}, 6, SZ_1G,
+ "PSU8GA30AT-GIA/ASU8GA30IT-G30CA", SZ_4K, SZ_256K, 218, 0,
+ NAND_RR_NONE},
+ { {0x7F, 0x7F, 0x7F, 0x7F, 0xC8, 0xDA, 0x00, 0x15}, 8, SZ_256M,
+ "PSU2GA30AT", SZ_2K, SZ_128K, 64, 0, NAND_RR_NONE},
+
+ {},/* may used to store nand chip info passed from bootloader */
+ {},/* null terminated */
+};
+
+/*****************************************************************************/
+struct nand_flash_dev *nand_get_flash_type_ex(struct nand_flash_dev_ex
+ *flash_dev_ex)
+{
+ struct nand_flash_special_dev *spl_dev;
+ unsigned char *byte = flash_dev_ex->ids;
+ struct nand_flash_dev *flash_type = &flash_dev_ex->flash_dev;
+
+ pr_info("Chip ID: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ byte[0], byte[1], byte[2], byte[3],
+ byte[4], byte[5], byte[6], byte[7]);
+
+ for (spl_dev = nand_flash_special_dev; spl_dev->length; spl_dev++) {
+ if (memcmp(byte, spl_dev->id, spl_dev->length))
+ continue;
+
+ flash_type->id = byte[1];
+ flash_type->name = spl_dev->name;
+
+ flash_type->pagesize = spl_dev->pagesize;
+ flash_type->chipsize = (unsigned long)
+ (spl_dev->chipsize >> 20);
+ flash_type->erasesize = spl_dev->erasesize;
+ flash_type->options = spl_dev->options;
+
+ flash_dev_ex->oobsize = spl_dev->oobsize;
+ flash_dev_ex->read_retry_type = spl_dev->read_retry_type;
+
+ return flash_type;
+ }
+
+ flash_dev_ex->read_retry_type = NAND_RR_NONE;
+
+ return NULL;
+}
+/*****************************************************************************/
+static char *ultohstr(u32 size, char *buffer)
+{
+ char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"};
+ int ix;
+
+ for (ix = 0; (ix < 5) && !(size & 0x3FF) && size; ix++)
+ size = (size >> 10);
+
+ sprintf(buffer, fmt[ix], size);
+ return buffer;
+}
+
+void nand_show_info(struct nand_flash_dev_ex *flash_dev_ex,
+ struct mtd_info *mtd,
+ char *vendor,
+ char *chipname)
+{
+ char buf[20];
+
+ pr_info("Nand: %s %s ", vendor, chipname);
+
+ if (flash_dev_ex->is_randomizer)
+ pr_info("Randomizer ");
+
+ if (flash_dev_ex->read_retry_type != NAND_RR_NONE)
+ pr_info("Read-Retry ");
+
+ pr_info("\n");
+
+ pr_info("Block:%sB ", ultohstr(mtd->erasesize, buf));
+ pr_info("Page:%sB ", ultohstr(mtd->writesize, buf));
+ pr_info("OOB:%sB ", ultohstr(flash_dev_ex->oobsize, buf));
+ pr_info("ECC:%s ", get_ecctype_str(flash_dev_ex->ecctype));
+}