summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/gpmi_nfc_gpmi.h71
-rw-r--r--drivers/mtd/nand/gpmi_nfc_hal.c102
2 files changed, 96 insertions, 77 deletions
diff --git a/drivers/mtd/nand/gpmi_nfc_gpmi.h b/drivers/mtd/nand/gpmi_nfc_gpmi.h
index 9f7780d..3623bbc 100644
--- a/drivers/mtd/nand/gpmi_nfc_gpmi.h
+++ b/drivers/mtd/nand/gpmi_nfc_gpmi.h
@@ -1,7 +1,7 @@
/*
* Freescale GPMI Register Definitions
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -808,75 +808,6 @@ static inline u32 gpmi_nfc_get_ecc_strength(u32 page_data_size,
return 0;
}
-static inline s32 gpmi_nfc_reset_block(void *hwreg, int is_enable)
-{
- int timeout;
-
- /* the process of software reset of IP block is done
- in several steps:
-
- - clear SFTRST and wait for block is enabled;
- - clear clock gating (CLKGATE bit);
- - set the SFTRST again and wait for block is in reset;
- - clear SFTRST and wait for reset completion.
- */
- /* clear SFTRST */
- REG_CLR_ADDR(hwreg, BM_GPMI_CTRL0_SFTRST);
-
- for (timeout = 1000000; timeout > 0; timeout--)
- /* still in SFTRST state ? */
- if ((REG_RD_ADDR(hwreg) & BM_GPMI_CTRL0_SFTRST) == 0)
- break;
- if (timeout <= 0) {
- printk(KERN_ERR "%s(%p): timeout when enabling\n",
- __func__, hwreg);
- return -ETIME;
- }
-
- /* clear CLKGATE */
- REG_CLR_ADDR(hwreg, BM_GPMI_CTRL0_CLKGATE);
-
- if (is_enable) {
- /* now again set SFTRST */
- REG_SET_ADDR(hwreg, BM_GPMI_CTRL0_SFTRST);
- for (timeout = 1000000; timeout > 0; timeout--)
- /* poll until CLKGATE set */
- if (REG_RD_ADDR(hwreg) & BM_GPMI_CTRL0_CLKGATE)
- break;
- if (timeout <= 0) {
- printk(KERN_ERR "%s(%p): timeout when resetting\n",
- __func__, hwreg);
- return -ETIME;
- }
-
- REG_CLR_ADDR(hwreg, BM_GPMI_CTRL0_SFTRST);
- for (timeout = 1000000; timeout > 0; timeout--)
- /* still in SFTRST state ? */
- if ((REG_RD_ADDR(hwreg) & BM_GPMI_CTRL0_SFTRST) == 0)
- break;
- if (timeout <= 0) {
- printk(KERN_ERR "%s(%p): timeout when enabling "
- "after reset\n", __func__, hwreg);
- return -ETIME;
- }
-
- /* clear CLKGATE */
- REG_CLR_ADDR(hwreg, BM_GPMI_CTRL0_CLKGATE);
- }
- for (timeout = 1000000; timeout > 0; timeout--)
- /* still in SFTRST state ? */
- if ((REG_RD_ADDR(hwreg) & BM_GPMI_CTRL0_CLKGATE) == 0)
- break;
-
- if (timeout <= 0) {
- printk(KERN_ERR "%s(%p): timeout when unclockgating\n",
- __func__, hwreg);
- return -ETIME;
- }
-
- return 0;
-}
-
/**
* struct gpmi_nfc_info - i.MX NFC per-device data.
*
diff --git a/drivers/mtd/nand/gpmi_nfc_hal.c b/drivers/mtd/nand/gpmi_nfc_hal.c
index 316385b..a756567 100644
--- a/drivers/mtd/nand/gpmi_nfc_hal.c
+++ b/drivers/mtd/nand/gpmi_nfc_hal.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
*
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -52,6 +52,94 @@ static struct gpmi_nfc_timing safe_timing = {
.m_u8tRHOH = -1,
};
+#define MXS_SET_ADDR 0x4
+#define MXS_CLR_ADDR 0x8
+/*
+ * Clear the bit and poll it cleared. This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+ int timeout = 0x400;
+
+ /* clear the bit */
+ writel(mask, addr + MXS_CLR_ADDR);
+
+ /*
+ * SFTRST needs 3 GPMI clocks to settle, the reference manual
+ * recommends to wait 1us.
+ */
+ udelay(1);
+
+ /* poll the bit becoming clear */
+ while ((readl(addr) & mask) && --timeout)
+ /* nothing */;
+
+ return !timeout;
+}
+
+#define MODULE_CLKGATE (1 << 30)
+#define MODULE_SFTRST (1 << 31)
+/*
+ * The current mxs_reset_block() will do two things:
+ * [1] enable the module.
+ * [2] reset the module.
+ *
+ * In most of the cases, it's ok.
+ * But in MX23, there is a hardware bug in the BCH block (see erratum #2847).
+ * If you try to soft reset the BCH block, it becomes unusable until
+ * the next hard reset. This case occurs in the NAND boot mode. When the board
+ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
+ * So If the driver tries to reset the BCH again, the BCH will not work anymore.
+ * You will see a DMA timeout in this case. The bug has been fixed
+ * in the following chips, such as MX28.
+ *
+ * To avoid this bug, just add a new parameter `just_enable` for
+ * the mxs_reset_block(), and rewrite it here.
+ */
+static int gpmi_reset_block(void __iomem *reset_addr, int just_enable)
+{
+ int ret;
+ int timeout = 0x400;
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear CLKGATE */
+ writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
+
+ if (!just_enable) {
+ /* set SFTRST to reset the block */
+ writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR);
+ udelay(1);
+
+ /* poll CLKGATE becoming set */
+ while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
+ /* nothing */;
+ if (unlikely(!timeout))
+ goto error;
+ }
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear and poll CLKGATE */
+ ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
+ if (unlikely(ret))
+ goto error;
+
+ return 0;
+
+error:
+ printf("%s(%p): module reset timeout\n", __func__, reset_addr);
+ return -ETIMEDOUT;
+}
+
/**
* init() - Initializes the NFC hardware.
*
@@ -86,7 +174,11 @@ static int init(void)
mxs_dma_init();
/* Reset the GPMI block. */
- gpmi_nfc_reset_block((void *)(CONFIG_GPMI_REG_BASE + HW_GPMI_CTRL0), 1);
+ i = 0;
+ do {
+ error = gpmi_reset_block((void *)CONFIG_GPMI_REG_BASE, 0);
+ i++;
+ } while (error != 0 && i < 10);
/* Choose NAND mode. */
REG_CLR(CONFIG_GPMI_REG_BASE, HW_GPMI_CTRL1,
@@ -149,11 +241,7 @@ static int set_geometry(struct mtd_info *mtd)
* the next hard reset.
*/
-#if defined(CONFIG_GPMI_NFC_V2)
- gpmi_nfc_reset_block((void *)CONFIG_BCH_REG_BASE + HW_BCH_CTRL, 0);
-#else
- gpmi_nfc_reset_block((void *)CONFIG_BCH_REG_BASE + HW_BCH_CTRL, 1);
-#endif
+ gpmi_reset_block((void *)CONFIG_BCH_REG_BASE + HW_BCH_CTRL, 0);
/* Configure layout 0. */
writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |