summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/raw/cortina_nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/raw/cortina_nand.c')
-rw-r--r--drivers/mtd/nand/raw/cortina_nand.c1390
1 files changed, 1390 insertions, 0 deletions
diff --git a/drivers/mtd/nand/raw/cortina_nand.c b/drivers/mtd/nand/raw/cortina_nand.c
new file mode 100644
index 0000000000..12bd1ded83
--- /dev/null
+++ b/drivers/mtd/nand/raw/cortina_nand.c
@@ -0,0 +1,1390 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Cortina Access Inc..
+ */
+
+#include <common.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/sizes.h>
+#include <log.h>
+#include <asm/io.h>
+#include <memalign.h>
+#include <nand.h>
+#include <dm/device_compat.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/errno.h>
+#include <asm/gpio.h>
+#include <fdtdec.h>
+#include <bouncebuf.h>
+#include <dm.h>
+#include "cortina_nand.h"
+
+static unsigned int *pread, *pwrite;
+
+static const struct udevice_id cortina_nand_dt_ids[] = {
+ {
+ .compatible = "cortina,ca-nand",
+ },
+ { /* sentinel */ }
+};
+
+static struct nand_ecclayout eccoob;
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+ int enabled; /* 1 to enable, 0 to disable */
+ s32 width; /* bit width, must be 8 */
+ u32 nand_ecc_strength;
+};
+
+struct nand_drv {
+ u32 fifo_index;
+ struct nand_ctlr *reg;
+ struct dma_global *dma_glb;
+ struct dma_ssp *dma_nand;
+ struct tx_descriptor_t *tx_desc;
+ struct rx_descriptor_t *rx_desc;
+ struct fdt_nand config;
+ unsigned int flash_base;
+};
+
+struct ca_nand_info {
+ struct udevice *dev;
+ struct nand_drv nand_ctrl;
+ struct nand_chip nand_chip;
+};
+
+/**
+ * Wait for command completion
+ *
+ * @param reg nand_ctlr structure
+ * @return
+ * 1 - Command completed
+ * 0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg, unsigned int mask)
+{
+ unsigned int reg_v = 0;
+
+ if (readl_poll_timeout(&reg->flash_flash_access_start, reg_v,
+ !(reg_v & mask), (FLASH_LONG_DELAY << 2))) {
+ pr_err("Nand CMD timeout!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Read one byte from the chip
+ *
+ * @param mtd MTD device structure
+ * @return data byte
+ *
+ * Read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+ u8 ret_v;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(31, 0),
+ NFLASH_GO | NFLASH_RD);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ ret_v = readl(&info->reg->flash_nf_data) >> (8 * info->fifo_index++);
+ info->fifo_index %= 4;
+
+ return (uint8_t)ret_v;
+}
+
+/**
+ * Read len bytes from the chip into a buffer
+ *
+ * @param mtd MTD device structure
+ * @param buf buffer to store data to
+ * @param len number of bytes to read
+ *
+ * Read function for 8bit bus-width
+ */
+static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ unsigned int reg;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ for (i = 0; i < len; i++) {
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ reg = readl(&info->reg->flash_nf_data) >>
+ (8 * info->fifo_index++);
+ memcpy(buf + i, &reg, 1);
+ info->fifo_index %= 4;
+ }
+}
+
+/**
+ * Check READY pin status to see if it is ready or not
+ *
+ * @param mtd MTD device structure
+ * @return
+ * 1 - ready
+ * 0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int reg_val;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ reg_val = readl(&info->reg->flash_status);
+ if (reg_val & NFLASH_READY)
+ return 1;
+ else
+ return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ case 0:
+ break;
+
+ default:
+ WARN_ON(chipnr);
+ }
+}
+
+int init_nand_dma(struct nand_chip *nand)
+{
+ int i;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(nand);
+
+ setbits_le32(&info->dma_glb->dma_glb_dma_lso_ctrl, TX_DMA_ENABLE);
+ setbits_le32(&info->dma_glb->dma_glb_dma_ssp_rx_ctrl,
+ TX_DMA_ENABLE | DMA_CHECK_OWNER);
+ setbits_le32(&info->dma_glb->dma_glb_dma_ssp_tx_ctrl,
+ RX_DMA_ENABLE | DMA_CHECK_OWNER);
+
+ info->tx_desc = malloc_cache_aligned((sizeof(struct tx_descriptor_t) *
+ CA_DMA_DESC_NUM));
+ info->rx_desc = malloc_cache_aligned((sizeof(struct rx_descriptor_t) *
+ CA_DMA_DESC_NUM));
+
+ if (!info->rx_desc && info->tx_desc) {
+ printf("Fail to alloc DMA descript!\n");
+ kfree(info->tx_desc);
+ return -ENOMEM;
+ } else if (info->rx_desc && !info->tx_desc) {
+ printf("Fail to alloc DMA descript!\n");
+ kfree(info->tx_desc);
+ return -ENOMEM;
+ }
+
+ /* set RX DMA base address and depth */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+ GENMASK(31, 4), (uintptr_t)info->rx_desc);
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+ GENMASK(3, 0), CA_DMA_DEPTH);
+
+ /* set TX DMA base address and depth */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+ GENMASK(31, 4), (uintptr_t)info->tx_desc);
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+ GENMASK(3, 0), CA_DMA_DEPTH);
+
+ memset((unsigned char *)info->tx_desc, 0,
+ (sizeof(struct tx_descriptor_t) * CA_DMA_DESC_NUM));
+ memset((unsigned char *)info->rx_desc, 0,
+ (sizeof(struct rx_descriptor_t) * CA_DMA_DESC_NUM));
+
+ for (i = 0; i < CA_DMA_DESC_NUM; i++) {
+ /* set owner bit as SW */
+ info->tx_desc[i].own = 1;
+ /* enable Scatter-Gather memory copy */
+ info->tx_desc[i].sgm = 0x1;
+ }
+
+ return 0;
+}
+
+/**
+ * Send command to NAND device
+ *
+ * @param mtd MTD device structure
+ * @param command the command to be sent
+ * @param column the column address for this command, -1 if none
+ * @param page_addr the page address for this command, -1 if none
+ */
+static void ca_nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+ unsigned int reg_v = 0;
+ u32 cmd = 0, cnt = 0, addr1 = 0, addr2 = 0;
+ int ret;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+ /*
+ * Write out the command to the device.
+ *
+ * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+ * here before mtd->writesize is initialized.
+ */
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ assert(mtd->writesize != 0);
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Reset FIFO before issue new command */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+ ret =
+ readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+ !(reg_v & RESET_NFLASH_FIFO), FLASH_SHORT_DELAY);
+ if (ret) {
+ printf("FIFO reset timeout\n");
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+ udelay(10);
+ }
+
+ /* Reset FIFO index
+ * Next read start from flash_nf_data[0]
+ */
+ info->fifo_index = 0;
+
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(11, 10),
+ NFLASH_REG_WIDTH_8);
+
+ /*
+ * Program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+ case NAND_CMD_READID:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_READID);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_1TOGO);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_1);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_DATA_4);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* clear FLASH_NF_ACCESS */
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ DISABLE_AUTO_RESET);
+
+ break;
+ case NAND_CMD_PARAM:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_PARAM);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_1TOGO);
+ /* 1 byte ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_1);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (SZ_4K - 1) << 8);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ break;
+ case NAND_CMD_READ0:
+ if (chip->chipsize < SZ_32M) {
+ cmd = NAND_CMD_READ0;
+ cnt = REG_CMD_COUNT_1TOGO | REG_ADDR_COUNT_3;
+ addr1 = (((page_addr & ADDR1_MASK0) << 8));
+ addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+ } else if (chip->chipsize >= SZ_32M &&
+ (chip->chipsize <= SZ_128M)) {
+ cmd = NAND_CMD_READ0;
+ cnt = REG_ADDR_COUNT_4;
+ if (mtd->writesize > (REG_DATA_COUNT_512_DATA >> 8)) {
+ cmd |= (NAND_CMD_READSTART << 8);
+ cnt |= REG_CMD_COUNT_2TOGO;
+ } else {
+ cnt |= REG_CMD_COUNT_1TOGO;
+ }
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ } else {
+ cmd = NAND_CMD_READ0 | (NAND_CMD_READSTART << 8);
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ }
+
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ cmd);
+ /* CMD & ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (mtd->writesize - 1) << 8);
+ /* OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ (mtd->oobsize - 1) << 22);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ addr1);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ addr2);
+
+ return;
+ case NAND_CMD_SEQIN:
+ if (chip->chipsize < SZ_32M) {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_3;
+ addr1 = (((page_addr & ADDR1_MASK0) << 8));
+ addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+ } else if (chip->chipsize >= SZ_32M &&
+ (chip->chipsize <= SZ_128M)) {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_4;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ } else {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ }
+
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_SEQIN | (NAND_CMD_PAGEPROG << 8));
+ /* CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (mtd->writesize - 1) << 8);
+ /* OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ (mtd->oobsize - 1) << 22);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ addr1);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ addr2);
+
+ return;
+ case NAND_CMD_PAGEPROG:
+ return;
+ case NAND_CMD_ERASE1:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8));
+ /* 2 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_2TOGO);
+ /* 3 byte ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_3);
+ /* 0 Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_EMPTY);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ page_addr);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* Issue command */
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+ break;
+ case NAND_CMD_ERASE2:
+ return;
+ case NAND_CMD_STATUS:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_STATUS);
+ /* 1 byte CMD cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+ /* 0 byte Addr cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_EMPTY);
+ /* 1 Data cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8));
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ break;
+ case NAND_CMD_RESET:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_RESET);
+ /* 1 byte CMD cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+ /* 0 byte Addr cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_EMPTY);
+ /* 0 Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_EMPTY);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* Issue command */
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_WT);
+
+ break;
+ case NAND_CMD_RNDOUT:
+ default:
+ printf("%s: Unsupported command %d\n", __func__, command);
+ return;
+ }
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info nand_info structure
+ * @return 0 if ok, -1 on error
+ */
+static int set_bus_width_page_size(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ if (info->config.width == SZ_8) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_8);
+ } else if (info->config.width == SZ_16) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_16);
+ } else {
+ debug("%s: Unsupported bus width %d\n", __func__,
+ info->config.width);
+ return -1;
+ }
+
+ if (mtd->writesize == SZ_512) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_512);
+ } else if (mtd->writesize == SZ_2K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_2K);
+ } else if (mtd->writesize == SZ_4K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_4K);
+ } else if (mtd->writesize == SZ_8K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_8K);
+ } else {
+ debug("%s: Unsupported page size %d\n", __func__,
+ mtd->writesize);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ca_do_bch_correction(struct nand_chip *chip,
+ unsigned int err_num, u8 *buff_ptr, int i)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ unsigned int reg_v, err_loc0, err_loc1;
+ int k, max_bitflips;
+
+ for (k = 0; k < (err_num + 1) / 2; k++) {
+ reg_v = readl(&info->reg->flash_nf_bch_error_loc01 + k);
+ err_loc0 = reg_v & BCH_ERR_LOC_MASK;
+ err_loc1 = (reg_v >> 16) & BCH_ERR_LOC_MASK;
+
+ if (err_loc0 / 8 < BCH_DATA_UNIT) {
+ printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+ chip->ecc.size + ((reg_v & 0x1fff) >> 3)),
+ buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+ buff_ptr[err_loc0 / 8] ^=
+ (1 << (reg_v & BCH_CORRECT_LOC_MASK));
+
+ printf("%x\n", buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+ max_bitflips++;
+ }
+
+ if (((k + 1) * 2) <= err_num && ((err_loc1 / 8) <
+ BCH_DATA_UNIT)) {
+ printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+ chip->ecc.size + (((reg_v >> 16) & 0x1fff) >>
+ 3)), buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+ buff_ptr[err_loc1 / 8] ^= (1 << ((reg_v >> 16) &
+ BCH_CORRECT_LOC_MASK));
+
+ printf("%x\n", buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+ max_bitflips++;
+ }
+ }
+
+ return max_bitflips;
+}
+
+static int ca_do_bch_decode(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int page, unsigned int addr)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ unsigned int reg_v, err_num;
+ unsigned char *ecc_code = chip->buffers->ecccode;
+ unsigned char *ecc_end_pos;
+ int ret, i, j, k, n, step, eccsteps, max_bitflips = 0;
+ u8 *buff_ptr = (u8 *)buf;
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccoob.eccpos[i]];
+
+ for (i = 0, eccsteps = chip->ecc.steps; eccsteps;
+ i += chip->ecc.bytes, eccsteps--) {
+ ecc_end_pos = ecc_code + chip->ecc.bytes;
+
+ for (j = 0, k = 0; j < chip->ecc.bytes; j += 4, k++) {
+ reg_v = 0;
+ for (n = 0; n < 4 && ecc_code != ecc_end_pos;
+ ++n, ++ecc_code) {
+ reg_v |= *ecc_code << (8 * n);
+ }
+ clrsetbits_le32(&info->reg->flash_nf_bch_oob0 + k,
+ GENMASK(31, 0), reg_v);
+ }
+
+ /* Clear ECC buffer */
+ setbits_le32(&info->reg->flash_nf_ecc_reset, RESET_NFLASH_ECC);
+ ret = readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+ !(reg_v & RESET_NFLASH_ECC),
+ FLASH_SHORT_DELAY);
+ if (ret)
+ pr_err("Reset ECC buffer fail\n");
+
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+ BCH_DISABLE);
+
+ /* Start BCH */
+ step = i / chip->ecc.bytes;
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(6, 4), step << 4);
+ setbits_le32(&info->reg->flash_nf_bch_control, BCH_ENABLE);
+ udelay(10);
+ setbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+ ret = readl_poll_timeout(&info->reg->flash_nf_bch_status, reg_v,
+ (reg_v & BCH_DECO_DONE),
+ FLASH_SHORT_DELAY);
+ if (ret)
+ pr_err("ECC Decode timeout\n");
+
+ /* Stop compare */
+ clrbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+ reg_v = readl(&info->reg->flash_nf_bch_status);
+ err_num = (reg_v >> 8) & BCH_ERR_NUM_MASK;
+ reg_v &= BCH_ERR_MASK;
+
+ /* Uncorrectable */
+ if (reg_v == BCH_UNCORRECTABLE) {
+ max_bitflips =
+ nand_check_erased_ecc_chunk(buff_ptr,
+ chip->ecc.size,
+ &chip->buffers->ecccode[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+
+ if (max_bitflips) {
+ mtd->ecc_stats.failed++;
+ pr_err("Uncorrectable error\n");
+ pr_err(" Page:%x step:%d\n", page, step);
+
+ return -1;
+ }
+ } else if (reg_v == BCH_CORRECTABLE_ERR) {
+ printf("Correctable error(%x)!! addr:%lx\n",
+ err_num, (unsigned long)addr - mtd->writesize);
+ printf("Dst buf: %p [ColSel:%x ]\n",
+ buff_ptr + reg_v * BCH_DATA_UNIT, step);
+
+ max_bitflips =
+ ca_do_bch_correction(chip, err_num, buff_ptr, i);
+ }
+
+ buff_ptr += BCH_DATA_UNIT;
+ }
+
+ /* Disable BCH */
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(31, 0),
+ BCH_DISABLE);
+
+ return max_bitflips;
+}
+
+static int ca_do_bch_encode(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct nand_drv *info;
+ unsigned int reg_v;
+ int i, j, n, eccsteps, gen_index;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ for (i = 0, n = 0, eccsteps = chip->ecc.steps; eccsteps;
+ i += chip->ecc.bytes, eccsteps--, n++) {
+ gen_index = 0;
+ for (j = 0; j < chip->ecc.bytes; j += 4, gen_index++) {
+ reg_v =
+ readl(&info->reg->flash_nf_bch_gen0_0 + gen_index +
+ 18 * n);
+ chip->oob_poi[eccoob.eccpos[i + j]] = reg_v & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 1]] =
+ (reg_v >> 8) & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 2]] =
+ (reg_v >> 16) & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 3]] =
+ (reg_v >> 24) & OOB_MASK;
+ }
+ }
+
+ /* Disable BCH */
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+ BCH_DISABLE);
+
+ return 0;
+}
+
+/**
+ * Page read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @param page page number
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int page, int with_ecc, int is_writing)
+{
+ unsigned int reg_v, ext_addr, addr, dma_index;
+ struct tx_descriptor_t *tx_desc;
+ struct rx_descriptor_t *rx_desc;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ /* reset ecc control */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ RESET_NFLASH_ECC);
+
+ /* flash interrupt */
+ clrsetbits_le32(&info->reg->flash_flash_interrupt, GENMASK(0, 0),
+ REGIRQ_CLEAR);
+
+ /* reset ecc control */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ RESET_NFLASH_ECC);
+
+ /* Disable TXQ */
+ clrbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0));
+
+ /* Clear interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, GENMASK(0, 0));
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, GENMASK(0, 0));
+
+ if (with_ecc == 1) {
+ switch (info->config.nand_ecc_strength) {
+ case ECC_STRENGTH_8:
+ reg_v = BCH_ERR_CAP_8;
+ break;
+ case ECC_STRENGTH_16:
+ reg_v = BCH_ERR_CAP_16;
+ break;
+ case ECC_STRENGTH_24:
+ reg_v = BCH_ERR_CAP_24;
+ break;
+ case ECC_STRENGTH_40:
+ reg_v = BCH_ERR_CAP_40;
+ break;
+ default:
+ reg_v = BCH_ERR_CAP_16;
+ break;
+ }
+ reg_v |= BCH_ENABLE;
+
+ /* BCH decode for flash read */
+ if (is_writing == 0)
+ reg_v |= BCH_DECODE;
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(31, 0), reg_v);
+ } else {
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(31, 0), 0);
+ }
+
+ /* Fill Extend address */
+ ext_addr = ((page << chip->page_shift) / EXT_ADDR_MASK);
+
+ clrsetbits_le32(&info->reg->flash_nf_access,
+ GENMASK(7, 0), (uintptr_t)ext_addr);
+
+ addr = (uintptr_t)((page << chip->page_shift) % EXT_ADDR_MASK);
+ addr = (uintptr_t)(addr + info->flash_base);
+
+ dma_index = readl(&info->dma_nand->dma_q_txq_wptr) & CA_DMA_Q_PTR_MASK;
+
+ tx_desc = info->tx_desc;
+ rx_desc = info->rx_desc;
+
+ /* TX/RX descriptor for page data */
+ tx_desc[dma_index].own = OWN_DMA;
+ tx_desc[dma_index].buf_len = mtd->writesize;
+ rx_desc[dma_index].own = OWN_DMA;
+ rx_desc[dma_index].buf_len = mtd->writesize;
+ if (is_writing == 0) {
+ tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(buf);
+ } else {
+ tx_desc[dma_index].buf_adr = (uintptr_t)buf;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(addr);
+ }
+
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+
+ /* TX/RX descriptor for OOB area */
+ addr = (uintptr_t)(addr + mtd->writesize);
+ tx_desc[dma_index].own = OWN_DMA;
+ tx_desc[dma_index].buf_len = mtd->oobsize;
+ rx_desc[dma_index].own = OWN_DMA;
+ rx_desc[dma_index].buf_len = mtd->oobsize;
+ if (is_writing) {
+ tx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+ rx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ } else {
+ tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+ }
+
+ if (is_writing == 1) {
+ clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+ FIFO_WRITE);
+ } else {
+ clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+ FIFO_READ);
+ }
+
+ /* Start FIFO request */
+ clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(2, 2),
+ NFLASH_FIFO_REQ);
+
+ /* Update DMA write pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+ dma_index);
+
+ /* Start DMA */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0),
+ TX_DMA_ENABLE);
+
+ /* Wait TX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("TX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear tx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+ /* Wait RX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt, reg_v,
+ (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("RX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear rx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+ /* wait NAND CMD done */
+ if (is_writing == 0) {
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+ printf("%s: Command timeout\n", __func__);
+ }
+
+ /* Update DMA read pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+ dma_index);
+
+ /* ECC correction */
+ if (with_ecc == 1) {
+ ret =
+ readl_poll_timeout(&info->reg->flash_nf_bch_status,
+ reg_v, (reg_v & BCH_GEN_DONE),
+ FLASH_LONG_DELAY);
+
+ if (ret) {
+ pr_err("BCH_GEN timeout! flash_nf_bch_status=[0x%x]\n",
+ reg_v);
+ return -ETIMEDOUT;
+ }
+
+ if (is_writing == 0)
+ ca_do_bch_decode(mtd, chip, buf, page, addr);
+ else
+ ca_do_bch_encode(mtd, chip, page);
+ }
+
+ if (is_writing) {
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+
+ /* Update DMA R/W pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+ dma_index);
+
+ /* Wait TX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("TX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear tx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+ /* Wait RX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("RX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear rx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+ /* wait NAND CMD done */
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+ printf("%s: Command timeout\n", __func__);
+
+ /* Update DMA R/W pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+ dma_index);
+ }
+
+ return 0;
+}
+
+/**
+ * Hardware ecc based page read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Read raw page data without ecc
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 0, 1);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * OOB data read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int with_ecc, int is_writing)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ u32 reg_val;
+ int rw_index;
+
+ if (is_writing) {
+ reg_val = NFLASH_GO | NFLASH_WT;
+ pwrite = (unsigned int *)chip->oob_poi;
+ } else {
+ reg_val = NFLASH_GO | NFLASH_RD;
+ pread = (unsigned int *)chip->oob_poi;
+ }
+
+ for (rw_index = 0; rw_index < mtd->oobsize / 4; rw_index++) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_32);
+ if (is_writing)
+ clrsetbits_le32(&info->reg->flash_nf_data,
+ GENMASK(31, 0), pwrite[rw_index]);
+
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(11, 10), reg_val);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ if (!is_writing)
+ pread[rw_index] = readl(&info->reg->flash_nf_data);
+ }
+ return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8))
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(7, 0),
+ NAND_CMD_READOOB);
+ ret = nand_rw_oob(mtd, chip, page, 0, 0);
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+ GENMASK(31, 0), ECC_RESET_ALL);
+
+ return ret;
+}
+
+/**
+ * OOB data write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8)) {
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_READOOB | (NAND_CMD_SEQIN << 8) |
+ (NAND_CMD_PAGEPROG << 16));
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_3TOGO);
+ }
+ ret = nand_rw_oob(mtd, chip, page, 1, 1);
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+ GENMASK(31, 0), ECC_RESET_ALL);
+
+ return ret;
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param dev Driver model device
+ * @param config Device tree NAND configuration
+ */
+static int fdt_decode_nand(struct udevice *dev, struct nand_drv *info)
+{
+ int ecc_strength;
+
+ info->reg = (struct nand_ctlr *)dev_read_addr(dev);
+ info->dma_glb = (struct dma_global *)dev_read_addr_index(dev, 1);
+ info->dma_nand = (struct dma_ssp *)dev_read_addr_index(dev, 2);
+ info->config.enabled = dev_read_enabled(dev);
+ ecc_strength = dev_read_u32_default(dev, "nand-ecc-strength", 16);
+ info->flash_base =
+ dev_read_u32_default(dev, "nand_flash_base_addr", NAND_BASE_ADDR);
+
+ switch (ecc_strength) {
+ case ECC_STRENGTH_8:
+ info->config.nand_ecc_strength = ECC_STRENGTH_8;
+ break;
+ case ECC_STRENGTH_16:
+ info->config.nand_ecc_strength = ECC_STRENGTH_16;
+ break;
+ case ECC_STRENGTH_24:
+ info->config.nand_ecc_strength = ECC_STRENGTH_24;
+ break;
+ case ECC_STRENGTH_40:
+ info->config.nand_ecc_strength = ECC_STRENGTH_40;
+ break;
+ default:
+ info->config.nand_ecc_strength = ECC_STRENGTH_16;
+ }
+
+ return 0;
+}
+
+/**
+ * config flash type
+ *
+ * @param chip nand chip info structure
+ */
+static void nand_config_flash_type(struct nand_chip *nand)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(nand);
+ struct mtd_info *mtd = nand_to_mtd(nand);
+
+ switch (mtd->writesize) {
+ case WRITE_SIZE_512:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_512);
+ break;
+ case WRITE_SIZE_2048:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_2K);
+ break;
+ case WRITE_SIZE_4096:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_4K);
+ break;
+ case WRITE_SIZE_8192:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_8K);
+ break;
+ default:
+ pr_err("Unsupported page size(0x%x)!", nand->ecc.size);
+ }
+}
+
+/**
+ * config oob layout
+ *
+ * @param chip nand chip info structure
+ * @return 0 when successfully completed
+ * -EINVAL when ECC bytes exceed OOB size
+ */
+static int nand_config_oob_layout(struct nand_chip *nand)
+{
+ int i, ecc_start_offset;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+
+ /* Calculate byte count for ECC */
+ eccoob.eccbytes = mtd->writesize / nand->ecc.size * nand->ecc.bytes;
+
+ if (mtd->oobsize < eccoob.eccbytes) {
+ pr_err("Spare area(%d) too small for BCH%d\n", nand->ecc.bytes,
+ nand->ecc.strength / 8);
+ pr_err("page_sz: %d\n", nand->ecc.size);
+ pr_err("oob_sz: %d\n", nand->ecc.bytes);
+ return -EINVAL;
+ }
+
+ /* Update OOB layout */
+ ecc_start_offset = mtd->oobsize - eccoob.eccbytes;
+ memset(eccoob.eccpos, 0, sizeof(eccoob.eccpos));
+ for (i = 0; i < eccoob.eccbytes; ++i)
+ eccoob.eccpos[i] = i + ecc_start_offset;
+
+ /* Unused spare area
+ * OOB[0] is bad block marker.
+ * Extra two byte is reserved as
+ * erase marker just right before ECC code.
+ */
+ eccoob.oobavail = nand->ecc.bytes - eccoob.eccbytes - 2;
+ eccoob.oobfree[0].offset = 2;
+ eccoob.oobfree[0].length =
+ mtd->oobsize - eccoob.eccbytes - eccoob.oobfree[0].offset - 1;
+
+ return 0;
+}
+
+static int ca_nand_probe(struct udevice *dev)
+{
+ struct ca_nand_info *ca_nand = dev_get_priv(dev);
+ struct nand_chip *nand = &ca_nand->nand_chip;
+ struct nand_drv *info = &ca_nand->nand_ctrl;
+ struct fdt_nand *config = &info->config;
+ struct mtd_info *our_mtd;
+ int ret;
+
+ if (fdt_decode_nand(dev, info)) {
+ printf("Could not decode nand-flash in device tree\n");
+ return -1;
+ }
+ if (!config->enabled)
+ return -1;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &eccoob;
+
+ nand->cmdfunc = ca_nand_command;
+ nand->read_byte = read_byte;
+ nand->read_buf = read_buf;
+ nand->ecc.read_page = nand_read_page_hwecc;
+ nand->ecc.write_page = nand_write_page_hwecc;
+ nand->ecc.read_page_raw = nand_read_page_raw;
+ nand->ecc.write_page_raw = nand_write_page_raw;
+ nand->ecc.read_oob = nand_read_oob;
+ nand->ecc.write_oob = nand_write_oob;
+ nand->ecc.strength = config->nand_ecc_strength;
+ nand->select_chip = nand_select_chip;
+ nand->dev_ready = nand_dev_ready;
+ nand_set_controller_data(nand, &ca_nand->nand_ctrl);
+
+ /* Disable subpage writes as we do not provide ecc->hwctl */
+ nand->options |= NAND_NO_SUBPAGE_WRITE | NAND_SKIP_BBTSCAN;
+
+ /* Configure flash type as P-NAND */
+ clrsetbits_le32(&info->reg->flash_type, FLASH_PIN,
+ FLASH_TYPE_4K | FLASH_SIZE_436OOB);
+ config->width = FLASH_WIDTH;
+
+ our_mtd = nand_to_mtd(nand);
+ ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
+
+ nand->ecc.size = BCH_DATA_UNIT;
+ nand->ecc.bytes = BCH_GF_PARAM_M * (nand->ecc.strength / 8);
+
+ /* Reconfig flash type according to ONFI */
+ nand_config_flash_type(nand);
+
+ ret = set_bus_width_page_size(our_mtd);
+ if (ret)
+ return ret;
+
+ /* Set the bad block position */
+ nand->badblockpos =
+ our_mtd->writesize >
+ 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+ /* Arrange OOB layout */
+ ret = nand_config_oob_layout(nand);
+ if (ret)
+ return ret;
+
+ /* Init DMA descriptor ring */
+ ret = init_nand_dma(nand);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(our_mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(0, our_mtd);
+ if (ret) {
+ dev_err(dev, "Failed to register MTD: %d\n", ret);
+ return ret;
+ }
+
+ ret = set_bus_width_page_size(our_mtd);
+ if (ret)
+ return ret;
+
+ printf("P-NAND : %s\n", our_mtd->name);
+ printf("Chip Size: %lldMB\n", nand->chipsize / (1024 * 1024));
+ printf("Block Size: %dKB\n", our_mtd->erasesize / 1024);
+ printf("Page Size: %dB\n", our_mtd->writesize);
+ printf("OOB Size: %dB\n", our_mtd->oobsize);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(cortina_nand) = {
+ .name = "CA-PNAND",
+ .id = UCLASS_MTD,
+ .of_match = cortina_nand_dt_ids,
+ .probe = ca_nand_probe,
+ .priv_auto = sizeof(struct ca_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_DRIVER_GET(cortina_nand), &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize %s. (error %d)\n", dev->name, ret);
+}