linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] Add support for Samsung SoC OneNAND controllers
@ 2010-04-28 15:46 Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size Marek Szyprowski
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series add support for Samsung SoC OneNAND controllers found
on S3C64xx, S5PC100 and S5PC110 chips. A few extensions to OneNAND core
were required to make the driver working properly on all supported SoCs.

The required platform setup code has been posted to relevant mailing
list (ARM Kernel: "[PATCH] ARM: Samsung: Add platform support code for
OneNAND controller" thread).

Changes since v1:
* addressed all coding style issues reported by Ben Dooks
* moved all resource definitions to platform device resources
* moved all resource definitions for different SoCs from regs-onenand.h to
  arch/arm/mach-{s3c64xx,s5pc100,s5pv210}/include/mach/map.h
* moved all #ifdef conditional code to separate functions

This patch series includes:

[PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size
[PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core
[PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read
[PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode
[PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
@ 2010-04-28 15:46 ` Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core Marek Szyprowski
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Kyungmin Park <kyungmin.park@samsung.com>

This patch adds support for OneNAND chips that have 4KB page size.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |   32 +++++++++++++++++++-------------
 include/linux/mtd/onenand.h        |    4 ++++
 2 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 32f0ed3..1b26f50 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -397,7 +397,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 		value = onenand_bufferram_address(this, block);
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
-		if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this))
+		if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this) ||
+		    ONENAND_IS_4KB_PAGE(this))
 			/* It is always BufferRAM0 */
 			ONENAND_SET_BUFFERRAM0(this);
 		else
@@ -426,7 +427,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 		case FLEXONENAND_CMD_RECOVER_LSB:
 		case ONENAND_CMD_READ:
 		case ONENAND_CMD_READOOB:
-			if (ONENAND_IS_MLC(this))
+			if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
 				/* It is always BufferRAM0 */
 				dataram = ONENAND_SET_BUFFERRAM0(this);
 			else
@@ -466,11 +467,11 @@ static inline int onenand_read_ecc(struct onenand_chip *this)
 {
 	int ecc, i, result = 0;
 
-	if (!FLEXONENAND(this))
+	if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this))
 		return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
 
 	for (i = 0; i < 4; i++) {
-		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i);
+		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2);
 		if (likely(!ecc))
 			continue;
 		if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
@@ -1425,7 +1426,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 	int ret;
 
 	onenand_get_device(mtd, FL_READING);
-	ret = ONENAND_IS_MLC(this) ?
+	ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
 		onenand_mlc_read_ops_nolock(mtd, from, &ops) :
 		onenand_read_ops_nolock(mtd, from, &ops);
 	onenand_release_device(mtd);
@@ -1460,7 +1461,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 
 	onenand_get_device(mtd, FL_READING);
 	if (ops->datbuf)
-		ret = ONENAND_IS_MLC(this) ?
+		ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
 			onenand_mlc_read_ops_nolock(mtd, from, ops) :
 			onenand_read_ops_nolock(mtd, from, ops);
 	else
@@ -1926,7 +1927,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 		 * 2 PLANE, MLC, and Flex-OneNAND do not support
 		 * write-while-program feature.
 		 */
-		if (!ONENAND_IS_2PLANE(this) && !first) {
+		if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) {
 			ONENAND_SET_PREV_BUFFERRAM(this);
 
 			ret = this->wait(mtd, FL_WRITING);
@@ -1957,7 +1958,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 		/*
 		 * 2 PLANE, MLC, and Flex-OneNAND wait here
 		 */
-		if (ONENAND_IS_2PLANE(this)) {
+		if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) {
 			ret = this->wait(mtd, FL_WRITING);
 
 			/* In partial page write we don't update bufferram */
@@ -2084,7 +2085,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 			memcpy(oobbuf + column, buf, thislen);
 		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
-		if (ONENAND_IS_MLC(this)) {
+		if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) {
 			/* Set main area of DataRAM to 0xff*/
 			memset(this->page_buf, 0xff, mtd->writesize);
 			this->write_bufferram(mtd, ONENAND_DATARAM,
@@ -3027,7 +3028,7 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
 	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
 	this->wait(mtd, FL_OTPING);
 
-	ret = ONENAND_IS_MLC(this) ?
+	ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
 		onenand_mlc_read_ops_nolock(mtd, from, &ops) :
 		onenand_read_ops_nolock(mtd, from, &ops);
 
@@ -3372,7 +3373,10 @@ static void onenand_check_features(struct mtd_info *mtd)
 	/* Lock scheme */
 	switch (density) {
 	case ONENAND_DEVICE_DENSITY_4Gb:
-		this->options |= ONENAND_HAS_2PLANE;
+		if (ONENAND_IS_DDP(this))
+			this->options |= ONENAND_HAS_2PLANE;
+		else
+			this->options |= ONENAND_HAS_4KB_PAGE;
 
 	case ONENAND_DEVICE_DENSITY_2Gb:
 		/* 2Gb DDP does not have 2 plane */
@@ -3393,7 +3397,7 @@ static void onenand_check_features(struct mtd_info *mtd)
 		break;
 	}
 
-	if (ONENAND_IS_MLC(this))
+	if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
 		this->options &= ~ONENAND_HAS_2PLANE;
 
 	if (FLEXONENAND(this)) {
@@ -3407,6 +3411,8 @@ static void onenand_check_features(struct mtd_info *mtd)
 		printk(KERN_DEBUG "Chip support all block unlock\n");
 	if (this->options & ONENAND_HAS_2PLANE)
 		printk(KERN_DEBUG "Chip has 2 plane\n");
+	if (this->options & ONENAND_HAS_4KB_PAGE)
+		printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
 }
 
 /**
@@ -3799,7 +3805,7 @@ static int onenand_probe(struct mtd_info *mtd)
 	/* The data buffer size is equal to page size */
 	mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
 	/* We use the full BufferRAM */
-	if (ONENAND_IS_MLC(this))
+	if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
 		mtd->writesize <<= 1;
 
 	mtd->oobsize = mtd->writesize >> 5;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 5509eb0..c9a3c35 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -175,10 +175,14 @@ struct onenand_chip {
 #define ONENAND_HAS_CONT_LOCK		(0x0001)
 #define ONENAND_HAS_UNLOCK_ALL		(0x0002)
 #define ONENAND_HAS_2PLANE		(0x0004)
+#define ONENAND_HAS_4KB_PAGE		(0x0008)
 #define ONENAND_SKIP_UNLOCK_CHECK	(0x0100)
 #define ONENAND_PAGEBUF_ALLOC		(0x1000)
 #define ONENAND_OOBBUF_ALLOC		(0x2000)
 
+#define ONENAND_IS_4KB_PAGE(this)					\
+	(this->options & ONENAND_HAS_4KB_PAGE)
+
 /*
  * OneNAND Flash Manufacturer ID Codes
  */
-- 
1.6.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size Marek Szyprowski
@ 2010-04-28 15:46 ` Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read Marek Szyprowski
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Kyungmin Park <kyungmin.park@samsung.com>

This patch extends OneNAND core code with support for OneNAND verify
write check. This is done by allocating the buffer for verify read
directly from the core code.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |   13 ++++++++++++-
 include/linux/mtd/onenand.h        |    3 +++
 2 files changed, 15 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 1b26f50..045811f 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -3932,6 +3932,13 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 				__func__);
 			return -ENOMEM;
 		}
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+		this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+		if (!this->verify_buf) {
+			kfree(this->page_buf);
+			return -ENOMEM;
+		}
+#endif
 		this->options |= ONENAND_PAGEBUF_ALLOC;
 	}
 	if (!this->oob_buf) {
@@ -4059,8 +4066,12 @@ void onenand_release(struct mtd_info *mtd)
 		kfree(this->bbm);
 	}
 	/* Buffers allocated by onenand_scan */
-	if (this->options & ONENAND_PAGEBUF_ALLOC)
+	if (this->options & ONENAND_PAGEBUF_ALLOC) {
 		kfree(this->page_buf);
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+		kfree(this->verify_buf);
+#endif
+	}
 	if (this->options & ONENAND_OOBBUF_ALLOC)
 		kfree(this->oob_buf);
 	kfree(mtd->eraseregions);
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index c9a3c35..9b43268 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -125,6 +125,9 @@ struct onenand_chip {
 	flstate_t		state;
 	unsigned char		*page_buf;
 	unsigned char		*oob_buf;
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+	unsigned char		*verify_buf;
+#endif
 
 	int			subpagesize;
 	struct nand_ecclayout	*ecclayout;
-- 
1.6.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core Marek Szyprowski
@ 2010-04-28 15:46 ` Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode Marek Szyprowski
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Kyungmin Park <kyungmin.park@samsung.com>

This patch adds a new callback for the underlying drivers, which is
called instead of accessing the buffer ram directly. This callback will
be used by Samsung OneNAND driver to implement DMA transfers on S5PC110
SoC.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |    6 ++----
 include/linux/mtd/onenand.h        |    2 ++
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 045811f..9827ab7 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1635,7 +1635,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
 static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
 {
 	struct onenand_chip *this = mtd->priv;
-	void __iomem *dataram;
 	int ret = 0;
 	int thislen, column;
 
@@ -1655,10 +1654,9 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
 
 		onenand_update_bufferram(mtd, addr, 1);
 
-		dataram = this->base + ONENAND_DATARAM;
-		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
+		this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
 
-		if (memcmp(buf, dataram + column, thislen))
+		if (memcmp(buf, this->verify_buf, thislen))
 			return -EBADMSG;
 
 		len -= thislen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 9b43268..c26ff86 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -212,6 +212,8 @@ struct mtd_partition;
 
 struct onenand_platform_data {
 	void		(*mmcontrol)(struct mtd_info *mtd, int sync_read);
+	int		(*read_bufferram)(struct mtd_info *mtd, int area,
+			unsigned char *buffer, int offset, size_t count);
 	struct mtd_partition *parts;
 	unsigned int	nr_parts;
 };
-- 
1.6.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
                   ` (2 preceding siblings ...)
  2010-04-28 15:46 ` [PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read Marek Szyprowski
@ 2010-04-28 15:46 ` Marek Szyprowski
  2010-04-28 15:46 ` [PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver Marek Szyprowski
  2010-05-04 13:21 ` [PATCH v2] Add support for Samsung SoC OneNAND controllers Artem Bityutskiy
  5 siblings, 0 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Kyungmin Park <kyungmin.park@samsung.com>

Some chips fails to identify properly when SYNC_WRITE mode is enabled
(the example is OneNAND on S5PC110 SoC). This patch adds a workaround
for such chips.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 9827ab7..26caf25 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -3763,6 +3763,12 @@ static int onenand_probe(struct mtd_info *mtd)
 	/* Restore system configuration 1 */
 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
 
+	/* Workaround */
+	if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) {
+		bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
+		bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+	}
+
 	/* Check manufacturer ID */
 	if (onenand_check_maf(bram_maf_id))
 		return -ENXIO;
@@ -3782,6 +3788,9 @@ static int onenand_probe(struct mtd_info *mtd)
 	this->device_id = dev_id;
 	this->version_id = ver_id;
 
+	/* Check OneNAND features */
+	onenand_check_features(mtd);
+
 	density = onenand_get_density(dev_id);
 	if (FLEXONENAND(this)) {
 		this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
@@ -3833,9 +3842,6 @@ static int onenand_probe(struct mtd_info *mtd)
 	else
 		mtd->size = this->chipsize;
 
-	/* Check OneNAND features */
-	onenand_check_features(mtd);
-
 	/*
 	 * We emulate the 4KiB page and 256KiB erase block size
 	 * But oobsize is still 64 bytes.
-- 
1.6.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
                   ` (3 preceding siblings ...)
  2010-04-28 15:46 ` [PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode Marek Szyprowski
@ 2010-04-28 15:46 ` Marek Szyprowski
  2010-05-04 13:21 ` [PATCH v2] Add support for Samsung SoC OneNAND controllers Artem Bityutskiy
  5 siblings, 0 replies; 7+ messages in thread
From: Marek Szyprowski @ 2010-04-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Kyungmin Park <kyungmin.park@samsung.com>

This patch adds a driver for OneNAND controller on Samsung SoCs.
Following SoCs are supported: S3C6400, S3C6410, S5PC100 and S5PC110.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/mtd/onenand/Kconfig   |    7 +
 drivers/mtd/onenand/Makefile  |    1 +
 drivers/mtd/onenand/samsung.c | 1071 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1079 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/onenand/samsung.c

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 3a9f157..9a49d68 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -30,6 +30,13 @@ config MTD_ONENAND_OMAP2
 	  Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU
 	  via the GPMC memory controller.
 
+config MTD_ONENAND_SAMSUNG
+        tristate "OneNAND on Samsung SOC controller support"
+        depends on MTD_ONENAND && (ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210)
+        help
+          Support for a OneNAND flash device connected to an Samsung SOC
+          S3C64XX/S5PC1XX controller.
+
 config MTD_ONENAND_OTP
 	bool "OneNAND OTP Support"
 	select HAVE_MTD_OTP
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile
index 64b6cc6..2b7884c 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/onenand/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_ONENAND)		+= onenand.o
 # Board specific.
 obj-$(CONFIG_MTD_ONENAND_GENERIC)	+= generic.o
 obj-$(CONFIG_MTD_ONENAND_OMAP2)		+= omap2.o
+obj-$(CONFIG_MTD_ONENAND_SAMSUNG)       += samsung.o
 
 # Simulator
 obj-$(CONFIG_MTD_ONENAND_SIM)		+= onenand_sim.o
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
new file mode 100644
index 0000000..368f4b5
--- /dev/null
+++ b/drivers/mtd/onenand/samsung.c
@@ -0,0 +1,1071 @@
+/*
+ * Samsung S3C64XX/S5PC1XX OneNAND driver
+ *
+ *  Copyright (C) 2008-2010 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *  Marek Szyprowski <m.szyprowski@samsung.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.
+ *
+ * Implementation:
+ *	S3C64XX and S5PC100: emulate the pseudo BufferRAM
+ *	S5PC110: use DMA
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/mach/flash.h>
+#include <plat/regs-onenand.h>
+
+#include <linux/io.h>
+
+enum soc_type {
+	TYPE_S3C6400,
+	TYPE_S3C6410,
+	TYPE_S5PC100,
+	TYPE_S5PC110,
+};
+
+#define ONENAND_ERASE_STATUS		0x00
+#define ONENAND_MULTI_ERASE_SET		0x01
+#define ONENAND_ERASE_START		0x03
+#define ONENAND_UNLOCK_START		0x08
+#define ONENAND_UNLOCK_END		0x09
+#define ONENAND_LOCK_START		0x0A
+#define ONENAND_LOCK_END		0x0B
+#define ONENAND_LOCK_TIGHT_START	0x0C
+#define ONENAND_LOCK_TIGHT_END		0x0D
+#define ONENAND_UNLOCK_ALL		0x0E
+#define ONENAND_OTP_ACCESS		0x12
+#define ONENAND_SPARE_ACCESS_ONLY	0x13
+#define ONENAND_MAIN_ACCESS_ONLY	0x14
+#define ONENAND_ERASE_VERIFY		0x15
+#define ONENAND_MAIN_SPARE_ACCESS	0x16
+#define ONENAND_PIPELINE_READ		0x4000
+
+#define MAP_00				(0x0)
+#define MAP_01				(0x1)
+#define MAP_10				(0x2)
+#define MAP_11				(0x3)
+
+#define S3C64XX_CMD_MAP_SHIFT		24
+#define S5PC1XX_CMD_MAP_SHIFT		26
+
+#define S3C6400_FBA_SHIFT		10
+#define S3C6400_FPA_SHIFT		4
+#define S3C6400_FSA_SHIFT		2
+
+#define S3C6410_FBA_SHIFT		12
+#define S3C6410_FPA_SHIFT		6
+#define S3C6410_FSA_SHIFT		4
+
+#define S5PC100_FBA_SHIFT		13
+#define S5PC100_FPA_SHIFT		7
+#define S5PC100_FSA_SHIFT		5
+
+/* S5PC110 specific definitions */
+#define S5PC110_DMA_SRC_ADDR		0x400
+#define S5PC110_DMA_SRC_CFG		0x404
+#define S5PC110_DMA_DST_ADDR		0x408
+#define S5PC110_DMA_DST_CFG		0x40C
+#define S5PC110_DMA_TRANS_SIZE		0x414
+#define S5PC110_DMA_TRANS_CMD		0x418
+#define S5PC110_DMA_TRANS_STATUS	0x41C
+#define S5PC110_DMA_TRANS_DIR		0x420
+
+#define S5PC110_DMA_CFG_SINGLE		(0x0 << 16)
+#define S5PC110_DMA_CFG_4BURST		(0x2 << 16)
+#define S5PC110_DMA_CFG_8BURST		(0x3 << 16)
+#define S5PC110_DMA_CFG_16BURST		(0x4 << 16)
+
+#define S5PC110_DMA_CFG_INC		(0x0 << 8)
+#define S5PC110_DMA_CFG_CNT		(0x1 << 8)
+
+#define S5PC110_DMA_CFG_8BIT		(0x0 << 0)
+#define S5PC110_DMA_CFG_16BIT		(0x1 << 0)
+#define S5PC110_DMA_CFG_32BIT		(0x2 << 0)
+
+#define S5PC110_DMA_SRC_CFG_READ	(S5PC110_DMA_CFG_16BURST | \
+					S5PC110_DMA_CFG_INC | \
+					S5PC110_DMA_CFG_16BIT)
+#define S5PC110_DMA_DST_CFG_READ	(S5PC110_DMA_CFG_16BURST | \
+					S5PC110_DMA_CFG_INC | \
+					S5PC110_DMA_CFG_32BIT)
+#define S5PC110_DMA_SRC_CFG_WRITE	(S5PC110_DMA_CFG_16BURST | \
+					S5PC110_DMA_CFG_INC | \
+					S5PC110_DMA_CFG_32BIT)
+#define S5PC110_DMA_DST_CFG_WRITE	(S5PC110_DMA_CFG_16BURST | \
+					S5PC110_DMA_CFG_INC | \
+					S5PC110_DMA_CFG_16BIT)
+
+#define S5PC110_DMA_TRANS_CMD_TDC	(0x1 << 18)
+#define S5PC110_DMA_TRANS_CMD_TEC	(0x1 << 16)
+#define S5PC110_DMA_TRANS_CMD_TR	(0x1 << 0)
+
+#define S5PC110_DMA_TRANS_STATUS_TD	(0x1 << 18)
+#define S5PC110_DMA_TRANS_STATUS_TB	(0x1 << 17)
+#define S5PC110_DMA_TRANS_STATUS_TE	(0x1 << 16)
+
+#define S5PC110_DMA_DIR_READ		0x0
+#define S5PC110_DMA_DIR_WRITE		0x1
+
+struct s3c_onenand {
+	struct mtd_info	*mtd;
+	struct platform_device	*pdev;
+	enum soc_type	type;
+	void __iomem	*base;
+	struct resource *base_res;
+	void __iomem	*ahb_addr;
+	struct resource *ahb_res;
+	int		bootram_command;
+	void __iomem	*page_buf;
+	void __iomem	*oob_buf;
+	unsigned int	(*mem_addr)(int fba, int fpa, int fsa);
+	unsigned int	(*cmd_map)(unsigned int type, unsigned int val);
+	void __iomem	*dma_addr;
+	struct resource *dma_res;
+	unsigned long	phys_base;
+#ifdef CONFIG_MTD_PARTITIONS
+	struct mtd_partition *parts;
+#endif
+};
+
+#define CMD_MAP_00(dev, addr)		(dev->cmd_map(MAP_00, ((addr) << 1)))
+#define CMD_MAP_01(dev, mem_addr)	(dev->cmd_map(MAP_01, (mem_addr)))
+#define CMD_MAP_10(dev, mem_addr)	(dev->cmd_map(MAP_10, (mem_addr)))
+#define CMD_MAP_11(dev, addr)		(dev->cmd_map(MAP_11, ((addr) << 2)))
+
+static struct s3c_onenand *onenand;
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL, };
+#endif
+
+static inline int s3c_read_reg(int offset)
+{
+	return readl(onenand->base + offset);
+}
+
+static inline void s3c_write_reg(int value, int offset)
+{
+	writel(value, onenand->base + offset);
+}
+
+static inline int s3c_read_cmd(unsigned int cmd)
+{
+	return readl(onenand->ahb_addr + cmd);
+}
+
+static inline void s3c_write_cmd(int value, unsigned int cmd)
+{
+	writel(value, onenand->ahb_addr + cmd);
+}
+
+#ifdef SAMSUNG_DEBUG
+static void s3c_dump_reg(void)
+{
+	int i;
+
+	for (i = 0; i < 0x400; i += 0x40) {
+		printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			(unsigned int) onenand->base + i,
+			s3c_read_reg(i), s3c_read_reg(i + 0x10),
+			s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30));
+	}
+}
+#endif
+
+static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val)
+{
+	return (type << S3C64XX_CMD_MAP_SHIFT) | val;
+}
+
+static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val)
+{
+	return (type << S5PC1XX_CMD_MAP_SHIFT) | val;
+}
+
+static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa)
+{
+	return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) |
+		(fsa << S3C6400_FSA_SHIFT);
+}
+
+static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa)
+{
+	return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) |
+		(fsa << S3C6410_FSA_SHIFT);
+}
+
+static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa)
+{
+	return (fba << S5PC100_FBA_SHIFT) | (fpa << S5PC100_FPA_SHIFT) |
+		(fsa << S5PC100_FSA_SHIFT);
+}
+
+static void s3c_onenand_reset(void)
+{
+	unsigned long timeout = 0x10000;
+	int stat;
+
+	s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET);
+	while (1 && timeout--) {
+		stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+		if (stat & RST_CMP)
+			break;
+	}
+	stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+	s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
+
+	/* Clear interrupt */
+	s3c_write_reg(0x0, INT_ERR_ACK_OFFSET);
+	/* Clear the ECC status */
+	s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET);
+}
+
+static unsigned short s3c_onenand_readw(void __iomem *addr)
+{
+	struct onenand_chip *this = onenand->mtd->priv;
+	struct device *dev = &onenand->pdev->dev;
+	int reg = addr - this->base;
+	int word_addr = reg >> 1;
+	int value;
+
+	/* It's used for probing time */
+	switch (reg) {
+	case ONENAND_REG_MANUFACTURER_ID:
+		return s3c_read_reg(MANUFACT_ID_OFFSET);
+	case ONENAND_REG_DEVICE_ID:
+		return s3c_read_reg(DEVICE_ID_OFFSET);
+	case ONENAND_REG_VERSION_ID:
+		return s3c_read_reg(FLASH_VER_ID_OFFSET);
+	case ONENAND_REG_DATA_BUFFER_SIZE:
+		return s3c_read_reg(DATA_BUF_SIZE_OFFSET);
+	case ONENAND_REG_TECHNOLOGY:
+		return s3c_read_reg(TECH_OFFSET);
+	case ONENAND_REG_SYS_CFG1:
+		return s3c_read_reg(MEM_CFG_OFFSET);
+
+	/* Used at unlock all status */
+	case ONENAND_REG_CTRL_STATUS:
+		return 0;
+
+	case ONENAND_REG_WP_STATUS:
+		return ONENAND_WP_US;
+
+	default:
+		break;
+	}
+
+	/* BootRAM access control */
+	if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) {
+		if (word_addr == 0)
+			return s3c_read_reg(MANUFACT_ID_OFFSET);
+		if (word_addr == 1)
+			return s3c_read_reg(DEVICE_ID_OFFSET);
+		if (word_addr == 2)
+			return s3c_read_reg(FLASH_VER_ID_OFFSET);
+	}
+
+	value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff;
+	dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__,
+		 word_addr, value);
+	return value;
+}
+
+static void s3c_onenand_writew(unsigned short value, void __iomem *addr)
+{
+	struct onenand_chip *this = onenand->mtd->priv;
+	struct device *dev = &onenand->pdev->dev;
+	unsigned int reg = addr - this->base;
+	unsigned int word_addr = reg >> 1;
+
+	/* It's used for probing time */
+	switch (reg) {
+	case ONENAND_REG_SYS_CFG1:
+		s3c_write_reg(value, MEM_CFG_OFFSET);
+		return;
+
+	case ONENAND_REG_START_ADDRESS1:
+	case ONENAND_REG_START_ADDRESS2:
+		return;
+
+	/* Lock/lock-tight/unlock/unlock_all */
+	case ONENAND_REG_START_BLOCK_ADDRESS:
+		return;
+
+	default:
+		break;
+	}
+
+	/* BootRAM access control */
+	if ((unsigned int)addr < ONENAND_DATARAM) {
+		if (value == ONENAND_CMD_READID) {
+			onenand->bootram_command = 1;
+			return;
+		}
+		if (value == ONENAND_CMD_RESET) {
+			s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET);
+			onenand->bootram_command = 0;
+			return;
+		}
+	}
+
+	dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__,
+		 word_addr, value);
+
+	s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr));
+}
+
+static int s3c_onenand_wait(struct mtd_info *mtd, int state)
+{
+	struct device *dev = &onenand->pdev->dev;
+	unsigned int flags = INT_ACT;
+	unsigned int stat, ecc;
+	unsigned long timeout;
+
+	switch (state) {
+	case FL_READING:
+		flags |= BLK_RW_CMP | LOAD_CMP;
+		break;
+	case FL_WRITING:
+		flags |= BLK_RW_CMP | PGM_CMP;
+		break;
+	case FL_ERASING:
+		flags |= BLK_RW_CMP | ERS_CMP;
+		break;
+	case FL_LOCKING:
+		flags |= BLK_RW_CMP;
+		break;
+	default:
+		break;
+	}
+
+	/* The 20 msec is enough */
+	timeout = jiffies + msecs_to_jiffies(20);
+	while (time_before(jiffies, timeout)) {
+		stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+		if (stat & flags)
+			break;
+
+		if (state != FL_READING)
+			cond_resched();
+	}
+	/* To get correct interrupt status in timeout case */
+	stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+	s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
+
+	/*
+	 * In the Spec. it checks the controller status first
+	 * However if you get the correct information in case of
+	 * power off recovery (POR) test, it should read ECC status first
+	 */
+	if (stat & LOAD_CMP) {
+		ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET);
+		if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
+			dev_info(dev, "%s: ECC error = 0x%04x\n", __func__,
+				 ecc);
+			mtd->ecc_stats.failed++;
+			return -EBADMSG;
+		}
+	}
+
+	if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) {
+		dev_info(dev, "%s: controller error = 0x%04x\n", __func__,
+			 stat);
+		if (stat & LOCKED_BLK)
+			dev_info(dev, "%s: it's locked error = 0x%04x\n",
+				 __func__, stat);
+
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
+			       size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	unsigned int *m, *s;
+	int fba, fpa, fsa = 0;
+	unsigned int mem_addr, cmd_map_01, cmd_map_10;
+	int i, mcount, scount;
+	int index;
+
+	fba = (int) (addr >> this->erase_shift);
+	fpa = (int) (addr >> this->page_shift);
+	fpa &= this->page_mask;
+
+	mem_addr = onenand->mem_addr(fba, fpa, fsa);
+	cmd_map_01 = CMD_MAP_01(onenand, mem_addr);
+	cmd_map_10 = CMD_MAP_10(onenand, mem_addr);
+
+	switch (cmd) {
+	case ONENAND_CMD_READ:
+	case ONENAND_CMD_READOOB:
+	case ONENAND_CMD_BUFFERRAM:
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+	default:
+		break;
+	}
+
+	index = ONENAND_CURRENT_BUFFERRAM(this);
+
+	/*
+	 * Emulate Two BufferRAMs and access with 4 bytes pointer
+	 */
+	m = (unsigned int *) onenand->page_buf;
+	s = (unsigned int *) onenand->oob_buf;
+
+	if (index) {
+		m += (this->writesize >> 2);
+		s += (mtd->oobsize >> 2);
+	}
+
+	mcount = mtd->writesize >> 2;
+	scount = mtd->oobsize >> 2;
+
+	switch (cmd) {
+	case ONENAND_CMD_READ:
+		/* Main */
+		for (i = 0; i < mcount; i++)
+			*m++ = s3c_read_cmd(cmd_map_01);
+		return 0;
+
+	case ONENAND_CMD_READOOB:
+		s3c_write_reg(TSRF, TRANS_SPARE_OFFSET);
+		/* Main */
+		for (i = 0; i < mcount; i++)
+			*m++ = s3c_read_cmd(cmd_map_01);
+
+		/* Spare */
+		for (i = 0; i < scount; i++)
+			*s++ = s3c_read_cmd(cmd_map_01);
+
+		s3c_write_reg(0, TRANS_SPARE_OFFSET);
+		return 0;
+
+	case ONENAND_CMD_PROG:
+		/* Main */
+		for (i = 0; i < mcount; i++)
+			s3c_write_cmd(*m++, cmd_map_01);
+		return 0;
+
+	case ONENAND_CMD_PROGOOB:
+		s3c_write_reg(TSRF, TRANS_SPARE_OFFSET);
+
+		/* Main - dummy write */
+		for (i = 0; i < mcount; i++)
+			s3c_write_cmd(0xffffffff, cmd_map_01);
+
+		/* Spare */
+		for (i = 0; i < scount; i++)
+			s3c_write_cmd(*s++, cmd_map_01);
+
+		s3c_write_reg(0, TRANS_SPARE_OFFSET);
+		return 0;
+
+	case ONENAND_CMD_UNLOCK_ALL:
+		s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10);
+		return 0;
+
+	case ONENAND_CMD_ERASE:
+		s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10);
+		return 0;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area)
+{
+	struct onenand_chip *this = mtd->priv;
+	int index = ONENAND_CURRENT_BUFFERRAM(this);
+	unsigned char *p;
+
+	if (area == ONENAND_DATARAM) {
+		p = (unsigned char *) onenand->page_buf;
+		if (index == 1)
+			p += this->writesize;
+	} else {
+		p = (unsigned char *) onenand->oob_buf;
+		if (index == 1)
+			p += mtd->oobsize;
+	}
+
+	return p;
+}
+
+static int onenand_read_bufferram(struct mtd_info *mtd, int area,
+				  unsigned char *buffer, int offset,
+				  size_t count)
+{
+	unsigned char *p;
+
+	p = s3c_get_bufferram(mtd, area);
+	memcpy(buffer, p + offset, count);
+	return 0;
+}
+
+static int onenand_write_bufferram(struct mtd_info *mtd, int area,
+				   const unsigned char *buffer, int offset,
+				   size_t count)
+{
+	unsigned char *p;
+
+	p = s3c_get_bufferram(mtd, area);
+	memcpy(p + offset, buffer, count);
+	return 0;
+}
+
+static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
+{
+	void __iomem *base = onenand->dma_addr;
+	int status;
+
+	writel(src, base + S5PC110_DMA_SRC_ADDR);
+	writel(dst, base + S5PC110_DMA_DST_ADDR);
+
+	if (direction == S5PC110_DMA_DIR_READ) {
+		writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
+		writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
+	} else {
+		writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
+		writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
+	}
+
+	writel(count, base + S5PC110_DMA_TRANS_SIZE);
+	writel(direction, base + S5PC110_DMA_TRANS_DIR);
+
+	writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
+
+	do {
+		status = readl(base + S5PC110_DMA_TRANS_STATUS);
+	} while (!(status & S5PC110_DMA_TRANS_STATUS_TD));
+
+	if (status & S5PC110_DMA_TRANS_STATUS_TE) {
+		writel(S5PC110_DMA_TRANS_CMD_TEC, base + S5PC110_DMA_TRANS_CMD);
+		writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
+		return -EIO;
+	}
+
+	writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
+
+	return 0;
+}
+
+static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
+		unsigned char *buffer, int offset, size_t count)
+{
+	struct onenand_chip *this = mtd->priv;
+	void __iomem *bufferram;
+	void __iomem *p;
+	void *buf = (void *) buffer;
+	dma_addr_t dma_src, dma_dst;
+	int err;
+
+	p = bufferram = this->base + area;
+	if (ONENAND_CURRENT_BUFFERRAM(this)) {
+		if (area == ONENAND_DATARAM)
+			p += this->writesize;
+		else
+			p += mtd->oobsize;
+	}
+
+	if (offset & 3 || (size_t) buf & 3 ||
+		!onenand->dma_addr || count != mtd->writesize)
+		goto normal;
+
+	/* Handle vmalloc address */
+	if (buf >= high_memory) {
+		struct page *page;
+
+		if (((size_t) buf & PAGE_MASK) !=
+		    ((size_t) (buf + count - 1) & PAGE_MASK))
+			goto normal;
+		page = vmalloc_to_page(buf);
+		if (!page)
+			goto normal;
+		buf = page_address(page) + ((size_t) buf & ~PAGE_MASK);
+	}
+
+	/* DMA routine */
+	dma_src = onenand->phys_base + (p - this->base);
+	dma_dst = dma_map_single(&onenand->pdev->dev,
+			buf, count, DMA_FROM_DEVICE);
+	if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) {
+		dev_err(&onenand->pdev->dev,
+			"Couldn't map a %d byte buffer for DMA\n", count);
+		goto normal;
+	}
+	err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
+			count, S5PC110_DMA_DIR_READ);
+	dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
+
+	if (!err)
+		return 0;
+
+normal:
+	if (count != mtd->writesize) {
+		/* Copy the bufferram to memory to prevent unaligned access */
+		memcpy(this->page_buf, bufferram, mtd->writesize);
+		p = this->page_buf + offset;
+	}
+
+	memcpy(buffer, p, count);
+
+	return 0;
+}
+
+static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state)
+{
+	unsigned int flags = INT_ACT | LOAD_CMP;
+	unsigned int stat;
+	unsigned long timeout;
+
+	/* The 20 msec is enough */
+	timeout = jiffies + msecs_to_jiffies(20);
+	while (time_before(jiffies, timeout)) {
+		stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+		if (stat & flags)
+			break;
+	}
+	/* To get correct interrupt status in timeout case */
+	stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
+	s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
+
+	if (stat & LD_FAIL_ECC_ERR) {
+		s3c_onenand_reset();
+		return ONENAND_BBT_READ_ERROR;
+	}
+
+	if (stat & LOAD_CMP) {
+		int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET);
+		if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
+			s3c_onenand_reset();
+			return ONENAND_BBT_READ_ERROR;
+		}
+	}
+
+	return 0;
+}
+
+static void s3c_onenand_check_lock_status(struct mtd_info *mtd)
+{
+	struct onenand_chip *this = mtd->priv;
+	struct device *dev = &onenand->pdev->dev;
+	unsigned int block, end;
+	int tmp;
+
+	end = this->chipsize >> this->erase_shift;
+
+	for (block = 0; block < end; block++) {
+		unsigned int mem_addr = onenand->mem_addr(block, 0, 0);
+		tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr));
+
+		if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) {
+			dev_err(dev, "block %d is write-protected!\n", block);
+			s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET);
+		}
+	}
+}
+
+static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs,
+				    size_t len, int cmd)
+{
+	struct onenand_chip *this = mtd->priv;
+	int start, end, start_mem_addr, end_mem_addr;
+
+	start = ofs >> this->erase_shift;
+	start_mem_addr = onenand->mem_addr(start, 0, 0);
+	end = start + (len >> this->erase_shift) - 1;
+	end_mem_addr = onenand->mem_addr(end, 0, 0);
+
+	if (cmd == ONENAND_CMD_LOCK) {
+		s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand,
+							     start_mem_addr));
+		s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand,
+							   end_mem_addr));
+	} else {
+		s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand,
+							       start_mem_addr));
+		s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand,
+							     end_mem_addr));
+	}
+
+	this->wait(mtd, FL_LOCKING);
+}
+
+static void s3c_unlock_all(struct mtd_info *mtd)
+{
+	struct onenand_chip *this = mtd->priv;
+	loff_t ofs = 0;
+	size_t len = this->chipsize;
+
+	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+		/* Write unlock command */
+		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+		/* No need to check return value */
+		this->wait(mtd, FL_LOCKING);
+
+		/* Workaround for all block unlock in DDP */
+		if (!ONENAND_IS_DDP(this)) {
+			s3c_onenand_check_lock_status(mtd);
+			return;
+		}
+
+		/* All blocks on another chip */
+		ofs = this->chipsize >> 1;
+		len = this->chipsize >> 1;
+	}
+
+	s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+
+	s3c_onenand_check_lock_status(mtd);
+}
+
+static void s3c_onenand_setup(struct mtd_info *mtd)
+{
+	struct onenand_chip *this = mtd->priv;
+
+	onenand->mtd = mtd;
+
+	if (onenand->type == TYPE_S3C6400) {
+		onenand->mem_addr = s3c6400_mem_addr;
+		onenand->cmd_map = s3c64xx_cmd_map;
+	} else if (onenand->type == TYPE_S3C6410) {
+		onenand->mem_addr = s3c6410_mem_addr;
+		onenand->cmd_map = s3c64xx_cmd_map;
+	} else if (onenand->type == TYPE_S5PC100) {
+		onenand->mem_addr = s5pc100_mem_addr;
+		onenand->cmd_map = s5pc1xx_cmd_map;
+	} else if (onenand->type == TYPE_S5PC110) {
+		/* Use generic onenand functions */
+		onenand->cmd_map = s5pc1xx_cmd_map;
+		this->read_bufferram = s5pc110_read_bufferram;
+		return;
+	} else {
+		BUG();
+	}
+
+	this->read_word = s3c_onenand_readw;
+	this->write_word = s3c_onenand_writew;
+
+	this->wait = s3c_onenand_wait;
+	this->bbt_wait = s3c_onenand_bbt_wait;
+	this->unlock_all = s3c_unlock_all;
+	this->command = s3c_onenand_command;
+
+	this->read_bufferram = onenand_read_bufferram;
+	this->write_bufferram = onenand_write_bufferram;
+}
+
+static int s3c_onenand_probe(struct platform_device *pdev)
+{
+	struct onenand_platform_data *pdata;
+	struct onenand_chip *this;
+	struct mtd_info *mtd;
+	struct resource *r;
+	int size, err;
+	unsigned long onenand_ctrl_cfg = 0;
+
+	pdata = pdev->dev.platform_data;
+	/* No need to check pdata. the platform data is optional */
+
+	size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
+	mtd = kzalloc(size, GFP_KERNEL);
+	if (!mtd) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL);
+	if (!onenand) {
+		err = -ENOMEM;
+		goto onenand_fail;
+	}
+
+	this = (struct onenand_chip *) &mtd[1];
+	mtd->priv = this;
+	mtd->dev.parent = &pdev->dev;
+	mtd->owner = THIS_MODULE;
+	onenand->pdev = pdev;
+	onenand->type = platform_get_device_id(pdev)->driver_data;
+
+	s3c_onenand_setup(mtd);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENOENT;
+		goto ahb_resource_failed;
+	}
+
+	onenand->base_res = request_mem_region(r->start, resource_size(r),
+					       pdev->name);
+	if (!onenand->base_res) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		err = -EBUSY;
+		goto resource_failed;
+	}
+
+	onenand->base = ioremap(r->start, resource_size(r));
+	if (!onenand->base) {
+		dev_err(&pdev->dev, "failed to map memory resource\n");
+		err = -EFAULT;
+		goto ioremap_failed;
+	}
+	/* Set onenand_chip also */
+	this->base = onenand->base;
+
+	/* Use runtime badblock check */
+	this->options |= ONENAND_SKIP_UNLOCK_CHECK;
+
+	if (onenand->type != TYPE_S5PC110) {
+		r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!r) {
+			dev_err(&pdev->dev, "no buffer memory resource defined\n");
+			return -ENOENT;
+			goto ahb_resource_failed;
+		}
+
+		onenand->ahb_res = request_mem_region(r->start, resource_size(r),
+						      pdev->name);
+		if (!onenand->ahb_res) {
+			dev_err(&pdev->dev, "failed to request buffer memory resource\n");
+			err = -EBUSY;
+			goto ahb_resource_failed;
+		}
+
+		onenand->ahb_addr = ioremap(r->start, resource_size(r));
+		if (!onenand->ahb_addr) {
+			dev_err(&pdev->dev, "failed to map buffer memory resource\n");
+			err = -EINVAL;
+			goto ahb_ioremap_failed;
+		}
+
+		/* Allocate 4KiB BufferRAM */
+		onenand->page_buf = kzalloc(SZ_4K, GFP_KERNEL);
+		if (!onenand->page_buf) {
+			err = -ENOMEM;
+			goto page_buf_fail;
+		}
+
+		/* Allocate 128 SpareRAM */
+		onenand->oob_buf = kzalloc(128, GFP_KERNEL);
+		if (!onenand->oob_buf) {
+			err = -ENOMEM;
+			goto oob_buf_fail;
+		}
+
+		/* S3C doesn't handle subpage write */
+		mtd->subpage_sft = 0;
+		this->subpagesize = mtd->writesize;
+
+	} else { /* S5PC110 */
+		r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!r) {
+			dev_err(&pdev->dev, "no dma memory resource defined\n");
+			return -ENOENT;
+			goto dma_resource_failed;
+		}
+
+		onenand->dma_res = request_mem_region(r->start, resource_size(r),
+						      pdev->name);
+		if (!onenand->dma_res) {
+			dev_err(&pdev->dev, "failed to request dma memory resource\n");
+			err = -EBUSY;
+			goto dma_resource_failed;
+		}
+
+		onenand->dma_addr = ioremap(r->start, resource_size(r));
+		if (!onenand->dma_addr) {
+			dev_err(&pdev->dev, "failed to map dma memory resource\n");
+			err = -EINVAL;
+			goto dma_ioremap_failed;
+		}
+
+		onenand->phys_base = onenand->base_res->start;
+
+		onenand_ctrl_cfg = readl(onenand->dma_addr + 0x100);
+		if ((onenand_ctrl_cfg & ONENAND_SYS_CFG1_SYNC_WRITE) &&
+		    onenand->dma_addr)
+			writel(onenand_ctrl_cfg & ~ONENAND_SYS_CFG1_SYNC_WRITE,
+					onenand->dma_addr + 0x100);
+		else
+			onenand_ctrl_cfg = 0;
+	}
+
+	if (onenand_scan(mtd, 1)) {
+		err = -EFAULT;
+		goto scan_failed;
+	}
+
+	if (onenand->type == TYPE_S5PC110) {
+		if (onenand_ctrl_cfg && onenand->dma_addr)
+			writel(onenand_ctrl_cfg, onenand->dma_addr + 0x100);
+	} else {
+		/* S3C doesn't handle subpage write */
+		mtd->subpage_sft = 0;
+		this->subpagesize = mtd->writesize;
+	}
+
+	if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ)
+		dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n");
+
+#ifdef CONFIG_MTD_PARTITIONS
+	err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0);
+	if (err > 0)
+		add_mtd_partitions(mtd, onenand->parts, err);
+	else if (err <= 0 && pdata && pdata->parts)
+		add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts);
+	else
+#endif
+		err = add_mtd_device(mtd);
+
+	platform_set_drvdata(pdev, mtd);
+
+	return 0;
+
+scan_failed:
+	if (onenand->dma_addr)
+		iounmap(onenand->dma_addr);
+dma_ioremap_failed:
+	if (onenand->dma_res)
+		release_mem_region(onenand->dma_res->start,
+				   resource_size(onenand->dma_res));
+	kfree(onenand->oob_buf);
+oob_buf_fail:
+	kfree(onenand->page_buf);
+page_buf_fail:
+	if (onenand->ahb_addr)
+		iounmap(onenand->ahb_addr);
+ahb_ioremap_failed:
+	if (onenand->ahb_res)
+		release_mem_region(onenand->ahb_res->start,
+				   resource_size(onenand->ahb_res));
+dma_resource_failed:
+ahb_resource_failed:
+	iounmap(onenand->base);
+ioremap_failed:
+	if (onenand->base_res)
+		release_mem_region(onenand->base_res->start,
+				   resource_size(onenand->base_res));
+resource_failed:
+	kfree(onenand);
+onenand_fail:
+	kfree(mtd);
+	return err;
+}
+
+static int __devexit s3c_onenand_remove(struct platform_device *pdev)
+{
+	struct mtd_info *mtd = platform_get_drvdata(pdev);
+
+	onenand_release(mtd);
+	if (onenand->ahb_addr)
+		iounmap(onenand->ahb_addr);
+	if (onenand->ahb_res)
+		release_mem_region(onenand->ahb_res->start,
+				   resource_size(onenand->ahb_res));
+	if (onenand->dma_addr)
+		iounmap(onenand->dma_addr);
+	if (onenand->dma_res)
+		release_mem_region(onenand->dma_res->start,
+				   resource_size(onenand->dma_res));
+
+	iounmap(onenand->base);
+	release_mem_region(onenand->base_res->start,
+			   resource_size(onenand->base_res));
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(onenand->oob_buf);
+	kfree(onenand->page_buf);
+	kfree(onenand);
+	kfree(mtd);
+	return 0;
+}
+
+static int s3c_pm_ops_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mtd_info *mtd = platform_get_drvdata(pdev);
+	struct onenand_chip *this = mtd->priv;
+
+	this->wait(mtd, FL_PM_SUSPENDED);
+	return mtd->suspend(mtd);
+}
+
+static  int s3c_pm_ops_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mtd_info *mtd = platform_get_drvdata(pdev);
+	struct onenand_chip *this = mtd->priv;
+
+	mtd->resume(mtd);
+	this->unlock_all(mtd);
+	return 0;
+}
+
+static const struct dev_pm_ops s3c_pm_ops = {
+	.suspend	= s3c_pm_ops_suspend,
+	.resume		= s3c_pm_ops_resume,
+};
+
+static struct platform_device_id s3c_onenand_driver_ids[] = {
+	{
+		.name		= "s3c6400-onenand",
+		.driver_data	= TYPE_S3C6400,
+	}, {
+		.name		= "s3c6410-onenand",
+		.driver_data	= TYPE_S3C6410,
+	}, {
+		.name		= "s5pc100-onenand",
+		.driver_data	= TYPE_S5PC100,
+	}, {
+		.name		= "s5pc110-onenand",
+		.driver_data	= TYPE_S5PC110,
+	}, { },
+};
+MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids);
+
+static struct platform_driver s3c_onenand_driver = {
+	.driver         = {
+		.name	= "samsung-onenand",
+		.pm	= &s3c_pm_ops,
+	},
+	.id_table	= s3c_onenand_driver_ids,
+	.probe          = s3c_onenand_probe,
+	.remove         = __devexit_p(s3c_onenand_remove),
+};
+
+static int __init s3c_onenand_init(void)
+{
+	return platform_driver_register(&s3c_onenand_driver);
+}
+
+static void __exit s3c_onenand_exit(void)
+{
+	platform_driver_unregister(&s3c_onenand_driver);
+}
+
+module_init(s3c_onenand_init);
+module_exit(s3c_onenand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("Samsung OneNAND controller support");
-- 
1.6.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2] Add support for Samsung SoC OneNAND controllers
  2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
                   ` (4 preceding siblings ...)
  2010-04-28 15:46 ` [PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver Marek Szyprowski
@ 2010-05-04 13:21 ` Artem Bityutskiy
  5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2010-05-04 13:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2010-04-28 at 17:46 +0200, Marek Szyprowski wrote:
> This patch series add support for Samsung SoC OneNAND controllers found
> on S3C64xx, S5PC100 and S5PC110 chips. A few extensions to OneNAND core
> were required to make the driver working properly on all supported SoCs.
> 
> The required platform setup code has been posted to relevant mailing
> list (ARM Kernel: "[PATCH] ARM: Samsung: Add platform support code for
> OneNAND controller" thread).
> 
> Changes since v1:
> * addressed all coding style issues reported by Ben Dooks
> * moved all resource definitions to platform device resources
> * moved all resource definitions for different SoCs from regs-onenand.h to
>   arch/arm/mach-{s3c64xx,s5pc100,s5pv210}/include/mach/map.h
> * moved all #ifdef conditional code to separate functions
> 
> This patch series includes:
> 
> [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size
> [PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core
> [PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read
> [PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode
> [PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver

Pushed this series to my l2-mtd-2.6.git / dunno

-- 
Best Regards,
Artem Bityutskiy (????? ????????)

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-05-04 13:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-04-28 15:46 [PATCH v2] Add support for Samsung SoC OneNAND controllers Marek Szyprowski
2010-04-28 15:46 ` [PATCH 1/5] drivers: mtd: onenand: add support for chips with 4KB page size Marek Szyprowski
2010-04-28 15:46 ` [PATCH 2/5] drivers: mtd: onenand: allocate verify buffer in the core Marek Szyprowski
2010-04-28 15:46 ` [PATCH 3/5] drivers: mtd: onenand: add new callback for bufferram read Marek Szyprowski
2010-04-28 15:46 ` [PATCH 4/5] drivers: mtd: onenand: add workaround for SYNC_WRITE mode Marek Szyprowski
2010-04-28 15:46 ` [PATCH 5/5] drivers: mtd: add Samsung SoC OneNAND driver Marek Szyprowski
2010-05-04 13:21 ` [PATCH v2] Add support for Samsung SoC OneNAND controllers Artem Bityutskiy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).