--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2009 by David Brownell *
+ * *
+ * 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. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+/*
+ * 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 "nand.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 {
+ target_t *target;
+
+ u8 chipsel; /* chipselect 0..3 == CS2..CS5 */
+ u8 eccmode;
+
+ /* Async EMIF controller base */
+ u32 aemif;
+
+ /* NAND chip addresses */
+ u32 data; /* without CLE or ALE */
+ u32 cmd; /* with CLE */
+ u32 addr; /* with ALE */
+
+ /* page i/o for the relevant flavor of hardware ECC */
+ int (*read_page)(struct nand_device_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+ int (*write_page)(struct nand_device_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 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(target_t *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_register_commands(struct command_context_s *cmd_ctx)
+{
+ return ERROR_OK;
+}
+
+static int davinci_init(struct nand_device_s *nand)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ u32 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 %08x 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_s *nand)
+{
+ return ERROR_OK;
+}
+
+static int davinci_nand_ready(struct nand_device_s *nand, int timeout)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ u32 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_s *nand, u8 command)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->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_s *nand, u8 address)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->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_s *nand, u16 data)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->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_s *nand, void *data)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->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 I/O be MUCH faster */
+
+static int davinci_read_block_data(struct nand_device_s *nand,
+ u8 *data, int data_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ u32 nfdata = info->data;
+ u32 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_s *nand,
+ u8 *data, int data_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ u32 nfdata = info->data;
+ u32 tmp;
+
+ if (!halted(target, "write_block"))
+ return ERROR_NAND_OPERATION_FAILED;
+
+ 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_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ u8 *ooballoc = NULL;
+ int status;
+
+ if (!nand->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+ if (!halted(info->target, "write_page"))
+ return ERROR_NAND_OPERATION_FAILED;
+
+ /* Always write both data and OOB ... we are not "raw" I/O! */
+ if (!data)
+ 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);
+ }
+
+ status = info->write_page(nand, page, data, data_size, oob, oob_size);
+ free(ooballoc);
+ return status;
+}
+
+static int davinci_read_page(struct nand_device_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+
+ if (!nand->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+ if (!halted(info->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_s *nand, u8 cmd, u32 page)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->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_writepage_tail(struct nand_device_s *nand,
+ u8 *oob, u32 oob_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ u8 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_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ unsigned oob_offset;
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ const u32 fcr_addr = info->aemif + NANDFCR;
+ const u32 ecc1_addr = info->aemif + NANDFECC + info->chipsel;
+ u32 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++] = (u8)(ecc1);
+ oob[oob_offset++] = (u8)(ecc1 >> 8);
+ oob[oob_offset++] = (u8)(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_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ static const u8 ecc512[] = {
+ 0, 1, 2, 3, 4, /* 5== mfr badblock */
+ 6, 7, /* 8..12 for BBT or JFFS2 */ 13, 14, 15,
+ };
+ static const u8 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 u8 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 u8 *l;
+ target_t *target = info->target;
+ const u32 fcr_addr = info->aemif + NANDFCR;
+ const u32 ecc4_addr = info->aemif + NAND4BITECC;
+ u32 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 {
+ u32 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_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ struct davinci_nand *info = nand->controller_priv;
+ target_t *target = info->target;
+ const u32 fcr_addr = info->aemif + NANDFCR;
+ const u32 ecc4_addr = info->aemif + NAND4BITECC;
+ u32 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 {
+ u32 raw_ecc[4], *p;
+ u8 *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;
+ oob_size -= 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_s *nand, u32 page,
+ u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ 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 */
+
+ do {
+ /* write 512 bytes */
+ davinci_read_block_data(nand, data, 512);
+ data += 512;
+ data_size -= 512;
+
+ /* read this "out-of-band" data -- infix */
+ davinci_read_block_data(nand, oob, 16);
+ oob += 16;
+ oob_size -= 16;
+ } while (data_size);
+
+ return ERROR_OK;
+}
+
+static int davinci_nand_device_command(struct command_context_s *cmd_ctx,
+ char *cmd, char **argv, int argc,
+ struct nand_device_s *nand)
+{
+ struct davinci_nand *info;
+ target_t *target;
+ unsigned long chip, aemif;
+ enum ecc eccmode;
+ int chipsel;
+ char *ep;
+
+ /* arguments:
+ * - "davinci"
+ * - target
+ * - nand chip address
+ * - ecc mode
+ * - aemif address
+ * Plus someday, optionally, ALE and CLE masks.
+ */
+ if (argc < 5) {
+ LOG_ERROR("parameters: %s target "
+ "chip_addr hwecc_mode aemif_addr",
+ argv[0]);
+ goto fail;
+ }
+
+ target = get_target(argv[1]);
+ if (!target) {
+ LOG_ERROR("invalid target %s", argv[1]);
+ goto fail;
+ }
+
+ chip = strtoul(argv[2], &ep, 0);
+ if (*ep || chip == 0 || chip == ULONG_MAX) {
+ LOG_ERROR("Invalid NAND chip address %s", argv[2]);
+ goto fail;
+ }
+
+ if (strcmp(argv[3], "hwecc1") == 0)
+ eccmode = HWECC1;
+ else if (strcmp(argv[3], "hwecc4") == 0)
+ eccmode = HWECC4;
+ else if (strcmp(argv[3], "hwecc4_infix") == 0)
+ eccmode = HWECC4_INFIX;
+ else {
+ LOG_ERROR("Invalid ecc mode %s", argv[3]);
+ goto fail;
+ }
+
+ aemif = strtoul(argv[4], &ep, 0);
+ if (*ep || chip == 0 || chip == ULONG_MAX) {
+ LOG_ERROR("Invalid AEMIF controller address %s", 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 < 0x0200000 || chip >= 0x0a000000) {
+ LOG_ERROR("NAND address %08lx out of range?", chip);
+ goto fail;
+ }
+ chipsel = (chip - 0x02000000) >> 21;
+ } else {
+ LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif);
+ goto fail;
+ }
+
+ info = calloc(1, sizeof *info);
+ if (info == NULL)
+ goto fail;
+
+ info->target = target;
+ info->eccmode = eccmode;
+ info->chipsel = chipsel;
+ info->aemif = aemif;
+ info->data = chip;
+ info->cmd = chip | 0x10;
+ info->addr = chip | 0x08;
+
+ nand->controller_priv = info;
+
+ /* 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;
+}
+
+nand_flash_controller_t davinci_nand_controller = {
+ .name = "davinci",
+ .nand_device_command = davinci_nand_device_command,
+ .register_commands = davinci_register_commands,
+ .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,
+};