diff options
Diffstat (limited to 'src/flash/nand')
-rw-r--r-- | src/flash/nand/Makefile.am | 27 | ||||
-rw-r--r-- | src/flash/nand/davinci.c | 756 | ||||
-rw-r--r-- | src/flash/nand/lpc3180.c | 910 | ||||
-rw-r--r-- | src/flash/nand/lpc3180.h | 40 | ||||
-rw-r--r-- | src/flash/nand/mx3.c | 879 | ||||
-rw-r--r-- | src/flash/nand/mx3.h | 117 | ||||
-rw-r--r-- | src/flash/nand/nonce.c | 80 | ||||
-rw-r--r-- | src/flash/nand/orion.c | 180 | ||||
-rw-r--r-- | src/flash/nand/s3c2410.c | 123 | ||||
-rw-r--r-- | src/flash/nand/s3c2412.c | 79 | ||||
-rw-r--r-- | src/flash/nand/s3c2440.c | 171 | ||||
-rw-r--r-- | src/flash/nand/s3c2443.c | 80 | ||||
-rw-r--r-- | src/flash/nand/s3c24xx.c | 133 | ||||
-rw-r--r-- | src/flash/nand/s3c24xx.h | 84 | ||||
-rw-r--r-- | src/flash/nand/s3c24xx_regs.h | 132 |
15 files changed, 3791 insertions, 0 deletions
diff --git a/src/flash/nand/Makefile.am b/src/flash/nand/Makefile.am new file mode 100644 index 00000000..e95717e2 --- /dev/null +++ b/src/flash/nand/Makefile.am @@ -0,0 +1,27 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/flash \ + -I$(top_srcdir)/src/helper \ + -I$(top_srcdir)/src/jtag \ + -I$(top_srcdir)/src/target + +noinst_LTLIBRARIES = libocdflashnand.la + +libocdflashnand_la_SOURCES = \ + nonce.c \ + davinci.c \ + lpc3180.c \ + mx3.c \ + orion.c \ + s3c24xx.c \ + s3c2410.c \ + s3c2412.c \ + s3c2440.c \ + s3c2443.c + +noinst_HEADERS = \ + lpc3180.h \ + mx3.h \ + s3c24xx.h \ + s3c24xx_regs.h + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff --git a/src/flash/nand/davinci.c b/src/flash/nand/davinci.c new file mode 100644 index 00000000..72cd378a --- /dev/null +++ b/src/flash/nand/davinci.c @@ -0,0 +1,756 @@ +/*************************************************************************** + * 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 "arm_nandio.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 { + struct target *target; + + uint8_t chipsel; /* chipselect 0..3 == CS2..CS5 */ + uint8_t eccmode; + + /* Async EMIF controller base */ + uint32_t aemif; + + /* NAND chip addresses */ + uint32_t data; /* without CLE or ALE */ + uint32_t cmd; /* with CLE */ + uint32_t addr; /* with ALE */ + + /* write acceleration */ + struct arm_nand_data io; + + /* page i/o for the relevant flavor of hardware ECC */ + int (*read_page)(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size); + int (*write_page)(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size); +}; + +#define NANDFCR 0x60 /* flash control register */ +#define NANDFSR 0x64 /* flash status register */ +#define NANDFECC 0x70 /* 1-bit ECC data, CS0, 1st of 4 */ +#define NAND4BITECCLOAD 0xbc /* 4-bit ECC, load saved values */ +#define NAND4BITECC 0xc0 /* 4-bit ECC data, 1st of 4 */ +#define NANDERRADDR 0xd0 /* 4-bit ECC err addr, 1st of 2 */ +#define NANDERRVAL 0xd8 /* 4-bit ECC err value, 1st of 2 */ + +static int halted(struct target *target, const char *label) +{ + if (target->state == TARGET_HALTED) + return true; + + LOG_ERROR("Target must be halted to use NAND controller (%s)", label); + return false; +} + +static int davinci_init(struct nand_device *nand) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + uint32_t nandfcr; + + if (!halted(target, "init")) + return ERROR_NAND_OPERATION_FAILED; + + /* We require something else to have configured AEMIF to talk + * to NAND chip in this range (including timings and width). + */ + target_read_u32(target, info->aemif + NANDFCR, &nandfcr); + if (!(nandfcr & (1 << info->chipsel))) { + LOG_ERROR("chip address %08" PRIx32 " not NAND-enabled?", info->data); + return ERROR_NAND_OPERATION_FAILED; + } + + /* REVISIT verify: AxCR must be in 8-bit mode, since that's all we + * tested. 16 bit support should work too; but not with 4-bit ECC. + */ + + return ERROR_OK; +} + +static int davinci_reset(struct nand_device *nand) +{ + return ERROR_OK; +} + +static int davinci_nand_ready(struct nand_device *nand, int timeout) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + uint32_t nandfsr; + + /* NOTE: return code is zero/error, else success; not ERROR_* */ + + if (!halted(target, "ready")) + return 0; + + do { + target_read_u32(target, info->aemif + NANDFSR, &nandfsr); + + if (nandfsr & 0x01) + return 1; + + alive_sleep(1); + } while (timeout-- > 0); + + return 0; +} + +static int davinci_command(struct nand_device *nand, uint8_t command) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = 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 *nand, uint8_t address) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *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 *nand, uint16_t data) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *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 *nand, void *data) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *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 reads be MUCH faster */ + +static int davinci_read_block_data(struct nand_device *nand, + uint8_t *data, int data_size) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + uint32_t nfdata = info->data; + uint32_t tmp; + + if (!halted(target, "read_block")) + return ERROR_NAND_OPERATION_FAILED; + + while (data_size >= 4) { + target_read_u32(target, nfdata, &tmp); + + data[0] = tmp; + data[1] = tmp >> 8; + data[2] = tmp >> 16; + data[3] = tmp >> 24; + + data_size -= 4; + data += 4; + } + + while (data_size > 0) { + target_read_u8(target, nfdata, data); + + data_size -= 1; + data += 1; + } + + return ERROR_OK; +} + +static int davinci_write_block_data(struct nand_device *nand, + uint8_t *data, int data_size) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + uint32_t nfdata = info->data; + uint32_t tmp; + int status; + + if (!halted(target, "write_block")) + return ERROR_NAND_OPERATION_FAILED; + + /* try the fast way first */ + status = arm_nandwrite(&info->io, data, data_size); + if (status != ERROR_NAND_NO_BUFFER) + return status; + + /* else do it slowly */ + while (data_size >= 4) { + tmp = le_to_h_u32(data); + target_write_u32(target, nfdata, tmp); + + data_size -= 4; + data += 4; + } + + while (data_size > 0) { + target_write_u8(target, nfdata, *data); + + data_size -= 1; + data += 1; + } + + return ERROR_OK; +} + +static int davinci_write_page(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + struct davinci_nand *info = nand->controller_priv; + uint8_t *ooballoc = NULL; + int status; + + if (!nand->device) + return ERROR_NAND_DEVICE_NOT_PROBED; + if (!halted(info->target, "write_page")) + return ERROR_NAND_OPERATION_FAILED; + + /* Always write both data and OOB ... we are not "raw" I/O! */ + if (!data) { + LOG_ERROR("Missing NAND data; try 'nand raw_access enable'\n"); + return ERROR_NAND_OPERATION_FAILED; + } + + /* If we're not given OOB, write 0xff where we don't write ECC codes. */ + switch (nand->page_size) { + case 512: + oob_size = 16; + break; + case 2048: + oob_size = 64; + break; + case 4096: + oob_size = 128; + break; + default: + return ERROR_NAND_OPERATION_FAILED; + } + if (!oob) { + ooballoc = malloc(oob_size); + if (!ooballoc) + return ERROR_NAND_OPERATION_FAILED; + oob = ooballoc; + memset(oob, 0x0ff, oob_size); + } + + /* REVISIT avoid wasting SRAM: unless nand->use_raw is set, + * use 512 byte chunks. Read side support will often want + * to include oob_size ... + */ + info->io.chunk_size = nand->page_size; + + status = info->write_page(nand, page, data, data_size, oob, oob_size); + free(ooballoc); + return status; +} + +static int davinci_read_page(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + struct davinci_nand *info = nand->controller_priv; + + if (!nand->device) + return ERROR_NAND_DEVICE_NOT_PROBED; + if (!halted(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 *nand, uint8_t cmd, uint32_t page) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *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 *nand, + uint8_t *oob, uint32_t oob_size) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + uint8_t status; + + if (oob_size) + davinci_write_block_data(nand, oob, oob_size); + + /* non-cachemode page program */ + target_write_u8(target, info->cmd, NAND_CMD_PAGEPROG); + + if (!davinci_nand_ready(nand, 100)) + return ERROR_NAND_OPERATION_TIMEOUT; + + if (nand_read_status(nand, &status) != ERROR_OK) { + LOG_ERROR("couldn't read status"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (status & NAND_STATUS_FAIL) { + LOG_ERROR("write operation failed, status: 0x%02x", status); + return ERROR_NAND_OPERATION_FAILED; + } + + return ERROR_OK; +} + +/* + * All DaVinci family chips support 1-bit ECC on a per-chipselect basis. + */ +static int davinci_write_page_ecc1(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + unsigned oob_offset; + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + const uint32_t fcr_addr = info->aemif + NANDFCR; + const uint32_t ecc1_addr = info->aemif + NANDFECC + (4 * info->chipsel); + uint32_t fcr, ecc1; + + /* Write contiguous ECC bytes starting at specified offset. + * NOTE: Linux reserves twice as many bytes as we need; and + * for 16-bit OOB, those extra bytes are discontiguous. + */ + switch (nand->page_size) { + case 512: + oob_offset = 0; + break; + case 2048: + oob_offset = 40; + break; + default: + oob_offset = 80; + break; + } + + davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page); + + /* scrub any old ECC state */ + target_read_u32(target, ecc1_addr, &ecc1); + + target_read_u32(target, fcr_addr, &fcr); + fcr |= 1 << (8 + info->chipsel); + + do { + /* set "start csX 1bit ecc" bit */ + target_write_u32(target, fcr_addr, fcr); + + /* write 512 bytes */ + davinci_write_block_data(nand, data, 512); + data += 512; + data_size -= 512; + + /* read the ecc, pack to 3 bytes, and invert so the ecc + * in an erased block is correct + */ + target_read_u32(target, ecc1_addr, &ecc1); + ecc1 = (ecc1 & 0x0fff) | ((ecc1 & 0x0fff0000) >> 4); + ecc1 = ~ecc1; + + /* save correct ECC code into oob data */ + oob[oob_offset++] = (uint8_t)(ecc1); + oob[oob_offset++] = (uint8_t)(ecc1 >> 8); + oob[oob_offset++] = (uint8_t)(ecc1 >> 16); + + } while (data_size); + + /* write OOB into spare area */ + return davinci_writepage_tail(nand, oob, oob_size); +} + +/* + * Preferred "new style" ECC layout for use with 4-bit ECC. This somewhat + * slows down large page reads done with error correction (since the OOB + * is read first, so its ECC data can be used incrementally), but the + * manufacturer bad block markers are safe. Contrast: old "infix" style. + */ +static int davinci_write_page_ecc4(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + static const uint8_t ecc512[] = { + 0, 1, 2, 3, 4, /* 5== mfr badblock */ + 6, 7, /* 8..12 for BBT or JFFS2 */ 13, 14, 15, + }; + static const uint8_t ecc2048[] = { + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }; + static const uint8_t ecc4096[] = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }; + + struct davinci_nand *info = nand->controller_priv; + const uint8_t *l; + struct target *target = info->target; + const uint32_t fcr_addr = info->aemif + NANDFCR; + const uint32_t ecc4_addr = info->aemif + NAND4BITECC; + uint32_t fcr, ecc4; + + /* Use the same ECC layout Linux uses. For small page chips + * it's a bit cramped. + * + * NOTE: at this writing, 4KB pages have issues in Linux + * because they need more than 64 bytes of ECC data, which + * the standard ECC logic can't handle. + */ + switch (nand->page_size) { + case 512: + l = ecc512; + break; + case 2048: + l = ecc2048; + break; + default: + l = ecc4096; + break; + } + + davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page); + + /* scrub any old ECC state */ + target_read_u32(target, info->aemif + NANDERRVAL, &ecc4); + + target_read_u32(target, fcr_addr, &fcr); + fcr &= ~(0x03 << 4); + fcr |= (1 << 12) | (info->chipsel << 4); + + do { + uint32_t raw_ecc[4], *p; + int i; + + /* start 4bit ecc on csX */ + target_write_u32(target, fcr_addr, fcr); + + /* write 512 bytes */ + davinci_write_block_data(nand, data, 512); + data += 512; + data_size -= 512; + + /* read the ecc, then save it into 10 bytes in the oob */ + for (i = 0; i < 4; i++) { + target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]); + raw_ecc[i] &= 0x03ff03ff; + } + for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { + oob[*l++] = p[0] & 0xff; + oob[*l++] = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + oob[*l++] = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + oob[*l++] = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + oob[*l++] = (p[1] >> 18) & 0xff; + } + + } while (data_size); + + /* write OOB into spare area */ + return davinci_writepage_tail(nand, oob, oob_size); +} + +/* + * "Infix" OOB ... like Linux ECC_HW_SYNDROME. Avoided because it trashes + * manufacturer bad block markers, except on small page chips. Once you + * write to a page using this scheme, you need specialized code to update + * it (code which ignores now-invalid bad block markers). + * + * This is needed *only* to support older firmware. Older ROM Boot Loaders + * need it to read their second stage loader (UBL) into SRAM, but from then + * on the whole system can use the cleaner non-infix layouts. Systems with + * older second stage loaders (ABL/U-Boot, etc) or other system software + * (MVL 4.x/5.x kernels, filesystems, etc) may need it more generally. + */ +static int davinci_write_page_ecc4infix(struct nand_device *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + struct davinci_nand *info = nand->controller_priv; + struct target *target = info->target; + const uint32_t fcr_addr = info->aemif + NANDFCR; + const uint32_t ecc4_addr = info->aemif + NAND4BITECC; + uint32_t fcr, ecc4; + + davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page); + + /* scrub any old ECC state */ + target_read_u32(target, info->aemif + NANDERRVAL, &ecc4); + + target_read_u32(target, fcr_addr, &fcr); + fcr &= ~(0x03 << 4); + fcr |= (1 << 12) | (info->chipsel << 4); + + do { + uint32_t raw_ecc[4], *p; + uint8_t *l; + int i; + + /* start 4bit ecc on csX */ + target_write_u32(target, fcr_addr, fcr); + + /* write 512 bytes */ + davinci_write_block_data(nand, data, 512); + data += 512; + data_size -= 512; + + /* read the ecc */ + for (i = 0; i < 4; i++) { + target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]); + raw_ecc[i] &= 0x03ff03ff; + } + + /* skip 6 bytes of prepad, then pack 10 packed ecc bytes */ + for (i = 0, l = oob + 6, p = raw_ecc; i < 2; i++, p += 2) { + *l++ = p[0] & 0xff; + *l++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + *l++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + *l++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + *l++ = (p[1] >> 18) & 0xff; + } + + /* write this "out-of-band" data -- infix */ + davinci_write_block_data(nand, oob, 16); + oob += 16; + 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 *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t 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; +} + +NAND_DEVICE_COMMAND_HANDLER(davinci_nand_device_command) +{ + struct davinci_nand *info; + struct target *target; + unsigned long chip, aemif; + enum ecc eccmode; + int chipsel; + + /* arguments: + * - "davinci" + * - target + * - nand chip address + * - ecc mode + * - aemif address + * Plus someday, optionally, ALE and CLE masks. + */ + if (CMD_ARGC < 5) { + LOG_ERROR("parameters: %s target " + "chip_addr hwecc_mode aemif_addr", + CMD_ARGV[0]); + goto fail; + } + + target = get_target(CMD_ARGV[1]); + if (!target) { + LOG_ERROR("invalid target %s", CMD_ARGV[1]); + goto fail; + } + + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], chip); + if (chip == 0) { + LOG_ERROR("Invalid NAND chip address %s", CMD_ARGV[2]); + goto fail; + } + + if (strcmp(CMD_ARGV[3], "hwecc1") == 0) + eccmode = HWECC1; + else if (strcmp(CMD_ARGV[3], "hwecc4") == 0) + eccmode = HWECC4; + else if (strcmp(CMD_ARGV[3], "hwecc4_infix") == 0) + eccmode = HWECC4_INFIX; + else { + LOG_ERROR("Invalid ecc mode %s", CMD_ARGV[3]); + goto fail; + } + + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[4], aemif); + if (aemif == 0) { + LOG_ERROR("Invalid AEMIF controller address %s", CMD_ARGV[4]); + goto fail; + } + + /* REVISIT what we'd *like* to do is look up valid ranges using + * target-specific declarations, and not even need to pass the + * AEMIF controller address. + */ + if (aemif == 0x01e00000 /* dm6446, dm357 */ + || aemif == 0x01e10000 /* dm335, dm355 */ + || aemif == 0x01d10000 /* dm365 */ + ) { + if (chip < 0x02000000 || chip >= 0x0a000000) { + LOG_ERROR("NAND address %08lx out of range?", chip); + goto fail; + } + chipsel = (chip - 0x02000000) >> 25; + } else { + LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif); + goto fail; + } + + info = calloc(1, sizeof *info); + if (info == 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; + + info->io.target = target; + info->io.data = info->data; + + /* NOTE: for now we don't do any error correction on read. + * Nothing else in OpenOCD currently corrects read errors, + * and in any case it's *writing* that we care most about. + */ + info->read_page = nand_read_page_raw; + + switch (eccmode) { + case HWECC1: + /* ECC_HW, 1-bit corrections, 3 bytes ECC per 512 data bytes */ + info->write_page = davinci_write_page_ecc1; + break; + case HWECC4: + /* ECC_HW, 4-bit corrections, 10 bytes ECC per 512 data bytes */ + info->write_page = davinci_write_page_ecc4; + break; + case HWECC4_INFIX: + /* Same 4-bit ECC HW, with problematic page/ecc layout */ + info->read_page = davinci_read_page_ecc4infix; + info->write_page = davinci_write_page_ecc4infix; + break; + } + + return ERROR_OK; + +fail: + return ERROR_NAND_OPERATION_FAILED; +} + +struct nand_flash_controller davinci_nand_controller = { + .name = "davinci", + .nand_device_command = davinci_nand_device_command, + .init = davinci_init, + .reset = davinci_reset, + .command = davinci_command, + .address = davinci_address, + .write_data = davinci_write_data, + .read_data = davinci_read_data, + .write_page = davinci_write_page, + .read_page = davinci_read_page, + .write_block_data = davinci_write_block_data, + .read_block_data = davinci_read_block_data, + .nand_ready = davinci_nand_ready, +}; diff --git a/src/flash/nand/lpc3180.c b/src/flash/nand/lpc3180.c new file mode 100644 index 00000000..031e6b1d --- /dev/null +++ b/src/flash/nand/lpc3180.c @@ -0,0 +1,910 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "lpc3180.h" +#include "nand.h" + +static int lpc3180_reset(struct nand_device *nand); +static int lpc3180_controller_ready(struct nand_device *nand, int timeout); + +/* nand device lpc3180 <target#> <oscillator_frequency> + */ +NAND_DEVICE_COMMAND_HANDLER(lpc3180_nand_device_command) +{ + if (CMD_ARGC < 3) + { + LOG_WARNING("incomplete 'lpc3180' nand flash configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + struct target *target = get_target(CMD_ARGV[1]); + if (NULL == target) + { + LOG_ERROR("target '%s' not defined", CMD_ARGV[1]); + return ERROR_NAND_DEVICE_INVALID; + } + + uint32_t osc_freq; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], osc_freq); + + struct lpc3180_nand_controller *lpc3180_info; + lpc3180_info = malloc(sizeof(struct lpc3180_nand_controller)); + nand->controller_priv = lpc3180_info; + + lpc3180_info->target = target; + lpc3180_info->osc_freq = osc_freq; + + if ((lpc3180_info->osc_freq < 1000) || (lpc3180_info->osc_freq > 20000)) + { + LOG_WARNING("LPC3180 oscillator frequency should be between 1000 and 20000 kHz, was %i", lpc3180_info->osc_freq); + } + lpc3180_info->selected_controller = LPC3180_NO_CONTROLLER; + lpc3180_info->sw_write_protection = 0; + lpc3180_info->sw_wp_lower_bound = 0x0; + lpc3180_info->sw_wp_upper_bound = 0x0; + + return ERROR_OK; +} + +static int lpc3180_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 lpc3180_cycle_time(struct lpc3180_nand_controller *lpc3180_info) +{ + struct target *target = lpc3180_info->target; + uint32_t sysclk_ctrl, pwr_ctrl, hclkdiv_ctrl, hclkpll_ctrl; + int sysclk; + int hclk; + int hclk_pll; + float cycle; + + /* calculate timings */ + + /* determine current SYSCLK (13'MHz or main oscillator) */ + target_read_u32(target, 0x40004050, &sysclk_ctrl); + + if ((sysclk_ctrl & 1) == 0) + sysclk = lpc3180_info->osc_freq; + else + sysclk = 13000; + + /* determine selected HCLK source */ + target_read_u32(target, 0x40004044, &pwr_ctrl); + + if ((pwr_ctrl & (1 << 2)) == 0) /* DIRECT RUN mode */ + { + hclk = sysclk; + } + else + { + target_read_u32(target, 0x40004058, &hclkpll_ctrl); + hclk_pll = lpc3180_pll(sysclk, hclkpll_ctrl); + + target_read_u32(target, 0x40004040, &hclkdiv_ctrl); + + 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("LPC3180 HCLK currently clocked at %i kHz", hclk); + + cycle = (1.0 / hclk) * 1000000.0; + + return cycle; +} + +static int lpc3180_init(struct nand_device *nand) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + int bus_width = nand->bus_width ? : 8; + int address_cycles = nand->address_cycles ? : 3; + int page_size = nand->page_size ? : 512; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + /* sanitize arguments */ + if ((bus_width != 8) && (bus_width != 16)) + { + LOG_ERROR("LPC3180 only supports 8 or 16 bit bus width, not %i", bus_width); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } + + /* The LPC3180 only brings out 8 bit NAND data bus, but the controller + * would support 16 bit, too, so we just warn about this for now + */ + if (bus_width == 16) + { + LOG_WARNING("LPC3180 only supports 8 bit bus width"); + } + + /* inform calling code about selected bus width */ + nand->bus_width = bus_width; + + if ((address_cycles != 3) && (address_cycles != 4)) + { + LOG_ERROR("LPC3180 only supports 3 or 4 address cycles, not %i", address_cycles); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } + + if ((page_size != 512) && (page_size != 2048)) + { + LOG_ERROR("LPC3180 only supports 512 or 2048 byte pages, not %i", page_size); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } + + /* select MLC controller if none is currently selected */ + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_DEBUG("no LPC3180 NAND flash controller selected, using default 'mlc'"); + lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER; + } + + if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + uint32_t mlc_icr_value = 0x0; + float cycle; + int twp, twh, trp, treh, trhz, trbwb, tcea; + + /* FLASHCLK_CTRL = 0x22 (enable clock for MLC flash controller) */ + target_write_u32(target, 0x400040c8, 0x22); + + /* MLC_CEH = 0x0 (Force nCE assert) */ + target_write_u32(target, 0x200b804c, 0x0); + + /* MLC_LOCK = 0xa25e (unlock protected registers) */ + target_write_u32(target, 0x200b8044, 0xa25e); + + /* MLC_ICR = configuration */ + if (lpc3180_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; + target_write_u32(target, 0x200b8030, mlc_icr_value); + + /* calculate NAND controller timings */ + cycle = lpc3180_cycle_time(lpc3180_info); + + 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) */ + target_write_u32(target, 0x200b8044, 0xa25e); + + /* MLC_TIME_REG */ + 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)); + + lpc3180_reset(nand); + } + else if (lpc3180_info->selected_controller == LPC3180_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 clock for SLC flash controller) */ + target_write_u32(target, 0x400040c8, 0x05); + + /* SLC_CFG = 0x (Force nCE assert, ECC enabled, WIDTH = bus_width) */ + target_write_u32(target, 0x20020014, 0x28 | (bus_width == 16) ? 1 : 0); + + /* calculate NAND controller timings */ + cycle = lpc3180_cycle_time(lpc3180_info); + + 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 */ + 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)); + + lpc3180_reset(nand); + } + + return ERROR_OK; +} + +static int lpc3180_reset(struct nand_device *nand) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* MLC_CMD = 0xff (reset controller and NAND device) */ + target_write_u32(target, 0x200b8000, 0xff); + + if (!lpc3180_controller_ready(nand, 100)) + { + LOG_ERROR("LPC3180 NAND controller timed out after reset"); + return ERROR_NAND_OPERATION_TIMEOUT; + } + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + /* SLC_CTRL = 0x6 (ECC_CLEAR, SW_RESET) */ + target_write_u32(target, 0x20020010, 0x6); + + if (!lpc3180_controller_ready(nand, 100)) + { + LOG_ERROR("LPC3180 NAND controller timed out after reset"); + return ERROR_NAND_OPERATION_TIMEOUT; + } + } + + return ERROR_OK; +} + +static int lpc3180_command(struct nand_device *nand, uint8_t command) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* MLC_CMD = command */ + target_write_u32(target, 0x200b8000, command); + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + /* SLC_CMD = command */ + target_write_u32(target, 0x20020008, command); + } + + return ERROR_OK; +} + +static int lpc3180_address(struct nand_device *nand, uint8_t address) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* MLC_ADDR = address */ + target_write_u32(target, 0x200b8004, address); + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + /* SLC_ADDR = address */ + target_write_u32(target, 0x20020004, address); + } + + return ERROR_OK; +} + +static int lpc3180_write_data(struct nand_device *nand, uint16_t data) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* MLC_DATA = data */ + target_write_u32(target, 0x200b0000, data); + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + /* SLC_DATA = data */ + target_write_u32(target, 0x20020000, data); + } + + return ERROR_OK; +} + +static int lpc3180_read_data(struct nand_device *nand, void *data) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* data = MLC_DATA, use sized access */ + if (nand->bus_width == 8) + { + uint8_t *data8 = data; + target_read_u8(target, 0x200b0000, data8); + } + else if (nand->bus_width == 16) + { + uint16_t *data16 = data; + target_read_u16(target, 0x200b0000, data16); + } + else + { + LOG_ERROR("BUG: bus_width neither 8 nor 16 bit"); + return ERROR_NAND_OPERATION_FAILED; + } + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + uint32_t data32; + + /* data = SLC_DATA, must use 32-bit access */ + target_read_u32(target, 0x20020000, &data32); + + if (nand->bus_width == 8) + { + uint8_t *data8 = data; + *data8 = data32 & 0xff; + } + else if (nand->bus_width == 16) + { + uint16_t *data16 = data; + *data16 = data32 & 0xffff; + } + else + { + LOG_ERROR("BUG: bus_width neither 8 nor 16 bit"); + return ERROR_NAND_OPERATION_FAILED; + } + } + + return ERROR_OK; +} + +static int lpc3180_write_page(struct nand_device *nand, uint32_t page, uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + int retval; + uint8_t status; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + uint8_t *page_buffer; + uint8_t *oob_buffer; + int quarter, num_quarters; + + if (!data && oob) + { + LOG_ERROR("LPC3180 MLC controller can't write OOB data only"); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } + + if (oob && (oob_size > 6)) + { + LOG_ERROR("LPC3180 MLC controller can't write more than 6 bytes of 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; + } + + /* MLC_CMD = sequential input */ + target_write_u32(target, 0x200b8000, NAND_CMD_SEQIN); + + page_buffer = malloc(512); + oob_buffer = malloc(6); + + if (nand->page_size == 512) + { + /* MLC_ADDR = 0x0 (one column cycle) */ + target_write_u32(target, 0x200b8004, 0x0); + + /* MLC_ADDR = row */ + target_write_u32(target, 0x200b8004, page & 0xff); + target_write_u32(target, 0x200b8004, (page >> 8) & 0xff); + + if (nand->address_cycles == 4) + target_write_u32(target, 0x200b8004, (page >> 16) & 0xff); + } + else + { + /* MLC_ADDR = 0x0 (two column cycles) */ + target_write_u32(target, 0x200b8004, 0x0); + target_write_u32(target, 0x200b8004, 0x0); + + /* MLC_ADDR = row */ + target_write_u32(target, 0x200b8004, page & 0xff); + target_write_u32(target, 0x200b8004, (page >> 8) & 0xff); + } + + /* 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, (nand->page_size == 512) ? 6 : 24); + if (oob) + { + memcpy(page_buffer, oob, thisrun_oob_size); + oob_size -= thisrun_oob_size; + oob += thisrun_oob_size; + } + + /* write MLC_ECC_ENC_REG to start encode cycle */ + target_write_u32(target, 0x200b8008, 0x0); + + target_write_memory(target, 0x200a8000, 4, 128, page_buffer + (quarter * 512)); + target_write_memory(target, 0x200a8000, 1, 6, oob_buffer + (quarter * 6)); + + /* write MLC_ECC_AUTO_ENC_REG to start auto encode */ + target_write_u32(target, 0x200b8010, 0x0); + + if (!lpc3180_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 */ + target_write_u32(target, 0x200b8000, NAND_CMD_PAGEPROG); + + 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; + } + + free(page_buffer); + free(oob_buffer); + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + return nand_write_page_raw(nand, page, data, data_size, oob, oob_size); + } + + return ERROR_OK; +} + +static int lpc3180_read_page(struct nand_device *nand, uint32_t page, uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER) + { + LOG_ERROR("BUG: no LPC3180 NAND flash controller selected"); + return ERROR_NAND_OPERATION_FAILED; + } + else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + uint8_t *page_buffer; + uint8_t *oob_buffer; + uint32_t page_bytes_done = 0; + uint32_t oob_bytes_done = 0; + uint32_t mlc_isr; + +#if 0 + if (oob && (oob_size > 6)) + { + LOG_ERROR("LPC3180 MLC controller can't read more than 6 bytes of OOB data"); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } +#endif + + if (data_size > (uint32_t)nand->page_size) + { + LOG_ERROR("data size exceeds page size"); + return ERROR_NAND_OPERATION_NOT_SUPPORTED; + } + + if (nand->page_size == 2048) + { + page_buffer = malloc(2048); + oob_buffer = malloc(64); + } + else + { + page_buffer = malloc(512); + oob_buffer = malloc(16); + } + + 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 + */ + target_write_u32(target, 0x200b8000, NAND_CMD_READOOB); + } + else + { + /* MLC_CMD = Read0 */ + target_write_u32(target, 0x200b8000, NAND_CMD_READ0); + } + + if (nand->page_size == 512) + { + /* small page device */ + /* MLC_ADDR = 0x0 (one column cycle) */ + target_write_u32(target, 0x200b8004, 0x0); + + /* MLC_ADDR = row */ + target_write_u32(target, 0x200b8004, page & 0xff); + target_write_u32(target, 0x200b8004, (page >> 8) & 0xff); + + if (nand->address_cycles == 4) + target_write_u32(target, 0x200b8004, (page >> 16) & 0xff); + } + else + { + /* large page device */ + /* MLC_ADDR = 0x0 (two column cycles) */ + target_write_u32(target, 0x200b8004, 0x0); + target_write_u32(target, 0x200b8004, 0x0); + + /* MLC_ADDR = row */ + target_write_u32(target, 0x200b8004, page & 0xff); + target_write_u32(target, 0x200b8004, (page >> 8) & 0xff); + + /* MLC_CMD = Read Start */ + target_write_u32(target, 0x200b8000, NAND_CMD_READSTART); + } + + while (page_bytes_done < (uint32_t)nand->page_size) + { + /* MLC_ECC_AUTO_DEC_REG = dummy */ + target_write_u32(target, 0x200b8014, 0xaa55aa55); + + if (!lpc3180_controller_ready(nand, 1000)) + { + LOG_ERROR("timeout while waiting for completion of auto decode cycle"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_read_u32(target, 0x200b8048, &mlc_isr); + + 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) + { + target_read_memory(target, 0x200a8000, 4, 128, page_buffer + page_bytes_done); + } + + if (oob) + { + target_read_memory(target, 0x200a8000, 4, 4, oob_buffer + oob_bytes_done); + } + + page_bytes_done += 512; + oob_bytes_done += 16; + } + + if (data) + memcpy(data, page_buffer, data_size); + + if (oob) + memcpy(oob, oob_buffer, oob_size); + + free(page_buffer); + free(oob_buffer); + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + return nand_read_page_raw(nand, page, data, data_size, oob, oob_size); + } + + return ERROR_OK; +} + +static int lpc3180_controller_ready(struct nand_device *nand, int timeout) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + uint8_t status = 0x0; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + do + { + if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + /* Read MLC_ISR, wait for controller to become ready */ + target_read_u8(target, 0x200b8048, &status); + + if (status & 2) + return 1; + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + /* we pretend that the SLC controller is always ready */ + return 1; + } + + alive_sleep(1); + } while (timeout-- > 0); + + return 0; +} + +static int lpc3180_nand_ready(struct nand_device *nand, int timeout) +{ + struct lpc3180_nand_controller *lpc3180_info = nand->controller_priv; + struct target *target = lpc3180_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("target must be halted to use LPC3180 NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + do + { + if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER) + { + uint8_t status = 0x0; + + /* Read MLC_ISR, wait for NAND flash device to become ready */ + target_read_u8(target, 0x200b8048, &status); + + if (status & 1) + return 1; + } + else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER) + { + uint32_t status = 0x0; + + /* Read SLC_STAT and check READY bit */ + target_read_u32(target, 0x20020018, &status); + + if (status & 1) + return 1; + } + + alive_sleep(1); + } while (timeout-- > 0); + + return 0; +} + +COMMAND_HANDLER(handle_lpc3180_select_command) +{ + struct lpc3180_nand_controller *lpc3180_info = NULL; + char *selected[] = + { + "no", "mlc", "slc" + }; + + if ((CMD_ARGC < 1) || (CMD_ARGC > 2)) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + unsigned num; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], 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; + } + + lpc3180_info = nand->controller_priv; + + if (CMD_ARGC == 2) + { + if (strcmp(CMD_ARGV[1], "mlc") == 0) + { + lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER; + } + else if (strcmp(CMD_ARGV[1], "slc") == 0) + { + lpc3180_info->selected_controller = LPC3180_SLC_CONTROLLER; + } + else + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + command_print(CMD_CTX, "%s controller selected", selected[lpc3180_info->selected_controller]); + + return ERROR_OK; +} + +static const struct command_registration lpc3180_exec_command_handlers[] = { + { + .name = "select", + .handler = &handle_lpc3180_select_command, + .mode = COMMAND_EXEC, + .help = "select <'mlc'|'slc'> controller (default is mlc)", + .usage = "<device_id> (mlc|slc)", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration lpc3180_command_handler[] = { + { + .name = "lpc3180", + .mode = COMMAND_ANY, + .help = "LPC3180 NAND flash controller commands", + .chain = lpc3180_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct nand_flash_controller lpc3180_nand_controller = { + .name = "lpc3180", + .commands = lpc3180_command_handler, + .nand_device_command = lpc3180_nand_device_command, + .init = lpc3180_init, + .reset = lpc3180_reset, + .command = lpc3180_command, + .address = lpc3180_address, + .write_data = lpc3180_write_data, + .read_data = lpc3180_read_data, + .write_page = lpc3180_write_page, + .read_page = lpc3180_read_page, + .controller_ready = lpc3180_controller_ready, + .nand_ready = lpc3180_nand_ready, + }; diff --git a/src/flash/nand/lpc3180.h b/src/flash/nand/lpc3180.h new file mode 100644 index 00000000..0891cedf --- /dev/null +++ b/src/flash/nand/lpc3180.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * 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 LPC3180_NAND_CONTROLLER_H +#define LPC3180_NAND_CONTROLLER_H + +enum lpc3180_selected_controller +{ + LPC3180_NO_CONTROLLER, + LPC3180_MLC_CONTROLLER, + LPC3180_SLC_CONTROLLER, +}; + +struct lpc3180_nand_controller +{ + struct target *target; + int osc_freq; + enum lpc3180_selected_controller selected_controller; + int sw_write_protection; + uint32_t sw_wp_lower_bound; + uint32_t sw_wp_upper_bound; +}; + +#endif /*LPC3180_NAND_CONTROLLER_H */ diff --git a/src/flash/nand/mx3.c b/src/flash/nand/mx3.c new file mode 100644 index 00000000..21577a6e --- /dev/null +++ b/src/flash/nand/mx3.c @@ -0,0 +1,879 @@ + +/*************************************************************************** + * Copyright (C) 2009 by Alexei Babich * + * Rezonans plc., Chelyabinsk, Russia * + * impatt@mail.ru * + * * + * 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. * + ***************************************************************************/ + +/* + * Freescale iMX3* OpenOCD NAND Flash controller support. + * + * Many thanks to Ben Dooks for writing s3c24xx driver. + */ + +/* +driver tested with STMicro NAND512W3A @imx31 +tested "nand probe #", "nand erase # 0 #", "nand dump # file 0 #", "nand write # file 0" +get_next_halfword_from_sram_buffer() not tested +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mx3.h" + +static const char target_not_halted_err_msg[] = + "target must be halted to use mx3 NAND flash controller"; +static const char data_block_size_err_msg[] = + "minimal granularity is one half-word, %" PRId32 " is incorrect"; +static const char sram_buffer_bounds_err_msg[] = + "trying to access out of SRAM buffer bound (addr=0x%" PRIx32 ")"; +static const char get_status_register_err_msg[] = "can't get NAND status"; +static uint32_t in_sram_address; +unsigned char sign_of_sequental_byte_read; + +static int test_iomux_settings (struct target * target, uint32_t value, + uint32_t mask, const char *text); +static int initialize_nf_controller (struct nand_device *nand); +static int get_next_byte_from_sram_buffer (struct target * target, uint8_t * value); +static int get_next_halfword_from_sram_buffer (struct target * target, + uint16_t * value); +static int poll_for_complete_op (struct target * target, const char *text); +static int validate_target_state (struct nand_device *nand); +static int do_data_output (struct nand_device *nand); + +static int imx31_command (struct nand_device *nand, uint8_t command); +static int imx31_address (struct nand_device *nand, uint8_t address); +static int imx31_controller_ready (struct nand_device *nand, int tout); + +NAND_DEVICE_COMMAND_HANDLER(imx31_nand_device_command) +{ + struct mx3_nf_controller *mx3_nf_info; + mx3_nf_info = malloc (sizeof (struct mx3_nf_controller)); + if (mx3_nf_info == NULL) + { + LOG_ERROR ("no memory for nand controller"); + return ERROR_FAIL; + } + + nand->controller_priv = mx3_nf_info; + + mx3_nf_info->target = get_target (CMD_ARGV[1]); + if (mx3_nf_info->target == NULL) + { + LOG_ERROR ("target '%s' not defined", CMD_ARGV[1]); + return ERROR_FAIL; + } + if (CMD_ARGC < 3) + { + LOG_ERROR ("use \"nand device imx31 target noecc|hwecc\""); + return ERROR_FAIL; + } + /* + * check hwecc requirements + */ + { + int hwecc_needed; + hwecc_needed = strcmp (CMD_ARGV[2], "hwecc"); + if (hwecc_needed == 0) + { + mx3_nf_info->flags.hw_ecc_enabled = 1; + } + else + { + mx3_nf_info->flags.hw_ecc_enabled = 0; + } + } + + mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE; + mx3_nf_info->fin = MX3_NF_FIN_NONE; + mx3_nf_info->flags.target_little_endian = + (mx3_nf_info->target->endianness == TARGET_LITTLE_ENDIAN); + /* + * testing host endianess + */ + { + int x = 1; + if (*(char *) &x == 1) + { + mx3_nf_info->flags.host_little_endian = 1; + } + else + { + mx3_nf_info->flags.host_little_endian = 0; + } + } + return ERROR_OK; +} + +static int imx31_init (struct nand_device *nand) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + + { + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state(nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + } + + { + uint16_t buffsize_register_content; + target_read_u16 (target, MX3_NF_BUFSIZ, &buffsize_register_content); + mx3_nf_info->flags.one_kb_sram = !(buffsize_register_content & 0x000f); + } + + { + uint32_t pcsr_register_content; + target_read_u32 (target, MX3_PCSR, &pcsr_register_content); + if (!nand->bus_width) + { + nand->bus_width = + (pcsr_register_content & 0x80000000) ? 16 : 8; + } + else + { + pcsr_register_content |= + ((nand->bus_width == 16) ? 0x80000000 : 0x00000000); + target_write_u32 (target, MX3_PCSR, pcsr_register_content); + } + + if (!nand->page_size) + { + nand->page_size = + (pcsr_register_content & 0x40000000) ? 2048 : 512; + } + else + { + pcsr_register_content |= + ((nand->page_size == 2048) ? 0x40000000 : 0x00000000); + target_write_u32 (target, MX3_PCSR, pcsr_register_content); + } + if (mx3_nf_info->flags.one_kb_sram && (nand->page_size == 2048)) + { + LOG_ERROR + ("NAND controller have only 1 kb SRAM, so pagesize 2048 is incompatible with it"); + } + } + + { + uint32_t cgr_register_content; + target_read_u32 (target, MX3_CCM_CGR2, &cgr_register_content); + if (!(cgr_register_content & 0x00000300)) + { + LOG_ERROR ("clock gating to EMI disabled"); + return ERROR_FAIL; + } + } + + { + uint32_t gpr_register_content; + target_read_u32 (target, MX3_GPR, &gpr_register_content); + if (gpr_register_content & 0x00000060) + { + LOG_ERROR ("pins mode overrided by GPR"); + return ERROR_FAIL; + } + } + + { + /* + * testing IOMUX settings; must be in "functional-mode output and + * functional-mode input" mode + */ + int test_iomux; + test_iomux = ERROR_OK; + test_iomux |= + test_iomux_settings (target, 0x43fac0c0, 0x7f7f7f00, "d0,d1,d2"); + test_iomux |= + test_iomux_settings (target, 0x43fac0c4, 0x7f7f7f7f, "d3,d4,d5,d6"); + test_iomux |= + test_iomux_settings (target, 0x43fac0c8, 0x0000007f, "d7"); + if (nand->bus_width == 16) + { + test_iomux |= + test_iomux_settings (target, 0x43fac0c8, 0x7f7f7f00, + "d8,d9,d10"); + test_iomux |= + test_iomux_settings (target, 0x43fac0cc, 0x7f7f7f7f, + "d11,d12,d13,d14"); + test_iomux |= + test_iomux_settings (target, 0x43fac0d0, 0x0000007f, "d15"); + } + test_iomux |= + test_iomux_settings (target, 0x43fac0d0, 0x7f7f7f00, + "nfwp,nfce,nfrb"); + test_iomux |= + test_iomux_settings (target, 0x43fac0d4, 0x7f7f7f7f, + "nfwe,nfre,nfale,nfcle"); + if (test_iomux != ERROR_OK) + { + return ERROR_FAIL; + } + } + + initialize_nf_controller (nand); + + { + int retval; + uint16_t nand_status_content; + retval = ERROR_OK; + retval |= imx31_command (nand, NAND_CMD_STATUS); + retval |= imx31_address (nand, 0x00); + retval |= do_data_output (nand); + if (retval != ERROR_OK) + { + LOG_ERROR (get_status_register_err_msg); + return ERROR_FAIL; + } + target_read_u16 (target, MX3_NF_MAIN_BUFFER0, &nand_status_content); + if (!(nand_status_content & 0x0080)) + { + /* + * is host-big-endian correctly ?? + */ + LOG_INFO ("NAND read-only"); + mx3_nf_info->flags.nand_readonly = 1; + } + else + { + mx3_nf_info->flags.nand_readonly = 0; + } + } + return ERROR_OK; +} + +static int imx31_read_data (struct nand_device *nand, void *data) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + { + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state (nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + } + + { + /* + * get data from nand chip + */ + int try_data_output_from_nand_chip; + try_data_output_from_nand_chip = do_data_output (nand); + if (try_data_output_from_nand_chip != ERROR_OK) + { + return try_data_output_from_nand_chip; + } + } + + if (nand->bus_width == 16) + { + get_next_halfword_from_sram_buffer (target, data); + } + else + { + get_next_byte_from_sram_buffer (target, data); + } + + return ERROR_OK; +} + +static int imx31_write_data (struct nand_device *nand, uint16_t data) +{ + LOG_ERROR ("write_data() not implemented"); + return ERROR_NAND_OPERATION_FAILED; +} + +static int imx31_nand_ready (struct nand_device *nand, int timeout) +{ + return imx31_controller_ready (nand, timeout); +} + +static int imx31_reset (struct nand_device *nand) +{ + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state (nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + initialize_nf_controller (nand); + return ERROR_OK; +} + +static int imx31_command (struct nand_device *nand, uint8_t command) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + { + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state (nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + } + + switch (command) + { + case NAND_CMD_READOOB: + command = NAND_CMD_READ0; + in_sram_address = MX3_NF_SPARE_BUFFER0; /* set read point for + * data_read() and + * read_block_data() to + * spare area in SRAM + * buffer */ + break; + case NAND_CMD_READ1: + command = NAND_CMD_READ0; + /* + * offset == one half of page size + */ + in_sram_address = + MX3_NF_MAIN_BUFFER0 + (nand->page_size >> 1); + default: + in_sram_address = MX3_NF_MAIN_BUFFER0; + } + + target_write_u16 (target, MX3_NF_FCMD, command); + /* + * start command input operation (set MX3_NF_BIT_OP_DONE==0) + */ + target_write_u16 (target, MX3_NF_CFG2, MX3_NF_BIT_OP_FCI); + { + int poll_result; + poll_result = poll_for_complete_op (target, "command"); + if (poll_result != ERROR_OK) + { + return poll_result; + } + } + /* + * reset cursor to begin of the buffer + */ + sign_of_sequental_byte_read = 0; + switch (command) + { + case NAND_CMD_READID: + mx3_nf_info->optype = MX3_NF_DATAOUT_NANDID; + mx3_nf_info->fin = MX3_NF_FIN_DATAOUT; + break; + case NAND_CMD_STATUS: + mx3_nf_info->optype = MX3_NF_DATAOUT_NANDSTATUS; + mx3_nf_info->fin = MX3_NF_FIN_DATAOUT; + break; + case NAND_CMD_READ0: + mx3_nf_info->fin = MX3_NF_FIN_DATAOUT; + mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE; + break; + default: + mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE; + } + return ERROR_OK; +} + +static int imx31_address (struct nand_device *nand, uint8_t address) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + { + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state (nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + } + + target_write_u16 (target, MX3_NF_FADDR, address); + /* + * start address input operation (set MX3_NF_BIT_OP_DONE==0) + */ + target_write_u16 (target, MX3_NF_CFG2, MX3_NF_BIT_OP_FAI); + { + int poll_result; + poll_result = poll_for_complete_op (target, "address"); + if (poll_result != ERROR_OK) + { + return poll_result; + } + } + return ERROR_OK; +} + +static int imx31_controller_ready (struct nand_device *nand, int tout) +{ + uint16_t poll_complete_status; + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + + { + /* + * validate target state + */ + int validate_target_result; + validate_target_result = validate_target_state (nand); + if (validate_target_result != ERROR_OK) + { + return validate_target_result; + } + } + + do + { + target_read_u16 (target, MX3_NF_CFG2, &poll_complete_status); + if (poll_complete_status & MX3_NF_BIT_OP_DONE) + { + return tout; + } + alive_sleep (1); + } + while (tout-- > 0); + return tout; +} + +static int imx31_write_page (struct nand_device *nand, uint32_t page, + uint8_t * data, uint32_t data_size, uint8_t * oob, + uint32_t oob_size) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + + if (data_size % 2) + { + LOG_ERROR (data_block_size_err_msg, data_size); + return ERROR_NAND_OPERATION_FAILED; + } + if (oob_size % 2) + { + LOG_ERROR (data_block_size_err_msg, oob_size); + return ERROR_NAND_OPERATION_FAILED; + } + if (!data) + { + LOG_ERROR ("nothing to program"); + return ERROR_NAND_OPERATION_FAILED; + } + { + /* + * validate target state + */ + int retval; + retval = validate_target_state (nand); + if (retval != ERROR_OK) + { + return retval; + } + } + { + int retval = ERROR_OK; + retval |= imx31_command(nand, NAND_CMD_SEQIN); + retval |= imx31_address(nand, 0x00); + retval |= imx31_address(nand, page & 0xff); + retval |= imx31_address(nand, (page >> 8) & 0xff); + if (nand->address_cycles >= 4) + { + retval |= imx31_address (nand, (page >> 16) & 0xff); + if (nand->address_cycles >= 5) + { + retval |= imx31_address (nand, (page >> 24) & 0xff); + } + } + target_write_buffer (target, MX3_NF_MAIN_BUFFER0, data_size, data); + if (oob) + { + if (mx3_nf_info->flags.hw_ecc_enabled) + { + /* + * part of spare block will be overrided by hardware + * ECC generator + */ + LOG_DEBUG + ("part of spare block will be overrided by hardware ECC generator"); + } + target_write_buffer (target, MX3_NF_SPARE_BUFFER0, oob_size, + oob); + } + /* + * start data input operation (set MX3_NF_BIT_OP_DONE==0) + */ + target_write_u16 (target, MX3_NF_CFG2, MX3_NF_BIT_OP_FDI); + { + int poll_result; + poll_result = poll_for_complete_op (target, "data input"); + if (poll_result != ERROR_OK) + { + return poll_result; + } + } + retval |= imx31_command (nand, NAND_CMD_PAGEPROG); + if (retval != ERROR_OK) + { + return retval; + } + + /* + * check status register + */ + { + uint16_t nand_status_content; + retval = ERROR_OK; + retval |= imx31_command(nand, NAND_CMD_STATUS); + retval |= imx31_address(nand, 0x00); + retval |= do_data_output(nand); + if (retval != ERROR_OK) + { + LOG_ERROR (get_status_register_err_msg); + return retval; + } + target_read_u16 (target, MX3_NF_MAIN_BUFFER0, &nand_status_content); + if (nand_status_content & 0x0001) + { + /* + * is host-big-endian correctly ?? + */ + return ERROR_NAND_OPERATION_FAILED; + } + } + } + return ERROR_OK; +} + +static int imx31_read_page (struct nand_device *nand, uint32_t page, + uint8_t * data, uint32_t data_size, uint8_t * oob, + uint32_t oob_size) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + + if (data_size % 2) + { + LOG_ERROR (data_block_size_err_msg, data_size); + return ERROR_NAND_OPERATION_FAILED; + } + if (oob_size % 2) + { + LOG_ERROR (data_block_size_err_msg, oob_size); + return ERROR_NAND_OPERATION_FAILED; + } + + { + /* + * validate target state + */ + int retval; + retval = validate_target_state(nand); + if (retval != ERROR_OK) + { + return retval; + } + } + { + int retval = ERROR_OK; + retval |= imx31_command(nand, NAND_CMD_READ0); + retval |= imx31_address(nand, 0x00); + retval |= imx31_address(nand, page & 0xff); + retval |= imx31_address(nand, (page >> 8) & 0xff); + if (nand->address_cycles >= 4) + { + retval |= imx31_address(nand, (page >> 16) & 0xff); + if (nand->address_cycles >= 5) + { + retval |= imx31_address(nand, (page >> 24) & 0xff); + retval |= imx31_command(nand, NAND_CMD_READSTART); + } + } + retval |= do_data_output (nand); + if (retval != ERROR_OK) + { + return retval; + } + + if (data) + { + target_read_buffer (target, MX3_NF_MAIN_BUFFER0, data_size, + data); + } + if (oob) + { + target_read_buffer (target, MX3_NF_SPARE_BUFFER0, oob_size, + oob); + } + } + return ERROR_OK; +} + +static int test_iomux_settings (struct target * target, uint32_t address, + uint32_t mask, const char *text) +{ + uint32_t register_content; + target_read_u32 (target, address, ®ister_content); + if ((register_content & mask) != (0x12121212 & mask)) + { + LOG_ERROR ("IOMUX for {%s} is bad", text); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int initialize_nf_controller (struct nand_device *nand) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + /* + * resets NAND flash controller in zero time ? I dont know. + */ + target_write_u16 (target, MX3_NF_CFG1, MX3_NF_BIT_RESET_EN); + { + uint16_t work_mode; + work_mode = MX3_NF_BIT_INT_DIS; /* disable interrupt */ + if (target->endianness == TARGET_BIG_ENDIAN) + { + work_mode |= MX3_NF_BIT_BE_EN; + } + if (mx3_nf_info->flags.hw_ecc_enabled) + { + work_mode |= MX3_NF_BIT_ECC_EN; + } + target_write_u16 (target, MX3_NF_CFG1, work_mode); + } + /* + * unlock SRAM buffer for write; 2 mean "Unlock", other values means "Lock" + */ + target_write_u16 (target, MX3_NF_BUFCFG, 2); + { + uint16_t temp; + target_read_u16 (target, MX3_NF_FWP, &temp); + if ((temp & 0x0007) == 1) + { + LOG_ERROR ("NAND flash is tight-locked, reset needed"); + return ERROR_FAIL; + } + + } + /* + * unlock NAND flash for write + */ + target_write_u16 (target, MX3_NF_FWP, 4); + target_write_u16 (target, MX3_NF_LOCKSTART, 0x0000); + target_write_u16 (target, MX3_NF_LOCKEND, 0xFFFF); + /* + * 0x0000 means that first SRAM buffer @0xB800_0000 will be used + */ + target_write_u16 (target, MX3_NF_BUFADDR, 0x0000); + /* + * address of SRAM buffer + */ + in_sram_address = MX3_NF_MAIN_BUFFER0; + sign_of_sequental_byte_read = 0; + return ERROR_OK; +} + +static int get_next_byte_from_sram_buffer (struct target * target, uint8_t * value) +{ + static uint8_t even_byte = 0; + /* + * host-big_endian ?? + */ + if (sign_of_sequental_byte_read == 0) + { + even_byte = 0; + } + if (in_sram_address > MX3_NF_LAST_BUFFER_ADDR) + { + LOG_ERROR (sram_buffer_bounds_err_msg, in_sram_address); + *value = 0; + sign_of_sequental_byte_read = 0; + even_byte = 0; + return ERROR_NAND_OPERATION_FAILED; + } + else + { + uint16_t temp; + target_read_u16 (target, in_sram_address, &temp); + if (even_byte) + { + *value = temp >> 8; + even_byte = 0; + in_sram_address += 2; + } + else + { + *value = temp & 0xff; + even_byte = 1; + } + } + sign_of_sequental_byte_read = 1; + return ERROR_OK; +} + +static int get_next_halfword_from_sram_buffer (struct target * target, + uint16_t * value) +{ + if (in_sram_address > MX3_NF_LAST_BUFFER_ADDR) + { + LOG_ERROR (sram_buffer_bounds_err_msg, in_sram_address); + *value = 0; + return ERROR_NAND_OPERATION_FAILED; + } + else + { + target_read_u16 (target, in_sram_address, value); + in_sram_address += 2; + } + return ERROR_OK; +} + +static int poll_for_complete_op (struct target * target, const char *text) +{ + uint16_t poll_complete_status; + for (int poll_cycle_count = 0; poll_cycle_count < 100; poll_cycle_count++) + { + usleep (25); + target_read_u16 (target, MX3_NF_CFG2, &poll_complete_status); + if (poll_complete_status & MX3_NF_BIT_OP_DONE) + { + break; + } + } + if (!(poll_complete_status & MX3_NF_BIT_OP_DONE)) + { + LOG_ERROR ("%s sending timeout", text); + return ERROR_NAND_OPERATION_FAILED; + } + return ERROR_OK; +} + +static int validate_target_state (struct nand_device *nand) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR (target_not_halted_err_msg); + return ERROR_NAND_OPERATION_FAILED; + } + + if (mx3_nf_info->flags.target_little_endian != + (target->endianness == TARGET_LITTLE_ENDIAN)) + { + /* + * endianness changed after NAND controller probed + */ + return ERROR_NAND_OPERATION_FAILED; + } + return ERROR_OK; +} + +static int do_data_output (struct nand_device *nand) +{ + struct mx3_nf_controller *mx3_nf_info = nand->controller_priv; + struct target *target = mx3_nf_info->target; + switch (mx3_nf_info->fin) + { + case MX3_NF_FIN_DATAOUT: + /* + * start data output operation (set MX3_NF_BIT_OP_DONE==0) + */ + target_write_u16 (target, MX3_NF_CFG2, + MX3_NF_BIT_DATAOUT_TYPE (mx3_nf_info-> + optype)); + { + int poll_result; + poll_result = poll_for_complete_op (target, "data output"); + if (poll_result != ERROR_OK) + { + return poll_result; + } + } + mx3_nf_info->fin = MX3_NF_FIN_NONE; + /* + * ECC stuff + */ + if ((mx3_nf_info->optype == MX3_NF_DATAOUT_PAGE) + && mx3_nf_info->flags.hw_ecc_enabled) + { + uint16_t ecc_status; + target_read_u16 (target, MX3_NF_ECCSTATUS, &ecc_status); + switch (ecc_status & 0x000c) + { + case 1 << 2: + LOG_DEBUG + ("main area readed with 1 (correctable) error"); + break; + case 2 << 2: + LOG_DEBUG + ("main area readed with more than 1 (incorrectable) error"); + return ERROR_NAND_OPERATION_FAILED; + break; + } + switch (ecc_status & 0x0003) + { + case 1: + LOG_DEBUG + ("spare area readed with 1 (correctable) error"); + break; + case 2: + LOG_DEBUG + ("main area readed with more than 1 (incorrectable) error"); + return ERROR_NAND_OPERATION_FAILED; + break; + } + } + break; + case MX3_NF_FIN_NONE: + break; + } + return ERROR_OK; +} + +struct nand_flash_controller imx31_nand_flash_controller = { + .name = "imx31", + .nand_device_command = &imx31_nand_device_command, + .init = &imx31_init, + .reset = &imx31_reset, + .command = &imx31_command, + .address = &imx31_address, + .write_data = &imx31_write_data, + .read_data = &imx31_read_data, + .write_page = &imx31_write_page, + .read_page = &imx31_read_page, + .controller_ready = &imx31_controller_ready, + .nand_ready = &imx31_nand_ready, + }; diff --git a/src/flash/nand/mx3.h b/src/flash/nand/mx3.h new file mode 100644 index 00000000..ddec92cf --- /dev/null +++ b/src/flash/nand/mx3.h @@ -0,0 +1,117 @@ + +/*************************************************************************** + * Copyright (C) 2009 by Alexei Babich * + * Rezonans plc., Chelyabinsk, Russia * + * impatt@mail.ru * + * * + * 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. * + ***************************************************************************/ + +/* + * Freescale iMX3* OpenOCD NAND Flash controller support. + * + * Many thanks to Ben Dooks for writing s3c24xx driver. + */ +#include <nand.h> + +#define MX3_NF_BASE_ADDR 0xb8000000 +#define MX3_NF_BUFSIZ (MX3_NF_BASE_ADDR + 0xe00) +#define MX3_NF_BUFADDR (MX3_NF_BASE_ADDR + 0xe04) +#define MX3_NF_FADDR (MX3_NF_BASE_ADDR + 0xe06) +#define MX3_NF_FCMD (MX3_NF_BASE_ADDR + 0xe08) +#define MX3_NF_BUFCFG (MX3_NF_BASE_ADDR + 0xe0a) +#define MX3_NF_ECCSTATUS (MX3_NF_BASE_ADDR + 0xe0c) +#define MX3_NF_ECCMAINPOS (MX3_NF_BASE_ADDR + 0xe0e) +#define MX3_NF_ECCSPAREPOS (MX3_NF_BASE_ADDR + 0xe10) +#define MX3_NF_FWP (MX3_NF_BASE_ADDR + 0xe12) +#define MX3_NF_LOCKSTART (MX3_NF_BASE_ADDR + 0xe14) +#define MX3_NF_LOCKEND (MX3_NF_BASE_ADDR + 0xe16) +#define MX3_NF_FWPSTATUS (MX3_NF_BASE_ADDR + 0xe18) + /* + * all bits not marked as self-clearing bit + */ +#define MX3_NF_CFG1 (MX3_NF_BASE_ADDR + 0xe1a) +#define MX3_NF_CFG2 (MX3_NF_BASE_ADDR + 0xe1c) + +#define MX3_NF_MAIN_BUFFER0 (MX3_NF_BASE_ADDR + 0x0000) +#define MX3_NF_MAIN_BUFFER1 (MX3_NF_BASE_ADDR + 0x0200) +#define MX3_NF_MAIN_BUFFER2 (MX3_NF_BASE_ADDR + 0x0400) +#define MX3_NF_MAIN_BUFFER3 (MX3_NF_BASE_ADDR + 0x0600) +#define MX3_NF_SPARE_BUFFER0 (MX3_NF_BASE_ADDR + 0x0800) +#define MX3_NF_SPARE_BUFFER1 (MX3_NF_BASE_ADDR + 0x0810) +#define MX3_NF_SPARE_BUFFER2 (MX3_NF_BASE_ADDR + 0x0820) +#define MX3_NF_SPARE_BUFFER3 (MX3_NF_BASE_ADDR + 0x0830) +#define MX3_NF_MAIN_BUFFER_LEN 512 +#define MX3_NF_SPARE_BUFFER_LEN 16 +#define MX3_NF_LAST_BUFFER_ADDR ((MX3_NF_SPARE_BUFFER3) + MX3_NF_SPARE_BUFFER_LEN - 2) + +/* bits in MX3_NF_CFG1 register */ +#define MX3_NF_BIT_SPARE_ONLY_EN (1<<2) +#define MX3_NF_BIT_ECC_EN (1<<3) +#define MX3_NF_BIT_INT_DIS (1<<4) +#define MX3_NF_BIT_BE_EN (1<<5) +#define MX3_NF_BIT_RESET_EN (1<<6) +#define MX3_NF_BIT_FORCE_CE (1<<7) + +/* bits in MX3_NF_CFG2 register */ + +/*Flash Command Input*/ +#define MX3_NF_BIT_OP_FCI (1<<0) + /* + * Flash Address Input + */ +#define MX3_NF_BIT_OP_FAI (1<<1) + /* + * Flash Data Input + */ +#define MX3_NF_BIT_OP_FDI (1<<2) + +/* see "enum mx_dataout_type" below */ +#define MX3_NF_BIT_DATAOUT_TYPE(x) ((x)<<3) +#define MX3_NF_BIT_OP_DONE (1<<15) + +#define MX3_CCM_CGR2 0x53f80028 +#define MX3_GPR 0x43fac008 +#define MX3_PCSR 0x53f8000c + +enum mx_dataout_type +{ + MX3_NF_DATAOUT_PAGE = 1, + MX3_NF_DATAOUT_NANDID = 2, + MX3_NF_DATAOUT_NANDSTATUS = 4, +}; +enum mx_nf_finalize_action +{ + MX3_NF_FIN_NONE, + MX3_NF_FIN_DATAOUT, +}; + +struct mx3_nf_flags +{ + unsigned host_little_endian:1; + unsigned target_little_endian:1; + unsigned nand_readonly:1; + unsigned one_kb_sram:1; + unsigned hw_ecc_enabled:1; +}; + +struct mx3_nf_controller +{ + struct target *target; + enum mx_dataout_type optype; + enum mx_nf_finalize_action fin; + struct mx3_nf_flags flags; +}; diff --git a/src/flash/nand/nonce.c b/src/flash/nand/nonce.c new file mode 100644 index 00000000..dae62a72 --- /dev/null +++ b/src/flash/nand/nonce.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> * + * * + * 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 "nand.h" + + +static int nonce_nand_command(struct nand_device *nand, uint8_t command) +{ + return ERROR_OK; +} +static int nonce_nand_address(struct nand_device *nand, uint8_t address) +{ + return ERROR_OK; +} +static int nonce_nand_read(struct nand_device *nand, void *data) +{ + return ERROR_OK; +} +static int nonce_nand_write(struct nand_device *nand, uint16_t data) +{ + return ERROR_OK; +} +static int nonce_nand_fast_block_write(struct nand_device *nand, + uint8_t *data, int size) +{ + return ERROR_OK; +} + +static int nonce_nand_reset(struct nand_device *nand) +{ + return nonce_nand_command(nand, NAND_CMD_RESET); +} + +static int nonce_nand_controller_ready(struct nand_device *nand, int timeout) +{ + return true; +} + +NAND_DEVICE_COMMAND_HANDLER(nonce_nand_device_command) +{ + return ERROR_OK; +} + +static int nonce_nand_init(struct nand_device *nand) +{ + return ERROR_OK; +} + +struct nand_flash_controller nonce_nand_controller = +{ + .name = "nonce", + .nand_device_command = &nonce_nand_device_command, + .init = &nonce_nand_init, + .reset = &nonce_nand_reset, + .command = &nonce_nand_command, + .address = &nonce_nand_address, + .read_data = &nonce_nand_read, + .write_data = &nonce_nand_write, + .write_block_data = &nonce_nand_fast_block_write, + .controller_ready = &nonce_nand_controller_ready, +}; diff --git a/src/flash/nand/orion.c b/src/flash/nand/orion.c new file mode 100644 index 00000000..77a03f27 --- /dev/null +++ b/src/flash/nand/orion.c @@ -0,0 +1,180 @@ +/*************************************************************************** + * Copyright (C) 2009 by Marvell Semiconductors, Inc. * + * Written by Nicolas Pitre <nico at marvell.com> * + * * + * 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. * + ***************************************************************************/ + +/* + * NAND controller interface for Marvell Orion/Kirkwood SoCs. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "arm_nandio.h" +#include "armv4_5.h" + + +struct orion_nand_controller +{ + struct target *target; + + struct arm_nand_data io; + + uint32_t cmd; + uint32_t addr; + uint32_t data; +}; + +#define CHECK_HALTED \ + do { \ + if (target->state != TARGET_HALTED) { \ + LOG_ERROR("NAND flash access requires halted target"); \ + return ERROR_NAND_OPERATION_FAILED; \ + } \ + } while (0) + +static int orion_nand_command(struct nand_device *nand, uint8_t command) +{ + struct orion_nand_controller *hw = nand->controller_priv; + struct target *target = hw->target; + + CHECK_HALTED; + target_write_u8(target, hw->cmd, command); + return ERROR_OK; +} + +static int orion_nand_address(struct nand_device *nand, uint8_t address) +{ + struct orion_nand_controller *hw = nand->controller_priv; + struct target *target = hw->target; + + CHECK_HALTED; + target_write_u8(target, hw->addr, address); + return ERROR_OK; +} + +static int orion_nand_read(struct nand_device *nand, void *data) +{ + struct orion_nand_controller *hw = nand->controller_priv; + struct target *target = hw->target; + + CHECK_HALTED; + target_read_u8(target, hw->data, data); + return ERROR_OK; +} + +static int orion_nand_write(struct nand_device *nand, uint16_t data) +{ + struct orion_nand_controller *hw = nand->controller_priv; + struct target *target = hw->target; + + CHECK_HALTED; + target_write_u8(target, hw->data, data); + return ERROR_OK; +} + +static int orion_nand_slow_block_write(struct nand_device *nand, uint8_t *data, int size) +{ + while (size--) + orion_nand_write(nand, *data++); + return ERROR_OK; +} + +static int orion_nand_fast_block_write(struct nand_device *nand, uint8_t *data, int size) +{ + struct orion_nand_controller *hw = nand->controller_priv; + int retval; + + hw->io.chunk_size = nand->page_size; + + retval = arm_nandwrite(&hw->io, data, size); + if (retval == ERROR_NAND_NO_BUFFER) + retval = orion_nand_slow_block_write(nand, data, size); + + return retval; +} + +static int orion_nand_reset(struct nand_device *nand) +{ + return orion_nand_command(nand, NAND_CMD_RESET); +} + +static int orion_nand_controller_ready(struct nand_device *nand, int timeout) +{ + return 1; +} + +NAND_DEVICE_COMMAND_HANDLER(orion_nand_device_command) +{ + struct orion_nand_controller *hw; + uint32_t base; + uint8_t ale, cle; + + if (CMD_ARGC != 3) { + LOG_ERROR("arguments must be: <target_id> <NAND_address>\n"); + return ERROR_NAND_DEVICE_INVALID; + } + + hw = calloc(1, sizeof(*hw)); + if (!hw) { + LOG_ERROR("no memory for nand controller\n"); + return ERROR_NAND_DEVICE_INVALID; + } + + nand->controller_priv = hw; + hw->target = get_target(CMD_ARGV[1]); + if (!hw->target) { + LOG_ERROR("target '%s' not defined", CMD_ARGV[1]); + free(hw); + return ERROR_NAND_DEVICE_INVALID; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], base); + cle = 0; + ale = 1; + + hw->data = base; + hw->cmd = base + (1 << cle); + hw->addr = base + (1 << ale); + + hw->io.target = hw->target; + hw->io.data = hw->data; + + return ERROR_OK; +} + +static int orion_nand_init(struct nand_device *nand) +{ + return ERROR_OK; +} + +struct nand_flash_controller orion_nand_controller = +{ + .name = "orion", + .command = orion_nand_command, + .address = orion_nand_address, + .read_data = orion_nand_read, + .write_data = orion_nand_write, + .write_block_data = orion_nand_fast_block_write, + .reset = orion_nand_reset, + .controller_ready = orion_nand_controller_ready, + .nand_device_command = orion_nand_device_command, + .init = orion_nand_init, +}; + diff --git a/src/flash/nand/s3c2410.c b/src/flash/nand/s3c2410.c new file mode 100644 index 00000000..3c391bce --- /dev/null +++ b/src/flash/nand/s3c2410.c @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C2410 OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "s3c24xx.h" + +NAND_DEVICE_COMMAND_HANDLER(s3c2410_nand_device_command) +{ + struct s3c24xx_nand_controller *info; + CALL_S3C24XX_DEVICE_COMMAND(nand, &info); + + /* fill in the address fields for the core device */ + info->cmd = S3C2410_NFCMD; + info->addr = S3C2410_NFADDR; + info->data = S3C2410_NFDATA; + info->nfstat = S3C2410_NFSTAT; + + return ERROR_OK; +} + +static int s3c2410_init(struct nand_device *nand) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + target_write_u32(target, S3C2410_NFCONF, + S3C2410_NFCONF_EN | S3C2410_NFCONF_TACLS(3) | + S3C2410_NFCONF_TWRPH0(5) | S3C2410_NFCONF_TWRPH1(3)); + + return ERROR_OK; +} + +static int s3c2410_write_data(struct nand_device *nand, uint16_t data) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_write_u32(target, S3C2410_NFDATA, data); + return ERROR_OK; +} + +static int s3c2410_read_data(struct nand_device *nand, void *data) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_read_u8(target, S3C2410_NFDATA, data); + return ERROR_OK; +} + +static int s3c2410_nand_ready(struct nand_device *nand, int timeout) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + uint8_t status; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + do { + target_read_u8(target, S3C2410_NFSTAT, &status); + + if (status & S3C2410_NFSTAT_BUSY) + return 1; + + alive_sleep(1); + } while (timeout-- > 0); + + return 0; +} + +struct nand_flash_controller s3c2410_nand_controller = { + .name = "s3c2410", + .nand_device_command = &s3c2410_nand_device_command, + .init = &s3c2410_init, + .reset = &s3c24xx_reset, + .command = &s3c24xx_command, + .address = &s3c24xx_address, + .write_data = &s3c2410_write_data, + .read_data = &s3c2410_read_data, + .write_page = s3c24xx_write_page, + .read_page = s3c24xx_read_page, + .controller_ready = &s3c24xx_controller_ready, + .nand_ready = &s3c2410_nand_ready, + }; diff --git a/src/flash/nand/s3c2412.c b/src/flash/nand/s3c2412.c new file mode 100644 index 00000000..57843053 --- /dev/null +++ b/src/flash/nand/s3c2412.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C2412 OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "s3c24xx.h" + +NAND_DEVICE_COMMAND_HANDLER(s3c2412_nand_device_command) +{ + struct s3c24xx_nand_controller *info; + CALL_S3C24XX_DEVICE_COMMAND(nand, &info); + + /* fill in the address fields for the core device */ + info->cmd = S3C2440_NFCMD; + info->addr = S3C2440_NFADDR; + info->data = S3C2440_NFDATA; + info->nfstat = S3C2412_NFSTAT; + + return ERROR_OK; +} + +static int s3c2412_init(struct nand_device *nand) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + target_write_u32(target, S3C2410_NFCONF, + S3C2440_NFCONF_TACLS(3) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); + + target_write_u32(target, S3C2440_NFCONT, + S3C2412_NFCONT_INIT_MAIN_ECC | + S3C2440_NFCONT_ENABLE); + + return ERROR_OK; +} + +struct nand_flash_controller s3c2412_nand_controller = { + .name = "s3c2412", + .nand_device_command = &s3c2412_nand_device_command, + .init = &s3c2412_init, + .reset = &s3c24xx_reset, + .command = &s3c24xx_command, + .address = &s3c24xx_address, + .write_data = &s3c24xx_write_data, + .read_data = &s3c24xx_read_data, + .write_page = s3c24xx_write_page, + .read_page = s3c24xx_read_page, + .write_block_data = &s3c2440_write_block_data, + .read_block_data = &s3c2440_read_block_data, + .controller_ready = &s3c24xx_controller_ready, + .nand_ready = &s3c2440_nand_ready, + }; diff --git a/src/flash/nand/s3c2440.c b/src/flash/nand/s3c2440.c new file mode 100644 index 00000000..d1a421e3 --- /dev/null +++ b/src/flash/nand/s3c2440.c @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C2440 OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "s3c24xx.h" + + +NAND_DEVICE_COMMAND_HANDLER(s3c2440_nand_device_command) +{ + struct s3c24xx_nand_controller *info; + CALL_S3C24XX_DEVICE_COMMAND(nand, &info); + + /* fill in the address fields for the core device */ + info->cmd = S3C2440_NFCMD; + info->addr = S3C2440_NFADDR; + info->data = S3C2440_NFDATA; + info->nfstat = S3C2440_NFSTAT; + + return ERROR_OK; +} + +static int s3c2440_init(struct nand_device *nand) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + target_write_u32(target, S3C2410_NFCONF, + S3C2440_NFCONF_TACLS(3) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); + + target_write_u32(target, S3C2440_NFCONT, + S3C2440_NFCONT_INITECC | S3C2440_NFCONT_ENABLE); + + return ERROR_OK; +} + +int s3c2440_nand_ready(struct nand_device *nand, int timeout) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + uint8_t status; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + do { + target_read_u8(target, s3c24xx_info->nfstat, &status); + + if (status & S3C2440_NFSTAT_READY) + return 1; + + alive_sleep(1); + } while (timeout-- > 0); + + + return 0; +} + +/* use the fact we can read/write 4 bytes in one go via a single 32bit op */ + +int s3c2440_read_block_data(struct nand_device *nand, uint8_t *data, int data_size) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + uint32_t nfdata = s3c24xx_info->data; + uint32_t tmp; + + LOG_INFO("%s: reading data: %p, %p, %d\n", __func__, nand, data, data_size); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + 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; +} + +int s3c2440_write_block_data(struct nand_device *nand, uint8_t *data, int data_size) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + uint32_t nfdata = s3c24xx_info->data; + uint32_t tmp; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + 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; +} + +struct nand_flash_controller s3c2440_nand_controller = { + .name = "s3c2440", + .nand_device_command = &s3c2440_nand_device_command, + .init = &s3c2440_init, + .reset = &s3c24xx_reset, + .command = &s3c24xx_command, + .address = &s3c24xx_address, + .write_data = &s3c24xx_write_data, + .read_data = &s3c24xx_read_data, + .write_page = s3c24xx_write_page, + .read_page = s3c24xx_read_page, + .write_block_data = &s3c2440_write_block_data, + .read_block_data = &s3c2440_read_block_data, + .controller_ready = &s3c24xx_controller_ready, + .nand_ready = &s3c2440_nand_ready, + }; diff --git a/src/flash/nand/s3c2443.c b/src/flash/nand/s3c2443.c new file mode 100644 index 00000000..d3414771 --- /dev/null +++ b/src/flash/nand/s3c2443.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C2443 OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "s3c24xx.h" + + +NAND_DEVICE_COMMAND_HANDLER(s3c2443_nand_device_command) +{ + struct s3c24xx_nand_controller *info; + CALL_S3C24XX_DEVICE_COMMAND(nand, &info); + + /* fill in the address fields for the core device */ + info->cmd = S3C2440_NFCMD; + info->addr = S3C2440_NFADDR; + info->data = S3C2440_NFDATA; + info->nfstat = S3C2412_NFSTAT; + + return ERROR_OK; +} + +static int s3c2443_init(struct nand_device *nand) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + target_write_u32(target, S3C2410_NFCONF, + S3C2440_NFCONF_TACLS(3) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); + + target_write_u32(target, S3C2440_NFCONT, + S3C2412_NFCONT_INIT_MAIN_ECC | + S3C2440_NFCONT_ENABLE); + + return ERROR_OK; +} + +struct nand_flash_controller s3c2443_nand_controller = { + .name = "s3c2443", + .nand_device_command = &s3c2443_nand_device_command, + .init = &s3c2443_init, + .reset = &s3c24xx_reset, + .command = &s3c24xx_command, + .address = &s3c24xx_address, + .write_data = &s3c24xx_write_data, + .read_data = &s3c24xx_read_data, + .write_page = s3c24xx_write_page, + .read_page = s3c24xx_read_page, + .write_block_data = &s3c2440_write_block_data, + .read_block_data = &s3c2440_read_block_data, + .controller_ready = &s3c24xx_controller_ready, + .nand_ready = &s3c2440_nand_ready, + }; diff --git a/src/flash/nand/s3c24xx.c b/src/flash/nand/s3c24xx.c new file mode 100644 index 00000000..d305b221 --- /dev/null +++ b/src/flash/nand/s3c24xx.c @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C24XX Series OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "s3c24xx.h" + + +S3C24XX_DEVICE_COMMAND() +{ + *info = NULL; + + struct s3c24xx_nand_controller *s3c24xx_info; + s3c24xx_info = malloc(sizeof(struct s3c24xx_nand_controller)); + if (s3c24xx_info == NULL) { + LOG_ERROR("no memory for nand controller\n"); + return -ENOMEM; + } + + nand->controller_priv = s3c24xx_info; + + s3c24xx_info->target = get_target(CMD_ARGV[1]); + if (s3c24xx_info->target == NULL) { + LOG_ERROR("target '%s' not defined", CMD_ARGV[1]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + *info = s3c24xx_info; + + return ERROR_OK; +} + +int s3c24xx_reset(struct nand_device *nand) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_write_u32(target, s3c24xx_info->cmd, 0xff); + + return ERROR_OK; +} + +int s3c24xx_command(struct nand_device *nand, uint8_t command) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_write_u16(target, s3c24xx_info->cmd, command); + return ERROR_OK; +} + + +int s3c24xx_address(struct nand_device *nand, uint8_t address) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_write_u16(target, s3c24xx_info->addr, address); + return ERROR_OK; +} + +int s3c24xx_write_data(struct nand_device *nand, uint16_t data) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_write_u8(target, s3c24xx_info->data, data); + return ERROR_OK; +} + +int s3c24xx_read_data(struct nand_device *nand, void *data) +{ + struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv; + struct target *target = s3c24xx_info->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target must be halted to use S3C24XX NAND flash controller"); + return ERROR_NAND_OPERATION_FAILED; + } + + target_read_u8(target, s3c24xx_info->data, data); + return ERROR_OK; +} + +int s3c24xx_controller_ready(struct nand_device *nand, int timeout) +{ + return 1; +} diff --git a/src/flash/nand/s3c24xx.h b/src/flash/nand/s3c24xx.h new file mode 100644 index 00000000..38057b27 --- /dev/null +++ b/src/flash/nand/s3c24xx.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2007, 2008 by Ben Dooks * + * ben@fluff.org * + * * + * 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 S3C24xx_NAND_H +#define S3C24xx_NAND_H + +/* + * S3C24XX Series OpenOCD NAND Flash controller support. + * + * Many thanks to Simtec Electronics for sponsoring this work. + */ + +#include "nand.h" +#include "s3c24xx_regs.h" + +struct s3c24xx_nand_controller +{ + struct target *target; + + /* register addresses */ + uint32_t cmd; + uint32_t addr; + uint32_t data; + uint32_t nfstat; +}; + +/* Default to using the un-translated NAND register based address */ +#undef S3C2410_NFREG +#define S3C2410_NFREG(x) ((x) + 0x4e000000) + +#define S3C24XX_DEVICE_COMMAND() \ + COMMAND_HELPER(s3c24xx_nand_device_command, \ + struct nand_device *nand, \ + struct s3c24xx_nand_controller **info) + +S3C24XX_DEVICE_COMMAND(); + +#define CALL_S3C24XX_DEVICE_COMMAND(d, i) \ + do { \ + int retval = CALL_COMMAND_HANDLER(s3c24xx_nand_device_command, d, i); \ + if (ERROR_OK != retval) \ + return retval; \ + } while (0) + +int s3c24xx_reset(struct nand_device *nand); + +int s3c24xx_command(struct nand_device *nand, uint8_t command); +int s3c24xx_address(struct nand_device *nand, uint8_t address); + +int s3c24xx_write_data(struct nand_device *nand, uint16_t data); +int s3c24xx_read_data(struct nand_device *nand, void *data); + +int s3c24xx_controller_ready(struct nand_device *nand, int tout); + +#define s3c24xx_write_page NULL +#define s3c24xx_read_page NULL + +/* code shared between different controllers */ + +int s3c2440_nand_ready(struct nand_device *nand, int timeout); + +int s3c2440_read_block_data(struct nand_device *nand, + uint8_t *data, int data_size); +int s3c2440_write_block_data(struct nand_device *nand, + uint8_t *data, int data_size); + +#endif // S3C24xx_NAND_H diff --git a/src/flash/nand/s3c24xx_regs.h b/src/flash/nand/s3c24xx_regs.h new file mode 100644 index 00000000..c8cbe789 --- /dev/null +++ b/src/flash/nand/s3c24xx_regs.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Simtec Electronics * + * linux@simtec.co.uk * + * http://www.simtec.co.uk/products/SWLINUX/ * + * * + * 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; version 2 of the License. * + * * + * 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. * + ***************************************************************************/ + +/* + * S3C2410 NAND register definitions + */ + +#ifndef __ASM_ARM_REGS_NAND +#define __ASM_ARM_REGS_NAND "$Id: nand.h,v 1.3 2003/12/09 11:36:29 ben Exp $" + +#define S3C2410_NFREG(x) (x) + +#define S3C2410_NFCONF S3C2410_NFREG(0x00) +#define S3C2410_NFCMD S3C2410_NFREG(0x04) +#define S3C2410_NFADDR S3C2410_NFREG(0x08) +#define S3C2410_NFDATA S3C2410_NFREG(0x0C) +#define S3C2410_NFSTAT S3C2410_NFREG(0x10) +#define S3C2410_NFECC S3C2410_NFREG(0x14) + +#define S3C2440_NFCONT S3C2410_NFREG(0x04) +#define S3C2440_NFCMD S3C2410_NFREG(0x08) +#define S3C2440_NFADDR S3C2410_NFREG(0x0C) +#define S3C2440_NFDATA S3C2410_NFREG(0x10) +#define S3C2440_NFECCD0 S3C2410_NFREG(0x14) +#define S3C2440_NFECCD1 S3C2410_NFREG(0x18) +#define S3C2440_NFECCD S3C2410_NFREG(0x1C) +#define S3C2440_NFSTAT S3C2410_NFREG(0x20) +#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24) +#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28) +#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) +#define S3C2440_NFMECC1 S3C2410_NFREG(0x30) +#define S3C2440_NFSECC S3C2410_NFREG(0x34) +#define S3C2440_NFSBLK S3C2410_NFREG(0x38) +#define S3C2440_NFEBLK S3C2410_NFREG(0x3C) + +#define S3C2412_NFSBLK S3C2410_NFREG(0x20) +#define S3C2412_NFEBLK S3C2410_NFREG(0x24) +#define S3C2412_NFSTAT S3C2410_NFREG(0x28) +#define S3C2412_NFMECC_ERR0 S3C2410_NFREG(0x2C) +#define S3C2412_NFMECC_ERR1 S3C2410_NFREG(0x30) +#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) +#define S3C2412_NFMECC1 S3C2410_NFREG(0x38) +#define S3C2412_NFSECC S3C2410_NFREG(0x3C) + +#define S3C2410_NFCONF_EN (1 << 15) +#define S3C2410_NFCONF_512BYTE (1 << 14) +#define S3C2410_NFCONF_4STEP (1 << 13) +#define S3C2410_NFCONF_INITECC (1 << 12) +#define S3C2410_NFCONF_nFCE (1 << 11) +#define S3C2410_NFCONF_TACLS(x) ((x) << 8) +#define S3C2410_NFCONF_TWRPH0(x) ((x) << 4) +#define S3C2410_NFCONF_TWRPH1(x) ((x) << 0) + +#define S3C2410_NFSTAT_BUSY (1 << 0) + +#define S3C2440_NFCONF_BUSWIDTH_8 (0 << 0) +#define S3C2440_NFCONF_BUSWIDTH_16 (1 << 0) +#define S3C2440_NFCONF_ADVFLASH (1 << 3) +#define S3C2440_NFCONF_TACLS(x) ((x) << 12) +#define S3C2440_NFCONF_TWRPH0(x) ((x) << 8) +#define S3C2440_NFCONF_TWRPH1(x) ((x) << 4) + +#define S3C2440_NFCONT_LOCKTIGHT (1 << 13) +#define S3C2440_NFCONT_SOFTLOCK (1 << 12) +#define S3C2440_NFCONT_ILLEGALACC_EN (1 << 10) +#define S3C2440_NFCONT_RNBINT_EN (1 << 9) +#define S3C2440_NFCONT_RN_FALLING (1 << 8) +#define S3C2440_NFCONT_SPARE_ECCLOCK (1 << 6) +#define S3C2440_NFCONT_MAIN_ECCLOCK (1 << 5) +#define S3C2440_NFCONT_INITECC (1 << 4) +#define S3C2440_NFCONT_nFCE (1 << 1) +#define S3C2440_NFCONT_ENABLE (1 << 0) + +#define S3C2440_NFSTAT_READY (1 << 0) +#define S3C2440_NFSTAT_nCE (1 << 1) +#define S3C2440_NFSTAT_RnB_CHANGE (1 << 2) +#define S3C2440_NFSTAT_ILLEGAL_ACCESS (1 << 3) + +#define S3C2412_NFCONF_NANDBOOT (1 << 31) +#define S3C2412_NFCONF_ECCCLKCON (1 << 30) +#define S3C2412_NFCONF_ECC_MLC (1 << 24) +#define S3C2412_NFCONF_TACLS_MASK (7 << 12) /* 1 extra bit of Tacls */ + +#define S3C2412_NFCONT_ECC4_DIRWR (1 << 18) +#define S3C2412_NFCONT_LOCKTIGHT (1 << 17) +#define S3C2412_NFCONT_SOFTLOCK (1 << 16) +#define S3C2412_NFCONT_ECC4_ENCINT (1 << 13) +#define S3C2412_NFCONT_ECC4_DECINT (1 << 12) +#define S3C2412_NFCONT_MAIN_ECC_LOCK (1 << 7) +#define S3C2412_NFCONT_INIT_MAIN_ECC (1 << 5) +#define S3C2412_NFCONT_nFCE1 (1 << 2) +#define S3C2412_NFCONT_nFCE0 (1 << 1) + +#define S3C2412_NFSTAT_ECC_ENCDONE (1 << 7) +#define S3C2412_NFSTAT_ECC_DECDONE (1 << 6) +#define S3C2412_NFSTAT_ILLEGAL_ACCESS (1 << 5) +#define S3C2412_NFSTAT_RnB_CHANGE (1 << 4) +#define S3C2412_NFSTAT_nFCE1 (1 << 3) +#define S3C2412_NFSTAT_nFCE0 (1 << 2) +#define S3C2412_NFSTAT_Res1 (1 << 1) +#define S3C2412_NFSTAT_READY (1 << 0) + +#define S3C2412_NFECCERR_SERRDATA(x) (((x) >> 21) & 0xf) +#define S3C2412_NFECCERR_SERRBIT(x) (((x) >> 18) & 0x7) +#define S3C2412_NFECCERR_MERRDATA(x) (((x) >> 7) & 0x3ff) +#define S3C2412_NFECCERR_MERRBIT(x) (((x) >> 4) & 0x7) +#define S3C2412_NFECCERR_SPARE_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_MAIN_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_NONE (0) +#define S3C2412_NFECCERR_1BIT (1) +#define S3C2412_NFECCERR_MULTIBIT (2) +#define S3C2412_NFECCERR_ECCAREA (3) + +#endif /* __ASM_ARM_REGS_NAND */ + |