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:
parent
ff0dab64b4
commit
2c22120fbd
|
@ -63,6 +63,7 @@ static int __devinit generic_onenand_probe(struct device *dev)
|
|||
}
|
||||
|
||||
info->onenand.mmcontrol = pdata->mmcontrol;
|
||||
info->onenand.irq = platform_get_irq(pdev, 0);
|
||||
|
||||
info->mtd.name = pdev->dev.bus_id;
|
||||
info->mtd.priv = &info->onenand;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/onenand.h>
|
||||
|
@ -339,6 +340,111 @@ static int onenand_wait(struct mtd_info *mtd, int state)
|
|||
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
|
||||
* @param mtd MTD data structure
|
||||
|
@ -1129,7 +1235,6 @@ static void onenand_sync(struct mtd_info *mtd)
|
|||
onenand_release_device(mtd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
|
||||
* @param mtd MTD device structure
|
||||
|
@ -1846,7 +1951,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
if (!this->command)
|
||||
this->command = onenand_command;
|
||||
if (!this->wait)
|
||||
this->wait = onenand_wait;
|
||||
onenand_setup_wait(mtd);
|
||||
|
||||
if (!this->read_bufferram)
|
||||
this->read_bufferram = onenand_read_bufferram;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define __LINUX_MTD_ONENAND_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mtd/onenand_regs.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
|
||||
|
@ -120,6 +121,9 @@ struct onenand_chip {
|
|||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
|
||||
struct completion complete;
|
||||
int irq;
|
||||
|
||||
spinlock_t chip_lock;
|
||||
wait_queue_head_t wq;
|
||||
onenand_state_t state;
|
||||
|
|
Loading…
Reference in New Issue