diff options
author | Huang Shijie <b32955@freescale.com> | 2010-11-01 15:56:21 +0800 |
---|---|---|
committer | Scott Sweeny <scott.sweeny@timesys.com> | 2011-01-19 11:50:19 -0500 |
commit | cd57afaed216dd5f90d9e9dd7bf7f6b0202415d6 (patch) | |
tree | 8671195872b9baa207dcb71767d8db8dc1e09f8b | |
parent | 3b84fc1092fb800e7edc20eb3d79c6528e2bc01a (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.c | 260 |
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, |