diff options
author | Jason Roberts <jason.e.roberts@intel.com> | 2010-05-13 15:57:33 +0100 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-05-13 16:12:16 +0100 |
commit | ce082596ae4308f67f0953a67db508bb085520fa (patch) | |
tree | c77be06bdf86c9232bea3514e371bd9bb7b4825a | |
parent | 1cd2620ca9332943c9fff84c0c9240982534d840 (diff) |
mtd/nand: Add Intel Moorestown/Denali NAND support
There is more work to be done on this but it is basically working now.
Signed-off-by: Jason Roberts <jason.e.roberts@intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | drivers/mtd/nand/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.c | 2134 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.h | 816 |
4 files changed, 2968 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8f402d46a36..98a04b3c952 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -50,6 +50,23 @@ config MTD_NAND_AUTCPU12 This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. +config MTD_NAND_DENALI + depends on PCI + tristate "Support Denali NAND controller on Intel Moorestown" + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. + +config MTD_NAND_DENALI_SCRATCH_REG_ADDR + hex "Denali NAND size scratch register address" + default "0xFF108018" + help + Some platforms place the NAND chip size in a scratch register + because (some versions of) the driver aren't able to automatically + determine the size of certain chips. Set the address of the + scratch register here to enable this feature. On Intel Moorestown + boards, the scratch register is at 0xFF108018. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 04bccf9d7b5..e8ab884ba47 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c new file mode 100644 index 00000000000..8a6ce0dd953 --- /dev/null +++ b/drivers/mtd/nand/denali.c @@ -0,0 +1,2134 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/mtd/mtd.h> +#include <linux/module.h> + +#include "denali.h" + +MODULE_LICENSE("GPL"); + +/* We define a module parameter that allows the user to override + * the hardware and decide what timing mode should be used. + */ +#define NAND_DEFAULT_TIMINGS -1 + +static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; +module_param(onfi_timing_mode, int, S_IRUGO); +MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates" + " use default timings"); + +#define DENALI_NAND_NAME "denali-nand" + +/* We define a macro here that combines all interrupts this driver uses into + * a single constant value, for convenience. */ +#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ + INTR_STATUS0__ECC_TRANSACTION_DONE | \ + INTR_STATUS0__ECC_ERR | \ + INTR_STATUS0__PROGRAM_FAIL | \ + INTR_STATUS0__LOAD_COMP | \ + INTR_STATUS0__PROGRAM_COMP | \ + INTR_STATUS0__TIME_OUT | \ + INTR_STATUS0__ERASE_FAIL | \ + INTR_STATUS0__RST_COMP | \ + INTR_STATUS0__ERASE_COMP) + +/* indicates whether or not the internal value for the flash bank is + valid or not */ +#define CHIP_SELECT_INVALID -1 + +#define SUPPORT_8BITECC 1 + +/* This macro divides two integers and rounds fractional values up + * to the nearest integer value. */ +#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) + +/* this macro allows us to convert from an MTD structure to our own + * device context (denali) structure. + */ +#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd) + +/* These constants are defined by the driver to enable common driver + configuration options. */ +#define SPARE_ACCESS 0x41 +#define MAIN_ACCESS 0x42 +#define MAIN_SPARE_ACCESS 0x43 + +#define DENALI_READ 0 +#define DENALI_WRITE 0x100 + +/* types of device accesses. We can issue commands and get status */ +#define COMMAND_CYCLE 0 +#define ADDR_CYCLE 1 +#define STATUS_CYCLE 2 + +/* this is a helper macro that allows us to + * format the bank into the proper bits for the controller */ +#define BANK(x) ((x) << 24) + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; + + +/* these are static lookup tables that give us easy access to + registers in the NAND controller. + */ +static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, + INTR_STATUS1, + INTR_STATUS2, + INTR_STATUS3}; + +static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, + DEVICE_RESET__BANK1, + DEVICE_RESET__BANK2, + DEVICE_RESET__BANK3}; + +static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, + INTR_STATUS1__TIME_OUT, + INTR_STATUS2__TIME_OUT, + INTR_STATUS3__TIME_OUT}; + +static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, + INTR_STATUS1__RST_COMP, + INTR_STATUS2__RST_COMP, + INTR_STATUS3__RST_COMP}; + +/* specifies the debug level of the driver */ +static int nand_debug_level = 0; + +/* forward declarations */ +static void clear_interrupts(struct denali_nand_info *denali); +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask); +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask); +static uint32_t read_interrupt_status(struct denali_nand_info *denali); + +#define DEBUG_DENALI 0 + +/* This is a wrapper for writing to the denali registers. + * this allows us to create debug information so we can + * observe how the driver is programming the device. + * it uses standard linux convention for (val, addr) */ +static void denali_write32(uint32_t value, void *addr) +{ + iowrite32(value, addr); + +#if DEBUG_DENALI + printk(KERN_ERR "wrote: 0x%x -> 0x%x\n", value, (uint32_t)((uint32_t)addr & 0x1fff)); +#endif +} + +/* Certain operations for the denali NAND controller use an indexed mode to read/write + data. The operation is performed by writing the address value of the command to + the device memory followed by the data. This function abstracts this common + operation. +*/ +static void index_addr(struct denali_nand_info *denali, uint32_t address, uint32_t data) +{ + denali_write32(address, denali->flash_mem); + denali_write32(data, denali->flash_mem + 0x10); +} + +/* Perform an indexed read of the device */ +static void index_addr_read_data(struct denali_nand_info *denali, + uint32_t address, uint32_t *pdata) +{ + denali_write32(address, denali->flash_mem); + *pdata = ioread32(denali->flash_mem + 0x10); +} + +/* We need to buffer some data for some of the NAND core routines. + * The operations manage buffering that data. */ +static void reset_buf(struct denali_nand_info *denali) +{ + denali->buf.head = denali->buf.tail = 0; +} + +static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) +{ + BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf)); + denali->buf.buf[denali->buf.tail++] = byte; +} + +/* reads the status of the device */ +static void read_status(struct denali_nand_info *denali) +{ + uint32_t cmd = 0x0; + + /* initialize the data buffer to store status */ + reset_buf(denali); + + /* initiate a device status read */ + cmd = MODE_11 | BANK(denali->flash_bank); + index_addr(denali, cmd | COMMAND_CYCLE, 0x70); + denali_write32(cmd | STATUS_CYCLE, denali->flash_mem); + + /* update buffer with status value */ + write_byte_to_buf(denali, ioread32(denali->flash_mem + 0x10)); + +#if DEBUG_DENALI + printk("device reporting status value of 0x%2x\n", denali->buf.buf[0]); +#endif +} + +/* resets a specific device connected to the core */ +static void reset_bank(struct denali_nand_info *denali) +{ + uint32_t irq_status = 0; + uint32_t irq_mask = reset_complete[denali->flash_bank] | + operation_timeout[denali->flash_bank]; + int bank = 0; + + clear_interrupts(denali); + + bank = device_reset_banks[denali->flash_bank]; + denali_write32(bank, denali->flash_reg + DEVICE_RESET); + + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status & operation_timeout[denali->flash_bank]) + { + printk(KERN_ERR "reset bank failed.\n"); + } +} + +/* Reset the flash controller */ +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) +{ + uint32_t i; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { + denali_write32(device_reset_banks[i], denali->flash_reg + DEVICE_RESET); + while (!(ioread32(denali->flash_reg + intr_status_addresses[i]) & + (reset_complete[i] | operation_timeout[i]))) + ; + if (ioread32(denali->flash_reg + intr_status_addresses[i]) & + operation_timeout[i]) + nand_dbg_print(NAND_DBG_WARN, + "NAND Reset operation timed out on bank %d\n", i); + } + + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + return PASS; +} + +/* this routine calculates the ONFI timing values for a given mode and programs + * the clocking register accordingly. The mode is determined by the get_onfi_nand_para + routine. + */ +static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode) +{ + uint16_t Trea[6] = {40, 30, 25, 20, 20, 16}; + uint16_t Trp[6] = {50, 25, 17, 15, 12, 10}; + uint16_t Treh[6] = {30, 15, 15, 10, 10, 7}; + uint16_t Trc[6] = {100, 50, 35, 30, 25, 20}; + uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15}; + uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5}; + uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25}; + uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70}; + uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60}; + uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15}; + + uint16_t TclsRising = 1; + uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid; + uint16_t dv_window = 0; + uint16_t en_lo, en_hi; + uint16_t acc_clks; + uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + en_lo = CEIL_DIV(Trp[mode], CLK_X); + en_hi = CEIL_DIV(Treh[mode], CLK_X); +#if ONFI_BLOOM_TIME + if ((en_hi * CLK_X) < (Treh[mode] + 2)) + en_hi++; +#endif + + if ((en_lo + en_hi) * CLK_X < Trc[mode]) + en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X); + + if ((en_lo + en_hi) < CLK_MULTI) + en_lo += CLK_MULTI - en_lo - en_hi; + + while (dv_window < 8) { + data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode]; + + data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode]; + + data_invalid = + data_invalid_rhoh < + data_invalid_rloh ? data_invalid_rhoh : data_invalid_rloh; + + dv_window = data_invalid - Trea[mode]; + + if (dv_window < 8) + en_lo++; + } + + acc_clks = CEIL_DIV(Trea[mode], CLK_X); + + while (((acc_clks * CLK_X) - Trea[mode]) < 3) + acc_clks++; + + if ((data_invalid - acc_clks * CLK_X) < 2) + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d: Warning!\n", + __FILE__, __LINE__); + + addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); + re_2_we = CEIL_DIV(Trhw[mode], CLK_X); + re_2_re = CEIL_DIV(Trhz[mode], CLK_X); + we_2_re = CEIL_DIV(Twhr[mode], CLK_X); + cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X); + if (!TclsRising) + cs_cnt = CEIL_DIV(Tcs[mode], CLK_X); + if (cs_cnt == 0) + cs_cnt = 1; + + if (Tcea[mode]) { + while (((cs_cnt * CLK_X) + Trea[mode]) < Tcea[mode]) + cs_cnt++; + } + +#if MODE5_WORKAROUND + if (mode == 5) + acc_clks = 5; +#endif + + /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */ + if ((ioread32(denali->flash_reg + MANUFACTURER_ID) == 0) && + (ioread32(denali->flash_reg + DEVICE_ID) == 0x88)) + acc_clks = 6; + + denali_write32(acc_clks, denali->flash_reg + ACC_CLKS); + denali_write32(re_2_we, denali->flash_reg + RE_2_WE); + denali_write32(re_2_re, denali->flash_reg + RE_2_RE); + denali_write32(we_2_re, denali->flash_reg + WE_2_RE); + denali_write32(addr_2_data, denali->flash_reg + ADDR_2_DATA); + denali_write32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(cs_cnt, denali->flash_reg + CS_SETUP_CNT); +} + +/* configures the initial ECC settings for the controller */ +static void set_ecc_config(struct denali_nand_info *denali) +{ +#if SUPPORT_8BITECC + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) < 4096) || + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) <= 128)) + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + + if ((ioread32(denali->flash_reg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) + == 1) { + denali->dev_info.wECCBytesPerSector = 4; + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = + denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } else { + denali->dev_info.wECCBytesPerSector = + (ioread32(denali->flash_reg + ECC_CORRECTION) & + ECC_CORRECTION__VALUE) * 13 / 8; + if ((denali->dev_info.wECCBytesPerSector) % 2 == 0) + denali->dev_info.wECCBytesPerSector += 2; + else + denali->dev_info.wECCBytesPerSector += 1; + + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } +} + +/* queries the NAND device to see what ONFI modes it supports. */ +static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) +{ + int i; + uint16_t blks_lun_l, blks_lun_h, n_of_luns; + uint32_t blockperlun, id; + + denali_write32(DEVICE_RESET__BANK0, denali->flash_reg + DEVICE_RESET); + + while (!((ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { + denali_write32(DEVICE_RESET__BANK1, denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) { + denali_write32(DEVICE_RESET__BANK2, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) { + denali_write32(DEVICE_RESET__BANK3, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__TIME_OUT))) + ; + } else { + printk(KERN_ERR "Getting a time out for bank 2!\n"); + } + } else { + printk(KERN_ERR "Getting a time out for bank 1!\n"); + } + } + + denali_write32(INTR_STATUS0__TIME_OUT, denali->flash_reg + INTR_STATUS0); + denali_write32(INTR_STATUS1__TIME_OUT, denali->flash_reg + INTR_STATUS1); + denali_write32(INTR_STATUS2__TIME_OUT, denali->flash_reg + INTR_STATUS2); + denali_write32(INTR_STATUS3__TIME_OUT, denali->flash_reg + INTR_STATUS3); + + denali->dev_info.wONFIDevFeatures = + ioread32(denali->flash_reg + ONFI_DEVICE_FEATURES); + denali->dev_info.wONFIOptCommands = + ioread32(denali->flash_reg + ONFI_OPTIONAL_COMMANDS); + denali->dev_info.wONFITimingMode = + ioread32(denali->flash_reg + ONFI_TIMING_MODE); + denali->dev_info.wONFIPgmCacheTimingMode = + ioread32(denali->flash_reg + ONFI_PGM_CACHE_TIMING_MODE); + + n_of_luns = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; + blks_lun_l = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); + blks_lun_h = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); + + blockperlun = (blks_lun_h << 16) | blks_lun_l; + + denali->dev_info.wTotalBlocks = n_of_luns * blockperlun; + + if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) & + ONFI_TIMING_MODE__VALUE)) + return FAIL; + + for (i = 5; i > 0; i--) { + if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & (0x01 << i)) + break; + } + + NAND_ONFi_Timing_Mode(denali, i); + + index_addr(denali, MODE_11 | 0, 0x90); + index_addr(denali, MODE_11 | 1, 0); + + for (i = 0; i < 3; i++) + index_addr_read_data(denali, MODE_11 | 2, &id); + + nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); + + denali->dev_info.MLCDevice = id & 0x0C; + + /* By now, all the ONFI devices we know support the page cache */ + /* rw feature. So here we enable the pipeline_rw_ahead feature */ + /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */ + /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */ + + return PASS; +} + +static void get_samsung_nand_para(struct denali_nand_info *denali) +{ + uint8_t no_of_planes; + uint32_t blk_size; + uint64_t plane_size, capacity; + uint32_t id_bytes[5]; + int i; + + index_addr(denali, (uint32_t)(MODE_11 | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | 1), 0); + for (i = 0; i < 5; i++) + index_addr_read_data(denali, (uint32_t)(MODE_11 | 2), &id_bytes[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + id_bytes[0], id_bytes[1], id_bytes[2], + id_bytes[3], id_bytes[4]); + + if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ + /* Set timing register values according to datasheet */ + denali_write32(5, denali->flash_reg + ACC_CLKS); + denali_write32(20, denali->flash_reg + RE_2_WE); + denali_write32(12, denali->flash_reg + WE_2_RE); + denali_write32(14, denali->flash_reg + ADDR_2_DATA); + denali_write32(3, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(2, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(2, denali->flash_reg + CS_SETUP_CNT); + } + + no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); + plane_size = (uint64_t)64 << ((id_bytes[4] & 0x70) >> 4); + blk_size = 64 << ((ioread32(denali->flash_reg + DEVICE_PARAM_1) & 0x30) >> 4); + capacity = (uint64_t)128 * plane_size * no_of_planes; + + do_div(capacity, blk_size); + denali->dev_info.wTotalBlocks = capacity; +} + +static void get_toshiba_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t tmp; + + /* Workaround to fix a controller bug which reports a wrong */ + /* spare area size for some kind of Toshiba NAND device */ + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) && + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) { + denali_write32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) * + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + denali_write32(tmp, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + } + + /* As Toshiba NAND can not provide it's block number, */ + /* so here we need user to provide the correct block */ + /* number in a scratch register before the Linux NAND */ + /* driver is loaded. If no valid value found in the scratch */ + /* register, then we use default block number value */ + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +static void get_hynix_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t main_size, spare_size; + + switch (denali->dev_info.wDeviceID) { + case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ + case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ + denali_write32(128, denali->flash_reg + PAGES_PER_BLOCK); + denali_write32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali_write32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + main_size = 4096 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + spare_size = 224 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali_write32(main_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + denali_write32(spare_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + denali_write32(0, denali->flash_reg + DEVICE_WIDTH); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + denali->dev_info.MLCDevice = 1; + break; + default: + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." + "Will use default parameter values instead.\n", + denali->dev_info.wDeviceID); + } + + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +/* determines how many NAND chips are connected to the controller. Note for + Intel CE4100 devices we don't support more than one device. + */ +static void find_valid_banks(struct denali_nand_info *denali) +{ + uint32_t id[LLD_MAX_FLASH_BANKS]; + int i; + + denali->total_used_banks = 1; + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); + index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "Return 1st ID for bank[%d]: %x\n", i, id[i]); + + if (i == 0) { + if (!(id[i] & 0x0ff)) + break; /* WTF? */ + } else { + if ((id[i] & 0x0ff) == (id[0] & 0x0ff)) + denali->total_used_banks++; + else + break; + } + } + + if (denali->platform == INTEL_CE4100) + { + /* Platform limitations of the CE4100 device limit + * users to a single chip solution for NAND. + * Multichip support is not enabled. + */ + if (denali->total_used_banks != 1) + { + printk(KERN_ERR "Sorry, Intel CE4100 only supports " + "a single NAND device.\n"); + BUG(); + } + } + nand_dbg_print(NAND_DBG_DEBUG, + "denali->total_used_banks: %d\n", denali->total_used_banks); +} + +static void detect_partition_feature(struct denali_nand_info *denali) +{ + if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & + PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + denali->dev_info.wSpectraStartBlock = + ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MIN_VALUE) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & + MIN_BLK_ADDR_1__VALUE); + + denali->dev_info.wSpectraEndBlock = + (((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MAX_VALUE) >> 2) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MAX_BLK_ADDR_1) & + MAX_BLK_ADDR_1__VALUE); + + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + + if (denali->dev_info.wSpectraEndBlock >= + denali->dev_info.wTotalBlocks) { + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + } + + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } +} + +static void dump_device_info(struct denali_nand_info *denali) +{ + nand_dbg_print(NAND_DBG_DEBUG, "denali->dev_info:\n"); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", + denali->dev_info.wDeviceMaker); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", + denali->dev_info.wDeviceID); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", + denali->dev_info.wDeviceType); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", + denali->dev_info.wSpectraStartBlock); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", + denali->dev_info.wSpectraEndBlock); + nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", + denali->dev_info.wTotalBlocks); + nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", + denali->dev_info.wPagesPerBlock); + nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", + denali->dev_info.wPageSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", + denali->dev_info.wPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", + denali->dev_info.wPageSpareSize); + nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", + denali->dev_info.wNumPageSpareFlag); + nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", + denali->dev_info.wECCBytesPerSector); + nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", + denali->dev_info.wBlockSize); + nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", + denali->dev_info.wBlockDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", + denali->dev_info.wDataBlockNum); + nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", + denali->dev_info.bPlaneNum); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", + denali->dev_info.wDeviceMainAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", + denali->dev_info.wDeviceSpareAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", + denali->dev_info.wDevicesConnected); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", + denali->dev_info.wDeviceWidth); + nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", + denali->dev_info.wHWRevision); + nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", + denali->dev_info.wHWFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", + denali->dev_info.wONFIDevFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", + denali->dev_info.wONFIOptCommands); + nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", + denali->dev_info.wONFITimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", + denali->dev_info.wONFIPgmCacheTimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", + denali->dev_info.MLCDevice ? "Yes" : "No"); + nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", + denali->dev_info.wSpareSkipBytes); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", + denali->dev_info.nBitsInPageNumber); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", + denali->dev_info.nBitsInPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", + denali->dev_info.nBitsInBlockDataSize); +} + +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) +{ + uint16_t status = PASS; + uint8_t no_of_planes; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali->dev_info.wDeviceMaker = ioread32(denali->flash_reg + MANUFACTURER_ID); + denali->dev_info.wDeviceID = ioread32(denali->flash_reg + DEVICE_ID); + denali->dev_info.bDeviceParam0 = ioread32(denali->flash_reg + DEVICE_PARAM_0); + denali->dev_info.bDeviceParam1 = ioread32(denali->flash_reg + DEVICE_PARAM_1); + denali->dev_info.bDeviceParam2 = ioread32(denali->flash_reg + DEVICE_PARAM_2); + + denali->dev_info.MLCDevice = ioread32(denali->flash_reg + DEVICE_PARAM_0) & 0x0c; + + if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ + if (FAIL == get_onfi_nand_para(denali)) + return FAIL; + } else if (denali->dev_info.wDeviceMaker == 0xEC) { /* Samsung NAND */ + get_samsung_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0x98) { /* Toshiba NAND */ + get_toshiba_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0xAD) { /* Hynix NAND */ + get_hynix_nand_para(denali); + } else { + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->dev_info.wHWRevision = ioread32(denali->flash_reg + REVISION); + denali->dev_info.wHWFeatures = ioread32(denali->flash_reg + FEATURES); + + denali->dev_info.wDeviceMainAreaSize = + ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali->dev_info.wDeviceSpareAreaSize = + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + + denali->dev_info.wPageDataSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + + /* Note: When using the Micon 4K NAND device, the controller will report + * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. + * And if force set it to 218 bytes, the controller can not work + * correctly. So just let it be. But keep in mind that this bug may + * cause + * other problems in future. - Yunpeng 2008-10-10 + */ + denali->dev_info.wPageSpareSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + + denali->dev_info.wPagesPerBlock = ioread32(denali->flash_reg + PAGES_PER_BLOCK); + + denali->dev_info.wPageSize = + denali->dev_info.wPageDataSize + denali->dev_info.wPageSpareSize; + denali->dev_info.wBlockSize = + denali->dev_info.wPageSize * denali->dev_info.wPagesPerBlock; + denali->dev_info.wBlockDataSize = + denali->dev_info.wPagesPerBlock * denali->dev_info.wPageDataSize; + + denali->dev_info.wDeviceWidth = ioread32(denali->flash_reg + DEVICE_WIDTH); + denali->dev_info.wDeviceType = + ((ioread32(denali->flash_reg + DEVICE_WIDTH) > 0) ? 16 : 8); + + denali->dev_info.wDevicesConnected = ioread32(denali->flash_reg + DEVICES_CONNECTED); + + denali->dev_info.wSpareSkipBytes = + ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES) * + denali->dev_info.wDevicesConnected; + + denali->dev_info.nBitsInPageNumber = + ilog2(denali->dev_info.wPagesPerBlock); + denali->dev_info.nBitsInPageDataSize = + ilog2(denali->dev_info.wPageDataSize); + denali->dev_info.nBitsInBlockDataSize = + ilog2(denali->dev_info.wBlockDataSize); + + set_ecc_config(denali); + + no_of_planes = ioread32(denali->flash_reg + NUMBER_OF_PLANES) & + NUMBER_OF_PLANES__VALUE; + + switch (no_of_planes) { + case 0: + case 1: + case 3: + case 7: + denali->dev_info.bPlaneNum = no_of_planes + 1; + break; + default: + status = FAIL; + break; + } + + find_valid_banks(denali); + + detect_partition_feature(denali); + + dump_device_info(denali); + + /* If the user specified to override the default timings + * with a specific ONFI mode, we apply those changes here. + */ + if (onfi_timing_mode != NAND_DEFAULT_TIMINGS) + { + NAND_ONFi_Timing_Mode(denali, onfi_timing_mode); + } + + return status; +} + +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, + uint16_t INT_ENABLE) +{ + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + if (INT_ENABLE) + denali_write32(1, denali->flash_reg + GLOBAL_INT_ENABLE); + else + denali_write32(0, denali->flash_reg + GLOBAL_INT_ENABLE); +} + +/* validation function to verify that the controlling software is making + a valid request + */ +static inline bool is_flash_bank_valid(int flash_bank) +{ + return (flash_bank >= 0 && flash_bank < 4); +} + +static void denali_irq_init(struct denali_nand_info *denali) +{ + uint32_t int_mask = 0; + + /* Disable global interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + + int_mask = DENALI_IRQ_ALL; + + /* Clear all status bits */ + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS0); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS1); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS2); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS3); + + denali_irq_enable(denali, int_mask); +} + |