// SPDX-License-Identifier: GPL-2.0-or-later

/***************************************************************************
 *   Copyright (C) 2009 by David Brownell                                  *
 ***************************************************************************/

/*
 * DaVinci family NAND controller support for OpenOCD.
 *
 * This driver uses hardware ECC (1-bit or 4-bit) unless
 * the chip is accessed in "raw" mode.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "imp.h"
#include "arm_io.h"
#include <target/target.h>

enum ecc {
	HWECC1,		/* all controllers support 1-bit ECC */
	HWECC4,		/* newer chips also have 4-bit ECC hardware */
	HWECC4_INFIX,	/* avoid this layout, except maybe for boot code */
};

struct davinci_nand {
	uint8_t chipsel;		/* chipselect 0..3 == CS2..CS5 */
	uint8_t eccmode;

	/* Async EMIF controller base */
	uint32_t aemif;

	/* NAND chip addresses */
	uint32_t data;				/* without CLE or ALE */
	uint32_t cmd;				/* with CLE */
	uint32_t addr;				/* with ALE */

	/* write acceleration */
	struct arm_nand_data io;

	/* page i/o for the relevant flavor of hardware ECC */
	int (*read_page)(struct nand_device *nand, uint32_t page,
			 uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
	int (*write_page)(struct nand_device *nand, uint32_t page,
			  uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
};

#define NANDFCR         0x60		/* flash control register */
#define NANDFSR         0x64		/* flash status register */
#define NANDFECC        0x70		/* 1-bit ECC data, CS0, 1st of 4 */
#define NAND4BITECCLOAD 0xbc		/* 4-bit ECC, load saved values */
#define NAND4BITECC     0xc0		/* 4-bit ECC data, 1st of 4 */
#define NANDERRADDR     0xd0		/* 4-bit ECC err addr, 1st of 2 */
#define NANDERRVAL      0xd8		/* 4-bit ECC err value, 1st of 2 */

static int halted(struct target *target, const char *label)
{
	if (target->state == TARGET_HALTED)
		return true;

	LOG_ERROR("Target must be halted to use NAND controller (%s)", label);
	return false;
}

static int davinci_init(struct nand_device *nand)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	uint32_t nandfcr;

	if (!halted(target, "init"))
		return ERROR_NAND_OPERATION_FAILED;

	/* We require something else to have configured AEMIF to talk
	 * to NAND chip in this range (including timings and width).
	 */
	target_read_u32(target, info->aemif + NANDFCR, &nandfcr);
	if (!(nandfcr & (1 << info->chipsel))) {
		LOG_ERROR("chip address %08" PRIx32 " not NAND-enabled?", info->data);
		return ERROR_NAND_OPERATION_FAILED;
	}

	/* REVISIT verify:  AxCR must be in 8-bit mode, since that's all we
	 * tested.  16 bit support should work too; but not with 4-bit ECC.
	 */

	return ERROR_OK;
}

static int davinci_reset(struct nand_device *nand)
{
	return ERROR_OK;
}

static int davinci_nand_ready(struct nand_device *nand, int timeout)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	uint32_t nandfsr;

	/* NOTE: return code is zero/error, else success; not ERROR_* */

	if (!halted(target, "ready"))
		return 0;

	do {
		target_read_u32(target, info->aemif + NANDFSR, &nandfsr);

		if (nandfsr & 0x01)
			return 1;

		alive_sleep(1);
	} while (timeout-- > 0);

	return 0;
}

static int davinci_command(struct nand_device *nand, uint8_t command)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;

	if (!halted(target, "command"))
		return ERROR_NAND_OPERATION_FAILED;

	target_write_u8(target, info->cmd, command);
	return ERROR_OK;
}

static int davinci_address(struct nand_device *nand, uint8_t address)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;

	if (!halted(target, "address"))
		return ERROR_NAND_OPERATION_FAILED;

	target_write_u8(target, info->addr, address);
	return ERROR_OK;
}

static int davinci_write_data(struct nand_device *nand, uint16_t data)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;

	if (!halted(target, "write_data"))
		return ERROR_NAND_OPERATION_FAILED;

	target_write_u8(target, info->data, data);
	return ERROR_OK;
}

static int davinci_read_data(struct nand_device *nand, void *data)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;

	if (!halted(target, "read_data"))
		return ERROR_NAND_OPERATION_FAILED;

	target_read_u8(target, info->data, data);
	return ERROR_OK;
}

/* REVISIT a bit of native code should let block reads be MUCH faster */

static int davinci_read_block_data(struct nand_device *nand,
	uint8_t *data, int data_size)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	uint32_t nfdata = info->data;
	uint32_t tmp;

	if (!halted(target, "read_block"))
		return ERROR_NAND_OPERATION_FAILED;

	while (data_size >= 4) {
		target_read_u32(target, nfdata, &tmp);

		data[0] = tmp;
		data[1] = tmp >> 8;
		data[2] = tmp >> 16;
		data[3] = tmp >> 24;

		data_size -= 4;
		data += 4;
	}

	while (data_size > 0) {
		target_read_u8(target, nfdata, data);

		data_size -= 1;
		data += 1;
	}

	return ERROR_OK;
}

static int davinci_write_block_data(struct nand_device *nand,
	uint8_t *data, int data_size)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	uint32_t nfdata = info->data;
	uint32_t tmp;
	int status;

	if (!halted(target, "write_block"))
		return ERROR_NAND_OPERATION_FAILED;

	/* try the fast way first */
	status = arm_nandwrite(&info->io, data, data_size);
	if (status != ERROR_NAND_NO_BUFFER)
		return status;

	/* else do it slowly */
	while (data_size >= 4) {
		tmp = le_to_h_u32(data);
		target_write_u32(target, nfdata, tmp);

		data_size -= 4;
		data += 4;
	}

	while (data_size > 0) {
		target_write_u8(target, nfdata, *data);

		data_size -= 1;
		data += 1;
	}

	return ERROR_OK;
}

static int davinci_write_page(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	struct davinci_nand *info = nand->controller_priv;
	uint8_t *ooballoc = NULL;
	int status;

	if (!nand->device)
		return ERROR_NAND_DEVICE_NOT_PROBED;
	if (!halted(nand->target, "write_page"))
		return ERROR_NAND_OPERATION_FAILED;

	/* Always write both data and OOB ... we are not "raw" I/O! */
	if (!data) {
		LOG_ERROR("Missing NAND data; try 'nand raw_access enable'");
		return ERROR_NAND_OPERATION_FAILED;
	}

	/* If we're not given OOB, write 0xff where we don't write ECC codes. */
	switch (nand->page_size) {
		case 512:
			oob_size = 16;
			break;
		case 2048:
			oob_size = 64;
			break;
		case 4096:
			oob_size = 128;
			break;
		default:
			return ERROR_NAND_OPERATION_FAILED;
	}
	if (!oob) {
		ooballoc = malloc(oob_size);
		if (!ooballoc)
			return ERROR_NAND_OPERATION_FAILED;
		oob = ooballoc;
		memset(oob, 0x0ff, oob_size);
	}

	/* REVISIT avoid wasting SRAM:  unless nand->use_raw is set,
	 * use 512 byte chunks.  Read side support will often want
	 * to include oob_size ...
	 */
	info->io.chunk_size = nand->page_size;

	status = info->write_page(nand, page, data, data_size, oob, oob_size);
	free(ooballoc);
	return status;
}

static int davinci_read_page(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	struct davinci_nand *info = nand->controller_priv;

	if (!nand->device)
		return ERROR_NAND_DEVICE_NOT_PROBED;
	if (!halted(nand->target, "read_page"))
		return ERROR_NAND_OPERATION_FAILED;

	return info->read_page(nand, page, data, data_size, oob, oob_size);
}

static void davinci_write_pagecmd(struct nand_device *nand, uint8_t cmd, uint32_t page)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	int page3 = nand->address_cycles - (nand->page_size == 512);

	/* write command ({page,otp}x{read,program} */
	target_write_u8(target, info->cmd, cmd);

	/* column address (beginning-of-page) */
	target_write_u8(target, info->addr, 0);
	if (nand->page_size > 512)
		target_write_u8(target, info->addr, 0);

	/* page address */
	target_write_u8(target, info->addr, page);
	target_write_u8(target, info->addr, page >> 8);
	if (page3)
		target_write_u8(target, info->addr, page >> 16);
	if (page3 == 2)
		target_write_u8(target, info->addr, page >> 24);
}

static int davinci_seek_column(struct nand_device *nand, uint16_t column)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;

	/* Random read, we must have issued a page read already */
	target_write_u8(target, info->cmd, NAND_CMD_RNDOUT);

	target_write_u8(target, info->addr, column);

	if (nand->page_size > 512) {
		target_write_u8(target, info->addr, column >> 8);
		target_write_u8(target, info->cmd, NAND_CMD_RNDOUTSTART);
	}

	if (!davinci_nand_ready(nand, 100))
		return ERROR_NAND_OPERATION_TIMEOUT;

	return ERROR_OK;
}

static int davinci_writepage_tail(struct nand_device *nand,
	uint8_t *oob, uint32_t oob_size)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	uint8_t status;

	if (oob_size)
		davinci_write_block_data(nand, oob, oob_size);

	/* non-cachemode page program */
	target_write_u8(target, info->cmd, NAND_CMD_PAGEPROG);

	if (!davinci_nand_ready(nand, 100))
		return ERROR_NAND_OPERATION_TIMEOUT;

	if (nand_read_status(nand, &status) != ERROR_OK) {
		LOG_ERROR("couldn't read status");
		return ERROR_NAND_OPERATION_FAILED;
	}

	if (status & NAND_STATUS_FAIL) {
		LOG_ERROR("write operation failed, status: 0x%02x", status);
		return ERROR_NAND_OPERATION_FAILED;
	}

	return ERROR_OK;
}

/*
 * All DaVinci family chips support 1-bit ECC on a per-chipselect basis.
 */
static int davinci_write_page_ecc1(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	unsigned oob_offset;
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	const uint32_t fcr_addr = info->aemif + NANDFCR;
	const uint32_t ecc1_addr = info->aemif + NANDFECC + (4 * info->chipsel);
	uint32_t fcr, ecc1;

	/* Write contiguous ECC bytes starting at specified offset.
	 * NOTE: Linux reserves twice as many bytes as we need; and
	 * for 16-bit OOB, those extra bytes are discontiguous.
	 */
	switch (nand->page_size) {
		case 512:
			oob_offset = 0;
			break;
		case 2048:
			oob_offset = 40;
			break;
		default:
			oob_offset = 80;
			break;
	}

	davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);

	/* scrub any old ECC state */
	target_read_u32(target, ecc1_addr, &ecc1);

	target_read_u32(target, fcr_addr, &fcr);
	fcr |= 1 << (8 + info->chipsel);

	do {
		/* set "start csX 1bit ecc" bit */
		target_write_u32(target, fcr_addr, fcr);

		/* write 512 bytes */
		davinci_write_block_data(nand, data, 512);
		data += 512;
		data_size -= 512;

		/* read the ecc, pack to 3 bytes, and invert so the ecc
		 * in an erased block is correct
		 */
		target_read_u32(target, ecc1_addr, &ecc1);
		ecc1 = (ecc1 & 0x0fff) | ((ecc1 & 0x0fff0000) >> 4);
		ecc1 = ~ecc1;

		/* save correct ECC code into oob data */
		oob[oob_offset++] = (uint8_t)(ecc1);
		oob[oob_offset++] = (uint8_t)(ecc1 >> 8);
		oob[oob_offset++] = (uint8_t)(ecc1 >> 16);

	} while (data_size);

	/* write OOB into spare area */
	return davinci_writepage_tail(nand, oob, oob_size);
}

/*
 * Preferred "new style" ECC layout for use with 4-bit ECC.  This somewhat
 * slows down large page reads done with error correction (since the OOB
 * is read first, so its ECC data can be used incrementally), but the
 * manufacturer bad block markers are safe.  Contrast:  old "infix" style.
 */
static int davinci_write_page_ecc4(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	static const uint8_t ecc512[] = {
		0, 1, 2, 3, 4,	/* 5== mfr badblock */
		6, 7, /* 8..12 for BBT or JFFS2 */ 13, 14, 15,
	};
	static const uint8_t ecc2048[] = {
		24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
		34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
		44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
		54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
	};
	static const uint8_t ecc4096[] = {
		48,  49,  50,  51,  52,  53,  54,  55,  56,  57,
		58,  59,  60,  61,  62,  63,  64,  65,  66,  67,
		68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
		78,  79,  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,
	};

	struct davinci_nand *info = nand->controller_priv;
	const uint8_t *l;
	struct target *target = nand->target;
	const uint32_t fcr_addr = info->aemif + NANDFCR;
	const uint32_t ecc4_addr = info->aemif + NAND4BITECC;
	uint32_t fcr, ecc4;

	/* Use the same ECC layout Linux uses.  For small page chips
	 * it's a bit cramped.
	 *
	 * NOTE:  at this writing, 4KB pages have issues in Linux
	 * because they need more than 64 bytes of ECC data, which
	 * the standard ECC logic can't handle.
	 */
	switch (nand->page_size) {
		case 512:
			l = ecc512;
			break;
		case 2048:
			l = ecc2048;
			break;
		default:
			l = ecc4096;
			break;
	}

	davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);

	/* scrub any old ECC state */
	target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);

	target_read_u32(target, fcr_addr, &fcr);
	fcr &= ~(0x03 << 4);
	fcr |= (1 << 12) | (info->chipsel << 4);

	do {
		uint32_t raw_ecc[4], *p;
		int i;

		/* start 4bit ecc on csX */
		target_write_u32(target, fcr_addr, fcr);

		/* write 512 bytes */
		davinci_write_block_data(nand, data, 512);
		data += 512;
		data_size -= 512;

		/* read the ecc, then save it into 10 bytes in the oob */
		for (i = 0; i < 4; i++) {
			target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
			raw_ecc[i] &= 0x03ff03ff;
		}
		for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
			oob[*l++] = p[0]        & 0xff;
			oob[*l++] = ((p[0] >>  8) & 0x03) | ((p[0] >> 14) & 0xfc);
			oob[*l++] = ((p[0] >> 22) & 0x0f) | ((p[1] <<  4) & 0xf0);
			oob[*l++] = ((p[1] >>  4) & 0x3f) | ((p[1] >> 10) & 0xc0);
			oob[*l++] = (p[1] >> 18) & 0xff;
		}

	} while (data_size);

	/* write OOB into spare area */
	return davinci_writepage_tail(nand, oob, oob_size);
}

/*
 * "Infix" OOB ... like Linux ECC_HW_SYNDROME.  Avoided because it trashes
 * manufacturer bad block markers, except on small page chips.  Once you
 * write to a page using this scheme, you need specialized code to update
 * it (code which ignores now-invalid bad block markers).
 *
 * This is needed *only* to support older firmware.  Older ROM Boot Loaders
 * need it to read their second stage loader (UBL) into SRAM, but from then
 * on the whole system can use the cleaner non-infix layouts.  Systems with
 * older second stage loaders (ABL/U-Boot, etc) or other system software
 * (MVL 4.x/5.x kernels, filesystems, etc) may need it more generally.
 */
static int davinci_write_page_ecc4infix(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	struct davinci_nand *info = nand->controller_priv;
	struct target *target = nand->target;
	const uint32_t fcr_addr = info->aemif + NANDFCR;
	const uint32_t ecc4_addr = info->aemif + NAND4BITECC;
	uint32_t fcr, ecc4;

	davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);

	/* scrub any old ECC state */
	target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);

	target_read_u32(target, fcr_addr, &fcr);
	fcr &= ~(0x03 << 4);
	fcr |= (1 << 12) | (info->chipsel << 4);

	do {
		uint32_t raw_ecc[4], *p;
		uint8_t *l;
		int i;

		/* start 4bit ecc on csX */
		target_write_u32(target, fcr_addr, fcr);

		/* write 512 bytes */
		davinci_write_block_data(nand, data, 512);
		data += 512;
		data_size -= 512;

		/* read the ecc */
		for (i = 0; i < 4; i++) {
			target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
			raw_ecc[i] &= 0x03ff03ff;
		}

		/* skip 6 bytes of prepad, then pack 10 packed ecc bytes */
		for (i = 0, l = oob + 6, p = raw_ecc; i < 2; i++, p += 2) {
			*l++ = p[0]        & 0xff;
			*l++ = ((p[0] >>  8) & 0x03) | ((p[0] >> 14) & 0xfc);
			*l++ = ((p[0] >> 22) & 0x0f) | ((p[1] <<  4) & 0xf0);
			*l++ = ((p[1] >>  4) & 0x3f) | ((p[1] >> 10) & 0xc0);
			*l++ = (p[1] >> 18) & 0xff;
		}

		/* write this "out-of-band" data -- infix */
		davinci_write_block_data(nand, oob, 16);
		oob += 16;
	} while (data_size);

	/* the last data and OOB writes included the spare area */
	return davinci_writepage_tail(nand, NULL, 0);
}

static int davinci_read_page_ecc4infix(struct nand_device *nand, uint32_t page,
	uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
	int read_size;
	int want_col, at_col;
	int ret;

	davinci_write_pagecmd(nand, NAND_CMD_READ0, page);

	/* large page devices need a start command */
	if (nand->page_size > 512)
		davinci_command(nand, NAND_CMD_READSTART);

	if (!davinci_nand_ready(nand, 100))
		return ERROR_NAND_OPERATION_TIMEOUT;

	/* NOTE:  not bothering to compute and use ECC data for now */

	want_col = 0;
	at_col = 0;
	while ((data && data_size) || (oob && oob_size)) {

		if (data && data_size) {
			if (want_col != at_col) {
				/* Reads are slow, so seek past them when we can */
				ret  = davinci_seek_column(nand, want_col);
				if (ret != ERROR_OK)
					return ret;
				at_col = want_col;
			}
			/* read 512 bytes or data_size, whichever is smaller*/
			read_size = data_size > 512 ? 512 : data_size;
			davinci_read_block_data(nand, data, read_size);
			data += read_size;
			data_size -= read_size;
			at_col += read_size;
		}
		want_col += 512;

		if (oob && oob_size) {
			if (want_col != at_col) {
				ret  = davinci_seek_column(nand, want_col);
				if (ret != ERROR_OK)
					return ret;
				at_col = want_col;
			}
			/* read this "out-of-band" data -- infix */
			read_size = oob_size > 16 ? 16 : oob_size;
			davinci_read_block_data(nand, oob, read_size);
			oob += read_size;
			oob_size -= read_size;
			at_col += read_size;
		}
		want_col += 16;
	}
	return ERROR_OK;
}

NAND_DEVICE_COMMAND_HANDLER(davinci_nand_device_command)
{
	struct davinci_nand *info;
	unsigned long chip, aemif;
	enum ecc eccmode;
	int chipsel;

	/* arguments:
	 *  - "davinci"
	 *  - target
	 *  - nand chip address
	 *  - ecc mode
	 *  - aemif address
	 * Plus someday, optionally, ALE and CLE masks.
	 */
	if (CMD_ARGC < 5)
		return ERROR_COMMAND_SYNTAX_ERROR;

	COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], chip);
	if (chip == 0) {
		LOG_ERROR("Invalid NAND chip address %s", CMD_ARGV[2]);
		goto fail;
	}

	if (strcmp(CMD_ARGV[3], "hwecc1") == 0)
		eccmode = HWECC1;
	else if (strcmp(CMD_ARGV[3], "hwecc4") == 0)
		eccmode = HWECC4;
	else if (strcmp(CMD_ARGV[3], "hwecc4_infix") == 0)
		eccmode = HWECC4_INFIX;
	else {
		LOG_ERROR("Invalid ecc mode %s", CMD_ARGV[3]);
		goto fail;
	}

	COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[4], aemif);
	if (aemif == 0) {
		LOG_ERROR("Invalid AEMIF controller address %s", CMD_ARGV[4]);
		goto fail;
	}

	/* REVISIT what we'd *like* to do is look up valid ranges using
	 * target-specific declarations, and not even need to pass the
	 * AEMIF controller address.
	 */
	if (aemif == 0x01e00000			/* dm6446, dm357 */
			|| aemif == 0x01e10000		/* dm335, dm355 */
			|| aemif == 0x01d10000		/* dm365 */
		) {
		if (chip < 0x02000000 || chip >= 0x0a000000) {
			LOG_ERROR("NAND address %08lx out of range?", chip);
			goto fail;
		}
		chipsel = (chip - 0x02000000) >> 25;
	} else {
		LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif);
		goto fail;
	}

	info = calloc(1, sizeof(*info));
	if (!info)
		goto fail;

	info->eccmode = eccmode;
	info->chipsel = chipsel;
	info->aemif = aemif;
	info->data = chip;
	info->cmd = chip | 0x10;
	info->addr = chip | 0x08;

	nand->controller_priv = info;

	info->io.target = nand->target;
	info->io.data = info->data;
	info->io.op = ARM_NAND_NONE;

	/* NOTE:  for now we don't do any error correction on read.
	 * Nothing else in OpenOCD currently corrects read errors,
	 * and in any case it's *writing* that we care most about.
	 */
	info->read_page = nand_read_page_raw;

	switch (eccmode) {
		case HWECC1:
			/* ECC_HW, 1-bit corrections, 3 bytes ECC per 512 data bytes */
			info->write_page = davinci_write_page_ecc1;
			break;
		case HWECC4:
			/* ECC_HW, 4-bit corrections, 10 bytes ECC per 512 data bytes */
			info->write_page = davinci_write_page_ecc4;
			break;
		case HWECC4_INFIX:
			/* Same 4-bit ECC HW, with problematic page/ecc layout */
			info->read_page = davinci_read_page_ecc4infix;
			info->write_page = davinci_write_page_ecc4infix;
			break;
	}

	return ERROR_OK;

fail:
	return ERROR_NAND_OPERATION_FAILED;
}

struct nand_flash_controller davinci_nand_controller = {
	.name                   = "davinci",
	.usage                  = "chip_addr hwecc_mode aemif_addr",
	.nand_device_command    = davinci_nand_device_command,
	.init                   = davinci_init,
	.reset                  = davinci_reset,
	.command                = davinci_command,
	.address                = davinci_address,
	.write_data             = davinci_write_data,
	.read_data              = davinci_read_data,
	.write_page             = davinci_write_page,
	.read_page              = davinci_read_page,
	.write_block_data       = davinci_write_block_data,
	.read_block_data        = davinci_read_block_data,
	.nand_ready             = davinci_nand_ready,
};
