imx: nandbcb: add support for i.MX7
Add support for updating FCB/DBBT on i.MX7: - additional new fields in FCB structure - Leverage hardware BCH/randomizer for writing FCB Signed-off-by: Igor Opaniuk <igor.opaniuk@toradex.com> Tested-by: Max Krummenacher <max.krummenacher@toradex.com> Reviewed-by: Oleksandr Suvorov <oleksandr.suvorov@toradex.com>
This commit is contained in:
		
							parent
							
								
									9ab5f221a5
								
							
						
					
					
						commit
						dad30dd72b
					
				| 
						 | 
					@ -106,6 +106,18 @@ struct fcb_block {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* The swap position of main area in spare area */
 | 
						/* The swap position of main area in spare area */
 | 
				
			||||||
	u32 spare_offset;
 | 
						u32 spare_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Actual for iMX7 only */
 | 
				
			||||||
 | 
						u32 onfi_sync_enable;
 | 
				
			||||||
 | 
						u32 onfi_sync_speed;
 | 
				
			||||||
 | 
						u32 onfi_sync_nand_data;
 | 
				
			||||||
 | 
						u32 reserved2[6];
 | 
				
			||||||
 | 
						u32 disbbm_search;
 | 
				
			||||||
 | 
						u32 disbbm_search_limit;
 | 
				
			||||||
 | 
						u32 reserved3[15];
 | 
				
			||||||
 | 
						u32 read_retry_enable;
 | 
				
			||||||
 | 
						u32 reserved4[1];
 | 
				
			||||||
 | 
						u32 fill_to_1024[183];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif	/* _IMX_NAND_BCB_H_ */
 | 
					#endif	/* _IMX_NAND_BCB_H_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,7 +82,7 @@ config CMD_NANDBCB
 | 
				
			||||||
	bool "i.MX6 NAND Boot Control Block(BCB) command"
 | 
						bool "i.MX6 NAND Boot Control Block(BCB) command"
 | 
				
			||||||
	depends on NAND && CMD_MTDPARTS
 | 
						depends on NAND && CMD_MTDPARTS
 | 
				
			||||||
	select BCH if MX6UL || MX6ULL
 | 
						select BCH if MX6UL || MX6ULL
 | 
				
			||||||
	default y if ARCH_MX6 && NAND_MXS
 | 
						default y if (ARCH_MX6 && NAND_MXS) || (ARCH_MX7 && NAND_MXS)
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  Unlike normal 'nand write/erase' commands, this command update
 | 
						  Unlike normal 'nand write/erase' commands, this command update
 | 
				
			||||||
	  Boot Control Block(BCB) for i.MX6 platform NAND IP's.
 | 
						  Boot Control Block(BCB) for i.MX6 platform NAND IP's.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
#include <linux/bch.h>
 | 
					#include <linux/bch.h>
 | 
				
			||||||
#include <linux/mtd/mtd.h>
 | 
					#include <linux/mtd/mtd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/arch/sys_proto.h>
 | 
				
			||||||
#include <asm/mach-imx/imx-nandbcb.h>
 | 
					#include <asm/mach-imx/imx-nandbcb.h>
 | 
				
			||||||
#include <asm/mach-imx/imximage.cfg>
 | 
					#include <asm/mach-imx/imximage.cfg>
 | 
				
			||||||
#include <mxs_nand.h>
 | 
					#include <mxs_nand.h>
 | 
				
			||||||
| 
						 | 
					@ -131,26 +132,36 @@ static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *chip = mtd_to_nand(mtd);
 | 
						struct nand_chip *chip = mtd_to_nand(mtd);
 | 
				
			||||||
	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
 | 
						struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
 | 
				
			||||||
 | 
						struct mxs_nand_layout l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mxs_nand_get_layout(mtd, &l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fcb->fingerprint = FCB_FINGERPRINT;
 | 
						fcb->fingerprint = FCB_FINGERPRINT;
 | 
				
			||||||
	fcb->version = FCB_VERSION_1;
 | 
						fcb->version = FCB_VERSION_1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fcb->pagesize = mtd->writesize;
 | 
						fcb->pagesize = mtd->writesize;
 | 
				
			||||||
	fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
 | 
						fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
 | 
				
			||||||
	fcb->sectors = mtd->erasesize / mtd->writesize;
 | 
						fcb->sectors = mtd->erasesize / mtd->writesize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Divide ECC strength by two and save the value into FCB structure. */
 | 
						fcb->meta_size = l.meta_size;
 | 
				
			||||||
	fcb->ecc_level = nand_info->bch_geometry.ecc_strength >> 1;
 | 
						fcb->nr_blocks = l.nblocks;
 | 
				
			||||||
 | 
						fcb->ecc_nr = l.data0_size;
 | 
				
			||||||
	fcb->ecc_type = fcb->ecc_level;
 | 
						fcb->ecc_level = l.ecc0;
 | 
				
			||||||
 | 
						fcb->ecc_size = l.datan_size;
 | 
				
			||||||
 | 
						fcb->ecc_type = l.eccn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Also hardcoded in kobs-ng */
 | 
						/* Also hardcoded in kobs-ng */
 | 
				
			||||||
	fcb->ecc_nr = 0x00000200;
 | 
						if (is_mx6()) {
 | 
				
			||||||
	fcb->ecc_size = 0x00000200;
 | 
							fcb->datasetup = 80;
 | 
				
			||||||
	fcb->datasetup = 80;
 | 
							fcb->datahold = 60;
 | 
				
			||||||
	fcb->datahold = 60;
 | 
							fcb->addr_setup = 25;
 | 
				
			||||||
	fcb->addr_setup = 25;
 | 
							fcb->dsample_time = 6;
 | 
				
			||||||
	fcb->dsample_time = 6;
 | 
						} else if (is_mx7()) {
 | 
				
			||||||
	fcb->meta_size = 10;
 | 
							fcb->datasetup = 10;
 | 
				
			||||||
 | 
							fcb->datahold = 7;
 | 
				
			||||||
 | 
							fcb->addr_setup = 15;
 | 
				
			||||||
 | 
							fcb->dsample_time = 6;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* DBBT search area starts at second page on first block */
 | 
						/* DBBT search area starts at second page on first block */
 | 
				
			||||||
	fcb->dbbt_start = 1;
 | 
						fcb->dbbt_start = 1;
 | 
				
			||||||
| 
						 | 
					@ -162,6 +173,9 @@ static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
 | 
						fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fcb->disbbm = 0;
 | 
				
			||||||
 | 
						fcb->disbbm_search = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
 | 
						fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,6 +211,7 @@ static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
 | 
				
			||||||
	size_t fwsize, dummy;
 | 
						size_t fwsize, dummy;
 | 
				
			||||||
	int i, ret;
 | 
						int i, ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fcb_raw_page = 0;
 | 
				
			||||||
	/* erase */
 | 
						/* erase */
 | 
				
			||||||
	memset(&opts, 0, sizeof(opts));
 | 
						memset(&opts, 0, sizeof(opts));
 | 
				
			||||||
	opts.offset = off;
 | 
						opts.offset = off;
 | 
				
			||||||
| 
						 | 
					@ -287,50 +302,79 @@ static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
 | 
				
			||||||
	else if (ret > 0)
 | 
						else if (ret > 0)
 | 
				
			||||||
		dbbt->dbbtpages = 1;
 | 
							dbbt->dbbtpages = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* write fcb/dbbt */
 | 
						/*
 | 
				
			||||||
	fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
 | 
						 * We prepare raw page only for i.MX6, for i.MX7 we
 | 
				
			||||||
	if (!fcb_raw_page) {
 | 
						 * leverage BCH hw module instead
 | 
				
			||||||
		debug("failed to allocate fcb_raw_page\n");
 | 
						 */
 | 
				
			||||||
		ret = -ENOMEM;
 | 
						if (is_mx6()) {
 | 
				
			||||||
		goto dbbt_data_page_err;
 | 
							/* write fcb/dbbt */
 | 
				
			||||||
	}
 | 
							fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize,
 | 
				
			||||||
 | 
									       GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!fcb_raw_page) {
 | 
				
			||||||
 | 
								debug("failed to allocate fcb_raw_page\n");
 | 
				
			||||||
 | 
								ret = -ENOMEM;
 | 
				
			||||||
 | 
								goto dbbt_data_page_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
 | 
					#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
 | 
				
			||||||
	/* 40 bit BCH, for i.MX6UL(L) */
 | 
							/* 40 bit BCH, for i.MX6UL(L) */
 | 
				
			||||||
	encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
 | 
							encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
 | 
							memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
 | 
				
			||||||
	encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
 | 
							encode_hamming_13_8(fcb_raw_page + 12,
 | 
				
			||||||
 | 
									    fcb_raw_page + 12 + 512, 512);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	/*
 | 
							/*
 | 
				
			||||||
	 * Set the first and second byte of OOB data to 0xFF, not 0x00. These
 | 
							 * Set the first and second byte of OOB data to 0xFF,
 | 
				
			||||||
	 * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
 | 
							 * not 0x00. These bytes are used as the Manufacturers Bad
 | 
				
			||||||
	 * the FCB is mostly written to the first page in a block, a scan for
 | 
							 * Block Marker (MBBM). Since the FCB is mostly written to
 | 
				
			||||||
	 * factory bad blocks will detect these blocks as bad, e.g. when
 | 
							 * the first page in a block, a scan for
 | 
				
			||||||
	 * function nand_scan_bbt() is executed to build a new bad block table.
 | 
							 * factory bad blocks will detect these blocks as bad, e.g.
 | 
				
			||||||
	 */
 | 
							 * when function nand_scan_bbt() is executed to build a new
 | 
				
			||||||
	memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
 | 
							 * bad block table.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	for (i = 0; i < nr_blks_fcb; i++) {
 | 
						for (i = 0; i < nr_blks_fcb; i++) {
 | 
				
			||||||
		if (mtd_block_isbad(mtd, off)) {
 | 
							if (mtd_block_isbad(mtd, off)) {
 | 
				
			||||||
			printf("Block %d is bad, skipped\n", i);
 | 
								printf("Block %d is bad, skipped\n", i);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* raw write */
 | 
							/*
 | 
				
			||||||
		mtd_oob_ops_t ops = {
 | 
							 * User BCH ECC hardware module for i.MX7
 | 
				
			||||||
			.datbuf = (u8 *)fcb_raw_page,
 | 
							 */
 | 
				
			||||||
			.oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
 | 
							if (is_mx7()) {
 | 
				
			||||||
			.len = mtd->writesize,
 | 
								u32 off = i * mtd->erasesize;
 | 
				
			||||||
			.ooblen = mtd->oobsize,
 | 
								size_t rwsize = sizeof(*fcb);
 | 
				
			||||||
			.mode = MTD_OPS_RAW
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
 | 
								printf("Writing %d bytes to 0x%x: ", rwsize, off);
 | 
				
			||||||
		if (ret)
 | 
					
 | 
				
			||||||
			goto fcb_raw_page_err;
 | 
								/* switch nand BCH to FCB compatible settings */
 | 
				
			||||||
		debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n",
 | 
								mxs_nand_mode_fcb(mtd);
 | 
				
			||||||
		      mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK");
 | 
								ret = nand_write(mtd, off, &rwsize,
 | 
				
			||||||
 | 
										 (unsigned char *)fcb);
 | 
				
			||||||
 | 
								mxs_nand_mode_normal(mtd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								printf("%s\n", ret ? "ERROR" : "OK");
 | 
				
			||||||
 | 
							} else if (is_mx6()) {
 | 
				
			||||||
 | 
								/* 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_raw_page_err;
 | 
				
			||||||
 | 
								debug("NAND fcb write: 0x%x offset 0x%x written: %s\n",
 | 
				
			||||||
 | 
								      mtd->erasesize * i, ops.len, ret ?
 | 
				
			||||||
 | 
								      "ERROR" : "OK");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
 | 
							ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
 | 
				
			||||||
				mtd->writesize, &dummy, dbbt_page);
 | 
									mtd->writesize, &dummy, dbbt_page);
 | 
				
			||||||
| 
						 | 
					@ -352,7 +396,8 @@ static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fcb_raw_page_err:
 | 
					fcb_raw_page_err:
 | 
				
			||||||
	kfree(fcb_raw_page);
 | 
						if (is_mx6())
 | 
				
			||||||
 | 
							kfree(fcb_raw_page);
 | 
				
			||||||
dbbt_data_page_err:
 | 
					dbbt_data_page_err:
 | 
				
			||||||
	kfree(dbbt_data_page);
 | 
						kfree(dbbt_data_page);
 | 
				
			||||||
dbbt_page_err:
 | 
					dbbt_page_err:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue