summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHuang Shijie <b32955@freescale.com>2010-11-01 15:56:21 +0800
committerScott Sweeny <scott.sweeny@timesys.com>2011-01-19 11:50:19 -0500
commitcd57afaed216dd5f90d9e9dd7bf7f6b0202415d6 (patch)
tree8671195872b9baa207dcb71767d8db8dc1e09f8b
parent3b84fc1092fb800e7edc20eb3d79c6528e2bc01a (diff)
ENGR00133178-9 NAND : add ONFI init code
Add the ONFI nand init code. In DDR mode of ONFI NAND, the data READ/WRITE will become 16-bit, although the ALE/CLE are still use the 8-bit. So changing the DMA command chains of ECC READ/WRITE page's operations to support the ONFI nand. Signed-off-by: Huang Shijie <b32955@freescale.com>
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c260
1 files changed, 234 insertions, 26 deletions
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c
index eee49ae58dea..0ca5a9e9fd4a 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c
@@ -24,6 +24,180 @@
#include "gpmi-nfc-gpmi-regs-v2.h"
#include "gpmi-nfc-bch-regs-v2.h"
+#define FEATURE_SIZE (4) /* p1, p2, p3, p4 */
+
+/*
+ * In DDR mode of ONFI NAND, the data READ/WRITE will become 16-bit,
+ * although the ALE/CLE are still use the 8-bit.
+ */
+static int onfi_ddr_mode;
+
+static void setup_ddr_timing(struct gpmi_nfc_data *this)
+{
+ uint32_t value;
+ struct resources *resources = &this->resources;
+
+ /* set timing 2 register */
+ value = BF_GPMI_TIMING2_DATA_PAUSE(0x6)
+ | BF_GPMI_TIMING2_CMDADD_PAUSE(0x4)
+ | BF_GPMI_TIMING2_POSTAMBLE_DELAY(0x2)
+ | BF_GPMI_TIMING2_PREAMBLE_DELAY(0x4)
+ | BF_GPMI_TIMING2_CE_DELAY(0x2)
+ | BF_GPMI_TIMING2_READ_LATENCY(0x2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_TIMING2);
+
+ /* set timing 1 register */
+ __raw_writel(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(0x500),
+ resources->gpmi_regs + HW_GPMI_TIMING1);
+
+ /* Put GPMI in NAND mode, disable device reset, and make certain
+ IRQRDY polarity is active high. */
+ value = BV_GPMI_CTRL1_GPMI_MODE__NAND
+ | BM_GPMI_CTRL1_GANGED_RDYBUSY
+ | BF_GPMI_CTRL1_WRN_DLY_SEL(0x3)
+ | (BV_GPMI_CTRL1_DEV_RESET__DISABLED << 3)
+ | (BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH << 2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+}
+
+static int enable_micron_ddr(struct gpmi_nfc_data *this)
+{
+ uint32_t value;
+ struct resources *resources = &this->resources;
+ struct mil *mil = &this->mil;
+ struct nand_chip *nand = &this->mil.nand;
+ struct mtd_info *mtd = &mil->mtd;
+ int saved_chip_number = 0;
+ uint8_t device_feature[FEATURE_SIZE];
+ int mode = 0;/* there is 5 mode available, default is 0 */
+
+ saved_chip_number = mil->current_chip;
+ nand->select_chip(mtd, 0);
+
+ /* [0] set proper timing */
+ __raw_writel(BF_GPMI_TIMING0_ADDRESS_SETUP(0x1)
+ | BF_GPMI_TIMING0_DATA_HOLD(0x3)
+ | BF_GPMI_TIMING0_DATA_SETUP(0x3),
+ resources->gpmi_regs + HW_GPMI_TIMING0);
+
+ /* [1] send SET FEATURE commond to NAND */
+ memset(device_feature, 0, sizeof(device_feature));
+ device_feature[0] = (0x1 << 4) | (mode & 0x7);
+
+ nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ nand->cmdfunc(mtd, NAND_CMD_SET_FEATURE, 1, -1);
+ nand->write_buf(mtd, device_feature, FEATURE_SIZE);
+
+ /* [2] set clk divider */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* [3] about the clock, pay attention! */
+ nand->select_chip(mtd, saved_chip_number);
+ {
+ struct clk *pll1;
+ pll1 = clk_get(NULL, "pll1_main_clk");
+ if (IS_ERR(pll1)) {
+ printk(KERN_INFO "No PLL1 clock\n");
+ return -EINVAL;
+ }
+ clk_set_parent(resources->clock, pll1);
+ clk_set_rate(resources->clock, 20000000);
+ }
+ nand->select_chip(mtd, 0);
+
+ /* [4] setup timing */
+ setup_ddr_timing(this);
+
+ /* [5] set to SYNC mode */
+ __raw_writel(BM_GPMI_CTRL1_TOGGLE_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+ __raw_writel(BM_GPMI_CTRL1_SSYNCMODE | BM_GPMI_CTRL1_GANGED_RDYBUSY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* [6] enable both write & read DDR DLLs */
+ value = BM_GPMI_READ_DDR_DLL_CTRL_REFCLK_ON |
+ BM_GPMI_READ_DDR_DLL_CTRL_ENABLE |
+ BF_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) |
+ BF_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ /* [7] reset read */
+ __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_RESET,
+ resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ value = value & ~BM_GPMI_READ_DDR_DLL_CTRL_RESET;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = BM_GPMI_WRITE_DDR_DLL_CTRL_REFCLK_ON |
+ BM_GPMI_WRITE_DDR_DLL_CTRL_ENABLE |
+ BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) |
+ BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7) ,
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [8] reset write */
+ __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_RESET,
+ resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [9] wait for locks for read and write */
+ do {
+ uint32_t read_status, write_status;
+ uint32_t r_mask, w_mask;
+
+ read_status = __raw_readl(resources->gpmi_regs
+ + HW_GPMI_READ_DDR_DLL_STS);
+ write_status = __raw_readl(resources->gpmi_regs
+ + HW_GPMI_WRITE_DDR_DLL_STS);
+
+ r_mask = (BM_GPMI_READ_DDR_DLL_STS_REF_LOCK |
+ BM_GPMI_READ_DDR_DLL_STS_SLV_LOCK);
+ w_mask = (BM_GPMI_WRITE_DDR_DLL_STS_REF_LOCK |
+ BM_GPMI_WRITE_DDR_DLL_STS_SLV_LOCK);
+
+ if (((read_status & r_mask) == r_mask)
+ && ((write_status & w_mask) == w_mask))
+ break;
+ } while (1);
+
+ /* [10] force update of read/write */
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_SLV_FORCE_UPD,
+ resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_FORCE_UPD,
+ resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [11] set gate update */
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ value |= BM_GPMI_READ_DDR_DLL_CTRL_GATE_UPDATE;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ value |= BM_GPMI_WRITE_DDR_DLL_CTRL_GATE_UPDATE;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ onfi_ddr_mode = 1;
+ nand->select_chip(mtd, saved_chip_number);
+
+ printk(KERN_INFO "Micron ONFI NAND enters synchronous mode %d\n", mode);
+ return 0;
+}
+
+/* To check if we need to initialize something else*/
+static int extra_init(struct gpmi_nfc_data *this)
+{
+ if (is_onfi_nand(&this->device_info))
+ return enable_micron_ddr(this);
+ return 0;
+}
+
/**
* init() - Initializes the NFC hardware.
*
@@ -89,6 +263,7 @@ static int set_geometry(struct gpmi_nfc_data *this)
unsigned int metadata_size;
unsigned int ecc_strength;
unsigned int page_size;
+ uint32_t value;
/* We make the abstract choices in a common function. */
@@ -120,18 +295,22 @@ static int set_geometry(struct gpmi_nfc_data *this)
/* Configure layout 0. */
- __raw_writel(
- BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
+ value = BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) |
BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) |
- BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size) ,
- resources->bch_regs + HW_BCH_FLASH0LAYOUT0);
+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size);
+ if (is_onfi_nand(&this->device_info))
+ value |= BM_BCH_FLASH0LAYOUT0_GF13_0_GF14_1;
- __raw_writel(
- BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
+ __raw_writel(value, resources->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ value = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) |
- BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size) ,
- resources->bch_regs + HW_BCH_FLASH0LAYOUT1);
+ BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size);
+ if (is_onfi_nand(&this->device_info))
+ value |= BM_BCH_FLASH0LAYOUT1_GF13_0_GF14_1;
+
+ __raw_writel(value, resources->bch_regs + HW_BCH_FLASH0LAYOUT1);
/* Set *all* chip selects to use layout 0. */
@@ -246,6 +425,7 @@ static void exit(struct gpmi_nfc_data *this)
gpmi_nfc_dma_exit(this);
}
+
/**
* begin() - Begin NFC I/O.
*
@@ -554,6 +734,7 @@ static int send_page(struct gpmi_nfc_data *this, unsigned chip,
uint32_t address;
uint32_t ecc_command;
uint32_t buffer_mask;
+ uint32_t value;
/* Compute the DMA channel. */
@@ -582,12 +763,14 @@ static int send_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
- (*d)->cmd.pio_words[0] =
- BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_WORD_LENGTH |
+ value = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
BF_GPMI_CTRL0_XFER_COUNT(0) ;
+ if (onfi_ddr_mode == 0)
+ value |= BM_GPMI_CTRL0_WORD_LENGTH;
+
+ (*d)->cmd.pio_words[0] = value;
(*d)->cmd.pio_words[1] = 0;
@@ -596,7 +779,12 @@ static int send_page(struct gpmi_nfc_data *this, unsigned chip,
BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
- (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+ if (onfi_ddr_mode)
+ value = nfc_geo->page_size_in_bytes >> 1;
+ else
+ value = nfc_geo->page_size_in_bytes;
+
+ (*d)->cmd.pio_words[3] = value;
(*d)->cmd.pio_words[4] = payload;
(*d)->cmd.pio_words[5] = auxiliary;
@@ -652,6 +840,7 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
uint32_t address;
uint32_t ecc_command;
uint32_t buffer_mask;
+ uint32_t value;
/* Compute the DMA channel. */
@@ -677,12 +866,15 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
- (*d)->cmd.pio_words[0] =
- BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_WORD_LENGTH |
+ value = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
BF_GPMI_CTRL0_XFER_COUNT(0) ;
+ if (onfi_ddr_mode == 0)
+ value |= BM_GPMI_CTRL0_WORD_LENGTH;
+
+ (*d)->cmd.pio_words[0] = value;
+
mxs_dma_desc_append(dma_channel, (*d));
d++;
@@ -710,19 +902,32 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
- (*d)->cmd.pio_words[0] =
- BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_WORD_LENGTH |
- BF_GPMI_CTRL0_CS(chip) |
- BF_GPMI_CTRL0_ADDRESS(address) |
- BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+ if (onfi_ddr_mode)
+ value = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes >> 1) ;
+ else
+ value = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes);
+
+ (*d)->cmd.pio_words[0] = value;
(*d)->cmd.pio_words[1] = 0;
(*d)->cmd.pio_words[2] =
BM_GPMI_ECCCTRL_ENABLE_ECC |
BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
- (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+
+ if (onfi_ddr_mode)
+ value = nfc_geo->page_size_in_bytes >> 1;
+ else
+ value = nfc_geo->page_size_in_bytes;
+ (*d)->cmd.pio_words[3] = value;
+
(*d)->cmd.pio_words[4] = payload;
(*d)->cmd.pio_words[5] = auxiliary;
@@ -749,12 +954,14 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
- (*d)->cmd.pio_words[0] =
- BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_WORD_LENGTH |
+ value = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
- BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+ BF_GPMI_CTRL0_XFER_COUNT(value) ;
+ if (onfi_ddr_mode == 0)
+ value |= BM_GPMI_CTRL0_WORD_LENGTH;
+
+ (*d)->cmd.pio_words[0] = value;
(*d)->cmd.pio_words[1] = 0;
(*d)->cmd.pio_words[2] = 0;
@@ -823,6 +1030,7 @@ struct nfc_hal gpmi_nfc_hal_v2 = {
.max_dll_clock_period_in_ns = 32,
.max_dll_delay_in_ns = 16,
.init = init,
+ .extra_init = extra_init,
.set_geometry = set_geometry,
.set_timing = set_timing,
.get_timing = get_timing,