Hi Yifeng, In some functions you deselect the chips. The MTD frame work has a functions for that and also keeps track of its select status, so I think that you shouldn't poke with that yourself and should therefore remove the deselections from your driver. /** * nand_deselect_target() - Deselect the currently selected target * @chip: NAND chip object * * Deselect the currently selected NAND target. The result of operations * executed on @chip after the target has been deselected is undefined. */ void nand_deselect_target(struct nand_chip *chip) { if (chip->legacy.select_chip) chip->legacy.select_chip(chip, -1); chip->cur_cs = -1; } EXPORT_SYMBOL_GPL(nand_deselect_target); On 10/31/20 12:58 PM, Johan Jonker wrote: [..] > On 10/28/20 10:53 AM, Yifeng Zhao wrote: [..] >> + >> +static int rk_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, >> + int oob_on, int page) >> +{ >> + struct mtd_info *mtd = nand_to_mtd(chip); >> + struct rk_nfc *nfc = nand_get_controller_data(chip); >> + struct nand_ecc_ctrl *ecc = &chip->ecc; >> + int ret = 0; >> + u32 i; >> + > > /* > * Normal timing and ECC layout size setup is already done in > * the rk_nfc_select_chip() function. > */ > > How about the ECC layout size setup for a boot block? > >> + if (!buf) >> + memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); >> +> + for (i = 0; i < ecc->steps; i++) { >> + /* Copy data to nfc buffer. */ >> + if (buf) >> + memcpy(rk_nfc_data_ptr(chip, i), >> + rk_nfc_buf_to_data_ptr(chip, buf, i), >> + ecc->size); > >> + /* >> + * The first four bytes of OOB are reserved for the >> + * boot ROM. In some debugging cases, such as with a >> + * read, erase and write back test these 4 bytes stored >> + * in OOB also need to be written back. >> + */ > > > /* > * The first four bytes of OOB are reserved for the > * boot ROM. In some debugging cases, such as with a > * read, erase and write back test these 4 bytes stored > * in OOB also need to be written back. > * > * The function nand_block_bad detects bad blocks like: > * > * bad = chip->oob_poi[chip->badblockpos]; > * > * chip->badblockpos == 0 for a large page NAND Flash, > * so chip->oob_poi[0] is the bad block mask (BBM). > * > * The OOB data layout on the NFC is: > * > * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... > * > * or > * > * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... > * > * The code here just swaps the first 4 bytes with the last > * 4 bytes without losing any data. > * > * The chip->oob_poi data layout: > * > * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 > * > * The rk_nfc_ooblayout_free() function already has reserved > * these 4 bytes with: > * > * oob_region->offset = NFC_SYS_DATA_SIZE + 2; > */ > > >> + if (!i) >> + memcpy(rk_nfc_oob_ptr(chip, i), >> + rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1), >> + NFC_SYS_DATA_SIZE); >> + else >> + memcpy(rk_nfc_oob_ptr(chip, i), >> + rk_nfc_buf_to_oob_ptr(chip, i - 1), >> + NFC_SYS_DATA_SIZE); >> + /* Copy ECC data to the NFC buffer. */ >> + memcpy(rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE, >> + rk_nfc_buf_to_oob_ecc_ptr(chip, i), >> + ecc->bytes); >> + } >> + >> + nand_prog_page_begin_op(chip, page, 0, NULL, 0); >> + rk_nfc_write_buf(nfc, buf, mtd->writesize + mtd->oobsize); >> + ret = nand_prog_page_end_op(chip); >> + >> + /* >> + * Deselect the currently selected target after the ops is done >> + * to reduce the power consumption. >> + */ >> + rk_nfc_select_chip(chip, -1); > > Does the MTD framework always select again? Remove. Do not assume that the MTD framework or user space knows that you have deselected the chip. > >> + >> + return ret; >> +} >> + >> +static int rk_nfc_write_oob(struct nand_chip *chip, int page) >> +{ >> + return rk_nfc_write_page_raw(chip, NULL, 1, page); >> +} >> + >> +static int rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, >> + int oob_on, int page) >> +{ >> + struct mtd_info *mtd = nand_to_mtd(chip); >> + struct rk_nfc *nfc = nand_get_controller_data(chip); >> + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); >> + struct nand_ecc_ctrl *ecc = &chip->ecc; >> + int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP : >> + NFC_MIN_OOB_PER_STEP; >> + int pages_per_blk = mtd->erasesize / mtd->writesize; >> + int ret = 0, i, boot_rom_mode = 0; >> + dma_addr_t dma_data, dma_oob; >> + u32 reg; >> + u8 *oob; >> + >> + nand_prog_page_begin_op(chip, page, 0, NULL, 0); >> + >> + memcpy(nfc->page_buf, buf, mtd->writesize); >> + >> + /* >> + * The first blocks (4, 8 or 16 depending on the device) are used >> + * by the boot ROM and the first 32 bits of OOB need to link to >> + * the next page address in the same block. We can't directly copy >> + * OOB data from the MTD framework, because this page address >> + * conflicts for example with the bad block marker (BBM), >> + * so we shift all OOB data including the BBM with 4 byte positions. >> + * As a consequence the OOB size available to the MTD framework is >> + * also reduced with 4 bytes. >> + * > >> + * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... > > > * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... > > keep layouts aligned > >> + * >> + * If a NAND is not a boot medium or the page is not a boot block, >> + * the first 4 bytes are left untouched by writing 0xFF to them. >> + * >> + * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... >> + * >> + * Configure the ECC algorithm supported by the boot ROM. >> + */ >> + if ((page < pages_per_blk * rknand->boot_blks) && > > if ((page < (pages_per_blk * rknand->boot_blks)) && > >> + (chip->options & NAND_IS_BOOT_MEDIUM)) { >> + boot_rom_mode = 1; >> + if (rknand->boot_ecc != ecc->strength) >> + rk_nfc_hw_ecc_setup(chip, ecc, >> + rknand->boot_ecc); >> + } >> + >> + for (i = 0; i < ecc->steps; i++) { >> + if (!i) { >> + reg = 0xFFFFFFFF; >> + } else { >> + oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; >> + reg = oob[0] | oob[1] << 8 | oob[2] << 16 | >> + oob[3] << 24; >> + } >> + if (!i && boot_rom_mode) >> + reg = (page & (pages_per_blk - 1)) * 4; >> + >> + if (nfc->cfg->type == NFC_V9) >> + nfc->oob_buf[i] = reg; >> + else >> + nfc->oob_buf[i * (oob_step / 4)] = reg; >> + } >> + >> + dma_data = dma_map_single(nfc->dev, (void *)nfc->page_buf, >> + mtd->writesize, DMA_TO_DEVICE); >> + dma_oob = dma_map_single(nfc->dev, nfc->oob_buf, >> + ecc->steps * oob_step, >> + DMA_TO_DEVICE); >> + >> + reinit_completion(&nfc->done); >> + writel(INT_DMA, nfc->regs + nfc->cfg->int_en_off); >> + >> + rk_nfc_xfer_start(nfc, NFC_WRITE, ecc->steps, dma_data, >> + dma_oob); >> + ret = wait_for_completion_timeout(&nfc->done, >> + msecs_to_jiffies(100)); >> + if (!ret) >> + dev_warn(nfc->dev, "write: wait dma done timeout.\n"); >> + /* >> + * Whether the DMA transfer is completed or not. The driver >> + * needs to check the NFC`s status register to see if the data >> + * transfer was completed. >> + */ >> + ret = rk_nfc_wait_for_xfer_done(nfc); >> + >> + dma_unmap_single(nfc->dev, dma_data, mtd->writesize, >> + DMA_TO_DEVICE); >> + dma_unmap_single(nfc->dev, dma_oob, ecc->steps * oob_step, >> + DMA_TO_DEVICE); >> + > >> + if (boot_rom_mode && rknand->boot_ecc != ecc->strength) >> + rk_nfc_hw_ecc_setup(chip, ecc, ecc->strength); >> + > >> + if (ret) { >> + ret = -EIO; >> + dev_err(nfc->dev, >> + "write: wait transfer done timeout.\n"); >> + } >> + > >> + if (ret) >> + return ret; > > remove, always deselect > > if (!ret) { > >> + >> + ret = nand_prog_page_end_op(chip); > > } > >> + >> + /* >> + * Deselect the currently selected target after the ops is done >> + * to reduce the power consumption. >> + */ >> + rk_nfc_select_chip(chip, -1); > > Does the MTD framework always select again? Remove. Do not assume that the MTD framework or user space knows that you have deselected the chip. > >> + >> + return ret; >> +} >> + >> +static int rk_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_on, >> + int page) >> +{ >> + struct mtd_info *mtd = nand_to_mtd(chip); >> + struct rk_nfc *nfc = nand_get_controller_data(chip); >> + struct nand_ecc_ctrl *ecc = &chip->ecc; >> + int i; >> + > > /* > * Normal timing and ECC layout size setup is already done in > * the rk_nfc_select_chip() function. > */ > > How about the ECC layout size setup for a boot block? > >> + nand_read_page_op(chip, page, 0, NULL, 0); >> + rk_nfc_read_buf(nfc, nfc->buffer, mtd->writesize + mtd->oobsize); >> + >> + /* >> + * Deselect the currently selected target after the ops is done >> + * to reduce the power consumption. >> + */ >> + rk_nfc_select_chip(chip, -1); Remove. Do not assume that the MTD framework or user space knows that you have deselected the chip. >> + >> + for (i = 0; i < ecc->steps; i++) { >> + /* >> + * The first four bytes of OOB are reserved for the >> + * boot ROM. In some debugging cases, such as with a read, >> + * erase and write back test, these 4 bytes also must be >> + * saved somewhere, otherwise this information will be >> + * lost during a write back. >> + */ >> + if (!i) >> + memcpy(rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1), >> + rk_nfc_oob_ptr(chip, i), >> + NFC_SYS_DATA_SIZE); >> + else >> + memcpy(rk_nfc_buf_to_oob_ptr(chip, i - 1), >> + rk_nfc_oob_ptr(chip, i), >> + NFC_SYS_DATA_SIZE); >> + /* Copy ECC data from the NFC buffer. */ >> + memcpy(rk_nfc_buf_to_oob_ecc_ptr(chip, i), >> + rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE, >> + ecc->bytes); >> + /* Copy data from the NFC buffer. */ >> + if (buf) >> + memcpy(rk_nfc_buf_to_data_ptr(chip, buf, i), >> + rk_nfc_data_ptr(chip, i), >> + ecc->size); >> + } >> + >> + return 0; >> +} >> + >> +static int rk_nfc_read_oob(struct nand_chip *chip, int page) >> +{ >> + return rk_nfc_read_page_raw(chip, NULL, 1, page); >> +} >> + >> +static int rk_nfc_read_page_hwecc(struct nand_chip *chip, u8 *buf, int oob_on, >> + int page) >> +{ >> + struct mtd_info *mtd = nand_to_mtd(chip); >> + struct rk_nfc *nfc = nand_get_controller_data(chip); >> + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); >> + struct nand_ecc_ctrl *ecc = &chip->ecc; >> + int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP : >> + NFC_MIN_OOB_PER_STEP; >> + int pages_per_blk = mtd->erasesize / mtd->writesize; >> + dma_addr_t dma_data, dma_oob; > >> + int ret = 0, i, boot_rom_mode = 0; > > int ret = 0, i, cnt, boot_rom_mode = 0; > >> + int bitflips = 0, bch_st; >> + u8 *oob; >> + u32 tmp; >> + >> + nand_read_page_op(chip, page, 0, NULL, 0); >> + >> + dma_data = dma_map_single(nfc->dev, nfc->page_buf, >> + mtd->writesize, >> + DMA_FROM_DEVICE); >> + dma_oob = dma_map_single(nfc->dev, nfc->oob_buf, >> + ecc->steps * oob_step, >> + DMA_FROM_DEVICE); >> + >> + /* >> + * The first blocks (4, 8 or 16 depending on the device) >> + * are used by the boot ROM. >> + * Configure the ECC algorithm supported by the boot ROM. >> + */ > >> + if ((page < pages_per_blk * rknand->boot_blks) && > >> + if ((page < (pages_per_blk * rknand->boot_blks)) && > >> + (chip->options & NAND_IS_BOOT_MEDIUM)) { >> + boot_rom_mode = 1; >> + if (rknand->boot_ecc != ecc->strength) >> + rk_nfc_hw_ecc_setup(chip, ecc, >> + rknand->boot_ecc); >> + } >> + >> + reinit_completion(&nfc->done); >> + writel(INT_DMA, nfc->regs + nfc->cfg->int_en_off); >> + rk_nfc_xfer_start(nfc, NFC_READ, ecc->steps, dma_data, >> + dma_oob); >> + ret = wait_for_completion_timeout(&nfc->done, >> + msecs_to_jiffies(100)); >> + if (!ret) >> + dev_warn(nfc->dev, "read: wait dma done timeout.\n"); >> + /* >> + * Whether the DMA transfer is completed or not. The driver >> + * needs to check the NFC`s status register to see if the data >> + * transfer was completed. >> + */ >> + ret = rk_nfc_wait_for_xfer_done(nfc); > > add empty line > >> + dma_unmap_single(nfc->dev, dma_data, mtd->writesize, >> + DMA_FROM_DEVICE); >> + dma_unmap_single(nfc->dev, dma_oob, ecc->steps * oob_step, >> + DMA_FROM_DEVICE); >> + >> + if (ret) { > >> + bitflips = -EIO; > > ret = -EIO; > > return only "0" or official error codes > >> + dev_err(nfc->dev, >> + "read: wait transfer done timeout.\n"); >> + goto out; >> + } >> + >> + for (i = 1; i < ecc->steps; i++) { >> + oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; >> + if (nfc->cfg->type == NFC_V9) >> + tmp = nfc->oob_buf[i]; >> + else >> + tmp = nfc->oob_buf[i * (oob_step / 4)]; >> + *oob++ = (u8)tmp; >> + *oob++ = (u8)(tmp >> 8); >> + *oob++ = (u8)(tmp >> 16); >> + *oob++ = (u8)(tmp >> 24); >> + } >> + >> + for (i = 0; i < (ecc->steps / 2); i++) { >> + bch_st = readl_relaxed(nfc->regs + >> + nfc->cfg->bch_st_off + i * 4); >> + if (bch_st & BIT(nfc->cfg->ecc0.err_flag_bit) || >> + bch_st & BIT(nfc->cfg->ecc1.err_flag_bit)) { >> + mtd->ecc_stats.failed++; > >> + bitflips = 0; > > max_bitflips = -1; > > use max_bitflips only for the error warning, not as return value > >> + } else { > >> + ret = ECC_ERR_CNT(bch_st, nfc->cfg->ecc0); > > use ret only with "0" or official error codes, use cnt instead > > cnt = ECC_ERR_CNT(bch_st, nfc->cfg->ecc0); > >> + mtd->ecc_stats.corrected += ret; > mtd->ecc_stats.corrected += cnt; > >> + bitflips = max_t(u32, bitflips, ret); > > bitflips = max_t(u32, bitflips, cnt); > >> + >> + ret = ECC_ERR_CNT(bch_st, nfc->cfg->ecc1); > > cnt = ECC_ERR_CNT(bch_st, nfc->cfg->ecc1); > >> + mtd->ecc_stats.corrected += ret; > > mtd->ecc_stats.corrected += cnt; > >> + bitflips = max_t(u32, bitflips, ret); > > bitflips = max_t(u32, bitflips, cnt); >> + } >> + } >> +out: >> + memcpy(buf, nfc->page_buf, mtd->writesize); >> + > >> + if (boot_rom_mode && rknand->boot_ecc != ecc->strength) >> + rk_nfc_hw_ecc_setup(chip, ecc, ecc->strength); >> + > >> + if (bitflips > ecc->strength) > > if (bitflips == -1) { > ret = -EIO; > >> + dev_err(nfc->dev, "read page: %x ecc error!\n", page); > > } > >> + >> + /* >> + * Deselect the currently selected target after the ops is done >> + * to reduce the power consumption. >> + */ >> + rk_nfc_select_chip(chip, -1); Remove. Do not assume that the MTD framework or user space knows that you have deselected the chip. >> + > >> + return bitflips; > > return ret; > > Return only "0" or official error codes > If you want to do a "bad block scan" function in user space analyse/use > "mtd->ecc_stats" instead. > >> +} >> +