[MTD] Simplify NAND locking
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Tue, 23 May 2006 09:37:03 +0000 (11:37 +0200)
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Tue, 23 May 2006 09:37:03 +0000 (11:37 +0200)
Replace the chip lock by a the controller lock. For simple drivers a
dummy controller structure is created by the scan code.
This simplifies the locking algorithm in nand_get/release_chip().

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/nand/nand_base.c
include/linux/mtd/nand.h

index 08dffb7a9389abbb56d9289663ff8708ebf45c30..7933ca273c951ab4dfdffdc1b2d7216e6eb09293 100644 (file)
@@ -172,20 +172,12 @@ static void nand_release_device(struct mtd_info *mtd)
        /* De-select the NAND device */
        this->select_chip(mtd, -1);
 
-       if (this->controller) {
-               /* Release the controller and the chip */
-               spin_lock(&this->controller->lock);
-               this->controller->active = NULL;
-               this->state = FL_READY;
-               wake_up(&this->controller->wq);
-               spin_unlock(&this->controller->lock);
-       } else {
-               /* Release the chip */
-               spin_lock(&this->chip_lock);
-               this->state = FL_READY;
-               wake_up(&this->wq);
-               spin_unlock(&this->chip_lock);
-       }
+       /* Release the controller and the chip */
+       spin_lock(&this->controller->lock);
+       this->controller->active = NULL;
+       this->state = FL_READY;
+       wake_up(&this->controller->wq);
+       spin_unlock(&this->controller->lock);
 }
 
 /**
@@ -765,25 +757,18 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column,
  */
 static int nand_get_device(struct nand_chip *this, struct mtd_info *mtd, int new_state)
 {
-       struct nand_chip *active;
-       spinlock_t *lock;
-       wait_queue_head_t *wq;
+       spinlock_t *lock = &this->controller->lock;
+       wait_queue_head_t *wq = &this->controller->wq;
        DECLARE_WAITQUEUE(wait, current);
-
-       lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
-       wq = (this->controller) ? &this->controller->wq : &this->wq;
  retry:
-       active = this;
        spin_lock(lock);
 
        /* Hardware controller shared among independend devices */
-       if (this->controller) {
-               if (this->controller->active)
-                       active = this->controller->active;
-               else
-                       this->controller->active = this;
-       }
-       if (active == this && this->state == FL_READY) {
+       /* Hardware controller shared among independend devices */
+       if (!this->controller->active)
+               this->controller->active = this;
+
+       if (this->controller->active == this && this->state == FL_READY) {
                this->state = new_state;
                spin_unlock(lock);
                return 0;
@@ -2312,6 +2297,22 @@ static void nand_resume(struct mtd_info *mtd)
 
 }
 
+/*
+ * Free allocated data structures
+ */
+static void nand_free_kmem(struct nand_chip *this)
+{
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_OOBBUF_ALLOC)
+               kfree(this->oob_buf);
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_DATABUF_ALLOC)
+               kfree(this->data_buf);
+       /* Controller allocated by nand_scan ? */
+       if (this->options & NAND_CONTROLLER_ALLOC)
+               kfree(this->controller);
+}
+
 /* module_text_address() isn't exported, and it's mostly a pointless
    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. */
@@ -2522,9 +2523,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                len = mtd->oobblock + mtd->oobsize;
                this->data_buf = kmalloc(len, GFP_KERNEL);
                if (!this->data_buf) {
-                       if (this->options & NAND_OOBBUF_ALLOC)
-                               kfree(this->oob_buf);
                        printk(KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+                       nand_free_kmem(this);
                        return -ENOMEM;
                }
                this->options |= NAND_DATABUF_ALLOC;
@@ -2657,8 +2657,17 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
 
        /* Initialize state, waitqueue and spinlock */
        this->state = FL_READY;
-       init_waitqueue_head(&this->wq);
-       spin_lock_init(&this->chip_lock);
+       if (!this->controller) {
+               this->controller = kzalloc(sizeof(struct nand_hw_control),
+                                          GFP_KERNEL);
+               if (!this->controller) {
+                       nand_free_kmem(this);
+                       return -ENOMEM;
+               }
+               this->options |= NAND_CONTROLLER_ALLOC;
+       }
+       init_waitqueue_head(&this->controller->wq);
+       spin_lock_init(&this->controller->lock);
 
        /* De-select the device */
        this->select_chip(mtd, -1);
@@ -2718,12 +2727,8 @@ void nand_release(struct mtd_info *mtd)
 
        /* Free bad block table memory */
        kfree(this->bbt);
-       /* Buffer allocated by nand_scan ? */
-       if (this->options & NAND_OOBBUF_ALLOC)
-               kfree(this->oob_buf);
-       /* Buffer allocated by nand_scan ? */
-       if (this->options & NAND_DATABUF_ALLOC)
-               kfree(this->data_buf);
+       /* Free buffers */
+       nand_free_kmem(this);
 }
 
 EXPORT_SYMBOL_GPL(nand_scan);
index da5e67b3fc70ec8851f5aee64ce9a9b4f7ea6686..b8792be3c4e057905d95630174db3b783e20c222 100644 (file)
@@ -227,6 +227,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
 #define NAND_SKIP_BBTSCAN      0x00040000
 
 /* Options set by nand scan */
+/* Nand scan has allocated controller struct */
+#define NAND_CONTROLLER_ALLOC  0x20000000
 /* Nand scan has allocated oob_buf */
 #define NAND_OOBBUF_ALLOC      0x40000000
 /* Nand scan has allocated data_buf */
@@ -294,7 +296,6 @@ struct nand_hw_control {
  * @eccbytes:          [INTERN] number of ecc bytes per ecc-calculation step
  * @eccsteps:          [INTERN] number of ecc calculation steps per page
  * @chip_delay:                [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
- * @chip_lock:         [INTERN] spinlock used to protect access to this structure and the chip
  * @wq:                        [INTERN] wait queue to sleep on if a NAND operation is in progress
  * @state:             [INTERN] the current state of the NAND device
  * @page_shift:                [INTERN] number of address bits in a page (column address bits)
@@ -317,7 +318,8 @@ struct nand_hw_control {
  * @bbt_td:            [REPLACEABLE] bad block table descriptor for flash lookup
  * @bbt_md:            [REPLACEABLE] bad block table mirror descriptor
  * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan
- * @controller:                [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
+ * @controller:                [REPLACEABLE] a pointer to a hardware controller structure
+ *                     which is shared among multiple independend devices
  * @priv:              [OPTIONAL] pointer to private chip date
  * @errstat:           [OPTIONAL] hardware specific function to perform additional error status checks
  *                     (determine if errors are correctable)
@@ -352,7 +354,6 @@ struct nand_chip {
        int             eccbytes;
        int             eccsteps;
        int             chip_delay;
-       spinlock_t      chip_lock;
        wait_queue_head_t wq;
        nand_state_t    state;
        int             page_shift;