NAND: Skip erase, read and write operations on bad blocks
Change to check for bad block before reading, writing or erasing any
page from NAND and ONENAND
diff --git a/platform/msm_shared/nand.c b/platform/msm_shared/nand.c
index a81a042..2a985c1 100755
--- a/platform/msm_shared/nand.c
+++ b/platform/msm_shared/nand.c
@@ -64,6 +64,9 @@
#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
+#define NAND_CFG0_RAW 0xA80420C0
+#define NAND_CFG1_RAW 0x5045D
+
static unsigned CFG0, CFG1;
#define CFG1_WIDE_FLASH (1U << 1)
@@ -207,16 +210,105 @@
return;
}
+static int flash_nand_block_isbad(dmov_s *cmdlist, unsigned *ptrlist,
+ unsigned page)
+{
+ dmov_s *cmd = cmdlist;
+ unsigned *ptr = ptrlist;
+ unsigned *data = ptrlist + 4;
+ char buf[4];
+
+ /* Check first page of this block */
+ if(page & 63)
+ page = page - (page & 63);
+
+ /* Check bad block marker */
+ data[0] = NAND_CMD_PAGE_READ; /* command */
+
+ /* addr0 */
+ if (CFG1 & CFG1_WIDE_FLASH)
+ data[1] = (page << 16) | (0x630 >> 1);
+ else
+ data[1] = (page << 16) | 0x630;
+
+ data[2] = (page >> 16) & 0xff; /* addr1 */
+ data[3] = 0 | 4; /* chipsel */
+ data[4] = NAND_CFG0_RAW & ~(7U << 6); /* cfg0 */
+ data[5] = NAND_CFG1_RAW | (CFG1 & CFG1_WIDE_FLASH); /* cfg1 */
+ data[6] = 1;
+ data[7] = CLEAN_DATA_32; /* flash status */
+ data[8] = CLEAN_DATA_32; /* buf status */
+
+ cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
+ cmd[0].src = paddr(&data[0]);
+ cmd[0].dst = NAND_FLASH_CMD;
+ cmd[0].len = 16;
+
+ cmd[1].cmd = 0;
+ cmd[1].src = paddr(&data[4]);
+ cmd[1].dst = NAND_DEV0_CFG0;
+ cmd[1].len = 8;
+
+ cmd[2].cmd = 0;
+ cmd[2].src = paddr(&data[6]);
+ cmd[2].dst = NAND_EXEC_CMD;
+ cmd[2].len = 4;
+
+ cmd[3].cmd = SRC_CRCI_NAND_DATA;
+ cmd[3].src = NAND_FLASH_STATUS;
+ cmd[3].dst = paddr(&data[7]);
+ cmd[3].len = 8;
+
+ cmd[4].cmd = CMD_OCU | CMD_LC;
+ cmd[4].src = NAND_FLASH_BUFFER + 464;
+ cmd[4].dst = paddr(&buf);
+ cmd[4].len = 4;
+
+ ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
+
+ dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
+
+#if VERBOSE
+ dprintf(INFO, "status: %x\n", data[7]);
+#endif
+
+ /* we fail if there was an operation error, a mpu error, or the
+ ** erase success bit was not set.
+ */
+ if(data[7] & 0x110) return -1;
+
+ /* Check for bad block marker byte */
+ if (CFG1 & CFG1_WIDE_FLASH) {
+ if (buf[0] != 0xFF || buf[1] != 0xFF)
+ return 1;
+ } else {
+ if (buf[0] != 0xFF)
+ return 1;
+ }
+
+ return 0;
+}
+
static int flash_nand_erase_block(dmov_s *cmdlist, unsigned *ptrlist,
unsigned page)
{
dmov_s *cmd = cmdlist;
unsigned *ptr = ptrlist;
unsigned *data = ptrlist + 4;
+ int isbad = 0;
/* only allow erasing on block boundaries */
if(page & 63) return -1;
+ /* Check for bad block and erase only if block is not marked bad */
+ isbad = flash_nand_block_isbad(cmdlist, ptrlist, page);
+
+ if (isbad) {
+ dprintf(INFO, "skipping @ %d (bad block)\n", page >> 6);
+ return -1;
+ }
+
+ /* Erase block */
data[0] = NAND_CMD_BLOCK_ERASE;
data[1] = page;
data[2] = 0;
@@ -288,6 +380,12 @@
unsigned addr = (unsigned) _addr;
unsigned spareaddr = (unsigned) _spareaddr;
unsigned n;
+ int isbad = 0;
+
+ /* Check for bad block and read only from a good block */
+ isbad = flash_nand_block_isbad(cmdlist, ptrlist, page);
+ if (isbad)
+ return -2;
data->cmd = NAND_CMD_PAGE_READ_ECC;
data->addr0 = page << 16;
@@ -671,12 +769,45 @@
unsigned data6;
};
+
+static int _flash_onenand_read_page(dmov_s *cmdlist, unsigned *ptrlist,
+ unsigned page, void *_addr,
+ void *_spareaddr, unsigned raw_mode);
+
+
+static int flash_onenand_block_isbad(dmov_s *cmdlist, unsigned *ptrlist,
+ unsigned page)
+{
+ unsigned char page_data[2112];
+ unsigned char *oobptr = &(page_data[2048]);
+
+ /* Going to first page of the block */
+ if(page & 63)
+ page = page - (page & 63);
+
+ /* Reading page in raw mode */
+ if (_flash_onenand_read_page(cmdlist, ptrlist,page, page_data, 0, 1))
+ return 1;
+
+ /* Checking if block is bad */
+ if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) ||
+ (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) ||
+ (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) ||
+ (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF)
+ )
+ {
+ return 1;
+ }
+ return 0;
+}
+
static int flash_onenand_erase_block(dmov_s *cmdlist, unsigned *ptrlist,
unsigned page)
{
dmov_s *cmd = cmdlist;
unsigned *ptr = ptrlist;
struct data_onenand_erase *data = (void *)ptrlist + 4;
+ int isbad = 0;
unsigned erasesize = (2048 << 6);
unsigned onenand_startaddr1 = DEVICE_FLASHCORE_0 | (page*2048)/erasesize;
unsigned onenand_startaddr8 = 0x0000;
@@ -689,6 +820,15 @@
if((page*2048) & (erasesize-1)) return -1;
+ /* Check for bad block and erase only if block is not marked bad */
+ isbad = flash_onenand_block_isbad(cmdlist, ptrlist, page);
+ if (isbad)
+ {
+ dprintf(INFO, "skipping @ %d (bad block)\n", page >> 6);
+ return -1;
+ }
+
+ /*Erase block*/
onenand_startaddr1 = DEVICE_FLASHCORE_0 |
((page*2048) / (erasesize));
onenand_startaddr8 = 0x0000;
@@ -968,7 +1108,8 @@
static int _flash_onenand_read_page(dmov_s *cmdlist, unsigned *ptrlist,
- unsigned page, void *_addr, void *_spareaddr)
+ unsigned page, void *_addr, void *_spareaddr,
+ unsigned raw_mode)
{
dmov_s *cmd = cmdlist;
unsigned *ptr = ptrlist;
@@ -986,11 +1127,19 @@
(erasesize - 1)) / writesize) << 2;
unsigned onenand_startaddr2 = DEVICE_BUFFERRAM_0 << 15;
unsigned onenand_startbuffer = DATARAM0_0 << 8;
- unsigned onenand_sysconfig1 = ONENAND_SYSCFG1_ECCENA;
+ unsigned onenand_sysconfig1 = (raw_mode == 1) ? ONENAND_SYSCFG1_ECCDIS :\
+ ONENAND_SYSCFG1_ECCENA;
unsigned controller_status;
unsigned interrupt_status;
unsigned ecc_status;
+ if (raw_mode != 1)
+ {
+ int isbad = 0;
+ isbad = flash_onenand_block_isbad(cmdlist, ptrlist, page);
+ if (isbad)
+ return -2;
+ }
//static int oobfree_offset[8] = {2, 14, 18, 30, 34, 46, 50, 62};
//static int oobfree_length[8] = {3, 2, 3, 2, 3, 2, 3, 2};
@@ -1259,6 +1408,46 @@
}
}
+ /* Read oob bytes in Raw Mode */
+ if (raw_mode == 1)
+ {
+ /* Block on cmd ready and write CMD register */
+ cmd->cmd = DST_CRCI_NAND_CMD;
+ cmd->src = paddr(&data->sfcmd[7]);
+ cmd->dst = NAND_SFLASHC_CMD;
+ cmd->len = 4;
+ cmd++;
+
+ /* Write the MACRO1 register */
+ cmd->cmd = 0;
+ cmd->src = paddr(&data->macro[4]);
+ cmd->dst = NAND_MACRO1_REG;
+ cmd->len = 4;
+ cmd++;
+
+ /* Kick the execute command */
+ cmd->cmd = 0;
+ cmd->src = paddr(&data->sfexec);
+ cmd->dst = NAND_SFLASHC_EXEC_CMD;
+ cmd->len = 4;
+ cmd++;
+
+ /* Block on data rdy, & read status register */
+ cmd->cmd = SRC_CRCI_NAND_DATA;
+ cmd->src = NAND_SFLASHC_STATUS;
+ cmd->dst = paddr(&data->sfstat[7]);
+ cmd->len = 4;
+ cmd++;
+
+ /* Transfer nand ctlr buf contents to usr buf */
+ cmd->cmd = 0;
+ cmd->src = NAND_FLASH_BUFFER;
+ cmd->dst = curr_addr;
+ cmd->len = 64;
+ curr_addr += 64;
+ cmd++;
+ }
+
/*************************************************************/
/* Restore the necessary registers to proper values */
/*************************************************************/
@@ -1884,12 +2073,24 @@
case FLASH_16BIT_NAND_DEVICE:
return _flash_nand_read_page(cmdlist, ptrlist, page, _addr, _spareaddr);
case FLASH_ONENAND_DEVICE:
- return _flash_onenand_read_page(cmdlist, ptrlist, page, _addr, _spareaddr);
+ return _flash_onenand_read_page(cmdlist, ptrlist, page, _addr, _spareaddr, 0);
default:
return -1;
}
}
+static int _flash_block_isbad(dmov_s *cmdlist, unsigned *ptrlist, unsigned page)
+{
+ switch(flash_info.type) {
+ case FLASH_8BIT_NAND_DEVICE:
+ case FLASH_16BIT_NAND_DEVICE:
+ return flash_nand_block_isbad(cmdlist, ptrlist, page);
+ case FLASH_ONENAND_DEVICE:
+ return flash_onenand_block_isbad(cmdlist, ptrlist, page);
+ default:
+ return -1;
+ }
+}
static int _flash_write_page(dmov_s *cmdlist, unsigned *ptrlist,
unsigned page, const void *_addr,
@@ -1969,20 +2170,45 @@
unsigned *spare = (unsigned*) flash_spare;
unsigned errors = 0;
unsigned char *image = data;
+ unsigned current_block = (page - (page & 63)) >> 6;
+ unsigned start_block = ptn->start;
+ int result = 0;
+ int isbad = 0;
if(offset & 2047)
return -1;
+ // Adjust page offset based on number of bad blocks from start to current page
+ while (start_block < current_block) {
+ isbad = _flash_block_isbad(flash_cmdlist, flash_ptrlist, start_block*64);
+ if (isbad)
+ page += 64;
+
+ start_block++;
+ }
+
while(page < lastpage) {
if(count == 0) {
dprintf(INFO, "flash_read_image: success (%d errors)\n", errors);
return 0;
}
- if(_flash_read_page(flash_cmdlist, flash_ptrlist, page++, image, spare)) {
+ result = _flash_read_page(flash_cmdlist, flash_ptrlist, page, image, spare);
+
+ if (result == -1) {
+ // bad page, go to next page
+ page++;
errors++;
continue;
}
+ else if (result == -2) {
+ // bad block, go to next block same offset
+ page += 64;
+ errors++;
+ continue;
+ }
+
+ page++;
image += 2048;
memcpy(image, spare, extra_per_page);
image += extra_per_page;