diff options
author | Markus Klotzbuecher <mk@denx.de> | 2008-07-10 10:26:07 +0200 |
---|---|---|
committer | Markus Klotzbuecher <mk@denx.de> | 2008-07-10 10:26:07 +0200 |
commit | 794a5924972fc8073616e98a2668da4a5f9aea90 (patch) | |
tree | dd0db39b3e183b5bcb0300d5377d7a0d5ac5fd0c /drivers/mtd | |
parent | f2aeecc320f5b181b30effcaa67683aec8d5a843 (diff) | |
parent | 4188f0491886b3b486164e819c0a83fdb97efd7d (diff) | |
download | u-boot-imx-794a5924972fc8073616e98a2668da4a5f9aea90.zip u-boot-imx-794a5924972fc8073616e98a2668da4a5f9aea90.tar.gz u-boot-imx-794a5924972fc8073616e98a2668da4a5f9aea90.tar.bz2 |
Merge branch 'master' of git://www.denx.de/git/u-boot
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/cfi_flash.c | 55 | ||||
-rw-r--r-- | drivers/mtd/jedec_flash.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_upm.c | 128 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 145 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 7 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 95 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 6 | ||||
-rw-r--r-- | drivers/mtd/spi/Makefile | 47 | ||||
-rw-r--r-- | drivers/mtd/spi/atmel.c | 362 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash.c | 162 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 45 |
12 files changed, 864 insertions, 194 deletions
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 68ab55f..c0ea97b 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -76,7 +76,9 @@ #define FLASH_CMD_PROTECT_SET 0x01 #define FLASH_CMD_PROTECT_CLEAR 0xD0 #define FLASH_CMD_CLEAR_STATUS 0x50 +#define FLASH_CMD_READ_STATUS 0x70 #define FLASH_CMD_WRITE_TO_BUFFER 0xE8 +#define FLASH_CMD_WRITE_BUFFER_PROG 0xE9 #define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0 #define FLASH_STATUS_DONE 0x80 @@ -136,6 +138,7 @@ #define CFI_CMDSET_MITSU_STANDARD 256 #define CFI_CMDSET_MITSU_EXTENDED 257 #define CFI_CMDSET_SST 258 +#define CFI_CMDSET_INTEL_PROG_REGIONS 512 #ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */ # undef FLASH_CMD_RESET @@ -298,17 +301,25 @@ static inline void flash_unmap(flash_info_t *info, flash_sect_t sect, /*----------------------------------------------------------------------- * make a proper sized command based on the port and chip widths */ -static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) +static void flash_make_cmd (flash_info_t * info, ulong cmd, void *cmdbuf) { int i; + int cword_offset; + int cp_offset; + uchar val; uchar *cp = (uchar *) cmdbuf; + for (i = info->portwidth; i > 0; i--){ + cword_offset = (info->portwidth-i)%info->chipwidth; #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - for (i = info->portwidth; i > 0; i--) + cp_offset = info->portwidth - i; + val = *((uchar*)&cmd + cword_offset); #else - for (i = 1; i <= info->portwidth; i++) + cp_offset = i - 1; + val = *((uchar*)&cmd + sizeof(ulong) - cword_offset - 1); #endif - *cp++ = (i & (info->chipwidth - 1)) ? '\0' : cmd; + cp[cp_offset] = (cword_offset >= sizeof(ulong)) ? 0x00 : val; + } } #ifdef DEBUG @@ -422,7 +433,7 @@ static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, * Write a proper sized command to the correct address */ static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, - uint offset, uchar cmd) + uint offset, ulong cmd) { void *addr; @@ -570,20 +581,16 @@ static int flash_toggle (flash_info_t * info, flash_sect_t sect, flash_make_cmd (info, cmd, &cword); switch (info->portwidth) { case FLASH_CFI_8BIT: - retval = ((flash_read8(addr) & cword.c) != - (flash_read8(addr) & cword.c)); + retval = flash_read8(addr) != flash_read8(addr); break; case FLASH_CFI_16BIT: - retval = ((flash_read16(addr) & cword.w) != - (flash_read16(addr) & cword.w)); + retval = flash_read16(addr) != flash_read16(addr); break; case FLASH_CFI_32BIT: - retval = ((flash_read32(addr) & cword.l) != - (flash_read32(addr) & cword.l)); + retval = flash_read32(addr) != flash_read32(addr); break; case FLASH_CFI_64BIT: - retval = ((flash_read64(addr) & cword.ll) != - (flash_read64(addr) & cword.ll)); + retval = flash_read64(addr) != flash_read64(addr); break; default: retval = 0; @@ -605,6 +612,7 @@ static int flash_is_busy (flash_info_t * info, flash_sect_t sect) int retval; switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); @@ -664,6 +672,7 @@ static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, retcode = flash_status_check (info, sector, tout, prompt); switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: if ((retcode == ERR_OK) @@ -792,6 +801,7 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest, flag = disable_interrupts (); switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); @@ -846,6 +856,7 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, int flag = 0; uint offset = 0; unsigned int shift; + uchar write_cmd; switch (info->portwidth) { case FLASH_CFI_8BIT: @@ -900,10 +911,14 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, sector = find_sector (info, dest); switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: + write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ? + FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER; flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); + flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS); + flash_write_cmd (info, sector, 0, write_cmd); retcode = flash_status_check (info, sector, info->buffer_write_tout, "write to buffer"); @@ -911,7 +926,7 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, /* reduce the number of loops by the width of * the port */ cnt = len >> shift; - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + flash_write_cmd (info, sector, 0, cnt - 1); while (cnt-- > 0) { switch (info->portwidth) { case FLASH_CFI_8BIT: @@ -1038,6 +1053,7 @@ int flash_erase (flash_info_t * info, int s_first, int s_last) for (sect = s_first; sect <= s_last; sect++) { if (info->protect[sect] == 0) { /* not protected */ switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: flash_write_cmd (info, sect, 0, @@ -1106,6 +1122,9 @@ void flash_print_info (flash_info_t * info) info->size >> 20, info->sector_count); printf (" "); switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + printf ("Intel Prog Regions"); + break; case CFI_CMDSET_INTEL_STANDARD: printf ("Intel Standard"); break; @@ -1496,6 +1515,7 @@ static void flash_read_jedec_ids (flash_info_t * info) info->device_id2 = 0; switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: cmdset_intel_read_jedec_ids(info); @@ -1550,6 +1570,7 @@ static int flash_detect_legacy(ulong base, int banknum) } switch(info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: info->cmd_reset = FLASH_CMD_RESET; @@ -1720,6 +1741,8 @@ ulong flash_get_size (ulong base, int banknum) int erase_region_count; struct cfi_qry qry; + memset(&qry, 0, sizeof(qry)); + info->ext_addr = 0; info->cfi_version = 0; #ifdef CFG_FLASH_PROTECTION @@ -1745,6 +1768,7 @@ ulong flash_get_size (ulong base, int banknum) #endif switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: cmdset_intel_init(info, &qry); @@ -1822,6 +1846,7 @@ ulong flash_get_size (ulong base, int banknum) * supported devices (intel...) */ switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: info->protect[sect_cnt] = diff --git a/drivers/mtd/jedec_flash.c b/drivers/mtd/jedec_flash.c index b958d17..9845e93 100644 --- a/drivers/mtd/jedec_flash.c +++ b/drivers/mtd/jedec_flash.c @@ -42,7 +42,7 @@ #define MANUFACTURER_SST 0x00BF /* AMD */ -#define AM29DL800BB 0x22C8 +#define AM29DL800BB 0x22CB #define AM29DL800BT 0x224A #define AM29F800BB 0x2258 diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 5cc410a..67ae9c8 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -20,112 +20,83 @@ #include <linux/mtd/fsl_upm.h> #include <nand.h> -#define FSL_UPM_MxMR_OP_NO (0 << 28) /* normal operation */ -#define FSL_UPM_MxMR_OP_WA (1 << 28) /* write array */ -#define FSL_UPM_MxMR_OP_RA (2 << 28) /* read array */ -#define FSL_UPM_MxMR_OP_RP (3 << 28) /* run pattern */ +static int fsl_upm_in_pattern; static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) { - out_be32(upm->mxmr, FSL_UPM_MxMR_OP_RP | pat_offset); + clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); } static void fsl_upm_end_pattern(struct fsl_upm *upm) { - out_be32(upm->mxmr, FSL_UPM_MxMR_OP_NO); - while (in_be32(upm->mxmr) != FSL_UPM_MxMR_OP_NO) + clrbits_be32(upm->mxmr, MxMR_OP_RUNP); + + while (in_be32(upm->mxmr) & MxMR_OP_RUNP) eieio(); } static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, u32 cmd) { - out_be32(upm->mar, cmd << (32 - width * 8)); - out_8(upm->io_addr, 0x0); -} - -static void fsl_upm_setup(struct fsl_upm *upm) -{ - int i; - - /* write upm array */ - out_be32(upm->mxmr, FSL_UPM_MxMR_OP_WA); - - for (i = 0; i < 64; i++) { - out_be32(upm->mdr, upm->array[i]); + out_be32(upm->mar, cmd << (32 - width)); + switch (width) { + case 8: out_8(upm->io_addr, 0x0); + break; + case 16: + out_be16(upm->io_addr, 0x0); + break; + case 32: + out_be32(upm->io_addr, 0x0); + break; } - - /* normal operation */ - out_be32(upm->mxmr, FSL_UPM_MxMR_OP_NO); - while (in_be32(upm->mxmr) != FSL_UPM_MxMR_OP_NO) - eieio(); } -static void fun_cmdfunc(struct mtd_info *mtd, unsigned command, int column, - int page_addr) +static void nand_hwcontrol (struct mtd_info *mtd, int cmd) { struct nand_chip *chip = mtd->priv; struct fsl_upm_nand *fun = chip->priv; - fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); - - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->oobblock) { - /* OOB area */ - column -= mtd->oobblock; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - fsl_upm_run_pattern(&fun->upm, fun->width, readcmd); + switch (cmd) { + case NAND_CTL_SETCLE: + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + fsl_upm_in_pattern++; + break; + case NAND_CTL_SETALE: + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + fsl_upm_in_pattern++; + break; + case NAND_CTL_CLRCLE: + case NAND_CTL_CLRALE: + fsl_upm_end_pattern(&fun->upm); + fsl_upm_in_pattern--; + break; } +} - fsl_upm_run_pattern(&fun->upm, fun->width, command); - - fsl_upm_end_pattern(&fun->upm); - - fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); - - if (column != -1) - fsl_upm_run_pattern(&fun->upm, fun->width, column); +static void nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *chip = mtd->priv; - if (page_addr != -1) { - fsl_upm_run_pattern(&fun->upm, fun->width, page_addr); - fsl_upm_run_pattern(&fun->upm, fun->width, - (page_addr >> 8) & 0xFF); - if (chip->chipsize > (32 << 20)) { - fsl_upm_run_pattern(&fun->upm, fun->width, - (page_addr >> 16) & 0x0f); - } - } + if (fsl_upm_in_pattern) { + struct fsl_upm_nand *fun = chip->priv; - fsl_upm_end_pattern(&fun->upm); + fsl_upm_run_pattern(&fun->upm, fun->width, byte); - if (fun->wait_pattern) { /* * Some boards/chips needs this. At least on MPC8360E-RDK we * need it. Probably weird chip, because I don't see any need * for this on MPC8555E + Samsung K9F1G08U0A. Usually here are * 0-2 unexpected busy states per block read. */ - while (!fun->dev_ready()) - debug("unexpected busy state\n"); + if (fun->wait_pattern) { + while (!fun->dev_ready()) + debug("unexpected busy state\n"); + } + } else { + out_8(chip->IO_ADDR_W, byte); } } -static void nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *chip = mtd->priv; - - out_8(chip->IO_ADDR_W, byte); -} - static u8 nand_read_byte(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; @@ -164,10 +135,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) return 0; } -static void nand_hwcontrol(struct mtd_info *mtd, int cmd) -{ -} - static int nand_dev_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; @@ -178,23 +145,20 @@ static int nand_dev_ready(struct mtd_info *mtd) int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) { - /* yet only 8 bit accessors implemented */ - if (fun->width != 1) + if (fun->width != 8 && fun->width != 16 && fun->width != 32) return -ENOSYS; - fsl_upm_setup(&fun->upm); - chip->priv = fun; chip->chip_delay = fun->chip_delay; chip->eccmode = NAND_ECC_SOFT; - chip->cmdfunc = fun_cmdfunc; chip->hwcontrol = nand_hwcontrol; chip->read_byte = nand_read_byte; chip->read_buf = nand_read_buf; chip->write_byte = nand_write_byte; chip->write_buf = nand_write_buf; chip->verify_buf = nand_verify_buf; - chip->dev_ready = nand_dev_ready; + if (fun->dev_ready) + chip->dev_ready = nand_dev_ready; return 0; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5aef31c..6416d15 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -113,18 +113,22 @@ static struct nand_oobinfo nand_oob_64 = { .oobfree = { {2, 38} } }; -/* This is used for padding purposes in nand_write_oob */ -static u_char ffchars[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +static struct nand_oobinfo nand_oob_128 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 48, + .eccpos = { + 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}, + .oobfree = { {2, 78} } }; +/* This is used for padding purposes in nand_write_oob */ +static u_char *ffchars; + /* * NAND low-level MTD interface functions */ @@ -193,6 +197,10 @@ static void nand_release_device (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; this->select_chip(mtd, -1); /* De-select the NAND device */ + if (ffchars) { + kfree(ffchars); + ffchars = NULL; + } } #endif @@ -891,7 +899,7 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) { int i, status; - u_char ecc_code[32]; + u_char ecc_code[NAND_MAX_OOBSIZE]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; uint *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; @@ -954,7 +962,9 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed write, page 0x%08x, ", + __FUNCTION__, page); return -EIO; } } else { @@ -1002,7 +1012,9 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int for (j = 0; j < eccsteps; j++) { /* Loop through and verify the data */ if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { - DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " + "Failed write verify, page 0x%08x ", + __FUNCTION__, page); goto out; } datidx += mtd->eccsize; @@ -1010,7 +1022,9 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int if (!hweccbytes) continue; if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { - DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " + "Failed write verify, page 0x%08x ", + __FUNCTION__, page); goto out; } oobofs += hweccbytes; @@ -1021,7 +1035,9 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int */ if (oobmode) { if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { - DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " + "Failed write verify, page 0x%08x ", + __FUNCTION__, page); goto out; } } else { @@ -1034,9 +1050,11 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int for (i = 0; i < ecccnt; i++) { int idx = oobsel->eccpos[i]; if (oobdata[idx] != oob_buf[oobofs + idx] ) { - DEBUG (MTD_DEBUG_LEVEL0, + MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: Failed ECC write " - "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + "verify, page 0x%08x, " + "%6i bytes were succesful\n", + __FUNCTION__, page, i); goto out; } } @@ -1112,8 +1130,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; u_char *data_poi, *oob_data = oob_buf; - u_char ecc_calc[32]; - u_char ecc_code[32]; + u_char ecc_calc[NAND_MAX_OOBSIZE]; + u_char ecc_code[NAND_MAX_OOBSIZE]; int eccmode, eccsteps; unsigned *oob_config; int datidx; @@ -1123,11 +1141,13 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, int oobreadlen; - DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", + (unsigned int) from, (int) len); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -1244,7 +1264,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, * generator for an error, reads back the syndrome and * does the error correction on the fly */ if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ecc_failed++; } @@ -1283,7 +1303,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } if (ecc_status == -1) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x\n", + page); ecc_failed++; } } @@ -1380,7 +1402,8 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t struct nand_chip *this = mtd->priv; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", + (unsigned int) from, (int) len); /* Shift to get page */ page = (int)(from >> this->page_shift); @@ -1394,7 +1417,8 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -1480,7 +1504,8 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_read_raw: Attempt read beyond end of device\n"); return -EINVAL; } @@ -1618,14 +1643,16 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, u_char *oobbuf, *bufstart; int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); - DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", + (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow write past end of device */ if ((to + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: Attempt to write past end of page\n"); return -EINVAL; } @@ -1687,7 +1714,8 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, */ ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); if (ret) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: write_page failed %d\n", ret); goto out; } /* Next oob page */ @@ -1711,7 +1739,8 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, page - startpage, oobbuf, oobsel, chipnr, (eccbuf != NULL)); if (ret) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " + "verify_pages failed %d\n", ret); goto out; } *retlen = written; @@ -1744,7 +1773,8 @@ cmp: if (!ret) *retlen = written; else - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: verify_pages failed %d\n", ret); out: /* Deselect and wake up anyone waiting on the device */ @@ -1769,7 +1799,8 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * int column, page, status, ret = -EIO, chipnr; struct nand_chip *this = mtd->priv; - DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int) to, (int) len); /* Shift to get page */ page = (int) (to >> this->page_shift); @@ -1783,7 +1814,8 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * /* Do not allow write past end of page */ if ((column + len) > mtd->oobsize) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Attempt to write past end of page\n"); return -EINVAL; } @@ -1811,6 +1843,16 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * if (NAND_MUST_PAD(this)) { /* Write out desired data */ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); + if (!ffchars) { + if (!(ffchars = kmalloc (mtd->oobsize, GFP_KERNEL))) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " + "No memory for padding array, " + "need %d bytes", mtd->oobsize); + ret = -ENOMEM; + goto out; + } + memset(ffchars, 0xff, mtd->oobsize); + } /* prepad 0xff for partial programming */ this->write_buf(mtd, ffchars, column); /* write data */ @@ -1830,7 +1872,8 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * /* See if device thinks it succeeded */ if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; } @@ -1842,7 +1885,8 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); if (this->verify_buf(mtd, buf, len)) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Failed write verify, page 0x%08x\n", page); ret = -EIO; goto out; } @@ -1902,12 +1946,14 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig for (i = 0; i < count; i++) total_len += (int) vecs[i].iov_len; - DEBUG (MTD_DEBUG_LEVEL3, - "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); + MTDDEBUG (MTD_DEBUG_LEVEL3, + "nand_writev: to = 0x%08x, len = %i, count = %ld\n", + (unsigned int) to, (unsigned int) total_len, count); /* Do not allow write past end of page */ if ((to + total_len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: Attempted write past end of device\n"); return -EINVAL; } @@ -2100,24 +2146,26 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; - DEBUG (MTD_DEBUG_LEVEL3, - "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", + (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & ((1 << this->phys_erase_shift) - 1)) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Erase past end of device\n"); return -EINVAL; } @@ -2139,7 +2187,8 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb /* Check the WP bit */ /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Device is write protected!!!\n"); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2169,7 +2218,8 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb /* See if block erase succeeded */ if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " + "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << this->page_shift); goto erase_exit; @@ -2212,7 +2262,7 @@ static void nand_sync (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); /* Grab the lock and see if the device is available */ nand_get_device (this, mtd, FL_SYNCING); @@ -2479,6 +2529,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips) case 64: this->autooob = &nand_oob_64; break; + case 128: + this->autooob = &nand_oob_128; + break; default: printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index eff76d7..a97743b 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1038,8 +1038,8 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) block = (int) (offs >> (this->bbt_erase_shift - 1)); res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; - DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, res, block >> 1); + MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: " + "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1); switch ((int)res) { case 0x00: return 0; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 6c5624a..c82f77b 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -153,6 +153,13 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) priv_nand->bbt = NULL; } + if (erase_length < meminfo->erasesize) { + printf("Warning: Erase size 0x%08x smaller than one " \ + "erase block 0x%08x\n",erase_length, meminfo->erasesize); + printf(" Erasing 0x%08x instead\n", meminfo->erasesize); + erase_length = meminfo->erasesize; + } + for (; erase.addr < opts->offset + erase_length; erase.addr += meminfo->erasesize) { diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d1b9f8f..d32e382 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -293,22 +293,22 @@ static int onenand_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ERROR) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: controller error = 0x%04x\n", ctrl); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_wait: controller error = 0x%04x\n", ctrl); return -EAGAIN; } if (ctrl & ONENAND_CTRL_LOCK) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: it's locked error = 0x%04x\n", ctrl); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_wait: it's locked error = 0x%04x\n", ctrl); return -EIO; } if (interrupt & ONENAND_INT_READ) { ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc & ONENAND_ECC_2BIT_ALL) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: ECC error = 0x%04x\n", ecc); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_wait: ECC error = 0x%04x\n", ecc); return -EBADMSG; } } @@ -524,13 +524,14 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, int thislen; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n", - (unsigned int)from, (int)len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_read_ecc: " + "from = 0x%08x, len = %i\n", + (unsigned int)from, (int)len); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_ecc: Attempt read beyond end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_read_ecc: " + "Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -561,8 +562,8 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, break; if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_ecc: read failed = %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_read_ecc: read failed = %d\n", ret); break; } @@ -615,16 +616,17 @@ int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, int read = 0, thislen, column; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", - (unsigned int)from, (int)len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_read_oob: " + "from = 0x%08x, len = %i\n", + (unsigned int)from, (int)len); /* Initialize return length value */ *retlen = 0; /* Do not allow reads past end of device */ if (unlikely((from + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_oob: Attempt read beyond end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_read_oob: " + "Attempt read beyond end of device\n"); return -EINVAL; } @@ -652,8 +654,8 @@ int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, break; if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_oob: read failed = %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_read_oob: read failed = %d\n", ret); break; } @@ -733,23 +735,24 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, int written = 0; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_write_ecc: " + "to = 0x%08x, len = %i\n", + (unsigned int)to, (int)len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: Attempt write to past end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_ecc: " + "Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: Attempt to write not page aligned data\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_ecc: " + "Attempt to write not page aligned data\n"); return -EINVAL; } @@ -772,8 +775,8 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: write filaed %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_write_ecc: write filaed %d\n", ret); break; } @@ -782,8 +785,8 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, /* Only check verify write turn on */ ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: verify failed %d\n", ret); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_write_ecc: verify failed %d\n", ret); break; } @@ -836,16 +839,17 @@ int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, int column, status; int written = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_write_oob: " + "to = 0x%08x, len = %i\n", + (unsigned int)to, (int)len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_oob: Attempt write to past end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_oob: " + "Attempt write to past end of device\n"); return -EINVAL; } @@ -904,28 +908,29 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) int len; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", + (unsigned int)instr->addr, (unsigned int)instr->len); block_size = (1 << this->erase_shift); /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Length not block aligned\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if (unlikely((instr->len + instr->addr) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Erase past end of device\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_erase: Erase past end of device\n"); return -EINVAL; } @@ -950,12 +955,12 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Check, if it is write protected */ if (ret) { if (ret == -EPERM) - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Device is write protected!!!\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " + "Device is write protected!!!\n"); else - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " + "Failed erase, block %d\n", + (unsigned)(addr >> this->erase_shift)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -988,7 +993,7 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) */ void onenand_sync(struct mtd_info *mtd) { - DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_SYNCING); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 5a610ee..87344ab 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -156,9 +156,9 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) block = (int)(offs >> (bbm->bbt_erase_shift - 1)); res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; - DEBUG(MTD_DEBUG_LEVEL2, - "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + MTDDEBUG (MTD_DEBUG_LEVEL2, + "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile new file mode 100644 index 0000000..af6af97 --- /dev/null +++ b/drivers/mtd/spi/Makefile @@ -0,0 +1,47 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libspi_flash.a + +COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o +COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c new file mode 100644 index 0000000..fb7a4a9 --- /dev/null +++ b/drivers/mtd/spi/atmel.c @@ -0,0 +1,362 @@ +/* + * Atmel SPI DataFlash support + * + * Copyright (C) 2008 Atmel Corporation + */ +#define DEBUG +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* AT45-specific commands */ +#define CMD_AT45_READ_STATUS 0xd7 +#define CMD_AT45_ERASE_PAGE 0x81 +#define CMD_AT45_LOAD_PROG_BUF1 0x82 +#define CMD_AT45_LOAD_BUF1 0x84 +#define CMD_AT45_LOAD_PROG_BUF2 0x85 +#define CMD_AT45_LOAD_BUF2 0x87 +#define CMD_AT45_PROG_BUF1 0x88 +#define CMD_AT45_PROG_BUF2 0x89 + +/* AT45 status register bits */ +#define AT45_STATUS_P2_PAGE_SIZE (1 << 0) +#define AT45_STATUS_READY (1 << 7) + +/* DataFlash family IDs, as obtained from the second idcode byte */ +#define DF_FAMILY_AT26F 0 +#define DF_FAMILY_AT45 1 +#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */ + +struct atmel_spi_flash_params { + u8 idcode1; + /* Log2 of page size in power-of-two mode */ + u8 l2_page_size; + u8 pages_per_block; + u8 blocks_per_sector; + u8 nr_sectors; + const char *name; +}; + +struct atmel_spi_flash { + const struct atmel_spi_flash_params *params; + struct spi_flash flash; +}; + +static inline struct atmel_spi_flash * +to_atmel_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct atmel_spi_flash, flash); +} + +static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { + { + .idcode1 = 0x28, + .l2_page_size = 10, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 32, + .name = "AT45DB642D", + }, +}; + +static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 cmd = CMD_AT45_READ_STATUS; + u8 status; + + timebase = get_timer(0); + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) + return -1; + + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if (status & AT45_STATUS_READY) + break; + } while (get_timer(timebase) < timeout); + + /* Deactivate CS */ + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if (status & AT45_STATUS_READY) + return 0; + + /* Timed out */ + return -1; +} + +/* + * Assemble the address part of a command for AT45 devices in + * non-power-of-two page size mode. + */ +static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) +{ + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + + /* + * The "extra" space per page is the power-of-two page size + * divided by 32. + */ + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + cmd[0] = page_addr >> (16 - page_shift); + cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[2] = byte_addr; +} + +static int dataflash_read_fast_p2(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int dataflash_read_fast_at45(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + at45_build_address(asf, cmd + 1, offset); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int dataflash_write_at45(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[3] = byte_addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: AT45: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + unsigned int page_shift; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8); + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + page_addr++; + } + + debug("SF: AT45: Successfully erased %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) +{ + const struct atmel_spi_flash_params *params; + unsigned long page_size; + unsigned int family; + struct atmel_spi_flash *asf; + unsigned int i; + int ret; + u8 status; + + for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) { + params = &atmel_spi_flash_table[i]; + if (params->idcode1 == idcode[1]) + break; + } + + if (i == ARRAY_SIZE(atmel_spi_flash_table)) { + debug("SF: Unsupported DataFlash ID %02x\n", + idcode[1]); + return NULL; + } + + asf = malloc(sizeof(struct atmel_spi_flash)); + if (!asf) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + asf->params = params; + asf->flash.spi = spi; + asf->flash.name = params->name; + + /* Assuming power-of-two page size initially. */ + page_size = 1 << params->l2_page_size; + + family = idcode[1] >> 5; + + switch (family) { + case DF_FAMILY_AT45: + /* + * AT45 chips have configurable page size. The status + * register indicates which configuration is active. + */ + ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1); + if (ret) + goto err; + + debug("SF: AT45 status register: %02x\n", status); + + if (!(status & AT45_STATUS_P2_PAGE_SIZE)) { + asf->flash.read = dataflash_read_fast_at45; + asf->flash.write = dataflash_write_at45; + asf->flash.erase = dataflash_erase_at45; + page_size += 1 << (params->l2_page_size - 5); + } else { + asf->flash.read = dataflash_read_fast_p2; + } + + break; + + case DF_FAMILY_AT26F: + case DF_FAMILY_AT26DF: + asf->flash.read = dataflash_read_fast_p2; + break; + + default: + debug("SF: Unsupported DataFlash family %u\n", family); + goto err; + } + + asf->flash.size = page_size * params->pages_per_block + * params->blocks_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, page_size, asf->flash.size); + + return &asf->flash; + +err: + free(asf); + return NULL; +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c new file mode 100644 index 0000000..d581cb3 --- /dev/null +++ b/drivers/mtd/spi/spi_flash.c @@ -0,0 +1,162 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + */ +#define DEBUG +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, 8, &cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + if (len) { + ret = spi_xfer(spi, len * 8, NULL, response, SPI_XFER_END); + if (ret) + debug("SF: Failed to read response (%zu bytes): %d\n", + len, ret); + } + + return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, NULL, data, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data, NULL, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + struct spi_slave *spi = flash->spi; + int ret; + + spi_claim_bus(spi); + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + spi_release_bus(spi); + + return ret; +} + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash; + int ret; + u8 idcode[3]; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + debug("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, &idcode, sizeof(idcode)); + if (ret) + goto err_read_id; + + debug("SF: Got idcode %02x %02x %02x\n", idcode[0], + idcode[1], idcode[2]); + + switch (idcode[0]) { +#ifdef CONFIG_SPI_FLASH_SPANSION + case 0x01: + flash = spi_flash_probe_spansion(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_ATMEL + case 0x1F: + flash = spi_flash_probe_atmel(spi, idcode); + break; +#endif + default: + debug("SF: Unsupported manufacturer %02X\n", idcode[0]); + flash = NULL; + break; + } + + if (!flash) + goto err_manufacturer_probe; + + spi_release_bus(spi); + + return flash; + +err_manufacturer_probe: +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h new file mode 100644 index 0000000..1438050 --- /dev/null +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -0,0 +1,45 @@ +/* + * SPI flash internal definitions + * + * Copyright (C) 2008 Atmel Corporation + */ + +/* Common parameters */ +#define SPI_FLASH_PROG_TIMEOUT ((10 * CFG_HZ) / 1000) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT ((50 * CFG_HZ) / 1000) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CFG_HZ) + +/* Common commands */ +#define CMD_READ_ID 0x9f + +#define CMD_READ_ARRAY_SLOW 0x03 +#define CMD_READ_ARRAY_FAST 0x0b +#define CMD_READ_ARRAY_LEGACY 0xe8 + +/* Send a single-byte command to the device and read the response */ +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); + +/* + * Send a multi-byte command to the device and read the response. Used + * for flash array reads, etc. + */ +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* + * Send a multi-byte command to the device followed by (optional) + * data. Used for programming the flash array, etc. + */ +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len); + +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* Manufacturer-specific probe functions */ +struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); |