lpc32xx: Flash driver
authorBjarne Steinsbo <bsteinsbo@gmail.com>
Tue, 15 Feb 2011 19:23:40 +0000 (20:23 +0100)
committerØyvind Harboe <oyvind.harboe@zylin.com>
Tue, 15 Feb 2011 19:23:40 +0000 (20:23 +0100)
Based on the lpc3180 driver, but released as a separate driver for two reasons:
 1) I don't have an lpc3180 to test it against, so it might unintentionally break compatibility.
 2) It's using a different OOB layout than lpc3180.

Rewritten so that it no longer borrows code from the NXP CDL library.  Instead borrowing code from the u-boot port to lpc32xx, written by Kevin Wells.

Tested on lpc3250 (Hitex LPC3250-Stick).  OOB layout is compatible with LPCLinux.

src/flash/nand/Makefile.am
src/flash/nand/driver.c
src/flash/nand/ecc.c
src/flash/nand/lpc32xx.c [new file with mode: 0644]
src/flash/nand/lpc32xx.h [new file with mode: 0644]

index 9aa0e69f16746363513744f29cc0a39620d5a940..23c303bfc1ad2c27039d2ecc650d6edf78bc7984 100644 (file)
@@ -16,6 +16,7 @@ NAND_DRIVERS = \
        nonce.c \
        davinci.c \
        lpc3180.c \
+       lpc32xx.c \
        mx2.c \
        mx3.c \
        orion.c \
@@ -35,6 +36,7 @@ noinst_HEADERS = \
        fileio.h \
        imp.h \
        lpc3180.h \
+       lpc32xx.h \
        mx2.h \
        mx3.h \
        s3c24xx.h \
index 522dfa40df961b7b4713e12de905d30c29236e6f..f34811ba2a7415c72b83481f4450e6e960c12bec 100644 (file)
@@ -31,6 +31,7 @@
 extern struct nand_flash_controller nonce_nand_controller;
 extern struct nand_flash_controller davinci_nand_controller;
 extern struct nand_flash_controller lpc3180_nand_controller;
+extern struct nand_flash_controller lpc32xx_nand_controller;
 extern struct nand_flash_controller orion_nand_controller;
 extern struct nand_flash_controller s3c2410_nand_controller;
 extern struct nand_flash_controller s3c2412_nand_controller;
@@ -49,6 +50,7 @@ static struct nand_flash_controller *nand_flash_controllers[] =
        &nonce_nand_controller,
        &davinci_nand_controller,
        &lpc3180_nand_controller,
+       &lpc32xx_nand_controller,
        &orion_nand_controller,
        &s3c2410_nand_controller,
        &s3c2412_nand_controller,
index 2de12d424510912e19ca020e6e932b6bb5e6e301..b4039976044df33d4baa37fa2212909ad8d1c845 100644 (file)
@@ -120,3 +120,64 @@ int nand_calculate_ecc(struct nand_device *nand, const uint8_t *dat, uint8_t *ec
 
        return 0;
 }
+
+static inline int countbits(uint32_t byte)
+{
+       int res = 0;
+
+       for (;byte; byte >>= 1)
+               res += byte & 0x01;
+       return res;
+}
+
+/**
+ * nand_correct_data - Detect and correct a 1 bit error for 256 byte block
+ */
+int nand_correct_data(struct nand_device *nand, u_char *dat,
+                     u_char *read_ecc, u_char *calc_ecc)
+{
+       uint8_t s0, s1, s2;
+
+#ifdef NAND_ECC_SMC
+       s0 = calc_ecc[0] ^ read_ecc[0];
+       s1 = calc_ecc[1] ^ read_ecc[1];
+       s2 = calc_ecc[2] ^ read_ecc[2];
+#else
+       s1 = calc_ecc[0] ^ read_ecc[0];
+       s0 = calc_ecc[1] ^ read_ecc[1];
+       s2 = calc_ecc[2] ^ read_ecc[2];
+#endif
+       if ((s0 | s1 | s2) == 0)
+               return 0;
+
+       /* Check for a single bit error */
+       if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+           ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+           ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+               uint32_t byteoffs, bitnum;
+
+               byteoffs = (s1 << 0) & 0x80;
+               byteoffs |= (s1 << 1) & 0x40;
+               byteoffs |= (s1 << 2) & 0x20;
+               byteoffs |= (s1 << 3) & 0x10;
+
+               byteoffs |= (s0 >> 4) & 0x08;
+               byteoffs |= (s0 >> 3) & 0x04;
+               byteoffs |= (s0 >> 2) & 0x02;
+               byteoffs |= (s0 >> 1) & 0x01;
+
+               bitnum = (s2 >> 5) & 0x04;
+               bitnum |= (s2 >> 4) & 0x02;
+               bitnum |= (s2 >> 3) & 0x01;
+
+               dat[byteoffs] ^= (1 << bitnum);
+
+               return 1;
+       }
+
+       if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+               return 1;
+
+       return -1;
+}
diff --git a/src/flash/nand/lpc32xx.c b/src/flash/nand/lpc32xx.c
new file mode 100644 (file)
index 0000000..5cde90c
--- /dev/null
@@ -0,0 +1,1828 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2011 Bjarne Steinsbo <bsteinsbo@gmail.com>              *
+ *   Copyright (C) 2010 richard vegh <vegh.ricsi@gmail.com>                *
+ *   Copyright (C) 2010 Oyvind Harboe <oyvind.harboe@zylin.com>            *
+ *                                                                         *
+ *   Based on a combination of the lpc3180 driver and code from            *
+ *   uboot-2009.03-lpc32xx by Kevin Wells.                                 *
+ *   Any bugs are mine. --BSt                                              *
+ *                                                                         *
+ *   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.             *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "lpc32xx.h"
+#include <target/target.h>
+
+static int lpc32xx_reset(struct nand_device *nand);
+static int lpc32xx_controller_ready(struct nand_device *nand, int timeout);
+static int lpc32xx_tc_ready(struct nand_device *nand, int timeout);
+extern int nand_correct_data(struct nand_device *nand, u_char *dat,
+                            u_char *read_ecc, u_char *calc_ecc);
+
+/* These are offset with the working area in IRAM when using DMA to
+ * read/write data to the SLC controller.
+ * - DMA descriptors will be put at start of working area,
+ * - Hardware generated ECC will be stored at ECC_OFFS
+ * - OOB wil be read/written from/to SPARE_OFFS
+ * - Actual page data will be read from/to DATA_OFFS
+ * There are unused holes between the used areas.
+ */
+#define ECC_OFFS   0x120
+#define SPARE_OFFS 0x140
+#define DATA_OFFS  0x200
+
+static int sp_ooblayout[] = {
+       10, 11, 12, 13, 14, 15
+};
+static int lp_ooblayout[] = {
+       40, 41, 42, 43, 44, 45,
+       46, 47, 48, 49, 50, 51,
+       52, 53, 54, 55, 56, 57,
+       58, 59, 60, 61, 62, 63
+};
+
+typedef struct {
+       volatile uint32_t dma_src;
+       volatile uint32_t dma_dest;
+       volatile uint32_t next_lli;
+       volatile uint32_t next_ctrl;
+} dmac_ll_t;
+
+static dmac_ll_t dmalist[(2048/256) * 2 + 1];
+
+/* nand device lpc32xx <target#> <oscillator_frequency>
+ */
+NAND_DEVICE_COMMAND_HANDLER(lpc32xx_nand_device_command)
+{
+       if (CMD_ARGC < 3) {
+               LOG_WARNING("incomplete 'lpc32xx' nand flash configuration");
+               return ERROR_FLASH_BANK_INVALID;
+       }
+
+       uint32_t osc_freq;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], osc_freq);
+
+       struct lpc32xx_nand_controller *lpc32xx_info;
+       lpc32xx_info = malloc(sizeof(struct lpc32xx_nand_controller));
+       nand->controller_priv = lpc32xx_info;
+
+       lpc32xx_info->osc_freq = osc_freq;
+
+       if ((lpc32xx_info->osc_freq < 1000) || (lpc32xx_info->osc_freq > 20000))
+               LOG_WARNING("LPC32xx oscillator frequency should be between "
+                           "1000 and 20000 kHz, was %i",
+                           lpc32xx_info->osc_freq);
+
+       lpc32xx_info->selected_controller = LPC32xx_NO_CONTROLLER;
+       lpc32xx_info->sw_write_protection = 0;
+       lpc32xx_info->sw_wp_lower_bound = 0x0;
+       lpc32xx_info->sw_wp_upper_bound = 0x0;
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_pll(int fclkin, uint32_t pll_ctrl)
+{
+       int bypass = (pll_ctrl & 0x8000) >> 15;
+       int direct = (pll_ctrl & 0x4000) >> 14;
+       int feedback = (pll_ctrl & 0x2000) >> 13;
+       int p = (1 << ((pll_ctrl & 0x1800) >> 11) * 2);
+       int n = ((pll_ctrl & 0x0600) >> 9) + 1;
+       int m = ((pll_ctrl & 0x01fe) >> 1) + 1;
+       int lock = (pll_ctrl & 0x1);
+
+       if (!lock)
+               LOG_WARNING("PLL is not locked");
+
+       if (!bypass && direct) /* direct mode */
+               return (m * fclkin) / n;
+
+       if (bypass && !direct) /* bypass mode */
+               return fclkin / (2 * p);
+
+       if (bypass & direct) /* direct bypass mode */
+               return fclkin;
+
+       if (feedback) /* integer mode */
+               return m * (fclkin / n);
+       else /* non-integer mode */
+               return (m / (2 * p)) * (fclkin / n);
+}
+
+static float lpc32xx_cycle_time(struct nand_device *nand)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       uint32_t sysclk_ctrl, pwr_ctrl, hclkdiv_ctrl, hclkpll_ctrl;
+       int sysclk;
+       int hclk;
+       int hclk_pll;
+       float cycle;
+       int retval;
+
+       /* calculate timings */
+
+       /* determine current SYSCLK (13'MHz or main oscillator) */
+       retval = target_read_u32(target, 0x40004050, &sysclk_ctrl);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("could not read SYSCLK_CTRL");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if ((sysclk_ctrl & 1) == 0)
+               sysclk = lpc32xx_info->osc_freq;
+       else
+               sysclk = 13000;
+
+       /* determine selected HCLK source */
+       retval = target_read_u32(target, 0x40004044, &pwr_ctrl);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("could not read HCLK_CTRL");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if ((pwr_ctrl & (1 << 2)) == 0) { /* DIRECT RUN mode */
+               hclk = sysclk;
+       } else {
+               retval = target_read_u32(target, 0x40004058, &hclkpll_ctrl);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not read HCLKPLL_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               hclk_pll = lpc32xx_pll(sysclk, hclkpll_ctrl);
+
+               retval = target_read_u32(target, 0x40004040, &hclkdiv_ctrl);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not read CLKDIV_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (pwr_ctrl & (1 << 10)) /* ARM_CLK and HCLK use PERIPH_CLK */
+                       hclk = hclk_pll / (((hclkdiv_ctrl & 0x7c) >> 2) + 1);
+               else /* HCLK uses HCLK_PLL */
+                       hclk = hclk_pll / (1 << (hclkdiv_ctrl & 0x3));
+       }
+
+       LOG_DEBUG("LPC32xx HCLK currently clocked at %i kHz", hclk);
+
+       cycle = (1.0 / hclk) * 1000000.0;
+
+       return cycle;
+}
+
+static int lpc32xx_init(struct nand_device *nand)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int bus_width = nand->bus_width ? : 8;
+       int address_cycles = nand->address_cycles ? : 3;
+       int page_size = nand->page_size ? : 512;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       /* sanitize arguments */
+       if (bus_width != 8) {
+               LOG_ERROR("LPC32xx doesn't support %i", bus_width);
+               return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+       }
+
+       /* inform calling code about selected bus width */
+       nand->bus_width = bus_width;
+
+       if ((address_cycles != 3) && (address_cycles != 4)) {
+               LOG_ERROR("LPC32xx driver doesn't support %i", address_cycles);
+               return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+       }
+
+       if ((page_size != 512) && (page_size != 2048)) {
+               LOG_ERROR("LPC32xx doesn't support page size %i", page_size);
+               return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+       }
+
+       /* select MLC controller if none is currently selected */
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_DEBUG("no LPC32xx NAND flash controller selected, "
+                         "using default 'mlc'");
+               lpc32xx_info->selected_controller = LPC32xx_MLC_CONTROLLER;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               uint32_t mlc_icr_value = 0x0;
+               float cycle;
+               int twp, twh, trp, treh, trhz, trbwb, tcea;
+
+               /* FLASHCLK_CTRL = 0x22 (enable clk for MLC) */
+               retval = target_write_u32(target, 0x400040c8, 0x22);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set FLASHCLK_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_CEH = 0x0 (Force nCE assert) */
+               retval = target_write_u32(target, 0x200b804c, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_CEH");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_LOCK = 0xa25e (unlock protected registers) */
+               retval = target_write_u32(target, 0x200b8044, 0xa25e);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_LOCK");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_ICR = configuration */
+               if (lpc32xx_info->sw_write_protection)
+                       mlc_icr_value |= 0x8;
+               if (page_size == 2048)
+                       mlc_icr_value |= 0x4;
+               if (address_cycles == 4)
+                       mlc_icr_value |= 0x2;
+               if (bus_width == 16)
+                       mlc_icr_value |= 0x1;
+               retval = target_write_u32(target, 0x200b8030, mlc_icr_value);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ICR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* calculate NAND controller timings */
+               cycle = lpc32xx_cycle_time(nand);
+
+               twp = ((40 / cycle) + 1);
+               twh = ((20 / cycle) + 1);
+               trp = ((30 / cycle) + 1);
+               treh = ((15 / cycle) + 1);
+               trhz = ((30 / cycle) + 1);
+               trbwb = ((100 / cycle) + 1);
+               tcea = ((45 / cycle) + 1);
+
+               /* MLC_LOCK = 0xa25e (unlock protected registers) */
+               retval = target_write_u32(target, 0x200b8044, 0xa25e);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_LOCK");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_TIME_REG */
+               retval = target_write_u32(target, 0x200b8034,
+                                         (twp & 0xf)
+                                         | ((twh & 0xf) << 4)
+                                         | ((trp & 0xf) << 8)
+                                         | ((treh & 0xf) << 12)
+                                         | ((trhz & 0x7) << 16)
+                                         | ((trbwb & 0x1f) << 19)
+                                         | ((tcea & 0x3) << 24));
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_TIME_REG");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               retval = lpc32xx_reset(nand);
+               if (ERROR_OK != retval)
+                       return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               float cycle;
+               int r_setup, r_hold, r_width, r_rdy;
+               int w_setup, w_hold, w_width, w_rdy;
+
+               /* FLASHCLK_CTRL = 0x05 (enable clk for SLC) */
+               retval = target_write_u32(target, 0x400040c8, 0x05);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set FLASHCLK_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* after reset set other registers of SLC,
+                * so reset calling is here at the begining
+                */
+               retval = lpc32xx_reset(nand);
+               if (ERROR_OK != retval)
+                       return ERROR_NAND_OPERATION_FAILED;
+
+               /* SLC_CFG =
+                       Force nCE assert,
+                       DMA ECC enabled,
+                       ECC enabled,
+                       DMA burst enabled,
+                       DMA read from SLC,
+                       WIDTH = bus_width)
+               */
+               retval = target_write_u32(target, 0x20020014,
+                                         0x3e | (bus_width == 16) ? 1 : 0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_CFG");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* SLC_IEN = 3 (INT_RDY_EN = 1) ,(INT_TC_STAT = 1) */
+               retval = target_write_u32(target, 0x20020020, 0x03);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_IEN");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* DMA configuration */
+
+               /* DMACLK_CTRL = 0x01 (enable clock for DMA controller) */
+               retval = target_write_u32(target, 0x400040e8, 0x01);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set DMACLK_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* DMACConfig = DMA enabled*/
+               retval = target_write_u32(target, 0x31000030, 0x01);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set DMACConfig");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* calculate NAND controller timings */
+               cycle = lpc32xx_cycle_time(nand);
+
+               r_setup = w_setup = 0;
+               r_hold = w_hold = 10 / cycle;
+               r_width = 30 / cycle;
+               w_width = 40 / cycle;
+               r_rdy = w_rdy = 100 / cycle;
+
+               /* SLC_TAC: SLC timing arcs register */
+               retval = target_write_u32(target, 0x2002002c,
+                                         (r_setup & 0xf)
+                                         | ((r_hold & 0xf) << 4)
+                                         | ((r_width & 0xf) << 8)
+                                         | ((r_rdy & 0xf) << 12)
+                                         | ((w_setup & 0xf) << 16)
+                                         | ((w_hold & 0xf) << 20)
+                                         | ((w_width & 0xf) << 24)
+                                         | ((w_rdy & 0xf) << 28));
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_TAC");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_reset(struct nand_device *nand)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use "
+                         "LPC32xx NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               /* MLC_CMD = 0xff (reset controller and NAND device) */
+               retval = target_write_u32(target, 0x200b8000, 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_CMD");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (!lpc32xx_controller_ready(nand, 100)) {
+                       LOG_ERROR("LPC32xx MLC NAND controller timed out "
+                                 "after reset");
+                       return ERROR_NAND_OPERATION_TIMEOUT;
+               }
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               /* SLC_CTRL = 0x6 (ECC_CLEAR, SW_RESET) */
+               retval = target_write_u32(target, 0x20020010, 0x6);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_CTRL");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (!lpc32xx_controller_ready(nand, 100))
+               {
+                       LOG_ERROR("LPC32xx SLC NAND controller timed out "
+                                 "after reset");
+                       return ERROR_NAND_OPERATION_TIMEOUT;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_command(struct nand_device *nand, uint8_t command)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use "
+                         "LPC32xx NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               /* MLC_CMD = command */
+               retval = target_write_u32(target, 0x200b8000, command);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_CMD");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               /* SLC_CMD = command */
+               retval = target_write_u32(target, 0x20020008, command);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_CMD");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_address(struct nand_device *nand, uint8_t address)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use "
+                         "LPC32xx NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               /* MLC_ADDR = address */
+               retval = target_write_u32(target, 0x200b8004, address);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               /* SLC_ADDR = address */
+               retval = target_write_u32(target, 0x20020004, address);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_write_data(struct nand_device *nand, uint16_t data)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use "
+                         "LPC32xx NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               /* MLC_DATA = data */
+               retval = target_write_u32(target, 0x200b0000, data);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_DATA");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               /* SLC_DATA = data */
+               retval = target_write_u32(target, 0x20020000, data);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set SLC_DATA");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_read_data(struct nand_device *nand, void *data)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               /* data = MLC_DATA, use sized access */
+               if (nand->bus_width == 8) {
+                       uint8_t *data8 = data;
+                       retval = target_read_u8(target, 0x200b0000, data8);
+               } else {
+                       LOG_ERROR("BUG: bus_width neither 8 nor 16 bit");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not read MLC_DATA");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               uint32_t data32;
+
+               /* data = SLC_DATA, must use 32-bit access */
+               retval = target_read_u32(target, 0x20020000, &data32);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not read SLC_DATA");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (nand->bus_width == 8) {
+                       uint8_t *data8 = data;
+                       *data8 = data32 & 0xff;
+               } else {
+                       LOG_ERROR("BUG: bus_width neither 8 nor 16 bit");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_write_page_mlc(struct nand_device *nand, uint32_t page,
+                                 uint8_t *data, uint32_t data_size,
+                                 uint8_t *oob, uint32_t oob_size)
+{
+       struct target *target = nand->target;
+       int retval;
+       uint8_t status;
+       static uint8_t page_buffer[512];
+       static uint8_t oob_buffer[6];
+       int quarter, num_quarters;
+
+       /* MLC_CMD = sequential input */
+       retval = target_write_u32(target, 0x200b8000, NAND_CMD_SEQIN);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("could not set MLC_CMD");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (nand->page_size == 512) {
+               /* MLC_ADDR = 0x0 (one column cycle) */
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_ADDR = row */
+               retval = target_write_u32(target, 0x200b8004, page & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004,
+                                         (page >> 8) & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (nand->address_cycles == 4) {
+                       retval = target_write_u32(target, 0x200b8004,
+                                                 (page >> 16) & 0xff);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not set MLC_ADDR");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+       } else {
+               /* MLC_ADDR = 0x0 (two column cycles) */
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_ADDR = row */
+               retval = target_write_u32(target, 0x200b8004, page & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004,
+                                         (page >> 8) & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       /* when using the MLC controller, we have to treat a large page device
+        * as being made out of four quarters, each the size of a small page
+        * device
+        */
+       num_quarters = (nand->page_size == 2048) ? 4 : 1;
+
+       for (quarter = 0; quarter < num_quarters; quarter++) {
+               int thisrun_data_size = (data_size > 512) ? 512 : data_size;
+               int thisrun_oob_size = (oob_size > 6) ? 6 : oob_size;
+
+               memset(page_buffer, 0xff, 512);
+               if (data) {
+                       memcpy(page_buffer, data, thisrun_data_size);
+                       data_size -= thisrun_data_size;
+                       data += thisrun_data_size;
+               }
+
+               memset(oob_buffer, 0xff, 6);
+               if (oob) {
+                       memcpy(oob_buffer, oob, thisrun_oob_size);
+                       oob_size -= thisrun_oob_size;
+                       oob += thisrun_oob_size;
+               }
+
+               /* write MLC_ECC_ENC_REG to start encode cycle */
+               retval = target_write_u32(target, 0x200b8008, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ECC_ENC_REG");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               retval = target_write_memory(target, 0x200a8000,
+                               4, 128, page_buffer);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_BUF (data)");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_memory(target, 0x200a8000,
+                               1, 6, oob_buffer);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_BUF (oob)");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* write MLC_ECC_AUTO_ENC_REG to start auto encode */
+               retval = target_write_u32(target, 0x200b8010, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ECC_AUTO_ENC_REG");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (!lpc32xx_controller_ready(nand, 1000)) {
+                       LOG_ERROR("timeout while waiting for "
+                                 "completion of auto encode cycle");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       /* MLC_CMD = auto program command */
+       retval = target_write_u32(target, 0x200b8000, NAND_CMD_PAGEPROG);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("could not set MLC_CMD");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if ((retval = 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 didn't pass, status: 0x%2.2x",
+                         status);
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       return ERROR_OK;
+}
+
+/* SLC controller in !raw mode will use target cpu to read/write nand from/to
+ * target internal memory.  The transfer to/from flash is done by DMA.  This
+ * function sets up the dma linked list in host memory for later transfer to
+ * target.
+ */
+static int lpc32xx_make_dma_list(uint32_t target_mem_base, uint32_t page_size,
+                                int do_read)
+{
+       uint32_t i, dmasrc, ctrl, ecc_ctrl, oob_ctrl, dmadst;
+
+       /* DMACCxControl =
+               TransferSize =64,
+               Source burst size =16,
+               Destination burst size = 16,
+               Source transfer width = 32 bit,
+               Destination transfer width = 32 bit,
+               Source AHB master select = M0,
+               Destination AHB master select = M0,
+               Source increment = 0, // set later
+               Destination increment = 0, // set later
+               Terminal count interrupt enable bit = 0 // set on last
+       */                      /*
+        * Write Operation Sequence for Small Block NAND
+        * ----------------------------------------------------------
+        * 1. X'fer 256 bytes of data from Memory to Flash.
+        * 2. Copy generated ECC data from Register to Spare Area
+        * 3. X'fer next 256 bytes of data from Memory to Flash.
+        * 4. Copy generated ECC data from Register to Spare Area.
+        * 5. X'fer 16 byets of Spare area from Memory to Flash.
+        * Read Operation Sequence for Small Block NAND
+        * ----------------------------------------------------------
+        * 1. X'fer 256 bytes of data from Flash to Memory.
+        * 2. Copy generated ECC data from Register to ECC calc Buffer.
+        * 3. X'fer next 256 bytes of data from Flash to Memory.
+        * 4. Copy generated ECC data from Register to ECC calc Buffer.
+        * 5. X'fer 16 bytes of Spare area from Flash to Memory.
+        * Write Operation Sequence for Large Block NAND
+        * ----------------------------------------------------------
+        * 1. Steps(1-4) of Write Operations repeate for four times
+        * which generates 16 DMA descriptors to X'fer 2048 bytes of
+        * data & 32 bytes of ECC data.
+        * 2. X'fer 64 bytes of Spare area from Memory to Flash.
+        * Read Operation Sequence for Large Block NAND
+        * ----------------------------------------------------------
+        * 1. Steps(1-4) of Read Operations repeate for four times
+        * which generates 16 DMA descriptors to X'fer 2048 bytes of
+        * data & 32 bytes of ECC data.
+        * 2. X'fer 64 bytes of Spare area from Flash to Memory.
+        */
+
+       ctrl = (0x40 | 3 << 12 | 3 << 15 | 2 << 18 | 2 << 21 | 0 << 24
+               | 0 << 25 | 0 << 26 | 0 << 27 | 0 << 31);
+
+       /* DMACCxControl =
+               TransferSize =1,
+               Source burst size =4,
+               Destination burst size = 4,
+               Source transfer width = 32 bit,
+               Destination transfer width = 32 bit,
+               Source AHB master select = M0,
+               Destination AHB master select = M0,
+               Source increment = 0,
+               Destination increment = 1,
+               Terminal count interrupt enable bit = 0
+        */
+       ecc_ctrl = 0x01 | 1 << 12 | 1 << 15 | 2 << 18 | 2 << 21 | 0 << 24
+                  | 0 << 25 | 0 << 26 | 1 << 27 | 0 << 31;
+
+       /* DMACCxControl =
+               TransferSize =16 for lp or 4 for sp,
+               Source burst size =16,
+               Destination burst size = 16,
+               Source transfer width = 32 bit,
+               Destination transfer width = 32 bit,
+               Source AHB master select = M0,
+               Destination AHB master select = M0,
+               Source increment = 0, // set later
+               Destination increment = 0, // set later
+               Terminal count interrupt enable bit = 1 // set on last
+        */
+       oob_ctrl = (page_size == 2048 ? 0x10 : 0x04)
+                  | 3 << 12 | 3 << 15 | 2 << 18 | 2 << 21 | 0 << 24
+                  | 0 << 25 | 0 << 26 | 0 << 27 | 1 << 31;
+       if (do_read) {
+               ctrl |= 1 << 27; /* Destination increment = 1 */
+               oob_ctrl |= 1 << 27; /* Destination increment = 1 */
+               dmasrc = 0x20020038; /* SLC_DMA_DATA */
+               dmadst = target_mem_base + DATA_OFFS;
+       } else {
+               ctrl |= 1 << 26; /* Source increment = 1 */
+               oob_ctrl |= 1 << 26; /* Source increment = 1 */
+               dmasrc = target_mem_base + DATA_OFFS;
+               dmadst = 0x20020038; /* SLC_DMA_DATA */
+       }
+       /*
+        * Write Operation Sequence for Small Block NAND
+        * ----------------------------------------------------------
+        * 1. X'fer 256 bytes of data from Memory to Flash.
+        * 2. Copy generated ECC data from Register to Spare Area
+        * 3. X'fer next 256 bytes of data from Memory to Flash.
+        * 4. Copy generated ECC data from Register to Spare Area.
+        * 5. X'fer 16 byets of Spare area from Memory to Flash.
+        * Read Operation Sequence for Small Block NAND
+        * ----------------------------------------------------------
+        * 1. X'fer 256 bytes of data from Flash to Memory.
+        * 2. Copy generated ECC data from Register to ECC calc Buffer.
+        * 3. X'fer next 256 bytes of data from Flash to Memory.
+        * 4. Copy generated ECC data from Register to ECC calc Buffer.
+        * 5. X'fer 16 bytes of Spare area from Flash to Memory.
+        * Write Operation Sequence for Large Block NAND
+        * ----------------------------------------------------------
+        * 1. Steps(1-4) of Write Operations repeate for four times
+        * which generates 16 DMA descriptors to X'fer 2048 bytes of
+        * data & 32 bytes of ECC data.
+        * 2. X'fer 64 bytes of Spare area from Memory to Flash.
+        * Read Operation Sequence for Large Block NAND
+        * ----------------------------------------------------------
+        * 1. Steps(1-4) of Read Operations repeate for four times
+        * which generates 16 DMA descriptors to X'fer 2048 bytes of
+        * data & 32 bytes of ECC data.
+        * 2. X'fer 64 bytes of Spare area from Flash to Memory.
+        */
+       for (i = 0; i < page_size/0x100; i++)
+       {
+               dmalist[i*2].dma_src = (do_read ? dmasrc : (dmasrc + i * 256));
+               dmalist[i*2].dma_dest = (do_read ? (dmadst + i * 256) : dmadst);
+               dmalist[i*2].next_lli =
+                       target_mem_base + (i*2 + 1) * sizeof(dmac_ll_t);
+               dmalist[i*2].next_ctrl = ctrl;
+
+               dmalist[(i*2) + 1].dma_src = 0x20020034; /* SLC_ECC */
+               dmalist[(i*2) + 1].dma_dest =
+                       target_mem_base + ECC_OFFS + i * 4;
+               dmalist[(i*2) + 1].next_lli =
+                        target_mem_base + (i*2 + 2) * sizeof(dmac_ll_t);
+               dmalist[(i*2) + 1].next_ctrl = ecc_ctrl;
+
+       }
+       if (do_read)
+       {
+               dmadst = target_mem_base + SPARE_OFFS;
+       } else {
+               dmasrc = target_mem_base + SPARE_OFFS;
+               dmalist[(i*2) - 1].next_lli = 0; /* last link = null on write */
+               dmalist[(i*2) - 1].next_ctrl |= (1 << 31); /* Set TC enable */
+       }
+       dmalist[i*2].dma_src = dmasrc;
+       dmalist[i*2].dma_dest = dmadst;
+       dmalist[i*2].next_lli = 0;
+       dmalist[i*2].next_ctrl = oob_ctrl;
+
+       return (i*2 + 1); /* Number of descriptors */
+}
+
+static int lpc32xx_start_slc_dma(struct nand_device *nand, uint32_t count,
+                                int do_wait)
+{
+       struct target *target = nand->target;
+       int retval;
+
+       /* DMACIntTCClear = ch0 */
+       retval = target_write_u32(target, 0x31000008, 1);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set DMACIntTCClear");
+               return retval;
+       }
+       
+       /* DMACIntErrClear = ch0 */
+       retval = target_write_u32(target, 0x31000010, 1);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set DMACIntErrClear");
+               return retval;
+       }
+       
+       /* DMACCxConfig=
+               E=1,
+               SrcPeripheral = 1 (SLC),
+               DestPeripheral = 1 (SLC),
+               FlowCntrl = 2 (Pher -> Mem, DMA),
+               IE = 0,
+               ITC = 0,
+               L= 0,
+               H=0
+       */
+       retval = target_write_u32(target, 0x31000110,
+                                 1 | 1<<1 | 1<<6 | 2<<11 | 0<<14
+                                 | 0<<15 | 0<<16 | 0<<18);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set DMACC0Config");
+               return retval;
+       }
+
+       /* SLC_CTRL = 3 (START DMA), ECC_CLEAR */
+       retval = target_write_u32(target, 0x20020010, 0x3);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set SLC_CTRL");
+               return retval;
+       }
+
+       /* SLC_ICR = 2, INT_TC_CLR, clear pending TC*/
+       retval = target_write_u32(target, 0x20020028, 2);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set SLC_ICR");
+               return retval;
+       }
+
+       /* SLC_TC */
+       retval = target_write_u32(target, 0x20020030, count);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("lpc32xx_start_slc_dma: Could not set SLC_TC");
+               return retval;
+       }
+
+       /* Wait finish */
+       if (do_wait && !lpc32xx_tc_ready(nand, 100)) {
+               LOG_ERROR("timeout while waiting for completion of DMA");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       return retval;
+}
+
+static int lpc32xx_dma_ready(struct nand_device *nand, int timeout)
+{
+       struct target *target = nand->target;
+
+       LOG_DEBUG("lpc32xx_dma_ready count start=%d", timeout);
+
+       do {
+               uint32_t tc_stat;
+               uint32_t err_stat;
+               int retval;
+
+               /* Read DMACRawIntTCStat */
+               retval = target_read_u32(target, 0x31000014, &tc_stat);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not read DMACRawIntTCStat");
+                       return 0;
+               }
+               /* Read DMACRawIntErrStat */
+               retval = target_read_u32(target, 0x31000018, &err_stat);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not read DMACRawIntErrStat");
+                       return 0;
+               }
+               if ((tc_stat | err_stat) & 1) {
+                       LOG_DEBUG("lpc32xx_dma_ready count=%d",
+                                 timeout);
+                       if (err_stat & 1) {
+                               LOG_ERROR("lpc32xx_dma_ready "
+                                         "DMA error, aborted");
+                               return 0;
+                       } else {
+                               return 1;
+                       }
+               }
+
+               alive_sleep(1);
+       } while (timeout-- > 0);
+
+       return 0;
+}
+
+static uint32_t slc_ecc_copy_to_buffer(uint8_t * spare,
+               const uint32_t * ecc, int count)
+{
+       int i;
+       for (i = 0; i < (count * 3); i += 3) {
+               uint32_t ce = ecc[i/3];
+               ce = ~(ce << 2) & 0xFFFFFF;
+               spare[i+2] = (uint8_t)(ce & 0xFF); ce >>= 8;
+               spare[i+1] = (uint8_t)(ce & 0xFF); ce >>= 8;
+               spare[i]   = (uint8_t)(ce & 0xFF);
+       }
+       return 0;
+}
+
+static void lpc32xx_dump_oob(uint8_t *oob, uint32_t oob_size)
+{
+       int addr = 0;
+       while (oob_size > 0) {
+               LOG_DEBUG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x", addr,
+                         oob[0], oob[1], oob[2], oob[3],
+                         oob[4], oob[5], oob[6], oob[7]);
+               oob += 8;
+               addr += 8;
+               oob_size -= 8;
+       }       
+}
+
+static int lpc32xx_write_page_slc(struct nand_device *nand,
+                                 struct working_area *pworking_area,
+                                 uint32_t page, uint8_t *data,
+                                 uint32_t data_size, uint8_t *oob,
+                                 uint32_t oob_size)
+{
+       struct target *target = nand->target;
+       int retval;
+       uint32_t target_mem_base;
+
+       LOG_DEBUG("SLC write page %x data=%d, oob=%d, "
+                 "data_size=%d, oob_size=%d",
+                 page, data != 0, oob != 0, data_size, oob_size);
+
+       target_mem_base = pworking_area->address;
+       /* 
+        * Skip writting page which has all 0xFF data as this will
+        * generate 0x0 value.
+        */
+       if (data && !oob) {
+               uint32_t i, all_ff = 1;
+               for (i = 0; i < data_size; i++)
+                       if (data[i] != 0xFF) {
+                               all_ff = 0;
+                               break;
+                       }
+               if (all_ff)
+                       return ERROR_OK;
+       }
+       /* Make the dma descriptors in local memory */
+       int nll = lpc32xx_make_dma_list(target_mem_base, nand->page_size, 0);
+       /* Write them to target.
+          XXX: Assumes host and target have same byte sex.
+       */
+       retval = target_write_memory(target, target_mem_base, 4,
+                                    nll * sizeof(dmac_ll_t) / 4,
+                                    (uint8_t *)dmalist);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not write DMA descriptors to IRAM");
+               return retval;
+       }
+
+       retval = nand_page_command(nand, page, NAND_CMD_SEQIN, !data);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("NAND_CMD_SEQIN failed");
+               return retval;
+       }
+
+        /* SLC_CFG =
+               Force nCE assert,
+               DMA ECC enabled,
+               ECC enabled,
+               DMA burst enabled,
+               DMA write to SLC,
+               WIDTH = bus_width
+       */
+       retval = target_write_u32(target, 0x20020014, 0x3c);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not set SLC_CFG");
+               return retval;
+       }
+       if (data) {
+               /* Write data to target */
+               static uint8_t fdata[2048];
+               memset(fdata, 0xFF, nand->page_size);
+               memcpy(fdata, data, data_size);
+               retval = target_write_memory(target,
+                                            target_mem_base + DATA_OFFS,
+                                            4, nand->page_size/4, fdata);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not write data to IRAM");
+                       return retval;
+               }
+
+               /* Write first decriptor to DMA controller */
+               retval = target_write_memory(target, 0x31000100, 4,
+                                            sizeof(dmac_ll_t) / 4,
+                                            (uint8_t *)dmalist);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not write DMA descriptor to DMAC");
+                       return retval;
+               }
+
+               /* Start xfer of data from iram to flash using DMA */
+               int tot_size = nand->page_size;
+               tot_size += tot_size == 2048 ? 64 : 16;
+               retval = lpc32xx_start_slc_dma(nand, tot_size, 0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("DMA failed");
+                       return retval;
+               }
+               
+               /* Wait for DMA to finish.  SLC is not finished at this stage */
+               if (!lpc32xx_dma_ready(nand, 100)) {
+                       LOG_ERROR("Data DMA failed during write");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+       } /* data xfer */
+
+       /* Copy OOB to iram */
+       static uint8_t foob[64];
+       int foob_size = nand->page_size == 2048 ? 64 : 16;
+       memset(foob, 0xFF, foob_size);
+       if (oob) { /* Raw mode */
+               memcpy(foob, oob, oob_size);
+       } else {
+               /* Get HW generated ECC, made while writing data */
+               int ecc_count = nand->page_size == 2048 ? 8 : 2;
+               static uint32_t hw_ecc[8];
+               retval = target_read_memory(target, target_mem_base + ECC_OFFS,
+                                           4, ecc_count, (uint8_t *)hw_ecc);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Reading hw generated ECC from IRAM failed");
+                       return retval;
+               }
+               /* Copy to oob, at correct offsets */
+               static uint8_t ecc[24];
+               slc_ecc_copy_to_buffer(ecc, hw_ecc, ecc_count);
+               int *layout = nand->page_size == 2048 ? lp_ooblayout : sp_ooblayout;
+               int i;
+               for (i = 0; i < ecc_count * 3; i++)
+                       foob[layout[i]] = ecc[i];
+               lpc32xx_dump_oob(foob, foob_size);
+       }
+       retval = target_write_memory(target, target_mem_base + SPARE_OFFS, 4,
+                                    foob_size / 4, foob);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Writing OOB to IRAM failed");
+               return retval;
+       }
+
+       /* Write OOB decriptor to DMA controller */
+       retval = target_write_memory(target, 0x31000100, 4,
+                                    sizeof(dmac_ll_t) / 4,
+                                    (uint8_t *)(&dmalist[nll-1]));
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not write OOB DMA descriptor to DMAC");
+               return retval;
+       }
+       if (data) {
+               /* Only restart DMA with last descriptor,
+                * don't setup SLC again */
+
+               /* DMACIntTCClear = ch0 */
+               retval = target_write_u32(target, 0x31000008, 1);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not set DMACIntTCClear");
+                       return retval;
+               }
+               /* DMACCxConfig=
+                       E=1,
+                       SrcPeripheral = 1 (SLC),
+                       DestPeripheral = 1 (SLC),
+                       FlowCntrl = 2 (Pher -> Mem, DMA),
+                       IE = 0,
+                       ITC = 0,
+                       L= 0,
+                       H=0
+               */
+               retval = target_write_u32(target, 0x31000110,
+                                         1 | 1<<1 | 1<<6 | 2<<11 | 0<<14
+                                         | 0<<15 | 0<<16 | 0<<18);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not set DMACC0Config");
+                       return retval;
+               }
+               /* Wait finish */
+               if (!lpc32xx_tc_ready(nand, 100)) {
+                       LOG_ERROR("timeout while waiting for "
+                                 "completion of DMA");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       } else {
+               /* Start xfer of data from iram to flash using DMA */
+               retval = lpc32xx_start_slc_dma(nand, foob_size, 1);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("DMA OOB failed");
+                       return retval;
+               }
+       }
+
+       /* Let NAND start actual writing */
+       retval = nand_write_finish(nand);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("nand_write_finish failed");
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_write_page(struct nand_device *nand, uint32_t page,
+                             uint8_t *data, uint32_t data_size,
+                             uint8_t *oob, uint32_t oob_size)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval = ERROR_OK;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               if (!data && oob) {
+                       LOG_ERROR("LPC32xx MLC controller can't write "
+                                 "OOB data only");
+                       return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+               }
+
+               if (oob && (oob_size > 24)) {
+                       LOG_ERROR("LPC32xx MLC controller can't write more "
+                                 "than 6 bytes for each quarter's OOB data");
+                       return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+               }
+
+               if (data_size > (uint32_t)nand->page_size) {
+                       LOG_ERROR("data size exceeds page size");
+                       return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+               }
+
+               retval = lpc32xx_write_page_mlc(nand, page, data, data_size,
+                                               oob, oob_size);
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               struct working_area *pworking_area;
+               if (!data && oob) {
+                       /*
+                        * if oob only mode is active original method is used
+                        * as SLC controller hangs during DMA interworking. (?)
+                        * Anyway the code supports the oob only mode below.
+                        */
+                       return nand_write_page_raw(nand, page, data,
+                                                  data_size, oob, oob_size);
+               }
+               retval = target_alloc_working_area(target,
+                                                  nand->page_size + DATA_OFFS,
+                                                  &pworking_area);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Can't allocate working area in "
+                                 "LPC internal RAM");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+               retval = lpc32xx_write_page_slc(nand, pworking_area, page,
+                                               data, data_size, oob, oob_size);
+               target_free_working_area(target, pworking_area);
+       }
+
+       return retval;
+}
+
+static int lpc32xx_read_page_mlc(struct nand_device *nand, uint32_t page,
+                                uint8_t *data, uint32_t data_size,
+                                uint8_t *oob, uint32_t oob_size)
+{
+       struct target *target = nand->target;
+       static uint8_t page_buffer[2048];
+       static uint8_t oob_buffer[64];
+       uint32_t page_bytes_done = 0;
+       uint32_t oob_bytes_done = 0;
+       uint32_t mlc_isr;
+       int retval;
+
+       if (!data && oob) {
+               /* MLC_CMD = Read OOB
+                * we can use the READOOB command on both small and large page
+                * devices, as the controller translates the 0x50 command to
+                * a 0x0 with appropriate positioning of the serial buffer
+                * read pointer
+                */
+               retval = target_write_u32(target, 0x200b8000, NAND_CMD_READOOB);
+       } else {
+               /* MLC_CMD = Read0 */
+               retval = target_write_u32(target, 0x200b8000, NAND_CMD_READ0);
+       }
+       if (ERROR_OK != retval) {
+               LOG_ERROR("could not set MLC_CMD");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+       if (nand->page_size == 512) {
+               /* small page device */
+               /* MLC_ADDR = 0x0 (one column cycle) */
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_ADDR = row */
+               retval = target_write_u32(target, 0x200b8004, page & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004,
+                                         (page >> 8) & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (nand->address_cycles == 4) {
+                       retval = target_write_u32(target, 0x200b8004,
+                                                 (page >> 16) & 0xff);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not set MLC_ADDR");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+       } else {
+               /* large page device */
+               /* MLC_ADDR = 0x0 (two column cycles) */
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004, 0x0);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_ADDR = row */
+               retval = target_write_u32(target, 0x200b8004, page & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+               retval = target_write_u32(target, 0x200b8004,
+                                         (page >> 8) & 0xff);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ADDR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               /* MLC_CMD = Read Start */
+               retval = target_write_u32(target, 0x200b8000,
+                                         NAND_CMD_READSTART);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_CMD");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       while (page_bytes_done < (uint32_t)nand->page_size) {
+               /* MLC_ECC_AUTO_DEC_REG = dummy */
+               retval = target_write_u32(target, 0x200b8014, 0xaa55aa55);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not set MLC_ECC_AUTO_DEC_REG");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (!lpc32xx_controller_ready(nand, 1000)) {
+                       LOG_ERROR("timeout while waiting for "
+                                 "completion of auto decode cycle");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               retval = target_read_u32(target, 0x200b8048, &mlc_isr);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("could not read MLC_ISR");
+                       return ERROR_NAND_OPERATION_FAILED;
+               }
+
+               if (mlc_isr & 0x8) {
+                       if (mlc_isr & 0x40) {
+                               LOG_ERROR("uncorrectable error detected: "
+                                         "0x%2.2x", (unsigned)mlc_isr);
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+
+                       LOG_WARNING("%i symbol error detected and corrected",
+                                   ((int)(((mlc_isr & 0x30) >> 4) + 1)));
+               }
+
+               if (data) {
+                       retval = target_read_memory(target, 0x200a8000, 4, 128,
+                                                page_buffer + page_bytes_done);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not read MLC_BUF (data)");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+
+               if (oob) {
+                       retval = target_read_memory(target, 0x200a8000, 4, 4,
+                                                oob_buffer + oob_bytes_done);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not read MLC_BUF (oob)");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+
+               page_bytes_done += 512;
+               oob_bytes_done += 16;
+       }
+
+       if (data)
+               memcpy(data, page_buffer, data_size);
+
+       if (oob)
+               memcpy(oob, oob_buffer, oob_size);
+
+       return ERROR_OK;
+}
+
+static int lpc32xx_read_page_slc(struct nand_device *nand,
+                                struct working_area *pworking_area,
+                                uint32_t page, uint8_t *data,
+                                uint32_t data_size, uint8_t *oob,
+                                uint32_t oob_size)
+{
+       struct target *target = nand->target;
+       int retval;
+       uint32_t target_mem_base;
+
+       LOG_DEBUG("SLC read page %x data=%d, oob=%d",
+                 page, data_size, oob_size);
+
+       target_mem_base = pworking_area->address;
+
+       /* Make the dma descriptors in local memory */
+       int nll = lpc32xx_make_dma_list(target_mem_base, nand->page_size, 1);
+       /* Write them to target.
+          XXX: Assumes host and target have same byte sex.
+       */
+       retval = target_write_memory(target, target_mem_base, 4,
+                                    nll * sizeof(dmac_ll_t) / 4,
+                                    (uint8_t *)dmalist);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not write DMA descriptors to IRAM");
+               return retval;
+       }
+
+       retval = nand_page_command(nand, page, NAND_CMD_READ0, 0);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("lpc32xx_read_page_slc: NAND_CMD_READ0 failed");
+               return retval;
+       }
+
+        /* SLC_CFG =
+               Force nCE assert,
+               DMA ECC enabled,
+               ECC enabled,
+               DMA burst enabled,
+               DMA read from SLC,
+               WIDTH = bus_width
+       */
+       retval = target_write_u32(target, 0x20020014, 0x3e);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("lpc32xx_read_page_slc: Could not set SLC_CFG");
+               return retval;
+       }
+
+       /* Write first decriptor to DMA controller */
+       retval = target_write_memory(target, 0x31000100, 4,
+                                    sizeof(dmac_ll_t) / 4, (uint8_t *)dmalist);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not write DMA descriptor to DMAC");
+               return retval;
+       }
+
+       /* Start xfer of data from flash to iram using DMA */
+       int tot_size = nand->page_size;
+       tot_size += nand->page_size == 2048 ? 64 : 16;
+       retval = lpc32xx_start_slc_dma(nand, tot_size, 1);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("lpc32xx_read_page_slc: DMA read failed");
+               return retval;
+       }
+
+       /* Copy data from iram */
+       if (data) {
+               retval = target_read_memory(target, target_mem_base + DATA_OFFS,
+                                           4, data_size/4, data);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not read data from IRAM");
+                       return retval;
+               }
+       }
+       if (oob) {
+               /* No error correction, just return data as read from flash */
+               retval = target_read_memory(target,
+                                           target_mem_base + SPARE_OFFS, 4,
+                                           oob_size/4, oob);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not read OOB from IRAM");
+                       return retval;
+               }
+               return ERROR_OK;
+       }
+
+       /* Copy OOB from flash, stored in IRAM */
+       static uint8_t foob[64];
+       retval = target_read_memory(target, target_mem_base + SPARE_OFFS,
+                                   4, nand->page_size == 2048 ? 16 : 4, foob);
+       lpc32xx_dump_oob(foob, nand->page_size == 2048 ? 64 : 16);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not read OOB from IRAM");
+               return retval;
+       }
+       /* Copy ECC from HW, generated while reading */
+       int ecc_count = nand->page_size == 2048 ? 8 : 2;
+       static uint32_t hw_ecc[8]; /* max size */
+       retval = target_read_memory(target, target_mem_base + ECC_OFFS, 4,
+                                   ecc_count, (uint8_t *)hw_ecc);
+       if (ERROR_OK != retval) {
+               LOG_ERROR("Could not read hw generated ECC from IRAM");
+               return retval;
+       }
+       static uint8_t ecc[24];
+       slc_ecc_copy_to_buffer(ecc, hw_ecc, ecc_count);
+       /* Copy ECC from flash using correct layout */
+       static uint8_t fecc[24]; /* max size */
+       int *layout = nand->page_size == 2048 ? lp_ooblayout : sp_ooblayout;
+       int i;
+       for (i = 0; i < ecc_count * 3; i++)
+               fecc[i] = foob[layout[i]];
+       /* Compare ECC and possibly correct data */
+       for (i = 0; i < ecc_count; i++) {
+               retval = nand_correct_data(nand, data + 256*i, &fecc[i * 3],
+                                          &ecc[i * 3]);
+               if (retval > 0)
+                       LOG_WARNING("error detected and corrected: %d/%d",
+                                    page, i);
+               if (retval < 0)
+                       break;
+       }
+       if (i == ecc_count)
+               retval = ERROR_OK;
+       else {
+               LOG_ERROR("uncorrectable error detected: %d/%d", page, i);
+               retval = ERROR_NAND_OPERATION_FAILED;
+       }
+       return retval;
+}
+
+static int lpc32xx_read_page(struct nand_device *nand, uint32_t page,
+                            uint8_t *data, uint32_t data_size,
+                            uint8_t *oob, uint32_t oob_size)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval = ERROR_OK;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       if (lpc32xx_info->selected_controller == LPC32xx_NO_CONTROLLER) {
+               LOG_ERROR("BUG: no LPC32xx NAND flash controller selected");
+               return ERROR_NAND_OPERATION_FAILED;
+       } else if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+               if (data_size > (uint32_t)nand->page_size) {
+                       LOG_ERROR("data size exceeds page size");
+                       return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+               }
+               retval = lpc32xx_read_page_mlc(nand, page, data, data_size,
+                                              oob, oob_size);
+       } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+               struct working_area *pworking_area;
+
+               retval = target_alloc_working_area(target,
+                                                  nand->page_size + 0x200,
+                                                  &pworking_area);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Can't allocate working area in "
+                                 "LPC internal RAM");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+               retval = lpc32xx_read_page_slc(nand, pworking_area, page,
+                                             data, data_size, oob, oob_size);
+               target_free_working_area(target, pworking_area);
+       }
+
+       return retval;
+}
+
+static int lpc32xx_controller_ready(struct nand_device *nand, int timeout)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       LOG_DEBUG("lpc32xx_controller_ready count start=%d", timeout);
+
+       do {
+               if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+                       uint8_t status;
+
+                       /* Read MLC_ISR, wait for controller to become ready */
+                       retval = target_read_u8(target, 0x200b8048, &status);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not set MLC_STAT");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+
+                       if (status & 2) {
+                               LOG_DEBUG("lpc32xx_controller_ready count=%d",
+                                         timeout);
+                               return 1;
+                       }
+               } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+                       uint32_t status;
+
+                       /* Read SLC_STAT and check READY bit */
+                       retval = target_read_u32(target, 0x20020018, &status);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not set SLC_STAT");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+
+                       if (status & 1) {
+                               LOG_DEBUG("lpc32xx_controller_ready count=%d",
+                                         timeout);
+                               return 1;
+                       }
+               }
+
+               alive_sleep(1);
+       } while (timeout-- > 0);
+
+       return 0;
+}
+
+static int lpc32xx_nand_ready(struct nand_device *nand, int timeout)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = nand->controller_priv;
+       struct target *target = nand->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("target must be halted to use LPC32xx "
+                         "NAND flash controller");
+               return ERROR_NAND_OPERATION_FAILED;
+       }
+
+       LOG_DEBUG("lpc32xx_nand_ready count start=%d", timeout);
+
+       do {
+               if (lpc32xx_info->selected_controller == LPC32xx_MLC_CONTROLLER) {
+                       uint8_t status = 0x0;
+
+                       /* Read MLC_ISR, wait for NAND flash device to
+                        * become ready */
+                       retval = target_read_u8(target, 0x200b8048, &status);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not read MLC_ISR");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+
+                       if (status & 1) {
+                               LOG_DEBUG("lpc32xx_nand_ready count end=%d",
+                                         timeout);
+                               return 1;
+                       }
+               } else if (lpc32xx_info->selected_controller == LPC32xx_SLC_CONTROLLER) {
+                       uint32_t status = 0x0;
+
+                       /* Read SLC_STAT and check READY bit */
+                       retval = target_read_u32(target, 0x20020018, &status);
+                       if (ERROR_OK != retval) {
+                               LOG_ERROR("could not read SLC_STAT");
+                               return ERROR_NAND_OPERATION_FAILED;
+                       }
+
+                       if (status & 1) {
+                               LOG_DEBUG("lpc32xx_nand_ready count end=%d",
+                                         timeout);
+                               return 1;
+                       }
+               }
+
+               alive_sleep(1);
+       } while (timeout-- > 0);
+
+       return 0;
+}
+
+static int lpc32xx_tc_ready(struct nand_device *nand, int timeout)
+{
+       struct target *target = nand->target;
+
+       LOG_DEBUG("lpc32xx_tc_ready count start=%d", timeout);
+
+       do {
+               uint32_t status = 0x0;
+               int retval;
+               /* Read SLC_INT_STAT and check INT_TC_STAT bit */
+               retval = target_read_u32(target, 0x2002001c, &status);
+               if (ERROR_OK != retval) {
+                       LOG_ERROR("Could not read SLC_INT_STAT");
+                       return 0;
+               }
+               if (status & 2){
+                       LOG_DEBUG("lpc32xx_tc_ready count=%d", timeout);
+                       return 1;
+               }
+
+               alive_sleep(1);
+       } while (timeout-- > 0);
+
+       return 0;
+}
+
+COMMAND_HANDLER(handle_lpc32xx_select_command)
+{
+       struct lpc32xx_nand_controller *lpc32xx_info = NULL;
+       char *selected[] = {
+               "no", "mlc", "slc"
+       };
+
+       if ((CMD_ARGC < 1) || (CMD_ARGC > 3)) {
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       unsigned num;
+       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
+       struct nand_device *nand = get_nand_device_by_num(num);
+       if (!nand) {
+               command_print(CMD_CTX, "nand device '#%s' is out of bounds",
+                             CMD_ARGV[0]);
+               return ERROR_OK;
+       }
+
+       lpc32xx_info = nand->controller_priv;
+
+       if (CMD_ARGC >= 2) {
+               if (strcmp(CMD_ARGV[1], "mlc") == 0) {
+                       lpc32xx_info->selected_controller =
+                               LPC32xx_MLC_CONTROLLER;
+               } else if (strcmp(CMD_ARGV[1], "slc") == 0) {
+                       lpc32xx_info->selected_controller =
+                               LPC32xx_SLC_CONTROLLER;
+               } else {
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       command_print(CMD_CTX, "%s controller selected",
+                     selected[lpc32xx_info->selected_controller]);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration lpc32xx_exec_command_handlers[] = {
+       {
+               .name = "select",
+               .handler = handle_lpc32xx_select_command,
+               .mode = COMMAND_EXEC,
+               .help = "select MLC or SLC controller (default is MLC)",
+               .usage = "bank_id ['mlc'|'slc' ]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+static const struct command_registration lpc32xx_command_handler[] = {
+       {
+               .name = "lpc32xx",
+               .mode = COMMAND_ANY,
+               .help = "LPC32xx NAND flash controller commands",
+               .chain = lpc32xx_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct nand_flash_controller lpc32xx_nand_controller = {
+       .name = "lpc32xx",
+       .commands = lpc32xx_command_handler,
+       .nand_device_command = lpc32xx_nand_device_command,
+       .init = lpc32xx_init,
+       .reset = lpc32xx_reset,
+       .command = lpc32xx_command,
+       .address = lpc32xx_address,
+       .write_data = lpc32xx_write_data,
+       .read_data = lpc32xx_read_data,
+       .write_page = lpc32xx_write_page,
+       .read_page = lpc32xx_read_page,
+       .nand_ready = lpc32xx_nand_ready,
+};
diff --git a/src/flash/nand/lpc32xx.h b/src/flash/nand/lpc32xx.h
new file mode 100644 (file)
index 0000000..fd87245
--- /dev/null
@@ -0,0 +1,39 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   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.             *
+ ***************************************************************************/
+#ifndef LPC32xx_NAND_CONTROLLER_H
+#define LPC32xx_NAND_CONTROLLER_H
+
+enum lpc32xx_selected_controller
+{
+       LPC32xx_NO_CONTROLLER,
+       LPC32xx_MLC_CONTROLLER,
+       LPC32xx_SLC_CONTROLLER,
+};
+
+struct lpc32xx_nand_controller
+{
+       int osc_freq;
+       enum lpc32xx_selected_controller selected_controller;
+       int sw_write_protection;
+       uint32_t sw_wp_lower_bound;
+       uint32_t sw_wp_upper_bound;
+};
+
+#endif /*LPC32xx_NAND_CONTROLLER_H */

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)