ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/drivers/mtd/nand/spinand.c b/marvell/uboot/drivers/mtd/nand/spinand.c
new file mode 100644
index 0000000..781c218
--- /dev/null
+++ b/marvell/uboot/drivers/mtd/nand/spinand.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2003-2013 Marvell Corporation
+ *
+ * Copyright (c) 2009-2010 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <common.h>
+#include <malloc.h>
+#include <spi_flash.h>
+#include <nand.h>
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include "spinand.h"
+#include <mtd/pxa3xx_bbm.h>
+#include <watchdog.h>
+#ifndef CONFIG_SF_DEFAULT_SPEED
+# define CONFIG_SF_DEFAULT_SPEED	1000000
+#endif
+#ifndef CONFIG_SF_DEFAULT_MODE
+# define CONFIG_SF_DEFAULT_MODE		SPI_MODE_3
+#endif
+
+#ifndef CONFIG_SPINAND_CS
+#define CONFIG_SPINAND_CS	25
+#endif
+#define FLASH_TIME_OUT  (20*CONFIG_SYS_HZ)
+#define BUFSIZE (2 * 2048)
+
+
+/* #define CONFIG_MTD_SPINAND_ONDIEECC 1 */
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int enable_hw_ecc;
+static int enable_read_hw_ecc;
+
+static struct nand_ecclayout spinand_oob_64 = {
+	.eccbytes = 24,
+	.eccpos = {
+		1, 2, 3, 4, 5, 6,
+		17, 18, 19, 20, 21, 22,
+		33, 34, 35, 36, 37, 38,
+		49, 50, 51, 52, 53, 54, },
+	.oobavail = 32,
+	.oobfree = {
+		{.offset = 8,
+		.length = 8},
+		{.offset = 24,
+		.length = 8},
+		{.offset = 40,
+		.length = 8},
+		{.offset = 56,
+		.length = 8}, }
+};
+#endif
+
+extern int spi_dma_xfer(struct spi_slave *slave, const u8 *cmd, size_t cmd_len,
+				const u32 *dout, u32 *din, size_t data_len);
+
+int spinand_cmd(struct spi_flash *spi_nand, struct spinand_cmd *cmd)
+{
+	char cmdbuf[16], cmd_len = 1;	
+	int ret, datalen = 0;
+	unsigned long flags = SPI_XFER_BEGIN;
+
+	cmdbuf[0] = cmd->cmd;
+	if (cmd->n_dummy) {
+		cmdbuf[1] = 0x0;
+		cmd_len += cmd->n_dummy;
+	} else if (cmd->addr) {
+		memcpy(&cmdbuf[1], cmd->addr, cmd->n_addr);
+		cmd_len += cmd->n_addr;
+	}
+
+	if (cmd->n_rx)
+		datalen = cmd->n_rx;	
+	else if (cmd->n_tx)
+		datalen = cmd->n_tx;		
+	else
+		flags |= SPI_XFER_END;
+
+	ret = spi_xfer(spi_nand->spi, cmd_len*8, cmdbuf, NULL, flags);
+	if (ret) {
+		debug("SPINAND: Failed to send command (%zu bytes): %d\n",
+				cmd_len, ret);
+	} else if (datalen != 0) {
+		ret = spi_xfer(spi_nand->spi, datalen * 8, cmd->tx_buf,
+			cmd->rx_buf, SPI_XFER_END);
+		if (ret)
+			debug("SPINAND: Failed to transfer %zu bytes of data: %d\n",
+					datalen, ret);
+	}
+
+	return ret;
+}
+
+static int spinand_dma_transfer(struct spi_flash *spi_nand, struct spinand_cmd *cmd)
+{
+	int ret = 0;
+	char cmdbuf[4], cmd_len = 1;	
+
+	cmdbuf[0] = cmd->cmd;
+	if(cmd->addr){
+		memcpy(&cmdbuf[1], cmd->addr, cmd->n_addr);
+		cmd_len += cmd->n_addr;
+	}
+	if (cmd->n_rx)
+		ret = spi_dma_xfer(spi_nand->spi, (u8 *)cmdbuf, cmd_len, NULL,
+			(u32 *)cmd->rx_buf, cmd->n_rx);
+	if (cmd->n_tx)
+		ret = spi_dma_xfer(spi_nand->spi, (u8 *)cmdbuf, cmd_len,
+			(u32 *)cmd->tx_buf, NULL, cmd->n_tx);
+	if (ret != 0) {
+		debug("SPINAND: error %d dma trasfer.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spinand_read_id(struct spi_flash *spi_nand, u8 *id)
+{
+	int retval;
+	u8 nand_id[2];
+	struct spinand_cmd cmd = {0};
+
+	cmd.cmd = CMD_READ_ID;
+	cmd.n_dummy = 1;
+	cmd.n_rx = 2;
+	cmd.rx_buf = &nand_id[0];
+
+	retval = spinand_cmd(spi_nand, &cmd);
+	if (retval != 0) {
+		debug("SPINAND: error %d reading id\n", retval);
+		return retval;
+	}
+	id[0] = nand_id[0];
+	id[1] = nand_id[1];
+
+	return 0;
+}
+
+static int spinand_read_status(struct spi_flash *spi_nand, uint8_t *status)
+{
+	struct spinand_cmd cmd = {0};
+	int ret;
+
+	cmd.cmd = CMD_READ_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_STATUS;
+	cmd.n_rx = 1;
+	cmd.rx_buf = status;
+
+	ret = spinand_cmd(spi_nand, &cmd);
+	if (ret != 0) {
+		debug("SPINAND: error %d read status register\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spinand_get_otp(struct spi_flash *spi_nand, u8 *otp)
+{
+	struct spinand_cmd cmd = {0};
+	int retval;
+
+	cmd.cmd = CMD_READ_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_OTP;
+	cmd.n_rx = 1;
+	cmd.rx_buf = otp;
+
+	retval = spinand_cmd(spi_nand, &cmd);
+	if (retval != 0) {
+		debug("SPINAND: error %d get otp\n", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static int spinand_get_protect(struct spi_flash *spi_nand, u8 *otp)
+{
+	struct spinand_cmd cmd = {0};
+	int retval;
+
+	cmd.cmd = CMD_READ_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_BLOCK_LOCK;
+	cmd.n_rx = 1;
+	cmd.rx_buf = otp;
+
+	retval = spinand_cmd(spi_nand, &cmd);
+	if (retval != 0) {
+		debug("SPINAND error %d get otp\n", retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int spinand_set_otp(struct spi_flash *spi_nand, u8 *otp)
+{
+	int retval;
+	struct spinand_cmd cmd = {0};
+
+	cmd.cmd = CMD_WRITE_REG,
+	cmd.n_addr = 1,
+	cmd.addr[0] = REG_OTP,
+	cmd.n_tx = 1,
+	cmd.tx_buf = otp,
+
+	retval = spinand_cmd(spi_nand, &cmd);
+	if (retval != 0) {
+		debug("SPINAND error %d set otp\n", retval);
+		return retval;
+	}
+	return 0;
+}
+#if 0
+static int wait_till_ready(struct spi_flash *spi_nand)
+{
+	unsigned long timebase;
+	int retval;
+	u8 stat = 0;
+
+	timebase = get_timer(0);
+	do {
+		WATCHDOG_RESET();
+
+		retval = spinand_read_status(spi_nand, &stat);
+		if (retval < 0)
+			return -1;
+		else if (!(stat & 0x1))
+			break;
+
+	} while (get_timer(timebase) < FLASH_TIME_OUT);
+
+	if ((stat & 0x1) == 0)
+		return 0;
+
+	return -1;
+}
+#endif
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int spinand_enable_ecc(struct spi_flash *spi_nand)
+{
+	int retval;
+	u8 otp = 0;
+
+	retval = spinand_get_otp(spi_nand, &otp);
+	if (retval != 0) {
+		debug("SPINAND get otp error.\n");
+		return retval;
+	}
+	if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
+		return 0;
+	} else {
+		otp |= OTP_ECC_MASK;
+		retval = spinand_set_otp(spi_nand, &otp);
+		retval = spinand_get_otp(spi_nand, &otp);
+		return retval;
+	}
+}
+#endif
+
+static int spinand_disable_ecc(struct spi_flash *spi_nand)
+{
+	int retval;
+	u8 otp = 0;
+
+	retval = spinand_get_otp(spi_nand, &otp);
+	if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
+		otp &= ~OTP_ECC_MASK;
+		retval = spinand_set_otp(spi_nand, &otp);
+		retval = spinand_get_otp(spi_nand, &otp);
+		return retval;
+	} else
+		return 0;
+}
+
+static int spinand_write_enable(struct spi_flash *spi_nand)
+{
+	struct spinand_cmd cmd = {0};
+
+	cmd.cmd = CMD_WR_ENABLE;
+	return spinand_cmd(spi_nand, &cmd);
+}
+
+static int spinand_read_page_to_cache(struct spi_flash *spi_nand, u16 page_id)
+{
+	struct spinand_cmd cmd = {0};
+	u16 row;
+
+	row = page_id;
+	cmd.cmd = CMD_READ;
+	cmd.n_addr = 3;
+	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+	cmd.addr[2] = (u8)(row & 0x00ff);
+
+	return spinand_cmd(spi_nand, &cmd);
+}
+
+static int spinand_read_from_cache(struct spi_flash *spi_nand, u16 byte_id,
+		u16 len, u8 *rbuf)
+{
+	struct spinand_cmd cmd = {0};
+	u16 column;
+	int ret;
+
+	column = byte_id;
+	cmd.cmd = CMD_READ_RDM;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+	cmd.addr[1] = (u8)(column & 0x00ff);
+	cmd.addr[2] = (u8)(0xff);
+	cmd.n_dummy = 0;
+	cmd.n_rx = len;
+	cmd.rx_buf = rbuf;
+	ret = spinand_dma_transfer(spi_nand, &cmd);
+	if (ret != 0) {
+		debug("SPINAND error %d read_from_cache\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int spinand_read_page(struct spi_flash *spi_nand, u16 page_id,
+		u16 offset, u16 len, u8 *rbuf)
+{
+	int ret;
+	u8 status = 0;
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+	if (enable_read_hw_ecc) {
+		if (spinand_enable_ecc(spi_nand))
+			debug("SPINAND enable HW ECC failed!");
+	}
+#endif
+	ret = spinand_read_page_to_cache(spi_nand, page_id);
+
+	while (1) {
+		ret = spinand_read_status(spi_nand, &status);	
+		if (ret < 0) {
+			debug("SPINAND err %d read status register\n", ret);
+			return ret;
+		}
+
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+				debug("SPINAND ecc error, page=%d\n", page_id);
+				return 0;
+			}
+			break;
+		}
+	}
+
+	ret = spinand_read_from_cache(spi_nand, offset, len, rbuf);
+	if (ret != 0)
+		debug("SPINAND read from cache failed!!\n");
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+	if (enable_read_hw_ecc) {
+		ret = spinand_disable_ecc(spi_nand);
+		enable_read_hw_ecc = 0;
+	}
+#endif
+	return 0;
+}
+
+static int spinand_program_data_to_cache(struct spi_flash *spi_nand,
+		u16 byte_id, u16 len, u8 *wbuf)
+{
+	struct spinand_cmd cmd = {0};
+	u16 column;
+	int ret;
+	column = byte_id;
+	cmd.cmd = CMD_PROG_PAGE_CLRCACHE;
+	cmd.n_addr = 2;
+	cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+	cmd.addr[1] = (u8)(column & 0x00ff);
+
+	cmd.n_tx = len;
+	cmd.tx_buf = wbuf;
+	ret = spinand_dma_transfer(spi_nand, &cmd);
+	if (ret != 0) {
+		debug("SPINAND error %d read_from_cache\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int spinand_program_execute(struct spi_flash *spi_nand, u16 page_id)
+{
+	struct spinand_cmd cmd = {0};
+	u16 row;
+
+	row = page_id;
+	cmd.cmd = CMD_PROG_PAGE_EXC;
+	cmd.n_addr = 3;
+	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+	cmd.addr[2] = (u8)(row & 0x00ff);
+
+	return spinand_cmd(spi_nand, &cmd);
+}
+
+static int spinand_program_page(struct spi_flash *spi_nand,
+		u16 page_id, u16 offset, u16 len, u8 *buf)
+{
+	int retval;
+	u8 status = 0;
+	uint8_t *wbuf;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+	/* unsigned int i j; */
+	enable_read_hw_ecc = 0;
+	/* wbuf = malloc(2112);
+	spinand_read_page(spi_nand, page_id, 0, 0x840, wbuf);
+	for (i = offset, j = 0; i < len; i++, j++)
+		wbuf[i] &= buf[j]; */
+
+	if (enable_hw_ecc)
+		retval = spinand_enable_ecc(spi_nand);
+#endif
+	wbuf = buf;
+
+	retval = spinand_write_enable(spi_nand);
+
+	retval = spinand_program_data_to_cache(spi_nand, offset, len, wbuf);
+	retval = spinand_program_execute(spi_nand, page_id);
+	while (1) {
+		retval = spinand_read_status(spi_nand, &status);
+		if (retval < 0) {
+			debug("SPINAND error %d reading status register\n",
+					retval);
+			return retval;
+		}
+
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+				debug("SPINAND program error, page %d\n", page_id);
+				return -1;
+			} else
+				break;
+		}
+	}
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+	if (enable_hw_ecc) {
+		retval = spinand_disable_ecc(spi_nand);
+		enable_hw_ecc = 0;
+	}
+#endif
+
+	return 0;
+}
+
+static int spinand_erase_block_erase(struct spi_flash *spi_nand, u16 block_id)
+{
+	struct spinand_cmd cmd = {0};
+	u16 row;
+
+	row = block_id;
+	cmd.cmd = CMD_ERASE_BLK;
+	cmd.n_addr = 3;
+	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+	cmd.addr[2] = (u8)(row & 0x00ff);
+
+	return spinand_cmd(spi_nand, &cmd);
+}
+
+static int spinand_erase_block(struct spi_flash *spi_nand, u16 block_id)
+{
+	int retval;
+	u8 status = 0;
+
+	retval = spinand_write_enable(spi_nand);
+
+	retval = spinand_erase_block_erase(spi_nand, block_id);
+	while (1) {
+		retval = spinand_read_status(spi_nand, &status);
+		if (retval < 0) {
+			debug("SPINAND error %d reading status register\n",
+					(int) retval);
+			return retval;
+		}
+
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+				debug("SPINAND erase error,block %d\n",
+					block_id);
+				return -1;
+			} else
+				break;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int spinand_write_page_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+	const uint8_t *p = buf;
+	int eccsize = chip->ecc.size;
+	int eccsteps = chip->ecc.steps;
+
+	enable_hw_ecc = 1;
+	chip->write_buf(mtd, p, eccsize * eccsteps);
+	return 0;
+}
+
+static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+		uint8_t *buf, int oob_required, int page)
+{
+	u8 status;
+	uint8_t *p = buf;
+	int eccsize = chip->ecc.size;
+	int eccsteps = chip->ecc.steps;
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+
+	enable_read_hw_ecc = 1;
+
+	chip->read_buf(mtd, p, eccsize * eccsteps);
+	if (oob_required)
+		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	while (1) {
+		spinand_read_status(info->spi, &status);
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+				debug("spinand: ECC error\n");
+				mtd->ecc_stats.failed++;
+			} else if ((status & STATUS_ECC_MASK) ==
+					STATUS_ECC_1BIT_CORRECTED)
+				mtd->ecc_stats.corrected++;
+			break;
+		}
+	}
+	return 0;
+
+}
+#endif
+
+static void spinand_select_chip(struct mtd_info *mtd, int dev)
+{
+}
+
+static uint8_t spinand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+	struct nand_state *state = (struct nand_state *)info->priv;
+	u8 data;
+
+	data = state->buf[state->buf_ptr];
+	state->buf_ptr++;
+	return data;
+}
+
+static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+	unsigned long timebase;
+	u8 status;
+
+	timebase = get_timer(0);
+	do {
+		WATCHDOG_RESET();
+
+		spinand_read_status(info->spi, &status);
+		if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL)
+			return NAND_STATUS_FAIL;
+
+		if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL)
+			return NAND_STATUS_FAIL;
+
+		if ((status & STATUS_OIP_MASK) == STATUS_READY)
+			return 0;
+
+	} while (get_timer(timebase) < FLASH_TIME_OUT);
+
+	return 0;
+}
+
+static void spinand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+		int len)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+	struct nand_state *state = (struct nand_state *)info->priv;
+
+	memcpy(state->buf+state->buf_ptr, buf, len);
+	state->buf_ptr += len;
+}
+
+static void spinand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+	struct nand_state *state = (struct nand_state *)info->priv;
+
+	memcpy(buf, state->buf+state->buf_ptr, len);
+	state->buf_ptr += len;
+	flush_dcache_all();
+}
+
+static int spinand_lock_block(struct spi_flash *spi_nand, u8 lock)
+{
+	struct spinand_cmd cmd = {0};
+	int ret;
+	u8 otp = 0;
+
+	cmd.cmd = CMD_WRITE_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_BLOCK_LOCK;
+	cmd.n_tx = 1;
+	cmd.tx_buf = &lock;
+
+	ret = spinand_cmd(spi_nand, &cmd);
+	if (ret != 0) {
+		debug("SPINAND: error %d lock block\n", ret);
+		return ret;
+	}
+
+	ret = spinand_get_protect(spi_nand, &otp);
+	return otp;
+}
+
+static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+		int column, int page)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	struct spinand_info *info = (struct spinand_info *)chip->priv;
+	struct nand_state *state = (struct nand_state *)info->priv;
+	struct pxa3xx_bbm *pxa3xx_bbm = mtd->bbm;
+	loff_t addr;
+
+	if (pxa3xx_bbm && (command == NAND_CMD_READOOB ||
+				command == NAND_CMD_READ0 ||
+				command == NAND_CMD_SEQIN ||
+				command == NAND_CMD_ERASE1)) {
+		addr = (loff_t)page << mtd->writesize_shift;
+		addr = pxa3xx_bbm->search(mtd, addr);
+		page = addr >> mtd->writesize_shift;
+	}
+	switch (command) {
+	/*
+	 * READ0 - read in first  0x800 bytes
+	 */
+	case NAND_CMD_READ1:
+	case NAND_CMD_READ0:
+		state->buf_ptr = 0;
+		spinand_read_page(info->spi, page, 0x0, 0x800, state->buf);
+		break;
+	/* READOOB reads only the OOB because no ECC is performed. */
+	case NAND_CMD_READOOB:
+		state->buf_ptr = 0;
+		spinand_read_page(info->spi, page, 0x800, 0x40, state->buf);
+		break;
+	case NAND_CMD_RNDOUT:
+		state->buf_ptr = column;
+		break;
+	case NAND_CMD_READID:
+		state->buf_ptr = 0;
+		spinand_read_id(info->spi, (u8 *)state->buf);
+		break;
+	case NAND_CMD_PARAM:
+		state->buf_ptr = 0;
+		break;
+	/* ERASE1 stores the block and page address */
+	case NAND_CMD_ERASE1:
+		spinand_erase_block(info->spi, page);
+		break;
+	/* ERASE2 uses the block and page address from ERASE1 */
+	case NAND_CMD_ERASE2:
+		break;
+	/* SEQIN sets up the addr buffer and all registers except the length */
+	case NAND_CMD_SEQIN:
+		state->col = column;
+		state->row = page;
+		state->buf_ptr = 0;
+		break;
+	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+	case NAND_CMD_PAGEPROG:
+		spinand_program_page(info->spi, state->row, state->col,
+				state->buf_ptr, state->buf);
+		break;
+	case NAND_CMD_STATUS:
+		spinand_get_otp(info->spi, state->buf);
+		if (!(state->buf[0] & 0x80))
+			state->buf[0] = 0x80;
+		state->buf_ptr = 0;
+		break;
+	case NAND_CMD_LOCK:
+		if (spinand_lock_block(info->spi, BL_ALL_UNLOCKED))
+			printf("Error: Fail to unlock SPI NAND.\n");
+		break;
+	/* RESET command */
+	case NAND_CMD_RESET:
+		break;
+	default:
+		debug("SPINAND: Unknown CMD: 0x%x\n", command);
+	}
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+	struct spinand_info *info;
+	struct spi_flash *spi_nand;
+	struct nand_state *state;
+	struct spi_slave *spi;
+	int ret;
+	int bus = 0;
+
+	unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
+	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
+
+	info  =	malloc(sizeof(struct spinand_info));
+	if (!info) {
+		fprintf(stderr, "Spinand out of memory. (%d bytes)\n",
+			sizeof(struct spinand_info));
+		ret = -ENOMEM;
+		goto err_alloc_info;
+	}
+	state = malloc(sizeof(struct nand_state));
+	if (!state) {
+		fprintf(stderr, "Spinand out of memory. (%d bytes)\n",
+			sizeof(struct nand_state));
+		ret = -ENOMEM;
+		goto err_alloc_state;
+	}
+	spi_nand = malloc(sizeof(struct spi_flash));
+	if (!spi_nand) {
+		fprintf(stderr, "Spinand out of memory (%d bytes)\n",
+			sizeof(struct spi_flash));
+		ret = -ENOMEM;
+		goto err_alloc_spinand;
+	}
+	spi = malloc(sizeof(struct spi_slave));
+	if (!spi) {
+		fprintf(stderr, "Spinand out of memory (%d bytes)\n",
+			sizeof(struct spi_slave));
+		ret = -ENOMEM;
+		goto err_alloc_spi;
+	}
+
+	info->priv	= state;
+	state->buf_ptr	= 0;
+	state->buf	= malloc(BUFSIZE);
+	if (!state->buf) {
+		fprintf(stderr, "Spinand out of memory (%d bytes)\n",
+			BUFSIZE);
+		ret = -ENOMEM;
+		goto err_alloc_buf;
+	}
+
+	spi_init();
+	spi = spi_setup_slave(bus, CONFIG_SPINAND_CS, speed, mode);
+	if (!spi) {
+		printf("SF: Failed to set up slave\n");
+		ret = 1;
+		goto err_alloc;
+	}
+
+	ret = spi_claim_bus(spi);
+	if (ret) {
+		debug("SF: Failed to claim SPI bus: %d\n", ret);
+		ret = 1;
+		goto err_alloc;
+	}
+
+	spi_nand->spi = spi;
+	info->spi = spi_nand;
+	nand->priv	= info;
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+	nand->ecc.mode	= NAND_ECC_HW;
+	nand->ecc.size	= 0x200;
+	nand->ecc.bytes	= 0x6;
+	nand->ecc.steps	= 0x4;
+
+	nand->ecc.total	= nand->ecc.steps * nand->ecc.bytes;
+	nand->ecc.layout = &spinand_oob_64;
+	nand->ecc.read_page = spinand_read_page_hwecc;
+	nand->ecc.write_page = spinand_write_page_hwecc;
+#else
+	nand->ecc.mode	= NAND_ECC_NONE;
+	ret = spinand_disable_ecc(info->spi);
+#endif
+	nand->read_buf	= spinand_read_buf;
+	nand->write_buf	= spinand_write_buf;
+	nand->read_byte	= spinand_read_byte;
+	nand->cmdfunc	= spinand_cmdfunc;
+	nand->waitfunc	= spinand_wait;
+	nand->options	|= NAND_CACHEPRG;
+	nand->select_chip = spinand_select_chip;
+
+#ifdef CONFIG_BBM
+	nand->scan_bbt		= pxa3xx_scan_bbt;
+	nand->block_markbad	= pxa3xx_block_markbad;
+	nand->block_bad		= pxa3xx_block_bad;
+#endif
+	return 0;
+err_alloc:
+	free(state->buf);
+err_alloc_buf:
+	free(spi);
+err_alloc_spi:
+	free(spi_nand);
+err_alloc_spinand:
+	free(state);
+err_alloc_state:
+	free(info);
+err_alloc_info:
+	return ret;
+}