diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 79c1edd..670720a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,7 +1185,7 @@ */ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, - uint8_t *bufpoi) + uint8_t *bufpoi, int page) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -1540,7 +1540,7 @@ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi); + col, bytes, bufpoi, page); else ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c index 20bda14..a9c6e96 100644 --- a/drivers/mtd/nand/nand_mxs.c +++ b/drivers/mtd/nand/nand_mxs.c @@ -635,11 +635,39 @@ return buf; } +static void mxs_nand_config_bch(struct mtd_info *mtd, int readlen) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + int chunk_size; + void __iomem *bch_regs = nand_info->bch_base; + + if (mxs_nand_is_imx6(nand_info)) + chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2; + else + chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + + writel((mxs_nand_ecc_chunk_cnt(readlen) - 1) + << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET | + MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET | + (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET | + chunk_size, + bch_regs + BCH_FLASH0LAYOUT0); + + writel(readlen << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET | + (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET | + chunk_size, + bch_regs + BCH_FLASH0LAYOUT1); +} + /* * Read a page from NAND. */ -static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int oob_required, int page) +static int __mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page, + int readlen) { struct mxs_nand_info *nand_info = nand->priv; struct mxs_dma_desc *d; @@ -647,7 +675,17 @@ uint32_t corrected = 0, failed = 0; uint8_t *status; unsigned int max_bitflips = 0; - int i, ret; + int i, ret, readtotal, nchunks, eccstrength, ecc_parity_size; + + eccstrength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); + + readlen = roundup(readlen, MXS_NAND_CHUNK_DATA_CHUNK_SIZE); + nchunks = mxs_nand_ecc_chunk_cnt(readlen); + ecc_parity_size = 13 * eccstrength / 8; + readtotal = MXS_NAND_METADATA_SIZE + + (MXS_NAND_CHUNK_DATA_CHUNK_SIZE + ecc_parity_size) * nchunks; + + mxs_nand_config_bch(mtd, readtotal); /* Compile the DMA descriptor - wait for ready. */ d = mxs_nand_get_dma_desc(nand_info); @@ -679,13 +717,13 @@ GPMI_CTRL0_WORD_LENGTH | (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | GPMI_CTRL0_ADDRESS_NAND_DATA | - (mtd->writesize + mtd->oobsize); + readtotal; d->cmd.pio_words[1] = 0; d->cmd.pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC | GPMI_ECCCTRL_ECC_CMD_DECODE | GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; + d->cmd.pio_words[3] = readtotal; d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; @@ -705,7 +743,7 @@ GPMI_CTRL0_WORD_LENGTH | (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | GPMI_CTRL0_ADDRESS_NAND_DATA | - (mtd->writesize + mtd->oobsize); + readtotal; d->cmd.pio_words[1] = 0; d->cmd.pio_words[2] = 0; @@ -737,11 +775,11 @@ /* Read DMA completed, now do the mark swapping. */ mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); - memcpy(buf, nand_info->data_buf, mtd->writesize); + memcpy(buf, nand_info->data_buf, readlen); /* Loop over status bytes, accumulating ECC status. */ status = nand_info->oob_buf + mxs_nand_aux_status_offset(); - for (i = 0; i < mxs_nand_ecc_chunk_cnt(mtd->writesize); i++) { + for (i = 0; i < nchunks; i++) { if (status[i] == 0x00) continue; @@ -816,9 +854,34 @@ rtn: mxs_nand_return_dma_descs(nand_info); + mxs_nand_config_bch(mtd, mtd->writesize + mtd->oobsize); + return ret ? ret : max_bitflips; } +static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return __mxs_nand_ecc_read_page(mtd, nand, buf, oob_required, page, + mtd->writesize); +} + +static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offs, uint32_t len, uint8_t *buf, int page) +{ + /* + * For now always read from the beginning of a page. Allowing + * offsets here makes __mxs_nand_ecc_read_page() more + * complicated. + */ + if (offs) { + len += offs; + offs = 0; + } + + return __mxs_nand_ecc_read_page(mtd, chip, buf, 0, page, len); +} + /* * Write a page to NAND. */ @@ -1104,7 +1167,6 @@ struct nand_chip *nand = mtd->priv; struct mxs_nand_info *nand_info = nand->priv; void __iomem *bch_regs = nand_info->bch_base; - uint32_t layout0, layout1; int ret; /* Reset BCH. Don't use SFTRST on MX23 due to Errata #2847 */ @@ -1113,36 +1175,7 @@ if (ret) return ret; - if (mxs_nand_is_imx6(nand_info)) { - layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) - << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET | - MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET | - (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET | - MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2; - - layout1 = (mtd->writesize + mtd->oobsize) - << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET | - (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET | - MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2; - } else { - layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) - << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET | - MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET | - (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << BCH_FLASHLAYOUT0_ECC0_OFFSET | - MXS_NAND_CHUNK_DATA_CHUNK_SIZE; - - layout1 = (mtd->writesize + mtd->oobsize) - << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET | - (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << BCH_FLASHLAYOUT1_ECCN_OFFSET | - MXS_NAND_CHUNK_DATA_CHUNK_SIZE; - } - - writel(layout0, bch_regs + BCH_FLASH0LAYOUT0); - writel(layout1, bch_regs + BCH_FLASH0LAYOUT1); + mxs_nand_config_bch(mtd, mtd->writesize + mtd->oobsize); /* Set *all* chip selects to use layout 0 */ writel(0, bch_regs + BCH_LAYOUTSELECT); @@ -1359,6 +1392,9 @@ nand->ecc.size = 512; nand->ecc.strength = 8; + nand->ecc.read_subpage = gpmi_ecc_read_subpage; + nand->options |= NAND_SUBPAGE_READ; + /* first scan to find the device and get the page size */ err = nand_scan_ident(mtd, 4, NULL); if (err) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 5beec39..76a6425 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -169,7 +169,7 @@ /* Device supports subpage reads */ /* Disabled in barebox for smaller binary sizes */ -#define NAND_SUBPAGE_READ (__BAREBOX__ ? 0x0 : 0x00001000) +#define NAND_SUBPAGE_READ (0x00001000) /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG @@ -352,7 +352,7 @@ int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offs, uint32_t len, uint8_t *buf); + uint32_t offs, uint32_t len, uint8_t *buf, int page); int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *data_buf, int oob_required);