[U-Boot] [PATCH 3/3] i.MX: nand: add nandbcb update command

Jagan Teki jagan at amarulasolutions.com
Tue Jan 9 18:49:54 UTC 2018


writing/updating boot image in nand device is not
straight forward in i.MX6 platform and it require
boot control block(BCB) configure.

It become difficult to use uboot 'nand' command to
write BCB since it require platform specific attributes
need to taken care.

It even difficult to use existing msx-nand.c driver by
incorporating BCB attributes like mxs_dma_desc does
because it require change in mtd and nand command.

So, cmd_nandbcb implemented in arch/arm/mach-imx

BCB contains two data structures, Firmware Configuration Block(FCB)
and Discovered Bad Block Table(DBBT). FCB has nand timings,
DBBT search area, page address of primary and secondary firmware.

for nand boot, up on reset bootrom look for FCB structure in
first block's if FCB found the nand timings are loaded for
further reads. once FCB read done, DTTB will load and
finally primary or secondary firmware will load which is boot image.

cmd_nandbcb will create FCB these structures
by taking mtd partition as an example.
- initial code will erase entire partition
- followed by FCB setup, like first 4 block for FCB/DBBT write,
  rest is split into two for primary and secondary firmware
- write firmware at primary and secondary blocks(two times same image)
- finally write fcb/dttb in first 4 block.

sample test:
============
icorem6qdl> fatload mmc 0:1 ${loadaddr} SPL
reading SPL
31744 bytes read in 15 ms (2 MiB/s)

icorem6qdl> nandbcb update $loadaddr spl $filesize
device 0 offset 0x0, size 0x7c00
Erasing at 0x1c0000 -- 100% complete.
NAND fw write: 0x100000 offset, 0x9000 bytes written: OK
NAND fw write: 0x180000 offset, 0x9000 bytes written: OK

Signed-off-by: Jagan Teki <jagan at amarulasolutions.com>
Signed-off-by: Sergey Kubushyn <ksi at koi8.net>
Signed-off-by: Jagan Teki <jagan at amarulasolutions.com>
---
 arch/arm/include/asm/mach-imx/imx-nandbcb.h |  99 ++++++++
 arch/arm/include/asm/mach-imx/mxs-nand.h    |  16 ++
 arch/arm/mach-imx/Kconfig                   |  11 +
 arch/arm/mach-imx/Makefile                  |   1 +
 arch/arm/mach-imx/cmd_nandbcb.c             | 341 ++++++++++++++++++++++++++++
 drivers/mtd/nand/mxs_nand.c                 |   9 +-
 6 files changed, 473 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/include/asm/mach-imx/imx-nandbcb.h
 create mode 100644 arch/arm/include/asm/mach-imx/mxs-nand.h
 create mode 100644 arch/arm/mach-imx/cmd_nandbcb.c

diff --git a/arch/arm/include/asm/mach-imx/imx-nandbcb.h b/arch/arm/include/asm/mach-imx/imx-nandbcb.h
new file mode 100644
index 0000000..e980f80
--- /dev/null
+++ b/arch/arm/include/asm/mach-imx/imx-nandbcb.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 Amarula Solutions B.V.
+ * Author: Jagan Teki <jagan at amarulasolutions.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _IMX_NAND_BCB_H_
+#define _IMX_NAND_BCB_H_
+
+#define FCB_FINGERPRINT		0x20424346      /* 'FCB' */
+#define FCB_VERSION_1		0x01000000
+
+#define DBBT_FINGERPRINT2	0x54424244	/* 'DBBT' */
+#define DBBT_VERSION_1		0x01000000
+
+struct dbbt_block {
+	u32 checksum;	/* reserved on i.MX6 */
+	u32 fingerprint;
+	u32 version;
+	u32 numberbb;	/* reserved on i.MX6 */
+	u32 dbbtnumofpages;
+};
+
+struct fcb_block {
+	u32 checksum;		/* First fingerprint in first byte */
+	u32 fingerprint;	/* 2nd fingerprint at byte 4 */
+	u32 version;		/* 3rd fingerprint at byte 8 */
+	u8 datasetup;
+	u8 datahold;
+	u8 addresssetup;
+	u8 dsample_time;
+
+	/* These are for application use only and not for ROM. */
+	u8 nandtimingstate;
+	u8 rea;
+	u8 rloh;
+	u8 rhoh;
+	u32 pagedatasize;	/* 2048 for 2K pages, 4096 for 4K pages */
+	u32 totalpagesize;	/* 2112 for 2K pages, 4314 for 4K pages */
+	u32 sectorsperblock;	/* Number of 2K sections per block */
+	u32 numberofnands;	/* Total Number of NANDs - not used by ROM */
+	u32 totalinternaldie;	/* Number of separate chips in this NAND */
+	u32 celltype;		/* MLC or SLC */
+	u32 eccblocknecctype;	/* Type of ECC, can be one of BCH-0-20 */
+	u32 eccblock0size; /* Number of bytes for Block0 - BCH */
+	u32 eccblocknsize; /* Block size in bytes for all blocks other than Block0 - BCH */
+	u32 eccblock0ecctype; /* Ecc level for Block 0 - BCH */
+	u32 metadatabytes; /* Metadata size - BCH */
+	u32 numeccblocksperpage; /* Number of blocks per page for ROM use - BCH */
+	u32 eccblocknecclevelsdk; /* Type of ECC, can be one of BCH-0-20 */
+	u32 eccblock0sizesdk; /* Number of bytes for Block0 - BCH */
+	u32 eccblocknsizesdk; /* Block size in bytes for all blocks other than Block0 - BCH */
+	u32 eccblock0ecclevelsdk; /* Ecc level for Block 0 - BCH */
+	u32 numeccblocksperpagesdk; /* Number of blocks per page for SDK use - BCH */
+	u32 metadatabytessdk; /* Metadata size - BCH */
+	u32 erasethreshold; /* To set into BCH_MODE register */
+	u32 bootpatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+	u32 patchsectors; /* Size of patch in sectors */
+	u32 firmware1_startingpage; /* Firmware image starts on this sector */
+	u32 firmware2_startingpage; /* Secondary FW Image starting Sector */
+	u32 pagesinfirmware1; /* Number of sectors in firmware image */
+	u32 pagesinfirmware2; /* Number of sector in secondary FW image */
+	u32 dbbtsearchareastartaddress; /* Page address where dbbt search area begins */
+
+	/* Byte in page data that have manufacturer marked bad block marker,
+	 * this will be swapped with metadata[0] to complete page data.
+	 */
+	u32 badblockmarkerbyte;	/* Byte in page data that have manufacturer marked bad block marker, */
+
+	/* For BCH ECC sizes other than 8 and 16 the bad block marker does not
+	 * start at 0th bit of badblockmarkerbyte. This field is used to get to
+	 * the start bit of bad block marker byte with in badblockmarkerbyte
+	 */
+	u32 badblockmarkerstartbit;
+
+	/* FCB value that gives byte offset for
+	 * bad block marker on physical NAND page
+	 */
+	u32 bbmarkerphysicaloffset;
+	u32 bchtype;
+
+	u32 tmtiming2_readlatency;
+	u32 tmtiming2_preambledelay;
+	u32 tmtiming2_cedelay;
+	u32 tmtiming2_postambledelay;
+	u32 tmtiming2_cmdaddpause;
+	u32 tmtiming2_datapause;
+	u32 tmspeed;
+	u32 tmtiming1_busytimeout;
+
+	/* the flag to enable (1)/disable(0) bi swap */
+	u32 disbbm;
+
+	/* The swap position of main area in spare area */
+	u32 bbmarkerphysicaloffsetinsparedata;
+};
+
+#endif	/* _IMX_NAND_BCB_H_ */
diff --git a/arch/arm/include/asm/mach-imx/mxs-nand.h b/arch/arm/include/asm/mach-imx/mxs-nand.h
new file mode 100644
index 0000000..6237307
--- /dev/null
+++ b/arch/arm/include/asm/mach-imx/mxs-nand.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2017 Jagan Teki <jagan at amarulasolutions.com>
+ * Copyright (C) 2016 Sergey Kubushyn <ksi at koi8.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _MXS_NAND_
+#define _MXS_NAND_
+
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+				   uint32_t page_oob_size);
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+
+#endif	/* _MXS_NAND_ */
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index e687048..dd7d4e9 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -65,6 +65,17 @@ config CMD_HDMIDETECT
 	  This enables the 'hdmidet' command which detects if an HDMI monitor
 	  is connected.
 
+config CMD_NANDBCB
+	bool "i.MX NAND Boot Control Block(BCB) command"
+	depends on NAND && CMD_MTDPARTS
+	default y if ARCH_MX6 && NAND_MXS
+	help
+	  Unlike normal 'nand write/erase' commands, this command update
+	  Boot Control Block(BCB) for i.MX6 platform NAND IP's.
+
+	  This is similar to kobs-ng, which is used in Linux as seprate
+	  rootfs package.
+
 config NXP_BOARD_REVISION
 	bool "Read NXP board revision from fuses"
 	depends on ARCH_MX6 || ARCH_MX7
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index d77c10e..86414bf 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -42,6 +42,7 @@ ifneq ($(CONFIG_SPL_BUILD),y)
 obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o
 obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
 obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
+obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
 endif
 
 PLUGIN = board/$(BOARDDIR)/plugin
diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c
new file mode 100644
index 0000000..5cf75c7
--- /dev/null
+++ b/arch/arm/mach-imx/cmd_nandbcb.c
@@ -0,0 +1,341 @@
+/*
+ * i.MX nand boot control block(bcb).
+ *
+ * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng
+ *
+ * Copyright (C) 2017 Jagan Teki <jagan at amarulasolutions.com>
+ * Copyright (C) 2016 Sergey Kubushyn <ksi at koi8.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <nand.h>
+
+#include <asm/io.h>
+#include <jffs2/jffs2.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/mach-imx/mxs-nand.h>
+#include <asm/mach-imx/imx-nandbcb.h>
+#include <asm/mach-imx/imximage.cfg>
+
+#define BF_VAL(v, bf)		(((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v, n)		(((v) >> (n)) & 0x1)
+
+static u8 calculate_parity_13_8(u8 d)
+{
+	u8 p = 0;
+
+	p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+	p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+	p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+	p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+	p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+
+	return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+	int i;
+	u8 *src = _src;
+	u8 *ecc = _ecc;
+
+	for (i = 0; i < size; i++)
+		ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static u32 calc_chksum(void *buf, size_t size)
+{
+	u32 chksum = 0;
+	u8 *bp = buf;
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		chksum += bp[i];
+
+	return ~chksum;
+}
+
+static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+	fcb->fingerprint = FCB_FINGERPRINT;
+	fcb->version = FCB_VERSION_1;
+	fcb->pagedatasize = mtd->writesize;
+	fcb->totalpagesize = mtd->writesize + mtd->oobsize;
+	fcb->sectorsperblock = mtd->erasesize / mtd->writesize;
+
+	/* Divide ECC strength by two and save the value into FCB structure. */
+	fcb->eccblock0ecctype =
+		mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1;
+
+	fcb->eccblocknecctype = fcb->eccblock0ecctype;
+
+	/* Also hardcoded in kobs-ng */
+	fcb->eccblock0size = 0x00000200;
+	fcb->eccblocknsize = 0x00000200;
+	fcb->datasetup = 80;
+	fcb->datahold = 60;
+	fcb->addresssetup = 25;
+	fcb->dsample_time = 6;
+	fcb->metadatabytes = 10;
+
+	/* DBBT search area starts at second page on first block */
+	fcb->dbbtsearchareastartaddress = 1;
+
+	fcb->badblockmarkerbyte = mxs_nand_mark_byte_offset(mtd);
+	fcb->badblockmarkerstartbit = mxs_nand_mark_bit_offset(mtd);
+
+	fcb->bbmarkerphysicaloffset = mtd->writesize;
+
+	fcb->numeccblocksperpage = mtd->writesize / fcb->eccblock0size - 1;
+
+	fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+}
+
+static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+	int n, n_bad_blocks = 0;
+	u32 *bb = buf + 0x8;
+	u32 *n_bad_blocksp = buf + 0x4;
+
+	for (n = 0; n < num_blocks; n++) {
+		loff_t offset = n * mtd->erasesize;
+			if (mtd_block_isbad(mtd, offset)) {
+				n_bad_blocks++;
+				*bb = n;
+				bb++;
+		}
+	}
+
+	*n_bad_blocksp = n_bad_blocks;
+
+	return n_bad_blocks;
+}
+
+static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
+			  size_t maxsize, const u_char *buf)
+{
+	nand_erase_options_t opts;
+	struct fcb_block *fcb;
+	struct dbbt_block *dbbt;
+	loff_t fw1_off, fw2_off;
+	void *fwbuf, *fcb_raw_page, *dbbt_page, *dbbt_data_page;
+	int nr_blks, nr_blks_fcb, nr_blks_fw, fw1_blk, fw2_blk;
+	size_t fwsize, dummy;
+	int i, ret;
+
+	/* erase */
+	memset(&opts, 0, sizeof(opts));
+	opts.offset = off;
+	opts.length = maxsize - 1;
+	ret = nand_erase_opts(mtd, &opts);
+	if (ret) {
+		printf("%s: erase failed\n", __func__);
+		return ret;
+	}
+
+	/*
+	 * Reference documentation from i.MX6DQRM section 8.5.2.2
+	 *
+	 * Nand Boot Control Block(BCB) contains two data structures,
+	 * - Firmware Configuration Block(FCB)
+	 * - Discovered Bad Block Table(DBBT)
+	 *
+	 * FCB contains,
+	 * - nand timings
+	 * - DBBT search page address,
+	 * - start page address of primary firmware
+	 * - start page address of secondary firmware
+	 *
+	 * setup fcb:
+	 * - number of blocks = mtd partition size / mtd erasesize
+	 * - two firmware blocks, primary and secondary
+	 * - first 4 block for FCB/DBBT
+	 * - rest split in half for primary and secondary firmware
+	 * - same firmware will write two times
+	 */
+	nr_blks_fcb = 4;
+	nr_blks = maxsize / mtd->erasesize;
+	nr_blks_fw = (nr_blks - nr_blks_fcb) / 2;
+	fw1_blk = nr_blks_fcb;
+	fw2_blk = fw1_blk + nr_blks_fw;
+
+	/* write fw */
+	fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
+		       mtd->writesize);
+	fwbuf = kzalloc(fwsize, GFP_KERNEL);
+	memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
+	fw1_off = fw1_blk * mtd->erasesize;
+	ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
+				  (u_char *)fwbuf, WITH_WR_VERIFY);
+	printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
+	       fw1_off, fwsize, ret ? "ERROR" : "OK");
+	if (ret)
+		goto fw_err;
+
+	/* write fw, 2nd time */
+	fw2_off = fw2_blk * mtd->erasesize;
+	ret = nand_write_skip_bad(mtd, fw2_off, &fwsize, NULL, maxsize,
+				  (u_char *)fwbuf, WITH_WR_VERIFY);
+	printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
+	       fw2_off, fwsize, ret ? "ERROR" : "OK");
+	if (ret)
+		goto fw_err;
+
+	/* fill fcb */
+	fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
+	fcb->firmware1_startingpage = (fw1_blk * mtd->erasesize) / mtd->writesize;
+	fcb->firmware2_startingpage = (fw2_blk * mtd->erasesize) / mtd->writesize;
+	fcb->pagesinfirmware1 =	size / mtd->writesize + 1;
+	fcb->pagesinfirmware2 = fcb->pagesinfirmware1;
+	fill_fcb(fcb, mtd);
+
+	/* fill dbbt */
+	dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+	dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+	dbbt = dbbt_page;
+	dbbt->checksum = 0;
+	dbbt->fingerprint = DBBT_FINGERPRINT2;
+	dbbt->version = DBBT_VERSION_1;
+	ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
+	if (ret < 0)
+		goto dbbt_err;
+	else if (ret > 0)
+		dbbt->dbbtnumofpages = 1;
+
+	/* write fcb/dbbt */
+	fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
+	encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
+	/*
+	 * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+	 * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+	 * the FCB is mostly written to the first page in a block, a scan for
+	 * factory bad blocks will detect these blocks as bad, e.g. when
+	 * function nand_scan_bbt() is executed to build a new bad block table.
+	 */
+	memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
+
+	for (i = 0; i < nr_blks_fcb; i++) {
+		if (mtd_block_isbad(mtd, off)) {
+			printf("Block %d is bad, skipped\n", i);
+			continue;
+		}
+
+		/* raw write */
+		mtd_oob_ops_t ops = {
+			.datbuf = (u8 *)fcb_raw_page,
+			.oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
+			.len = mtd->writesize,
+			.ooblen = mtd->oobsize,
+			.mode = MTD_OPS_RAW
+		};
+
+		ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
+		if (ret)
+			goto fcb_err;
+		debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n",
+		      mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK");
+
+		ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+				mtd->writesize, &dummy, dbbt_page);
+		if (ret)
+			goto fcb_err;
+		debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n",
+		      mtd->erasesize * i + mtd->writesize, dummy,
+		      ret ? "ERROR" : "OK");
+
+		/* dbbtnumofpages == 0 if no bad blocks */
+		if (dbbt->dbbtnumofpages > 0) {
+			ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 5,
+					mtd->writesize, &dummy, dbbt_data_page);
+			if (ret)
+				goto fcb_err;
+		}
+	}
+
+fcb_err:
+	kfree(fcb_raw_page);
+dbbt_err:
+	kfree(dbbt_page);
+	kfree(dbbt_data_page);
+fw_err:
+	kfree(fwbuf);
+
+	return ret;
+}
+
+static int do_nandbcb_update(int argc, char * const argv[])
+{
+	struct mtd_info *mtd;
+	loff_t addr, offset, size, maxsize;
+	char *endp;
+	u_char *buf;
+	int dev;
+	int ret;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	dev = nand_curr_device;
+	if (dev < 0) {
+		printf("failed to get nand_curr_device, run nand device");
+		return CMD_RET_FAILURE;
+	}
+
+	addr = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return CMD_RET_FAILURE;
+
+	mtd = get_nand_dev_by_index(dev);
+	if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
+			     &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
+		return CMD_RET_FAILURE;
+
+	buf = map_physmem(addr, size, MAP_WRBACK);
+	if (!buf) {
+		puts("failed to map physical memory\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = nandbcb_update(mtd, offset, size, maxsize, buf);
+
+	return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
+		      char * const argv[])
+{
+	const char *cmd;
+	int ret = 0;
+
+	if (argc < 5)
+		goto usage;
+
+	cmd = argv[1];
+	--argc;
+	++argv;
+
+	if (strcmp(cmd, "update") == 0) {
+		ret = do_nandbcb_update(argc, argv);
+		goto done;
+	}
+
+done:
+	if (ret != -1)
+		return ret;
+usage:
+	return CMD_RET_USAGE;
+}
+
+static char nandbcb_help_text[] =
+	"update addr off|partition len	- update 'len' bytes starting at\n"
+	"	'off|part' to memory address 'addr', skipping  bad blocks";
+
+U_BOOT_CMD(
+	nandbcb, 5, 1, do_nandbcb,
+	"i.MX6 Nand BCB",
+	nandbcb_help_text
+);
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index bed9b65..d696dd2 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -26,6 +26,7 @@
 #include <asm/mach-imx/regs-gpmi.h>
 #include <asm/arch/sys_proto.h>
 #include <asm/mach-imx/dma.h>
+#include <asm/mach-imx/mxs-nand.h>
 
 #define	MXS_NAND_DMA_DESCRIPTOR_COUNT		4
 
@@ -150,8 +151,8 @@ static uint32_t mxs_nand_aux_status_offset(void)
 	return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
 }
 
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
-						uint32_t page_oob_size)
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+				   uint32_t page_oob_size)
 {
 	int ecc_strength;
 	int max_ecc_strength_supported;
@@ -226,14 +227,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
 	return block_mark_bit_offset;
 }
 
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
 {
 	uint32_t ecc_strength;
 	ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
 	return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
 }
 
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
 {
 	uint32_t ecc_strength;
 	ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
-- 
2.7.4



More information about the U-Boot mailing list