Commit 63f1474c authored by Sascha Hauer's avatar Sascha Hauer Committed by Linus Torvalds

mxc_nand: do not depend on disabling the irq in the interrupt handler

This patch reverts the driver to enabling/disabling the NFC interrupt
mask rather than enabling/disabling the system interrupt.  This cleans
up the driver so that it doesn't rely on interrupts being disabled
within the interrupt handler.

For i.MX21 we keep the current behaviour, that is calling
enable_irq/disable_irq_nosync to enable/disable interrupts.  This patch
is based on earlier work by John Ogness.
Signed-off-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Acked-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Tested-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f68c834b
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h>
#include <linux/completion.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <mach/mxc_nand.h> #include <mach/mxc_nand.h>
...@@ -151,7 +153,7 @@ struct mxc_nand_host { ...@@ -151,7 +153,7 @@ struct mxc_nand_host {
int irq; int irq;
int eccsize; int eccsize;
wait_queue_head_t irq_waitq; struct completion op_completion;
uint8_t *data_buf; uint8_t *data_buf;
unsigned int buf_start; unsigned int buf_start;
...@@ -164,6 +166,7 @@ struct mxc_nand_host { ...@@ -164,6 +166,7 @@ struct mxc_nand_host {
void (*send_read_id)(struct mxc_nand_host *); void (*send_read_id)(struct mxc_nand_host *);
uint16_t (*get_dev_status)(struct mxc_nand_host *); uint16_t (*get_dev_status)(struct mxc_nand_host *);
int (*check_int)(struct mxc_nand_host *); int (*check_int)(struct mxc_nand_host *);
void (*irq_control)(struct mxc_nand_host *, int);
}; };
/* OOB placement block for use with hardware ecc generation */ /* OOB placement block for use with hardware ecc generation */
...@@ -216,9 +219,12 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) ...@@ -216,9 +219,12 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
{ {
struct mxc_nand_host *host = dev_id; struct mxc_nand_host *host = dev_id;
disable_irq_nosync(irq); if (!host->check_int(host))
return IRQ_NONE;
wake_up(&host->irq_waitq); host->irq_control(host, 0);
complete(&host->op_completion);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -245,11 +251,54 @@ static int check_int_v1_v2(struct mxc_nand_host *host) ...@@ -245,11 +251,54 @@ static int check_int_v1_v2(struct mxc_nand_host *host)
if (!(tmp & NFC_V1_V2_CONFIG2_INT)) if (!(tmp & NFC_V1_V2_CONFIG2_INT))
return 0; return 0;
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); if (!cpu_is_mx21())
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
return 1; return 1;
} }
/*
* It has been observed that the i.MX21 cannot read the CONFIG2:INT bit
* if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the
* driver can enable/disable the irq line rather than simply masking the
* interrupts.
*/
static void irq_control_mx21(struct mxc_nand_host *host, int activate)
{
if (activate)
enable_irq(host->irq);
else
disable_irq_nosync(host->irq);
}
static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
{
uint16_t tmp;
tmp = readw(NFC_V1_V2_CONFIG1);
if (activate)
tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
else
tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
writew(tmp, NFC_V1_V2_CONFIG1);
}
static void irq_control_v3(struct mxc_nand_host *host, int activate)
{
uint32_t tmp;
tmp = readl(NFC_V3_CONFIG2);
if (activate)
tmp &= ~NFC_V3_CONFIG2_INT_MSK;
else
tmp |= NFC_V3_CONFIG2_INT_MSK;
writel(tmp, NFC_V3_CONFIG2);
}
/* This function polls the NANDFC to wait for the basic operation to /* This function polls the NANDFC to wait for the basic operation to
* complete by checking the INT bit of config2 register. * complete by checking the INT bit of config2 register.
*/ */
...@@ -259,10 +308,9 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) ...@@ -259,10 +308,9 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
if (useirq) { if (useirq) {
if (!host->check_int(host)) { if (!host->check_int(host)) {
INIT_COMPLETION(host->op_completion);
enable_irq(host->irq); host->irq_control(host, 1);
wait_for_completion(&host->op_completion);
wait_event(host->irq_waitq, host->check_int(host));
} }
} else { } else {
while (max_retries-- > 0) { while (max_retries-- > 0) {
...@@ -799,6 +847,7 @@ static void preset_v3(struct mtd_info *mtd) ...@@ -799,6 +847,7 @@ static void preset_v3(struct mtd_info *mtd)
NFC_V3_CONFIG2_2CMD_PHASES | NFC_V3_CONFIG2_2CMD_PHASES |
NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
NFC_V3_CONFIG2_ST_CMD(0x70) | NFC_V3_CONFIG2_ST_CMD(0x70) |
NFC_V3_CONFIG2_INT_MSK |
NFC_V3_CONFIG2_NUM_ADDR_PHASE0; NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
if (chip->ecc.mode == NAND_ECC_HW) if (chip->ecc.mode == NAND_ECC_HW)
...@@ -1024,6 +1073,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) ...@@ -1024,6 +1073,10 @@ static int __init mxcnd_probe(struct platform_device *pdev)
host->send_read_id = send_read_id_v1_v2; host->send_read_id = send_read_id_v1_v2;
host->get_dev_status = get_dev_status_v1_v2; host->get_dev_status = get_dev_status_v1_v2;
host->check_int = check_int_v1_v2; host->check_int = check_int_v1_v2;
if (cpu_is_mx21())
host->irq_control = irq_control_mx21;
else
host->irq_control = irq_control_v1_v2;
} }
if (nfc_is_v21()) { if (nfc_is_v21()) {
...@@ -1062,6 +1115,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) ...@@ -1062,6 +1115,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
host->send_read_id = send_read_id_v3; host->send_read_id = send_read_id_v3;
host->check_int = check_int_v3; host->check_int = check_int_v3;
host->get_dev_status = get_dev_status_v3; host->get_dev_status = get_dev_status_v3;
host->irq_control = irq_control_v3;
oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_smallpage = &nandv2_hw_eccoob_smallpage;
oob_largepage = &nandv2_hw_eccoob_largepage; oob_largepage = &nandv2_hw_eccoob_largepage;
} else } else
...@@ -1093,14 +1147,34 @@ static int __init mxcnd_probe(struct platform_device *pdev) ...@@ -1093,14 +1147,34 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->options |= NAND_USE_FLASH_BBT; this->options |= NAND_USE_FLASH_BBT;
} }
init_waitqueue_head(&host->irq_waitq); init_completion(&host->op_completion);
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
/*
* mask the interrupt. For i.MX21 explicitely call
* irq_control_v1_v2 to use the mask bit. We can't call
* disable_irq_nosync() for an interrupt we do not own yet.
*/
if (cpu_is_mx21())
irq_control_v1_v2(host, 0);
else
host->irq_control(host, 0);
err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
if (err) if (err)
goto eirq; goto eirq;
host->irq_control(host, 0);
/*
* Now that the interrupt is disabled make sure the interrupt
* mask bit is cleared on i.MX21. Otherwise we can't read
* the interrupt status bit on this machine.
*/
if (cpu_is_mx21())
irq_control_v1_v2(host, 1);
/* first scan to find the device and get the page size */ /* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) { if (nand_scan_ident(mtd, 1, NULL)) {
err = -ENXIO; err = -ENXIO;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment