*
* 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.115 2004/08/09 13:19:45 dwmw2 Exp $
+ * $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner 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_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
/**
- * nand_release_chip - [GENERIC] release chip
+ * nand_release_device - [GENERIC] release chip
* @mtd: MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
-static void nand_release_chip (struct mtd_info *mtd)
+static void nand_release_device (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_bh (&this->chip_lock);
+ spin_lock (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
- spin_unlock_bh (&this->chip_lock);
+ spin_unlock (&this->chip_lock);
}
/**
chipnr = (int)(ofs >> this->chip_shift);
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_READING);
+ nand_get_device (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_chip(mtd);
+ nand_release_device(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 higher density devices */
- if (this->chipsize & 0x0c000000)
+ /* One more address cycle for devices > 32MiB */
+ if (this->chipsize > (32 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
}
/**
- * nand_get_chip - [GENERIC] Get chip for selected access
+ * nand_get_device - [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_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+static void nand_get_device (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:
- spin_lock_bh (&this->chip_lock);
-
- if (this->state == FL_READY) {
- this->state = new_state;
- spin_unlock_bh (&this->chip_lock);
- return;
+ /* 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);
}
-
+
+ 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 (&this->wq, &wait);
- spin_unlock_bh (&this->chip_lock);
+ add_wait_queue (&active->wq, &wait);
+ spin_unlock (&active->chip_lock);
schedule ();
- remove_wait_queue (&this->wq, &wait);
+ remove_wait_queue (&active->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) {
- spin_unlock_bh (&this->chip_lock);
+ if (this->state != state)
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_chip (this, mtd ,FL_READING);
+ nand_get_device (this, mtd ,FL_READING);
/* use userspace supplied oobinfo, if zero */
if (oobsel == NULL)
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
/*
* Return success, if no ECC failures, else -EBADMSG
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd , FL_READING);
+ nand_get_device (this, mtd , FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
/* Return happy */
*retlen = len;
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd , FL_READING);
+ nand_get_device (this, mtd , FL_READING);
this->select_chip (mtd, chip);
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
return 0;
}
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (this, mtd, FL_WRITING);
/* Calculate chipnr */
chipnr = (int)(to >> this->chip_shift);
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
return ret;
}
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (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_chip(mtd);
+ nand_release_device(mtd);
return ret;
}
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (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_chip(mtd);
+ nand_release_device(mtd);
*retlen = written;
return ret;
instr->fail_addr = 0xffffffff;
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_ERASING);
+ nand_get_device (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_chip(mtd);
+ nand_release_device(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");
-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);
+ /* 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);
}