generic-poky/meta/recipes-kernel/linux/linux-rp-2.6.23/zylonite_mtd-r0.patch

4094 lines
126 KiB
Diff
Raw Blame History

Gross hacks to make the Zylonite boot from flash in VGA.
Flash driver forward ported to 2.6.14
Index: linux-2.6.23/drivers/mtd/nand/Kconfig
===================================================================
--- linux-2.6.23.orig/drivers/mtd/nand/Kconfig 2007-10-09 21:31:38.000000000 +0100
+++ linux-2.6.23/drivers/mtd/nand/Kconfig 2008-02-13 00:59:45.000000000 +0000
@@ -223,6 +223,10 @@
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on ARCH_PXA
+config MTD_NAND_ZYLONITE
+ tristate "Support for NAND Flash on Zylonite"
+ depends on ARCH_PXA
+
config MTD_NAND_BASLER_EXCITE
tristate "Support for NAND Flash on Basler eXcite"
depends on BASLER_EXCITE
Index: linux-2.6.23/drivers/mtd/nand/Makefile
===================================================================
--- linux-2.6.23.orig/drivers/mtd/nand/Makefile 2007-10-09 21:31:38.000000000 +0100
+++ linux-2.6.23/drivers/mtd/nand/Makefile 2008-02-13 00:59:45.000000000 +0000
@@ -19,6 +19,7 @@
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
+obj-$(CONFIG_MTD_NAND_ZYLONITE) += mhn_nand.o
obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
Index: linux-2.6.23/drivers/mtd/nand/mhn_nand.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.23/drivers/mtd/nand/mhn_nand.c 2008-02-13 00:59:45.000000000 +0000
@@ -0,0 +1,3869 @@
+/*
+ * drivers/mtd/nand/mhn_nand.c
+ *
+ * Copyright (C) 2005 Intel Coporation (chao.xie@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device on zylonite board
+ * which utilizes the Samsung K9K1216Q0C parts. This is a 64Mibit NAND
+ * flash device.
+
+ *(C) Copyright 2006 Marvell International Ltd.
+ * All Rights Reserved
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/arch/mfp.h>
+//#include <asm/arch/cpu-freq-voltage-mhn.h>
+
+//#define NDCR 0xf0000000
+//#define NDCR (*((volatile u32 *)0xf0000000))
+//#define NDCR __REG_2(0x43100000) /* Data Flash Control register */
+#define NDCR_SPARE_EN (0x1<<31)
+#define NDCR_ECC_EN (0x1<<30)
+#define NDCR_DMA_EN (0x1<<29)
+#define NDCR_ND_RUN (0x1<<28)
+#define NDCR_DWIDTH_C (0x1<<27)
+#define NDCR_DWIDTH_M (0x1<<26)
+#define NDCR_PAGE_SZ (0x1<<24)
+#define NDCR_NCSX (0x1<<23)
+#define NDCR_ND_MODE (0x3<<21)
+#define NDCR_NAND_MODE 0x0
+#define NDCR_CLR_PG_CNT (0x1<<20)
+#define NDCR_CLR_ECC ( 0x1<<19)
+#define NDCR_RD_ID_CNT_MASK (0x7<<16)
+#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
+#define NDCR_RA_START (0x1<<15)
+#define NDCR_PG_PER_BLK (0x1<<14)
+#define NDCR_ND_ARB_EN (0x1<<12)
+
+//#define NDSR (*((volatile u32 *)0xf0000014))
+//#define NDSR __REG_2(0x43100014) /* Data Controller Status Register */
+#define NDSR_RDY (0x1<<11)
+#define NDSR_CS0_PAGED (0x1<<10)
+#define NDSR_CS1_PAGED (0x1<<9)
+#define NDSR_CS0_CMDD (0x1<<8)
+#define NDSR_CS1_CMDD (0x1<<7)
+#define NDSR_CS0_BBD (0x1<<6)
+#define NDSR_CS1_BBD (0x1<<5)
+#define NDSR_DBERR (0x1<<4)
+#define NDSR_SBERR (0x1<<3)
+#define NDSR_WRDREQ (0x1<<2)
+#define NDSR_RDDREQ (0x1<<1)
+#define NDSR_WRCMDREQ (0x1)
+
+#define OSCR __REG(0x40A00010) /* OS Timer Counter Register */
+//#define NDCB0 __REG_2(0x43100048) /* Data Controller Command Buffer0 */
+//#define NDCB1 __REG_2(0x4310004C) /* Data Controller Command Buffer1 */
+//#define NDCB2 __REG_2(0x43100050) /* Data Controller Command Buffer2 */
+#define NDCB0_AUTO_RS (0x1<<25)
+#define NDCB0_CSEL (0x1<<24)
+#define NDCB0_CMD_TYPE_MASK (0x7<<21)
+#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC (0x1<<20)
+#define NDCB0_DBC (0x1<<19)
+#define NDCB0_ADDR_CYC_MASK (0x7<<16)
+#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK (0xff<<8)
+#define NDCB0_CMD1_MASK (0xff)
+#define NDCB0_ADDR_CYC_SHIFT (16)
+#define DCMD0 __REG(0x4000020c) /* DMA Command Address Register Channel 0 */
+#define DCMD1 __REG(0x4000021c) /* DMA Command Address Register Channel 1 */
+#define DCMD2 __REG(0x4000022c) /* DMA Command Address Register Channel 2 */
+#define DCMD3 __REG(0x4000023c) /* DMA Command Address Register Channel 3 */
+#define DCMD4 __REG(0x4000024c) /* DMA Command Address Register Channel 4 */
+#define DCMD5 __REG(0x4000025c) /* DMA Command Address Register Channel 5 */
+#define DCMD6 __REG(0x4000026c) /* DMA Command Address Register Channel 6 */
+#define DCMD7 __REG(0x4000027c) /* DMA Command Address Register Channel 7 */
+#define DCMD8 __REG(0x4000028c) /* DMA Command Address Register Channel 8 */
+#define DCMD9 __REG(0x4000029c) /* DMA Command Address Register Channel 9 */
+#define DCMD10 __REG(0x400002ac) /* DMA Command Address Register Channel 10 */
+#define DCMD11 __REG(0x400002bc) /* DMA Command Address Register Channel 11 */
+#define DCMD12 __REG(0x400002cc) /* DMA Command Address Register Channel 12 */
+#define DCMD13 __REG(0x400002dc) /* DMA Command Address Register Channel 13 */
+#define DCMD14 __REG(0x400002ec) /* DMA Command Address Register Channel 14 */
+#define DCMD15 __REG(0x400002fc) /* DMA Command Address Register Channel 15 */
+#define DCMD(x) __REG2(0x4000020c, (x) << 4)
+#define DCMD_INCSRCADDR (1 << 31) /* Source Address Increment Setting. */
+#define DCMD_INCTRGADDR (1 << 30) /* Target Address Increment Setting. */
+#define DCMD_FLOWSRC (1 << 29) /* Flow Control by the source. */
+#define DCMD_FLOWTRG (1 << 28) /* Flow Control by the target. */
+#define DCMD_STARTIRQEN (1 << 22) /* Start Interrupt Enable */
+#define DCMD_ENDIRQEN (1 << 21) /* End Interrupt Enable */
+#define DCMD_ENDIAN (1 << 18) /* Device Endian-ness. */
+#define DCMD_BURST8 (1 << 16) /* 8 byte burst */
+#define DCMD_BURST16 (2 << 16) /* 16 byte burst */
+#define DCMD_BURST32 (3 << 16) /* 32 byte burst */
+#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */
+#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */
+#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */
+#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */
+#define DCMD_RXPCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
+#define DCMD_RXMCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
+#define DCMD_TXPCDR (DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH4)
+#define DRCMR(n) __REG2(0x40000100, (n)<<2)
+#define DRCMR97 __REG(0x40001184) /* Request to Channel Map Register for NAND interface data transmit & receive Request */
+#define DRCMR98 __REG(0x40001188) /* Reserved */
+#define DRCMR99 __REG(0x4000118C) /* Request to Channel Map Register for NAND interface command transmit Request */
+#define DRCMRRXSADR DRCMR2
+#define DRCMRTXSADR DRCMR3
+#define DRCMRRXBTRBR DRCMR4
+#define DRCMRTXBTTHR DRCMR5
+#define DRCMRRXFFRBR DRCMR6
+#define DRCMRTXFFTHR DRCMR7
+#define DRCMRRXMCDR DRCMR8
+#define DRCMRRXMODR DRCMR9
+#define DRCMRTXMODR DRCMR10
+#define DRCMRRXPCDR DRCMR11
+#define DRCMRTXPCDR DRCMR12
+#define DRCMRRXSSDR DRCMR13
+#define DRCMRTXSSDR DRCMR14
+#define DRCMRRXICDR DRCMR17
+#define DRCMRTXICDR DRCMR18
+#define DRCMRRXSTRBR DRCMR19
+#define DRCMRTXSTTHR DRCMR20
+#define DRCMRRXMMC DRCMR21
+#define DRCMRTXMMC DRCMR22
+#define DRCMRRXMMC2 DRCMR93
+#define DRCMRTXMMC2 DRCMR94
+#define DRCMRRXMMC3 DRCMR100
+#define DRCMRTXMMC3 DRCMR101
+#define DRCMRUDC(x) DRCMR((x) + 24)
+#define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */
+#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */
+#define DCSR0 __REG(0x40000000) /* DMA Control / Status Register for Channel 0 */
+#define DCSR1 __REG(0x40000004) /* DMA Control / Status Register for Channel 1 */
+#define DCSR2 __REG(0x40000008) /* DMA Control / Status Register for Channel 2 */
+#define DCSR3 __REG(0x4000000c) /* DMA Control / Status Register for Channel 3 */
+#define DCSR4 __REG(0x40000010) /* DMA Control / Status Register for Channel 4 */
+#define DCSR5 __REG(0x40000014) /* DMA Control / Status Register for Channel 5 */
+#define DCSR6 __REG(0x40000018) /* DMA Control / Status Register for Channel 6 */
+#define DCSR7 __REG(0x4000001c) /* DMA Control / Status Register for Channel 7 */
+#define DCSR8 __REG(0x40000020) /* DMA Control / Status Register for Channel 8 */
+#define DCSR9 __REG(0x40000024) /* DMA Control / Status Register for Channel 9 */
+#define DCSR10 __REG(0x40000028) /* DMA Control / Status Register for Channel 10 */
+#define DCSR11 __REG(0x4000002c) /* DMA Control / Status Register for Channel 11 */
+#define DCSR12 __REG(0x40000030) /* DMA Control / Status Register for Channel 12 */
+#define DCSR13 __REG(0x40000034) /* DMA Control / Status Register for Channel 13 */
+#define DCSR14 __REG(0x40000038) /* DMA Control / Status Register for Channel 14 */
+#define DCSR15 __REG(0x4000003c) /* DMA Control / Status Register for Channel 15 */
+#define DCSR16 __REG(0x40000040) /* DMA Control / Status Register for Channel 16 */
+#define DCSR17 __REG(0x40000044) /* DMA Control / Status Register for Channel 17 */
+#define DCSR18 __REG(0x40000048) /* DMA Control / Status Register for Channel 18 */
+#define DCSR19 __REG(0x4000004c) /* DMA Control / Status Register for Channel 19 */
+#define DCSR20 __REG(0x40000050) /* DMA Control / Status Register for Channel 20 */
+#define DCSR21 __REG(0x40000054) /* DMA Control / Status Register for Channel 21 */
+#define DCSR22 __REG(0x40000058) /* DMA Control / Status Register for Channel 22 */
+#define DCSR23 __REG(0x4000005c) /* DMA Control / Status Register for Channel 23 */
+#define DCSR24 __REG(0x40000060) /* DMA Control / Status Register for Channel 24 */
+#define DCSR25 __REG(0x40000064) /* DMA Control / Status Register for Channel 25 */
+#define DCSR26 __REG(0x40000068) /* DMA Control / Status Register for Channel 26 */
+#define DCSR27 __REG(0x4000006c) /* DMA Control / Status Register for Channel 27 */
+#define DCSR28 __REG(0x40000070) /* DMA Control / Status Register for Channel 28 */
+#define DCSR29 __REG(0x40000074) /* DMA Control / Status Register for Channel 29 */
+#define DCSR30 __REG(0x40000078) /* DMA Control / Status Register for Channel 30 */
+#define DCSR31 __REG(0x4000007c) /* DMA Control / Status Register for Channel 31 */
+#define DCSR(x) __REG2(0x40000000, (x) << 2)
+#define DCSR_RUN (1 << 31) /* Run Bit (read / write) */
+#define DCSR_NODESC (1 << 30) /* No-Descriptor Fetch (read / write) */
+#define DCSR_STOPIRQEN (1 << 29) /* Stop Interrupt Enable (read / write) */
+#define DCSR_EORIRQEN (1 << 28) /* End of Receive Interrupt Enable (R/W) */
+#define DCSR_EORJMPEN (1 << 27) /* Jump to next descriptor on EOR */
+#define DCSR_EORSTOPEN (1 << 26) /* STOP on an EOR */
+#define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */
+#define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */
+#define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */
+#define DCSR_EORINTR (1 << 9) /* The end of Receive */
+#define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */
+#define DCSR_RASINTR (1 << 4) /* Request After Channel Stopped */
+#define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */
+#define DCSR_ENDINTR (1 << 2) /* End Interrupt (read / write) */
+#define DCSR_STARTINTR (1 << 1) /* Start Interrupt (read / write) */
+#define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt (read / write) */
+#define DDADR(x) __REG2(0x40000200, (x) << 4)
+//#define __REG_2(x) (*((volatile u32 *)io_p2v_2(x)))
+#define IRQ_NAND PXA_IRQ(45)
+#define CKEN_NAND 4 ///< NAND Flash Controller Clock Enable
+
+/* #define CONFIG_MTD_NAND_MONAHANS_DEBUG */
+#ifdef CONFIG_MTD_NAND_MONAHANS_DEBUG
+#define D1(x) do { \
+ printk(KERN_DEBUG "%s: ", __FUNCTION__); \
+ x; \
+ }while(0)
+
+#define DPRINTK(fmt,args...) printk(KERN_DEBUG fmt, ##args )
+#define PRINT_BUF(buf, num) print_buf(buf, num)
+#else
+#define D1(x)
+#define DPRINTK(fmt,args...)
+#define PRINT_BUF(buf, num)
+#endif
+
+/* DFC timing 0 register */
+#define DFC_TIMING_tRP 0
+#define DFC_TIMING_tRH 3
+#define DFC_TIMING_tWP 8
+#define DFC_TIMING_tWH 11
+#define DFC_TIMING_tCS 16
+#define DFC_TIMING_tCH 19
+
+/* DFC timing 1 register */
+#define DFC_TIMING_tAR 0
+#define DFC_TIMING_tWHR 4
+#define DFC_TIMING_tR 16
+
+/* max value for each timing setting in DFC */
+#define DFC_TIMING_MAX_tCH 7
+#define DFC_TIMING_MAX_tCS 7
+#define DFC_TIMING_MAX_tWH 7
+#define DFC_TIMING_MAX_tWP 7
+#define DFC_TIMING_MAX_tRH 7
+#define DFC_TIMING_MAX_tRP 7
+#define DFC_TIMING_MAX_tR 65535
+#define DFC_TIMING_MAX_tWHR 15
+#define DFC_TIMING_MAX_tAR 15
+
+/*
+ * The Data Flash Controller Flash timing structure
+ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
+ * user should use value at end of each row of following member
+ * bracketed.
+ */
+struct dfc_flash_timing {
+ uint32_t tCH; /* Enable signal hold time */
+ uint32_t tCS; /* Enable signal setup time */
+ uint32_t tWH; /* ND_nWE high duration */
+ uint32_t tWP; /* ND_nWE pulse time */
+ uint32_t tRH; /* ND_nRE high duration */
+ uint32_t tRP; /* ND_nRE pulse width */
+ uint32_t tR; /* ND_nWE high to ND_nRE low for read */
+ uint32_t tWHR;/* ND_nWE high to ND_nRE low delay for status read */
+ uint32_t tAR; /* ND_ALE low to ND_nRE low delay */
+};
+
+/* DFC command type */
+enum {
+ DFC_CMD_READ = 0x00000000,
+ DFC_CMD_PROGRAM = 0x00200000,
+ DFC_CMD_ERASE = 0x00400000,
+ DFC_CMD_READ_ID = 0x00600000,
+ DFC_CMD_STATUS_READ = 0x00800000,
+ DFC_CMD_RESET = 0x00a00000
+};
+
+/*
+ * The Data Flash Controller Flash specification structure
+ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
+ * user should use value at end of each row of following member
+ * bracketed.
+ */
+struct dfc_flash_info {
+ struct dfc_flash_timing timing; /* NAND Flash timing */
+
+ int enable_arbiter;/* Data flash bus arbiter enable (ND_ARB_EN) */
+ uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
+ uint32_t row_addr_start;/* Row address start position (RA_START) */
+ uint32_t read_id_bytes; /* returned ID bytes(RD_ID_CNT) */
+ uint32_t dfc_mode; /* NAND, CARBONDALE, PIXLEY... (ND_MODE) */
+ uint32_t ncsx; /* Chip select don't care bit (NCSX) */
+ uint32_t page_size; /* Page size in bytes (PAGE_SZ) */
+ uint32_t oob_size; /* OOB size */
+ uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
+ uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
+ uint32_t num_blocks; /* Number of physical blocks in Flash */
+ uint32_t chip_id;
+
+ /* command codes */
+ uint32_t read1; /* Read */
+ uint32_t read2; /* unused, DFC don't support yet */
+ uint32_t program; /* two cycle command */
+ uint32_t read_status;
+ uint32_t read_id;
+ uint32_t erase; /* two cycle command */
+ uint32_t reset;
+ uint32_t lock; /* lock whole flash */
+ uint32_t unlock; /* two cycle command, supporting partial unlock */
+ uint32_t lock_status; /* read block lock status */
+
+ /* addr2ndcb1 - encode address cycles into register NDCB1 */
+ /* ndbbr2addr - convert register NDBBR to bad block address */
+ int (*addr2ndcb1)(uint16_t cmd, uint32_t addr, uint32_t *p);
+ int (*ndbbr2addr)(uint16_t cmd, uint32_t ndbbr,uint32_t *p);
+};
+
+enum {
+ DFC_FLASH_NULL = 0 ,
+ DFC_FLASH_Samsung_512Mb_X_16 = 1,
+ DFC_FLASH_Micron_1Gb_X_8 = 2,
+ DFC_FLASH_Micron_1Gb_X_16 = 3,
+ DFC_FLASH_STM_1Gb_X_16 = 4,
+ DFC_FLASH_STM_2Gb_X_16 = 5,
+ DFC_FLASH_END,
+};
+
+static int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info);
+
+#define DFC_NDCR 0
+#define DFC_NDTR0CS0 1
+#define DFC_NDTR1CS0 3
+#define DFC_NDSR 5
+#define DFC_NDPCR 6
+#define DFC_NDBDR0 7
+#define DFC_NDBDR1 8
+#define DFC_NDDB 16
+#define DFC_NDCB0 18
+#define DFC_NDCB1 19
+#define DFC_NDCB2 20
+
+/* The Data Flash Controller Mode structure */
+struct dfc_mode {
+ int enable_dma; /* DMA, or nonDMA mode */
+ int enable_ecc; /* ECC on/off */
+ int enable_spare; /* Spare enable */
+ int chip_select; /* CS0 or CS1 */
+};
+
+/* The Data Flash Controller Context structure */
+struct dfc_context {
+ unsigned char __iomem *membase; /* DFC register base */
+ struct dfc_mode *dfc_mode; /* DFC mode */
+ int data_dma_ch; /* Data DMA channel number */
+ int cmd_dma_ch; /* CMD DMA channel number */
+ struct dfc_flash_info *flash_info; /* Flash Spec */
+ struct mtd_info *mtd;
+};
+
+#define NDCB0_DMA_ADDR 0x43100048
+#define NDDB_DMA_ADDR 0x43100040
+
+#define NDSR_MASK 0xFFF
+
+/* The following data is a rough evaluation */
+
+/* microsecond, for readID/readStatus/reset */
+#define NAND_OTHER_TIMEOUT 10
+/* microsecond, for readID/readStatus/reset */
+#define NAND_CMD_TIMEOUT 10
+
+#define BBT_BLOCK_BAD 0x03
+#define BBT_BLOCK_GOOD 0x00
+#define BBT_BLOCK_REV1 0x01
+#define BBT_BLOCK_REV2 0x02
+
+#define BUFLEN (2048 + 64)
+
+/*
+ * DFC data size enumeration transfered from/to controller,
+ * including padding (zero)to be a multiple of 32.
+ */
+enum {
+ DFC_DATA_SIZE_STATUS = 8, /* ReadStatus/ReadBlockLockStatus */
+ DFC_DATA_SIZE_ID = 7, /* ReadID */
+
+ DFC_DATA_SIZE_32 = 32,
+ DFC_DATA_SIZE_512 = 512, /* R/W disabling spare area */
+ DFC_DATA_SIZE_520 = 520, /* Spare=1, ECC=1 */
+ DFC_DATA_SIZE_528 = 528, /* Spare=1, ECC=0 */
+ DFC_DATA_SIZE_544 = 544, /* R/W enabling spare area.(DMA mode)*/
+
+ DFC_DATA_SIZE_64 = 64,
+ DFC_DATA_SIZE_2048 = 2048, /* R/W disabling spare area */
+ DFC_DATA_SIZE_2088 = 2088, /* R/W enabling spare area with ecc */
+ DFC_DATA_SIZE_2112 = 2112, /* R/W enabling spare area without ecc*/
+ DFC_DATA_SIZE_2096 = 2096, /* R/W enabling spare area */
+ DFC_DATA_SIZE_UNUSED = 0xFFFF
+};
+
+/* DFC padding size enumeration transfered from/to controller */
+enum {
+ /*
+ * ReadStatus/ReadBlockLockStatus/ReadID/
+ * Read/Program disabling spare area(Both 512 and 2048)
+ * Read/Program enabling spare area, disabling ECC
+ */
+ DFC_PADDING_SIZE_0 = 0,
+
+ /* Read/program with SPARE_EN=1, ECC_EN=0, pgSize=512 */
+ DFC_PADDING_SIZE_16 = 16,
+ /* for read/program with SPARE_EN=1, ECC_EN=1, pgSize=512 and 2048 */
+ DFC_PADDING_SIZE_24 = 24,
+ DFC_PADDING_SIZE_UNUSED = 0xFFFF
+};
+
+static unsigned int flash_config = DFC_FLASH_NULL;
+
+void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t);
+void dfc_set_dma(struct dfc_context *context);
+void dfc_set_ecc(struct dfc_context *context);
+void dfc_set_spare(struct dfc_context *context);
+
+int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
+ int *data_size, int *padding);
+
+static int dfc_wait_event(struct dfc_context *context, uint32_t event,
+ uint32_t *event_out, uint32_t timeout, int enable_int);
+
+int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
+ uint32_t addr, int num_pages);
+
+void dfc_stop(struct dfc_context *context);
+void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+ int nbytes, int data_size);
+void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+ int nbytes, int data_size);
+
+void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
+void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
+
+void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr);
+
+void dfc_clear_int(struct dfc_context *context, uint32_t int_mask);
+void dfc_enable_int(struct dfc_context *context, uint32_t int_mask);
+void dfc_disable_int(struct dfc_context *context, uint32_t int_mask);
+
+/* high level primitives */
+int dfc_init(struct dfc_context *context, int type);
+int dfc_init_no_gpio(struct dfc_context *context, int type);
+
+int dfc_reset_flash(struct dfc_context *context);
+
+int dfc_setup_cmd_dma(struct dfc_context *context,
+ uint16_t cmd, uint32_t addr, int num_pages,
+ uint32_t *buf, uint32_t buf_phys,
+ uint32_t next_desc_phys, uint32_t dma_int_en,
+ struct pxa_dma_desc *dma_desc);
+
+int dfc_setup_data_dma(struct dfc_context *context,
+ uint16_t cmd, uint32_t buf_phys,
+ uint32_t next_desc_phys, uint32_t dma_int_en,
+ struct pxa_dma_desc *dma_desc);
+
+void dfc_start_cmd_dma(struct dfc_context *context,
+ struct pxa_dma_desc *dma_desc);
+void dfc_start_data_dma(struct dfc_context *context,
+ struct pxa_dma_desc *dma_desc);
+static int monahans_df_dev_ready(struct mtd_info *mtd);
+
+#ifdef CONFIG_DVFM
+static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info);
+static struct mhn_fv_notifier dvfm_notifier = {
+ .name = "monahans-nand-flash",
+ .priority = 0,
+ .notifier_call = mhn_nand_dvfm_notifier,
+};
+#endif
+
+static unsigned short search_rel_block(int block, struct mtd_info *mtd);
+
+/*****************************************************************************
+ * The DFC registers read/write routines
+ *****************************************************************************/
+static inline void dfc_write(struct dfc_context *context, int offset,
+ unsigned long value)
+{
+ offset <<= 2;
+ writel(value, context->membase + offset);
+}
+
+static inline unsigned int dfc_read(struct dfc_context *context, int offset)
+{
+ offset <<= 2;
+ return __raw_readl(context->membase + offset);
+}
+
+/****************************************************************************
+ * Flash Information
+ ***************************************************************************/
+
+static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info samsung512MbX16 =
+{
+ .timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 0, /* tCS, Enable signal setup time */
+ .tWH = 20, /* tWH, ND_nWE high duration */
+ .tWP = 40, /* tWP, ND_nWE pulse time */
+ .tRH = 30, /* tRH, ND_nRE high duration */
+ .tRP = 40, /* tRP, ND_nRE pulse width */
+ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+ .tR = 11123,
+ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
+ .tWHR = 110,
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+ },
+ .enable_arbiter = 1, /* Data flash bus arbiter enable */
+ .page_per_block = 32, /* Pages per block */
+ .row_addr_start = 0, /* Second cycle start, Row address start position */
+ .read_id_bytes = 2, /* 2 bytes, returned ID bytes */
+ .dfc_mode = 0, /* NAND mode */
+ .ncsx = 0,
+ .page_size = 512, /* Page size in bytes */
+ .oob_size = 16, /* OOB size in bytes */
+ .flash_width = 16, /* Width of Flash memory */
+ .dfc_width = 16, /* Width of flash controller */
+ .num_blocks = 4096, /* Number of physical blocks in Flash */
+ .chip_id = 0x46ec,
+
+ /* command codes */
+ .read1 = 0x0000, /* Read */
+ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
+ .lock_status = 0x007A, /* Read block lock status */
+ .addr2ndcb1 = Samsung512MbX16Addr2NDCB1,
+ .ndbbr2addr = Samsung512MbX16NDBBR2Addr,
+};
+
+static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+ uint32_t ndcb1 = 0;
+
+ if (addr >= 0x4000000) return -EINVAL;
+
+ if (cmd == samsung512MbX16.read1 || cmd == samsung512MbX16.program) {
+ ndcb1 = (addr & 0xFF) | ((addr >> 1) & 0x01FFFF00);
+ } else if (cmd == samsung512MbX16.erase) {
+ ndcb1 = ((addr >> 9) & 0x00FFFFFF);
+ }
+
+ *p = ndcb1;
+ return 0;
+
+}
+
+static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+ *p = ndbbr << 9;
+ return 0;
+}
+
+static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info micron1GbX8 =
+{
+ .timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 25, /* tCS, Enable signal setup time */
+ .tWH = 15, /* tWH, ND_nWE high duration */
+ .tWP = 25, /* tWP, ND_nWE pulse time */
+ .tRH = 15, /* tRH, ND_nRE high duration */
+ .tRP = 25, /* tRP, ND_nRE pulse width */
+ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+ .tR = 25000,
+ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
+ .tWHR = 60,
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+ },
+ .enable_arbiter = 1, /* Data flash bus arbiter enable */
+ .page_per_block = 64, /* Pages per block */
+ .row_addr_start = 1, /* Second cycle start, Row address start position */
+ .read_id_bytes = 4, /* Returned ID bytes */
+ .dfc_mode = 0, /* NAND mode */
+ .ncsx = 0,
+ .page_size = 2048, /* Page size in bytes */
+ .oob_size = 64, /* OOB size in bytes */
+ .flash_width = 8, /* Width of Flash memory */
+ .dfc_width = 8, /* Width of flash controller */
+ .num_blocks = 1024, /* Number of physical blocks in Flash */
+ .chip_id = 0xa12c,
+ /* command codes */
+ .read1 = 0x3000, /* Read */
+ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
+ .lock_status = 0x007A, /* Read block lock status */
+ .addr2ndcb1 = Micron1GbX8Addr2NDCB1,
+ .ndbbr2addr = Micron1GbX8NDBBR2Addr,
+};
+
+static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+ uint32_t ndcb1 = 0;
+ uint32_t page;
+
+ if (addr >= 0x8000000)
+ return -EINVAL;
+ page = addr / micron1GbX8.page_size;
+ addr = (page / micron1GbX8.page_per_block) << 18 |
+ (page % micron1GbX8.page_per_block) << 12;
+
+ if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
+ ndcb1 = (addr & 0xFFF) | ((addr << 4) & 0xFFFF0000);
+ }
+ else if (cmd == micron1GbX8.erase) {
+ ndcb1 = ((addr >> 18) << 6) & 0xFFFF;
+ }
+
+ *p = ndcb1;
+ return 0;
+}
+
+static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+ if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
+ *p = ((ndbbr & 0xF) << 8) | ((ndbbr >> 8) << 16);
+ }
+ else if (cmd == micron1GbX8.erase) {
+ *p = (ndbbr >> 6) << 18;
+ }
+
+
+ return 0;
+}
+
+
+static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info micron1GbX16 =
+{
+ .timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 25, /* tCS, Enable signal setup time */
+ .tWH = 15, /* tWH, ND_nWE high duration */
+ .tWP = 25, /* tWP, ND_nWE pulse time */
+ .tRH = 15, /* tRH, ND_nRE high duration */
+ .tRP = 25, /* tRP, ND_nRE pulse width */
+ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+ .tR = 25000,
+ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
+ .tWHR = 60,
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+ },
+ .enable_arbiter = 1, /* Data flash bus arbiter enable */
+ .page_per_block = 64, /* Pages per block */
+ .row_addr_start = 1, /* Second cycle start, Row address start position */
+ .read_id_bytes = 4, /* Returned ID bytes */
+ .dfc_mode = 0, /* NAND mode */
+ .ncsx = 0,
+ .page_size = 2048, /* Page size in bytes */
+ .oob_size = 64, /* OOB size in bytes */
+ .flash_width = 16, /* Width of Flash memory */
+ .dfc_width = 16, /* Width of flash controller */
+ .num_blocks = 1024, /* Number of physical blocks in Flash */
+ .chip_id = 0xb12c,
+
+ /* command codes */
+ .read1 = 0x3000, /* Read */
+ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
+ .lock_status = 0x007A, /* Read block lock status */
+ .addr2ndcb1 = Micron1GbX16Addr2NDCB1,
+ .ndbbr2addr = Micron1GbX16NDBBR2Addr,
+};
+
+static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+ uint32_t ndcb1 = 0;
+ uint32_t page;
+
+ if (addr >= 0x8000000)
+ return -EINVAL;
+ page = addr / micron1GbX16.page_size;
+ addr = (page / micron1GbX16.page_per_block) << 17 |
+ (page % micron1GbX16.page_per_block) << 11;
+
+ if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
+ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+ }
+ else if (cmd == micron1GbX16.erase) {
+ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+ }
+ *p = ndcb1;
+ return 0;
+}
+
+static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+ if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
+ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+ }
+ else if (cmd == micron1GbX16.erase) {
+ *p = (ndbbr >> 6) << 17;
+ }
+
+ return 0;
+}
+
+static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info stm1GbX16 =
+{
+ .timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 10, /* tCS, Enable signal setup time */
+ .tWH = 20, /* tWH, ND_nWE high duration */
+ .tWP = 25, /* tWP, ND_nWE pulse time */
+ .tRH = 20, /* tRH, ND_nRE high duration */
+ .tRP = 25, /* tRP, ND_nRE pulse width */
+ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+ .tR = 25000,
+ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
+ .tWHR = 60,
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+ },
+ .enable_arbiter = 1, /* Data flash bus arbiter enable */
+ .page_per_block = 64, /* Pages per block */
+ .row_addr_start = 1, /* Second cycle start, Row address start position */
+ .read_id_bytes = 4, /* Returned ID bytes */
+ .dfc_mode = 0, /* NAND mode */
+ .ncsx = 0,
+ .page_size = 2048, /* Page size in bytes */
+ .oob_size = 64, /* OOB size in bytes */
+ .flash_width = 16, /* Width of Flash memory */
+ .dfc_width = 16, /* Width of flash controller */
+ .num_blocks = 1024, /* Number of physical blocks in Flash */
+ .chip_id = 0xb120,
+
+ /* command codes */
+ .read1 = 0x3000, /* Read */
+ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
+ .lock_status = 0x007A, /* Read block lock status */
+ .addr2ndcb1 = STM1GbX16Addr2NDCB1,
+ .ndbbr2addr = STM1GbX16NDBBR2Addr,
+};
+
+static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+ uint32_t ndcb1 = 0;
+ uint32_t page;
+
+ if (addr >= 0x8000000)
+ return -EINVAL;
+ page = addr / stm1GbX16.page_size;
+ addr = (page / stm1GbX16.page_per_block) << 17 |
+ (page % stm1GbX16.page_per_block) << 11;
+
+ if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
+ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+ }
+ else if (cmd == stm1GbX16.erase) {
+ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+ }
+ *p = ndcb1;
+ return 0;
+}
+
+static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+ if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
+ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+ }
+ else if (cmd == stm1GbX16.erase) {
+ *p = (ndbbr >> 6) << 17;
+ }
+
+ return 0;
+}
+
+static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info stm2GbX16 =
+{
+ .timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 10, /* tCS, Enable signal setup time */
+ .tWH = 20, /* tWH, ND_nWE high duration */
+ .tWP = 25, /* tWP, ND_nWE pulse time */
+ .tRH = 20, /* tRH, ND_nRE high duration */
+ .tRP = 25, /* tRP, ND_nRE pulse width */
+ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+ .tR = 25000,
+ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
+ .tWHR = 60,
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+ },
+ .enable_arbiter = 1, /* Data flash bus arbiter enable */
+ .page_per_block = 64, /* Pages per block */
+ .row_addr_start = 1, /* Second cycle start, Row address start position */
+ .read_id_bytes = 4, /* Returned ID bytes */
+ .dfc_mode = 0, /* NAND mode */
+ .ncsx = 0,
+ .page_size = 2048, /* Page size in bytes */
+ .oob_size = 64, /* OOB size in bytes */
+ .flash_width = 16, /* Width of Flash memory */
+ .dfc_width = 16, /* Width of flash controller */
+ .num_blocks = 2048, /* Number of physical blocks in Flash */
+ .chip_id = 0xca20,
+
+ /* command codes */
+ .read1 = 0x3000, /* Read */
+ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
+ .lock_status = 0x007A, /* Read block lock status */
+ .addr2ndcb1 = STM2GbX16Addr2NDCB1,
+ .ndbbr2addr = STM2GbX16NDBBR2Addr,
+};
+
+static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+ uint32_t ndcb1 = 0;
+ uint32_t page;
+
+ if (addr >= 0x8000000)
+ return -EINVAL;
+ page = addr / stm2GbX16.page_size;
+ addr = (page / stm2GbX16.page_per_block) << 17 |
+ (page % stm2GbX16.page_per_block) << 11;
+
+ if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
+ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+ }
+ else if (cmd == stm2GbX16.erase) {
+ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+ }
+ *p = ndcb1;
+ return 0;
+}
+
+static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+ if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
+ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+ }
+ else if (cmd == stm2GbX16.erase) {
+ *p = (ndbbr >> 6) << 17;
+ }
+
+ return 0;
+}
+
+static struct {
+ int type;
+ struct dfc_flash_info *flash_info;
+} type_info[] = {
+ { DFC_FLASH_Samsung_512Mb_X_16, &samsung512MbX16},
+ { DFC_FLASH_Micron_1Gb_X_8, &micron1GbX8},
+ { DFC_FLASH_Micron_1Gb_X_16, &micron1GbX16},
+ { DFC_FLASH_STM_1Gb_X_16, &stm1GbX16},
+ { DFC_FLASH_STM_2Gb_X_16, &stm2GbX16},
+ { DFC_FLASH_NULL, NULL},
+};
+
+int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info)
+{
+ uint32_t i = 0;
+
+ while(type_info[i].type != DFC_FLASH_NULL) {
+ if (type_info[i].type == type) {
+ *flash_info = type_info[i].flash_info;
+ return 0;
+ }
+ i++;
+ }
+ *flash_info = NULL;
+ return -EINVAL;
+}
+
+/******************************************************************************
+ dfc_set_timing
+
+ Description:
+ This function sets flash timing property in DFC timing register
+ according to input timing value embodied in context structure.
+ It is called once during the hardware initialization.
+ Input Parameters:
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+//#if defined(CONFIG_CPU_MONAHANS_L) || defined(CONFIG_CPU_MONAHANS_LV)
+#define DFC_CLOCK 208
+//#else
+//#define DFC_CLOCK 104
+//#endif
+#define CLOCK_NS DFC_CLOCK/1000
+
+void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t)
+{
+ struct dfc_flash_timing timing = *t;
+
+ uint32_t r0 = 0;
+ uint32_t r1 = 0;
+
+ /*
+ * num of clock cycles = time (ns) / one clock sycle (ns) + 1
+ * - integer division will truncate the result, so add a 1 in all cases
+ * - subtract the extra 1 cycle added to all register timing values
+ */
+ timing.tCH = min(((int) (timing.tCH * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tCH);
+ timing.tCS = min(((int) (timing.tCS * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tCS);
+ timing.tWH = min(((int) (timing.tWH * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tWH);
+ timing.tWP = min(((int) (timing.tWP * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tWP);
+ timing.tRH = min(((int) (timing.tRH * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tRH);
+ timing.tRP = min(((int) (timing.tRP * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tRP);
+
+ r0 = (timing.tCH << DFC_TIMING_tCH) |
+ (timing.tCS << DFC_TIMING_tCS) |
+ (timing.tWH << DFC_TIMING_tWH) |
+ (timing.tWP << DFC_TIMING_tWP) |
+ (timing.tRH << DFC_TIMING_tRH) |
+ (timing.tRP << DFC_TIMING_tRP);
+
+ dfc_write(context, DFC_NDTR0CS0, r0);
+
+ timing.tR = min(((int) (timing.tR * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tR);
+ timing.tWHR = min(((int) (timing.tWHR * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tWHR);
+ timing.tAR = min(((int) (timing.tAR * CLOCK_NS) + 1),
+ DFC_TIMING_MAX_tAR);
+
+ r1 = (timing.tR << DFC_TIMING_tR) |
+ (timing.tWHR << DFC_TIMING_tWHR) |
+ (timing.tAR << DFC_TIMING_tAR);
+
+ dfc_write(context, DFC_NDTR1CS0, r1);
+ return;
+}
+
+/******************************************************************************
+ dfc_set_dma
+
+ Description:
+ Enables or Disables DMA in line with setting in DFC mode of context
+ structure. DMA mode of DFC. Performs a read-modify-write operation that
+ only changes the driven DMA_EN bit field In DMA mode, all commands and
+ data are transferred by DMA. DMA can be enable/disable on the fly.
+ Input Parameters:
+ context -Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void
+dfc_set_dma(struct dfc_context* context)
+{
+ uint32_t ndcr;
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ if (context->dfc_mode->enable_dma)
+ ndcr |= NDCR_DMA_EN;
+ else
+ ndcr &= ~NDCR_DMA_EN;
+
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ /* Read again to make sure write work */
+ ndcr = dfc_read(context, DFC_NDCR);
+ return;
+}
+
+
+/******************************************************************************
+ dfc_set_ecc
+
+ Description:
+ This function enables or disables hardware ECC capability of DFC in line
+ with setting in DFC mode of context structure.
+ Input Parameters:
+ context -Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void
+dfc_set_ecc(struct dfc_context* context)
+{
+ uint32_t ndcr;
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ if (context->dfc_mode->enable_ecc)
+ ndcr |= NDCR_ECC_EN;
+ else
+ ndcr &= ~NDCR_ECC_EN;
+
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ /* Read again to make sure write work */
+ ndcr = dfc_read(context, DFC_NDCR);
+ return;
+}
+
+/******************************************************************************
+ dfc_set_spare
+
+ Description:
+ This function enables or disables accesses to spare area of NAND Flash
+ through DFC in line with setting in DFC mode of context structure.
+ Input Parameters:
+ context -Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void
+dfc_set_spare(struct dfc_context* context)
+{
+ uint32_t ndcr;
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ if (context->dfc_mode->enable_spare)
+ ndcr |= NDCR_SPARE_EN;
+ else
+ ndcr &= ~NDCR_SPARE_EN;
+
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ /* Read again to make sure write work */
+ ndcr = dfc_read(context, DFC_NDCR);
+ return;
+}
+
+static unsigned int get_delta (unsigned int start)
+{
+ unsigned int stop = OSCR;
+ return (stop - start);
+}
+
+static int dfc_wait_event(struct dfc_context *context, uint32_t event,
+ uint32_t *event_out, uint32_t timeout, int enable_int)
+{
+ uint32_t ndsr;
+ uint32_t to = 3 * timeout; /* 3 ticks ~ 1us */
+ int status;
+ int start = OSCR;
+
+ if (enable_int)
+ dfc_enable_int(context, event);
+
+ while (1) {
+ ndsr = dfc_read(context, DFC_NDSR);
+ ndsr &= NDSR_MASK;
+ if (ndsr & event) {
+ /* event happened */
+ *event_out = ndsr & event;
+ dfc_clear_int(context, *event_out);
+ status = 0;
+ break;
+ } else if (get_delta(start) > to) {
+ status = -ETIME;
+ break;
+ }
+ }
+
+ if (enable_int)
+ dfc_disable_int(context, event);
+ return status;
+}
+
+/******************************************************************************
+ dfc_get_pattern
+
+ Description:
+ This function is used to retrieve buffer size setting for a transaction
+ based on cmd.
+ Input Parameters:
+ context - Pointer to DFC context structure
+ cmd
+ Specifies type of command to be sent to NAND flash .The LSB of this
+ parameter defines the first command code for 2-cycles command. The
+ MSB defines the second command code for 2-cycles command. If MSB is
+ set to zero, this indicates that one cycle command
+ Output Parameters:
+ data_size
+ It is used to retrieve length of data transferred to/from DFC,
+ which includes padding bytes
+ padding
+ It is used to retrieve how many padding bytes there should be
+ in buffer of data_size.
+ Returns:
+ 0
+ If size setting is returned successfully
+ -EINVAL
+ If page size specified in flash spec of context structure is not 512 or
+ 2048;If specified command index is not read1/program/erase/reset/readID/
+ readStatus.
+*******************************************************************************/
+int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
+ int *data_size, int *padding)
+{
+ struct dfc_mode* dfc_mode = context->dfc_mode;
+ struct dfc_flash_info * flash_info = context->flash_info;
+ uint32_t page_size = context->flash_info->page_size; /* 512 or 2048 */
+
+ if (cmd == flash_info->read1 ||
+ cmd == flash_info->program) {
+ if (512 == page_size) {
+ /* add for DMA */
+ if (dfc_mode->enable_dma) {
+ *data_size = DFC_DATA_SIZE_544;
+ if (dfc_mode->enable_ecc)
+ *padding = DFC_PADDING_SIZE_24;
+ else
+ *padding = DFC_PADDING_SIZE_16;
+ } else if (!dfc_mode->enable_spare) {
+ *data_size = DFC_DATA_SIZE_512;
+ *padding = DFC_PADDING_SIZE_0;
+ } else {
+
+ if (dfc_mode->enable_ecc)
+ *data_size = DFC_DATA_SIZE_520;
+ else
+ *data_size = DFC_DATA_SIZE_528;
+
+ *padding = DFC_PADDING_SIZE_0;
+ }
+ } else if (2048 == page_size) {
+ /* add for DMA */
+ if (dfc_mode->enable_dma) {
+ *data_size = DFC_DATA_SIZE_2112;
+ if (dfc_mode->enable_ecc)
+ *padding = DFC_PADDING_SIZE_24;
+ else
+ *padding = DFC_PADDING_SIZE_0;
+ } else if (!dfc_mode->enable_spare) {
+ *data_size = DFC_DATA_SIZE_2048;
+ *padding = DFC_PADDING_SIZE_0;
+ } else {
+
+ if (dfc_mode->enable_ecc)
+ *data_size = DFC_DATA_SIZE_2088;
+ else
+ *data_size = DFC_DATA_SIZE_2112;
+
+ *padding = DFC_PADDING_SIZE_0;
+ }
+ } else /* if the page_size is neither 512 or 2048 */
+ return -EINVAL;
+ } else if (cmd == flash_info->read_id) {
+ *data_size = DFC_DATA_SIZE_ID;
+ *padding = DFC_PADDING_SIZE_0;
+ } else if(cmd == flash_info->read_status) {
+ *data_size = DFC_DATA_SIZE_STATUS;
+ *padding = DFC_PADDING_SIZE_0;
+ } else if (cmd == flash_info->erase || cmd == flash_info->reset) {
+ *data_size = DFC_DATA_SIZE_UNUSED;
+ *padding = DFC_PADDING_SIZE_UNUSED;
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+
+/******************************************************************************
+ dfc_send_cmd
+
+ Description:
+ This function configures DFC to send command through DFC to NAND flash
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ cmd
+ Specifies type of command to be sent to NAND flash .The LSB of this
+ parameter defines the first command code for 2-cycles command. The
+ MSB defines the second command code for 2-cycles command. If MSB is
+ set to zero, this indicates that one cycle command
+ addr
+ Address sent out to the flash device withthis command. For page read/
+ program commands , 4-cycles address is sent. For erase command only
+ 3-cycles address is sent. If it is equal to 0xFFFFFFFF, the address
+ should not be used.
+ num_pages
+ It specifies the number of pages of data to be transferred for
+ a program or read commands. Unused for any other commands than
+ read/program.
+
+ Output Parameters:
+ None
+ Returns:
+ 0
+ If size setting is returned successfully
+ -EINVAL
+ If specified command index is not read1/program/erase/reset/readID/
+ readStatus.
+*******************************************************************************/
+int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
+ uint32_t addr, int num_pages)
+{
+ struct dfc_flash_info *flash_info = context->flash_info;
+ struct dfc_mode *dfc_mode = context->dfc_mode;
+ uint8_t cmd2;
+ uint32_t event_out;
+ uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
+ int status;
+
+ /* It is a must to set ND_RUN firstly, then write command buffer
+ * If conversely,it does not work
+ */
+ dfc_write(context, DFC_NDSR, NDSR_MASK);
+
+ /* Set ND_RUN */
+ ndcr = dfc_read(context, DFC_NDCR);
+ dfc_write(context, DFC_NDCR, (ndcr | NDCR_ND_RUN));
+
+ // Wait for write command request
+ status = dfc_wait_event(context, NDSR_WRCMDREQ,
+ &event_out, NAND_CMD_TIMEOUT, 0);
+
+ if (status) /* Timeout */
+ return status;
+
+ cmd2 = (cmd>>8) & 0xFF;
+ ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
+
+ if (cmd == flash_info->read1) {
+ if (0xFFFFFFFF != addr) {
+ ndcb0 |= NDCB0_ADDR_CYC(4);
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ ndcb2 = (num_pages - 1) << 8;
+ }
+ } else if (cmd == flash_info->program) {
+ ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+ ndcb0 |= NDCB0_ADDR_CYC(4);
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ ndcb2 = (num_pages-1) << 8;
+ } else if (cmd == flash_info->erase) {
+ ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
+ ndcb0 |= NDCB0_ADDR_CYC(3);
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ } else if (cmd == flash_info->read_id) {
+ ndcb0 |= NDCB0_CMD_TYPE(3);
+ } else if(cmd == flash_info->read_status) {
+ ndcb0 |= NDCB0_CMD_TYPE(4);
+ } else if(cmd == flash_info->reset) {
+ ndcb0 |= NDCB0_CMD_TYPE(5);
+ } else if (cmd == flash_info->lock) {
+ ndcb0 |= NDCB0_CMD_TYPE(5);
+ } else
+ return -EINVAL;
+
+ /* Write to DFC command register */
+ dfc_write(context, DFC_NDCB0, ndcb0);
+ dfc_write(context, DFC_NDCB0, ndcb1);
+ dfc_write(context, DFC_NDCB0, ndcb2);
+
+ return 0;
+}
+
+/******************************************************************************
+ dfc_stop
+
+ Description:
+ This function clears ND_RUN bit of NDCR.
+ Input Parameters:
+ context--Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_stop(struct dfc_context *context)
+{
+ unsigned int ndcr;
+ ndcr = dfc_read(context, DFC_NDCR);
+ dfc_write(context, DFC_NDCR, (ndcr & ~NDCR_ND_RUN));
+ ndcr = dfc_read(context, DFC_NDCR);
+
+ return;
+}
+
+int dfc_setup_cmd_dma(struct dfc_context *context,
+ uint16_t cmd, uint32_t addr, int num_pages,
+ uint32_t *buf, uint32_t buf_phys,
+ uint32_t next_desc_phys, uint32_t dma_int_en,
+ struct pxa_dma_desc *dma_desc)
+{
+ struct dfc_flash_info *flash_info = context->flash_info;
+ struct dfc_mode *dfc_mode = context->dfc_mode;
+ uint8_t cmd2;
+ uint32_t event_out;
+ uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
+ int status;
+
+ /*
+ * It is a must to set ND_RUN firstly, then write command buffer
+ * If conversely,it does not work
+ */
+ dfc_write(context, DFC_NDSR, NDSR_MASK);
+
+ /* Set ND_RUN */
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr |= NDCR_ND_RUN;
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ /* Wait for write command request */
+ status = dfc_wait_event(context, NDSR_WRCMDREQ,
+ &event_out, NAND_CMD_TIMEOUT, 0);
+
+ if (status)
+ return status; /* Timeout */
+
+ cmd2 = (cmd>>8) & 0xFF;
+ ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
+
+ if (cmd == flash_info->read1) {
+ if (0xFFFFFFFF != addr) {
+ ndcb0 |= NDCB0_ADDR_CYC(4);
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ ndcb2 = (num_pages-1) << 8;
+ }
+ } else if (cmd == flash_info->program) {
+ ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+ ndcb0 |= NDCB0_ADDR_CYC(4);
+
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ ndcb2 = (num_pages-1) << 8;
+ } else if (cmd == flash_info->erase) {
+ ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
+ ndcb0 |= NDCB0_ADDR_CYC(3);
+
+ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+ if (status)
+ return status;
+ } else if (cmd == flash_info->read_id) {
+ ndcb0 |= NDCB0_CMD_TYPE(3);
+ } else if (cmd == flash_info->read_status) {
+ ndcb0 |= NDCB0_CMD_TYPE(4);
+ } else if (cmd == flash_info->reset) {
+ ndcb0 |= NDCB0_CMD_TYPE(5);
+ } else if (cmd == flash_info->lock) {
+ ndcb0 |= NDCB0_CMD_TYPE(5);
+ } else
+ return -EINVAL;
+
+ *((uint32_t *)buf) = ndcb0;
+ *((uint32_t *)buf + 1) = ndcb1;
+ *((uint32_t *)buf + 2) = ndcb2;
+
+ dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
+
+ dma_desc->ddadr = next_desc_phys;
+ dma_desc->dsadr = buf_phys;
+ dma_desc->dtadr = NDCB0_DMA_ADDR;
+ dma_desc->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
+ DCMD_WIDTH4 | DCMD_BURST16 | 12;
+ return 0;
+}
+
+int dfc_setup_data_dma(struct dfc_context* context,
+ uint16_t cmd, uint32_t buf_phys,
+ uint32_t next_desc_phys, uint32_t dma_int_en,
+ struct pxa_dma_desc* dma_desc)
+{
+ struct dfc_flash_info * flash_info = context->flash_info;
+ int data_size, padding;
+
+ dfc_get_pattern(context, cmd, &data_size, &padding);
+
+ dma_desc->ddadr = next_desc_phys;
+ dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
+
+ if (cmd == flash_info->program) {
+
+ dma_desc->dsadr = buf_phys;
+ dma_desc->dtadr = NDDB_DMA_ADDR;
+ dma_desc->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
+ DCMD_WIDTH4 | DCMD_BURST32 | data_size;
+
+ } else if (cmd == flash_info->read1 || cmd == flash_info->read_id ||
+ cmd == flash_info->read_status) {
+
+ dma_desc->dsadr = NDDB_DMA_ADDR;
+ dma_desc->dtadr = buf_phys;
+ dma_desc->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | dma_int_en |
+ DCMD_WIDTH4 | DCMD_BURST32 | data_size;
+ }
+ else
+ return -EINVAL;
+ return 0;
+}
+
+void dfc_start_cmd_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
+{
+ DRCMR99 = DRCMR_MAPVLD | context->cmd_dma_ch; /* NAND CMD DRCMR */
+ DDADR(context->cmd_dma_ch) = (uint32_t)dma_desc;
+ DCSR(context->cmd_dma_ch) |= DCSR_RUN;
+}
+
+void dfc_start_data_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
+{
+ DRCMR97 = DRCMR_MAPVLD | context->data_dma_ch;
+ DDADR(context->data_dma_ch) = (uint32_t)dma_desc;
+ DCSR(context->data_dma_ch) |= DCSR_RUN;
+}
+
+/******************************************************************************
+ dfc_read_fifo_partial
+
+ Description:
+ This function reads data from data buffer of DFC.Bytes can be any less than
+ or equal to data_size, the left is ignored by ReadFIFO though they will be
+ read from NDDB to clear data buffer.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ nbytes
+ Indicating how much data should be read into buffer.
+ data_size
+ Specifing length of data transferred to/from DFC, which includes
+ padding bytes
+ Output Parameters:
+ pBuffer
+ Pointer to the data buffer where data should be placed.
+ Returns:
+ None
+*******************************************************************************/
+void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+ int nbytes, int data_size)
+{
+ uint32_t data = 0;
+ uint32_t i = 0;
+ uint32_t bytes_multi;
+ uint32_t bytes_remain;
+
+
+ if (1 == data_size) {
+ data = dfc_read(context, DFC_NDDB) & 0xFF;
+ *buffer++ = (uint8_t)data;
+ } else if (2 == data_size) {
+ data = dfc_read(context, DFC_NDDB) & 0xFFFF;
+ *buffer++ = data & 0xFF;
+ *buffer++ = (data >> 8) & 0xFF;
+ } else {
+ bytes_multi = (nbytes & 0xFFFFFFFC);
+ bytes_remain = nbytes & 0x03;
+
+ i = 0;
+ /* Read the bytes_multi*4 bytes data */
+ while (i < bytes_multi) {
+ data = dfc_read(context, DFC_NDDB);
+ /* FIXME: we don't know whether the buffer
+ * align to 4 bytes or not. Cast the buffer
+ * to int is not safe here. Especially under
+ * gcc 4.x. Used memcpy here. But the memcpy
+ * may be not correct on BE architecture.
+ * --by Yin, Fengwei
+ */
+ memcpy(buffer, &data, sizeof(data));
+ i += sizeof(data);
+ buffer += sizeof(data);
+ }
+
+ /* Read the left bytes_remain bytes data */
+ if (bytes_remain) {
+ data = dfc_read(context, DFC_NDDB);
+ for (i = 0; i < bytes_remain; i++)
+ *buffer++ = (uint8_t)((data >> (8*i)) & 0xFF);
+ }
+
+ /* When read the remain bytes, we always read 4 bytes data
+ * to DFC. So the data_size should subtract following number.
+ */
+ data_size -= bytes_multi + (bytes_remain ? sizeof(data) : 0);
+
+ /* We need Read data_size bytes data totally */
+ while (data_size > 0) {
+ data = dfc_read(context, DFC_NDDB);
+ data_size -= sizeof(data);
+ }
+
+/*
+ while(i < ((uint32_t)data_size) ) {
+ if (i < bytes_multi) {
+ temp = (uint32_t *)buffer;
+ *temp = dfc_reg->nddb;
+ } else if (i == bytes_multi && bytes_remain){
+ uint32_t j = 0;
+ data = dfc_reg->nddb;
+ while (j++ < bytes_remain) {
+ *buffer++ = (uint8_t) \
+ ((data>>(8*j)) & 0xFF);
+ }
+ } else {
+ data = dfc_reg->nddb;
+ }
+ i += 4;
+ buffer += 4;
+ }
+*/
+ }
+ return;
+}
+
+/******************************************************************************
+ dfc_write_fifo_partial
+
+ Description:
+ Write to data buffer of DFC from a buffer. Bytes can be same as
+ data_size, also can be data_size-padding, but can<61><6E>t be random value,
+ the left will be automatically padded by WriteFIFO.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ bytes
+ Indicating how much data should be read into buffer.
+ data_size
+ Specifing length of data transferred to/from DFC, which includes
+ padding bytes
+ buffer
+ Pointer to the data buffer where data will be taken from to be written
+ to DFC data buffer
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+ int nbytes, int data_size)
+{
+ uint32_t i = 0;
+
+ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+ uint32_t bytes_remain = nbytes & 0x03;
+ uint32_t temp;
+ /*
+ * caller guarantee buffer contains appropriate data thereby
+ * it is impossible for nbytes not to be a multiple of 4 byte
+ */
+
+ /* Write the bytes_multi*4 bytes data */
+ while (i < bytes_multi) {
+ temp = buffer[0] | buffer[1] << 8 |
+ buffer[2] << 16 | buffer[3] << 24;
+ dfc_write(context, DFC_NDDB, temp);
+ buffer += 4;
+ i += 4;
+ }
+
+ /* Write the left bytes_remain bytes data */
+ if (bytes_remain) {
+ temp = 0xFFFFFFFF;
+ for (i = 0; i < bytes_remain; i++)
+ temp &= *buffer++ << i*8;
+
+ dfc_write(context, DFC_NDDB, temp);
+ }
+
+ /* When write the remain bytes, we always write 4 bytes data
+ * to DFC. So the data_size should subtract following number.
+ */
+ data_size -= bytes_multi + (bytes_remain ? sizeof(temp) : 0);
+
+ while (data_size > 0) {
+ dfc_write(context, DFC_NDDB, 0xFFFFFFFF);
+ data_size -= 4;
+ }
+
+/*
+ while (i < ((uint32_t)data_size)) {
+ if (i < bytes_multi) {
+ temp = (uint32_t *)buffer;
+ dfc_reg->nddb = *temp;
+ }
+ else if (i == bytes_multi && bytes_remain) {
+ uint32_t j = 0, data = 0xFFFFFFFF;
+ while (j < bytes_remain) {
+ data &= (uint8_t)(*buffer) << j;
+ buffer++;
+ j++;
+ }
+ dfc_reg->nddb = data;
+ }
+ else {
+ dfc_reg->nddb = 0xFFFFFFFF;
+ }
+ i += 4;
+ buffer += 4;
+ }
+*/
+
+ return;
+}
+
+/******************************************************************************
+ dfc_read_fifo
+ Description:
+ This function reads data from data buffer of DFC.Bytes can be any less
+ than or equal to data_size, the left is ignored by ReadFIFO though they
+ will be read from NDDB to clear data buffer.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ nbytes
+ Indicating how much data should be read into buffer.
+ data_size
+ Specifing length of data transferred to/from DFC, which includes
+ padding bytes
+ Output Parameters:
+ buffer
+ Pointer to the data buffer where data should be placed.
+ Returns:
+ None
+*******************************************************************************/
+
+void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
+{
+ uint32_t i = 0;
+
+ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+ uint32_t bytes_remain = nbytes & 0x03;
+ uint32_t temp;
+
+ /* Read the bytes_multi*4 bytes data */
+ while (i < bytes_multi) {
+ temp = dfc_read(context, DFC_NDDB);
+ /* FIXME: we don't know whether the buffer
+ * align to 4 bytes or not. Cast the buffer
+ * to int is not safe here. Especially under
+ * gcc 4.x. Used memcpy here. But the memcpy
+ * may be not correct on BE architecture.
+ * --by Yin, Fengwei
+ */
+ memcpy(buffer, &temp, sizeof(temp));
+ i += sizeof(temp);
+ buffer += sizeof(temp);
+ }
+
+ /* Read the left bytes_remain bytes data */
+ temp = dfc_read(context, DFC_NDDB);
+ for (i = 0; i < bytes_remain; i++) {
+ *buffer++ = (uint8_t)((temp >> (8*i)) & 0xFF);
+ }
+
+/*
+ while (i < bytes_multi) {
+ temp = (uint32_t *)buffer;
+ *temp = dfc_reg->nddb;
+ i += 4;
+ buffer += 4;
+ }
+
+ if (bytes_remain) {
+ data = dfc_reg->nddb;
+ for (i = 0; i < bytes_remain; i++) {
+ *buffer++ = (uint8_t)((data>>(8*i)) & 0xFF);
+ }
+ }
+*/
+
+ return;
+}
+
+/******************************************************************************
+ dfc_write_fifo
+ Description:
+ Write to data buffer of DFC from a buffer.Bytes can be same as data_size,
+ also can be data_size-padding, but can<61><6E>t be random value, the left will
+ be automatically padded by WriteFIFO.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ nbytes
+ Indicating how much data should be read into buffer.
+ data_size
+ Specifing length of data transferred to/from DFC, which includes
+ padding bytes
+ buffer
+ Pointer to the data buffer where data will be taken from to be written to
+ DFC data buffer
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
+{
+ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+ uint32_t bytes_remain = nbytes & 0x03;
+ uint32_t i=0;
+ uint32_t temp;
+
+ /* Write the bytes_multi*4 bytes data */
+ while (i < bytes_multi) {
+ temp = buffer[0] | buffer[1] << 8 |
+ buffer[2] << 16 | buffer[3] << 24;
+ dfc_write(context, DFC_NDDB, temp);
+ buffer += 4;
+ i += 4;
+ }
+
+ /* Write the left bytes_remain bytes data */
+ temp = 0xFFFFFFFF;
+ for (i = 0; i < bytes_remain; i++)
+ temp &= *buffer++ << i*8;
+ dfc_write(context, DFC_NDDB, temp);
+
+/*
+ while (i < nbytes) {
+ temp = (uint32_t *)buffer;
+ dfc_reg->nddb = *temp;
+ i += 4;
+ buffer += 4;
+ }
+*/
+}
+
+/******************************************************************************
+ dfc_read_badblock_addr
+
+ Description:
+ This function reads bad block address in units of block starting from 0
+ if bad block is detected. It takes into the account if the operation is
+ for CS0 or CS1 depending on settings of chip_select parameter of DFC
+ Mode structure.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ Output Parameters:
+ pBadBlockAddr
+ Used to retrieve bad block address back to caller if bad block is
+ detected
+ Returns:
+ None
+*******************************************************************************/
+void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr)
+{
+ uint32_t ndbdr;
+ if (0 == context->dfc_mode->chip_select)
+ ndbdr = dfc_read(context, DFC_NDBDR0);
+ else
+ ndbdr = dfc_read(context, DFC_NDBDR1);
+
+ if (512 == context->flash_info->page_size) {
+ ndbdr = (ndbdr >> 5) & 0xFFF;
+ *bbaddr = ndbdr;
+ } else if (2048 == context->flash_info->page_size) {
+ /* 16 bits LB */
+ ndbdr = (ndbdr >> 8);
+ *bbaddr = ndbdr;
+ }
+ return;
+}
+
+/******************************************************************************
+ dfc_enable_int
+
+ Description:
+ This function is used to enable DFC interrupts. The bits in int_mask
+ will be used to unmask NDCR register to enable corresponding interrupts.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ int_mask
+ Specifies what interrupts to enable
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_enable_int(struct dfc_context *context, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr &= ~int_mask;
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ return;
+}
+
+/******************************************************************************
+ dfc_disable_int
+
+ Description:
+ This function is used to disable DFC interrupts.
+ The bits inint_mask will be used to mask NDCR register to disable
+ corresponding interrupts.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ int_mask
+ Specifies what interrupts to disable
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_disable_int(struct dfc_context *context, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr |= int_mask;
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ ndcr = dfc_read(context, DFC_NDCR);
+ return;
+}
+
+/******************************************************************************
+ dfc_clear_int
+
+ Description:
+ This function is used to disable DFC interrupts.
+ The bits in int_mask will be used to clear corresponding interrupts
+ in NDCR register
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ int_mask
+ Specifies what interrupts to clear
+ Output Parameters:
+ None
+ Returns:
+ None
+*******************************************************************************/
+void dfc_clear_int(struct dfc_context *context, uint32_t int_mask)
+{
+ dfc_write(context, DFC_NDSR, int_mask);
+
+ dfc_read(context, DFC_NDSR);
+ return;
+}
+
+/*
+ * high level primitives
+ */
+
+/******************************************************************************
+ dfc_init
+
+ Description:
+ This function does entire DFC initialization according to the NAND
+ flash type currently used with platform, including setting MFP, set
+ flash timing, set DFC mode, configuring specified flash parameters
+ in DFC, clear ECC logic and page count register.
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ 0
+ if MFPRs are set correctly
+ -EINVAL
+ if specified flash is not support by check bytes per page and pages per
+ block
+******************************************************************************/
+
+static mfp_cfg_t pxa300_nand_cfg[] = {
+ /* NAND */
+ MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
+};
+
+#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
+
+int dfc_init(struct dfc_context* context, int type)
+{
+ int status;
+ struct dfc_flash_info * flash_info;
+ uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+
+ status = dfc_get_flash_info(type, &flash_info);
+ if (status)
+ return status;
+ context->flash_info = flash_info;
+
+ pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa300_nand_cfg));
+ //enable_dfc_pins();
+
+ dfc_set_timing(context, &context->flash_info->timing);
+
+ if (flash_info->enable_arbiter)
+ ndcr |= NDCR_ND_ARB_EN;
+
+ if (64 == flash_info->page_per_block)
+ ndcr |= NDCR_PG_PER_BLK;
+ else if (32 != flash_info->page_per_block)
+ return -EINVAL;
+
+ if (flash_info->row_addr_start)
+ ndcr |= NDCR_RA_START;
+
+ ndcr |= (flash_info->read_id_bytes)<<16;
+
+ ndcr |= (flash_info->dfc_mode) << 21;
+
+ if (flash_info->ncsx)
+ ndcr |= NDCR_NCSX;
+
+ if (2048 == flash_info->page_size)
+ ndcr |= NDCR_PAGE_SZ;
+ else if (512 != flash_info->page_size)
+ return -EINVAL;
+
+ if (16 == flash_info->flash_width)
+ ndcr |= NDCR_DWIDTH_M;
+ else if (8 != flash_info->flash_width)
+ return -EINVAL;
+
+ if (16 == flash_info->dfc_width)
+ ndcr |= NDCR_DWIDTH_C;
+ else if (8 != flash_info->dfc_width)
+ return -EINVAL;
+
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ dfc_set_dma(context);
+ dfc_set_ecc(context);
+ dfc_set_spare(context);
+
+ return 0;
+}
+
+/******************************************************************************
+ dfc_init_no_gpio
+
+ Description:
+ This function does entire DFC initialization according to the NAND
+ flash type currently used with platform, including set flash timing,
+ set DFC mode, configuring specified flash parameters in DFC, clear
+ ECC logic and page count register. The only difference with dfc_init
+ is that it does not set MFP&GPIO, very useful in OS loader
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ 0
+ if MFPRs are set correctly
+ -EINVAL
+ if specified flash is not support by check bytes per page and pages
+ per block
+******************************************************************************/
+int dfc_init_no_gpio(struct dfc_context* context, int type)
+{
+ struct dfc_flash_info * flash_info;
+ uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+ int status;
+
+ status = dfc_get_flash_info(type, &flash_info);
+ if (status)
+ return status;
+ context->flash_info = flash_info;
+
+ dfc_set_timing(context, &context->flash_info->timing);
+
+ if (flash_info->enable_arbiter)
+ ndcr |= NDCR_ND_ARB_EN;
+
+ if (64 == flash_info->page_per_block)
+ ndcr |= NDCR_PG_PER_BLK;
+ else if (32 != flash_info->page_per_block)
+ return -EINVAL;
+
+ if (flash_info->row_addr_start)
+ ndcr |= NDCR_RA_START;
+
+ ndcr |= (flash_info->read_id_bytes)<<16;
+
+ ndcr |= (flash_info->dfc_mode) << 21;
+
+ if (flash_info->ncsx)
+ ndcr |= NDCR_NCSX;
+
+ if (2048 == flash_info->page_size)
+ ndcr |= NDCR_PAGE_SZ;
+ else if (512 != flash_info->page_size)
+ return -EINVAL;
+
+ if (16 == flash_info->flash_width)
+ ndcr |= NDCR_DWIDTH_M;
+ else if (8 != flash_info->flash_width)
+ return -EINVAL;
+
+ if (16 == flash_info->dfc_width)
+ ndcr |= NDCR_DWIDTH_C;
+ else if (8 != flash_info->dfc_width)
+ return -EINVAL;
+
+ dfc_write(context, DFC_NDCR, ndcr);
+
+ dfc_set_dma(context);
+ dfc_set_ecc(context);
+ dfc_set_spare(context);
+
+ return 0;
+}
+
+/*
+ * This macro will be used in following NAND operation functions.
+ * It is used to clear command buffer to ensure cmd buffer is empty
+ * in case of operation is timeout
+ */
+#define ClearCMDBuf() do { \
+ dfc_stop(context); \
+ udelay(NAND_OTHER_TIMEOUT); \
+ } while (0)
+
+/******************************************************************************
+ dfc_reset_flash
+
+ Description:
+ It reset the flash. The function can be called at any time when the
+ device is in Busy state during random read/program/erase mode and
+ reset operation will abort all these operations. After reset operation
+ the device is ready to wait for next command
+ Input Parameters:
+ context
+ Pointer to DFC context structure
+ Output Parameters:
+ None
+ Returns:
+ 0
+ execution succeeds
+ -ETIME
+ if timeout
+*******************************************************************************/
+int dfc_reset_flash(struct dfc_context *context)
+{
+ struct dfc_flash_info *flash_info = context->flash_info;
+ uint32_t event, event_out;
+ unsigned long timeo;
+ int status;
+
+ /* Send command */
+ dfc_send_cmd(context, (uint16_t)flash_info->reset, 0xFFFFFFFF, 0);
+
+ event = (context->dfc_mode->chip_select)? \
+ NDSR_CS1_CMDD : NDSR_CS0_CMDD;
+
+ /* Wait for CMDDM(command done successfully) */
+ status = dfc_wait_event(context, event, &event_out,
+ NAND_OTHER_TIMEOUT, 0);
+
+ if (status) {
+ ClearCMDBuf();
+ return status;
+ }
+
+
+ /* Wait until flash device is stable or timeout (10ms) */
+ timeo = jiffies + HZ;
+ do {
+ if (monahans_df_dev_ready(context->mtd))
+ break;
+ } while (time_before(jiffies, timeo));
+
+ return 0;
+}
+
+int dfc_readid(struct dfc_context *context, uint32_t *id)
+{
+ struct dfc_flash_info *flash_info = context->flash_info;
+ uint32_t event_out;
+ int status;
+ char tmp[DFC_DATA_SIZE_ID];
+
+ /* Send command */
+ status = dfc_send_cmd(context, (uint16_t)flash_info->read_id,
+ 0xFFFFFFFF, 0);
+ if (status) {
+ ClearCMDBuf();
+ return status;
+ }
+
+ /* Wait for CMDDM(command done successfully) */
+ status = dfc_wait_event(context, NDSR_RDDREQ, &event_out,
+ NAND_OTHER_TIMEOUT, 0);
+ if (status) {
+ ClearCMDBuf();
+ return status;
+ }
+ dfc_read_fifo_partial(context, (unsigned char *)tmp,
+ context->flash_info->read_id_bytes, DFC_DATA_SIZE_ID);
+
+ *id = tmp[0] | (tmp[1] << 8);
+ return 0;
+}
+
+#define ERR_NONE 0x0
+#define ERR_DMABUSERR (-0x01)
+#define ERR_SENDCMD (-0x02)
+#define ERR_DBERR (-0x03)
+#define ERR_BBERR (-0x04)
+#define ERR_BUSY (-0x05)
+
+#define STATE_CMD_SEND 0x1
+#define STATE_CMD_HANDLE 0x2
+#define STATE_DMA_TRANSFER 0x3
+#define STATE_DMA_DONE 0x4
+#define STATE_READY 0x5
+#define STATE_SUSPENDED 0x6
+#define STATE_DATA_TRANSFER 0x7
+
+#define NAND_RELOC_MAX 127
+#define NAND_RELOC_HEADER 0x524e
+#define MAX_CHIP 1
+#define NAND_CMD_DMA_LEN 12
+
+#define MAX_TIM_SIZE 0x1000
+#define MAX_BBT_SLOTS 24
+
+struct reloc_item {
+ unsigned short from;
+ unsigned short to;
+};
+
+struct reloc_table {
+ unsigned short header;
+ unsigned short total;
+ struct reloc_item reloc[NAND_RELOC_MAX];
+};
+
+struct monahans_dfc_info {
+ unsigned int state;
+ struct dfc_context *context;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ dma_addr_t data_buf_addr;
+ char *data_buf;
+ int data_dma;
+ struct pxa_dma_desc *data_desc;
+ dma_addr_t data_desc_addr;
+ dma_addr_t cmd_buf_addr;
+ char *cmd_buf;
+ int cmd_dma;
+ struct pxa_dma_desc *cmd_desc;
+ dma_addr_t cmd_desc_addr;
+ u64 dma_mask;
+#else
+ char *data_buf;
+#endif
+ u32 current_slot;
+ struct reloc_table table;
+ unsigned int table_init;
+ /* relate to the command */
+ unsigned int cmd;
+ unsigned int addr;
+ unsigned int column;
+ int retcode;
+ unsigned int buf_count;
+ struct completion cmd_complete;
+};
+
+static struct dfc_mode dfc_mode =
+{
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ 1, /* enable DMA */
+#else
+ 0,
+#endif
+ 1, /* enable ECC */
+ 1, /* enable SPARE */
+ 0, /* CS0 */
+};
+
+
+struct dfc_context dfc_context =
+{
+ 0, /* Initialized at function monahans_df_init() */
+ &dfc_mode,
+ 0, /* data dma channel */
+ 0, /* cmd dma channel */
+ NULL, /* &zylonite_flashinfo */
+};
+
+
+/*
+ * MTD structure for Zylonite board
+ */
+static struct mtd_info *monahans_mtd = NULL;
+
+/*
+ * BootRom and XDB will use last 127 block, and they will keep all the status
+ * of the bootloader and image, so skip the first 2M size and last 2M size
+ */
+static struct mtd_partition partition_info[] = {
+ {
+ name: "Bootloader",
+//#ifdef CONFIG_CPU_MONAHANS_LV
+ size: 0x00060000,
+//#else
+// size: 0x00040000,
+//#endif
+ offset: 0,
+ mask_flags: MTD_WRITEABLE /* force read-only */
+ },{
+ name: "Kernel",
+ size: 0x00200000,
+//#ifdef CONFIG_CPU_MONAHANS_LV
+ offset: 0x00060000,
+//#else
+// offset: 0x00040000,
+//#endif
+ mask_flags: MTD_WRITEABLE /* force read-only */
+ },{
+ name: "Filesystem",
+ size: 0x05000000,
+//#ifdef CONFIG_CPU_MONAHANS_LV
+ offset: 0x00260000,
+//#else
+// offset: 0x00240000,
+//#endif
+ }, {
+ name: "MassStorage",
+ size: 0x0, /* It will be set at probe function */
+ offset: MTDPART_OFS_APPEND /* Append after fs section */
+ }, {
+ name: "BBT",
+ size: 0x0, /* It will be set at probe function */
+ offset: MTDPART_OFS_APPEND,/* Append after fs section */
+ mask_flags: MTD_WRITEABLE /* force read-only */
+ }
+};
+
+#define PART_NUM ARRAY_SIZE(partition_info)
+
+/* MHN_OBM_V2 is related to BBT in MOBM V2
+ * MHN_OBM_V3 is related to BBT in MOBM V3
+ */
+enum {
+ MHN_OBM_NULL = 0,
+ MHN_OBM_V1,
+ MHN_OBM_V2,
+ MHN_OBM_V3,
+ MHN_OBM_INVAL
+} MHN_OBM_TYPE;
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+static uint8_t scan_main_bbt_pattern[] = { 'p', 'x', 'a', '1' };
+static uint8_t scan_mirror_bbt_pattern[] = { '0', 'a', 'x', 'p' };
+
+static struct nand_bbt_descr monahans_bbt_default = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .maxblocks = 2,
+ .len = 2,
+ .offs = 0,
+ .pattern = scan_ff_pattern,
+};
+
+static struct nand_bbt_descr monahans_bbt_main = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .veroffs = 6,
+ .maxblocks = 2,
+ .offs = 2,
+ .len = 4,
+ .pattern = scan_main_bbt_pattern,
+};
+
+static struct nand_bbt_descr monahans_bbt_mirror = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .veroffs = 6,
+ .maxblocks = 2,
+ .offs = 2,
+ .len = 4,
+ .pattern = scan_mirror_bbt_pattern,
+};
+
+#if 0
+static struct nand_bbt_descr monahans_bbt_main = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .veroffs = 2,
+ .maxblocks = 2,
+ .offs = 0x0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+static struct nand_bbt_descr monahans_bbt_mirror = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .veroffs = 2,
+ .maxblocks = 2,
+ .offs = 0x0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+#endif
+
+static struct nand_ecclayout monahans_lb_nand_oob = {
+ .eccbytes = 24,
+ .eccpos = {
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = { {2, 38} }
+};
+
+/*
+ * Monahans OOB size is only 8 bytes, and the rest 8 bytes is controlled by
+ * hardware for ECC. We construct virutal ECC buffer. Acutally, ECC is 6 bytes
+ * and the remain 2 bytes are reserved.
+ */
+static struct nand_ecclayout monahans_sb_nand_oob = {
+ .eccbytes = 6,
+ .eccpos = {8, 9, 10, 11, 12, 13 },
+ .oobfree = { {2, 6} }
+};
+
+
+static inline int is_buf_blank(u8 * buf, int size)
+{
+ int i = 0;
+ while(i < size) {
+ if (*((unsigned long *)(buf + i)) != 0xFFFFFFFF)
+ return 0;
+ i += 4;
+ }
+ if (i > size) {
+ i -= 4;
+ while( i < size) {
+ if(*(buf + i) != 0xFF)
+ return 0;
+ i++;
+ }
+ }
+ return 1;
+}
+
+static void print_buf(char *buf, int num)
+{
+ int i = 0;
+
+ while (i < num) {
+ printk(KERN_ERR "0x%08x: %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (unsigned int) (i), buf[i], buf[i+1], buf[i+2],
+ buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7],
+ buf[i+8], buf[i+9], buf[i+10],buf[i+11], buf[i+12],
+ buf[i+13], buf[i+14], buf[i+15]);
+ i += 16;
+ }
+}
+
+static int inline enable_dfc_dma(struct dfc_context *context, int enable)
+{
+ int ret = dfc_mode.enable_dma;
+ unsigned long ndcr;
+
+ if (!enable) {
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr &= ~NDCR_DMA_EN;
+ dfc_write(context, DFC_NDCR, ndcr);
+ dfc_mode.enable_dma = 0;
+ } else {
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr |= NDCR_DMA_EN;
+ dfc_write(context, DFC_NDCR, ndcr);
+ dfc_mode.enable_dma = 1;
+ }
+ return ret;
+}
+
+
+static void inline dump_info(struct monahans_dfc_info *info)
+{
+ if (!info)
+ return;
+
+ printk(KERN_ERR "cmd:0x%x; addr:0x%x; retcode:%d; state:%d \n",
+ info->cmd, info->addr, info->retcode, info->state);
+}
+
+static void inline enable_hw_ecc(struct dfc_context* context, int enable)
+{
+ unsigned long ndcr;
+
+ if (!enable) {
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr &= ~NDCR_ECC_EN;
+ dfc_write(context, DFC_NDCR, ndcr);
+ dfc_mode.enable_ecc = 0;
+ }
+ else {
+ ndcr = dfc_read(context, DFC_NDCR);
+ ndcr |= NDCR_ECC_EN;
+ dfc_write(context, DFC_NDCR, ndcr);
+ dfc_mode.enable_ecc = 1;
+ }
+}
+
+/*
+ * Now, we are not sure that the NDSR_RDY mean the flash is ready.
+ * Need more test.
+ */
+static int monahans_df_dev_ready(struct mtd_info *mtd)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ struct dfc_context* context = info->context;
+
+ return ((dfc_read(context, DFC_NDSR) & NDSR_RDY));
+}
+
+/* each read, we can only read 4bytes from NDDB, we must buffer it */
+static u_char monahans_df_read_byte(struct mtd_info *mtd)
+{
+ char retval = 0xFF;
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ if (info->column < info->buf_count) {
+ /* Has just send a new command? */
+ retval = info->data_buf[info->column++];
+ }
+ return retval;
+}
+
+static void monahans_df_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+ info->data_buf[info->column++] = byte;
+}
+
+static u16 monahans_df_read_word(struct mtd_info *mtd)
+{
+ u16 retval = 0xFFFF;
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ if (!(info->column & 0x01) && info->column < info->buf_count) {
+ retval = *((u16 *)(info->data_buf+info->column));
+ info->column += 2;
+ }
+ return retval;
+}
+
+static void monahans_df_write_word(struct mtd_info *mtd, u16 word)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ if (!(info->column & 0x01) && info->column < info->buf_count) {
+ *((u16 *)(info->data_buf+info->column)) = word;
+ info->column += 2;
+ }
+}
+
+static void monahans_df_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+ int real_len = min((unsigned int)len, info->buf_count - info->column);
+
+ memcpy(buf, info->data_buf + info->column, real_len);
+ info->column += real_len;
+}
+
+static void monahans_df_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+ int real_len = min((unsigned int)len, info->buf_count - info->column);
+
+ memcpy(info->data_buf + info->column, buf, real_len);
+ info->column += real_len;
+}
+
+static int monahans_df_verify_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ return 0;
+}
+
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+static void monahans_dfc_cmd_dma_irq(int channel, void *data,
+ struct pt_regs *regs)
+{
+ unsigned int dcsr;
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
+ struct dfc_context* context = info->context;
+ struct dfc_mode* dfc_mode = context->dfc_mode;
+ unsigned int intm;
+
+ dcsr = DCSR(channel);
+ DCSR(channel) = dcsr;
+
+ intm = (dfc_mode->chip_select) ? \
+ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ D1(printk("cmd dma interrupt, channel:%d, DCSR:0x%08x\n", \
+ channel, dcsr));
+
+ if (dcsr & DCSR_BUSERR) {
+ info->retcode = ERR_DMABUSERR;
+ complete(&info->cmd_complete);
+ } else {
+ if ((info->cmd == NAND_CMD_READ0) ||
+ (info->cmd == NAND_CMD_READOOB)|| \
+ (info->cmd == NAND_CMD_READID) || \
+ (info->cmd == NAND_CMD_STATUS)) {
+ dfc_enable_int(context, NDSR_RDDREQ | NDSR_DBERR);
+ } else if (info->cmd == NAND_CMD_PAGEPROG)
+ dfc_enable_int(context, NDSR_WRDREQ);
+ else if (info->cmd == NAND_CMD_ERASE1)
+ dfc_enable_int(context, intm);
+ }
+
+ return;
+}
+
+
+static void monahans_dfc_data_dma_irq(int channel, void *data,
+ struct pt_regs *regs)
+{
+ unsigned int dcsr, intm;
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
+ struct dfc_context* context = info->context;
+ struct dfc_mode* dfc_mode = context->dfc_mode;
+
+ dcsr = DCSR(channel);
+ DCSR(channel) = dcsr;
+
+ intm = (dfc_mode->chip_select) ? \
+ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ D1(printk("data dma interrupt, channel:%d, DCSR:0x%08x\n",
+ channel, dcsr));
+ if (dcsr & DCSR_BUSERR) {
+ info->retcode = ERR_DMABUSERR;
+ complete(&info->cmd_complete);
+ }
+
+ if (info->cmd == NAND_CMD_PAGEPROG) {
+ /* DMA interrupt may be interrupted by other IRQs*/
+ info->state = STATE_DMA_DONE;
+ dfc_enable_int(context, intm);
+ } else {
+ info->state = STATE_READY;
+ complete(&info->cmd_complete);
+ }
+
+}
+#endif
+
+static irqreturn_t monahans_dfc_irq(int irq, void *devid)
+{
+ unsigned int status, event, intm, cmd;
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)devid;
+ struct dfc_context* context = info->context;
+ struct dfc_mode* dfc_mode = context->dfc_mode;
+
+ intm = (dfc_mode->chip_select) ? \
+ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+ event = (dfc_mode->chip_select) ? \
+ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ status = dfc_read(context, DFC_NDSR);
+ D1(printk("DFC irq, NDSR:0x%x\n", status));
+ if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
+ if (status & NDSR_DBERR) {
+ info->retcode = ERR_DBERR;
+ }
+
+ dfc_disable_int(context, NDSR_RDDREQ | NDSR_DBERR);
+ dfc_clear_int(context, NDSR_RDDREQ | NDSR_DBERR);
+ if (info->cmd == NAND_CMD_READID)
+ cmd = context->flash_info->read_id;
+ else if (info->cmd == NAND_CMD_STATUS)
+ cmd = context->flash_info->read_status;
+ else if (info->cmd == NAND_CMD_READ0 ||
+ info->cmd == NAND_CMD_READOOB)
+ cmd = context->flash_info->read1;
+ else {
+ printk(KERN_ERR "No according command:0x%x happens\n",
+ info->cmd);
+ goto out;
+ }
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ info->state = STATE_DMA_TRANSFER;
+ dfc_start_data_dma(context,
+ (struct pxa_dma_desc*)info->data_desc_addr);
+#else
+ info->state = STATE_DATA_TRANSFER;
+ complete(&info->cmd_complete);
+#endif
+ } else if (status & NDSR_WRDREQ) {
+ dfc_disable_int(context, NDSR_WRDREQ);
+ dfc_clear_int(context, NDSR_WRDREQ);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ info->state = STATE_DMA_TRANSFER;
+ dfc_start_data_dma(context,
+ (struct pxa_dma_desc*)info->data_desc_addr);
+#else
+ info->state = STATE_DATA_TRANSFER;
+ complete(&info->cmd_complete);
+#endif
+ } else if (status & event) {
+ if (status & NDSR_CS0_BBD) {
+ info->retcode = ERR_BBERR;
+ }
+
+ dfc_disable_int(context, intm);
+ dfc_clear_int(context, event);
+ info->state = STATE_READY;
+ complete(&info->cmd_complete);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static int dfc_send_command(struct mtd_info *mtd, unsigned int cmd,
+ unsigned int addr, unsigned int num_pages,
+ unsigned int event)
+{
+
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+ struct dfc_context* context = info->context;
+ int status;
+ int ret;
+
+ D1(printk("ready send command, cmd:0x%x, at address:0x%x,"
+ " num_pages:%d, wait event:0x%x\n", cmd, addr, num_pages, event));
+
+ info->state = STATE_CMD_SEND;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ status = dfc_setup_cmd_dma(context, cmd, addr, num_pages,
+ (uint32_t *)info->cmd_buf, info->cmd_buf_addr,
+ DDADR_STOP, DCMD_ENDIRQEN, info->cmd_desc);
+#else
+ status = dfc_send_cmd(context, cmd, addr, num_pages);
+#endif
+ if (status) {
+ info->retcode = ERR_SENDCMD;
+ dfc_stop(context);
+ udelay(20);
+ printk(KERN_ERR "fail send command\n");
+ return info->retcode;
+ }
+ info->state = STATE_CMD_HANDLE;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_setup_data_dma(context, cmd, info->data_buf_addr,
+ DDADR_STOP, DCMD_ENDIRQEN, info->data_desc);
+ dfc_start_cmd_dma(context, (struct pxa_dma_desc*)info->cmd_desc_addr);
+#endif
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_enable_int(context, event);
+#endif
+ ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
+ if (!ret){
+ printk(KERN_ERR "Command time out\n");
+ dump_info(info);
+ }
+ D1(printk("command return, cmd:0x%x, retcode:%d\n",
+ info->cmd, info->retcode));
+ return 0;
+}
+
+static void monahans_df_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr )
+{
+ struct nand_chip *this = (struct nand_chip *)(mtd->priv);
+ struct monahans_dfc_info *info =
+ (struct monahans_dfc_info *)(this->priv);
+ struct dfc_context *context = info->context;
+ struct dfc_flash_info * flash_info = context->flash_info;
+ int ret, pages_shift;
+ int status;
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ int datasize;
+ int paddingsize;
+#endif
+ unsigned int to;
+
+ D1(printk("command:0x%x at address:0x%x, column:0x%x\n",
+ command, page_addr, column));
+
+ if (info->state != STATE_READY) {
+ printk(KERN_ERR "CHIP is not ready.\n");
+ dump_info(info);
+ info->retcode = ERR_BUSY;
+ return;
+ }
+ info->retcode = ERR_NONE;
+ pages_shift = this->phys_erase_shift - this->page_shift;
+ if (info->table_init) {
+ to = search_rel_block((page_addr >> pages_shift), mtd);
+ if (to) {
+ page_addr = (to << pages_shift) | (page_addr
+ & ((1 << pages_shift) - 1));
+ }
+ }
+
+ switch ( command ) {
+ case NAND_CMD_READOOB:
+ /*
+ * DFC has mark the last 8 bytes OOB data if HARDEARE_ECC is
+ * enabled. We must first disable the HARDWARE_ECC for getting
+ * all the 16 bytes OOB
+ */
+ enable_hw_ecc(context, 0);
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ info->column = mtd->writesize + column;
+ info->cmd = command;
+ info->addr = page_addr << this->page_shift;
+ ret = dfc_send_command(mtd, flash_info->read1, info->addr,
+ 1, NDSR_RDDREQ | NDSR_DBERR);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_get_pattern(info->context, flash_info->read1, &datasize,
+ &paddingsize);
+ dfc_read_fifo_partial(info->context, info->data_buf,
+ min(info->buf_count, datasize), datasize);
+ info->state = STATE_READY;
+#endif
+ /* We only are OOB, so if the data has error, does not matter */
+ if (info->retcode == ERR_DBERR)
+ info->retcode = ERR_NONE;
+ enable_hw_ecc(context, 1);
+ break;
+
+ case NAND_CMD_READ0:
+ enable_hw_ecc(context, 1);
+ info->column = column;
+ info->cmd = command;
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ memset(info->data_buf, 0xFF, info->buf_count);
+ info->addr = page_addr << this->page_shift;
+
+ ret = dfc_send_command(mtd, flash_info->read1, info->addr,
+ 1, NDSR_RDDREQ | NDSR_DBERR);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_get_pattern(info->context, flash_info->read1, &datasize,
+ &paddingsize);
+ dfc_read_fifo_partial(info->context, info->data_buf,
+ min(info->buf_count, datasize), datasize);
+ info->state = STATE_READY;
+#endif
+ /* When the data buf is blank, the DFC will report DB error */
+ if (info->retcode == ERR_DBERR && is_buf_blank(info->data_buf,
+ mtd->writesize))
+ info->retcode = ERR_NONE;
+
+ if (info->retcode == ERR_DBERR) {
+ printk(KERN_ERR "DB error at address 0x%x\n",
+ info->addr);
+ print_buf(info->data_buf, info->buf_count);
+ }
+ break;
+ case NAND_CMD_SEQIN:
+ /* Write only OOB? */
+
+ info->cmd = command;
+ if (column >= mtd->writesize) {
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ enable_hw_ecc(context, 0);
+ } else {
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ enable_hw_ecc(context, 1);
+ }
+ memset(info->data_buf, 0xFF, mtd->writesize + mtd->oobsize);
+ info->column = column;
+ info->addr = page_addr << this->page_shift;
+ break;
+ case NAND_CMD_PAGEPROG:
+ /* prevois command is NAND_CMD_SEIN ?*/
+ if (info->cmd != NAND_CMD_SEQIN) {
+ info->cmd = command;
+ info->retcode = ERR_SENDCMD;
+ printk(KERN_ERR "Monahans NAND device: "
+ "No NAND_CMD_SEQIN executed before.\n");
+ enable_hw_ecc(context, 1);
+ break;
+ }
+ info->cmd = command;
+ ret = dfc_send_command(mtd, flash_info->program, info->addr,
+ 1, NDSR_WRDREQ);
+
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ if (ret != 0)
+ break;
+
+ dfc_get_pattern(info->context, flash_info->program, &datasize,
+ &paddingsize);
+ dfc_write_fifo_partial(info->context, info->data_buf, datasize,
+ datasize);
+
+ if (info->context->dfc_mode->chip_select)
+ dfc_enable_int(info->context,
+ NDSR_CS1_BBD | NDSR_CS1_CMDD);
+ else
+ dfc_enable_int(info->context,
+ NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
+ if (!ret){
+ printk(KERN_ERR "Programm Command time out\n");
+ dump_info(info);
+ }
+
+ if (info->retcode == ERR_BBERR) {
+ mtd->block_markbad(mtd, info->addr);
+ }
+#endif
+ break;
+ case NAND_CMD_ERASE1:
+ info->cmd = command;
+ info->addr = (page_addr >> pages_shift) << this->phys_erase_shift;
+
+ if (info->context->dfc_mode->chip_select)
+ ret = dfc_send_command(mtd, flash_info->erase,
+ info->addr, 0, NDSR_CS1_BBD | NDSR_CS1_CMDD);
+ else
+ ret = dfc_send_command(mtd, flash_info->erase,
+ info->addr, 0, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ if (info->retcode == ERR_BBERR) {
+ mtd->block_markbad(mtd, info->addr);
+ }
+ break;
+ case NAND_CMD_ERASE2:
+ break;
+ case NAND_CMD_READID:
+ info->cmd = command;
+ info->buf_count = flash_info->read_id_bytes;
+ info->column = 0;
+ info->addr = 0xFFFFFFFF;
+ ret = dfc_send_command(mtd, flash_info->read_id, info->addr,
+ 0, NDSR_RDDREQ);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_get_pattern(info->context, flash_info->read_id, &datasize,
+ &paddingsize);
+ dfc_read_fifo_partial(info->context, info->data_buf,
+ info->buf_count, datasize);
+ info->state = STATE_READY;
+#endif
+ D1(printk("ReadID, [1]:0x%x, [2]:0x%x\n",
+ info->data_buf[0], info->data_buf[1]));
+ break;
+ case NAND_CMD_STATUS:
+ info->cmd = command;
+ info->buf_count = 1;
+ info->column = 0;
+ info->addr = 0xFFFFFFFF;
+ ret = dfc_send_command(mtd, flash_info->read_status,
+ info->addr, 0, NDSR_RDDREQ);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+ dfc_get_pattern(info->context, flash_info->read_status,
+ &datasize, &paddingsize);
+ dfc_read_fifo_partial(info->context, info->data_buf,
+ info->buf_count, datasize);
+ info->state = STATE_READY;
+#endif
+ break;
+
+ case NAND_CMD_RESET:
+ status = dfc_reset_flash(&dfc_context);
+ if (status) {
+ printk(KERN_WARNING "Monahans NAND device:"
+ "NAND_CMD_RESET error\n");
+ }
+ break;
+ default:
+ printk(KERN_WARNING "Monahans NAND device:"
+ "Non-support the command.\n");
+ break;
+ }
+
+ if (info->retcode != ERR_NONE)
+ dfc_stop(info->context);
+}
+
+static void monahans_df_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ if (chip <= MAX_CHIP)
+ info->context->dfc_mode->chip_select = chip;
+ else
+ printk(KERN_ERR "Monahans NAND device:"
+ "not select the NAND chips!\n");
+}
+
+static int monahans_df_waitfunc(struct mtd_info *mtd,
+ struct nand_chip *this)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ /* monahans_df_send_command has waited for command complete */
+ if (this->state == FL_WRITING || this->state == FL_ERASING) {
+ if (info->retcode == ERR_NONE)
+ return 0;
+ else {
+ /*
+ * any error make it return 0x01 which will tell
+ * the caller the erase and write fail
+ */
+ return 0x01;
+ }
+ }
+
+ return 0;
+}
+
+static int monahans_df_calculate_ecc(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_code)
+{
+ return 0;
+}
+
+static int monahans_df_correct_data(struct mtd_info *mtd,
+ u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ /*
+ * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
+ * consider it as a ecc error which will tell the caller the
+ * read fail We have distinguish all the errors, but the
+ * nand_read_ecc only check this function return value
+ */
+ if (info->retcode != ERR_NONE)
+ return -1;
+
+ return 0;
+}
+
+static void monahans_df_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ return;
+}
+
+/*
+ * The relocation table management is different between MOBM V2 and V3.
+ *
+ * MOBM V2 is applied on chips taped out before MhnLV A0.
+ * MOBM V3 is applied on chips taped out after MhnLV A0. It's also applied
+ * on MhnLV A0.
+ */
+static int calc_obm_ver(void)
+{
+ unsigned int cpuid;
+ /* read CPU ID */
+ __asm__ (
+ "mrc p15, 0, %0, c0, c0, 0\n"
+ : "=r" (cpuid)
+ );
+ /* It's not xscale chip. */
+ if ((cpuid & 0xFFFF0000) != 0x69050000)
+ return MHN_OBM_INVAL;
+ /* It's MhnP Ax */
+ if ((cpuid & 0x0000FFF0) == 0x00006420)
+ return MHN_OBM_V2;
+ /* It's MhnP Bx */
+ if ((cpuid & 0x0000FFF0) == 0x00006820) {
+ if ((cpuid & 0x0F) <= 5)
+ return MHN_OBM_V2;
+ else
+ return MHN_OBM_V3;
+ }
+ /* It's MhnL Ax */
+ if ((cpuid & 0x0000FFF0) == 0x00006880) {
+ if ((cpuid & 0x0F) == 0)
+ return MHN_OBM_V2;
+ else
+ return MHN_OBM_V3;
+ }
+ /* It's MhnLV Ax */
+ if ((cpuid & 0x0000FFF0) == 0x00006890)
+ return MHN_OBM_V3;
+ return MHN_OBM_INVAL;
+}
+
+
+/*
+ * MOBM maintains a relocation table. It's used to replace bad blocks.
+ * If block A is bad, it will use block B instead.
+ * There're 127 relocated blocks. All of them reside in the bottom of NAND
+ * flash. So they're reserved and can't be calculated in mtd size and chip
+ * size.
+ */
+static int read_reloc_table(struct mtd_info *mtd)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ struct dfc_context *context = NULL;
+ struct reloc_table *table = NULL;
+ int page, maxslot;
+ int obm, valid;
+
+ obm = calc_obm_ver();
+ this = (struct nand_chip *)(mtd->priv);
+ info = (struct monahans_dfc_info *)(this->priv);
+ context = info->context;
+
+ mtd->size -= (NAND_RELOC_MAX * mtd->erasesize);
+ this->chipsize -= (NAND_RELOC_MAX << this->phys_erase_shift);
+ page = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+ this->select_chip(mtd, 0);
+ valid = 0;
+ if (obm == MHN_OBM_V2) {
+ /* On MOBM V2, the relocation table resides in the last page
+ * of the first block.
+ */
+ memset(info->data_buf, 0, BUFLEN);
+ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+ memcpy(((unsigned char *)&(info->table)), info->data_buf,
+ sizeof(struct reloc_table));
+ if (info->table.header == NAND_RELOC_HEADER)
+ valid = 1;
+ } else if (obm == MHN_OBM_V3) {
+ /* On MOBM V3, there're several relocation tables in the first
+ * block.
+ * When new bad blocks are found, a new relocation table will
+ * be generated and written back to the first block. But the
+ * original relocation table won't be erased. Even if the new
+ * relocation table is written wrong, system can still find an
+ * old one.
+ * One page contains one slot.
+ */
+ maxslot = 1 << (this->phys_erase_shift - this->page_shift);
+ page = maxslot - MAX_BBT_SLOTS;
+ for (; page < maxslot; page++) {
+ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+ table = (struct reloc_table *)info->data_buf;
+ if (info->retcode == ERR_NONE) {
+ if (table->header != NAND_RELOC_HEADER) {
+ continue;
+ } else {
+ memcpy(((unsigned char *)&(info->table)),
+ table, sizeof(struct reloc_table));
+ valid = 1;
+ break;
+ }
+ }
+ }
+
+ } else {
+ printk(KERN_ERR "The version of MOBM isn't supported\n");
+ }
+ if (valid) {
+ memcpy(((unsigned char *)&(info->table)), info->data_buf,
+ sizeof(struct reloc_table));
+ printk(KERN_DEBUG "relocation table at page:%d\n", page);
+ PRINT_BUF((unsigned char *)&(info->table),
+ sizeof(struct reloc_table));
+ info->table_init = 1;
+ } else {
+ /* There should be a valid relocation table slot at least. */
+ printk(KERN_ERR "NO VALID relocation table can be \
+ recognized\n");
+ printk(KERN_ERR "CAUTION: It may cause unpredicated error\n");
+ printk(KERN_ERR "Please re-initialize the NAND flash.\n");
+ memset((unsigned char *)&(info->table), 0,
+ sizeof(struct reloc_table));
+ info->table_init = 0;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* add the relocation entry into the relocation table
+ * It's valid on MOBM V3.
+ * If the relocated block is bad, an new entry will be added into the
+ * bottom of the relocation table.
+ */
+static int update_rel_table(struct mtd_info *mtd, int block)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ struct reloc_table *table = NULL;
+ int obm, reloc_block;
+
+ this = (struct nand_chip *)(mtd->priv);
+ info = (struct monahans_dfc_info *)(this->priv);
+ obm = calc_obm_ver();
+ if (obm == MHN_OBM_V3) {
+ table = &info->table;
+ if (info->table_init == 0) {
+ printk(KERN_ERR "Error: the initial relocation \
+ table can't be read\n");
+ memset(table, 0, sizeof(struct reloc_table));
+ table->header = NAND_RELOC_HEADER;
+ info->table_init = 1;
+ }
+ if (table->total == 0) {
+ /* Point to the first relocated block.
+ * It resides in the last block of flash.
+ * the relocation entry has calculated in
+ * chipsize
+ */
+ reloc_block = (this->chipsize
+ >> this->phys_erase_shift)
+ + NAND_RELOC_MAX - 1;
+ } else if (table->total < NAND_RELOC_MAX) {
+ reloc_block = table->reloc[table->total - 1].to - 1;
+ } else {
+ printk(KERN_ERR "Relocation table exceed max number, \
+ cannot mark block 0x%x as bad block\n", block);
+ return -ENOSPC;
+ }
+ /* Make sure that reloc_block is pointing to a valid block */
+ for (; ; reloc_block--) {
+ /* The relocate table is full */
+ if (reloc_block < (this->chipsize
+ >> this->phys_erase_shift))
+ return -ENOSPC;
+ this->cmdfunc(mtd, NAND_CMD_ERASE1, 0, reloc_block
+ << (this->phys_erase_shift
+ - this->page_shift));
+ if (info->retcode == ERR_NONE)
+ break;
+ }
+ /* Create the relocated block information in the table */
+ table->reloc[table->total].from = block;
+ table->reloc[table->total].to = reloc_block;
+ table->total++;
+ }
+ return 0;
+}
+
+/* Write the relocation table back to device, if there's room. */
+static int sync_rel_table(struct mtd_info *mtd, int *idx)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ int obm, start_page, len;
+
+ if (*idx >= MAX_BBT_SLOTS) {
+ printk(KERN_ERR "Can't write relocation table to device \
+ any more.\n");
+ return -1;
+ }
+ if (*idx < 0) {
+ printk(KERN_ERR "Wrong Slot is specified.\n");
+ return -1;
+ }
+ this = (struct nand_chip *)(mtd->priv);
+ info = (struct monahans_dfc_info *)(this->priv);
+ len = 4;
+ len += info->table.total << 2;
+ obm = calc_obm_ver();
+ if (obm == MHN_OBM_V3) {
+ /* write to device */
+ start_page = 1 << (this->phys_erase_shift - this->page_shift);
+ start_page = start_page - 1 - *idx;
+ memset(&(info->data_buf), 0xFF, BUFLEN);
+ memcpy(&(info->data_buf), &(info->table), len);
+
+ printk(KERN_DEBUG "DUMP relocation table before write. \
+ page:0x%x\n", start_page);
+ monahans_df_command(mtd, NAND_CMD_SEQIN, 0, start_page);
+ monahans_df_command(mtd, NAND_CMD_PAGEPROG, 0, start_page);
+ /* write to idx */
+ (*idx)++;
+ /* dump it */
+ memset(&(info->data_buf), 0, BUFLEN);
+ monahans_df_command(mtd, NAND_CMD_READOOB, 0, start_page);
+ PRINT_BUF(info->data_buf, len);
+ }
+ return 0;
+}
+
+
+/* Find the relocated block of the bad one.
+ * If it's a good block, return 0. Otherwise, return a relocated one.
+ * idx points to the next relocation entry
+ * If the relocated block is bad, an new entry will be added into the
+ * bottom of the relocation table.
+ */
+static unsigned short search_rel_block(int block, struct mtd_info *mtd)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ struct reloc_table *table = NULL;
+ int i, max, reloc_block = 0;
+
+ this = (struct nand_chip *)(mtd->priv);
+ info = (struct monahans_dfc_info *)(this->priv);
+ table = &(info->table);
+ if ((block <= 0) || (block > this->chipsize)
+ || (info->table_init == 0) || (table->total == 0))
+ return 0;
+ if (table->total > NAND_RELOC_MAX)
+ table->total = NAND_RELOC_MAX;
+ max = table->total;
+ for (i = 0; i < max; i++) {
+ if (block == table->reloc[i].from)
+ reloc_block = table->reloc[i].to;
+ }
+ return reloc_block;
+}
+
+/*
+ * Check whether the block is a bad one.
+ * At first, it will search the relocation table.
+ * If necessary, it will search the BBT. Because relocation table can only
+ * maintain limited record. If there're more bad blocks, they can't be
+ * recorded in relocation table. They can only be recorded in BBT.
+ */
+static int monahans_df_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ struct nand_chip *this = NULL;
+ int page, block, reloc_block, chipnr, res = 0;
+ u16 bad;
+
+ /* At here, we only support one flash chip */
+ this = (struct nand_chip *)mtd->priv;
+ block = (int)(ofs >> this->phys_erase_shift);
+ /* search the block in the relocation table */
+ reloc_block = search_rel_block(block, mtd);
+ if (reloc_block) {
+ ofs = ((reloc_block << this->phys_erase_shift) |
+ (ofs & ((1 << this->phys_erase_shift) - 1)));
+ }
+
+ /* search BBT
+ * Maybe the relocation table is full, but some bad blocks aren't
+ * recordered in it.
+ * The below code are copied from nand_block_bad().
+ */
+ if (getchip) {
+ page = (int)(ofs >> this->page_shift);
+ chipnr = (int)(ofs >> this->chip_shift);
+
+ /* Select the NAND chips */
+ this->select_chip(mtd, chipnr);
+ } else
+ page = (int)ofs;
+
+ if (this->options & NAND_BUSWIDTH_16) {
+ this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE,
+ page & this->pagemask);
+ bad = cpu_to_le16(this->read_word(mtd));
+ if (this->badblockpos & 0x1)
+ bad >>= 1;
+ if ((bad & 0xFF) != 0xFF)
+ res = 1;
+ } else {
+ this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos,
+ page & this->pagemask);
+ if (this->read_byte(mtd) != 0xFF)
+ res = 1;
+ }
+
+ return res;
+}
+
+static int monahans_df_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ unsigned char buf[2] = {0, 0};
+ int block, reloc_block, page, ret;
+
+ this = (struct nand_chip *)mtd->priv;
+ info = (struct monahans_dfc_info *)(this->priv);
+ /* Get block number */
+ block = ((int)ofs) >> this->bbt_erase_shift;
+ ret = update_rel_table(mtd, block);
+ if (!ret) {
+ sync_rel_table(mtd, &(info->current_slot));
+ return 0;
+ } else {
+ reloc_block = search_rel_block(block, mtd);
+ if (reloc_block)
+ block = reloc_block;
+ if (this->bbt)
+ this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+ }
+
+ /* Do we have a flash based bad block table ? */
+ if (this->options & NAND_USE_FLASH_BBT)
+ return nand_update_bbt(mtd, ofs);
+
+ /* mark the bad block flag at the first two pages */
+ page = block << (this->phys_erase_shift - this->page_shift);
+ ofs = mtd->writesize + this->badblockpos;
+ this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
+ this->write_buf(mtd, buf, 2);
+ this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ page++;
+ this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
+ this->write_buf(mtd, buf, 2);
+ this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ return 0;
+}
+
+static int dump_bbt_flash(struct mtd_info *mtd)
+{
+ struct nand_chip *this = NULL;
+ struct monahans_dfc_info *info = NULL;
+ int block, page, totlen;
+
+ this = (struct nand_chip *)mtd->priv;
+ info = (struct monahans_dfc_info *)this->priv;
+ block = (this->chipsize >> this->phys_erase_shift) - 1;
+ totlen = (this->chipsize >> this->phys_erase_shift) >> 2;
+ printk(KERN_ERR "totlen:0x%x\n", totlen);
+ this->select_chip(mtd, 0);
+ if (this->bbt_td) {
+ printk(KERN_ERR "BBT page:0x%x\n", this->bbt_td->pages[0]);
+ page = this->bbt_td->pages[0];
+ if (this->bbt_td->pages[0] <= 0) {
+ page = block << (this->phys_erase_shift
+ - this->page_shift);
+ }
+ while (totlen > 0) {
+ printk(KERN_ERR "page:0x%x\n", page);
+ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+ printk(KERN_ERR "read result:0x%x\n", info->retcode);
+ PRINT_BUF(info->data_buf, BUFLEN);
+ totlen -= (1 << this->page_shift);
+ page++;
+ }
+ }
+ if (this->bbt_md) {
+ printk(KERN_ERR "BBT page:0x%x\n", this->bbt_md->pages[0]);
+ page = this->bbt_md->pages[0];
+ if (this->bbt_td->pages[0] <= 0) {
+ page = block << (this->phys_erase_shift
+ - this->page_shift);
+ }
+ while (totlen > 0) {
+ printk(KERN_ERR "page:0x%x\n", page);
+ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+ printk(KERN_ERR "read result:0x%x\n", info->retcode);
+ PRINT_BUF(info->data_buf, BUFLEN);
+ totlen -= (1 << this->page_shift);
+ page++;
+ }
+
+ }
+ return 0;
+}
+
+static int dump_bbt_mem(struct mtd_info *mtd)
+{
+ struct nand_chip *this = NULL;
+
+ this = (struct nand_chip *)mtd->priv;
+ PRINT_BUF(this->bbt, 225);
+ return 0;
+}
+
+static int monahans_df_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = NULL;
+ int ret;
+
+ this = (struct nand_chip *)mtd->priv;
+ ret = read_reloc_table(mtd);
+ if (ret) {
+ printk(KERN_ERR "Failed to get relocation table\n");
+ printk(KERN_ERR "Try to build a new BBT. It may result \
+ unpredicated error.\n");
+ /* Create new memory based and flash based BBT */
+ }
+ nand_scan_bbt(mtd, &monahans_bbt_default);
+ //dump_bbt_flash(mtd);
+ dump_bbt_mem(mtd);
+ return 0;
+#if 0
+ /* Read flashed based BBT from device */
+ return (nand_scan_bbt(mtd, &monahans_bbt_main));
+#endif
+}
+
+
+static int monahans_df_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct monahans_dfc_info *info;
+ int status = -1;
+ unsigned int data_buf_len;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ unsigned int buf_len;
+#endif
+ int i, ret = 0;
+
+ printk(KERN_ERR "Nand driver probe\n");
+
+ dfc_context.membase = ioremap_nocache(0x43100000, 0x100000);
+ if (!dfc_context.membase)
+ printk(KERN_ERR "Couldn't ioremap\n");
+
+ pxa_set_cken(CKEN_NAND, 1);
+
+ for (i = DFC_FLASH_NULL + 1; i < DFC_FLASH_END; i++)
+ {
+ uint32_t id;
+
+ status = dfc_init(&dfc_context, i);
+ if (status)
+ continue;
+ status = dfc_readid(&dfc_context, &id);
+ if (status)
+ continue;
+ printk(KERN_DEBUG "id:0x%x, chipid:0x%x\n",
+ id, dfc_context.flash_info->chip_id);
+ if (id == dfc_context.flash_info->chip_id)
+ break;
+ }
+
+ if(i == DFC_FLASH_END) {
+ printk(KERN_ALERT "Monahans NAND device:"
+ "Nand Flash initialize failure!\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ flash_config = i;
+
+ monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+ sizeof(struct monahans_dfc_info) , GFP_KERNEL);
+ if (!monahans_mtd) {
+ printk (KERN_ERR "Monahans NAND device:"
+ "Unable to allocate NAND MTD device structure.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
+ info = (struct monahans_dfc_info *)((void *)this + sizeof(struct nand_chip));
+ dfc_context.mtd = monahans_mtd;
+
+ monahans_mtd->priv = this;
+ this->priv = info;
+ data_buf_len = dfc_context.flash_info->page_size +
+ dfc_context.flash_info->oob_size;
+ info->state = STATE_READY;
+ init_completion(&info->cmd_complete);
+ info->table_init = 0;
+ memset(&info->table, 0x0, sizeof(struct reloc_table));
+ printk(KERN_DEBUG "%s: this->controller: 0x%x, &this->controller: 0x%x\n",__func__, (unsigned int)this->controller, (unsigned int)&(this->controller));
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ info->dma_mask = 0xffffffffUL;
+
+ dev->dma_mask = &info->dma_mask;
+ dev->coherent_dma_mask = 0xffffffffUL;
+
+ /* alloc dma data buffer for data
+ * buffer + 2*descriptor + command buffer
+ */
+ buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
+ ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
+
+ printk(KERN_INFO "Try to allocate dma buffer(len:%d)"
+ "for data buffer + 2*descriptor + command buffer\n", buf_len);
+ info->data_desc = (struct pxa_dma_desc*)dma_alloc_writecombine(dev,
+ buf_len, &info->data_desc_addr, GFP_KERNEL);
+ if (!info->data_desc) {
+ printk(KERN_ERR "Monahans NAND device:"
+ "Unable to alloc dma buffer\n");
+ ret = -ENOMEM;
+ goto free_mtd;
+ }
+
+ info->cmd_desc = (struct pxa_dma_desc*)((char *)info->data_desc +
+ sizeof(struct pxa_dma_desc));
+ info->cmd_desc_addr = (dma_addr_t)((char *)info->data_desc_addr +
+ sizeof(struct pxa_dma_desc));
+ info->data_buf = (char *)info->data_desc +
+ ALIGN(2*sizeof(struct pxa_dma_desc), 32);
+ info->data_buf_addr = (dma_addr_t)((char *)info->data_desc_addr +
+ ALIGN(2*sizeof(struct pxa_dma_desc), 32));
+ info->cmd_buf = (char *)info->data_buf + ALIGN(data_buf_len, 32);
+ info->cmd_buf_addr = (dma_addr_t)((char *)info->data_buf_addr +
+ ALIGN(data_buf_len, 32));
+
+ D1(printk("Get dma buffer for data dma descriptor, virt:0x%x, phys0x:%x\n",
+ (unsigned int)info->data_desc, info->data_desc_addr));
+ D1(printk("Get dma buffer for command dma descriptors, virt:0x%x,"
+ "phys0x:%x\n", (unsigned int)info->cmd_desc, info->cmd_desc_addr));
+ D1(printk("Get dma buffer for data, virt:0x%x, phys0x:%x\n",
+ (unsigned int)info->data_buf, info->data_buf_addr));
+ D1(printk("Get dma buffer for command, virt:0x%x, phys0x:%x\n",
+ (unsigned int)info->cmd_buf, info->cmd_buf_addr));
+
+ D1(printk("Try to allocate dma channel for data\n"));
+
+ info->data_dma = pxa_request_dma("NAND DATA", DMA_PRIO_LOW,
+ monahans_dfc_data_dma_irq, info);
+ if (info->data_dma < 0) {
+ printk(KERN_ERR "Monahans NAND device:"
+ "Unable to alloc dma channel for data\n");
+ ret = info->data_dma;
+ goto free_buf;
+ }
+ D1(printk("Get dma channel:%d for data\n", info->data_dma));
+
+ D1(printk("Try to allocate dma channel for command\n"));
+ info->cmd_dma = pxa_request_dma("NAND CMD", DMA_PRIO_LOW,
+ monahans_dfc_cmd_dma_irq, info);
+ if (info->cmd_dma < 0) {
+ printk(KERN_ERR "Monahans NAND device:"
+ "Unable to alloc dma channel for command\n");
+ ret = info->cmd_dma;
+ goto free_data_dma;
+ }
+ D1(printk("Get dma channel:%d for command\n", info->cmd_dma));
+
+ dfc_context.cmd_dma_ch = info->cmd_dma;
+ dfc_context.data_dma_ch = info->data_dma;
+#else
+ printk(KERN_DEBUG "Try to allocate data buffer(len:%d)\n", data_buf_len);
+ info->data_buf = kmalloc(data_buf_len, GFP_KERNEL);
+ if (!info->data_buf) {
+ printk(KERN_ERR "Monahans NAND device:"
+ "Unable to alloc data buffer\n");
+ ret = -ENOMEM;
+ goto free_mtd;
+ }
+#endif
+
+ D1(printk("Try to request irq:%d\n", IRQ_NAND));
+ ret = request_irq(IRQ_NAND, monahans_dfc_irq, 0, pdev->name, info);
+ if (ret < 0) {
+ printk(KERN_ERR "Monahans NAND device: Unable to request irq\n");
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ goto free_cmd_dma;
+#else
+ goto free_buf;
+#endif
+ }
+
+ D1(printk("Success request irq\n"));
+
+ /* set address of NAND IO lines */
+ this->options = (dfc_context.flash_info->flash_width == 16)? \
+ NAND_BUSWIDTH_16: 0 | NAND_USE_FLASH_BBT;
+
+ /* this->IO_ADDR_R = this->IO_ADDR_W = NDDB */
+ this->waitfunc = monahans_df_waitfunc;
+ this->select_chip = monahans_df_select_chip;
+ this->dev_ready = monahans_df_dev_ready;
+ this->cmdfunc = monahans_df_command;
+ this->read_word= monahans_df_read_word;
+ /*this->write_word= monahans_df_write_word;*/
+ this->read_byte = monahans_df_read_byte;
+ this->read_buf = monahans_df_read_buf;
+ this->write_buf = monahans_df_write_buf;
+ this->verify_buf = monahans_df_verify_buf;
+ this->ecc.hwctl = monahans_df_enable_hwecc;
+ this->ecc.calculate = monahans_df_calculate_ecc;
+ this->ecc.correct = monahans_df_correct_data;
+ this->block_bad = monahans_df_block_bad;
+ this->block_markbad = monahans_df_block_markbad;
+ this->scan_bbt = monahans_df_scan_bbt;
+ this->chip_delay= 25;
+ this->bbt_td = &monahans_bbt_main;
+ this->bbt_md = &monahans_bbt_mirror;
+
+ /* If the NAND flash is small block flash, only 512-byte pagesize
+ * is supported.
+ * Adjust parameters of BBT what is depended on large block nand
+ * flash or small block nand flash.
+ */
+ if (dfc_context.flash_info->oob_size > 16) {
+ this->ecc.layout = &monahans_lb_nand_oob;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 2048;
+ this->ecc.bytes = 24;
+ this->bbt_td->offs = 2;
+ this->bbt_td->veroffs = 6;
+ this->bbt_md->offs = 2;
+ this->bbt_md->veroffs = 6;
+ this->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ monahans_bbt_default.offs = NAND_LARGE_BADBLOCK_POS;
+ monahans_bbt_default.len = 2;
+ /* when scan_bbt() is executed, bbt version can get */
+ monahans_bbt_default.veroffs = 2;
+ } else {
+ this->ecc.layout = &monahans_sb_nand_oob;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 512;
+ this->ecc.bytes = 6;
+ this->bbt_td->offs = 8;
+ this->bbt_td->veroffs = 12;
+ this->bbt_md->offs = 8;
+ this->bbt_md->veroffs = 12;
+ this->badblockpos = NAND_SMALL_BADBLOCK_POS;
+ monahans_bbt_default.offs = NAND_SMALL_BADBLOCK_POS;
+ monahans_bbt_default.len = 1;
+ monahans_bbt_default.veroffs = 8;
+ }
+
+ info->context = &dfc_context;
+ /* TODO: allocate dma buffer and channel */
+
+ platform_set_drvdata(pdev, monahans_mtd);
+
+ if (nand_scan(monahans_mtd, 1)) {
+ printk(KERN_ERR "Nand scan failed\n");
+ ret = -ENXIO;
+ goto free_irq;
+ }
+
+ /* There is a potential limitation that no more partition can be
+ * added between MassStorage and BBT(last block).
+ *
+ * The last 127 blocks is reserved for relocation table, they aren't
+ * statistical data of mtd size and chip size.
+ *
+ * BBT partitions contains 4 blocks. Two blocks are used to store
+ * main descriptor, the other two are used to store mirror descriptor.
+ */
+ partition_info[PART_NUM - 1].size = (monahans_bbt_main.maxblocks
+ + monahans_bbt_mirror.maxblocks)
+ << this->phys_erase_shift;
+ partition_info[PART_NUM - 1].offset = this->chipsize
+ - partition_info[PART_NUM - 1].size;
+ partition_info[PART_NUM - 2].offset = partition_info[PART_NUM - 3].offset
+ + partition_info[PART_NUM - 3].size;
+ partition_info[PART_NUM - 2].size = this->chipsize
+ - partition_info[PART_NUM - 2].offset
+ - partition_info[PART_NUM - 1].size;
+ add_mtd_partitions(monahans_mtd, partition_info, PART_NUM);
+
+#ifdef CONFIG_DVFM
+ dvfm_notifier.client_data = info;
+ mhn_fv_register_notifier(&dvfm_notifier);
+#endif
+
+ return 0;
+
+free_irq:
+ free_irq(IRQ_NAND, info);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+free_cmd_dma:
+ pxa_free_dma(info->cmd_dma);
+free_data_dma:
+ pxa_free_dma(info->data_dma);
+free_buf:
+ dma_free_writecombine(dev, buf_len, info->data_desc, info->data_desc_addr);
+#else
+free_buf:
+ kfree(info->data_buf);
+#endif
+free_mtd:
+ kfree(monahans_mtd);
+out:
+ return ret;
+
+}
+
+static int __devexit monahans_df_remove(struct platform_device *dev)
+{
+ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ unsigned int data_buf_len = dfc_context.flash_info->page_size +
+ dfc_context.flash_info->oob_size;
+ unsigned int buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
+ ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
+#endif
+
+#ifdef CONFIG_DVFM
+ mhn_fv_unregister_notifier(&dvfm_notifier);
+#endif
+
+ platform_set_drvdata(dev, NULL);
+
+ del_mtd_device(mtd);
+ del_mtd_partitions(mtd);
+ free_irq(IRQ_NAND, info);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+ pxa_free_dma(info->cmd_dma);
+ pxa_free_dma(info->data_dma);
+ dma_free_writecombine(dev, buf_len, info->data_desc,
+ info->data_desc_addr);
+#else
+ kfree(info->data_buf);
+#endif
+ kfree(mtd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int monahans_df_suspend(struct platform_device *dev, pm_message_t state, u32 level)
+{
+ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+
+ if( SUSPEND_DISABLE == level){ /*SUSPEND_NOTIFY*/
+ if (info->state != STATE_READY) {
+ printk(KERN_ERR "current state is %d\n", info->state);
+ return -EAGAIN;
+ }
+ info->state = STATE_SUSPENDED;
+ /*
+ * The PM code need read the mobm from NAND.
+ * So the NAND clock can't be stop here.
+ * The PM code will cover this.
+ */
+ /* pxa_set_cken(CKEN_NAND, 0); */
+ }
+ return 0;
+}
+
+static int monahans_df_resume(struct platform_device *dev, u32 level)
+{
+ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+ (((struct nand_chip *)(mtd->priv))->priv);
+ int status;
+
+ if(RESUME_ENABLE == level){
+ if (info->state != STATE_SUSPENDED)
+ printk(KERN_WARNING "Error State after resume back\n");
+
+ info->state = STATE_READY;
+
+ pxa_set_cken(CKEN_NAND, 1);
+
+ status = dfc_init(&dfc_context, flash_config);
+ if (status) {
+ printk(KERN_ALERT "Monahans NAND device:"
+ "Nand Flash initialize failure!\n");
+ return -ENXIO;
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_DVFM
+static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info)
+{
+ struct monahans_dfc_info *dfc_info =
+ (struct monahans_dfc_info *)client_data;
+
+ switch (cmd) {
+ case FV_NOTIFIER_QUERY_SET :
+ if (dfc_info->state != STATE_READY)
+ return -1;
+ break;
+
+ case FV_NOTIFIER_PRE_SET :
+ break;
+
+ case FV_NOTIFIER_POST_SET :
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+static struct platform_driver monahans_df_driver = {
+ .probe = monahans_df_probe,
+ .remove = __devexit_p(monahans_df_remove),
+#ifdef CONFIG_PM
+ .suspend = monahans_df_suspend,
+ .resume = monahans_df_resume,
+#endif
+ .driver = {
+ .name = "monahans-nand-flash",
+ }
+};
+
+static void __exit monahans_df_cleanup(void)
+{
+ printk(KERN_ERR "Nand driver registered\n");
+ platform_driver_unregister(&monahans_df_driver);
+}
+
+static int __init monahans_df_init(void)
+{
+ return platform_driver_register(&monahans_df_driver);
+}
+
+module_init(monahans_df_init);
+module_exit(monahans_df_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jingqing.xu (jingqing.xu@intel.com)");
+MODULE_DESCRIPTION("Glue logic layer for NAND flash on monahans DFC");
+
+
Index: linux-2.6.23/arch/arm/mach-pxa/zylonite.c
===================================================================
--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite.c 2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/arch/arm/mach-pxa/zylonite.c 2008-02-13 09:11:02.000000000 +0000
@@ -29,6 +29,8 @@
#include "generic.h"
int gpio_backlight;
+int gpio_vsync;
+int gpio_vsync1;
int gpio_eth_irq;
int lcd_id;
@@ -54,6 +56,16 @@
.resource = smc91x_resources,
};
+static struct platform_device nand_device = {
+ .name = "monahans-nand-flash",
+ .id = -1,
+};
+
+static struct platform_device touch_device = {
+ .name = "pxa2xx-touch",
+ .id = -1,
+};
+
#if defined(CONFIG_FB_PXA) || (CONFIG_FB_PXA_MODULES)
static void zylonite_backlight_power(int on)
{
@@ -96,7 +108,7 @@
};
static struct pxafb_mode_info sharp_ls037_modes[] = {
- [0] = {
+ [1] = {
.pixclock = 158000,
.xres = 240,
.yres = 320,
@@ -109,8 +121,8 @@
.lower_margin = 3,
.sync = 0,
},
- [1] = {
- .pixclock = 39700,
+ [0] = {
+ .pixclock = 45000,
.xres = 480,
.yres = 640,
.bpp = 16,
@@ -137,6 +149,11 @@
/* backlight GPIO: output, default on */
gpio_direction_output(gpio_backlight, 1);
+ gpio_direction_output(gpio_vsync, 0);
+ gpio_direction_output(gpio_vsync1, 0);
+
+ printk(KERN_ERR "LCD ID is %x\n", lcd_id);
+
if (lcd_id & 0x20) {
set_pxa_fb_info(&zylonite_sharp_lcd_info);
return;
@@ -169,6 +186,8 @@
smc91x_resources[1].start = gpio_to_irq(gpio_eth_irq);
smc91x_resources[1].end = gpio_to_irq(gpio_eth_irq);
platform_device_register(&smc91x_device);
+ platform_device_register(&nand_device);
+ platform_device_register(&touch_device);
zylonite_init_lcd();
}
Index: linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c
===================================================================
--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite_pxa300.c 2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c 2008-02-13 14:01:13.000000000 +0000
@@ -62,12 +62,12 @@
GPIO110_UART3_RXD,
/* AC97 */
- GPIO23_AC97_nACRESET,
+ /*GPIO23_AC97_nACRESET,
GPIO24_AC97_SYSCLK,
GPIO29_AC97_BITCLK,
GPIO25_AC97_SDATA_IN_0,
GPIO27_AC97_SDATA_OUT,
- GPIO28_AC97_SYNC,
+ GPIO28_AC97_SYNC,*/
/* Keypad */
GPIO107_KP_DKIN_0,
@@ -104,6 +104,41 @@
/* Ethernet */
GPIO2_nCS3,
GPIO99_GPIO,
+
+ /* NAND */
+ MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
+ MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
+ MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
+
+ /* AC97 */
+ MFP_CFG_X(GPIO23, AF1, DS03X, PULL_LOW),
+ MFP_CFG_X(GPIO27, AF1, DS03X, PULL_LOW),
+ MFP_CFG_X(GPIO28, AF1, DS03X, PULL_LOW),
+ MFP_CFG_X(GPIO29, AF1, DS03X, PULL_LOW),
+ MFP_CFG_X(GPIO25, AF1, DS03X, PULL_LOW),
+
+ MFP_CFG_X(GPIO26, AF0, DS01X, PULL_LOW), /* Interrupt */
+ MFP_CFG_X(GPIO24, AF0, DS03X, PULL_LOW), /*SYSCLK external */
+ MFP_CFG_X(GPIO11, AF0, DS01X, PULL_LOW),
};
static mfp_cfg_t pxa310_mfp_cfg[] __initdata = {
@@ -163,6 +198,9 @@
pxa3xx_mfp_write(lcd_detect_pins[i], mfpr_save[i]);
}
+extern int gpio_vsync;
+extern int gpio_vsync1;
+
void __init zylonite_pxa300_init(void)
{
if (cpu_is_pxa300() || cpu_is_pxa310()) {
@@ -174,6 +212,8 @@
/* GPIO pin assignment */
gpio_backlight = mfp_to_gpio(MFP_PIN_GPIO20);
+ gpio_vsync = mfp_to_gpio(GPIO76_LCD_VSYNC);
+ gpio_vsync1 = mfp_to_gpio(GPIO71_LCD_LDD_17);
}
if (cpu_is_pxa300()) {
Index: linux-2.6.23/drivers/video/pxafb.c
===================================================================
--- linux-2.6.23.orig/drivers/video/pxafb.c 2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/drivers/video/pxafb.c 2008-02-13 00:59:45.000000000 +0000
@@ -1543,9 +1543,9 @@
if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)
dev_warn(&dev->dev, "machine LCCR0 setting contains illegal bits: %08x\n",
inf->lccr0 & LCCR0_INVALID_CONFIG_MASK);
- if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
- dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
- inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
+ //if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
+ // dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
+ // inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
if (inf->lccr0 & LCCR0_DPD &&
((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||
(inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||
Index: linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h
===================================================================
--- linux-2.6.23.orig/include/asm-arm/arch-pxa/mfp-pxa300.h 2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h 2008-02-13 00:59:45.000000000 +0000
@@ -175,13 +175,13 @@
#define GPIO68_LCD_LDD_14 MFP_CFG_DRV(GPIO68, AF1, DS01X)
#define GPIO69_LCD_LDD_15 MFP_CFG_DRV(GPIO69, AF1, DS01X)
#define GPIO70_LCD_LDD_16 MFP_CFG_DRV(GPIO70, AF1, DS01X)
-#define GPIO71_LCD_LDD_17 MFP_CFG_DRV(GPIO71, AF1, DS01X)
+#define GPIO71_LCD_LDD_17 MFP_CFG_DRV(GPIO71, AF0, DS01X)
#define GPIO62_LCD_CS_N MFP_CFG_DRV(GPIO62, AF2, DS01X)
#define GPIO72_LCD_FCLK MFP_CFG_DRV(GPIO72, AF1, DS01X)
#define GPIO73_LCD_LCLK MFP_CFG_DRV(GPIO73, AF1, DS01X)
#define GPIO74_LCD_PCLK MFP_CFG_DRV(GPIO74, AF1, DS01X)
#define GPIO75_LCD_BIAS MFP_CFG_DRV(GPIO75, AF1, DS01X)
-#define GPIO76_LCD_VSYNC MFP_CFG_DRV(GPIO76, AF2, DS01X)
+#define GPIO76_LCD_VSYNC MFP_CFG_DRV(GPIO76, AF0, DS01X)
#define GPIO15_LCD_CS_N MFP_CFG_DRV(GPIO15, AF2, DS01X)
#define GPIO127_LCD_CS_N MFP_CFG_DRV(GPIO127, AF1, DS01X)