From: 赵仪峰 <yifeng.zhao@rock-chips.com>
To: "Johan Jonker" <jbx6244@gmail.com>,
"Miquel Raynal" <miquel.raynal@bootlin.com>,
richard <richard@nod.at>, vigneshr <vigneshr@ti.com>,
robh+dt <robh+dt@kernel.org>
Cc: devicetree <devicetree@vger.kernel.org>,
HeikoStübner <heiko@sntech.de>,
linux-kernel <linux-kernel@vger.kernel.org>,
linux-rockchip <linux-rockchip@lists.infradead.org>,
linux-mtd <linux-mtd@lists.infradead.org>,
linux-arm-kernel <linux-arm-kernel@lists.infradead.org>
Subject: Re: Re: [PATCH v13 2/8] mtd: rawnand: rockchip: NFC drivers for RK3308, RK2928 and others
Date: Mon, 2 Nov 2020 11:46:04 +0800 [thread overview]
Message-ID: <20201102114503679684135@rock-chips.com> (raw)
In-Reply-To: 0dabd80e-b281-be65-b8e2-da00f46964fb@gmail.com
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.
>>
>>> +}
>>> +
>
>
>
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
WARNING: multiple messages have this Message-ID (diff)
From: 赵仪峰 <yifeng.zhao@rock-chips.com>
To: "Johan Jonker" <jbx6244@gmail.com>,
"Miquel Raynal" <miquel.raynal@bootlin.com>,
richard <richard@nod.at>, vigneshr <vigneshr@ti.com>,
robh+dt <robh+dt@kernel.org>
Cc: devicetree <devicetree@vger.kernel.org>,
HeikoStübner <heiko@sntech.de>,
linux-kernel <linux-kernel@vger.kernel.org>,
linux-rockchip <linux-rockchip@lists.infradead.org>,
linux-mtd <linux-mtd@lists.infradead.org>,
linux-arm-kernel <linux-arm-kernel@lists.infradead.org>
Subject: Re: Re: [PATCH v13 2/8] mtd: rawnand: rockchip: NFC drivers for RK3308, RK2928 and others
Date: Mon, 2 Nov 2020 11:46:04 +0800 [thread overview]
Message-ID: <20201102114503679684135@rock-chips.com> (raw)
In-Reply-To: 0dabd80e-b281-be65-b8e2-da00f46964fb@gmail.com
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.
>>
>>> +}
>>> +
>
>
>
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
WARNING: multiple messages have this Message-ID (diff)
From: 赵仪峰 <yifeng.zhao@rock-chips.com>
To: "Johan Jonker" <jbx6244@gmail.com>,
"Miquel Raynal" <miquel.raynal@bootlin.com>,
richard <richard@nod.at>, vigneshr <vigneshr@ti.com>,
robh+dt <robh+dt@kernel.org>
Cc: devicetree <devicetree@vger.kernel.org>,
HeikoStübner <heiko@sntech.de>,
linux-kernel <linux-kernel@vger.kernel.org>,
linux-rockchip <linux-rockchip@lists.infradead.org>,
linux-mtd <linux-mtd@lists.infradead.org>,
linux-arm-kernel <linux-arm-kernel@lists.infradead.org>
Subject: Re: Re: [PATCH v13 2/8] mtd: rawnand: rockchip: NFC drivers for RK3308, RK2928 and others
Date: Mon, 2 Nov 2020 11:46:04 +0800 [thread overview]
Message-ID: <20201102114503679684135@rock-chips.com> (raw)
In-Reply-To: 0dabd80e-b281-be65-b8e2-da00f46964fb@gmail.com
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.
>>
>>> +}
>>> +
>
>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
WARNING: multiple messages have this Message-ID (diff)
From: 赵仪峰 <yifeng.zhao@rock-chips.com>
To: "Johan Jonker" <jbx6244@gmail.com>,
"Miquel Raynal" <miquel.raynal@bootlin.com>,
richard <richard@nod.at>, vigneshr <vigneshr@ti.com>,
robh+dt <robh+dt@kernel.org>
Cc: devicetree <devicetree@vger.kernel.org>,
HeikoStübner <heiko@sntech.de>,
linux-kernel <linux-kernel@vger.kernel.org>,
linux-rockchip <linux-rockchip@lists.infradead.org>,
linux-mtd <linux-mtd@lists.infradead.org>,
linux-arm-kernel <linux-arm-kernel@lists.infradead.org>
Subject: Re: Re: [PATCH v13 2/8] mtd: rawnand: rockchip: NFC drivers for RK3308, RK2928 and others
Date: Mon, 2 Nov 2020 11:46:04 +0800 [thread overview]
Message-ID: <20201102114503679684135@rock-chips.com> (raw)
In-Reply-To: 0dabd80e-b281-be65-b8e2-da00f46964fb@gmail.com
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.
>>
>>> +}
>>> +
>
>
>
next prev parent reply other threads:[~2020-11-02 3:47 UTC|newest]
Thread overview: 95+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-28 9:53 [PATCH v13 0/8] Add Rockchip NFC drivers for RK3308 and others Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` [PATCH v13 1/8] dt-bindings: mtd: Describe Rockchip RK3xxx NAND flash controller Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` [PATCH v13 2/8] mtd: rawnand: rockchip: NFC drivers for RK3308, RK2928 and others Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 10:48 ` Miquel Raynal
2020-10-28 10:48 ` Miquel Raynal
2020-10-28 10:48 ` Miquel Raynal
2020-10-28 10:48 ` Miquel Raynal
2020-10-30 10:12 ` 赵仪峰
2020-10-30 10:12 ` 赵仪峰
2020-10-30 10:12 ` 赵仪峰
2020-10-30 10:12 ` 赵仪峰
2020-10-30 10:26 ` Miquel Raynal
2020-10-30 10:26 ` Miquel Raynal
2020-10-30 10:26 ` Miquel Raynal
2020-10-30 10:26 ` Miquel Raynal
2020-10-31 11:58 ` Johan Jonker
2020-10-31 11:58 ` Johan Jonker
2020-10-31 11:58 ` Johan Jonker
2020-10-31 11:58 ` Johan Jonker
2020-10-31 13:45 ` Johan Jonker
2020-10-31 13:45 ` Johan Jonker
2020-10-31 13:45 ` Johan Jonker
2020-10-31 13:45 ` Johan Jonker
2020-11-02 3:46 ` 赵仪峰 [this message]
2020-11-02 3:46 ` 赵仪峰
2020-11-02 3:46 ` 赵仪峰
2020-11-02 3:46 ` 赵仪峰
2020-11-02 7:32 ` Miquel Raynal
2020-11-02 7:32 ` Miquel Raynal
2020-11-02 7:32 ` Miquel Raynal
2020-11-02 7:32 ` Miquel Raynal
2020-11-02 8:14 ` 赵仪峰
2020-11-02 8:14 ` 赵仪峰
2020-11-02 8:14 ` 赵仪峰
2020-11-02 8:14 ` 赵仪峰
2020-11-02 8:20 ` Miquel Raynal
2020-11-02 8:20 ` Miquel Raynal
2020-11-02 8:20 ` Miquel Raynal
2020-11-02 8:20 ` Miquel Raynal
[not found] ` <e02e13a0-769d-6b73-c87e-5b7d75fd4254@rock-chips.com>
2020-11-02 12:57 ` Johan Jonker
2020-11-02 12:57 ` Johan Jonker
2020-11-02 12:57 ` Johan Jonker
2020-11-02 12:57 ` Johan Jonker
2020-11-02 13:07 ` Miquel Raynal
2020-11-02 13:07 ` Miquel Raynal
2020-11-02 13:07 ` Miquel Raynal
2020-11-02 13:07 ` Miquel Raynal
2020-11-02 13:11 ` Johan Jonker
2020-11-02 13:11 ` Johan Jonker
2020-11-02 13:11 ` Johan Jonker
2020-11-02 13:11 ` Johan Jonker
2020-11-02 16:31 ` Johan Jonker
2020-11-02 16:31 ` Johan Jonker
2020-11-02 16:31 ` Johan Jonker
2020-11-02 16:31 ` Johan Jonker
2020-11-02 17:00 ` Miquel Raynal
2020-11-02 17:00 ` Miquel Raynal
2020-11-02 17:00 ` Miquel Raynal
2020-11-02 17:00 ` Miquel Raynal
2020-11-02 17:11 ` Johan Jonker
2020-11-02 17:11 ` Johan Jonker
2020-11-02 17:11 ` Johan Jonker
2020-11-02 17:11 ` Johan Jonker
2020-11-04 7:30 ` 赵仪峰
2020-11-04 7:30 ` 赵仪峰
2020-11-04 7:30 ` 赵仪峰
2020-11-04 7:30 ` 赵仪峰
2020-11-04 7:34 ` 赵仪峰
2020-11-04 7:34 ` 赵仪峰
2020-11-04 7:34 ` 赵仪峰
2020-11-04 7:34 ` 赵仪峰
2020-10-28 9:53 ` [PATCH v13 3/8] MAINTAINERS: add maintainers to ROCKCHIP NFC Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` [PATCH v13 4/8] arm64: dts: rockchip: Add NFC node for RK3308 SoC Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:53 ` Yifeng Zhao
2020-10-28 9:54 ` [PATCH v13 5/8] arm64: dts: rockchip: Add NFC node for PX30 SoC Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` [PATCH v13 6/8] arm: dts: rockchip: Add NFC node for RV1108 SoC Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` [PATCH v13 7/8] arm: dts: rockchip: Add NFC node for RK2928 and other SoCs Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` [PATCH v13 8/8] arm: dts: rockchip: Add NFC node for RK3036 SoC Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
2020-10-28 9:54 ` Yifeng Zhao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201102114503679684135@rock-chips.com \
--to=yifeng.zhao@rock-chips.com \
--cc=devicetree@vger.kernel.org \
--cc=heiko@sntech.de \
--cc=jbx6244@gmail.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=linux-rockchip@lists.infradead.org \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=robh+dt@kernel.org \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.