[PATCH] ARM: MXC: mxc_nand: support i.MX21

Ivo Clarysse ivo.clarysse at gmail.com
Tue Apr 6 09:52:23 EDT 2010


Allow mxc_nand.c to function on i.MX21 SoCs, since:

1) On i.MX21, if the NFC_INT_MASK bit in NFC_CONFIG1 is set, the NFC_INT
  bit of NFC_CONFIG2 always reads out zero, even if an operation is
  completed.

2) On i.MX21, sending a RESET command to the NAND flash controller does
  not trigger an interrupt, nor does it cause the NFC_INT bit of
  NFC_CONFIG2 to get set.

To accommodate for this, I modified the interrupt handler to use
completions instead of a wait queue, and check the value of NFC_CONFIG2
in the interrupt handler instead of after wait_event(..)

To allow polling for a basic NFC operation to complete, I leave
the interrupt handler disabled using disable_irq(..) and only enable it
for interrupt-based basic operations.

Signed-off-by: Ivo Clarysse <ivo.clarysse at gmail.com>
---
diff -u -r -N linux-2.6.33.2/drivers/mtd/nand/mxc_nand.c
linux-2.6.33.2-mine/drivers/mtd/nand/mxc_nand.c
--- linux-2.6.33.2/drivers/mtd/nand/mxc_nand.c	2010-04-02
01:02:33.000000000 +0200
+++ linux-2.6.33.2-mine/drivers/mtd/nand/mxc_nand.c	2010-04-06
15:40:55.000000000 +0200
@@ -38,7 +38,7 @@
 #define DRIVER_NAME "mxc_nand"

 #define nfc_is_v21()		(cpu_is_mx25() || cpu_is_mx35())
-#define nfc_is_v1()		(cpu_is_mx31() || cpu_is_mx27())
+#define nfc_is_v1()		(cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())

 /* Addresses for NFC registers */
 #define NFC_BUF_SIZE		0xE00
@@ -111,7 +111,7 @@
 	int			clk_act;
 	int			irq;

-	wait_queue_head_t	irq_waitq;
+	struct completion	op_completion;

 	uint8_t			*data_buf;
 	unsigned int		buf_start;
@@ -168,13 +168,21 @@
 {
 	struct mxc_nand_host *host = dev_id;

-	uint16_t tmp;
+	uint16_t tmp, config2;
+
+	/* On i.MX21, setting NFC_INT_MSK of NFC_CONFIG2
+	 * will clear NFC_INT in NFC_CONFIG1, so we read
+	 * NFC_CONFIG2 first.
+	 */
+	config2 = readw(host->regs + NFC_CONFIG2);

 	tmp = readw(host->regs + NFC_CONFIG1);
 	tmp |= NFC_INT_MSK; /* Disable interrupt */
 	writew(tmp, host->regs + NFC_CONFIG1);

-	wake_up(&host->irq_waitq);
+	if (config2 & NFC_INT) {
+		complete(&host->op_completion);
+	}

 	return IRQ_HANDLED;
 }
@@ -185,7 +193,7 @@
 static void wait_op_done(struct mxc_nand_host *host, int useirq)
 {
 	uint32_t tmp;
-	int max_retries = 2000;
+	int max_retries = 8000;

 	if (useirq) {
 		if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) {
@@ -194,14 +202,28 @@
 			tmp  &= ~NFC_INT_MSK;	/* Enable interrupt */
 			writew(tmp, host->regs + NFC_CONFIG1);

-			wait_event(host->irq_waitq,
-				readw(host->regs + NFC_CONFIG2) & NFC_INT);
+			INIT_COMPLETION(host->op_completion);
+
+			if (cpu_is_mx21()) {
+				enable_irq(host->irq);
+			}
+
+			wait_for_completion(&host->op_completion);
+
+			if (cpu_is_mx21()) {
+				disable_irq(host->irq);
+			}

 			tmp = readw(host->regs + NFC_CONFIG2);
 			tmp  &= ~NFC_INT;
 			writew(tmp, host->regs + NFC_CONFIG2);
 		}
 	} else {
+		if (cpu_is_mx21()) {
+			tmp = readw(host->regs + NFC_CONFIG1);
+			tmp  &= ~NFC_INT_MSK;	/* Enable interrupt */
+			writew(tmp, host->regs + NFC_CONFIG1);
+		}
 		while (max_retries-- > 0) {
 			if (readw(host->regs + NFC_CONFIG2) & NFC_INT) {
 				tmp = readw(host->regs + NFC_CONFIG2);
@@ -211,6 +233,11 @@
 			}
 			udelay(1);
 		}
+		if (cpu_is_mx21()) {
+			tmp = readw(host->regs + NFC_CONFIG1);
+			tmp  |= NFC_INT_MSK;
+			writew(tmp, host->regs + NFC_CONFIG1);
+		}
 		if (max_retries < 0)
 			DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n",
 			      __func__);
@@ -559,6 +586,24 @@

 	/* Command pre-processing step */
 	switch (command) {
+	case NAND_CMD_RESET:
+		if (cpu_is_mx21()) {
+			int max_retries = 100;
+			writew(command, host->regs + NFC_FLASH_CMD);
+			writew(NFC_CMD, host->regs + NFC_CONFIG2);
+			/* Reset completion is indicated by NFC_CONFIG2 */
+			/* being set to 0 */
+			while (max_retries-- > 0) {
+				if (readw(host->regs + NFC_CONFIG2) == 0) {
+					break;
+				}
+				udelay(1);
+			}
+			if (max_retries < 0)
+				DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n",
+				      __func__);
+		}
+		break;

 	case NAND_CMD_STATUS:
 		host->buf_start = 0;
@@ -758,7 +803,7 @@
 	tmp &= ~NFC_SP_EN;
 	writew(tmp, host->regs + NFC_CONFIG1);

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

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

@@ -766,6 +811,10 @@
 	if (err)
 		goto eirq;

+	if (cpu_is_mx21()) {
+		disable_irq(host->irq);
+	}
+
 	/* Reset NAND */
 	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);



More information about the linux-arm-kernel mailing list