mxc_nand: Add v3 (i.MX51) Support
authorSascha Hauer <s.hauer@pengutronix.de>
Fri, 6 Aug 2010 13:53:11 +0000 (15:53 +0200)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 6 Aug 2010 15:38:09 +0000 (16:38 +0100)
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/mtd/nand/Kconfig
drivers/mtd/nand/mxc_nand.c

index 1d69920a2c93d24b11e7a74e21b3c23e3c33ee11..79afdc1b3377139d1e41b68a715b19696883eb48 100644 (file)
@@ -481,7 +481,7 @@ config MTD_NAND_MPC5121_NFC
 
 config MTD_NAND_MXC
        tristate "MXC NAND support"
-       depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3
+       depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51
        help
          This enables the driver for the NAND flash controller on the
          MXC processors.
index 1433a83891096eb19979ce92bf3426c0740a4055..3657a6eb026fc80c6e5175e237e76dd857d9a396 100644 (file)
@@ -39,6 +39,8 @@
 
 #define nfc_is_v21()           (cpu_is_mx25() || cpu_is_mx35())
 #define nfc_is_v1()            (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
+#define nfc_is_v3_2()          cpu_is_mx51()
+#define nfc_is_v3()            nfc_is_v3_2()
 
 /* Addresses for NFC registers */
 #define NFC_V1_V2_BUF_SIZE             (host->regs + 0x00)
 #define NFC_ID                         (1 << 4)
 #define NFC_STATUS                     (1 << 5)
 
+#define NFC_V3_FLASH_CMD               (host->regs_axi + 0x00)
+#define NFC_V3_FLASH_ADDR0             (host->regs_axi + 0x04)
+
+#define NFC_V3_CONFIG1                 (host->regs_axi + 0x34)
+#define NFC_V3_CONFIG1_SP_EN           (1 << 0)
+#define NFC_V3_CONFIG1_RBA(x)          (((x) & 0x7 ) << 4)
+
+#define NFC_V3_ECC_STATUS_RESULT       (host->regs_axi + 0x38)
+
+#define NFC_V3_LAUNCH                  (host->regs_axi + 0x40)
+
+#define NFC_V3_WRPROT                  (host->regs_ip + 0x0)
+#define NFC_V3_WRPROT_LOCK_TIGHT       (1 << 0)
+#define NFC_V3_WRPROT_LOCK             (1 << 1)
+#define NFC_V3_WRPROT_UNLOCK           (1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK       (2 << 6)
+
+#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0   (host->regs_ip + 0x04)
+
+#define NFC_V3_CONFIG2                 (host->regs_ip + 0x24)
+#define NFC_V3_CONFIG2_PS_512                  (0 << 0)
+#define NFC_V3_CONFIG2_PS_2048                 (1 << 0)
+#define NFC_V3_CONFIG2_PS_4096                 (2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE               (1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN                  (1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES             (1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0         (1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8              (1 << 6)
+#define NFC_V3_CONFIG2_PPB(x)                  (((x) & 0x3) << 7)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x)      (((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK                 (1 << 15)
+#define NFC_V3_CONFIG2_ST_CMD(x)               (((x) & 0xff) << 24)
+#define NFC_V3_CONFIG2_SPAS(x)                 (((x) & 0xff) << 16)
+
+#define NFC_V3_CONFIG3                         (host->regs_ip + 0x28)
+#define NFC_V3_CONFIG3_ADD_OP(x)               (((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8                     (1 << 3)
+#define NFC_V3_CONFIG3_SBB(x)                  (((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x)       (((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE                        (1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA                 (1 << 20)
+
+#define NFC_V3_IPC                     (host->regs_ip + 0x2C)
+#define NFC_V3_IPC_CREQ                        (1 << 0)
+#define NFC_V3_IPC_INT                 (1 << 31)
+
+#define NFC_V3_DELAY_LINE              (host->regs_ip + 0x34)
+
 struct mxc_nand_host {
        struct mtd_info         mtd;
        struct nand_chip        nand;
@@ -91,6 +141,8 @@ struct mxc_nand_host {
 
        void __iomem            *base;
        void __iomem            *regs;
+       void __iomem            *regs_axi;
+       void __iomem            *regs_ip;
        int                     status_request;
        struct clk              *clk;
        int                     clk_act;
@@ -169,6 +221,20 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int check_int_v3(struct mxc_nand_host *host)
+{
+       uint32_t tmp;
+
+       tmp = readl(NFC_V3_IPC);
+       if (!(tmp & NFC_V3_IPC_INT))
+               return 0;
+
+       tmp &= ~NFC_V3_IPC_INT;
+       writel(tmp, NFC_V3_IPC);
+
+       return 1;
+}
+
 static int check_int_v1_v2(struct mxc_nand_host *host)
 {
        uint32_t tmp;
@@ -209,6 +275,18 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
        }
 }
 
+static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
+{
+       /* fill command */
+       writel(cmd, NFC_V3_FLASH_CMD);
+
+       /* send out command */
+       writel(NFC_CMD, NFC_V3_LAUNCH);
+
+       /* Wait for operation to complete */
+       wait_op_done(host, useirq);
+}
+
 /* This function issues the specified command to the NAND device and
  * waits for completion. */
 static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
@@ -237,6 +315,17 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
        }
 }
 
+static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
+{
+       /* fill address */
+       writel(addr, NFC_V3_FLASH_ADDR0);
+
+       /* send out address */
+       writel(NFC_ADDR, NFC_V3_LAUNCH);
+
+       wait_op_done(host, 0);
+}
+
 /* This function sends an address (or partial address) to the
  * NAND device. The address is used to select the source/destination for
  * a NAND command. */
@@ -251,6 +340,22 @@ static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islas
        wait_op_done(host, islast);
 }
 
+static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
+{
+       struct nand_chip *nand_chip = mtd->priv;
+       struct mxc_nand_host *host = nand_chip->priv;
+       uint32_t tmp;
+
+       tmp = readl(NFC_V3_CONFIG1);
+       tmp &= ~(7 << 4);
+       writel(tmp, NFC_V3_CONFIG1);
+
+       /* transfer data from NFC ram to nand */
+       writel(ops, NFC_V3_LAUNCH);
+
+       wait_op_done(host, false);
+}
+
 static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
 {
        struct nand_chip *nand_chip = mtd->priv;
@@ -274,6 +379,16 @@ static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
        }
 }
 
+static void send_read_id_v3(struct mxc_nand_host *host)
+{
+       /* Read ID into main buffer */
+       writel(NFC_ID, NFC_V3_LAUNCH);
+
+       wait_op_done(host, true);
+
+       memcpy(host->data_buf, host->main_area0, 16);
+}
+
 /* Request the NANDFC to perform a read of the NAND device ID. */
 static void send_read_id_v1_v2(struct mxc_nand_host *host)
 {
@@ -299,6 +414,14 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)
        memcpy(host->data_buf, host->main_area0, 16);
 }
 
+static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
+{
+       writew(NFC_STATUS, NFC_V3_LAUNCH);
+       wait_op_done(host, true);
+
+       return readl(NFC_V3_CONFIG1) >> 16;
+}
+
 /* This function requests the NANDFC to perform a read of the
  * NAND device status and returns the current status. */
 static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
@@ -381,7 +504,10 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
 
        no_subpages = mtd->writesize >> 9;
 
-       ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
+       if (nfc_is_v21())
+               ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
+       else
+               ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);
 
        do {
                err = ecc_stat & ecc_bit_mask;
@@ -643,6 +769,72 @@ static void preset_v1_v2(struct mtd_info *mtd)
        writew(0x4, NFC_V1_V2_WRPROT);
 }
 
+static void preset_v3(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mxc_nand_host *host = chip->priv;
+       uint32_t config2, config3;
+       int i, addr_phases;
+
+       writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
+       writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
+
+       /* Unlock the internal RAM Buffer */
+       writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
+                       NFC_V3_WRPROT);
+
+       /* Blocks to be unlocked */
+       for (i = 0; i < NAND_MAX_CHIPS; i++)
+               writel(0x0 |    (0xffff << 16),
+                               NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
+
+       writel(0, NFC_V3_IPC);
+
+       config2 = NFC_V3_CONFIG2_ONE_CYCLE |
+               NFC_V3_CONFIG2_2CMD_PHASES |
+               NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
+               NFC_V3_CONFIG2_ST_CMD(0x70) |
+               NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
+
+       if (chip->ecc.mode == NAND_ECC_HW)
+               config2 |= NFC_V3_CONFIG2_ECC_EN;
+
+       addr_phases = fls(chip->pagemask) >> 3;
+
+       if (mtd->writesize == 2048) {
+               config2 |= NFC_V3_CONFIG2_PS_2048;
+               config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+       } else if (mtd->writesize == 4096) {
+               config2 |= NFC_V3_CONFIG2_PS_4096;
+               config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+       } else {
+               config2 |= NFC_V3_CONFIG2_PS_512;
+               config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
+       }
+
+       if (mtd->writesize) {
+               config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6);
+               host->eccsize = get_eccsize(mtd);
+               if (host->eccsize == 8)
+                       config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
+       }
+
+       writel(config2, NFC_V3_CONFIG2);
+
+       config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
+                       NFC_V3_CONFIG3_NO_SDMA |
+                       NFC_V3_CONFIG3_RBB_MODE |
+                       NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+                       NFC_V3_CONFIG3_ADD_OP(0);
+
+       if (!(chip->options & NAND_BUSWIDTH_16))
+               config3 |= NFC_V3_CONFIG3_FW8;
+
+       writel(config3, NFC_V3_CONFIG3);
+
+       writel(0, NFC_V3_DELAY_LINE);
+}
+
 /* 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,
@@ -843,6 +1035,30 @@ static int __init mxcnd_probe(struct platform_device *pdev)
                oob_smallpage = &nandv1_hw_eccoob_smallpage;
                oob_largepage = &nandv1_hw_eccoob_largepage;
                this->ecc.bytes = 3;
+               host->eccsize = 1;
+       } else if (nfc_is_v3_2()) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               if (!res) {
+                       err = -ENODEV;
+                       goto eirq;
+               }
+               host->regs_ip = ioremap(res->start, resource_size(res));
+               if (!host->regs_ip) {
+                       err = -ENOMEM;
+                       goto eirq;
+               }
+               host->regs_axi = host->base + 0x1e00;
+               host->spare0 = host->base + 0x1000;
+               host->spare_len = 64;
+               host->preset = preset_v3;
+               host->send_cmd = send_cmd_v3;
+               host->send_addr = send_addr_v3;
+               host->send_page = send_page_v3;
+               host->send_read_id = send_read_id_v3;
+               host->check_int = check_int_v3;
+               host->get_dev_status = get_dev_status_v3;
+               oob_smallpage = &nandv2_hw_eccoob_smallpage;
+               oob_largepage = &nandv2_hw_eccoob_largepage;
        } else
                BUG();
 
@@ -918,6 +1134,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
 escan:
        free_irq(host->irq, host);
 eirq:
+       if (host->regs_ip)
+               iounmap(host->regs_ip);
        iounmap(host->base);
 eres:
        clk_put(host->clk);
@@ -937,6 +1155,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
 
        nand_release(&host->mtd);
        free_irq(host->irq, host);
+       if (host->regs_ip)
+               iounmap(host->regs_ip);
        iounmap(host->base);
        kfree(host);