This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / mtd / nand / nand_base.c
index 181b952..ff6adf4 100644 (file)
  *  
  *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
  *
- *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared
- *             among multiple independend devices. Suggestions and initial patch
- *             from Ben Dooks <ben-mtd@fluff.org>
- *
  * Credits:
  *     David Woodhouse for adding multichip support  
  *     
@@ -41,7 +37,7 @@
  *     The AG-AND chips have nice features for speed improvement,
  *     which are not supported yet. Read / program 4 pages in one go.
  *
- * $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner Exp $
+ * $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 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
@@ -135,31 +131,25 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
 #define nand_verify_pages(...) (0)
 #endif
                
-static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
 
 /**
- * nand_release_device - [GENERIC] release chip
+ * nand_release_chip - [GENERIC] release chip
  * @mtd:       MTD device structure
  * 
  * Deselect, release chip lock and wake up anyone waiting on the device 
  */
-static void nand_release_device (struct mtd_info *mtd)
+static void nand_release_chip (struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
 
        /* De-select the NAND device */
        this->select_chip(mtd, -1);
-       /* Do we have a hardware controller ? */
-       if (this->controller) {
-               spin_lock(&this->controller->lock);
-               this->controller->active = NULL;
-               spin_unlock(&this->controller->lock);
-       }
        /* Release the chip */
-       spin_lock (&this->chip_lock);
+       spin_lock_bh (&this->chip_lock);
        this->state = FL_READY;
        wake_up (&this->wq);
-       spin_unlock (&this->chip_lock);
+       spin_unlock_bh (&this->chip_lock);
 }
 
 /**
@@ -398,7 +388,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                chipnr = (int)(ofs >> this->chip_shift);
 
                /* Grab the lock and see if the device is available */
-               nand_get_device (this, mtd, FL_READING);
+               nand_get_chip (this, mtd, FL_READING);
 
                /* Select the NAND device */
                this->select_chip(mtd, chipnr);
@@ -420,7 +410,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                
        if (getchip) {
                /* Deselect and wake up anyone waiting on the device */
-               nand_release_device(mtd);
+               nand_release_chip(mtd);
        }       
        
        return res;
@@ -543,8 +533,8 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
                if (page_addr != -1) {
                        this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
                        this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
-                       /* One more address cycle for devices > 32MiB */
-                       if (this->chipsize > (32 << 20))
+                       /* One more address cycle for higher density devices */
+                       if (this->chipsize & 0x0c000000) 
                                this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
                }
                /* Latch in address */
@@ -699,16 +689,15 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
 }
 
 /**
- * nand_get_device - [GENERIC] Get chip for selected access
+ * nand_get_chip - [GENERIC] Get chip for selected access
  * @this:      the nand chip descriptor
  * @mtd:       MTD device structure
  * @new_state: the state which is requested 
  *
  * Get the device and lock it for exclusive access
  */
-static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
 {
-       struct nand_chip *active = this;
 
        DECLARE_WAITQUEUE (wait, current);
 
@@ -716,29 +705,19 @@ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int n
         * Grab the lock and see if the device is available 
        */
 retry:
-       /* Hardware controller shared among independend devices */
-       if (this->controller) {
-               spin_lock (&this->controller->lock);
-               if (this->controller->active)
-                       active = this->controller->active;
-               else
-                       this->controller->active = this;
-               spin_unlock (&this->controller->lock);
+       spin_lock_bh (&this->chip_lock);
+
+       if (this->state == FL_READY) {
+               this->state = new_state;
+               spin_unlock_bh (&this->chip_lock);
+               return;
        }
-       
-       if (active == this) {
-               spin_lock (&this->chip_lock);
-               if (this->state == FL_READY) {
-                       this->state = new_state;
-                       spin_unlock (&this->chip_lock);
-                       return;
-               }
-       }       
+
        set_current_state (TASK_UNINTERRUPTIBLE);
-       add_wait_queue (&active->wq, &wait);
-       spin_unlock (&active->chip_lock);
+       add_wait_queue (&this->wq, &wait);
+       spin_unlock_bh (&this->chip_lock);
        schedule ();
-       remove_wait_queue (&active->wq, &wait);
+       remove_wait_queue (&this->wq, &wait);
        goto retry;
 }
 
@@ -768,6 +747,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
         * any case on any machine. */
        ndelay (100);
 
+       spin_lock_bh (&this->chip_lock);
        if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
                this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
        else    
@@ -775,19 +755,24 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 
        while (time_before(jiffies, timeo)) {           
                /* Check, if we were interrupted */
-               if (this->state != state)
+               if (this->state != state) {
+                       spin_unlock_bh (&this->chip_lock);
                        return 0;
-
+               }
                if (this->dev_ready) {
                        if (this->dev_ready(mtd))
-                               break;  
-               } else {
-                       if (this->read_byte(mtd) & NAND_STATUS_READY)
                                break;
                }
+               if (this->read_byte(mtd) & NAND_STATUS_READY)
+                       break;
+                                               
+               spin_unlock_bh (&this->chip_lock);
                yield ();
+               spin_lock_bh (&this->chip_lock);
        }
        status = (int) this->read_byte(mtd);
+       spin_unlock_bh (&this->chip_lock);
+
        return status;
 }
 
@@ -1066,7 +1051,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd ,FL_READING);
+       nand_get_chip (this, mtd ,FL_READING);
 
        /* use userspace supplied oobinfo, if zero */
        if (oobsel == NULL)
@@ -1296,7 +1281,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
        }
 
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        /*
         * Return success, if no ECC failures, else -EBADMSG
@@ -1343,7 +1328,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd , FL_READING);
+       nand_get_chip (this, mtd , FL_READING);
 
        /* Select the NAND device */
        this->select_chip(mtd, chipnr);
@@ -1394,7 +1379,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
        }
 
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        /* Return happy */
        *retlen = len;
@@ -1428,7 +1413,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd , FL_READING);
+       nand_get_chip (this, mtd , FL_READING);
 
        this->select_chip (mtd, chip);
        
@@ -1457,7 +1442,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
        }
 
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
        return 0;
 }
 
@@ -1579,7 +1564,7 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_WRITING);
+       nand_get_chip (this, mtd, FL_WRITING);
 
        /* Calculate chipnr */
        chipnr = (int)(to >> this->chip_shift);
@@ -1684,7 +1669,7 @@ cmp:
 
 out:
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        return ret;
 }
@@ -1724,7 +1709,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_WRITING);
+       nand_get_chip (this, mtd, FL_WRITING);
 
        /* Select the NAND device */
        this->select_chip(mtd, chipnr);
@@ -1786,7 +1771,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
        ret = 0;
 out:
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        return ret;
 }
@@ -1853,7 +1838,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_WRITING);
+       nand_get_chip (this, mtd, FL_WRITING);
 
        /* Get the current chip-nr */
        chipnr = (int) (to >> this->chip_shift);
@@ -1967,7 +1952,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
        ret = 0;
 out:
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        *retlen = written;
        return ret;
@@ -2056,7 +2041,7 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
        instr->fail_addr = 0xffffffff;
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_ERASING);
+       nand_get_chip (this, mtd, FL_ERASING);
 
        /* Shift to get first page */
        page = (int) (instr->addr >> this->page_shift);
@@ -2127,7 +2112,7 @@ erase_exit:
                mtd_erase_callback(instr);
 
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       nand_release_chip(mtd);
 
        /* Return more or less happy */
        return ret;
@@ -2142,13 +2127,43 @@ erase_exit:
 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");
 
-       /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_SYNCING);
-       /* Release it and go back */
-       nand_release_device (mtd);
+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);
 }