*
* 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
*
* 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
#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);
}
/**
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);
if (getchip) {
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
}
return res;
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 */
}
/**
- * 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);
* 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;
}
* 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
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;
}
}
/* 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)
}
/* 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
}
/* 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);
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
/* Return happy */
*retlen = 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);
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
return 0;
}
}
/* 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);
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
return ret;
}
}
/* 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);
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
return ret;
}
}
/* 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);
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
+ nand_release_chip(mtd);
*retlen = written;
return ret;
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);
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;
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);
}