From mboxrd@z Thu Jan 1 00:00:00 1970 From: ivo.clarysse@gmail.com (Ivo Clarysse) Date: Tue, 6 Apr 2010 15:52:23 +0200 Subject: [PATCH] ARM: MXC: mxc_nand: support i.MX21 Message-ID: To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org 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 --- 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);