blob: 7811c61d220c9b86eaee4943ef9b3e69e8e4b6d5 [file] [log] [blame]
Shyam Saini1d43e242019-06-14 13:05:33 +05301/*
2 * i.MX6 nand boot control block(bcb).
3 *
4 * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng
5 *
6 * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
7 * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net>
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 */
11
12#include <common.h>
13#include <nand.h>
14
15#include <asm/io.h>
16#include <jffs2/jffs2.h>
17#include <linux/mtd/mtd.h>
18
19#include <asm/mach-imx/imx-nandbcb.h>
20#include <asm/mach-imx/imximage.cfg>
21#include <mxs_nand.h>
22#include <linux/mtd/mtd.h>
23#include <nand.h>
24
25#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
26#define GETBIT(v, n) (((v) >> (n)) & 0x1)
27
28static u8 calculate_parity_13_8(u8 d)
29{
30 u8 p = 0;
31
32 p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
33 p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^
34 GETBIT(d, 1)) << 1;
35 p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^
36 GETBIT(d, 0)) << 2;
37 p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
38 p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^
39 GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
40
41 return p;
42}
43
44static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
45{
46 int i;
47 u8 *src = _src;
48 u8 *ecc = _ecc;
49
50 for (i = 0; i < size; i++)
51 ecc[i] = calculate_parity_13_8(src[i]);
52}
53
54static u32 calc_chksum(void *buf, size_t size)
55{
56 u32 chksum = 0;
57 u8 *bp = buf;
58 size_t i;
59
60 for (i = 0; i < size; i++)
61 chksum += bp[i];
62
63 return ~chksum;
64}
65
66static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
67{
68 struct nand_chip *chip = mtd_to_nand(mtd);
69 struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
70
71 fcb->fingerprint = FCB_FINGERPRINT;
72 fcb->version = FCB_VERSION_1;
73 fcb->pagesize = mtd->writesize;
74 fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
75 fcb->sectors = mtd->erasesize / mtd->writesize;
76
77 /* Divide ECC strength by two and save the value into FCB structure. */
78 fcb->ecc_level = nand_info->bch_geometry.ecc_strength >> 1;
79
80 fcb->ecc_type = fcb->ecc_level;
81
82 /* Also hardcoded in kobs-ng */
83 fcb->ecc_nr = 0x00000200;
84 fcb->ecc_size = 0x00000200;
85 fcb->datasetup = 80;
86 fcb->datahold = 60;
87 fcb->addr_setup = 25;
88 fcb->dsample_time = 6;
89 fcb->meta_size = 10;
90
91 /* DBBT search area starts at second page on first block */
92 fcb->dbbt_start = 1;
93
94 fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
95 fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
96
97 fcb->phy_offset = mtd->writesize;
98
99 fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
100
101 fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
102}
103
104static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
105{
106 int n, n_bad_blocks = 0;
107 u32 *bb = buf + 0x8;
108 u32 *n_bad_blocksp = buf + 0x4;
109
110 for (n = 0; n < num_blocks; n++) {
111 loff_t offset = n * mtd->erasesize;
112 if (mtd_block_isbad(mtd, offset)) {
113 n_bad_blocks++;
114 *bb = n;
115 bb++;
116 }
117 }
118
119 *n_bad_blocksp = n_bad_blocks;
120
121 return n_bad_blocks;
122}
123
124static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
125 size_t maxsize, const u_char *buf)
126{
127 nand_erase_options_t opts;
128 struct fcb_block *fcb;
129 struct dbbt_block *dbbt;
130 loff_t fw1_off;
131 void *fwbuf, *fcb_raw_page, *dbbt_page, *dbbt_data_page;
132 int nr_blks, nr_blks_fcb, fw1_blk;
133 size_t fwsize, dummy;
134 int i, ret;
135
136 /* erase */
137 memset(&opts, 0, sizeof(opts));
138 opts.offset = off;
139 opts.length = maxsize - 1;
140 ret = nand_erase_opts(mtd, &opts);
141 if (ret) {
142 printf("%s: erase failed (ret = %d)\n", __func__, ret);
143 return ret;
144 }
145
146 /*
147 * Reference documentation from i.MX6DQRM section 8.5.2.2
148 *
149 * Nand Boot Control Block(BCB) contains two data structures,
150 * - Firmware Configuration Block(FCB)
151 * - Discovered Bad Block Table(DBBT)
152 *
153 * FCB contains,
154 * - nand timings
155 * - DBBT search page address,
156 * - start page address of primary firmware
157 * - start page address of secondary firmware
158 *
159 * setup fcb:
160 * - number of blocks = mtd partition size / mtd erasesize
161 * - two firmware blocks, primary and secondary
162 * - first 4 block for FCB/DBBT
163 * - rest split in half for primary and secondary firmware
164 * - same firmware will write two times
165 */
166 nr_blks_fcb = 2;
167 nr_blks = maxsize / mtd->erasesize;
168 fw1_blk = nr_blks_fcb;
169
170 /* write fw */
171 fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
172 mtd->writesize);
173 fwbuf = kzalloc(fwsize, GFP_KERNEL);
174 if (!fwbuf) {
175 debug("failed to allocate fwbuf\n");
176 ret = -ENOMEM;
177 goto err;
178 }
179
180 memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
181 fw1_off = fw1_blk * mtd->erasesize;
182 ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
183 (u_char *)fwbuf, WITH_WR_VERIFY);
184 printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
185 fw1_off, fwsize, ret ? "ERROR" : "OK");
186 if (ret)
187 goto fwbuf_err;
188
189 /* fill fcb */
190 fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
191 if (!fcb) {
192 debug("failed to allocate fcb\n");
193 ret = -ENOMEM;
194 goto fwbuf_err;
195 }
196
197 fcb->fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize;
198 fcb->fw1_pages = size / mtd->writesize + 1;
199 fill_fcb(fcb, mtd);
200
201 /* fill dbbt */
202 dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
203 if (!dbbt_page) {
204 debug("failed to allocate dbbt_page\n");
205 ret = -ENOMEM;
206 goto fcb_err;
207 }
208
209 dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
210 if (!dbbt_data_page) {
211 debug("failed to allocate dbbt_data_page\n");
212 ret = -ENOMEM;
213 goto dbbt_page_err;
214 }
215
216 dbbt = dbbt_page;
217 dbbt->checksum = 0;
218 dbbt->fingerprint = DBBT_FINGERPRINT2;
219 dbbt->version = DBBT_VERSION_1;
220 ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
221 if (ret < 0)
222 goto dbbt_data_page_err;
223 else if (ret > 0)
224 dbbt->dbbtpages = 1;
225
226 /* write fcb/dbbt */
227 fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
228 if (!fcb_raw_page) {
229 debug("failed to allocate fcb_raw_page\n");
230 ret = -ENOMEM;
231 goto dbbt_data_page_err;
232 }
233
234 memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
235 encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
236 /*
237 * Set the first and second byte of OOB data to 0xFF, not 0x00. These
238 * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
239 * the FCB is mostly written to the first page in a block, a scan for
240 * factory bad blocks will detect these blocks as bad, e.g. when
241 * function nand_scan_bbt() is executed to build a new bad block table.
242 */
243 memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
244
245 for (i = 0; i < nr_blks_fcb; i++) {
246 if (mtd_block_isbad(mtd, off)) {
247 printf("Block %d is bad, skipped\n", i);
248 continue;
249 }
250
251 /* raw write */
252 mtd_oob_ops_t ops = {
253 .datbuf = (u8 *)fcb_raw_page,
254 .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
255 .len = mtd->writesize,
256 .ooblen = mtd->oobsize,
257 .mode = MTD_OPS_RAW
258 };
259
260 ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
261 if (ret)
262 goto fcb_raw_page_err;
263 debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n",
264 mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK");
265
266 ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
267 mtd->writesize, &dummy, dbbt_page);
268 if (ret)
269 goto fcb_raw_page_err;
270 debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n",
271 mtd->erasesize * i + mtd->writesize, dummy,
272 ret ? "ERROR" : "OK");
273
274 /* dbbtpages == 0 if no bad blocks */
275 if (dbbt->dbbtpages > 0) {
276 loff_t to = (mtd->erasesize * i + mtd->writesize * 5);
277
278 ret = mtd_write(mtd, to, mtd->writesize, &dummy,
279 dbbt_data_page);
280 if (ret)
281 goto fcb_raw_page_err;
282 }
283 }
284
285fcb_raw_page_err:
286 kfree(fcb_raw_page);
287dbbt_data_page_err:
288 kfree(dbbt_data_page);
289dbbt_page_err:
290 kfree(dbbt_page);
291fcb_err:
292 kfree(fcb);
293fwbuf_err:
294 kfree(fwbuf);
295err:
296 return ret;
297}
298
299static int do_nandbcb_update(int argc, char * const argv[])
300{
301 struct mtd_info *mtd;
302 loff_t addr, offset, size, maxsize;
303 char *endp;
304 u_char *buf;
305 int dev;
306 int ret;
307
308 if (argc != 4)
309 return CMD_RET_USAGE;
310
311 dev = nand_curr_device;
312 if (dev < 0) {
313 printf("failed to get nand_curr_device, run nand device");
314 return CMD_RET_FAILURE;
315 }
316
317 addr = simple_strtoul(argv[1], &endp, 16);
318 if (*argv[1] == 0 || *endp != 0)
319 return CMD_RET_FAILURE;
320
321 mtd = get_nand_dev_by_index(dev);
322 if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
323 &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
324 return CMD_RET_FAILURE;
325
326 buf = map_physmem(addr, size, MAP_WRBACK);
327 if (!buf) {
328 puts("failed to map physical memory\n");
329 return CMD_RET_FAILURE;
330 }
331
332 ret = nandbcb_update(mtd, offset, size, maxsize, buf);
333
334 return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
335}
336
337static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
338 char * const argv[])
339{
340 const char *cmd;
341 int ret = 0;
342
343 if (argc < 5)
344 goto usage;
345
346 cmd = argv[1];
347 --argc;
348 ++argv;
349
350 if (strcmp(cmd, "update") == 0) {
351 ret = do_nandbcb_update(argc, argv);
352 goto done;
353 }
354
355done:
356 if (ret != -1)
357 return ret;
358usage:
359 return CMD_RET_USAGE;
360}
361
Parthiban Nallathambi4ee0ff12019-08-23 18:35:10 +0200362#ifdef CONFIG_SYS_LONGHELP
Shyam Saini1d43e242019-06-14 13:05:33 +0530363static char nandbcb_help_text[] =
364 "update addr off|partition len - update 'len' bytes starting at\n"
365 " 'off|part' to memory address 'addr', skipping bad blocks";
Parthiban Nallathambi4ee0ff12019-08-23 18:35:10 +0200366#endif
Shyam Saini1d43e242019-06-14 13:05:33 +0530367
368U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb,
369 "i.MX6 Nand BCB",
370 nandbcb_help_text
371);