uboot/arch/arm/mach-imx/cmd_nandbcb.c
<<
>>
Prefs
   1/*
   2 * i.MX nand boot control block(bcb).
   3 *
   4 * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng
   5 *
   6 * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
   7 * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net>
   8 *
   9 * Reconstucted by Han Xu <han.xu@nxp.com>
  10 *
  11 * SPDX-License-Identifier:     GPL-2.0+
  12 */
  13
  14#include <common.h>
  15#include <command.h>
  16#include <log.h>
  17#include <malloc.h>
  18#include <nand.h>
  19#include <dm/devres.h>
  20#include <linux/bug.h>
  21
  22#include <asm/io.h>
  23#include <jffs2/jffs2.h>
  24#include <linux/bch.h>
  25#include <linux/mtd/mtd.h>
  26
  27#include <asm/arch/sys_proto.h>
  28#include <asm/mach-imx/imx-nandbcb.h>
  29#include <asm/mach-imx/imximage.cfg>
  30#include <mxs_nand.h>
  31#include <linux/mtd/mtd.h>
  32#include <nand.h>
  33#include <fuse.h>
  34
  35#include "../../../cmd/legacy-mtd-utils.h"
  36
  37/* FCB related flags */
  38/* FCB layout with leading 12B reserved */
  39#define FCB_LAYOUT_RESV_12B             BIT(0)
  40/* FCB layout with leading 32B meta data */
  41#define FCB_LAYOUT_META_32B             BIT(1)
  42/* FCB encrypted by Hamming code */
  43#define FCB_ENCODE_HAMMING              BIT(2)
  44/* FCB encrypted by 40bit BCH */
  45#define FCB_ENCODE_BCH_40b              BIT(3)
  46/* FCB encrypted by 62bit BCH */
  47#define FCB_ENCODE_BCH_62b              BIT(4)
  48/* FCB encrypted by BCH */
  49#define FCB_ENCODE_BCH                  (FCB_ENCODE_BCH_40b | FCB_ENCODE_BCH_62b)
  50/* FCB data was randomized */
  51#define FCB_RANDON_ENABLED              BIT(5)
  52
  53/* Firmware related flags */
  54/* No 1K padding */
  55#define FIRMWARE_NEED_PADDING           BIT(8)
  56/* Extra firmware*/
  57#define FIRMWARE_EXTRA_ONE              BIT(9)
  58/* Secondary firmware on fixed address */
  59#define FIRMWARE_SECONDARY_FIXED_ADDR   BIT(10)
  60
  61/* Boot search related flags */
  62#define BT_SEARCH_CNT_FROM_FUSE         BIT(16)
  63
  64struct platform_config {
  65        int misc_flags;
  66};
  67
  68static struct platform_config plat_config;
  69
  70/* imx6q/dl/solo */
  71static struct platform_config imx6qdl_plat_config = {
  72        .misc_flags = FCB_LAYOUT_RESV_12B |
  73                     FCB_ENCODE_HAMMING |
  74                     FIRMWARE_NEED_PADDING,
  75};
  76
  77static struct platform_config imx6sx_plat_config = {
  78        .misc_flags = FCB_LAYOUT_META_32B |
  79                     FCB_ENCODE_BCH_62b |
  80                     FIRMWARE_NEED_PADDING |
  81                     FCB_RANDON_ENABLED,
  82};
  83
  84static struct platform_config imx7d_plat_config = {
  85        .misc_flags = FCB_LAYOUT_META_32B |
  86                     FCB_ENCODE_BCH_62b |
  87                     FIRMWARE_NEED_PADDING |
  88                     FCB_RANDON_ENABLED,
  89};
  90
  91/* imx6ul/ull/ulz */
  92static struct platform_config imx6ul_plat_config = {
  93        .misc_flags = FCB_LAYOUT_META_32B |
  94                     FCB_ENCODE_BCH_40b |
  95                     FIRMWARE_NEED_PADDING,
  96};
  97
  98static struct platform_config imx8mq_plat_config = {
  99        .misc_flags = FCB_LAYOUT_META_32B |
 100                     FCB_ENCODE_BCH_62b |
 101                     FIRMWARE_NEED_PADDING |
 102                     FCB_RANDON_ENABLED |
 103                     FIRMWARE_EXTRA_ONE,
 104};
 105
 106/* all other imx8mm */
 107static struct platform_config imx8mm_plat_config = {
 108        .misc_flags = FCB_LAYOUT_META_32B |
 109                     FCB_ENCODE_BCH_62b |
 110                     FIRMWARE_NEED_PADDING |
 111                     FCB_RANDON_ENABLED,
 112};
 113
 114/* imx8mn */
 115static struct platform_config imx8mn_plat_config = {
 116        .misc_flags = FCB_LAYOUT_META_32B |
 117                     FCB_ENCODE_BCH_62b |
 118                     FCB_RANDON_ENABLED |
 119                     FIRMWARE_SECONDARY_FIXED_ADDR |
 120                     BT_SEARCH_CNT_FROM_FUSE,
 121};
 122
 123/* imx8qx/qm */
 124static struct platform_config imx8q_plat_config = {
 125        .misc_flags = FCB_LAYOUT_META_32B |
 126                     FCB_ENCODE_BCH_62b |
 127                     FCB_RANDON_ENABLED |
 128                     FIRMWARE_SECONDARY_FIXED_ADDR |
 129                     BT_SEARCH_CNT_FROM_FUSE,
 130};
 131
 132/* boot search related variables and definitions */
 133static int g_boot_search_count = 4;
 134static int g_boot_search_stride;
 135static int g_pages_per_stride;
 136
 137/* mtd config structure */
 138struct boot_config {
 139        int dev;
 140        struct mtd_info *mtd;
 141        loff_t maxsize;
 142        loff_t input_size;
 143        loff_t offset;
 144        loff_t boot_stream1_address;
 145        loff_t boot_stream2_address;
 146        size_t boot_stream1_size;
 147        size_t boot_stream2_size;
 148        size_t max_boot_stream_size;
 149        int stride_size_in_byte;
 150        int search_area_size_in_bytes;
 151        int search_area_size_in_pages;
 152        int secondary_boot_stream_off_in_MB;
 153};
 154
 155/* boot_stream config structure */
 156struct boot_stream_config {
 157        char bs_label[32];
 158        loff_t bs_addr;
 159        size_t bs_size;
 160        void *bs_buf;
 161        loff_t next_bs_addr;
 162        bool need_padding;
 163};
 164
 165/* FW index */
 166#define FW1_ONLY        1
 167#define FW2_ONLY        2
 168#define FW_ALL          FW1_ONLY | FW2_ONLY
 169#define FW_INX(x)       (1 << (x))
 170
 171/* NAND convert macros */
 172#define CONV_TO_PAGES(x)        ((u32)(x) / (u32)(mtd->writesize))
 173#define CONV_TO_BLOCKS(x)       ((u32)(x) / (u32)(mtd->erasesize))
 174
 175#define GETBIT(v, n)            (((v) >> (n)) & 0x1)
 176#define IMX8MQ_SPL_SZ 0x3e000
 177#define IMX8MQ_HDMI_FW_SZ 0x19c00
 178
 179static int nandbcb_get_info(int argc, char * const argv[],
 180                            struct boot_config *boot_cfg)
 181{
 182        int dev;
 183        struct mtd_info *mtd;
 184
 185        dev = nand_curr_device;
 186        if (dev < 0) {
 187                printf("failed to get nand_curr_device, run nand device\n");
 188                return CMD_RET_FAILURE;
 189        }
 190
 191        mtd = get_nand_dev_by_index(dev);
 192        if (!mtd) {
 193                printf("failed to get mtd info\n");
 194                return CMD_RET_FAILURE;
 195        }
 196
 197        boot_cfg->dev = dev;
 198        boot_cfg->mtd = mtd;
 199
 200        return CMD_RET_SUCCESS;
 201}
 202
 203static int nandbcb_get_size(int argc, char * const argv[], int num,
 204                            struct boot_config *boot_cfg)
 205{
 206        int dev;
 207        loff_t offset, size, maxsize;
 208        struct mtd_info *mtd;
 209
 210        dev = boot_cfg->dev;
 211        mtd = boot_cfg->mtd;
 212        size = 0;
 213
 214        if (mtd_arg_off_size(argc - num, argv + num, &dev, &offset, &size,
 215                             &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
 216                return CMD_RET_FAILURE;
 217
 218        boot_cfg->maxsize = maxsize;
 219        boot_cfg->offset = offset;
 220
 221        debug("max: %llx, offset: %llx\n", maxsize, offset);
 222
 223        if (size && size != maxsize)
 224                boot_cfg->input_size = size;
 225
 226        return CMD_RET_SUCCESS;
 227}
 228
 229static int nandbcb_set_boot_config(int argc, char * const argv[],
 230                                   struct boot_config *boot_cfg)
 231{
 232        struct mtd_info *mtd;
 233        loff_t maxsize;
 234        loff_t boot_stream1_address, boot_stream2_address, max_boot_stream_size;
 235
 236        if (!boot_cfg->mtd) {
 237                printf("Didn't get the mtd info, quit\n");
 238                return CMD_RET_FAILURE;
 239        }
 240        mtd = boot_cfg->mtd;
 241
 242        /*
 243         * By default
 244         * set the search count as 4
 245         * set each FCB/DBBT/Firmware offset at the beginning of blocks
 246         * customers may change the value as needed
 247         */
 248
 249        /* if need more compact layout, change these values */
 250        /* g_boot_search_count was set as 4 at the definition*/
 251        /* g_pages_per_stride was set as block size */
 252
 253        g_pages_per_stride = mtd->erasesize / mtd->writesize;
 254
 255        g_boot_search_stride = mtd->writesize * g_pages_per_stride;
 256
 257        boot_cfg->stride_size_in_byte = g_boot_search_stride * mtd->writesize;
 258        boot_cfg->search_area_size_in_bytes =
 259                g_boot_search_count * g_boot_search_stride;
 260        boot_cfg->search_area_size_in_pages =
 261                boot_cfg->search_area_size_in_bytes / mtd->writesize;
 262
 263        /* after FCB/DBBT, split the rest of area for two Firmwares */
 264        if (!boot_cfg->maxsize) {
 265                printf("Didn't get the maxsize, quit\n");
 266                return CMD_RET_FAILURE;
 267        }
 268        maxsize = boot_cfg->maxsize;
 269        /* align to page boundary */
 270        maxsize = ((u32)(maxsize + mtd->writesize - 1)) / (u32)mtd->writesize
 271                        * mtd->writesize;
 272
 273        boot_stream1_address = 2 * boot_cfg->search_area_size_in_bytes;
 274        boot_stream2_address = ((maxsize - boot_stream1_address) / 2 +
 275                               boot_stream1_address);
 276
 277        if (boot_cfg->secondary_boot_stream_off_in_MB)
 278                boot_stream2_address = boot_cfg->secondary_boot_stream_off_in_MB * 1024 * 1024;
 279
 280        max_boot_stream_size = boot_stream2_address - boot_stream1_address;
 281
 282        /* sanity check */
 283        if (max_boot_stream_size <= 0) {
 284                debug("st1_addr: %llx, st2_addr: %llx, max: %llx\n",
 285                      boot_stream1_address, boot_stream2_address,
 286                      max_boot_stream_size);
 287                printf("something wrong with firmware address settings\n");
 288                return CMD_RET_FAILURE;
 289        }
 290        boot_cfg->boot_stream1_address = boot_stream1_address;
 291        boot_cfg->boot_stream2_address = boot_stream2_address;
 292        boot_cfg->max_boot_stream_size = max_boot_stream_size;
 293
 294        /* set the boot_stream size as the input size now */
 295        if (boot_cfg->input_size) {
 296                boot_cfg->boot_stream1_size = boot_cfg->input_size;
 297                boot_cfg->boot_stream2_size = boot_cfg->input_size;
 298        }
 299
 300        return CMD_RET_SUCCESS;
 301}
 302
 303static int nandbcb_check_space(struct boot_config *boot_cfg)
 304{
 305        size_t maxsize = boot_cfg->maxsize;
 306        size_t max_boot_stream_size = boot_cfg->max_boot_stream_size;
 307        loff_t boot_stream2_address = boot_cfg->boot_stream2_address;
 308
 309        if (boot_cfg->boot_stream1_size &&
 310            boot_cfg->boot_stream1_size > max_boot_stream_size) {
 311                printf("boot stream1 doesn't fit, check partition size or settings\n");
 312                return CMD_RET_FAILURE;
 313        }
 314
 315        if (boot_cfg->boot_stream2_size &&
 316            boot_cfg->boot_stream2_size > maxsize - boot_stream2_address) {
 317                printf("boot stream2 doesn't fit, check partition size or settings\n");
 318                return CMD_RET_FAILURE;
 319        }
 320
 321        return CMD_RET_SUCCESS;
 322}
 323
 324#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
 325static uint8_t reverse_bit(uint8_t b)
 326{
 327        b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
 328        b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
 329        b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
 330
 331        return b;
 332}
 333
 334static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits)
 335{
 336        int i, j, m = 13;
 337        int blocksize = 128;
 338        int numblocks = 8;
 339        int ecc_buf_size = (m * eccbits + 7) / 8;
 340        struct bch_control *bch = init_bch(m, eccbits, 0);
 341        u8 *ecc_buf = kzalloc(ecc_buf_size, GFP_KERNEL);
 342        u8 *tmp_buf = kzalloc(blocksize * numblocks, GFP_KERNEL);
 343        u8 *psrc, *pdst;
 344
 345        /*
 346         * The blocks here are bit aligned. If eccbits is a multiple of 8,
 347         * we just can copy bytes. Otherwiese we must move the blocks to
 348         * the next free bit position.
 349         */
 350        WARN_ON(eccbits % 8);
 351
 352        memcpy(tmp_buf, fcb, sizeof(*fcb));
 353
 354        for (i = 0; i < numblocks; i++) {
 355                memset(ecc_buf, 0, ecc_buf_size);
 356                psrc = tmp_buf + i * blocksize;
 357                pdst = buf + i * (blocksize + ecc_buf_size);
 358
 359                /* copy data byte aligned to destination buf */
 360                memcpy(pdst, psrc, blocksize);
 361
 362                /*
 363                 * imx-kobs use a modified encode_bch which reverse the
 364                 * bit order of the data before calculating bch.
 365                 * Do this in the buffer and use the bch lib here.
 366                 */
 367                for (j = 0; j < blocksize; j++)
 368                        psrc[j] = reverse_bit(psrc[j]);
 369
 370                encode_bch(bch, psrc, blocksize, ecc_buf);
 371
 372                /* reverse ecc bit */
 373                for (j = 0; j < ecc_buf_size; j++)
 374                        ecc_buf[j] = reverse_bit(ecc_buf[j]);
 375
 376                /* Here eccbuf is byte aligned and we can just copy it */
 377                memcpy(pdst + blocksize, ecc_buf, ecc_buf_size);
 378        }
 379
 380        kfree(ecc_buf);
 381        kfree(tmp_buf);
 382        free_bch(bch);
 383}
 384#else
 385
 386static u8 calculate_parity_13_8(u8 d)
 387{
 388        u8 p = 0;
 389
 390        p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
 391        p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^
 392              GETBIT(d, 1)) << 1;
 393        p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^
 394              GETBIT(d, 0)) << 2;
 395        p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
 396        p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^
 397              GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
 398
 399        return p;
 400}
 401
 402static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
 403{
 404        int i;
 405        u8 *src = _src;
 406        u8 *ecc = _ecc;
 407
 408        for (i = 0; i < size; i++)
 409                ecc[i] = calculate_parity_13_8(src[i]);
 410}
 411#endif
 412
 413static u32 calc_chksum(void *buf, size_t size)
 414{
 415        u32 chksum = 0;
 416        u8 *bp = buf;
 417        size_t i;
 418
 419        for (i = 0; i < size; i++)
 420                chksum += bp[i];
 421
 422        return ~chksum;
 423}
 424
 425static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg)
 426{
 427        struct mtd_info *mtd = boot_cfg->mtd;
 428        struct nand_chip *chip = mtd_to_nand(mtd);
 429        struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
 430        struct mxs_nand_layout l;
 431
 432        mxs_nand_get_layout(mtd, &l);
 433
 434        fcb->fingerprint = FCB_FINGERPRINT;
 435        fcb->version = FCB_VERSION_1;
 436
 437        fcb->datasetup = 80;
 438        fcb->datahold = 60;
 439        fcb->addr_setup = 25;
 440        fcb->dsample_time = 6;
 441
 442        fcb->pagesize = mtd->writesize;
 443        fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
 444        fcb->sectors = mtd->erasesize / mtd->writesize;
 445
 446        fcb->meta_size = l.meta_size;
 447        fcb->nr_blocks = l.nblocks;
 448        fcb->ecc_nr = l.data0_size;
 449        fcb->ecc_level = l.ecc0;
 450        fcb->ecc_size = l.datan_size;
 451        fcb->ecc_type = l.eccn;
 452        fcb->bchtype = l.gf_len;
 453
 454        /* DBBT search area starts from the next block after all FCB */
 455        fcb->dbbt_start = boot_cfg->search_area_size_in_pages;
 456
 457        fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
 458        fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
 459
 460        fcb->phy_offset = mtd->writesize;
 461
 462        fcb->disbbm = 0;
 463
 464        fcb->fw1_start = CONV_TO_PAGES(boot_cfg->boot_stream1_address);
 465        fcb->fw2_start = CONV_TO_PAGES(boot_cfg->boot_stream2_address);
 466        fcb->fw1_pages = CONV_TO_PAGES(boot_cfg->boot_stream1_size);
 467        fcb->fw2_pages = CONV_TO_PAGES(boot_cfg->boot_stream2_size);
 468
 469        fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
 470}
 471
 472static int fill_dbbt_data(struct mtd_info *mtd, void *buf, int num_blocks)
 473{
 474        int n, n_bad_blocks = 0;
 475        u32 *bb = buf + 0x8;
 476        u32 *n_bad_blocksp = buf + 0x4;
 477
 478        for (n = 0; n < num_blocks; n++) {
 479                loff_t offset = n * mtd->erasesize;
 480                        if (mtd_block_isbad(mtd, offset)) {
 481                                n_bad_blocks++;
 482                                *bb = n;
 483                                bb++;
 484                }
 485        }
 486
 487        *n_bad_blocksp = n_bad_blocks;
 488
 489        return n_bad_blocks;
 490}
 491
 492/*
 493 * return 1     - bad block
 494 * return 0     - read successfully
 495 * return < 0   - read failed
 496 */
 497static int read_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb,
 498                    loff_t off)
 499{
 500        struct mtd_info *mtd;
 501        void *fcb_raw_page;
 502        size_t size;
 503        int ret = 0;
 504
 505        mtd = boot_cfg->mtd;
 506        fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
 507
 508        if (mtd_block_isbad(mtd, off)) {
 509                printf("Block %d is bad, skipped\n", (int)CONV_TO_BLOCKS(off));
 510                return 1;
 511        }
 512
 513        /*
 514         * User BCH hardware to decode ECC for FCB
 515         */
 516        if (plat_config.misc_flags & FCB_ENCODE_BCH) {
 517                size = sizeof(struct fcb_block);
 518
 519                /* switch nand BCH to FCB compatible settings */
 520                if (plat_config.misc_flags & FCB_ENCODE_BCH_62b)
 521                        mxs_nand_mode_fcb_62bit(mtd);
 522                else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b)
 523                        mxs_nand_mode_fcb_40bit(mtd);
 524
 525                ret = nand_read(mtd, off, &size, (u_char *)fcb);
 526
 527                /* switch BCH back */
 528                mxs_nand_mode_normal(mtd);
 529                printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n",
 530                       off, size, ret ? "ERROR" : "OK");
 531
 532        } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) {
 533                /* raw read*/
 534                mtd_oob_ops_t ops = {
 535                        .datbuf = (u8 *)fcb_raw_page,
 536                        .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
 537                        .len = mtd->writesize,
 538                        .ooblen = mtd->oobsize,
 539                        .mode = MTD_OPS_RAW
 540                        };
 541
 542                ret = mtd_read_oob(mtd, off, &ops);
 543                printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n",
 544                       off, ops.len, ret ? "ERROR" : "OK");
 545        }
 546
 547        if (ret)
 548                goto fcb_raw_page_err;
 549
 550        if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) &&
 551            (plat_config.misc_flags & FCB_LAYOUT_RESV_12B))
 552                memcpy(fcb, fcb_raw_page + 12, sizeof(struct fcb_block));
 553
 554/* TODO: check if it can pass Hamming check */
 555
 556fcb_raw_page_err:
 557        kfree(fcb_raw_page);
 558
 559        return ret;
 560}
 561
 562static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb)
 563{
 564        struct mtd_info *mtd;
 565        void *fcb_raw_page = NULL;
 566        int i, ret;
 567        loff_t off;
 568        size_t size;
 569
 570        mtd = boot_cfg->mtd;
 571
 572        /*
 573         * We prepare raw page only for i.MX6, for i.MX7 we
 574         * leverage BCH hw module instead
 575         */
 576        if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) &&
 577            (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) {
 578                fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize,
 579                                       GFP_KERNEL);
 580                if (!fcb_raw_page) {
 581                        debug("failed to allocate fcb_raw_page\n");
 582                        ret = -ENOMEM;
 583                        return ret;
 584                }
 585
 586#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
 587                /* 40 bit BCH, for i.MX6UL(L) */
 588                encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
 589#else
 590                memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
 591                encode_hamming_13_8(fcb_raw_page + 12,
 592                                    fcb_raw_page + 12 + 512, 512);
 593#endif
 594                /*
 595                 * Set the first and second byte of OOB data to 0xFF,
 596                 * not 0x00. These bytes are used as the Manufacturers Bad
 597                 * Block Marker (MBBM). Since the FCB is mostly written to
 598                 * the first page in a block, a scan for
 599                 * factory bad blocks will detect these blocks as bad, e.g.
 600                 * when function nand_scan_bbt() is executed to build a new
 601                 * bad block table.
 602                 */
 603                memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
 604        }
 605
 606        /* start writing FCB from the very beginning */
 607        off = 0;
 608
 609        for (i = 0; i < g_boot_search_count; i++) {
 610                if (mtd_block_isbad(mtd, off)) {
 611                        printf("Block %d is bad, skipped\n", i);
 612                        continue;
 613                }
 614
 615                /*
 616                 * User BCH hardware module to generate ECC for FCB
 617                 */
 618                if (plat_config.misc_flags & FCB_ENCODE_BCH) {
 619                        size = sizeof(struct fcb_block);
 620
 621                        /* switch nand BCH to FCB compatible settings */
 622                        if (plat_config.misc_flags & FCB_ENCODE_BCH_62b)
 623                                mxs_nand_mode_fcb_62bit(mtd);
 624                        else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b)
 625                                mxs_nand_mode_fcb_40bit(mtd);
 626
 627                        ret = nand_write(mtd, off, &size, (u_char *)fcb);
 628
 629                        /* switch BCH back */
 630                        mxs_nand_mode_normal(mtd);
 631                        printf("NAND FCB write to 0x%zx offset 0x%llx written: %s\n",
 632                               size, off, ret ? "ERROR" : "OK");
 633
 634                } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) {
 635                        /* raw write */
 636                        mtd_oob_ops_t ops = {
 637                                .datbuf = (u8 *)fcb_raw_page,
 638                                .oobbuf = ((u8 *)fcb_raw_page) +
 639                                          mtd->writesize,
 640                                .len = mtd->writesize,
 641                                .ooblen = mtd->oobsize,
 642                                .mode = MTD_OPS_RAW
 643                        };
 644
 645                        ret = mtd_write_oob(mtd, off, &ops);
 646                        printf("NAND FCB write to 0x%llxx offset 0x%zx written: %s\n", off, ops.len, ret ? "ERROR" : "OK");
 647                }
 648
 649                if (ret)
 650                        goto fcb_raw_page_err;
 651
 652                /* next writing location */
 653                off += g_boot_search_stride;
 654        }
 655
 656        return 0;
 657
 658fcb_raw_page_err:
 659        kfree(fcb_raw_page);
 660
 661        return ret;
 662}
 663
 664/*
 665 * return 1     - bad block
 666 * return 0     - read successfully
 667 * return < 0   - read failed
 668 */
 669static int read_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt,
 670                      void *dbbt_data_page, loff_t off)
 671{
 672        size_t size;
 673        struct mtd_info *mtd;
 674        loff_t to;
 675        int ret;
 676
 677        mtd = boot_cfg->mtd;
 678
 679        if (mtd_block_isbad(mtd, off)) {
 680                printf("Block %d is bad, skipped\n",
 681                       (int)CONV_TO_BLOCKS(off));
 682                return 1;
 683        }
 684
 685        size = sizeof(struct dbbt_block);
 686        ret = nand_read(mtd, off, &size, (u_char *)dbbt);
 687        printf("NAND DBBT read from 0x%llx offset 0x%zx read: %s\n",
 688               off, size, ret ? "ERROR" : "OK");
 689        if (ret)
 690                return ret;
 691
 692        /* dbbtpages == 0 if no bad blocks */
 693        if (dbbt->dbbtpages > 0) {
 694                to = off + 4 * mtd->writesize;
 695                size = mtd->writesize;
 696                ret = nand_read(mtd, to, &size, dbbt_data_page);
 697                printf("DBBT data read from 0x%llx offset 0x%zx read: %s\n",
 698                       to, size, ret ? "ERROR" : "OK");
 699
 700                if (ret)
 701                        return ret;
 702        }
 703
 704        return 0;
 705}
 706
 707static int write_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt,
 708                      void *dbbt_data_page)
 709{
 710        int i;
 711        loff_t off, to;
 712        size_t size;
 713        struct mtd_info *mtd;
 714        int ret;
 715
 716        mtd = boot_cfg->mtd;
 717
 718        /* start writing DBBT after all FCBs */
 719        off = boot_cfg->search_area_size_in_bytes;
 720        size = mtd->writesize;
 721
 722        for (i = 0; i < g_boot_search_count; i++) {
 723                if (mtd_block_isbad(mtd, off)) {
 724                        printf("Block %d is bad, skipped\n",
 725                               (int)(i + CONV_TO_BLOCKS(off)));
 726                        continue;
 727                }
 728
 729                ret = nand_write(mtd, off, &size, (u_char *)dbbt);
 730                printf("NAND DBBT write to 0x%llx offset 0x%zx written: %s\n",
 731                       off, size, ret ? "ERROR" : "OK");
 732                if (ret)
 733                        return ret;
 734
 735                /* dbbtpages == 0 if no bad blocks */
 736                if (dbbt->dbbtpages > 0) {
 737                        to = off + 4 * mtd->writesize;
 738                        ret = nand_write(mtd, to, &size, dbbt_data_page);
 739                        printf("DBBT data write to 0x%llx offset 0x%zx written: %s\n",
 740                               to, size, ret ? "ERROR" : "OK");
 741
 742                if (ret)
 743                        return ret;
 744                }
 745
 746                /* next writing location */
 747                off += g_boot_search_stride;
 748        }
 749
 750        return 0;
 751}
 752
 753/* reuse the check_skip_len from nand_util.c with minor change*/
 754static int check_skip_length(struct boot_config *boot_cfg, loff_t offset,
 755                             size_t length, size_t *used)
 756{
 757        struct mtd_info *mtd = boot_cfg->mtd;
 758        size_t maxsize = boot_cfg->maxsize;
 759        size_t len_excl_bad = 0;
 760        int ret = 0;
 761
 762        while (len_excl_bad < length) {
 763                size_t block_len, block_off;
 764                loff_t block_start;
 765
 766                if (offset >= maxsize)
 767                        return -1;
 768
 769                block_start = offset & ~(loff_t)(mtd->erasesize - 1);
 770                block_off = offset & (mtd->erasesize - 1);
 771                block_len = mtd->erasesize - block_off;
 772
 773                if (!nand_block_isbad(mtd, block_start))
 774                        len_excl_bad += block_len;
 775                else
 776                        ret = 1;
 777
 778                offset += block_len;
 779                *used += block_len;
 780        }
 781
 782        /* If the length is not a multiple of block_len, adjust. */
 783        if (len_excl_bad > length)
 784                *used -= (len_excl_bad - length);
 785
 786        return ret;
 787}
 788
 789static int nandbcb_get_next_good_blk_addr(struct boot_config *boot_cfg,
 790                                          struct boot_stream_config *bs_cfg)
 791{
 792        struct mtd_info *mtd = boot_cfg->mtd;
 793        loff_t offset = bs_cfg->bs_addr;
 794        size_t length = bs_cfg->bs_size;
 795        size_t used = 0;
 796        int ret;
 797
 798        ret = check_skip_length(boot_cfg, offset, length, &used);
 799
 800        if (ret < 0)
 801                return ret;
 802
 803        /* get next image address */
 804        bs_cfg->next_bs_addr = (u32)(offset + used + mtd->erasesize - 1)
 805                                 / (u32)mtd->erasesize * mtd->erasesize;
 806
 807        return ret;
 808}
 809
 810static int nandbcb_write_bs_skip_bad(struct boot_config *boot_cfg,
 811                                     struct boot_stream_config *bs_cfg)
 812{
 813        struct mtd_info *mtd;
 814        void *buf;
 815        loff_t offset, maxsize;
 816        size_t size;
 817        size_t length;
 818        int ret;
 819        bool padding_flag = false;
 820
 821        mtd = boot_cfg->mtd;
 822        offset = bs_cfg->bs_addr;
 823        maxsize = boot_cfg->maxsize;
 824        size = bs_cfg->bs_size;
 825
 826        /* some boot images may need leading offset */
 827        if (bs_cfg->need_padding &&
 828            (plat_config.misc_flags & FIRMWARE_NEED_PADDING))
 829                padding_flag = 1;
 830
 831        if (padding_flag)
 832                length = ALIGN(size + FLASH_OFFSET_STANDARD, mtd->writesize);
 833        else
 834                length = ALIGN(size, mtd->writesize);
 835
 836        buf = kzalloc(length, GFP_KERNEL);
 837        if (!buf) {
 838                printf("failed to allocate buffer for firmware\n");
 839                ret = -ENOMEM;
 840                return ret;
 841        }
 842
 843        if (padding_flag)
 844                memcpy(buf + FLASH_OFFSET_STANDARD, bs_cfg->bs_buf, size);
 845        else
 846                memcpy(buf, bs_cfg->bs_buf, size);
 847
 848        ret = nand_write_skip_bad(mtd, offset, &length, NULL, maxsize,
 849                                  (u_char *)buf, WITH_WR_VERIFY);
 850        printf("Write %s @0x%llx offset, 0x%zx bytes written: %s\n",
 851               bs_cfg->bs_label, offset, length, ret ? "ERROR" : "OK");
 852
 853        if (ret)
 854                /* write image failed, quit */
 855                goto err;
 856
 857        /* get next good blk address if needed */
 858        if (bs_cfg->need_padding) {
 859                ret = nandbcb_get_next_good_blk_addr(boot_cfg, bs_cfg);
 860                if (ret < 0) {
 861                        printf("Next image cannot fit in NAND partition\n");
 862                        goto err;
 863                }
 864        }
 865
 866        /* now we know how the exact image size written to NAND */
 867        bs_cfg->bs_size = length;
 868        return 0;
 869err:
 870        kfree(buf);
 871        return ret;
 872}
 873
 874static int nandbcb_write_fw(struct boot_config *boot_cfg, u_char *buf,
 875                            int index)
 876{
 877        int i;
 878        loff_t offset;
 879        size_t size;
 880        loff_t next_bs_addr;
 881        struct boot_stream_config bs_cfg;
 882        int ret;
 883
 884        for (i = 0; i < 2; ++i) {
 885                if (!(FW_INX(i) & index))
 886                        continue;
 887
 888                if (i == 0) {
 889                        offset = boot_cfg->boot_stream1_address;
 890                        size = boot_cfg->boot_stream1_size;
 891                } else {
 892                        offset = boot_cfg->boot_stream2_address;
 893                        size = boot_cfg->boot_stream2_size;
 894                }
 895
 896                /* write Firmware*/
 897                if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) {
 898                        memset(&bs_cfg, 0, sizeof(struct boot_stream_config));
 899                        sprintf(bs_cfg.bs_label, "firmware%d", i);
 900                        bs_cfg.bs_addr = offset;
 901                        bs_cfg.bs_size = size;
 902                        bs_cfg.bs_buf = buf;
 903                        bs_cfg.need_padding = 1;
 904
 905                        ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg);
 906                        if (ret)
 907                                return ret;
 908
 909                        /* update the boot stream size */
 910                        if (i == 0)
 911                                boot_cfg->boot_stream1_size = bs_cfg.bs_size;
 912                        else
 913                                boot_cfg->boot_stream2_size = bs_cfg.bs_size;
 914
 915                } else {
 916                /* some platforms need extra firmware */
 917                        memset(&bs_cfg, 0, sizeof(struct boot_stream_config));
 918                        sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 1);
 919                        bs_cfg.bs_addr = offset;
 920                        bs_cfg.bs_size = IMX8MQ_HDMI_FW_SZ;
 921                        bs_cfg.bs_buf = buf;
 922                        bs_cfg.need_padding = 1;
 923
 924                        ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg);
 925                        if (ret)
 926                                return ret;
 927
 928                        /* update the boot stream size */
 929                        if (i == 0)
 930                                boot_cfg->boot_stream1_size = bs_cfg.bs_size;
 931                        else
 932                                boot_cfg->boot_stream2_size = bs_cfg.bs_size;
 933
 934                        /* get next image address */
 935                        next_bs_addr = bs_cfg.next_bs_addr;
 936
 937                        memset(&bs_cfg, 0, sizeof(struct boot_stream_config));
 938                        sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 2);
 939                        bs_cfg.bs_addr = next_bs_addr;
 940                        bs_cfg.bs_size = IMX8MQ_SPL_SZ;
 941                        bs_cfg.bs_buf = (u_char *)(buf + IMX8MQ_HDMI_FW_SZ);
 942                        bs_cfg.need_padding = 0;
 943
 944                        ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg);
 945                        if (ret)
 946                                return ret;
 947                }
 948        }
 949
 950        return 0;
 951}
 952
 953static int nandbcb_init(struct boot_config *boot_cfg, u_char *buf)
 954{
 955        struct mtd_info *mtd;
 956        nand_erase_options_t opts;
 957        struct fcb_block *fcb;
 958        struct dbbt_block *dbbt;
 959        void *dbbt_page, *dbbt_data_page;
 960        int ret;
 961        loff_t maxsize, off;
 962
 963        mtd = boot_cfg->mtd;
 964        maxsize = boot_cfg->maxsize;
 965        off = boot_cfg->offset;
 966
 967        /* erase */
 968        memset(&opts, 0, sizeof(opts));
 969        opts.offset = off;
 970        opts.length = maxsize - 1;
 971        ret = nand_erase_opts(mtd, &opts);
 972        if (ret) {
 973                printf("%s: erase failed (ret = %d)\n", __func__, ret);
 974                return ret;
 975        }
 976
 977        /*
 978         * Reference documentation from i.MX6DQRM section 8.5.2.2
 979         *
 980         * Nand Boot Control Block(BCB) contains two data structures,
 981         * - Firmware Configuration Block(FCB)
 982         * - Discovered Bad Block Table(DBBT)
 983         *
 984         * FCB contains,
 985         * - nand timings
 986         * - DBBT search page address,
 987         * - start page address of primary firmware
 988         * - start page address of secondary firmware
 989         *
 990         * setup fcb:
 991         * - number of blocks = mtd partition size / mtd erasesize
 992         * - two firmware blocks, primary and secondary
 993         * - first 4 block for FCB/DBBT
 994         * - rest split in half for primary and secondary firmware
 995         * - same firmware write twice
 996         */
 997
 998        /* write Firmware*/
 999        ret = nandbcb_write_fw(boot_cfg, buf, FW_ALL);
1000        if (ret)
1001                goto err;
1002
1003        /* fill fcb */
1004        fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
1005        if (!fcb) {
1006                debug("failed to allocate fcb\n");
1007                ret = -ENOMEM;
1008                return ret;
1009        }
1010        fill_fcb(fcb, boot_cfg);
1011
1012        ret = write_fcb(boot_cfg, fcb);
1013
1014        /* fill dbbt */
1015        dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
1016        if (!dbbt_page) {
1017                debug("failed to allocate dbbt_page\n");
1018                ret = -ENOMEM;
1019                goto fcb_err;
1020        }
1021
1022        dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
1023        if (!dbbt_data_page) {
1024                debug("failed to allocate dbbt_data_page\n");
1025                ret = -ENOMEM;
1026                goto dbbt_page_err;
1027        }
1028
1029        dbbt = dbbt_page;
1030        dbbt->checksum = 0;
1031        dbbt->fingerprint = DBBT_FINGERPRINT;
1032        dbbt->version = DBBT_VERSION_1;
1033        ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize));
1034        if (ret < 0)
1035                goto dbbt_data_page_err;
1036        else if (ret > 0)
1037                dbbt->dbbtpages = 1;
1038
1039        /* write dbbt */
1040        ret = write_dbbt(boot_cfg, dbbt, dbbt_data_page);
1041        if (ret < 0)
1042                printf("failed to write FCB/DBBT\n");
1043
1044dbbt_data_page_err:
1045        kfree(dbbt_data_page);
1046dbbt_page_err:
1047        kfree(dbbt_page);
1048fcb_err:
1049        kfree(fcb);
1050err:
1051        return ret;
1052}
1053
1054static int do_nandbcb_bcbonly(int argc, char *const argv[])
1055{
1056        struct fcb_block *fcb;
1057        struct dbbt_block *dbbt;
1058        struct mtd_info *mtd;
1059        nand_erase_options_t opts;
1060        size_t maxsize;
1061        loff_t off;
1062        void *dbbt_page, *dbbt_data_page;
1063        int ret;
1064        struct boot_config cfg;
1065
1066        if (argc < 4)
1067                return CMD_RET_USAGE;
1068
1069        memset(&cfg, 0, sizeof(struct boot_config));
1070        if (nandbcb_get_info(argc, argv, &cfg))
1071                return CMD_RET_FAILURE;
1072
1073        /* only get the partition info */
1074        if (nandbcb_get_size(2, argv, 1, &cfg))
1075                return CMD_RET_FAILURE;
1076
1077        if (nandbcb_set_boot_config(argc, argv, &cfg))
1078                return CMD_RET_FAILURE;
1079
1080        mtd = cfg.mtd;
1081
1082        cfg.boot_stream1_address = simple_strtoul(argv[2], NULL, 16);
1083        cfg.boot_stream1_size = simple_strtoul(argv[3], NULL, 16);
1084        cfg.boot_stream1_size = ALIGN(cfg.boot_stream1_size, mtd->writesize);
1085
1086        if (argc > 5) {
1087                cfg.boot_stream2_address = simple_strtoul(argv[4], NULL, 16);
1088                cfg.boot_stream2_size = simple_strtoul(argv[5], NULL, 16);
1089                cfg.boot_stream2_size = ALIGN(cfg.boot_stream2_size,
1090                                              mtd->writesize);
1091        }
1092
1093        /* sanity check */
1094        nandbcb_check_space(&cfg);
1095
1096        maxsize = cfg.maxsize;
1097        off = cfg.offset;
1098
1099        /* erase the previous FCB/DBBT */
1100        memset(&opts, 0, sizeof(opts));
1101        opts.offset = off;
1102        opts.length = g_boot_search_stride * 2;
1103        ret = nand_erase_opts(mtd, &opts);
1104        if (ret) {
1105                printf("%s: erase failed (ret = %d)\n", __func__, ret);
1106                return CMD_RET_FAILURE;
1107        }
1108
1109        /* fill fcb */
1110        fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
1111        if (!fcb) {
1112                printf("failed to allocate fcb\n");
1113                ret = -ENOMEM;
1114                return CMD_RET_FAILURE;
1115        }
1116
1117        fill_fcb(fcb, &cfg);
1118
1119        /* write fcb */
1120        ret = write_fcb(&cfg, fcb);
1121
1122        /* fill dbbt */
1123        dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
1124        if (!dbbt_page) {
1125                printf("failed to allocate dbbt_page\n");
1126                ret = -ENOMEM;
1127                goto fcb_err;
1128        }
1129
1130        dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
1131        if (!dbbt_data_page) {
1132                printf("failed to allocate dbbt_data_page\n");
1133                ret = -ENOMEM;
1134                goto dbbt_page_err;
1135        }
1136
1137        dbbt = dbbt_page;
1138        dbbt->checksum = 0;
1139        dbbt->fingerprint = DBBT_FINGERPRINT;
1140        dbbt->version = DBBT_VERSION_1;
1141        ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize));
1142        if (ret < 0)
1143                goto dbbt_data_page_err;
1144        else if (ret > 0)
1145                dbbt->dbbtpages = 1;
1146
1147        /* write dbbt */
1148        ret = write_dbbt(&cfg, dbbt, dbbt_data_page);
1149
1150dbbt_data_page_err:
1151        kfree(dbbt_data_page);
1152dbbt_page_err:
1153        kfree(dbbt_page);
1154fcb_err:
1155        kfree(fcb);
1156
1157        if (ret < 0) {
1158                printf("failed to write FCB/DBBT\n");
1159                return CMD_RET_FAILURE;
1160        }
1161
1162        return CMD_RET_SUCCESS;
1163}
1164
1165/* dump data which is read from NAND chip */
1166void dump_structure(struct boot_config *boot_cfg, struct fcb_block *fcb,
1167                    struct dbbt_block *dbbt, void *dbbt_data_page)
1168{
1169        int i;
1170        struct mtd_info *mtd = boot_cfg->mtd;
1171
1172        #define P1(x) printf("  %s = 0x%08x\n", #x, fcb->x)
1173                printf("FCB\n");
1174                P1(checksum);
1175                P1(fingerprint);
1176                P1(version);
1177        #undef P1
1178        #define P1(x)   printf("  %s = %d\n", #x, fcb->x)
1179                P1(datasetup);
1180                P1(datahold);
1181                P1(addr_setup);
1182                P1(dsample_time);
1183                P1(pagesize);
1184                P1(oob_pagesize);
1185                P1(sectors);
1186                P1(nr_nand);
1187                P1(nr_die);
1188                P1(celltype);
1189                P1(ecc_type);
1190                P1(ecc_nr);
1191                P1(ecc_size);
1192                P1(ecc_level);
1193                P1(meta_size);
1194                P1(nr_blocks);
1195                P1(ecc_type_sdk);
1196                P1(ecc_nr_sdk);
1197                P1(ecc_size_sdk);
1198                P1(ecc_level_sdk);
1199                P1(nr_blocks_sdk);
1200                P1(meta_size_sdk);
1201                P1(erase_th);
1202                P1(bootpatch);
1203                P1(patch_size);
1204                P1(fw1_start);
1205                P1(fw2_start);
1206                P1(fw1_pages);
1207                P1(fw2_pages);
1208                P1(dbbt_start);
1209                P1(bb_byte);
1210                P1(bb_start_bit);
1211                P1(phy_offset);
1212                P1(bchtype);
1213                P1(readlatency);
1214                P1(predelay);
1215                P1(cedelay);
1216                P1(postdelay);
1217                P1(cmdaddpause);
1218                P1(datapause);
1219                P1(tmspeed);
1220                P1(busytimeout);
1221                P1(disbbm);
1222                P1(spare_offset);
1223#if !defined(CONFIG_MX6) || defined(CONFIG_MX6SX) || \
1224        defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
1225                P1(onfi_sync_enable);
1226                P1(onfi_sync_speed);
1227                P1(onfi_sync_nand_data);
1228                P1(disbbm_search);
1229                P1(disbbm_search_limit);
1230                P1(read_retry_enable);
1231#endif
1232        #undef P1
1233        #define P1(x)   printf("  %s = 0x%08x\n", #x, dbbt->x)
1234                printf("DBBT :\n");
1235                P1(checksum);
1236                P1(fingerprint);
1237                P1(version);
1238        #undef P1
1239        #define P1(x)   printf("  %s = %d\n", #x, dbbt->x)
1240                P1(dbbtpages);
1241        #undef P1
1242
1243        for (i = 0; i < dbbt->dbbtpages; ++i)
1244                printf("%d ", *((u32 *)(dbbt_data_page + i)));
1245
1246        if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) {
1247                printf("Firmware: image #0 @ 0x%x size 0x%x\n",
1248                       fcb->fw1_start, fcb->fw1_pages * mtd->writesize);
1249                printf("Firmware: image #1 @ 0x%x size 0x%x\n",
1250                       fcb->fw2_start, fcb->fw2_pages * mtd->writesize);
1251        } else {
1252                printf("Firmware: image #0 @ 0x%x size 0x%x\n",
1253                       fcb->fw1_start, fcb->fw1_pages * mtd->writesize);
1254                printf("Firmware: image #1 @ 0x%x size 0x%x\n",
1255                       fcb->fw2_start, fcb->fw2_pages * mtd->writesize);
1256                /* TODO: Add extra image information */
1257        }
1258}
1259
1260static bool check_fingerprint(void *data, int fingerprint)
1261{
1262        int off = 4;
1263
1264        return (*(int *)(data + off) == fingerprint);
1265}
1266
1267static int fuse_to_search_count(u32 bank, u32 word, u32 mask, u32 off)
1268{
1269        int err;
1270        u32 val;
1271        int ret;
1272
1273        /* by default, the boot search count from fuse should be 2 */
1274        err = fuse_read(bank, word, &val);
1275        if (err)
1276                return 2;
1277
1278        val = (val & mask) >> off;
1279
1280        switch (val) {
1281                case 0:
1282                        ret = 2;
1283                        break;
1284                case 1:
1285                case 2:
1286                case 3:
1287                        ret = 1 << val;
1288                        break;
1289                default:
1290                        ret = 2;
1291        }
1292
1293        return ret;
1294}
1295
1296static int nandbcb_dump(struct boot_config *boot_cfg)
1297{
1298        int i;
1299        loff_t off;
1300        struct mtd_info *mtd = boot_cfg->mtd;
1301        struct fcb_block fcb, fcb_copy;
1302        struct dbbt_block dbbt, dbbt_copy;
1303        void *dbbt_data_page, *dbbt_data_page_copy;
1304        bool fcb_not_found, dbbt_not_found;
1305        int ret = 0;
1306
1307        dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
1308        if (!dbbt_data_page) {
1309                printf("failed to allocate dbbt_data_page\n");
1310                ret = -ENOMEM;
1311                return ret;
1312        }
1313
1314        dbbt_data_page_copy = kzalloc(mtd->writesize, GFP_KERNEL);
1315        if (!dbbt_data_page_copy) {
1316                printf("failed to allocate dbbt_data_page\n");
1317                ret = -ENOMEM;
1318                goto dbbt_page_err;
1319        }
1320
1321        /* read fcb */
1322        fcb_not_found = 1;
1323        off = 0;
1324        for (i = 0; i < g_boot_search_count; ++i) {
1325                if (fcb_not_found) {
1326                        ret = read_fcb(boot_cfg, &fcb, off);
1327
1328                        if (ret < 0)
1329                                goto dbbt_page_copy_err;
1330                        else if (ret == 1)
1331                                continue;
1332                        else if (ret == 0)
1333                                if (check_fingerprint(&fcb, FCB_FINGERPRINT))
1334                                        fcb_not_found = 0;
1335                } else {
1336                        ret = read_fcb(boot_cfg, &fcb_copy, off);
1337
1338                        if (ret < 0)
1339                                goto dbbt_page_copy_err;
1340                        if (memcmp(&fcb, &fcb_copy,
1341                                   sizeof(struct fcb_block))) {
1342                                printf("FCB copies are not identical\n");
1343                                ret = -EINVAL;
1344                                goto dbbt_page_copy_err;
1345                        }
1346                }
1347
1348                /* next read location */
1349                off += g_boot_search_stride;
1350        }
1351
1352        /* read dbbt*/
1353        dbbt_not_found = 1;
1354        off = boot_cfg->search_area_size_in_bytes;
1355        for (i = 0; i < g_boot_search_count; ++i) {
1356                if (dbbt_not_found) {
1357                        ret = read_dbbt(boot_cfg, &dbbt, dbbt_data_page, off);
1358
1359                        if (ret < 0)
1360                                goto dbbt_page_copy_err;
1361                        else if (ret == 1)
1362                                continue;
1363                        else if (ret == 0)
1364                                if (check_fingerprint(&dbbt, DBBT_FINGERPRINT))
1365                                        dbbt_not_found = 0;
1366                } else {
1367                        ret = read_dbbt(boot_cfg, &dbbt_copy,
1368                                        dbbt_data_page_copy, off);
1369
1370                        if (ret < 0)
1371                                goto dbbt_page_copy_err;
1372                        if (memcmp(&dbbt, &dbbt_copy,
1373                                   sizeof(struct dbbt_block))) {
1374                                printf("DBBT copies are not identical\n");
1375                                ret = -EINVAL;
1376                                goto dbbt_page_copy_err;
1377                        }
1378                        if (dbbt.dbbtpages > 0 &&
1379                            memcmp(dbbt_data_page, dbbt_data_page_copy,
1380                                   mtd->writesize)) {
1381                                printf("DBBT data copies are not identical\n");
1382                                ret = -EINVAL;
1383                                goto dbbt_page_copy_err;
1384                        }
1385                }
1386
1387                /* next read location */
1388                off += g_boot_search_stride;
1389        }
1390
1391        dump_structure(boot_cfg, &fcb, &dbbt, dbbt_data_page);
1392
1393dbbt_page_copy_err:
1394        kfree(dbbt_data_page_copy);
1395dbbt_page_err:
1396        kfree(dbbt_data_page);
1397
1398        return ret;
1399}
1400
1401static int do_nandbcb_dump(int argc, char * const argv[])
1402{
1403        struct boot_config cfg;
1404        int ret;
1405
1406        if (argc != 2)
1407                return CMD_RET_USAGE;
1408
1409        memset(&cfg, 0, sizeof(struct boot_config));
1410        if (nandbcb_get_info(argc, argv, &cfg))
1411                return CMD_RET_FAILURE;
1412
1413        if (nandbcb_get_size(argc, argv, 1, &cfg))
1414                return CMD_RET_FAILURE;
1415
1416        if (nandbcb_set_boot_config(argc, argv, &cfg))
1417                return CMD_RET_FAILURE;
1418
1419        ret = nandbcb_dump(&cfg);
1420        if (ret)
1421                return ret;
1422
1423        return ret;
1424}
1425
1426static int do_nandbcb_init(int argc, char * const argv[])
1427{
1428        u_char *buf;
1429        size_t size;
1430        loff_t addr;
1431        char *endp;
1432        int ret;
1433        struct boot_config cfg;
1434
1435        if (argc != 4)
1436                return CMD_RET_USAGE;
1437
1438        memset(&cfg, 0, sizeof(struct boot_config));
1439        if (nandbcb_get_info(argc, argv, &cfg))
1440                return CMD_RET_FAILURE;
1441
1442        if (nandbcb_get_size(argc, argv, 2, &cfg))
1443                return CMD_RET_FAILURE;
1444        size = cfg.boot_stream1_size;
1445
1446        if (nandbcb_set_boot_config(argc, argv, &cfg))
1447                return CMD_RET_FAILURE;
1448
1449        addr = simple_strtoul(argv[1], &endp, 16);
1450        if (*argv[1] == 0 || *endp != 0)
1451                return CMD_RET_FAILURE;
1452
1453        buf = map_physmem(addr, size, MAP_WRBACK);
1454        if (!buf) {
1455                puts("failed to map physical memory\n");
1456                return CMD_RET_FAILURE;
1457        }
1458
1459        ret = nandbcb_init(&cfg, buf);
1460
1461        return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
1462}
1463
1464static int do_nandbcb(struct cmd_tbl *cmdtp, int flag, int argc,
1465                      char *const argv[])
1466{
1467        const char *cmd;
1468        int ret = 0;
1469
1470        if (argc < 3)
1471                goto usage;
1472
1473        /* check the platform config first */
1474        if (is_mx6sx()) {
1475                plat_config = imx6sx_plat_config;
1476        } else if (is_mx7()) {
1477                plat_config = imx7d_plat_config;
1478        } else if (is_mx6ul() || is_mx6ull()) {
1479                plat_config = imx6ul_plat_config;
1480        } else if (is_mx6() && !is_mx6sx() && !is_mx6ul() && !is_mx6ull()) {
1481                plat_config = imx6qdl_plat_config;
1482        } else if (is_imx8mq()) {
1483                plat_config = imx8mq_plat_config;
1484        } else if (is_imx8mm()) {
1485                plat_config = imx8mm_plat_config;
1486        } else if (is_imx8mn()) {
1487                plat_config = imx8mn_plat_config;
1488        } else if (is_imx8qm() || is_imx8qxp()) {
1489                plat_config = imx8q_plat_config;
1490        } else {
1491                printf("ERROR: Unknown platform\n");
1492                return CMD_RET_FAILURE;
1493        }
1494
1495        if (plat_config.misc_flags & BT_SEARCH_CNT_FROM_FUSE) {
1496                if (is_imx8qxp()) {
1497                        g_boot_search_count = fuse_to_search_count(0, 720,
1498                                                                   0xc0, 6);
1499                        printf("search count set to %d from fuse\n",
1500                               g_boot_search_count);
1501                }
1502        }
1503
1504        cmd = argv[1];
1505        --argc;
1506        ++argv;
1507
1508        if (strcmp(cmd, "init") == 0) {
1509                ret = do_nandbcb_init(argc, argv);
1510                goto done;
1511        }
1512
1513        if (strcmp(cmd, "dump") == 0) {
1514                ret = do_nandbcb_dump(argc, argv);
1515                goto done;
1516        }
1517
1518        if (strcmp(cmd, "bcbonly") == 0) {
1519                ret = do_nandbcb_bcbonly(argc, argv);
1520                goto done;
1521        }
1522
1523done:
1524        if (ret != -1)
1525                return ret;
1526usage:
1527        return CMD_RET_USAGE;
1528}
1529
1530#ifdef CONFIG_SYS_LONGHELP
1531static char nandbcb_help_text[] =
1532        "init addr off|partition len - update 'len' bytes starting at\n"
1533        "       'off|part' to memory address 'addr', skipping  bad blocks\n"
1534        "nandbcb bcbonly off|partition fw1-off fw1-size [fw2-off fw2-size]\n"
1535        "           - write BCB only (FCB and DBBT)\n"
1536        "       where `fwx-size` is fw sizes in bytes, `fw1-off`\n"
1537        "       and `fw2-off` - firmware offsets\n"
1538        "       FIY, BCB isn't erased automatically, so mtd erase should\n"
1539        "       be called in advance before writing new BCB:\n"
1540        "           > mtd erase mx7-bcb\n"
1541        "nandbcb dump off|partition - dump/verify boot structures\n";
1542#endif
1543
1544U_BOOT_CMD(nandbcb, 7, 1, do_nandbcb,
1545           "i.MX NAND Boot Control Blocks write",
1546           nandbcb_help_text
1547);
1548