Skip to content

Commit 63f1474

Browse files
saschahauertorvalds
authored andcommitted
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: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: John Ogness <john.ogness@linutronix.de> Tested-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent f68c834 commit 63f1474

File tree

1 file changed

+83
-9
lines changed

1 file changed

+83
-9
lines changed

drivers/mtd/nand/mxc_nand.c

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <linux/clk.h>
3131
#include <linux/err.h>
3232
#include <linux/io.h>
33+
#include <linux/irq.h>
34+
#include <linux/completion.h>
3335

3436
#include <asm/mach/flash.h>
3537
#include <mach/mxc_nand.h>
@@ -151,7 +153,7 @@ struct mxc_nand_host {
151153
int irq;
152154
int eccsize;
153155

154-
wait_queue_head_t irq_waitq;
156+
struct completion op_completion;
155157

156158
uint8_t *data_buf;
157159
unsigned int buf_start;
@@ -164,6 +166,7 @@ struct mxc_nand_host {
164166
void (*send_read_id)(struct mxc_nand_host *);
165167
uint16_t (*get_dev_status)(struct mxc_nand_host *);
166168
int (*check_int)(struct mxc_nand_host *);
169+
void (*irq_control)(struct mxc_nand_host *, int);
167170
};
168171

169172
/* OOB placement block for use with hardware ecc generation */
@@ -216,9 +219,12 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
216219
{
217220
struct mxc_nand_host *host = dev_id;
218221

219-
disable_irq_nosync(irq);
222+
if (!host->check_int(host))
223+
return IRQ_NONE;
220224

221-
wake_up(&host->irq_waitq);
225+
host->irq_control(host, 0);
226+
227+
complete(&host->op_completion);
222228

223229
return IRQ_HANDLED;
224230
}
@@ -245,11 +251,54 @@ static int check_int_v1_v2(struct mxc_nand_host *host)
245251
if (!(tmp & NFC_V1_V2_CONFIG2_INT))
246252
return 0;
247253

248-
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
254+
if (!cpu_is_mx21())
255+
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
249256

250257
return 1;
251258
}
252259

260+
/*
261+
* It has been observed that the i.MX21 cannot read the CONFIG2:INT bit
262+
* if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the
263+
* driver can enable/disable the irq line rather than simply masking the
264+
* interrupts.
265+
*/
266+
static void irq_control_mx21(struct mxc_nand_host *host, int activate)
267+
{
268+
if (activate)
269+
enable_irq(host->irq);
270+
else
271+
disable_irq_nosync(host->irq);
272+
}
273+
274+
static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
275+
{
276+
uint16_t tmp;
277+
278+
tmp = readw(NFC_V1_V2_CONFIG1);
279+
280+
if (activate)
281+
tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
282+
else
283+
tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
284+
285+
writew(tmp, NFC_V1_V2_CONFIG1);
286+
}
287+
288+
static void irq_control_v3(struct mxc_nand_host *host, int activate)
289+
{
290+
uint32_t tmp;
291+
292+
tmp = readl(NFC_V3_CONFIG2);
293+
294+
if (activate)
295+
tmp &= ~NFC_V3_CONFIG2_INT_MSK;
296+
else
297+
tmp |= NFC_V3_CONFIG2_INT_MSK;
298+
299+
writel(tmp, NFC_V3_CONFIG2);
300+
}
301+
253302
/* This function polls the NANDFC to wait for the basic operation to
254303
* complete by checking the INT bit of config2 register.
255304
*/
@@ -259,10 +308,9 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
259308

260309
if (useirq) {
261310
if (!host->check_int(host)) {
262-
263-
enable_irq(host->irq);
264-
265-
wait_event(host->irq_waitq, host->check_int(host));
311+
INIT_COMPLETION(host->op_completion);
312+
host->irq_control(host, 1);
313+
wait_for_completion(&host->op_completion);
266314
}
267315
} else {
268316
while (max_retries-- > 0) {
@@ -799,6 +847,7 @@ static void preset_v3(struct mtd_info *mtd)
799847
NFC_V3_CONFIG2_2CMD_PHASES |
800848
NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
801849
NFC_V3_CONFIG2_ST_CMD(0x70) |
850+
NFC_V3_CONFIG2_INT_MSK |
802851
NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
803852

804853
if (chip->ecc.mode == NAND_ECC_HW)
@@ -1024,6 +1073,10 @@ static int __init mxcnd_probe(struct platform_device *pdev)
10241073
host->send_read_id = send_read_id_v1_v2;
10251074
host->get_dev_status = get_dev_status_v1_v2;
10261075
host->check_int = check_int_v1_v2;
1076+
if (cpu_is_mx21())
1077+
host->irq_control = irq_control_mx21;
1078+
else
1079+
host->irq_control = irq_control_v1_v2;
10271080
}
10281081

10291082
if (nfc_is_v21()) {
@@ -1062,6 +1115,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
10621115
host->send_read_id = send_read_id_v3;
10631116
host->check_int = check_int_v3;
10641117
host->get_dev_status = get_dev_status_v3;
1118+
host->irq_control = irq_control_v3;
10651119
oob_smallpage = &nandv2_hw_eccoob_smallpage;
10661120
oob_largepage = &nandv2_hw_eccoob_largepage;
10671121
} else
@@ -1093,14 +1147,34 @@ static int __init mxcnd_probe(struct platform_device *pdev)
10931147
this->options |= NAND_USE_FLASH_BBT;
10941148
}
10951149

1096-
init_waitqueue_head(&host->irq_waitq);
1150+
init_completion(&host->op_completion);
10971151

10981152
host->irq = platform_get_irq(pdev, 0);
10991153

1154+
/*
1155+
* mask the interrupt. For i.MX21 explicitely call
1156+
* irq_control_v1_v2 to use the mask bit. We can't call
1157+
* disable_irq_nosync() for an interrupt we do not own yet.
1158+
*/
1159+
if (cpu_is_mx21())
1160+
irq_control_v1_v2(host, 0);
1161+
else
1162+
host->irq_control(host, 0);
1163+
11001164
err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
11011165
if (err)
11021166
goto eirq;
11031167

1168+
host->irq_control(host, 0);
1169+
1170+
/*
1171+
* Now that the interrupt is disabled make sure the interrupt
1172+
* mask bit is cleared on i.MX21. Otherwise we can't read
1173+
* the interrupt status bit on this machine.
1174+
*/
1175+
if (cpu_is_mx21())
1176+
irq_control_v1_v2(host, 1);
1177+
11041178
/* first scan to find the device and get the page size */
11051179
if (nand_scan_ident(mtd, 1, NULL)) {
11061180
err = -ENXIO;

0 commit comments

Comments
 (0)