[PATCH v2] tools: sunxi-spl-image-builder: support H6/H616 NAND boot
    Michael Nazzareno Trimarchi 
    michael at amarulasolutions.com
       
    Mon Oct 27 07:32:58 CET 2025
    
    
  
Hi Richard
On Tue, Oct 21, 2025 at 10:01 AM Richard Genoud
<richard.genoud at bootlin.com> wrote:
>
> The H6/H616 boot ROM doesn't expect a SPL scrambled the same way as
> older SoCs.
> It doesn't use a specific seeds table, it expects a maximized ECC
> (BCH-80), a specific BBM (FF000301) and doesn't work if empty pages are
> skipped (it needs its specific BBM, even in the padding).
>
> So, add a -h6 parameter to support H6/616 with:
> - more ECC strengths
> - specific BBM
> - default_scrambler_seeds[] with all values
> - no empty pages skip
>
> In Kconfig, select BCH-80 by default for SUNXI_SPL_ECC_STRENGTH to make
> BROM happy.
>
> And in scripts/Makefile.xpl, use --h6 parameter when building for a
> SUN50I_GEN_H6 SoC.
>
> Tested on Whatsminer H616 board, booting from NAND.
>
> Co-developed-by: James Hilliard <james.hilliard1 at gmail.com>
> Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
> Signed-off-by: Richard Genoud <richard.genoud at bootlin.com>
> ---
>  drivers/mtd/nand/raw/Kconfig    |  1 +
>  scripts/Makefile.xpl            |  1 +
>  tools/sunxi-spl-image-builder.c | 89 ++++++++++++++++++++++++++-------
>  3 files changed, 72 insertions(+), 19 deletions(-)
>
> Changes from v1:
> - move up "default 80 if SUN50I_GEN_H6" (sorry for the noise)
>
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index 754b99bf3eb6..eb939ee85f88 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -483,6 +483,7 @@ if NAND_SUNXI
>
>  config NAND_SUNXI_SPL_ECC_STRENGTH
>         int "Allwinner NAND SPL ECC Strength"
> +       default 80 if SUN50I_GEN_H6
>         default 64
>
>  config NAND_SUNXI_SPL_ECC_SIZE
> diff --git a/scripts/Makefile.xpl b/scripts/Makefile.xpl
> index 52f014ad3324..aa4ee5cc1ce7 100644
> --- a/scripts/Makefile.xpl
> +++ b/scripts/Makefile.xpl
> @@ -455,6 +455,7 @@ $(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE
>
>  quiet_cmd_sunxi_spl_image_builder = SUNXI_SPL_IMAGE_BUILDER $@
>  cmd_sunxi_spl_image_builder = $(objtree)/tools/sunxi-spl-image-builder \
> +                               $(if $(CONFIG_SUN50I_GEN_H6),--h6) \
>                                 -c $(CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH)/$(CONFIG_NAND_SUNXI_SPL_ECC_SIZE) \
>                                 -p $(CONFIG_SYS_NAND_PAGE_SIZE) \
>                                 -o $(CONFIG_SYS_NAND_OOBSIZE) \
> diff --git a/tools/sunxi-spl-image-builder.c b/tools/sunxi-spl-image-builder.c
> index a367f1177403..f9ba1c474be9 100644
> --- a/tools/sunxi-spl-image-builder.c
> +++ b/tools/sunxi-spl-image-builder.c
> @@ -27,6 +27,7 @@ struct image_info {
>         int eraseblock_size;
>         int scramble;
>         int boot0;
> +       int h6;
Was it better to call it soc and then enable this option for this core
and for the feature core that
will have the same?
Then you can move to switch default to handle the cases.
Michael
>         off_t offset;
>         const char *source;
>         const char *dest;
> @@ -84,18 +85,29 @@ static void scramble(const struct image_info *info,
>         uint16_t state;
>         int i;
>
> -       /* Boot0 is always scrambled no matter the command line option. */
> -       if (info->boot0) {
> +       /*
> +        * Bail out earlier if the user didn't ask for scrambling.
> +        * But Boot0 is always scrambled no matter the command line option.
> +        */
> +       if (!info->boot0 && !info->scramble)
> +               return;
> +
> +       /*
> +        * On H6, the BROM scrambler seed is no different than the default one
> +        */
> +       if (info->boot0 && !info->h6) {
>                 state = brom_scrambler_seeds[0];
>         } else {
> -               unsigned seedmod = info->eraseblock_size / info->page_size;
> +               unsigned int seedmod;
>
> -               /* Bail out earlier if the user didn't ask for scrambling. */
> -               if (!info->scramble)
> -                       return;
> -
> -               if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
> +               if (info->h6) {
> +                       /* H6 boot0 uses all 128 seeds */
>                         seedmod = ARRAY_SIZE(default_scrambler_seeds);
> +               } else {
> +                       seedmod = info->eraseblock_size / info->page_size;
> +                       if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
> +                               seedmod = ARRAY_SIZE(default_scrambler_seeds);
> +               }
>
>                 state = default_scrambler_seeds[page % seedmod];
>         }
> @@ -137,14 +149,19 @@ static int write_page(const struct image_info *info, uint8_t *buffer,
>
>         fwrite(buffer, info->page_size + info->oob_size, 1, dst);
>
> -       for (i = 0; i < info->usable_page_size; i++) {
> -               if (buffer[i] !=  0xff)
> -                       break;
> -       }
> +       /*
> +        * H6 BROM doesn't support empty pages
> +        */
> +       if (!info->h6) {
> +               for (i = 0; i < info->usable_page_size; i++) {
> +                       if (buffer[i] !=  0xff)
> +                               break;
> +               }
>
> -       /* We leave empty pages at 0xff. */
> -       if (i == info->usable_page_size)
> -               return 0;
> +               /* We leave empty pages at 0xff. */
> +               if (i == info->usable_page_size)
> +                       return 0;
> +       }
>
>         /* Restore the source pointer to read it again. */
>         fseek(src, -cnt, SEEK_CUR);
> @@ -212,6 +229,14 @@ static int write_page(const struct image_info *info, uint8_t *buffer,
>                 }
>
>                 memset(ecc, 0, eccbytes);
> +
> +               if (info->h6) {
> +                       /* BBM taken from vendor code: FF 00 03 01 */
> +                       buffer[info->ecc_step_size + 1] = 0;
> +                       buffer[info->ecc_step_size + 2] = 3; // NAND_VERSION_0
> +                       buffer[info->ecc_step_size + 3] = 1; // NAND_VERSION_1
> +               }
> +
>                 swap_bits(buffer, info->ecc_step_size + 4);
>                 encode_bch(bch, buffer, info->ecc_step_size + 4, ecc);
>                 swap_bits(buffer, info->ecc_step_size + 4);
> @@ -303,6 +328,7 @@ static void display_help(int status)
>                 "-e <size>        --eraseblock=<size>  Erase block size\n"
>                 "-b               --boot0              Build a boot0 image.\n"
>                 "-s               --scramble           Scramble data\n"
> +               "-6               --h6                 Build an image compatible with H6/H616 SoC\n"
>                 "-a <offset>      --address=<offset>   Where the image will be programmed.\n"
>                 "\n"
>                 "Notes:\n"
> @@ -313,6 +339,9 @@ static void display_help(int status)
>                 "  Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n"
>                 "  Valid ECC step size: 512 and 1024\n"
>                 "\n"
> +               "On H6/H616, the only ECC step size supported is 1024, but more ECC\n"
> +               "strengths are supported: 44, 52, 68, 72, 76, 80\n"
> +               "\n"
>                 "If you are building a boot0 image, you'll have specify extra options.\n"
>                 "These options should be chosen based on the layouts described here:\n"
>                 "  http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n"
> @@ -342,7 +371,12 @@ static void display_help(int status)
>
>  static int check_image_info(struct image_info *info)
>  {
> -       static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
> +       static int ecc_strengths_a10[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
> +       static int ecc_strengths_h6[] = {
> +               16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
> +       };
> +       int *valid_ecc_strengths;
> +       size_t nstrengths;
>         int eccbytes, eccsteps;
>         unsigned i;
>
> @@ -367,12 +401,25 @@ static int check_image_info(struct image_info *info)
>                 return -EINVAL;
>         }
>
> -       for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) {
> +       if (info->h6) {
> +               if (info->ecc_step_size != 1024) {
> +                       fprintf(stderr,
> +                               "H6 SoCs supports only 1024 bytes ECC step\n");
> +                       return -EINVAL;
> +               }
> +               valid_ecc_strengths = ecc_strengths_h6;
> +               nstrengths = ARRAY_SIZE(ecc_strengths_h6);
> +       } else {
> +               valid_ecc_strengths = ecc_strengths_a10;
> +               nstrengths = ARRAY_SIZE(ecc_strengths_a10);
> +       }
> +
> +       for (i = 0; i < nstrengths; i++) {
>                 if (valid_ecc_strengths[i] == info->ecc_strength)
>                         break;
>         }
>
> -       if (i == ARRAY_SIZE(valid_ecc_strengths)) {
> +       if (i == nstrengths) {
>                 fprintf(stderr, "Invalid ECC strength argument: %d\n",
>                         info->ecc_strength);
>                 return -EINVAL;
> @@ -416,10 +463,11 @@ int main(int argc, char **argv)
>                         {"boot0", no_argument, 0, 'b'},
>                         {"scramble", no_argument, 0, 's'},
>                         {"address", required_argument, 0, 'a'},
> +                       {"h6", no_argument, 0, '6'},
>                         {0, 0, 0, 0},
>                 };
>
> -               int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh",
> +               int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh6",
>                                 long_options, &option_index);
>                 if (c == EOF)
>                         break;
> @@ -454,6 +502,9 @@ int main(int argc, char **argv)
>                 case 'a':
>                         info.offset = strtoull(optarg, NULL, 0);
>                         break;
> +               case '6':
> +                       info.h6 = 1;
> +                       break;
>                 case '?':
>                         display_help(-1);
>                         break;
>
> base-commit: 2ba64e303b2706e5c42a6bf982326d632342ca66
> prerequisite-patch-id: 364de1b9fd9b3226884355a639c58e6d12f6f03b
> --
> 2.47.3
>
-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael at amarulasolutions.com
__________________________________
Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info at amarulasolutions.com
www.amarulasolutions.com
    
    
More information about the U-Boot
mailing list