2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* drivers/mtd/nand.c
|
|
|
|
*
|
|
|
|
* Overview:
|
|
|
|
* This is the generic MTD driver for NAND flash devices. It should be
|
|
|
|
* capable of working with almost all NAND chips currently available.
|
|
|
|
* Basic support for AG-AND chips is provided.
|
2005-11-07 18:15:49 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Additional technical information is available on
|
2007-07-28 18:07:16 +07:00
|
|
|
* http://www.linux-mtd.infradead.org/doc/nand.html
|
2005-11-07 18:15:49 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
2006-05-24 17:07:37 +07:00
|
|
|
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2006-05-24 17:07:37 +07:00
|
|
|
* Credits:
|
2005-11-07 18:15:49 +07:00
|
|
|
* David Woodhouse for adding multichip support
|
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Aleph One Ltd. and Toby Churchill Ltd. for supporting the
|
|
|
|
* rework for 2K page size chips
|
|
|
|
*
|
2006-05-24 17:07:37 +07:00
|
|
|
* TODO:
|
2005-04-17 05:20:36 +07:00
|
|
|
* Enable cached programming for 2k page size chips
|
|
|
|
* Check, if mtd->ecctype should be set to MTD_ECC_HW
|
|
|
|
* 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.
|
2007-07-23 20:06:50 +07:00
|
|
|
* BBT table is not serialized, has to be fixed
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2006-05-14 07:20:46 +07:00
|
|
|
#include <linux/module.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/errno.h>
|
2006-05-23 16:54:38 +07:00
|
|
|
#include <linux/err.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/mtd/mtd.h>
|
|
|
|
#include <linux/mtd/nand.h>
|
|
|
|
#include <linux/mtd/nand_ecc.h>
|
|
|
|
#include <linux/mtd/compatmac.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/bitops.h>
|
2006-03-31 17:31:14 +07:00
|
|
|
#include <linux/leds.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
|
|
#include <linux/mtd/partitions.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Define default oob placement schemes for large and small page devices */
|
2006-05-28 03:16:10 +07:00
|
|
|
static struct nand_ecclayout nand_oob_8 = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.eccbytes = 3,
|
|
|
|
.eccpos = {0, 1, 2},
|
2006-05-28 03:16:10 +07:00
|
|
|
.oobfree = {
|
|
|
|
{.offset = 3,
|
|
|
|
.length = 2},
|
|
|
|
{.offset = 6,
|
|
|
|
.length = 2}}
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-05-28 03:16:10 +07:00
|
|
|
static struct nand_ecclayout nand_oob_16 = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.eccbytes = 6,
|
|
|
|
.eccpos = {0, 1, 2, 3, 6, 7},
|
2006-05-28 03:16:10 +07:00
|
|
|
.oobfree = {
|
|
|
|
{.offset = 8,
|
|
|
|
. length = 8}}
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-05-28 03:16:10 +07:00
|
|
|
static struct nand_ecclayout nand_oob_64 = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.eccbytes = 24,
|
|
|
|
.eccpos = {
|
2006-05-14 00:07:53 +07:00
|
|
|
40, 41, 42, 43, 44, 45, 46, 47,
|
|
|
|
48, 49, 50, 51, 52, 53, 54, 55,
|
|
|
|
56, 57, 58, 59, 60, 61, 62, 63},
|
2006-05-28 03:16:10 +07:00
|
|
|
.oobfree = {
|
|
|
|
{.offset = 2,
|
|
|
|
.length = 38}}
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2007-12-12 23:27:03 +07:00
|
|
|
static struct nand_ecclayout nand_oob_128 = {
|
|
|
|
.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 = {
|
|
|
|
{.offset = 2,
|
|
|
|
.length = 78}}
|
|
|
|
};
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
|
2006-05-23 16:50:56 +07:00
|
|
|
int new_state);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
struct mtd_oob_ops *ops);
|
|
|
|
|
2006-05-24 04:48:57 +07:00
|
|
|
/*
|
2008-02-03 22:22:34 +07:00
|
|
|
* For devices which display every fart in the system on a separate LED. Is
|
2006-05-24 04:48:57 +07:00
|
|
|
* compiled away when LED support is disabled.
|
|
|
|
*/
|
|
|
|
DEFINE_LED_TRIGGER(nand_led_trigger);
|
|
|
|
|
2010-02-03 15:42:24 +07:00
|
|
|
static int check_offs_len(struct mtd_info *mtd,
|
|
|
|
loff_t ofs, uint64_t len)
|
|
|
|
{
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Start address must align on block boundary */
|
|
|
|
if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Length must align on block boundary */
|
|
|
|
if (len & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
|
|
|
|
__func__);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not allow past end of device */
|
|
|
|
if (ofs + len > mtd->size) {
|
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n",
|
|
|
|
__func__);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* nand_release_device - [GENERIC] release chip
|
|
|
|
* @mtd: MTD device structure
|
2005-11-07 18:15:49 +07:00
|
|
|
*
|
|
|
|
* Deselect, release chip lock and wake up anyone waiting on the device
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static void nand_release_device(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* De-select the NAND device */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, -1);
|
2005-06-01 02:39:20 +07:00
|
|
|
|
2006-05-23 16:37:03 +07:00
|
|
|
/* Release the controller and the chip */
|
2006-05-24 17:07:37 +07:00
|
|
|
spin_lock(&chip->controller->lock);
|
|
|
|
chip->controller->active = NULL;
|
|
|
|
chip->state = FL_READY;
|
|
|
|
wake_up(&chip->controller->wq);
|
|
|
|
spin_unlock(&chip->controller->lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_read_byte - [DEFAULT] read one byte from the chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*
|
|
|
|
* Default read function for 8bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static uint8_t nand_read_byte(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
return readb(chip->IO_ADDR_R);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*
|
2005-11-07 18:15:49 +07:00
|
|
|
* Default read function for 16bit buswith with
|
2005-04-17 05:20:36 +07:00
|
|
|
* endianess conversion
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static uint8_t nand_read_byte16(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_read_word - [DEFAULT] read one word from the chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*
|
2005-11-07 18:15:49 +07:00
|
|
|
* Default read function for 16bit buswith without
|
2005-04-17 05:20:36 +07:00
|
|
|
* endianess conversion
|
|
|
|
*/
|
|
|
|
static u16 nand_read_word(struct mtd_info *mtd)
|
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
return readw(chip->IO_ADDR_R);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_select_chip - [DEFAULT] control CE line
|
|
|
|
* @mtd: MTD device structure
|
2006-06-29 11:48:27 +07:00
|
|
|
* @chipnr: chipnumber to select, -1 for deselect
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Default select function for 1 chip devices.
|
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
|
|
|
|
switch (chipnr) {
|
2005-04-17 05:20:36 +07:00
|
|
|
case -1:
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_write_buf - [DEFAULT] write buffer to chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: data buffer
|
|
|
|
* @len: number of bytes to write
|
|
|
|
*
|
|
|
|
* Default write function for 8bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
writeb(buf[i], chip->IO_ADDR_W);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-07 18:15:49 +07:00
|
|
|
* nand_read_buf - [DEFAULT] read chip data into buffer
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: buffer to store date
|
|
|
|
* @len: number of bytes to read
|
|
|
|
*
|
|
|
|
* Default read function for 8bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
buf[i] = readb(chip->IO_ADDR_R);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-07 18:15:49 +07:00
|
|
|
* nand_verify_buf - [DEFAULT] Verify chip data against buffer
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: buffer containing the data to compare
|
|
|
|
* @len: number of bytes to compare
|
|
|
|
*
|
|
|
|
* Default verify function for 8bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
if (buf[i] != readb(chip->IO_ADDR_R))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_write_buf16 - [DEFAULT] write buffer to chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: data buffer
|
|
|
|
* @len: number of bytes to write
|
|
|
|
*
|
|
|
|
* Default write function for 16bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
u16 *p = (u16 *) buf;
|
|
|
|
len >>= 1;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
writew(p[i], chip->IO_ADDR_W);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-07 18:15:49 +07:00
|
|
|
* nand_read_buf16 - [DEFAULT] read chip data into buffer
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: buffer to store date
|
|
|
|
* @len: number of bytes to read
|
|
|
|
*
|
|
|
|
* Default read function for 16bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
u16 *p = (u16 *) buf;
|
|
|
|
len >>= 1;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
p[i] = readw(chip->IO_ADDR_R);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-07 18:15:49 +07:00
|
|
|
* nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @buf: buffer containing the data to compare
|
|
|
|
* @len: number of bytes to compare
|
|
|
|
*
|
|
|
|
* Default verify function for 16bit buswith
|
|
|
|
*/
|
2006-05-23 16:52:35 +07:00
|
|
|
static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
u16 *p = (u16 *) buf;
|
|
|
|
len >>= 1;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 0; i < len; i++)
|
2006-05-24 17:07:37 +07:00
|
|
|
if (p[i] != readw(chip->IO_ADDR_R))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @ofs: offset from device start
|
|
|
|
* @getchip: 0, if the chip is already selected
|
|
|
|
*
|
2005-11-07 18:15:49 +07:00
|
|
|
* Check, if the block is bad.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
|
|
|
{
|
|
|
|
int page, chipnr, res = 0;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
u16 bad;
|
|
|
|
|
2007-05-03 13:39:37 +07:00
|
|
|
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (getchip) {
|
2006-05-24 17:07:37 +07:00
|
|
|
chipnr = (int)(ofs >> chip->chip_shift);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_get_device(chip, mtd, FL_READING);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Select the NAND device */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, chipnr);
|
2007-05-03 13:39:37 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_BUSWIDTH_16) {
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
|
2007-05-03 13:39:37 +07:00
|
|
|
page);
|
2006-05-24 17:07:37 +07:00
|
|
|
bad = cpu_to_le16(chip->read_word(mtd));
|
|
|
|
if (chip->badblockpos & 0x1)
|
2005-11-02 23:54:46 +07:00
|
|
|
bad >>= 8;
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((bad & 0xFF) != 0xff)
|
|
|
|
res = 1;
|
|
|
|
} else {
|
2007-05-03 13:39:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->read_byte(mtd) != 0xff)
|
2005-04-17 05:20:36 +07:00
|
|
|
res = 1;
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (getchip)
|
2005-04-17 05:20:36 +07:00
|
|
|
nand_release_device(mtd);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_default_block_markbad - [DEFAULT] mark a block bad
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @ofs: offset from device start
|
|
|
|
*
|
|
|
|
* This is the default implementation, which can be overridden by
|
|
|
|
* a hardware specific driver.
|
|
|
|
*/
|
|
|
|
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-23 16:52:35 +07:00
|
|
|
uint8_t buf[2] = { 0, 0 };
|
2006-05-30 05:37:34 +07:00
|
|
|
int block, ret;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Get block number */
|
2007-04-18 00:50:59 +07:00
|
|
|
block = (int)(ofs >> chip->bbt_erase_shift);
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->bbt)
|
|
|
|
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Do we have a flash based bad block table ? */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_USE_FLASH_BBT)
|
2006-05-30 05:37:34 +07:00
|
|
|
ret = nand_update_bbt(mtd, ofs);
|
|
|
|
else {
|
|
|
|
/* We write two bytes, so we dont have to mess with 16 bit
|
|
|
|
* access
|
|
|
|
*/
|
2007-07-23 20:06:50 +07:00
|
|
|
nand_get_device(chip, mtd, FL_WRITING);
|
2006-05-30 05:37:34 +07:00
|
|
|
ofs += mtd->oobsize;
|
2006-10-23 14:33:34 +07:00
|
|
|
chip->ops.len = chip->ops.ooblen = 2;
|
2006-05-30 05:37:34 +07:00
|
|
|
chip->ops.datbuf = NULL;
|
|
|
|
chip->ops.oobbuf = buf;
|
|
|
|
chip->ops.ooboffs = chip->badblockpos & ~0x01;
|
|
|
|
|
|
|
|
ret = nand_do_write_oob(mtd, ofs, &chip->ops);
|
2007-07-23 20:06:50 +07:00
|
|
|
nand_release_device(mtd);
|
2006-05-30 05:37:34 +07:00
|
|
|
}
|
|
|
|
if (!ret)
|
|
|
|
mtd->ecc_stats.badblocks++;
|
2007-07-23 20:06:50 +07:00
|
|
|
|
2006-05-30 05:37:34 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/**
|
2005-04-17 05:20:36 +07:00
|
|
|
* nand_check_wp - [GENERIC] check if the chip is write protected
|
|
|
|
* @mtd: MTD device structure
|
2005-11-07 18:15:49 +07:00
|
|
|
* Check, if the device is write protected
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2005-11-07 18:15:49 +07:00
|
|
|
* The function expects, that the device is already selected
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static int nand_check_wp(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Check the WP bit */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
|
|
return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @ofs: offset from device start
|
|
|
|
* @getchip: 0, if the chip is already selected
|
|
|
|
* @allowbbt: 1, if its allowed to access the bbt area
|
|
|
|
*
|
|
|
|
* Check, if the block is bad. Either by reading the bad block table or
|
|
|
|
* calling of the scan function.
|
|
|
|
*/
|
2006-05-23 16:50:56 +07:00
|
|
|
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
|
|
|
int allowbbt)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->bbt)
|
|
|
|
return chip->block_bad(mtd, ofs, getchip);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Return info from the table */
|
2006-05-14 00:07:53 +07:00
|
|
|
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
/**
|
|
|
|
* panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @timeo: Timeout
|
|
|
|
*
|
|
|
|
* Helper function for nand_wait_ready used when needing to wait in interrupt
|
|
|
|
* context.
|
|
|
|
*/
|
|
|
|
static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
|
|
|
|
{
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Wait for the device to get ready */
|
|
|
|
for (i = 0; i < timeo; i++) {
|
|
|
|
if (chip->dev_ready(mtd))
|
|
|
|
break;
|
|
|
|
touch_softlockup_watchdog();
|
|
|
|
mdelay(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/*
|
2005-02-23 04:56:49 +07:00
|
|
|
* Wait for the ready pin, after a command
|
|
|
|
* The timeout is catched later.
|
|
|
|
*/
|
2006-09-25 23:05:24 +07:00
|
|
|
void nand_wait_ready(struct mtd_info *mtd)
|
2005-02-23 04:56:49 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-14 00:07:53 +07:00
|
|
|
unsigned long timeo = jiffies + 2;
|
2005-02-23 04:56:49 +07:00
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
/* 400ms timeout */
|
|
|
|
if (in_interrupt() || oops_in_progress)
|
|
|
|
return panic_nand_wait_ready(mtd, 400);
|
|
|
|
|
2006-03-31 17:31:14 +07:00
|
|
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
2005-02-23 04:56:49 +07:00
|
|
|
/* wait until command is processed or timeout occures */
|
|
|
|
do {
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->dev_ready(mtd))
|
2006-03-31 17:31:14 +07:00
|
|
|
break;
|
2005-09-07 05:16:27 +07:00
|
|
|
touch_softlockup_watchdog();
|
2005-11-07 18:15:49 +07:00
|
|
|
} while (time_before(jiffies, timeo));
|
2006-03-31 17:31:14 +07:00
|
|
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
2005-02-23 04:56:49 +07:00
|
|
|
}
|
2006-09-25 23:05:24 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nand_wait_ready);
|
2005-02-23 04:56:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* nand_command - [DEFAULT] Send command to NAND device
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @command: the command to be sent
|
|
|
|
* @column: the column address for this command, -1 if none
|
|
|
|
* @page_addr: the page address for this command, -1 if none
|
|
|
|
*
|
|
|
|
* Send command to NAND device. This function is used for small page
|
|
|
|
* devices (256/512 Bytes per page)
|
|
|
|
*/
|
2006-05-24 04:25:53 +07:00
|
|
|
static void nand_command(struct mtd_info *mtd, unsigned int command,
|
|
|
|
int column, int page_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
register struct nand_chip *chip = mtd->priv;
|
2006-05-24 04:25:53 +07:00
|
|
|
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write out the command to the device.
|
|
|
|
*/
|
|
|
|
if (command == NAND_CMD_SEQIN) {
|
|
|
|
int readcmd;
|
|
|
|
|
2006-05-23 04:18:05 +07:00
|
|
|
if (column >= mtd->writesize) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* OOB area */
|
2006-05-23 04:18:05 +07:00
|
|
|
column -= mtd->writesize;
|
2005-04-17 05:20:36 +07:00
|
|
|
readcmd = NAND_CMD_READOOB;
|
|
|
|
} else if (column < 256) {
|
|
|
|
/* First 256 bytes --> READ0 */
|
|
|
|
readcmd = NAND_CMD_READ0;
|
|
|
|
} else {
|
|
|
|
column -= 256;
|
|
|
|
readcmd = NAND_CMD_READ1;
|
|
|
|
}
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, readcmd, ctrl);
|
2006-05-24 04:25:53 +07:00
|
|
|
ctrl &= ~NAND_CTRL_CHANGE;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, command, ctrl);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 04:25:53 +07:00
|
|
|
/*
|
|
|
|
* Address cycle, when necessary
|
|
|
|
*/
|
|
|
|
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
|
|
|
|
/* Serially input address */
|
|
|
|
if (column != -1) {
|
|
|
|
/* Adjust columns for 16 bit buswidth */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_BUSWIDTH_16)
|
2006-05-24 04:25:53 +07:00
|
|
|
column >>= 1;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, column, ctrl);
|
2006-05-24 04:25:53 +07:00
|
|
|
ctrl &= ~NAND_CTRL_CHANGE;
|
|
|
|
}
|
|
|
|
if (page_addr != -1) {
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, page_addr, ctrl);
|
2006-05-24 04:25:53 +07:00
|
|
|
ctrl &= ~NAND_CTRL_CHANGE;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
|
2006-05-24 04:25:53 +07:00
|
|
|
/* One more address cycle for devices > 32MiB */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->chipsize > (32 << 20))
|
|
|
|
chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* program and erase have their own busy handlers
|
2005-04-17 05:20:36 +07:00
|
|
|
* status and sequential in needs no delay
|
2006-05-14 00:07:53 +07:00
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (command) {
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case NAND_CMD_PAGEPROG:
|
|
|
|
case NAND_CMD_ERASE1:
|
|
|
|
case NAND_CMD_ERASE2:
|
|
|
|
case NAND_CMD_SEQIN:
|
|
|
|
case NAND_CMD_STATUS:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NAND_CMD_RESET:
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->dev_ready)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2006-05-24 17:07:37 +07:00
|
|
|
udelay(chip->chip_delay);
|
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
|
2006-05-24 04:25:53 +07:00
|
|
|
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
2006-05-25 03:57:09 +07:00
|
|
|
chip->cmd_ctrl(mtd,
|
|
|
|
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
2006-05-24 17:07:37 +07:00
|
|
|
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
/* This applies to read commands */
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2005-11-07 18:15:49 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* If we don't have access to the busy pin, we apply the given
|
|
|
|
* command delay
|
2006-05-14 00:07:53 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->dev_ready) {
|
|
|
|
udelay(chip->chip_delay);
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2005-11-07 18:15:49 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
/* Apply this short delay always to ensure that we do wait tWB in
|
|
|
|
* any case on any machine. */
|
2006-05-14 00:07:53 +07:00
|
|
|
ndelay(100);
|
2005-02-23 04:56:49 +07:00
|
|
|
|
|
|
|
nand_wait_ready(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_command_lp - [DEFAULT] Send command to NAND large page device
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @command: the command to be sent
|
|
|
|
* @column: the column address for this command, -1 if none
|
|
|
|
* @page_addr: the page address for this command, -1 if none
|
|
|
|
*
|
2006-05-24 04:25:53 +07:00
|
|
|
* Send command to NAND device. This is the version for the new large page
|
|
|
|
* devices We dont have the separate regions as we have in the small page
|
|
|
|
* devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-05-24 04:25:53 +07:00
|
|
|
static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|
|
|
int column, int page_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
register struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Emulate NAND_CMD_READOOB */
|
|
|
|
if (command == NAND_CMD_READOOB) {
|
2006-05-23 04:18:05 +07:00
|
|
|
column += mtd->writesize;
|
2005-04-17 05:20:36 +07:00
|
|
|
command = NAND_CMD_READ0;
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 04:25:53 +07:00
|
|
|
/* Command latch cycle */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, command & 0xff,
|
2006-05-24 04:25:53 +07:00
|
|
|
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (column != -1 || page_addr != -1) {
|
2006-05-24 04:25:53 +07:00
|
|
|
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Serially input address */
|
|
|
|
if (column != -1) {
|
|
|
|
/* Adjust columns for 16 bit buswidth */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_BUSWIDTH_16)
|
2005-04-17 05:20:36 +07:00
|
|
|
column >>= 1;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, column, ctrl);
|
2006-05-24 04:25:53 +07:00
|
|
|
ctrl &= ~NAND_CTRL_CHANGE;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, column >> 8, ctrl);
|
2005-11-07 18:15:49 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (page_addr != -1) {
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, page_addr, ctrl);
|
|
|
|
chip->cmd_ctrl(mtd, page_addr >> 8,
|
2006-05-24 04:25:53 +07:00
|
|
|
NAND_NCE | NAND_ALE);
|
2005-04-17 05:20:36 +07:00
|
|
|
/* One more address cycle for devices > 128MiB */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->chipsize > (128 << 20))
|
|
|
|
chip->cmd_ctrl(mtd, page_addr >> 16,
|
2006-05-24 04:25:53 +07:00
|
|
|
NAND_NCE | NAND_ALE);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* program and erase have their own busy handlers
|
2005-01-18 01:35:25 +07:00
|
|
|
* status, sequential in, and deplete1 need no delay
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (command) {
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case NAND_CMD_CACHEDPROG:
|
|
|
|
case NAND_CMD_PAGEPROG:
|
|
|
|
case NAND_CMD_ERASE1:
|
|
|
|
case NAND_CMD_ERASE2:
|
|
|
|
case NAND_CMD_SEQIN:
|
2006-06-21 01:05:05 +07:00
|
|
|
case NAND_CMD_RNDIN:
|
2005-04-17 05:20:36 +07:00
|
|
|
case NAND_CMD_STATUS:
|
2005-01-18 01:35:25 +07:00
|
|
|
case NAND_CMD_DEPLETE1:
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
/*
|
|
|
|
* read error status commands require only a short delay
|
|
|
|
*/
|
2005-01-18 01:35:25 +07:00
|
|
|
case NAND_CMD_STATUS_ERROR:
|
|
|
|
case NAND_CMD_STATUS_ERROR0:
|
|
|
|
case NAND_CMD_STATUS_ERROR1:
|
|
|
|
case NAND_CMD_STATUS_ERROR2:
|
|
|
|
case NAND_CMD_STATUS_ERROR3:
|
2006-05-24 17:07:37 +07:00
|
|
|
udelay(chip->chip_delay);
|
2005-01-18 01:35:25 +07:00
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
case NAND_CMD_RESET:
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->dev_ready)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2006-05-24 17:07:37 +07:00
|
|
|
udelay(chip->chip_delay);
|
2006-05-25 03:57:09 +07:00
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
|
|
|
|
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
|
|
|
NAND_NCE | NAND_CTRL_CHANGE);
|
2006-05-24 17:07:37 +07:00
|
|
|
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
case NAND_CMD_RNDOUT:
|
|
|
|
/* No ready / busy check necessary */
|
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
|
|
|
|
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
|
|
|
NAND_NCE | NAND_CTRL_CHANGE);
|
|
|
|
return;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case NAND_CMD_READ0:
|
2006-05-25 03:57:09 +07:00
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
|
|
|
|
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
|
|
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
|
|
|
NAND_NCE | NAND_CTRL_CHANGE);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
/* This applies to read commands */
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2005-11-07 18:15:49 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* If we don't have access to the busy pin, we apply the given
|
|
|
|
* command delay
|
2006-05-14 00:07:53 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->dev_ready) {
|
|
|
|
udelay(chip->chip_delay);
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2005-11-07 18:15:49 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-02-23 04:56:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Apply this short delay always to ensure that we do wait tWB in
|
|
|
|
* any case on any machine. */
|
2006-05-14 00:07:53 +07:00
|
|
|
ndelay(100);
|
2005-02-23 04:56:49 +07:00
|
|
|
|
|
|
|
nand_wait_ready(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
/**
|
|
|
|
* panic_nand_get_device - [GENERIC] Get chip for selected access
|
|
|
|
* @chip: the nand chip descriptor
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @new_state: the state which is requested
|
|
|
|
*
|
|
|
|
* Used when in panic, no locks are taken.
|
|
|
|
*/
|
|
|
|
static void panic_nand_get_device(struct nand_chip *chip,
|
|
|
|
struct mtd_info *mtd, int new_state)
|
|
|
|
{
|
|
|
|
/* Hardware controller shared among independend devices */
|
|
|
|
chip->controller->active = chip;
|
|
|
|
chip->state = new_state;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* nand_get_device - [GENERIC] Get chip for selected access
|
2006-06-29 11:48:27 +07:00
|
|
|
* @chip: the nand chip descriptor
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
2005-11-07 18:15:49 +07:00
|
|
|
* @new_state: the state which is requested
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Get the device and lock it for exclusive access
|
|
|
|
*/
|
2006-05-23 16:50:56 +07:00
|
|
|
static int
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
spinlock_t *lock = &chip->controller->lock;
|
|
|
|
wait_queue_head_t *wq = &chip->controller->wq;
|
2006-05-14 00:07:53 +07:00
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
|
retry:
|
2005-06-01 02:39:20 +07:00
|
|
|
spin_lock(lock);
|
|
|
|
|
2009-07-09 22:11:22 +07:00
|
|
|
/* Hardware controller shared among independent devices */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->controller->active)
|
|
|
|
chip->controller->active = chip;
|
2006-05-23 16:37:03 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->controller->active == chip && chip->state == FL_READY) {
|
|
|
|
chip->state = new_state;
|
2005-06-01 02:39:20 +07:00
|
|
|
spin_unlock(lock);
|
2005-09-15 20:58:53 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (new_state == FL_PM_SUSPENDED) {
|
2009-11-18 05:45:49 +07:00
|
|
|
if (chip->controller->active->state == FL_PM_SUSPENDED) {
|
|
|
|
chip->state = FL_PM_SUSPENDED;
|
|
|
|
spin_unlock(lock);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
spin_unlock(lock);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2005-06-01 02:39:20 +07:00
|
|
|
}
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
add_wait_queue(wq, &wait);
|
|
|
|
spin_unlock(lock);
|
|
|
|
schedule();
|
|
|
|
remove_wait_queue(wq, &wait);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
/**
|
|
|
|
* panic_nand_wait - [GENERIC] wait until the command is done
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @chip: NAND chip structure
|
|
|
|
* @timeo: Timeout
|
|
|
|
*
|
|
|
|
* Wait for command done. This is a helper function for nand_wait used when
|
|
|
|
* we are in interrupt context. May happen when in panic and trying to write
|
|
|
|
* an oops trough mtdoops.
|
|
|
|
*/
|
|
|
|
static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
unsigned long timeo)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < timeo; i++) {
|
|
|
|
if (chip->dev_ready) {
|
|
|
|
if (chip->dev_ready(mtd))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mdelay(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* nand_wait - [DEFAULT] wait until the command is done
|
|
|
|
* @mtd: MTD device structure
|
2006-06-29 11:48:27 +07:00
|
|
|
* @chip: NAND chip structure
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Wait for command done. This applies to erase and program only
|
2005-11-07 18:15:49 +07:00
|
|
|
* Erase can take up to 400ms and program up to 20ms according to
|
2005-04-17 05:20:36 +07:00
|
|
|
* general NAND and SmartMedia specs
|
2006-06-29 11:48:27 +07:00
|
|
|
*/
|
2006-06-21 01:05:05 +07:00
|
|
|
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
unsigned long timeo = jiffies;
|
2006-06-21 01:05:05 +07:00
|
|
|
int status, state = chip->state;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (state == FL_ERASING)
|
2006-05-14 00:07:53 +07:00
|
|
|
timeo += (HZ * 400) / 1000;
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
2006-05-14 00:07:53 +07:00
|
|
|
timeo += (HZ * 20) / 1000;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-31 17:31:14 +07:00
|
|
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Apply this short delay always to ensure that we do wait tWB in
|
|
|
|
* any case on any machine. */
|
2006-05-14 00:07:53 +07:00
|
|
|
ndelay(100);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
|
2005-11-07 18:15:49 +07:00
|
|
|
else
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
if (in_interrupt() || oops_in_progress)
|
|
|
|
panic_nand_wait(mtd, chip, timeo);
|
|
|
|
else {
|
|
|
|
while (time_before(jiffies, timeo)) {
|
|
|
|
if (chip->dev_ready) {
|
|
|
|
if (chip->dev_ready(mtd))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cond_resched();
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2006-03-31 17:31:14 +07:00
|
|
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
status = (int)chip->read_byte(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
/**
|
|
|
|
* nand_read_page_raw - [Intern] read raw page data without ecc
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
*
|
|
|
|
* Not for syndrome calculating ecc controllers, which use a special oob layout
|
2006-05-29 08:26:58 +07:00
|
|
|
*/
|
|
|
|
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
2009-09-19 02:51:46 +07:00
|
|
|
uint8_t *buf, int page)
|
2006-05-29 08:26:58 +07:00
|
|
|
{
|
|
|
|
chip->read_buf(mtd, buf, mtd->writesize);
|
|
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
/**
|
|
|
|
* nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
*
|
|
|
|
* We need a special oob layout and handling even when OOB isn't used.
|
|
|
|
*/
|
|
|
|
static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
2009-09-19 02:51:46 +07:00
|
|
|
uint8_t *buf, int page)
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
{
|
|
|
|
int eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
uint8_t *oob = chip->oob_poi;
|
|
|
|
int steps, size;
|
|
|
|
|
|
|
|
for (steps = chip->ecc.steps; steps > 0; steps--) {
|
|
|
|
chip->read_buf(mtd, buf, eccsize);
|
|
|
|
buf += eccsize;
|
|
|
|
|
|
|
|
if (chip->ecc.prepad) {
|
|
|
|
chip->read_buf(mtd, oob, chip->ecc.prepad);
|
|
|
|
oob += chip->ecc.prepad;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
|
|
oob += eccbytes;
|
|
|
|
|
|
|
|
if (chip->ecc.postpad) {
|
|
|
|
chip->read_buf(mtd, oob, chip->ecc.postpad);
|
|
|
|
oob += chip->ecc.postpad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size = mtd->oobsize - (oob - chip->oob_poi);
|
|
|
|
if (size)
|
|
|
|
chip->read_buf(mtd, oob, size);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_read_page_swecc - [REPLACABLE] software ecc based page read function
|
2006-05-25 15:07:16 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
2005-01-24 10:07:46 +07:00
|
|
|
*/
|
2006-05-25 15:07:16 +07:00
|
|
|
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
2009-09-19 02:51:46 +07:00
|
|
|
uint8_t *buf, int page)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-25 15:07:16 +07:00
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
|
|
|
uint8_t *p = buf;
|
2006-09-25 23:08:04 +07:00
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
2007-05-29 01:17:54 +07:00
|
|
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2009-09-19 02:51:46 +07:00
|
|
|
chip->ecc.read_page_raw(mtd, chip, buf, page);
|
2006-05-25 15:07:16 +07:00
|
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
|
|
|
|
for (i = 0; i < chip->ecc.total; i++)
|
2006-05-26 23:52:08 +07:00
|
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
2006-05-25 15:07:16 +07:00
|
|
|
|
|
|
|
eccsteps = chip->ecc.steps;
|
|
|
|
p = buf;
|
|
|
|
|
|
|
|
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
int stat;
|
|
|
|
|
|
|
|
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
2007-10-18 04:33:23 +07:00
|
|
|
if (stat < 0)
|
2006-05-25 15:07:16 +07:00
|
|
|
mtd->ecc_stats.failed++;
|
|
|
|
else
|
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
|
|
}
|
|
|
|
return 0;
|
2005-04-05 01:56:32 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-05-15 23:23:18 +07:00
|
|
|
/**
|
|
|
|
* nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
2008-08-21 04:32:08 +07:00
|
|
|
* @data_offs: offset of requested data within the page
|
|
|
|
* @readlen: data length
|
|
|
|
* @bufpoi: buffer to store read data
|
2008-05-15 23:23:18 +07:00
|
|
|
*/
|
|
|
|
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 == -1)
|
|
|
|
mtd->ecc_stats.failed++;
|
|
|
|
else
|
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-01-24 10:07:46 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
|
2006-05-25 15:07:16 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
2005-01-24 10:07:46 +07:00
|
|
|
*
|
2006-05-25 15:07:16 +07:00
|
|
|
* Not for syndrome calculating ecc controllers which need a special oob layout
|
2005-01-24 10:07:46 +07:00
|
|
|
*/
|
2006-05-25 15:07:16 +07:00
|
|
|
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
2009-09-19 02:51:46 +07:00
|
|
|
uint8_t *buf, int page)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-25 15:07:16 +07:00
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
|
|
|
uint8_t *p = buf;
|
2006-09-25 23:08:04 +07:00
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
2007-05-29 01:17:54 +07:00
|
|
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
2006-05-25 15:07:16 +07:00
|
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
|
|
chip->read_buf(mtd, p, eccsize);
|
|
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-26 23:52:08 +07:00
|
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
for (i = 0; i < chip->ecc.total; i++)
|
2006-05-26 23:52:08 +07:00
|
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
eccsteps = chip->ecc.steps;
|
|
|
|
p = buf;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
int stat;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
2007-10-18 04:33:23 +07:00
|
|
|
if (stat < 0)
|
2006-05-25 15:07:16 +07:00
|
|
|
mtd->ecc_stats.failed++;
|
|
|
|
else
|
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-09-19 02:51:47 +07:00
|
|
|
/**
|
|
|
|
* nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
2009-09-19 02:51:47 +07:00
|
|
|
*
|
|
|
|
* Hardware ECC for large page chips, require OOB to be read first.
|
|
|
|
* For this ECC mode, the write_page method is re-used from ECC_HW.
|
|
|
|
* These methods read/write ECC from the OOB area, unlike the
|
|
|
|
* ECC_HW_SYNDROME support with multiple ECC steps, follows the
|
|
|
|
* "infix ECC" scheme and reads/writes ECC from the data area, by
|
|
|
|
* overwriting the NAND manufacturer bad block markings.
|
|
|
|
*/
|
|
|
|
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|
|
|
struct nand_chip *chip, uint8_t *buf, int page)
|
|
|
|
{
|
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
|
|
|
uint8_t *p = buf;
|
|
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
|
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
|
|
|
|
|
|
/* Read the OOB area first */
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
|
|
|
|
|
|
for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
int stat;
|
|
|
|
|
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
|
|
chip->read_buf(mtd, p, eccsize);
|
|
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
|
|
|
|
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
|
|
|
|
if (stat < 0)
|
|
|
|
mtd->ecc_stats.failed++;
|
|
|
|
else
|
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
|
2006-05-25 15:07:16 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: buffer to store read data
|
2009-09-24 19:04:53 +07:00
|
|
|
* @page: page number to read
|
2006-05-25 15:07:16 +07:00
|
|
|
*
|
|
|
|
* The hw generator calculates the error syndrome automatically. Therefor
|
2006-05-26 23:52:08 +07:00
|
|
|
* we need a special oob layout and handling.
|
2006-05-25 15:07:16 +07:00
|
|
|
*/
|
|
|
|
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
2009-09-19 02:51:46 +07:00
|
|
|
uint8_t *buf, int page)
|
2006-05-25 15:07:16 +07:00
|
|
|
{
|
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
|
|
|
uint8_t *p = buf;
|
2006-05-26 23:52:08 +07:00
|
|
|
uint8_t *oob = chip->oob_poi;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
int stat;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
|
|
chip->read_buf(mtd, p, eccsize);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (chip->ecc.prepad) {
|
|
|
|
chip->read_buf(mtd, oob, chip->ecc.prepad);
|
|
|
|
oob += chip->ecc.prepad;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
|
|
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
|
|
stat = chip->ecc.correct(mtd, p, oob, NULL);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2007-10-18 04:33:23 +07:00
|
|
|
if (stat < 0)
|
2006-05-25 15:07:16 +07:00
|
|
|
mtd->ecc_stats.failed++;
|
2005-11-07 18:15:49 +07:00
|
|
|
else
|
2006-05-25 15:07:16 +07:00
|
|
|
mtd->ecc_stats.corrected += stat;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
oob += eccbytes;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (chip->ecc.postpad) {
|
|
|
|
chip->read_buf(mtd, oob, chip->ecc.postpad);
|
|
|
|
oob += chip->ecc.postpad;
|
2005-11-07 18:15:49 +07:00
|
|
|
}
|
2006-05-25 15:07:16 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
/* Calculate remaining oob bytes */
|
2006-06-07 12:34:37 +07:00
|
|
|
i = mtd->oobsize - (oob - chip->oob_poi);
|
2006-05-25 15:07:16 +07:00
|
|
|
if (i)
|
|
|
|
chip->read_buf(mtd, oob, i);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_transfer_oob - [Internal] Transfer oob to client buffer
|
|
|
|
* @chip: nand chip structure
|
2006-06-29 11:48:27 +07:00
|
|
|
* @oob: oob destination address
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob ops structure
|
2006-11-03 22:20:38 +07:00
|
|
|
* @len: size of oob to transfer
|
2006-05-29 08:26:58 +07:00
|
|
|
*/
|
|
|
|
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
|
2006-11-03 22:20:38 +07:00
|
|
|
struct mtd_oob_ops *ops, size_t len)
|
2006-05-29 08:26:58 +07:00
|
|
|
{
|
|
|
|
switch(ops->mode) {
|
|
|
|
|
|
|
|
case MTD_OOB_PLACE:
|
|
|
|
case MTD_OOB_RAW:
|
|
|
|
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
|
|
|
|
return oob + len;
|
|
|
|
|
|
|
|
case MTD_OOB_AUTO: {
|
|
|
|
struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
2006-06-21 01:05:05 +07:00
|
|
|
uint32_t boffs = 0, roffs = ops->ooboffs;
|
|
|
|
size_t bytes = 0;
|
2006-05-29 08:26:58 +07:00
|
|
|
|
|
|
|
for(; free->length && len; free++, len -= bytes) {
|
2006-06-21 01:05:05 +07:00
|
|
|
/* Read request not from offset 0 ? */
|
|
|
|
if (unlikely(roffs)) {
|
|
|
|
if (roffs >= free->length) {
|
|
|
|
roffs -= free->length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
boffs = free->offset + roffs;
|
|
|
|
bytes = min_t(size_t, len,
|
|
|
|
(free->length - roffs));
|
|
|
|
roffs = 0;
|
|
|
|
} else {
|
|
|
|
bytes = min_t(size_t, len, free->length);
|
|
|
|
boffs = free->offset;
|
|
|
|
}
|
|
|
|
memcpy(oob, chip->oob_poi + boffs, bytes);
|
2006-05-29 08:26:58 +07:00
|
|
|
oob += bytes;
|
|
|
|
}
|
|
|
|
return oob;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_do_read_ops - [Internal] Read data with ECC
|
2006-05-25 15:07:16 +07:00
|
|
|
*
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @from: offset to read from
|
2006-06-29 11:48:27 +07:00
|
|
|
* @ops: oob ops structure
|
2006-05-25 15:07:16 +07:00
|
|
|
*
|
|
|
|
* Internal function. Called with chip held.
|
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
|
|
struct mtd_oob_ops *ops)
|
2006-05-25 15:07:16 +07:00
|
|
|
{
|
|
|
|
int chipnr, page, realpage, col, bytes, aligned;
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
struct mtd_ecc_stats stats;
|
|
|
|
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
|
|
|
|
int sndcmd = 1;
|
|
|
|
int ret = 0;
|
2006-05-29 08:26:58 +07:00
|
|
|
uint32_t readlen = ops->len;
|
2006-11-03 22:20:38 +07:00
|
|
|
uint32_t oobreadlen = ops->ooblen;
|
2006-05-29 08:26:58 +07:00
|
|
|
uint8_t *bufpoi, *oob, *buf;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
stats = mtd->ecc_stats;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
chipnr = (int)(from >> chip->chip_shift);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
realpage = (int)(from >> chip->page_shift);
|
|
|
|
page = realpage & chip->pagemask;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
col = (int)(from & (mtd->writesize - 1));
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
buf = ops->datbuf;
|
|
|
|
oob = ops->oobbuf;
|
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
while(1) {
|
|
|
|
bytes = min(mtd->writesize - col, readlen);
|
|
|
|
aligned = (bytes == mtd->writesize);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
/* Is the current page in the buffer ? */
|
2006-05-29 08:26:58 +07:00
|
|
|
if (realpage != chip->pagebuf || oob) {
|
2006-09-25 23:08:04 +07:00
|
|
|
bufpoi = aligned ? buf : chip->buffers->databuf;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (likely(sndcmd)) {
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
|
|
|
sndcmd = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
/* Now read the page into the buffer */
|
2006-09-25 23:12:39 +07:00
|
|
|
if (unlikely(ops->mode == MTD_OOB_RAW))
|
2009-09-19 02:51:46 +07:00
|
|
|
ret = chip->ecc.read_page_raw(mtd, chip,
|
|
|
|
bufpoi, page);
|
2008-05-15 23:23:18 +07:00
|
|
|
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
|
|
|
|
ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
|
2006-09-25 23:12:39 +07:00
|
|
|
else
|
2009-09-19 02:51:46 +07:00
|
|
|
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
|
|
|
page);
|
2006-05-25 15:07:16 +07:00
|
|
|
if (ret < 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2006-05-25 15:07:16 +07:00
|
|
|
|
|
|
|
/* Transfer not aligned data */
|
|
|
|
if (!aligned) {
|
2008-05-15 23:23:18 +07:00
|
|
|
if (!NAND_SUBPAGE_READ(chip) && !oob)
|
|
|
|
chip->pagebuf = realpage;
|
2006-09-25 23:08:04 +07:00
|
|
|
memcpy(buf, chip->buffers->databuf + col, bytes);
|
2006-05-25 15:07:16 +07:00
|
|
|
}
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
buf += bytes;
|
|
|
|
|
|
|
|
if (unlikely(oob)) {
|
|
|
|
/* Raw mode does data:oob:data:oob */
|
2006-11-03 22:20:38 +07:00
|
|
|
if (ops->mode != MTD_OOB_RAW) {
|
|
|
|
int toread = min(oobreadlen,
|
|
|
|
chip->ecc.layout->oobavail);
|
|
|
|
if (toread) {
|
|
|
|
oob = nand_transfer_oob(chip,
|
|
|
|
oob, ops, toread);
|
|
|
|
oobreadlen -= toread;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
buf = nand_transfer_oob(chip,
|
|
|
|
buf, ops, mtd->oobsize);
|
2006-05-29 08:26:58 +07:00
|
|
|
}
|
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (!(chip->options & NAND_NO_READRDY)) {
|
|
|
|
/*
|
|
|
|
* Apply delay or wait for ready/busy pin. Do
|
|
|
|
* this before the AUTOINCR check, so no
|
|
|
|
* problems arise if a chip which does auto
|
|
|
|
* increment is marked as NOAUTOINCR by the
|
|
|
|
* board driver.
|
|
|
|
*/
|
|
|
|
if (!chip->dev_ready)
|
|
|
|
udelay(chip->chip_delay);
|
|
|
|
else
|
|
|
|
nand_wait_ready(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-29 08:26:58 +07:00
|
|
|
} else {
|
2006-09-25 23:08:04 +07:00
|
|
|
memcpy(buf, chip->buffers->databuf + col, bytes);
|
2006-05-29 08:26:58 +07:00
|
|
|
buf += bytes;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
readlen -= bytes;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (!readlen)
|
2005-11-07 18:15:49 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* For subsequent reads align to page boundary. */
|
|
|
|
col = 0;
|
|
|
|
/* Increment page address */
|
|
|
|
realpage++;
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
page = realpage & chip->pagemask;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Check, if we cross a chip boundary */
|
|
|
|
if (!page) {
|
|
|
|
chipnr++;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, -1);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/* Check, if the chip supports auto page increment
|
|
|
|
* or if we have hit a block boundary.
|
2006-05-14 00:07:53 +07:00
|
|
|
*/
|
2006-05-25 15:07:16 +07:00
|
|
|
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
|
2005-11-07 18:15:49 +07:00
|
|
|
sndcmd = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
ops->retlen = ops->len - (size_t) readlen;
|
2006-11-03 22:20:38 +07:00
|
|
|
if (oob)
|
|
|
|
ops->oobretlen = ops->ooblen - oobreadlen;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2006-05-29 19:56:39 +07:00
|
|
|
if (mtd->ecc_stats.failed - stats.failed)
|
|
|
|
return -EBADMSG;
|
|
|
|
|
|
|
|
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
|
2006-05-25 15:07:16 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @from: offset to read from
|
|
|
|
* @len: number of bytes to read
|
|
|
|
* @retlen: pointer to variable to store the number of read bytes
|
|
|
|
* @buf: the databuffer to put data
|
|
|
|
*
|
|
|
|
* Get hold of the chip and call nand_do_read
|
|
|
|
*/
|
|
|
|
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
|
|
size_t *retlen, uint8_t *buf)
|
|
|
|
{
|
2006-05-29 08:26:58 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-25 15:07:16 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Do not allow reads past end of device */
|
|
|
|
if ((from + len) > mtd->size)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
nand_get_device(chip, mtd, FL_READING);
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
chip->ops.len = len;
|
|
|
|
chip->ops.datbuf = buf;
|
|
|
|
chip->ops.oobbuf = NULL;
|
|
|
|
|
|
|
|
ret = nand_do_read_ops(mtd, from, &chip->ops);
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2006-08-27 15:23:33 +07:00
|
|
|
*retlen = chip->ops.retlen;
|
|
|
|
|
2006-05-25 15:07:16 +07:00
|
|
|
nand_release_device(mtd);
|
|
|
|
|
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
/**
|
|
|
|
* nand_read_oob_std - [REPLACABLE] the most common OOB data read function
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @page: page number to read
|
|
|
|
* @sndcmd: flag whether to issue read command or not
|
|
|
|
*/
|
|
|
|
static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
int page, int sndcmd)
|
|
|
|
{
|
|
|
|
if (sndcmd) {
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
|
|
sndcmd = 0;
|
|
|
|
}
|
|
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
return sndcmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
|
|
|
|
* with syndromes
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @page: page number to read
|
|
|
|
* @sndcmd: flag whether to issue read command or not
|
|
|
|
*/
|
|
|
|
static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
int page, int sndcmd)
|
|
|
|
{
|
|
|
|
uint8_t *buf = chip->oob_poi;
|
|
|
|
int length = mtd->oobsize;
|
|
|
|
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
|
|
int eccsize = chip->ecc.size;
|
|
|
|
uint8_t *bufpoi = buf;
|
|
|
|
int i, toread, sndrnd = 0, pos;
|
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
|
|
|
|
for (i = 0; i < chip->ecc.steps; i++) {
|
|
|
|
if (sndrnd) {
|
|
|
|
pos = eccsize + i * (eccsize + chunk);
|
|
|
|
if (mtd->writesize > 512)
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
|
|
|
|
else
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
|
|
|
|
} else
|
|
|
|
sndrnd = 1;
|
|
|
|
toread = min_t(int, length, chunk);
|
|
|
|
chip->read_buf(mtd, bufpoi, toread);
|
|
|
|
bufpoi += toread;
|
|
|
|
length -= toread;
|
|
|
|
}
|
|
|
|
if (length > 0)
|
|
|
|
chip->read_buf(mtd, bufpoi, length);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_write_oob_std - [REPLACABLE] the most common OOB data write function
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @page: page number to write
|
|
|
|
*/
|
|
|
|
static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
int page)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
const uint8_t *buf = chip->oob_poi;
|
|
|
|
int length = mtd->oobsize;
|
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
|
|
|
|
chip->write_buf(mtd, buf, length);
|
|
|
|
/* Send command to program the OOB data */
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
|
|
|
|
|
|
|
status = chip->waitfunc(mtd, chip);
|
|
|
|
|
2006-06-21 16:51:20 +07:00
|
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
2006-06-21 01:05:05 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
|
|
|
|
* with syndrome - only for large page flash !
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @page: page number to write
|
|
|
|
*/
|
|
|
|
static int nand_write_oob_syndrome(struct mtd_info *mtd,
|
|
|
|
struct nand_chip *chip, int page)
|
|
|
|
{
|
|
|
|
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
|
|
int eccsize = chip->ecc.size, length = mtd->oobsize;
|
|
|
|
int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
|
|
|
|
const uint8_t *bufpoi = chip->oob_poi;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* data-ecc-data-ecc ... ecc-oob
|
|
|
|
* or
|
|
|
|
* data-pad-ecc-pad-data-pad .... ecc-pad-oob
|
|
|
|
*/
|
|
|
|
if (!chip->ecc.prepad && !chip->ecc.postpad) {
|
|
|
|
pos = steps * (eccsize + chunk);
|
|
|
|
steps = 0;
|
|
|
|
} else
|
2006-07-11 14:11:25 +07:00
|
|
|
pos = eccsize;
|
2006-06-21 01:05:05 +07:00
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
|
|
|
|
for (i = 0; i < steps; i++) {
|
|
|
|
if (sndcmd) {
|
|
|
|
if (mtd->writesize <= 512) {
|
|
|
|
uint32_t fill = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
len = eccsize;
|
|
|
|
while (len > 0) {
|
|
|
|
int num = min_t(int, len, 4);
|
|
|
|
chip->write_buf(mtd, (uint8_t *)&fill,
|
|
|
|
num);
|
|
|
|
len -= num;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pos = eccsize + i * (eccsize + chunk);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sndcmd = 1;
|
|
|
|
len = min_t(int, length, chunk);
|
|
|
|
chip->write_buf(mtd, bufpoi, len);
|
|
|
|
bufpoi += len;
|
|
|
|
length -= len;
|
|
|
|
}
|
|
|
|
if (length > 0)
|
|
|
|
chip->write_buf(mtd, bufpoi, length);
|
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
|
|
|
status = chip->waitfunc(mtd, chip);
|
|
|
|
|
|
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_do_read_oob - [Intern] NAND read out-of-band
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @from: offset to read from
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob operations description structure
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* NAND read out-of-band data from the spare area
|
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|
|
|
struct mtd_oob_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-06-21 01:05:05 +07:00
|
|
|
int page, realpage, chipnr, sndcmd = 1;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-25 14:51:54 +07:00
|
|
|
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
|
2006-11-03 22:20:38 +07:00
|
|
|
int readlen = ops->ooblen;
|
|
|
|
int len;
|
2006-06-21 01:05:05 +07:00
|
|
|
uint8_t *buf = ops->oobbuf;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
|
|
|
|
__func__, (unsigned long long)from, readlen);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-01-31 22:58:29 +07:00
|
|
|
if (ops->mode == MTD_OOB_AUTO)
|
2006-11-03 22:20:38 +07:00
|
|
|
len = chip->ecc.layout->oobavail;
|
2007-01-31 22:58:29 +07:00
|
|
|
else
|
|
|
|
len = mtd->oobsize;
|
|
|
|
|
|
|
|
if (unlikely(ops->ooboffs >= len)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read "
|
|
|
|
"outside oob\n", __func__);
|
2007-01-31 22:58:29 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not allow reads past end of device */
|
|
|
|
if (unlikely(from >= mtd->size ||
|
|
|
|
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
|
|
|
|
(from >> chip->page_shift)) * len)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
|
|
|
|
"of device\n", __func__);
|
2007-01-31 22:58:29 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2006-11-03 22:20:38 +07:00
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
chipnr = (int)(from >> chip->chip_shift);
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
/* Shift to get page */
|
|
|
|
realpage = (int)(from >> chip->page_shift);
|
|
|
|
page = realpage & chip->pagemask;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
while(1) {
|
2006-06-21 01:05:05 +07:00
|
|
|
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
|
2006-11-03 22:20:38 +07:00
|
|
|
|
|
|
|
len = min(len, readlen);
|
|
|
|
buf = nand_transfer_oob(chip, buf, ops, len);
|
2006-05-29 08:26:58 +07:00
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
if (!(chip->options & NAND_NO_READRDY)) {
|
|
|
|
/*
|
|
|
|
* Apply delay or wait for ready/busy pin. Do this
|
|
|
|
* before the AUTOINCR check, so no problems arise if a
|
|
|
|
* chip which does auto increment is marked as
|
|
|
|
* NOAUTOINCR by the board driver.
|
2005-07-15 20:53:51 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->dev_ready)
|
|
|
|
udelay(chip->chip_delay);
|
2005-07-15 20:53:51 +07:00
|
|
|
else
|
|
|
|
nand_wait_ready(mtd);
|
2006-05-25 14:51:54 +07:00
|
|
|
}
|
2005-07-15 20:53:51 +07:00
|
|
|
|
2006-11-03 22:20:38 +07:00
|
|
|
readlen -= len;
|
2006-06-21 16:51:20 +07:00
|
|
|
if (!readlen)
|
|
|
|
break;
|
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
/* Increment page address */
|
|
|
|
realpage++;
|
|
|
|
|
|
|
|
page = realpage & chip->pagemask;
|
|
|
|
/* Check, if we cross a chip boundary */
|
|
|
|
if (!page) {
|
|
|
|
chipnr++;
|
|
|
|
chip->select_chip(mtd, -1);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-25 14:51:54 +07:00
|
|
|
|
|
|
|
/* Check, if the chip supports auto page increment
|
|
|
|
* or if we have hit a block boundary.
|
|
|
|
*/
|
|
|
|
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
|
|
|
|
sndcmd = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-11-03 22:20:38 +07:00
|
|
|
ops->oobretlen = ops->ooblen;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @from: offset to read from
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob operation description structure
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2006-05-29 08:26:58 +07:00
|
|
|
* NAND read data and/or out-of-band data
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|
|
|
struct mtd_oob_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-29 08:26:58 +07:00
|
|
|
int ret = -ENOTSUPP;
|
|
|
|
|
|
|
|
ops->retlen = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Do not allow reads past end of device */
|
2006-11-03 22:20:38 +07:00
|
|
|
if (ops->datbuf && (from + ops->len) > mtd->size) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read "
|
|
|
|
"beyond end of device\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_get_device(chip, mtd, FL_READING);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
switch(ops->mode) {
|
|
|
|
case MTD_OOB_PLACE:
|
|
|
|
case MTD_OOB_AUTO:
|
|
|
|
case MTD_OOB_RAW:
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
if (!ops->datbuf)
|
|
|
|
ret = nand_do_read_oob(mtd, from, ops);
|
|
|
|
else
|
|
|
|
ret = nand_do_read_ops(mtd, from, ops);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
out:
|
|
|
|
nand_release_device(mtd);
|
|
|
|
return ret;
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
/**
|
|
|
|
* nand_write_page_raw - [Intern] raw page write function
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: data buffer
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
*
|
|
|
|
* Not for syndrome calculating ecc controllers, which use a special oob layout
|
2006-05-29 08:26:58 +07:00
|
|
|
*/
|
|
|
|
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
const uint8_t *buf)
|
|
|
|
{
|
|
|
|
chip->write_buf(mtd, buf, mtd->writesize);
|
|
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
/**
|
|
|
|
* nand_write_page_raw_syndrome - [Intern] raw page write function
|
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: data buffer
|
|
|
|
*
|
|
|
|
* We need a special oob layout and handling even when ECC isn't checked.
|
|
|
|
*/
|
|
|
|
static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
const uint8_t *buf)
|
|
|
|
{
|
|
|
|
int eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
uint8_t *oob = chip->oob_poi;
|
|
|
|
int steps, size;
|
|
|
|
|
|
|
|
for (steps = chip->ecc.steps; steps > 0; steps--) {
|
|
|
|
chip->write_buf(mtd, buf, eccsize);
|
|
|
|
buf += eccsize;
|
|
|
|
|
|
|
|
if (chip->ecc.prepad) {
|
|
|
|
chip->write_buf(mtd, oob, chip->ecc.prepad);
|
|
|
|
oob += chip->ecc.prepad;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
|
|
oob += eccbytes;
|
|
|
|
|
|
|
|
if (chip->ecc.postpad) {
|
|
|
|
chip->write_buf(mtd, oob, chip->ecc.postpad);
|
|
|
|
oob += chip->ecc.postpad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size = mtd->oobsize - (oob - chip->oob_poi);
|
|
|
|
if (size)
|
|
|
|
chip->write_buf(mtd, oob, size);
|
|
|
|
}
|
2006-05-23 22:21:03 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_write_page_swecc - [REPLACABLE] software ecc based page write function
|
2006-05-26 23:52:08 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: data buffer
|
2006-05-23 22:21:03 +07:00
|
|
|
*/
|
2006-05-26 23:52:08 +07:00
|
|
|
static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
const uint8_t *buf)
|
2006-05-23 22:21:03 +07:00
|
|
|
{
|
2006-05-26 23:52:08 +07:00
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
2006-09-25 23:08:04 +07:00
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
2006-05-26 23:52:08 +07:00
|
|
|
const uint8_t *p = buf;
|
2007-05-29 01:17:54 +07:00
|
|
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
2006-05-23 22:21:03 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
/* Software ecc calculation */
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
2006-05-23 22:21:03 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
2006-05-23 22:21:03 +07:00
|
|
|
|
2007-04-05 16:44:05 +07:00
|
|
|
chip->ecc.write_page_raw(mtd, chip, buf);
|
2006-05-26 23:52:08 +07:00
|
|
|
}
|
2006-05-23 22:21:03 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
|
2006-05-26 23:52:08 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: data buffer
|
|
|
|
*/
|
|
|
|
static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
const uint8_t *buf)
|
|
|
|
{
|
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
2006-09-25 23:08:04 +07:00
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
2006-05-26 23:52:08 +07:00
|
|
|
const uint8_t *p = buf;
|
2007-05-29 01:17:54 +07:00
|
|
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
2006-05-23 22:21:03 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
2006-05-27 05:05:44 +07:00
|
|
|
chip->write_buf(mtd, p, eccsize);
|
2006-05-26 23:52:08 +07:00
|
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
2006-05-23 22:21:03 +07:00
|
|
|
}
|
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
|
|
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
2006-05-23 22:21:03 +07:00
|
|
|
}
|
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/**
|
2006-10-19 20:04:02 +07:00
|
|
|
* nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
|
2006-05-26 23:52:08 +07:00
|
|
|
* @mtd: mtd info structure
|
|
|
|
* @chip: nand chip info structure
|
|
|
|
* @buf: data buffer
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2006-05-26 23:52:08 +07:00
|
|
|
* The hw generator calculates the error syndrome automatically. Therefor
|
|
|
|
* we need a special oob layout and handling.
|
|
|
|
*/
|
|
|
|
static void nand_write_page_syndrome(struct mtd_info *mtd,
|
|
|
|
struct nand_chip *chip, const uint8_t *buf)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-26 23:52:08 +07:00
|
|
|
int i, eccsize = chip->ecc.size;
|
|
|
|
int eccbytes = chip->ecc.bytes;
|
|
|
|
int eccsteps = chip->ecc.steps;
|
|
|
|
const uint8_t *p = buf;
|
|
|
|
uint8_t *oob = chip->oob_poi;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
|
|
chip->write_buf(mtd, p, eccsize);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
if (chip->ecc.prepad) {
|
|
|
|
chip->write_buf(mtd, oob, chip->ecc.prepad);
|
|
|
|
oob += chip->ecc.prepad;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip->ecc.calculate(mtd, p, oob);
|
|
|
|
chip->write_buf(mtd, oob, eccbytes);
|
|
|
|
oob += eccbytes;
|
|
|
|
|
|
|
|
if (chip->ecc.postpad) {
|
|
|
|
chip->write_buf(mtd, oob, chip->ecc.postpad);
|
|
|
|
oob += chip->ecc.postpad;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2006-05-26 23:52:08 +07:00
|
|
|
|
|
|
|
/* Calculate remaining oob bytes */
|
2006-06-07 12:34:37 +07:00
|
|
|
i = mtd->oobsize - (oob - chip->oob_poi);
|
2006-05-26 23:52:08 +07:00
|
|
|
if (i)
|
|
|
|
chip->write_buf(mtd, oob, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-09-25 23:12:39 +07:00
|
|
|
* nand_write_page - [REPLACEABLE] write one page
|
2006-05-26 23:52:08 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @chip: NAND chip descriptor
|
|
|
|
* @buf: the data to write
|
|
|
|
* @page: page number to write
|
|
|
|
* @cached: cached programming
|
2006-10-28 04:24:47 +07:00
|
|
|
* @raw: use _raw version of write_page
|
2006-05-26 23:52:08 +07:00
|
|
|
*/
|
|
|
|
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
2006-09-25 23:12:39 +07:00
|
|
|
const uint8_t *buf, int page, int cached, int raw)
|
2006-05-26 23:52:08 +07:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
|
|
|
|
2006-09-25 23:12:39 +07:00
|
|
|
if (unlikely(raw))
|
|
|
|
chip->ecc.write_page_raw(mtd, chip, buf);
|
|
|
|
else
|
|
|
|
chip->ecc.write_page(mtd, chip, buf);
|
2006-05-26 23:52:08 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Cached progamming disabled for now, Not sure if its worth the
|
|
|
|
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
|
|
|
|
*/
|
|
|
|
cached = 0;
|
|
|
|
|
|
|
|
if (!cached || !(chip->options & NAND_CACHEPRG)) {
|
|
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
2006-06-21 01:05:05 +07:00
|
|
|
status = chip->waitfunc(mtd, chip);
|
2006-05-26 23:52:08 +07:00
|
|
|
/*
|
|
|
|
* See if operation failed and additional status checks are
|
|
|
|
* available
|
|
|
|
*/
|
|
|
|
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
|
|
|
|
status = chip->errstat(mtd, chip, FL_WRITING, status,
|
|
|
|
page);
|
|
|
|
|
|
|
|
if (status & NAND_STATUS_FAIL)
|
|
|
|
return -EIO;
|
|
|
|
} else {
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
|
2006-06-21 01:05:05 +07:00
|
|
|
status = chip->waitfunc(mtd, chip);
|
2006-05-26 23:52:08 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
|
|
|
|
/* Send command to read back the data */
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
|
|
|
|
|
|
if (chip->verify_buf(mtd, buf, mtd->writesize))
|
|
|
|
return -EIO;
|
|
|
|
#endif
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
/**
|
|
|
|
* nand_fill_oob - [Internal] Transfer client buffer to oob
|
|
|
|
* @chip: nand chip structure
|
|
|
|
* @oob: oob data buffer
|
|
|
|
* @ops: oob ops structure
|
|
|
|
*/
|
|
|
|
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
|
|
|
|
struct mtd_oob_ops *ops)
|
|
|
|
{
|
|
|
|
size_t len = ops->ooblen;
|
|
|
|
|
|
|
|
switch(ops->mode) {
|
|
|
|
|
|
|
|
case MTD_OOB_PLACE:
|
|
|
|
case MTD_OOB_RAW:
|
|
|
|
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
|
|
|
|
return oob + len;
|
|
|
|
|
|
|
|
case MTD_OOB_AUTO: {
|
|
|
|
struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
2006-06-21 01:05:05 +07:00
|
|
|
uint32_t boffs = 0, woffs = ops->ooboffs;
|
|
|
|
size_t bytes = 0;
|
2006-05-29 08:26:58 +07:00
|
|
|
|
|
|
|
for(; free->length && len; free++, len -= bytes) {
|
2006-06-21 01:05:05 +07:00
|
|
|
/* Write request not from offset 0 ? */
|
|
|
|
if (unlikely(woffs)) {
|
|
|
|
if (woffs >= free->length) {
|
|
|
|
woffs -= free->length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
boffs = free->offset + woffs;
|
|
|
|
bytes = min_t(size_t, len,
|
|
|
|
(free->length - woffs));
|
|
|
|
woffs = 0;
|
|
|
|
} else {
|
|
|
|
bytes = min_t(size_t, len, free->length);
|
|
|
|
boffs = free->offset;
|
|
|
|
}
|
2006-07-11 14:11:25 +07:00
|
|
|
memcpy(chip->oob_poi + boffs, oob, bytes);
|
2006-05-29 08:26:58 +07:00
|
|
|
oob += bytes;
|
|
|
|
}
|
|
|
|
return oob;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-09-28 20:38:36 +07:00
|
|
|
#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_do_write_ops - [Internal] NAND write with ECC
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @to: offset to write to
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob operations description structure
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* NAND write with ECC
|
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
|
|
struct mtd_oob_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-09-28 20:38:36 +07:00
|
|
|
int chipnr, realpage, page, blockmask, column;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-29 08:26:58 +07:00
|
|
|
uint32_t writelen = ops->len;
|
|
|
|
uint8_t *oob = ops->oobbuf;
|
|
|
|
uint8_t *buf = ops->datbuf;
|
2006-09-28 20:38:36 +07:00
|
|
|
int ret, subpage;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
ops->retlen = 0;
|
2006-09-28 20:38:36 +07:00
|
|
|
if (!writelen)
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/* reject writes, which are not page aligned */
|
2006-05-29 08:26:58 +07:00
|
|
|
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
printk(KERN_NOTICE "%s: Attempt to write not "
|
|
|
|
"page aligned data\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2006-09-28 20:38:36 +07:00
|
|
|
column = to & (mtd->writesize - 1);
|
|
|
|
subpage = column || (writelen & (mtd->writesize - 1));
|
|
|
|
|
|
|
|
if (subpage && oob)
|
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-28 05:11:45 +07:00
|
|
|
chipnr = (int)(to >> chip->chip_shift);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Check, if it is write protected */
|
|
|
|
if (nand_check_wp(mtd))
|
2006-05-29 08:26:58 +07:00
|
|
|
return -EIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
realpage = (int)(to >> chip->page_shift);
|
|
|
|
page = realpage & chip->pagemask;
|
|
|
|
blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
|
|
|
|
|
|
|
|
/* Invalidate the page cache, when we write to the cached page */
|
|
|
|
if (to <= (chip->pagebuf << chip->page_shift) &&
|
2006-05-29 08:26:58 +07:00
|
|
|
(chip->pagebuf << chip->page_shift) < (to + ops->len))
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->pagebuf = -1;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-10-21 23:09:53 +07:00
|
|
|
/* If we're not given explicit OOB data, let it be 0xFF */
|
|
|
|
if (likely(!oob))
|
|
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
while(1) {
|
2006-09-28 20:38:36 +07:00
|
|
|
int bytes = mtd->writesize;
|
2006-05-26 23:52:08 +07:00
|
|
|
int cached = writelen > bytes && page != blockmask;
|
2006-09-28 20:38:36 +07:00
|
|
|
uint8_t *wbuf = buf;
|
|
|
|
|
|
|
|
/* Partial page write ? */
|
|
|
|
if (unlikely(column || writelen < (mtd->writesize - 1))) {
|
|
|
|
cached = 0;
|
|
|
|
bytes = min_t(int, bytes - column, (int) writelen);
|
|
|
|
chip->pagebuf = -1;
|
|
|
|
memset(chip->buffers->databuf, 0xff, mtd->writesize);
|
|
|
|
memcpy(&chip->buffers->databuf[column], buf, bytes);
|
|
|
|
wbuf = chip->buffers->databuf;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
if (unlikely(oob))
|
|
|
|
oob = nand_fill_oob(chip, oob, ops);
|
|
|
|
|
2006-09-28 20:38:36 +07:00
|
|
|
ret = chip->write_page(mtd, chip, wbuf, page, cached,
|
2006-09-25 23:12:39 +07:00
|
|
|
(ops->mode == MTD_OOB_RAW));
|
2006-05-26 23:52:08 +07:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
writelen -= bytes;
|
|
|
|
if (!writelen)
|
|
|
|
break;
|
|
|
|
|
2006-09-28 20:38:36 +07:00
|
|
|
column = 0;
|
2006-05-26 23:52:08 +07:00
|
|
|
buf += bytes;
|
|
|
|
realpage++;
|
|
|
|
|
|
|
|
page = realpage & chip->pagemask;
|
|
|
|
/* Check, if we cross a chip boundary */
|
|
|
|
if (!page) {
|
|
|
|
chipnr++;
|
|
|
|
chip->select_chip(mtd, -1);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2006-05-29 08:26:58 +07:00
|
|
|
|
|
|
|
ops->retlen = ops->len - writelen;
|
2006-11-03 22:20:38 +07:00
|
|
|
if (unlikely(oob))
|
|
|
|
ops->oobretlen = ops->ooblen;
|
2005-04-17 05:20:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-05 20:55:52 +07:00
|
|
|
/**
|
|
|
|
* panic_nand_write - [MTD Interface] NAND write with ECC
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @to: offset to write to
|
|
|
|
* @len: number of bytes to write
|
|
|
|
* @retlen: pointer to variable to store the number of written bytes
|
|
|
|
* @buf: the data to write
|
|
|
|
*
|
|
|
|
* NAND write with ECC. Used when performing writes in interrupt context, this
|
|
|
|
* may for example be called by mtdoops when writing an oops while in panic.
|
|
|
|
*/
|
|
|
|
static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
|
|
size_t *retlen, const uint8_t *buf)
|
|
|
|
{
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Do not allow reads past end of device */
|
|
|
|
if ((to + len) > mtd->size)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Wait for the device to get ready. */
|
|
|
|
panic_nand_wait(mtd, chip, 400);
|
|
|
|
|
|
|
|
/* Grab the device. */
|
|
|
|
panic_nand_get_device(chip, mtd, FL_WRITING);
|
|
|
|
|
|
|
|
chip->ops.len = len;
|
|
|
|
chip->ops.datbuf = (uint8_t *)buf;
|
|
|
|
chip->ops.oobbuf = NULL;
|
|
|
|
|
|
|
|
ret = nand_do_write_ops(mtd, to, &chip->ops);
|
|
|
|
|
|
|
|
*retlen = chip->ops.retlen;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_write - [MTD Interface] NAND write with ECC
|
2006-05-26 23:52:08 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @to: offset to write to
|
|
|
|
* @len: number of bytes to write
|
2006-05-29 08:26:58 +07:00
|
|
|
* @retlen: pointer to variable to store the number of written bytes
|
|
|
|
* @buf: the data to write
|
2006-05-26 23:52:08 +07:00
|
|
|
*
|
2006-05-29 08:26:58 +07:00
|
|
|
* NAND write with ECC
|
2006-05-26 23:52:08 +07:00
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
|
|
size_t *retlen, const uint8_t *buf)
|
2006-05-26 23:52:08 +07:00
|
|
|
{
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
int ret;
|
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
/* Do not allow reads past end of device */
|
|
|
|
if ((to + len) > mtd->size)
|
2006-05-26 23:52:08 +07:00
|
|
|
return -EINVAL;
|
2006-05-29 08:26:58 +07:00
|
|
|
if (!len)
|
|
|
|
return 0;
|
2006-05-26 23:52:08 +07:00
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
nand_get_device(chip, mtd, FL_WRITING);
|
2006-05-26 23:52:08 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
chip->ops.len = len;
|
|
|
|
chip->ops.datbuf = (uint8_t *)buf;
|
|
|
|
chip->ops.oobbuf = NULL;
|
2006-05-26 23:52:08 +07:00
|
|
|
|
2006-05-29 08:26:58 +07:00
|
|
|
ret = nand_do_write_ops(mtd, to, &chip->ops);
|
2006-05-26 23:52:08 +07:00
|
|
|
|
2006-08-27 15:23:33 +07:00
|
|
|
*retlen = chip->ops.retlen;
|
|
|
|
|
2006-05-26 23:52:08 +07:00
|
|
|
nand_release_device(mtd);
|
2006-05-29 08:26:58 +07:00
|
|
|
|
|
|
|
return ret;
|
2006-05-26 23:52:08 +07:00
|
|
|
}
|
2006-05-25 14:51:54 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
2006-05-29 08:26:58 +07:00
|
|
|
* nand_do_write_oob - [MTD Interface] NAND write out-of-band
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @to: offset to write to
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob operation description structure
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* NAND write out-of-band
|
|
|
|
*/
|
2006-05-29 08:26:58 +07:00
|
|
|
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
struct mtd_oob_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-01-31 22:58:29 +07:00
|
|
|
int chipnr, page, status, len;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
|
|
|
|
__func__, (unsigned int)to, (int)ops->ooblen);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-01-31 22:58:29 +07:00
|
|
|
if (ops->mode == MTD_OOB_AUTO)
|
|
|
|
len = chip->ecc.layout->oobavail;
|
|
|
|
else
|
|
|
|
len = mtd->oobsize;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Do not allow write past end of page */
|
2007-01-31 22:58:29 +07:00
|
|
|
if ((ops->ooboffs + ops->ooblen) > len) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write "
|
|
|
|
"past end of page\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-01-31 22:58:29 +07:00
|
|
|
if (unlikely(ops->ooboffs >= len)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start "
|
|
|
|
"write outside oob\n", __func__);
|
2007-01-31 22:58:29 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not allow reads past end of device */
|
|
|
|
if (unlikely(to >= mtd->size ||
|
|
|
|
ops->ooboffs + ops->ooblen >
|
|
|
|
((mtd->size >> chip->page_shift) -
|
|
|
|
(to >> chip->page_shift)) * len)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
|
|
|
|
"end of device\n", __func__);
|
2007-01-31 22:58:29 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
chipnr = (int)(to >> chip->chip_shift);
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 14:51:54 +07:00
|
|
|
/* Shift to get page */
|
|
|
|
page = (int)(to >> chip->page_shift);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the chip. Some chips (like the Toshiba TC5832DC found in one
|
|
|
|
* of my DiskOnChip 2000 test units) will clear the whole data page too
|
|
|
|
* if we don't do this. I have no clue why, but I seem to have 'fixed'
|
|
|
|
* it in the doc2000 driver in August 1999. dwmw2.
|
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Check, if it is write protected */
|
|
|
|
if (nand_check_wp(mtd))
|
2006-05-29 08:26:58 +07:00
|
|
|
return -EROFS;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Invalidate the page cache, if we write to the cached page */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (page == chip->pagebuf)
|
|
|
|
chip->pagebuf = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
|
|
nand_fill_oob(chip, ops->oobbuf, ops);
|
|
|
|
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
|
|
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
if (status)
|
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-11-03 22:20:38 +07:00
|
|
|
ops->oobretlen = ops->ooblen;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
return 0;
|
2006-05-29 08:26:58 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
|
|
|
|
* @mtd: MTD device structure
|
2006-06-29 11:48:27 +07:00
|
|
|
* @to: offset to write to
|
2006-05-29 08:26:58 +07:00
|
|
|
* @ops: oob operation description structure
|
|
|
|
*/
|
|
|
|
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
struct mtd_oob_ops *ops)
|
|
|
|
{
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
int ret = -ENOTSUPP;
|
|
|
|
|
|
|
|
ops->retlen = 0;
|
|
|
|
|
|
|
|
/* Do not allow writes past end of device */
|
2006-11-03 22:20:38 +07:00
|
|
|
if (ops->datbuf && (to + ops->len) > mtd->size) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
|
|
|
|
"end of device\n", __func__);
|
2006-05-29 08:26:58 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
nand_get_device(chip, mtd, FL_WRITING);
|
2006-05-29 08:26:58 +07:00
|
|
|
|
|
|
|
switch(ops->mode) {
|
|
|
|
case MTD_OOB_PLACE:
|
|
|
|
case MTD_OOB_AUTO:
|
|
|
|
case MTD_OOB_RAW:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ops->datbuf)
|
|
|
|
ret = nand_do_write_oob(mtd, to, ops);
|
|
|
|
else
|
|
|
|
ret = nand_do_write_ops(mtd, to, ops);
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
nand_release_device(mtd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* single_erease_cmd - [GENERIC] NAND standard block erase command function
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @page: the page address of the block which will be erased
|
|
|
|
*
|
|
|
|
* Standard erase command for NAND chips
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static void single_erase_cmd(struct mtd_info *mtd, int page)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Send commands to erase a block */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* multi_erease_cmd - [GENERIC] AND specific block erase command function
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @page: the page address of the block which will be erased
|
|
|
|
*
|
|
|
|
* AND multi block erase command function
|
|
|
|
* Erase 4 consecutive blocks
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static void multi_erase_cmd(struct mtd_info *mtd, int page)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Send commands to erase a block */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_erase - [MTD Interface] erase block(s)
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @instr: erase instruction
|
|
|
|
*
|
|
|
|
* Erase one ore more blocks
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-14 00:07:53 +07:00
|
|
|
return nand_erase_nand(mtd, instr, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-01-18 01:35:25 +07:00
|
|
|
#define BBT_PAGE_MASK 0xffffff3f
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
2006-05-24 17:07:37 +07:00
|
|
|
* nand_erase_nand - [Internal] erase block(s)
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @instr: erase instruction
|
|
|
|
* @allowbbt: allow erasing the bbt area
|
|
|
|
*
|
|
|
|
* Erase one ore more blocks
|
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|
|
|
int allowbbt)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-12-10 20:37:21 +07:00
|
|
|
int page, status, pages_per_block, ret, chipnr;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2008-12-10 20:37:21 +07:00
|
|
|
loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};
|
2006-05-24 17:07:37 +07:00
|
|
|
unsigned int bbt_masked_page = 0xffffffff;
|
2008-12-10 20:37:21 +07:00
|
|
|
loff_t len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
|
|
|
|
__func__, (unsigned long long)instr->addr,
|
|
|
|
(unsigned long long)instr->len);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-02-03 15:42:24 +07:00
|
|
|
if (check_offs_len(mtd, instr->addr, instr->len))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-08-12 16:40:50 +07:00
|
|
|
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Grab the lock and see if the device is available */
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_get_device(chip, mtd, FL_ERASING);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Shift to get first page */
|
2006-05-24 17:07:37 +07:00
|
|
|
page = (int)(instr->addr >> chip->page_shift);
|
|
|
|
chipnr = (int)(instr->addr >> chip->chip_shift);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Calculate pages in each block */
|
2006-05-24 17:07:37 +07:00
|
|
|
pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Select the NAND device */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Check, if it is write protected */
|
|
|
|
if (nand_check_wp(mtd)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
|
|
|
|
__func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
instr->state = MTD_ERASE_FAILED;
|
|
|
|
goto erase_exit;
|
|
|
|
}
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* If BBT requires refresh, set the BBT page mask to see if the BBT
|
|
|
|
* should be rewritten. Otherwise the mask is set to 0xffffffff which
|
|
|
|
* can not be matched. This is also done when the bbt is actually
|
|
|
|
* erased to avoid recusrsive updates
|
|
|
|
*/
|
|
|
|
if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
|
|
|
|
bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
|
2005-01-18 01:35:25 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Loop through the pages */
|
|
|
|
len = instr->len;
|
|
|
|
|
|
|
|
instr->state = MTD_ERASING;
|
|
|
|
|
|
|
|
while (len) {
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* heck if we have a bad block, we do not erase bad blocks !
|
|
|
|
*/
|
|
|
|
if (nand_block_checkbad(mtd, ((loff_t) page) <<
|
|
|
|
chip->page_shift, 0, allowbbt)) {
|
2009-07-07 17:19:49 +07:00
|
|
|
printk(KERN_WARNING "%s: attempt to erase a bad block "
|
|
|
|
"at page 0x%08x\n", __func__, page);
|
2005-04-17 05:20:36 +07:00
|
|
|
instr->state = MTD_ERASE_FAILED;
|
|
|
|
goto erase_exit;
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* Invalidate the page cache, if we erase the block which
|
|
|
|
* contains the current cached page
|
|
|
|
*/
|
|
|
|
if (page <= chip->pagebuf && chip->pagebuf <
|
|
|
|
(page + pages_per_block))
|
|
|
|
chip->pagebuf = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->erase_cmd(mtd, page & chip->pagemask);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-06-21 01:05:05 +07:00
|
|
|
status = chip->waitfunc(mtd, chip);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* See if operation failed and additional status checks are
|
|
|
|
* available
|
|
|
|
*/
|
|
|
|
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
|
|
|
|
status = chip->errstat(mtd, chip, FL_ERASING,
|
|
|
|
status, page);
|
2005-01-24 10:07:46 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* See if block erase succeeded */
|
2005-01-24 01:30:53 +07:00
|
|
|
if (status & NAND_STATUS_FAIL) {
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
|
|
|
|
"page 0x%08x\n", __func__, page);
|
2005-04-17 05:20:36 +07:00
|
|
|
instr->state = MTD_ERASE_FAILED;
|
2008-12-10 20:37:21 +07:00
|
|
|
instr->fail_addr =
|
|
|
|
((loff_t)page << chip->page_shift);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto erase_exit;
|
|
|
|
}
|
2005-01-18 01:35:25 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* If BBT requires refresh, set the BBT rewrite flag to the
|
|
|
|
* page being erased
|
|
|
|
*/
|
|
|
|
if (bbt_masked_page != 0xffffffff &&
|
|
|
|
(page & BBT_PAGE_MASK) == bbt_masked_page)
|
2008-12-10 20:37:21 +07:00
|
|
|
rewrite_bbt[chipnr] =
|
|
|
|
((loff_t)page << chip->page_shift);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Increment page address and decrement length */
|
2006-05-24 17:07:37 +07:00
|
|
|
len -= (1 << chip->phys_erase_shift);
|
2005-04-17 05:20:36 +07:00
|
|
|
page += pages_per_block;
|
|
|
|
|
|
|
|
/* Check, if we cross a chip boundary */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (len && !(page & chip->pagemask)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
chipnr++;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, -1);
|
|
|
|
chip->select_chip(mtd, chipnr);
|
2005-01-18 01:35:25 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* If BBT requires refresh and BBT-PERCHIP, set the BBT
|
|
|
|
* page mask to see if this BBT should be rewritten
|
|
|
|
*/
|
|
|
|
if (bbt_masked_page != 0xffffffff &&
|
|
|
|
(chip->bbt_td->options & NAND_BBT_PERCHIP))
|
|
|
|
bbt_masked_page = chip->bbt_td->pages[chipnr] &
|
|
|
|
BBT_PAGE_MASK;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
instr->state = MTD_ERASE_DONE;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
erase_exit:
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
|
|
|
|
|
|
|
|
/* Deselect and wake up anyone waiting on the device */
|
|
|
|
nand_release_device(mtd);
|
|
|
|
|
2007-10-07 02:01:59 +07:00
|
|
|
/* Do call back function */
|
|
|
|
if (!ret)
|
|
|
|
mtd_erase_callback(instr);
|
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/*
|
|
|
|
* If BBT requires refresh and erase was successful, rewrite any
|
|
|
|
* selected bad block tables
|
|
|
|
*/
|
|
|
|
if (bbt_masked_page == 0xffffffff || ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
|
|
|
|
if (!rewrite_bbt[chipnr])
|
|
|
|
continue;
|
|
|
|
/* update the BBT for chip */
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "
|
|
|
|
"(%d:0x%0llx 0x%0x)\n", __func__, chipnr,
|
|
|
|
rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]);
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_update_bbt(mtd, rewrite_bbt[chipnr]);
|
2005-01-18 01:35:25 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Return more or less happy */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_sync - [MTD Interface] sync
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*
|
|
|
|
* Sync is actually a wait for chip ready function
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static void nand_sync(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-07-07 17:19:49 +07:00
|
|
|
DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Grab the lock and see if the device is available */
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_get_device(chip, mtd, FL_SYNCING);
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Release it and go back */
|
2006-05-14 00:07:53 +07:00
|
|
|
nand_release_device(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-05-24 17:07:37 +07:00
|
|
|
* nand_block_isbad - [MTD Interface] Check if block at offset is bad
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
2006-06-29 11:48:27 +07:00
|
|
|
* @offs: offset relative to mtd start
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
/* Check for invalid offset */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (offs > mtd->size)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
return nand_block_checkbad(mtd, offs, 1, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-05-24 17:07:37 +07:00
|
|
|
* nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @ofs: offset relative to mtd start
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
int ret;
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
if ((ret = nand_block_isbad(mtd, ofs))) {
|
|
|
|
/* If it was bad already, return success and do nothing. */
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ret > 0)
|
|
|
|
return 0;
|
2006-05-14 00:07:53 +07:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
return chip->block_markbad(mtd, ofs);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-09-15 20:58:53 +07:00
|
|
|
/**
|
|
|
|
* nand_suspend - [MTD Interface] Suspend the NAND flash
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*/
|
|
|
|
static int nand_suspend(struct mtd_info *mtd)
|
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-09-15 20:58:53 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
return nand_get_device(chip, mtd, FL_PM_SUSPENDED);
|
2005-09-15 20:58:53 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_resume - [MTD Interface] Resume the NAND flash
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*/
|
|
|
|
static void nand_resume(struct mtd_info *mtd)
|
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-09-15 20:58:53 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->state == FL_PM_SUSPENDED)
|
2005-09-15 20:58:53 +07:00
|
|
|
nand_release_device(mtd);
|
|
|
|
else
|
2009-07-07 17:19:49 +07:00
|
|
|
printk(KERN_ERR "%s called for a chip which is not "
|
|
|
|
"in suspended state\n", __func__);
|
2005-09-15 20:58:53 +07:00
|
|
|
}
|
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/*
|
|
|
|
* Set default functions
|
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
static void nand_set_defaults(struct nand_chip *chip, int busw)
|
2006-05-23 16:54:38 +07:00
|
|
|
{
|
2005-04-17 05:20:36 +07:00
|
|
|
/* check for proper chip_delay setup, set 20us if not */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (!chip->chip_delay)
|
|
|
|
chip->chip_delay = 20;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* check, if a user supplied command function given */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->cmdfunc == NULL)
|
|
|
|
chip->cmdfunc = nand_command;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* check, if a user supplied wait function given */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->waitfunc == NULL)
|
|
|
|
chip->waitfunc = nand_wait;
|
|
|
|
|
|
|
|
if (!chip->select_chip)
|
|
|
|
chip->select_chip = nand_select_chip;
|
|
|
|
if (!chip->read_byte)
|
|
|
|
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
|
|
|
|
if (!chip->read_word)
|
|
|
|
chip->read_word = nand_read_word;
|
|
|
|
if (!chip->block_bad)
|
|
|
|
chip->block_bad = nand_block_bad;
|
|
|
|
if (!chip->block_markbad)
|
|
|
|
chip->block_markbad = nand_default_block_markbad;
|
|
|
|
if (!chip->write_buf)
|
|
|
|
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
|
|
|
|
if (!chip->read_buf)
|
|
|
|
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
|
|
|
if (!chip->verify_buf)
|
|
|
|
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
|
|
|
|
if (!chip->scan_bbt)
|
|
|
|
chip->scan_bbt = nand_default_bbt;
|
2006-05-26 23:52:08 +07:00
|
|
|
|
|
|
|
if (!chip->controller) {
|
|
|
|
chip->controller = &chip->hwcontrol;
|
|
|
|
spin_lock_init(&chip->controller->lock);
|
|
|
|
init_waitqueue_head(&chip->controller->wq);
|
|
|
|
}
|
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-05-24 17:07:37 +07:00
|
|
|
* Get the flash and manufacturer id and lookup if the type is supported
|
2006-05-23 16:54:38 +07:00
|
|
|
*/
|
|
|
|
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip,
|
2006-05-23 16:54:38 +07:00
|
|
|
int busw, int *maf_id)
|
|
|
|
{
|
|
|
|
struct nand_flash_dev *type = NULL;
|
|
|
|
int i, dev_id, maf_idx;
|
2008-04-14 20:58:58 +07:00
|
|
|
int tmp_id, tmp_manf;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Select the device */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-09-15 19:37:29 +07:00
|
|
|
/*
|
|
|
|
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
|
|
|
* after power-up
|
|
|
|
*/
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Send the command for reading device ID */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Read manufacturer and device IDs */
|
2006-05-24 17:07:37 +07:00
|
|
|
*maf_id = chip->read_byte(mtd);
|
|
|
|
dev_id = chip->read_byte(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-14 20:58:58 +07:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Lookup the flash id */
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
2006-05-23 16:54:38 +07:00
|
|
|
if (dev_id == nand_flash_ids[i].id) {
|
|
|
|
type = &nand_flash_ids[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
if (!type)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
2006-05-27 06:02:13 +07:00
|
|
|
if (!mtd->name)
|
|
|
|
mtd->name = type->name;
|
|
|
|
|
2008-12-10 20:37:21 +07:00
|
|
|
chip->chipsize = (uint64_t)type->chipsize << 20;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
/* Newer devices have all the information in additional id bytes */
|
2006-05-27 06:02:13 +07:00
|
|
|
if (!type->pagesize) {
|
2006-05-23 16:54:38 +07:00
|
|
|
int extid;
|
2006-09-28 20:38:36 +07:00
|
|
|
/* The 3rd id byte holds MLC / multichip data */
|
|
|
|
chip->cellinfo = chip->read_byte(mtd);
|
2006-05-23 16:54:38 +07:00
|
|
|
/* The 4th id byte is the important one */
|
2006-05-24 17:07:37 +07:00
|
|
|
extid = chip->read_byte(mtd);
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Calc pagesize */
|
2006-05-23 17:37:31 +07:00
|
|
|
mtd->writesize = 1024 << (extid & 0x3);
|
2006-05-23 16:54:38 +07:00
|
|
|
extid >>= 2;
|
|
|
|
/* Calc oobsize */
|
2006-05-23 17:37:31 +07:00
|
|
|
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
|
2006-05-23 16:54:38 +07:00
|
|
|
extid >>= 2;
|
|
|
|
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
|
|
|
mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
|
|
|
extid >>= 2;
|
|
|
|
/* Get buswidth information */
|
|
|
|
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
} else {
|
|
|
|
/*
|
2006-05-24 17:07:37 +07:00
|
|
|
* Old devices have chip data hardcoded in the device id table
|
2006-05-23 16:54:38 +07:00
|
|
|
*/
|
2006-05-27 06:02:13 +07:00
|
|
|
mtd->erasesize = type->erasesize;
|
|
|
|
mtd->writesize = type->pagesize;
|
2006-05-23 17:37:31 +07:00
|
|
|
mtd->oobsize = mtd->writesize / 32;
|
2006-05-27 06:02:13 +07:00
|
|
|
busw = type->options & NAND_BUSWIDTH_16;
|
2006-05-23 16:54:38 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Try to identify manufacturer */
|
2006-07-15 19:26:18 +07:00
|
|
|
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
|
2006-05-23 16:54:38 +07:00
|
|
|
if (nand_manuf_ids[maf_idx].id == *maf_id)
|
|
|
|
break;
|
|
|
|
}
|
2005-02-16 16:39:39 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/*
|
|
|
|
* Check, if buswidth is correct. Hardware drivers should set
|
2006-05-24 17:07:37 +07:00
|
|
|
* chip correct !
|
2006-05-23 16:54:38 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
|
2006-05-23 16:54:38 +07:00
|
|
|
printk(KERN_INFO "NAND device: Manufacturer ID:"
|
|
|
|
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
|
|
|
|
dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
|
|
|
|
printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
|
2006-05-24 17:07:37 +07:00
|
|
|
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
2006-05-23 16:54:38 +07:00
|
|
|
busw ? 16 : 8);
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Calculate the address shift from the page size */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->page_shift = ffs(mtd->writesize) - 1;
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Convert chipsize to number of pages per chip -1. */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->bbt_erase_shift = chip->phys_erase_shift =
|
2006-05-23 16:54:38 +07:00
|
|
|
ffs(mtd->erasesize) - 1;
|
2008-12-10 20:37:21 +07:00
|
|
|
if (chip->chipsize & 0xffffffff)
|
|
|
|
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
|
|
|
|
else
|
|
|
|
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Set the bad block position */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->badblockpos = mtd->writesize > 512 ?
|
2006-05-23 16:54:38 +07:00
|
|
|
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Get chip options, preserve non chip based options */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
2006-05-27 06:02:13 +07:00
|
|
|
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
/*
|
2006-05-24 17:07:37 +07:00
|
|
|
* Set chip as a default. Board drivers can override it, if necessary
|
2006-05-23 16:54:38 +07:00
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->options |= NAND_NO_AUTOINCR;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
/* Check if chip is a not a samsung device. Do not clear the
|
2006-05-23 16:54:38 +07:00
|
|
|
* options for chips which are not having an extended id.
|
|
|
|
*/
|
2006-05-27 06:02:13 +07:00
|
|
|
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
/* Check for AND chips with 4 page planes */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_4PAGE_ARRAY)
|
|
|
|
chip->erase_cmd = multi_erase_cmd;
|
2006-05-23 16:54:38 +07:00
|
|
|
else
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->erase_cmd = single_erase_cmd;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
/* Do not replace user supplied command function ! */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
|
|
|
chip->cmdfunc = nand_command_lp;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
printk(KERN_INFO "NAND device: Manufacturer ID:"
|
|
|
|
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
|
|
|
|
nand_manuf_ids[maf_idx].name, type->name);
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-09-25 23:06:53 +07:00
|
|
|
* nand_scan_ident - [NAND Interface] Scan for the NAND device
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @maxchips: Number of chips to scan for
|
2006-05-23 16:54:38 +07:00
|
|
|
*
|
2006-09-25 23:06:53 +07:00
|
|
|
* This is the first phase of the normal nand_scan() function. It
|
|
|
|
* reads the flash ID and sets up MTD fields accordingly.
|
2006-05-23 16:54:38 +07:00
|
|
|
*
|
2006-09-25 23:06:53 +07:00
|
|
|
* The mtd->owner field must be set to the module of the caller.
|
2006-05-23 16:54:38 +07:00
|
|
|
*/
|
2006-09-25 23:06:53 +07:00
|
|
|
int nand_scan_ident(struct mtd_info *mtd, int maxchips)
|
2006-05-23 16:54:38 +07:00
|
|
|
{
|
|
|
|
int i, busw, nand_maf_id;
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2006-05-23 16:54:38 +07:00
|
|
|
struct nand_flash_dev *type;
|
|
|
|
|
|
|
|
/* Get buswidth to select the correct functions */
|
2006-05-24 17:07:37 +07:00
|
|
|
busw = chip->options & NAND_BUSWIDTH_16;
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Set the default functions */
|
2006-05-24 17:07:37 +07:00
|
|
|
nand_set_defaults(chip, busw);
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
/* Read the flash type */
|
2006-05-24 17:07:37 +07:00
|
|
|
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
|
2006-05-23 16:54:38 +07:00
|
|
|
|
|
|
|
if (IS_ERR(type)) {
|
2009-11-03 01:12:33 +07:00
|
|
|
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
|
|
|
printk(KERN_WARNING "No NAND device found.\n");
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, -1);
|
2006-05-23 16:54:38 +07:00
|
|
|
return PTR_ERR(type);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/* Check for a chip array */
|
2006-05-14 00:07:53 +07:00
|
|
|
for (i = 1; i < maxchips; i++) {
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, i);
|
2008-09-15 19:37:29 +07:00
|
|
|
/* See comment in nand_get_flash_type for reset */
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Send the command for reading device ID */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Read manufacturer and device IDs */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (nand_maf_id != chip->read_byte(mtd) ||
|
|
|
|
type->id != chip->read_byte(mtd))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i > 1)
|
|
|
|
printk(KERN_INFO "%d NAND chips detected\n", i);
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Store the number of chips and calc total size for mtd */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->numchips = i;
|
|
|
|
mtd->size = i * chip->chipsize;
|
2006-05-23 16:54:38 +07:00
|
|
|
|
2006-09-25 23:06:53 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
*
|
|
|
|
* This is the second phase of the normal nand_scan() function. It
|
|
|
|
* fills out all the uninitialized function pointers with the defaults
|
|
|
|
* and scans for a bad block table if appropriate.
|
|
|
|
*/
|
|
|
|
int nand_scan_tail(struct mtd_info *mtd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
|
2006-09-25 23:08:04 +07:00
|
|
|
if (!(chip->options & NAND_OWN_BUFFERS))
|
|
|
|
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
|
|
|
|
if (!chip->buffers)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2006-10-21 23:09:53 +07:00
|
|
|
/* Set the internal oob buffer location, just after the page data */
|
2006-10-22 07:47:45 +07:00
|
|
|
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/*
|
|
|
|
* If no default placement scheme is given, select an appropriate one
|
|
|
|
*/
|
2006-05-28 03:16:10 +07:00
|
|
|
if (!chip->ecc.layout) {
|
2005-11-07 18:15:49 +07:00
|
|
|
switch (mtd->oobsize) {
|
2005-04-17 05:20:36 +07:00
|
|
|
case 8:
|
2006-05-28 03:16:10 +07:00
|
|
|
chip->ecc.layout = &nand_oob_8;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case 16:
|
2006-05-28 03:16:10 +07:00
|
|
|
chip->ecc.layout = &nand_oob_16;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case 64:
|
2006-05-28 03:16:10 +07:00
|
|
|
chip->ecc.layout = &nand_oob_64;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-12-12 23:27:03 +07:00
|
|
|
case 128:
|
|
|
|
chip->ecc.layout = &nand_oob_128;
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2006-05-23 16:54:38 +07:00
|
|
|
printk(KERN_WARNING "No oob scheme defined for "
|
|
|
|
"oobsize %d\n", mtd->oobsize);
|
2005-04-17 05:20:36 +07:00
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-09-25 23:12:39 +07:00
|
|
|
if (!chip->write_page)
|
|
|
|
chip->write_page = nand_write_page;
|
|
|
|
|
2005-11-07 18:15:49 +07:00
|
|
|
/*
|
2006-05-23 16:54:38 +07:00
|
|
|
* check ECC mode, default to software if 3byte/512byte hardware ECC is
|
|
|
|
* selected and we have 256 byte pagesize fallback to software ECC
|
2006-05-14 00:07:53 +07:00
|
|
|
*/
|
2006-09-25 23:12:39 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
switch (chip->ecc.mode) {
|
2009-09-19 02:51:47 +07:00
|
|
|
case NAND_ECC_HW_OOB_FIRST:
|
|
|
|
/* Similar to NAND_ECC_HW, but a separate read_page handle */
|
|
|
|
if (!chip->ecc.calculate || !chip->ecc.correct ||
|
|
|
|
!chip->ecc.hwctl) {
|
|
|
|
printk(KERN_WARNING "No ECC functions supplied; "
|
|
|
|
"Hardware ECC not possible\n");
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
if (!chip->ecc.read_page)
|
|
|
|
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
|
|
|
|
|
2006-05-23 17:00:46 +07:00
|
|
|
case NAND_ECC_HW:
|
2006-05-25 15:07:16 +07:00
|
|
|
/* Use standard hwecc read page function ? */
|
|
|
|
if (!chip->ecc.read_page)
|
|
|
|
chip->ecc.read_page = nand_read_page_hwecc;
|
2006-05-26 23:52:08 +07:00
|
|
|
if (!chip->ecc.write_page)
|
|
|
|
chip->ecc.write_page = nand_write_page_hwecc;
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
if (!chip->ecc.read_page_raw)
|
|
|
|
chip->ecc.read_page_raw = nand_read_page_raw;
|
|
|
|
if (!chip->ecc.write_page_raw)
|
|
|
|
chip->ecc.write_page_raw = nand_write_page_raw;
|
2006-06-21 01:05:05 +07:00
|
|
|
if (!chip->ecc.read_oob)
|
|
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
|
|
if (!chip->ecc.write_oob)
|
|
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2006-05-23 17:00:46 +07:00
|
|
|
case NAND_ECC_HW_SYNDROME:
|
2007-12-14 00:15:28 +07:00
|
|
|
if ((!chip->ecc.calculate || !chip->ecc.correct ||
|
|
|
|
!chip->ecc.hwctl) &&
|
|
|
|
(!chip->ecc.read_page ||
|
2008-01-16 23:36:03 +07:00
|
|
|
chip->ecc.read_page == nand_read_page_hwecc ||
|
2007-12-14 00:15:28 +07:00
|
|
|
!chip->ecc.write_page ||
|
2008-01-16 23:36:03 +07:00
|
|
|
chip->ecc.write_page == nand_write_page_hwecc)) {
|
2009-09-19 02:51:47 +07:00
|
|
|
printk(KERN_WARNING "No ECC functions supplied; "
|
2006-05-23 17:00:46 +07:00
|
|
|
"Hardware ECC not possible\n");
|
|
|
|
BUG();
|
|
|
|
}
|
2006-05-26 23:52:08 +07:00
|
|
|
/* Use standard syndrome read/write page function ? */
|
2006-05-25 15:07:16 +07:00
|
|
|
if (!chip->ecc.read_page)
|
|
|
|
chip->ecc.read_page = nand_read_page_syndrome;
|
2006-05-26 23:52:08 +07:00
|
|
|
if (!chip->ecc.write_page)
|
|
|
|
chip->ecc.write_page = nand_write_page_syndrome;
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
if (!chip->ecc.read_page_raw)
|
|
|
|
chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
|
|
|
|
if (!chip->ecc.write_page_raw)
|
|
|
|
chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
|
2006-06-21 01:05:05 +07:00
|
|
|
if (!chip->ecc.read_oob)
|
|
|
|
chip->ecc.read_oob = nand_read_oob_syndrome;
|
|
|
|
if (!chip->ecc.write_oob)
|
|
|
|
chip->ecc.write_oob = nand_write_oob_syndrome;
|
2006-05-25 15:07:16 +07:00
|
|
|
|
2006-05-24 17:07:37 +07:00
|
|
|
if (mtd->writesize >= chip->ecc.size)
|
2006-05-23 17:00:46 +07:00
|
|
|
break;
|
|
|
|
printk(KERN_WARNING "%d byte HW ECC not possible on "
|
|
|
|
"%d byte page size, fallback to SW ECC\n",
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.size, mtd->writesize);
|
|
|
|
chip->ecc.mode = NAND_ECC_SOFT;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-23 17:00:46 +07:00
|
|
|
case NAND_ECC_SOFT:
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.calculate = nand_calculate_ecc;
|
|
|
|
chip->ecc.correct = nand_correct_data;
|
2006-05-25 15:07:16 +07:00
|
|
|
chip->ecc.read_page = nand_read_page_swecc;
|
2008-05-15 23:23:18 +07:00
|
|
|
chip->ecc.read_subpage = nand_read_subpage;
|
2006-05-26 23:52:08 +07:00
|
|
|
chip->ecc.write_page = nand_write_page_swecc;
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
chip->ecc.read_page_raw = nand_read_page_raw;
|
|
|
|
chip->ecc.write_page_raw = nand_write_page_raw;
|
2006-06-21 01:05:05 +07:00
|
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
2008-12-12 07:10:57 +07:00
|
|
|
if (!chip->ecc.size)
|
|
|
|
chip->ecc.size = 256;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.bytes = 3;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
|
|
|
case NAND_ECC_NONE:
|
2006-05-23 16:54:38 +07:00
|
|
|
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
|
|
|
|
"This is not recommended !!\n");
|
2006-05-29 08:26:58 +07:00
|
|
|
chip->ecc.read_page = nand_read_page_raw;
|
|
|
|
chip->ecc.write_page = nand_write_page_raw;
|
2006-06-21 01:05:05 +07:00
|
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
[MTD] [NAND] fix "raw" reads with ECC syndrome layouts
The syndrome based page read/write routines store ECC, and possibly other
"OOB" data, right after each chunk of ECC'd data. With ECC chunk size of
512 bytes and a large page (2KiB) NAND, the layout is:
data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover
Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines
use a traditional layout -- data OOB, disregarding the prepad and postpad
values -- so when they're used with that type of ECC hardware, those calls
mix up the data and OOB. Which means, in particular, that bad block
tables won't be found on startup, with data corruption and related chaos
ensuing.
The current syndrome-based drivers in mainline all seem to use one chunk
per page; presumably they haven't noticed such bugs.
Fix this, by adding read/write page_raw_syndrome() routines as siblings of
the existing non-raw routines; "raw" just means to bypass the ECC
computations, not change data and OOB layout.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-03-05 03:01:36 +07:00
|
|
|
chip->ecc.read_page_raw = nand_read_page_raw;
|
|
|
|
chip->ecc.write_page_raw = nand_write_page_raw;
|
2006-06-21 01:05:05 +07:00
|
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.size = mtd->writesize;
|
|
|
|
chip->ecc.bytes = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2006-09-25 23:12:39 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2006-05-23 16:54:38 +07:00
|
|
|
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.mode);
|
2005-11-07 18:15:49 +07:00
|
|
|
BUG();
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-05-28 03:16:10 +07:00
|
|
|
/*
|
|
|
|
* The number of bytes available for a client to place data into
|
|
|
|
* the out of band area
|
|
|
|
*/
|
|
|
|
chip->ecc.layout->oobavail = 0;
|
2009-04-22 09:51:20 +07:00
|
|
|
for (i = 0; chip->ecc.layout->oobfree[i].length
|
|
|
|
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
|
2006-05-28 03:16:10 +07:00
|
|
|
chip->ecc.layout->oobavail +=
|
|
|
|
chip->ecc.layout->oobfree[i].length;
|
2007-03-06 20:56:34 +07:00
|
|
|
mtd->oobavail = chip->ecc.layout->oobavail;
|
2006-05-28 03:16:10 +07:00
|
|
|
|
2006-05-23 16:54:38 +07:00
|
|
|
/*
|
|
|
|
* Set the number of read / write steps for one page depending on ECC
|
|
|
|
* mode
|
|
|
|
*/
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->ecc.steps = mtd->writesize / chip->ecc.size;
|
|
|
|
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
|
2006-05-23 17:00:46 +07:00
|
|
|
printk(KERN_WARNING "Invalid ecc parameters\n");
|
|
|
|
BUG();
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-05-25 15:07:16 +07:00
|
|
|
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
|
2005-11-07 18:15:49 +07:00
|
|
|
|
2006-09-28 20:38:36 +07:00
|
|
|
/*
|
|
|
|
* Allow subpage writes up to ecc.steps. Not possible for MLC
|
|
|
|
* FLASH.
|
|
|
|
*/
|
|
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
|
|
|
|
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
|
|
|
|
switch(chip->ecc.steps) {
|
|
|
|
case 2:
|
|
|
|
mtd->subpage_sft = 1;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
case 8:
|
2007-12-12 23:27:03 +07:00
|
|
|
case 16:
|
2006-09-28 20:38:36 +07:00
|
|
|
mtd->subpage_sft = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
|
|
|
|
|
2006-05-25 14:45:29 +07:00
|
|
|
/* Initialize state */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->state = FL_READY;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* De-select the device */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->select_chip(mtd, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Invalidate the pagebuffer reference */
|
2006-05-24 17:07:37 +07:00
|
|
|
chip->pagebuf = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Fill in remaining MTD driver data */
|
|
|
|
mtd->type = MTD_NANDFLASH;
|
2006-05-23 04:18:29 +07:00
|
|
|
mtd->flags = MTD_CAP_NANDFLASH;
|
2005-04-17 05:20:36 +07:00
|
|
|
mtd->erase = nand_erase;
|
|
|
|
mtd->point = NULL;
|
|
|
|
mtd->unpoint = NULL;
|
|
|
|
mtd->read = nand_read;
|
|
|
|
mtd->write = nand_write;
|
2009-10-05 20:55:52 +07:00
|
|
|
mtd->panic_write = panic_nand_write;
|
2005-04-17 05:20:36 +07:00
|
|
|
mtd->read_oob = nand_read_oob;
|
|
|
|
mtd->write_oob = nand_write_oob;
|
|
|
|
mtd->sync = nand_sync;
|
|
|
|
mtd->lock = NULL;
|
|
|
|
mtd->unlock = NULL;
|
2005-09-15 20:58:53 +07:00
|
|
|
mtd->suspend = nand_suspend;
|
|
|
|
mtd->resume = nand_resume;
|
2005-04-17 05:20:36 +07:00
|
|
|
mtd->block_isbad = nand_block_isbad;
|
|
|
|
mtd->block_markbad = nand_block_markbad;
|
|
|
|
|
2006-05-28 03:16:10 +07:00
|
|
|
/* propagate ecc.layout to mtd_info */
|
|
|
|
mtd->ecclayout = chip->ecc.layout;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-02-09 19:20:00 +07:00
|
|
|
/* Check, if we should skip the bad block table scan */
|
2006-05-24 17:07:37 +07:00
|
|
|
if (chip->options & NAND_SKIP_BBTSCAN)
|
2005-02-09 19:20:00 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Build bad block table */
|
2006-05-24 17:07:37 +07:00
|
|
|
return chip->scan_bbt(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-04-01 02:05:31 +07:00
|
|
|
/* is_module_text_address() isn't exported, and it's mostly a pointless
|
2006-09-25 23:06:53 +07:00
|
|
|
test if this is a module _anyway_ -- they'd have to try _really_ hard
|
|
|
|
to call us from in-kernel code if the core NAND support is modular. */
|
|
|
|
#ifdef MODULE
|
|
|
|
#define caller_is_module() (1)
|
|
|
|
#else
|
|
|
|
#define caller_is_module() \
|
2009-04-01 02:05:31 +07:00
|
|
|
is_module_text_address((unsigned long)__builtin_return_address(0))
|
2006-09-25 23:06:53 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nand_scan - [NAND Interface] Scan for the NAND device
|
|
|
|
* @mtd: MTD device structure
|
|
|
|
* @maxchips: Number of chips to scan for
|
|
|
|
*
|
|
|
|
* This fills out all the uninitialized function pointers
|
|
|
|
* with the defaults.
|
|
|
|
* The flash ID is read and the mtd/chip structures are
|
|
|
|
* filled with the appropriate values.
|
|
|
|
* The mtd->owner field must be set to the module of the caller
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int nand_scan(struct mtd_info *mtd, int maxchips)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Many callers got this wrong, so check for it for a while... */
|
|
|
|
if (!mtd->owner && caller_is_module()) {
|
2009-07-07 17:19:49 +07:00
|
|
|
printk(KERN_CRIT "%s called with NULL mtd->owner!\n",
|
|
|
|
__func__);
|
2006-09-25 23:06:53 +07:00
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = nand_scan_ident(mtd, maxchips);
|
|
|
|
if (!ret)
|
|
|
|
ret = nand_scan_tail(mtd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
2005-11-07 18:15:49 +07:00
|
|
|
* nand_release - [NAND Interface] Free resources held by the NAND device
|
2005-04-17 05:20:36 +07:00
|
|
|
* @mtd: MTD device structure
|
|
|
|
*/
|
2006-05-14 00:07:53 +07:00
|
|
|
void nand_release(struct mtd_info *mtd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-24 17:07:37 +07:00
|
|
|
struct nand_chip *chip = mtd->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
|
|
/* Deregister partitions */
|
2006-05-14 00:07:53 +07:00
|
|
|
del_mtd_partitions(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
/* Deregister the device */
|
2006-05-14 00:07:53 +07:00
|
|
|
del_mtd_device(mtd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-07 16:01:27 +07:00
|
|
|
/* Free bad block table memory */
|
2006-05-24 17:07:37 +07:00
|
|
|
kfree(chip->bbt);
|
2006-09-25 23:08:04 +07:00
|
|
|
if (!(chip->options & NAND_OWN_BUFFERS))
|
|
|
|
kfree(chip->buffers);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nand_scan);
|
2006-09-25 23:06:53 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nand_scan_ident);
|
|
|
|
EXPORT_SYMBOL_GPL(nand_scan_tail);
|
2006-05-14 00:07:53 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nand_release);
|
2006-03-31 17:31:14 +07:00
|
|
|
|
|
|
|
static int __init nand_base_init(void)
|
|
|
|
{
|
|
|
|
led_trigger_register_simple("nand-disk", &nand_led_trigger);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit nand_base_exit(void)
|
|
|
|
{
|
|
|
|
led_trigger_unregister_simple(nand_led_trigger);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(nand_base_init);
|
|
|
|
module_exit(nand_base_exit);
|
|
|
|
|
2006-05-14 00:07:53 +07:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
|
|
|
|
MODULE_DESCRIPTION("Generic NAND flash driver code");
|