Patchwork [U-Boot,v3,07/11] mtd: nand: add Faraday FTNANDC021 NAND controller support

login
register
mail settings
Submitter Kuo-Jung Su
Date April 26, 2013, 8:02 a.m.
Message ID <1366963360-2987-8-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/239719/
State Superseded
Delegated to: Scott Wood
Headers show

Comments

Kuo-Jung Su - April 26, 2013, 8:02 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

Faraday FTNANDC021 is a integrated NAND flash controller.
It use a build-in command table to abstract the underlying
NAND flash control logic.

For example:

Issuing a command 0x10 to FTNANDC021 would result in
a page write + a read status operation.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
CC: Scott Wood <scottwood@freescale.com>
---
 README                        |    7 +
 drivers/mtd/nand/Makefile     |    1 +
 drivers/mtd/nand/ftnandc021.c |  724 +++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/ftnandc021.h |  137 ++++++++
 include/faraday/nand.h        |   34 ++
 5 files changed, 903 insertions(+)
 create mode 100644 drivers/mtd/nand/ftnandc021.c
 create mode 100644 drivers/mtd/nand/ftnandc021.h
 create mode 100644 include/faraday/nand.h
Scott Wood - April 26, 2013, 11:41 p.m.
On 04/26/2013 03:02:36 AM, Kuo-Jung Su wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
> 
> Faraday FTNANDC021 is a integrated NAND flash controller.
> It use a build-in command table to abstract the underlying
> NAND flash control logic.
> 
> For example:
> 
> Issuing a command 0x10 to FTNANDC021 would result in
> a page write + a read status operation.
> 
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> CC: Scott Wood <scottwood@freescale.com>
> ---
>  README                        |    7 +
>  drivers/mtd/nand/Makefile     |    1 +
>  drivers/mtd/nand/ftnandc021.c |  724  
> +++++++++++++++++++++++++++++++++++++++++
>  drivers/mtd/nand/ftnandc021.h |  137 ++++++++
>  include/faraday/nand.h        |   34 ++
>  5 files changed, 903 insertions(+)
>  create mode 100644 drivers/mtd/nand/ftnandc021.c
>  create mode 100644 drivers/mtd/nand/ftnandc021.h
>  create mode 100644 include/faraday/nand.h
> 
> diff --git a/README b/README
> index 862bb3e..adc198f 100644
> --- a/README
> +++ b/README
> @@ -3872,6 +3872,13 @@ Low Level (hardware related) configuration  
> options:
>  		- drivers/mtd/nand/ndfc.c
>  		- drivers/mtd/nand/mxc_nand.c
> 
> +- CONFIG_SYS_NAND_TIMING
> +		Defined to tell the NAND controller that the NAND chip  
> is using
> +		a customized timing parameters.
> +		Not all NAND drivers use this symbol.
> +		Example of drivers that use it:
> +		- drivers/mtd/nand/ftnandc021.c

This doesn't seem to have any standardized meaning (even if that meaning
is applicable to only a subset of controllers), so please call it
CONFIG_SYS_FTNANDC021_TIMING and document the ftnandc021-specific
semantics.

> diff --git a/drivers/mtd/nand/ftnandc021.c  
> b/drivers/mtd/nand/ftnandc021.c
> new file mode 100644
> index 0000000..39c181f
> --- /dev/null
> +++ b/drivers/mtd/nand/ftnandc021.c
> @@ -0,0 +1,724 @@
> +/*
> + * Faraday NAND Flash Controller
> + *
> + * (C) Copyright 2010 Faraday Technology
> + * Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is released under the terms of GPL v2 and any later  
> version.
> + * See the file COPYING in the root directory of the source tree for  
> details.
> + */
> +
> +#include <common.h>
> +#include <asm/errno.h>
> +#include <asm/io.h>
> +#include <asm/unaligned.h>
> +#include <nand.h>
> +#include <malloc.h>
> +#include <faraday/nand.h>
> +
> +#include "ftnandc021.h"
> +
> +#define CFG_HWECC	/* Enable hardware ECC */

If this is really to be optional, call it CONFIG_FTNANDC021_ECC (HWECC
suggests the alternative is SW ECC, not no ECC) and define it in the
board config file (or better, invert it to CONFIG_FTNANDC021_NO_ECC so
ECC is the default).

If it's not meant to be optional, just remove the non-ECC code.

If the ECC doesn't cause major problems in a reasonable use case, I'd
rather see the non-ECC code just removed.

> +static struct nand_ecclayout ftnandc021_ecclayout[] = {
> +	{ /* page size = 512 (oob size = 16) */
> +		.eccbytes = 6,
> +		.eccpos = { 0, 1, 2, 3, 6, 7 },
> +		.oobfree = {
> +#ifdef CFG_HWECC
> +			{ 9, 3 },
> +#else
> +			{ 8, 4 },
> +#endif
> +		}
> +	},
> +	{ /* page size = 2048 (oob size = 64) */
> +		.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 = {
> +#ifdef CFG_HWECC
> +			{ 9, 3 },
> +#else
> +			{ 8, 4 },
> +#endif
> +		},
> +	},
> +	{ /* page size = 4096 (oob size = 128) */
> +		.eccbytes = 48,
> +		.eccpos = {
> +			80, 81, 82, 83, 84, 85, 86, 87,
> +			88, 89, 90, 91, 92, 93, 94, 95,
> +			96, 97, 98, 99, 100, 101, 102, 103,
> +			104, 105, 106, 107, 108, 109, 110, 111,
> +			112, 113, 114, 115, 116, 117, 118, 119,
> +			120, 121, 122, 123, 124, 125, 126, 127
> +		},
> +		.oobfree = {
> +#ifdef CFG_HWECC
> +			{ 9, 7 },
> +#else
> +			{ 8, 8 },
> +#endif
> +		},
> +	},
> +};

Shouldn't .eccpos depend on HWECC?

> +static int ftnandc021_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
> +	uint8_t *read_ecc, uint8_t *calc_ecc)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	struct faraday_nand_chip *info = chip->priv;
> +	struct ftnandc021_chip *priv = info->priv;
> +	struct ftnandc021_regs __iomem *regs = priv->regs;
> +	uint32_t st = readl(&regs->ecc_sr);
> +	int ret = 0;
> +
> +	if (st & ECC_SR_CERR) {
> +		printf("ftnandc021: ecc corection error\n");
> +		ret = -EIO;
> +	} else if (st & ECC_SR_ERR) {
> +		printf("ftnandc021: ecc error\n");
> +		ret = -EIO;
> +	}
> +
> +	return ret;

Can you detect correctable errors?

> +#ifdef CFG_HWECC
> +	chip->ecc.bytes          = chip->ecc.layout->eccbytes;
> +	chip->ecc.size           = info->pgsz;
> +	chip->ecc.steps          = 1;

Is it really all in one step, regardless of page size?

-Scott
Kuo-Jung Su - April 29, 2013, 3:28 a.m.
2013/4/27 Scott Wood <scottwood@freescale.com>:
> On 04/26/2013 03:02:36 AM, Kuo-Jung Su wrote:
>>
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> Faraday FTNANDC021 is a integrated NAND flash controller.
>> It use a build-in command table to abstract the underlying
>> NAND flash control logic.
>>
>> For example:
>>
>> Issuing a command 0x10 to FTNANDC021 would result in
>> a page write + a read status operation.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> CC: Scott Wood <scottwood@freescale.com>
>> ---
>>  README                        |    7 +
>>  drivers/mtd/nand/Makefile     |    1 +
>>  drivers/mtd/nand/ftnandc021.c |  724
>> +++++++++++++++++++++++++++++++++++++++++
>>  drivers/mtd/nand/ftnandc021.h |  137 ++++++++
>>  include/faraday/nand.h        |   34 ++
>>  5 files changed, 903 insertions(+)
>>  create mode 100644 drivers/mtd/nand/ftnandc021.c
>>  create mode 100644 drivers/mtd/nand/ftnandc021.h
>>  create mode 100644 include/faraday/nand.h
>>
>> diff --git a/README b/README
>> index 862bb3e..adc198f 100644
>> --- a/README
>> +++ b/README
>> @@ -3872,6 +3872,13 @@ Low Level (hardware related) configuration options:
>>                 - drivers/mtd/nand/ndfc.c
>>                 - drivers/mtd/nand/mxc_nand.c
>>
>> +- CONFIG_SYS_NAND_TIMING
>> +               Defined to tell the NAND controller that the NAND chip is
>> using
>> +               a customized timing parameters.
>> +               Not all NAND drivers use this symbol.
>> +               Example of drivers that use it:
>> +               - drivers/mtd/nand/ftnandc021.c
>
>
> This doesn't seem to have any standardized meaning (even if that meaning
> is applicable to only a subset of controllers), so please call it
> CONFIG_SYS_FTNANDC021_TIMING and document the ftnandc021-specific
> semantics.
>
>

Got it, thanks

>> diff --git a/drivers/mtd/nand/ftnandc021.c b/drivers/mtd/nand/ftnandc021.c
>> new file mode 100644
>> index 0000000..39c181f
>> --- /dev/null
>> +++ b/drivers/mtd/nand/ftnandc021.c
>> @@ -0,0 +1,724 @@
>> +/*
>> + * Faraday NAND Flash Controller
>> + *
>> + * (C) Copyright 2010 Faraday Technology
>> + * Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is released under the terms of GPL v2 and any later version.
>> + * See the file COPYING in the root directory of the source tree for
>> details.
>> + */
>> +
>> +#include <common.h>
>> +#include <asm/errno.h>
>> +#include <asm/io.h>
>> +#include <asm/unaligned.h>
>> +#include <nand.h>
>> +#include <malloc.h>
>> +#include <faraday/nand.h>
>> +
>> +#include "ftnandc021.h"
>> +
>> +#define CFG_HWECC      /* Enable hardware ECC */
>
>
> If this is really to be optional, call it CONFIG_FTNANDC021_ECC (HWECC
> suggests the alternative is SW ECC, not no ECC) and define it in the
> board config file (or better, invert it to CONFIG_FTNANDC021_NO_ECC so
> ECC is the default).
>
> If it's not meant to be optional, just remove the non-ECC code.
>
> If the ECC doesn't cause major problems in a reasonable use case, I'd
> rather see the non-ECC code just removed.
>
>

Got it, thanks
I'll just remove the non-ECC codes later

>> +static struct nand_ecclayout ftnandc021_ecclayout[] = {
>> +       { /* page size = 512 (oob size = 16) */
>> +               .eccbytes = 6,
>> +               .eccpos = { 0, 1, 2, 3, 6, 7 },
>> +               .oobfree = {
>> +#ifdef CFG_HWECC
>> +                       { 9, 3 },
>> +#else
>> +                       { 8, 4 },
>> +#endif
>> +               }
>> +       },
>> +       { /* page size = 2048 (oob size = 64) */
>> +               .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 = {
>> +#ifdef CFG_HWECC
>> +                       { 9, 3 },
>> +#else
>> +                       { 8, 4 },
>> +#endif
>> +               },
>> +       },
>> +       { /* page size = 4096 (oob size = 128) */
>> +               .eccbytes = 48,
>> +               .eccpos = {
>> +                       80, 81, 82, 83, 84, 85, 86, 87,
>> +                       88, 89, 90, 91, 92, 93, 94, 95,
>> +                       96, 97, 98, 99, 100, 101, 102, 103,
>> +                       104, 105, 106, 107, 108, 109, 110, 111,
>> +                       112, 113, 114, 115, 116, 117, 118, 119,
>> +                       120, 121, 122, 123, 124, 125, 126, 127
>> +               },
>> +               .oobfree = {
>> +#ifdef CFG_HWECC
>> +                       { 9, 7 },
>> +#else
>> +                       { 8, 8 },
>> +#endif
>> +               },
>> +       },
>> +};
>
>
> Shouldn't .eccpos depend on HWECC?
>
>

Actually it means nothing here, the ECC function is designed to be some kind of
a blackbox to users (i.e softwares), the actual position of ECC codes
are not documented
in the datasheet, I have to dig it out from RTL which I do not have
permission to access.

And there are only 5 bytes are free to users in 2K page flash, users
actually could only
control the 'value' of the 5 bytes, the actual position of these data
are hard-coded in RTL level.

P.S: The 5-bytes are:  1-byte Bad Block Info + 4-bytes data in OOB.

>> +static int ftnandc021_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
>> +       uint8_t *read_ecc, uint8_t *calc_ecc)
>> +{
>> +       struct nand_chip *chip = mtd->priv;
>> +       struct faraday_nand_chip *info = chip->priv;
>> +       struct ftnandc021_chip *priv = info->priv;
>> +       struct ftnandc021_regs __iomem *regs = priv->regs;
>> +       uint32_t st = readl(&regs->ecc_sr);
>> +       int ret = 0;
>> +
>> +       if (st & ECC_SR_CERR) {
>> +               printf("ftnandc021: ecc corection error\n");
>> +               ret = -EIO;
>> +       } else if (st & ECC_SR_ERR) {
>> +               printf("ftnandc021: ecc error\n");
>> +               ret = -EIO;
>> +       }
>> +
>> +       return ret;
>
>
> Can you detect correctable errors?
>
>

No, the FTNANDC021 does not have this function.
Like I said, it was designed for SSD applications, which usually uses
some sort of
bare-metal software, and cares nothing about if it has encounter
correctable ECC error.

>> +#ifdef CFG_HWECC
>> +       chip->ecc.bytes          = chip->ecc.layout->eccbytes;
>> +       chip->ecc.size           = info->pgsz;
>> +       chip->ecc.steps          = 1;
>
>
> Is it really all in one step, regardless of page size?
>

Theoretical, NO.
It uses R-S or BCH for Forwarding Error Correction, which is selected
at RTL level.
Which means it actually 522 bytes per step at RS and 528 bytes per step at BCH.

However it's all blackbox to software, the FTNANDC021 simply supports only
page read, so looks like everything is done at 1 step.

> -Scott



--
Best wishes,
Kuo-Jung Su
Kuo-Jung Su - April 30, 2013, 1:33 a.m.
2013/4/30 Scott Wood <scottwood@freescale.com>:
> On 04/28/2013 10:28:14 PM, Kuo-Jung Su wrote:
>>
>> 2013/4/27 Scott Wood <scottwood@freescale.com>:
>>
>> > Shouldn't .eccpos depend on HWECC?
>> >
>> >
>>
>> Actually it means nothing here, the ECC function is designed to be some
>> kind of
>> a blackbox to users (i.e softwares), the actual position of ECC codes
>> are not documented
>> in the datasheet, I have to dig it out from RTL which I do not have
>> permission to access.
>
>
> Maybe get rid of .eccpos altogether, and supply your own (much simpler)
> hwecc read/write_page functions -- similar to what fsl_elbc_nand, which also
> has transparent (but not hidden) ECC, does?  This would also let you get rid
> of hwctl/calculate/correct.
>
>

Got it, thanks
I'll check it later.

>> > Is it really all in one step, regardless of page size?
>> >
>>
>> Theoretical, NO.
>> It uses R-S or BCH for Forwarding Error Correction, which is selected
>> at RTL level.
>> Which means it actually 522 bytes per step at RS and 528 bytes per step at
>> BCH.
>>
>> However it's all blackbox to software, the FTNANDC021 simply supports only
>> page read, so looks like everything is done at 1 step.
>
>
> It would matter if you could detect correctable errors, but since you can't,
> it doesn't.
>

Got it, thanks

> -Scott



--
Best wishes,
Kuo-Jung Su

Patch

diff --git a/README b/README
index 862bb3e..adc198f 100644
--- a/README
+++ b/README
@@ -3872,6 +3872,13 @@  Low Level (hardware related) configuration options:
 		- drivers/mtd/nand/ndfc.c
 		- drivers/mtd/nand/mxc_nand.c
 
+- CONFIG_SYS_NAND_TIMING
+		Defined to tell the NAND controller that the NAND chip is using
+		a customized timing parameters.
+		Not all NAND drivers use this symbol.
+		Example of drivers that use it:
+		- drivers/mtd/nand/ftnandc021.c
+
 - CONFIG_SYS_NDFC_EBC0_CFG
 		Sets the EBC0_CFG register for the NDFC. If not defined
 		a default value will be used.
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 35769c5..f6f89f0 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -63,6 +63,7 @@  COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
 COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o
 COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
 COBJS-$(CONFIG_NAND_FSMC) += fsmc_nand.o
+COBJS-$(CONFIG_NAND_FTNANDC021) += ftnandc021.o
 COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o
 COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o
 COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
diff --git a/drivers/mtd/nand/ftnandc021.c b/drivers/mtd/nand/ftnandc021.c
new file mode 100644
index 0000000..39c181f
--- /dev/null
+++ b/drivers/mtd/nand/ftnandc021.c
@@ -0,0 +1,724 @@ 
+/*
+ * Faraday NAND Flash Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <nand.h>
+#include <malloc.h>
+#include <faraday/nand.h>
+
+#include "ftnandc021.h"
+
+#define CFG_HWECC	/* Enable hardware ECC */
+
+struct ftnandc021_chip {
+	void __iomem *regs;
+	int col;    /* current column address */
+	int row;    /* current row address/page index */
+	int cmd;    /* current NAND command code */
+	int cmd_hc; /* current FTNANDC021 command code */
+
+	struct {
+		int idx;
+		int len;
+		u8  dat[NAND_MAX_OOBSIZE];
+	} buf;
+};
+
+static struct nand_ecclayout ftnandc021_ecclayout[] = {
+	{ /* page size = 512 (oob size = 16) */
+		.eccbytes = 6,
+		.eccpos = { 0, 1, 2, 3, 6, 7 },
+		.oobfree = {
+#ifdef CFG_HWECC
+			{ 9, 3 },
+#else
+			{ 8, 4 },
+#endif
+		}
+	},
+	{ /* page size = 2048 (oob size = 64) */
+		.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 = {
+#ifdef CFG_HWECC
+			{ 9, 3 },
+#else
+			{ 8, 4 },
+#endif
+		},
+	},
+	{ /* page size = 4096 (oob size = 128) */
+		.eccbytes = 48,
+		.eccpos = {
+			80, 81, 82, 83, 84, 85, 86, 87,
+			88, 89, 90, 91, 92, 93, 94, 95,
+			96, 97, 98, 99, 100, 101, 102, 103,
+			104, 105, 106, 107, 108, 109, 110, 111,
+			112, 113, 114, 115, 116, 117, 118, 119,
+			120, 121, 122, 123, 124, 125, 126, 127
+		},
+		.oobfree = {
+#ifdef CFG_HWECC
+			{ 9, 7 },
+#else
+			{ 8, 8 },
+#endif
+		},
+	},
+};
+
+static inline int ftnandc021_ckst(struct ftnandc021_chip *priv)
+{
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint32_t st = readl(&regs->idr[1]);
+
+	if (st & NAND_STATUS_FAIL)
+		return -EIO;
+
+	if (!(st & NAND_STATUS_READY))
+		return -EBUSY;
+
+	if (!(st & NAND_STATUS_WP))
+		return -EIO;
+
+	return 0;
+}
+
+static inline int ftnandc021_wait(struct ftnandc021_chip *priv)
+{
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	char rc = 'c';
+	ulong ts;
+
+	for (ts = get_timer(0); get_timer(ts) < 200; ) {
+		if (readl(&regs->sr) & SR_ECC) {
+			rc = 'e';
+			break;
+		}
+		if (!(readl(&regs->acr) & ACR_START)) {
+			rc = 0;
+			break;
+		}
+	}
+
+	switch (rc) {
+	case 'e':
+		printf("ftnandc021: ecc timeout, cmd_hc=%d\n",
+			priv->cmd_hc);
+		break;
+	case 'c':
+		printf("ftnandc021: cmd timeout, cmd_hc=%d\n",
+			priv->cmd_hc);
+		break;
+	default:
+		break;
+	}
+
+	return rc ? -ETIMEDOUT : 0;
+}
+
+#ifdef CFG_HWECC
+
+static int ftnandc021_reset(struct nand_chip *chip);
+static int ftnandc021_command(struct ftnandc021_chip *priv, uint32_t cmd);
+static void ftnandc021_write_oob(struct mtd_info *mtd,
+	const uint8_t *buf, int len);
+
+static void ftnandc021_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+	/* nothing needs to be done */
+}
+
+static int ftnandc021_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
+	uint8_t *ecc_code)
+{
+	return 0;
+}
+
+static int ftnandc021_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
+	uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint32_t st = readl(&regs->ecc_sr);
+	int ret = 0;
+
+	if (st & ECC_SR_CERR) {
+		printf("ftnandc021: ecc corection error\n");
+		ret = -EIO;
+	} else if (st & ECC_SR_ERR) {
+		printf("ftnandc021: ecc error\n");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int ftnandc021_read_page_raw(struct mtd_info *mtd,
+	struct nand_chip *chip, uint8_t *buf, int page)
+{
+	printf("ftnandc021: read_page_raw is not supported\n");
+	return -EIO;
+}
+
+static void ftnandc021_write_page_raw(struct mtd_info *mtd,
+	struct nand_chip *chip, const uint8_t *buf)
+{
+	printf("ftnandc021: write_page_raw is not supported\n");
+}
+
+#endif /* #ifdef CFG_HWECC */
+
+static int ftnandc021_command(struct ftnandc021_chip *priv, uint32_t cmd)
+{
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	int ret = 0;
+
+	priv->cmd_hc = cmd;
+
+	writel(ACR_START | ACR_CMD(cmd), &regs->acr);
+
+	/*
+	 * pgread    : (We have queued data at the IO port)
+	 * pgwrite   : (We have queued data at the IO port)
+	 * bkerase   : nand_wait + nand_ckst
+	 * oobwr     : nand_wait + nand_ckst
+	 * otherwise : nand_wait
+	 */
+	switch (cmd) {
+	case FTNANDC021_CMD_RDPG:
+	case FTNANDC021_CMD_WRPG:
+		break;
+	case FTNANDC021_CMD_ERBLK:
+	case FTNANDC021_CMD_WROOB:
+		ret = ftnandc021_wait(priv) || ftnandc021_ckst(priv);
+		break;
+	default:
+		ret = ftnandc021_wait(priv);
+	}
+
+	return ret;
+}
+
+static int ftnandc021_reset(struct nand_chip *chip)
+{
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint32_t ts, bk, pg, ac, mask;
+#ifdef CONFIG_SYS_NAND_TIMING
+	uint32_t timing[] = CONFIG_SYS_NAND_TIMING;
+
+	writel(timing[0], &regs->atr[0]);
+	writel(timing[1], &regs->atr[1]);
+#endif
+
+	writel(0, &regs->ier);
+	writel(0, &regs->pir);
+	writel(0xff, &regs->bbiwr);
+	writel(0xffffffff, &regs->lsnwr);
+	writel(0xffffffff, &regs->crcwr);
+
+	if (chip->options & NAND_BUSWIDTH_16)
+		writel(FCR_SWCRC | FCR_IGNCRC | FCR_16BIT, &regs->fcr);
+	else
+		writel(FCR_SWCRC | FCR_IGNCRC, &regs->fcr);
+
+	/* chip reset */
+	mask = SRR_CHIP_RESET;
+#ifdef CFG_HWECC
+	mask |= SRR_ECC_EN;
+#endif
+	writel(mask, &regs->srr);
+	for (ts = get_timer(0); get_timer(ts) < 500; ) {
+		if (readl(&regs->srr) & SRR_CHIP_RESET)
+			continue;
+		break;
+	}
+	if (readl(&regs->srr) & SRR_CHIP_RESET) {
+		printf("ftnandc021: reset failed\n");
+		return -ENXIO;
+	}
+
+	/* sanity check on page size */
+	if (info->pgsz != 512 && info->pgsz != 2048 && info->pgsz != 4096) {
+		printf("ftnandc021: invalid page size=%d\n", info->pgsz);
+		return -EINVAL;
+	}
+
+	bk = ffs(info->bksz / info->pgsz) - 5;
+	pg = (info->pgsz < 2048) ? 0 : (ffs(info->pgsz) - 11);
+	ac = info->alen - 3;
+
+	writel(MCR_ME(0) | MCR_32GB | (bk << 16) | (pg << 8) | (ac << 10),
+		&regs->mcr);
+
+	/* IO mode = PIO */
+	writel(0, &regs->bcr);
+
+	/* ECC mode */
+	chip->ecc.layout         = ftnandc021_ecclayout + pg;
+#ifdef CFG_HWECC
+	chip->ecc.bytes          = chip->ecc.layout->eccbytes;
+	chip->ecc.size           = info->pgsz;
+	chip->ecc.steps          = 1;
+	chip->ecc.hwctl          = ftnandc021_ecc_hwctl;
+	chip->ecc.calculate      = ftnandc021_ecc_calculate;
+	chip->ecc.correct        = ftnandc021_ecc_correct;
+	chip->ecc.read_page_raw  = ftnandc021_read_page_raw;
+	chip->ecc.write_page_raw = ftnandc021_write_page_raw;
+	chip->ecc.mode           = NAND_ECC_HW;
+#else
+	chip->ecc.mode           = NAND_ECC_NONE;
+#endif
+
+	/* reset the attached flash */
+	if (ftnandc021_command(priv, FTNANDC021_CMD_RESET))
+		return -ENXIO;
+
+	return 0;
+}
+
+/*
+ * Check hardware register for wait status. Returns 1 if device is ready,
+ * 0 if it is still busy.
+ */
+static int ftnandc021_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	int ret = 1;
+
+	if (ftnandc021_wait(priv))
+		ret = 0;
+	else if (!(readl(&regs->sr) & SR_READY))
+		ret = 0;
+
+	return ret;
+}
+
+static int ftnandc021_pio_wait(struct ftnandc021_chip *priv)
+{
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	int ret = -ETIMEDOUT;
+	uint32_t ts;
+
+	for (ts = get_timer(0); get_timer(ts) < 200; ) {
+		if (!(readl(&regs->ior) & IOR_READY))
+			continue;
+		ret = 0;
+		break;
+	}
+
+	if (ret)
+		printf("ftnandc021: pio timeout\n");
+
+	return ret;
+}
+
+static void ftnandc021_read_oob(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint32_t tmp;
+
+	memset(buf, 0xff, len);
+
+	/* bad block */
+	buf[chip->badblockpos] = readl(&regs->bbird) & 0xff;
+
+	/* data */
+	tmp = readl(&regs->crcrd);
+	buf[8] = (tmp >> 0) & 0xff;
+	buf[9] = (tmp >> 8) & 0xff;
+	if (mtd->writesize >=  4096) {
+		buf[12] = (tmp >> 16) & 0xff;
+		buf[13] = (tmp >> 24) & 0xff;
+	}
+
+	tmp = readl(&regs->lsnrd);
+	buf[10] = (tmp >> 0) & 0xff;
+	buf[11] = (tmp >> 8) & 0xff;
+	if (mtd->writesize >= 4096) {
+		buf[14] = (tmp >> 16) & 0xff;
+		buf[15] = (tmp >> 24) & 0xff;
+	}
+}
+
+static void ftnandc021_write_oob(struct mtd_info *mtd,
+	const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint32_t tmp;
+
+	/* bad block */
+	tmp = buf[chip->badblockpos];
+	writel(tmp, &regs->bbiwr);
+
+	/* data */
+#ifdef CFG_HWECC
+	/* mark it as 'not blank' */
+	tmp = 'W';
+#else
+	tmp = buf[8];
+#endif
+	tmp |= buf[9] << 8;
+	if (mtd->writesize >= 4096)
+		tmp |= (buf[12] << 16) | (buf[13] << 24);
+	writel(tmp, &regs->crcwr);
+
+	tmp = buf[10] | (buf[11] << 8);
+	if (mtd->writesize >= 4096)
+		tmp |= (buf[14] << 16) | (buf[15] << 24);
+	writel(tmp, &regs->lsnwr);
+}
+
+static uint8_t ftnandc021_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	uint8_t ret = 0xff;
+
+	switch (priv->cmd_hc) {
+	case FTNANDC021_CMD_RDID:
+	case FTNANDC021_CMD_RDOOB:
+		if (priv->buf.idx >= priv->buf.len)
+			break;
+		ret = priv->buf.dat[priv->buf.idx];
+		priv->buf.idx += 1;
+		break;
+	case FTNANDC021_CMD_RDST:
+		ret = (uint8_t)(readl(&regs->idr[1]) & 0xff);
+		break;
+	default:
+		printf("ftnandc021: unknown cmd=0x%x in read_byte\n",
+			priv->cmd_hc);
+		break;
+	}
+
+	return ret;
+}
+
+static uint16_t ftnandc021_read_word(struct mtd_info *mtd)
+{
+	uint16_t ret = 0xffff;
+	uint8_t *buf = (uint8_t *)&ret;
+
+	/* LSB format */
+	buf[0] = ftnandc021_read_byte(mtd);
+	buf[1] = ftnandc021_read_byte(mtd);
+
+	return ret;
+}
+
+static int ftnandc021_copy_oob(struct mtd_info *mtd, int off,
+	uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	int pos;
+
+	pos = min(off, mtd->oobsize);
+	len = min(mtd->oobsize - pos, len);
+	memcpy(buf, priv->buf.dat + pos, len);
+
+	return len;
+}
+
+/**
+ * Read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void ftnandc021_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	int off;
+
+	if (priv->col >= mtd->writesize) {
+		len = ftnandc021_copy_oob(mtd,
+			priv->col - mtd->writesize, buf, len);
+		priv->col += len;
+		if (priv->cmd == NAND_CMD_READOOB)
+			priv->buf.idx += len;
+		return;
+	}
+
+	if (priv->cmd == NAND_CMD_READOOB)
+		BUG();	/* should never happen */
+
+#ifdef CFG_HWECC
+	/* skip if it's a blank page */
+	if (priv->buf.dat[8] != 'W') {
+		memset(buf, 0xff, len);
+		return;
+	}
+#endif
+
+	off = 0;
+	while (off < len && priv->col < mtd->writesize) {
+		ftnandc021_pio_wait(priv);
+		*(uint32_t *)(buf + off) = readl(&regs->dr);
+		priv->col += 4;
+		off += 4;
+	}
+
+	if (!ftnandc021_wait(priv)
+		&& off < len && priv->col >= mtd->writesize) {
+		len = ftnandc021_copy_oob(mtd,
+			priv->col - mtd->writesize, buf + off, len - off);
+		off += len;
+		priv->col += len;
+	}
+}
+
+/**
+ * Write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void ftnandc021_write_buf(
+	struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+	int off;
+
+	/*
+	 * FTNANDC021 HW design issues:
+	 *
+	 * 1. OOB data must be set before issuing write command,
+	 *    so it's too late to do it right here
+	 * 2. Only after command issued, the data register
+	 *    could accept data.
+	 */
+	if (priv->col >= mtd->writesize)
+		return;
+
+	for (off = 0; off < len && priv->col < mtd->writesize; ) {
+		ftnandc021_pio_wait(priv);
+		writel(*(uint32_t *)(buf + off), &regs->dr);
+		priv->col += 4;
+		off += 4;
+	}
+
+	ftnandc021_wait(priv);
+}
+
+/**
+ * Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ */
+static int ftnandc021_verify_buf(struct mtd_info *mtd,
+	const uint8_t *buf, int len)
+{
+	int ret = 0;
+	uint8_t *tmp;
+
+	len = min_t(int, len, mtd->writesize);
+	tmp = malloc(mtd->writesize);
+
+	if (!tmp) {
+		printf("ftnandc021: out of memory\n");
+		return -ENOMEM;
+	} else {
+		ftnandc021_read_buf(mtd, tmp, len);
+		if (memcmp(tmp, buf, len))
+			ret = -EINVAL;
+	}
+
+	free(tmp);
+	return ret;
+}
+
+static void ftnandc021_cmdfunc(struct mtd_info *mtd,
+	unsigned cmd, int col, int row)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct faraday_nand_chip *info = chip->priv;
+	struct ftnandc021_chip *priv = info->priv;
+	struct ftnandc021_regs __iomem *regs = priv->regs;
+
+	priv->cmd = cmd;
+	priv->col = col;
+	priv->row = row;
+
+	switch (cmd) {
+	case NAND_CMD_READID:	/* 0x90 */
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RDID)) {
+			printf("ftnandc021: RDID failed.\n");
+			break;
+		}
+		priv->buf.idx = 0;
+		priv->buf.len = 8;
+		put_unaligned_le32(readl(&regs->idr[0]),
+			priv->buf.dat);
+		put_unaligned_le32(readl(&regs->idr[1]),
+			priv->buf.dat + 4);
+		break;
+
+	case NAND_CMD_READOOB:	/* 0x50 */
+		writel(row, &regs->pir);
+		writel(1, &regs->pcr);
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RDOOB)) {
+			printf("ftnandc021: RDOOB failed.\n");
+			break;
+		}
+		if (mtd->oobsize > NAND_MAX_OOBSIZE)
+			BUG(); /* should never happen */
+		ftnandc021_read_oob(mtd, priv->buf.dat, mtd->oobsize);
+		priv->buf.idx = 0;
+		priv->buf.len = mtd->oobsize;
+		priv->col = mtd->writesize;
+		break;
+
+	case NAND_CMD_READ0:	/* 0x00 */
+		writel(row, &regs->pir);
+		writel(1, &regs->pcr);
+#ifdef CFG_HWECC
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RDOOB)) {
+			printf("ftnandc021: RDOOB failed.\n");
+			break;
+		}
+		ftnandc021_read_oob(mtd, priv->buf.dat, mtd->oobsize);
+		/* cancel if it's a blank page */
+		if (priv->buf.dat[8] != 'W') {
+			debug("ftnandc021: skip page %d\n", row);
+			break;
+		}
+#endif
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RDPG))
+			printf("ftnandc021: RDPG failed.\n");
+		break;
+
+	case NAND_CMD_ERASE1:	/* 0x60 */
+		writel(row, &regs->pir);
+		writel(1, &regs->pcr);
+		break;
+
+	case NAND_CMD_ERASE2:	/* 0xD0 */
+		if (ftnandc021_command(priv, FTNANDC021_CMD_ERBLK))
+			printf("ftnandc021: ERBLK failed\n");
+		break;
+
+	case NAND_CMD_STATUS:	/* 0x70 */
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RDST))
+			printf("ftnandc021: RDST failed\n");
+		break;
+
+	case NAND_CMD_SEQIN:	/* 0x80 (Write Stage 1.) */
+		writel(row, &regs->pir);
+		writel(1, &regs->pcr);
+		/* OOB data must be set before issuing command */
+		ftnandc021_write_oob(mtd, chip->oob_poi, mtd->oobsize);
+		if (priv->col >= mtd->writesize) {
+			if (ftnandc021_command(priv, FTNANDC021_CMD_WROOB))
+				printf("ftnandc021: WROOB failed\n");
+		} else {
+			if (ftnandc021_command(priv, FTNANDC021_CMD_WRPG))
+				printf("ftnandc021: WRPG failed\n");
+		}
+		break;
+
+	case NAND_CMD_PAGEPROG:	/* 0x10 (Write Stage 2.) */
+		/* nothing needs to be done */
+		break;
+
+	case NAND_CMD_RESET:	/* 0xFF */
+		if (ftnandc021_command(priv, FTNANDC021_CMD_RESET))
+			printf("ftnandc021: RESET failed.\n");
+		break;
+
+	default:
+		printf("ftnandc021: unknown cmd=0x%x\n", cmd);
+	}
+}
+
+/**
+ * hardware specific access to control-lines
+ * @mtd: MTD device structure
+ * @cmd: command to device
+ * @ctrl:
+ * NAND_NCE: bit 0 -> don't care
+ * NAND_CLE: bit 1 -> Command Latch
+ * NAND_ALE: bit 2 -> Address Latch
+ *
+ * NOTE: boards may use different bits for these!!
+ */
+static void ftnandc021_hwcontrol(struct mtd_info *mtd,
+	int cmd, unsigned int ctrl)
+{
+	/* nothing needs to be done */
+}
+
+int ftnandc021_init(struct nand_chip *chip)
+{
+	struct faraday_nand_chip *info;
+	struct ftnandc021_chip *priv;
+
+	info = chip->priv;
+	if (!info)
+		return -EINVAL;
+
+	priv = calloc(1, sizeof(struct ftnandc021_chip));
+	if (!priv)
+		return -ENOMEM;
+	info->priv = priv;
+	priv->regs = info->regs;
+
+	debug("ftnandc021: pg=%dK, bk=%dK, alen=%d\n",
+		   info->pgsz >> 10, info->bksz >> 10, info->alen);
+
+	/* hardware reset */
+	if (ftnandc021_reset(chip))
+		return -EINVAL;
+
+	/* hwcontrol always must be implemented */
+	chip->cmd_ctrl   = ftnandc021_hwcontrol;
+	chip->cmdfunc    = ftnandc021_cmdfunc;
+	chip->dev_ready  = ftnandc021_dev_ready;
+	chip->chip_delay = 0;
+
+	chip->read_byte  = ftnandc021_read_byte;
+	chip->read_word  = ftnandc021_read_word;
+	chip->read_buf   = ftnandc021_read_buf;
+	chip->write_buf  = ftnandc021_write_buf;
+	chip->verify_buf = ftnandc021_verify_buf;
+
+	return 0;
+}
diff --git a/drivers/mtd/nand/ftnandc021.h b/drivers/mtd/nand/ftnandc021.h
new file mode 100644
index 0000000..eb75d73
--- /dev/null
+++ b/drivers/mtd/nand/ftnandc021.h
@@ -0,0 +1,137 @@ 
+/*
+ * Faraday NAND Flash Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef _FTNANDC021_H
+#define _FTNANDC021_H
+
+/* NANDC control registers */
+struct ftnandc021_regs {
+ /* 0x000 ~ 0x0fc */
+	uint32_t ecc_pr[4];/* ECC Parity Register */
+	uint32_t ecc_sr; /* ECC Status Register */
+	uint32_t rsvd0[59];
+
+ /* 0x100 ~ 0x1fc */
+	uint32_t sr;	 /* Status Register */
+	uint32_t acr;	 /* Access Control Register */
+	uint32_t fcr;	 /* Flow Control Register */
+	uint32_t pir;	 /* Page Index Register */
+	uint32_t mcr;	 /* Memory Configuration Register */
+	uint32_t atr[2]; /* AC Timing Register */
+	uint32_t rsvd1[1];
+	uint32_t idr[2]; /* Device ID Register */
+	uint32_t ier;	 /* Interrupt Enable Register */
+	uint32_t iscr;	 /* Interrupt Status Clear Register */
+	uint32_t rsvd2[4];
+	uint32_t bbiwr;	 /* Bad Block Info Write */
+	uint32_t lsn;	 /* LSN Initialize */
+	uint32_t crcwr;	 /* LSN CRC Write */
+	uint32_t lsnwr;	 /* LSN Write */
+	uint32_t bbird;	 /* Bad Block Info Read */
+	uint32_t lsnrd;	 /* LSN Read */
+	uint32_t crcrd;	 /* CRC Read */
+	uint32_t rsvd3[41];
+
+ /* 0x200 ~ 0x2fc */
+	uint32_t rsvd4[1];
+	uint32_t icr;	 /* BMC Interrupt Control Register */
+	uint32_t ior;	 /* BMC PIO Status Register */
+	uint32_t bcr;	 /* BMC Burst Control Register */
+	uint32_t rsvd5[60];
+
+ /* 0x300 ~ 0x3fc */
+	uint32_t dr;	 /* MLC Data Register */
+	uint32_t isr;	 /* MLC Interrupt Status Register */
+	uint32_t pcr;	 /* Page Count Register */
+	uint32_t srr;	 /* MLC Software Reset Register */
+	uint32_t rsvd7[58];
+	uint32_t revr;	 /* Revision Register */
+	uint32_t cfgr;	 /* Configuration Register */
+};
+
+/* bit mask */
+#define ECC_SR_CERR      BIT_MASK(3)  /* correction error */
+#define ECC_SR_ERR       BIT_MASK(2)  /* ecc error */
+#define ECC_SR_DEC       BIT_MASK(1)  /* ecc decode finished */
+#define ECC_SR_ENC       BIT_MASK(0)  /* ecc encode finished */
+
+#define SR_BLANK         BIT_MASK(7)  /* blanking check failed */
+#define SR_ECC           BIT_MASK(6)  /* ecc timeout */
+#define SR_STS           BIT_MASK(4)  /* status error */
+#define SR_CRC           BIT_MASK(3)  /* crc error */
+#define SR_CMD           BIT_MASK(2)  /* command finished */
+#define SR_READY         BIT_MASK(1)  /* chip ready/busy */
+#define SR_ENA           BIT_MASK(0)  /* chip enabled */
+
+#define ACR_CMD(x)       (((x) & 0x1f) << 8) /* command code */
+#define ACR_START        BIT_MASK(7)  /* command start */
+
+#define FCR_SWCRC        BIT_MASK(8)  /* CRC controlled by Software */
+#define FCR_IGNCRC       BIT_MASK(7)  /* Bypass/Ignore CRC checking */
+#define FCR_16BIT        BIT_MASK(4)  /* 16 bit data bus */
+#define FCR_WPROT        BIT_MASK(3)  /* write protected */
+#define FCR_NOSC         BIT_MASK(2)  /* bypass status check error */
+#define FCR_MICRON       BIT_MASK(1)  /* Micron 2-plane command */
+#define FCR_NOBC         BIT_MASK(0)  /* skip blanking check error */
+
+#define IER_ENA          BIT_MASK(7)  /* interrupt enabled */
+#define IER_ECC          BIT_MASK(3)  /* ecc error timeout */
+#define IER_STS          BIT_MASK(2)  /* status error */
+#define IER_CRC          BIT_MASK(1)  /* crc error */
+#define IER_CMD          BIT_MASK(0)  /* command finished */
+
+#define IOR_READY        BIT_MASK(0)  /* PIO ready */
+
+#define SRR_ECC_EN       BIT_MASK(8)  /* ECC enabled */
+#define SRR_NANDC_RESET  BIT_MASK(2)  /* NANDC reset */
+#define SRR_BMC_RESET    BIT_MASK(1)  /* BMC reset */
+#define SRR_ECC_RESET    BIT_MASK(0)  /* ECC reset */
+#define SRR_CHIP_RESET   (SRR_NANDC_RESET | SRR_BMC_RESET | SRR_ECC_RESET)
+
+#define MCR_BS16P        (0 << 16) /* page count per block */
+#define MCR_BS32P        (1 << 16)
+#define MCR_BS64P        (2 << 16)
+#define MCR_BS128P       (3 << 16)
+#define MCR_1PLANE       (0 << 14) /* memory architecture */
+#define MCR_2PLANE       (1 << 14)
+#define MCR_SERIAL       (0 << 12) /* interleaving: off, 2 flash, 4 flash */
+#define MCR_IL2          (1 << 12)
+#define MCR_IL4          (2 << 12)
+#define MCR_ALEN3        (0 << 10) /* address length */
+#define MCR_ALEN4        (1 << 10)
+#define MCR_ALEN5        (2 << 10)
+#define MCR_PS512        (0 << 8)  /* size per page (bytes) */
+#define MCR_PS2048       (1 << 8)
+#define MCR_PS4096       (2 << 8)
+#define MCR_16MB         (0 << 4)  /* flash size */
+#define MCR_32MB         (1 << 4)
+#define MCR_64MB         (2 << 4)
+#define MCR_128MB        (3 << 4)
+#define MCR_256MB        (4 << 4)
+#define MCR_512MB        (5 << 4)
+#define MCR_1GB          (6 << 4)
+#define MCR_2GB          (7 << 4)
+#define MCR_4GB          (8 << 4)
+#define MCR_8GB          (9 << 4)
+#define MCR_16GB         (10 << 4)
+#define MCR_32GB         (11 << 4)
+#define MCR_ME(n)        (1 << (n)) /* module enable, 0 <= n <= 3 */
+
+/* FTNANDC021 integrated command set */
+#define FTNANDC021_CMD_RDID  0x01   /* read id */
+#define FTNANDC021_CMD_RESET 0x02   /* reset flash */
+#define FTNANDC021_CMD_RDST  0x04   /* read status */
+#define FTNANDC021_CMD_RDPG  0x05   /* read page (data + oob) */
+#define FTNANDC021_CMD_RDOOB 0x06   /* read oob */
+#define FTNANDC021_CMD_WRPG  0x10   /* write page (data + oob) */
+#define FTNANDC021_CMD_ERBLK 0x11   /* erase block */
+#define FTNANDC021_CMD_WROOB 0x13   /* write oob */
+
+#endif
diff --git a/include/faraday/nand.h b/include/faraday/nand.h
new file mode 100644
index 0000000..144ec13
--- /dev/null
+++ b/include/faraday/nand.h
@@ -0,0 +1,34 @@ 
+/*
+ * Faraday NAND Flash Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef __FARADAY_NAND_H
+#define __FARADAY_NAND_H
+
+#include <nand.h>
+
+/**
+ * struct faraday_nand_chip - chip level device structure
+ * @regs: base address of hardware registers
+ * @priv: hardware controller specific settings
+ * @alen: address length/cycle
+ * @pgsz: page size (byte)
+ * @bksz: block size (byte)
+ */
+struct faraday_nand_chip {
+	void __iomem *regs;
+	void *priv;
+	uint32_t alen;
+	uint32_t pgsz;
+	uint32_t bksz;
+};
+
+int ftnandc021_init(struct nand_chip *chip);
+
+#endif /* _FARADAY_NAND_H */