summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorMarkus Klotzbuecher <mk@denx.de>2008-07-10 10:26:07 +0200
committerMarkus Klotzbuecher <mk@denx.de>2008-07-10 10:26:07 +0200
commit794a5924972fc8073616e98a2668da4a5f9aea90 (patch)
treedd0db39b3e183b5bcb0300d5377d7a0d5ac5fd0c /drivers/mtd
parentf2aeecc320f5b181b30effcaa67683aec8d5a843 (diff)
parent4188f0491886b3b486164e819c0a83fdb97efd7d (diff)
downloadu-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.c55
-rw-r--r--drivers/mtd/jedec_flash.c2
-rw-r--r--drivers/mtd/nand/fsl_upm.c128
-rw-r--r--drivers/mtd/nand/nand_base.c145
-rw-r--r--drivers/mtd/nand/nand_bbt.c4
-rw-r--r--drivers/mtd/nand/nand_util.c7
-rw-r--r--drivers/mtd/onenand/onenand_base.c95
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c6
-rw-r--r--drivers/mtd/spi/Makefile47
-rw-r--r--drivers/mtd/spi/atmel.c362
-rw-r--r--drivers/mtd/spi/spi_flash.c162
-rw-r--r--drivers/mtd/spi/spi_flash_internal.h45
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);