Commit 1d8a50993c2783892428c47cfe2e6241fd106caa
Committed by
Ye Li
1 parent
b01b7e1d59
Exists in
smarc_8mm-imx_v2019.04_4.19.35_1.1.0
and in
1 other branch
MLK-22582-2: nandbcb: add nandbcb dump command for i.MX8MM
Verify/dump boot structures written to NAND Flash chip. Signed-off-by: Alice Guo <alice.guo@nxp.com> (cherry picked from commit 215953009a0429705a3aaaa7c495e6ad7a0a8a3a)
Showing 1 changed file with 235 additions and 6 deletions Inline Diff
arch/arm/mach-imx/cmd_nandbcb.c
1 | /* | 1 | /* |
2 | * i.MX6 nand boot control block(bcb). | 2 | * i.MX6 nand boot control block(bcb). |
3 | * | 3 | * |
4 | * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng | 4 | * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng |
5 | * | 5 | * |
6 | * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> | 6 | * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> |
7 | * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net> | 7 | * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net> |
8 | * | 8 | * |
9 | * SPDX-License-Identifier: GPL-2.0+ | 9 | * SPDX-License-Identifier: GPL-2.0+ |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <common.h> | 12 | #include <common.h> |
13 | #include <nand.h> | 13 | #include <nand.h> |
14 | 14 | ||
15 | #include <asm/io.h> | 15 | #include <asm/io.h> |
16 | #include <jffs2/jffs2.h> | 16 | #include <jffs2/jffs2.h> |
17 | #include <linux/mtd/mtd.h> | 17 | #include <linux/mtd/mtd.h> |
18 | 18 | ||
19 | #include <asm/arch/sys_proto.h> | 19 | #include <asm/arch/sys_proto.h> |
20 | #include <asm/mach-imx/imx-nandbcb.h> | 20 | #include <asm/mach-imx/imx-nandbcb.h> |
21 | #include <asm/mach-imx/imximage.cfg> | 21 | #include <asm/mach-imx/imximage.cfg> |
22 | #include <mxs_nand.h> | 22 | #include <mxs_nand.h> |
23 | #include <linux/mtd/mtd.h> | 23 | #include <linux/mtd/mtd.h> |
24 | #include <nand.h> | 24 | #include <nand.h> |
25 | #include <div64.h> | ||
25 | 26 | ||
26 | #define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET) | 27 | #define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET) |
27 | #define GETBIT(v, n) (((v) >> (n)) & 0x1) | 28 | #define GETBIT(v, n) (((v) >> (n)) & 0x1) |
28 | #define IMX8MQ_SPL_SZ 0x3e000 | 29 | #define IMX8MQ_SPL_SZ 0x3e000 |
29 | #define IMX8MQ_HDMI_FW_SZ 0x19c00 | 30 | #define IMX8MQ_HDMI_FW_SZ 0x19c00 |
31 | #define BOOT_SEARCH_COUNT 2 | ||
30 | 32 | ||
33 | struct mtd_info *dump_mtd; | ||
34 | static loff_t dump_nandboot_size; | ||
35 | static struct fcb_block dump_fill_fcb; | ||
36 | static struct dbbt_block dump_fill_dbbt; | ||
37 | static struct fcb_block dump_nand_fcb[BOOT_SEARCH_COUNT]; | ||
38 | static struct dbbt_block dump_nand_dbbt[BOOT_SEARCH_COUNT]; | ||
39 | static u32 dump_fcb_off[BOOT_SEARCH_COUNT]; | ||
40 | static u32 dump_dbbt_off[BOOT_SEARCH_COUNT]; | ||
41 | |||
31 | static u8 calculate_parity_13_8(u8 d) | 42 | static u8 calculate_parity_13_8(u8 d) |
32 | { | 43 | { |
33 | u8 p = 0; | 44 | u8 p = 0; |
34 | 45 | ||
35 | p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0; | 46 | p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0; |
36 | p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ | 47 | p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ |
37 | GETBIT(d, 1)) << 1; | 48 | GETBIT(d, 1)) << 1; |
38 | p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ | 49 | p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ |
39 | GETBIT(d, 0)) << 2; | 50 | GETBIT(d, 0)) << 2; |
40 | p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3; | 51 | p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3; |
41 | p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ | 52 | p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ |
42 | GETBIT(d, 1) ^ GETBIT(d, 0)) << 4; | 53 | GETBIT(d, 1) ^ GETBIT(d, 0)) << 4; |
43 | 54 | ||
44 | return p; | 55 | return p; |
45 | } | 56 | } |
46 | 57 | ||
47 | static void encode_hamming_13_8(void *_src, void *_ecc, size_t size) | 58 | static void encode_hamming_13_8(void *_src, void *_ecc, size_t size) |
48 | { | 59 | { |
49 | int i; | 60 | int i; |
50 | u8 *src = _src; | 61 | u8 *src = _src; |
51 | u8 *ecc = _ecc; | 62 | u8 *ecc = _ecc; |
52 | 63 | ||
53 | for (i = 0; i < size; i++) | 64 | for (i = 0; i < size; i++) |
54 | ecc[i] = calculate_parity_13_8(src[i]); | 65 | ecc[i] = calculate_parity_13_8(src[i]); |
55 | } | 66 | } |
56 | 67 | ||
57 | static u32 calc_chksum(void *buf, size_t size) | 68 | static u32 calc_chksum(void *buf, size_t size) |
58 | { | 69 | { |
59 | u32 chksum = 0; | 70 | u32 chksum = 0; |
60 | u8 *bp = buf; | 71 | u8 *bp = buf; |
61 | size_t i; | 72 | size_t i; |
62 | 73 | ||
63 | for (i = 0; i < size; i++) | 74 | for (i = 0; i < size; i++) |
64 | chksum += bp[i]; | 75 | chksum += bp[i]; |
65 | 76 | ||
66 | return ~chksum; | 77 | return ~chksum; |
67 | } | 78 | } |
68 | 79 | ||
69 | static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd, | 80 | static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd, |
70 | u32 fw1_start, u32 fw2_start, u32 fw_pages) | 81 | u32 fw1_start, u32 fw2_start, u32 fw_pages) |
71 | { | 82 | { |
72 | struct nand_chip *chip = mtd_to_nand(mtd); | 83 | struct nand_chip *chip = mtd_to_nand(mtd); |
73 | struct mxs_nand_info *nand_info = nand_get_controller_data(chip); | 84 | struct mxs_nand_info *nand_info = nand_get_controller_data(chip); |
74 | struct mxs_nand_layout l; | 85 | struct mxs_nand_layout l; |
75 | 86 | ||
76 | mxs_nand_get_layout(mtd, &l); | 87 | mxs_nand_get_layout(mtd, &l); |
77 | 88 | ||
78 | fcb->fingerprint = FCB_FINGERPRINT; | 89 | fcb->fingerprint = FCB_FINGERPRINT; |
79 | fcb->version = FCB_VERSION_1; | 90 | fcb->version = FCB_VERSION_1; |
80 | 91 | ||
81 | fcb->pagesize = mtd->writesize; | 92 | fcb->pagesize = mtd->writesize; |
82 | fcb->oob_pagesize = mtd->writesize + mtd->oobsize; | 93 | fcb->oob_pagesize = mtd->writesize + mtd->oobsize; |
83 | fcb->sectors = mtd->erasesize / mtd->writesize; | 94 | fcb->sectors = mtd->erasesize / mtd->writesize; |
84 | 95 | ||
85 | fcb->meta_size = l.meta_size; | 96 | fcb->meta_size = l.meta_size; |
86 | fcb->nr_blocks = l.nblocks; | 97 | fcb->nr_blocks = l.nblocks; |
87 | fcb->ecc_nr = l.data0_size; | 98 | fcb->ecc_nr = l.data0_size; |
88 | fcb->ecc_level = l.ecc0; | 99 | fcb->ecc_level = l.ecc0; |
89 | fcb->ecc_size = l.datan_size; | 100 | fcb->ecc_size = l.datan_size; |
90 | fcb->ecc_type = l.eccn; | 101 | fcb->ecc_type = l.eccn; |
91 | fcb->bchtype = l.gf_len; | 102 | fcb->bchtype = l.gf_len; |
92 | 103 | ||
93 | /* Also hardcoded in kobs-ng */ | 104 | /* Also hardcoded in kobs-ng */ |
94 | if (is_mx6() || is_imx8m()) { | 105 | if (is_mx6() || is_imx8m()) { |
95 | fcb->datasetup = 80; | 106 | fcb->datasetup = 80; |
96 | fcb->datahold = 60; | 107 | fcb->datahold = 60; |
97 | fcb->addr_setup = 25; | 108 | fcb->addr_setup = 25; |
98 | fcb->dsample_time = 6; | 109 | fcb->dsample_time = 6; |
99 | } else if (is_mx7()) { | 110 | } else if (is_mx7()) { |
100 | fcb->datasetup = 10; | 111 | fcb->datasetup = 10; |
101 | fcb->datahold = 7; | 112 | fcb->datahold = 7; |
102 | fcb->addr_setup = 15; | 113 | fcb->addr_setup = 15; |
103 | fcb->dsample_time = 6; | 114 | fcb->dsample_time = 6; |
104 | } | 115 | } |
105 | 116 | ||
106 | /* DBBT search area starts at second page on first block */ | 117 | /* DBBT search area starts at second page on first block */ |
107 | fcb->dbbt_start = 1; | 118 | fcb->dbbt_start = 1; |
108 | 119 | ||
109 | fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; | 120 | fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; |
110 | fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; | 121 | fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; |
111 | 122 | ||
112 | fcb->phy_offset = mtd->writesize; | 123 | fcb->phy_offset = mtd->writesize; |
113 | 124 | ||
114 | fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1; | 125 | fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1; |
115 | 126 | ||
116 | fcb->disbbm = 0; | 127 | fcb->disbbm = 0; |
117 | fcb->disbbm_search = 0; | 128 | fcb->disbbm_search = 0; |
118 | 129 | ||
119 | fcb->fw1_start = fw1_start; /* Firmware image starts on this sector */ | 130 | fcb->fw1_start = fw1_start; /* Firmware image starts on this sector */ |
120 | fcb->fw2_start = fw2_start; /* Secondary FW Image starting Sector */ | 131 | fcb->fw2_start = fw2_start; /* Secondary FW Image starting Sector */ |
121 | fcb->fw1_pages = fw_pages; /* Number of sectors in firmware image */ | 132 | fcb->fw1_pages = fw_pages; /* Number of sectors in firmware image */ |
122 | fcb->fw2_pages = fw_pages; /* Number of sector in secondary FW image */ | 133 | fcb->fw2_pages = fw_pages; /* Number of sector in secondary FW image */ |
123 | 134 | ||
124 | fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); | 135 | fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); |
125 | } | 136 | } |
126 | 137 | ||
127 | static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks) | 138 | static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks) |
128 | { | 139 | { |
129 | int n, n_bad_blocks = 0; | 140 | int n, n_bad_blocks = 0; |
130 | u32 *bb = buf + 0x8; | 141 | u32 *bb = buf + 0x8; |
131 | u32 *n_bad_blocksp = buf + 0x4; | 142 | u32 *n_bad_blocksp = buf + 0x4; |
132 | 143 | ||
133 | for (n = 0; n < num_blocks; n++) { | 144 | for (n = 0; n < num_blocks; n++) { |
134 | loff_t offset = n * mtd->erasesize; | 145 | loff_t offset = n * mtd->erasesize; |
135 | if (mtd_block_isbad(mtd, offset)) { | 146 | if (mtd_block_isbad(mtd, offset)) { |
136 | n_bad_blocks++; | 147 | n_bad_blocks++; |
137 | *bb = n; | 148 | *bb = n; |
138 | bb++; | 149 | bb++; |
139 | } | 150 | } |
140 | } | 151 | } |
141 | 152 | ||
142 | *n_bad_blocksp = n_bad_blocks; | 153 | *n_bad_blocksp = n_bad_blocks; |
143 | 154 | ||
144 | return n_bad_blocks; | 155 | return n_bad_blocks; |
145 | } | 156 | } |
146 | 157 | ||
147 | static int write_fcb_dbbt(struct mtd_info *mtd, struct fcb_block *fcb, | 158 | static int write_fcb_dbbt_and_readback(struct mtd_info *mtd, struct fcb_block *fcb, |
148 | struct dbbt_block *dbbt, void *dbbt_data_page, | 159 | struct dbbt_block *dbbt, void *dbbt_data_page, |
149 | loff_t off) | 160 | loff_t off) |
150 | { | 161 | { |
151 | void *fcb_raw_page = 0; | 162 | void *fcb_raw_page = 0; |
152 | int i, ret; | 163 | int i, ret; |
153 | size_t dummy; | 164 | size_t dummy; |
154 | 165 | ||
155 | /* | 166 | /* |
156 | * We prepare raw page only for i.MX6, for i.MX7 we | 167 | * We prepare raw page only for i.MX6, for i.MX7 we |
157 | * leverage BCH hw module instead | 168 | * leverage BCH hw module instead |
158 | */ | 169 | */ |
159 | if (is_mx6()) { | 170 | if (is_mx6()) { |
160 | /* write fcb/dbbt */ | 171 | /* write fcb/dbbt */ |
161 | fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, | 172 | fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, |
162 | GFP_KERNEL); | 173 | GFP_KERNEL); |
163 | if (!fcb_raw_page) { | 174 | if (!fcb_raw_page) { |
164 | debug("failed to allocate fcb_raw_page\n"); | 175 | debug("failed to allocate fcb_raw_page\n"); |
165 | ret = -ENOMEM; | 176 | ret = -ENOMEM; |
166 | return ret; | 177 | return ret; |
167 | } | 178 | } |
168 | 179 | ||
169 | memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); | 180 | memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); |
170 | encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + | 181 | encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + |
171 | 12 + 512, 512); | 182 | 12 + 512, 512); |
172 | /* | 183 | /* |
173 | * Set the first and second byte of OOB data to 0xFF, | 184 | * Set the first and second byte of OOB data to 0xFF, |
174 | * not 0x00. These bytes are used as the Manufacturers Bad | 185 | * not 0x00. These bytes are used as the Manufacturers Bad |
175 | * Block Marker (MBBM). Since the FCB is mostly written to | 186 | * Block Marker (MBBM). Since the FCB is mostly written to |
176 | * the first page in a block, a scan for | 187 | * the first page in a block, a scan for |
177 | * factory bad blocks will detect these blocks as bad, e.g. | 188 | * factory bad blocks will detect these blocks as bad, e.g. |
178 | * when function nand_scan_bbt() is executed to build a new | 189 | * when function nand_scan_bbt() is executed to build a new |
179 | * bad block table. | 190 | * bad block table. |
180 | */ | 191 | */ |
181 | memset(fcb_raw_page + mtd->writesize, 0xFF, 2); | 192 | memset(fcb_raw_page + mtd->writesize, 0xFF, 2); |
182 | } | 193 | } |
183 | for (i = 0; i < 2; i++) { | 194 | for (i = 0; i < 2; i++) { |
184 | if (mtd_block_isbad(mtd, off)) { | 195 | if (mtd_block_isbad(mtd, off)) { |
185 | printf("Block %d is bad, skipped\n", i); | 196 | printf("Block %d is bad, skipped\n", i); |
186 | continue; | 197 | continue; |
187 | } | 198 | } |
188 | 199 | ||
189 | /* | 200 | /* |
190 | * User BCH ECC hardware module for i.MX7 | 201 | * User BCH ECC hardware module for i.MX7 |
191 | */ | 202 | */ |
192 | if (is_mx7() || is_imx8m()) { | 203 | if (is_mx7() || is_imx8m()) { |
193 | u32 off = i * mtd->erasesize; | 204 | u32 off = i * mtd->erasesize; |
194 | size_t rwsize = sizeof(*fcb); | 205 | size_t rwsize = sizeof(*fcb); |
195 | 206 | ||
196 | printf("Writing %zd bytes to 0x%x: ", rwsize, off); | 207 | printf("Writing %zd bytes to 0x%x: ", rwsize, off); |
197 | 208 | ||
198 | /* switch nand BCH to FCB compatible settings */ | 209 | /* switch nand BCH to FCB compatible settings */ |
199 | mxs_nand_mode_fcb(mtd); | 210 | mxs_nand_mode_fcb(mtd); |
200 | ret = nand_write(mtd, off, &rwsize, | 211 | ret = nand_write(mtd, off, &rwsize, |
201 | (unsigned char *)fcb); | 212 | (unsigned char *)fcb); |
213 | |||
214 | dump_fcb_off[i] = off; | ||
215 | nand_read(mtd, off, &rwsize, | ||
216 | (unsigned char *)(dump_nand_fcb + i)); | ||
217 | |||
202 | mxs_nand_mode_normal(mtd); | 218 | mxs_nand_mode_normal(mtd); |
203 | 219 | ||
204 | printf("%s\n", ret ? "ERROR" : "OK"); | 220 | printf("%s\n", ret ? "ERROR" : "OK"); |
205 | } else if (is_mx6()) { | 221 | } else if (is_mx6()) { |
206 | /* raw write */ | 222 | /* raw write */ |
207 | mtd_oob_ops_t ops = { | 223 | mtd_oob_ops_t ops = { |
208 | .datbuf = (u8 *)fcb_raw_page, | 224 | .datbuf = (u8 *)fcb_raw_page, |
209 | .oobbuf = ((u8 *)fcb_raw_page) + | 225 | .oobbuf = ((u8 *)fcb_raw_page) + |
210 | mtd->writesize, | 226 | mtd->writesize, |
211 | .len = mtd->writesize, | 227 | .len = mtd->writesize, |
212 | .ooblen = mtd->oobsize, | 228 | .ooblen = mtd->oobsize, |
213 | .mode = MTD_OPS_RAW | 229 | .mode = MTD_OPS_RAW |
214 | }; | 230 | }; |
215 | 231 | ||
216 | ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops); | 232 | ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops); |
217 | if (ret) | 233 | if (ret) |
218 | goto fcb_raw_page_err; | 234 | goto fcb_raw_page_err; |
219 | debug("NAND fcb write: 0x%x offset 0x%zx written: %s\n", | 235 | debug("NAND fcb write: 0x%x offset 0x%zx written: %s\n", |
220 | mtd->erasesize * i, ops.len, ret ? | 236 | mtd->erasesize * i, ops.len, ret ? |
221 | "ERROR" : "OK"); | 237 | "ERROR" : "OK"); |
222 | } | 238 | } |
223 | 239 | ||
224 | ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize, | 240 | ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize, |
225 | mtd->writesize, &dummy, (void *)dbbt); | 241 | mtd->writesize, &dummy, (void *)dbbt); |
226 | if (ret) | 242 | if (ret) |
227 | goto fcb_raw_page_err; | 243 | goto fcb_raw_page_err; |
228 | debug("NAND dbbt write: 0x%x offset, 0x%zx bytes written: %s\n", | 244 | debug("NAND dbbt write: 0x%x offset, 0x%zx bytes written: %s\n", |
229 | mtd->erasesize * i + mtd->writesize, dummy, | 245 | mtd->erasesize * i + mtd->writesize, dummy, |
230 | ret ? "ERROR" : "OK"); | 246 | ret ? "ERROR" : "OK"); |
231 | 247 | ||
248 | dump_dbbt_off[i] = mtd->erasesize * i + mtd->writesize; | ||
249 | size_t rwsize = sizeof(*dbbt); | ||
250 | |||
251 | nand_read(mtd, dump_dbbt_off[i], &rwsize, (unsigned char *)(dump_nand_dbbt + i)); | ||
252 | |||
232 | /* dbbtpages == 0 if no bad blocks */ | 253 | /* dbbtpages == 0 if no bad blocks */ |
233 | if (dbbt->dbbtpages > 0) { | 254 | if (dbbt->dbbtpages > 0) { |
234 | loff_t to = (mtd->erasesize * i + mtd->writesize * 5); | 255 | loff_t to = (mtd->erasesize * i + mtd->writesize * 5); |
235 | 256 | ||
236 | ret = mtd_write(mtd, to, mtd->writesize, &dummy, | 257 | ret = mtd_write(mtd, to, mtd->writesize, &dummy, |
237 | dbbt_data_page); | 258 | dbbt_data_page); |
238 | if (ret) | 259 | if (ret) |
239 | goto fcb_raw_page_err; | 260 | goto fcb_raw_page_err; |
240 | } | 261 | } |
241 | } | 262 | } |
242 | 263 | ||
243 | fcb_raw_page_err: | 264 | fcb_raw_page_err: |
244 | if (is_mx6()) | 265 | if (is_mx6()) |
245 | kfree(fcb_raw_page); | 266 | kfree(fcb_raw_page); |
246 | 267 | ||
247 | return ret; | 268 | return ret; |
248 | } | 269 | } |
249 | 270 | ||
250 | static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, | 271 | static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, |
251 | size_t maxsize, const u_char *buf) | 272 | size_t maxsize, const u_char *buf) |
252 | { | 273 | { |
253 | nand_erase_options_t opts; | 274 | nand_erase_options_t opts; |
254 | struct fcb_block *fcb; | 275 | struct fcb_block *fcb; |
255 | struct dbbt_block *dbbt; | 276 | struct dbbt_block *dbbt; |
256 | loff_t fw1_off; | 277 | loff_t fw1_off; |
257 | void *fwbuf, *dbbt_page, *dbbt_data_page; | 278 | void *fwbuf, *dbbt_page, *dbbt_data_page; |
258 | u32 fw1_start, fw1_pages; | 279 | u32 fw1_start, fw1_pages; |
259 | int nr_blks, nr_blks_fcb, fw1_blk; | 280 | int nr_blks, nr_blks_fcb, fw1_blk; |
260 | size_t fwsize; | 281 | size_t fwsize; |
261 | int ret; | 282 | int ret; |
262 | size_t extra_fwsize; | 283 | size_t extra_fwsize; |
263 | void *extra_fwbuf; | 284 | void *extra_fwbuf; |
264 | loff_t extra_fw1_off; | 285 | loff_t extra_fw1_off; |
265 | 286 | ||
266 | /* erase */ | 287 | /* erase */ |
267 | memset(&opts, 0, sizeof(opts)); | 288 | memset(&opts, 0, sizeof(opts)); |
268 | opts.offset = off; | 289 | opts.offset = off; |
269 | opts.length = maxsize - 1; | 290 | opts.length = maxsize - 1; |
270 | ret = nand_erase_opts(mtd, &opts); | 291 | ret = nand_erase_opts(mtd, &opts); |
271 | if (ret) { | 292 | if (ret) { |
272 | printf("%s: erase failed (ret = %d)\n", __func__, ret); | 293 | printf("%s: erase failed (ret = %d)\n", __func__, ret); |
273 | return ret; | 294 | return ret; |
274 | } | 295 | } |
275 | 296 | ||
276 | /* | 297 | /* |
277 | * Reference documentation from i.MX6DQRM section 8.5.2.2 | 298 | * Reference documentation from i.MX6DQRM section 8.5.2.2 |
278 | * | 299 | * |
279 | * Nand Boot Control Block(BCB) contains two data structures, | 300 | * Nand Boot Control Block(BCB) contains two data structures, |
280 | * - Firmware Configuration Block(FCB) | 301 | * - Firmware Configuration Block(FCB) |
281 | * - Discovered Bad Block Table(DBBT) | 302 | * - Discovered Bad Block Table(DBBT) |
282 | * | 303 | * |
283 | * FCB contains, | 304 | * FCB contains, |
284 | * - nand timings | 305 | * - nand timings |
285 | * - DBBT search page address, | 306 | * - DBBT search page address, |
286 | * - start page address of primary firmware | 307 | * - start page address of primary firmware |
287 | * - start page address of secondary firmware | 308 | * - start page address of secondary firmware |
288 | * | 309 | * |
289 | * setup fcb: | 310 | * setup fcb: |
290 | * - number of blocks = mtd partition size / mtd erasesize | 311 | * - number of blocks = mtd partition size / mtd erasesize |
291 | * - two firmware blocks, primary and secondary | 312 | * - two firmware blocks, primary and secondary |
292 | * - first 4 block for FCB/DBBT | 313 | * - first 4 block for FCB/DBBT |
293 | * - rest split in half for primary and secondary firmware | 314 | * - rest split in half for primary and secondary firmware |
294 | * - same firmware will write two times | 315 | * - same firmware will write two times |
295 | */ | 316 | */ |
296 | nr_blks_fcb = 2; | 317 | nr_blks_fcb = BOOT_SEARCH_COUNT; |
297 | nr_blks = maxsize / mtd->erasesize; | 318 | nr_blks = maxsize / mtd->erasesize; |
298 | fw1_blk = nr_blks_fcb; | 319 | fw1_blk = nr_blks_fcb; |
299 | 320 | ||
300 | /* write fw */ | 321 | /* write fw */ |
301 | fwbuf = NULL; | 322 | fwbuf = NULL; |
302 | if (is_mx6() || is_mx7()) { | 323 | if (is_mx6() || is_mx7()) { |
303 | fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize, | 324 | fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize, |
304 | mtd->writesize); | 325 | mtd->writesize); |
305 | fwbuf = kzalloc(fwsize, GFP_KERNEL); | 326 | fwbuf = kzalloc(fwsize, GFP_KERNEL); |
306 | if (!fwbuf) { | 327 | if (!fwbuf) { |
307 | debug("failed to allocate fwbuf\n"); | 328 | debug("failed to allocate fwbuf\n"); |
308 | ret = -ENOMEM; | 329 | ret = -ENOMEM; |
309 | goto err; | 330 | goto err; |
310 | } | 331 | } |
311 | 332 | ||
312 | memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size); | 333 | memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size); |
313 | fw1_off = fw1_blk * mtd->erasesize; | 334 | fw1_off = fw1_blk * mtd->erasesize; |
314 | ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, | 335 | ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, |
315 | (u_char *)fwbuf, WITH_WR_VERIFY); | 336 | (u_char *)fwbuf, WITH_WR_VERIFY); |
316 | printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", | 337 | printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", |
317 | fw1_off, fwsize, ret ? "ERROR" : "OK"); | 338 | fw1_off, fwsize, ret ? "ERROR" : "OK"); |
318 | if (ret) | 339 | if (ret) |
319 | goto fwbuf_err; | 340 | goto fwbuf_err; |
320 | } else if (is_imx8m()) { | 341 | } else if (is_imx8m()) { |
321 | fwsize = ALIGN(IMX8MQ_SPL_SZ + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize); | 342 | fwsize = ALIGN(IMX8MQ_SPL_SZ + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize); |
322 | fwbuf = kzalloc(fwsize, GFP_KERNEL); | 343 | fwbuf = kzalloc(fwsize, GFP_KERNEL); |
323 | if (!fwbuf) { | 344 | if (!fwbuf) { |
324 | debug("failed to allocate fwbuf\n"); | 345 | debug("failed to allocate fwbuf\n"); |
325 | ret = -ENOMEM; | 346 | ret = -ENOMEM; |
326 | goto err; | 347 | goto err; |
327 | } | 348 | } |
328 | 349 | ||
329 | memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, IMX8MQ_SPL_SZ); | 350 | memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, IMX8MQ_SPL_SZ); |
330 | fw1_off = fw1_blk * mtd->erasesize; | 351 | fw1_off = fw1_blk * mtd->erasesize; |
331 | ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, | 352 | ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, |
332 | (u_char *)fwbuf, WITH_WR_VERIFY); | 353 | (u_char *)fwbuf, WITH_WR_VERIFY); |
333 | printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", | 354 | printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", |
334 | fw1_off, fwsize, ret ? "ERROR" : "OK"); | 355 | fw1_off, fwsize, ret ? "ERROR" : "OK"); |
335 | if (ret) | 356 | if (ret) |
336 | goto fwbuf_err; | 357 | goto fwbuf_err; |
337 | 358 | ||
338 | extra_fwsize = ALIGN(IMX8MQ_SPL_SZ + mtd->writesize, mtd->writesize); | 359 | extra_fwsize = ALIGN(IMX8MQ_SPL_SZ + mtd->writesize, mtd->writesize); |
339 | extra_fwbuf = kzalloc(extra_fwsize, GFP_KERNEL); | 360 | extra_fwbuf = kzalloc(extra_fwsize, GFP_KERNEL); |
340 | extra_fw1_off = fw1_off + mtd->erasesize * ((IMX8MQ_SPL_SZ + mtd->erasesize - 1) / mtd->erasesize); | 361 | extra_fw1_off = fw1_off + mtd->erasesize * ((IMX8MQ_SPL_SZ + mtd->erasesize - 1) / mtd->erasesize); |
341 | if (!extra_fwbuf) { | 362 | if (!extra_fwbuf) { |
342 | debug("failed to allocate fwbuf\n"); | 363 | debug("failed to allocate fwbuf\n"); |
343 | ret = -ENOMEM; | 364 | ret = -ENOMEM; |
344 | goto fwbuf_err; | 365 | goto fwbuf_err; |
345 | } | 366 | } |
346 | 367 | ||
347 | memcpy(extra_fwbuf, buf + IMX8MQ_HDMI_FW_SZ, IMX8MQ_SPL_SZ); | 368 | memcpy(extra_fwbuf, buf + IMX8MQ_HDMI_FW_SZ, IMX8MQ_SPL_SZ); |
348 | ret = nand_write_skip_bad(mtd, extra_fw1_off, &extra_fwsize, NULL, maxsize, | 369 | ret = nand_write_skip_bad(mtd, extra_fw1_off, &extra_fwsize, NULL, maxsize, |
349 | (u_char *)extra_fwbuf, WITH_WR_VERIFY); | 370 | (u_char *)extra_fwbuf, WITH_WR_VERIFY); |
350 | printf("NAND extra_fw write: 0x%llx offset, 0x%zx bytes written: %s\n", | 371 | printf("NAND extra_fw write: 0x%llx offset, 0x%zx bytes written: %s\n", |
351 | extra_fw1_off, extra_fwsize, ret ? "ERROR" : "OK"); | 372 | extra_fw1_off, extra_fwsize, ret ? "ERROR" : "OK"); |
352 | if (ret) { | 373 | if (ret) { |
353 | kfree(extra_fwbuf); | 374 | kfree(extra_fwbuf); |
354 | goto fwbuf_err; | 375 | goto fwbuf_err; |
355 | } | 376 | } |
356 | } | 377 | } |
357 | 378 | ||
358 | /* fill fcb */ | 379 | /* fill fcb */ |
359 | fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); | 380 | fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); |
360 | if (!fcb) { | 381 | if (!fcb) { |
361 | debug("failed to allocate fcb\n"); | 382 | debug("failed to allocate fcb\n"); |
362 | ret = -ENOMEM; | 383 | ret = -ENOMEM; |
363 | goto fwbuf_err; | 384 | goto fwbuf_err; |
364 | } | 385 | } |
365 | 386 | ||
366 | fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize; | 387 | fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize; |
367 | fw1_pages = size / mtd->writesize + 1; | 388 | fw1_pages = size / mtd->writesize + 1; |
368 | if (is_imx8m()) | 389 | if (is_imx8m()) |
369 | fw1_pages = (IMX8MQ_SPL_SZ + (mtd->writesize - 1)) / mtd->writesize; | 390 | fw1_pages = (IMX8MQ_SPL_SZ + (mtd->writesize - 1)) / mtd->writesize; |
370 | fill_fcb(fcb, mtd, fw1_start, 0, fw1_pages); | 391 | fill_fcb(fcb, mtd, fw1_start, 0, fw1_pages); |
371 | 392 | ||
393 | dump_fill_fcb = *fcb; | ||
394 | |||
372 | /* fill dbbt */ | 395 | /* fill dbbt */ |
373 | dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); | 396 | dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); |
374 | if (!dbbt_page) { | 397 | if (!dbbt_page) { |
375 | debug("failed to allocate dbbt_page\n"); | 398 | debug("failed to allocate dbbt_page\n"); |
376 | ret = -ENOMEM; | 399 | ret = -ENOMEM; |
377 | goto fcb_err; | 400 | goto fcb_err; |
378 | } | 401 | } |
379 | 402 | ||
380 | dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); | 403 | dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); |
381 | if (!dbbt_data_page) { | 404 | if (!dbbt_data_page) { |
382 | debug("failed to allocate dbbt_data_page\n"); | 405 | debug("failed to allocate dbbt_data_page\n"); |
383 | ret = -ENOMEM; | 406 | ret = -ENOMEM; |
384 | goto dbbt_page_err; | 407 | goto dbbt_page_err; |
385 | } | 408 | } |
386 | 409 | ||
387 | dbbt = dbbt_page; | 410 | dbbt = dbbt_page; |
388 | dbbt->checksum = 0; | 411 | dbbt->checksum = 0; |
389 | dbbt->fingerprint = DBBT_FINGERPRINT2; | 412 | dbbt->fingerprint = DBBT_FINGERPRINT2; |
390 | dbbt->version = DBBT_VERSION_1; | 413 | dbbt->version = DBBT_VERSION_1; |
391 | ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks); | 414 | ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks); |
392 | if (ret < 0) | 415 | if (ret < 0) |
393 | goto dbbt_data_page_err; | 416 | goto dbbt_data_page_err; |
394 | else if (ret > 0) | 417 | else if (ret > 0) |
395 | dbbt->dbbtpages = 1; | 418 | dbbt->dbbtpages = 1; |
396 | 419 | ||
420 | dump_fill_dbbt = *dbbt; | ||
421 | |||
397 | /* write fcb and dbbt to nand */ | 422 | /* write fcb and dbbt to nand */ |
398 | ret = write_fcb_dbbt(mtd, fcb, dbbt, dbbt_data_page, off); | 423 | ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, off); |
399 | if (ret < 0) | 424 | if (ret < 0) |
400 | printf("failed to write FCB/DBBT\n"); | 425 | printf("failed to write FCB/DBBT\n"); |
401 | 426 | ||
402 | dbbt_data_page_err: | 427 | dbbt_data_page_err: |
403 | kfree(dbbt_data_page); | 428 | kfree(dbbt_data_page); |
404 | dbbt_page_err: | 429 | dbbt_page_err: |
405 | kfree(dbbt_page); | 430 | kfree(dbbt_page); |
406 | fcb_err: | 431 | fcb_err: |
407 | kfree(fcb); | 432 | kfree(fcb); |
408 | fwbuf_err: | 433 | fwbuf_err: |
409 | kfree(fwbuf); | 434 | kfree(fwbuf); |
410 | err: | 435 | err: |
411 | return ret; | 436 | return ret; |
412 | } | 437 | } |
413 | 438 | ||
414 | static int do_nandbcb_bcbonly(int argc, char * const argv[]) | 439 | static int do_nandbcb_bcbonly(int argc, char * const argv[]) |
415 | { | 440 | { |
416 | struct fcb_block *fcb; | 441 | struct fcb_block *fcb; |
417 | struct dbbt_block *dbbt; | 442 | struct dbbt_block *dbbt; |
418 | u32 fw_len, fw1_off, fw2_off; | 443 | u32 fw_len, fw1_off, fw2_off; |
419 | struct mtd_info *mtd; | 444 | struct mtd_info *mtd; |
420 | void *dbbt_page, *dbbt_data_page; | 445 | void *dbbt_page, *dbbt_data_page; |
421 | int dev, ret; | 446 | int dev, ret; |
422 | 447 | ||
423 | dev = nand_curr_device; | 448 | dev = nand_curr_device; |
424 | if ((dev < 0) || (dev >= CONFIG_SYS_MAX_NAND_DEVICE) || | 449 | if ((dev < 0) || (dev >= CONFIG_SYS_MAX_NAND_DEVICE) || |
425 | (!get_nand_dev_by_index(dev))) { | 450 | (!get_nand_dev_by_index(dev))) { |
426 | puts("No devices available\n"); | 451 | puts("No devices available\n"); |
427 | return CMD_RET_FAILURE; | 452 | return CMD_RET_FAILURE; |
428 | } | 453 | } |
429 | 454 | ||
430 | mtd = get_nand_dev_by_index(dev); | 455 | mtd = get_nand_dev_by_index(dev); |
431 | 456 | ||
432 | if (argc < 3) | 457 | if (argc < 3) |
433 | return CMD_RET_FAILURE; | 458 | return CMD_RET_FAILURE; |
434 | 459 | ||
435 | fw_len = simple_strtoul(argv[1], NULL, 16); | 460 | fw_len = simple_strtoul(argv[1], NULL, 16); |
436 | fw1_off = simple_strtoul(argv[2], NULL, 16); | 461 | fw1_off = simple_strtoul(argv[2], NULL, 16); |
437 | 462 | ||
438 | if (argc > 3) | 463 | if (argc > 3) |
439 | fw2_off = simple_strtoul(argv[3], NULL, 16); | 464 | fw2_off = simple_strtoul(argv[3], NULL, 16); |
440 | else | 465 | else |
441 | fw2_off = fw1_off; | 466 | fw2_off = fw1_off; |
442 | 467 | ||
443 | /* fill fcb */ | 468 | /* fill fcb */ |
444 | fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); | 469 | fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); |
445 | if (!fcb) { | 470 | if (!fcb) { |
446 | debug("failed to allocate fcb\n"); | 471 | debug("failed to allocate fcb\n"); |
447 | ret = -ENOMEM; | 472 | ret = -ENOMEM; |
448 | return CMD_RET_FAILURE; | 473 | return CMD_RET_FAILURE; |
449 | } | 474 | } |
450 | 475 | ||
451 | fill_fcb(fcb, mtd, fw1_off / mtd->writesize, | 476 | fill_fcb(fcb, mtd, fw1_off / mtd->writesize, |
452 | fw2_off / mtd->writesize, fw_len / mtd->writesize); | 477 | fw2_off / mtd->writesize, fw_len / mtd->writesize); |
453 | 478 | ||
454 | /* fill dbbt */ | 479 | /* fill dbbt */ |
455 | dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); | 480 | dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); |
456 | if (!dbbt_page) { | 481 | if (!dbbt_page) { |
457 | debug("failed to allocate dbbt_page\n"); | 482 | debug("failed to allocate dbbt_page\n"); |
458 | ret = -ENOMEM; | 483 | ret = -ENOMEM; |
459 | goto fcb_err; | 484 | goto fcb_err; |
460 | } | 485 | } |
461 | 486 | ||
462 | dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); | 487 | dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); |
463 | if (!dbbt_data_page) { | 488 | if (!dbbt_data_page) { |
464 | debug("failed to allocate dbbt_data_page\n"); | 489 | debug("failed to allocate dbbt_data_page\n"); |
465 | ret = -ENOMEM; | 490 | ret = -ENOMEM; |
466 | goto dbbt_page_err; | 491 | goto dbbt_page_err; |
467 | } | 492 | } |
468 | 493 | ||
469 | dbbt = dbbt_page; | 494 | dbbt = dbbt_page; |
470 | dbbt->checksum = 0; | 495 | dbbt->checksum = 0; |
471 | dbbt->fingerprint = DBBT_FINGERPRINT2; | 496 | dbbt->fingerprint = DBBT_FINGERPRINT2; |
472 | dbbt->version = DBBT_VERSION_1; | 497 | dbbt->version = DBBT_VERSION_1; |
473 | ret = dbbt_fill_data(mtd, dbbt_data_page, 0); | 498 | ret = dbbt_fill_data(mtd, dbbt_data_page, 0); |
474 | if (ret < 0) | 499 | if (ret < 0) |
475 | goto dbbt_data_page_err; | 500 | goto dbbt_data_page_err; |
476 | else if (ret > 0) | 501 | else if (ret > 0) |
477 | dbbt->dbbtpages = 1; | 502 | dbbt->dbbtpages = 1; |
478 | 503 | ||
479 | /* write fcb and dbbt to nand */ | 504 | /* write fcb and dbbt to nand */ |
480 | ret = write_fcb_dbbt(mtd, fcb, dbbt, dbbt_data_page, 0); | 505 | ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, 0); |
481 | dbbt_data_page_err: | 506 | dbbt_data_page_err: |
482 | kfree(dbbt_data_page); | 507 | kfree(dbbt_data_page); |
483 | dbbt_page_err: | 508 | dbbt_page_err: |
484 | kfree(dbbt_page); | 509 | kfree(dbbt_page); |
485 | fcb_err: | 510 | fcb_err: |
486 | kfree(fcb); | 511 | kfree(fcb); |
487 | 512 | ||
488 | if (ret < 0) { | 513 | if (ret < 0) { |
489 | printf("failed to write FCB/DBBT\n"); | 514 | printf("failed to write FCB/DBBT\n"); |
490 | return CMD_RET_FAILURE; | 515 | return CMD_RET_FAILURE; |
491 | } | 516 | } |
492 | 517 | ||
493 | return CMD_RET_SUCCESS; | 518 | return CMD_RET_SUCCESS; |
494 | } | 519 | } |
495 | 520 | ||
521 | /* dump data which is planned to be encoded and written to NAND chip */ | ||
522 | void mtd_cfg_dump(void) | ||
523 | { | ||
524 | u64 blocks; | ||
525 | |||
526 | printf("MTD CONFIG:\n"); | ||
527 | printf(" %s = %d\n", "data_setup_time", dump_fill_fcb.datasetup); | ||
528 | printf(" %s = %d\n", "data_hold_time", dump_fill_fcb.datahold); | ||
529 | printf(" %s = %d\n", "address_setup_time", dump_fill_fcb.addr_setup); | ||
530 | printf(" %s = %d\n", "data_sample_time", dump_fill_fcb.dsample_time); | ||
531 | |||
532 | printf("NFC geometry :\n"); | ||
533 | printf("\tECC Strength : %d\n", dump_mtd->ecc_strength); | ||
534 | printf("\tPage Size in Bytes : %d\n", dump_fill_fcb.oob_pagesize); | ||
535 | printf("\tMetadata size : %d\n", dump_fill_fcb.meta_size); | ||
536 | printf("\tECC Chunk Size in byte : %d\n", dump_fill_fcb.ecc_size); | ||
537 | printf("\tECC Chunk count : %d\n", dump_fill_fcb.nr_blocks + 1); | ||
538 | printf("\tBlock Mark Byte Offset : %d\n", dump_fill_fcb.bb_byte); | ||
539 | printf("\tBlock Mark Bit Offset : %d\n", dump_fill_fcb.bb_start_bit); | ||
540 | printf("====================================================\n"); | ||
541 | |||
542 | printf("mtd: partition #0\n"); | ||
543 | printf(" %s = %d\n", "type", dump_mtd->type); | ||
544 | printf(" %s = %d\n", "flags", dump_mtd->flags); | ||
545 | printf(" %s = %llu\n", "size", dump_nandboot_size); | ||
546 | printf(" %s = %d\n", "erasesize", dump_mtd->erasesize); | ||
547 | printf(" %s = %d\n", "writesize", dump_mtd->writesize); | ||
548 | printf(" %s = %d\n", "oobsize", dump_mtd->oobsize); | ||
549 | blocks = dump_nandboot_size; | ||
550 | do_div(blocks, dump_mtd->erasesize); | ||
551 | printf(" %s = %llu\n", "blocks", blocks); | ||
552 | } | ||
553 | |||
554 | /* dump data which is read from NAND chip */ | ||
555 | void mtd_dump_structure(int i) | ||
556 | { | ||
557 | #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_fcb[i].x) | ||
558 | printf("FCB %d:\n", i); | ||
559 | P1(checksum); | ||
560 | P1(fingerprint); | ||
561 | P1(version); | ||
562 | #undef P1 | ||
563 | #define P1(x) printf(" %s = %d\n", #x, dump_nand_fcb[i].x) | ||
564 | P1(datasetup); | ||
565 | P1(datahold); | ||
566 | P1(addr_setup); | ||
567 | P1(dsample_time); | ||
568 | P1(pagesize); | ||
569 | P1(oob_pagesize); | ||
570 | P1(sectors); | ||
571 | P1(nr_nand); | ||
572 | P1(nr_die); | ||
573 | P1(celltype); | ||
574 | P1(ecc_type); | ||
575 | P1(ecc_nr); | ||
576 | P1(ecc_size); | ||
577 | P1(ecc_level); | ||
578 | P1(meta_size); | ||
579 | P1(nr_blocks); | ||
580 | P1(ecc_type_sdk); | ||
581 | P1(ecc_nr_sdk); | ||
582 | P1(ecc_size_sdk); | ||
583 | P1(ecc_level_sdk); | ||
584 | P1(nr_blocks_sdk); | ||
585 | P1(meta_size_sdk); | ||
586 | P1(erase_th); | ||
587 | P1(bootpatch); | ||
588 | P1(patch_size); | ||
589 | P1(fw1_start); | ||
590 | P1(fw2_start); | ||
591 | P1(fw1_pages); | ||
592 | P1(fw2_pages); | ||
593 | P1(dbbt_start); | ||
594 | P1(bb_byte); | ||
595 | P1(bb_start_bit); | ||
596 | P1(phy_offset); | ||
597 | P1(bchtype); | ||
598 | P1(readlatency); | ||
599 | P1(predelay); | ||
600 | P1(cedelay); | ||
601 | P1(postdelay); | ||
602 | P1(cmdaddpause); | ||
603 | P1(datapause); | ||
604 | P1(tmspeed); | ||
605 | P1(busytimeout); | ||
606 | P1(disbbm); | ||
607 | P1(spare_offset); | ||
608 | P1(onfi_sync_enable); | ||
609 | P1(onfi_sync_speed); | ||
610 | P1(onfi_sync_nand_data); | ||
611 | P1(disbbm_search); | ||
612 | P1(disbbm_search_limit); | ||
613 | P1(read_retry_enable); | ||
614 | #undef P1 | ||
615 | #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_dbbt[i].x) | ||
616 | printf("DBBT %d:\n", i); | ||
617 | P1(checksum); | ||
618 | P1(fingerprint); | ||
619 | P1(version); | ||
620 | #undef P1 | ||
621 | #define P1(x) printf(" %s = %d\n", #x, dump_nand_dbbt[i].x) | ||
622 | P1(numberbb); | ||
623 | #undef P1 | ||
624 | |||
625 | printf("Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n", | ||
626 | dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize, | ||
627 | dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize, | ||
628 | dump_nandboot_size - dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize); | ||
629 | if (is_imx8m()) | ||
630 | printf("Extra Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n", | ||
631 | dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize + dump_mtd->erasesize * ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) / dump_mtd->erasesize), | ||
632 | dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize, | ||
633 | dump_nandboot_size - (dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize + dump_mtd->erasesize * ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) / dump_mtd->erasesize))); | ||
634 | } | ||
635 | |||
636 | static int do_nandbcb_dump(int argc, char * const argv[]) | ||
637 | { | ||
638 | int num; | ||
639 | int stride; | ||
640 | int search_area_sz; | ||
641 | bool bab_block_table[BOOT_SEARCH_COUNT]; | ||
642 | int bab_block_flag; | ||
643 | |||
644 | if (argc != 2) | ||
645 | return CMD_RET_USAGE; | ||
646 | |||
647 | switch (argv[1][0]) { | ||
648 | case '0': | ||
649 | num = 0; | ||
650 | break; | ||
651 | case '1': | ||
652 | num = 1; | ||
653 | break; | ||
654 | default: | ||
655 | return CMD_RET_USAGE; | ||
656 | } | ||
657 | |||
658 | /* dump data which is planned to be encoded and written to NAND chip */ | ||
659 | mtd_cfg_dump(); | ||
660 | |||
661 | stride = dump_mtd->erasesize; | ||
662 | search_area_sz = BOOT_SEARCH_COUNT * stride; | ||
663 | printf("stride: %x, search_area_sz: %x\n", stride, search_area_sz); | ||
664 | |||
665 | bab_block_flag = 0; | ||
666 | for (int i = 0; i < BOOT_SEARCH_COUNT; i++) { | ||
667 | if (mtd_block_isbad(dump_mtd, (loff_t)(dump_mtd->erasesize * i))) { | ||
668 | bab_block_table[i] = 1; | ||
669 | bab_block_flag = 1; | ||
670 | continue; | ||
671 | } | ||
672 | bab_block_table[i] = 0; | ||
673 | if (memcpy(dump_nand_fcb + i, &dump_fill_fcb, sizeof(dump_fill_fcb)) != 0) { | ||
674 | printf("mtd: found FCB%d candidate version %08x @%d:0x%x\n", | ||
675 | i, dump_nand_fcb[i].version, i, dump_fcb_off[i]); | ||
676 | } else { | ||
677 | printf("mtd: FCB%d not found\n", i); | ||
678 | } | ||
679 | } | ||
680 | |||
681 | for (int i = 0; i < BOOT_SEARCH_COUNT; i++) { | ||
682 | if (mtd_block_isbad(dump_mtd, (loff_t)(dump_mtd->erasesize * i))) | ||
683 | continue; | ||
684 | |||
685 | if (memcpy(dump_nand_dbbt + i, &dump_fill_dbbt, sizeof(dump_fill_dbbt)) != 0) { | ||
686 | printf("mtd: DBBT%d found\n", i); | ||
687 | printf("mtd: Valid DBBT%d found @%d:0x%x\n", i, i, dump_dbbt_off[i]); | ||
688 | |||
689 | } else { | ||
690 | printf("mtd: DBBT%d not found\n", i); | ||
691 | } | ||
692 | } | ||
693 | if (bab_block_flag == 0) | ||
694 | printf("no bad block found, dbbt: %08x\n", dump_fill_dbbt.fingerprint); | ||
695 | else | ||
696 | for (int i = 0; i < BOOT_SEARCH_COUNT; i++) | ||
697 | if (bab_block_table[i] == 1) | ||
698 | printf("mtd: bad block @ 0x%llx\n", (loff_t)(dump_mtd->erasesize * i)); | ||
699 | |||
700 | /* dump data which is read from NAND chip */ | ||
701 | if (num > (BOOT_SEARCH_COUNT - 1)) | ||
702 | return CMD_RET_USAGE; | ||
703 | |||
704 | if (bab_block_table[num] == 1) { | ||
705 | printf("mtd: bad block @ 0x%llx (FCB - DBBT)\n", (loff_t)(dump_mtd->erasesize * num)); | ||
706 | return CMD_RET_USAGE; | ||
707 | } | ||
708 | |||
709 | mtd_dump_structure(num); | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
496 | static int do_nandbcb_update(int argc, char * const argv[]) | 714 | static int do_nandbcb_update(int argc, char * const argv[]) |
497 | { | 715 | { |
498 | struct mtd_info *mtd; | 716 | struct mtd_info *mtd; |
499 | loff_t addr, offset, size, maxsize; | 717 | loff_t addr, offset, size, maxsize; |
500 | char *endp; | 718 | char *endp; |
501 | u_char *buf; | 719 | u_char *buf; |
502 | int dev; | 720 | int dev; |
503 | int ret; | 721 | int ret; |
504 | 722 | ||
505 | if (argc != 4) | 723 | if (argc != 4) |
506 | return CMD_RET_USAGE; | 724 | return CMD_RET_USAGE; |
507 | 725 | ||
508 | dev = nand_curr_device; | 726 | dev = nand_curr_device; |
509 | if (dev < 0) { | 727 | if (dev < 0) { |
510 | printf("failed to get nand_curr_device, run nand device\n"); | 728 | printf("failed to get nand_curr_device, run nand device\n"); |
511 | return CMD_RET_FAILURE; | 729 | return CMD_RET_FAILURE; |
512 | } | 730 | } |
513 | 731 | ||
514 | addr = simple_strtoul(argv[1], &endp, 16); | 732 | addr = simple_strtoul(argv[1], &endp, 16); |
515 | if (*argv[1] == 0 || *endp != 0) | 733 | if (*argv[1] == 0 || *endp != 0) |
516 | return CMD_RET_FAILURE; | 734 | return CMD_RET_FAILURE; |
517 | 735 | ||
518 | mtd = get_nand_dev_by_index(dev); | 736 | mtd = get_nand_dev_by_index(dev); |
519 | if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size, | 737 | if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size, |
520 | &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) | 738 | &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) |
521 | return CMD_RET_FAILURE; | 739 | return CMD_RET_FAILURE; |
522 | 740 | ||
741 | /* dump_mtd and dump_nandboot_size are used for "nandbcb dump [-v]" */ | ||
742 | dump_mtd = mtd; | ||
743 | dump_nandboot_size = maxsize; | ||
744 | |||
523 | buf = map_physmem(addr, size, MAP_WRBACK); | 745 | buf = map_physmem(addr, size, MAP_WRBACK); |
524 | if (!buf) { | 746 | if (!buf) { |
525 | puts("failed to map physical memory\n"); | 747 | puts("failed to map physical memory\n"); |
526 | return CMD_RET_FAILURE; | 748 | return CMD_RET_FAILURE; |
527 | } | 749 | } |
528 | 750 | ||
529 | ret = nandbcb_update(mtd, offset, size, maxsize, buf); | 751 | ret = nandbcb_update(mtd, offset, size, maxsize, buf); |
530 | 752 | ||
531 | return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; | 753 | return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; |
532 | } | 754 | } |
533 | 755 | ||
534 | static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc, | 756 | static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc, |
535 | char * const argv[]) | 757 | char * const argv[]) |
536 | { | 758 | { |
537 | const char *cmd; | 759 | const char *cmd; |
538 | int ret = 0; | 760 | int ret = 0; |
539 | 761 | ||
540 | if (argc < 5) | 762 | if (argc < 3) |
541 | goto usage; | 763 | goto usage; |
542 | 764 | ||
543 | cmd = argv[1]; | 765 | cmd = argv[1]; |
544 | --argc; | 766 | --argc; |
545 | ++argv; | 767 | ++argv; |
546 | 768 | ||
547 | if (strcmp(cmd, "update") == 0) { | 769 | if (strcmp(cmd, "update") == 0) { |
548 | ret = do_nandbcb_update(argc, argv); | 770 | ret = do_nandbcb_update(argc, argv); |
549 | goto done; | 771 | goto done; |
550 | } | 772 | } |
551 | 773 | ||
774 | if (strcmp(cmd, "dump") == 0) { | ||
775 | ret = do_nandbcb_dump(argc, argv); | ||
776 | goto done; | ||
777 | } | ||
778 | |||
552 | if (strcmp(cmd, "bcbonly") == 0) { | 779 | if (strcmp(cmd, "bcbonly") == 0) { |
553 | ret = do_nandbcb_bcbonly(argc, argv); | 780 | ret = do_nandbcb_bcbonly(argc, argv); |
554 | goto done; | 781 | goto done; |
555 | } | 782 | } |
556 | 783 | ||
557 | done: | 784 | done: |
558 | if (ret != -1) | 785 | if (ret != -1) |
559 | return ret; | 786 | return ret; |
560 | usage: | 787 | usage: |
561 | return CMD_RET_USAGE; | 788 | return CMD_RET_USAGE; |
562 | } | 789 | } |
563 | 790 | ||
564 | static char nandbcb_help_text[] = | 791 | static char nandbcb_help_text[] = |
565 | "update addr off|partition len - update 'len' bytes starting at\n" | 792 | "update addr off|partition len - update 'len' bytes starting at\n" |
566 | " 'off|part' to memory address 'addr', skipping bad blocks\n" | 793 | " 'off|part' to memory address 'addr', skipping bad blocks\n" |
567 | "bcbonly fw-size fw1-off [fw2-off] - write only BCB (FCB and DBBT)\n" | 794 | "bcbonly fw-size fw1-off [fw2-off] - write only BCB (FCB and DBBT)\n" |
568 | " where `fw-size` is fw sizes in bytes, `fw1-off` and\n" | 795 | " where `fw-size` is fw sizes in bytes, `fw1-off` and\n" |
569 | " and `fw2-off` - firmware offsets "; | 796 | " and `fw2-off` - firmware offsets \n" |
797 | "nandbcb dump num - verify/dump boot structures\n" | ||
798 | " 'num' can be set to 0 and 1"; | ||
570 | 799 | ||
571 | U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb, | 800 | U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb, |
572 | "i.MX6 Nand BCB", | 801 | "i.MX6 Nand BCB", |
573 | nandbcb_help_text | 802 | nandbcb_help_text |
574 | ); | 803 | ); |
575 | 804 |