uboot/arch/arm/mach-imx/cmd_nandbcb.c
<<
>>
Prefs
   1/*
   2 * i.MX6 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 * SPDX-License-Identifier:     GPL-2.0+
  10 */
  11
  12#include <common.h>
  13#include <nand.h>
  14
  15#include <asm/io.h>
  16#include <jffs2/jffs2.h>
  17#include <linux/bch.h>
  18#include <linux/mtd/mtd.h>
  19
  20#include <asm/arch/sys_proto.h>
  21#include <asm/mach-imx/imx-nandbcb.h>
  22#include <asm/mach-imx/imximage.cfg>
  23#include <mxs_nand.h>
  24#include <linux/mtd/mtd.h>
  25#include <nand.h>
  26
  27#include "../../../cmd/legacy-mtd-utils.h"
  28
  29#define BF_VAL(v, bf)           (((v) & bf##_MASK) >> bf##_OFFSET)
  30#define GETBIT(v, n)            (((v) >> (n)) & 0x1)
  31
  32#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
  33static uint8_t reverse_bit(uint8_t b)
  34{
  35        b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
  36        b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
  37        b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
  38
  39        return b;
  40}
  41
  42static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits)
  43{
  44        int i, j, m = 13;
  45        int blocksize = 128;
  46        int numblocks = 8;
  47        int ecc_buf_size = (m * eccbits + 7) / 8;
  48        struct bch_control *bch = init_bch(m, eccbits, 0);
  49        u8 *ecc_buf = kzalloc(ecc_buf_size, GFP_KERNEL);
  50        u8 *tmp_buf = kzalloc(blocksize * numblocks, GFP_KERNEL);
  51        u8 *psrc, *pdst;
  52
  53        /*
  54         * The blocks here are bit aligned. If eccbits is a multiple of 8,
  55         * we just can copy bytes. Otherwiese we must move the blocks to
  56         * the next free bit position.
  57         */
  58        WARN_ON(eccbits % 8);
  59
  60        memcpy(tmp_buf, fcb, sizeof(*fcb));
  61
  62        for (i = 0; i < numblocks; i++) {
  63                memset(ecc_buf, 0, ecc_buf_size);
  64                psrc = tmp_buf + i * blocksize;
  65                pdst = buf + i * (blocksize + ecc_buf_size);
  66
  67                /* copy data byte aligned to destination buf */
  68                memcpy(pdst, psrc, blocksize);
  69
  70                /*
  71                 * imx-kobs use a modified encode_bch which reverse the
  72                 * bit order of the data before calculating bch.
  73                 * Do this in the buffer and use the bch lib here.
  74                 */
  75                for (j = 0; j < blocksize; j++)
  76                        psrc[j] = reverse_bit(psrc[j]);
  77
  78                encode_bch(bch, psrc, blocksize, ecc_buf);
  79
  80                /* reverse ecc bit */
  81                for (j = 0; j < ecc_buf_size; j++)
  82                        ecc_buf[j] = reverse_bit(ecc_buf[j]);
  83
  84                /* Here eccbuf is byte aligned and we can just copy it */
  85                memcpy(pdst + blocksize, ecc_buf, ecc_buf_size);
  86        }
  87
  88        kfree(ecc_buf);
  89        kfree(tmp_buf);
  90        free_bch(bch);
  91}
  92#else
  93
  94static u8 calculate_parity_13_8(u8 d)
  95{
  96        u8 p = 0;
  97
  98        p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
  99        p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^
 100              GETBIT(d, 1)) << 1;
 101        p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^
 102              GETBIT(d, 0)) << 2;
 103        p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
 104        p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^
 105              GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
 106
 107        return p;
 108}
 109
 110static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
 111{
 112        int i;
 113        u8 *src = _src;
 114        u8 *ecc = _ecc;
 115
 116        for (i = 0; i < size; i++)
 117                ecc[i] = calculate_parity_13_8(src[i]);
 118}
 119#endif
 120
 121static u32 calc_chksum(void *buf, size_t size)
 122{
 123        u32 chksum = 0;
 124        u8 *bp = buf;
 125        size_t i;
 126
 127        for (i = 0; i < size; i++)
 128                chksum += bp[i];
 129
 130        return ~chksum;
 131}
 132
 133static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd,
 134                     u32 fw1_start, u32 fw2_start, u32 fw_pages)
 135{
 136        struct nand_chip *chip = mtd_to_nand(mtd);
 137        struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
 138        struct mxs_nand_layout l;
 139
 140        mxs_nand_get_layout(mtd, &l);
 141
 142        fcb->fingerprint = FCB_FINGERPRINT;
 143        fcb->version = FCB_VERSION_1;
 144
 145        fcb->pagesize = mtd->writesize;
 146        fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
 147        fcb->sectors = mtd->erasesize / mtd->writesize;
 148
 149        fcb->meta_size = l.meta_size;
 150        fcb->nr_blocks = l.nblocks;
 151        fcb->ecc_nr = l.data0_size;
 152        fcb->ecc_level = l.ecc0;
 153        fcb->ecc_size = l.datan_size;
 154        fcb->ecc_type = l.eccn;
 155
 156        /* Also hardcoded in kobs-ng */
 157        if (is_mx6()) {
 158                fcb->datasetup = 80;
 159                fcb->datahold = 60;
 160                fcb->addr_setup = 25;
 161                fcb->dsample_time = 6;
 162        } else if (is_mx7()) {
 163                fcb->datasetup = 10;
 164                fcb->datahold = 7;
 165                fcb->addr_setup = 15;
 166                fcb->dsample_time = 6;
 167        }
 168
 169        /* DBBT search area starts at second page on first block */
 170        fcb->dbbt_start = 1;
 171
 172        fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
 173        fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
 174
 175        fcb->phy_offset = mtd->writesize;
 176
 177        fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
 178
 179        fcb->disbbm = 0;
 180        fcb->disbbm_search = 0;
 181
 182        fcb->fw1_start = fw1_start; /* Firmware image starts on this sector */
 183        fcb->fw2_start = fw2_start; /* Secondary FW Image starting Sector */
 184        fcb->fw1_pages = fw_pages; /* Number of sectors in firmware image */
 185        fcb->fw2_pages = fw_pages; /* Number of sector in secondary FW image */
 186
 187        fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
 188}
 189
 190static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
 191{
 192        int n, n_bad_blocks = 0;
 193        u32 *bb = buf + 0x8;
 194        u32 *n_bad_blocksp = buf + 0x4;
 195
 196        for (n = 0; n < num_blocks; n++) {
 197                loff_t offset = n * mtd->erasesize;
 198                        if (mtd_block_isbad(mtd, offset)) {
 199                                n_bad_blocks++;
 200                                *bb = n;
 201                                bb++;
 202                }
 203        }
 204
 205        *n_bad_blocksp = n_bad_blocks;
 206
 207        return n_bad_blocks;
 208}
 209
 210static int write_fcb_dbbt(struct mtd_info *mtd, struct fcb_block *fcb,
 211                          struct dbbt_block *dbbt, void *dbbt_data_page,
 212                          loff_t off)
 213{
 214        void *fcb_raw_page = 0;
 215        int i, ret;
 216        size_t dummy;
 217
 218        /*
 219         * We prepare raw page only for i.MX6, for i.MX7 we
 220         * leverage BCH hw module instead
 221         */
 222        if (is_mx6()) {
 223                /* write fcb/dbbt */
 224                fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize,
 225                                       GFP_KERNEL);
 226                if (!fcb_raw_page) {
 227                        debug("failed to allocate fcb_raw_page\n");
 228                        ret = -ENOMEM;
 229                        return ret;
 230                }
 231
 232#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
 233                /* 40 bit BCH, for i.MX6UL(L) */
 234                encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
 235#else
 236                memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
 237                encode_hamming_13_8(fcb_raw_page + 12,
 238                                    fcb_raw_page + 12 + 512, 512);
 239#endif
 240                /*
 241                 * Set the first and second byte of OOB data to 0xFF,
 242                 * not 0x00. These bytes are used as the Manufacturers Bad
 243                 * Block Marker (MBBM). Since the FCB is mostly written to
 244                 * the first page in a block, a scan for
 245                 * factory bad blocks will detect these blocks as bad, e.g.
 246                 * when function nand_scan_bbt() is executed to build a new
 247                 * bad block table.
 248                 */
 249                memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
 250        }
 251        for (i = 0; i < 2; i++) {
 252                if (mtd_block_isbad(mtd, off)) {
 253                        printf("Block %d is bad, skipped\n", i);
 254                        continue;
 255                }
 256
 257                /*
 258                 * User BCH ECC hardware module for i.MX7
 259                 */
 260                if (is_mx7()) {
 261                        u32 off = i * mtd->erasesize;
 262                        size_t rwsize = sizeof(*fcb);
 263
 264                        printf("Writing %d bytes to 0x%x: ", rwsize, off);
 265
 266                        /* switch nand BCH to FCB compatible settings */
 267                        mxs_nand_mode_fcb(mtd);
 268                        ret = nand_write(mtd, off, &rwsize,
 269                                         (unsigned char *)fcb);
 270                        mxs_nand_mode_normal(mtd);
 271
 272                        printf("%s\n", ret ? "ERROR" : "OK");
 273                } else if (is_mx6()) {
 274                        /* raw write */
 275                        mtd_oob_ops_t ops = {
 276                                .datbuf = (u8 *)fcb_raw_page,
 277                                .oobbuf = ((u8 *)fcb_raw_page) +
 278                                          mtd->writesize,
 279                                .len = mtd->writesize,
 280                                .ooblen = mtd->oobsize,
 281                                .mode = MTD_OPS_RAW
 282                        };
 283
 284                        ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
 285                        if (ret)
 286                                goto fcb_raw_page_err;
 287                        debug("NAND fcb write: 0x%x offset 0x%x written: %s\n",
 288                              mtd->erasesize * i, ops.len, ret ?
 289                              "ERROR" : "OK");
 290                }
 291
 292                ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
 293                                mtd->writesize, &dummy, (void *)dbbt);
 294                if (ret)
 295                        goto fcb_raw_page_err;
 296                debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n",
 297                      mtd->erasesize * i + mtd->writesize, dummy,
 298                      ret ? "ERROR" : "OK");
 299
 300                /* dbbtpages == 0 if no bad blocks */
 301                if (dbbt->dbbtpages > 0) {
 302                        loff_t to = (mtd->erasesize * i + mtd->writesize * 5);
 303
 304                        ret = mtd_write(mtd, to, mtd->writesize, &dummy,
 305                                        dbbt_data_page);
 306                        if (ret)
 307                                goto fcb_raw_page_err;
 308                }
 309        }
 310
 311fcb_raw_page_err:
 312        if (is_mx6())
 313                kfree(fcb_raw_page);
 314
 315        return ret;
 316}
 317
 318static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
 319                          size_t maxsize, const u_char *buf)
 320{
 321        nand_erase_options_t opts;
 322        struct fcb_block *fcb;
 323        struct dbbt_block *dbbt;
 324        loff_t fw1_off;
 325        void *fwbuf, *dbbt_page, *dbbt_data_page;
 326        u32 fw1_start, fw1_pages;
 327        int nr_blks, nr_blks_fcb, fw1_blk;
 328        size_t fwsize;
 329        int ret;
 330
 331        /* erase */
 332        memset(&opts, 0, sizeof(opts));
 333        opts.offset = off;
 334        opts.length = maxsize - 1;
 335        ret = nand_erase_opts(mtd, &opts);
 336        if (ret) {
 337                printf("%s: erase failed (ret = %d)\n", __func__, ret);
 338                return ret;
 339        }
 340
 341        /*
 342         * Reference documentation from i.MX6DQRM section 8.5.2.2
 343         *
 344         * Nand Boot Control Block(BCB) contains two data structures,
 345         * - Firmware Configuration Block(FCB)
 346         * - Discovered Bad Block Table(DBBT)
 347         *
 348         * FCB contains,
 349         * - nand timings
 350         * - DBBT search page address,
 351         * - start page address of primary firmware
 352         * - start page address of secondary firmware
 353         *
 354         * setup fcb:
 355         * - number of blocks = mtd partition size / mtd erasesize
 356         * - two firmware blocks, primary and secondary
 357         * - first 4 block for FCB/DBBT
 358         * - rest split in half for primary and secondary firmware
 359         * - same firmware will write two times
 360         */
 361        nr_blks_fcb = 2;
 362        nr_blks = maxsize / mtd->erasesize;
 363        fw1_blk = nr_blks_fcb;
 364
 365        /* write fw */
 366        fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
 367                       mtd->writesize);
 368        fwbuf = kzalloc(fwsize, GFP_KERNEL);
 369        if (!fwbuf) {
 370                debug("failed to allocate fwbuf\n");
 371                ret = -ENOMEM;
 372                goto err;
 373        }
 374
 375        memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
 376        fw1_off = fw1_blk * mtd->erasesize;
 377        ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
 378                                  (u_char *)fwbuf, WITH_WR_VERIFY);
 379        printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
 380               fw1_off, fwsize, ret ? "ERROR" : "OK");
 381        if (ret)
 382                goto fwbuf_err;
 383
 384        /* fill fcb */
 385        fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
 386        if (!fcb) {
 387                debug("failed to allocate fcb\n");
 388                ret = -ENOMEM;
 389                goto fwbuf_err;
 390        }
 391
 392        fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize;
 393        fw1_pages = size / mtd->writesize + 1;
 394        fill_fcb(fcb, mtd, fw1_start, 0, fw1_pages);
 395
 396        /* fill dbbt */
 397        dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
 398        if (!dbbt_page) {
 399                debug("failed to allocate dbbt_page\n");
 400                ret = -ENOMEM;
 401                goto fcb_err;
 402        }
 403
 404        dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
 405        if (!dbbt_data_page) {
 406                debug("failed to allocate dbbt_data_page\n");
 407                ret = -ENOMEM;
 408                goto dbbt_page_err;
 409        }
 410
 411        dbbt = dbbt_page;
 412        dbbt->checksum = 0;
 413        dbbt->fingerprint = DBBT_FINGERPRINT2;
 414        dbbt->version = DBBT_VERSION_1;
 415        ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
 416        if (ret < 0)
 417                goto dbbt_data_page_err;
 418        else if (ret > 0)
 419                dbbt->dbbtpages = 1;
 420
 421        /* write fcb and dbbt to nand */
 422        ret = write_fcb_dbbt(mtd, fcb, dbbt, dbbt_data_page, off);
 423        if (ret < 0)
 424                printf("failed to write FCB/DBBT\n");
 425
 426dbbt_data_page_err:
 427        kfree(dbbt_data_page);
 428dbbt_page_err:
 429        kfree(dbbt_page);
 430fcb_err:
 431        kfree(fcb);
 432fwbuf_err:
 433        kfree(fwbuf);
 434err:
 435        return ret;
 436}
 437
 438static int do_nandbcb_bcbonly(int argc, char * const argv[])
 439{
 440        struct fcb_block *fcb;
 441        struct dbbt_block *dbbt;
 442        u32 fw_len, fw1_off, fw2_off;
 443        struct mtd_info *mtd;
 444        void *dbbt_page, *dbbt_data_page;
 445        int dev, ret;
 446
 447        dev = nand_curr_device;
 448        if ((dev < 0) || (dev >= CONFIG_SYS_MAX_NAND_DEVICE) ||
 449            (!get_nand_dev_by_index(dev))) {
 450                puts("No devices available\n");
 451                return CMD_RET_FAILURE;
 452        }
 453
 454        mtd = get_nand_dev_by_index(dev);
 455
 456        if (argc < 3)
 457                return CMD_RET_FAILURE;
 458
 459        fw_len = simple_strtoul(argv[1], NULL, 16);
 460        fw1_off = simple_strtoul(argv[2], NULL, 16);
 461
 462        if (argc > 3)
 463                fw2_off = simple_strtoul(argv[3], NULL, 16);
 464        else
 465                fw2_off = fw1_off;
 466
 467        /* fill fcb */
 468        fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
 469        if (!fcb) {
 470                debug("failed to allocate fcb\n");
 471                ret = -ENOMEM;
 472                return CMD_RET_FAILURE;
 473        }
 474
 475        fill_fcb(fcb, mtd, fw1_off / mtd->writesize,
 476                 fw2_off / mtd->writesize, fw_len / mtd->writesize);
 477
 478        /* fill dbbt */
 479        dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
 480        if (!dbbt_page) {
 481                debug("failed to allocate dbbt_page\n");
 482                ret = -ENOMEM;
 483                goto fcb_err;
 484        }
 485
 486        dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
 487        if (!dbbt_data_page) {
 488                debug("failed to allocate dbbt_data_page\n");
 489                ret = -ENOMEM;
 490                goto dbbt_page_err;
 491        }
 492
 493        dbbt = dbbt_page;
 494        dbbt->checksum = 0;
 495        dbbt->fingerprint = DBBT_FINGERPRINT2;
 496        dbbt->version = DBBT_VERSION_1;
 497        ret = dbbt_fill_data(mtd, dbbt_data_page, 0);
 498        if (ret < 0)
 499                goto dbbt_data_page_err;
 500        else if (ret > 0)
 501                dbbt->dbbtpages = 1;
 502
 503        /* write fcb and dbbt to nand */
 504        ret = write_fcb_dbbt(mtd, fcb, dbbt, dbbt_data_page, 0);
 505dbbt_data_page_err:
 506        kfree(dbbt_data_page);
 507dbbt_page_err:
 508        kfree(dbbt_page);
 509fcb_err:
 510        kfree(fcb);
 511
 512        if (ret < 0) {
 513                printf("failed to write FCB/DBBT\n");
 514                return CMD_RET_FAILURE;
 515        }
 516
 517        return CMD_RET_SUCCESS;
 518}
 519
 520static int do_nandbcb_update(int argc, char * const argv[])
 521{
 522        struct mtd_info *mtd;
 523        loff_t addr, offset, size, maxsize;
 524        char *endp;
 525        u_char *buf;
 526        int dev;
 527        int ret;
 528
 529        if (argc != 4)
 530                return CMD_RET_USAGE;
 531
 532        dev = nand_curr_device;
 533        if (dev < 0) {
 534                printf("failed to get nand_curr_device, run nand device\n");
 535                return CMD_RET_FAILURE;
 536        }
 537
 538        addr = simple_strtoul(argv[1], &endp, 16);
 539        if (*argv[1] == 0 || *endp != 0)
 540                return CMD_RET_FAILURE;
 541
 542        mtd = get_nand_dev_by_index(dev);
 543        if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
 544                             &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
 545                return CMD_RET_FAILURE;
 546
 547        buf = map_physmem(addr, size, MAP_WRBACK);
 548        if (!buf) {
 549                puts("failed to map physical memory\n");
 550                return CMD_RET_FAILURE;
 551        }
 552
 553        ret = nandbcb_update(mtd, offset, size, maxsize, buf);
 554
 555        return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
 556}
 557
 558static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
 559                      char * const argv[])
 560{
 561        const char *cmd;
 562        int ret = 0;
 563
 564        if (argc < 5)
 565                goto usage;
 566
 567        cmd = argv[1];
 568        --argc;
 569        ++argv;
 570
 571        if (strcmp(cmd, "update") == 0) {
 572                ret = do_nandbcb_update(argc, argv);
 573                goto done;
 574        }
 575
 576        if (strcmp(cmd, "bcbonly") == 0) {
 577                ret = do_nandbcb_bcbonly(argc, argv);
 578                goto done;
 579        }
 580
 581done:
 582        if (ret != -1)
 583                return ret;
 584usage:
 585        return CMD_RET_USAGE;
 586}
 587
 588#ifdef CONFIG_SYS_LONGHELP
 589static char nandbcb_help_text[] =
 590        "update addr off|partition len  - update 'len' bytes starting at\n"
 591        "       'off|part' to memory address 'addr', skipping  bad blocks\n"
 592        "bcbonly fw-size fw1-off [fw2-off] - write only BCB (FCB and DBBT)\n"
 593        "       where `fw-size` is fw sizes in bytes, `fw1-off` and\n"
 594        "       and `fw2-off` - firmware offsets                ";
 595#endif
 596
 597U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb,
 598           "i.MX6 Nand BCB",
 599           nandbcb_help_text
 600);
 601