summaryrefslogtreecommitdiffstats
path: root/common/imx-bbu-nand-fcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/imx-bbu-nand-fcb.c')
-rw-r--r--common/imx-bbu-nand-fcb.c574
1 files changed, 373 insertions, 201 deletions
diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c
index aa3f733912..0d46192720 100644
--- a/common/imx-bbu-nand-fcb.c
+++ b/common/imx-bbu-nand-fcb.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014 Sascha Hauer, Pengutronix
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation.
- *
*/
#define pr_fmt(fmt) "imx-bbu-nand-fcb: " fmt
@@ -28,6 +14,8 @@
#include <linux/sizes.h>
#include <bbu.h>
#include <fs.h>
+#include <command.h>
+#include <complete.h>
#include <linux/mtd/mtd-abi.h>
#include <linux/mtd/nand_mxs.h>
#include <linux/mtd/mtd.h>
@@ -37,95 +25,27 @@
#include <linux/bitops.h>
#include <io.h>
#include <crc.h>
-#include <mach/generic.h>
#include <mtd/mtd-peb.h>
+#include <soc/imx/imx-nand-bcb.h>
+#ifdef CONFIG_ARCH_IMX
+#include <mach/imx/imx6.h>
+#include <mach/imx/generic.h>
+#else
+#include <mach/mxs/generic.h>
+#endif
-#ifdef CONFIG_ARCH_IMX6
-#include <mach/imx6.h>
static inline int fcb_is_bch_encoded(void)
{
return cpu_is_mx6ul() || cpu_is_mx6ull();
}
-#else
-static inline int fcb_is_bch_encoded(void)
-{
- return 0;
-}
-#endif
-
-struct dbbt_block {
- uint32_t Checksum;
- uint32_t FingerPrint;
- uint32_t Version;
- uint32_t numberBB; /* reserved on i.MX6 */
- uint32_t DBBTNumOfPages;
-};
-
-struct fcb_block {
- uint32_t Checksum; /* First fingerprint in first byte */
- uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
- uint32_t Version; /* 3rd fingerprint at byte 8 */
- uint8_t DataSetup;
- uint8_t DataHold;
- uint8_t AddressSetup;
- uint8_t DSAMPLE_TIME;
- /* These are for application use only and not for ROM. */
- uint8_t NandTimingState;
- uint8_t REA;
- uint8_t RLOH;
- uint8_t RHOH;
- uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
- uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
- uint32_t SectorsPerBlock; /* Number of 2K sections per block */
- uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
- uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
- uint32_t CellType; /* MLC or SLC */
- uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
- uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
- uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
- uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
- uint32_t MetadataBytes; /* Metadata size - BCH */
- uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
- uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
- uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
- uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
- uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
- uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
- uint32_t MetadataBytesSDK; /* Metadata size - BCH */
- uint32_t EraseThreshold; /* To set into BCH_MODE register */
- uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
- uint32_t PatchSectors; /* Size of patch in sectors */
- uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
- uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
- uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
- uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
- uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
- uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
- /* this will be swapped with metadata[0] to complete page data. */
- uint32_t BadBlockMarkerStartBit;/* 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 */
- uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
- uint32_t BCHType;
-
- uint32_t TMTiming2_ReadLatency;
- uint32_t TMTiming2_PreambleDelay;
- uint32_t TMTiming2_CEDelay;
- uint32_t TMTiming2_PostambleDelay;
- uint32_t TMTiming2_CmdAddPause;
- uint32_t TMTiming2_DataPause;
- uint32_t TMSpeed;
- uint32_t TMTiming1_BusyTimeout;
-
- uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
- uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
-};
struct imx_nand_fcb_bbu_handler {
struct bbu_handler handler;
void (*fcb_create)(struct imx_nand_fcb_bbu_handler *imx_handler,
struct fcb_block *fcb, struct mtd_info *mtd);
+ int (*fcb_read)(struct mtd_info *mtd, int block, struct fcb_block **retfcb);
+ int (*fcb_write)(struct mtd_info *mtd, int block, struct fcb_block *fcb);
enum filetype filetype;
};
@@ -204,7 +124,7 @@ static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits)
free_bch(bch);
}
-static struct fcb_block *read_fcb_bch(void *rawpage, int eccbits)
+static struct fcb_block *fcb_decode_bch(void *rawpage, int eccbits)
{
int i, j, ret, errbit, m = 13;
int blocksize = 128;
@@ -308,7 +228,7 @@ static uint32_t calc_chksum(void *buf, size_t size)
return ~chksum;
}
-static struct fcb_block *read_fcb_hamming_13_8(void *rawpage)
+static struct fcb_block *fcb_decode_hamming_13_8(void *rawpage)
{
int i;
int bitflips = 0, bit_to_flip;
@@ -323,7 +243,7 @@ static struct fcb_block *read_fcb_hamming_13_8(void *rawpage)
* correcting bitflips. This means we cannot allow bitflips in the
* fingerprint and version. We bail out with an error if it's not correct.
* This is currently done in the i.MX6qdl path. It needs to be checked if
- * the same happens in the BCH encoded variants (i.MX6ul(l)) aswell.
+ * the same happens in the BCH encoded variants (i.MX6ul(l)) as well.
*/
if (((struct fcb_block *)fcb)->FingerPrint != 0x20424346 ||
((struct fcb_block *)fcb)->Version != 0x01000000)
@@ -363,57 +283,57 @@ static __maybe_unused void dump_fcb(void *buf)
{
struct fcb_block *fcb = buf;
- pr_debug("Checksum: 0x%08x\n", fcb->Checksum);
- pr_debug("FingerPrint: 0x%08x\n", fcb->FingerPrint);
- pr_debug("Version: 0x%08x\n", fcb->Version);
- pr_debug("DataSetup: 0x%02x\n", fcb->DataSetup);
- pr_debug("DataHold: 0x%02x\n", fcb->DataHold);
- pr_debug("AddressSetup: 0x%02x\n", fcb->AddressSetup);
- pr_debug("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME);
- pr_debug("NandTimingState: 0x%02x\n", fcb->NandTimingState);
- pr_debug("REA: 0x%02x\n", fcb->REA);
- pr_debug("RLOH: 0x%02x\n", fcb->RLOH);
- pr_debug("RHOH: 0x%02x\n", fcb->RHOH);
- pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize);
- pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize);
- pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock);
- pr_debug("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs);
- pr_debug("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie);
- pr_debug("CellType: 0x%08x\n", fcb->CellType);
- pr_debug("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType);
- pr_debug("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size);
- pr_debug("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize);
- pr_debug("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType);
- pr_debug("MetadataBytes: 0x%08x\n", fcb->MetadataBytes);
- pr_debug("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage);
- pr_debug("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK);
- pr_debug("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK);
- pr_debug("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK);
- pr_debug("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK);
- pr_debug("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK);
- pr_debug("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK);
- pr_debug("EraseThreshold: 0x%08x\n", fcb->EraseThreshold);
- pr_debug("BootPatch: 0x%08x\n", fcb->BootPatch);
- pr_debug("PatchSectors: 0x%08x\n", fcb->PatchSectors);
- pr_debug("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage);
- pr_debug("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage);
- pr_debug("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1);
- pr_debug("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2);
- pr_debug("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress);
- pr_debug("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte);
- pr_debug("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit);
- pr_debug("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset);
- pr_debug("BCHType: 0x%08x\n", fcb->BCHType);
- pr_debug("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency);
- pr_debug("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay);
- pr_debug("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay);
- pr_debug("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay);
- pr_debug("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause);
- pr_debug("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause);
- pr_debug("TMSpeed: 0x%08x\n", fcb->TMSpeed);
- pr_debug("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout);
- pr_debug("DISBBM: 0x%08x\n", fcb->DISBBM);
- pr_debug("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData);
+ printf("Checksum: 0x%08x\n", fcb->Checksum);
+ printf("FingerPrint: 0x%08x\n", fcb->FingerPrint);
+ printf("Version: 0x%08x\n", fcb->Version);
+ printf("DataSetup: 0x%02x\n", fcb->DataSetup);
+ printf("DataHold: 0x%02x\n", fcb->DataHold);
+ printf("AddressSetup: 0x%02x\n", fcb->AddressSetup);
+ printf("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME);
+ printf("NandTimingState: 0x%02x\n", fcb->NandTimingState);
+ printf("REA: 0x%02x\n", fcb->REA);
+ printf("RLOH: 0x%02x\n", fcb->RLOH);
+ printf("RHOH: 0x%02x\n", fcb->RHOH);
+ printf("PageDataSize: 0x%08x\n", fcb->PageDataSize);
+ printf("TotalPageSize: 0x%08x\n", fcb->TotalPageSize);
+ printf("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock);
+ printf("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs);
+ printf("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie);
+ printf("CellType: 0x%08x\n", fcb->CellType);
+ printf("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType);
+ printf("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size);
+ printf("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize);
+ printf("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType);
+ printf("MetadataBytes: 0x%08x\n", fcb->MetadataBytes);
+ printf("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage);
+ printf("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK);
+ printf("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK);
+ printf("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK);
+ printf("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK);
+ printf("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK);
+ printf("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK);
+ printf("EraseThreshold: 0x%08x\n", fcb->EraseThreshold);
+ printf("BootPatch: 0x%08x\n", fcb->BootPatch);
+ printf("PatchSectors: 0x%08x\n", fcb->PatchSectors);
+ printf("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage);
+ printf("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage);
+ printf("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1);
+ printf("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2);
+ printf("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress);
+ printf("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte);
+ printf("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit);
+ printf("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset);
+ printf("BCHType: 0x%08x\n", fcb->BCHType);
+ printf("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency);
+ printf("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay);
+ printf("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay);
+ printf("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay);
+ printf("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause);
+ printf("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause);
+ printf("TMSpeed: 0x%08x\n", fcb->TMSpeed);
+ printf("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout);
+ printf("DISBBM: 0x%08x\n", fcb->DISBBM);
+ printf("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData);
}
static __maybe_unused ssize_t raw_read_page(struct mtd_info *mtd, void *dst, loff_t offset)
@@ -448,7 +368,7 @@ static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
return ret;
}
-static int read_fcb(struct mtd_info *mtd, int num, struct fcb_block **retfcb)
+static int fcb_read_hamming_13_8(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
{
int ret;
struct fcb_block *fcb;
@@ -458,19 +378,71 @@ static int read_fcb(struct mtd_info *mtd, int num, struct fcb_block **retfcb)
rawpage = xmalloc(mtd->writesize + mtd->oobsize);
- ret = raw_read_page(mtd, rawpage, mtd->erasesize * num);
+ ret = raw_read_page(mtd, rawpage, mtd->erasesize * block);
if (ret) {
- pr_err("Cannot read block %d\n", num);
+ pr_err("Cannot read block %d\n", block);
goto err;
}
- if (fcb_is_bch_encoded())
- fcb = read_fcb_bch(rawpage, 40);
- else
- fcb = read_fcb_hamming_13_8(rawpage);
+ fcb = fcb_decode_hamming_13_8(rawpage);
+ if (IS_ERR(fcb)) {
+ pr_err("Cannot decode fcb on block %d\n", block);
+ ret = PTR_ERR(fcb);
+ goto err;
+ }
+
+ *retfcb = fcb;
+ ret = 0;
+err:
+ free(rawpage);
+
+ return ret;
+}
+
+static int fcb_write_hamming_13_8(struct mtd_info *mtd, int block, struct fcb_block *fcb)
+{
+ void *fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize);
+ int ret;
+
+ memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
+
+ /*
+ * 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);
+
+ encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
+
+ ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize);
+
+ free(fcb_raw_page);
+
+ return ret;
+}
+
+static int fcb_read_bch(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
+{
+ int ret;
+ struct fcb_block *fcb;
+ void *rawpage;
+
+ *retfcb = NULL;
+
+ rawpage = xmalloc(mtd->writesize + mtd->oobsize);
+ ret = raw_read_page(mtd, rawpage, mtd->erasesize * block);
+ if (ret) {
+ pr_err("Cannot read block %d\n", block);
+ goto err;
+ }
+
+ fcb = fcb_decode_bch(rawpage, 40);
if (IS_ERR(fcb)) {
- pr_err("Cannot read fcb on block %d\n", num);
+ pr_err("Cannot decode fcb on block %d\n", block);
ret = PTR_ERR(fcb);
goto err;
}
@@ -483,6 +455,31 @@ err:
return ret;
}
+static int fcb_write_bch(struct mtd_info *mtd, int block, struct fcb_block *fcb)
+{
+ void *fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize);
+ int ret;
+
+ /* 40 bit BCH, for i.MX6UL(L) */
+ encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
+
+ /*
+ * 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);
+
+ ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize);
+
+ free(fcb_raw_page);
+
+ return ret;
+}
+
+
static int fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
struct fcb_block *fcb, struct mtd_info *mtd)
{
@@ -519,6 +516,9 @@ static int fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
imx_handler->fcb_create(imx_handler, fcb, mtd);
+ fcb->DISBBM = 0;
+ fcb->disbbm_search = 0;
+
fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
return 0;
@@ -582,7 +582,8 @@ static int imx_bbu_firmware_start_block(struct mtd_info *mtd, int num)
* @num: The slot number (0 or 1)
*
* This returns the start page for a firmware slot, to be written into the
- * Firmwaren_startingPage field in the FCB.
+ * Firmwaren_startingPage field in the FCB or a negative error code in case
+ * of a failure.
*/
static int imx_bbu_firmware_fcb_start_page(struct mtd_info *mtd, int num)
{
@@ -593,6 +594,11 @@ static int imx_bbu_firmware_fcb_start_page(struct mtd_info *mtd, int num)
blocksleft = imx_bbu_firmware_max_blocks(mtd);
+ if (blocksleft <= 0) {
+ pr_err("partition size too small for both firmwares\n");
+ return -ENOMEM;
+ }
+
/*
* The ROM only checks for a bad block when advancing the read position,
* but not if the initial block is good, hence we cannot directly point
@@ -726,7 +732,7 @@ static void imx28_dbbt_create(struct dbbt_block *dbbt, int num_bad_blocks)
* imx_bbu_write_fcb - Write FCB and DBBT raw data to the device
* @mtd: The mtd Nand device
* @block: The block to write to
- * @fcb_raw_page: The raw FCB data
+ * @fcb: FCB
* @dbbt_data_page: The DBBT data
*
* This function writes the FCB/DBBT data to the block given in @block
@@ -735,7 +741,8 @@ static void imx28_dbbt_create(struct dbbt_block *dbbt, int num_bad_blocks)
*
* return: 0 on success or a negative error code otherwise.
*/
-static int imx_bbu_write_fcb(struct mtd_info *mtd, int block, void *fcb_raw_page,
+static int imx_bbu_write_fcb(struct imx_nand_fcb_bbu_handler *imx_handler,
+ struct mtd_info *mtd, int block, struct fcb_block *fcb,
void *dbbt_data_page)
{
struct dbbt_block *dbbt;
@@ -757,7 +764,7 @@ again:
if (ret)
return ret;
- ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize);
+ ret = imx_handler->fcb_write(mtd, block, fcb);
if (ret) {
pr_err("Writing FCB on block %d failed with %s\n",
block, strerror(-ret));
@@ -918,13 +925,14 @@ out:
*
* return: 0 if the FCB/DBBT are valid, a negative error code otherwise
*/
-static int fcb_dbbt_check(struct mtd_info *mtd, int num, struct fcb_block *fcb)
+static int fcb_dbbt_check(struct imx_nand_fcb_bbu_handler *imx_handler,
+ struct mtd_info *mtd, int num, struct fcb_block *fcb)
{
int ret;
struct fcb_block *f;
int pages_per_block = mtd->erasesize / mtd->writesize;
- ret = read_fcb(mtd, num, &f);
+ ret = imx_handler->fcb_read(mtd, num, &f);
if (ret)
return ret;
@@ -960,11 +968,11 @@ out:
*
* return: 0 for success or a negative error code otherwise.
*/
-static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb)
+static int imx_bbu_write_fcbs_dbbts(struct imx_nand_fcb_bbu_handler *imx_handler,
+ struct mtd_info *mtd, struct fcb_block *fcb)
{
void *dbbt = NULL;
int i, ret, valid = 0;
- void *fcb_raw_page;
/*
* The DBBT search start page is configurable in the FCB block.
@@ -974,28 +982,8 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb)
if (fcb->DBBTSearchAreaStartAddress != 1)
return -EINVAL;
- fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize);
-
- if (fcb_is_bch_encoded()) {
- /* 40 bit BCH, for i.MX6UL(L) */
- encode_bch_ecc(fcb_raw_page + 32, fcb, 40);
- } else {
- memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
- encode_hamming_13_8(fcb_raw_page + 12,
- fcb_raw_page + 12 + 512, 512);
- }
-
dbbt = dbbt_data_create(mtd);
- /*
- * 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);
-
pr_info("Writing FCBs/DBBTs with primary/secondary Firmwares at pages %d/%d\n",
fcb->Firmware1_startingPage, fcb->Firmware2_startingPage);
@@ -1003,7 +991,7 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb)
if (mtd_peb_is_bad(mtd, i))
continue;
- if (!fcb_dbbt_check(mtd, i, fcb)) {
+ if (!fcb_dbbt_check(imx_handler, mtd, i, fcb)) {
valid++;
pr_info("FCB/DBBT on block %d still valid\n", i);
continue;
@@ -1011,14 +999,13 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb)
pr_info("Writing FCB/DBBT on block %d\n", i);
- ret = imx_bbu_write_fcb(mtd, i, fcb_raw_page, dbbt);
+ ret = imx_bbu_write_fcb(imx_handler, mtd, i, fcb, dbbt);
if (ret)
pr_err("Writing FCB/DBBT %d failed with: %s\n", i, strerror(-ret));
else
valid++;
}
- free(fcb_raw_page);
free(dbbt);
if (!valid)
@@ -1230,7 +1217,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
enum filetype filetype;
unsigned num_blocks_fw, fw_size;
int used = 0;
- int fw_orig_len;
+ int fw_orig_len = 0;
int used_refresh = 0, unused_refresh = 0;
if (data->image) {
@@ -1258,7 +1245,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
}
for (i = 0; i < 4; i++) {
- read_fcb(mtd, i, &fcb);
+ imx_handler->fcb_read(mtd, i, &fcb);
if (fcb)
break;
}
@@ -1285,7 +1272,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
* ----------------------
*
* We want a robust update in which a power failure may occur
- * everytime without bricking the board, so here's the strategy:
+ * every time without bricking the board, so here's the strategy:
*
* The FCBs contain pointers to the firmware slots in the
* Firmware1_startingPage and Firmware2_startingPage fields. Note that
@@ -1338,8 +1325,17 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
free(fcb);
fcb = xzalloc(sizeof(*fcb));
- fcb->Firmware1_startingPage = imx_bbu_firmware_fcb_start_page(mtd, !used);
- fcb->Firmware2_startingPage = imx_bbu_firmware_fcb_start_page(mtd, used);
+
+ ret = imx_bbu_firmware_fcb_start_page(mtd, !used);
+ if (ret < 0)
+ goto out;
+ fcb->Firmware1_startingPage = ret;
+
+ ret = imx_bbu_firmware_fcb_start_page(mtd, used);
+ if (ret < 0)
+ goto out;
+ fcb->Firmware2_startingPage = ret;
+
fcb->PagesInFirmware1 = fw_size / mtd->writesize;
fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
@@ -1392,7 +1388,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
* just written as primary firmware. From now on the new
* firmware will be booted.
*/
- ret = imx_bbu_write_fcbs_dbbts(mtd, fcb);
+ ret = imx_bbu_write_fcbs_dbbts(imx_handler, mtd, fcb);
if (ret < 0)
goto out;
@@ -1413,7 +1409,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
*/
if (ret > 0) {
pr_info("New bad blocks detected, writing FCBs/DBBTs again\n");
- ret = imx_bbu_write_fcbs_dbbts(mtd, fcb);
+ ret = imx_bbu_write_fcbs_dbbts(imx_handler, mtd, fcb);
if (ret < 0)
goto out;
}
@@ -1446,6 +1442,14 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags)
imx_handler->fcb_create = imx6_fcb_create;
imx_handler->filetype = filetype_arm_barebox;
+ if (fcb_is_bch_encoded()) {
+ imx_handler->fcb_read = fcb_read_bch;
+ imx_handler->fcb_write = fcb_write_bch;
+ } else {
+ imx_handler->fcb_read = fcb_read_hamming_13_8;
+ imx_handler->fcb_write = fcb_write_hamming_13_8;
+ }
+
handler = &imx_handler->handler;
handler->devicefile = "nand0.barebox";
handler->name = name;
@@ -1459,9 +1463,6 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags)
return ret;
}
-#ifdef CONFIG_ARCH_IMX28
-#include <mach/imx28-regs.h>
-
#define GPMI_TIMING0 0x00000070
#define GPMI_TIMING0_ADDRESS_SETUP_MASK (0xff << 16)
#define GPMI_TIMING0_ADDRESS_SETUP_OFFSET 16
@@ -1479,18 +1480,31 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags)
#define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24
#define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16)
#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
-#define BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0xfff
+#define MX28_BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
+#define MX28_BCH_FLASHLAYOUT0_ECC0_OFFSET 12
+#define BCH_FLASHLAYOUT0_ECC0_MASK (0x1f << 11)
+#define BCH_FLASHLAYOUT0_ECC0_OFFSET 11
+#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_MASK BIT(10)
+#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET 10
+#define BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0x3ff
#define BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET 0
+#define MX28_BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0xfff
#define BCH_FLASH0LAYOUT1 0x00000090
#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
-#define BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0xfff
+#define BCH_FLASHLAYOUT1_ECCN_MASK (0x1f << 11)
+#define BCH_FLASHLAYOUT1_ECCN_OFFSET 11
+#define MX28_BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
+#define MX28_BCH_FLASHLAYOUT1_ECCN_OFFSET 12
+#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK BIT(10)
+#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET 10
#define BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET 0
+#define BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0x3ff
+#define MX28_BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0xfff
+
+#ifdef CONFIG_ARCH_IMX28
+#include <mach/mxs/imx28-regs.h>
static void imx28_fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
struct fcb_block *fcb, struct mtd_info *mtd)
@@ -1520,6 +1534,8 @@ int imx28_bbu_nand_register_handler(const char *name, unsigned long flags)
imx_handler = xzalloc(sizeof(*imx_handler));
imx_handler->fcb_create = imx28_fcb_create;
+ imx_handler->fcb_read = fcb_read_hamming_13_8;
+ imx_handler->fcb_write = fcb_write_hamming_13_8;
imx_handler->filetype = filetype_mxs_bootstream;
@@ -1536,3 +1552,159 @@ int imx28_bbu_nand_register_handler(const char *name, unsigned long flags)
return ret;
}
#endif
+
+static int imx7_fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
+{
+ struct fcb_block *fcb = xzalloc(mtd->writesize);
+ int ret;
+
+ ret = mxs_nand_read_fcb_bch62(block, fcb, sizeof(*fcb));
+ if (ret)
+ free(fcb);
+ else
+ *retfcb = fcb;
+
+ return ret;
+}
+
+#ifdef CONFIG_ARCH_IMX7
+#include <mach/imx/imx7-regs.h>
+
+static void imx7_fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
+ struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ void __iomem *bch_regs = IOMEM(MX7_BCH_BASE);
+ u32 fl0, fl1;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->DataSetup = 10;
+ fcb->DataHold = 7;
+ fcb->AddressSetup = 15;
+ fcb->DSAMPLE_TIME = 6;
+
+ fl0 = readl(bch_regs + BCH_FLASH0LAYOUT0);
+ fcb->MetadataBytes = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+ fcb->NumEccBlocksPerPage = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
+ fcb->EccBlock0Size = 4 * BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE);
+ fcb->EccBlock0EccType = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
+
+ fl1 = readl(bch_regs + BCH_FLASH0LAYOUT1);
+ fcb->EccBlockNSize = 4 * BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE);
+ fcb->EccBlockNEccType = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
+ fcb->BCHType = BF_VAL(fl1, BCH_FLASHLAYOUT1_GF13_0_GF14_1);
+}
+
+static int imx7_fcb_write(struct mtd_info *mtd, int block, struct fcb_block *fcb)
+{
+ return mxs_nand_write_fcb_bch62(block, fcb, sizeof(*fcb));
+}
+
+int imx7_bbu_nand_register_handler(const char *name, unsigned long flags)
+{
+ struct imx_nand_fcb_bbu_handler *imx_handler;
+ struct bbu_handler *handler;
+ int ret;
+
+ imx_handler = xzalloc(sizeof(*imx_handler));
+ imx_handler->fcb_create = imx7_fcb_create;
+ imx_handler->fcb_read = imx7_fcb_read;
+ imx_handler->fcb_write = imx7_fcb_write;
+ imx_handler->filetype = filetype_arm_barebox;
+
+ handler = &imx_handler->handler;
+ handler->devicefile = "nand0.barebox";
+ handler->name = name;
+ handler->flags = flags | BBU_HANDLER_CAN_REFRESH;
+ handler->handler = imx_bbu_nand_update;
+
+ ret = bbu_register_handler(handler);
+ if (ret)
+ free(handler);
+
+ return ret;
+}
+#endif
+
+static void dump_fcb_n(struct fcb_block **fcbs, int n)
+{
+ int i;
+
+ if (!n || !fcbs[n])
+ goto skip_compare;
+
+ for (i = 0; i < n; i++) {
+ if (!fcbs[i] || !fcbs[n])
+ continue;
+
+ if (!memcmp(fcbs[i], fcbs[n], sizeof(struct fcb_block))) {
+ printf("FCB block#%d: same as FCB block#%d\n", n, i);
+ return;
+ }
+ }
+
+skip_compare:
+ if (fcbs[n]) {
+ printf("FCB block#%d:\n", n);
+ dump_fcb(fcbs[n]);
+ } else {
+ printf("FCB block#%d: NULL\n", n);
+ }
+}
+
+static int fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
+{
+ if (cpu_is_mx7())
+ return imx7_fcb_read(mtd, block, retfcb);
+ else if (fcb_is_bch_encoded())
+ return fcb_read_bch(mtd, block, retfcb);
+ else
+ return fcb_read_hamming_13_8(mtd, block, retfcb);
+}
+
+static int cmd_fcb(int argc, char *argv[])
+{
+ struct cdev *cdev;
+ struct mtd_info *mtd;
+ struct fcb_block *fcb[4] = {};
+ int i, ret;
+
+ cdev = cdev_open_by_name("nand0", O_RDONLY);
+ if (!cdev) {
+ printf("Cannot open nand0\n");
+ return COMMAND_ERROR;
+ }
+
+ mtd = cdev->mtd;
+ if (!mtd) {
+ ret = COMMAND_ERROR;
+ goto out;
+ }
+
+ for (i = 0; i < 4; i++)
+ fcb_read(mtd, i, &fcb[i]);
+
+ for (i = 0; i < 4; i++)
+ dump_fcb_n(fcb, i);
+
+ for (i = 0; i < 4; i++)
+ free(fcb[i]);
+
+ ret = 0;
+
+out:
+ cdev_close(cdev);
+
+ return ret;
+}
+
+BAREBOX_CMD_HELP_START(fcb)
+BAREBOX_CMD_HELP_TEXT("Dump FCB as found on /dev/nand0")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(fcb)
+ .cmd = cmd_fcb,
+ BAREBOX_CMD_DESC("Dump Flash Control Block (FCB)")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_fcb_help)
+ BAREBOX_CMD_COMPLETE(empty_complete)
+BAREBOX_CMD_END