MTD: OneNAND: interrupt based wait support

We can use the two methods to wait.
  1. polling: read interrupt status register
  2. interrupt: use kernel ineterrupt mechanism

  To use interrupt method, you first connect onenand interrupt pin to your
platform and configure interrupt properly

Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
This commit is contained in:
Kyungmin Park 2006-11-16 11:23:48 +09:00
parent ff0dab64b4
commit 2c22120fbd
3 changed files with 112 additions and 2 deletions

View File

@ -63,6 +63,7 @@ static int __devinit generic_onenand_probe(struct device *dev)
} }
info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.mmcontrol = pdata->mmcontrol;
info->onenand.irq = platform_get_irq(pdev, 0);
info->mtd.name = pdev->dev.bus_id; info->mtd.name = pdev->dev.bus_id;
info->mtd.priv = &info->onenand; info->mtd.priv = &info->onenand;

View File

@ -13,6 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h> #include <linux/mtd/onenand.h>
@ -339,6 +340,111 @@ static int onenand_wait(struct mtd_info *mtd, int state)
return 0; return 0;
} }
/*
* onenand_interrupt - [DEFAULT] onenand interrupt handler
* @param irq onenand interrupt number
* @param dev_id interrupt data
*
* complete the work
*/
static irqreturn_t onenand_interrupt(int irq, void *data)
{
struct onenand_chip *this = (struct onenand_chip *) data;
/* To handle shared interrupt */
if (!this->complete.done)
complete(&this->complete);
return IRQ_HANDLED;
}
/*
* onenand_interrupt_wait - [DEFAULT] wait until the command is done
* @param mtd MTD device structure
* @param state state to select the max. timeout value
*
* Wait for command done.
*/
static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
{
struct onenand_chip *this = mtd->priv;
/* To prevent soft lockup */
touch_softlockup_watchdog();
wait_for_completion(&this->complete);
return onenand_wait(mtd, state);
}
/*
* onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
* @param mtd MTD device structure
* @param state state to select the max. timeout value
*
* Try interrupt based wait (It is used one-time)
*/
static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
{
struct onenand_chip *this = mtd->priv;
unsigned long remain, timeout;
/* We use interrupt wait first */
this->wait = onenand_interrupt_wait;
/* To prevent soft lockup */
touch_softlockup_watchdog();
timeout = msecs_to_jiffies(100);
remain = wait_for_completion_timeout(&this->complete, timeout);
if (!remain) {
printk(KERN_INFO "OneNAND: There's no interrupt. "
"We use the normal wait\n");
/* Release the irq */
free_irq(this->irq, this);
this->wait = onenand_wait;
}
return onenand_wait(mtd, state);
}
/*
* onenand_setup_wait - [OneNAND Interface] setup onenand wait method
* @param mtd MTD device structure
*
* There's two method to wait onenand work
* 1. polling - read interrupt status register
* 2. interrupt - use the kernel interrupt method
*/
static void onenand_setup_wait(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
int syscfg;
init_completion(&this->complete);
if (this->irq <= 0) {
this->wait = onenand_wait;
return;
}
if (request_irq(this->irq, &onenand_interrupt,
IRQF_SHARED, "onenand", this)) {
/* If we can't get irq, use the normal wait */
this->wait = onenand_wait;
return;
}
/* Enable interrupt */
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
syscfg |= ONENAND_SYS_CFG1_IOBE;
this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
this->wait = onenand_try_interrupt_wait;
}
/** /**
* onenand_bufferram_offset - [DEFAULT] BufferRAM offset * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
* @param mtd MTD data structure * @param mtd MTD data structure
@ -1129,7 +1235,6 @@ static void onenand_sync(struct mtd_info *mtd)
onenand_release_device(mtd); onenand_release_device(mtd);
} }
/** /**
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
* @param mtd MTD device structure * @param mtd MTD device structure
@ -1846,7 +1951,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
if (!this->command) if (!this->command)
this->command = onenand_command; this->command = onenand_command;
if (!this->wait) if (!this->wait)
this->wait = onenand_wait; onenand_setup_wait(mtd);
if (!this->read_bufferram) if (!this->read_bufferram)
this->read_bufferram = onenand_read_bufferram; this->read_bufferram = onenand_read_bufferram;

View File

@ -13,6 +13,7 @@
#define __LINUX_MTD_ONENAND_H #define __LINUX_MTD_ONENAND_H
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/mtd/onenand_regs.h> #include <linux/mtd/onenand_regs.h>
#include <linux/mtd/bbm.h> #include <linux/mtd/bbm.h>
@ -120,6 +121,9 @@ struct onenand_chip {
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
int (*scan_bbt)(struct mtd_info *mtd); int (*scan_bbt)(struct mtd_info *mtd);
struct completion complete;
int irq;
spinlock_t chip_lock; spinlock_t chip_lock;
wait_queue_head_t wq; wait_queue_head_t wq;
onenand_state_t state; onenand_state_t state;