blob: 02a65ffd438e8c604f6fd0dd4394ce4968a28503 [file] [log] [blame]
Shyam Saini1d43e242019-06-14 13:05:33 +05301/*
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>
Simon Glass336d4612020-02-03 07:36:16 -070013#include <malloc.h>
Shyam Saini1d43e242019-06-14 13:05:33 +053014#include <nand.h>
Simon Glass61b29b82020-02-03 07:36:15 -070015#include <dm/devres.h>
Shyam Saini1d43e242019-06-14 13:05:33 +053016
17#include <asm/io.h>
18#include <jffs2/jffs2.h>
Parthiban Nallathambi6aa87492019-10-18 11:46:19 +020019#include <linux/bch.h>
Shyam Saini1d43e242019-06-14 13:05:33 +053020#include <linux/mtd/mtd.h>
21
Igor Opaniukdad30dd2019-11-03 16:49:44 +010022#include <asm/arch/sys_proto.h>
Shyam Saini1d43e242019-06-14 13:05:33 +053023#include <asm/mach-imx/imx-nandbcb.h>
24#include <asm/mach-imx/imximage.cfg>
25#include <mxs_nand.h>
26#include <linux/mtd/mtd.h>
27#include <nand.h>
Alice Guo0b103372020-05-05 22:04:01 +080028#include <div64.h>
Shyam Saini1d43e242019-06-14 13:05:33 +053029
Miquel Raynaleb446ef2019-10-25 19:39:29 +020030#include "../../../cmd/legacy-mtd-utils.h"
31
Shyam Saini1d43e242019-06-14 13:05:33 +053032#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
33#define GETBIT(v, n) (((v) >> (n)) & 0x1)
Alice Guo66dbd9c2020-05-05 22:04:00 +080034#define IMX8MQ_SPL_SZ 0x3e000
35#define IMX8MQ_HDMI_FW_SZ 0x19c00
Alice Guo0b103372020-05-05 22:04:01 +080036#define BOOT_SEARCH_COUNT 2
37
38struct mtd_info *dump_mtd;
39static loff_t dump_nandboot_size;
40static struct fcb_block dump_fill_fcb;
41static struct dbbt_block dump_fill_dbbt;
42static struct fcb_block dump_nand_fcb[BOOT_SEARCH_COUNT];
43static struct dbbt_block dump_nand_dbbt[BOOT_SEARCH_COUNT];
44static u32 dump_fcb_off[BOOT_SEARCH_COUNT];
45static u32 dump_dbbt_off[BOOT_SEARCH_COUNT];
Shyam Saini1d43e242019-06-14 13:05:33 +053046
Parthiban Nallathambi6aa87492019-10-18 11:46:19 +020047#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
48static uint8_t reverse_bit(uint8_t b)
49{
50 b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
51 b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
52 b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
53
54 return b;
55}
56
57static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits)
58{
59 int i, j, m = 13;
60 int blocksize = 128;
61 int numblocks = 8;
62 int ecc_buf_size = (m * eccbits + 7) / 8;
63 struct bch_control *bch = init_bch(m, eccbits, 0);
64 u8 *ecc_buf = kzalloc(ecc_buf_size, GFP_KERNEL);
65 u8 *tmp_buf = kzalloc(blocksize * numblocks, GFP_KERNEL);
66 u8 *psrc, *pdst;
67
68 /*
69 * The blocks here are bit aligned. If eccbits is a multiple of 8,
70 * we just can copy bytes. Otherwiese we must move the blocks to
71 * the next free bit position.
72 */
73 WARN_ON(eccbits % 8);
74
75 memcpy(tmp_buf, fcb, sizeof(*fcb));
76
77 for (i = 0; i < numblocks; i++) {
78 memset(ecc_buf, 0, ecc_buf_size);
79 psrc = tmp_buf + i * blocksize;
80 pdst = buf + i * (blocksize + ecc_buf_size);
81
82 /* copy data byte aligned to destination buf */
83 memcpy(pdst, psrc, blocksize);
84
85 /*
86 * imx-kobs use a modified encode_bch which reverse the
87 * bit order of the data before calculating bch.
88 * Do this in the buffer and use the bch lib here.
89 */
90 for (j = 0; j < blocksize; j++)
91 psrc[j] = reverse_bit(psrc[j]);
92
93 encode_bch(bch, psrc, blocksize, ecc_buf);
94
95 /* reverse ecc bit */
96 for (j = 0; j < ecc_buf_size; j++)
97 ecc_buf[j] = reverse_bit(ecc_buf[j]);
98
99 /* Here eccbuf is byte aligned and we can just copy it */
100 memcpy(pdst + blocksize, ecc_buf, ecc_buf_size);
101 }
102
103 kfree(ecc_buf);
104 kfree(tmp_buf);
105 free_bch(bch);
106}
107#else
108
Shyam Saini1d43e242019-06-14 13:05:33 +0530109static u8 calculate_parity_13_8(u8 d)
110{
111 u8 p = 0;
112
113 p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
114 p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^
115 GETBIT(d, 1)) << 1;
116 p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^
117 GETBIT(d, 0)) << 2;
118 p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
119 p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^
120 GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
121
122 return p;
123}
124
125static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
126{
127 int i;
128 u8 *src = _src;
129 u8 *ecc = _ecc;
130
131 for (i = 0; i < size; i++)
132 ecc[i] = calculate_parity_13_8(src[i]);
133}
Parthiban Nallathambi6aa87492019-10-18 11:46:19 +0200134#endif
Shyam Saini1d43e242019-06-14 13:05:33 +0530135
136static u32 calc_chksum(void *buf, size_t size)
137{
138 u32 chksum = 0;
139 u8 *bp = buf;
140 size_t i;
141
142 for (i = 0; i < size; i++)
143 chksum += bp[i];
144
145 return ~chksum;
146}
147
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100148static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd,
149 u32 fw1_start, u32 fw2_start, u32 fw_pages)
Shyam Saini1d43e242019-06-14 13:05:33 +0530150{
151 struct nand_chip *chip = mtd_to_nand(mtd);
152 struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
Igor Opaniukdad30dd2019-11-03 16:49:44 +0100153 struct mxs_nand_layout l;
154
155 mxs_nand_get_layout(mtd, &l);
Shyam Saini1d43e242019-06-14 13:05:33 +0530156
157 fcb->fingerprint = FCB_FINGERPRINT;
158 fcb->version = FCB_VERSION_1;
Igor Opaniukdad30dd2019-11-03 16:49:44 +0100159
Shyam Saini1d43e242019-06-14 13:05:33 +0530160 fcb->pagesize = mtd->writesize;
161 fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
162 fcb->sectors = mtd->erasesize / mtd->writesize;
163
Igor Opaniukdad30dd2019-11-03 16:49:44 +0100164 fcb->meta_size = l.meta_size;
165 fcb->nr_blocks = l.nblocks;
166 fcb->ecc_nr = l.data0_size;
167 fcb->ecc_level = l.ecc0;
168 fcb->ecc_size = l.datan_size;
169 fcb->ecc_type = l.eccn;
Han Xuc6ed3502020-05-05 22:03:59 +0800170 fcb->bchtype = l.gf_len;
Shyam Saini1d43e242019-06-14 13:05:33 +0530171
172 /* Also hardcoded in kobs-ng */
Alice Guo66dbd9c2020-05-05 22:04:00 +0800173 if (is_mx6() || is_imx8m()) {
Igor Opaniukdad30dd2019-11-03 16:49:44 +0100174 fcb->datasetup = 80;
175 fcb->datahold = 60;
176 fcb->addr_setup = 25;
177 fcb->dsample_time = 6;
178 } else if (is_mx7()) {
179 fcb->datasetup = 10;
180 fcb->datahold = 7;
181 fcb->addr_setup = 15;
182 fcb->dsample_time = 6;
183 }
Shyam Saini1d43e242019-06-14 13:05:33 +0530184
185 /* DBBT search area starts at second page on first block */
186 fcb->dbbt_start = 1;
187
188 fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
189 fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
190
191 fcb->phy_offset = mtd->writesize;
192
193 fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
194
Igor Opaniukdad30dd2019-11-03 16:49:44 +0100195 fcb->disbbm = 0;
196 fcb->disbbm_search = 0;
197
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100198 fcb->fw1_start = fw1_start; /* Firmware image starts on this sector */
199 fcb->fw2_start = fw2_start; /* Secondary FW Image starting Sector */
200 fcb->fw1_pages = fw_pages; /* Number of sectors in firmware image */
201 fcb->fw2_pages = fw_pages; /* Number of sector in secondary FW image */
202
Shyam Saini1d43e242019-06-14 13:05:33 +0530203 fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
204}
205
206static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
207{
208 int n, n_bad_blocks = 0;
209 u32 *bb = buf + 0x8;
210 u32 *n_bad_blocksp = buf + 0x4;
211
212 for (n = 0; n < num_blocks; n++) {
213 loff_t offset = n * mtd->erasesize;
214 if (mtd_block_isbad(mtd, offset)) {
215 n_bad_blocks++;
216 *bb = n;
217 bb++;
218 }
219 }
220
221 *n_bad_blocksp = n_bad_blocks;
222
223 return n_bad_blocks;
224}
225
Alice Guo0b103372020-05-05 22:04:01 +0800226static int write_fcb_dbbt_and_readback(struct mtd_info *mtd,
227 struct fcb_block *fcb,
228 struct dbbt_block *dbbt,
229 void *dbbt_data_page, loff_t off)
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100230{
231 void *fcb_raw_page = 0;
232 int i, ret;
233 size_t dummy;
234
235 /*
236 * We prepare raw page only for i.MX6, for i.MX7 we
237 * leverage BCH hw module instead
238 */
239 if (is_mx6()) {
240 /* write fcb/dbbt */
241 fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize,
242 GFP_KERNEL);
243 if (!fcb_raw_page) {
244 debug("failed to allocate fcb_raw_page\n");
245 ret = -ENOMEM;
246 return ret;
247 }
248
249#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
250 /* 40 bit BCH, for i.MX6UL(L) */
251 encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
252#else
253 memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
254 encode_hamming_13_8(fcb_raw_page + 12,
255 fcb_raw_page + 12 + 512, 512);
256#endif
257 /*
258 * Set the first and second byte of OOB data to 0xFF,
259 * not 0x00. These bytes are used as the Manufacturers Bad
260 * Block Marker (MBBM). Since the FCB is mostly written to
261 * the first page in a block, a scan for
262 * factory bad blocks will detect these blocks as bad, e.g.
263 * when function nand_scan_bbt() is executed to build a new
264 * bad block table.
265 */
266 memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
267 }
268 for (i = 0; i < 2; i++) {
269 if (mtd_block_isbad(mtd, off)) {
270 printf("Block %d is bad, skipped\n", i);
271 continue;
272 }
273
274 /*
275 * User BCH ECC hardware module for i.MX7
276 */
Alice Guo66dbd9c2020-05-05 22:04:00 +0800277 if (is_mx7() || is_imx8m()) {
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100278 u32 off = i * mtd->erasesize;
279 size_t rwsize = sizeof(*fcb);
280
Alice Guo66dbd9c2020-05-05 22:04:00 +0800281 printf("Writing %zd bytes to 0x%x: ", rwsize, off);
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100282
283 /* switch nand BCH to FCB compatible settings */
284 mxs_nand_mode_fcb(mtd);
285 ret = nand_write(mtd, off, &rwsize,
286 (unsigned char *)fcb);
Alice Guo0b103372020-05-05 22:04:01 +0800287
288 dump_fcb_off[i] = off;
289 nand_read(mtd, off, &rwsize,
290 (unsigned char *)(dump_nand_fcb + i));
291
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100292 mxs_nand_mode_normal(mtd);
293
294 printf("%s\n", ret ? "ERROR" : "OK");
295 } else if (is_mx6()) {
296 /* raw write */
297 mtd_oob_ops_t ops = {
298 .datbuf = (u8 *)fcb_raw_page,
299 .oobbuf = ((u8 *)fcb_raw_page) +
300 mtd->writesize,
301 .len = mtd->writesize,
302 .ooblen = mtd->oobsize,
303 .mode = MTD_OPS_RAW
304 };
305
306 ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
307 if (ret)
308 goto fcb_raw_page_err;
Alice Guo66dbd9c2020-05-05 22:04:00 +0800309 debug("NAND fcb write: 0x%x offset 0x%zx written: %s\n",
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100310 mtd->erasesize * i, ops.len, ret ?
311 "ERROR" : "OK");
312 }
313
314 ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
315 mtd->writesize, &dummy, (void *)dbbt);
316 if (ret)
317 goto fcb_raw_page_err;
Alice Guo66dbd9c2020-05-05 22:04:00 +0800318 debug("NAND dbbt write: 0x%x offset, 0x%zx bytes written: %s\n",
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100319 mtd->erasesize * i + mtd->writesize, dummy,
320 ret ? "ERROR" : "OK");
321
Alice Guo0b103372020-05-05 22:04:01 +0800322 dump_dbbt_off[i] = mtd->erasesize * i + mtd->writesize;
323 size_t rwsize = sizeof(*dbbt);
324
325 nand_read(mtd, dump_dbbt_off[i], &rwsize,
326 (unsigned char *)(dump_nand_dbbt + i));
327
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100328 /* dbbtpages == 0 if no bad blocks */
329 if (dbbt->dbbtpages > 0) {
330 loff_t to = (mtd->erasesize * i + mtd->writesize * 5);
331
332 ret = mtd_write(mtd, to, mtd->writesize, &dummy,
333 dbbt_data_page);
334 if (ret)
335 goto fcb_raw_page_err;
336 }
337 }
338
339fcb_raw_page_err:
340 if (is_mx6())
341 kfree(fcb_raw_page);
342
343 return ret;
344}
345
Shyam Saini1d43e242019-06-14 13:05:33 +0530346static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
347 size_t maxsize, const u_char *buf)
348{
349 nand_erase_options_t opts;
350 struct fcb_block *fcb;
351 struct dbbt_block *dbbt;
352 loff_t fw1_off;
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100353 void *fwbuf, *dbbt_page, *dbbt_data_page;
354 u32 fw1_start, fw1_pages;
Shyam Saini1d43e242019-06-14 13:05:33 +0530355 int nr_blks, nr_blks_fcb, fw1_blk;
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100356 size_t fwsize;
357 int ret;
Alice Guo66dbd9c2020-05-05 22:04:00 +0800358 size_t extra_fwsize;
359 void *extra_fwbuf;
360 loff_t extra_fw1_off;
Shyam Saini1d43e242019-06-14 13:05:33 +0530361
362 /* erase */
363 memset(&opts, 0, sizeof(opts));
364 opts.offset = off;
365 opts.length = maxsize - 1;
366 ret = nand_erase_opts(mtd, &opts);
367 if (ret) {
368 printf("%s: erase failed (ret = %d)\n", __func__, ret);
369 return ret;
370 }
371
372 /*
373 * Reference documentation from i.MX6DQRM section 8.5.2.2
374 *
375 * Nand Boot Control Block(BCB) contains two data structures,
376 * - Firmware Configuration Block(FCB)
377 * - Discovered Bad Block Table(DBBT)
378 *
379 * FCB contains,
380 * - nand timings
381 * - DBBT search page address,
382 * - start page address of primary firmware
383 * - start page address of secondary firmware
384 *
385 * setup fcb:
386 * - number of blocks = mtd partition size / mtd erasesize
387 * - two firmware blocks, primary and secondary
388 * - first 4 block for FCB/DBBT
389 * - rest split in half for primary and secondary firmware
390 * - same firmware will write two times
391 */
Alice Guo0b103372020-05-05 22:04:01 +0800392 nr_blks_fcb = BOOT_SEARCH_COUNT;
Shyam Saini1d43e242019-06-14 13:05:33 +0530393 nr_blks = maxsize / mtd->erasesize;
394 fw1_blk = nr_blks_fcb;
395
396 /* write fw */
Alice Guo66dbd9c2020-05-05 22:04:00 +0800397 fwbuf = NULL;
398 if (is_mx6() || is_mx7()) {
399 fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
400 mtd->writesize);
401 fwbuf = kzalloc(fwsize, GFP_KERNEL);
402 if (!fwbuf) {
403 debug("failed to allocate fwbuf\n");
404 ret = -ENOMEM;
405 goto err;
406 }
Shyam Saini1d43e242019-06-14 13:05:33 +0530407
Alice Guo66dbd9c2020-05-05 22:04:00 +0800408 memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
409 fw1_off = fw1_blk * mtd->erasesize;
410 ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
411 (u_char *)fwbuf, WITH_WR_VERIFY);
412 printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n",
413 fw1_off, fwsize, ret ? "ERROR" : "OK");
414 if (ret)
415 goto fwbuf_err;
416 } else if (is_imx8m()) {
417 fwsize = ALIGN(IMX8MQ_SPL_SZ + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize);
418 fwbuf = kzalloc(fwsize, GFP_KERNEL);
419 if (!fwbuf) {
420 printf("failed to allocate fwbuf\n");
421 ret = -ENOMEM;
422 goto err;
423 }
424
425 memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, IMX8MQ_SPL_SZ);
426 fw1_off = fw1_blk * mtd->erasesize;
427 ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
428 (u_char *)fwbuf, WITH_WR_VERIFY);
429 printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n",
430 fw1_off, fwsize, ret ? "ERROR" : "OK");
431 if (ret)
432 goto fwbuf_err;
433
434 extra_fwsize = ALIGN(IMX8MQ_SPL_SZ + mtd->writesize, mtd->writesize);
435 extra_fwbuf = kzalloc(extra_fwsize, GFP_KERNEL);
436 extra_fw1_off = fw1_off + mtd->erasesize * ((IMX8MQ_SPL_SZ + mtd->erasesize - 1) / mtd->erasesize);
437 if (!extra_fwbuf) {
438 printf("failed to allocate fwbuf\n");
439 ret = -ENOMEM;
440 goto fwbuf_err;
441 }
442
443 memcpy(extra_fwbuf, buf + IMX8MQ_HDMI_FW_SZ, IMX8MQ_SPL_SZ);
444 ret = nand_write_skip_bad(mtd, extra_fw1_off, &extra_fwsize, NULL, maxsize,
445 (u_char *)extra_fwbuf, WITH_WR_VERIFY);
446 printf("NAND extra_fw write: 0x%llx offset, 0x%zx bytes written: %s\n",
447 extra_fw1_off, extra_fwsize, ret ? "ERROR" : "OK");
448 if (ret) {
449 kfree(extra_fwbuf);
450 goto fwbuf_err;
451 }
452 }
Shyam Saini1d43e242019-06-14 13:05:33 +0530453
454 /* fill fcb */
455 fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
456 if (!fcb) {
457 debug("failed to allocate fcb\n");
458 ret = -ENOMEM;
459 goto fwbuf_err;
460 }
461
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100462 fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize;
463 fw1_pages = size / mtd->writesize + 1;
Alice Guo66dbd9c2020-05-05 22:04:00 +0800464 if (is_imx8m())
465 fw1_pages = (IMX8MQ_SPL_SZ + (mtd->writesize - 1)) / mtd->writesize;
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100466 fill_fcb(fcb, mtd, fw1_start, 0, fw1_pages);
Shyam Saini1d43e242019-06-14 13:05:33 +0530467
Alice Guo0b103372020-05-05 22:04:01 +0800468 dump_fill_fcb = *fcb;
469
Shyam Saini1d43e242019-06-14 13:05:33 +0530470 /* fill dbbt */
471 dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
472 if (!dbbt_page) {
473 debug("failed to allocate dbbt_page\n");
474 ret = -ENOMEM;
475 goto fcb_err;
476 }
477
478 dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
479 if (!dbbt_data_page) {
480 debug("failed to allocate dbbt_data_page\n");
481 ret = -ENOMEM;
482 goto dbbt_page_err;
483 }
484
485 dbbt = dbbt_page;
486 dbbt->checksum = 0;
487 dbbt->fingerprint = DBBT_FINGERPRINT2;
488 dbbt->version = DBBT_VERSION_1;
489 ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
490 if (ret < 0)
491 goto dbbt_data_page_err;
492 else if (ret > 0)
493 dbbt->dbbtpages = 1;
494
Alice Guo0b103372020-05-05 22:04:01 +0800495 dump_fill_dbbt = *dbbt;
496
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100497 /* write fcb and dbbt to nand */
Alice Guo0b103372020-05-05 22:04:01 +0800498 ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, off);
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100499 if (ret < 0)
500 printf("failed to write FCB/DBBT\n");
Shyam Saini1d43e242019-06-14 13:05:33 +0530501
Shyam Saini1d43e242019-06-14 13:05:33 +0530502dbbt_data_page_err:
503 kfree(dbbt_data_page);
504dbbt_page_err:
505 kfree(dbbt_page);
506fcb_err:
507 kfree(fcb);
508fwbuf_err:
509 kfree(fwbuf);
510err:
511 return ret;
512}
513
Igor Opaniukae8a53e2019-11-03 16:49:46 +0100514static int do_nandbcb_bcbonly(int argc, char * const argv[])
515{
516 struct fcb_block *fcb;
517 struct dbbt_block *dbbt;
518 u32 fw_len, fw1_off, fw2_off;
519 struct mtd_info *mtd;
520 void *dbbt_page, *dbbt_data_page;
521 int dev, ret;
522
523 dev = nand_curr_device;
524 if ((dev < 0) || (dev >= CONFIG_SYS_MAX_NAND_DEVICE) ||
525 (!get_nand_dev_by_index(dev))) {
526 puts("No devices available\n");
527 return CMD_RET_FAILURE;
528 }
529
530 mtd = get_nand_dev_by_index(dev);
531
532 if (argc < 3)
533 return CMD_RET_FAILURE;
534
535 fw_len = simple_strtoul(argv[1], NULL, 16);
536 fw1_off = simple_strtoul(argv[2], NULL, 16);
537
538 if (argc > 3)
539 fw2_off = simple_strtoul(argv[3], NULL, 16);
540 else
541 fw2_off = fw1_off;
542
543 /* fill fcb */
544 fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
545 if (!fcb) {
546 debug("failed to allocate fcb\n");
547 ret = -ENOMEM;
548 return CMD_RET_FAILURE;
549 }
550
551 fill_fcb(fcb, mtd, fw1_off / mtd->writesize,
552 fw2_off / mtd->writesize, fw_len / mtd->writesize);
553
554 /* fill dbbt */
555 dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
556 if (!dbbt_page) {
557 debug("failed to allocate dbbt_page\n");
558 ret = -ENOMEM;
559 goto fcb_err;
560 }
561
562 dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
563 if (!dbbt_data_page) {
564 debug("failed to allocate dbbt_data_page\n");
565 ret = -ENOMEM;
566 goto dbbt_page_err;
567 }
568
569 dbbt = dbbt_page;
570 dbbt->checksum = 0;
571 dbbt->fingerprint = DBBT_FINGERPRINT2;
572 dbbt->version = DBBT_VERSION_1;
573 ret = dbbt_fill_data(mtd, dbbt_data_page, 0);
574 if (ret < 0)
575 goto dbbt_data_page_err;
576 else if (ret > 0)
577 dbbt->dbbtpages = 1;
578
579 /* write fcb and dbbt to nand */
Alice Guo0b103372020-05-05 22:04:01 +0800580 ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, 0);
Igor Opaniukae8a53e2019-11-03 16:49:46 +0100581dbbt_data_page_err:
582 kfree(dbbt_data_page);
583dbbt_page_err:
584 kfree(dbbt_page);
585fcb_err:
586 kfree(fcb);
587
588 if (ret < 0) {
589 printf("failed to write FCB/DBBT\n");
590 return CMD_RET_FAILURE;
591 }
592
593 return CMD_RET_SUCCESS;
594}
595
Alice Guo0b103372020-05-05 22:04:01 +0800596/* dump data which is planned to be encoded and written to NAND chip */
597void mtd_cfg_dump(void)
598{
599 u64 blocks;
600
601 printf("MTD CONFIG:\n");
602 printf(" %s = %d\n", "data_setup_time", dump_fill_fcb.datasetup);
603 printf(" %s = %d\n", "data_hold_time", dump_fill_fcb.datahold);
604 printf(" %s = %d\n", "address_setup_time", dump_fill_fcb.addr_setup);
605 printf(" %s = %d\n", "data_sample_time", dump_fill_fcb.dsample_time);
606
607 printf("NFC geometry :\n");
608 printf("\tECC Strength : %d\n", dump_mtd->ecc_strength);
609 printf("\tPage Size in Bytes : %d\n", dump_fill_fcb.oob_pagesize);
610 printf("\tMetadata size : %d\n", dump_fill_fcb.meta_size);
611 printf("\tECC Chunk Size in byte : %d\n", dump_fill_fcb.ecc_size);
612 printf("\tECC Chunk count : %d\n", dump_fill_fcb.nr_blocks + 1);
613 printf("\tBlock Mark Byte Offset : %d\n", dump_fill_fcb.bb_byte);
614 printf("\tBlock Mark Bit Offset : %d\n", dump_fill_fcb.bb_start_bit);
615 printf("====================================================\n");
616
617 printf("mtd: partition #0\n");
618 printf(" %s = %d\n", "type", dump_mtd->type);
619 printf(" %s = %d\n", "flags", dump_mtd->flags);
620 printf(" %s = %llu\n", "size", dump_nandboot_size);
621 printf(" %s = %d\n", "erasesize", dump_mtd->erasesize);
622 printf(" %s = %d\n", "writesize", dump_mtd->writesize);
623 printf(" %s = %d\n", "oobsize", dump_mtd->oobsize);
624 blocks = dump_nandboot_size;
625 do_div(blocks, dump_mtd->erasesize);
626 printf(" %s = %llu\n", "blocks", blocks);
627}
628
629/* dump data which is read from NAND chip */
630void mtd_dump_structure(int i)
631{
632 #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_fcb[i].x)
633 printf("FCB %d:\n", i);
634 P1(checksum);
635 P1(fingerprint);
636 P1(version);
637 #undef P1
638 #define P1(x) printf(" %s = %d\n", #x, dump_nand_fcb[i].x)
639 P1(datasetup);
640 P1(datahold);
641 P1(addr_setup);
642 P1(dsample_time);
643 P1(pagesize);
644 P1(oob_pagesize);
645 P1(sectors);
646 P1(nr_nand);
647 P1(nr_die);
648 P1(celltype);
649 P1(ecc_type);
650 P1(ecc_nr);
651 P1(ecc_size);
652 P1(ecc_level);
653 P1(meta_size);
654 P1(nr_blocks);
655 P1(ecc_type_sdk);
656 P1(ecc_nr_sdk);
657 P1(ecc_size_sdk);
658 P1(ecc_level_sdk);
659 P1(nr_blocks_sdk);
660 P1(meta_size_sdk);
661 P1(erase_th);
662 P1(bootpatch);
663 P1(patch_size);
664 P1(fw1_start);
665 P1(fw2_start);
666 P1(fw1_pages);
667 P1(fw2_pages);
668 P1(dbbt_start);
669 P1(bb_byte);
670 P1(bb_start_bit);
671 P1(phy_offset);
672 P1(bchtype);
673 P1(readlatency);
674 P1(predelay);
675 P1(cedelay);
676 P1(postdelay);
677 P1(cmdaddpause);
678 P1(datapause);
679 P1(tmspeed);
680 P1(busytimeout);
681 P1(disbbm);
682 P1(spare_offset);
683 P1(onfi_sync_enable);
684 P1(onfi_sync_speed);
685 P1(onfi_sync_nand_data);
686 P1(disbbm_search);
687 P1(disbbm_search_limit);
688 P1(read_retry_enable);
689 #undef P1
690 #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_dbbt[i].x)
691 printf("DBBT %d:\n", i);
692 P1(checksum);
693 P1(fingerprint);
694 P1(version);
695 #undef P1
696 #define P1(x) printf(" %s = %d\n", #x, dump_nand_dbbt[i].x)
697 P1(numberbb);
698 #undef P1
699
700 printf("Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n",
701 dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize,
702 dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize,
703 dump_nandboot_size - dump_nand_fcb[i].fw1_start *
704 dump_nand_fcb[i].pagesize);
705 if (is_imx8m()) {
706 printf("Extra Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n",
707 dump_nand_fcb[i].fw1_start *
708 dump_nand_fcb[i].pagesize + dump_mtd->erasesize *
709 ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) /
710 dump_mtd->erasesize),
711 dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize,
712 dump_nandboot_size -
713 (dump_nand_fcb[i].fw1_start *
714 dump_nand_fcb[i].pagesize + dump_mtd->erasesize *
715 ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) /
716 dump_mtd->erasesize)));
717 }
718}
719
720static int do_nandbcb_dump(int argc, char * const argv[])
721{
722 int num;
723 int stride;
724 int search_area_sz;
725 bool bab_block_table[BOOT_SEARCH_COUNT];
726 int bab_block_flag;
727
728 if (argc != 2)
729 return CMD_RET_USAGE;
730
731 switch (argv[1][0]) {
732 case '0':
733 num = 0;
734 break;
735 case '1':
736 num = 1;
737 break;
738 default:
739 return CMD_RET_USAGE;
740 }
741
742 /* dump data which is planned to be encoded and written to NAND chip */
743 mtd_cfg_dump();
744
745 stride = dump_mtd->erasesize;
746 search_area_sz = BOOT_SEARCH_COUNT * stride;
747 printf("stride: %x, search_area_sz: %x\n", stride, search_area_sz);
748
749 bab_block_flag = 0;
750 for (int i = 0; i < BOOT_SEARCH_COUNT; i++) {
751 if (mtd_block_isbad(dump_mtd,
752 (loff_t)(dump_mtd->erasesize * i))) {
753 bab_block_table[i] = 1;
754 bab_block_flag = 1;
755 continue;
756 }
757 bab_block_table[i] = 0;
758 if (!memcmp(dump_nand_fcb + i, &dump_fill_fcb,
759 sizeof(dump_fill_fcb))) {
760 printf("mtd: found FCB%d candidate version %08x @%d:0x%x\n",
761 i, dump_nand_fcb[i].version, i, dump_fcb_off[i]);
762 } else {
763 printf("mtd: FCB%d not found\n", i);
764 }
765 }
766
767 for (int i = 0; i < BOOT_SEARCH_COUNT; i++) {
768 if (mtd_block_isbad(dump_mtd,
769 (loff_t)(dump_mtd->erasesize * i)))
770 continue;
771
772 if (!memcmp(dump_nand_dbbt + i, &dump_fill_dbbt,
773 sizeof(dump_fill_dbbt))) {
774 printf("mtd: DBBT%d found\n", i);
775 printf("mtd: Valid DBBT%d found @%d:0x%x\n",
776 i, i, dump_dbbt_off[i]);
777
778 } else {
779 printf("mtd: DBBT%d not found\n", i);
780 }
781 }
782 if (bab_block_flag == 0) {
783 printf("no bad block found, dbbt: %08x\n",
784 dump_fill_dbbt.fingerprint);
785 } else {
786 for (int i = 0; i < BOOT_SEARCH_COUNT; i++) {
787 if (bab_block_table[i] == 1)
788 printf("mtd: bad block @ 0x%llx\n",
789 (loff_t)(dump_mtd->erasesize * i));
790 }
791 }
792
793 /* dump data which is read from NAND chip */
794 if (num > (BOOT_SEARCH_COUNT - 1))
795 return CMD_RET_USAGE;
796
797 if (bab_block_table[num] == 1) {
798 printf("mtd: bad block @ 0x%llx (FCB - DBBT)\n",
799 (loff_t)(dump_mtd->erasesize * num));
800 return CMD_RET_USAGE;
801 }
802
803 mtd_dump_structure(num);
804
805 return 0;
806}
807
Shyam Saini1d43e242019-06-14 13:05:33 +0530808static int do_nandbcb_update(int argc, char * const argv[])
809{
810 struct mtd_info *mtd;
811 loff_t addr, offset, size, maxsize;
812 char *endp;
813 u_char *buf;
814 int dev;
815 int ret;
816
817 if (argc != 4)
818 return CMD_RET_USAGE;
819
820 dev = nand_curr_device;
821 if (dev < 0) {
Igor Opaniuk1b899a82019-11-03 16:49:45 +0100822 printf("failed to get nand_curr_device, run nand device\n");
Shyam Saini1d43e242019-06-14 13:05:33 +0530823 return CMD_RET_FAILURE;
824 }
825
826 addr = simple_strtoul(argv[1], &endp, 16);
827 if (*argv[1] == 0 || *endp != 0)
828 return CMD_RET_FAILURE;
829
830 mtd = get_nand_dev_by_index(dev);
831 if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
832 &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
833 return CMD_RET_FAILURE;
834
Alice Guo0b103372020-05-05 22:04:01 +0800835 /* dump_mtd and dump_nandboot_size are used for "nandbcb dump [-v]" */
836 dump_mtd = mtd;
837 dump_nandboot_size = maxsize;
838
Shyam Saini1d43e242019-06-14 13:05:33 +0530839 buf = map_physmem(addr, size, MAP_WRBACK);
840 if (!buf) {
841 puts("failed to map physical memory\n");
842 return CMD_RET_FAILURE;
843 }
844
845 ret = nandbcb_update(mtd, offset, size, maxsize, buf);
846
847 return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
848}
849
850static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
851 char * const argv[])
852{
853 const char *cmd;
854 int ret = 0;
855
Alice Guo0b103372020-05-05 22:04:01 +0800856 if (argc < 3)
Shyam Saini1d43e242019-06-14 13:05:33 +0530857 goto usage;
858
859 cmd = argv[1];
860 --argc;
861 ++argv;
862
863 if (strcmp(cmd, "update") == 0) {
864 ret = do_nandbcb_update(argc, argv);
865 goto done;
866 }
867
Alice Guo0b103372020-05-05 22:04:01 +0800868 if (strcmp(cmd, "dump") == 0) {
869 ret = do_nandbcb_dump(argc, argv);
870 goto done;
871 }
872
Igor Opaniukae8a53e2019-11-03 16:49:46 +0100873 if (strcmp(cmd, "bcbonly") == 0) {
874 ret = do_nandbcb_bcbonly(argc, argv);
875 goto done;
876 }
877
Shyam Saini1d43e242019-06-14 13:05:33 +0530878done:
879 if (ret != -1)
880 return ret;
881usage:
882 return CMD_RET_USAGE;
883}
884
Parthiban Nallathambi4ee0ff12019-08-23 18:35:10 +0200885#ifdef CONFIG_SYS_LONGHELP
Shyam Saini1d43e242019-06-14 13:05:33 +0530886static char nandbcb_help_text[] =
887 "update addr off|partition len - update 'len' bytes starting at\n"
Igor Opaniukae8a53e2019-11-03 16:49:46 +0100888 " 'off|part' to memory address 'addr', skipping bad blocks\n"
889 "bcbonly fw-size fw1-off [fw2-off] - write only BCB (FCB and DBBT)\n"
Igor Opaniuk061b63b2019-12-16 14:06:44 +0200890 " where `fw-size` is fw sizes in bytes, `fw1-off`\n"
891 " and `fw2-off` - firmware offsets\n"
892 " FIY, BCB isn't erased automatically, so mtd erase should\n"
893 " be called in advance before writing new BCB:\n"
Alice Guo0b103372020-05-05 22:04:01 +0800894 " > mtd erase mx7-bcb\n"
895 "nandbcb dump num - verify/dump boot structures\n"
896 " 'num' can be set to 0 and 1";
Parthiban Nallathambi4ee0ff12019-08-23 18:35:10 +0200897#endif
Shyam Saini1d43e242019-06-14 13:05:33 +0530898
899U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb,
Igor Opaniuk061b63b2019-12-16 14:06:44 +0200900 "i.MX6/i.MX7 NAND Boot Control Blocks write",
Shyam Saini1d43e242019-06-14 13:05:33 +0530901 nandbcb_help_text
902);