Hi Johan,

void nand_deselect_target(struct nand_chip *chip)
{
        if (chip->legacy.select_chip)
                chip->legacy.select_chip(chip, -1);

        chip->cur_cs = -1;
}

I need add the code below and it work. 

   chip->legacy.select_chip = rk_nfc_select_chip;

But I found almost all nandc drivers do not add this code. Is there any other 
way to implement it?

--------------

>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.
>>
>>> +}
>>> +
>
>
>

Reply via email to