summaryrefslogtreecommitdiff
path: root/platform/msm_shared/qpic_nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'platform/msm_shared/qpic_nand.c')
-rw-r--r--platform/msm_shared/qpic_nand.c295
1 files changed, 278 insertions, 17 deletions
diff --git a/platform/msm_shared/qpic_nand.c b/platform/msm_shared/qpic_nand.c
index ae1cba00..4caa5c8d 100644
--- a/platform/msm_shared/qpic_nand.c
+++ b/platform/msm_shared/qpic_nand.c
@@ -48,6 +48,8 @@ static uint32_t cfg1;
static uint32_t cfg0_raw;
static uint32_t cfg1_raw;
static uint32_t ecc_bch_cfg;
+static uint32_t ecc_cfg_raw;
+static uint32_t ecc_parity_bytes;
struct cmd_element ce_array[100] __attribute__ ((aligned(16)));
struct cmd_element ce_read_array[20] __attribute__ ((aligned(16)));
@@ -55,6 +57,8 @@ struct cmd_element ce_read_array[20] __attribute__ ((aligned(16)));
#define QPIC_BAM_DATA_FIFO_SIZE 64
#define QPIC_BAM_CMD_FIFO_SIZE 64
+#define THRESHOLD_BIT_FLIPS 4
+
static struct bam_desc cmd_desc_fifo[QPIC_BAM_CMD_FIFO_SIZE] __attribute__ ((aligned(BAM_DESC_SIZE)));
static struct bam_desc data_desc_fifo[QPIC_BAM_DATA_FIFO_SIZE] __attribute__ ((aligned(BAM_DESC_SIZE)));
@@ -516,6 +520,7 @@ qpic_nand_onfi_save_params(struct onfi_param_page *param_page, struct flash_info
static void
qpic_nand_save_config(struct flash_info *flash)
{
+ uint32_t spare_bytes = 0;
/* Save Configurations */
flash->cws_per_page = flash->page_size >> NAND_CW_DIV_RIGHT_SHIFT;
@@ -564,6 +569,10 @@ qpic_nand_save_config(struct flash_info *flash)
*/
flash->bad_blk_loc = flash->page_size - flash->cw_size * (flash->cws_per_page - 1) + 1;
+ /* Calculate the parity and spare bytes */
+ ecc_parity_bytes = (flash->ecc_width & NAND_WITH_8_BIT_ECC) ? (flash->widebus ? 14 : 13) : (flash->widebus ? 8 : 7) ;
+ spare_bytes = flash->cw_size - (USER_DATA_BYTES_PER_CW + ecc_parity_bytes);
+
cfg0 |= ((flash->cws_per_page - 1) << NAND_DEV0_CFG0_CW_PER_PAGE_SHIFT) /* 4/8 cw/pg for 2/4k */
|(DATA_BYTES_IN_IMG_PER_CW << NAND_DEV0_CFG0_UD_SIZE_BYTES_SHIFT) /* 516 user data bytes */
|(5 << NAND_DEV0_CFG0_ADDR_CYCLE_SHIFT) /* 5 address cycles */
@@ -576,16 +585,16 @@ qpic_nand_save_config(struct flash_info *flash)
|(2 << NAND_DEV0_CFG1_WR_RD_BSY_GAP_SHIFT) /* 8 cycle tWB/tRB */
|(flash->widebus << NAND_DEV0_CFG1_WIDE_BUS_SHIFT); /* preserve wide flash flag */
- cfg0_raw = ((flash->cws_per_page- 1) << NAND_DEV0_CFG0_CW_PER_PAGE_SHIFT)
+ cfg0_raw = ((flash->cws_per_page - 1) << NAND_DEV0_CFG0_CW_PER_PAGE_SHIFT)
|(5 << NAND_DEV0_CFG0_ADDR_CYCLE_SHIFT)
- |(516 << NAND_DEV0_CFG0_UD_SIZE_BYTES_SHIFT) //figure out the size of cw
- | (1 << NAND_DEV0_CFG0_DIS_STS_AFTER_WR_SHIFT);
+ |(512 << NAND_DEV0_CFG0_UD_SIZE_BYTES_SHIFT)
+ | (spare_bytes << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT);
- cfg1_raw = (7 << NAND_DEV0_CFG1_RECOVERY_CYCLES_SHIFT)
- | (0 << NAND_DEV0_CFG1_CS_ACTIVE_BSY_SHIFT)
- | (17 << NAND_DEV0_CFG1_BAD_BLK_BYTE_NUM_SHIFT)
+ cfg1_raw = (2 << NAND_DEV0_CFG1_WR_RD_BSY_GAP_SHIFT)
| (1 << NAND_DEV0_CFG1_BAD_BLK_IN_SPARE_SHIFT)
- | (2 << NAND_DEV0_CFG1_WR_RD_BSY_GAP_SHIFT)
+ | (21 << NAND_DEV0_CFG1_BAD_BLK_BYTE_NUM_SHIFT)
+ | (0 << NAND_DEV0_CFG1_CS_ACTIVE_BSY_SHIFT)
+ | (7 << NAND_DEV0_CFG1_RECOVERY_CYCLES_SHIFT)
| (flash->widebus << NAND_DEV0_CFG1_WIDE_BUS_SHIFT)
|1 ; /* to disable reed solomon ecc..this feild is now read only. */
@@ -593,6 +602,18 @@ qpic_nand_save_config(struct flash_info *flash)
| (0 << NAND_DEV0_ECC_SW_RESET_SHIFT) /* Put ECC core in op mode */
| (DATA_BYTES_IN_IMG_PER_CW << NAND_DEV0_ECC_NUM_DATA_BYTES)
| (1 << NAND_DEV0_ECC_FORCE_CLK_OPEN_SHIFT); /* Enable all clocks */
+
+ ecc_cfg_raw = (1 << NAND_DEV0_ECC_FORCE_CLK_OPEN_SHIFT)
+ | (DATA_BYTES_IN_IMG_PER_CW << NAND_DEV0_ECC_NUM_DATA_BYTES)
+ | (ecc_parity_bytes << NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT)
+ | (0 << NAND_DEV0_ECC_SW_RESET_SHIFT)
+ | (1 << NAND_DEV0_ECC_DISABLE_SHIFT);
+
+#if DEBUG_QPIC_NAND
+ dprintf(INFO, "CFG0: 0x%08x CFG1: 0x%08x\n", cfg0, cfg1);
+ dprintf(INFO, "CFG0_RAW: 0x%08x CFG1_RAW: 0x%08x\n", cfg0_raw, cfg1_raw);
+ dprintf(INFO, "ECC_BCH_CFG: 0x%08x ECC_CFG_RAW: 0x%08x\n", ecc_bch_cfg, ecc_cfg_raw);
+#endif
}
/* Onfi probe should issue the following commands to the flash device:
@@ -1381,14 +1402,209 @@ flash_set_ptable(struct ptable *new_ptable)
flash_ptable = new_ptable;
}
+static int find_num_zeros_per_cw(uint8_t *ecc_buf, uint32_t ecc_bytes)
+{
+ uint8_t val;
+ uint32_t i;
+ int num_zeros = 0;
+
+ for (i = 0; i < ecc_bytes; i++)
+ {
+ val = ecc_buf[i];
+ while (val)
+ {
+ if ((val & 1) == 0)
+ num_zeros++;
+ if (num_zeros > THRESHOLD_BIT_FLIPS)
+ goto out;
+ val >>= 1;
+ }
+ }
+
+out:
+ return num_zeros;
+}
+
+static int qpic_nand_read_erased_page(uint32_t page)
+{
+ struct cfg_params params;
+ uint32_t ecc;
+ uint32_t flash_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
+ uint32_t buffer_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
+ uint32_t addr_loc_0;
+ uint32_t total_ecc_bytes = 0;
+ struct cmd_element *cmd_list_ptr = ce_array;
+ struct cmd_element *cmd_list_ptr_start = ce_array;
+ uint32_t num_cmd_desc = 0;
+ uint32_t num_data_desc = 0;
+ uint32_t i;
+ int nand_ret = NANDC_RESULT_SUCCESS;
+ uint8_t flags = 0;
+ uint32_t *cmd_list_temp = NULL;
+ uint8_t *ecc_buf = NULL;
+ uint8_t *ecc_temp = NULL;
+ int num_zeros = 0;
+#if DEBUG_QPIC_NAND
+ uint32_t *buffer_temp = NULL;
+#endif
+
+ total_ecc_bytes = (ecc_parity_bytes * flash.cws_per_page);
+ ecc_buf = memalign(16, total_ecc_bytes);
+ ASSERT(ecc_buf);
+
+ memset(ecc_buf, 0, total_ecc_bytes);
+
+ ecc_temp = ecc_buf;
+#if DEBUG_QPIC_NAND
+ buffer_temp = (uint32_t*)ecc_buf;
+#endif
+ params.addr0 = page << 16;
+ params.addr1 = (page >> 16) & 0xff;
+ params.cfg0 = cfg0_raw;
+ params.cfg1 = cfg1_raw;
+ params.cmd = NAND_CMD_PAGE_READ;
+ params.exec = 1;
+ ecc = ecc_cfg_raw;
+
+ /* Read all the Data bytes in the first 3 CWs. */
+ addr_loc_0 = NAND_RD_LOC_OFFSET(517);
+ addr_loc_0 |= NAND_RD_LOC_SIZE(ecc_parity_bytes);
+ addr_loc_0 |= NAND_RD_LOC_LAST_BIT(1);
+
+ /* Queue up the command and data descriptors for all the codewords in a page
+ * and do a single bam transfer at the end.*/
+ for (i = 0; i < flash.cws_per_page; i++)
+ {
+ num_cmd_desc = 0;
+ num_data_desc = 0;
+ flags = 0;
+
+ if (i == 0)
+ {
+ /* Set the lock flag for the first CW */
+ flags = BAM_DESC_LOCK_FLAG;
+
+ cmd_list_ptr = qpic_nand_add_addr_n_cfg_ce(&params, cmd_list_ptr);
+
+ bam_add_cmd_element(cmd_list_ptr, NAND_DEV0_ECC_CFG,(uint32_t)ecc, CE_WRITE_TYPE);
+ cmd_list_ptr++;
+
+ bam_add_cmd_element(cmd_list_ptr, NAND_FLASH_CMD, (uint32_t)params.cmd, CE_WRITE_TYPE);
+ cmd_list_ptr++;
+
+ /* Write addr loc 0. */
+ bam_add_cmd_element(cmd_list_ptr,
+ NAND_READ_LOCATION_n(0),
+ (uint32_t)addr_loc_0,
+ CE_WRITE_TYPE);
+
+ cmd_list_ptr++;
+ }
+ else
+ cmd_list_ptr_start = cmd_list_ptr;
+
+ if (i == flash.cws_per_page - 1)
+ flags = BAM_DESC_INT_FLAG;
+
+ /* Add Data desc */
+ bam_add_one_desc(&bam,
+ DATA_PRODUCER_PIPE_INDEX,
+ (unsigned char *)PA((addr_t)ecc_temp),
+ ecc_parity_bytes,
+ flags);
+ num_data_desc++;
+ bam_sys_gen_event(&bam, DATA_PRODUCER_PIPE_INDEX, num_data_desc);
+
+ bam_add_cmd_element(cmd_list_ptr,
+ NAND_EXEC_CMD,
+ (uint32_t)params.exec,
+ CE_WRITE_TYPE);
+ cmd_list_ptr++;
+
+ /* Enqueue the desc for the above commands */
+ bam_add_one_desc(&bam,
+ CMD_PIPE_INDEX,
+ (unsigned char*)cmd_list_ptr_start,
+ PA((uint32_t)cmd_list_ptr - (uint32_t)cmd_list_ptr_start),
+ BAM_DESC_NWD_FLAG | BAM_DESC_CMD_FLAG | flags);
+ num_cmd_desc++;
+
+ bam_add_cmd_element(cmd_list_ptr, NAND_FLASH_STATUS, (uint32_t)PA((addr_t)&(flash_sts[i])), CE_READ_TYPE);
+
+ cmd_list_temp = (uint32_t *)cmd_list_ptr;
+
+ cmd_list_ptr++;
+
+ bam_add_cmd_element(cmd_list_ptr, NAND_BUFFER_STATUS, (uint32_t)PA((addr_t)&(buffer_sts[i])), CE_READ_TYPE);
+ cmd_list_ptr++;
+
+ if (i == flash.cws_per_page - 1)
+ {
+ /* Unlock flag for the last CW */
+ flags = BAM_DESC_CMD_FLAG | BAM_DESC_UNLOCK_FLAG;
+ }
+ else
+ flags = BAM_DESC_CMD_FLAG;
+
+ /* Enqueue the desc for the above command */
+ bam_add_one_desc(&bam,
+ CMD_PIPE_INDEX,
+ (unsigned char*)PA((addr_t)cmd_list_temp),
+ PA((uint32_t)cmd_list_ptr - (uint32_t)cmd_list_temp),
+ flags);
+ num_cmd_desc++;
+
+ ecc_temp += ecc_parity_bytes;
+
+ /* Notify BAM HW about the newly added descriptors */
+ bam_sys_gen_event(&bam, CMD_PIPE_INDEX, num_cmd_desc);
+ }
+
+ qpic_nand_wait_for_data(DATA_PRODUCER_PIPE_INDEX);
+
+ /* Find number of bit flips in the ecc & if there are more than "threshold" bit flips then
+ * the page is bad otherwise the page is erased page
+ */
+ ecc_temp = ecc_buf;
+
+ for (i = 0; i < flash.cws_per_page; i++)
+ {
+ num_zeros = find_num_zeros_per_cw(ecc_temp, ecc_parity_bytes);
+
+ if (num_zeros > THRESHOLD_BIT_FLIPS)
+ {
+ nand_ret = NANDC_RESULT_BAD_PAGE;
+ goto qpic_nand_read_page_error;
+ }
+
+ ecc_temp += ecc_parity_bytes;
+ }
+
+qpic_nand_read_page_error:
+
+#if DEBUG_QPIC_NAND
+ for(i = 0; i < 24; i += 8)
+ {
+ printf("ECC: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ buffer_temp[i], buffer_temp[i+1], buffer_temp[i+2], buffer_temp[i+3],
+ buffer_temp[i+4], buffer_temp[i+5], buffer_temp[i+6], buffer_temp[i+7]);
+ }
+ printf("ECC: %08x %08x\n", buffer_temp[24], buffer_temp[25]);
+#endif
+
+ free(ecc_buf);
+ return nand_ret;
+}
+
/* Note: No support for raw reads. */
static int
qpic_nand_read_page(uint32_t page, unsigned char* buffer, unsigned char* spareaddr)
{
struct cfg_params params;
uint32_t ecc;
- uint32_t flash_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
- uint32_t buffer_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
+ uint32_t flash_sts[QPIC_NAND_MAX_CWS_IN_PAGE] = {0};
+ uint32_t buffer_sts[QPIC_NAND_MAX_CWS_IN_PAGE] = {0};
+ uint32_t erased_cw_sts[QPIC_NAND_MAX_CWS_IN_PAGE] = {0};
uint32_t addr_loc_0;
uint32_t addr_loc_1;
struct cmd_element *cmd_list_ptr = ce_array;
@@ -1400,6 +1616,9 @@ qpic_nand_read_page(uint32_t page, unsigned char* buffer, unsigned char* sparead
int nand_ret = NANDC_RESULT_SUCCESS;
uint8_t flags = 0;
uint32_t *cmd_list_temp = NULL;
+#if DEBUG_QPIC_NAND
+ uint8_t *buffer_temp = buffer;
+#endif
/* UD bytes in last CW is 512 - cws_per_page *4.
* Since each of the CW read earlier reads 4 spare bytes.
@@ -1411,7 +1630,7 @@ qpic_nand_read_page(uint32_t page, unsigned char* buffer, unsigned char* sparead
params.addr1 = (page >> 16) & 0xff;
params.cfg0 = cfg0;
params.cfg1 = cfg1;
- params.cmd = NAND_CMD_PAGE_READ_ALL;
+ params.cmd = NAND_CMD_PAGE_READ_ECC;
params.exec = 1;
ecc = ecc_bch_cfg;
@@ -1522,6 +1741,10 @@ qpic_nand_read_page(uint32_t page, unsigned char* buffer, unsigned char* sparead
bam_add_cmd_element(cmd_list_ptr, NAND_BUFFER_STATUS, (uint32_t)PA((addr_t)&(buffer_sts[i])), CE_READ_TYPE);
cmd_list_ptr++;
+ /* Read erased CW status */
+ bam_add_cmd_element(cmd_list_ptr, NAND_ERASED_CW_DETECT_STATUS, (uint32_t)PA((addr_t)&erased_cw_sts[i]), CE_READ_TYPE);
+ cmd_list_ptr++;
+
if (i == flash.cws_per_page - 1)
{
flags = BAM_DESC_CMD_FLAG | BAM_DESC_UNLOCK_FLAG;
@@ -1545,19 +1768,57 @@ qpic_nand_read_page(uint32_t page, unsigned char* buffer, unsigned char* sparead
qpic_nand_wait_for_data(DATA_PRODUCER_PIPE_INDEX);
- /* Check status */
+ /* Check flash read status & errors */
for (i = 0; i < flash.cws_per_page ; i ++)
{
- flash_sts[i] = qpic_nand_check_status(flash_sts[i]);
- if (flash_sts[i])
+#if DEBUG_QPIC_NAND
+ dprintf(INFO, "FLASH STATUS: 0x%08x, BUFFER STATUS: 0x%08x, ERASED CW STATUS: 0x%08x\n",
+ flash_sts[i], buffer_sts[i], erased_cw_sts[i]);
+#endif
+
+ /* If MPU or flash op erros are set, look for erased cw status.
+ * If erased CW status is not set then look for bit flips to confirm
+ * if the page is and erased page or a bad page
+ */
+ if (flash_sts[i] & (NAND_FLASH_OP_ERR | NAND_FLASH_MPU_ERR))
{
- nand_ret = NANDC_RESULT_BAD_PAGE;
- dprintf(CRITICAL, "NAND page read failed. page: %x status %x\n", page, flash_sts[i]);
- goto qpic_nand_read_page_error;
+ if ((erased_cw_sts[i] & NAND_ERASED_CW) != NAND_ERASED_CW)
+ {
+#if DEBUG_QPIC_NAND
+ dprintf(CRITICAL, "Page: 0x%08x, addr0: 0x%08x, addr1: 0x%08x\n", page, params.addr0, params.addr1);
+#endif
+ /*
+ * Depending on the process technology used there could be bit flips on
+ * pages on the NAND card
+ * When any page is erased the controller fills the page with all 1's.
+ * When we try to read from an erased page and there are bit flips the
+ * controller would not detect the page as erased page instead throws
+ * an uncorrectable ecc error.
+ * The NAND data sheet for that card would specify the number of bit flips
+ * expected per code word. If the number of bit flips is less than expected
+ * bit flips then we should ignore the uncorrectable ECC error and consider
+ * the page as an erased page.
+ */
+#if DEBUG_QPIC_NAND
+ for(i = 0; i < 4096; i += 8)
+ {
+ printf("DATA: %x %x %x %x %x %x %x %x",
+ buffer_temp[i], buffer_temp[i+1], buffer_temp[i+2], buffer_temp[i+3],
+ buffer_temp[i+4], buffer_temp[i+5], buffer_temp[i+6], buffer_temp[i+7]);
+ i += 8;
+ printf("DATA: %x %x %x %x %x %x %x %x\n",
+ buffer_temp[i], buffer_temp[i+1], buffer_temp[i+2], buffer_temp[i+3],
+ buffer_temp[i+4], buffer_temp[i+5], buffer_temp[i+6], buffer_temp[i+7]);
+ }
+#endif
+ nand_ret = qpic_nand_read_erased_page(page);
+ goto qpic_nand_read_page_error;
+ }
}
}
+
qpic_nand_read_page_error:
-return nand_ret;
+ return nand_ret;
}
/**