[7/7] mtd: nand: Add a common spl layer for nand subsystem

Message ID 20220727093748.1415135-8-michael@amarulasolutions.com
State New
Headers show
Series
  • NAND new improvements
Related show

Commit Message

Michael Nazzareno Trimarchi July 27, 2022, 9:37 a.m. UTC
Avoid code duplication accross drivers but make them use
the same implementation. Create nand_common_spl to implement
the part that does not depend on nand chipset. Apply to

- mxs nand spl driver
- mt7621 spl driver

The mt7621 now as side effect implement nand_spl_adjust_offset, that
implements bad block handle for complex image like fitImage

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
---
 drivers/mtd/nand/raw/Makefile          |   4 +-
 drivers/mtd/nand/raw/mt7621_nand_spl.c | 188 +------------------
 drivers/mtd/nand/raw/mxs_nand_spl.c    | 176 +-----------------
 drivers/mtd/nand/raw/nand_common_spl.c | 245 +++++++++++++++++++++++++
 drivers/mtd/nand/raw/nand_common_spl.h |  15 ++
 5 files changed, 269 insertions(+), 359 deletions(-)
 create mode 100644 drivers/mtd/nand/raw/nand_common_spl.c
 create mode 100644 drivers/mtd/nand/raw/nand_common_spl.h

Comments

Dario Binacchi July 27, 2022, 1:50 p.m. UTC | #1
Hi Michael,

On Wed, Jul 27, 2022 at 11:38 AM Michael Trimarchi
<michael@amarulasolutions.com> wrote:
>
> Avoid code duplication accross drivers but make them use
> the same implementation. Create nand_common_spl to implement
> the part that does not depend on nand chipset. Apply to
>
> - mxs nand spl driver
> - mt7621 spl driver
>
> The mt7621 now as side effect implement nand_spl_adjust_offset, that
> implements bad block handle for complex image like fitImage
>
> Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
> ---
>  drivers/mtd/nand/raw/Makefile          |   4 +-
>  drivers/mtd/nand/raw/mt7621_nand_spl.c | 188 +------------------
>  drivers/mtd/nand/raw/mxs_nand_spl.c    | 176 +-----------------
>  drivers/mtd/nand/raw/nand_common_spl.c | 245 +++++++++++++++++++++++++
>  drivers/mtd/nand/raw/nand_common_spl.h |  15 ++
>  5 files changed, 269 insertions(+), 359 deletions(-)
>  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.c
>  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.h
>
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index a398aa9d88..82ddb2b5d8 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -87,8 +87,8 @@ else  # minimal SPL drivers
>  obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
>  obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
>  obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
> -obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
> +obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o nand_common_spl.o
>  obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
> -obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
> +obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o nand_common_spl.o
>
>  endif # drivers
> diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> index 114fc8b7ce..254e14a553 100644
> --- a/drivers/mtd/nand/raw/mt7621_nand_spl.c
> +++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> @@ -10,187 +10,13 @@
>  #include <linux/sizes.h>
>  #include <linux/delay.h>
>  #include <linux/mtd/rawnand.h>
> +#include "nand_common_spl.h"
>  #include "mt7621_nand.h"
> +#include "nand_common_spl.h"

You already included "nand_common_spl.h"

>
>  static struct mt7621_nfc nfc_dev;
> -static u8 *buffer;
>  static int nand_valid;
>
> -static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
> -                           int column, int page_addr)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -
> -       /* Command latch cycle */
> -       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> -
> -       if (column != -1 || page_addr != -1) {
> -               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> -
> -               /* Serially input address */
> -               if (column != -1) {
> -                       chip->cmd_ctrl(mtd, column, ctrl);
> -                       ctrl &= ~NAND_CTRL_CHANGE;
> -                       if (command != NAND_CMD_READID)
> -                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> -               }
> -               if (page_addr != -1) {
> -                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> -                       chip->cmd_ctrl(mtd, page_addr >> 8,
> -                                      NAND_NCE | NAND_ALE);
> -                       if (chip->options & NAND_ROW_ADDR_3)
> -                               chip->cmd_ctrl(mtd, page_addr >> 16,
> -                                              NAND_NCE | NAND_ALE);
> -               }
> -       }
> -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> -
> -       /*
> -        * Program and erase have their own busy handlers status, sequential
> -        * in and status need no delay.
> -        */
> -       switch (command) {
> -       case NAND_CMD_STATUS:
> -       case NAND_CMD_READID:
> -       case NAND_CMD_SET_FEATURES:
> -               return;
> -
> -       case NAND_CMD_READ0:
> -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> -                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> -                              NAND_NCE | NAND_CTRL_CHANGE);
> -       }
> -
> -       /*
> -        * Apply this short delay always to ensure that we do wait tWB in
> -        * any case on any machine.
> -        */
> -       ndelay(100);
> -
> -       nand_wait_ready(mtd);
> -}
> -
> -static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
> -                              unsigned int page)
> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -
> -       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> -       if (ret < 0 || ret > chip->ecc.strength)
> -               return -1;
> -
> -       return 0;
> -}
> -
> -static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> -                             unsigned int page)
> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -
> -       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> -       if (ret < 0)
> -               return -1;
> -
> -       if (len > mtd->oobsize)
> -               len = mtd->oobsize;
> -
> -       memcpy(buf, chip->oob_poi, len);
> -
> -       return 0;
> -}
> -
> -static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       u32 pages_per_block, i = 0;
> -       int ret;
> -       u8 bad;
> -
> -       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> -
> -       /* Read from first/last page(s) if necessary */
> -       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> -               page += pages_per_block - 1;
> -               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> -                       page--;
> -       }
> -
> -       do {
> -               ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
> -               if (ret)
> -                       return ret;
> -
> -               ret = bad != 0xFF;
> -
> -               i++;
> -               page++;
> -       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> -
> -       return ret;
> -}
> -
> -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> -{
> -       struct mt7621_nfc *nfc = &nfc_dev;
> -       struct nand_chip *chip = &nfc->nand;
> -       struct mtd_info *mtd = &chip->mtd;
> -       u32 addr, col, page, chksz;
> -       bool check_bad = true;
> -
> -       if (!nand_valid)
> -               return -ENODEV;
> -
> -       while (size) {
> -               if (check_bad || !(offs & mtd->erasesize_mask)) {
> -                       addr = offs & (~mtd->erasesize_mask);
> -                       page = addr >> mtd->writesize_shift;
> -                       if (nfc_check_bad_block(mtd, page)) {
> -                               /* Skip bad block */
> -                               if (addr >= mtd->size - mtd->erasesize)
> -                                       return -1;
> -
> -                               offs += mtd->erasesize;
> -                               continue;
> -                       }
> -
> -                       check_bad = false;
> -               }
> -
> -               col = offs & mtd->writesize_mask;
> -               page = offs >> mtd->writesize_shift;
> -               chksz = min(mtd->writesize - col, (uint32_t)size);
> -
> -               if (unlikely(chksz < mtd->writesize)) {
> -                       /* Not reading a full page */
> -                       if (nfc_read_page_hwecc(mtd, buffer, page))
> -                               return -1;
> -
> -                       memcpy(dest, buffer + col, chksz);
> -               } else {
> -                       if (nfc_read_page_hwecc(mtd, dest, page))
> -                               return -1;
> -               }
> -
> -               dest += chksz;
> -               offs += chksz;
> -               size -= chksz;
> -       }
> -
> -       return 0;
> -}
> -
> -int nand_default_bbt(struct mtd_info *mtd)
> -{
> -       return 0;
> -}
> -
>  unsigned long nand_size(void)
>  {
>         if (!nand_valid)
> @@ -203,10 +29,6 @@ unsigned long nand_size(void)
>         return SZ_2G;
>  }
>
> -void nand_deselect(void)
> -{
> -}
> -
>  void nand_init(void)
>  {
>         struct mtd_info *mtd;
> @@ -219,7 +41,7 @@ void nand_init(void)
>
>         chip = &nfc_dev.nand;
>         mtd = &chip->mtd;
> -       chip->cmdfunc = nand_command_lp;
> +       chip->cmdfunc = nand_spl_command_lp;
>
>         if (mt7621_nfc_spl_post_init(&nfc_dev))
>                 return;
> @@ -229,9 +51,7 @@ void nand_init(void)
>         mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
>         mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
>
> -       buffer = malloc(mtd->writesize);
> -       if (!buffer)
> -               return;
> +       nand_spl_init(chip);
>
>         nand_valid = 1;
>  }
> diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
> index c7ea09e2f9..5cff6020c4 100644
> --- a/drivers/mtd/nand/raw/mxs_nand_spl.c
> +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
> @@ -14,66 +14,11 @@
>  #include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/mtd/rawnand.h>
> +#include "nand_common_spl.h"
>
>  static struct mtd_info *mtd;
>  static struct nand_chip nand_chip;
>
> -static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
> -                            int column, int page_addr)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       u32 timeo, time_start;
> -
> -       /* write out the command to the device */
> -       chip->cmd_ctrl(mtd, command, NAND_CLE);
> -
> -       /* Serially input address */
> -       if (column != -1) {
> -               /* Adjust columns for 16 bit buswidth */
> -               if (chip->options & NAND_BUSWIDTH_16 &&
> -                               !nand_opcode_8bits(command))
> -                       column >>= 1;
> -               chip->cmd_ctrl(mtd, column, NAND_ALE);
> -
> -               /*
> -                * Assume LP NAND here, so use two bytes column address
> -                * but not for CMD_READID and CMD_PARAM, which require
> -                * only one byte column address
> -                */
> -               if (command != NAND_CMD_READID &&
> -                       command != NAND_CMD_PARAM)
> -                       chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
> -       }
> -       if (page_addr != -1) {
> -               chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
> -               chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
> -               /* One more address cycle for devices > 128MiB */
> -               if (chip->chipsize > (128 << 20))
> -                       chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
> -       }
> -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> -
> -       if (command == NAND_CMD_READ0) {
> -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> -       } else if (command == NAND_CMD_RNDOUT) {
> -               /* No ready / busy check necessary */
> -               chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
> -                              NAND_NCE | NAND_CLE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> -                              NAND_NCE);
> -       }
> -
> -       /* wait for nand ready */
> -       ndelay(100);
> -       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> -       time_start = get_timer(0);
> -       while (get_timer(time_start) < timeo) {
> -               if (chip->dev_ready(mtd))
> -                       break;
> -       }
> -}
> -
>  #if defined (CONFIG_SPL_NAND_IDENT)
>
>  /* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
> @@ -175,35 +120,6 @@ static int mxs_flash_ident(struct mtd_info *mtd)
>         return ret;
>  }
>
> -static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -       ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
> -       if (ret < 0) {
> -               printf("read_page failed %d\n", ret);
> -               return -1;
> -       }
> -       return 0;
> -}
> -
> -static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       unsigned int block = offs >> chip->phys_erase_shift;
> -       unsigned int page = offs >> chip->page_shift;
> -
> -       debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
> -             page);
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
> -       memset(chip->oob_poi, 0, mtd->oobsize);
> -       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> -
> -       return chip->oob_poi[0] != 0xff;
> -}
> -
>  /* setup mtd and nand structs and init mxs_nand driver */
>  void nand_init(void)
>  {
> @@ -215,7 +131,7 @@ void nand_init(void)
>         mxs_nand_init_spl(&nand_chip);
>         mtd = nand_to_mtd(&nand_chip);
>         /* set mtd functions */
> -       nand_chip.cmdfunc = mxs_nand_command;
> +       nand_chip.cmdfunc = nand_spl_command_lp;
>         nand_chip.scan_bbt = nand_default_bbt;
>         nand_chip.numchips = 1;
>
> @@ -234,92 +150,6 @@ void nand_init(void)
>         mtd->size = nand_chip.chipsize;
>         nand_chip.scan_bbt(mtd);
>         mxs_nand_setup_ecc(mtd);
> -}
> -
> -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
> -{
> -       unsigned int sz;
> -       unsigned int block, lastblock;
> -       unsigned int page, page_offset;
> -       unsigned int nand_page_per_block;
> -       struct nand_chip *chip;
> -       u8 *page_buf = NULL;
> -
> -       chip = mtd_to_nand(mtd);
> -       if (!chip->numchips)
> -               return -ENODEV;
> -
> -       page_buf = malloc(mtd->writesize);
> -       if (!page_buf)
> -               return -ENOMEM;
> -
> -       /* offs has to be aligned to a page address! */
> -       block = offs / mtd->erasesize;
> -       lastblock = (offs + size - 1) / mtd->erasesize;
> -       page = (offs % mtd->erasesize) / mtd->writesize;
> -       page_offset = offs % mtd->writesize;
> -       nand_page_per_block = mtd->erasesize / mtd->writesize;
> -
> -       while (block <= lastblock) {
> -               if (!is_badblock(mtd, mtd->erasesize * block, 1)) {
> -                       /* Skip bad blocks */
> -                       while (page < nand_page_per_block && size > 0) {
> -                               int curr_page = nand_page_per_block * block + page;
> -
> -                               if (mxs_read_page_ecc(mtd, page_buf, curr_page) < 0) {
> -                                       free(page_buf);
> -                                       return -EIO;
> -                               }
> -
> -                               if (size > (mtd->writesize - page_offset))
> -                                       sz = (mtd->writesize - page_offset);
> -                               else
> -                                       sz = size;
> -
> -                               memcpy(dst, page_buf + page_offset, sz);
> -                               dst += sz;
> -                               size -= sz;
> -                               page_offset = 0;
> -                               page++;
> -                       }
> -
> -                       page = 0;
> -               } else {
> -                       lastblock++;
> -               }
> -
> -               block++;
> -       }
> -
> -       free(page_buf);
> -
> -       return 0;
> -}
> -
> -int nand_default_bbt(struct mtd_info *mtd)
> -{
> -       return 0;
> -}
> -
> -void nand_deselect(void)
> -{
> -}
> -
> -u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> -{
> -       unsigned int block, lastblock;
> -
> -       block = sector / mtd->erasesize;
> -       lastblock = (sector + offs) / mtd->erasesize;
> -
> -       while (block <= lastblock) {
> -               if (is_badblock(mtd, block * mtd->erasesize, 1)) {
> -                       offs += mtd->erasesize;
> -                       lastblock++;
> -               }
> -
> -               block++;
> -       }
>
> -       return offs;
> +       nand_spl_init(&nand_chip);
>  }
> diff --git a/drivers/mtd/nand/raw/nand_common_spl.c b/drivers/mtd/nand/raw/nand_common_spl.c
> new file mode 100644
> index 0000000000..0595fcbc26
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/nand_common_spl.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
I> +/*
> + * Copyright (C) 2022 Amarula Solutions B.V. All rights reserved.
> + *
> + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> + * Author: Weijie Gao <weijie.gao@mediatek.com>
> + */
> +
> +#include <image.h>
> +#include <malloc.h>
> +#include <time.h>
> +#include <linux/sizes.h>
> +#include <linux/delay.h>
> +#include <linux/mtd/rawnand.h>
> +
> +static struct nand_chip *nand;
> +static u8 *buffer;
> +static int nand_valid;
> +

I don't like these static variables. I've looked around the code and I
think we could think about making
some of the improvements that Sean was thinking about. For example
nand_spl_adjust_offset ()
contained in nand_spl_loaders.c and nand_common_spl.c are almost the
same. In this case,
Sitara would also be useful for refactoring and testing it.

> +int nand_spl_init(struct nand_chip *chip)
> +{
> +       struct mtd_info *mtd = nand_to_mtd(chip);
> +
> +       if (!mtd)
> +               return -EINVAL;
> +
> +       buffer = malloc(mtd->writesize);
> +       if (!buffer)
> +               return -ENOMEM;
> +
> +       nand = chip;
> +       nand_valid = 1;
> +
> +       return 0;
> +}
> +
> +static struct nand_chip *nand_spl_get_chip(void)
> +{
> +       return nand;
> +}
> +
> +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> +                        int column, int page_addr)
> +{
> +       register struct nand_chip *chip = mtd_to_nand(mtd);
> +       u32 timeo, time_start;
> +
> +       /* Command latch cycle */
> +       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> +
> +       if (column != -1 || page_addr != -1) {
> +               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> +
> +               /* Serially input address */
> +               if (column != -1) {
> +                       chip->cmd_ctrl(mtd, column, ctrl);
> +                       ctrl &= ~NAND_CTRL_CHANGE;
> +                       if (command != NAND_CMD_READID)
> +                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> +               }
> +               if (page_addr != -1) {
> +                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> +                       chip->cmd_ctrl(mtd, page_addr >> 8,
> +                                      NAND_NCE | NAND_ALE);
> +                       if (chip->options & NAND_ROW_ADDR_3)
> +                               chip->cmd_ctrl(mtd, page_addr >> 16,
> +                                              NAND_NCE | NAND_ALE);
> +               }
> +       }
> +       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> +
> +       /*
> +        * Program and erase have their own busy handlers status, sequential
> +        * in and status need no delay.
> +        */
> +       switch (command) {
> +       case NAND_CMD_STATUS:
> +       case NAND_CMD_READID:
> +       case NAND_CMD_SET_FEATURES:
> +               return;
> +
> +       case NAND_CMD_READ0:
> +               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> +                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> +               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> +                              NAND_NCE | NAND_CTRL_CHANGE);
> +       }
> +
> +       /*
> +        * Apply this short delay always to ensure that we do wait tWB in
> +        * any case on any machine.
> +        */
> +       ndelay(100);
> +
> +       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> +       time_start = get_timer(0);
> +       while (get_timer(time_start) < timeo) {
> +               if (chip->dev_ready(mtd))
> +                       break;
> +       }
> +}
> +
> +static int nand_spl_read_page_hwecc(struct mtd_info *mtd, void *buf,
> +                              unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       int ret;
> +
> +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> +
> +       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> +       if (ret < 0 || ret > chip->ecc.strength)
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static int nand_spl_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> +                             unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       int ret;
> +
> +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> +
> +       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> +       if (ret < 0)
> +               return -1;
> +
> +       if (len > mtd->oobsize)
> +               len = mtd->oobsize;
> +
> +       memcpy(buf, chip->oob_poi, len);
> +
> +       return 0;
> +}
> +
> +static int nand_spl_check_bad_block(struct mtd_info *mtd, unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       u32 pages_per_block, i = 0;
> +       int ret;
> +       u8 bad;
> +
> +       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> +
> +       /* Read from first/last page(s) if necessary */
> +       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> +               page += pages_per_block - 1;
> +               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> +                       page--;
> +       }
> +
> +       do {
> +               ret = nand_spl_read_oob_hwecc(mtd, &bad, 1, page);
> +               if (ret)
> +                       return ret;
> +
> +               ret = bad != 0xFF;
> +
> +               i++;
> +               page++;
> +       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> +
> +       return ret;
> +}
> +
> +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> +{
> +       struct nand_chip *chip = nand_spl_get_chip();
> +       struct mtd_info *mtd = &chip->mtd;
> +       u32 addr, col, page, chksz;
> +       bool check_bad = true;
> +
> +       if (!nand_valid)
> +               return -ENODEV;
> +
> +       while (size) {
> +               if (check_bad || !(offs & mtd->erasesize_mask)) {
> +                       addr = offs & (~mtd->erasesize_mask);
> +                       page = addr >> mtd->writesize_shift;
> +                       if (nand_spl_check_bad_block(mtd, page)) {
> +                               /* Skip bad block */
> +                               if (addr >= mtd->size - mtd->erasesize)
> +                                       return -ENXIO;
> +
> +                               offs += mtd->erasesize;
> +                               continue;
> +                       }
> +
> +                       check_bad = false;
> +               }
> +
> +               col = offs & mtd->writesize_mask;
> +               page = offs >> mtd->writesize_shift;
> +               chksz = min(mtd->writesize - col, (uint32_t)size);
> +
> +               if (unlikely(chksz < mtd->writesize)) {
> +                       /* Not reading a full page */
> +                       if (nand_spl_read_page_hwecc(mtd, buffer, page))
> +                               return -EIO;
> +
> +                       memcpy(dest, buffer + col, chksz);
> +               } else {
> +                       if (nand_spl_read_page_hwecc(mtd, dest, page))
> +                               return -EIO;
> +               }
> +
> +               dest += chksz;
> +               offs += chksz;
> +               size -= chksz;
> +       }
> +
> +       return 0;
> +}
> +
> +int nand_default_bbt(struct mtd_info *mtd)
> +{
> +       return 0;
> +}
> +
> +void nand_deselect(void)
> +{
> +}
> +
> +u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> +{
> +       struct nand_chip *chip = nand_spl_get_chip();
> +       struct mtd_info *mtd = nand_to_mtd(chip);
> +       unsigned int block, lastblock;
> +
> +       block = sector / mtd->erasesize;
> +       lastblock = (sector + offs) / mtd->erasesize;
> +
> +       while (block <= lastblock) {
> +               if (nand_spl_check_bad_block(mtd, block * mtd->erasesize)) {
> +                       offs += mtd->erasesize;
> +                       lastblock++;
> +               }
> +
> +               block++;
> +       }
> +
> +       return offs;
> +}
> diff --git a/drivers/mtd/nand/raw/nand_common_spl.h b/drivers/mtd/nand/raw/nand_common_spl.h
> new file mode 100644
> index 0000000000..36bf63b230
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/nand_common_spl.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 Amarula Solution B.V. All rights reserved.
> + *
> + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> + */
> +
> +#ifndef _NAND_COMMON_SPL_H
> +#define _NAND_COMMON_SPL_H
> +
> +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> +                        int column, int page_addr);
> +int nand_spl_init(struct nand_chip *chip);
> +
> +#endif /* _NAND_COMMON_SPL_H_ */
> --
> 2.34.1
>

It is difficult to do a safe review. Better than test it on bsh.
Dario
Michael Nazzareno Trimarchi July 27, 2022, 1:55 p.m. UTC | #2
Hi Dario

On Wed, Jul 27, 2022 at 3:50 PM Dario Binacchi
<dario.binacchi@amarulasolutions.com> wrote:
>
> Hi Michael,
>
> On Wed, Jul 27, 2022 at 11:38 AM Michael Trimarchi
> <michael@amarulasolutions.com> wrote:
> >
> > Avoid code duplication accross drivers but make them use
> > the same implementation. Create nand_common_spl to implement
> > the part that does not depend on nand chipset. Apply to
> >
> > - mxs nand spl driver
> > - mt7621 spl driver
> >
> > The mt7621 now as side effect implement nand_spl_adjust_offset, that
> > implements bad block handle for complex image like fitImage
> >
> > Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
> > ---
> >  drivers/mtd/nand/raw/Makefile          |   4 +-
> >  drivers/mtd/nand/raw/mt7621_nand_spl.c | 188 +------------------
> >  drivers/mtd/nand/raw/mxs_nand_spl.c    | 176 +-----------------
> >  drivers/mtd/nand/raw/nand_common_spl.c | 245 +++++++++++++++++++++++++
> >  drivers/mtd/nand/raw/nand_common_spl.h |  15 ++
> >  5 files changed, 269 insertions(+), 359 deletions(-)
> >  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.c
> >  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.h
> >
> > diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> > index a398aa9d88..82ddb2b5d8 100644
> > --- a/drivers/mtd/nand/raw/Makefile
> > +++ b/drivers/mtd/nand/raw/Makefile
> > @@ -87,8 +87,8 @@ else  # minimal SPL drivers
> >  obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
> >  obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
> >  obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
> > -obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
> > +obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o nand_common_spl.o
> >  obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
> > -obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
> > +obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o nand_common_spl.o
> >
> >  endif # drivers
> > diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> > index 114fc8b7ce..254e14a553 100644
> > --- a/drivers/mtd/nand/raw/mt7621_nand_spl.c
> > +++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> > @@ -10,187 +10,13 @@
> >  #include <linux/sizes.h>
> >  #include <linux/delay.h>
> >  #include <linux/mtd/rawnand.h>
> > +#include "nand_common_spl.h"
> >  #include "mt7621_nand.h"
> > +#include "nand_common_spl.h"
>
> You already included "nand_common_spl.h"
>

Yes sorry

> >
> >  static struct mt7621_nfc nfc_dev;
> > -static u8 *buffer;
> >  static int nand_valid;
> >
> > -static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
> > -                           int column, int page_addr)
> > -{
> > -       register struct nand_chip *chip = mtd_to_nand(mtd);
> > -
> > -       /* Command latch cycle */
> > -       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> > -
> > -       if (column != -1 || page_addr != -1) {
> > -               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> > -
> > -               /* Serially input address */
> > -               if (column != -1) {
> > -                       chip->cmd_ctrl(mtd, column, ctrl);
> > -                       ctrl &= ~NAND_CTRL_CHANGE;
> > -                       if (command != NAND_CMD_READID)
> > -                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> > -               }
> > -               if (page_addr != -1) {
> > -                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> > -                       chip->cmd_ctrl(mtd, page_addr >> 8,
> > -                                      NAND_NCE | NAND_ALE);
> > -                       if (chip->options & NAND_ROW_ADDR_3)
> > -                               chip->cmd_ctrl(mtd, page_addr >> 16,
> > -                                              NAND_NCE | NAND_ALE);
> > -               }
> > -       }
> > -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> > -
> > -       /*
> > -        * Program and erase have their own busy handlers status, sequential
> > -        * in and status need no delay.
> > -        */
> > -       switch (command) {
> > -       case NAND_CMD_STATUS:
> > -       case NAND_CMD_READID:
> > -       case NAND_CMD_SET_FEATURES:
> > -               return;
> > -
> > -       case NAND_CMD_READ0:
> > -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> > -                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> > -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> > -                              NAND_NCE | NAND_CTRL_CHANGE);
> > -       }
> > -
> > -       /*
> > -        * Apply this short delay always to ensure that we do wait tWB in
> > -        * any case on any machine.
> > -        */
> > -       ndelay(100);
> > -
> > -       nand_wait_ready(mtd);
> > -}
> > -
> > -static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
> > -                              unsigned int page)
> > -{
> > -       struct nand_chip *chip = mtd_to_nand(mtd);
> > -       int ret;
> > -
> > -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> > -
> > -       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> > -       if (ret < 0 || ret > chip->ecc.strength)
> > -               return -1;
> > -
> > -       return 0;
> > -}
> > -
> > -static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> > -                             unsigned int page)
> > -{
> > -       struct nand_chip *chip = mtd_to_nand(mtd);
> > -       int ret;
> > -
> > -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> > -
> > -       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> > -       if (ret < 0)
> > -               return -1;
> > -
> > -       if (len > mtd->oobsize)
> > -               len = mtd->oobsize;
> > -
> > -       memcpy(buf, chip->oob_poi, len);
> > -
> > -       return 0;
> > -}
> > -
> > -static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
> > -{
> > -       struct nand_chip *chip = mtd_to_nand(mtd);
> > -       u32 pages_per_block, i = 0;
> > -       int ret;
> > -       u8 bad;
> > -
> > -       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> > -
> > -       /* Read from first/last page(s) if necessary */
> > -       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> > -               page += pages_per_block - 1;
> > -               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> > -                       page--;
> > -       }
> > -
> > -       do {
> > -               ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
> > -               if (ret)
> > -                       return ret;
> > -
> > -               ret = bad != 0xFF;
> > -
> > -               i++;
> > -               page++;
> > -       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> > -
> > -       return ret;
> > -}
> > -
> > -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> > -{
> > -       struct mt7621_nfc *nfc = &nfc_dev;
> > -       struct nand_chip *chip = &nfc->nand;
> > -       struct mtd_info *mtd = &chip->mtd;
> > -       u32 addr, col, page, chksz;
> > -       bool check_bad = true;
> > -
> > -       if (!nand_valid)
> > -               return -ENODEV;
> > -
> > -       while (size) {
> > -               if (check_bad || !(offs & mtd->erasesize_mask)) {
> > -                       addr = offs & (~mtd->erasesize_mask);
> > -                       page = addr >> mtd->writesize_shift;
> > -                       if (nfc_check_bad_block(mtd, page)) {
> > -                               /* Skip bad block */
> > -                               if (addr >= mtd->size - mtd->erasesize)
> > -                                       return -1;
> > -
> > -                               offs += mtd->erasesize;
> > -                               continue;
> > -                       }
> > -
> > -                       check_bad = false;
> > -               }
> > -
> > -               col = offs & mtd->writesize_mask;
> > -               page = offs >> mtd->writesize_shift;
> > -               chksz = min(mtd->writesize - col, (uint32_t)size);
> > -
> > -               if (unlikely(chksz < mtd->writesize)) {
> > -                       /* Not reading a full page */
> > -                       if (nfc_read_page_hwecc(mtd, buffer, page))
> > -                               return -1;
> > -
> > -                       memcpy(dest, buffer + col, chksz);
> > -               } else {
> > -                       if (nfc_read_page_hwecc(mtd, dest, page))
> > -                               return -1;
> > -               }
> > -
> > -               dest += chksz;
> > -               offs += chksz;
> > -               size -= chksz;
> > -       }
> > -
> > -       return 0;
> > -}
> > -
> > -int nand_default_bbt(struct mtd_info *mtd)
> > -{
> > -       return 0;
> > -}
> > -
> >  unsigned long nand_size(void)
> >  {
> >         if (!nand_valid)
> > @@ -203,10 +29,6 @@ unsigned long nand_size(void)
> >         return SZ_2G;
> >  }
> >
> > -void nand_deselect(void)
> > -{
> > -}
> > -
> >  void nand_init(void)
> >  {
> >         struct mtd_info *mtd;
> > @@ -219,7 +41,7 @@ void nand_init(void)
> >
> >         chip = &nfc_dev.nand;
> >         mtd = &chip->mtd;
> > -       chip->cmdfunc = nand_command_lp;
> > +       chip->cmdfunc = nand_spl_command_lp;
> >
> >         if (mt7621_nfc_spl_post_init(&nfc_dev))
> >                 return;
> > @@ -229,9 +51,7 @@ void nand_init(void)
> >         mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
> >         mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
> >
> > -       buffer = malloc(mtd->writesize);
> > -       if (!buffer)
> > -               return;
> > +       nand_spl_init(chip);
> >
> >         nand_valid = 1;
> >  }
> > diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
> > index c7ea09e2f9..5cff6020c4 100644
> > --- a/drivers/mtd/nand/raw/mxs_nand_spl.c
> > +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
> > @@ -14,66 +14,11 @@
> >  #include <linux/delay.h>
> >  #include <linux/err.h>
> >  #include <linux/mtd/rawnand.h>
> > +#include "nand_common_spl.h"
> >
> >  static struct mtd_info *mtd;
> >  static struct nand_chip nand_chip;
> >
> > -static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
> > -                            int column, int page_addr)
> > -{
> > -       register struct nand_chip *chip = mtd_to_nand(mtd);
> > -       u32 timeo, time_start;
> > -
> > -       /* write out the command to the device */
> > -       chip->cmd_ctrl(mtd, command, NAND_CLE);
> > -
> > -       /* Serially input address */
> > -       if (column != -1) {
> > -               /* Adjust columns for 16 bit buswidth */
> > -               if (chip->options & NAND_BUSWIDTH_16 &&
> > -                               !nand_opcode_8bits(command))
> > -                       column >>= 1;
> > -               chip->cmd_ctrl(mtd, column, NAND_ALE);
> > -
> > -               /*
> > -                * Assume LP NAND here, so use two bytes column address
> > -                * but not for CMD_READID and CMD_PARAM, which require
> > -                * only one byte column address
> > -                */
> > -               if (command != NAND_CMD_READID &&
> > -                       command != NAND_CMD_PARAM)
> > -                       chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
> > -       }
> > -       if (page_addr != -1) {
> > -               chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
> > -               chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
> > -               /* One more address cycle for devices > 128MiB */
> > -               if (chip->chipsize > (128 << 20))
> > -                       chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
> > -       }
> > -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> > -
> > -       if (command == NAND_CMD_READ0) {
> > -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
> > -               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> > -       } else if (command == NAND_CMD_RNDOUT) {
> > -               /* No ready / busy check necessary */
> > -               chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
> > -                              NAND_NCE | NAND_CLE);
> > -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> > -                              NAND_NCE);
> > -       }
> > -
> > -       /* wait for nand ready */
> > -       ndelay(100);
> > -       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> > -       time_start = get_timer(0);
> > -       while (get_timer(time_start) < timeo) {
> > -               if (chip->dev_ready(mtd))
> > -                       break;
> > -       }
> > -}
> > -
> >  #if defined (CONFIG_SPL_NAND_IDENT)
> >
> >  /* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
> > @@ -175,35 +120,6 @@ static int mxs_flash_ident(struct mtd_info *mtd)
> >         return ret;
> >  }
> >
> > -static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
> > -{
> > -       register struct nand_chip *chip = mtd_to_nand(mtd);
> > -       int ret;
> > -
> > -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> > -       ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
> > -       if (ret < 0) {
> > -               printf("read_page failed %d\n", ret);
> > -               return -1;
> > -       }
> > -       return 0;
> > -}
> > -
> > -static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
> > -{
> > -       register struct nand_chip *chip = mtd_to_nand(mtd);
> > -       unsigned int block = offs >> chip->phys_erase_shift;
> > -       unsigned int page = offs >> chip->page_shift;
> > -
> > -       debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
> > -             page);
> > -       chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
> > -       memset(chip->oob_poi, 0, mtd->oobsize);
> > -       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> > -
> > -       return chip->oob_poi[0] != 0xff;
> > -}
> > -
> >  /* setup mtd and nand structs and init mxs_nand driver */
> >  void nand_init(void)
> >  {
> > @@ -215,7 +131,7 @@ void nand_init(void)
> >         mxs_nand_init_spl(&nand_chip);
> >         mtd = nand_to_mtd(&nand_chip);
> >         /* set mtd functions */
> > -       nand_chip.cmdfunc = mxs_nand_command;
> > +       nand_chip.cmdfunc = nand_spl_command_lp;
> >         nand_chip.scan_bbt = nand_default_bbt;
> >         nand_chip.numchips = 1;
> >
> > @@ -234,92 +150,6 @@ void nand_init(void)
> >         mtd->size = nand_chip.chipsize;
> >         nand_chip.scan_bbt(mtd);
> >         mxs_nand_setup_ecc(mtd);
> > -}
> > -
> > -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
> > -{
> > -       unsigned int sz;
> > -       unsigned int block, lastblock;
> > -       unsigned int page, page_offset;
> > -       unsigned int nand_page_per_block;
> > -       struct nand_chip *chip;
> > -       u8 *page_buf = NULL;
> > -
> > -       chip = mtd_to_nand(mtd);
> > -       if (!chip->numchips)
> > -               return -ENODEV;
> > -
> > -       page_buf = malloc(mtd->writesize);
> > -       if (!page_buf)
> > -               return -ENOMEM;
> > -
> > -       /* offs has to be aligned to a page address! */
> > -       block = offs / mtd->erasesize;
> > -       lastblock = (offs + size - 1) / mtd->erasesize;
> > -       page = (offs % mtd->erasesize) / mtd->writesize;
> > -       page_offset = offs % mtd->writesize;
> > -       nand_page_per_block = mtd->erasesize / mtd->writesize;
> > -
> > -       while (block <= lastblock) {
> > -               if (!is_badblock(mtd, mtd->erasesize * block, 1)) {
> > -                       /* Skip bad blocks */
> > -                       while (page < nand_page_per_block && size > 0) {
> > -                               int curr_page = nand_page_per_block * block + page;
> > -
> > -                               if (mxs_read_page_ecc(mtd, page_buf, curr_page) < 0) {
> > -                                       free(page_buf);
> > -                                       return -EIO;
> > -                               }
> > -
> > -                               if (size > (mtd->writesize - page_offset))
> > -                                       sz = (mtd->writesize - page_offset);
> > -                               else
> > -                                       sz = size;
> > -
> > -                               memcpy(dst, page_buf + page_offset, sz);
> > -                               dst += sz;
> > -                               size -= sz;
> > -                               page_offset = 0;
> > -                               page++;
> > -                       }
> > -
> > -                       page = 0;
> > -               } else {
> > -                       lastblock++;
> > -               }
> > -
> > -               block++;
> > -       }
> > -
> > -       free(page_buf);
> > -
> > -       return 0;
> > -}
> > -
> > -int nand_default_bbt(struct mtd_info *mtd)
> > -{
> > -       return 0;
> > -}
> > -
> > -void nand_deselect(void)
> > -{
> > -}
> > -
> > -u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> > -{
> > -       unsigned int block, lastblock;
> > -
> > -       block = sector / mtd->erasesize;
> > -       lastblock = (sector + offs) / mtd->erasesize;
> > -
> > -       while (block <= lastblock) {
> > -               if (is_badblock(mtd, block * mtd->erasesize, 1)) {
> > -                       offs += mtd->erasesize;
> > -                       lastblock++;
> > -               }
> > -
> > -               block++;
> > -       }
> >
> > -       return offs;
> > +       nand_spl_init(&nand_chip);
> >  }
> > diff --git a/drivers/mtd/nand/raw/nand_common_spl.c b/drivers/mtd/nand/raw/nand_common_spl.c
> > new file mode 100644
> > index 0000000000..0595fcbc26
> > --- /dev/null
> > +++ b/drivers/mtd/nand/raw/nand_common_spl.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0
> I> +/*
> > + * Copyright (C) 2022 Amarula Solutions B.V. All rights reserved.
> > + *
> > + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> > + * Author: Weijie Gao <weijie.gao@mediatek.com>
> > + */
> > +
> > +#include <image.h>
> > +#include <malloc.h>
> > +#include <time.h>
> > +#include <linux/sizes.h>
> > +#include <linux/delay.h>
> > +#include <linux/mtd/rawnand.h>
> > +
> > +static struct nand_chip *nand;
> > +static u8 *buffer;
> > +static int nand_valid;
> > +
>
> I don't like these static variables. I've looked around the code and I
> think we could think about making

The reason of the variable is to reuse as much the code of the other
architecture
that was already tested.

> some of the improvements that Sean was thinking about. For example
> nand_spl_adjust_offset ()
> contained in nand_spl_loaders.c and nand_common_spl.c are almost the
> same. In this case,
> Sitara would also be useful for refactoring and testing it.

I would like to keep things in step and able to be tested. If you want
to merge them, I think
this is can be done in separated patch. You are welcome to merge them
all. I can not do proper testing
but I can prepare a new patch to merge them all

Michael

>
> > +int nand_spl_init(struct nand_chip *chip)
> > +{
> > +       struct mtd_info *mtd = nand_to_mtd(chip);
> > +
> > +       if (!mtd)
> > +               return -EINVAL;
> > +
> > +       buffer = malloc(mtd->writesize);
> > +       if (!buffer)
> > +               return -ENOMEM;
> > +
> > +       nand = chip;
> > +       nand_valid = 1;
> > +
> > +       return 0;
> > +}
> > +
> > +static struct nand_chip *nand_spl_get_chip(void)
> > +{
> > +       return nand;
> > +}
> > +
> > +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> > +                        int column, int page_addr)
> > +{
> > +       register struct nand_chip *chip = mtd_to_nand(mtd);
> > +       u32 timeo, time_start;
> > +
> > +       /* Command latch cycle */
> > +       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> > +
> > +       if (column != -1 || page_addr != -1) {
> > +               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> > +
> > +               /* Serially input address */
> > +               if (column != -1) {
> > +                       chip->cmd_ctrl(mtd, column, ctrl);
> > +                       ctrl &= ~NAND_CTRL_CHANGE;
> > +                       if (command != NAND_CMD_READID)
> > +                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> > +               }
> > +               if (page_addr != -1) {
> > +                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> > +                       chip->cmd_ctrl(mtd, page_addr >> 8,
> > +                                      NAND_NCE | NAND_ALE);
> > +                       if (chip->options & NAND_ROW_ADDR_3)
> > +                               chip->cmd_ctrl(mtd, page_addr >> 16,
> > +                                              NAND_NCE | NAND_ALE);
> > +               }
> > +       }
> > +       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> > +
> > +       /*
> > +        * Program and erase have their own busy handlers status, sequential
> > +        * in and status need no delay.
> > +        */
> > +       switch (command) {
> > +       case NAND_CMD_STATUS:
> > +       case NAND_CMD_READID:
> > +       case NAND_CMD_SET_FEATURES:
> > +               return;
> > +
> > +       case NAND_CMD_READ0:
> > +               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> > +                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> > +               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> > +                              NAND_NCE | NAND_CTRL_CHANGE);
> > +       }
> > +
> > +       /*
> > +        * Apply this short delay always to ensure that we do wait tWB in
> > +        * any case on any machine.
> > +        */
> > +       ndelay(100);
> > +
> > +       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> > +       time_start = get_timer(0);
> > +       while (get_timer(time_start) < timeo) {
> > +               if (chip->dev_ready(mtd))
> > +                       break;
> > +       }
> > +}
> > +
> > +static int nand_spl_read_page_hwecc(struct mtd_info *mtd, void *buf,
> > +                              unsigned int page)
> > +{
> > +       struct nand_chip *chip = mtd_to_nand(mtd);
> > +       int ret;
> > +
> > +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> > +
> > +       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> > +       if (ret < 0 || ret > chip->ecc.strength)
> > +               return -1;
> > +
> > +       return 0;
> > +}
> > +
> > +static int nand_spl_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> > +                             unsigned int page)
> > +{
> > +       struct nand_chip *chip = mtd_to_nand(mtd);
> > +       int ret;
> > +
> > +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> > +
> > +       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> > +       if (ret < 0)
> > +               return -1;
> > +
> > +       if (len > mtd->oobsize)
> > +               len = mtd->oobsize;
> > +
> > +       memcpy(buf, chip->oob_poi, len);
> > +
> > +       return 0;
> > +}
> > +
> > +static int nand_spl_check_bad_block(struct mtd_info *mtd, unsigned int page)
> > +{
> > +       struct nand_chip *chip = mtd_to_nand(mtd);
> > +       u32 pages_per_block, i = 0;
> > +       int ret;
> > +       u8 bad;
> > +
> > +       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> > +
> > +       /* Read from first/last page(s) if necessary */
> > +       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> > +               page += pages_per_block - 1;
> > +               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> > +                       page--;
> > +       }
> > +
> > +       do {
> > +               ret = nand_spl_read_oob_hwecc(mtd, &bad, 1, page);
> > +               if (ret)
> > +                       return ret;
> > +
> > +               ret = bad != 0xFF;
> > +
> > +               i++;
> > +               page++;
> > +       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> > +
> > +       return ret;
> > +}
> > +
> > +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> > +{
> > +       struct nand_chip *chip = nand_spl_get_chip();
> > +       struct mtd_info *mtd = &chip->mtd;
> > +       u32 addr, col, page, chksz;
> > +       bool check_bad = true;
> > +
> > +       if (!nand_valid)
> > +               return -ENODEV;
> > +
> > +       while (size) {
> > +               if (check_bad || !(offs & mtd->erasesize_mask)) {
> > +                       addr = offs & (~mtd->erasesize_mask);
> > +                       page = addr >> mtd->writesize_shift;
> > +                       if (nand_spl_check_bad_block(mtd, page)) {
> > +                               /* Skip bad block */
> > +                               if (addr >= mtd->size - mtd->erasesize)
> > +                                       return -ENXIO;
> > +
> > +                               offs += mtd->erasesize;
> > +                               continue;
> > +                       }
> > +
> > +                       check_bad = false;
> > +               }
> > +
> > +               col = offs & mtd->writesize_mask;
> > +               page = offs >> mtd->writesize_shift;
> > +               chksz = min(mtd->writesize - col, (uint32_t)size);
> > +
> > +               if (unlikely(chksz < mtd->writesize)) {
> > +                       /* Not reading a full page */
> > +                       if (nand_spl_read_page_hwecc(mtd, buffer, page))
> > +                               return -EIO;
> > +
> > +                       memcpy(dest, buffer + col, chksz);
> > +               } else {
> > +                       if (nand_spl_read_page_hwecc(mtd, dest, page))
> > +                               return -EIO;
> > +               }
> > +
> > +               dest += chksz;
> > +               offs += chksz;
> > +               size -= chksz;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +int nand_default_bbt(struct mtd_info *mtd)
> > +{
> > +       return 0;
> > +}
> > +
> > +void nand_deselect(void)
> > +{
> > +}
> > +
> > +u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> > +{
> > +       struct nand_chip *chip = nand_spl_get_chip();
> > +       struct mtd_info *mtd = nand_to_mtd(chip);
> > +       unsigned int block, lastblock;
> > +
> > +       block = sector / mtd->erasesize;
> > +       lastblock = (sector + offs) / mtd->erasesize;
> > +
> > +       while (block <= lastblock) {
> > +               if (nand_spl_check_bad_block(mtd, block * mtd->erasesize)) {
> > +                       offs += mtd->erasesize;
> > +                       lastblock++;
> > +               }
> > +
> > +               block++;
> > +       }
> > +
> > +       return offs;
> > +}
> > diff --git a/drivers/mtd/nand/raw/nand_common_spl.h b/drivers/mtd/nand/raw/nand_common_spl.h
> > new file mode 100644
> > index 0000000000..36bf63b230
> > --- /dev/null
> > +++ b/drivers/mtd/nand/raw/nand_common_spl.h
> > @@ -0,0 +1,15 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2022 Amarula Solution B.V. All rights reserved.
> > + *
> > + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> > + */
> > +
> > +#ifndef _NAND_COMMON_SPL_H
> > +#define _NAND_COMMON_SPL_H
> > +
> > +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> > +                        int column, int page_addr);
> > +int nand_spl_init(struct nand_chip *chip);
> > +
> > +#endif /* _NAND_COMMON_SPL_H_ */
> > --
> > 2.34.1
> >
>
> It is difficult to do a safe review. Better than test it on bsh.

Ok

Michael

> Dario
>
> --
>
> Dario Binacchi
>
> Embedded Linux Developer
>
> dario.binacchi@amarulasolutions.com
>
> __________________________________
>
>
> Amarula Solutions SRL
>
> Via Le Canevare 30, 31100 Treviso, Veneto, IT
>
> T. +39 042 243 5310
> info@amarulasolutions.com
>
> www.amarulasolutions.com
Dario Binacchi July 27, 2022, 2:22 p.m. UTC | #3
Hi Michael,

On Wed, Jul 27, 2022 at 11:38 AM Michael Trimarchi
<michael@amarulasolutions.com> wrote:
>
> Avoid code duplication accross drivers but make them use
> the same implementation. Create nand_common_spl to implement
> the part that does not depend on nand chipset. Apply to
>
> - mxs nand spl driver
> - mt7621 spl driver
>
> The mt7621 now as side effect implement nand_spl_adjust_offset, that
> implements bad block handle for complex image like fitImage
>
> Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
> ---
>  drivers/mtd/nand/raw/Makefile          |   4 +-
>  drivers/mtd/nand/raw/mt7621_nand_spl.c | 188 +------------------
>  drivers/mtd/nand/raw/mxs_nand_spl.c    | 176 +-----------------
>  drivers/mtd/nand/raw/nand_common_spl.c | 245 +++++++++++++++++++++++++
>  drivers/mtd/nand/raw/nand_common_spl.h |  15 ++
>  5 files changed, 269 insertions(+), 359 deletions(-)
>  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.c
>  create mode 100644 drivers/mtd/nand/raw/nand_common_spl.h
>
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index a398aa9d88..82ddb2b5d8 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -87,8 +87,8 @@ else  # minimal SPL drivers
>  obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
>  obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
>  obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
> -obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
> +obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o nand_common_spl.o
>  obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
> -obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
> +obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o nand_common_spl.o
>
>  endif # drivers
> diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> index 114fc8b7ce..254e14a553 100644
> --- a/drivers/mtd/nand/raw/mt7621_nand_spl.c
> +++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
> @@ -10,187 +10,13 @@
>  #include <linux/sizes.h>
>  #include <linux/delay.h>
>  #include <linux/mtd/rawnand.h>
> +#include "nand_common_spl.h"
>  #include "mt7621_nand.h"
> +#include "nand_common_spl.h"
>
>  static struct mt7621_nfc nfc_dev;
> -static u8 *buffer;
>  static int nand_valid;
>
> -static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
> -                           int column, int page_addr)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -
> -       /* Command latch cycle */
> -       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> -
> -       if (column != -1 || page_addr != -1) {
> -               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> -
> -               /* Serially input address */
> -               if (column != -1) {
> -                       chip->cmd_ctrl(mtd, column, ctrl);
> -                       ctrl &= ~NAND_CTRL_CHANGE;
> -                       if (command != NAND_CMD_READID)
> -                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> -               }
> -               if (page_addr != -1) {
> -                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> -                       chip->cmd_ctrl(mtd, page_addr >> 8,
> -                                      NAND_NCE | NAND_ALE);
> -                       if (chip->options & NAND_ROW_ADDR_3)
> -                               chip->cmd_ctrl(mtd, page_addr >> 16,
> -                                              NAND_NCE | NAND_ALE);
> -               }
> -       }
> -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> -
> -       /*
> -        * Program and erase have their own busy handlers status, sequential
> -        * in and status need no delay.
> -        */
> -       switch (command) {
> -       case NAND_CMD_STATUS:
> -       case NAND_CMD_READID:
> -       case NAND_CMD_SET_FEATURES:
> -               return;
> -
> -       case NAND_CMD_READ0:
> -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> -                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> -                              NAND_NCE | NAND_CTRL_CHANGE);
> -       }
> -
> -       /*
> -        * Apply this short delay always to ensure that we do wait tWB in
> -        * any case on any machine.
> -        */
> -       ndelay(100);
> -
> -       nand_wait_ready(mtd);
> -}
> -
> -static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
> -                              unsigned int page)

Alignment should match open parenthesis

> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -
> -       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> -       if (ret < 0 || ret > chip->ecc.strength)
> -               return -1;
> -
> -       return 0;
> -}
> -
> -static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> -                             unsigned int page)

Alignment should match open parenthesis

> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -
> -       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> -       if (ret < 0)
> -               return -1;
> -
> -       if (len > mtd->oobsize)
> -               len = mtd->oobsize;
> -
> -       memcpy(buf, chip->oob_poi, len);
> -
> -       return 0;
> -}
> -
> -static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
> -{
> -       struct nand_chip *chip = mtd_to_nand(mtd);
> -       u32 pages_per_block, i = 0;
> -       int ret;
> -       u8 bad;
> -
> -       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> -
> -       /* Read from first/last page(s) if necessary */
> -       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> -               page += pages_per_block - 1;
> -               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> -                       page--;
> -       }
> -
> -       do {
> -               ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
> -               if (ret)
> -                       return ret;
> -
> -               ret = bad != 0xFF;
> -
> -               i++;
> -               page++;
> -       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> -
> -       return ret;
> -}
> -
> -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)

Prefer kernel type 'u32' over 'uint32_t'
These was the warnings raised by patman

Dario

> -{
> -       struct mt7621_nfc *nfc = &nfc_dev;
> -       struct nand_chip *chip = &nfc->nand;
> -       struct mtd_info *mtd = &chip->mtd;
> -       u32 addr, col, page, chksz;
> -       bool check_bad = true;
> -
> -       if (!nand_valid)
> -               return -ENODEV;
> -
> -       while (size) {
> -               if (check_bad || !(offs & mtd->erasesize_mask)) {
> -                       addr = offs & (~mtd->erasesize_mask);
> -                       page = addr >> mtd->writesize_shift;
> -                       if (nfc_check_bad_block(mtd, page)) {
> -                               /* Skip bad block */
> -                               if (addr >= mtd->size - mtd->erasesize)
> -                                       return -1;
> -
> -                               offs += mtd->erasesize;
> -                               continue;
> -                       }
> -
> -                       check_bad = false;
> -               }
> -
> -               col = offs & mtd->writesize_mask;
> -               page = offs >> mtd->writesize_shift;
> -               chksz = min(mtd->writesize - col, (uint32_t)size);
> -
> -               if (unlikely(chksz < mtd->writesize)) {
> -                       /* Not reading a full page */
> -                       if (nfc_read_page_hwecc(mtd, buffer, page))
> -                               return -1;
> -
> -                       memcpy(dest, buffer + col, chksz);
> -               } else {
> -                       if (nfc_read_page_hwecc(mtd, dest, page))
> -                               return -1;
> -               }
> -
> -               dest += chksz;
> -               offs += chksz;
> -               size -= chksz;
> -       }
> -
> -       return 0;
> -}
> -
> -int nand_default_bbt(struct mtd_info *mtd)
> -{
> -       return 0;
> -}
> -
>  unsigned long nand_size(void)
>  {
>         if (!nand_valid)
> @@ -203,10 +29,6 @@ unsigned long nand_size(void)
>         return SZ_2G;
>  }
>
> -void nand_deselect(void)
> -{
> -}
> -
>  void nand_init(void)
>  {
>         struct mtd_info *mtd;
> @@ -219,7 +41,7 @@ void nand_init(void)
>
>         chip = &nfc_dev.nand;
>         mtd = &chip->mtd;
> -       chip->cmdfunc = nand_command_lp;
> +       chip->cmdfunc = nand_spl_command_lp;
>
>         if (mt7621_nfc_spl_post_init(&nfc_dev))
>                 return;
> @@ -229,9 +51,7 @@ void nand_init(void)
>         mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
>         mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
>
> -       buffer = malloc(mtd->writesize);
> -       if (!buffer)
> -               return;
> +       nand_spl_init(chip);
>
>         nand_valid = 1;
>  }
> diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
> index c7ea09e2f9..5cff6020c4 100644
> --- a/drivers/mtd/nand/raw/mxs_nand_spl.c
> +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
> @@ -14,66 +14,11 @@
>  #include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/mtd/rawnand.h>
> +#include "nand_common_spl.h"
>
>  static struct mtd_info *mtd;
>  static struct nand_chip nand_chip;
>
> -static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
> -                            int column, int page_addr)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       u32 timeo, time_start;
> -
> -       /* write out the command to the device */
> -       chip->cmd_ctrl(mtd, command, NAND_CLE);
> -
> -       /* Serially input address */
> -       if (column != -1) {
> -               /* Adjust columns for 16 bit buswidth */
> -               if (chip->options & NAND_BUSWIDTH_16 &&
> -                               !nand_opcode_8bits(command))
> -                       column >>= 1;
> -               chip->cmd_ctrl(mtd, column, NAND_ALE);
> -
> -               /*
> -                * Assume LP NAND here, so use two bytes column address
> -                * but not for CMD_READID and CMD_PARAM, which require
> -                * only one byte column address
> -                */
> -               if (command != NAND_CMD_READID &&
> -                       command != NAND_CMD_PARAM)
> -                       chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
> -       }
> -       if (page_addr != -1) {
> -               chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
> -               chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
> -               /* One more address cycle for devices > 128MiB */
> -               if (chip->chipsize > (128 << 20))
> -                       chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
> -       }
> -       chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> -
> -       if (command == NAND_CMD_READ0) {
> -               chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
> -       } else if (command == NAND_CMD_RNDOUT) {
> -               /* No ready / busy check necessary */
> -               chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
> -                              NAND_NCE | NAND_CLE);
> -               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> -                              NAND_NCE);
> -       }
> -
> -       /* wait for nand ready */
> -       ndelay(100);
> -       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> -       time_start = get_timer(0);
> -       while (get_timer(time_start) < timeo) {
> -               if (chip->dev_ready(mtd))
> -                       break;
> -       }
> -}
> -
>  #if defined (CONFIG_SPL_NAND_IDENT)
>
>  /* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
> @@ -175,35 +120,6 @@ static int mxs_flash_ident(struct mtd_info *mtd)
>         return ret;
>  }
>
> -static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       int ret;
> -
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> -       ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
> -       if (ret < 0) {
> -               printf("read_page failed %d\n", ret);
> -               return -1;
> -       }
> -       return 0;
> -}
> -
> -static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
> -{
> -       register struct nand_chip *chip = mtd_to_nand(mtd);
> -       unsigned int block = offs >> chip->phys_erase_shift;
> -       unsigned int page = offs >> chip->page_shift;
> -
> -       debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
> -             page);
> -       chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
> -       memset(chip->oob_poi, 0, mtd->oobsize);
> -       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> -
> -       return chip->oob_poi[0] != 0xff;
> -}
> -
>  /* setup mtd and nand structs and init mxs_nand driver */
>  void nand_init(void)
>  {
> @@ -215,7 +131,7 @@ void nand_init(void)
>         mxs_nand_init_spl(&nand_chip);
>         mtd = nand_to_mtd(&nand_chip);
>         /* set mtd functions */
> -       nand_chip.cmdfunc = mxs_nand_command;
> +       nand_chip.cmdfunc = nand_spl_command_lp;
>         nand_chip.scan_bbt = nand_default_bbt;
>         nand_chip.numchips = 1;
>
> @@ -234,92 +150,6 @@ void nand_init(void)
>         mtd->size = nand_chip.chipsize;
>         nand_chip.scan_bbt(mtd);
>         mxs_nand_setup_ecc(mtd);
> -}
> -
> -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
> -{
> -       unsigned int sz;
> -       unsigned int block, lastblock;
> -       unsigned int page, page_offset;
> -       unsigned int nand_page_per_block;
> -       struct nand_chip *chip;
> -       u8 *page_buf = NULL;
> -
> -       chip = mtd_to_nand(mtd);
> -       if (!chip->numchips)
> -               return -ENODEV;
> -
> -       page_buf = malloc(mtd->writesize);
> -       if (!page_buf)
> -               return -ENOMEM;
> -
> -       /* offs has to be aligned to a page address! */
> -       block = offs / mtd->erasesize;
> -       lastblock = (offs + size - 1) / mtd->erasesize;
> -       page = (offs % mtd->erasesize) / mtd->writesize;
> -       page_offset = offs % mtd->writesize;
> -       nand_page_per_block = mtd->erasesize / mtd->writesize;
> -
> -       while (block <= lastblock) {
> -               if (!is_badblock(mtd, mtd->erasesize * block, 1)) {
> -                       /* Skip bad blocks */
> -                       while (page < nand_page_per_block && size > 0) {
> -                               int curr_page = nand_page_per_block * block + page;
> -
> -                               if (mxs_read_page_ecc(mtd, page_buf, curr_page) < 0) {
> -                                       free(page_buf);
> -                                       return -EIO;
> -                               }
> -
> -                               if (size > (mtd->writesize - page_offset))
> -                                       sz = (mtd->writesize - page_offset);
> -                               else
> -                                       sz = size;
> -
> -                               memcpy(dst, page_buf + page_offset, sz);
> -                               dst += sz;
> -                               size -= sz;
> -                               page_offset = 0;
> -                               page++;
> -                       }
> -
> -                       page = 0;
> -               } else {
> -                       lastblock++;
> -               }
> -
> -               block++;
> -       }
> -
> -       free(page_buf);
> -
> -       return 0;
> -}
> -
> -int nand_default_bbt(struct mtd_info *mtd)
> -{
> -       return 0;
> -}
> -
> -void nand_deselect(void)
> -{
> -}
> -
> -u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> -{
> -       unsigned int block, lastblock;
> -
> -       block = sector / mtd->erasesize;
> -       lastblock = (sector + offs) / mtd->erasesize;
> -
> -       while (block <= lastblock) {
> -               if (is_badblock(mtd, block * mtd->erasesize, 1)) {
> -                       offs += mtd->erasesize;
> -                       lastblock++;
> -               }
> -
> -               block++;
> -       }
>
> -       return offs;
> +       nand_spl_init(&nand_chip);
>  }
> diff --git a/drivers/mtd/nand/raw/nand_common_spl.c b/drivers/mtd/nand/raw/nand_common_spl.c
> new file mode 100644
> index 0000000000..0595fcbc26
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/nand_common_spl.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Amarula Solutions B.V. All rights reserved.
> + *
> + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> + * Author: Weijie Gao <weijie.gao@mediatek.com>
> + */
> +
> +#include <image.h>
> +#include <malloc.h>
> +#include <time.h>
> +#include <linux/sizes.h>
> +#include <linux/delay.h>
> +#include <linux/mtd/rawnand.h>
> +
> +static struct nand_chip *nand;
> +static u8 *buffer;
> +static int nand_valid;
> +
> +int nand_spl_init(struct nand_chip *chip)
> +{
> +       struct mtd_info *mtd = nand_to_mtd(chip);
> +
> +       if (!mtd)
> +               return -EINVAL;
> +
> +       buffer = malloc(mtd->writesize);
> +       if (!buffer)
> +               return -ENOMEM;
> +
> +       nand = chip;
> +       nand_valid = 1;
> +
> +       return 0;
> +}
> +
> +static struct nand_chip *nand_spl_get_chip(void)
> +{
> +       return nand;
> +}
> +
> +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> +                        int column, int page_addr)
> +{
> +       register struct nand_chip *chip = mtd_to_nand(mtd);
> +       u32 timeo, time_start;
> +
> +       /* Command latch cycle */
> +       chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> +
> +       if (column != -1 || page_addr != -1) {
> +               int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
> +
> +               /* Serially input address */
> +               if (column != -1) {
> +                       chip->cmd_ctrl(mtd, column, ctrl);
> +                       ctrl &= ~NAND_CTRL_CHANGE;
> +                       if (command != NAND_CMD_READID)
> +                               chip->cmd_ctrl(mtd, column >> 8, ctrl);
> +               }
> +               if (page_addr != -1) {
> +                       chip->cmd_ctrl(mtd, page_addr, ctrl);
> +                       chip->cmd_ctrl(mtd, page_addr >> 8,
> +                                      NAND_NCE | NAND_ALE);
> +                       if (chip->options & NAND_ROW_ADDR_3)
> +                               chip->cmd_ctrl(mtd, page_addr >> 16,
> +                                              NAND_NCE | NAND_ALE);
> +               }
> +       }
> +       chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
> +
> +       /*
> +        * Program and erase have their own busy handlers status, sequential
> +        * in and status need no delay.
> +        */
> +       switch (command) {
> +       case NAND_CMD_STATUS:
> +       case NAND_CMD_READID:
> +       case NAND_CMD_SET_FEATURES:
> +               return;
> +
> +       case NAND_CMD_READ0:
> +               chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
> +                              NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
> +               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
> +                              NAND_NCE | NAND_CTRL_CHANGE);
> +       }
> +
> +       /*
> +        * Apply this short delay always to ensure that we do wait tWB in
> +        * any case on any machine.
> +        */
> +       ndelay(100);
> +
> +       timeo = (CONFIG_SYS_HZ * 20) / 1000;
> +       time_start = get_timer(0);
> +       while (get_timer(time_start) < timeo) {
> +               if (chip->dev_ready(mtd))
> +                       break;
> +       }
> +}
> +
> +static int nand_spl_read_page_hwecc(struct mtd_info *mtd, void *buf,
> +                              unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       int ret;
> +
> +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> +
> +       ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
> +       if (ret < 0 || ret > chip->ecc.strength)
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static int nand_spl_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
> +                             unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       int ret;
> +
> +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
> +
> +       ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
> +       if (ret < 0)
> +               return -1;
> +
> +       if (len > mtd->oobsize)
> +               len = mtd->oobsize;
> +
> +       memcpy(buf, chip->oob_poi, len);
> +
> +       return 0;
> +}
> +
> +static int nand_spl_check_bad_block(struct mtd_info *mtd, unsigned int page)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       u32 pages_per_block, i = 0;
> +       int ret;
> +       u8 bad;
> +
> +       pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
> +
> +       /* Read from first/last page(s) if necessary */
> +       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
> +               page += pages_per_block - 1;
> +               if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
> +                       page--;
> +       }
> +
> +       do {
> +               ret = nand_spl_read_oob_hwecc(mtd, &bad, 1, page);
> +               if (ret)
> +                       return ret;
> +
> +               ret = bad != 0xFF;
> +
> +               i++;
> +               page++;
> +       } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
> +
> +       return ret;
> +}
> +
> +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> +{
> +       struct nand_chip *chip = nand_spl_get_chip();
> +       struct mtd_info *mtd = &chip->mtd;
> +       u32 addr, col, page, chksz;
> +       bool check_bad = true;
> +
> +       if (!nand_valid)
> +               return -ENODEV;
> +
> +       while (size) {
> +               if (check_bad || !(offs & mtd->erasesize_mask)) {
> +                       addr = offs & (~mtd->erasesize_mask);
> +                       page = addr >> mtd->writesize_shift;
> +                       if (nand_spl_check_bad_block(mtd, page)) {
> +                               /* Skip bad block */
> +                               if (addr >= mtd->size - mtd->erasesize)
> +                                       return -ENXIO;
> +
> +                               offs += mtd->erasesize;
> +                               continue;
> +                       }
> +
> +                       check_bad = false;
> +               }
> +
> +               col = offs & mtd->writesize_mask;
> +               page = offs >> mtd->writesize_shift;
> +               chksz = min(mtd->writesize - col, (uint32_t)size);
> +
> +               if (unlikely(chksz < mtd->writesize)) {
> +                       /* Not reading a full page */
> +                       if (nand_spl_read_page_hwecc(mtd, buffer, page))
> +                               return -EIO;
> +
> +                       memcpy(dest, buffer + col, chksz);
> +               } else {
> +                       if (nand_spl_read_page_hwecc(mtd, dest, page))
> +                               return -EIO;
> +               }
> +
> +               dest += chksz;
> +               offs += chksz;
> +               size -= chksz;
> +       }
> +
> +       return 0;
> +}
> +
> +int nand_default_bbt(struct mtd_info *mtd)
> +{
> +       return 0;
> +}
> +
> +void nand_deselect(void)
> +{
> +}
> +
> +u32 nand_spl_adjust_offset(u32 sector, u32 offs)
> +{
> +       struct nand_chip *chip = nand_spl_get_chip();
> +       struct mtd_info *mtd = nand_to_mtd(chip);
> +       unsigned int block, lastblock;
> +
> +       block = sector / mtd->erasesize;
> +       lastblock = (sector + offs) / mtd->erasesize;
> +
> +       while (block <= lastblock) {
> +               if (nand_spl_check_bad_block(mtd, block * mtd->erasesize)) {
> +                       offs += mtd->erasesize;
> +                       lastblock++;
> +               }
> +
> +               block++;
> +       }
> +
> +       return offs;
> +}
> diff --git a/drivers/mtd/nand/raw/nand_common_spl.h b/drivers/mtd/nand/raw/nand_common_spl.h
> new file mode 100644
> index 0000000000..36bf63b230
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/nand_common_spl.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 Amarula Solution B.V. All rights reserved.
> + *
> + * Author: Michael Trimarchi <michael@amarulasolutions.com>
> + */
> +
> +#ifndef _NAND_COMMON_SPL_H
> +#define _NAND_COMMON_SPL_H
> +
> +void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
> +                        int column, int page_addr);
> +int nand_spl_init(struct nand_chip *chip);
> +
> +#endif /* _NAND_COMMON_SPL_H_ */
> --
> 2.34.1
>

Patch

diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index a398aa9d88..82ddb2b5d8 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -87,8 +87,8 @@  else  # minimal SPL drivers
 obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
 obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
 obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
-obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
+obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o nand_common_spl.o
 obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
-obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
+obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o nand_common_spl.o
 
 endif # drivers
diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
index 114fc8b7ce..254e14a553 100644
--- a/drivers/mtd/nand/raw/mt7621_nand_spl.c
+++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
@@ -10,187 +10,13 @@ 
 #include <linux/sizes.h>
 #include <linux/delay.h>
 #include <linux/mtd/rawnand.h>
+#include "nand_common_spl.h"
 #include "mt7621_nand.h"
+#include "nand_common_spl.h"
 
 static struct mt7621_nfc nfc_dev;
-static u8 *buffer;
 static int nand_valid;
 
-static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
-			    int column, int page_addr)
-{
-	register struct nand_chip *chip = mtd_to_nand(mtd);
-
-	/* Command latch cycle */
-	chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
-	if (column != -1 || page_addr != -1) {
-		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
-
-		/* Serially input address */
-		if (column != -1) {
-			chip->cmd_ctrl(mtd, column, ctrl);
-			ctrl &= ~NAND_CTRL_CHANGE;
-			if (command != NAND_CMD_READID)
-				chip->cmd_ctrl(mtd, column >> 8, ctrl);
-		}
-		if (page_addr != -1) {
-			chip->cmd_ctrl(mtd, page_addr, ctrl);
-			chip->cmd_ctrl(mtd, page_addr >> 8,
-				       NAND_NCE | NAND_ALE);
-			if (chip->options & NAND_ROW_ADDR_3)
-				chip->cmd_ctrl(mtd, page_addr >> 16,
-					       NAND_NCE | NAND_ALE);
-		}
-	}
-	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-
-	/*
-	 * Program and erase have their own busy handlers status, sequential
-	 * in and status need no delay.
-	 */
-	switch (command) {
-	case NAND_CMD_STATUS:
-	case NAND_CMD_READID:
-	case NAND_CMD_SET_FEATURES:
-		return;
-
-	case NAND_CMD_READ0:
-		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
-			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
-			       NAND_NCE | NAND_CTRL_CHANGE);
-	}
-
-	/*
-	 * Apply this short delay always to ensure that we do wait tWB in
-	 * any case on any machine.
-	 */
-	ndelay(100);
-
-	nand_wait_ready(mtd);
-}
-
-static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
-			       unsigned int page)
-{
-	struct nand_chip *chip = mtd_to_nand(mtd);
-	int ret;
-
-	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
-
-	ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
-	if (ret < 0 || ret > chip->ecc.strength)
-		return -1;
-
-	return 0;
-}
-
-static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
-			      unsigned int page)
-{
-	struct nand_chip *chip = mtd_to_nand(mtd);
-	int ret;
-
-	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
-
-	ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
-	if (ret < 0)
-		return -1;
-
-	if (len > mtd->oobsize)
-		len = mtd->oobsize;
-
-	memcpy(buf, chip->oob_poi, len);
-
-	return 0;
-}
-
-static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
-{
-	struct nand_chip *chip = mtd_to_nand(mtd);
-	u32 pages_per_block, i = 0;
-	int ret;
-	u8 bad;
-
-	pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
-
-	/* Read from first/last page(s) if necessary */
-	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
-		page += pages_per_block - 1;
-		if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
-			page--;
-	}
-
-	do {
-		ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
-		if (ret)
-			return ret;
-
-		ret = bad != 0xFF;
-
-		i++;
-		page++;
-	} while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
-
-	return ret;
-}
-
-int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
-{
-	struct mt7621_nfc *nfc = &nfc_dev;
-	struct nand_chip *chip = &nfc->nand;
-	struct mtd_info *mtd = &chip->mtd;
-	u32 addr, col, page, chksz;
-	bool check_bad = true;
-
-	if (!nand_valid)
-		return -ENODEV;
-
-	while (size) {
-		if (check_bad || !(offs & mtd->erasesize_mask)) {
-			addr = offs & (~mtd->erasesize_mask);
-			page = addr >> mtd->writesize_shift;
-			if (nfc_check_bad_block(mtd, page)) {
-				/* Skip bad block */
-				if (addr >= mtd->size - mtd->erasesize)
-					return -1;
-
-				offs += mtd->erasesize;
-				continue;
-			}
-
-			check_bad = false;
-		}
-
-		col = offs & mtd->writesize_mask;
-		page = offs >> mtd->writesize_shift;
-		chksz = min(mtd->writesize - col, (uint32_t)size);
-
-		if (unlikely(chksz < mtd->writesize)) {
-			/* Not reading a full page */
-			if (nfc_read_page_hwecc(mtd, buffer, page))
-				return -1;
-
-			memcpy(dest, buffer + col, chksz);
-		} else {
-			if (nfc_read_page_hwecc(mtd, dest, page))
-				return -1;
-		}
-
-		dest += chksz;
-		offs += chksz;
-		size -= chksz;
-	}
-
-	return 0;
-}
-
-int nand_default_bbt(struct mtd_info *mtd)
-{
-	return 0;
-}
-
 unsigned long nand_size(void)
 {
 	if (!nand_valid)
@@ -203,10 +29,6 @@  unsigned long nand_size(void)
 	return SZ_2G;
 }
 
-void nand_deselect(void)
-{
-}
-
 void nand_init(void)
 {
 	struct mtd_info *mtd;
@@ -219,7 +41,7 @@  void nand_init(void)
 
 	chip = &nfc_dev.nand;
 	mtd = &chip->mtd;
-	chip->cmdfunc = nand_command_lp;
+	chip->cmdfunc = nand_spl_command_lp;
 
 	if (mt7621_nfc_spl_post_init(&nfc_dev))
 		return;
@@ -229,9 +51,7 @@  void nand_init(void)
 	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
 	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
 
-	buffer = malloc(mtd->writesize);
-	if (!buffer)
-		return;
+	nand_spl_init(chip);
 
 	nand_valid = 1;
 }
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
index c7ea09e2f9..5cff6020c4 100644
--- a/drivers/mtd/nand/raw/mxs_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -14,66 +14,11 @@ 
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/mtd/rawnand.h>
+#include "nand_common_spl.h"
 
 static struct mtd_info *mtd;
 static struct nand_chip nand_chip;
 
-static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
-			     int column, int page_addr)
-{
-	register struct nand_chip *chip = mtd_to_nand(mtd);
-	u32 timeo, time_start;
-
-	/* write out the command to the device */
-	chip->cmd_ctrl(mtd, command, NAND_CLE);
-
-	/* Serially input address */
-	if (column != -1) {
-		/* Adjust columns for 16 bit buswidth */
-		if (chip->options & NAND_BUSWIDTH_16 &&
-				!nand_opcode_8bits(command))
-			column >>= 1;
-		chip->cmd_ctrl(mtd, column, NAND_ALE);
-
-		/*
-		 * Assume LP NAND here, so use two bytes column address
-		 * but not for CMD_READID and CMD_PARAM, which require
-		 * only one byte column address
-		 */
-		if (command != NAND_CMD_READID &&
-			command != NAND_CMD_PARAM)
-			chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
-	}
-	if (page_addr != -1) {
-		chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
-		chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
-		/* One more address cycle for devices > 128MiB */
-		if (chip->chipsize > (128 << 20))
-			chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
-	}
-	chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
-
-	if (command == NAND_CMD_READ0) {
-		chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
-		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
-	} else if (command == NAND_CMD_RNDOUT) {
-		/* No ready / busy check necessary */
-		chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
-			       NAND_NCE | NAND_CLE);
-		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
-			       NAND_NCE);
-	}
-
-	/* wait for nand ready */
-	ndelay(100);
-	timeo = (CONFIG_SYS_HZ * 20) / 1000;
-	time_start = get_timer(0);
-	while (get_timer(time_start) < timeo) {
-		if (chip->dev_ready(mtd))
-			break;
-	}
-}
-
 #if defined (CONFIG_SPL_NAND_IDENT)
 
 /* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
@@ -175,35 +120,6 @@  static int mxs_flash_ident(struct mtd_info *mtd)
 	return ret;
 }
 
-static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
-{
-	register struct nand_chip *chip = mtd_to_nand(mtd);
-	int ret;
-
-	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
-	ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
-	if (ret < 0) {
-		printf("read_page failed %d\n", ret);
-		return -1;
-	}
-	return 0;
-}
-
-static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
-{
-	register struct nand_chip *chip = mtd_to_nand(mtd);
-	unsigned int block = offs >> chip->phys_erase_shift;
-	unsigned int page = offs >> chip->page_shift;
-
-	debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
-	      page);
-	chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
-	memset(chip->oob_poi, 0, mtd->oobsize);
-	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-	return chip->oob_poi[0] != 0xff;
-}
-
 /* setup mtd and nand structs and init mxs_nand driver */
 void nand_init(void)
 {
@@ -215,7 +131,7 @@  void nand_init(void)
 	mxs_nand_init_spl(&nand_chip);
 	mtd = nand_to_mtd(&nand_chip);
 	/* set mtd functions */
-	nand_chip.cmdfunc = mxs_nand_command;
+	nand_chip.cmdfunc = nand_spl_command_lp;
 	nand_chip.scan_bbt = nand_default_bbt;
 	nand_chip.numchips = 1;
 
@@ -234,92 +150,6 @@  void nand_init(void)
 	mtd->size = nand_chip.chipsize;
 	nand_chip.scan_bbt(mtd);
 	mxs_nand_setup_ecc(mtd);
-}
-
-int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
-{
-	unsigned int sz;
-	unsigned int block, lastblock;
-	unsigned int page, page_offset;
-	unsigned int nand_page_per_block;
-	struct nand_chip *chip;
-	u8 *page_buf = NULL;
-
-	chip = mtd_to_nand(mtd);
-	if (!chip->numchips)
-		return -ENODEV;
-
-	page_buf = malloc(mtd->writesize);
-	if (!page_buf)
-		return -ENOMEM;
-
-	/* offs has to be aligned to a page address! */
-	block = offs / mtd->erasesize;
-	lastblock = (offs + size - 1) / mtd->erasesize;
-	page = (offs % mtd->erasesize) / mtd->writesize;
-	page_offset = offs % mtd->writesize;
-	nand_page_per_block = mtd->erasesize / mtd->writesize;
-
-	while (block <= lastblock) {
-		if (!is_badblock(mtd, mtd->erasesize * block, 1)) {
-			/* Skip bad blocks */
-			while (page < nand_page_per_block && size > 0) {
-				int curr_page = nand_page_per_block * block + page;
-
-				if (mxs_read_page_ecc(mtd, page_buf, curr_page) < 0) {
-					free(page_buf);
-					return -EIO;
-				}
-
-				if (size > (mtd->writesize - page_offset))
-					sz = (mtd->writesize - page_offset);
-				else
-					sz = size;
-
-				memcpy(dst, page_buf + page_offset, sz);
-				dst += sz;
-				size -= sz;
-				page_offset = 0;
-				page++;
-			}
-
-			page = 0;
-		} else {
-			lastblock++;
-		}
-
-		block++;
-	}
-
-	free(page_buf);
-
-	return 0;
-}
-
-int nand_default_bbt(struct mtd_info *mtd)
-{
-	return 0;
-}
-
-void nand_deselect(void)
-{
-}
-
-u32 nand_spl_adjust_offset(u32 sector, u32 offs)
-{
-	unsigned int block, lastblock;
-
-	block = sector / mtd->erasesize;
-	lastblock = (sector + offs) / mtd->erasesize;
-
-	while (block <= lastblock) {
-		if (is_badblock(mtd, block * mtd->erasesize, 1)) {
-			offs += mtd->erasesize;
-			lastblock++;
-		}
-
-		block++;
-	}
 
-	return offs;
+	nand_spl_init(&nand_chip);
 }
diff --git a/drivers/mtd/nand/raw/nand_common_spl.c b/drivers/mtd/nand/raw/nand_common_spl.c
new file mode 100644
index 0000000000..0595fcbc26
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_common_spl.c
@@ -0,0 +1,245 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Amarula Solutions B.V. All rights reserved.
+ *
+ * Author: Michael Trimarchi <michael@amarulasolutions.com>
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <image.h>
+#include <malloc.h>
+#include <time.h>
+#include <linux/sizes.h>
+#include <linux/delay.h>
+#include <linux/mtd/rawnand.h>
+
+static struct nand_chip *nand;
+static u8 *buffer;
+static int nand_valid;
+
+int nand_spl_init(struct nand_chip *chip)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+
+	if (!mtd)
+		return -EINVAL;
+
+	buffer = malloc(mtd->writesize);
+	if (!buffer)
+		return -ENOMEM;
+
+	nand = chip;
+	nand_valid = 1;
+
+	return 0;
+}
+
+static struct nand_chip *nand_spl_get_chip(void)
+{
+	return nand;
+}
+
+void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
+			 int column, int page_addr)
+{
+	register struct nand_chip *chip = mtd_to_nand(mtd);
+	u32 timeo, time_start;
+
+	/* Command latch cycle */
+	chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+	if (column != -1 || page_addr != -1) {
+		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+		/* Serially input address */
+		if (column != -1) {
+			chip->cmd_ctrl(mtd, column, ctrl);
+			ctrl &= ~NAND_CTRL_CHANGE;
+			if (command != NAND_CMD_READID)
+				chip->cmd_ctrl(mtd, column >> 8, ctrl);
+		}
+		if (page_addr != -1) {
+			chip->cmd_ctrl(mtd, page_addr, ctrl);
+			chip->cmd_ctrl(mtd, page_addr >> 8,
+				       NAND_NCE | NAND_ALE);
+			if (chip->options & NAND_ROW_ADDR_3)
+				chip->cmd_ctrl(mtd, page_addr >> 16,
+					       NAND_NCE | NAND_ALE);
+		}
+	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * Program and erase have their own busy handlers status, sequential
+	 * in and status need no delay.
+	 */
+	switch (command) {
+	case NAND_CMD_STATUS:
+	case NAND_CMD_READID:
+	case NAND_CMD_SET_FEATURES:
+		return;
+
+	case NAND_CMD_READ0:
+		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+	}
+
+	/*
+	 * Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine.
+	 */
+	ndelay(100);
+
+	timeo = (CONFIG_SYS_HZ * 20) / 1000;
+	time_start = get_timer(0);
+	while (get_timer(time_start) < timeo) {
+		if (chip->dev_ready(mtd))
+			break;
+	}
+}
+
+static int nand_spl_read_page_hwecc(struct mtd_info *mtd, void *buf,
+			       unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+	ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
+	if (ret < 0 || ret > chip->ecc.strength)
+		return -1;
+
+	return 0;
+}
+
+static int nand_spl_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
+			      unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+	ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
+	if (ret < 0)
+		return -1;
+
+	if (len > mtd->oobsize)
+		len = mtd->oobsize;
+
+	memcpy(buf, chip->oob_poi, len);
+
+	return 0;
+}
+
+static int nand_spl_check_bad_block(struct mtd_info *mtd, unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	u32 pages_per_block, i = 0;
+	int ret;
+	u8 bad;
+
+	pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
+
+	/* Read from first/last page(s) if necessary */
+	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
+		page += pages_per_block - 1;
+		if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
+			page--;
+	}
+
+	do {
+		ret = nand_spl_read_oob_hwecc(mtd, &bad, 1, page);
+		if (ret)
+			return ret;
+
+		ret = bad != 0xFF;
+
+		i++;
+		page++;
+	} while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+	return ret;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
+{
+	struct nand_chip *chip = nand_spl_get_chip();
+	struct mtd_info *mtd = &chip->mtd;
+	u32 addr, col, page, chksz;
+	bool check_bad = true;
+
+	if (!nand_valid)
+		return -ENODEV;
+
+	while (size) {
+		if (check_bad || !(offs & mtd->erasesize_mask)) {
+			addr = offs & (~mtd->erasesize_mask);
+			page = addr >> mtd->writesize_shift;
+			if (nand_spl_check_bad_block(mtd, page)) {
+				/* Skip bad block */
+				if (addr >= mtd->size - mtd->erasesize)
+					return -ENXIO;
+
+				offs += mtd->erasesize;
+				continue;
+			}
+
+			check_bad = false;
+		}
+
+		col = offs & mtd->writesize_mask;
+		page = offs >> mtd->writesize_shift;
+		chksz = min(mtd->writesize - col, (uint32_t)size);
+
+		if (unlikely(chksz < mtd->writesize)) {
+			/* Not reading a full page */
+			if (nand_spl_read_page_hwecc(mtd, buffer, page))
+				return -EIO;
+
+			memcpy(dest, buffer + col, chksz);
+		} else {
+			if (nand_spl_read_page_hwecc(mtd, dest, page))
+				return -EIO;
+		}
+
+		dest += chksz;
+		offs += chksz;
+		size -= chksz;
+	}
+
+	return 0;
+}
+
+int nand_default_bbt(struct mtd_info *mtd)
+{
+	return 0;
+}
+
+void nand_deselect(void)
+{
+}
+
+u32 nand_spl_adjust_offset(u32 sector, u32 offs)
+{
+	struct nand_chip *chip = nand_spl_get_chip();
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	unsigned int block, lastblock;
+
+	block = sector / mtd->erasesize;
+	lastblock = (sector + offs) / mtd->erasesize;
+
+	while (block <= lastblock) {
+		if (nand_spl_check_bad_block(mtd, block * mtd->erasesize)) {
+			offs += mtd->erasesize;
+			lastblock++;
+		}
+
+		block++;
+	}
+
+	return offs;
+}
diff --git a/drivers/mtd/nand/raw/nand_common_spl.h b/drivers/mtd/nand/raw/nand_common_spl.h
new file mode 100644
index 0000000000..36bf63b230
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_common_spl.h
@@ -0,0 +1,15 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Amarula Solution B.V. All rights reserved.
+ *
+ * Author: Michael Trimarchi <michael@amarulasolutions.com>
+ */
+
+#ifndef _NAND_COMMON_SPL_H
+#define _NAND_COMMON_SPL_H
+
+void nand_spl_command_lp(struct mtd_info *mtd, unsigned int command,
+			 int column, int page_addr);
+int nand_spl_init(struct nand_chip *chip);
+
+#endif /* _NAND_COMMON_SPL_H_ */