This brings the core NAND code up to date with the Linux kernel.
Since there were several drivers in Linux as of the last update that are
not in u-boot, I'm not bringing over new drivers that have been added
since in the absence of an interested party.
I did not update OneNAND since it was recently synced by Kyungmin Park,
and I'm not sure exactly what the common ancestor is.
Signed-off-by: Scott Wood <scottwood@freescale.com>
* converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
*
* Interface to generic NAND code for M-Systems DiskOnChip devices
- *
- * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $
*/
#include <common.h>
0xe0000, 0xe2000, 0xe4000, 0xe6000,
0xe8000, 0xea000, 0xec000, 0xee000,
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
-#elif defined(__PPC__)
- 0xe4000000,
-#elif defined(CONFIG_MOMENCO_OCELOT)
- 0x2f000000,
- 0xff000000,
-#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
- 0xff000000,
#else
#warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif
}
}
/* If the parity is wrong, no rescue possible */
- return parity ? -1 : nerr;
+ return parity ? -EBADMSG : nerr;
}
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- if (no_ecc_failures && (ret == -1)) {
+ if (no_ecc_failures && (ret == -EBADMSG)) {
printk(KERN_ERR "suppressing ECC failure\n");
ret = 0;
}
goto out;
mh = (struct NFTLMediaHeader *)buf;
- mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
- mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
- mh->FormattedSize = le32_to_cpu(mh->FormattedSize);
+ le16_to_cpus(&mh->NumEraseUnits);
+ le16_to_cpus(&mh->FirstPhysicalEUN);
+ le32_to_cpus(&mh->FormattedSize);
printk(KERN_INFO " DataOrgID = %s\n"
" NumEraseUnits = %d\n"
doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
mh = (struct INFTLMediaHeader *)buf;
- mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
- mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
- mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
- mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
- mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
- mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
+ le32_to_cpus(&mh->NoOfBootImageBlocks);
+ le32_to_cpus(&mh->NoOfBinaryPartitions);
+ le32_to_cpus(&mh->NoOfBDTLPartitions);
+ le32_to_cpus(&mh->BlockMultiplierBits);
+ le32_to_cpus(&mh->FormatFlags);
+ le32_to_cpus(&mh->PercentUsed);
printk(KERN_INFO " bootRecordID = %s\n"
" NoOfBootImageBlocks = %d\n"
/* Scan the partitions */
for (i = 0; (i < 4); i++) {
ip = &(mh->Partitions[i]);
- ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
- ip->firstUnit = le32_to_cpu(ip->firstUnit);
- ip->lastUnit = le32_to_cpu(ip->lastUnit);
- ip->flags = le32_to_cpu(ip->flags);
- ip->spareUnits = le32_to_cpu(ip->spareUnits);
- ip->Reserved0 = le32_to_cpu(ip->Reserved0);
+ le32_to_cpus(&ip->virtualUnits);
+ le32_to_cpus(&ip->firstUnit);
+ le32_to_cpus(&ip->lastUnit);
+ le32_to_cpus(&ip->flags);
+ le32_to_cpus(&ip->spareUnits);
+ le32_to_cpus(&ip->Reserved0);
printk(KERN_INFO " PARTITION[%d] ->\n"
" virtualUnits = %d\n"
* Basic support for AG-AND chips is provided.
*
* Additional technical information is available on
- * http://www.linux-mtd.infradead.org/tech/nand.html
+ * http://www.linux-mtd.infradead.org/doc/nand.html
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
* if we have HW ecc support.
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
+ * BBT table is not serialized, has to be fixed
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
/*
- * For devices which display every fart in the system on a seperate LED. Is
+ * For devices which display every fart in the system on a separate LED. Is
* compiled away when LED support is disabled.
*/
/* XXX U-BOOT XXX */
/* We write two bytes, so we dont have to mess with 16 bit
* access
*/
+ nand_get_device(chip, mtd, FL_WRITING);
ofs += mtd->oobsize;
chip->ops.len = chip->ops.ooblen = 2;
chip->ops.datbuf = NULL;
chip->ops.ooboffs = chip->badblockpos & ~0x01;
ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ nand_release_device(mtd);
}
if (!ret)
mtd->ecc_stats.badblocks++;
+
return ret;
}
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat == -1)
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/**
+ * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @dataofs offset of requested data within the page
+ * @readlen data length
+ * @buf: buffer to store read data
+ */
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+{
+ int start_step, end_step, num_steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p;
+ int data_col_addr, i, gaps = 0;
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+
+ /* Column address wihin the page aligned to ECC size (256bytes). */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+
+ /* Data size aligned to ECC ecc.size*/
+ datafrag_len = num_steps * chip->ecc.size;
+ eccfrag_len = num_steps * chip->ecc.bytes;
+
+ data_col_addr = start_step * chip->ecc.size;
+ /* If we read not a page aligned data */
+ if (data_col_addr != 0)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+
+ p = bufpoi + data_col_addr;
+ chip->read_buf(mtd, p, datafrag_len);
+
+ /* Calculate ECC */
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+ chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+ /* The performance is faster if to position offsets
+ according to ecc.pos. Let make sure here that
+ there are no gaps in ecc positions */
+ for (i = 0; i < eccfrag_len - 1; i++) {
+ if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
+ eccpos[i + start_step * chip->ecc.bytes + 1]) {
+ gaps = 1;
+ break;
+ }
+ }
+ if (gaps) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ } else {
+ /* send the command to read the particular ecc bytes */
+ /* take care about buswidth alignment in read_buf */
+ aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
+ aligned_len = eccfrag_len;
+ if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
+ aligned_len++;
+ if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
+ aligned_len++;
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
+ chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+ }
+
+ for (i = 0; i < eccfrag_len; i++)
+ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+
+ p = bufpoi + data_col_addr;
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+ if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
chip->read_buf(mtd, oob, eccbytes);
stat = chip->ecc.correct(mtd, p, oob, NULL);
- if (stat == -1)
+ if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
/* Now read the page into the buffer */
if (unlikely(ops->mode == MTD_OOB_RAW))
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+ else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
+ ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi);
if (ret < 0)
/* Transfer not aligned data */
if (!aligned) {
- chip->pagebuf = realpage;
+ if (!NAND_SUBPAGE_READ(chip) && !oob)
+ chip->pagebuf = realpage;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
erase_exit:
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
nand_release_device(mtd);
+ /* Do call back function */
+ if (!ret)
+ mtd_erase_callback(instr);
+
/*
* If BBT requires refresh and erase was successful, rewrite any
* selected bad block tables
{
struct nand_flash_dev *type = NULL;
int i, dev_id, maf_idx;
+ int tmp_id, tmp_manf;
/* Select the device */
chip->select_chip(mtd, 0);
*maf_id = chip->read_byte(mtd);
dev_id = chip->read_byte(mtd);
+ /* Try again to make sure, as some systems the bus-hold or other
+ * interface concerns can cause random data which looks like a
+ * possibly credible NAND flash to appear. If the two results do
+ * not match, ignore the device completely.
+ */
+
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+
+ tmp_manf = chip->read_byte(mtd);
+ tmp_id = chip->read_byte(mtd);
+
+ if (tmp_manf != *maf_id || tmp_id != dev_id) {
+ printk(KERN_INFO "%s: second ID read did not match "
+ "%02x,%02x against %02x,%02x\n", __func__,
+ *maf_id, dev_id, tmp_manf, tmp_id);
+ return ERR_PTR(-ENODEV);
+ }
+
/* Lookup the flash id */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (dev_id == nand_flash_ids[i].id) {
chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
+ chip->ecc.read_subpage = nand_read_subpage;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
*
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
*
- * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
*
- * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
- *
* This file 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 or (at your option) any
#include <linux/mtd/nand_ecc.h>
#endif
-#include<linux/mtd/mtd.h>
+#include <asm/errno.h>
+#include <linux/mtd/mtd.h>
/*
* NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(),
if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
return 1;
- return -1;
+ return -EBADMSG;
}
/* XXX U-BOOT XXX */
*
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
*
- * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
+ {NAND_MFR_AMD, "AMD"},
{0x0, "Unknown"}
};
MTDDEBUG (MTD_DEBUG_LEVEL3,
"onenand_erase: start = 0x%08x, len = %i\n",
- (unsigned int)instr->addr, (unsigned int)ins tr->len);
+ (unsigned int)instr->addr, (unsigned int)instr->len);
block_size = (1 << this->erase_shift);
/*
- * $Id: blktrans.h,v 1.6 2005/11/07 11:14:54 gleixner Exp $
- *
* (C) 2003 David Woodhouse <dwmw2@infradead.org>
*
* Interface to Linux block layer for MTD 'translation layers'.
* Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
* Copyright (C) 2002-2003 SnapGear Inc
*
- * $Id: doc2000.h,v 1.25 2005/11/07 11:14:54 gleixner Exp $
- *
* Released under GPL
*/
/*
- * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $
- *
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
*
* Released under GPL
u_int32_t oobavail; /* Available OOB bytes per block */
/* Kernel-only stuff starts here. */
- char *name;
+ const char *name;
int index;
/* ecc layout structure pointer - read only ! */
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
+ /*
+ * Erase is an asynchronous operation. Device drivers are supposed
+ * to call instr->callback() whenever the operation completes, even
+ * if it completes with a failure.
+ * Callers are supposed to pass a callback function and wait for it
+ * to be called before writing to the block.
+ */
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* This stuff for eXecute-In-Place */
- int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
+ /* phys is optional and may be set to NULL */
+ int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void **virt, phys_addr_t *phys);
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
- void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
+ void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+ /* In blackbox flight recorder like scenarios we want to make successful
+ writes in interrupt context. panic_write() is only intended to be
+ called when its known the kernel is about to panic and we need the
+ write to succeed. Since the kernel is not going to be running for much
+ longer, this function can break locks and delay to ensure the write
+ succeeds (but not sleep). */
+
+ int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
int (*read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
printk(KERN_INFO args); \
} while(0)
#else /* CONFIG_MTD_DEBUG */
-#define MTDDEBUG(n, args...) do { } while(0)
+#define MTDDEBUG(n, args...) \
+ do { \
+ if (0) \
+ printk(KERN_INFO args); \
+ } while(0)
#endif /* CONFIG_MTD_DEBUG */
#endif /* __MTD_MTD_H__ */
/*
* linux/include/linux/mtd/nand.h
*
- * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+ * Copyright (c) 2000 David Woodhouse <dwmw2@infradead.org>
* Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de>
*
- * $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+/* Large page NAND with SOFT_ECC should support subpage reads */
+#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT) \
+ && (chip->page_shift > 9))
/* Mask to zero out the chip options, which come from the id table */
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
int (*read_page)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf);
+ int (*read_subpage)(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint32_t offs, uint32_t len,
+ uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
#define NAND_MFR_STMICRO 0x20
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
+#define NAND_MFR_AMD 0x01
/**
* struct nand_flash_dev - NAND Flash Device ID Structure
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
- * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
/*
- * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
- *
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
*/
int NFTL_mount(struct NFTLrecord *s);
int NFTL_formatblock(struct NFTLrecord *s, int block);
+int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+ size_t *retlen, uint8_t *buf);
+int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+ size_t *retlen, uint8_t *buf);
+
#ifndef NFTL_MAJOR
#define NFTL_MAJOR 93
#endif
* @param page_shift [INTERN] number of address bits in a page
* @param ppb_shift [INTERN] number of address bits in a pages per block
* @param page_mask [INTERN] a page per block mask
+ * @param writesize [INTERN] a real page size
* @param bufferam_index [INTERN] BufferRAM index
* @param bufferam [INTERN] BufferRAM info
* @param readw [REPLACEABLE] hardware specific function for read short
/*
* Device ID Register F001h (R)
*/
+#define ONENAND_DEVICE_DENSITY_MASK (0xf)
#define ONENAND_DEVICE_DENSITY_SHIFT (4)
#define ONENAND_DEVICE_IS_DDP (1 << 3)
#define ONENAND_DEVICE_IS_DEMUX (1 << 2)
#define ONENAND_DEVICE_VCC_MASK (0x3)
#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
+#define ONENAND_DEVICE_DENSITY_1Gb (0x003)
+#define ONENAND_DEVICE_DENSITY_2Gb (0x004)
+#define ONENAND_DEVICE_DENSITY_4Gb (0x005)
/*
* Version ID Register F002h (R)
#define ONENAND_CMD_READOOB (0x13)
#define ONENAND_CMD_PROG (0x80)
#define ONENAND_CMD_PROGOOB (0x1A)
+#define ONENAND_CMD_2X_PROG (0x7D)
+#define ONENAND_CMD_2X_CACHE_PROG (0x7F)
#define ONENAND_CMD_UNLOCK (0x23)
#define ONENAND_CMD_LOCK (0x2A)
#define ONENAND_CMD_LOCK_TIGHT (0x2C)