From: Johannes Zink <[email protected]>

The FCB on the i.MX7 is written in BCH62 mode and with randomizer
enabled. This needs special FCB read/write functions. Add them to the
driver.

Signed-off-by: Johannes Zink <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
 drivers/mtd/nand/nand_mxs.c    | 209 ++++++++++++++++++++++++++++++++-
 include/linux/mtd/nand_mxs.h   |   4 +
 include/soc/imx/imx-nand-bcb.h |  11 ++
 3 files changed, 222 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 1d0a38dc9e..5faa17a4bd 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -108,6 +108,10 @@
 #define        GPMI_ECCCTRL_ECC_CMD_OFFSET                     13
 #define        GPMI_ECCCTRL_ECC_CMD_DECODE                     (0x0 << 13)
 #define        GPMI_ECCCTRL_ECC_CMD_ENCODE                     (0x1 << 13)
+#define GPMI_ECCCTRL_RANDOMIZER_ENABLE                  (1 << 11)
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE0                   0
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE1                   (1 << 9)
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE2                   (2 << 9)
 #define        GPMI_ECCCTRL_ENABLE_ECC                         (1 << 12)
 #define        GPMI_ECCCTRL_BUFFER_MASK_MASK                   0x1ff
 #define        GPMI_ECCCTRL_BUFFER_MASK_OFFSET                 0
@@ -138,6 +142,9 @@
 #define        BCH_FLASHLAYOUT0_ECC0_MASK                      (0xf << 12)
 #define        BCH_FLASHLAYOUT0_ECC0_OFFSET                    12
 #define        IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET               11
+#define        BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET              0
+#define        BCH_FLASHLAYOUT0_GF13_0_GF14_1_MASK             BIT(10)
+#define        BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET           10
 
 #define BCH_FLASH0LAYOUT1                      0x00000090
 #define        BCH_FLASHLAYOUT1_PAGE_SIZE_MASK                 (0xffff << 16)
@@ -145,6 +152,9 @@
 #define        BCH_FLASHLAYOUT1_ECCN_MASK                      (0xf << 12)
 #define        BCH_FLASHLAYOUT1_ECCN_OFFSET                    12
 #define        IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET               11
+#define        BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK             BIT(10)
+#define        BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET           10
+#define        BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET              0
 
 #define        MXS_NAND_DMA_DESCRIPTOR_COUNT           4
 
@@ -752,7 +762,8 @@ static void mxs_nand_config_bch(struct nand_chip *chip, int 
readlen)
        writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
 }
 
-static int mxs_nand_do_bch_read(struct nand_chip *chip, int channel, int 
readtotal)
+static int mxs_nand_do_bch_read(struct nand_chip *chip, int channel, int 
readtotal,
+                               bool randomizer, int page)
 {
        struct mxs_nand_info *nand_info = chip->priv;
        struct mxs_dma_desc *d;
@@ -798,6 +809,12 @@ static int mxs_nand_do_bch_read(struct nand_chip *chip, 
int channel, int readtot
        d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
        d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
 
+       if (randomizer) {
+               d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+                                      GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+               d->cmd.pio_words[3] |= (page % 256) << 16;
+       }
+
        mxs_dma_desc_append(channel, d);
 
        /* Compile the DMA descriptor - disable the BCH block. */
@@ -874,7 +891,7 @@ static int __mxs_nand_ecc_read_page(struct nand_chip *chip,
 
        mxs_nand_config_bch(chip, readtotal);
 
-       mxs_nand_do_bch_read(chip, channel, readtotal);
+       mxs_nand_do_bch_read(chip, channel, readtotal, false, page);
 
        /* Read DMA completed, now do the mark swapping. */
        mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
@@ -1247,6 +1264,194 @@ static int mxs_nand_block_markbad(struct nand_chip 
*chip , loff_t ofs)
        return 0;
 }
 
+#define BCH62_WRITESIZE                1024
+#define BCH62_OOBSIZE          838
+#define BCH62_PAGESIZE         (BCH62_WRITESIZE + BCH62_OOBSIZE)
+
+static void mxs_nand_mode_fcb_62bit(struct mxs_nand_info *nand_info)
+{
+       void __iomem *bch_regs;
+       u32 fl0, fl1;
+
+       bch_regs = nand_info->bch_base;
+
+       /* 8 ecc_chunks */
+       fl0 = 7 << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+       /* 32 bytes for metadata */
+       fl0 |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
+       /* using ECC62 level to be performed */
+       fl0 |= 0x1F << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET;
+       /* 0x20 * 4 bytes of the data0 block */
+       fl0 |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
+       fl0 |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
+       writel(fl0, bch_regs + BCH_FLASH0LAYOUT0);
+
+       /* 1024 for data + 838 for OOB */
+       fl1 = BCH62_PAGESIZE << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
+       /* using ECC62 level to be performed */
+       fl1 |= 0x1f << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET;
+       /* 0x20 * 4 bytes of the data0 block */
+       fl1 |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
+       fl1 |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
+       writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
+}
+
+int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size)
+{
+       struct nand_chip *chip;
+       struct mxs_nand_info *nand_info;
+       struct mtd_info *mtd = mxs_nand_mtd;
+       int ret;
+       int page;
+       int flips = 0;
+       uint8_t *status;
+       int i;
+
+       if (!mtd)
+               return -ENODEV;
+
+       chip = mtd_to_nand(mtd);
+       nand_info = chip->priv;
+
+       nand_select_target(chip, 0);
+
+       page = block * (mtd->erasesize / mtd->writesize);
+
+       mxs_nand_mode_fcb_62bit(nand_info);
+
+       nand_read_page_op(chip, page, 0, NULL, 0);
+
+       ret = mxs_nand_do_bch_read(chip, 0, BCH62_PAGESIZE, true, page);
+       if (ret)
+               goto out;
+
+       /* Read DMA completed, now do the mark swapping. */
+       mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
+
+       /* Loop over status bytes, accumulating ECC status. */
+       status = nand_info->oob_buf + 32;
+
+       for (i = 0; i < 8; i++) {
+               switch (status[i]) {
+               case 0x0:
+                       break;
+               case 0xff:
+                       /*
+                        * A status of 0xff means the chunk is erased, but due 
to
+                        * the randomizer we see this as random data. Explicitly
+                        * memset it.
+                        */
+                       memset(nand_info->data_buf + 0x80 * i, 0xff, 0x80);
+                       break;
+               case 0xfe:
+                       ret = -EBADMSG;
+                       goto out;
+               default:
+                       flips += status[0];
+                       break;
+               }
+       }
+
+       memcpy(buf, nand_info->data_buf, size);
+
+out:
+       mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
+       nand_deselect_target(chip);
+
+       return ret;
+}
+
+int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
+{
+       struct nand_chip *chip;
+       struct mtd_info *mtd = mxs_nand_mtd;
+       struct mxs_nand_info *nand_info;
+       struct mxs_dma_desc *d;
+       uint32_t channel;
+       int ret = 0;
+       int page;
+
+       if (!mtd)
+               return -ENODEV;
+
+       if (size > BCH62_WRITESIZE)
+               return -EINVAL;
+
+       chip = mtd_to_nand(mtd);
+       nand_info = chip->priv;
+       channel = nand_info->dma_channel_base;
+
+       mxs_nand_mode_fcb_62bit(nand_info);
+
+       nand_select_target(chip, 0);
+
+       page = block * (mtd->erasesize / mtd->writesize);
+
+       nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+       memset(nand_info->data_buf, 0x0, BCH62_WRITESIZE);
+       memcpy(nand_info->data_buf, buf, size);
+
+       /* Handle block mark swapping. */
+       mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
+
+       /* Compile the DMA descriptor - write data. */
+       d = mxs_nand_get_dma_desc(nand_info);
+       d->cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+                     MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
+                     (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+       d->cmd.address = 0;
+
+       d->cmd.pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE |
+                             GPMI_CTRL0_WORD_LENGTH |
+                             GPMI_CTRL0_ADDRESS_NAND_DATA;
+       d->cmd.pio_words[1] = 0;
+       d->cmd.pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC |
+                             GPMI_ECCCTRL_ECC_CMD_ENCODE |
+                             GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
+       d->cmd.pio_words[3] = BCH62_PAGESIZE;
+       d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
+       d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+
+       d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+                              GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+       /*
+        * Write NAND page number needed to be randomized
+        * to GPMI_ECCCOUNT register.
+        *
+        * The value is between 0-255. For additional details
+        * check 9.6.6.4 of i.MX7D Applications Processor reference
+        */
+       d->cmd.pio_words[3] |= (page % 256) << 16;
+
+       mxs_dma_desc_append(channel, d);
+
+       /* Execute the DMA chain. */
+       ret = mxs_dma_go(channel);
+       if (ret) {
+               dev_err(nand_info->dev, "MXS NAND: DMA write error: %d\n", ret);
+               goto out;
+       }
+
+       ret = mxs_nand_wait_for_bch_complete(nand_info);
+       if (ret) {
+               dev_err(nand_info->dev, "MXS NAND: BCH write timeout\n");
+               goto out;
+       }
+
+out:
+       mxs_nand_return_dma_descs(nand_info);
+
+       if (!ret)
+               ret = nand_prog_page_end_op(chip);
+
+       mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
+       nand_deselect_target(chip);
+
+       return ret;
+}
+
 /*
  * Nominally, the purpose of this function is to look for or create the bad
  * block table. In fact, since the we call this function at the very end of
diff --git a/include/linux/mtd/nand_mxs.h b/include/linux/mtd/nand_mxs.h
index 7eda0b8e63..4c1e90d6f0 100644
--- a/include/linux/mtd/nand_mxs.h
+++ b/include/linux/mtd/nand_mxs.h
@@ -28,5 +28,9 @@
  */
 
 int mxs_nand_get_geo(int *ecc_strength, int *bb_mark_bit_offset);
+int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size);
+int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size);
+
+struct mtd_info;
 
 #endif /* __NAND_MXS_H */
diff --git a/include/soc/imx/imx-nand-bcb.h b/include/soc/imx/imx-nand-bcb.h
index 6c42d80428..c5481e602e 100644
--- a/include/soc/imx/imx-nand-bcb.h
+++ b/include/soc/imx/imx-nand-bcb.h
@@ -77,6 +77,17 @@ struct fcb_block {
 
        uint32_t DISBBM;        /* the flag to enable (1)/disable(0) bi swap */
        uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of 
main area in spare area */
+
+       /* iMX7 only */
+       uint32_t onfi_sync_enable; /* enable Onfi nand sync support */
+       uint32_t onfi_sync_speed;  /* Speed for Onfi nand sync mode */
+       uint32_t onfi_sync_nand_data; /* parameters for Onfi nand sync mode 
timing */
+       uint32_t reserved[6];
+       uint32_t disbbm_search; /* disable bad block search function when 
reading the firmware, only using DBBT */
+       uint32_t disbbm_search_limit; /* ???randomizer type 2 enable ???*/
+       uint32_t reserved1[15]; /* reserved for future use */
+       uint32_t read_retry_enable; /* enable read retry for DBBT and firmware 
*/
+       uint32_t reserved2[1]; /*reserved, keep at 0 */
 };
 
 #endif /* __MACH_IMX_NAND_BCB_H */
-- 
2.30.2


Reply via email to