diff mbox

MTD: NAND: pxa3xx_nand: add ability to keep controller settings defined by OBM/bootloader

Message ID c1ead69a0e31fff83da1f5b89c81f2e8cef4b6bc.1234871317.git.mike@compulab.co.il
State New, archived
Headers show

Commit Message

Mike Rapoport Feb. 17, 2009, 11:54 a.m. UTC
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Acked-by: Eric Miao <eric.miao@marvell.com>

---
 arch/arm/mach-pxa/include/mach/pxa3xx_nand.h |    3 +
 drivers/mtd/nand/pxa3xx_nand.c               |  103 +++++++++++++++++++++++++-
 2 files changed, 105 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/arch/arm/mach-pxa/include/mach/pxa3xx_nand.h b/arch/arm/mach-pxa/include/mach/pxa3xx_nand.h
index eb35fca..3478eae 100644
--- a/arch/arm/mach-pxa/include/mach/pxa3xx_nand.h
+++ b/arch/arm/mach-pxa/include/mach/pxa3xx_nand.h
@@ -49,6 +49,9 @@  struct pxa3xx_nand_platform_data {
 	 */
 	int	enable_arbiter;
 
+	/* allow platform code to keep OBM/bootloader defined NFC config */
+	int	keep_config;
+
 	const struct mtd_partition		*parts;
 	unsigned int				nr_parts;
 
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index ead4a7a..2857a6a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -171,7 +171,13 @@  static int use_dma = 1;
 module_param(use_dma, bool, 0444);
 MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
 
-#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
+/*
+ * Default NAND flash controller configuration setup by the
+ * bootloader. This configuration is used only when pdata->keep_config is set
+ */
+static struct pxa3xx_nand_timing default_timing;
+static struct pxa3xx_nand_flash default_flash;
+
 static struct pxa3xx_nand_cmdset smallpage_cmdset = {
 	.read1		= 0x0000,
 	.read2		= 0x0050,
@@ -198,6 +204,7 @@  static struct pxa3xx_nand_cmdset largepage_cmdset = {
 	.lock_status	= 0x007A,
 };
 
+#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
 static struct pxa3xx_nand_timing samsung512MbX16_timing = {
 	.tCH	= 10,
 	.tCS	= 0,
@@ -297,9 +304,23 @@  static struct pxa3xx_nand_flash *builtin_flash_types[] = {
 #define NDTR1_tWHR(c)	(min((c), 15) << 4)
 #define NDTR1_tAR(c)	(min((c), 15) << 0)
 
+#define tCH_NDTR0(r)	(((r) >> 19) & 0x7)
+#define tCS_NDTR0(r)	(((r) >> 16) & 0x7)
+#define tWH_NDTR0(r)	(((r) >> 11) & 0x7)
+#define tWP_NDTR0(r)	(((r) >> 8) & 0x7)
+#define tRH_NDTR0(r)	(((r) >> 3) & 0x7)
+#define tRP_NDTR0(r)	(((r) >> 0) & 0x7)
+
+#define tR_NDTR1(r)	(((r) >> 16) & 0xffff)
+#define tWHR_NDTR1(r)	(((r) >> 4) & 0xf)
+#define tAR_NDTR1(r)	(((r) >> 0) & 0xf)
+
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)(((ns) * (clk / 1000000) / 1000) - 1)
 
+/* convert nand flash controller clock cycles to nano-seconds */
+#define cycle2ns(c, clk)	((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
+
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 				   const struct pxa3xx_nand_timing *t)
 {
@@ -921,6 +942,82 @@  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	return 0;
 }
 
+static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
+				      struct pxa3xx_nand_timing *t)
+{
+	unsigned long nand_clk = clk_get_rate(info->clk);
+	uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
+	uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
+
+	t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
+	t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
+	t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
+	t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
+	t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
+	t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
+
+	t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
+	t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
+	t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
+}
+
+static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+{
+	uint32_t ndcr = nand_readl(info, NDCR);
+	struct nand_flash_dev *type = NULL;
+	uint32_t id = -1;
+	int i;
+
+	default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
+	default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
+	default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
+	default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
+
+	if (default_flash.page_size == 2048)
+		default_flash.cmdset = &largepage_cmdset;
+	else
+		default_flash.cmdset = &smallpage_cmdset;
+
+	/* set info fields needed to __readid */
+	info->flash_info = &default_flash;
+	info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
+	info->reg_ndcr = ndcr;
+
+	if (__readid(info, &id))
+		return -ENODEV;
+
+	/* Lookup the flash id */
+	id = (id >> 8) & 0xff;		/* device id is byte 2 */
+	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+		if (id == nand_flash_ids[i].id) {
+			type =  &nand_flash_ids[i];
+			break;
+		}
+	}
+
+	if (!type)
+		return -ENODEV;
+
+	/* fill the missing flash information */
+	i = __ffs(default_flash.page_per_block * default_flash.page_size);
+	default_flash.num_blocks = type->chipsize << (20 - i);
+
+	info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
+
+	/* calculate addressing information */
+	info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
+
+	if (default_flash.num_blocks * default_flash.page_per_block > 65536)
+		info->row_addr_cycles = 3;
+	else
+		info->row_addr_cycles = 2;
+
+	pxa3xx_nand_detect_timing(info, &default_timing);
+	default_flash.timing = &default_timing;
+
+	return 0;
+}
+
 static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
 				    const struct pxa3xx_nand_platform_data *pdata)
 {
@@ -928,6 +1025,10 @@  static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
 	uint32_t id = -1;
 	int i;
 
+	if (pdata->keep_config)
+		if (pxa3xx_nand_detect_config(info) == 0)
+			return 0;
+
 	for (i = 0; i<pdata->num_flash; ++i) {
 		f = pdata->flash + i;