]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/tx6/flash.c
Merge branch 'tx53-update-2016-02-03' into karo-tx6
[karo-tx-uboot.git] / board / karo / tx6 / flash.c
1 /*
2  * Copyright (C) 2012-2015 Lothar Waßmann <LW@KARO-electronics.de>
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <common.h>
19 #include <malloc.h>
20 #include <nand.h>
21 #include <errno.h>
22
23 #include <linux/err.h>
24 #include <jffs2/load_kernel.h>
25
26 #include <asm/io.h>
27 #include <linux/sizes.h>
28 #include <asm/arch/sys_proto.h>
29 #include <asm/imx-common/regs-gpmi.h>
30 #include <asm/imx-common/regs-bch.h>
31
32 struct mx6_nand_timing {
33         u8 data_setup;
34         u8 data_hold;
35         u8 address_setup;
36         u8 dsample_time;
37         u8 nand_timing_state;
38         u8 tREA;
39         u8 tRLOH;
40         u8 tRHOH;
41 };
42
43 struct mx6_fcb {
44         u32 checksum;
45         u32 fingerprint;
46         u32 version;
47         struct mx6_nand_timing timing;
48         u32 page_data_size;
49         u32 total_page_size;
50         u32 sectors_per_block;
51         u32 number_of_nands;    /* not used by ROM code */
52         u32 total_internal_die; /* not used by ROM code */
53         u32 cell_type;          /* not used by ROM code */
54         u32 ecc_blockn_type;
55         u32 ecc_block0_size;
56         u32 ecc_blockn_size;
57         u32 ecc_block0_type;
58         u32 metadata_size;
59         u32 ecc_blocks_per_page;
60         u32 rsrvd1[6];          /* not used by ROM code */
61         u32 bch_mode;           /* erase_threshold */
62         u32 rsrvd2[2];
63         u32 fw1_start_page;
64         u32 fw2_start_page;
65         u32 fw1_sectors;
66         u32 fw2_sectors;
67         u32 dbbt_search_area;
68         u32 bb_mark_byte;
69         u32 bb_mark_startbit;
70         u32 bb_mark_phys_offset;
71         u32 bch_type;
72         u32 rsrvd3[8]; /* Toggle NAND timing parameters */
73         u32 disbbm;
74         u32 bb_mark_spare_offset;
75         u32 rsrvd4[9]; /* ONFI NAND parameters */
76         u32 disbb_search;
77 };
78
79 #define BF_VAL(v, bf)           (((v) & bf##_MASK) >> bf##_OFFSET)
80 #define BF_SET_VAL(r, v, bf)    r = ((r) & ~bf##_MASK) | (((v) << bf##_OFFSET) & bf##_MASK)
81
82 static nand_info_t *mtd = &nand_info[0];
83 static bool doit;
84
85 #define BIT(v,n)        (((v) >> (n)) & 0x1)
86
87 static u8 calculate_parity_13_8(u8 d)
88 {
89         u8 p = 0;
90
91         p |= (BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 3) ^ BIT(d, 2))             << 0;
92         p |= (BIT(d, 7) ^ BIT(d, 5) ^ BIT(d, 4) ^ BIT(d, 2) ^ BIT(d, 1)) << 1;
93         p |= (BIT(d, 7) ^ BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 1) ^ BIT(d, 0)) << 2;
94         p |= (BIT(d, 7) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 0))             << 3;
95         p |= (BIT(d, 6) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 2) ^ BIT(d, 1) ^ BIT(d, 0)) << 4;
96         return p;
97 }
98
99 static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
100 {
101         int i;
102         u8 *src = _src;
103         u8 *ecc = _ecc;
104
105         for (i = 0; i < size; i++)
106                 ecc[i] = calculate_parity_13_8(src[i]);
107 }
108
109 static struct bch_regs bch_save;
110 static struct bch_regs *bch_base = (void *)BCH_BASE_ADDRESS;
111
112 /*
113  * Reprogram BCH engine for 40bit ECC on chunks of 128 byte
114  * and 32 byte of metadata as required by the i.MX6UL ROM code.
115  */
116 static void tx6_init_bch(void)
117 {
118         u32 fl0 = readl(&bch_base->hw_bch_flash0layout0);
119         u32 fl1 = readl(&bch_base->hw_bch_flash0layout1);
120
121         bch_save.hw_bch_flash0layout0 = fl0;
122         bch_save.hw_bch_flash0layout1 = fl1;
123
124         BF_SET_VAL(fl0, 32, BCH_FLASHLAYOUT0_META_SIZE);
125         BF_SET_VAL(fl0, 7, BCH_FLASHLAYOUT0_NBLOCKS);
126
127         BF_SET_VAL(fl0, 0x14, BCH_FLASHLAYOUT0_ECC0);
128         BF_SET_VAL(fl0, 128 / 4, BCH_FLASHLAYOUT0_DATA0_SIZE);
129
130         BF_SET_VAL(fl1, 0x14, BCH_FLASHLAYOUT1_ECCN);
131         BF_SET_VAL(fl1, 128 / 4, BCH_FLASHLAYOUT1_DATAN_SIZE);
132
133         writel(fl0, &bch_base->hw_bch_flash0layout0);
134         writel(fl1, &bch_base->hw_bch_flash0layout1);
135 }
136
137 static void tx6_restore_bch(void)
138 {
139         writel(bch_save.hw_bch_flash0layout0, &bch_base->hw_bch_flash0layout0);
140         writel(bch_save.hw_bch_flash0layout1, &bch_base->hw_bch_flash0layout1);
141 }
142
143 static u32 calc_chksum(void *buf, size_t size)
144 {
145         u32 chksum = 0;
146         u8 *bp = buf;
147         size_t i;
148
149         for (i = 0; i < size; i++) {
150                 chksum += bp[i];
151         }
152         return ~chksum;
153 }
154
155 /*
156  * return number of blocks to skip for a contiguous partition
157  * of given # blocks
158  */
159 static int find_contig_space(int block, int num_blocks, int max_blocks)
160 {
161         int skip = 0;
162         int found = 0;
163         int last = block + max_blocks;
164
165         debug("Searching %u contiguous blocks from %d..%d\n",
166                 num_blocks, block, block + max_blocks - 1);
167         for (; block < last; block++) {
168                 if (nand_block_isbad(mtd, block * mtd->erasesize)) {
169                         skip += found + 1;
170                         found = 0;
171                         debug("Skipping %u blocks to %u\n",
172                                 skip, block + 1);
173                 } else {
174                         found++;
175                         if (found >= num_blocks) {
176                                 debug("Found %u good blocks from %d..%d\n",
177                                         found, block - found + 1, block);
178                                 return skip;
179                         }
180                 }
181         }
182         return -ENOSPC;
183 }
184
185 #define offset_of(p, m)         ((void *)&(p)->m - (void *)(p))
186 #define pr_fcb_val(p, n)        debug("%-24s[%02x]=%08x(%d)\n", #n, offset_of(p, n), (p)->n, (p)->n)
187
188 static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
189                                 int fw2_start_block, int fw_num_blocks)
190 {
191         struct gpmi_regs *gpmi_base = (void *)GPMI_BASE_ADDRESS;
192         u32 fl0, fl1;
193         u32 t0;
194         struct mx6_fcb *fcb;
195         int fcb_offs;
196
197         if (gpmi_base == NULL || bch_base == NULL) {
198                 return ERR_PTR(-ENOMEM);
199         }
200
201         fl0 = readl(&bch_base->hw_bch_flash0layout0);
202         fl1 = readl(&bch_base->hw_bch_flash0layout1);
203         t0 = readl(&gpmi_base->hw_gpmi_timing0);
204
205         if (!is_cpu_type(MXC_CPU_MX6UL)) {
206                 int metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
207
208                 fcb = buf + ALIGN(metadata_size, 4);
209                 fcb_offs = (void *)fcb - buf;
210
211                 memset(buf, 0xff, fcb_offs);
212         } else {
213                 fcb = buf;
214                 fcb_offs = 0;
215         }
216
217         memset(fcb, 0, mtd->erasesize - fcb_offs);
218
219         strncpy((char *)&fcb->fingerprint, "FCB ", 4);
220         fcb->version = cpu_to_be32(1);
221
222         fcb->disbb_search = 0;
223         fcb->disbbm = 1;
224
225         /* ROM code assumes GPMI clock of 25 MHz */
226         fcb->timing.data_setup = BF_VAL(t0, GPMI_TIMING0_DATA_SETUP) * 40;
227         fcb->timing.data_hold = BF_VAL(t0, GPMI_TIMING0_DATA_HOLD) * 40;
228         fcb->timing.address_setup = BF_VAL(t0, GPMI_TIMING0_ADDRESS_SETUP) * 40;
229
230         fcb->page_data_size = mtd->writesize;
231         fcb->total_page_size = mtd->writesize + mtd->oobsize;
232         fcb->sectors_per_block = mtd->erasesize / mtd->writesize;
233
234         fcb->ecc_block0_type = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
235         fcb->ecc_block0_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE) * 4;
236         fcb->ecc_blockn_type = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
237         fcb->ecc_blockn_size = BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE) * 4;
238
239         pr_fcb_val(fcb, ecc_block0_type);
240         pr_fcb_val(fcb, ecc_blockn_type);
241         pr_fcb_val(fcb, ecc_block0_size);
242         pr_fcb_val(fcb, ecc_blockn_size);
243
244         fcb->metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
245         fcb->ecc_blocks_per_page = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
246         fcb->bch_mode = readl(&bch_base->hw_bch_mode);
247         fcb->bch_type = 0; /* BCH20 */
248
249         fcb->fw1_start_page = fw1_start_block * fcb->sectors_per_block;
250         fcb->fw1_sectors = fw_num_blocks * fcb->sectors_per_block;
251         pr_fcb_val(fcb, fw1_start_page);
252         pr_fcb_val(fcb, fw1_sectors);
253
254         if (fw2_start_block != 0 &&
255                 fw2_start_block < lldiv(mtd->size, mtd->erasesize)) {
256                 fcb->fw2_start_page = fw2_start_block * fcb->sectors_per_block;
257                 fcb->fw2_sectors = fcb->fw1_sectors;
258                 pr_fcb_val(fcb, fw2_start_page);
259                 pr_fcb_val(fcb, fw2_sectors);
260         }
261
262         fcb->dbbt_search_area = 0;
263
264         fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
265         return fcb;
266 }
267
268 static int find_fcb(void *ref, int page)
269 {
270         int ret = 0;
271         struct nand_chip *chip = mtd->priv;
272         void *buf = malloc(mtd->erasesize);
273
274         if (buf == NULL) {
275                 return -ENOMEM;
276         }
277         chip->select_chip(mtd, 0);
278         chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
279         ret = chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
280         if (ret) {
281                 printf("Failed to read FCB from page %u: %d\n", page, ret);
282                 goto out;
283         }
284         if (memcmp(buf, ref, mtd->writesize) == 0) {
285                 debug("Found FCB in page %u (%08x)\n",
286                         page, page * mtd->writesize);
287                 ret = 1;
288         }
289 out:
290         chip->select_chip(mtd, -1);
291         free(buf);
292         return ret;
293 }
294
295 static int write_fcb(void *buf, int block)
296 {
297         int ret;
298         struct nand_chip *chip = mtd->priv;
299         int page = block * mtd->erasesize / mtd->writesize;
300
301         ret = find_fcb(buf, page);
302         if (ret > 0) {
303                 printf("FCB at block %d is up to date\n", block);
304                 return 0;
305         }
306
307         if (doit) {
308                 ret = nand_erase(mtd, block * mtd->erasesize, mtd->erasesize);
309                 if (ret) {
310                         printf("Failed to erase FCB block %u\n", block);
311                         return ret;
312                 }
313         }
314
315         printf("Writing FCB to block %d @ %08llx\n", block,
316                 (u64)block * mtd->erasesize);
317         if (doit) {
318                 if (is_cpu_type(MXC_CPU_MX6UL)) {
319                         size_t len = mtd->writesize;
320
321                         tx6_init_bch();
322                         printf("writing block %u from buffer %p\n", block, buf);
323                         ret = nand_write(mtd, block * mtd->erasesize, &len, buf);
324                         tx6_restore_bch();
325                 } else {
326                         chip->select_chip(mtd, 0);
327                         ret = chip->write_page(mtd, chip, 0, mtd->writesize,
328                                         buf, 1, page, 0, 1);
329                 }
330                 if (ret) {
331                         printf("Failed to write FCB to block %u: %d\n", block, ret);
332                 }
333                 chip->select_chip(mtd, -1);
334         }
335         return ret;
336 }
337
338 #define chk_overlap(a,b)                                \
339         ((a##_start_block <= b##_end_block &&           \
340                 a##_end_block >= b##_start_block) ||    \
341         (b##_start_block <= a##_end_block &&            \
342                 b##_end_block >= a##_start_block))
343
344 #define fail_if_overlap(a,b,m1,m2) do {                                 \
345         if (!doit)                                                      \
346                 printf("check: %s[%lu..%lu] <> %s[%lu..%lu]\n",         \
347                         m1, a##_start_block, a##_end_block,             \
348                         m2, b##_start_block, b##_end_block);            \
349         if (a##_end_block < a##_start_block)                            \
350                 printf("Invalid start/end block # for %s\n", m1);       \
351         if (b##_end_block < b##_start_block)                            \
352                 printf("Invalid start/end block # for %s\n", m2);       \
353         if (chk_overlap(a, b)) {                                        \
354                 printf("%s blocks %lu..%lu overlap %s in blocks %lu..%lu!\n", \
355                         m1, a##_start_block, a##_end_block,             \
356                         m2, b##_start_block, b##_end_block);            \
357                 return -EINVAL;                                         \
358         }                                                               \
359 } while (0)
360
361 static int tx6_prog_uboot(void *addr, int start_block, int skip,
362                         size_t size, size_t max_len)
363 {
364         int ret;
365         nand_erase_options_t erase_opts = { 0, };
366         size_t actual;
367         size_t prg_length = max_len - skip * mtd->erasesize;
368         int prg_start = (start_block + skip) * mtd->erasesize;
369
370         erase_opts.offset = start_block * mtd->erasesize;
371         erase_opts.length = max_len;
372         erase_opts.quiet = 1;
373
374         printf("Erasing flash @ %08llx..%08llx\n", erase_opts.offset,
375                 erase_opts.offset + erase_opts.length - 1);
376         if (doit) {
377                 ret = nand_erase_opts(mtd, &erase_opts);
378                 if (ret) {
379                         printf("Failed to erase flash: %d\n", ret);
380                         return ret;
381                 }
382         }
383
384         printf("Programming flash @ %08x..%08x from %p\n",
385                 prg_start, prg_start + size - 1, addr);
386         if (doit) {
387                 actual = size;
388                 ret = nand_write_skip_bad(mtd, prg_start, &actual, NULL,
389                                         prg_length, addr, 0);
390                 if (ret) {
391                         printf("Failed to program flash: %d\n", ret);
392                         return ret;
393                 }
394                 if (actual < size) {
395                         printf("Could only write %u of %u bytes\n", actual, size);
396                         return -EIO;
397                 }
398         }
399         return 0;
400 }
401
402 #ifdef CONFIG_ENV_IS_IN_NAND
403 #ifndef CONFIG_ENV_OFFSET_REDUND
404 #define TOTAL_ENV_SIZE CONFIG_ENV_RANGE
405 #else
406 #define TOTAL_ENV_SIZE (CONFIG_ENV_RANGE * 2)
407 #endif
408 #endif
409
410 int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
411 {
412         int ret;
413         const unsigned long fcb_start_block = 0, fcb_end_block = 0;
414         int erase_size = mtd->erasesize;
415         void *buf;
416         char *load_addr;
417         char *file_size;
418         size_t size = 0;
419         void *addr = NULL;
420         struct mx6_fcb *fcb;
421         unsigned long mtd_num_blocks = lldiv(mtd->size, mtd->erasesize);
422 #ifdef CONFIG_ENV_IS_IN_NAND
423         unsigned long env_start_block = CONFIG_ENV_OFFSET / mtd->erasesize;
424         unsigned long env_end_block = env_start_block +
425                 DIV_ROUND_UP(TOTAL_ENV_SIZE, mtd->erasesize) - 1;
426 #endif
427         int optind;
428         int fw2_set = 0;
429         unsigned long fw1_start_block = 0, fw1_end_block;
430         unsigned long fw2_start_block = 0, fw2_end_block;
431         unsigned long fw_num_blocks;
432         int fw1_skip, fw2_skip;
433         unsigned long extra_blocks = 0;
434         u64 max_len1, max_len2;
435         struct mtd_device *dev;
436         struct part_info *part_info;
437         struct part_info *redund_part_info;
438         const char *uboot_part = "u-boot";
439         const char *redund_part = NULL;
440         u8 part_num;
441         u8 redund_part_num;
442
443         ret = mtdparts_init();
444         if (ret)
445                 return ret;
446
447         doit = true;
448         for (optind = 1; optind < argc; optind++) {
449                 char *endp;
450
451                 if (strcmp(argv[optind], "-f") == 0) {
452                         if (optind >= argc - 1) {
453                                 printf("Option %s requires an argument\n",
454                                         argv[optind]);
455                                 return -EINVAL;
456                         }
457                         optind++;
458                         fw1_start_block = simple_strtoul(argv[optind], &endp, 0);
459                         if (*endp != '\0') {
460                                 uboot_part = argv[optind];
461                                 continue;
462                         }
463                         uboot_part = NULL;
464                         if (fw1_start_block >= mtd_num_blocks) {
465                                 printf("Block number %lu is out of range: 0..%lu\n",
466                                         fw1_start_block, mtd_num_blocks - 1);
467                                 return -EINVAL;
468                         }
469                 } else if (strcmp(argv[optind], "-r") == 0) {
470                         fw2_set = 1;
471                         if (optind < argc - 1 && argv[optind + 1][0] != '-') {
472                                 optind++;
473                                 fw2_start_block = simple_strtoul(argv[optind],
474                                                                 &endp, 0);
475                                 if (*endp != '\0') {
476                                         redund_part = argv[optind];
477                                         continue;
478                                 }
479                                 if (fw2_start_block >= mtd_num_blocks) {
480                                         printf("Block number %lu is out of range: 0..%lu\n",
481                                                 fw2_start_block,
482                                                 mtd_num_blocks - 1);
483                                         return -EINVAL;
484                                 }
485                         }
486                 } else if (strcmp(argv[optind], "-e") == 0) {
487                         if (optind >= argc - 1) {
488                                 printf("Option %s requires an argument\n",
489                                         argv[optind]);
490                                 return -EINVAL;
491                         }
492                         optind++;
493                         extra_blocks = simple_strtoul(argv[optind], NULL, 0);
494                         if (extra_blocks >= mtd_num_blocks) {
495                                 printf("Extra block count %lu is out of range: 0..%lu\n",
496                                         extra_blocks,
497                                         mtd_num_blocks - 1);
498                                 return -EINVAL;
499                         }
500                 } else if (strcmp(argv[optind], "-n") == 0) {
501                         doit = false;
502                 } else if (argv[optind][0] == '-') {
503                         printf("Unrecognized option %s\n", argv[optind]);
504                         return -EINVAL;
505                 } else {
506                         break;
507                 }
508         }
509
510         load_addr = getenv("fileaddr");
511         file_size = getenv("filesize");
512
513         if (argc - optind < 1 && load_addr == NULL) {
514                 printf("Load address not specified\n");
515                 return -EINVAL;
516         }
517         if (argc - optind < 2 && file_size == NULL) {
518                 if (uboot_part) {
519                         printf("WARNING: Image size not specified; overwriting whole '%s' partition\n",
520                                 uboot_part);
521                         printf("This will only work, if there are no bad blocks inside this partition!\n");
522                 } else {
523                         printf("ERROR: Image size must be specified\n");
524                         return -EINVAL;
525                 }
526         }
527         if (argc > optind) {
528                 load_addr = NULL;
529                 addr = (void *)simple_strtoul(argv[optind], NULL, 16);
530                 optind++;
531         }
532         if (argc > optind) {
533                 file_size = NULL;
534                 size = simple_strtoul(argv[optind], NULL, 16);
535                 optind++;
536         }
537         if (load_addr != NULL) {
538                 addr = (void *)simple_strtoul(load_addr, NULL, 16);
539                 printf("Using default load address %p\n", addr);
540         }
541         if (file_size != NULL) {
542                 size = simple_strtoul(file_size, NULL, 16);
543                 printf("Using default file size %08x\n", size);
544         }
545         if (size > 0)
546                 fw_num_blocks = DIV_ROUND_UP(size, mtd->erasesize);
547         else
548                 fw_num_blocks = 0;
549
550         if (uboot_part) {
551                 ret = find_dev_and_part(uboot_part, &dev, &part_num,
552                                         &part_info);
553                 if (ret) {
554                         printf("Failed to find '%s' partition: %d\n",
555                                 uboot_part, ret);
556                         return ret;
557                 }
558                 fw1_start_block = lldiv(part_info->offset, mtd->erasesize);
559                 max_len1 = part_info->size;
560                 if (size == 0)
561                         fw_num_blocks = lldiv(max_len1, mtd->erasesize);
562         } else {
563                 max_len1 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
564         }
565
566         if (redund_part) {
567                 ret = find_dev_and_part(redund_part, &dev, &redund_part_num,
568                                         &redund_part_info);
569                 if (ret) {
570                         printf("Failed to find '%s' partition: %d\n",
571                                 redund_part, ret);
572                         return ret;
573                 }
574                 fw2_start_block = lldiv(redund_part_info->offset, mtd->erasesize);
575                 max_len2 = redund_part_info->size;
576                 if (fw2_start_block == fcb_start_block) {
577                         fw2_start_block++;
578                         max_len2 -= mtd->erasesize;
579                 }
580                 if (size == 0)
581                         fw_num_blocks = lldiv(max_len2, mtd->erasesize);
582         } else if (fw2_set) {
583                 max_len2 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
584         } else {
585                 max_len2 = 0;
586         }
587
588         fw1_skip = find_contig_space(fw1_start_block, fw_num_blocks,
589                                 lldiv(max_len1, mtd->erasesize));
590         if (fw1_skip < 0) {
591                 printf("Could not find %lu contiguous good blocks for fw image in blocks %lu..%llu\n",
592                         fw_num_blocks, fw1_start_block,
593                         fw1_start_block + lldiv(max_len1, mtd->erasesize) - 1);
594                 if (uboot_part) {
595 #ifdef CONFIG_ENV_IS_IN_NAND
596                         if (part_info->offset <= CONFIG_ENV_OFFSET + TOTAL_ENV_SIZE) {
597                                 printf("Use a different partition\n");
598                         } else {
599                                 printf("Increase the size of the '%s' partition\n",
600                                         uboot_part);
601                         }
602 #else
603                         printf("Increase the size of the '%s' partition\n",
604                                 uboot_part);
605 #endif
606                 } else {
607                         printf("Increase the number of spare blocks to use with the '-e' option\n");
608                 }
609                 return -ENOSPC;
610         }
611         if (extra_blocks)
612                 fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
613         else
614                 fw1_end_block = fw1_start_block + fw_num_blocks + fw1_skip - 1;
615
616         if (fw2_set && fw2_start_block == 0)
617                 fw2_start_block = fw1_end_block + 1;
618         if (fw2_start_block > 0) {
619                 fw2_skip = find_contig_space(fw2_start_block, fw_num_blocks,
620                                         lldiv(max_len2, mtd->erasesize));
621                 if (fw2_skip < 0) {
622                         printf("Could not find %lu contiguous good blocks for redundant fw image in blocks %lu..%llu\n",
623                                 fw_num_blocks, fw2_start_block,
624                                 fw2_start_block + lldiv(max_len2, mtd->erasesize) - 1);
625                         if (redund_part) {
626                                 printf("Increase the size of the '%s' partition or use a different partition\n",
627                                         redund_part);
628                         } else {
629                                 printf("Increase the number of spare blocks to use with the '-e' option\n");
630                         }
631                         return -ENOSPC;
632                 }
633         } else {
634                 fw2_skip = 0;
635         }
636         if (extra_blocks)
637                 fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
638         else
639                 fw2_end_block = fw2_start_block + fw_num_blocks + fw2_skip - 1;
640
641 #ifdef CONFIG_ENV_IS_IN_NAND
642         fail_if_overlap(fcb, env, "FCB", "Environment");
643         fail_if_overlap(fw1, env, "FW1", "Environment");
644 #endif
645         fail_if_overlap(fcb, fw1, "FCB", "FW1");
646         if (fw2_set) {
647                 fail_if_overlap(fcb, fw2, "FCB", "FW2");
648 #ifdef CONFIG_ENV_IS_IN_NAND
649                 fail_if_overlap(fw2, env, "FW2", "Environment");
650 #endif
651                 fail_if_overlap(fw1, fw2, "FW1", "FW2");
652         }
653         fw1_start_block += fw1_skip;
654         fw2_start_block += fw2_skip;
655
656         buf = memalign(SZ_128K, erase_size);
657         if (buf == NULL) {
658                 printf("Failed to allocate buffer\n");
659                 return -ENOMEM;
660         }
661
662         fcb = create_fcb(buf, fw1_start_block,
663                         fw2_start_block, fw_num_blocks);
664         if (IS_ERR(fcb)) {
665                 printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
666                 ret = PTR_ERR(fcb);
667                 goto out;
668         }
669         if (!is_cpu_type(MXC_CPU_MX6UL))
670                 encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
671
672         ret = write_fcb(buf, fcb_start_block);
673         if (ret) {
674                 printf("Failed to write FCB to block %lu\n", fcb_start_block);
675                 return ret;
676         }
677
678         printf("Programming U-Boot image from %p to block %lu @ %08llx\n",
679                 buf, fw1_start_block, (u64)fw1_start_block * mtd->erasesize);
680         ret = tx6_prog_uboot(addr, fw1_start_block, fw1_skip, size,
681                         max_len1);
682
683         if (ret || fw2_start_block == 0)
684                 goto out;
685
686         printf("Programming redundant U-Boot image to block %lu @ %08llx\n",
687                 fw2_start_block, (u64)fw2_start_block * mtd->erasesize);
688         ret = tx6_prog_uboot(addr, fw2_start_block, fw2_skip, size,
689                         max_len2);
690 out:
691         free(buf);
692         return ret;
693 }
694
695 U_BOOT_CMD(romupdate, 11, 0, do_update,
696         "Creates an FCB data structure and writes an U-Boot image to flash",
697         "[-f {<part>|block#}] [-r [{<part>|block#}]] [-e #] [<address>] [<length>]\n"
698         "\t-f <part>\twrite bootloader image to partition <part>\n"
699         "\t-f #\t\twrite bootloader image at block # (decimal)\n"
700         "\t-r\t\twrite redundant bootloader image at next free block after first image\n"
701         "\t-r <part>\twrite redundant bootloader image to partition <part>\n"
702         "\t-r #\t\twrite redundant bootloader image at block # (decimal)\n"
703         "\t-e #\t\tspecify number of redundant blocks per boot loader image\n"
704         "\t\t\t(only valid if -f or -r specify a flash address rather than a partition name)\n"
705         "\t-n\t\tshow what would be done without actually updating the flash\n"
706         "\t<address>\tRAM address of bootloader image (default: ${fileaddr})\n"
707         "\t<length>\tlength of bootloader image in RAM (default: ${filesize})"
708         );