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