[PATCH v2] tools: sunxi-spl-image-builder: support H6/H616 NAND boot
    Richard GENOUD 
    richard.genoud at bootlin.com
       
    Tue Oct 28 08:38:58 CET 2025
    
    
  
Hi,
Le 27/10/2025 à 07:32, Michael Nazzareno Trimarchi a écrit :
> 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.
Indeed, it would be more convenient.
Thanks!
> 
> 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
>>
> 
> 
-- 
Richard Genoud, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
    
    
More information about the U-Boot
mailing list