Subject: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

Added the basic driver for Arasan Nand Flash Controller used in
Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
correction.

Signed-off-by: Punnaiah Choudary Kalluri <[email protected]>
---
Changes in v5:
- Renamed the driver filei as arasan_nand.c
- Fixed all comments relaqted coding style
- Fixed comments related to propagating the errors
- Modified the anfc_write_page_hwecc as per the write_page
prototype
Changes in v4:
- Added support for onfi timing mode configuration
- Added clock supppport
- Added support for multiple chipselects
Changes in v3:
- Removed unused variables
- Avoided busy loop and used jifies based implementation
- Fixed compiler warnings "right shift count >= width of type"
- Removed unneeded codei and improved error reporting
- Added onfi version check to ensure reading the valid address cycles
Changes in v2:
- Added missing of.h to avoid kbuild system report error
---
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 1017 insertions(+)
create mode 100644 drivers/mtd/nand/arasan_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2896640..9c620fb 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -546,4 +546,10 @@ config MTD_NAND_HISI504
help
Enables support for NAND controller on Hisilicon SoC Hip04.

+config MTD_NAND_ARASAN
+ tristate "Support for Arasan Nand Flash controller"
+ help
+ Enables the driver for the Arasan Nand Flash controller on
+ Zynq UltraScale+ MPSoC.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 2c7f014..3b993cb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
+obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o

nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
new file mode 100644
index 0000000..e882e63
--- /dev/null
+++ b/drivers/mtd/nand/arasan_nand.c
@@ -0,0 +1,1010 @@
+/*
+ * Arasan Nand Flash Controller Driver
+ *
+ * Copyright (C) 2014 - 2015 Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_mtd.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "arasan_nand"
+#define EVNT_TIMEOUT 1000
+#define STATUS_TIMEOUT 2000
+
+#define PKT_OFST 0x00
+#define MEM_ADDR1_OFST 0x04
+#define MEM_ADDR2_OFST 0x08
+#define CMD_OFST 0x0C
+#define PROG_OFST 0x10
+#define INTR_STS_EN_OFST 0x14
+#define INTR_SIG_EN_OFST 0x18
+#define INTR_STS_OFST 0x1C
+#define READY_STS_OFST 0x20
+#define DMA_ADDR1_OFST 0x24
+#define FLASH_STS_OFST 0x28
+#define DATA_PORT_OFST 0x30
+#define ECC_OFST 0x34
+#define ECC_ERR_CNT_OFST 0x38
+#define ECC_SPR_CMD_OFST 0x3C
+#define ECC_ERR_CNT_1BIT_OFST 0x40
+#define ECC_ERR_CNT_2BIT_OFST 0x44
+#define DMA_ADDR0_OFST 0x50
+#define DATA_INTERFACE_REG 0x6C
+
+#define PKT_CNT_SHIFT 12
+
+#define ECC_ENABLE BIT(31)
+#define DMA_EN_MASK GENMASK(27, 26)
+#define DMA_ENABLE 0x2
+#define DMA_EN_SHIFT 26
+#define REG_PAGE_SIZE_MASK GENMASK(25, 23)
+#define REG_PAGE_SIZE_SHIFT 23
+#define REG_PAGE_SIZE_512 0
+#define REG_PAGE_SIZE_1K 5
+#define REG_PAGE_SIZE_2K 1
+#define REG_PAGE_SIZE_4K 2
+#define REG_PAGE_SIZE_8K 3
+#define REG_PAGE_SIZE_16K 4
+#define CMD2_SHIFT 8
+#define ADDR_CYCLES_SHIFT 28
+
+#define XFER_COMPLETE BIT(2)
+#define READ_READY BIT(1)
+#define WRITE_READY BIT(0)
+#define MBIT_ERROR BIT(3)
+#define ERR_INTRPT BIT(4)
+
+#define PROG_PGRD BIT(0)
+#define PROG_ERASE BIT(2)
+#define PROG_STATUS BIT(3)
+#define PROG_PGPROG BIT(4)
+#define PROG_RDID BIT(6)
+#define PROG_RDPARAM BIT(7)
+#define PROG_RST BIT(8)
+#define PROG_GET_FEATURE BIT(9)
+#define PROG_SET_FEATURE BIT(10)
+
+#define ONFI_STATUS_FAIL BIT(0)
+#define ONFI_STATUS_READY BIT(6)
+
+#define PG_ADDR_SHIFT 16
+#define BCH_MODE_SHIFT 25
+#define BCH_EN_SHIFT 27
+#define ECC_SIZE_SHIFT 16
+
+#define MEM_ADDR_MASK GENMASK(7, 0)
+#define BCH_MODE_MASK GENMASK(27, 25)
+
+#define CS_MASK GENMASK(31, 30)
+#define CS_SHIFT 30
+
+#define PAGE_ERR_CNT_MASK GENMASK(16, 8)
+#define PKT_ERR_CNT_MASK GENMASK(7, 0)
+
+#define NVDDR_MODE BIT(9)
+#define NVDDR_TIMING_MODE_SHIFT 3
+
+#define ONFI_ID_LEN 8
+#define TEMP_BUF_SIZE 512
+#define NVDDR_MODE_PACKET_SIZE 8
+#define SDR_MODE_PACKET_SIZE 4
+
+/**
+ * struct anfc_ecc_matrix - Defines ecc information storage format
+ * @pagesize: Page size in bytes.
+ * @codeword_size: Code word size information.
+ * @eccbits: Number of ecc bits.
+ * @bch: Bch / Hamming mode enable/disable.
+ * @eccsize: Ecc size information.
+ */
+struct anfc_ecc_matrix {
+ u32 pagesize;
+ u32 codeword_size;
+ u8 eccbits;
+ u8 bch;
+ u16 eccsize;
+};
+
+static const struct anfc_ecc_matrix ecc_matrix[] = {
+ {512, 512, 1, 0, 0x3},
+ {512, 512, 4, 1, 0x7},
+ {512, 512, 8, 1, 0xD},
+ /* 2K byte page */
+ {2048, 512, 1, 0, 0xC},
+ {2048, 512, 4, 1, 0x1A},
+ {2048, 512, 8, 1, 0x34},
+ {2048, 512, 12, 1, 0x4E},
+ {2048, 1024, 24, 1, 0x54},
+ /* 4K byte page */
+ {4096, 512, 1, 0, 0x18},
+ {4096, 512, 4, 1, 0x34},
+ {4096, 512, 8, 1, 0x68},
+ {4096, 512, 12, 1, 0x9C},
+ {4096, 1024, 4, 1, 0xA8},
+ /* 8K byte page */
+ {8192, 512, 1, 0, 0x30},
+ {8192, 512, 4, 1, 0x68},
+ {8192, 512, 8, 1, 0xD0},
+ {8192, 512, 12, 1, 0x138},
+ {8192, 1024, 24, 1, 0x150},
+ /* 16K byte page */
+ {16384, 512, 1, 0, 0x60},
+ {16384, 512, 4, 1, 0xD0},
+ {16384, 512, 8, 1, 0x1A0},
+ {16384, 512, 12, 1, 0x270},
+ {16384, 1024, 24, 1, 0x2A0}
+};
+
+/**
+ * struct anfc - Defines the Arasan NAND flash driver instance
+ * @chip: NAND chip information structure.
+ * @mtd: MTD information structure.
+ * @dev: Pointer to the device structure.
+ * @base: Virtual address of the NAND flash device.
+ * @curr_cmd: Current command issued.
+ * @clk_sys: Pointer to the system clock.
+ * @clk_flash: Pointer to the flash clock.
+ * @dma: Dma enable/disable.
+ * @bch: Bch / Hamming mode enable/disable.
+ * @err: Error identifier.
+ * @iswriteoob: Identifies if oob write operation is required.
+ * @buf: Buffer used for read/write byte operations.
+ * @raddr_cycles: Row address cycle information.
+ * @caddr_cycles: Column address cycle information.
+ * @irq: irq number
+ * @pktsize: Packet size for read / write operation.
+ * @bufshift: Variable used for indexing buffer operation
+ * @rdintrmask: Interrupt mask value for read operation.
+ * @num_cs: Number of chip selects in use.
+ * @spktsize: Packet size in ddr mode for status operation.
+ * @bufrdy: Completion event for buffer ready.
+ * @xfercomp: Completion event for transfer complete.
+ * @ecclayout: Ecc layout object
+ */
+struct anfc {
+ struct nand_chip chip;
+ struct mtd_info mtd;
+ struct device *dev;
+
+ void __iomem *base;
+ int curr_cmd;
+ struct clk *clk_sys;
+ struct clk *clk_flash;
+
+ bool dma;
+ bool bch;
+ bool err;
+ bool iswriteoob;
+
+ u8 buf[TEMP_BUF_SIZE];
+
+ u16 raddr_cycles;
+ u16 caddr_cycles;
+
+ u32 irq;
+ u32 pktsize;
+ u32 bufshift;
+ u32 rdintrmask;
+ u32 num_cs;
+ u32 spktsize;
+
+ struct completion bufrdy;
+ struct completion xfercomp;
+ struct nand_ecclayout ecclayout;
+};
+
+static inline struct anfc *to_anfc(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct anfc, mtd);
+}
+
+static u8 anfc_page(u32 pagesize)
+{
+ switch (pagesize) {
+ case 512:
+ return REG_PAGE_SIZE_512;
+ case 1024:
+ return REG_PAGE_SIZE_1K;
+ case 2048:
+ return REG_PAGE_SIZE_2K;
+ case 4096:
+ return REG_PAGE_SIZE_4K;
+ case 8192:
+ return REG_PAGE_SIZE_8K;
+ case 16384:
+ return REG_PAGE_SIZE_16K;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
+{
+ writel(val, nfc->base + INTR_STS_EN_OFST);
+ writel(val, nfc->base + INTR_SIG_EN_OFST);
+}
+
+static int anfc_wait_for_event(struct anfc *nfc, u32 event)
+{
+ struct completion *comp;
+
+ if (event == XFER_COMPLETE)
+ comp = &nfc->xfercomp;
+ else
+ comp = &nfc->bufrdy;
+
+ return wait_for_completion_timeout(comp,
+ msecs_to_jiffies(EVNT_TIMEOUT));
+}
+
+static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
+ u32 pktcount)
+{
+ writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
+}
+
+static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
+{
+ writel(cmd1 | (cmd2 << CMD2_SHIFT) |
+ (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
+ nfc->base + ECC_SPR_CMD_OFST);
+}
+
+static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
+{
+ u32 val;
+
+ writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
+
+ val = readl(nfc->base + MEM_ADDR2_OFST);
+ val = (val & ~MEM_ADDR_MASK) |
+ ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
+ writel(val, nfc->base + MEM_ADDR2_OFST);
+}
+
+static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
+ u8 dmamode, u32 pagesize, u8 addrcycles)
+{
+ u32 regval;
+
+ regval = cmd1 | (cmd2 << CMD2_SHIFT);
+ if (dmamode && nfc->dma)
+ regval |= DMA_ENABLE << DMA_EN_SHIFT;
+ if (addrcycles)
+ regval |= addrcycles << ADDR_CYCLES_SHIFT;
+ if (pagesize)
+ regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
+ writel(regval, nfc->base + CMD_OFST);
+}
+
+static int anfc_device_ready(struct mtd_info *mtd,
+ struct nand_chip *chip)
+{
+ u8 status;
+ unsigned long timeout = jiffies + STATUS_TIMEOUT;
+ struct anfc *nfc = to_anfc(mtd);
+
+ do {
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
+ status = chip->read_byte(mtd);
+ if (status & ONFI_STATUS_READY)
+ break;
+ cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (status & ONFI_STATUS_FAIL)
+ return NAND_STATUS_FAIL;
+
+ if (time_after_eq(jiffies, timeout)) {
+ dev_err(nfc->dev, "%s timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct anfc *nfc = to_anfc(mtd);
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ if (nfc->dma)
+ nfc->rdintrmask = XFER_COMPLETE;
+ else
+ nfc->rdintrmask = READ_READY;
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct anfc *nfc = to_anfc(mtd);
+
+ nfc->iswriteoob = true;
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nfc->iswriteoob = false;
+
+ return 0;
+}
+
+static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ u32 pktcount, pktsize;
+ unsigned int buf_rd_cnt = 0;
+ u32 *bufptr = (u32 *)buf;
+ struct anfc *nfc = to_anfc(mtd);
+ dma_addr_t paddr;
+
+ if (nfc->curr_cmd == NAND_CMD_READ0) {
+ pktsize = nfc->pktsize;
+ pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
+ } else {
+ pktsize = len;
+ pktcount = 1;
+ }
+
+ anfc_setpktszcnt(nfc, pktsize, pktcount);
+
+ if (nfc->dma) {
+ paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(nfc->dev, paddr)) {
+ dev_err(nfc->dev, "Read buffer mapping error");
+ return;
+ }
+ lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
+ anfc_enable_intrs(nfc, nfc->rdintrmask);
+ writel(PROG_PGRD, nfc->base + PROG_OFST);
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+ dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
+ return;
+ }
+
+ anfc_enable_intrs(nfc, nfc->rdintrmask);
+ writel(PROG_PGRD, nfc->base + PROG_OFST);
+
+ while (buf_rd_cnt < pktcount) {
+
+ anfc_wait_for_event(nfc, READ_READY);
+ buf_rd_cnt++;
+
+ if (buf_rd_cnt == pktcount)
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+
+ readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
+ bufptr += pktsize/4;
+
+ if (buf_rd_cnt < pktcount)
+ anfc_enable_intrs(nfc, nfc->rdintrmask);
+ }
+
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+}
+
+static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ u32 pktcount, pktsize;
+ unsigned int buf_wr_cnt = 0;
+ u32 *bufptr = (u32 *)buf;
+ struct anfc *nfc = to_anfc(mtd);
+ dma_addr_t paddr;
+
+ if (nfc->iswriteoob) {
+ pktsize = len;
+ pktcount = 1;
+ } else {
+ pktsize = nfc->pktsize;
+ pktcount = mtd->writesize / pktsize;
+ }
+
+ anfc_setpktszcnt(nfc, pktsize, pktcount);
+
+ if (nfc->dma) {
+ paddr = dma_map_single(nfc->dev, (void *)buf, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(nfc->dev, paddr)) {
+ dev_err(nfc->dev, "Write buffer mapping error");
+ return;
+ }
+ lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+ writel(PROG_PGPROG, nfc->base + PROG_OFST);
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+ dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
+ return;
+ }
+
+ anfc_enable_intrs(nfc, WRITE_READY);
+ writel(PROG_PGPROG, nfc->base + PROG_OFST);
+
+ while (buf_wr_cnt < pktcount) {
+ anfc_wait_for_event(nfc, WRITE_READY);
+
+ buf_wr_cnt++;
+ if (buf_wr_cnt == pktcount)
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+
+ writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
+ bufptr += pktsize/4;
+
+ if (buf_wr_cnt < pktcount)
+ anfc_enable_intrs(nfc, WRITE_READY);
+ }
+
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+}
+
+static int anfc_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ u32 val;
+ struct anfc *nfc = to_anfc(mtd);
+
+ anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
+
+ val = readl(nfc->base + CMD_OFST);
+ val = val | ECC_ENABLE;
+ writel(val, nfc->base + CMD_OFST);
+
+ if (nfc->dma)
+ nfc->rdintrmask = XFER_COMPLETE;
+ else
+ nfc->rdintrmask = READ_READY;
+
+ if (!nfc->bch)
+ nfc->rdintrmask = MBIT_ERROR;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+
+ val = readl(nfc->base + ECC_ERR_CNT_OFST);
+ if (nfc->bch) {
+ mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
+ } else {
+ val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
+ mtd->ecc_stats.corrected += val;
+ val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
+ mtd->ecc_stats.failed += val;
+ /* Clear ecc error count register 1Bit, 2Bit */
+ writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
+ writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
+ }
+ nfc->err = false;
+
+ if (oob_required)
+ chip->ecc.read_oob(mtd, chip, page);
+
+ return 0;
+}
+
+static int anfc_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ u32 val;
+ unsigned int i;
+ struct anfc *nfc = to_anfc(mtd);
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
+
+ val = readl(nfc->base + CMD_OFST);
+ val = val | ECC_ENABLE;
+ writel(val, nfc->base + CMD_OFST);
+
+ chip->write_buf(mtd, buf, mtd->writesize);
+
+ if (oob_required) {
+ anfc_device_ready(mtd, chip);
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ if (nfc->dma)
+ nfc->rdintrmask = XFER_COMPLETE;
+ else
+ nfc->rdintrmask = READ_READY;
+ chip->read_buf(mtd, ecc_calc, mtd->oobsize);
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
+ chip->ecc.write_oob(mtd, chip, page);
+ }
+
+ return 0;
+}
+
+static u8 anfc_read_byte(struct mtd_info *mtd)
+{
+ struct anfc *nfc = to_anfc(mtd);
+
+ return nfc->buf[nfc->bufshift++];
+}
+
+static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
+{
+ u32 *bufptr = (u32 *)buf;
+
+ anfc_enable_intrs(nfc, WRITE_READY);
+
+ writel(prog, nfc->base + PROG_OFST);
+ anfc_wait_for_event(nfc, WRITE_READY);
+
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+ writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+}
+
+static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
+{
+ u32 *bufptr = (u32 *)nfc->buf;
+
+ anfc_enable_intrs(nfc, READ_READY);
+
+ writel(prog, nfc->base + PROG_OFST);
+ anfc_wait_for_event(nfc, READ_READY);
+
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+ readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+}
+
+static int anfc_ecc_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ u32 ecc_addr, regval;
+ unsigned int bchmode = 0, i, oob_index;
+ struct nand_chip *nand_chip = mtd->priv;
+ struct anfc *nfc = to_anfc(mtd);
+ int found = -1;
+
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.read_page = anfc_read_page_hwecc;
+ nand_chip->ecc.write_page = anfc_write_page_hwecc;
+ nand_chip->ecc.write_oob = anfc_write_oob;
+ nand_chip->ecc.read_oob = anfc_read_oob;
+
+ for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
+ if ((ecc_matrix[i].pagesize == mtd->writesize) &&
+ (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
+ found = i;
+ if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
+ break;
+ }
+ }
+
+ if (found < 0) {
+ dev_err(nfc->dev, "ECC scheme not supported");
+ return 1;
+ }
+ if (ecc_matrix[found].bch) {
+ switch (ecc_matrix[found].eccbits) {
+ case 12:
+ bchmode = 0x1;
+ break;
+ case 8:
+ bchmode = 0x2;
+ break;
+ case 4:
+ bchmode = 0x3;
+ break;
+ case 24:
+ bchmode = 0x4;
+ break;
+ default:
+ bchmode = 0x0;
+ }
+ }
+
+ nand_chip->ecc.strength = ecc_matrix[found].eccbits;
+ nand_chip->ecc.size = ecc_matrix[found].codeword_size;
+ nand_chip->ecc.steps = ecc_matrix[found].pagesize /
+ ecc_matrix[found].codeword_size;
+ nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
+ nand_chip->ecc.steps;
+ nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
+ nfc->bch = ecc_matrix[found].bch;
+ oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
+ ecc_addr = mtd->writesize + oob_index;
+
+ for (i = 0; i < nand_chip->ecc.size; i++)
+ nfc->ecclayout.eccpos[i] = oob_index + i;
+
+ nfc->ecclayout.oobfree->offset = 2;
+ nfc->ecclayout.oobfree->length = oob_index -
+ nfc->ecclayout.oobfree->offset;
+
+ nand_chip->ecc.layout = &nfc->ecclayout;
+ regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
+ (ecc_matrix[found].bch << BCH_EN_SHIFT);
+ writel(regval, nfc->base + ECC_OFST);
+
+ regval = readl(nfc->base + MEM_ADDR2_OFST);
+ regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
+ writel(regval, nfc->base + MEM_ADDR2_OFST);
+
+ if (nand_chip->ecc_step_ds >= 1024)
+ nfc->pktsize = 1024;
+ else
+ nfc->pktsize = 512;
+
+ return 0;
+}
+
+static void anfc_cmd_function(struct mtd_info *mtd,
+ unsigned int cmd, int column, int page_addr)
+{
+ struct anfc *nfc = to_anfc(mtd);
+ bool wait = false, read = false;
+ u32 addrcycles, prog;
+ u32 *bufptr = (u32 *)nfc->buf;
+
+ nfc->bufshift = 0;
+ nfc->curr_cmd = cmd;
+
+ if (page_addr == -1)
+ page_addr = 0;
+ if (column == -1)
+ column = 0;
+
+ switch (cmd) {
+ case NAND_CMD_RESET:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
+ prog = PROG_RST;
+ wait = true;
+ break;
+ case NAND_CMD_SEQIN:
+ addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
+ anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
+ mtd->writesize, addrcycles);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ break;
+ case NAND_CMD_READOOB:
+ column += mtd->writesize;
+ case NAND_CMD_READ0:
+ case NAND_CMD_READ1:
+ addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
+ anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
+ mtd->writesize, addrcycles);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ break;
+ case NAND_CMD_RNDOUT:
+ anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
+ mtd->writesize, 2);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ if (nfc->dma)
+ nfc->rdintrmask = XFER_COMPLETE;
+ else
+ nfc->rdintrmask = READ_READY;
+ break;
+ case NAND_CMD_PARAM:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
+ anfc_readfifo(nfc, PROG_RDPARAM,
+ sizeof(struct nand_onfi_params));
+ break;
+ case NAND_CMD_READID:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
+ anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
+ break;
+ case NAND_CMD_ERASE1:
+ addrcycles = nfc->raddr_cycles;
+ prog = PROG_ERASE;
+ anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
+ column = page_addr & 0xffff;
+ page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ wait = true;
+ break;
+ case NAND_CMD_STATUS:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
+ anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ prog = PROG_STATUS;
+ wait = read = true;
+ break;
+ case NAND_CMD_GET_FEATURES:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ anfc_setpktszcnt(nfc, nfc->spktsize, 1);
+ anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
+ break;
+ case NAND_CMD_SET_FEATURES:
+ anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
+ anfc_setpagecoladdr(nfc, page_addr, column);
+ anfc_setpktszcnt(nfc, nfc->spktsize, 1);
+ break;
+ default:
+ return;
+ }
+
+ if (wait) {
+ anfc_enable_intrs(nfc, XFER_COMPLETE);
+ writel(prog, nfc->base + PROG_OFST);
+ anfc_wait_for_event(nfc, XFER_COMPLETE);
+ }
+
+ if (read)
+ bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
+}
+
+static void anfc_select_chip(struct mtd_info *mtd, int num)
+{
+ u32 val;
+ struct anfc *nfc = to_anfc(mtd);
+
+ if (num == -1)
+ return;
+
+ val = readl(nfc->base + MEM_ADDR2_OFST);
+ val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
+ writel(val, nfc->base + MEM_ADDR2_OFST);
+}
+
+static irqreturn_t anfc_irq_handler(int irq, void *ptr)
+{
+ struct anfc *nfc = ptr;
+ u32 regval = 0, status;
+
+ status = readl(nfc->base + INTR_STS_OFST);
+ if (status & XFER_COMPLETE) {
+ complete(&nfc->xfercomp);
+ regval |= XFER_COMPLETE;
+ }
+
+ if (status & READ_READY) {
+ complete(&nfc->bufrdy);
+ regval |= READ_READY;
+ }
+
+ if (status & WRITE_READY) {
+ complete(&nfc->bufrdy);
+ regval |= WRITE_READY;
+ }
+
+ if (status & MBIT_ERROR) {
+ nfc->err = true;
+ complete(&nfc->bufrdy);
+ regval |= MBIT_ERROR;
+ }
+
+ if (regval) {
+ writel(regval, nfc->base + INTR_STS_OFST);
+ writel(0, nfc->base + INTR_STS_EN_OFST);
+ writel(0, nfc->base + INTR_SIG_EN_OFST);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, uint8_t *subfeature_param)
+{
+ struct anfc *nfc = to_anfc(mtd);
+ int status;
+
+ if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
+ & ONFI_OPT_CMD_SET_GET_FEATURES))
+ return -EINVAL;
+
+ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
+ anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static int anfc_init_timing_mode(struct anfc *nfc)
+{
+ int mode, err;
+ unsigned int feature[2], regval, i;
+ struct nand_chip *chip = &nfc->chip;
+ struct mtd_info *mtd = &nfc->mtd;
+
+ memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
+ /* Get nvddr timing modes */
+ mode = onfi_get_sync_timing_mode(chip) & 0xff;
+ if (!mode) {
+ mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
+ regval = mode;
+ } else {
+ mode = fls(mode) - 1;
+ regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
+ mode |= ONFI_DATA_INTERFACE_NVDDR;
+ }
+
+ feature[0] = mode;
+ for (i = 0; i < nfc->num_cs; i++) {
+ chip->select_chip(mtd, i);
+ err = chip->onfi_set_features(mtd, chip,
+ ONFI_FEATURE_ADDR_TIMING_MODE,
+ (uint8_t *)feature);
+ if (err)
+ return err;
+ }
+ writel(regval, nfc->base + DATA_INTERFACE_REG);
+
+ if (mode & ONFI_DATA_INTERFACE_NVDDR)
+ nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
+
+ return 0;
+}
+
+static int anfc_probe(struct platform_device *pdev)
+{
+ struct anfc *nfc;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ struct resource *res;
+ struct mtd_part_parser_data ppdata;
+ int err;
+
+ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->base))
+ return PTR_ERR(nfc->base);
+
+ mtd = &nfc->mtd;
+ nand_chip = &nfc->chip;
+ nand_chip->priv = nfc;
+ mtd->priv = nand_chip;
+ mtd->name = DRIVER_NAME;
+ nfc->dev = &pdev->dev;
+ mtd->dev.parent = &pdev->dev;
+
+ nand_chip->cmdfunc = anfc_cmd_function;
+ nand_chip->waitfunc = anfc_device_ready;
+ nand_chip->chip_delay = 30;
+ nand_chip->read_buf = anfc_read_buf;
+ nand_chip->write_buf = anfc_write_buf;
+ nand_chip->read_byte = anfc_read_byte;
+ nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
+ nand_chip->bbt_options = NAND_BBT_USE_FLASH;
+ nand_chip->select_chip = anfc_select_chip;
+ nand_chip->onfi_set_features = anfc_onfi_set_features;
+ nfc->dma = of_property_read_bool(pdev->dev.of_node,
+ "arasan,has-mdma");
+ nfc->num_cs = 1;
+ of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);
+ platform_set_drvdata(pdev, nfc);
+ init_completion(&nfc->bufrdy);
+ init_completion(&nfc->xfercomp);
+ nfc->irq = platform_get_irq(pdev, 0);
+ if (nfc->irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ return -ENXIO;
+ }
+ err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
+ 0, "arasannfc", nfc);
+ if (err)
+ return err;
+ nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
+ if (IS_ERR(nfc->clk_sys)) {
+ dev_err(&pdev->dev, "sys clock not found.\n");
+ return PTR_ERR(nfc->clk_sys);
+ }
+
+ nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
+ if (IS_ERR(nfc->clk_flash)) {
+ dev_err(&pdev->dev, "flash clock not found.\n");
+ return PTR_ERR(nfc->clk_flash);
+ }
+
+ err = clk_prepare_enable(nfc->clk_sys);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to enable sys clock.\n");
+ return err;
+ }
+
+ err = clk_prepare_enable(nfc->clk_flash);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to enable flash clock.\n");
+ goto clk_dis_sys;
+ }
+
+ nfc->spktsize = SDR_MODE_PACKET_SIZE;
+ err = nand_scan_ident(mtd, nfc->num_cs, NULL);
+ if (err) {
+ dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
+ goto clk_dis_all;
+ }
+ if (nand_chip->onfi_version) {
+ nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
+ nfc->caddr_cycles =
+ (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
+ } else {
+ /*For non-ONFI devices, configuring the address cyles as 5 */
+ nfc->raddr_cycles = nfc->caddr_cycles = 5;
+ }
+
+ err = anfc_init_timing_mode(nfc);
+ if (err) {
+ dev_err(&pdev->dev, "timing mode init failed\n");
+ goto clk_dis_all;
+ }
+
+ err = anfc_ecc_init(mtd, &nand_chip->ecc);
+ if (err)
+ goto clk_dis_all;
+
+ err = nand_scan_tail(mtd);
+ if (err) {
+ dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
+ goto clk_dis_all;
+ }
+
+ ppdata.of_node = pdev->dev.of_node;
+
+ err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);
+ if (err)
+ goto clk_dis_all;
+
+ return 0;
+
+clk_dis_all:
+ clk_disable_unprepare(nfc->clk_flash);
+clk_dis_sys:
+ clk_disable_unprepare(nfc->clk_sys);
+
+ return err;
+}
+
+static int anfc_remove(struct platform_device *pdev)
+{
+ struct anfc *nfc = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(nfc->clk_sys);
+ clk_disable_unprepare(nfc->clk_flash);
+
+ nand_release(&nfc->mtd);
+
+ return 0;
+}
+
+static const struct of_device_id anfc_ids[] = {
+ { .compatible = "arasan,nfc-v3p10" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, anfc_ids);
+
+static struct platform_driver anfc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = anfc_ids,
+ },
+ .probe = anfc_probe,
+ .remove = anfc_remove,
+};
+module_platform_driver(anfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xilinx, Inc");
+MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");
--
2.1.2


2016-03-08 00:10:59

by Brian Norris

[permalink] [raw]
Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

+ Boris

Punnaiah,

Can you fix the threading on your mail client? Your patch series does
not appear as a thread, because you didn't get the mail headers right
(References and In-Reply-To). If you're having trouble, try git-send-email.

On Sat, Nov 21, 2015 at 08:09:48PM +0530, Punnaiah Choudary Kalluri wrote:
> Added the basic driver for Arasan Nand Flash Controller used in
> Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
> correction.
>
> Signed-off-by: Punnaiah Choudary Kalluri <[email protected]>
> ---
> Changes in v5:
> - Renamed the driver filei as arasan_nand.c
> - Fixed all comments relaqted coding style
> - Fixed comments related to propagating the errors
> - Modified the anfc_write_page_hwecc as per the write_page
> prototype
> Changes in v4:
> - Added support for onfi timing mode configuration
> - Added clock supppport
> - Added support for multiple chipselects
> Changes in v3:
> - Removed unused variables
> - Avoided busy loop and used jifies based implementation
> - Fixed compiler warnings "right shift count >= width of type"
> - Removed unneeded codei and improved error reporting
> - Added onfi version check to ensure reading the valid address cycles
> Changes in v2:
> - Added missing of.h to avoid kbuild system report error
> ---
> drivers/mtd/nand/Kconfig | 6 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1017 insertions(+)
> create mode 100644 drivers/mtd/nand/arasan_nand.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 2896640..9c620fb 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -546,4 +546,10 @@ config MTD_NAND_HISI504
> help
> Enables support for NAND controller on Hisilicon SoC Hip04.
>
> +config MTD_NAND_ARASAN

I think you have some missing dependencies here, like HAS_IOMEM.

> + tristate "Support for Arasan Nand Flash controller"
> + help
> + Enables the driver for the Arasan Nand Flash controller on
> + Zynq UltraScale+ MPSoC.
> +
> endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 2c7f014..3b993cb 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
> obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
> obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
> +obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
>
> nand-objs := nand_base.o nand_bbt.o nand_timings.o
> diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
> new file mode 100644
> index 0000000..e882e63
> --- /dev/null
> +++ b/drivers/mtd/nand/arasan_nand.c
> @@ -0,0 +1,1010 @@
> +/*
> + * Arasan Nand Flash Controller Driver
> + *
> + * Copyright (C) 2014 - 2015 Xilinx, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/of.h>
> +#include <linux/of_mtd.h>
> +#include <linux/platform_device.h>
> +
> +#define DRIVER_NAME "arasan_nand"
> +#define EVNT_TIMEOUT 1000
> +#define STATUS_TIMEOUT 2000
> +
> +#define PKT_OFST 0x00
> +#define MEM_ADDR1_OFST 0x04
> +#define MEM_ADDR2_OFST 0x08
> +#define CMD_OFST 0x0C
> +#define PROG_OFST 0x10
> +#define INTR_STS_EN_OFST 0x14
> +#define INTR_SIG_EN_OFST 0x18
> +#define INTR_STS_OFST 0x1C
> +#define READY_STS_OFST 0x20
> +#define DMA_ADDR1_OFST 0x24
> +#define FLASH_STS_OFST 0x28
> +#define DATA_PORT_OFST 0x30
> +#define ECC_OFST 0x34
> +#define ECC_ERR_CNT_OFST 0x38
> +#define ECC_SPR_CMD_OFST 0x3C
> +#define ECC_ERR_CNT_1BIT_OFST 0x40
> +#define ECC_ERR_CNT_2BIT_OFST 0x44
> +#define DMA_ADDR0_OFST 0x50
> +#define DATA_INTERFACE_REG 0x6C
> +
> +#define PKT_CNT_SHIFT 12
> +
> +#define ECC_ENABLE BIT(31)
> +#define DMA_EN_MASK GENMASK(27, 26)
> +#define DMA_ENABLE 0x2
> +#define DMA_EN_SHIFT 26
> +#define REG_PAGE_SIZE_MASK GENMASK(25, 23)
> +#define REG_PAGE_SIZE_SHIFT 23
> +#define REG_PAGE_SIZE_512 0
> +#define REG_PAGE_SIZE_1K 5
> +#define REG_PAGE_SIZE_2K 1
> +#define REG_PAGE_SIZE_4K 2
> +#define REG_PAGE_SIZE_8K 3
> +#define REG_PAGE_SIZE_16K 4
> +#define CMD2_SHIFT 8
> +#define ADDR_CYCLES_SHIFT 28
> +
> +#define XFER_COMPLETE BIT(2)
> +#define READ_READY BIT(1)
> +#define WRITE_READY BIT(0)
> +#define MBIT_ERROR BIT(3)
> +#define ERR_INTRPT BIT(4)
> +
> +#define PROG_PGRD BIT(0)
> +#define PROG_ERASE BIT(2)
> +#define PROG_STATUS BIT(3)
> +#define PROG_PGPROG BIT(4)
> +#define PROG_RDID BIT(6)
> +#define PROG_RDPARAM BIT(7)
> +#define PROG_RST BIT(8)
> +#define PROG_GET_FEATURE BIT(9)
> +#define PROG_SET_FEATURE BIT(10)
> +
> +#define ONFI_STATUS_FAIL BIT(0)
> +#define ONFI_STATUS_READY BIT(6)
> +
> +#define PG_ADDR_SHIFT 16
> +#define BCH_MODE_SHIFT 25
> +#define BCH_EN_SHIFT 27
> +#define ECC_SIZE_SHIFT 16
> +
> +#define MEM_ADDR_MASK GENMASK(7, 0)
> +#define BCH_MODE_MASK GENMASK(27, 25)
> +
> +#define CS_MASK GENMASK(31, 30)
> +#define CS_SHIFT 30
> +
> +#define PAGE_ERR_CNT_MASK GENMASK(16, 8)
> +#define PKT_ERR_CNT_MASK GENMASK(7, 0)
> +
> +#define NVDDR_MODE BIT(9)
> +#define NVDDR_TIMING_MODE_SHIFT 3
> +
> +#define ONFI_ID_LEN 8
> +#define TEMP_BUF_SIZE 512
> +#define NVDDR_MODE_PACKET_SIZE 8
> +#define SDR_MODE_PACKET_SIZE 4
> +
> +/**
> + * struct anfc_ecc_matrix - Defines ecc information storage format
> + * @pagesize: Page size in bytes.
> + * @codeword_size: Code word size information.
> + * @eccbits: Number of ecc bits.
> + * @bch: Bch / Hamming mode enable/disable.
> + * @eccsize: Ecc size information.
> + */
> +struct anfc_ecc_matrix {
> + u32 pagesize;
> + u32 codeword_size;
> + u8 eccbits;
> + u8 bch;
> + u16 eccsize;
> +};
> +
> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> + {512, 512, 1, 0, 0x3},
> + {512, 512, 4, 1, 0x7},
> + {512, 512, 8, 1, 0xD},
> + /* 2K byte page */
> + {2048, 512, 1, 0, 0xC},
> + {2048, 512, 4, 1, 0x1A},
> + {2048, 512, 8, 1, 0x34},
> + {2048, 512, 12, 1, 0x4E},
> + {2048, 1024, 24, 1, 0x54},
> + /* 4K byte page */
> + {4096, 512, 1, 0, 0x18},
> + {4096, 512, 4, 1, 0x34},
> + {4096, 512, 8, 1, 0x68},
> + {4096, 512, 12, 1, 0x9C},
> + {4096, 1024, 4, 1, 0xA8},
> + /* 8K byte page */
> + {8192, 512, 1, 0, 0x30},
> + {8192, 512, 4, 1, 0x68},
> + {8192, 512, 8, 1, 0xD0},
> + {8192, 512, 12, 1, 0x138},
> + {8192, 1024, 24, 1, 0x150},
> + /* 16K byte page */
> + {16384, 512, 1, 0, 0x60},
> + {16384, 512, 4, 1, 0xD0},
> + {16384, 512, 8, 1, 0x1A0},
> + {16384, 512, 12, 1, 0x270},
> + {16384, 1024, 24, 1, 0x2A0}
> +};
> +
> +/**
> + * struct anfc - Defines the Arasan NAND flash driver instance
> + * @chip: NAND chip information structure.
> + * @mtd: MTD information structure.
> + * @dev: Pointer to the device structure.
> + * @base: Virtual address of the NAND flash device.
> + * @curr_cmd: Current command issued.
> + * @clk_sys: Pointer to the system clock.
> + * @clk_flash: Pointer to the flash clock.
> + * @dma: Dma enable/disable.
> + * @bch: Bch / Hamming mode enable/disable.
> + * @err: Error identifier.
> + * @iswriteoob: Identifies if oob write operation is required.
> + * @buf: Buffer used for read/write byte operations.
> + * @raddr_cycles: Row address cycle information.
> + * @caddr_cycles: Column address cycle information.
> + * @irq: irq number
> + * @pktsize: Packet size for read / write operation.
> + * @bufshift: Variable used for indexing buffer operation
> + * @rdintrmask: Interrupt mask value for read operation.
> + * @num_cs: Number of chip selects in use.
> + * @spktsize: Packet size in ddr mode for status operation.
> + * @bufrdy: Completion event for buffer ready.
> + * @xfercomp: Completion event for transfer complete.
> + * @ecclayout: Ecc layout object
> + */
> +struct anfc {
> + struct nand_chip chip;
> + struct mtd_info mtd;
> + struct device *dev;
> +
> + void __iomem *base;
> + int curr_cmd;
> + struct clk *clk_sys;
> + struct clk *clk_flash;
> +
> + bool dma;
> + bool bch;
> + bool err;
> + bool iswriteoob;
> +
> + u8 buf[TEMP_BUF_SIZE];
> +
> + u16 raddr_cycles;
> + u16 caddr_cycles;
> +
> + u32 irq;
> + u32 pktsize;
> + u32 bufshift;
> + u32 rdintrmask;
> + u32 num_cs;
> + u32 spktsize;
> +
> + struct completion bufrdy;
> + struct completion xfercomp;
> + struct nand_ecclayout ecclayout;
> +};
> +
> +static inline struct anfc *to_anfc(struct mtd_info *mtd)
> +{
> + return container_of(mtd, struct anfc, mtd);
> +}
> +
> +static u8 anfc_page(u32 pagesize)
> +{
> + switch (pagesize) {
> + case 512:
> + return REG_PAGE_SIZE_512;
> + case 1024:
> + return REG_PAGE_SIZE_1K;
> + case 2048:
> + return REG_PAGE_SIZE_2K;
> + case 4096:
> + return REG_PAGE_SIZE_4K;
> + case 8192:
> + return REG_PAGE_SIZE_8K;
> + case 16384:
> + return REG_PAGE_SIZE_16K;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
> +{
> + writel(val, nfc->base + INTR_STS_EN_OFST);
> + writel(val, nfc->base + INTR_SIG_EN_OFST);
> +}
> +
> +static int anfc_wait_for_event(struct anfc *nfc, u32 event)
> +{
> + struct completion *comp;
> +
> + if (event == XFER_COMPLETE)
> + comp = &nfc->xfercomp;
> + else
> + comp = &nfc->bufrdy;
> +
> + return wait_for_completion_timeout(comp,
> + msecs_to_jiffies(EVNT_TIMEOUT));
> +}
> +
> +static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
> + u32 pktcount)
> +{
> + writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
> +}
> +
> +static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
> +{
> + writel(cmd1 | (cmd2 << CMD2_SHIFT) |
> + (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
> + nfc->base + ECC_SPR_CMD_OFST);
> +}
> +
> +static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
> +{
> + u32 val;
> +
> + writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
> +
> + val = readl(nfc->base + MEM_ADDR2_OFST);
> + val = (val & ~MEM_ADDR_MASK) |
> + ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
> + writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
> + u8 dmamode, u32 pagesize, u8 addrcycles)
> +{
> + u32 regval;
> +
> + regval = cmd1 | (cmd2 << CMD2_SHIFT);
> + if (dmamode && nfc->dma)
> + regval |= DMA_ENABLE << DMA_EN_SHIFT;
> + if (addrcycles)
> + regval |= addrcycles << ADDR_CYCLES_SHIFT;
> + if (pagesize)
> + regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
> + writel(regval, nfc->base + CMD_OFST);
> +}
> +
> +static int anfc_device_ready(struct mtd_info *mtd,
> + struct nand_chip *chip)
> +{
> + u8 status;
> + unsigned long timeout = jiffies + STATUS_TIMEOUT;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + do {
> + chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
> + status = chip->read_byte(mtd);
> + if (status & ONFI_STATUS_READY)
> + break;
> + cpu_relax();
> + } while (!time_after_eq(jiffies, timeout));
> +
> + if (status & ONFI_STATUS_FAIL)
> + return NAND_STATUS_FAIL;
> +
> + if (time_after_eq(jiffies, timeout)) {
> + dev_err(nfc->dev, "%s timed out\n", __func__);
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
> + int page)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> + return 0;
> +}
> +
> +static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
> + int page)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + nfc->iswriteoob = true;
> + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
> + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
> + nfc->iswriteoob = false;
> +
> + return 0;
> +}
> +
> +static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> + u32 pktcount, pktsize;
> + unsigned int buf_rd_cnt = 0;
> + u32 *bufptr = (u32 *)buf;
> + struct anfc *nfc = to_anfc(mtd);
> + dma_addr_t paddr;
> +
> + if (nfc->curr_cmd == NAND_CMD_READ0) {
> + pktsize = nfc->pktsize;
> + pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
> + } else {
> + pktsize = len;
> + pktcount = 1;
> + }
> +
> + anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> + if (nfc->dma) {
> + paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
> + if (dma_mapping_error(nfc->dev, paddr)) {
> + dev_err(nfc->dev, "Read buffer mapping error");
> + return;
> + }
> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + writel(PROG_PGRD, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
> + return;
> + }
> +
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + writel(PROG_PGRD, nfc->base + PROG_OFST);
> +
> + while (buf_rd_cnt < pktcount) {
> +
> + anfc_wait_for_event(nfc, READ_READY);
> + buf_rd_cnt++;
> +
> + if (buf_rd_cnt == pktcount)
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> + readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> + bufptr += pktsize/4;
> +
> + if (buf_rd_cnt < pktcount)
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + }
> +
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
> +{
> + u32 pktcount, pktsize;
> + unsigned int buf_wr_cnt = 0;
> + u32 *bufptr = (u32 *)buf;
> + struct anfc *nfc = to_anfc(mtd);
> + dma_addr_t paddr;
> +
> + if (nfc->iswriteoob) {
> + pktsize = len;
> + pktcount = 1;
> + } else {
> + pktsize = nfc->pktsize;
> + pktcount = mtd->writesize / pktsize;
> + }
> +
> + anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> + if (nfc->dma) {
> + paddr = dma_map_single(nfc->dev, (void *)buf, len,
> + DMA_TO_DEVICE);
> + if (dma_mapping_error(nfc->dev, paddr)) {
> + dev_err(nfc->dev, "Write buffer mapping error");
> + return;
> + }
> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
> + return;
> + }
> +
> + anfc_enable_intrs(nfc, WRITE_READY);
> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
> +
> + while (buf_wr_cnt < pktcount) {
> + anfc_wait_for_event(nfc, WRITE_READY);
> +
> + buf_wr_cnt++;
> + if (buf_wr_cnt == pktcount)
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> + writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> + bufptr += pktsize/4;
> +
> + if (buf_wr_cnt < pktcount)
> + anfc_enable_intrs(nfc, WRITE_READY);
> + }
> +
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip, uint8_t *buf,
> + int oob_required, int page)
> +{
> + u32 val;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
> +
> + val = readl(nfc->base + CMD_OFST);
> + val = val | ECC_ENABLE;
> + writel(val, nfc->base + CMD_OFST);
> +
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> +
> + if (!nfc->bch)
> + nfc->rdintrmask = MBIT_ERROR;
> +
> + chip->read_buf(mtd, buf, mtd->writesize);
> +
> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
> + if (nfc->bch) {
> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> + } else {
> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> + mtd->ecc_stats.corrected += val;
> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> + mtd->ecc_stats.failed += val;
> + /* Clear ecc error count register 1Bit, 2Bit */
> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> + }
> + nfc->err = false;
> +
> + if (oob_required)
> + chip->ecc.read_oob(mtd, chip, page);
> +
> + return 0;
> +}
> +
> +static int anfc_write_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip, const uint8_t *buf,
> + int oob_required, int page)
> +{
> + u32 val;
> + unsigned int i;
> + struct anfc *nfc = to_anfc(mtd);
> + uint8_t *ecc_calc = chip->buffers->ecccalc;
> + uint32_t *eccpos = chip->ecc.layout->eccpos;
> +
> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
> +
> + val = readl(nfc->base + CMD_OFST);
> + val = val | ECC_ENABLE;
> + writel(val, nfc->base + CMD_OFST);
> +
> + chip->write_buf(mtd, buf, mtd->writesize);
> +
> + if (oob_required) {
> + anfc_device_ready(mtd, chip);
> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> + chip->read_buf(mtd, ecc_calc, mtd->oobsize);
> + for (i = 0; i < chip->ecc.total; i++)
> + chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
> + chip->ecc.write_oob(mtd, chip, page);
> + }
> +
> + return 0;
> +}
> +
> +static u8 anfc_read_byte(struct mtd_info *mtd)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + return nfc->buf[nfc->bufshift++];
> +}
> +
> +static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
> +{
> + u32 *bufptr = (u32 *)buf;
> +
> + anfc_enable_intrs(nfc, WRITE_READY);
> +
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, WRITE_READY);
> +
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
> +{
> + u32 *bufptr = (u32 *)nfc->buf;
> +
> + anfc_enable_intrs(nfc, READ_READY);
> +
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, READ_READY);
> +
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static int anfc_ecc_init(struct mtd_info *mtd,
> + struct nand_ecc_ctrl *ecc)
> +{
> + u32 ecc_addr, regval;
> + unsigned int bchmode = 0, i, oob_index;
> + struct nand_chip *nand_chip = mtd->priv;
> + struct anfc *nfc = to_anfc(mtd);
> + int found = -1;
> +
> + nand_chip->ecc.mode = NAND_ECC_HW;
> + nand_chip->ecc.read_page = anfc_read_page_hwecc;
> + nand_chip->ecc.write_page = anfc_write_page_hwecc;
> + nand_chip->ecc.write_oob = anfc_write_oob;
> + nand_chip->ecc.read_oob = anfc_read_oob;
> +
> + for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
> + if ((ecc_matrix[i].pagesize == mtd->writesize) &&
> + (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
> + found = i;
> + if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
> + break;
> + }
> + }
> +
> + if (found < 0) {
> + dev_err(nfc->dev, "ECC scheme not supported");
> + return 1;
> + }
> + if (ecc_matrix[found].bch) {
> + switch (ecc_matrix[found].eccbits) {
> + case 12:
> + bchmode = 0x1;
> + break;
> + case 8:
> + bchmode = 0x2;
> + break;
> + case 4:
> + bchmode = 0x3;
> + break;
> + case 24:
> + bchmode = 0x4;
> + break;
> + default:
> + bchmode = 0x0;
> + }
> + }
> +
> + nand_chip->ecc.strength = ecc_matrix[found].eccbits;
> + nand_chip->ecc.size = ecc_matrix[found].codeword_size;
> + nand_chip->ecc.steps = ecc_matrix[found].pagesize /
> + ecc_matrix[found].codeword_size;
> + nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
> + nand_chip->ecc.steps;
> + nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
> + nfc->bch = ecc_matrix[found].bch;
> + oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
> + ecc_addr = mtd->writesize + oob_index;
> +
> + for (i = 0; i < nand_chip->ecc.size; i++)
> + nfc->ecclayout.eccpos[i] = oob_index + i;
> +
> + nfc->ecclayout.oobfree->offset = 2;
> + nfc->ecclayout.oobfree->length = oob_index -
> + nfc->ecclayout.oobfree->offset;
> +
> + nand_chip->ecc.layout = &nfc->ecclayout;

FYI, we're deprecating this usage of ecclayout. You might take a look at Boris'
latest (which we'll probably merge very soon):

http://lists.infradead.org/pipermail/linux-mtd/2016-March/065925.html

> + regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
> + (ecc_matrix[found].bch << BCH_EN_SHIFT);
> + writel(regval, nfc->base + ECC_OFST);
> +
> + regval = readl(nfc->base + MEM_ADDR2_OFST);
> + regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
> + writel(regval, nfc->base + MEM_ADDR2_OFST);
> +
> + if (nand_chip->ecc_step_ds >= 1024)
> + nfc->pktsize = 1024;
> + else
> + nfc->pktsize = 512;
> +
> + return 0;
> +}
> +
> +static void anfc_cmd_function(struct mtd_info *mtd,
> + unsigned int cmd, int column, int page_addr)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> + bool wait = false, read = false;
> + u32 addrcycles, prog;
> + u32 *bufptr = (u32 *)nfc->buf;
> +
> + nfc->bufshift = 0;
> + nfc->curr_cmd = cmd;
> +
> + if (page_addr == -1)
> + page_addr = 0;
> + if (column == -1)
> + column = 0;
> +
> + switch (cmd) {
> + case NAND_CMD_RESET:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> + prog = PROG_RST;
> + wait = true;
> + break;
> + case NAND_CMD_SEQIN:
> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> + mtd->writesize, addrcycles);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + break;
> + case NAND_CMD_READOOB:
> + column += mtd->writesize;
> + case NAND_CMD_READ0:
> + case NAND_CMD_READ1:
> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
> + mtd->writesize, addrcycles);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + break;
> + case NAND_CMD_RNDOUT:
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> + mtd->writesize, 2);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> + break;
> + case NAND_CMD_PARAM:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> + anfc_readfifo(nfc, PROG_RDPARAM,
> + sizeof(struct nand_onfi_params));
> + break;
> + case NAND_CMD_READID:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> + break;
> + case NAND_CMD_ERASE1:
> + addrcycles = nfc->raddr_cycles;
> + prog = PROG_ERASE;
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
> + column = page_addr & 0xffff;
> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + wait = true;
> + break;
> + case NAND_CMD_STATUS:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + prog = PROG_STATUS;
> + wait = read = true;
> + break;
> + case NAND_CMD_GET_FEATURES:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> + break;
> + case NAND_CMD_SET_FEATURES:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> + break;
> + default:
> + return;
> + }
> +
> + if (wait) {
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + }
> +
> + if (read)
> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
> +}
> +
> +static void anfc_select_chip(struct mtd_info *mtd, int num)
> +{
> + u32 val;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + if (num == -1)
> + return;
> +
> + val = readl(nfc->base + MEM_ADDR2_OFST);
> + val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
> + writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static irqreturn_t anfc_irq_handler(int irq, void *ptr)
> +{
> + struct anfc *nfc = ptr;
> + u32 regval = 0, status;
> +
> + status = readl(nfc->base + INTR_STS_OFST);
> + if (status & XFER_COMPLETE) {
> + complete(&nfc->xfercomp);
> + regval |= XFER_COMPLETE;
> + }
> +
> + if (status & READ_READY) {
> + complete(&nfc->bufrdy);
> + regval |= READ_READY;
> + }
> +
> + if (status & WRITE_READY) {
> + complete(&nfc->bufrdy);
> + regval |= WRITE_READY;
> + }
> +
> + if (status & MBIT_ERROR) {
> + nfc->err = true;
> + complete(&nfc->bufrdy);
> + regval |= MBIT_ERROR;
> + }
> +
> + if (regval) {
> + writel(regval, nfc->base + INTR_STS_OFST);
> + writel(0, nfc->base + INTR_STS_EN_OFST);
> + writel(0, nfc->base + INTR_SIG_EN_OFST);
> +
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}
> +
> +static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> + int addr, uint8_t *subfeature_param)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> + int status;
> +
> + if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
> + & ONFI_OPT_CMD_SET_GET_FEATURES))
> + return -EINVAL;
> +
> + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
> + anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
> +
> + status = chip->waitfunc(mtd, chip);
> + if (status & NAND_STATUS_FAIL)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int anfc_init_timing_mode(struct anfc *nfc)
> +{
> + int mode, err;
> + unsigned int feature[2], regval, i;
> + struct nand_chip *chip = &nfc->chip;
> + struct mtd_info *mtd = &nfc->mtd;
> +
> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> + /* Get nvddr timing modes */
> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
> + if (!mode) {
> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> + regval = mode;
> + } else {
> + mode = fls(mode) - 1;
> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
> + mode |= ONFI_DATA_INTERFACE_NVDDR;
> + }
> +
> + feature[0] = mode;
> + for (i = 0; i < nfc->num_cs; i++) {
> + chip->select_chip(mtd, i);
> + err = chip->onfi_set_features(mtd, chip,
> + ONFI_FEATURE_ADDR_TIMING_MODE,
> + (uint8_t *)feature);
> + if (err)
> + return err;
> + }
> + writel(regval, nfc->base + DATA_INTERFACE_REG);
> +
> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
> +
> + return 0;
> +}
> +
> +static int anfc_probe(struct platform_device *pdev)
> +{
> + struct anfc *nfc;
> + struct mtd_info *mtd;
> + struct nand_chip *nand_chip;
> + struct resource *res;
> + struct mtd_part_parser_data ppdata;
> + int err;
> +
> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> + if (!nfc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(nfc->base))
> + return PTR_ERR(nfc->base);
> +
> + mtd = &nfc->mtd;
> + nand_chip = &nfc->chip;
> + nand_chip->priv = nfc;
> + mtd->priv = nand_chip;
> + mtd->name = DRIVER_NAME;
> + nfc->dev = &pdev->dev;
> + mtd->dev.parent = &pdev->dev;
> +
> + nand_chip->cmdfunc = anfc_cmd_function;
> + nand_chip->waitfunc = anfc_device_ready;
> + nand_chip->chip_delay = 30;
> + nand_chip->read_buf = anfc_read_buf;
> + nand_chip->write_buf = anfc_write_buf;
> + nand_chip->read_byte = anfc_read_byte;
> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> + nand_chip->select_chip = anfc_select_chip;
> + nand_chip->onfi_set_features = anfc_onfi_set_features;
> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
> + "arasan,has-mdma");
> + nfc->num_cs = 1;
> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);

I get the feeling that this device tree binding doesn't support multiple
chips very well, but I can't find the v5 binding to confirm...

Normally, we suggest that you represent the NAND chip separate from the
NAND controller. See, e.g.,
Documentation/devicetree/bindings/mtd/sunxi-nand.txt which has:

nfc: nand@01c03000 {
compatible = "allwinner,sun4i-a10-nand";
reg = <0x01c03000 0x1000>;
...
nand@0 {
reg = <0>;
...
};

// you could have nand@1, nand@2, etc.
};


> + platform_set_drvdata(pdev, nfc);
> + init_completion(&nfc->bufrdy);
> + init_completion(&nfc->xfercomp);
> + nfc->irq = platform_get_irq(pdev, 0);
> + if (nfc->irq < 0) {

drivers/mtd/nand/arasan_nand.c: In function 'anfc_probe':
drivers/mtd/nand/arasan_nand.c:900:15: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits]
if (nfc->irq < 0) {
^

> + dev_err(&pdev->dev, "platform_get_irq failed\n");
> + return -ENXIO;
> + }
> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> + 0, "arasannfc", nfc);
> + if (err)
> + return err;
> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> + if (IS_ERR(nfc->clk_sys)) {
> + dev_err(&pdev->dev, "sys clock not found.\n");
> + return PTR_ERR(nfc->clk_sys);
> + }
> +
> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> + if (IS_ERR(nfc->clk_flash)) {
> + dev_err(&pdev->dev, "flash clock not found.\n");
> + return PTR_ERR(nfc->clk_flash);
> + }
> +
> + err = clk_prepare_enable(nfc->clk_sys);
> + if (err) {
> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> + return err;
> + }
> +
> + err = clk_prepare_enable(nfc->clk_flash);
> + if (err) {
> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> + goto clk_dis_sys;
> + }
> +
> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> + if (err) {
> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> + goto clk_dis_all;
> + }
> + if (nand_chip->onfi_version) {
> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
> + nfc->caddr_cycles =
> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> + } else {
> + /*For non-ONFI devices, configuring the address cyles as 5 */

Space after /*

> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
> + }
> +
> + err = anfc_init_timing_mode(nfc);
> + if (err) {
> + dev_err(&pdev->dev, "timing mode init failed\n");
> + goto clk_dis_all;
> + }
> +
> + err = anfc_ecc_init(mtd, &nand_chip->ecc);
> + if (err)
> + goto clk_dis_all;
> +
> + err = nand_scan_tail(mtd);
> + if (err) {
> + dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
> + goto clk_dis_all;
> + }
> +
> + ppdata.of_node = pdev->dev.of_node;

The of_node field is gone now. Just use nand_set_flash_node(). (Please rebase
on l2-mtd.git before submitting MTD changes. See:
http://linux-mtd.infradead.org/source.html )

> +
> + err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);

Then, you won't need ppdata, so you can just do:

err = mtd_device_register(&nfc->mtd, NULL, 0);

> + if (err)
> + goto clk_dis_all;
> +
> + return 0;
> +
> +clk_dis_all:
> + clk_disable_unprepare(nfc->clk_flash);
> +clk_dis_sys:
> + clk_disable_unprepare(nfc->clk_sys);
> +
> + return err;
> +}
> +
> +static int anfc_remove(struct platform_device *pdev)
> +{
> + struct anfc *nfc = platform_get_drvdata(pdev);
> +
> + clk_disable_unprepare(nfc->clk_sys);
> + clk_disable_unprepare(nfc->clk_flash);
> +
> + nand_release(&nfc->mtd);

Probably want to disable the clocks only after nand_release()?
Otherwise, you might have a race.

> +
> + return 0;
> +}
> +
> +static const struct of_device_id anfc_ids[] = {
> + { .compatible = "arasan,nfc-v3p10" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, anfc_ids);
> +
> +static struct platform_driver anfc_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = anfc_ids,
> + },
> + .probe = anfc_probe,
> + .remove = anfc_remove,
> +};
> +module_platform_driver(anfc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx, Inc");
> +MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");

Brian

Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

HI Brian,

On Tue, Mar 8, 2016 at 5:37 AM, Brian Norris
<[email protected]> wrote:
> + Boris
>
> Punnaiah,
>
> Can you fix the threading on your mail client? Your patch series does
> not appear as a thread, because you didn't get the mail headers right
> (References and In-Reply-To). If you're having trouble, try git-send-email.

Sure and thanks for the review. I will address the below commnets in v6 and
release the patches for review soon.

Regards,
Punnaiah.

>
> On Sat, Nov 21, 2015 at 08:09:48PM +0530, Punnaiah Choudary Kalluri wrote:
>> Added the basic driver for Arasan Nand Flash Controller used in
>> Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
>> correction.
>>
>> Signed-off-by: Punnaiah Choudary Kalluri <[email protected]>
>> ---
>> Changes in v5:
>> - Renamed the driver filei as arasan_nand.c
>> - Fixed all comments relaqted coding style
>> - Fixed comments related to propagating the errors
>> - Modified the anfc_write_page_hwecc as per the write_page
>> prototype
>> Changes in v4:
>> - Added support for onfi timing mode configuration
>> - Added clock supppport
>> - Added support for multiple chipselects
>> Changes in v3:
>> - Removed unused variables
>> - Avoided busy loop and used jifies based implementation
>> - Fixed compiler warnings "right shift count >= width of type"
>> - Removed unneeded codei and improved error reporting
>> - Added onfi version check to ensure reading the valid address cycles
>> Changes in v2:
>> - Added missing of.h to avoid kbuild system report error
>> ---
>> drivers/mtd/nand/Kconfig | 6 +
>> drivers/mtd/nand/Makefile | 1 +
>> drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 1017 insertions(+)
>> create mode 100644 drivers/mtd/nand/arasan_nand.c
>>
>> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
>> index 2896640..9c620fb 100644
>> --- a/drivers/mtd/nand/Kconfig
>> +++ b/drivers/mtd/nand/Kconfig
>> @@ -546,4 +546,10 @@ config MTD_NAND_HISI504
>> help
>> Enables support for NAND controller on Hisilicon SoC Hip04.
>>
>> +config MTD_NAND_ARASAN
>
> I think you have some missing dependencies here, like HAS_IOMEM.
>
>> + tristate "Support for Arasan Nand Flash controller"
>> + help
>> + Enables the driver for the Arasan Nand Flash controller on
>> + Zynq UltraScale+ MPSoC.
>> +
>> endif # MTD_NAND
>> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
>> index 2c7f014..3b993cb 100644
>> --- a/drivers/mtd/nand/Makefile
>> +++ b/drivers/mtd/nand/Makefile
>> @@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
>> obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
>> obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
>> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
>> +obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
>>
>> nand-objs := nand_base.o nand_bbt.o nand_timings.o
>> diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
>> new file mode 100644
>> index 0000000..e882e63
>> --- /dev/null
>> +++ b/drivers/mtd/nand/arasan_nand.c
>> @@ -0,0 +1,1010 @@
>> +/*
>> + * Arasan Nand Flash Controller Driver
>> + *
>> + * Copyright (C) 2014 - 2015 Xilinx, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation; either version 2 of the License, or (at your
>> + * option) any later version.
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io-64-nonatomic-lo-hi.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/nand.h>
>> +#include <linux/mtd/partitions.h>
>> +#include <linux/of.h>
>> +#include <linux/of_mtd.h>
>> +#include <linux/platform_device.h>
>> +
>> +#define DRIVER_NAME "arasan_nand"
>> +#define EVNT_TIMEOUT 1000
>> +#define STATUS_TIMEOUT 2000
>> +
>> +#define PKT_OFST 0x00
>> +#define MEM_ADDR1_OFST 0x04
>> +#define MEM_ADDR2_OFST 0x08
>> +#define CMD_OFST 0x0C
>> +#define PROG_OFST 0x10
>> +#define INTR_STS_EN_OFST 0x14
>> +#define INTR_SIG_EN_OFST 0x18
>> +#define INTR_STS_OFST 0x1C
>> +#define READY_STS_OFST 0x20
>> +#define DMA_ADDR1_OFST 0x24
>> +#define FLASH_STS_OFST 0x28
>> +#define DATA_PORT_OFST 0x30
>> +#define ECC_OFST 0x34
>> +#define ECC_ERR_CNT_OFST 0x38
>> +#define ECC_SPR_CMD_OFST 0x3C
>> +#define ECC_ERR_CNT_1BIT_OFST 0x40
>> +#define ECC_ERR_CNT_2BIT_OFST 0x44
>> +#define DMA_ADDR0_OFST 0x50
>> +#define DATA_INTERFACE_REG 0x6C
>> +
>> +#define PKT_CNT_SHIFT 12
>> +
>> +#define ECC_ENABLE BIT(31)
>> +#define DMA_EN_MASK GENMASK(27, 26)
>> +#define DMA_ENABLE 0x2
>> +#define DMA_EN_SHIFT 26
>> +#define REG_PAGE_SIZE_MASK GENMASK(25, 23)
>> +#define REG_PAGE_SIZE_SHIFT 23
>> +#define REG_PAGE_SIZE_512 0
>> +#define REG_PAGE_SIZE_1K 5
>> +#define REG_PAGE_SIZE_2K 1
>> +#define REG_PAGE_SIZE_4K 2
>> +#define REG_PAGE_SIZE_8K 3
>> +#define REG_PAGE_SIZE_16K 4
>> +#define CMD2_SHIFT 8
>> +#define ADDR_CYCLES_SHIFT 28
>> +
>> +#define XFER_COMPLETE BIT(2)
>> +#define READ_READY BIT(1)
>> +#define WRITE_READY BIT(0)
>> +#define MBIT_ERROR BIT(3)
>> +#define ERR_INTRPT BIT(4)
>> +
>> +#define PROG_PGRD BIT(0)
>> +#define PROG_ERASE BIT(2)
>> +#define PROG_STATUS BIT(3)
>> +#define PROG_PGPROG BIT(4)
>> +#define PROG_RDID BIT(6)
>> +#define PROG_RDPARAM BIT(7)
>> +#define PROG_RST BIT(8)
>> +#define PROG_GET_FEATURE BIT(9)
>> +#define PROG_SET_FEATURE BIT(10)
>> +
>> +#define ONFI_STATUS_FAIL BIT(0)
>> +#define ONFI_STATUS_READY BIT(6)
>> +
>> +#define PG_ADDR_SHIFT 16
>> +#define BCH_MODE_SHIFT 25
>> +#define BCH_EN_SHIFT 27
>> +#define ECC_SIZE_SHIFT 16
>> +
>> +#define MEM_ADDR_MASK GENMASK(7, 0)
>> +#define BCH_MODE_MASK GENMASK(27, 25)
>> +
>> +#define CS_MASK GENMASK(31, 30)
>> +#define CS_SHIFT 30
>> +
>> +#define PAGE_ERR_CNT_MASK GENMASK(16, 8)
>> +#define PKT_ERR_CNT_MASK GENMASK(7, 0)
>> +
>> +#define NVDDR_MODE BIT(9)
>> +#define NVDDR_TIMING_MODE_SHIFT 3
>> +
>> +#define ONFI_ID_LEN 8
>> +#define TEMP_BUF_SIZE 512
>> +#define NVDDR_MODE_PACKET_SIZE 8
>> +#define SDR_MODE_PACKET_SIZE 4
>> +
>> +/**
>> + * struct anfc_ecc_matrix - Defines ecc information storage format
>> + * @pagesize: Page size in bytes.
>> + * @codeword_size: Code word size information.
>> + * @eccbits: Number of ecc bits.
>> + * @bch: Bch / Hamming mode enable/disable.
>> + * @eccsize: Ecc size information.
>> + */
>> +struct anfc_ecc_matrix {
>> + u32 pagesize;
>> + u32 codeword_size;
>> + u8 eccbits;
>> + u8 bch;
>> + u16 eccsize;
>> +};
>> +
>> +static const struct anfc_ecc_matrix ecc_matrix[] = {
>> + {512, 512, 1, 0, 0x3},
>> + {512, 512, 4, 1, 0x7},
>> + {512, 512, 8, 1, 0xD},
>> + /* 2K byte page */
>> + {2048, 512, 1, 0, 0xC},
>> + {2048, 512, 4, 1, 0x1A},
>> + {2048, 512, 8, 1, 0x34},
>> + {2048, 512, 12, 1, 0x4E},
>> + {2048, 1024, 24, 1, 0x54},
>> + /* 4K byte page */
>> + {4096, 512, 1, 0, 0x18},
>> + {4096, 512, 4, 1, 0x34},
>> + {4096, 512, 8, 1, 0x68},
>> + {4096, 512, 12, 1, 0x9C},
>> + {4096, 1024, 4, 1, 0xA8},
>> + /* 8K byte page */
>> + {8192, 512, 1, 0, 0x30},
>> + {8192, 512, 4, 1, 0x68},
>> + {8192, 512, 8, 1, 0xD0},
>> + {8192, 512, 12, 1, 0x138},
>> + {8192, 1024, 24, 1, 0x150},
>> + /* 16K byte page */
>> + {16384, 512, 1, 0, 0x60},
>> + {16384, 512, 4, 1, 0xD0},
>> + {16384, 512, 8, 1, 0x1A0},
>> + {16384, 512, 12, 1, 0x270},
>> + {16384, 1024, 24, 1, 0x2A0}
>> +};
>> +
>> +/**
>> + * struct anfc - Defines the Arasan NAND flash driver instance
>> + * @chip: NAND chip information structure.
>> + * @mtd: MTD information structure.
>> + * @dev: Pointer to the device structure.
>> + * @base: Virtual address of the NAND flash device.
>> + * @curr_cmd: Current command issued.
>> + * @clk_sys: Pointer to the system clock.
>> + * @clk_flash: Pointer to the flash clock.
>> + * @dma: Dma enable/disable.
>> + * @bch: Bch / Hamming mode enable/disable.
>> + * @err: Error identifier.
>> + * @iswriteoob: Identifies if oob write operation is required.
>> + * @buf: Buffer used for read/write byte operations.
>> + * @raddr_cycles: Row address cycle information.
>> + * @caddr_cycles: Column address cycle information.
>> + * @irq: irq number
>> + * @pktsize: Packet size for read / write operation.
>> + * @bufshift: Variable used for indexing buffer operation
>> + * @rdintrmask: Interrupt mask value for read operation.
>> + * @num_cs: Number of chip selects in use.
>> + * @spktsize: Packet size in ddr mode for status operation.
>> + * @bufrdy: Completion event for buffer ready.
>> + * @xfercomp: Completion event for transfer complete.
>> + * @ecclayout: Ecc layout object
>> + */
>> +struct anfc {
>> + struct nand_chip chip;
>> + struct mtd_info mtd;
>> + struct device *dev;
>> +
>> + void __iomem *base;
>> + int curr_cmd;
>> + struct clk *clk_sys;
>> + struct clk *clk_flash;
>> +
>> + bool dma;
>> + bool bch;
>> + bool err;
>> + bool iswriteoob;
>> +
>> + u8 buf[TEMP_BUF_SIZE];
>> +
>> + u16 raddr_cycles;
>> + u16 caddr_cycles;
>> +
>> + u32 irq;
>> + u32 pktsize;
>> + u32 bufshift;
>> + u32 rdintrmask;
>> + u32 num_cs;
>> + u32 spktsize;
>> +
>> + struct completion bufrdy;
>> + struct completion xfercomp;
>> + struct nand_ecclayout ecclayout;
>> +};
>> +
>> +static inline struct anfc *to_anfc(struct mtd_info *mtd)
>> +{
>> + return container_of(mtd, struct anfc, mtd);
>> +}
>> +
>> +static u8 anfc_page(u32 pagesize)
>> +{
>> + switch (pagesize) {
>> + case 512:
>> + return REG_PAGE_SIZE_512;
>> + case 1024:
>> + return REG_PAGE_SIZE_1K;
>> + case 2048:
>> + return REG_PAGE_SIZE_2K;
>> + case 4096:
>> + return REG_PAGE_SIZE_4K;
>> + case 8192:
>> + return REG_PAGE_SIZE_8K;
>> + case 16384:
>> + return REG_PAGE_SIZE_16K;
>> + default:
>> + break;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
>> +{
>> + writel(val, nfc->base + INTR_STS_EN_OFST);
>> + writel(val, nfc->base + INTR_SIG_EN_OFST);
>> +}
>> +
>> +static int anfc_wait_for_event(struct anfc *nfc, u32 event)
>> +{
>> + struct completion *comp;
>> +
>> + if (event == XFER_COMPLETE)
>> + comp = &nfc->xfercomp;
>> + else
>> + comp = &nfc->bufrdy;
>> +
>> + return wait_for_completion_timeout(comp,
>> + msecs_to_jiffies(EVNT_TIMEOUT));
>> +}
>> +
>> +static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
>> + u32 pktcount)
>> +{
>> + writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
>> +}
>> +
>> +static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
>> +{
>> + writel(cmd1 | (cmd2 << CMD2_SHIFT) |
>> + (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
>> + nfc->base + ECC_SPR_CMD_OFST);
>> +}
>> +
>> +static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
>> +{
>> + u32 val;
>> +
>> + writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
>> +
>> + val = readl(nfc->base + MEM_ADDR2_OFST);
>> + val = (val & ~MEM_ADDR_MASK) |
>> + ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
>> + writel(val, nfc->base + MEM_ADDR2_OFST);
>> +}
>> +
>> +static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
>> + u8 dmamode, u32 pagesize, u8 addrcycles)
>> +{
>> + u32 regval;
>> +
>> + regval = cmd1 | (cmd2 << CMD2_SHIFT);
>> + if (dmamode && nfc->dma)
>> + regval |= DMA_ENABLE << DMA_EN_SHIFT;
>> + if (addrcycles)
>> + regval |= addrcycles << ADDR_CYCLES_SHIFT;
>> + if (pagesize)
>> + regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
>> + writel(regval, nfc->base + CMD_OFST);
>> +}
>> +
>> +static int anfc_device_ready(struct mtd_info *mtd,
>> + struct nand_chip *chip)
>> +{
>> + u8 status;
>> + unsigned long timeout = jiffies + STATUS_TIMEOUT;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + do {
>> + chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
>> + status = chip->read_byte(mtd);
>> + if (status & ONFI_STATUS_READY)
>> + break;
>> + cpu_relax();
>> + } while (!time_after_eq(jiffies, timeout));
>> +
>> + if (status & ONFI_STATUS_FAIL)
>> + return NAND_STATUS_FAIL;
>> +
>> + if (time_after_eq(jiffies, timeout)) {
>> + dev_err(nfc->dev, "%s timed out\n", __func__);
>> + return -ETIMEDOUT;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
>> + int page)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
>> + int page)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + nfc->iswriteoob = true;
>> + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
>> + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
>> + nfc->iswriteoob = false;
>> +
>> + return 0;
>> +}
>> +
>> +static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>> +{
>> + u32 pktcount, pktsize;
>> + unsigned int buf_rd_cnt = 0;
>> + u32 *bufptr = (u32 *)buf;
>> + struct anfc *nfc = to_anfc(mtd);
>> + dma_addr_t paddr;
>> +
>> + if (nfc->curr_cmd == NAND_CMD_READ0) {
>> + pktsize = nfc->pktsize;
>> + pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
>> + } else {
>> + pktsize = len;
>> + pktcount = 1;
>> + }
>> +
>> + anfc_setpktszcnt(nfc, pktsize, pktcount);
>> +
>> + if (nfc->dma) {
>> + paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
>> + if (dma_mapping_error(nfc->dev, paddr)) {
>> + dev_err(nfc->dev, "Read buffer mapping error");
>> + return;
>> + }
>> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + writel(PROG_PGRD, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
>> + return;
>> + }
>> +
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + writel(PROG_PGRD, nfc->base + PROG_OFST);
>> +
>> + while (buf_rd_cnt < pktcount) {
>> +
>> + anfc_wait_for_event(nfc, READ_READY);
>> + buf_rd_cnt++;
>> +
>> + if (buf_rd_cnt == pktcount)
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> +
>> + readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
>> + bufptr += pktsize/4;
>> +
>> + if (buf_rd_cnt < pktcount)
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + }
>> +
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>> +
>> +static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
>> +{
>> + u32 pktcount, pktsize;
>> + unsigned int buf_wr_cnt = 0;
>> + u32 *bufptr = (u32 *)buf;
>> + struct anfc *nfc = to_anfc(mtd);
>> + dma_addr_t paddr;
>> +
>> + if (nfc->iswriteoob) {
>> + pktsize = len;
>> + pktcount = 1;
>> + } else {
>> + pktsize = nfc->pktsize;
>> + pktcount = mtd->writesize / pktsize;
>> + }
>> +
>> + anfc_setpktszcnt(nfc, pktsize, pktcount);
>> +
>> + if (nfc->dma) {
>> + paddr = dma_map_single(nfc->dev, (void *)buf, len,
>> + DMA_TO_DEVICE);
>> + if (dma_mapping_error(nfc->dev, paddr)) {
>> + dev_err(nfc->dev, "Write buffer mapping error");
>> + return;
>> + }
>> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
>> + return;
>> + }
>> +
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
>> +
>> + while (buf_wr_cnt < pktcount) {
>> + anfc_wait_for_event(nfc, WRITE_READY);
>> +
>> + buf_wr_cnt++;
>> + if (buf_wr_cnt == pktcount)
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> +
>> + writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
>> + bufptr += pktsize/4;
>> +
>> + if (buf_wr_cnt < pktcount)
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> + }
>> +
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>> +
>> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
>> + struct nand_chip *chip, uint8_t *buf,
>> + int oob_required, int page)
>> +{
>> + u32 val;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
>> +
>> + val = readl(nfc->base + CMD_OFST);
>> + val = val | ECC_ENABLE;
>> + writel(val, nfc->base + CMD_OFST);
>> +
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> +
>> + if (!nfc->bch)
>> + nfc->rdintrmask = MBIT_ERROR;
>> +
>> + chip->read_buf(mtd, buf, mtd->writesize);
>> +
>> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
>> + if (nfc->bch) {
>> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
>> + } else {
>> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
>> + mtd->ecc_stats.corrected += val;
>> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
>> + mtd->ecc_stats.failed += val;
>> + /* Clear ecc error count register 1Bit, 2Bit */
>> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
>> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
>> + }
>> + nfc->err = false;
>> +
>> + if (oob_required)
>> + chip->ecc.read_oob(mtd, chip, page);
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_write_page_hwecc(struct mtd_info *mtd,
>> + struct nand_chip *chip, const uint8_t *buf,
>> + int oob_required, int page)
>> +{
>> + u32 val;
>> + unsigned int i;
>> + struct anfc *nfc = to_anfc(mtd);
>> + uint8_t *ecc_calc = chip->buffers->ecccalc;
>> + uint32_t *eccpos = chip->ecc.layout->eccpos;
>> +
>> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
>> +
>> + val = readl(nfc->base + CMD_OFST);
>> + val = val | ECC_ENABLE;
>> + writel(val, nfc->base + CMD_OFST);
>> +
>> + chip->write_buf(mtd, buf, mtd->writesize);
>> +
>> + if (oob_required) {
>> + anfc_device_ready(mtd, chip);
>> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> + chip->read_buf(mtd, ecc_calc, mtd->oobsize);
>> + for (i = 0; i < chip->ecc.total; i++)
>> + chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
>> + chip->ecc.write_oob(mtd, chip, page);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static u8 anfc_read_byte(struct mtd_info *mtd)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + return nfc->buf[nfc->bufshift++];
>> +}
>> +
>> +static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
>> +{
>> + u32 *bufptr = (u32 *)buf;
>> +
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> +
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, WRITE_READY);
>> +
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>> +
>> +static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
>> +{
>> + u32 *bufptr = (u32 *)nfc->buf;
>> +
>> + anfc_enable_intrs(nfc, READ_READY);
>> +
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, READ_READY);
>> +
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>> +
>> +static int anfc_ecc_init(struct mtd_info *mtd,
>> + struct nand_ecc_ctrl *ecc)
>> +{
>> + u32 ecc_addr, regval;
>> + unsigned int bchmode = 0, i, oob_index;
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct anfc *nfc = to_anfc(mtd);
>> + int found = -1;
>> +
>> + nand_chip->ecc.mode = NAND_ECC_HW;
>> + nand_chip->ecc.read_page = anfc_read_page_hwecc;
>> + nand_chip->ecc.write_page = anfc_write_page_hwecc;
>> + nand_chip->ecc.write_oob = anfc_write_oob;
>> + nand_chip->ecc.read_oob = anfc_read_oob;
>> +
>> + for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
>> + if ((ecc_matrix[i].pagesize == mtd->writesize) &&
>> + (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
>> + found = i;
>> + if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
>> + break;
>> + }
>> + }
>> +
>> + if (found < 0) {
>> + dev_err(nfc->dev, "ECC scheme not supported");
>> + return 1;
>> + }
>> + if (ecc_matrix[found].bch) {
>> + switch (ecc_matrix[found].eccbits) {
>> + case 12:
>> + bchmode = 0x1;
>> + break;
>> + case 8:
>> + bchmode = 0x2;
>> + break;
>> + case 4:
>> + bchmode = 0x3;
>> + break;
>> + case 24:
>> + bchmode = 0x4;
>> + break;
>> + default:
>> + bchmode = 0x0;
>> + }
>> + }
>> +
>> + nand_chip->ecc.strength = ecc_matrix[found].eccbits;
>> + nand_chip->ecc.size = ecc_matrix[found].codeword_size;
>> + nand_chip->ecc.steps = ecc_matrix[found].pagesize /
>> + ecc_matrix[found].codeword_size;
>> + nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
>> + nand_chip->ecc.steps;
>> + nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
>> + nfc->bch = ecc_matrix[found].bch;
>> + oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
>> + ecc_addr = mtd->writesize + oob_index;
>> +
>> + for (i = 0; i < nand_chip->ecc.size; i++)
>> + nfc->ecclayout.eccpos[i] = oob_index + i;
>> +
>> + nfc->ecclayout.oobfree->offset = 2;
>> + nfc->ecclayout.oobfree->length = oob_index -
>> + nfc->ecclayout.oobfree->offset;
>> +
>> + nand_chip->ecc.layout = &nfc->ecclayout;
>
> FYI, we're deprecating this usage of ecclayout. You might take a look at Boris'
> latest (which we'll probably merge very soon):
>
> http://lists.infradead.org/pipermail/linux-mtd/2016-March/065925.html
>
>> + regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
>> + (ecc_matrix[found].bch << BCH_EN_SHIFT);
>> + writel(regval, nfc->base + ECC_OFST);
>> +
>> + regval = readl(nfc->base + MEM_ADDR2_OFST);
>> + regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
>> + writel(regval, nfc->base + MEM_ADDR2_OFST);
>> +
>> + if (nand_chip->ecc_step_ds >= 1024)
>> + nfc->pktsize = 1024;
>> + else
>> + nfc->pktsize = 512;
>> +
>> + return 0;
>> +}
>> +
>> +static void anfc_cmd_function(struct mtd_info *mtd,
>> + unsigned int cmd, int column, int page_addr)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> + bool wait = false, read = false;
>> + u32 addrcycles, prog;
>> + u32 *bufptr = (u32 *)nfc->buf;
>> +
>> + nfc->bufshift = 0;
>> + nfc->curr_cmd = cmd;
>> +
>> + if (page_addr == -1)
>> + page_addr = 0;
>> + if (column == -1)
>> + column = 0;
>> +
>> + switch (cmd) {
>> + case NAND_CMD_RESET:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
>> + prog = PROG_RST;
>> + wait = true;
>> + break;
>> + case NAND_CMD_SEQIN:
>> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
>> + mtd->writesize, addrcycles);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + break;
>> + case NAND_CMD_READOOB:
>> + column += mtd->writesize;
>> + case NAND_CMD_READ0:
>> + case NAND_CMD_READ1:
>> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
>> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
>> + mtd->writesize, addrcycles);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + break;
>> + case NAND_CMD_RNDOUT:
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
>> + mtd->writesize, 2);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> + break;
>> + case NAND_CMD_PARAM:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
>> + anfc_readfifo(nfc, PROG_RDPARAM,
>> + sizeof(struct nand_onfi_params));
>> + break;
>> + case NAND_CMD_READID:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
>> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
>> + break;
>> + case NAND_CMD_ERASE1:
>> + addrcycles = nfc->raddr_cycles;
>> + prog = PROG_ERASE;
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
>> + column = page_addr & 0xffff;
>> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + wait = true;
>> + break;
>> + case NAND_CMD_STATUS:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
>> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + prog = PROG_STATUS;
>> + wait = read = true;
>> + break;
>> + case NAND_CMD_GET_FEATURES:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
>> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
>> + break;
>> + case NAND_CMD_SET_FEATURES:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
>> + break;
>> + default:
>> + return;
>> + }
>> +
>> + if (wait) {
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + }
>> +
>> + if (read)
>> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
>> +}
>> +
>> +static void anfc_select_chip(struct mtd_info *mtd, int num)
>> +{
>> + u32 val;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + if (num == -1)
>> + return;
>> +
>> + val = readl(nfc->base + MEM_ADDR2_OFST);
>> + val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
>> + writel(val, nfc->base + MEM_ADDR2_OFST);
>> +}
>> +
>> +static irqreturn_t anfc_irq_handler(int irq, void *ptr)
>> +{
>> + struct anfc *nfc = ptr;
>> + u32 regval = 0, status;
>> +
>> + status = readl(nfc->base + INTR_STS_OFST);
>> + if (status & XFER_COMPLETE) {
>> + complete(&nfc->xfercomp);
>> + regval |= XFER_COMPLETE;
>> + }
>> +
>> + if (status & READ_READY) {
>> + complete(&nfc->bufrdy);
>> + regval |= READ_READY;
>> + }
>> +
>> + if (status & WRITE_READY) {
>> + complete(&nfc->bufrdy);
>> + regval |= WRITE_READY;
>> + }
>> +
>> + if (status & MBIT_ERROR) {
>> + nfc->err = true;
>> + complete(&nfc->bufrdy);
>> + regval |= MBIT_ERROR;
>> + }
>> +
>> + if (regval) {
>> + writel(regval, nfc->base + INTR_STS_OFST);
>> + writel(0, nfc->base + INTR_STS_EN_OFST);
>> + writel(0, nfc->base + INTR_SIG_EN_OFST);
>> +
>> + return IRQ_HANDLED;
>> + }
>> +
>> + return IRQ_NONE;
>> +}
>> +
>> +static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
>> + int addr, uint8_t *subfeature_param)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> + int status;
>> +
>> + if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
>> + & ONFI_OPT_CMD_SET_GET_FEATURES))
>> + return -EINVAL;
>> +
>> + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
>> + anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
>> +
>> + status = chip->waitfunc(mtd, chip);
>> + if (status & NAND_STATUS_FAIL)
>> + return -EIO;
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_init_timing_mode(struct anfc *nfc)
>> +{
>> + int mode, err;
>> + unsigned int feature[2], regval, i;
>> + struct nand_chip *chip = &nfc->chip;
>> + struct mtd_info *mtd = &nfc->mtd;
>> +
>> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
>> + /* Get nvddr timing modes */
>> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
>> + if (!mode) {
>> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
>> + regval = mode;
>> + } else {
>> + mode = fls(mode) - 1;
>> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
>> + mode |= ONFI_DATA_INTERFACE_NVDDR;
>> + }
>> +
>> + feature[0] = mode;
>> + for (i = 0; i < nfc->num_cs; i++) {
>> + chip->select_chip(mtd, i);
>> + err = chip->onfi_set_features(mtd, chip,
>> + ONFI_FEATURE_ADDR_TIMING_MODE,
>> + (uint8_t *)feature);
>> + if (err)
>> + return err;
>> + }
>> + writel(regval, nfc->base + DATA_INTERFACE_REG);
>> +
>> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
>> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_probe(struct platform_device *pdev)
>> +{
>> + struct anfc *nfc;
>> + struct mtd_info *mtd;
>> + struct nand_chip *nand_chip;
>> + struct resource *res;
>> + struct mtd_part_parser_data ppdata;
>> + int err;
>> +
>> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
>> + if (!nfc)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(nfc->base))
>> + return PTR_ERR(nfc->base);
>> +
>> + mtd = &nfc->mtd;
>> + nand_chip = &nfc->chip;
>> + nand_chip->priv = nfc;
>> + mtd->priv = nand_chip;
>> + mtd->name = DRIVER_NAME;
>> + nfc->dev = &pdev->dev;
>> + mtd->dev.parent = &pdev->dev;
>> +
>> + nand_chip->cmdfunc = anfc_cmd_function;
>> + nand_chip->waitfunc = anfc_device_ready;
>> + nand_chip->chip_delay = 30;
>> + nand_chip->read_buf = anfc_read_buf;
>> + nand_chip->write_buf = anfc_write_buf;
>> + nand_chip->read_byte = anfc_read_byte;
>> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
>> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
>> + nand_chip->select_chip = anfc_select_chip;
>> + nand_chip->onfi_set_features = anfc_onfi_set_features;
>> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
>> + "arasan,has-mdma");
>> + nfc->num_cs = 1;
>> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);
>
> I get the feeling that this device tree binding doesn't support multiple
> chips very well, but I can't find the v5 binding to confirm...
>
> Normally, we suggest that you represent the NAND chip separate from the
> NAND controller. See, e.g.,
> Documentation/devicetree/bindings/mtd/sunxi-nand.txt which has:
>
> nfc: nand@01c03000 {
> compatible = "allwinner,sun4i-a10-nand";
> reg = <0x01c03000 0x1000>;
> ...
> nand@0 {
> reg = <0>;
> ...
> };
>
> // you could have nand@1, nand@2, etc.
> };
>
>
>> + platform_set_drvdata(pdev, nfc);
>> + init_completion(&nfc->bufrdy);
>> + init_completion(&nfc->xfercomp);
>> + nfc->irq = platform_get_irq(pdev, 0);
>> + if (nfc->irq < 0) {
>
> drivers/mtd/nand/arasan_nand.c: In function 'anfc_probe':
> drivers/mtd/nand/arasan_nand.c:900:15: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits]
> if (nfc->irq < 0) {
> ^
>
>> + dev_err(&pdev->dev, "platform_get_irq failed\n");
>> + return -ENXIO;
>> + }
>> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
>> + 0, "arasannfc", nfc);
>> + if (err)
>> + return err;
>> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
>> + if (IS_ERR(nfc->clk_sys)) {
>> + dev_err(&pdev->dev, "sys clock not found.\n");
>> + return PTR_ERR(nfc->clk_sys);
>> + }
>> +
>> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
>> + if (IS_ERR(nfc->clk_flash)) {
>> + dev_err(&pdev->dev, "flash clock not found.\n");
>> + return PTR_ERR(nfc->clk_flash);
>> + }
>> +
>> + err = clk_prepare_enable(nfc->clk_sys);
>> + if (err) {
>> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
>> + return err;
>> + }
>> +
>> + err = clk_prepare_enable(nfc->clk_flash);
>> + if (err) {
>> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
>> + goto clk_dis_sys;
>> + }
>> +
>> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
>> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
>> + if (err) {
>> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
>> + goto clk_dis_all;
>> + }
>> + if (nand_chip->onfi_version) {
>> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
>> + nfc->caddr_cycles =
>> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
>> + } else {
>> + /*For non-ONFI devices, configuring the address cyles as 5 */
>
> Space after /*
>
>> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
>> + }
>> +
>> + err = anfc_init_timing_mode(nfc);
>> + if (err) {
>> + dev_err(&pdev->dev, "timing mode init failed\n");
>> + goto clk_dis_all;
>> + }
>> +
>> + err = anfc_ecc_init(mtd, &nand_chip->ecc);
>> + if (err)
>> + goto clk_dis_all;
>> +
>> + err = nand_scan_tail(mtd);
>> + if (err) {
>> + dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
>> + goto clk_dis_all;
>> + }
>> +
>> + ppdata.of_node = pdev->dev.of_node;
>
> The of_node field is gone now. Just use nand_set_flash_node(). (Please rebase
> on l2-mtd.git before submitting MTD changes. See:
> http://linux-mtd.infradead.org/source.html )
>
>> +
>> + err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);
>
> Then, you won't need ppdata, so you can just do:
>
> err = mtd_device_register(&nfc->mtd, NULL, 0);
>
>> + if (err)
>> + goto clk_dis_all;
>> +
>> + return 0;
>> +
>> +clk_dis_all:
>> + clk_disable_unprepare(nfc->clk_flash);
>> +clk_dis_sys:
>> + clk_disable_unprepare(nfc->clk_sys);
>> +
>> + return err;
>> +}
>> +
>> +static int anfc_remove(struct platform_device *pdev)
>> +{
>> + struct anfc *nfc = platform_get_drvdata(pdev);
>> +
>> + clk_disable_unprepare(nfc->clk_sys);
>> + clk_disable_unprepare(nfc->clk_flash);
>> +
>> + nand_release(&nfc->mtd);
>
> Probably want to disable the clocks only after nand_release()?
> Otherwise, you might have a race.
>
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id anfc_ids[] = {
>> + { .compatible = "arasan,nfc-v3p10" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, anfc_ids);
>> +
>> +static struct platform_driver anfc_driver = {
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = anfc_ids,
>> + },
>> + .probe = anfc_probe,
>> + .remove = anfc_remove,
>> +};
>> +module_platform_driver(anfc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Xilinx, Inc");
>> +MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");
>
> Brian

2016-03-08 14:38:35

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

Hi Punnaiah,

Sorry for the late review.

On Sat, 21 Nov 2015 20:09:48 +0530
Punnaiah Choudary Kalluri <[email protected]> wrote:

> Added the basic driver for Arasan Nand Flash Controller used in
> Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
> correction.
>
> Signed-off-by: Punnaiah Choudary Kalluri <[email protected]>
> ---
> Changes in v5:
> - Renamed the driver filei as arasan_nand.c
> - Fixed all comments relaqted coding style
> - Fixed comments related to propagating the errors
> - Modified the anfc_write_page_hwecc as per the write_page
> prototype
> Changes in v4:
> - Added support for onfi timing mode configuration
> - Added clock supppport
> - Added support for multiple chipselects
> Changes in v3:
> - Removed unused variables
> - Avoided busy loop and used jifies based implementation
> - Fixed compiler warnings "right shift count >= width of type"
> - Removed unneeded codei and improved error reporting
> - Added onfi version check to ensure reading the valid address cycles
> Changes in v2:
> - Added missing of.h to avoid kbuild system report error
> ---
> drivers/mtd/nand/Kconfig | 6 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1017 insertions(+)
> create mode 100644 drivers/mtd/nand/arasan_nand.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 2896640..9c620fb 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -546,4 +546,10 @@ config MTD_NAND_HISI504
> help
> Enables support for NAND controller on Hisilicon SoC Hip04.
>
> +config MTD_NAND_ARASAN
> + tristate "Support for Arasan Nand Flash controller"
> + help
> + Enables the driver for the Arasan Nand Flash controller on
> + Zynq UltraScale+ MPSoC.
> +
> endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 2c7f014..3b993cb 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
> obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
> obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
> +obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
>
> nand-objs := nand_base.o nand_bbt.o nand_timings.o
> diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
> new file mode 100644
> index 0000000..e882e63
> --- /dev/null
> +++ b/drivers/mtd/nand/arasan_nand.c
> @@ -0,0 +1,1010 @@
> +/*
> + * Arasan Nand Flash Controller Driver
> + *
> + * Copyright (C) 2014 - 2015 Xilinx, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/of.h>
> +#include <linux/of_mtd.h>
> +#include <linux/platform_device.h>
> +
> +#define DRIVER_NAME "arasan_nand"
> +#define EVNT_TIMEOUT 1000
> +#define STATUS_TIMEOUT 2000
> +
> +#define PKT_OFST 0x00
> +#define MEM_ADDR1_OFST 0x04
> +#define MEM_ADDR2_OFST 0x08
> +#define CMD_OFST 0x0C
> +#define PROG_OFST 0x10
> +#define INTR_STS_EN_OFST 0x14
> +#define INTR_SIG_EN_OFST 0x18
> +#define INTR_STS_OFST 0x1C
> +#define READY_STS_OFST 0x20
> +#define DMA_ADDR1_OFST 0x24
> +#define FLASH_STS_OFST 0x28
> +#define DATA_PORT_OFST 0x30
> +#define ECC_OFST 0x34
> +#define ECC_ERR_CNT_OFST 0x38
> +#define ECC_SPR_CMD_OFST 0x3C
> +#define ECC_ERR_CNT_1BIT_OFST 0x40
> +#define ECC_ERR_CNT_2BIT_OFST 0x44
> +#define DMA_ADDR0_OFST 0x50
> +#define DATA_INTERFACE_REG 0x6C
> +
> +#define PKT_CNT_SHIFT 12
> +
> +#define ECC_ENABLE BIT(31)
> +#define DMA_EN_MASK GENMASK(27, 26)
> +#define DMA_ENABLE 0x2
> +#define DMA_EN_SHIFT 26
> +#define REG_PAGE_SIZE_MASK GENMASK(25, 23)
> +#define REG_PAGE_SIZE_SHIFT 23
> +#define REG_PAGE_SIZE_512 0
> +#define REG_PAGE_SIZE_1K 5
> +#define REG_PAGE_SIZE_2K 1
> +#define REG_PAGE_SIZE_4K 2
> +#define REG_PAGE_SIZE_8K 3
> +#define REG_PAGE_SIZE_16K 4
> +#define CMD2_SHIFT 8
> +#define ADDR_CYCLES_SHIFT 28
> +
> +#define XFER_COMPLETE BIT(2)
> +#define READ_READY BIT(1)
> +#define WRITE_READY BIT(0)
> +#define MBIT_ERROR BIT(3)
> +#define ERR_INTRPT BIT(4)
> +
> +#define PROG_PGRD BIT(0)
> +#define PROG_ERASE BIT(2)
> +#define PROG_STATUS BIT(3)
> +#define PROG_PGPROG BIT(4)
> +#define PROG_RDID BIT(6)
> +#define PROG_RDPARAM BIT(7)
> +#define PROG_RST BIT(8)
> +#define PROG_GET_FEATURE BIT(9)
> +#define PROG_SET_FEATURE BIT(10)
> +
> +#define ONFI_STATUS_FAIL BIT(0)
> +#define ONFI_STATUS_READY BIT(6)
> +
> +#define PG_ADDR_SHIFT 16
> +#define BCH_MODE_SHIFT 25
> +#define BCH_EN_SHIFT 27
> +#define ECC_SIZE_SHIFT 16
> +
> +#define MEM_ADDR_MASK GENMASK(7, 0)
> +#define BCH_MODE_MASK GENMASK(27, 25)
> +
> +#define CS_MASK GENMASK(31, 30)
> +#define CS_SHIFT 30
> +
> +#define PAGE_ERR_CNT_MASK GENMASK(16, 8)
> +#define PKT_ERR_CNT_MASK GENMASK(7, 0)
> +
> +#define NVDDR_MODE BIT(9)
> +#define NVDDR_TIMING_MODE_SHIFT 3
> +
> +#define ONFI_ID_LEN 8
> +#define TEMP_BUF_SIZE 512
> +#define NVDDR_MODE_PACKET_SIZE 8
> +#define SDR_MODE_PACKET_SIZE 4
> +
> +/**
> + * struct anfc_ecc_matrix - Defines ecc information storage format
> + * @pagesize: Page size in bytes.
> + * @codeword_size: Code word size information.
> + * @eccbits: Number of ecc bits.
> + * @bch: Bch / Hamming mode enable/disable.
> + * @eccsize: Ecc size information.
> + */
> +struct anfc_ecc_matrix {
> + u32 pagesize;
> + u32 codeword_size;
> + u8 eccbits;
> + u8 bch;
> + u16 eccsize;
> +};
> +
> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> + {512, 512, 1, 0, 0x3},
> + {512, 512, 4, 1, 0x7},
> + {512, 512, 8, 1, 0xD},
> + /* 2K byte page */
> + {2048, 512, 1, 0, 0xC},
> + {2048, 512, 4, 1, 0x1A},
> + {2048, 512, 8, 1, 0x34},
> + {2048, 512, 12, 1, 0x4E},
> + {2048, 1024, 24, 1, 0x54},
> + /* 4K byte page */
> + {4096, 512, 1, 0, 0x18},
> + {4096, 512, 4, 1, 0x34},
> + {4096, 512, 8, 1, 0x68},
> + {4096, 512, 12, 1, 0x9C},
> + {4096, 1024, 4, 1, 0xA8},
> + /* 8K byte page */
> + {8192, 512, 1, 0, 0x30},
> + {8192, 512, 4, 1, 0x68},
> + {8192, 512, 8, 1, 0xD0},
> + {8192, 512, 12, 1, 0x138},
> + {8192, 1024, 24, 1, 0x150},
> + /* 16K byte page */
> + {16384, 512, 1, 0, 0x60},
> + {16384, 512, 4, 1, 0xD0},
> + {16384, 512, 8, 1, 0x1A0},
> + {16384, 512, 12, 1, 0x270},
> + {16384, 1024, 24, 1, 0x2A0}
> +};

Do you really need this association table. IMO, those are
information you could deduce from nand_chip info (ecc_strength,
ecc_step_size, ...). eccsize can be calculated this way:

info->pagesize = mtd->writesize;
info->codeword_size = chip->ecc_step_ds;
info->eccbits = chip->ecc_strength_ds;

if (info->eccbits > 1)
info->bch = true;

steps = info->pagesize / info->codeword_size;

if (!bch)
info->eccsize = 3 * steps;
else
info->eccsize =
DIV_ROUND_UP(fls(8 * info->codeword_size) *
ecc->strength * steps, 8);

And, too bad we have to deal with another engine operating at the bit
level instead of aligning each ECC step to the next byte (GPMI engine
does that too, and it's a pain to deal with).

> +
> +/**
> + * struct anfc - Defines the Arasan NAND flash driver instance
> + * @chip: NAND chip information structure.
> + * @mtd: MTD information structure.
> + * @dev: Pointer to the device structure.
> + * @base: Virtual address of the NAND flash device.
> + * @curr_cmd: Current command issued.
> + * @clk_sys: Pointer to the system clock.
> + * @clk_flash: Pointer to the flash clock.
> + * @dma: Dma enable/disable.
> + * @bch: Bch / Hamming mode enable/disable.
> + * @err: Error identifier.
> + * @iswriteoob: Identifies if oob write operation is required.
> + * @buf: Buffer used for read/write byte operations.
> + * @raddr_cycles: Row address cycle information.
> + * @caddr_cycles: Column address cycle information.
> + * @irq: irq number
> + * @pktsize: Packet size for read / write operation.
> + * @bufshift: Variable used for indexing buffer operation
> + * @rdintrmask: Interrupt mask value for read operation.
> + * @num_cs: Number of chip selects in use.
> + * @spktsize: Packet size in ddr mode for status operation.
> + * @bufrdy: Completion event for buffer ready.
> + * @xfercomp: Completion event for transfer complete.
> + * @ecclayout: Ecc layout object
> + */
> +struct anfc {
> + struct nand_chip chip;
> + struct mtd_info mtd;

Drop this field ^ . It's now directly embedded in nand_chip and can be
accessed with nand_to_mtd().

> + struct device *dev;
> +
> + void __iomem *base;
> + int curr_cmd;
> + struct clk *clk_sys;
> + struct clk *clk_flash;
> +
> + bool dma;
> + bool bch;
> + bool err;
> + bool iswriteoob;
> +
> + u8 buf[TEMP_BUF_SIZE];
> +
> + u16 raddr_cycles;
> + u16 caddr_cycles;
> +
> + u32 irq;
> + u32 pktsize;
> + u32 bufshift;
> + u32 rdintrmask;
> + u32 num_cs;
> + u32 spktsize;
> +
> + struct completion bufrdy;
> + struct completion xfercomp;

I'm pretty sure you don't need 2 different completion fields. You can
do the same thing with a single one and a different approach (see here
[2]).

> + struct nand_ecclayout ecclayout;

As pointed by Brian, you probably start using the mtd_ooblayout_ops
approach, which should normally reach mainline before this driver.

> +};

As many other NAND controller drivers do, it seems your mixing the NAND
controller and NAND chip concept.

Could you create and anand struct containing the nand_chip field and
every other fields that are chip specific, and keep the controller
specific data in this anfc struct?

Note that the anand and anfc struct can be like together through the
chip->controller field (you can have a look at the sunxi_nand driver as
a reference).

> +
> +static inline struct anfc *to_anfc(struct mtd_info *mtd)
> +{
> + return container_of(mtd, struct anfc, mtd);
> +}

This should be adapted accordingly.

> +
> +static u8 anfc_page(u32 pagesize)
> +{
> + switch (pagesize) {
> + case 512:
> + return REG_PAGE_SIZE_512;
> + case 1024:
> + return REG_PAGE_SIZE_1K;
> + case 2048:
> + return REG_PAGE_SIZE_2K;
> + case 4096:
> + return REG_PAGE_SIZE_4K;
> + case 8192:
> + return REG_PAGE_SIZE_8K;
> + case 16384:
> + return REG_PAGE_SIZE_16K;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
> +{
> + writel(val, nfc->base + INTR_STS_EN_OFST);
> + writel(val, nfc->base + INTR_SIG_EN_OFST);
> +}
> +
> +static int anfc_wait_for_event(struct anfc *nfc, u32 event)
> +{
> + struct completion *comp;
> +
> + if (event == XFER_COMPLETE)
> + comp = &nfc->xfercomp;
> + else
> + comp = &nfc->bufrdy;
> +

As said previously, I doubt you need 2 different completion objects.
And you should probably enable interrupts directly inside this function.

> + return wait_for_completion_timeout(comp,
> + msecs_to_jiffies(EVNT_TIMEOUT));
> +}
> +
> +static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
> + u32 pktcount)
> +{
> + writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
> +}
> +
> +static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
> +{
> + writel(cmd1 | (cmd2 << CMD2_SHIFT) |
> + (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
> + nfc->base + ECC_SPR_CMD_OFST);
> +}
> +
> +static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
> +{
> + u32 val;
> +
> + writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
> +
> + val = readl(nfc->base + MEM_ADDR2_OFST);
> + val = (val & ~MEM_ADDR_MASK) |
> + ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
> + writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
> + u8 dmamode, u32 pagesize, u8 addrcycles)
> +{
> + u32 regval;
> +
> + regval = cmd1 | (cmd2 << CMD2_SHIFT);
> + if (dmamode && nfc->dma)
> + regval |= DMA_ENABLE << DMA_EN_SHIFT;
> + if (addrcycles)
> + regval |= addrcycles << ADDR_CYCLES_SHIFT;
> + if (pagesize)
> + regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
> + writel(regval, nfc->base + CMD_OFST);
> +}
> +
> +static int anfc_device_ready(struct mtd_info *mtd,
> + struct nand_chip *chip)

You named the function anfc_device_ready() while it"s actually
implementing ->waitfunc. Could you rename it to anfc_waitfunc() to
avoid confusion.

> +{
> + u8 status;
> + unsigned long timeout = jiffies + STATUS_TIMEOUT;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + do {
> + chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
> + status = chip->read_byte(mtd);
> + if (status & ONFI_STATUS_READY)
> + break;
> + cpu_relax();
> + } while (!time_after_eq(jiffies, timeout));
> +
> + if (status & ONFI_STATUS_FAIL)
> + return NAND_STATUS_FAIL;
> +
> + if (time_after_eq(jiffies, timeout)) {
> + dev_err(nfc->dev, "%s timed out\n", __func__);
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}

It seems you're re-implementing nand_wait() [1]. Maybe it's worth
exporting this function instead of re-implementing it.

> +
> +static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
> + int page)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;

->rdintrmask assignment should be moved into ->read_buf()
implementation IMO.

> + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> + return 0;
> +}
> +
> +static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
> + int page)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + nfc->iswriteoob = true;
> + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
> + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
> + nfc->iswriteoob = false;
> +
> + return 0;
> +}
> +
> +static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> + u32 pktcount, pktsize;
> + unsigned int buf_rd_cnt = 0;
> + u32 *bufptr = (u32 *)buf;
> + struct anfc *nfc = to_anfc(mtd);
> + dma_addr_t paddr;
> +
> + if (nfc->curr_cmd == NAND_CMD_READ0) {
> + pktsize = nfc->pktsize;
> + pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
> + } else {
> + pktsize = len;
> + pktcount = 1;
> + }

Can we avoid making this selection conditional on the READ0 cmd?
I've worked with some NANDs that required accessing specific features
using a vendor-specific command + a READ command, and I'm not sure we
want to use this pktsize optimization in this case.

Maybe you should set the pktsize info in your ecc->read_page()
implementation (of course this means you'll have to reset it to 1
before leaving ->read_page()), and just calculate pktcount here.

> +
> + anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> + if (nfc->dma) {
> + paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);

What if buf has been vmallocated, and len is more than PAGE_SIZE (or
buf is not page aligned, and buf + len cross a page boundary).

I proposed a generic approach to deal with that [3], but if you only
support physically contiguous buffers, then you should make sure buf
was not vmallocated (using is_vmalloc_addr()).

> + if (dma_mapping_error(nfc->dev, paddr)) {
> + dev_err(nfc->dev, "Read buffer mapping error");
> + return;
> + }
> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + writel(PROG_PGRD, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
> + return;
> + }
> +
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + writel(PROG_PGRD, nfc->base + PROG_OFST);
> +
> + while (buf_rd_cnt < pktcount) {
> +
> + anfc_wait_for_event(nfc, READ_READY);
> + buf_rd_cnt++;
> +
> + if (buf_rd_cnt == pktcount)
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> + readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> + bufptr += pktsize/4;
> +
> + if (buf_rd_cnt < pktcount)
> + anfc_enable_intrs(nfc, nfc->rdintrmask);
> + }
> +
> + anfc_wait_for_event(nfc, XFER_COMPLETE);

Hm, this looks like a complicated way of doing what you want. How about
changing for the following logic?

for (buf_rd_cnt = 0; buf_rd_cnt < pktcount; buf_rd_cnt)
{
anfc_wait_for_event(nfc, READ_READY);
readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
bufptr += pktsize/4;
}

anfc_wait_for_event(nfc, XFER_COMPLETE);

This implementation suppose you've moved interrupt activation in
anfc_wait_for_event().

> +}
> +
> +static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
> +{
> + u32 pktcount, pktsize;
> + unsigned int buf_wr_cnt = 0;
> + u32 *bufptr = (u32 *)buf;
> + struct anfc *nfc = to_anfc(mtd);
> + dma_addr_t paddr;
> +
> + if (nfc->iswriteoob) {
> + pktsize = len;
> + pktcount = 1;
> + } else {
> + pktsize = nfc->pktsize;
> + pktcount = mtd->writesize / pktsize;
> + }

Same remark as for read_buf(). pktsize should probably be set to
nfc->pktsize in ecc->write_page() implem, and reset to 1 before leaving
the function.

> +
> + anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> + if (nfc->dma) {
> + paddr = dma_map_single(nfc->dev, (void *)buf, len,
> + DMA_TO_DEVICE);
> + if (dma_mapping_error(nfc->dev, paddr)) {
> + dev_err(nfc->dev, "Write buffer mapping error");
> + return;
> + }
> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
> + return;
> + }
> +
> + anfc_enable_intrs(nfc, WRITE_READY);
> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
> +
> + while (buf_wr_cnt < pktcount) {
> + anfc_wait_for_event(nfc, WRITE_READY);
> +
> + buf_wr_cnt++;
> + if (buf_wr_cnt == pktcount)
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> + writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> + bufptr += pktsize/4;
> +
> + if (buf_wr_cnt < pktcount)
> + anfc_enable_intrs(nfc, WRITE_READY);
> + }
> +
> + anfc_wait_for_event(nfc, XFER_COMPLETE);

And the same goes for the other comments I made on ->read_buf().

> +}
> +
> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip, uint8_t *buf,
> + int oob_required, int page)
> +{
> + u32 val;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
> +
> + val = readl(nfc->base + CMD_OFST);
> + val = val | ECC_ENABLE;
> + writel(val, nfc->base + CMD_OFST);
> +
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> +
> + if (!nfc->bch)
> + nfc->rdintrmask = MBIT_ERROR;
> +
> + chip->read_buf(mtd, buf, mtd->writesize);
> +
> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
> + if (nfc->bch) {
> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> + } else {
> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> + mtd->ecc_stats.corrected += val;
> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> + mtd->ecc_stats.failed += val;
> + /* Clear ecc error count register 1Bit, 2Bit */
> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> + }

Not sure if your controller already handles the 'bitflips in erased
pages' case, but you might want to have a look at the
nand_check_erased_ecc_chunk() [4] function if that's not the case.

> + nfc->err = false;
> +
> + if (oob_required)
> + chip->ecc.read_oob(mtd, chip, page);

You should disable the ECC engine before leaving the function.

> +
> + return 0;
> +}
> +
> +static int anfc_write_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip, const uint8_t *buf,
> + int oob_required, int page)
> +{
> + u32 val;
> + unsigned int i;
> + struct anfc *nfc = to_anfc(mtd);
> + uint8_t *ecc_calc = chip->buffers->ecccalc;
> + uint32_t *eccpos = chip->ecc.layout->eccpos;
> +
> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
> +
> + val = readl(nfc->base + CMD_OFST);
> + val = val | ECC_ENABLE;
> + writel(val, nfc->base + CMD_OFST);
> +
> + chip->write_buf(mtd, buf, mtd->writesize);
> +
> + if (oob_required) {
> + anfc_device_ready(mtd, chip);
> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> + chip->read_buf(mtd, ecc_calc, mtd->oobsize);
> + for (i = 0; i < chip->ecc.total; i++)
> + chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
> + chip->ecc.write_oob(mtd, chip, page);
> + }
> +

Ditto.

> + return 0;
> +}
> +
> +static u8 anfc_read_byte(struct mtd_info *mtd)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> +
> + return nfc->buf[nfc->bufshift++];

->read_byte() should normally be a wrapper around ->read_buf(), because
you don't know ahead of time, how many time ->read_byte() will be
called after the CMD/ADDR cycles, and thus cannot deduce how much
should be put in the temporary buffer.
So I'd suggest you put the logic to fill nfc->buf[] directly in there
instead of putting it in ->cmdfunc().

> +}
> +
> +static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
> +{
> + u32 *bufptr = (u32 *)buf;
> +
> + anfc_enable_intrs(nfc, WRITE_READY);
> +
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, WRITE_READY);
> +
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
> +{
> + u32 *bufptr = (u32 *)nfc->buf;
> +
> + anfc_enable_intrs(nfc, READ_READY);
> +
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, READ_READY);
> +
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}

If you do what I suggest, you shouldn't need those
anfc_writefifo/readfifo() functions (or at least, not in their current
form).

> +
> +static int anfc_ecc_init(struct mtd_info *mtd,
> + struct nand_ecc_ctrl *ecc)
> +{
> + u32 ecc_addr, regval;
> + unsigned int bchmode = 0, i, oob_index;
> + struct nand_chip *nand_chip = mtd->priv;
> + struct anfc *nfc = to_anfc(mtd);
> + int found = -1;
> +
> + nand_chip->ecc.mode = NAND_ECC_HW;
> + nand_chip->ecc.read_page = anfc_read_page_hwecc;
> + nand_chip->ecc.write_page = anfc_write_page_hwecc;
> + nand_chip->ecc.write_oob = anfc_write_oob;
> + nand_chip->ecc.read_oob = anfc_read_oob;
> +
> + for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
> + if ((ecc_matrix[i].pagesize == mtd->writesize) &&
> + (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
> + found = i;
> + if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
> + break;
> + }
> + }

Should be replaced by the logic I proposed at the beginning of this
answer.

> +
> + if (found < 0) {
> + dev_err(nfc->dev, "ECC scheme not supported");
> + return 1;
> + }
> + if (ecc_matrix[found].bch) {
> + switch (ecc_matrix[found].eccbits) {
> + case 12:
> + bchmode = 0x1;
> + break;
> + case 8:
> + bchmode = 0x2;
> + break;
> + case 4:
> + bchmode = 0x3;
> + break;
> + case 24:
> + bchmode = 0x4;
> + break;
> + default:
> + bchmode = 0x0;
> + }
> + }
> +
> + nand_chip->ecc.strength = ecc_matrix[found].eccbits;
> + nand_chip->ecc.size = ecc_matrix[found].codeword_size;
> + nand_chip->ecc.steps = ecc_matrix[found].pagesize /
> + ecc_matrix[found].codeword_size;
> + nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
> + nand_chip->ecc.steps;

Okay, because of the unaligned eccsize value, you might end up with
something slightly different from the reality.

> + nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
> + nfc->bch = ecc_matrix[found].bch;
> + oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
> + ecc_addr = mtd->writesize + oob_index;
> +
> + for (i = 0; i < nand_chip->ecc.size; i++)
> + nfc->ecclayout.eccpos[i] = oob_index + i;
> +
> + nfc->ecclayout.oobfree->offset = 2;
> + nfc->ecclayout.oobfree->length = oob_index -
> + nfc->ecclayout.oobfree->offset;
> +
> + nand_chip->ecc.layout = &nfc->ecclayout;

Should be replaced by mtd_set_ooblayout().

> + regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
> + (ecc_matrix[found].bch << BCH_EN_SHIFT);
> + writel(regval, nfc->base + ECC_OFST);
> +
> + regval = readl(nfc->base + MEM_ADDR2_OFST);
> + regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
> + writel(regval, nfc->base + MEM_ADDR2_OFST);
> +
> + if (nand_chip->ecc_step_ds >= 1024)
> + nfc->pktsize = 1024;
> + else
> + nfc->pktsize = 512;
> +
> + return 0;
> +}
> +
> +static void anfc_cmd_function(struct mtd_info *mtd,
> + unsigned int cmd, int column, int page_addr)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> + bool wait = false, read = false;
> + u32 addrcycles, prog;
> + u32 *bufptr = (u32 *)nfc->buf;
> +
> + nfc->bufshift = 0;
> + nfc->curr_cmd = cmd;
> +
> + if (page_addr == -1)
> + page_addr = 0;
> + if (column == -1)
> + column = 0;
> +
> + switch (cmd) {
> + case NAND_CMD_RESET:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> + prog = PROG_RST;
> + wait = true;
> + break;
> + case NAND_CMD_SEQIN:
> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> + mtd->writesize, addrcycles);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + break;
> + case NAND_CMD_READOOB:
> + column += mtd->writesize;
> + case NAND_CMD_READ0:
> + case NAND_CMD_READ1:
> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
> + mtd->writesize, addrcycles);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + break;
> + case NAND_CMD_RNDOUT:
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> + mtd->writesize, 2);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + if (nfc->dma)
> + nfc->rdintrmask = XFER_COMPLETE;
> + else
> + nfc->rdintrmask = READ_READY;
> + break;
> + case NAND_CMD_PARAM:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> + anfc_readfifo(nfc, PROG_RDPARAM,
> + sizeof(struct nand_onfi_params));
> + break;
> + case NAND_CMD_READID:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> + break;
> + case NAND_CMD_ERASE1:
> + addrcycles = nfc->raddr_cycles;
> + prog = PROG_ERASE;
> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
> + column = page_addr & 0xffff;
> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + wait = true;
> + break;
> + case NAND_CMD_STATUS:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + prog = PROG_STATUS;
> + wait = read = true;
> + break;
> + case NAND_CMD_GET_FEATURES:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> + break;
> + case NAND_CMD_SET_FEATURES:
> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> + anfc_setpagecoladdr(nfc, page_addr, column);
> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> + break;
> + default:
> + return;
> + }
> +
> + if (wait) {
> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> + writel(prog, nfc->base + PROG_OFST);
> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> + }
> +
> + if (read)
> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
> +}

Can you try to implement ->cmd_ctrl() instead of ->cmdfunc(). This way
you'll benefit from all the improvements we'll to the default
nand_command_lp() and nand_command() implementation, and this also ease
maintenance on our side.
According to what I've seen so far this should be doable.

> +
> +static void anfc_select_chip(struct mtd_info *mtd, int num)
> +{
> + u32 val;
> + struct anfc *nfc = to_anfc(mtd);
> +
> + if (num == -1)
> + return;
> +
> + val = readl(nfc->base + MEM_ADDR2_OFST);
> + val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
> + writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static irqreturn_t anfc_irq_handler(int irq, void *ptr)
> +{
> + struct anfc *nfc = ptr;
> + u32 regval = 0, status;
> +
> + status = readl(nfc->base + INTR_STS_OFST);
> + if (status & XFER_COMPLETE) {
> + complete(&nfc->xfercomp);
> + regval |= XFER_COMPLETE;
> + }
> +
> + if (status & READ_READY) {
> + complete(&nfc->bufrdy);
> + regval |= READ_READY;
> + }
> +
> + if (status & WRITE_READY) {
> + complete(&nfc->bufrdy);
> + regval |= WRITE_READY;
> + }
> +
> + if (status & MBIT_ERROR) {
> + nfc->err = true;
> + complete(&nfc->bufrdy);
> + regval |= MBIT_ERROR;
> + }
> +
> + if (regval) {
> + writel(regval, nfc->base + INTR_STS_OFST);
> + writel(0, nfc->base + INTR_STS_EN_OFST);
> + writel(0, nfc->base + INTR_SIG_EN_OFST);
> +
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}

Should be reworked to use only one completion object.

> +
> +static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> + int addr, uint8_t *subfeature_param)
> +{
> + struct anfc *nfc = to_anfc(mtd);
> + int status;
> +
> + if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
> + & ONFI_OPT_CMD_SET_GET_FEATURES))
> + return -EINVAL;
> +
> + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
> + anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
> +
> + status = chip->waitfunc(mtd, chip);
> + if (status & NAND_STATUS_FAIL)
> + return -EIO;
> +
> + return 0;
> +}
[]
A concrete example of why you should rework the
->read_byte()/->write_byte() implementation: this way you could get
rid of this custom ->set_features() implem.

> +
> +static int anfc_init_timing_mode(struct anfc *nfc)
> +{
> + int mode, err;
> + unsigned int feature[2], regval, i;
> + struct nand_chip *chip = &nfc->chip;
> + struct mtd_info *mtd = &nfc->mtd;
> +
> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> + /* Get nvddr timing modes */
> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
> + if (!mode) {
> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> + regval = mode;
> + } else {
> + mode = fls(mode) - 1;
> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
> + mode |= ONFI_DATA_INTERFACE_NVDDR;
> + }
> +
> + feature[0] = mode;
> + for (i = 0; i < nfc->num_cs; i++) {
> + chip->select_chip(mtd, i);
> + err = chip->onfi_set_features(mtd, chip,
> + ONFI_FEATURE_ADDR_TIMING_MODE,
> + (uint8_t *)feature);
> + if (err)
> + return err;
> + }
> + writel(regval, nfc->base + DATA_INTERFACE_REG);
> +
> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;

You seem to switch from SDR to DDR mode, but I don't see any timing
configuration. How is your controller able to adapt to different NAND
timings?

> +
> + return 0;
> +}
> +
> +static int anfc_probe(struct platform_device *pdev)
> +{
> + struct anfc *nfc;
> + struct mtd_info *mtd;
> + struct nand_chip *nand_chip;
> + struct resource *res;
> + struct mtd_part_parser_data ppdata;
> + int err;
> +
> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> + if (!nfc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(nfc->base))
> + return PTR_ERR(nfc->base);
> +
> + mtd = &nfc->mtd;
> + nand_chip = &nfc->chip;
> + nand_chip->priv = nfc;
> + mtd->priv = nand_chip;
> + mtd->name = DRIVER_NAME;
> + nfc->dev = &pdev->dev;
> + mtd->dev.parent = &pdev->dev;
> +
> + nand_chip->cmdfunc = anfc_cmd_function;
> + nand_chip->waitfunc = anfc_device_ready;

You can leave nand_chip->waitfunc to NULL since your implementation is
doing exactly what the default implementation does.

> + nand_chip->chip_delay = 30;
> + nand_chip->read_buf = anfc_read_buf;
> + nand_chip->write_buf = anfc_write_buf;
> + nand_chip->read_byte = anfc_read_byte;
> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> + nand_chip->select_chip = anfc_select_chip;
> + nand_chip->onfi_set_features = anfc_onfi_set_features;
> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
> + "arasan,has-mdma");
> + nfc->num_cs = 1;
> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);

Already mentioned by Brian, but you should have a look at the
sunxi-nand binding to see how to represent the NAND controller and its
chips in the DT.

> + platform_set_drvdata(pdev, nfc);
> + init_completion(&nfc->bufrdy);
> + init_completion(&nfc->xfercomp);
> + nfc->irq = platform_get_irq(pdev, 0);
> + if (nfc->irq < 0) {
> + dev_err(&pdev->dev, "platform_get_irq failed\n");
> + return -ENXIO;
> + }
> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> + 0, "arasannfc", nfc);
> + if (err)
> + return err;
> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> + if (IS_ERR(nfc->clk_sys)) {
> + dev_err(&pdev->dev, "sys clock not found.\n");
> + return PTR_ERR(nfc->clk_sys);
> + }
> +
> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> + if (IS_ERR(nfc->clk_flash)) {
> + dev_err(&pdev->dev, "flash clock not found.\n");
> + return PTR_ERR(nfc->clk_flash);
> + }
> +
> + err = clk_prepare_enable(nfc->clk_sys);
> + if (err) {
> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> + return err;
> + }
> +
> + err = clk_prepare_enable(nfc->clk_flash);
> + if (err) {
> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> + goto clk_dis_sys;
> + }
> +
> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> + if (err) {
> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> + goto clk_dis_all;
> + }
> + if (nand_chip->onfi_version) {
> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
> + nfc->caddr_cycles =
> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> + } else {
> + /*For non-ONFI devices, configuring the address cyles as 5 */
> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
> + }

Hm, this should not be dependent on ONFI support. It all depends on the
CHIP size, which you can deduce from mtd->writesize.

> +
> + err = anfc_init_timing_mode(nfc);
> + if (err) {
> + dev_err(&pdev->dev, "timing mode init failed\n");
> + goto clk_dis_all;
> + }
> +
> + err = anfc_ecc_init(mtd, &nand_chip->ecc);
> + if (err)
> + goto clk_dis_all;
> +
> + err = nand_scan_tail(mtd);
> + if (err) {
> + dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
> + goto clk_dis_all;
> + }
> +
> + ppdata.of_node = pdev->dev.of_node;
> +
> + err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);
> + if (err)
> + goto clk_dis_all;
> +
> + return 0;
> +
> +clk_dis_all:
> + clk_disable_unprepare(nfc->clk_flash);
> +clk_dis_sys:
> + clk_disable_unprepare(nfc->clk_sys);
> +
> + return err;
> +}
> +
> +static int anfc_remove(struct platform_device *pdev)
> +{
> + struct anfc *nfc = platform_get_drvdata(pdev);
> +
> + clk_disable_unprepare(nfc->clk_sys);
> + clk_disable_unprepare(nfc->clk_flash);
> +
> + nand_release(&nfc->mtd);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id anfc_ids[] = {
> + { .compatible = "arasan,nfc-v3p10" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, anfc_ids);
> +
> +static struct platform_driver anfc_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = anfc_ids,
> + },
> + .probe = anfc_probe,
> + .remove = anfc_remove,
> +};
> +module_platform_driver(anfc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx, Inc");
> +MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");

That's all I got for now.

Best Regards,

Boris


[1]http://lxr.free-electrons.com/source/drivers/mtd/nand/nand_base.c#L899
[2]http://lxr.free-electrons.com/source/drivers/mtd/nand/sunxi_nand.c#L283
[3]https://lkml.org/lkml/2016/3/8/270
[4]http://lxr.free-electrons.com/source/drivers/mtd/nand/nand_base.c#L1161

--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

Hi Boris,

On Tue, Mar 8, 2016 at 8:08 PM, Boris Brezillon
<[email protected]> wrote:
> Hi Punnaiah,
>
> Sorry for the late review.
>
> On Sat, 21 Nov 2015 20:09:48 +0530
> Punnaiah Choudary Kalluri <[email protected]> wrote:
>
>> Added the basic driver for Arasan Nand Flash Controller used in
>> Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
>> correction.
>>
>> Signed-off-by: Punnaiah Choudary Kalluri <[email protected]>
>> ---
>> Changes in v5:
>> - Renamed the driver filei as arasan_nand.c
>> - Fixed all comments relaqted coding style
>> - Fixed comments related to propagating the errors
>> - Modified the anfc_write_page_hwecc as per the write_page
>> prototype
>> Changes in v4:
>> - Added support for onfi timing mode configuration
>> - Added clock supppport
>> - Added support for multiple chipselects
>> Changes in v3:
>> - Removed unused variables
>> - Avoided busy loop and used jifies based implementation
>> - Fixed compiler warnings "right shift count >= width of type"
>> - Removed unneeded codei and improved error reporting
>> - Added onfi version check to ensure reading the valid address cycles
>> Changes in v2:
>> - Added missing of.h to avoid kbuild system report error
>> ---
>> drivers/mtd/nand/Kconfig | 6 +
>> drivers/mtd/nand/Makefile | 1 +
>> drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 1017 insertions(+)
>> create mode 100644 drivers/mtd/nand/arasan_nand.c
>>
>> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
>> index 2896640..9c620fb 100644
>> --- a/drivers/mtd/nand/Kconfig
>> +++ b/drivers/mtd/nand/Kconfig
>> @@ -546,4 +546,10 @@ config MTD_NAND_HISI504
>> help
>> Enables support for NAND controller on Hisilicon SoC Hip04.
>>
>> +config MTD_NAND_ARASAN
>> + tristate "Support for Arasan Nand Flash controller"
>> + help
>> + Enables the driver for the Arasan Nand Flash controller on
>> + Zynq UltraScale+ MPSoC.
>> +
>> endif # MTD_NAND
>> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
>> index 2c7f014..3b993cb 100644
>> --- a/drivers/mtd/nand/Makefile
>> +++ b/drivers/mtd/nand/Makefile
>> @@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
>> obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
>> obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
>> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
>> +obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
>>
>> nand-objs := nand_base.o nand_bbt.o nand_timings.o
>> diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
>> new file mode 100644
>> index 0000000..e882e63
>> --- /dev/null
>> +++ b/drivers/mtd/nand/arasan_nand.c
>> @@ -0,0 +1,1010 @@
>> +/*
>> + * Arasan Nand Flash Controller Driver
>> + *
>> + * Copyright (C) 2014 - 2015 Xilinx, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation; either version 2 of the License, or (at your
>> + * option) any later version.
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io-64-nonatomic-lo-hi.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/nand.h>
>> +#include <linux/mtd/partitions.h>
>> +#include <linux/of.h>
>> +#include <linux/of_mtd.h>
>> +#include <linux/platform_device.h>
>> +
>> +#define DRIVER_NAME "arasan_nand"
>> +#define EVNT_TIMEOUT 1000
>> +#define STATUS_TIMEOUT 2000
>> +
>> +#define PKT_OFST 0x00
>> +#define MEM_ADDR1_OFST 0x04
>> +#define MEM_ADDR2_OFST 0x08
>> +#define CMD_OFST 0x0C
>> +#define PROG_OFST 0x10
>> +#define INTR_STS_EN_OFST 0x14
>> +#define INTR_SIG_EN_OFST 0x18
>> +#define INTR_STS_OFST 0x1C
>> +#define READY_STS_OFST 0x20
>> +#define DMA_ADDR1_OFST 0x24
>> +#define FLASH_STS_OFST 0x28
>> +#define DATA_PORT_OFST 0x30
>> +#define ECC_OFST 0x34
>> +#define ECC_ERR_CNT_OFST 0x38
>> +#define ECC_SPR_CMD_OFST 0x3C
>> +#define ECC_ERR_CNT_1BIT_OFST 0x40
>> +#define ECC_ERR_CNT_2BIT_OFST 0x44
>> +#define DMA_ADDR0_OFST 0x50
>> +#define DATA_INTERFACE_REG 0x6C
>> +
>> +#define PKT_CNT_SHIFT 12
>> +
>> +#define ECC_ENABLE BIT(31)
>> +#define DMA_EN_MASK GENMASK(27, 26)
>> +#define DMA_ENABLE 0x2
>> +#define DMA_EN_SHIFT 26
>> +#define REG_PAGE_SIZE_MASK GENMASK(25, 23)
>> +#define REG_PAGE_SIZE_SHIFT 23
>> +#define REG_PAGE_SIZE_512 0
>> +#define REG_PAGE_SIZE_1K 5
>> +#define REG_PAGE_SIZE_2K 1
>> +#define REG_PAGE_SIZE_4K 2
>> +#define REG_PAGE_SIZE_8K 3
>> +#define REG_PAGE_SIZE_16K 4
>> +#define CMD2_SHIFT 8
>> +#define ADDR_CYCLES_SHIFT 28
>> +
>> +#define XFER_COMPLETE BIT(2)
>> +#define READ_READY BIT(1)
>> +#define WRITE_READY BIT(0)
>> +#define MBIT_ERROR BIT(3)
>> +#define ERR_INTRPT BIT(4)
>> +
>> +#define PROG_PGRD BIT(0)
>> +#define PROG_ERASE BIT(2)
>> +#define PROG_STATUS BIT(3)
>> +#define PROG_PGPROG BIT(4)
>> +#define PROG_RDID BIT(6)
>> +#define PROG_RDPARAM BIT(7)
>> +#define PROG_RST BIT(8)
>> +#define PROG_GET_FEATURE BIT(9)
>> +#define PROG_SET_FEATURE BIT(10)
>> +
>> +#define ONFI_STATUS_FAIL BIT(0)
>> +#define ONFI_STATUS_READY BIT(6)
>> +
>> +#define PG_ADDR_SHIFT 16
>> +#define BCH_MODE_SHIFT 25
>> +#define BCH_EN_SHIFT 27
>> +#define ECC_SIZE_SHIFT 16
>> +
>> +#define MEM_ADDR_MASK GENMASK(7, 0)
>> +#define BCH_MODE_MASK GENMASK(27, 25)
>> +
>> +#define CS_MASK GENMASK(31, 30)
>> +#define CS_SHIFT 30
>> +
>> +#define PAGE_ERR_CNT_MASK GENMASK(16, 8)
>> +#define PKT_ERR_CNT_MASK GENMASK(7, 0)
>> +
>> +#define NVDDR_MODE BIT(9)
>> +#define NVDDR_TIMING_MODE_SHIFT 3
>> +
>> +#define ONFI_ID_LEN 8
>> +#define TEMP_BUF_SIZE 512
>> +#define NVDDR_MODE_PACKET_SIZE 8
>> +#define SDR_MODE_PACKET_SIZE 4
>> +
>> +/**
>> + * struct anfc_ecc_matrix - Defines ecc information storage format
>> + * @pagesize: Page size in bytes.
>> + * @codeword_size: Code word size information.
>> + * @eccbits: Number of ecc bits.
>> + * @bch: Bch / Hamming mode enable/disable.
>> + * @eccsize: Ecc size information.
>> + */
>> +struct anfc_ecc_matrix {
>> + u32 pagesize;
>> + u32 codeword_size;
>> + u8 eccbits;
>> + u8 bch;
>> + u16 eccsize;
>> +};
>> +
>> +static const struct anfc_ecc_matrix ecc_matrix[] = {
>> + {512, 512, 1, 0, 0x3},
>> + {512, 512, 4, 1, 0x7},
>> + {512, 512, 8, 1, 0xD},
>> + /* 2K byte page */
>> + {2048, 512, 1, 0, 0xC},
>> + {2048, 512, 4, 1, 0x1A},
>> + {2048, 512, 8, 1, 0x34},
>> + {2048, 512, 12, 1, 0x4E},
>> + {2048, 1024, 24, 1, 0x54},
>> + /* 4K byte page */
>> + {4096, 512, 1, 0, 0x18},
>> + {4096, 512, 4, 1, 0x34},
>> + {4096, 512, 8, 1, 0x68},
>> + {4096, 512, 12, 1, 0x9C},
>> + {4096, 1024, 4, 1, 0xA8},
>> + /* 8K byte page */
>> + {8192, 512, 1, 0, 0x30},
>> + {8192, 512, 4, 1, 0x68},
>> + {8192, 512, 8, 1, 0xD0},
>> + {8192, 512, 12, 1, 0x138},
>> + {8192, 1024, 24, 1, 0x150},
>> + /* 16K byte page */
>> + {16384, 512, 1, 0, 0x60},
>> + {16384, 512, 4, 1, 0xD0},
>> + {16384, 512, 8, 1, 0x1A0},
>> + {16384, 512, 12, 1, 0x270},
>> + {16384, 1024, 24, 1, 0x2A0}
>> +};
>
> Do you really need this association table. IMO, those are
> information you could deduce from nand_chip info (ecc_strength,
> ecc_step_size, ...). eccsize can be calculated this way:
>
> info->pagesize = mtd->writesize;
> info->codeword_size = chip->ecc_step_ds;
> info->eccbits = chip->ecc_strength_ds;
>
> if (info->eccbits > 1)
> info->bch = true;
>
> steps = info->pagesize / info->codeword_size;
>
> if (!bch)
> info->eccsize = 3 * steps;
> else
> info->eccsize =
> DIV_ROUND_UP(fls(8 * info->codeword_size) *
> ecc->strength * steps, 8);
>
> And, too bad we have to deal with another engine operating at the bit
> level instead of aligning each ECC step to the next byte (GPMI engine
> does that too, and it's a pain to deal with).
>

1. There is no direct formula for calculating the ecc size. For bch
ecc algorithms,
the ecc size can vary based on the chosen polynomial.

2. This controller supports only the page sizes, number of ecc bits
and code word size
defined in the table. driver returns error if there is no match for
the page size and codeword size.

>> +
>> +/**
>> + * struct anfc - Defines the Arasan NAND flash driver instance
>> + * @chip: NAND chip information structure.
>> + * @mtd: MTD information structure.
>> + * @dev: Pointer to the device structure.
>> + * @base: Virtual address of the NAND flash device.
>> + * @curr_cmd: Current command issued.
>> + * @clk_sys: Pointer to the system clock.
>> + * @clk_flash: Pointer to the flash clock.
>> + * @dma: Dma enable/disable.
>> + * @bch: Bch / Hamming mode enable/disable.
>> + * @err: Error identifier.
>> + * @iswriteoob: Identifies if oob write operation is required.
>> + * @buf: Buffer used for read/write byte operations.
>> + * @raddr_cycles: Row address cycle information.
>> + * @caddr_cycles: Column address cycle information.
>> + * @irq: irq number
>> + * @pktsize: Packet size for read / write operation.
>> + * @bufshift: Variable used for indexing buffer operation
>> + * @rdintrmask: Interrupt mask value for read operation.
>> + * @num_cs: Number of chip selects in use.
>> + * @spktsize: Packet size in ddr mode for status operation.
>> + * @bufrdy: Completion event for buffer ready.
>> + * @xfercomp: Completion event for transfer complete.
>> + * @ecclayout: Ecc layout object
>> + */
>> +struct anfc {
>> + struct nand_chip chip;
>> + struct mtd_info mtd;
>
> Drop this field ^ . It's now directly embedded in nand_chip and can be
> accessed with nand_to_mtd().
>
>> + struct device *dev;
>> +
>> + void __iomem *base;
>> + int curr_cmd;
>> + struct clk *clk_sys;
>> + struct clk *clk_flash;
>> +
>> + bool dma;
>> + bool bch;
>> + bool err;
>> + bool iswriteoob;
>> +
>> + u8 buf[TEMP_BUF_SIZE];
>> +
>> + u16 raddr_cycles;
>> + u16 caddr_cycles;
>> +
>> + u32 irq;
>> + u32 pktsize;
>> + u32 bufshift;
>> + u32 rdintrmask;
>> + u32 num_cs;
>> + u32 spktsize;
>> +
>> + struct completion bufrdy;
>> + struct completion xfercomp;
>
> I'm pretty sure you don't need 2 different completion fields. You can
> do the same thing with a single one and a different approach (see here
> [2]).
>
>> + struct nand_ecclayout ecclayout;
>
> As pointed by Brian, you probably start using the mtd_ooblayout_ops
> approach, which should normally reach mainline before this driver.
>

Yes.

>> +};
>
> As many other NAND controller drivers do, it seems your mixing the NAND
> controller and NAND chip concept.
>
> Could you create and anand struct containing the nand_chip field and
> every other fields that are chip specific, and keep the controller
> specific data in this anfc struct?
>
> Note that the anand and anfc struct can be like together through the
> chip->controller field (you can have a look at the sunxi_nand driver as
> a reference).
>

Ok. I will look into this.

>> +
>> +static inline struct anfc *to_anfc(struct mtd_info *mtd)
>> +{
>> + return container_of(mtd, struct anfc, mtd);
>> +}
>
> This should be adapted accordingly.
>
>> +
>> +static u8 anfc_page(u32 pagesize)
>> +{
>> + switch (pagesize) {
>> + case 512:
>> + return REG_PAGE_SIZE_512;
>> + case 1024:
>> + return REG_PAGE_SIZE_1K;
>> + case 2048:
>> + return REG_PAGE_SIZE_2K;
>> + case 4096:
>> + return REG_PAGE_SIZE_4K;
>> + case 8192:
>> + return REG_PAGE_SIZE_8K;
>> + case 16384:
>> + return REG_PAGE_SIZE_16K;
>> + default:
>> + break;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
>> +{
>> + writel(val, nfc->base + INTR_STS_EN_OFST);
>> + writel(val, nfc->base + INTR_SIG_EN_OFST);
>> +}
>> +
>> +static int anfc_wait_for_event(struct anfc *nfc, u32 event)
>> +{
>> + struct completion *comp;
>> +
>> + if (event == XFER_COMPLETE)
>> + comp = &nfc->xfercomp;
>> + else
>> + comp = &nfc->bufrdy;
>> +
>
> As said previously, I doubt you need 2 different completion objects.
> And you should probably enable interrupts directly inside this function.
>
>> + return wait_for_completion_timeout(comp,
>> + msecs_to_jiffies(EVNT_TIMEOUT));
>> +}
>> +
>> +static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
>> + u32 pktcount)
>> +{
>> + writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
>> +}
>> +
>> +static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
>> +{
>> + writel(cmd1 | (cmd2 << CMD2_SHIFT) |
>> + (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
>> + nfc->base + ECC_SPR_CMD_OFST);
>> +}
>> +
>> +static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
>> +{
>> + u32 val;
>> +
>> + writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
>> +
>> + val = readl(nfc->base + MEM_ADDR2_OFST);
>> + val = (val & ~MEM_ADDR_MASK) |
>> + ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
>> + writel(val, nfc->base + MEM_ADDR2_OFST);
>> +}
>> +
>> +static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
>> + u8 dmamode, u32 pagesize, u8 addrcycles)
>> +{
>> + u32 regval;
>> +
>> + regval = cmd1 | (cmd2 << CMD2_SHIFT);
>> + if (dmamode && nfc->dma)
>> + regval |= DMA_ENABLE << DMA_EN_SHIFT;
>> + if (addrcycles)
>> + regval |= addrcycles << ADDR_CYCLES_SHIFT;
>> + if (pagesize)
>> + regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
>> + writel(regval, nfc->base + CMD_OFST);
>> +}
>> +
>> +static int anfc_device_ready(struct mtd_info *mtd,
>> + struct nand_chip *chip)
>
> You named the function anfc_device_ready() while it"s actually
> implementing ->waitfunc. Could you rename it to anfc_waitfunc() to
> avoid confusion.
>

As you suggested below, it is replicating the nand_wait function.
so, i will cosider using the generic functions.

>> +{
>> + u8 status;
>> + unsigned long timeout = jiffies + STATUS_TIMEOUT;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + do {
>> + chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
>> + status = chip->read_byte(mtd);
>> + if (status & ONFI_STATUS_READY)
>> + break;
>> + cpu_relax();
>> + } while (!time_after_eq(jiffies, timeout));
>> +
>> + if (status & ONFI_STATUS_FAIL)
>> + return NAND_STATUS_FAIL;
>> +
>> + if (time_after_eq(jiffies, timeout)) {
>> + dev_err(nfc->dev, "%s timed out\n", __func__);
>> + return -ETIMEDOUT;
>> + }
>> +
>> + return 0;
>> +}
>
> It seems you're re-implementing nand_wait() [1]. Maybe it's worth
> exporting this function instead of re-implementing it.
>
>> +
>> +static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
>> + int page)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>
> ->rdintrmask assignment should be moved into ->read_buf()
> implementation IMO.
>
>> + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
>> + int page)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + nfc->iswriteoob = true;
>> + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
>> + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
>> + nfc->iswriteoob = false;
>> +
>> + return 0;
>> +}
>> +
>> +static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>> +{
>> + u32 pktcount, pktsize;
>> + unsigned int buf_rd_cnt = 0;
>> + u32 *bufptr = (u32 *)buf;
>> + struct anfc *nfc = to_anfc(mtd);
>> + dma_addr_t paddr;
>> +
>> + if (nfc->curr_cmd == NAND_CMD_READ0) {
>> + pktsize = nfc->pktsize;
>> + pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
>> + } else {
>> + pktsize = len;
>> + pktcount = 1;
>> + }
>
> Can we avoid making this selection conditional on the READ0 cmd?
> I've worked with some NANDs that required accessing specific features
> using a vendor-specific command + a READ command, and I'm not sure we
> want to use this pktsize optimization in this case.
>
> Maybe you should set the pktsize info in your ecc->read_page()
> implementation (of course this means you'll have to reset it to 1
> before leaving ->read_page()), and just calculate pktcount here.
>

let me check on this.

>> +
>> + anfc_setpktszcnt(nfc, pktsize, pktcount);
>> +
>> + if (nfc->dma) {
>> + paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
>
> What if buf has been vmallocated, and len is more than PAGE_SIZE (or
> buf is not page aligned, and buf + len cross a page boundary).
>
> I proposed a generic approach to deal with that [3], but if you only
> support physically contiguous buffers, then you should make sure buf
> was not vmallocated (using is_vmalloc_addr()).
>

Ok. Our controller supports physically contiguous buffers. i will check
if it is vmallocated.

>> + if (dma_mapping_error(nfc->dev, paddr)) {
>> + dev_err(nfc->dev, "Read buffer mapping error");
>> + return;
>> + }
>> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + writel(PROG_PGRD, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
>> + return;
>> + }
>> +
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + writel(PROG_PGRD, nfc->base + PROG_OFST);
>> +
>> + while (buf_rd_cnt < pktcount) {
>> +
>> + anfc_wait_for_event(nfc, READ_READY);
>> + buf_rd_cnt++;
>> +
>> + if (buf_rd_cnt == pktcount)
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> +
>> + readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
>> + bufptr += pktsize/4;
>> +
>> + if (buf_rd_cnt < pktcount)
>> + anfc_enable_intrs(nfc, nfc->rdintrmask);
>> + }
>> +
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>
> Hm, this looks like a complicated way of doing what you want. How about
> changing for the following logic?
>
> for (buf_rd_cnt = 0; buf_rd_cnt < pktcount; buf_rd_cnt)
> {
> anfc_wait_for_event(nfc, READ_READY);
> readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> bufptr += pktsize/4;
> }
>
> anfc_wait_for_event(nfc, XFER_COMPLETE);
>
> This implementation suppose you've moved interrupt activation in
> anfc_wait_for_event().
>

Ok. Thanks. I will switch to for loop than while loop.

>> +}
>> +
>> +static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
>> +{
>> + u32 pktcount, pktsize;
>> + unsigned int buf_wr_cnt = 0;
>> + u32 *bufptr = (u32 *)buf;
>> + struct anfc *nfc = to_anfc(mtd);
>> + dma_addr_t paddr;
>> +
>> + if (nfc->iswriteoob) {
>> + pktsize = len;
>> + pktcount = 1;
>> + } else {
>> + pktsize = nfc->pktsize;
>> + pktcount = mtd->writesize / pktsize;
>> + }
>
> Same remark as for read_buf(). pktsize should probably be set to
> nfc->pktsize in ecc->write_page() implem, and reset to 1 before leaving
> the function.
>
>> +
>> + anfc_setpktszcnt(nfc, pktsize, pktcount);
>> +
>> + if (nfc->dma) {
>> + paddr = dma_map_single(nfc->dev, (void *)buf, len,
>> + DMA_TO_DEVICE);
>> + if (dma_mapping_error(nfc->dev, paddr)) {
>> + dev_err(nfc->dev, "Write buffer mapping error");
>> + return;
>> + }
>> + lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
>> + return;
>> + }
>> +
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> + writel(PROG_PGPROG, nfc->base + PROG_OFST);
>> +
>> + while (buf_wr_cnt < pktcount) {
>> + anfc_wait_for_event(nfc, WRITE_READY);
>> +
>> + buf_wr_cnt++;
>> + if (buf_wr_cnt == pktcount)
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> +
>> + writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
>> + bufptr += pktsize/4;
>> +
>> + if (buf_wr_cnt < pktcount)
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> + }
>> +
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>
> And the same goes for the other comments I made on ->read_buf().
>
>> +}
>> +
>> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
>> + struct nand_chip *chip, uint8_t *buf,
>> + int oob_required, int page)
>> +{
>> + u32 val;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
>> +
>> + val = readl(nfc->base + CMD_OFST);
>> + val = val | ECC_ENABLE;
>> + writel(val, nfc->base + CMD_OFST);
>> +
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> +
>> + if (!nfc->bch)
>> + nfc->rdintrmask = MBIT_ERROR;
>> +
>> + chip->read_buf(mtd, buf, mtd->writesize);
>> +
>> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
>> + if (nfc->bch) {
>> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
>> + } else {
>> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
>> + mtd->ecc_stats.corrected += val;
>> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
>> + mtd->ecc_stats.failed += val;
>> + /* Clear ecc error count register 1Bit, 2Bit */
>> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
>> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
>> + }
>
> Not sure if your controller already handles the 'bitflips in erased
> pages' case, but you might want to have a look at the
> nand_check_erased_ecc_chunk() [4] function if that's not the case.
>

it will not handle the bitflips in erased pages. I will check this one.

>> + nfc->err = false;
>> +
>> + if (oob_required)
>> + chip->ecc.read_oob(mtd, chip, page);
>
> You should disable the ECC engine before leaving the function.
>

Not needed. Because ecc state need to be configured for every nand command.
so, no need to disable explicitly.

>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_write_page_hwecc(struct mtd_info *mtd,
>> + struct nand_chip *chip, const uint8_t *buf,
>> + int oob_required, int page)
>> +{
>> + u32 val;
>> + unsigned int i;
>> + struct anfc *nfc = to_anfc(mtd);
>> + uint8_t *ecc_calc = chip->buffers->ecccalc;
>> + uint32_t *eccpos = chip->ecc.layout->eccpos;
>> +
>> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
>> +
>> + val = readl(nfc->base + CMD_OFST);
>> + val = val | ECC_ENABLE;
>> + writel(val, nfc->base + CMD_OFST);
>> +
>> + chip->write_buf(mtd, buf, mtd->writesize);
>> +
>> + if (oob_required) {
>> + anfc_device_ready(mtd, chip);
>> + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> + chip->read_buf(mtd, ecc_calc, mtd->oobsize);
>> + for (i = 0; i < chip->ecc.total; i++)
>> + chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
>> + chip->ecc.write_oob(mtd, chip, page);
>> + }
>> +
>
> Ditto.
>
>> + return 0;
>> +}
>> +
>> +static u8 anfc_read_byte(struct mtd_info *mtd)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + return nfc->buf[nfc->bufshift++];
>
> ->read_byte() should normally be a wrapper around ->read_buf(), because
> you don't know ahead of time, how many time ->read_byte() will be
> called after the CMD/ADDR cycles, and thus cannot deduce how much
> should be put in the temporary buffer.
> So I'd suggest you put the logic to fill nfc->buf[] directly in there
> instead of putting it in ->cmdfunc().
>

Controller doesn't support dma for readid, parameter page commands
and the response for these commands shall be read from the fifo which is
of 32 bit width.

Also for status command, the response shall be read from the
controller register.

Arasan controller will not allow byte reads. So, i have opted this
implemetation.

In either case, number of bytes to be read will not ne known. For now,
it estimates
the number of bytes to be read based on the command that is issued and
resets the
buffer shift counter if it see a new command request.

Also its not a generic controller, supports only the commands defined in the
program register.



>> +}
>> +
>> +static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
>> +{
>> + u32 *bufptr = (u32 *)buf;
>> +
>> + anfc_enable_intrs(nfc, WRITE_READY);
>> +
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, WRITE_READY);
>> +
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>> +
>> +static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
>> +{
>> + u32 *bufptr = (u32 *)nfc->buf;
>> +
>> + anfc_enable_intrs(nfc, READ_READY);
>> +
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, READ_READY);
>> +
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> +}
>
> If you do what I suggest, you shouldn't need those
> anfc_writefifo/readfifo() functions (or at least, not in their current
> form).
>
>> +
>> +static int anfc_ecc_init(struct mtd_info *mtd,
>> + struct nand_ecc_ctrl *ecc)
>> +{
>> + u32 ecc_addr, regval;
>> + unsigned int bchmode = 0, i, oob_index;
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct anfc *nfc = to_anfc(mtd);
>> + int found = -1;
>> +
>> + nand_chip->ecc.mode = NAND_ECC_HW;
>> + nand_chip->ecc.read_page = anfc_read_page_hwecc;
>> + nand_chip->ecc.write_page = anfc_write_page_hwecc;
>> + nand_chip->ecc.write_oob = anfc_write_oob;
>> + nand_chip->ecc.read_oob = anfc_read_oob;
>> +
>> + for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
>> + if ((ecc_matrix[i].pagesize == mtd->writesize) &&
>> + (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
>> + found = i;
>> + if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
>> + break;
>> + }
>> + }
>
> Should be replaced by the logic I proposed at the beginning of this
> answer.
>
>> +
>> + if (found < 0) {
>> + dev_err(nfc->dev, "ECC scheme not supported");
>> + return 1;
>> + }
>> + if (ecc_matrix[found].bch) {
>> + switch (ecc_matrix[found].eccbits) {
>> + case 12:
>> + bchmode = 0x1;
>> + break;
>> + case 8:
>> + bchmode = 0x2;
>> + break;
>> + case 4:
>> + bchmode = 0x3;
>> + break;
>> + case 24:
>> + bchmode = 0x4;
>> + break;
>> + default:
>> + bchmode = 0x0;
>> + }
>> + }
>> +
>> + nand_chip->ecc.strength = ecc_matrix[found].eccbits;
>> + nand_chip->ecc.size = ecc_matrix[found].codeword_size;
>> + nand_chip->ecc.steps = ecc_matrix[found].pagesize /
>> + ecc_matrix[found].codeword_size;
>> + nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
>> + nand_chip->ecc.steps;
>
> Okay, because of the unaligned eccsize value, you might end up with
> something slightly different from the reality.
>
>> + nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
>> + nfc->bch = ecc_matrix[found].bch;
>> + oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
>> + ecc_addr = mtd->writesize + oob_index;
>> +
>> + for (i = 0; i < nand_chip->ecc.size; i++)
>> + nfc->ecclayout.eccpos[i] = oob_index + i;
>> +
>> + nfc->ecclayout.oobfree->offset = 2;
>> + nfc->ecclayout.oobfree->length = oob_index -
>> + nfc->ecclayout.oobfree->offset;
>> +
>> + nand_chip->ecc.layout = &nfc->ecclayout;
>
> Should be replaced by mtd_set_ooblayout().
>
>> + regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
>> + (ecc_matrix[found].bch << BCH_EN_SHIFT);
>> + writel(regval, nfc->base + ECC_OFST);
>> +
>> + regval = readl(nfc->base + MEM_ADDR2_OFST);
>> + regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
>> + writel(regval, nfc->base + MEM_ADDR2_OFST);
>> +
>> + if (nand_chip->ecc_step_ds >= 1024)
>> + nfc->pktsize = 1024;
>> + else
>> + nfc->pktsize = 512;
>> +
>> + return 0;
>> +}
>> +
>> +static void anfc_cmd_function(struct mtd_info *mtd,
>> + unsigned int cmd, int column, int page_addr)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> + bool wait = false, read = false;
>> + u32 addrcycles, prog;
>> + u32 *bufptr = (u32 *)nfc->buf;
>> +
>> + nfc->bufshift = 0;
>> + nfc->curr_cmd = cmd;
>> +
>> + if (page_addr == -1)
>> + page_addr = 0;
>> + if (column == -1)
>> + column = 0;
>> +
>> + switch (cmd) {
>> + case NAND_CMD_RESET:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
>> + prog = PROG_RST;
>> + wait = true;
>> + break;
>> + case NAND_CMD_SEQIN:
>> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
>> + mtd->writesize, addrcycles);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + break;
>> + case NAND_CMD_READOOB:
>> + column += mtd->writesize;
>> + case NAND_CMD_READ0:
>> + case NAND_CMD_READ1:
>> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
>> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
>> + mtd->writesize, addrcycles);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + break;
>> + case NAND_CMD_RNDOUT:
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
>> + mtd->writesize, 2);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + if (nfc->dma)
>> + nfc->rdintrmask = XFER_COMPLETE;
>> + else
>> + nfc->rdintrmask = READ_READY;
>> + break;
>> + case NAND_CMD_PARAM:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
>> + anfc_readfifo(nfc, PROG_RDPARAM,
>> + sizeof(struct nand_onfi_params));
>> + break;
>> + case NAND_CMD_READID:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
>> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
>> + break;
>> + case NAND_CMD_ERASE1:
>> + addrcycles = nfc->raddr_cycles;
>> + prog = PROG_ERASE;
>> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
>> + column = page_addr & 0xffff;
>> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + wait = true;
>> + break;
>> + case NAND_CMD_STATUS:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
>> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + prog = PROG_STATUS;
>> + wait = read = true;
>> + break;
>> + case NAND_CMD_GET_FEATURES:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
>> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
>> + break;
>> + case NAND_CMD_SET_FEATURES:
>> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
>> + anfc_setpagecoladdr(nfc, page_addr, column);
>> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
>> + break;
>> + default:
>> + return;
>> + }
>> +
>> + if (wait) {
>> + anfc_enable_intrs(nfc, XFER_COMPLETE);
>> + writel(prog, nfc->base + PROG_OFST);
>> + anfc_wait_for_event(nfc, XFER_COMPLETE);
>> + }
>> +
>> + if (read)
>> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
>> +}
>
> Can you try to implement ->cmd_ctrl() instead of ->cmdfunc(). This way
> you'll benefit from all the improvements we'll to the default
> nand_command_lp() and nand_command() implementation, and this also ease
> maintenance on our side.
> According to what I've seen so far this should be doable.
>

I see your point but since this controller is providing the glue logic
for issuing the nand
commands, i doubt adopting cmd_ctrl would be not stright forward. IMHO, cmd_ctrl
shall be used for controllers that provide low level access and allow
more confgurable options.


>> +
>> +static void anfc_select_chip(struct mtd_info *mtd, int num)
>> +{
>> + u32 val;
>> + struct anfc *nfc = to_anfc(mtd);
>> +
>> + if (num == -1)
>> + return;
>> +
>> + val = readl(nfc->base + MEM_ADDR2_OFST);
>> + val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
>> + writel(val, nfc->base + MEM_ADDR2_OFST);
>> +}
>> +
>> +static irqreturn_t anfc_irq_handler(int irq, void *ptr)
>> +{
>> + struct anfc *nfc = ptr;
>> + u32 regval = 0, status;
>> +
>> + status = readl(nfc->base + INTR_STS_OFST);
>> + if (status & XFER_COMPLETE) {
>> + complete(&nfc->xfercomp);
>> + regval |= XFER_COMPLETE;
>> + }
>> +
>> + if (status & READ_READY) {
>> + complete(&nfc->bufrdy);
>> + regval |= READ_READY;
>> + }
>> +
>> + if (status & WRITE_READY) {
>> + complete(&nfc->bufrdy);
>> + regval |= WRITE_READY;
>> + }
>> +
>> + if (status & MBIT_ERROR) {
>> + nfc->err = true;
>> + complete(&nfc->bufrdy);
>> + regval |= MBIT_ERROR;
>> + }
>> +
>> + if (regval) {
>> + writel(regval, nfc->base + INTR_STS_OFST);
>> + writel(0, nfc->base + INTR_STS_EN_OFST);
>> + writel(0, nfc->base + INTR_SIG_EN_OFST);
>> +
>> + return IRQ_HANDLED;
>> + }
>> +
>> + return IRQ_NONE;
>> +}
>
> Should be reworked to use only one completion object.
>
>> +
>> +static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
>> + int addr, uint8_t *subfeature_param)
>> +{
>> + struct anfc *nfc = to_anfc(mtd);
>> + int status;
>> +
>> + if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
>> + & ONFI_OPT_CMD_SET_GET_FEATURES))
>> + return -EINVAL;
>> +
>> + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
>> + anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
>> +
>> + status = chip->waitfunc(mtd, chip);
>> + if (status & NAND_STATUS_FAIL)
>> + return -EIO;
>> +
>> + return 0;
>> +}
> []
> A concrete example of why you should rework the
> ->read_byte()/->write_byte() implementation: this way you could get
> rid of this custom ->set_features() implem.
>
>> +
>> +static int anfc_init_timing_mode(struct anfc *nfc)
>> +{
>> + int mode, err;
>> + unsigned int feature[2], regval, i;
>> + struct nand_chip *chip = &nfc->chip;
>> + struct mtd_info *mtd = &nfc->mtd;
>> +
>> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
>> + /* Get nvddr timing modes */
>> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
>> + if (!mode) {
>> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
>> + regval = mode;
>> + } else {
>> + mode = fls(mode) - 1;
>> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
>> + mode |= ONFI_DATA_INTERFACE_NVDDR;
>> + }
>> +
>> + feature[0] = mode;
>> + for (i = 0; i < nfc->num_cs; i++) {
>> + chip->select_chip(mtd, i);
>> + err = chip->onfi_set_features(mtd, chip,
>> + ONFI_FEATURE_ADDR_TIMING_MODE,
>> + (uint8_t *)feature);
>> + if (err)
>> + return err;
>> + }
>> + writel(regval, nfc->base + DATA_INTERFACE_REG);
>> +
>> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
>> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
>
> You seem to switch from SDR to DDR mode, but I don't see any timing
> configuration. How is your controller able to adapt to different NAND
> timings?
>

it is doing the timing mode configuration. it will adapt to the timing
parameters
defined in the ONFI 3.1 spec for each of tje timing mode i.e 0-5.

>> +
>> + return 0;
>> +}
>> +
>> +static int anfc_probe(struct platform_device *pdev)
>> +{
>> + struct anfc *nfc;
>> + struct mtd_info *mtd;
>> + struct nand_chip *nand_chip;
>> + struct resource *res;
>> + struct mtd_part_parser_data ppdata;
>> + int err;
>> +
>> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
>> + if (!nfc)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(nfc->base))
>> + return PTR_ERR(nfc->base);
>> +
>> + mtd = &nfc->mtd;
>> + nand_chip = &nfc->chip;
>> + nand_chip->priv = nfc;
>> + mtd->priv = nand_chip;
>> + mtd->name = DRIVER_NAME;
>> + nfc->dev = &pdev->dev;
>> + mtd->dev.parent = &pdev->dev;
>> +
>> + nand_chip->cmdfunc = anfc_cmd_function;
>> + nand_chip->waitfunc = anfc_device_ready;
>
> You can leave nand_chip->waitfunc to NULL since your implementation is
> doing exactly what the default implementation does.
>
>> + nand_chip->chip_delay = 30;
>> + nand_chip->read_buf = anfc_read_buf;
>> + nand_chip->write_buf = anfc_write_buf;
>> + nand_chip->read_byte = anfc_read_byte;
>> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
>> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
>> + nand_chip->select_chip = anfc_select_chip;
>> + nand_chip->onfi_set_features = anfc_onfi_set_features;
>> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
>> + "arasan,has-mdma");
>> + nfc->num_cs = 1;
>> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);
>
> Already mentioned by Brian, but you should have a look at the
> sunxi-nand binding to see how to represent the NAND controller and its
> chips in the DT.
>

Ok. i will check the implementation.

>> + platform_set_drvdata(pdev, nfc);
>> + init_completion(&nfc->bufrdy);
>> + init_completion(&nfc->xfercomp);
>> + nfc->irq = platform_get_irq(pdev, 0);
>> + if (nfc->irq < 0) {
>> + dev_err(&pdev->dev, "platform_get_irq failed\n");
>> + return -ENXIO;
>> + }
>> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
>> + 0, "arasannfc", nfc);
>> + if (err)
>> + return err;
>> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
>> + if (IS_ERR(nfc->clk_sys)) {
>> + dev_err(&pdev->dev, "sys clock not found.\n");
>> + return PTR_ERR(nfc->clk_sys);
>> + }
>> +
>> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
>> + if (IS_ERR(nfc->clk_flash)) {
>> + dev_err(&pdev->dev, "flash clock not found.\n");
>> + return PTR_ERR(nfc->clk_flash);
>> + }
>> +
>> + err = clk_prepare_enable(nfc->clk_sys);
>> + if (err) {
>> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
>> + return err;
>> + }
>> +
>> + err = clk_prepare_enable(nfc->clk_flash);
>> + if (err) {
>> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
>> + goto clk_dis_sys;
>> + }
>> +
>> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
>> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
>> + if (err) {
>> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
>> + goto clk_dis_all;
>> + }
>> + if (nand_chip->onfi_version) {
>> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
>> + nfc->caddr_cycles =
>> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
>> + } else {
>> + /*For non-ONFI devices, configuring the address cyles as 5 */
>> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
>> + }
>
> Hm, this should not be dependent on ONFI support. It all depends on the
> CHIP size, which you can deduce from mtd->writesize.
>

Understood. but i have kept those values as fallback. In general this
controller talks
with onfi devices only. probably i need to keep error message return from there.

Thanks a lot for the detailed review. i will work on the commnets and
release the updated
patches for review.

Regards,
Punnaiah

>> +
>> + err = anfc_init_timing_mode(nfc);
>> + if (err) {
>> + dev_err(&pdev->dev, "timing mode init failed\n");
>> + goto clk_dis_all;
>> + }
>> +
>> + err = anfc_ecc_init(mtd, &nand_chip->ecc);
>> + if (err)
>> + goto clk_dis_all;
>> +
>> + err = nand_scan_tail(mtd);
>> + if (err) {
>> + dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
>> + goto clk_dis_all;
>> + }
>> +
>> + ppdata.of_node = pdev->dev.of_node;
>> +
>> + err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);
>> + if (err)
>> + goto clk_dis_all;
>> +
>> + return 0;
>> +
>> +clk_dis_all:
>> + clk_disable_unprepare(nfc->clk_flash);
>> +clk_dis_sys:
>> + clk_disable_unprepare(nfc->clk_sys);
>> +
>> + return err;
>> +}
>> +
>> +static int anfc_remove(struct platform_device *pdev)
>> +{
>> + struct anfc *nfc = platform_get_drvdata(pdev);
>> +
>> + clk_disable_unprepare(nfc->clk_sys);
>> + clk_disable_unprepare(nfc->clk_flash);
>> +
>> + nand_release(&nfc->mtd);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id anfc_ids[] = {
>> + { .compatible = "arasan,nfc-v3p10" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, anfc_ids);
>> +
>> +static struct platform_driver anfc_driver = {
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = anfc_ids,
>> + },
>> + .probe = anfc_probe,
>> + .remove = anfc_remove,
>> +};
>> +module_platform_driver(anfc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Xilinx, Inc");
>> +MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");
>
> That's all I got for now.
>
> Best Regards,
>
> Boris
>
>
> [1]http://lxr.free-electrons.com/source/drivers/mtd/nand/nand_base.c#L899
> [2]http://lxr.free-electrons.com/source/drivers/mtd/nand/sunxi_nand.c#L283
> [3]https://lkml.org/lkml/2016/3/8/270
> [4]http://lxr.free-electrons.com/source/drivers/mtd/nand/nand_base.c#L1161
>
> --
> Boris Brezillon, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

2016-03-09 09:50:26

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

Hi Punnaiah,

On Wed, 9 Mar 2016 00:10:52 +0530
punnaiah choudary kalluri <[email protected]> wrote:


> >> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> >> + {512, 512, 1, 0, 0x3},
> >> + {512, 512, 4, 1, 0x7},
> >> + {512, 512, 8, 1, 0xD},
> >> + /* 2K byte page */
> >> + {2048, 512, 1, 0, 0xC},
> >> + {2048, 512, 4, 1, 0x1A},
> >> + {2048, 512, 8, 1, 0x34},
> >> + {2048, 512, 12, 1, 0x4E},
> >> + {2048, 1024, 24, 1, 0x54},
> >> + /* 4K byte page */
> >> + {4096, 512, 1, 0, 0x18},
> >> + {4096, 512, 4, 1, 0x34},
> >> + {4096, 512, 8, 1, 0x68},
> >> + {4096, 512, 12, 1, 0x9C},
> >> + {4096, 1024, 4, 1, 0xA8},
> >> + /* 8K byte page */
> >> + {8192, 512, 1, 0, 0x30},
> >> + {8192, 512, 4, 1, 0x68},
> >> + {8192, 512, 8, 1, 0xD0},
> >> + {8192, 512, 12, 1, 0x138},
> >> + {8192, 1024, 24, 1, 0x150},
> >> + /* 16K byte page */
> >> + {16384, 512, 1, 0, 0x60},
> >> + {16384, 512, 4, 1, 0xD0},
> >> + {16384, 512, 8, 1, 0x1A0},
> >> + {16384, 512, 12, 1, 0x270},
> >> + {16384, 1024, 24, 1, 0x2A0}
> >> +};
> >
> > Do you really need this association table. IMO, those are
> > information you could deduce from nand_chip info (ecc_strength,
> > ecc_step_size, ...). eccsize can be calculated this way:
> >
> > info->pagesize = mtd->writesize;
> > info->codeword_size = chip->ecc_step_ds;
> > info->eccbits = chip->ecc_strength_ds;
> >
> > if (info->eccbits > 1)
> > info->bch = true;
> >
> > steps = info->pagesize / info->codeword_size;
> >
> > if (!bch)
> > info->eccsize = 3 * steps;
> > else
> > info->eccsize =
> > DIV_ROUND_UP(fls(8 * info->codeword_size) *
> > ecc->strength * steps, 8);
> >
> > And, too bad we have to deal with another engine operating at the bit
> > level instead of aligning each ECC step to the next byte (GPMI engine
> > does that too, and it's a pain to deal with).
> >
>
> 1. There is no direct formula for calculating the ecc size. For bch
> ecc algorithms,
> the ecc size can vary based on the chosen polynomial.

I checked for all the table entries, and this formula works (note that
it takes the codeword_size into account, which is probably what you
are talking about when mentioning the different polynomials).

>
> 2. This controller supports only the page sizes, number of ecc bits
> and code word size
> defined in the table. driver returns error if there is no match for
> the page size and codeword size.

I agree for the pagesize, ECC step_size and ECC strength limitations,
but that's something you can check without having this association
table. And from my experience, keeping such tables to describe all the
possible associations is not a good idea :-/.

[...]

> >> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> >> + struct nand_chip *chip, uint8_t *buf,
> >> + int oob_required, int page)
> >> +{
> >> + u32 val;
> >> + struct anfc *nfc = to_anfc(mtd);
> >> +
> >> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
> >> +
> >> + val = readl(nfc->base + CMD_OFST);
> >> + val = val | ECC_ENABLE;
> >> + writel(val, nfc->base + CMD_OFST);
> >> +
> >> + if (nfc->dma)
> >> + nfc->rdintrmask = XFER_COMPLETE;
> >> + else
> >> + nfc->rdintrmask = READ_READY;
> >> +
> >> + if (!nfc->bch)
> >> + nfc->rdintrmask = MBIT_ERROR;
> >> +
> >> + chip->read_buf(mtd, buf, mtd->writesize);
> >> +
> >> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
> >> + if (nfc->bch) {
> >> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> >> + } else {
> >> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> >> + mtd->ecc_stats.corrected += val;
> >> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> >> + mtd->ecc_stats.failed += val;
> >> + /* Clear ecc error count register 1Bit, 2Bit */
> >> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> >> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> >> + }
> >
> > Not sure if your controller already handles the 'bitflips in erased
> > pages' case, but you might want to have a look at the
> > nand_check_erased_ecc_chunk() [4] function if that's not the case.
> >
>
> it will not handle the bitflips in erased pages. I will check this one.
>
> >> + nfc->err = false;
> >> +
> >> + if (oob_required)
> >> + chip->ecc.read_oob(mtd, chip, page);
> >
> > You should disable the ECC engine before leaving the function.
> >
>
> Not needed. Because ecc state need to be configured for every nand command.
> so, no need to disable explicitly.

Except, you don't know what the next NAND command will be, and if it's
a raw access ECC_ENABLE bit has to be cleared.
Also, you don't know when the NAND command is sent, which type of read
will take place, so putting that in ->cmdfunc() is not a solution.

>
> >> +
> >> + return 0;
> >> +}
> >> +

[...]

> >> +
> >> +static u8 anfc_read_byte(struct mtd_info *mtd)
> >> +{
> >> + struct anfc *nfc = to_anfc(mtd);
> >> +
> >> + return nfc->buf[nfc->bufshift++];
> >
> > ->read_byte() should normally be a wrapper around ->read_buf(), because
> > you don't know ahead of time, how many time ->read_byte() will be
> > called after the CMD/ADDR cycles, and thus cannot deduce how much
> > should be put in the temporary buffer.
> > So I'd suggest you put the logic to fill nfc->buf[] directly in there
> > instead of putting it in ->cmdfunc().
> >
>
> Controller doesn't support dma for readid, parameter page commands
> and the response for these commands shall be read from the fifo which is
> of 32 bit width.

Okay, but I was not referring to DMA here. If ->read_buf() always
implies using DMA, then maybe you should rework it to make DMA
operations optional, and only activate them when ->read_page() is used.

>
> Also for status command, the response shall be read from the
> controller register.
>
> Arasan controller will not allow byte reads. So, i have opted this
> implemetation.
>
> In either case, number of bytes to be read will not ne known. For now,
> it estimates
> the number of bytes to be read based on the command that is issued and
> resets the
> buffer shift counter if it see a new command request.

Which is not a good idea. As I said, you can't guess how many bytes the
framework will read after a specific command (CCed Ezequiel, who,
IIRC, had this kind of problems with the pxa3xx driver).

>
> Also its not a generic controller, supports only the commands defined in the
> program register.
>

Hm, are you sure there's no way to send raw commands, or CMD and ADDR
cycles independently?
AFAICS, you're still configuring the ADDR and CMD cycles manually (in
anfc_prepare_cmd()), which seems pretty generic to me...

>
>
> >> +}

[...]

> >> +static void anfc_cmd_function(struct mtd_info *mtd,
> >> + unsigned int cmd, int column, int page_addr)
> >> +{
> >> + struct anfc *nfc = to_anfc(mtd);
> >> + bool wait = false, read = false;
> >> + u32 addrcycles, prog;
> >> + u32 *bufptr = (u32 *)nfc->buf;
> >> +
> >> + nfc->bufshift = 0;
> >> + nfc->curr_cmd = cmd;
> >> +
> >> + if (page_addr == -1)
> >> + page_addr = 0;
> >> + if (column == -1)
> >> + column = 0;
> >> +
> >> + switch (cmd) {
> >> + case NAND_CMD_RESET:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> >> + prog = PROG_RST;
> >> + wait = true;
> >> + break;
> >> + case NAND_CMD_SEQIN:
> >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> >> + mtd->writesize, addrcycles);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + break;
> >> + case NAND_CMD_READOOB:
> >> + column += mtd->writesize;
> >> + case NAND_CMD_READ0:
> >> + case NAND_CMD_READ1:
> >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> >> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
> >> + mtd->writesize, addrcycles);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + break;
> >> + case NAND_CMD_RNDOUT:
> >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> >> + mtd->writesize, 2);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + if (nfc->dma)
> >> + nfc->rdintrmask = XFER_COMPLETE;
> >> + else
> >> + nfc->rdintrmask = READ_READY;
> >> + break;
> >> + case NAND_CMD_PARAM:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> >> + anfc_readfifo(nfc, PROG_RDPARAM,
> >> + sizeof(struct nand_onfi_params));
> >> + break;
> >> + case NAND_CMD_READID:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> >> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> >> + break;
> >> + case NAND_CMD_ERASE1:
> >> + addrcycles = nfc->raddr_cycles;
> >> + prog = PROG_ERASE;
> >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
> >> + column = page_addr & 0xffff;
> >> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + wait = true;
> >> + break;
> >> + case NAND_CMD_STATUS:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> >> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + prog = PROG_STATUS;
> >> + wait = read = true;
> >> + break;
> >> + case NAND_CMD_GET_FEATURES:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> >> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> >> + break;
> >> + case NAND_CMD_SET_FEATURES:
> >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> >> + anfc_setpagecoladdr(nfc, page_addr, column);
> >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> >> + break;
> >> + default:
> >> + return;
> >> + }
> >> +
> >> + if (wait) {
> >> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> >> + writel(prog, nfc->base + PROG_OFST);
> >> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> >> + }
> >> +
> >> + if (read)
> >> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
> >> +}
> >
> > Can you try to implement ->cmd_ctrl() instead of ->cmdfunc(). This way
> > you'll benefit from all the improvements we'll to the default
> > nand_command_lp() and nand_command() implementation, and this also ease
> > maintenance on our side.
> > According to what I've seen so far this should be doable.
> >
>
> I see your point but since this controller is providing the glue logic
> for issuing the nand
> commands, i doubt adopting cmd_ctrl would be not stright forward. IMHO, cmd_ctrl
> shall be used for controllers that provide low level access and allow
> more confgurable options.

And as pointed above, your controller seems to be able to do that, but
maybe I'm missing something.

I know it implies reworking your driver, but as I said, if we keep
adding new drivers which are not able to send generic CMDs, then we'll
be screwed when we'll want to add support for newer NANDs (MLC NANDs),
which are usually providing private/vendor-specific commands for
common functions (like read-retry).

Patching all NAND controllers to support those new NANDs is not a
viable option, this is why I'd like to avoid those custom ->cmdfunc()
implementations in new NAND drivers, unless I'm proven this is really
impossible to do.

>
>

[...]

> >> +
> >> +static int anfc_init_timing_mode(struct anfc *nfc)
> >> +{
> >> + int mode, err;
> >> + unsigned int feature[2], regval, i;
> >> + struct nand_chip *chip = &nfc->chip;
> >> + struct mtd_info *mtd = &nfc->mtd;
> >> +
> >> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> >> + /* Get nvddr timing modes */
> >> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
> >> + if (!mode) {
> >> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> >> + regval = mode;
> >> + } else {
> >> + mode = fls(mode) - 1;
> >> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
> >> + mode |= ONFI_DATA_INTERFACE_NVDDR;
> >> + }
> >> +
> >> + feature[0] = mode;
> >> + for (i = 0; i < nfc->num_cs; i++) {
> >> + chip->select_chip(mtd, i);

You select the chip here, but it's never de-selected, which means the
last chip in the array stay selected until someone send a new command.

> >> + err = chip->onfi_set_features(mtd, chip,
> >> + ONFI_FEATURE_ADDR_TIMING_MODE,
> >> + (uint8_t *)feature);
> >> + if (err)
> >> + return err;
> >> + }
> >> + writel(regval, nfc->base + DATA_INTERFACE_REG);
> >> +
> >> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
> >> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
> >
> > You seem to switch from SDR to DDR mode, but I don't see any timing
> > configuration. How is your controller able to adapt to different NAND
> > timings?
> >
>
> it is doing the timing mode configuration. it will adapt to the timing
> parameters
> defined in the ONFI 3.1 spec for each of tje timing mode i.e 0-5.

I know what ONFI timings mode are, but usually when you change the mode
on the NAND side, you have to adapt your timings on the controller
side, and I don't see anything related to timings config on the
controller side here, hence my question.

>
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int anfc_probe(struct platform_device *pdev)
> >> +{
> >> + struct anfc *nfc;
> >> + struct mtd_info *mtd;
> >> + struct nand_chip *nand_chip;
> >> + struct resource *res;
> >> + struct mtd_part_parser_data ppdata;
> >> + int err;
> >> +
> >> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> >> + if (!nfc)
> >> + return -ENOMEM;
> >> +
> >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
> >> + if (IS_ERR(nfc->base))
> >> + return PTR_ERR(nfc->base);
> >> +
> >> + mtd = &nfc->mtd;
> >> + nand_chip = &nfc->chip;
> >> + nand_chip->priv = nfc;
> >> + mtd->priv = nand_chip;
> >> + mtd->name = DRIVER_NAME;
> >> + nfc->dev = &pdev->dev;
> >> + mtd->dev.parent = &pdev->dev;
> >> +
> >> + nand_chip->cmdfunc = anfc_cmd_function;
> >> + nand_chip->waitfunc = anfc_device_ready;
> >
> > You can leave nand_chip->waitfunc to NULL since your implementation is
> > doing exactly what the default implementation does.
> >
> >> + nand_chip->chip_delay = 30;
> >> + nand_chip->read_buf = anfc_read_buf;
> >> + nand_chip->write_buf = anfc_write_buf;
> >> + nand_chip->read_byte = anfc_read_byte;
> >> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> >> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> >> + nand_chip->select_chip = anfc_select_chip;
> >> + nand_chip->onfi_set_features = anfc_onfi_set_features;
> >> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
> >> + "arasan,has-mdma");
> >> + nfc->num_cs = 1;
> >> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);
> >
> > Already mentioned by Brian, but you should have a look at the
> > sunxi-nand binding to see how to represent the NAND controller and its
> > chips in the DT.
> >
>
> Ok. i will check the implementation.
>
> >> + platform_set_drvdata(pdev, nfc);
> >> + init_completion(&nfc->bufrdy);
> >> + init_completion(&nfc->xfercomp);
> >> + nfc->irq = platform_get_irq(pdev, 0);
> >> + if (nfc->irq < 0) {
> >> + dev_err(&pdev->dev, "platform_get_irq failed\n");
> >> + return -ENXIO;
> >> + }
> >> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> >> + 0, "arasannfc", nfc);
> >> + if (err)
> >> + return err;
> >> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> >> + if (IS_ERR(nfc->clk_sys)) {
> >> + dev_err(&pdev->dev, "sys clock not found.\n");
> >> + return PTR_ERR(nfc->clk_sys);
> >> + }
> >> +
> >> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> >> + if (IS_ERR(nfc->clk_flash)) {
> >> + dev_err(&pdev->dev, "flash clock not found.\n");
> >> + return PTR_ERR(nfc->clk_flash);
> >> + }
> >> +
> >> + err = clk_prepare_enable(nfc->clk_sys);
> >> + if (err) {
> >> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> >> + return err;
> >> + }
> >> +
> >> + err = clk_prepare_enable(nfc->clk_flash);
> >> + if (err) {
> >> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> >> + goto clk_dis_sys;
> >> + }
> >> +
> >> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
> >> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> >> + if (err) {
> >> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> >> + goto clk_dis_all;
> >> + }
> >> + if (nand_chip->onfi_version) {
> >> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
> >> + nfc->caddr_cycles =
> >> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> >> + } else {
> >> + /*For non-ONFI devices, configuring the address cyles as 5 */
> >> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
> >> + }
> >
> > Hm, this should not be dependent on ONFI support. It all depends on the
> > CHIP size, which you can deduce from mtd->writesize.
> >
>
> Understood. but i have kept those values as fallback. In general this
> controller talks
> with onfi devices only.

Hm, not sure this is a good argument. Your NAND controller should work
with both ONFI and non-ONFI NANDs: you don't know which NAND will be
available on future board designs...

Anyway, the check you're looking for is in nand_base.c IIRC.

Best Regards,

Boris


--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

2016-12-05 09:23:52

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

+Marek who reviewed v6

Hi Punnaiah,

I see you sent a v6, but you never answered the questions/remarks I had
in this email.

Can you please try to answer them (I'd like to understand how the
controller works)?

Thanks,

Boris

On Wed, 9 Mar 2016 10:50:07 +0100
Boris Brezillon <[email protected]> wrote:

> Hi Punnaiah,
>
> On Wed, 9 Mar 2016 00:10:52 +0530
> punnaiah choudary kalluri <[email protected]> wrote:
>
>
> > >> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> > >> + {512, 512, 1, 0, 0x3},
> > >> + {512, 512, 4, 1, 0x7},
> > >> + {512, 512, 8, 1, 0xD},
> > >> + /* 2K byte page */
> > >> + {2048, 512, 1, 0, 0xC},
> > >> + {2048, 512, 4, 1, 0x1A},
> > >> + {2048, 512, 8, 1, 0x34},
> > >> + {2048, 512, 12, 1, 0x4E},
> > >> + {2048, 1024, 24, 1, 0x54},
> > >> + /* 4K byte page */
> > >> + {4096, 512, 1, 0, 0x18},
> > >> + {4096, 512, 4, 1, 0x34},
> > >> + {4096, 512, 8, 1, 0x68},
> > >> + {4096, 512, 12, 1, 0x9C},
> > >> + {4096, 1024, 4, 1, 0xA8},
> > >> + /* 8K byte page */
> > >> + {8192, 512, 1, 0, 0x30},
> > >> + {8192, 512, 4, 1, 0x68},
> > >> + {8192, 512, 8, 1, 0xD0},
> > >> + {8192, 512, 12, 1, 0x138},
> > >> + {8192, 1024, 24, 1, 0x150},
> > >> + /* 16K byte page */
> > >> + {16384, 512, 1, 0, 0x60},
> > >> + {16384, 512, 4, 1, 0xD0},
> > >> + {16384, 512, 8, 1, 0x1A0},
> > >> + {16384, 512, 12, 1, 0x270},
> > >> + {16384, 1024, 24, 1, 0x2A0}
> > >> +};
> > >
> > > Do you really need this association table. IMO, those are
> > > information you could deduce from nand_chip info (ecc_strength,
> > > ecc_step_size, ...). eccsize can be calculated this way:
> > >
> > > info->pagesize = mtd->writesize;
> > > info->codeword_size = chip->ecc_step_ds;
> > > info->eccbits = chip->ecc_strength_ds;
> > >
> > > if (info->eccbits > 1)
> > > info->bch = true;
> > >
> > > steps = info->pagesize / info->codeword_size;
> > >
> > > if (!bch)
> > > info->eccsize = 3 * steps;
> > > else
> > > info->eccsize =
> > > DIV_ROUND_UP(fls(8 * info->codeword_size) *
> > > ecc->strength * steps, 8);
> > >
> > > And, too bad we have to deal with another engine operating at the bit
> > > level instead of aligning each ECC step to the next byte (GPMI engine
> > > does that too, and it's a pain to deal with).
> > >
> >
> > 1. There is no direct formula for calculating the ecc size. For bch
> > ecc algorithms,
> > the ecc size can vary based on the chosen polynomial.
>
> I checked for all the table entries, and this formula works (note that
> it takes the codeword_size into account, which is probably what you
> are talking about when mentioning the different polynomials).
>
> >
> > 2. This controller supports only the page sizes, number of ecc bits
> > and code word size
> > defined in the table. driver returns error if there is no match for
> > the page size and codeword size.
>
> I agree for the pagesize, ECC step_size and ECC strength limitations,
> but that's something you can check without having this association
> table. And from my experience, keeping such tables to describe all the
> possible associations is not a good idea :-/.
>
> [...]
>
> > >> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> > >> + struct nand_chip *chip, uint8_t *buf,
> > >> + int oob_required, int page)
> > >> +{
> > >> + u32 val;
> > >> + struct anfc *nfc = to_anfc(mtd);
> > >> +
> > >> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
> > >> +
> > >> + val = readl(nfc->base + CMD_OFST);
> > >> + val = val | ECC_ENABLE;
> > >> + writel(val, nfc->base + CMD_OFST);
> > >> +
> > >> + if (nfc->dma)
> > >> + nfc->rdintrmask = XFER_COMPLETE;
> > >> + else
> > >> + nfc->rdintrmask = READ_READY;
> > >> +
> > >> + if (!nfc->bch)
> > >> + nfc->rdintrmask = MBIT_ERROR;
> > >> +
> > >> + chip->read_buf(mtd, buf, mtd->writesize);
> > >> +
> > >> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
> > >> + if (nfc->bch) {
> > >> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> > >> + } else {
> > >> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> > >> + mtd->ecc_stats.corrected += val;
> > >> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> > >> + mtd->ecc_stats.failed += val;
> > >> + /* Clear ecc error count register 1Bit, 2Bit */
> > >> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> > >> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> > >> + }
> > >
> > > Not sure if your controller already handles the 'bitflips in erased
> > > pages' case, but you might want to have a look at the
> > > nand_check_erased_ecc_chunk() [4] function if that's not the case.
> > >
> >
> > it will not handle the bitflips in erased pages. I will check this one.
> >
> > >> + nfc->err = false;
> > >> +
> > >> + if (oob_required)
> > >> + chip->ecc.read_oob(mtd, chip, page);
> > >
> > > You should disable the ECC engine before leaving the function.
> > >
> >
> > Not needed. Because ecc state need to be configured for every nand command.
> > so, no need to disable explicitly.
>
> Except, you don't know what the next NAND command will be, and if it's
> a raw access ECC_ENABLE bit has to be cleared.
> Also, you don't know when the NAND command is sent, which type of read
> will take place, so putting that in ->cmdfunc() is not a solution.
>
> >
> > >> +
> > >> + return 0;
> > >> +}
> > >> +
>
> [...]
>
> > >> +
> > >> +static u8 anfc_read_byte(struct mtd_info *mtd)
> > >> +{
> > >> + struct anfc *nfc = to_anfc(mtd);
> > >> +
> > >> + return nfc->buf[nfc->bufshift++];
> > >
> > > ->read_byte() should normally be a wrapper around ->read_buf(), because
> > > you don't know ahead of time, how many time ->read_byte() will be
> > > called after the CMD/ADDR cycles, and thus cannot deduce how much
> > > should be put in the temporary buffer.
> > > So I'd suggest you put the logic to fill nfc->buf[] directly in there
> > > instead of putting it in ->cmdfunc().
> > >
> >
> > Controller doesn't support dma for readid, parameter page commands
> > and the response for these commands shall be read from the fifo which is
> > of 32 bit width.
>
> Okay, but I was not referring to DMA here. If ->read_buf() always
> implies using DMA, then maybe you should rework it to make DMA
> operations optional, and only activate them when ->read_page() is used.
>
> >
> > Also for status command, the response shall be read from the
> > controller register.
> >
> > Arasan controller will not allow byte reads. So, i have opted this
> > implemetation.
> >
> > In either case, number of bytes to be read will not ne known. For now,
> > it estimates
> > the number of bytes to be read based on the command that is issued and
> > resets the
> > buffer shift counter if it see a new command request.
>
> Which is not a good idea. As I said, you can't guess how many bytes the
> framework will read after a specific command (CCed Ezequiel, who,
> IIRC, had this kind of problems with the pxa3xx driver).
>
> >
> > Also its not a generic controller, supports only the commands defined in the
> > program register.
> >
>
> Hm, are you sure there's no way to send raw commands, or CMD and ADDR
> cycles independently?
> AFAICS, you're still configuring the ADDR and CMD cycles manually (in
> anfc_prepare_cmd()), which seems pretty generic to me...
>
> >
> >
> > >> +}
>
> [...]
>
> > >> +static void anfc_cmd_function(struct mtd_info *mtd,
> > >> + unsigned int cmd, int column, int page_addr)
> > >> +{
> > >> + struct anfc *nfc = to_anfc(mtd);
> > >> + bool wait = false, read = false;
> > >> + u32 addrcycles, prog;
> > >> + u32 *bufptr = (u32 *)nfc->buf;
> > >> +
> > >> + nfc->bufshift = 0;
> > >> + nfc->curr_cmd = cmd;
> > >> +
> > >> + if (page_addr == -1)
> > >> + page_addr = 0;
> > >> + if (column == -1)
> > >> + column = 0;
> > >> +
> > >> + switch (cmd) {
> > >> + case NAND_CMD_RESET:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> > >> + prog = PROG_RST;
> > >> + wait = true;
> > >> + break;
> > >> + case NAND_CMD_SEQIN:
> > >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> > >> + mtd->writesize, addrcycles);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + break;
> > >> + case NAND_CMD_READOOB:
> > >> + column += mtd->writesize;
> > >> + case NAND_CMD_READ0:
> > >> + case NAND_CMD_READ1:
> > >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> > >> + anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
> > >> + mtd->writesize, addrcycles);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + break;
> > >> + case NAND_CMD_RNDOUT:
> > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> > >> + mtd->writesize, 2);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + if (nfc->dma)
> > >> + nfc->rdintrmask = XFER_COMPLETE;
> > >> + else
> > >> + nfc->rdintrmask = READ_READY;
> > >> + break;
> > >> + case NAND_CMD_PARAM:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> > >> + anfc_readfifo(nfc, PROG_RDPARAM,
> > >> + sizeof(struct nand_onfi_params));
> > >> + break;
> > >> + case NAND_CMD_READID:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> > >> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> > >> + break;
> > >> + case NAND_CMD_ERASE1:
> > >> + addrcycles = nfc->raddr_cycles;
> > >> + prog = PROG_ERASE;
> > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
> > >> + column = page_addr & 0xffff;
> > >> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + wait = true;
> > >> + break;
> > >> + case NAND_CMD_STATUS:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> > >> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + prog = PROG_STATUS;
> > >> + wait = read = true;
> > >> + break;
> > >> + case NAND_CMD_GET_FEATURES:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> > >> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> > >> + break;
> > >> + case NAND_CMD_SET_FEATURES:
> > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> > >> + break;
> > >> + default:
> > >> + return;
> > >> + }
> > >> +
> > >> + if (wait) {
> > >> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> > >> + writel(prog, nfc->base + PROG_OFST);
> > >> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> > >> + }
> > >> +
> > >> + if (read)
> > >> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
> > >> +}
> > >
> > > Can you try to implement ->cmd_ctrl() instead of ->cmdfunc(). This way
> > > you'll benefit from all the improvements we'll to the default
> > > nand_command_lp() and nand_command() implementation, and this also ease
> > > maintenance on our side.
> > > According to what I've seen so far this should be doable.
> > >
> >
> > I see your point but since this controller is providing the glue logic
> > for issuing the nand
> > commands, i doubt adopting cmd_ctrl would be not stright forward. IMHO, cmd_ctrl
> > shall be used for controllers that provide low level access and allow
> > more confgurable options.
>
> And as pointed above, your controller seems to be able to do that, but
> maybe I'm missing something.
>
> I know it implies reworking your driver, but as I said, if we keep
> adding new drivers which are not able to send generic CMDs, then we'll
> be screwed when we'll want to add support for newer NANDs (MLC NANDs),
> which are usually providing private/vendor-specific commands for
> common functions (like read-retry).
>
> Patching all NAND controllers to support those new NANDs is not a
> viable option, this is why I'd like to avoid those custom ->cmdfunc()
> implementations in new NAND drivers, unless I'm proven this is really
> impossible to do.
>
> >
> >
>
> [...]
>
> > >> +
> > >> +static int anfc_init_timing_mode(struct anfc *nfc)
> > >> +{
> > >> + int mode, err;
> > >> + unsigned int feature[2], regval, i;
> > >> + struct nand_chip *chip = &nfc->chip;
> > >> + struct mtd_info *mtd = &nfc->mtd;
> > >> +
> > >> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> > >> + /* Get nvddr timing modes */
> > >> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
> > >> + if (!mode) {
> > >> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> > >> + regval = mode;
> > >> + } else {
> > >> + mode = fls(mode) - 1;
> > >> + regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
> > >> + mode |= ONFI_DATA_INTERFACE_NVDDR;
> > >> + }
> > >> +
> > >> + feature[0] = mode;
> > >> + for (i = 0; i < nfc->num_cs; i++) {
> > >> + chip->select_chip(mtd, i);
>
> You select the chip here, but it's never de-selected, which means the
> last chip in the array stay selected until someone send a new command.
>
> > >> + err = chip->onfi_set_features(mtd, chip,
> > >> + ONFI_FEATURE_ADDR_TIMING_MODE,
> > >> + (uint8_t *)feature);
> > >> + if (err)
> > >> + return err;
> > >> + }
> > >> + writel(regval, nfc->base + DATA_INTERFACE_REG);
> > >> +
> > >> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
> > >> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
> > >
> > > You seem to switch from SDR to DDR mode, but I don't see any timing
> > > configuration. How is your controller able to adapt to different NAND
> > > timings?
> > >
> >
> > it is doing the timing mode configuration. it will adapt to the timing
> > parameters
> > defined in the ONFI 3.1 spec for each of tje timing mode i.e 0-5.
>
> I know what ONFI timings mode are, but usually when you change the mode
> on the NAND side, you have to adapt your timings on the controller
> side, and I don't see anything related to timings config on the
> controller side here, hence my question.
>
> >
> > >> +
> > >> + return 0;
> > >> +}
> > >> +
> > >> +static int anfc_probe(struct platform_device *pdev)
> > >> +{
> > >> + struct anfc *nfc;
> > >> + struct mtd_info *mtd;
> > >> + struct nand_chip *nand_chip;
> > >> + struct resource *res;
> > >> + struct mtd_part_parser_data ppdata;
> > >> + int err;
> > >> +
> > >> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> > >> + if (!nfc)
> > >> + return -ENOMEM;
> > >> +
> > >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > >> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
> > >> + if (IS_ERR(nfc->base))
> > >> + return PTR_ERR(nfc->base);
> > >> +
> > >> + mtd = &nfc->mtd;
> > >> + nand_chip = &nfc->chip;
> > >> + nand_chip->priv = nfc;
> > >> + mtd->priv = nand_chip;
> > >> + mtd->name = DRIVER_NAME;
> > >> + nfc->dev = &pdev->dev;
> > >> + mtd->dev.parent = &pdev->dev;
> > >> +
> > >> + nand_chip->cmdfunc = anfc_cmd_function;
> > >> + nand_chip->waitfunc = anfc_device_ready;
> > >
> > > You can leave nand_chip->waitfunc to NULL since your implementation is
> > > doing exactly what the default implementation does.
> > >
> > >> + nand_chip->chip_delay = 30;
> > >> + nand_chip->read_buf = anfc_read_buf;
> > >> + nand_chip->write_buf = anfc_write_buf;
> > >> + nand_chip->read_byte = anfc_read_byte;
> > >> + nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> > >> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> > >> + nand_chip->select_chip = anfc_select_chip;
> > >> + nand_chip->onfi_set_features = anfc_onfi_set_features;
> > >> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
> > >> + "arasan,has-mdma");
> > >> + nfc->num_cs = 1;
> > >> + of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);
> > >
> > > Already mentioned by Brian, but you should have a look at the
> > > sunxi-nand binding to see how to represent the NAND controller and its
> > > chips in the DT.
> > >
> >
> > Ok. i will check the implementation.
> >
> > >> + platform_set_drvdata(pdev, nfc);
> > >> + init_completion(&nfc->bufrdy);
> > >> + init_completion(&nfc->xfercomp);
> > >> + nfc->irq = platform_get_irq(pdev, 0);
> > >> + if (nfc->irq < 0) {
> > >> + dev_err(&pdev->dev, "platform_get_irq failed\n");
> > >> + return -ENXIO;
> > >> + }
> > >> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> > >> + 0, "arasannfc", nfc);
> > >> + if (err)
> > >> + return err;
> > >> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> > >> + if (IS_ERR(nfc->clk_sys)) {
> > >> + dev_err(&pdev->dev, "sys clock not found.\n");
> > >> + return PTR_ERR(nfc->clk_sys);
> > >> + }
> > >> +
> > >> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> > >> + if (IS_ERR(nfc->clk_flash)) {
> > >> + dev_err(&pdev->dev, "flash clock not found.\n");
> > >> + return PTR_ERR(nfc->clk_flash);
> > >> + }
> > >> +
> > >> + err = clk_prepare_enable(nfc->clk_sys);
> > >> + if (err) {
> > >> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> > >> + return err;
> > >> + }
> > >> +
> > >> + err = clk_prepare_enable(nfc->clk_flash);
> > >> + if (err) {
> > >> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> > >> + goto clk_dis_sys;
> > >> + }
> > >> +
> > >> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
> > >> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> > >> + if (err) {
> > >> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> > >> + goto clk_dis_all;
> > >> + }
> > >> + if (nand_chip->onfi_version) {
> > >> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
> > >> + nfc->caddr_cycles =
> > >> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> > >> + } else {
> > >> + /*For non-ONFI devices, configuring the address cyles as 5 */
> > >> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
> > >> + }
> > >
> > > Hm, this should not be dependent on ONFI support. It all depends on the
> > > CHIP size, which you can deduce from mtd->writesize.
> > >
> >
> > Understood. but i have kept those values as fallback. In general this
> > controller talks
> > with onfi devices only.
>
> Hm, not sure this is a good argument. Your NAND controller should work
> with both ONFI and non-ONFI NANDs: you don't know which NAND will be
> available on future board designs...
>
> Anyway, the check you're looking for is in nand_base.c IIRC.
>
> Best Regards,
>
> Boris
>
>

Subject: RE: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller

Hi Boris,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, December 05, 2016 2:31 PM
> To: Punnaiah Choudary Kalluri <[email protected]>
> Cc: Florian Fainelli <[email protected]>; Kevin Hao
> <[email protected]>; [email protected]; [email protected]; Andy
> Shevchenko <[email protected]>; Punnaiah Choudary
> <[email protected]>; [email protected]; [email protected];
> Ezequiel Garcia <[email protected]>; linux-
> [email protected]; Punnaiah Choudary Kalluri <[email protected]>;
> Michal Simek <[email protected]>; Brian Norris
> <[email protected]>; David Woodhouse
> <[email protected]>; [email protected]; Marek Vasut <[email protected]>
> Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash
> Controller
>
> +Marek who reviewed v6
>
> Hi Punnaiah,
>
> I see you sent a v6, but you never answered the questions/remarks I had in
> this email.
>


My appolgies. Somehow I missed the below mail.

> Can you please try to answer them (I'd like to understand how the controller
> works)?
>

Sure.
https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf
NAND chapter starts from page 577

> Thanks,
>
> Boris
>
> On Wed, 9 Mar 2016 10:50:07 +0100
> Boris Brezillon <[email protected]> wrote:
>
> > Hi Punnaiah,
> >
> > On Wed, 9 Mar 2016 00:10:52 +0530
> > punnaiah choudary kalluri <[email protected]> wrote:
> >
> >
> > > >> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> > > >> + {512, 512, 1, 0, 0x3},
> > > >> + {512, 512, 4, 1, 0x7},
> > > >> + {512, 512, 8, 1, 0xD},
> > > >> + /* 2K byte page */
> > > >> + {2048, 512, 1, 0, 0xC},
> > > >> + {2048, 512, 4, 1, 0x1A},
> > > >> + {2048, 512, 8, 1, 0x34},
> > > >> + {2048, 512, 12, 1, 0x4E},
> > > >> + {2048, 1024, 24, 1, 0x54},
> > > >> + /* 4K byte page */
> > > >> + {4096, 512, 1, 0, 0x18},
> > > >> + {4096, 512, 4, 1, 0x34},
> > > >> + {4096, 512, 8, 1, 0x68},
> > > >> + {4096, 512, 12, 1, 0x9C},
> > > >> + {4096, 1024, 4, 1, 0xA8},
> > > >> + /* 8K byte page */
> > > >> + {8192, 512, 1, 0, 0x30},
> > > >> + {8192, 512, 4, 1, 0x68},
> > > >> + {8192, 512, 8, 1, 0xD0},
> > > >> + {8192, 512, 12, 1, 0x138},
> > > >> + {8192, 1024, 24, 1, 0x150},
> > > >> + /* 16K byte page */
> > > >> + {16384, 512, 1, 0, 0x60},
> > > >> + {16384, 512, 4, 1, 0xD0},
> > > >> + {16384, 512, 8, 1, 0x1A0},
> > > >> + {16384, 512, 12, 1, 0x270},
> > > >> + {16384, 1024, 24, 1, 0x2A0}
> > > >> +};
> > > >
> > > > Do you really need this association table. IMO, those are
> > > > information you could deduce from nand_chip info (ecc_strength,
> > > > ecc_step_size, ...). eccsize can be calculated this way:
> > > >
> > > > info->pagesize = mtd->writesize;
> > > > info->codeword_size = chip->ecc_step_ds;
> > > > info->eccbits = chip->ecc_strength_ds;
> > > >
> > > > if (info->eccbits > 1)
> > > > info->bch = true;
> > > >
> > > > steps = info->pagesize / info->codeword_size;
> > > >
> > > > if (!bch)
> > > > info->eccsize = 3 * steps;
> > > > else
> > > > info->eccsize =
> > > > DIV_ROUND_UP(fls(8 * info->codeword_size) *
> > > > ecc->strength * steps, 8);
> > > >
> > > > And, too bad we have to deal with another engine operating at the
> > > > bit level instead of aligning each ECC step to the next byte (GPMI
> > > > engine does that too, and it's a pain to deal with).
> > > >
> > >
> > > 1. There is no direct formula for calculating the ecc size. For bch
> > > ecc algorithms, the ecc size can vary based on the chosen
> > > polynomial.
> >
> > I checked for all the table entries, and this formula works (note that
> > it takes the codeword_size into account, which is probably what you
> > are talking about when mentioning the different polynomials).
> >

Agree. I have tried your formula and calculated the ecc size and they are
Matching to the table. Polynomial part is still not clear and I have requested
the IP vendor for more information on this.

> > >
> > > 2. This controller supports only the page sizes, number of ecc bits
> > > and code word size defined in the table. driver returns error if
> > > there is no match for the page size and codeword size.
> >
> > I agree for the pagesize, ECC step_size and ECC strength limitations,
> > but that's something you can check without having this association
> > table. And from my experience, keeping such tables to describe all the
> > possible associations is not a good idea :-/.
> >

Yes I removed it and updated the code in v6.

> > [...]
> >
> > > >> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> > > >> + struct nand_chip *chip, uint8_t *buf,
> > > >> + int oob_required, int page) {
> > > >> + u32 val;
> > > >> + struct anfc *nfc = to_anfc(mtd);
> > > >> +
> > > >> + anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT,
> > > >> + NAND_CMD_RNDOUTSTART);
> > > >> +
> > > >> + val = readl(nfc->base + CMD_OFST);
> > > >> + val = val | ECC_ENABLE;
> > > >> + writel(val, nfc->base + CMD_OFST);
> > > >> +
> > > >> + if (nfc->dma)
> > > >> + nfc->rdintrmask = XFER_COMPLETE;
> > > >> + else
> > > >> + nfc->rdintrmask = READ_READY;
> > > >> +
> > > >> + if (!nfc->bch)
> > > >> + nfc->rdintrmask = MBIT_ERROR;
> > > >> +
> > > >> + chip->read_buf(mtd, buf, mtd->writesize);
> > > >> +
> > > >> + val = readl(nfc->base + ECC_ERR_CNT_OFST);
> > > >> + if (nfc->bch) {
> > > >> + mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> > > >> + } else {
> > > >> + val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> > > >> + mtd->ecc_stats.corrected += val;
> > > >> + val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> > > >> + mtd->ecc_stats.failed += val;
> > > >> + /* Clear ecc error count register 1Bit, 2Bit */
> > > >> + writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> > > >> + writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> > > >> + }
> > > >
> > > > Not sure if your controller already handles the 'bitflips in
> > > > erased pages' case, but you might want to have a look at the
> > > > nand_check_erased_ecc_chunk() [4] function if that's not the case.
> > > >
> > >
> > > it will not handle the bitflips in erased pages. I will check this one.
> > >
> > > >> + nfc->err = false;
> > > >> +
> > > >> + if (oob_required)
> > > >> + chip->ecc.read_oob(mtd, chip, page);
> > > >
> > > > You should disable the ECC engine before leaving the function.
> > > >
> > >
> > > Not needed. Because ecc state need to be configured for every nand
> command.
> > > so, no need to disable explicitly.
> >
> > Except, you don't know what the next NAND command will be, and if it's
> > a raw access ECC_ENABLE bit has to be cleared.
> > Also, you don't know when the NAND command is sent, which type of read
> > will take place, so putting that in ->cmdfunc() is not a solution.
> >

ECC will be enabled only for page read/write operations defined for hwecc.
In all other cases it will be diabled.
As per the controller spec, the ecc should be enabled while framing the
Command register itself and before issuing the command in program register.
So, every time, a new command issued this bit will be overwritten.

> > >
> > > >> +
> > > >> + return 0;
> > > >> +}
> > > >> +
> >
> > [...]
> >
> > > >> +
> > > >> +static u8 anfc_read_byte(struct mtd_info *mtd) {
> > > >> + struct anfc *nfc = to_anfc(mtd);
> > > >> +
> > > >> + return nfc->buf[nfc->bufshift++];
> > > >
> > > > ->read_byte() should normally be a wrapper around ->read_buf(),
> > > > ->because
> > > > you don't know ahead of time, how many time ->read_byte() will be
> > > > called after the CMD/ADDR cycles, and thus cannot deduce how much
> > > > should be put in the temporary buffer.
> > > > So I'd suggest you put the logic to fill nfc->buf[] directly in
> > > > there instead of putting it in ->cmdfunc().
> > > >
> > >
> > > Controller doesn't support dma for readid, parameter page commands
> > > and the response for these commands shall be read from the fifo
> > > which is of 32 bit width.
> >
> > Okay, but I was not referring to DMA here. If ->read_buf() always
> > implies using DMA, then maybe you should rework it to make DMA
> > operations optional, and only activate them when ->read_page() is used.
> >

Ok.

> > >
> > > Also for status command, the response shall be read from the
> > > controller register.
> > >
> > > Arasan controller will not allow byte reads. So, i have opted this
> > > implemetation.
> > >
> > > In either case, number of bytes to be read will not ne known. For
> > > now, it estimates the number of bytes to be read based on the
> > > command that is issued and resets the buffer shift counter if it see
> > > a new command request.
> >
> > Which is not a good idea. As I said, you can't guess how many bytes
> > the framework will read after a specific command (CCed Ezequiel, who,
> > IIRC, had this kind of problems with the pxa3xx driver).
> >

Agree. But I don't have a choice here. I have just verified the pxa3xx
Implementation for read_byte and it also using the similar approach.
The framework should definitely read the finite number of bytes and it
Should be as per the onfi spec. Could you point me the case/command
That may read variable number of bytes ?



> > >
> > > Also its not a generic controller, supports only the commands
> > > defined in the program register.
> > >
> >
> > Hm, are you sure there's no way to send raw commands, or CMD and ADDR
> > cycles independently?
> > AFAICS, you're still configuring the ADDR and CMD cycles manually (in
> > anfc_prepare_cmd()), which seems pretty generic to me...
> >
Yes. But yet the outset we need to write the specific command bit in
program register though we program the addr and command registers.
you could see the macros start with PROG_XXX for programing the specific
command in program register. After writing this bit, then only controller starts
the communication with the flash device.

> > >
> > >
> > > >> +}
> >
> > [...]
> >
> > > >> +static void anfc_cmd_function(struct mtd_info *mtd,
> > > >> + unsigned int cmd, int column, int
> > > >> +page_addr) {
> > > >> + struct anfc *nfc = to_anfc(mtd);
> > > >> + bool wait = false, read = false;
> > > >> + u32 addrcycles, prog;
> > > >> + u32 *bufptr = (u32 *)nfc->buf;
> > > >> +
> > > >> + nfc->bufshift = 0;
> > > >> + nfc->curr_cmd = cmd;
> > > >> +
> > > >> + if (page_addr == -1)
> > > >> + page_addr = 0;
> > > >> + if (column == -1)
> > > >> + column = 0;
> > > >> +
> > > >> + switch (cmd) {
> > > >> + case NAND_CMD_RESET:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> > > >> + prog = PROG_RST;
> > > >> + wait = true;
> > > >> + break;
> > > >> + case NAND_CMD_SEQIN:
> > > >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> > > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> > > >> + mtd->writesize, addrcycles);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + break;
> > > >> + case NAND_CMD_READOOB:
> > > >> + column += mtd->writesize;
> > > >> + case NAND_CMD_READ0:
> > > >> + case NAND_CMD_READ1:
> > > >> + addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> > > >> + anfc_prepare_cmd(nfc, NAND_CMD_READ0,
> NAND_CMD_READSTART, 1,
> > > >> + mtd->writesize, addrcycles);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + break;
> > > >> + case NAND_CMD_RNDOUT:
> > > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> > > >> + mtd->writesize, 2);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + if (nfc->dma)
> > > >> + nfc->rdintrmask = XFER_COMPLETE;
> > > >> + else
> > > >> + nfc->rdintrmask = READ_READY;
> > > >> + break;
> > > >> + case NAND_CMD_PARAM:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> > > >> + anfc_readfifo(nfc, PROG_RDPARAM,
> > > >> + sizeof(struct nand_onfi_params));
> > > >> + break;
> > > >> + case NAND_CMD_READID:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> > > >> + anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> > > >> + break;
> > > >> + case NAND_CMD_ERASE1:
> > > >> + addrcycles = nfc->raddr_cycles;
> > > >> + prog = PROG_ERASE;
> > > >> + anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0,
> addrcycles);
> > > >> + column = page_addr & 0xffff;
> > > >> + page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + wait = true;
> > > >> + break;
> > > >> + case NAND_CMD_STATUS:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> > > >> + anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + prog = PROG_STATUS;
> > > >> + wait = read = true;
> > > >> + break;
> > > >> + case NAND_CMD_GET_FEATURES:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> > > >> + anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> > > >> + break;
> > > >> + case NAND_CMD_SET_FEATURES:
> > > >> + anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> > > >> + anfc_setpagecoladdr(nfc, page_addr, column);
> > > >> + anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> > > >> + break;
> > > >> + default:
> > > >> + return;
> > > >> + }
> > > >> +
> > > >> + if (wait) {
> > > >> + anfc_enable_intrs(nfc, XFER_COMPLETE);
> > > >> + writel(prog, nfc->base + PROG_OFST);
> > > >> + anfc_wait_for_event(nfc, XFER_COMPLETE);
> > > >> + }
> > > >> +
> > > >> + if (read)
> > > >> + bufptr[0] = readl(nfc->base + FLASH_STS_OFST); }
> > > >
> > > > Can you try to implement ->cmd_ctrl() instead of ->cmdfunc(). This
> > > > way you'll benefit from all the improvements we'll to the default
> > > > nand_command_lp() and nand_command() implementation, and this
> also
> > > > ease maintenance on our side.
> > > > According to what I've seen so far this should be doable.
> > > >
> > >
> > > I see your point but since this controller is providing the glue
> > > logic for issuing the nand commands, i doubt adopting cmd_ctrl would
> > > be not stright forward. IMHO, cmd_ctrl shall be used for controllers
> > > that provide low level access and allow more confgurable options.
> >
> > And as pointed above, your controller seems to be able to do that, but
> > maybe I'm missing something.
> >
> > I know it implies reworking your driver, but as I said, if we keep
> > adding new drivers which are not able to send generic CMDs, then we'll
> > be screwed when we'll want to add support for newer NANDs (MLC
> NANDs),
> > which are usually providing private/vendor-specific commands for
> > common functions (like read-retry).
> >
As I mentioned, the controller can issue up to 26 commands defined
In onfi 3.1 spec. No support for any other private/vendor specific commands.
The program register has 26 bits to trigger the any of one of these 26 commands
In a given time.

> > Patching all NAND controllers to support those new NANDs is not a
> > viable option, this is why I'd like to avoid those custom ->cmdfunc()
> > implementations in new NAND drivers, unless I'm proven this is really
> > impossible to do.
> >
> > >
> > >
> >
> > [...]
> >
> > > >> +
> > > >> +static int anfc_init_timing_mode(struct anfc *nfc) {
> > > >> + int mode, err;
> > > >> + unsigned int feature[2], regval, i;
> > > >> + struct nand_chip *chip = &nfc->chip;
> > > >> + struct mtd_info *mtd = &nfc->mtd;
> > > >> +
> > > >> + memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> > > >> + /* Get nvddr timing modes */
> > > >> + mode = onfi_get_sync_timing_mode(chip) & 0xff;
> > > >> + if (!mode) {
> > > >> + mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> > > >> + regval = mode;
> > > >> + } else {
> > > >> + mode = fls(mode) - 1;
> > > >> + regval = NVDDR_MODE | mode <<
> NVDDR_TIMING_MODE_SHIFT;
> > > >> + mode |= ONFI_DATA_INTERFACE_NVDDR;
> > > >> + }
> > > >> +
> > > >> + feature[0] = mode;
> > > >> + for (i = 0; i < nfc->num_cs; i++) {
> > > >> + chip->select_chip(mtd, i);
> >
> > You select the chip here, but it's never de-selected, which means the
> > last chip in the array stay selected until someone send a new command.
> >

Yes. Corrected it. In general it has two values 0 or 1
0 for chip select 1 and 1 for chip select 2. So, ideally it will not affect the CS line
Though you have not opted for de-select. So, I didn't caught this issue. But
As per the sequence It need to deselected.

> > > >> + err = chip->onfi_set_features(mtd, chip,
> > > >> + ONFI_FEATURE_ADDR_TIMING_MODE,
> > > >> + (uint8_t *)feature);
> > > >> + if (err)
> > > >> + return err;
> > > >> + }
> > > >> + writel(regval, nfc->base + DATA_INTERFACE_REG);
> > > >> +
> > > >> + if (mode & ONFI_DATA_INTERFACE_NVDDR)
> > > >> + nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
> > > >
> > > > You seem to switch from SDR to DDR mode, but I don't see any
> > > > timing configuration. How is your controller able to adapt to
> > > > different NAND timings?
> > > >
> > >
> > > it is doing the timing mode configuration. it will adapt to the
> > > timing parameters defined in the ONFI 3.1 spec for each of tje
> > > timing mode i.e 0-5.
> >
> > I know what ONFI timings mode are, but usually when you change the
> > mode on the NAND side, you have to adapt your timings on the
> > controller side, and I don't see anything related to timings config on
> > the controller side here, hence my question.

Ok. Yes, you are correct. Controller is proving the option to configure the timing
Modes. But not the way other controllers are doing like tRR, tWR..
It is providing the register called Data interface register which allow use configuring the
Interface type i.e SDR/DDR and timing modes i.e. 0 to 5. So, controller will generate the
Required timings as per the ONFI spec and based on the timing mode selected.

> >
> > >
> > > >> +
> > > >> + return 0;
> > > >> +}
> > > >> +
> > > >> +static int anfc_probe(struct platform_device *pdev) {
> > > >> + struct anfc *nfc;
> > > >> + struct mtd_info *mtd;
> > > >> + struct nand_chip *nand_chip;
> > > >> + struct resource *res;
> > > >> + struct mtd_part_parser_data ppdata;
> > > >> + int err;
> > > >> +
> > > >> + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> > > >> + if (!nfc)
> > > >> + return -ENOMEM;
> > > >> +
> > > >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > >> + nfc->base = devm_ioremap_resource(&pdev->dev, res);
> > > >> + if (IS_ERR(nfc->base))
> > > >> + return PTR_ERR(nfc->base);
> > > >> +
> > > >> + mtd = &nfc->mtd;
> > > >> + nand_chip = &nfc->chip;
> > > >> + nand_chip->priv = nfc;
> > > >> + mtd->priv = nand_chip;
> > > >> + mtd->name = DRIVER_NAME;
> > > >> + nfc->dev = &pdev->dev;
> > > >> + mtd->dev.parent = &pdev->dev;
> > > >> +
> > > >> + nand_chip->cmdfunc = anfc_cmd_function;
> > > >> + nand_chip->waitfunc = anfc_device_ready;
> > > >
> > > > You can leave nand_chip->waitfunc to NULL since your
> > > > implementation is doing exactly what the default implementation does.
> > > >
> > > >> + nand_chip->chip_delay = 30;
> > > >> + nand_chip->read_buf = anfc_read_buf;
> > > >> + nand_chip->write_buf = anfc_write_buf;
> > > >> + nand_chip->read_byte = anfc_read_byte;
> > > >> + nand_chip->options = NAND_BUSWIDTH_AUTO |
> NAND_NO_SUBPAGE_WRITE;
> > > >> + nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> > > >> + nand_chip->select_chip = anfc_select_chip;
> > > >> + nand_chip->onfi_set_features = anfc_onfi_set_features;
> > > >> + nfc->dma = of_property_read_bool(pdev->dev.of_node,
> > > >> + "arasan,has-mdma");
> > > >> + nfc->num_cs = 1;
> > > >> + of_property_read_u32(pdev->dev.of_node, "num-cs",
> > > >> + &nfc->num_cs);
> > > >
> > > > Already mentioned by Brian, but you should have a look at the
> > > > sunxi-nand binding to see how to represent the NAND controller and
> > > > its chips in the DT.
> > > >
> > >
> > > Ok. i will check the implementation.
> > >
> > > >> + platform_set_drvdata(pdev, nfc);
> > > >> + init_completion(&nfc->bufrdy);
> > > >> + init_completion(&nfc->xfercomp);
> > > >> + nfc->irq = platform_get_irq(pdev, 0);
> > > >> + if (nfc->irq < 0) {
> > > >> + dev_err(&pdev->dev, "platform_get_irq failed\n");
> > > >> + return -ENXIO;
> > > >> + }
> > > >> + err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> > > >> + 0, "arasannfc", nfc);
> > > >> + if (err)
> > > >> + return err;
> > > >> + nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> > > >> + if (IS_ERR(nfc->clk_sys)) {
> > > >> + dev_err(&pdev->dev, "sys clock not found.\n");
> > > >> + return PTR_ERR(nfc->clk_sys);
> > > >> + }
> > > >> +
> > > >> + nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> > > >> + if (IS_ERR(nfc->clk_flash)) {
> > > >> + dev_err(&pdev->dev, "flash clock not found.\n");
> > > >> + return PTR_ERR(nfc->clk_flash);
> > > >> + }
> > > >> +
> > > >> + err = clk_prepare_enable(nfc->clk_sys);
> > > >> + if (err) {
> > > >> + dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> > > >> + return err;
> > > >> + }
> > > >> +
> > > >> + err = clk_prepare_enable(nfc->clk_flash);
> > > >> + if (err) {
> > > >> + dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> > > >> + goto clk_dis_sys;
> > > >> + }
> > > >> +
> > > >> + nfc->spktsize = SDR_MODE_PACKET_SIZE;
> > > >> + err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> > > >> + if (err) {
> > > >> + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> > > >> + goto clk_dis_all;
> > > >> + }
> > > >> + if (nand_chip->onfi_version) {
> > > >> + nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles &
> 0xf;
> > > >> + nfc->caddr_cycles =
> > > >> + (nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> > > >> + } else {
> > > >> + /*For non-ONFI devices, configuring the address cyles as 5 */
> > > >> + nfc->raddr_cycles = nfc->caddr_cycles = 5;
> > > >> + }
> > > >
> > > > Hm, this should not be dependent on ONFI support. It all depends
> > > > on the CHIP size, which you can deduce from mtd->writesize.
> > > >
> > >
> > > Understood. but i have kept those values as fallback. In general
> > > this controller talks with onfi devices only.
> >
> > Hm, not sure this is a good argument. Your NAND controller should work
> > with both ONFI and non-ONFI NANDs: you don't know which NAND will be
> > available on future board designs...

Agree. But the controller itself claims it supports ONFI devices and the init sequence
recommends to check ONFI signature first. Also our chip will not boot if the device
Is not compliant to ONFI. So, it's a known limitation that the controller will not work
for non-ONFI devices.

Regards,
Punnaiah
> >
> > Anyway, the check you're looking for is in nand_base.c IIRC.
> >
> > Best Regards,
> >
> > Boris
> >
> >