karo: tx6: remove unused code from flash.c
[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
81 static nand_info_t *mtd = &nand_info[0];
82 static bool doit;
83
84 #define BIT(v,n)        (((v) >> (n)) & 0x1)
85
86 static u8 calculate_parity_13_8(u8 d)
87 {
88         u8 p = 0;
89
90         p |= (BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 3) ^ BIT(d, 2))             << 0;
91         p |= (BIT(d, 7) ^ BIT(d, 5) ^ BIT(d, 4) ^ BIT(d, 2) ^ BIT(d, 1)) << 1;
92         p |= (BIT(d, 7) ^ BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 1) ^ BIT(d, 0)) << 2;
93         p |= (BIT(d, 7) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 0))             << 3;
94         p |= (BIT(d, 6) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 2) ^ BIT(d, 1) ^ BIT(d, 0)) << 4;
95         return p;
96 }
97
98 static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
99 {
100         int i;
101         u8 *src = _src;
102         u8 *ecc = _ecc;
103
104         for (i = 0; i < size; i++)
105                 ecc[i] = calculate_parity_13_8(src[i]);
106 }
107
108 static u32 calc_chksum(void *buf, size_t size)
109 {
110         u32 chksum = 0;
111         u8 *bp = buf;
112         size_t i;
113
114         for (i = 0; i < size; i++) {
115                 chksum += bp[i];
116         }
117         return ~chksum;
118 }
119
120 /*
121  * return number of blocks to skip for a contiguous partition
122  * of given # blocks
123  */
124 static int find_contig_space(int block, int num_blocks, int max_blocks)
125 {
126         int skip = 0;
127         int found = 0;
128         int last = block + max_blocks;
129
130         debug("Searching %u contiguous blocks from %d..%d\n",
131                 num_blocks, block, block + max_blocks - 1);
132         for (; block < last; block++) {
133                 if (nand_block_isbad(mtd, block * mtd->erasesize)) {
134                         skip += found + 1;
135                         found = 0;
136                         debug("Skipping %u blocks to %u\n",
137                                 skip, block + 1);
138                 } else {
139                         found++;
140                         if (found >= num_blocks) {
141                                 debug("Found %u good blocks from %d..%d\n",
142                                         found, block - found + 1, block);
143                                 return skip;
144                         }
145                 }
146         }
147         return -ENOSPC;
148 }
149
150 #define offset_of(p, m)         ((void *)&(p)->m - (void *)(p))
151 #define pr_fcb_val(p, n)        debug("%-24s[%02x]=%08x(%d)\n", #n, offset_of(p, n), (p)->n, (p)->n)
152
153 static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
154                                 int fw2_start_block, int fw_num_blocks)
155 {
156         struct gpmi_regs *gpmi_base = (void *)GPMI_BASE_ADDRESS;
157         struct bch_regs *bch_base = (void *)BCH_BASE_ADDRESS;
158         u32 fl0, fl1;
159         u32 t0;
160         int metadata_size;
161         struct mx6_fcb *fcb;
162         int fcb_offs;
163
164         if (gpmi_base == NULL || bch_base == NULL) {
165                 return ERR_PTR(-ENOMEM);
166         }
167
168         fl0 = readl(&bch_base->hw_bch_flash0layout0);
169         fl1 = readl(&bch_base->hw_bch_flash0layout1);
170         t0 = readl(&gpmi_base->hw_gpmi_timing0);
171
172         metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
173
174         fcb = buf + ALIGN(metadata_size, 4);
175         fcb_offs = (void *)fcb - buf;
176
177         memset(buf, 0xff, fcb_offs);
178         memset(fcb, 0x00, sizeof(*fcb));
179         memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
180
181         strncpy((char *)&fcb->fingerprint, "FCB ", 4);
182         fcb->version = cpu_to_be32(1);
183
184         fcb->disbb_search = 0;
185         fcb->disbbm = 1;
186
187         /* ROM code assumes GPMI clock of 25 MHz */
188         fcb->timing.data_setup = BF_VAL(t0, GPMI_TIMING0_DATA_SETUP) * 40;
189         fcb->timing.data_hold = BF_VAL(t0, GPMI_TIMING0_DATA_HOLD) * 40;
190         fcb->timing.address_setup = BF_VAL(t0, GPMI_TIMING0_ADDRESS_SETUP) * 40;
191
192         fcb->page_data_size = mtd->writesize;
193         fcb->total_page_size = mtd->writesize + mtd->oobsize;
194         fcb->sectors_per_block = mtd->erasesize / mtd->writesize;
195
196         fcb->ecc_block0_type = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
197         fcb->ecc_block0_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE) * 4;
198         fcb->ecc_blockn_type = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
199         fcb->ecc_blockn_size = BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE) * 4;
200
201         pr_fcb_val(fcb, ecc_block0_type);
202         pr_fcb_val(fcb, ecc_blockn_type);
203         pr_fcb_val(fcb, ecc_block0_size);
204         pr_fcb_val(fcb, ecc_blockn_size);
205
206         fcb->metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
207         fcb->ecc_blocks_per_page = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
208         fcb->bch_mode = readl(&bch_base->hw_bch_mode);
209         fcb->bch_type = 0; /* BCH20 */
210
211         fcb->fw1_start_page = fw1_start_block * fcb->sectors_per_block;
212         fcb->fw1_sectors = fw_num_blocks * fcb->sectors_per_block;
213         pr_fcb_val(fcb, fw1_start_page);
214         pr_fcb_val(fcb, fw1_sectors);
215
216         if (fw2_start_block != 0 &&
217                 fw2_start_block < lldiv(mtd->size, mtd->erasesize)) {
218                 fcb->fw2_start_page = fw2_start_block * fcb->sectors_per_block;
219                 fcb->fw2_sectors = fcb->fw1_sectors;
220                 pr_fcb_val(fcb, fw2_start_page);
221                 pr_fcb_val(fcb, fw2_sectors);
222         }
223
224         fcb->dbbt_search_area = 0;
225
226         fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
227         return fcb;
228 }
229
230 static int find_fcb(void *ref, int page)
231 {
232         int ret = 0;
233         struct nand_chip *chip = mtd->priv;
234         void *buf = malloc(mtd->erasesize);
235
236         if (buf == NULL) {
237                 return -ENOMEM;
238         }
239         chip->select_chip(mtd, 0);
240         chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
241         ret = chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
242         if (ret) {
243                 printf("Failed to read FCB from page %u: %d\n", page, ret);
244                 goto out;
245         }
246         if (memcmp(buf, ref, mtd->writesize) == 0) {
247                 debug("Found FCB in page %u (%08x)\n",
248                         page, page * mtd->writesize);
249                 ret = 1;
250         }
251 out:
252         chip->select_chip(mtd, -1);
253         free(buf);
254         return ret;
255 }
256
257 static int write_fcb(void *buf, int block)
258 {
259         int ret;
260         struct nand_chip *chip = mtd->priv;
261         int page = block * mtd->erasesize / mtd->writesize;
262
263         ret = find_fcb(buf, page);
264         if (ret > 0) {
265                 printf("FCB at block %d is up to date\n", block);
266                 return 0;
267         }
268
269         if (doit) {
270                 ret = nand_erase(mtd, block * mtd->erasesize, mtd->erasesize);
271                 if (ret) {
272                         printf("Failed to erase FCB block %u\n", block);
273                         return ret;
274                 }
275         }
276
277         printf("Writing FCB to block %d @ %08llx\n", block,
278                 (u64)block * mtd->erasesize);
279         if (doit) {
280                 chip->select_chip(mtd, 0);
281                 ret = chip->write_page(mtd, chip, 0, mtd->writesize,
282                                 buf, 1, page, 0, 1);
283                 if (ret) {
284                         printf("Failed to write FCB to block %u: %d\n", block, ret);
285                 }
286                 chip->select_chip(mtd, -1);
287         }
288         return ret;
289 }
290
291 #define chk_overlap(a,b)                                \
292         ((a##_start_block <= b##_end_block &&           \
293                 a##_end_block >= b##_start_block) ||    \
294         (b##_start_block <= a##_end_block &&            \
295                 b##_end_block >= a##_start_block))
296
297 #define fail_if_overlap(a,b,m1,m2) do {                                 \
298         if (!doit)                                                      \
299                 printf("check: %s[%lu..%lu] <> %s[%lu..%lu]\n",         \
300                         m1, a##_start_block, a##_end_block,             \
301                         m2, b##_start_block, b##_end_block);            \
302         if (a##_end_block < a##_start_block)                            \
303                 printf("Invalid start/end block # for %s\n", m1);       \
304         if (b##_end_block < b##_start_block)                            \
305                 printf("Invalid start/end block # for %s\n", m2);       \
306         if (chk_overlap(a, b)) {                                        \
307                 printf("%s blocks %lu..%lu overlap %s in blocks %lu..%lu!\n", \
308                         m1, a##_start_block, a##_end_block,             \
309                         m2, b##_start_block, b##_end_block);            \
310                 return -EINVAL;                                         \
311         }                                                               \
312 } while (0)
313
314 static int tx6_prog_uboot(void *addr, int start_block, int skip,
315                         size_t size, size_t max_len)
316 {
317         int ret;
318         nand_erase_options_t erase_opts = { 0, };
319         size_t actual;
320         size_t prg_length = max_len - skip * mtd->erasesize;
321         int prg_start = (start_block + skip) * mtd->erasesize;
322
323         erase_opts.offset = start_block * mtd->erasesize;
324         erase_opts.length = max_len;
325         erase_opts.quiet = 1;
326
327         printf("Erasing flash @ %08llx..%08llx\n", erase_opts.offset,
328                 erase_opts.offset + erase_opts.length - 1);
329         if (doit) {
330                 ret = nand_erase_opts(mtd, &erase_opts);
331                 if (ret) {
332                         printf("Failed to erase flash: %d\n", ret);
333                         return ret;
334                 }
335         }
336
337         printf("Programming flash @ %08x..%08x from %p\n",
338                 prg_start, prg_start + size - 1, addr);
339         if (doit) {
340                 actual = size;
341                 ret = nand_write_skip_bad(mtd, prg_start, &actual, NULL,
342                                         prg_length, addr, WITH_DROP_FFS);
343                 if (ret) {
344                         printf("Failed to program flash: %d\n", ret);
345                         return ret;
346                 }
347                 if (actual < size) {
348                         printf("Could only write %u of %u bytes\n", actual, size);
349                         return -EIO;
350                 }
351         }
352         return 0;
353 }
354
355 #ifdef CONFIG_ENV_IS_IN_NAND
356 #ifndef CONFIG_ENV_OFFSET_REDUND
357 #define TOTAL_ENV_SIZE CONFIG_ENV_RANGE
358 #else
359 #define TOTAL_ENV_SIZE (CONFIG_ENV_RANGE * 2)
360 #endif
361 #endif
362
363 int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
364 {
365         int ret;
366         const unsigned long fcb_start_block = 0, fcb_end_block = 0;
367         int erase_size = mtd->erasesize;
368         void *buf;
369         char *load_addr;
370         char *file_size;
371         size_t size = 0;
372         void *addr = NULL;
373         struct mx6_fcb *fcb;
374         unsigned long mtd_num_blocks = lldiv(mtd->size, mtd->erasesize);
375 #ifdef CONFIG_ENV_IS_IN_NAND
376         unsigned long env_start_block = CONFIG_ENV_OFFSET / mtd->erasesize;
377         unsigned long env_end_block = env_start_block +
378                 DIV_ROUND_UP(TOTAL_ENV_SIZE, mtd->erasesize) - 1;
379 #endif
380         int optind;
381         int fw2_set = 0;
382         unsigned long fw1_start_block = 0, fw1_end_block;
383         unsigned long fw2_start_block = 0, fw2_end_block;
384         unsigned long fw_num_blocks;
385         int fw1_skip, fw2_skip;
386         unsigned long extra_blocks = 0;
387         u64 max_len1, max_len2;
388         struct mtd_device *dev;
389         struct part_info *part_info;
390         struct part_info *redund_part_info;
391         const char *uboot_part = "u-boot";
392         const char *redund_part = NULL;
393         u8 part_num;
394         u8 redund_part_num;
395
396         ret = mtdparts_init();
397         if (ret)
398                 return ret;
399
400         doit = true;
401         for (optind = 1; optind < argc; optind++) {
402                 char *endp;
403
404                 if (strcmp(argv[optind], "-f") == 0) {
405                         if (optind >= argc - 1) {
406                                 printf("Option %s requires an argument\n",
407                                         argv[optind]);
408                                 return -EINVAL;
409                         }
410                         optind++;
411                         fw1_start_block = simple_strtoul(argv[optind], &endp, 0);
412                         if (*endp != '\0') {
413                                 uboot_part = argv[optind];
414                                 continue;
415                         }
416                         uboot_part = NULL;
417                         if (fw1_start_block >= mtd_num_blocks) {
418                                 printf("Block number %lu is out of range: 0..%lu\n",
419                                         fw1_start_block, mtd_num_blocks - 1);
420                                 return -EINVAL;
421                         }
422                 } else if (strcmp(argv[optind], "-r") == 0) {
423                         fw2_set = 1;
424                         if (optind < argc - 1 && argv[optind + 1][0] != '-') {
425                                 optind++;
426                                 fw2_start_block = simple_strtoul(argv[optind],
427                                                                 &endp, 0);
428                                 if (*endp != '\0') {
429                                         redund_part = argv[optind];
430                                         continue;
431                                 }
432                                 if (fw2_start_block >= mtd_num_blocks) {
433                                         printf("Block number %lu is out of range: 0..%lu\n",
434                                                 fw2_start_block,
435                                                 mtd_num_blocks - 1);
436                                         return -EINVAL;
437                                 }
438                         }
439                 } else if (strcmp(argv[optind], "-e") == 0) {
440                         if (optind >= argc - 1) {
441                                 printf("Option %s requires an argument\n",
442                                         argv[optind]);
443                                 return -EINVAL;
444                         }
445                         optind++;
446                         extra_blocks = simple_strtoul(argv[optind], NULL, 0);
447                         if (extra_blocks >= mtd_num_blocks) {
448                                 printf("Extra block count %lu is out of range: 0..%lu\n",
449                                         extra_blocks,
450                                         mtd_num_blocks - 1);
451                                 return -EINVAL;
452                         }
453                 } else if (strcmp(argv[optind], "-n") == 0) {
454                         doit = false;
455                 } else if (argv[optind][0] == '-') {
456                         printf("Unrecognized option %s\n", argv[optind]);
457                         return -EINVAL;
458                 } else {
459                         break;
460                 }
461         }
462
463         load_addr = getenv("fileaddr");
464         file_size = getenv("filesize");
465
466         if (argc - optind < 1 && load_addr == NULL) {
467                 printf("Load address not specified\n");
468                 return -EINVAL;
469         }
470         if (argc - optind < 2 && file_size == NULL) {
471                 if (uboot_part) {
472                         printf("WARNING: Image size not specified; overwriting whole '%s' partition\n",
473                                 uboot_part);
474                         printf("This will only work, if there are no bad blocks inside this partition!\n");
475                 } else {
476                         printf("ERROR: Image size must be specified\n");
477                         return -EINVAL;
478                 }
479         }
480         if (argc > optind) {
481                 load_addr = NULL;
482                 addr = (void *)simple_strtoul(argv[optind], NULL, 16);
483                 optind++;
484         }
485         if (argc > optind) {
486                 file_size = NULL;
487                 size = simple_strtoul(argv[optind], NULL, 16);
488                 optind++;
489         }
490         if (load_addr != NULL) {
491                 addr = (void *)simple_strtoul(load_addr, NULL, 16);
492                 printf("Using default load address %p\n", addr);
493         }
494         if (file_size != NULL) {
495                 size = simple_strtoul(file_size, NULL, 16);
496                 printf("Using default file size %08x\n", size);
497         }
498         if (size > 0)
499                 fw_num_blocks = DIV_ROUND_UP(size, mtd->erasesize);
500         else
501                 fw_num_blocks = 0;
502
503         if (uboot_part) {
504                 ret = find_dev_and_part(uboot_part, &dev, &part_num,
505                                         &part_info);
506                 if (ret) {
507                         printf("Failed to find '%s' partition: %d\n",
508                                 uboot_part, ret);
509                         return ret;
510                 }
511                 fw1_start_block = lldiv(part_info->offset, mtd->erasesize);
512                 max_len1 = part_info->size;
513                 if (size == 0)
514                         fw_num_blocks = lldiv(max_len1, mtd->erasesize);
515         } else {
516                 max_len1 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
517         }
518
519         if (redund_part) {
520                 ret = find_dev_and_part(redund_part, &dev, &redund_part_num,
521                                         &redund_part_info);
522                 if (ret) {
523                         printf("Failed to find '%s' partition: %d\n",
524                                 redund_part, ret);
525                         return ret;
526                 }
527                 fw2_start_block = lldiv(redund_part_info->offset, mtd->erasesize);
528                 max_len2 = redund_part_info->size;
529                 if (fw2_start_block == fcb_start_block) {
530                         fw2_start_block++;
531                         max_len2 -= mtd->erasesize;
532                 }
533                 if (size == 0)
534                         fw_num_blocks = lldiv(max_len2, mtd->erasesize);
535         } else if (fw2_set) {
536                 max_len2 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
537         } else {
538                 max_len2 = 0;
539         }
540
541         fw1_skip = find_contig_space(fw1_start_block, fw_num_blocks,
542                                 lldiv(max_len1, mtd->erasesize));
543         if (fw1_skip < 0) {
544                 printf("Could not find %lu contiguous good blocks for fw image in blocks %lu..%llu\n",
545                         fw_num_blocks, fw1_start_block,
546                         fw1_start_block + lldiv(max_len1, mtd->erasesize) - 1);
547                 if (uboot_part) {
548 #ifdef CONFIG_ENV_IS_IN_NAND
549                         if (part_info->offset <= CONFIG_ENV_OFFSET + TOTAL_ENV_SIZE) {
550                                 printf("Use a different partition\n");
551                         } else {
552                                 printf("Increase the size of the '%s' partition\n",
553                                         uboot_part);
554                         }
555 #else
556                         printf("Increase the size of the '%s' partition\n",
557                                 uboot_part);
558 #endif
559                 } else {
560                         printf("Increase the number of spare blocks to use with the '-e' option\n");
561                 }
562                 return -ENOSPC;
563         }
564         if (extra_blocks)
565                 fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
566         else
567                 fw1_end_block = fw1_start_block + fw_num_blocks + fw1_skip - 1;
568
569         if (fw2_set && fw2_start_block == 0)
570                 fw2_start_block = fw1_end_block + 1;
571         if (fw2_start_block > 0) {
572                 fw2_skip = find_contig_space(fw2_start_block, fw_num_blocks,
573                                         lldiv(max_len2, mtd->erasesize));
574                 if (fw2_skip < 0) {
575                         printf("Could not find %lu contiguous good blocks for redundant fw image in blocks %lu..%llu\n",
576                                 fw_num_blocks, fw2_start_block,
577                                 fw2_start_block + lldiv(max_len2, mtd->erasesize) - 1);
578                         if (redund_part) {
579                                 printf("Increase the size of the '%s' partition or use a different partition\n",
580                                         redund_part);
581                         } else {
582                                 printf("Increase the number of spare blocks to use with the '-e' option\n");
583                         }
584                         return -ENOSPC;
585                 }
586         } else {
587                 fw2_skip = 0;
588         }
589         if (extra_blocks)
590                 fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
591         else
592                 fw2_end_block = fw2_start_block + fw_num_blocks + fw2_skip - 1;
593
594 #ifdef CONFIG_ENV_IS_IN_NAND
595         fail_if_overlap(fcb, env, "FCB", "Environment");
596         fail_if_overlap(fw1, env, "FW1", "Environment");
597 #endif
598         fail_if_overlap(fcb, fw1, "FCB", "FW1");
599         if (fw2_set) {
600                 fail_if_overlap(fcb, fw2, "FCB", "FW2");
601 #ifdef CONFIG_ENV_IS_IN_NAND
602                 fail_if_overlap(fw2, env, "FW2", "Environment");
603 #endif
604                 fail_if_overlap(fw1, fw2, "FW1", "FW2");
605         }
606         fw1_start_block += fw1_skip;
607         fw2_start_block += fw2_skip;
608
609         buf = memalign(SZ_128K, erase_size);
610         if (buf == NULL) {
611                 printf("Failed to allocate buffer\n");
612                 return -ENOMEM;
613         }
614
615         fcb = create_fcb(buf, fw1_start_block,
616                         fw2_start_block, fw_num_blocks);
617         if (IS_ERR(fcb)) {
618                 printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
619                 ret = PTR_ERR(fcb);
620                 goto out;
621         }
622         encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
623
624         ret = write_fcb(buf, fcb_start_block);
625         if (ret) {
626                 printf("Failed to write FCB to block %lu\n", fcb_start_block);
627                 return ret;
628         }
629
630         printf("Programming U-Boot image from %p to block %lu @ %08llx\n",
631                 buf, fw1_start_block, (u64)fw1_start_block * mtd->erasesize);
632         ret = tx6_prog_uboot(addr, fw1_start_block, fw1_skip, size,
633                         max_len1);
634
635         if (ret || fw2_start_block == 0)
636                 goto out;
637
638         printf("Programming redundant U-Boot image to block %lu @ %08llx\n",
639                 fw2_start_block, (u64)fw2_start_block * mtd->erasesize);
640         ret = tx6_prog_uboot(addr, fw2_start_block, fw2_skip, size,
641                         max_len2);
642 out:
643         free(buf);
644         return ret;
645 }
646
647 U_BOOT_CMD(romupdate, 11, 0, do_update,
648         "Creates an FCB data structure and writes an U-Boot image to flash",
649         "[-f {<part>|block#}] [-r [{<part>|block#}]] [-e #] [<address>] [<length>]\n"
650         "\t-f <part>\twrite bootloader image to partition <part>\n"
651         "\t-f #\t\twrite bootloader image at block # (decimal)\n"
652         "\t-r\t\twrite redundant bootloader image at next free block after first image\n"
653         "\t-r <part>\twrite redundant bootloader image to partition <part>\n"
654         "\t-r #\t\twrite redundant bootloader image at block # (decimal)\n"
655         "\t-e #\t\tspecify number of redundant blocks per boot loader image\n"
656         "\t\t\t(only valid if -f or -r specify a flash address rather than a partition name)\n"
657         "\t-n\t\tshow what would be done without actually updating the flash\n"
658         "\t<address>\tRAM address of bootloader image (default: ${fileaddr})\n"
659         "\t<length>\tlength of bootloader image in RAM (default: ${filesize})"
660         );