X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fflash%2Fdavinci_nand.c;h=b6210b8fdffc623468f37917538556dc1070e3b5;hb=fb50efc6e712f413d109c3d77683d0358a557ed4;hp=cab489f8d701c36d32e912e7f400ba73905897a6;hpb=c0fc8f93f1eabe8a4adfed7784b1416b257cf035;p=openocd diff --git a/src/flash/davinci_nand.c b/src/flash/davinci_nand.c index cab489f8..b6210b8f 100644 --- a/src/flash/davinci_nand.c +++ b/src/flash/davinci_nand.c @@ -28,7 +28,7 @@ #include "config.h" #endif -#include "nand.h" +#include "arm_nandio.h" enum ecc { @@ -40,22 +40,25 @@ enum ecc { struct davinci_nand { target_t *target; - u8 chipsel; /* chipselect 0..3 == CS2..CS5 */ - u8 eccmode; + uint8_t chipsel; /* chipselect 0..3 == CS2..CS5 */ + uint8_t eccmode; /* Async EMIF controller base */ - u32 aemif; + uint32_t aemif; /* NAND chip addresses */ - u32 data; /* without CLE or ALE */ - u32 cmd; /* with CLE */ - u32 addr; /* with ALE */ + 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_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size); - int (*write_page)(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size); + int (*read_page)(struct nand_device_s *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size); + int (*write_page)(struct nand_device_s *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size); }; #define NANDFCR 0x60 /* flash control register */ @@ -84,7 +87,7 @@ static int davinci_init(struct nand_device_s *nand) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; - u32 nandfcr; + uint32_t nandfcr; if (!halted(target, "init")) return ERROR_NAND_OPERATION_FAILED; @@ -94,7 +97,7 @@ static int davinci_init(struct nand_device_s *nand) */ target_read_u32(target, info->aemif + NANDFCR, &nandfcr); if (!(nandfcr & (1 << info->chipsel))) { - LOG_ERROR("chip address %08x not NAND-enabled?", info->data); + LOG_ERROR("chip address %08" PRIx32 " not NAND-enabled?", info->data); return ERROR_NAND_OPERATION_FAILED; } @@ -114,7 +117,7 @@ static int davinci_nand_ready(struct nand_device_s *nand, int timeout) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; - u32 nandfsr; + uint32_t nandfsr; /* NOTE: return code is zero/error, else success; not ERROR_* */ @@ -133,7 +136,7 @@ static int davinci_nand_ready(struct nand_device_s *nand, int timeout) return 0; } -static int davinci_command(struct nand_device_s *nand, u8 command) +static int davinci_command(struct nand_device_s *nand, uint8_t command) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; @@ -145,7 +148,7 @@ static int davinci_command(struct nand_device_s *nand, u8 command) return ERROR_OK; } -static int davinci_address(struct nand_device_s *nand, u8 address) +static int davinci_address(struct nand_device_s *nand, uint8_t address) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; @@ -157,7 +160,7 @@ static int davinci_address(struct nand_device_s *nand, u8 address) return ERROR_OK; } -static int davinci_write_data(struct nand_device_s *nand, u16 data) +static int davinci_write_data(struct nand_device_s *nand, uint16_t data) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; @@ -181,15 +184,15 @@ static int davinci_read_data(struct nand_device_s *nand, void *data) return ERROR_OK; } -/* REVISIT a bit of native code should let block I/O be MUCH faster */ +/* REVISIT a bit of native code should let block reads be MUCH faster */ static int davinci_read_block_data(struct nand_device_s *nand, - u8 *data, int data_size) + uint8_t *data, int data_size) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; - u32 nfdata = info->data; - u32 tmp; + uint32_t nfdata = info->data; + uint32_t tmp; if (!halted(target, "read_block")) return ERROR_NAND_OPERATION_FAILED; @@ -217,16 +220,23 @@ static int davinci_read_block_data(struct nand_device_s *nand, } static int davinci_write_block_data(struct nand_device_s *nand, - u8 *data, int data_size) + uint8_t *data, int data_size) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; - u32 nfdata = info->data; - u32 tmp; + 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); @@ -245,11 +255,11 @@ static int davinci_write_block_data(struct nand_device_s *nand, return ERROR_OK; } -static int davinci_write_page(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_write_page(struct nand_device_s *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; - u8 *ooballoc = NULL; + uint8_t *ooballoc = NULL; int status; if (!nand->device) @@ -258,8 +268,10 @@ static int davinci_write_page(struct nand_device_s *nand, u32 page, return ERROR_NAND_OPERATION_FAILED; /* Always write both data and OOB ... we are not "raw" I/O! */ - if (!data) + 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) { @@ -277,19 +289,25 @@ static int davinci_write_page(struct nand_device_s *nand, u32 page, } if (!oob) { ooballoc = malloc(oob_size); - if (ooballoc) + 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_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_read_page(struct nand_device_s *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; @@ -301,7 +319,7 @@ static int davinci_read_page(struct nand_device_s *nand, u32 page, return info->read_page(nand, page, data, data_size, oob, oob_size); } -static void davinci_write_pagecmd(struct nand_device_s *nand, u8 cmd, u32 page) +static void davinci_write_pagecmd(struct nand_device_s *nand, uint8_t cmd, uint32_t page) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; @@ -325,11 +343,11 @@ static void davinci_write_pagecmd(struct nand_device_s *nand, u8 cmd, u32 page) } static int davinci_writepage_tail(struct nand_device_s *nand, - u8 *oob, u32 oob_size) + uint8_t *oob, uint32_t oob_size) { struct davinci_nand *info = nand->controller_priv; target_t *target = info->target; - u8 status; + uint8_t status; if (oob_size) davinci_write_block_data(nand, oob, oob_size); @@ -356,15 +374,15 @@ static int davinci_writepage_tail(struct nand_device_s *nand, /* * All DaVinci family chips support 1-bit ECC on a per-chipselect basis. */ -static int davinci_write_page_ecc1(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_write_page_ecc1(struct nand_device_s *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; target_t *target = info->target; - const u32 fcr_addr = info->aemif + NANDFCR; - const u32 ecc1_addr = info->aemif + NANDFECC + info->chipsel; - u32 fcr, ecc1; + 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 @@ -407,9 +425,9 @@ static int davinci_write_page_ecc1(struct nand_device_s *nand, u32 page, ecc1 = ~ecc1; /* save correct ECC code into oob data */ - oob[oob_offset++] = (u8)(ecc1); - oob[oob_offset++] = (u8)(ecc1 >> 8); - oob[oob_offset++] = (u8)(ecc1 >> 16); + oob[oob_offset++] = (uint8_t)(ecc1); + oob[oob_offset++] = (uint8_t)(ecc1 >> 8); + oob[oob_offset++] = (uint8_t)(ecc1 >> 16); } while (data_size); @@ -423,20 +441,20 @@ static int davinci_write_page_ecc1(struct nand_device_s *nand, u32 page, * is read first, so its ECC data can be used incrementally), but the * manufacturer bad block markers are safe. Contrast: old "infix" style. */ -static int davinci_write_page_ecc4(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_write_page_ecc4(struct nand_device_s *nand, uint32_t page, + uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size) { - static const u8 ecc512[] = { + 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 u8 ecc2048[] = { + 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 u8 ecc4096[] = { + 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, @@ -448,11 +466,11 @@ static int davinci_write_page_ecc4(struct nand_device_s *nand, u32 page, }; struct davinci_nand *info = nand->controller_priv; - const u8 *l; + const uint8_t *l; target_t *target = info->target; - const u32 fcr_addr = info->aemif + NANDFCR; - const u32 ecc4_addr = info->aemif + NAND4BITECC; - u32 fcr, ecc4; + 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. @@ -483,7 +501,7 @@ static int davinci_write_page_ecc4(struct nand_device_s *nand, u32 page, fcr |= (1 << 12) | (info->chipsel << 4); do { - u32 raw_ecc[4], *p; + uint32_t raw_ecc[4], *p; int i; /* start 4bit ecc on csX */ @@ -525,14 +543,14 @@ static int davinci_write_page_ecc4(struct nand_device_s *nand, u32 page, * older second stage loaders (ABL/U-Boot, etc) or other system software * (MVL 4.x/5.x kernels, filesystems, etc) may need it more generally. */ -static int davinci_write_page_ecc4infix(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_write_page_ecc4infix(struct nand_device_s *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; target_t *target = info->target; - const u32 fcr_addr = info->aemif + NANDFCR; - const u32 ecc4_addr = info->aemif + NAND4BITECC; - u32 fcr, ecc4; + 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); @@ -544,8 +562,8 @@ static int davinci_write_page_ecc4infix(struct nand_device_s *nand, u32 page, fcr |= (1 << 12) | (info->chipsel << 4); do { - u32 raw_ecc[4], *p; - u8 *l; + uint32_t raw_ecc[4], *p; + uint8_t *l; int i; /* start 4bit ecc on csX */ @@ -582,8 +600,8 @@ static int davinci_write_page_ecc4infix(struct nand_device_s *nand, u32 page, return davinci_writepage_tail(nand, NULL, 0); } -static int davinci_read_page_ecc4infix(struct nand_device_s *nand, u32 page, - u8 *data, u32 data_size, u8 *oob, u32 oob_size) +static int davinci_read_page_ecc4infix(struct nand_device_s *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); @@ -620,7 +638,6 @@ static int davinci_nand_device_command(struct command_context_s *cmd_ctx, unsigned long chip, aemif; enum ecc eccmode; int chipsel; - char *ep; /* arguments: * - "davinci" @@ -643,8 +660,8 @@ static int davinci_nand_device_command(struct command_context_s *cmd_ctx, goto fail; } - chip = strtoul(argv[2], &ep, 0); - if (*ep || chip == 0 || chip == ULONG_MAX) { + COMMAND_PARSE_NUMBER(ulong, argv[2], chip); + if (chip == 0) { LOG_ERROR("Invalid NAND chip address %s", argv[2]); goto fail; } @@ -660,8 +677,8 @@ static int davinci_nand_device_command(struct command_context_s *cmd_ctx, goto fail; } - aemif = strtoul(argv[4], &ep, 0); - if (*ep || chip == 0 || chip == ULONG_MAX) { + COMMAND_PARSE_NUMBER(ulong, argv[4], aemif); + if (aemif == 0) { LOG_ERROR("Invalid AEMIF controller address %s", argv[4]); goto fail; } @@ -674,11 +691,11 @@ static int davinci_nand_device_command(struct command_context_s *cmd_ctx, || aemif == 0x01e10000 /* dm335, dm355 */ || aemif == 0x01d10000 /* dm365 */ ) { - if (chip < 0x0200000 || chip >= 0x0a000000) { + if (chip < 0x02000000 || chip >= 0x0a000000) { LOG_ERROR("NAND address %08lx out of range?", chip); goto fail; } - chipsel = (chip - 0x02000000) >> 21; + chipsel = (chip - 0x02000000) >> 25; } else { LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif); goto fail; @@ -698,6 +715,9 @@ static int davinci_nand_device_command(struct command_context_s *cmd_ctx, 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.