Hi KOBAYASHI,

Le Fri, 26 May 2017 10:15:35 +0900,
KOBAYASHI Yoshitake <yoshitake.kobaya...@toshiba.co.jp> a écrit :

> This patch enables support for Toshiba BENAND. It was originally posted on
>   https://lkml.org/lkml/2015/7/24/571
> 
> This patch is rewrote to adapt the recent mtd "separate vendor specific code
> from core" cleanup process.
> 
> Signed-off-by: KOBAYASHI Yoshitake <yoshitake.kobaya...@toshiba.co.jp>
> ---
>  drivers/mtd/nand/Kconfig        |  17 ++++++
>  drivers/mtd/nand/nand_base.c    |  15 ++++++
>  drivers/mtd/nand/nand_toshiba.c | 112 
> +++++++++++++++++++++++++++++++++++++++-
>  include/linux/mtd/nand.h        |   1 +
>  4 files changed, 143 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 0bd2319..6634d4b 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -36,6 +36,23 @@ config MTD_NAND_ECC_BCH
>         ECC codes. They are used with NAND devices requiring more than 1 bit
>         of error correction.
>  
> +config MTD_NAND_BENAND
> +     bool "Support for Toshiba BENAND (Built-in ECC NAND)"
> +     default n
> +     help
> +       This enables support for Toshiba BENAND.
> +       Toshiba BENAND is a SLC NAND solution that automatically
> +       generates ECC inside NAND chip.
> +
> +config MTD_NAND_BENAND_ECC_STATUS
> +     bool "Enable ECC Status Read Command(0x7A)"
> +     depends on MTD_NAND_BENAND
> +     default n
> +     help
> +       This enables support for ECC Status Read Command(0x7A) of BENAND.
> +       When this enables, report the real number of bitflips.
> +       In other cases, report the assumud number.
> +

Please drop the Kconfig options. The amount of code added here is quite
small, and I don't think we need to compile it out. If the vendor code
continues to grow we'll see how we want to deal with that, but we're
not there yet.

>  config MTD_SM_COMMON
>       tristate
>       default n
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 43722a8..ab8652e 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -4186,6 +4186,7 @@ static const char * const nand_ecc_modes[] = {
>       [NAND_ECC_HW_SYNDROME]  = "hw_syndrome",
>       [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
>       [NAND_ECC_ON_DIE]       = "on-die",
> +     [NAND_ECC_BENAND]       = "benand",

No. BENAND and on-die ECC are the same thing (not the same
implementation, but the same feature). Please re-use the existing
ECC_ON_DIE definition.

>  };
>  
>  static int of_get_nand_ecc_mode(struct device_node *np)
> @@ -4751,6 +4752,19 @@ int nand_scan_tail(struct mtd_info *mtd)
>                       ecc->write_oob = nand_write_oob_std;
>               break;
>  
> +     case NAND_ECC_BENAND:
> +             if (!ecc->read_page || !ecc->read_subpage) {
> +                     WARN(1, "No ECC functions supplied; benand ECC not 
> possible\n");
> +                     ret = -EINVAL;
> +                     goto err_free;
> +             }
> +             ecc->write_page = nand_write_page_raw;
> +             ecc->read_page_raw = nand_read_page_raw;
> +             ecc->write_page_raw = nand_write_page_raw;
> +             ecc->read_oob = nand_read_oob_std;
> +             ecc->write_oob = nand_write_oob_std;
> +             break;

Ditto.

> +
>       case NAND_ECC_NONE:
>               pr_warn("NAND_ECC_NONE selected by board driver. This is not 
> recommended!\n");
>               ecc->read_page = nand_read_page_raw;
> @@ -4831,6 +4845,7 @@ int nand_scan_tail(struct mtd_info *mtd)
>       /* Large page NAND with SOFT_ECC should support subpage reads */
>       switch (ecc->mode) {
>       case NAND_ECC_SOFT:
> +     case NAND_ECC_BENAND:

And here as well.

>               if (chip->page_shift > 9)
>                       chip->options |= NAND_SUBPAGE_READ;
>               break;
> diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
> index fa787ba..bb3c852 100644
> --- a/drivers/mtd/nand/nand_toshiba.c
> +++ b/drivers/mtd/nand/nand_toshiba.c
> @@ -17,6 +17,97 @@
>  
>  #include <linux/mtd/nand.h>
>  
> +/* ECC Status Read Command for BENAND */
> +#define NAND_CMD_ECC_STATUS  0x7A

Can you prefix the name with TOSHIBA_:

#define TOSHIBA_NAND_CMD_ECC_STATUS     0x7a

> +
> +/* Recommended to rewrite for BENAND */
> +#define NAND_STATUS_RECOM_REWRT      0x08

Ditto, add a TOSHIBA somewhere:

#define TOSHIBA_NAND_STATUS_REWRITE_RECOMMENDED         BIT(3)

But anyway, I'm not sure we want to use this metric since we have the
number of corrected bitflips thanks to the TOSHIBA_NAND_CMD_ECC_STATUS
command.

> +
> +

Drop the extra empty line.

> +static int toshiba_nand_benand_status_chk(struct mtd_info *mtd,
> +                     struct nand_chip *chip)

Try to align parameters on the open parenthesis when you have multiple
lines.

> +{
> +     unsigned int max_bitflips = 0;
> +     u8 status;
> +
> +     /* Check Read Status */
> +     chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
> +     status = chip->read_byte(mtd);
> +
> +     /* timeout */
> +     if (!(status & NAND_STATUS_READY)) {
> +             pr_debug("BENAND : Time Out!\n");
> +             return -EIO;
> +     }

Hm, I think this one has already been checked by the core.

> +
> +     /* uncorrectable */
> +     else if (status & NAND_STATUS_FAIL)
> +             mtd->ecc_stats.failed++;

You have all the information to add the exact number of failures (it's
a per-sector information, not per-page).

> +
> +     /* correctable */
> +     else if (status & NAND_STATUS_RECOM_REWRT) {
> +             if (chip->cmd_ctrl &&
> +                     IS_ENABLED(CONFIG_MTD_NAND_BENAND_ECC_STATUS)) {
> +
> +                     int i;
> +                     u8 ecc_status;
> +                     unsigned int bitflips;
> +
> +                     /* Check Read ECC Status */
> +                     chip->cmd_ctrl(mtd, NAND_CMD_ECC_STATUS,
> +                             NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> +                     /* Get bitflips info per 512Byte */
> +                     for (i = 0; i < mtd->writesize >> 9; i++) {
> +                             ecc_status = chip->read_byte(mtd);
> +                             bitflips = ecc_status & 0x0f;
> +                             max_bitflips = max_t(unsigned int,
> +                                             max_bitflips, bitflips);
> +                     }
> +                     mtd->ecc_stats.corrected += max_bitflips;
> +             } else {
> +                     /*
> +                      * If can't use chip->cmd_ctrl,
> +                      * we can't get real number of bitflips.
> +                      * So, we set max_bitflips mtd->bitflip_threshold.
> +                      */

And that's exactly the kind of situation I want to avoid. I hate the
fact that, depending on the NAND controller, we have this feature
working or not. Well, if it's a real limitation of the
controller, that's acceptable, but most of the time it's just a driver
limitation.

I'd like to have the ->exec_op() [1] infrastructure ready before we
start adding vendor specific commands. It probably needs to be extended
with an ->supports_op() hook to ask the controller whether it supports a
specific operation or not and let the core/vendor driver decide whether
this part can be attached to the controller based on the result.

> +                     max_bitflips = mtd->bitflip_threshold;
> +                     mtd->ecc_stats.corrected += max_bitflips;
> +             }
> +     }

For the if () ... else if () ... blocks please try to do:

        if (...) {
                /* comment here */
                ...
        } else if (...) {
                /* comment here */
                ...
        } else {
                /* comment here */
                ...
        }

> +
> +     return max_bitflips;
> +}
> +
> +static int toshiba_nand_read_page_benand(struct mtd_info *mtd,
> +                     struct nand_chip *chip, uint8_t *buf,
> +                     int oob_required, int page)
> +{
> +     unsigned int max_bitflips = 0;
> +
> +     chip->ecc.read_page_raw(mtd, chip, buf, oob_required, page);
> +     max_bitflips = toshiba_nand_benand_status_chk(mtd, chip);
> +
> +     return max_bitflips;
> +}
> +
> +static int toshiba_nand_read_subpage_benand(struct mtd_info *mtd,
> +                     struct nand_chip *chip, uint32_t data_offs,
> +                     uint32_t readlen, uint8_t *bufpoi, int page)
> +{
> +     uint8_t *p;
> +     unsigned int max_bitflips = 0;
> +
> +     if (data_offs != 0)
> +             chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1);
> +
> +     p = bufpoi + data_offs;
> +     chip->read_buf(mtd, p, readlen);
> +
> +     max_bitflips = toshiba_nand_benand_status_chk(mtd, chip);
> +
> +     return max_bitflips;
> +}
> +
>  static void toshiba_nand_decode_id(struct nand_chip *chip)
>  {
>       struct mtd_info *mtd = nand_to_mtd(chip);
> @@ -33,15 +124,32 @@ static void toshiba_nand_decode_id(struct nand_chip 
> *chip)
>        */
>       if (chip->id.len >= 6 && nand_is_slc(chip) &&
>           (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
> -         !(chip->id.data[4] & 0x80) /* !BENAND */)
> -             mtd->oobsize = 32 * mtd->writesize >> 9;
> +         (chip->id.data[4] & 0x80) /* BENAND */) {
> +             if (IS_ENABLED(CONFIG_MTD_NAND_BENAND))
> +                     chip->ecc.mode = NAND_ECC_BENAND;

No, you should not set that explicitly. This choice should be left to
the user. Now, since the internal ECC engine cannot be disabled here,
you should fail in toshiba_nand_init() if you are dealing with a BENAND
and chip->ecc.mode != NAND_ECC_ON_DIE.


> +     } else {
> +             mtd->oobsize = 32 * mtd->writesize >> 9;        /* !BENAND */
> +     }

You're changing the ID decoding logic here.

It should be:

        if (chip->id.len >= 6 && nand_is_slc(chip) &&
            (chip->id.data[5] & 0x7) == 0x6 /* 24nm */) {
                if (chip->id.data[4] & 0x80)
                        chip->ecc.mode = NAND_ECC_BENAND;
                else
                        mtd->oobsize = 32 * mtd->writesize >> 9;
        }
>  }
>  
>  static int toshiba_nand_init(struct nand_chip *chip)
>  {
> +     struct mtd_info *mtd = nand_to_mtd(chip);
> +
>       if (nand_is_slc(chip))
>               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
>  
> +     if (chip->ecc.mode == NAND_ECC_BENAND) {
> +             chip->ecc.options = NAND_ECC_CUSTOM_PAGE_ACCESS;
> +             chip->ecc.bytes = 0;

It should be set to 16 according to the datasheet. But I guess this is
not exactly true. I'm pretty sure we can use some of these bytes to
store real data. Assuming you're using BCH, only 13bytes are needed for
8bits/512bytes strength, and I guess the BBM region is also left
untouched (first 2 bytes of the OOB region).

> +             chip->ecc.strength = 8;
> +             chip->ecc.total = 0;

No need to explicitly set that one, but you should set chip->ecc.size
to 512.

> +             chip->ecc.read_page = toshiba_nand_read_page_benand;
> +             chip->ecc.read_subpage = toshiba_nand_read_subpage_benand;
> +
> +             mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
> +     }

Should be:


        if (chip->id.len >= 6 && nand_is_slc(chip) &&
            (chip->id.data[5] & 0x7) == 0x6 &&
            (chip->id.data[4] & 0x80)) {
                /* BENAND */

                /*
                 * We can't disable the internal ECC engine, the user
                 * has to use on-die ECC, there is no alternative.
                 */
                if (chip->ecc.mode != NAND_ECC_ON_DIE)
                        return -EINVAL;

                ....
        }

> +
>       return 0;
>  }
>  
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 3a6a77f..6821334 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -117,6 +117,7 @@ typedef enum {
>       NAND_ECC_HW_SYNDROME,
>       NAND_ECC_HW_OOB_FIRST,
>       NAND_ECC_ON_DIE,
> +     NAND_ECC_BENAND,

As explained above: this is unneeded.

>  } nand_ecc_modes_t;
>  
>  enum nand_ecc_algo {

[1]https://github.com/bbrezillon/linux/commits/nand/exec_op1

Reply via email to