From f271049e2010b918f83dc1c7bbd5d75f4710506a Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 17 Feb 2009 13:54:47 +0200 Subject: [MTD] [NAND] pxa3xx_nand: add ability to keep controller settings defined by OBM/bootloader Signed-off-by: Mike Rapoport Acked-by: Eric Miao Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 103 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ead4a7a72d44..2857a6a37b5c 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; inum_flash; ++i) { f = pdata->flash + i; -- cgit v1.2.3