!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/mtd/nand/   drwxr-xr-x
Free 318.33 GB of 458.09 GB (69.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     nand.c (33.96 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 *  drivers/mtd/nand.c
 *
 *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
 *
 * $Id: nand.c,v 1.12 2001/10/02 15:05:14 dwmw2 Exp $
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *  Overview:
 *   This is the generic MTD driver for NAND flash devices. It should be
 *   capable of working with almost all NAND chips currently available.
 */

#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ids.h>
#include <linux/interrupt.h>
#include <asm/io.h>

#ifdef CONFIG_MTD_NAND_ECC
#include <linux/mtd/nand_ecc.h>
#endif

/*
 * Macros for low-level register control
 */
#define NAND_CTRL (*(volatile unsigned char *) \
            ((struct nand_chip *) mtd->priv)->CTRL_ADDR)
#define nand_select()    NAND_CTRL &= ~this->NCE; \
            nand_command(mtd, NAND_CMD_RESET, -1, -1); \
            udelay (10);
#define nand_deselect() NAND_CTRL |= ~this->NCE;

/*
 * NAND low-level MTD interface functions
 */
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
            size_t *retlen, u_char *buf);
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf, u_char *ecc_code);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf);
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
            size_t *retlen, const u_char *buf);
static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf,
                u_char *ecc_code);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
                unsigned long count, loff_t to, size_t *retlen);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);

/*
 * Send command to NAND device
 */
static void nand_command (struct mtd_info *mtd, unsigned command,
                int column, int page_addr)
{
    register struct nand_chip *this = mtd->priv;
    register unsigned long NAND_IO_ADDR = this->IO_ADDR;

    /* Begin command latch cycle */
    NAND_CTRL |= this->CLE;

    /*
     * Write out the command to the device.
     */
    if (command != NAND_CMD_SEQIN)    
        writeb (command, NAND_IO_ADDR);
    else {
        if (mtd->oobblock == 256 && column >= 256) {
            column -= 256;
            writeb(NAND_CMD_RESET, NAND_IO_ADDR);
            writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
            writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
        }
        else if (mtd->oobblock == 512 && column >= 256) {
            if (column < 512) {
                column -= 256;
                writeb(NAND_CMD_READ1, NAND_IO_ADDR);
                writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
            }
            else {
                column -= 512;
                writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
                writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
            }
        }
        else {
            writeb(NAND_CMD_READ0, NAND_IO_ADDR);
            writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
        }
    }

    /* Set ALE and clear CLE to start address cycle */
    NAND_CTRL &= ~this->CLE;
    NAND_CTRL |= this->ALE;

    /* Serially input address */
    if (column != -1)
        writeb (column, NAND_IO_ADDR);
    if (page_addr != -1) {
        writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
        writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
        /* One more address cycle for higher density devices */
        if (mtd->size & 0x0c000000) {
            writeb ((unsigned char) ((page_addr >> 16) & 0x0f),
                    NAND_IO_ADDR);
        }
    }

    /* Latch in address */
    NAND_CTRL &= ~this->ALE;

    /* Pause for 15us */
    udelay (15);
}

/*
 * NAND read
 */
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
            size_t *retlen, u_char *buf)
{
#ifdef CONFIG_MTD_NAND_ECC
    struct nand_chip *this = mtd->priv;
    
    return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);
#else
    return nand_read_ecc (mtd, from, len, retlen, buf, NULL);
#endif
}

/*
 * NAND read with ECC
 */
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf, u_char *ecc_code)
{
    int j, col, page, state;
    int erase_state = 0;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);
#ifdef CONFIG_MTD_NAND_ECC
    int ecc_result;
    u_char ecc_calc[6];
#endif

    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from,
        (int) len);

    /* Do not allow reads past end of device */
    if ((from + len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_read_ecc: Attempt read beyond end of device\n");
        *retlen = 0;
        return -EINVAL;
    }

    /* Grab the lock and see if the device is available */
retry:
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_READING;
        spin_unlock_bh (&this->chip_lock);
        break;

    case FL_ERASING:
        this->state = FL_READING;
        erase_state = 1;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* First we calculate the starting page */
    page = from >> this->page_shift;

    /* Get raw starting column */
    col = from & (mtd->oobblock - 1);

    /* State machine for devices having pages larger than 256 bytes */
    state = (col < mtd->eccsize) ? 0 : 1;

    /* Calculate column address within ECC block context */
    col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;

    /* Initialize return value */
    *retlen = 0;

    /* Select the NAND device */
    nand_select ();

    /* Loop until all data read */
    while (*retlen < len) {

#ifdef CONFIG_MTD_NAND_ECC
        /* Send the read command */
        if (!state)
            nand_command (mtd, NAND_CMD_READ0, 0x00, page);
        else 
            nand_command (mtd, NAND_CMD_READ1, 0x00, page);

        /* Read in a block big enough for ECC */
        for (j=0 ; j < mtd->eccsize ; j++)
            this->data_buf[j] = readb (this->IO_ADDR);

        /* Read in the out-of-band data */
        if (!state) {
            nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
            for (j=0 ; j<3 ; j++)
                ecc_code[j] = readb(this->IO_ADDR);
            nand_command (mtd, NAND_CMD_READ0, 0x00, page);
        }
        else {
            nand_command (mtd, NAND_CMD_READOOB, 0x03, page);
            for (j=3 ; j<6 ; j++)
                ecc_code[j] = readb(this->IO_ADDR);
            nand_command (mtd, NAND_CMD_READ0, 0x00, page);
        }

        /* Calculate the ECC and verify it */
        if (!state) {
            nand_calculate_ecc (&this->data_buf[0],
                        &ecc_calc[0]);
            ecc_result = nand_correct_data (&this->data_buf[0],
                        &ecc_code[0], &ecc_calc[0]);
        }
        else {
            nand_calculate_ecc (&this->data_buf[0],
                        &ecc_calc[3]);
            ecc_result = nand_correct_data (&this->data_buf[0],
                        &ecc_code[3], &ecc_calc[3]);
        }
        if (ecc_result == -1) {
            DEBUG (MTD_DEBUG_LEVEL0,
                "nand_read_ecc: " \
                "Failed ECC read, page 0x%08x\n", page);
            nand_deselect ();
            spin_lock_bh (&this->chip_lock);
            if (erase_state)
                this->state = FL_ERASING;
            else
                this->state = FL_READY;
            wake_up (&this->wq);
            spin_unlock_bh (&this->chip_lock);
            return -EIO;
        }

        /* Read the data from ECC data buffer into return buffer */
        if ((*retlen + (mtd->eccsize - col)) >= len) {
            while (*retlen < len)
                buf[(*retlen)++] = this->data_buf[col++];
            /* We're done */
            continue;
        }
        else
            for (j=col ; j < mtd->eccsize ; j++)
                buf[(*retlen)++] = this->data_buf[j];
#else
        /* Send the read command */
        if (!state)
            nand_command (mtd, NAND_CMD_READ0, col, page);
        else 
            nand_command (mtd, NAND_CMD_READ1, col, page);

        /* Read the data directly into the return buffer */ 
        if ((*retlen + (mtd->eccsize - col)) >= len) {
            while (*retlen < len)
                buf[(*retlen)++] = readb (this->IO_ADDR);
            /* We're done */
            continue;
        }
        else
            for (j=col ; j < mtd->eccsize ; j++)
                buf[(*retlen)++] = readb (this->IO_ADDR);
#endif

        /*
         * If the amount of data to be read is greater than
         * (256 - col), then all subsequent reads will take
         * place on page or half-page (in the case of 512 byte
         * page devices) aligned boundaries and the column
         * address will be zero. Setting the column address to
         * to zero after the first read allows us to simplify
         * the reading of data and the if/else statements above.
         */
        if (col)
            col = 0x00;

        /* Increment page address */
        if ((mtd->oobblock == 256) || state)
            page++;

        /* Toggle state machine */
        if (mtd->oobblock == 512)
            state = state ? 0 : 1;
    }

    /* De-select the NAND device */
    nand_deselect ();

    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    if (erase_state)
        this->state = FL_ERASING;
    else
        this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);
    
    /* Return happy */
    return 0;
}

/*
 * NAND read out-of-band
 */
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
{
    int i, col, page;
    int erase_state = 0;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);
    
    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from,
        (int) len);

    /* Shift to get page */
    page = ((int) from) >> this->page_shift;

    /* Mask to get column */
    col = from & 0x0f;

    /* Initialize return length value */
    *retlen = 0;

    /* Do not allow read past end of page */
    if ((col + len) > mtd->oobsize) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_read_oob: Attempt read past end of page " \
            "0x%08x, column %i, length %i\n", page, col, len);
        return -EINVAL;
    }

retry:
    /* Grab the lock and see if the device is available */
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_READING;
        spin_unlock_bh (&this->chip_lock);
        break;

    case FL_ERASING:
        this->state = FL_READING;
        erase_state = 1;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* Select the NAND device */
    nand_select ();

    /* Send the read command */
    nand_command (mtd, NAND_CMD_READOOB, col, page);    

    /* Read the data */
    for (i = 0 ; i < len ; i++)
        buf[i] = readb (this->IO_ADDR);

    /* De-select the NAND device */
    nand_deselect ();

    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    if (erase_state)
        this->state = FL_ERASING;
    else
        this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);

    /* Return happy */
    *retlen = len;
    return 0;
}

/*
 * NAND write
 */
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
            size_t *retlen, const u_char *buf)
{
#ifdef CONFIG_MTD_NAND_ECC
    struct nand_chip *this = mtd->priv;
    
    return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf);
#else
    return nand_write_ecc (mtd, to, len, retlen, buf, NULL);
#endif
}

/*
 * NAND write with ECC
 */
static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf,
                u_char *ecc_code)
{
    int i, page, col, cnt, status;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);
#ifdef CONFIG_MTD_NAND_ECC
    int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;
#endif

    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to,
        (int) len);

    /* Do not allow write past end of page */
    if ((to + len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_write_ecc: Attempted write past end of device\n");
        return -EINVAL;
    }

retry:
    /* Grab the lock and see if the device is available */
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_WRITING;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* Shift to get page */
    page = ((int) to) >> this->page_shift;

    /* Get the starting column */
    col = to & (mtd->oobblock - 1);

    /* Initialize return length value */
    *retlen = 0;

    /* Select the NAND device */
    nand_select ();

    /* Check the WP bit */
    nand_command (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(readb (this->IO_ADDR) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_write_ecc: Device is write protected!!!\n");
        nand_deselect ();
        spin_lock_bh (&this->chip_lock);
        this->state = FL_READY;
        wake_up (&this->wq);
        spin_unlock_bh (&this->chip_lock);
        return -EIO;
    }

    /* Loop until all data is written */
    while (*retlen < len) {
        /* Write data into buffer */
        if ((col + len) >= mtd->oobblock)
            for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++)
                this->data_buf[i] = buf[(*retlen + cnt)];
        else
            for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++)
                this->data_buf[i] = buf[(*retlen + cnt)];
        
#ifdef CONFIG_MTD_NAND_ECC
        /* Zero out the ECC array */
        for (i=0 ; i < 6 ; i++)
            ecc_code[i] = 0x00;

        /* Calculate and write the ECC if we have enough data */
        if ((col < mtd->eccsize) &&
            ((col + (len - *retlen)) >= mtd->eccsize)) {
            nand_command (mtd, NAND_CMD_READ0, col, page);
            for (i=0 ; i < col ; i++)
                this->data_buf[i] = readb (this->IO_ADDR); 
            nand_calculate_ecc (&this->data_buf[0], &ecc_code[0]);
            for (i=0 ; i<3 ; i++)
                this->data_buf[(mtd->oobblock + i)] =
                    ecc_code[i];
        }

        /* Calculate and write the second ECC if we have enough data */
        if ((mtd->oobblock == 512) &&
            ((col + (len - *retlen)) >= mtd->oobblock)) {
            nand_calculate_ecc (&this->data_buf[256], &ecc_code[3]);
            for (i=3 ; i<6 ; i++)
                this->data_buf[(mtd->oobblock + i)] =
                    ecc_code[i];
        }

        /* Write ones for partial page programming */
        for (i=ecc_bytes ; i < mtd->oobsize ; i++)
            this->data_buf[(mtd->oobblock + i)] = 0xff;
#else
        /* Write ones for partial page programming */
        for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
            this->data_buf[i] = 0xff;
#endif

        /* Write pre-padding bytes into buffer */
        for (i=0 ; i < col ; i++)
            this->data_buf[i] = 0xff;

        /* Write post-padding bytes into buffer */
        if ((col + (len - *retlen)) < mtd->oobblock) {
            for(i=(col + cnt) ; i < mtd->oobblock ; i++)
                this->data_buf[i] = 0xff;
        }

        /* Send command to begin auto page programming */
        nand_command (mtd, NAND_CMD_SEQIN, 0x00, page);

        /* Write out complete page of data */
        for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++)
            writeb (this->data_buf[i], this->IO_ADDR);

        /* Send command to actually program the data */
        nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);

        /*
         * Wait for program operation to complete. This could
         * take up to 3000us (3ms) on some devices, so we try
         * and exit as quickly as possible.
         */
        status = 0;
        for (i=0 ; i<24 ; i++) {
            /* Delay for 125us */
            udelay (125);

            /* Check the status */
            nand_command (mtd, NAND_CMD_STATUS, -1, -1);
            status = (int) readb (this->IO_ADDR);
            if (status & 0x40)
                break;
        }

        /* See if device thinks it succeeded */
        if (status & 0x01) {
            DEBUG (MTD_DEBUG_LEVEL0,
                "nand_write_ecc: " \
                "Failed write, page 0x%08x, " \
                "%6i bytes were succesful\n", page, *retlen);
            nand_deselect ();
            spin_lock_bh (&this->chip_lock);
            this->state = FL_READY;
            wake_up (&this->wq);
            spin_unlock_bh (&this->chip_lock);
            return -EIO;
        }

#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
        /*
         * The NAND device assumes that it is always writing to
         * a cleanly erased page. Hence, it performs its internal
         * write verification only on bits that transitioned from
         * 1 to 0. The device does NOT verify the whole page on a
         * byte by byte basis. It is possible that the page was
         * not completely erased or the page is becoming unusable
         * due to wear. The read with ECC would catch the error
         * later when the ECC page check fails, but we would rather
         * catch it early in the page write stage. Better to write
         * no data than invalid data.
         */
        
        /* Send command to read back the page */
        if (col < mtd->eccsize)
            nand_command (mtd, NAND_CMD_READ0, col, page);
        else
            nand_command (mtd, NAND_CMD_READ1, col - 256, page);

        /* Loop through and verify the data */
        for (i=col ; i < cnt ; i++) {
            if (this->data_buf[i] != readb (this->IO_ADDR)) {
                DEBUG (MTD_DEBUG_LEVEL0,
                    "nand_write_ecc: " \
                    "Failed write verify, page 0x%08x, " \
                    "%6i bytes were succesful\n",
                    page, *retlen);
                nand_deselect ();
                spin_lock_bh (&this->chip_lock);
                this->state = FL_READY;
                wake_up (&this->wq);
                spin_unlock_bh (&this->chip_lock);
                return -EIO;
            }
        }

#ifdef CONFIG_MTD_NAND_ECC
        /*
         * We also want to check that the ECC bytes wrote
         * correctly for the same reasons stated above.
         */
        nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
        for (i=0 ; i < ecc_bytes ; i++) {
            if ((readb (this->IO_ADDR) != ecc_code[i]) &&
                    ecc_code[i]) {
                DEBUG (MTD_DEBUG_LEVEL0,
                    "nand_write_ecc: Failed ECC write " \
                    "verify, page 0x%08x, " \
                    "%6i bytes were succesful\n",
                    page, i);
                nand_deselect ();
                spin_lock_bh (&this->chip_lock);
                this->state = FL_READY;
                wake_up (&this->wq);
                spin_unlock_bh (&this->chip_lock);
                return -EIO;
            }
        }
#endif

#endif

        /*
         * If we are writing a large amount of data and/or it
         * crosses page or half-page boundaries, we set the
         * the column to zero. It simplifies the program logic.
         */
        if (col)
            col = 0x00;

        /* Update written bytes count */
        *retlen += cnt;

        /* Increment page address */
        page++;
    }

    /* De-select the NAND device */
    nand_deselect ();

    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);

    /* Return happy */
    *retlen = len;
    return 0;
}

/*
 * NAND write out-of-band
 */
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
{
    int i, column, page, status;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);
    
    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to,
        (int) len);

    /* Shift to get page */
    page = ((int) to) >> this->page_shift;

    /* Mask to get column */
    column = to & 0x1f;

    /* Initialize return length value */
    *retlen = 0;

    /* Do not allow write past end of page */
    if ((column + len) > mtd->oobsize) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_write_oob: Attempt to write past end of page\n");
        return -EINVAL;
    }

retry:
    /* Grab the lock and see if the device is available */
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_WRITING;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* Select the NAND device */
    nand_select ();

    /* Check the WP bit */
    nand_command (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(readb (this->IO_ADDR) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_write_oob: Device is write protected!!!\n");
        nand_deselect ();
        spin_lock_bh (&this->chip_lock);
        this->state = FL_READY;
        wake_up (&this->wq);
        spin_unlock_bh (&this->chip_lock);
        return -EIO;
    }

    /* Write out desired data */
    nand_command (mtd, NAND_CMD_SEQIN, column + 512, page);
    for (i=0 ; i<len ; i++)
        writeb (buf[i], this->IO_ADDR);

    /* Send command to program the OOB data */
    nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);

    /*
     * Wait for program operation to complete. This could
     * take up to 3000us (3ms) on some devices, so we try
     * and exit as quickly as possible.
     */
    status = 0;
    for (i=0 ; i<24 ; i++) {
        /* Delay for 125us */
        udelay (125);

        /* Check the status */
        nand_command (mtd, NAND_CMD_STATUS, -1, -1);
        status = (int) readb (this->IO_ADDR);
        if (status & 0x40)
            break;
    }

    /* See if device thinks it succeeded */
    if (status & 0x01) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_write_oob: " \
            "Failed write, page 0x%08x\n", page);
        nand_deselect ();
        spin_lock_bh (&this->chip_lock);
        this->state = FL_READY;
        wake_up (&this->wq);
        spin_unlock_bh (&this->chip_lock);
        return -EIO;
    }

#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
    /* Send command to read back the data */
    nand_command (mtd, NAND_CMD_READOOB, column, page);

    /* Loop through and verify the data */
    for (i=0 ; i<len ; i++) {
        if (buf[i] != readb (this->IO_ADDR)) {
            DEBUG (MTD_DEBUG_LEVEL0,
                "nand_write_oob: " \
                "Failed write verify, page 0x%08x\n", page);
            nand_deselect ();
            spin_lock_bh (&this->chip_lock);
            this->state = FL_READY;
            wake_up (&this->wq);
            spin_unlock_bh (&this->chip_lock);
            return -EIO;
        }
    }
#endif

    /* De-select the NAND device */
    nand_deselect ();

    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);

    /* Return happy */
    *retlen = len;
    return 0;
}

/*
 * NAND write with iovec
 */
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
                unsigned long count, loff_t to, size_t *retlen)
{
    int i, page, col, cnt, len, total_len, status;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);
#ifdef CONFIG_MTD_NAND_ECC
    int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;
#endif

    /* Calculate total length of data */
    total_len = 0;
    for (i=0 ; i < count ; i++)
        total_len += (int) vecs[i].iov_len;

    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_writev: to = 0x%08x, len = %i\n", (unsigned int) to,
            (unsigned int) total_len);

    /* Do not allow write past end of page */
    if ((to + total_len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_writev: Attempted write past end of device\n");
        return -EINVAL;
    }

retry:
    /* Grab the lock and see if the device is available */
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_WRITING;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* Shift to get page */
    page = ((int) to) >> this->page_shift;

    /* Get the starting column */
    col = to & (mtd->oobblock - 1);

    /* Initialize return length value */
    *retlen = 0;

    /* Select the NAND device */
    nand_select ();

    /* Check the WP bit */
    nand_command (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(readb (this->IO_ADDR) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_writev: Device is write protected!!!\n");
        nand_deselect ();
        spin_lock_bh (&this->chip_lock);
        this->state = FL_READY;
        wake_up (&this->wq);
        spin_unlock_bh (&this->chip_lock);
        return -EIO;
    }

    /* Loop until all iovecs' data has been written */
    cnt = col;
    len = 0;
    while (count) {
        /* Do any need pre-fill for partial page programming */
        for (i=0 ; i < cnt ; i++)
            this->data_buf[i] = 0xff;

        /*
         * Read data out of each tuple until we have a full page
         * to write or we've read all the tuples.
         */
        while ((cnt < mtd->oobblock) && count) {
            this->data_buf[cnt++] =
                ((u_char *) vecs->iov_base)[len++];
            if (len >= (int) vecs->iov_len) {
                vecs++;
                len = 0;
                count--;
            }
        }
        
        /* Do any need post-fill for partial page programming */
        for (i=cnt ; i < mtd->oobblock ; i++)
            this->data_buf[i] = 0xff;

#ifdef CONFIG_MTD_NAND_ECC
        /* Zero out the ECC array */
        for (i=0 ; i < 6 ; i++)
            this->ecc_code_buf[i] = 0x00;

        /* Calculate and write the first ECC */
        if (col >= mtd->eccsize) {
            nand_command (mtd, NAND_CMD_READ0, col, page);
            for (i=0 ; i < col ; i++)
                this->data_buf[i] = readb (this->IO_ADDR); 
            nand_calculate_ecc (&this->data_buf[0],
                &(this->ecc_code_buf[0]));
            for (i=0 ; i<3 ; i++)
                this->data_buf[(mtd->oobblock + i)] =
                    this->ecc_code_buf[i];
        }

        /* Calculate and write the second ECC */
        if ((mtd->oobblock == 512) && (cnt == mtd->oobblock)) {
            nand_calculate_ecc (&this->data_buf[256],
                &(this->ecc_code_buf[3]));
            for (i=3 ; i<6 ; i++)
                this->data_buf[(mtd->oobblock + i)] =
                    this->ecc_code_buf[i];
        }

        /* Write ones for partial page programming */
        for (i=ecc_bytes ; i < mtd->oobsize ; i++)
            this->data_buf[(mtd->oobblock + i)] = 0xff;
#else
        /* Write ones for partial page programming */
        for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
            this->data_buf[i] = 0xff;
#endif
        /* Send command to begin auto page programming */
        nand_command (mtd, NAND_CMD_SEQIN, 0x00, page);

        /* Write out complete page of data */
        for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++)
            writeb (this->data_buf[i], this->IO_ADDR);

        /* Send command to actually program the data */
        nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);

        /*
         * Wait for program operation to complete. This could
         * take up to 3000us (3ms) on some devices, so we try
         * and exit as quickly as possible.
         */
        status = 0;
        for (i=0 ; i<24 ; i++) {
            /* Delay for 125us */
            udelay (125);

            /* Check the status */
            nand_command (mtd, NAND_CMD_STATUS, -1, -1);
            status = (int) readb (this->IO_ADDR);
            if (status & 0x40)
                break;
        }

        /* See if device thinks it succeeded */
        if (status & 0x01) {
            DEBUG (MTD_DEBUG_LEVEL0,
                "nand_writev: " \
                "Failed write, page 0x%08x, " \
                "%6i bytes were succesful\n", page, *retlen);
            nand_deselect ();
            spin_lock_bh (&this->chip_lock);
            this->state = FL_READY;
            wake_up (&this->wq);
            spin_unlock_bh (&this->chip_lock);
            return -EIO;
        }

#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
        /*
         * The NAND device assumes that it is always writing to
         * a cleanly erased page. Hence, it performs its internal
         * write verification only on bits that transitioned from
         * 1 to 0. The device does NOT verify the whole page on a
         * byte by byte basis. It is possible that the page was
         * not completely erased or the page is becoming unusable
         * due to wear. The read with ECC would catch the error
         * later when the ECC page check fails, but we would rather
         * catch it early in the page write stage. Better to write
         * no data than invalid data.
         */
        
        /* Send command to read back the page */
        if (col < mtd->eccsize)
            nand_command (mtd, NAND_CMD_READ0, col, page);
        else
            nand_command (mtd, NAND_CMD_READ1, col - 256, page);

        /* Loop through and verify the data */
        for (i=col ; i < cnt ; i++) {
            if (this->data_buf[i] != readb (this->IO_ADDR)) {
                DEBUG (MTD_DEBUG_LEVEL0,
                    "nand_writev: " \
                    "Failed write verify, page 0x%08x, " \
                    "%6i bytes were succesful\n",
                    page, *retlen);
                nand_deselect ();
                spin_lock_bh (&this->chip_lock);
                this->state = FL_READY;
                wake_up (&this->wq);
                spin_unlock_bh (&this->chip_lock);
                return -EIO;
            }
        }

#ifdef CONFIG_MTD_NAND_ECC
        /*
         * We also want to check that the ECC bytes wrote
         * correctly for the same reasons stated above.
         */
        nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
        for (i=0 ; i < ecc_bytes ; i++) {
            if ((readb (this->IO_ADDR) != this->ecc_code_buf[i]) &&
                    this->ecc_code_buf[i]) {
                DEBUG (MTD_DEBUG_LEVEL0,
                    "nand_writev: Failed ECC write " \
                    "verify, page 0x%08x, " \
                    "%6i bytes were succesful\n",
                    page, i);
                nand_deselect ();
                spin_lock_bh (&this->chip_lock);
                this->state = FL_READY;
                wake_up (&this->wq);
                spin_unlock_bh (&this->chip_lock);
                return -EIO;
            }
        }
#endif

#endif
        /* Update written bytes count */
        *retlen += (cnt - col);

        /* Reset written byte counter and column */
        col = cnt = 0;

        /* Increment page address */
        page++;
    }

    /* De-select the NAND device */
    nand_deselect ();

    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);

    /* Return happy */
    return 0;
}

/*
 * NAND erase a block
 */
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
{
    int i, page, len, status, pages_per_block;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);

    DEBUG (MTD_DEBUG_LEVEL3,
        "nand_erase: start = 0x%08x, len = %i\n",
        (unsigned int) instr->addr, (unsigned int) instr->len);

    /* Start address must align on block boundary */
    if (instr->addr & (mtd->erasesize - 1)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_erase: Unaligned address\n");
        return -EINVAL;
    }

    /* Length must align on block boundary */
    if (instr->len & (mtd->erasesize - 1)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_erase: Length not block aligned\n");
        return -EINVAL;
    }

    /* Do not allow erase past end of device */
    if ((instr->len + instr->addr) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_erase: Erase past end of device\n");
        return -EINVAL;
    }

retry:
    /* Grab the lock and see if the device is available */
    spin_lock_bh (&this->chip_lock);

    switch (this->state) {
    case FL_READY:
        this->state = FL_ERASING;
        break;

    default:
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    };

    /* Shift to get first page */
    page = (int) (instr->addr >> this->page_shift);

    /* Calculate pages in each block */
    pages_per_block = mtd->erasesize / mtd->oobblock;

    /* Select the NAND device */
    nand_select ();

    /* Check the WP bit */
    nand_command (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(readb (this->IO_ADDR) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0,
            "nand_erase: Device is write protected!!!\n");
        nand_deselect ();
        this->state = FL_READY;
        spin_unlock_bh (&this->chip_lock);
        return -EIO;
    }

    /* Loop through the pages */
    len = instr->len;
    while (len) {
        /* Send commands to erase a page */
        nand_command(mtd, NAND_CMD_ERASE1, -1, page);
        nand_command(mtd, NAND_CMD_ERASE2, -1, -1);

        /*
         * Wait for program operation to complete. This could
         * take up to 4000us (4ms) on some devices, so we try
         * and exit as quickly as possible.
         */
        status = 0;
        for (i=0 ; i<32 ; i++) {
            /* Delay for 125us */
            udelay (125);

            /* Check the status */
            nand_command (mtd, NAND_CMD_STATUS, -1, -1);
            status = (int) readb (this->IO_ADDR);
            if (status & 0x40)
                break;
        }

        /* See if block erase succeeded */
        if (status & 0x01) {
            DEBUG (MTD_DEBUG_LEVEL0,
                "nand_erase: " \
                "Failed erase, page 0x%08x\n", page);
            nand_deselect ();
            this->state = FL_READY;
            spin_unlock_bh (&this->chip_lock);
            return -EIO;
        }

        /* Increment page address and decrement length */
        len -= mtd->erasesize;
        page += pages_per_block;

        /* Release the spin lock */
        spin_unlock_bh (&this->chip_lock);

erase_retry:
        /* Check the state and sleep if it changed */
        spin_lock_bh (&this->chip_lock);
        if (this->state == FL_ERASING) {
            continue;
        }
        else {
            set_current_state (TASK_UNINTERRUPTIBLE);
            add_wait_queue (&this->wq, &wait);
            spin_unlock_bh (&this->chip_lock);
            schedule();

            remove_wait_queue (&this->wq, &wait);
            goto erase_retry;
        }
    }
    spin_unlock_bh (&this->chip_lock);

    /* De-select the NAND device */
    nand_deselect ();

    /* Do call back function */
    if (instr->callback)
        instr->callback (instr);

    /* The device is ready */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    spin_unlock_bh (&this->chip_lock);

    /* Return happy */
    return 0;
}

/*
 * NAND sync
 */
static void nand_sync (struct mtd_info *mtd)
{
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE(wait, current);

    DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");

retry:
    /* Grab the spinlock */
    spin_lock_bh(&this->chip_lock);

    /* See what's going on */
    switch(this->state) {
    case FL_READY:
    case FL_SYNCING:
        this->state = FL_SYNCING;
        spin_unlock_bh (&this->chip_lock);
        break;

    default:
        /* Not an idle state */
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule ();

        remove_wait_queue (&this->wq, &wait);
        goto retry;
    }

        /* Lock the device */
    spin_lock_bh (&this->chip_lock);

    /* Set the device to be ready again */
    if (this->state == FL_SYNCING) {
        this->state = FL_READY;
        wake_up (&this->wq);
    }

        /* Unlock the device */
    spin_unlock_bh (&this->chip_lock);
}

/*
 * Scan for the NAND device
 */
int nand_scan (struct mtd_info *mtd)
{
    int i, nand_maf_id, nand_dev_id;
    struct nand_chip *this = mtd->priv;

    /* Select the device */
    nand_select ();

    /* Send the command for reading device ID */
    nand_command (mtd, NAND_CMD_READID, 0x00, -1);

    /* Read manufacturer and device IDs */
    nand_maf_id = readb (this->IO_ADDR);
    nand_dev_id = readb (this->IO_ADDR);

    /* Print and store flash device information */
    for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (nand_maf_id == nand_flash_ids[i].manufacture_id &&
            nand_dev_id == nand_flash_ids[i].model_id) {
            if (!mtd->size) {
                mtd->name = nand_flash_ids[i].name;
                mtd->erasesize = nand_flash_ids[i].erasesize;
                mtd->size = (1 << nand_flash_ids[i].chipshift);
                mtd->eccsize = 256;
                if (nand_flash_ids[i].page256) {
                    mtd->oobblock = 256;
                    mtd->oobsize = 8;
                    this->page_shift = 8;
                }
                else {
                    mtd->oobblock = 512;
                    mtd->oobsize = 16;
                    this->page_shift = 9;
                }
            }
            printk (KERN_INFO "NAND device: Manufacture ID:" \
                " 0x%02x, Chip ID: 0x%02x (%s)\n",
                   nand_maf_id, nand_dev_id, mtd->name);
            break;
        }
    }

    /* Initialize state and spinlock */
    this->state = FL_READY;
    spin_lock_init(&this->chip_lock);

    /* De-select the device */
    nand_deselect ();

    /* Print warning message for no device */
    if (!mtd->size) {
        printk (KERN_WARNING "No NAND device found!!!\n");
        return 1;
    }

    /* Fill in remaining MTD driver data */
    mtd->type = MTD_NANDFLASH;
    mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
    mtd->module = THIS_MODULE;
    mtd->ecctype = MTD_ECC_SW;
    mtd->erase = nand_erase;
    mtd->point = NULL;
    mtd->unpoint = NULL;
    mtd->read = nand_read;
    mtd->write = nand_write;
    mtd->read_ecc = nand_read_ecc;
    mtd->write_ecc = nand_write_ecc;
    mtd->read_oob = nand_read_oob;
    mtd->write_oob = nand_write_oob;
    mtd->readv = NULL;
    mtd->writev = nand_writev;
    mtd->sync = nand_sync;
    mtd->lock = NULL;
    mtd->unlock = NULL;
    mtd->suspend = NULL;
    mtd->resume = NULL;

    /* Return happy */
    return 0;
}

EXPORT_SYMBOL(nand_scan);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com");
MODULE_DESCRIPTION("Generic NAND flash driver code");

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0152 ]--