Skip to contentSkip to conversation

ARM: MXC: mxc_nand: support i.MX21

PATCH Superseded Ivo Clarysse 3 participants 6 messages Apr 6, 2010 – Apr 27, 2010 Memory technology devices (MTD)

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ARM: MXC: mxc_nand: support i.MX21

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.

---
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);

#1 lore raw

PATCH 1/1 ARM: MXC: mxc_nand: support i.MX21

Quoted 55 lines · Ivo Clarysse

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.

---
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

(quoted diff) +8 -3
@@ -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.
+	 */

Review line 185

Quoted 10 lines
(quoted diff) +4 -1
+	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);
+	}

Why do we need the check for NFC_INT? This is no shared interrupt so we can be sure the nfc actually has an interrupt.
What's wrong with the current waitqueue approach?

Quoted 13 lines
(quoted diff) +1 -1
 	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) {

Review line 205

Quoted 6 lines
(quoted diff) +1 -2
@@ -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);

This does not work. You have to call INIT_COMPLETION before enabling the interrupt, otherwise we get stuck when the interrupt comes between enabling it and initialising the completion.

Quoted 37 lines
(quoted diff) +20 -0
+
+			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__);

Review line 593

Quoted 8 lines
(quoted diff) +5 -0
@@ -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);

I just realized that the original driver does not handle the reset command at all. Maybe we should fix this first, like this:

	case NAND_CMD_RESET:
		writew(command, host->regs + NFC_FLASH_CMD);
		writew(NFC_CMD, host->regs + NFC_CONFIG2);
		if (cpu_is_mx21()){
			...
		} else
			wait_op_done(host, 0);
Quoted 36 lines
(quoted diff) +18 -1
+			/* 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);
 
Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#2 lore raw

PATCH v2 1/1 ARM: MXC: mxc_nand: support i.MX21

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.

This patch:

- Uses enable_irq / disable_irq_nosync instead of NFC_INT_MASK to mask NFC interrupts (allowing NFC_CONFIG2:NFC_INT to used on i.MX21)

- (Re-)sets all NFC registers after reset

---
--- 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-08
14:01:58.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
@@ -168,11 +168,7 @@
{
struct mxc_nand_host *host = dev_id;

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

wake_up(&host->irq_waitq);

@@ -184,15 +180,13 @@
*/
static void wait_op_done(struct mxc_nand_host *host, int useirq) {
- uint32_t tmp;
- int max_retries = 2000;
+ uint16_t tmp;
+ int max_retries = 8000;

if (useirq) {
if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) {

- tmp = readw(host->regs + NFC_CONFIG1);
- tmp &= ~NFC_INT_MSK; /* Enable interrupt */
- writew(tmp, host->regs + NFC_CONFIG1);
+ enable_irq(host->irq);

wait_event(host->irq_waitq,
readw(host->regs + NFC_CONFIG2) & NFC_INT);
@@ -226,8 +220,23 @@
writew(cmd, host->regs + NFC_FLASH_CMD); writew(NFC_CMD, host->regs + NFC_CONFIG2);

- /* Wait for operation to complete */
- wait_op_done(host, useirq);
+ if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
+ int max_retries = 100;
+ /* 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__);
+ } else {
+ /* Wait for operation to complete */
+ wait_op_done(host, useirq);
+ }
}

/* This function sends an address (or partial address) to the
@@ -542,6 +551,41 @@
}
}

+static void preset(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct mxc_nand_host *host = nand_chip->priv;
+ uint16_t tmp;
+
+ /* enable interrupt, disable spare enable */
+ tmp = readw(host->regs + NFC_CONFIG1);
+ tmp &= ~NFC_INT_MSK;
+ tmp &= ~NFC_SP_EN;
+ if (nand_chip->ecc.mode == NAND_ECC_HW) {
+ tmp |= NFC_ECC_EN;
+ } else {
+ tmp &= ~NFC_ECC_EN;
+ }
+ writew(tmp, host->regs + NFC_CONFIG1);
+ /* preset operation */
+
+ /* Unlock the internal RAM Buffer */
+ writew(0x2, host->regs + NFC_CONFIG);
+
+ /* Blocks to be unlocked */
+ if (nfc_is_v21()) {
+ writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
+ writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
+ } else if (nfc_is_v1()) {
+ writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
+ writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
+ } else
+ BUG();
+
+ /* Unlock Block Command for given address range */
+ writew(0x4, host->regs + NFC_WRPROT);
+}
+
/* Used by the upper layer to write command to NAND Flash for
* different operations to be carried out on NAND Flash */
static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
@@ -559,6 +603,10 @@

/* Command pre-processing step */
switch (command) {
+ case NAND_CMD_RESET:
+ send_cmd(host, command, false);
+ preset(mtd);
+ break;

case NAND_CMD_STATUS:
host->buf_start = 0;
@@ -679,7 +727,6 @@
struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host;
struct resource *res;
- uint16_t tmp;
int err = 0, nr_parts = 0;
struct nand_ecclayout *oob_smallpage, *oob_largepage;

@@ -743,51 +790,17 @@
host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage;
+ this->ecc.bytes = 9;
} else if (nfc_is_v1()) {
host->regs = host->base; host->spare0 = host->base + 0x800; host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage;
- } else
- BUG();
-
- /* disable interrupt and spare enable */
- tmp = readw(host->regs + NFC_CONFIG1);
- tmp |= NFC_INT_MSK;
- tmp &= ~NFC_SP_EN;
- writew(tmp, host->regs + NFC_CONFIG1);
-
- init_waitqueue_head(&host->irq_waitq);
-
- host->irq = platform_get_irq(pdev, 0);
-
- err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host);
- if (err)
- goto eirq;
-
- /* Reset NAND */
- this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-
- /* preset operation */
- /* Unlock the internal RAM Buffer */
- writew(0x2, host->regs + NFC_CONFIG);
-
- /* Blocks to be unlocked */
- if (nfc_is_v21()) {
- writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
- writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
- this->ecc.bytes = 9;
- } else if (nfc_is_v1()) {
- writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
- writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
this->ecc.bytes = 3;
} else
BUG();

- /* Unlock Block Command for given address range */
- writew(0x4, host->regs + NFC_WRPROT);
-
this->ecc.size = 512;
this->ecc.layout = oob_smallpage;

@@ -796,14 +809,8 @@
this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW;
- tmp = readw(host->regs + NFC_CONFIG1);
- tmp |= NFC_ECC_EN;
- writew(tmp, host->regs + NFC_CONFIG1);
} else {
this->ecc.mode = NAND_ECC_SOFT;
- tmp = readw(host->regs + NFC_CONFIG1);
- tmp &= ~NFC_ECC_EN;
- writew(tmp, host->regs + NFC_CONFIG1);
}

/* NAND bus width determines access funtions used by upper layer */
@@ -817,6 +824,14 @@
this->options |= NAND_USE_FLASH_BBT;
}

+ init_waitqueue_head(&host->irq_waitq);
+
+ host->irq = platform_get_irq(pdev, 0);
+
+ err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
+ if (err)
+ goto eirq;
+
/* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) {
err = -ENXIO;

#3 lore raw

PATCH v2 1/1 ARM: MXC: mxc_nand: support i.MX21

Quoted 10 lines · Ivo Clarysse

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.

Quoted 6 lines

This patch:

- Uses enable_irq / disable_irq_nosync instead of NFC_INT_MASK to mask NFC interrupts (allowing NFC_CONFIG2:NFC_INT to used on i.MX21)

- (Re-)sets all NFC registers after reset

Looks much better without all the i.MX21 specific handling. Can you please make two patches out of it? One which fixes the reset command and factors out the init sequence to preset() and one patch which adds i.MX21 support.

Sascha

Quoted 219 lines

---
--- 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-08
14:01:58.000000000 +0200

(quoted diff) +70 -55
@@ -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
@@ -168,11 +168,7 @@
 {
 	struct mxc_nand_host *host = dev_id;
 
-	uint16_t tmp;
-
-	tmp = readw(host->regs + NFC_CONFIG1);
-	tmp |= NFC_INT_MSK; /* Disable interrupt */
-	writew(tmp, host->regs + NFC_CONFIG1);
+	disable_irq_nosync(irq);
 
 	wake_up(&host->irq_waitq);
 
@@ -184,15 +180,13 @@
  */
 static void wait_op_done(struct mxc_nand_host *host, int useirq)
 {
-	uint32_t tmp;
-	int max_retries = 2000;
+	uint16_t tmp;
+	int max_retries = 8000;
 
 	if (useirq) {
 		if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) {
 
-			tmp = readw(host->regs + NFC_CONFIG1);
-			tmp  &= ~NFC_INT_MSK;	/* Enable interrupt */
-			writew(tmp, host->regs + NFC_CONFIG1);
+			enable_irq(host->irq);
 
 			wait_event(host->irq_waitq,
 				readw(host->regs + NFC_CONFIG2) & NFC_INT);
@@ -226,8 +220,23 @@
 	writew(cmd, host->regs + NFC_FLASH_CMD);
 	writew(NFC_CMD, host->regs + NFC_CONFIG2);
 
-	/* Wait for operation to complete */
-	wait_op_done(host, useirq);
+	if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
+		int max_retries = 100;
+		/* 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__);
+	} else {
+		/* Wait for operation to complete */
+		wait_op_done(host, useirq);
+	}
 }
 
 /* This function sends an address (or partial address) to the
@@ -542,6 +551,41 @@
 	}
 }
 
+static void preset(struct mtd_info *mtd)
+{
+	struct nand_chip *nand_chip = mtd->priv;
+	struct mxc_nand_host *host = nand_chip->priv;
+	uint16_t tmp;
+
+	/* enable interrupt, disable spare enable */
+	tmp = readw(host->regs + NFC_CONFIG1);
+	tmp &= ~NFC_INT_MSK;
+	tmp &= ~NFC_SP_EN;
+	if (nand_chip->ecc.mode == NAND_ECC_HW) {
+		tmp |= NFC_ECC_EN;
+	} else {
+		tmp &= ~NFC_ECC_EN;
+	}
+	writew(tmp, host->regs + NFC_CONFIG1);
+	/* preset operation */
+
+	/* Unlock the internal RAM Buffer */
+	writew(0x2, host->regs + NFC_CONFIG);
+
+	/* Blocks to be unlocked */
+	if (nfc_is_v21()) {
+		writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
+		writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
+	} else if (nfc_is_v1()) {
+		writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
+		writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
+	} else
+		BUG();
+
+	/* Unlock Block Command for given address range */
+	writew(0x4, host->regs + NFC_WRPROT);
+}
+
 /* Used by the upper layer to write command to NAND Flash for
  * different operations to be carried out on NAND Flash */
 static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
@@ -559,6 +603,10 @@
 
 	/* Command pre-processing step */
 	switch (command) {
+	case NAND_CMD_RESET:
+		send_cmd(host, command, false);
+		preset(mtd);
+		break;
 
 	case NAND_CMD_STATUS:
 		host->buf_start = 0;
@@ -679,7 +727,6 @@
 	struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct mxc_nand_host *host;
 	struct resource *res;
-	uint16_t tmp;
 	int err = 0, nr_parts = 0;
 	struct nand_ecclayout *oob_smallpage, *oob_largepage;
 
@@ -743,51 +790,17 @@
 		host->spare_len = 64;
 		oob_smallpage = &nandv2_hw_eccoob_smallpage;
 		oob_largepage = &nandv2_hw_eccoob_largepage;
+		this->ecc.bytes = 9;
 	} else if (nfc_is_v1()) {
 		host->regs = host->base;
 		host->spare0 = host->base + 0x800;
 		host->spare_len = 16;
 		oob_smallpage = &nandv1_hw_eccoob_smallpage;
 		oob_largepage = &nandv1_hw_eccoob_largepage;
-	} else
-		BUG();
-
-	/* disable interrupt and spare enable */
-	tmp = readw(host->regs + NFC_CONFIG1);
-	tmp |= NFC_INT_MSK;
-	tmp &= ~NFC_SP_EN;
-	writew(tmp, host->regs + NFC_CONFIG1);
-
-	init_waitqueue_head(&host->irq_waitq);
-
-	host->irq = platform_get_irq(pdev, 0);
-
-	err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host);
-	if (err)
-		goto eirq;
-
-	/* Reset NAND */
-	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-
-	/* preset operation */
-	/* Unlock the internal RAM Buffer */
-	writew(0x2, host->regs + NFC_CONFIG);
-
-	/* Blocks to be unlocked */
-	if (nfc_is_v21()) {
-		writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
-	        writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
-		this->ecc.bytes = 9;
-	} else if (nfc_is_v1()) {
-		writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
-	        writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
 		this->ecc.bytes = 3;
 	} else
 		BUG();
 
-	/* Unlock Block Command for given address range */
-	writew(0x4, host->regs + NFC_WRPROT);
-
 	this->ecc.size = 512;
 	this->ecc.layout = oob_smallpage;
 
@@ -796,14 +809,8 @@
 		this->ecc.hwctl = mxc_nand_enable_hwecc;
 		this->ecc.correct = mxc_nand_correct_data;
 		this->ecc.mode = NAND_ECC_HW;
-		tmp = readw(host->regs + NFC_CONFIG1);
-		tmp |= NFC_ECC_EN;
-		writew(tmp, host->regs + NFC_CONFIG1);
 	} else {
 		this->ecc.mode = NAND_ECC_SOFT;
-		tmp = readw(host->regs + NFC_CONFIG1);
-		tmp &= ~NFC_ECC_EN;
-		writew(tmp, host->regs + NFC_CONFIG1);
 	}
 
 	/* NAND bus width determines access funtions used by upper layer */
@@ -817,6 +824,14 @@
 		this->options |= NAND_USE_FLASH_BBT;
 	}
 
+	init_waitqueue_head(&host->irq_waitq);
+
+	host->irq = platform_get_irq(pdev, 0);
+
+	err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
+	if (err)
+		goto eirq;
+
 	/* first scan to find the device and get the page size */
 	if (nand_scan_ident(mtd, 1)) {
 		err = -ENXIO;
Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#4 lore raw

v3 cover ARM: MXC: mxc_nand: support i.MX21

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.

3) After reset is performed, NFC registers are again in their default values.

This patch series:

- (Re-)sets all NFC registers after reset

- Uses enable_irq / disable_irq_nosync instead of NFC_INT_MASK to mask NFC interrupts (allowing NFC_CONFIG2:NFC_INT to used on i.MX21)

#5 lore raw

v3 cover ARM: MXC: mxc_nand: support i.MX21

Quoted 13 lines · Ivo Clarysse

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.

3) After reset is performed, NFC registers are again in their default values.

Quoted 6 lines

This patch series:

- (Re-)sets all NFC registers after reset

- Uses enable_irq / disable_irq_nosync instead of NFC_INT_MASK to mask NFC interrupts (allowing NFC_CONFIG2:NFC_INT to used on i.MX21)

Pushed both patches to l2-mtd-2.6.git / dunno with

See
http://git.infradead.org/users/dedekind/l2-mtd-2.6.git/shortlog/refs/heads/dunno

Signature · 2 lines
Best Regards,
Artem Bityutskiy (????? ????????)