platform: msm_shared: Introduce new functions & update bad block handling

This patch introduces 2 new functions:
qpic_nand_read - general function for reading more then one page of data
qpic_nand_write - general function for writing more then one page of data
flash_block_size and flash_spare_size used to retrieve NAND parameters

When erase/read/write block/page fails the block should be marked bad.

Change-Id: Iab74c33ba9d038e53b5c5728abdb561824dd1f78
diff --git a/include/dev/flash.h b/include/dev/flash.h
index 6c2402d..f2b10db 100644
--- a/include/dev/flash.h
+++ b/include/dev/flash.h
@@ -70,13 +70,14 @@
 		   unsigned offset, void *data, unsigned bytes);
 int flash_write(struct ptentry *ptn, unsigned write_extra_bytes, const void *data,
 		unsigned bytes);
-
 static inline int flash_read(struct ptentry *ptn, unsigned offset, void *data,
 			     unsigned bytes)
 {
 	return flash_read_ext(ptn, 0, offset, data, bytes);
 }
 unsigned flash_page_size(void);
+unsigned flash_block_size(void);
+unsigned flash_spare_size(void);
 int flash_ecc_bch_enabled(void);
 
 
diff --git a/platform/msm_shared/include/qpic_nand.h b/platform/msm_shared/include/qpic_nand.h
index 34ceb57..f640cf8 100644
--- a/platform/msm_shared/include/qpic_nand.h
+++ b/platform/msm_shared/include/qpic_nand.h
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2008, Google Inc.
  * All rights reserved.
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -349,5 +349,11 @@
 qpic_nand_uninit();
 /* Api to return the nand base */
 uint32_t nand_device_base();
+nand_result_t qpic_nand_read(uint32_t start_page, uint32_t num_pages,
+		unsigned char* buffer, unsigned char* spareaddr);
+nand_result_t qpic_nand_write(uint32_t start_page, uint32_t num_pages,
+		unsigned char* buffer, unsigned  write_extra_bytes);
+nand_result_t qpic_nand_block_isbad(unsigned page);
+nand_result_t qpic_nand_blk_erase(uint32_t page);
 
 #endif
diff --git a/platform/msm_shared/qpic_nand.c b/platform/msm_shared/qpic_nand.c
index 7cb57bc..91d0815 100644
--- a/platform/msm_shared/qpic_nand.c
+++ b/platform/msm_shared/qpic_nand.c
@@ -73,6 +73,8 @@
 	/* Note: Width flag is 0 for 8 bit Flash and 1 for 16 bit flash   */
 };
 
+static int qpic_nand_mark_badblock(uint32_t page);
+
 static void
 qpic_nand_wait_for_cmd_exec(uint32_t num_desc)
 {
@@ -826,8 +828,13 @@
 	return nand_ret;
 }
 
-static int
-qpic_nand_block_isbad(unsigned page)
+/**
+ * qpic_nand_block_isbad() - Checks is given block is bad
+ * @page - number of page the block starts at
+ *
+ * Returns nand_result_t
+ */
+nand_result_t qpic_nand_block_isbad(unsigned page)
 {
 	unsigned cwperpage;
 	struct cfg_params params;
@@ -895,8 +902,7 @@
 /* Function to erase a block on the nand.
  * page: Starting page address for the block.
  */
-static int
-qpic_nand_blk_erase(uint32_t page)
+nand_result_t qpic_nand_blk_erase(uint32_t page)
 {
 	struct cfg_params cfg;
 	struct cmd_element *cmd_list_ptr = ce_array;
@@ -980,6 +986,7 @@
 		dprintf(CRITICAL,
 				"NAND Erase error: Block address belongs to bad block: %d\n",
 				blk_addr);
+		qpic_nand_mark_badblock(page);
 		return NANDC_RESULT_FAILURE;
 	}
 
@@ -987,6 +994,7 @@
 	if (!(status & PROG_ERASE_OP_RESULT))
 		return NANDC_RESULT_SUCCESS;
 
+	qpic_nand_mark_badblock(page);
 	return NANDC_RESULT_FAILURE;
 }
 
@@ -1340,6 +1348,12 @@
     return flash.num_blocks;
 }
 
+unsigned
+flash_spare_size(void)
+{
+    return flash.spare_size;
+}
+
 struct ptable *
 flash_get_ptable(void)
 {
@@ -1540,6 +1554,105 @@
 return nand_ret;
 }
 
+/**
+ * qpic_nand_read() - read data
+ * @start_page: number of page to begin reading from
+ * @num_pages: number of pages to read
+ * @buffer: buffer where to store the read data
+ * @spareaddr: buffer where to store spare data.
+ * 		If null, spare data wont be read
+ *
+ * This function reads @num_pages starting from @start_page and stores the
+ * read data in buffer. Note that it's in the caller responsibility to make
+ * sure the read pages are all from same partition.
+ *
+ * Returns nand_result_t
+ */
+nand_result_t qpic_nand_read(uint32_t start_page, uint32_t num_pages,
+		unsigned char* buffer, unsigned char* spareaddr)
+{
+	unsigned i = 0, ret = 0;
+
+	if (!buffer) {
+		dprintf(CRITICAL, "qpic_nand_read: buffer = null\n");
+		return NANDC_RESULT_PARAM_INVALID;
+	}
+	while (i < num_pages) {
+		ret = qpic_nand_read_page(start_page + i, buffer + flash.page_size * i,
+				spareaddr);
+		i++;
+		if (ret == NANDC_RESULT_BAD_PAGE)
+			qpic_nand_mark_badblock(start_page + i);
+		if (ret) {
+			dprintf(CRITICAL,
+					"qpic_nand_read: reading page %d failed with %d err\n",
+					start_page + i, ret);
+			return ret;
+		}
+	}
+	return NANDC_RESULT_SUCCESS;
+}
+
+/**
+ * qpic_nand_write() - read data
+ * @start_page: number of page to begin writing to
+ * @num_pages: number of pages to write
+ * @buffer: buffer to be written
+ * @write_extra_bytes: true if spare data (ox 0xff) to be written
+ *
+ * This function writes @num_pages starting from @start_page. Note that it's
+ * in the caller responsibility to make sure the written pages are all from
+ * same partition.
+ *
+ * Returns nand_result_t
+ */
+nand_result_t qpic_nand_write(uint32_t start_page, uint32_t num_pages,
+		unsigned char* buffer, unsigned  write_extra_bytes)
+{
+	int i = 0, ret = NANDC_RESULT_SUCCESS;
+	uint32_t *spare = (unsigned *)flash_spare_bytes;
+	uint32_t wsize;
+	uint32_t spare_byte_count = 0;
+
+	if (!buffer) {
+		dprintf(CRITICAL, "qpic_nand_write: buffer = null\n");
+		return NANDC_RESULT_PARAM_INVALID;
+	}
+	spare_byte_count = ((flash.cw_size * flash.cws_per_page)- flash.page_size);
+
+	if (write_extra_bytes)
+		wsize = flash.page_size + spare_byte_count;
+	else
+		wsize = flash.page_size;
+
+	memset(spare, 0xff, (spare_byte_count / flash.cws_per_page));
+
+	for (i = 0; i < (int)num_pages; i++) {
+		memcpy(rdwr_buf, buffer, flash.page_size);
+		if (write_extra_bytes) {
+			memcpy(rdwr_buf + flash.page_size,
+					buffer + flash.page_size, spare_byte_count);
+			ret = qpic_nand_write_page(start_page + i,
+					NAND_CFG, rdwr_buf, rdwr_buf + flash.page_size);
+		} else {
+			ret = qpic_nand_write_page(start_page + i,
+					NAND_CFG, rdwr_buf, spare);
+		}
+		if (ret) {
+			dprintf(CRITICAL,
+					"flash_write: write failure @ page %d, block %d\n",
+					start_page + i,
+					(start_page + i) / flash.num_pages_per_blk);
+			if (ret == NANDC_RESULT_BAD_PAGE)
+				qpic_nand_mark_badblock(start_page + i);
+			goto out;
+		}
+		buffer += wsize;
+	}
+out:
+	return ret;
+}
+
 /* Function to read a flash partition.
  * ptn : Partition to read.
  * extra_per_page : Spare data to be read.