|
@@ -28,17 +28,6 @@
|
|
|
|
|
|
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"
|
|
|
|
|
|
/*
|
|
@@ -63,10 +52,12 @@ MODULE_PARM_DESC(onfi_timing_mode,
|
|
|
#define CHIP_SELECT_INVALID -1
|
|
|
|
|
|
/*
|
|
|
- * This macro divides two integers and rounds fractional values up
|
|
|
- * to the nearest integer value.
|
|
|
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
|
|
|
+ * clk_x is an integral multiple N of the core clk. The value N is configured
|
|
|
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
|
|
|
+ * to the largest value to make it work with any possible configuration.
|
|
|
*/
|
|
|
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
|
|
|
+#define DENALI_CLK_X_MULT 6
|
|
|
|
|
|
/*
|
|
|
* this macro allows us to convert from an MTD structure to our own
|
|
@@ -195,148 +186,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
|
|
|
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_set(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 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;
|
|
|
-
|
|
|
- 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)
|
|
|
- dev_warn(denali->dev, "%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 (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;
|
|
|
-
|
|
|
- iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
|
|
|
- iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
|
|
|
- iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
|
|
|
- iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
|
|
|
- iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
|
|
|
- iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
- iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
- iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
|
|
|
-}
|
|
|
-
|
|
|
-/* 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;
|
|
|
-
|
|
|
- /*
|
|
|
- * we needn't to do a reset here because driver has already
|
|
|
- * reset all the banks before
|
|
|
- */
|
|
|
- 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_set(denali, i);
|
|
|
-
|
|
|
- /*
|
|
|
- * 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 device_id)
|
|
|
-{
|
|
|
- if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
|
|
|
- /* Set timing register values according to datasheet */
|
|
|
- iowrite32(5, denali->flash_reg + ACC_CLKS);
|
|
|
- iowrite32(20, denali->flash_reg + RE_2_WE);
|
|
|
- iowrite32(12, denali->flash_reg + WE_2_RE);
|
|
|
- iowrite32(14, denali->flash_reg + ADDR_2_DATA);
|
|
|
- iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
- iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
- iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Use the configuration feature register to determine the maximum number of
|
|
|
* banks that the hardware supports.
|
|
@@ -352,58 +201,6 @@ static void detect_max_banks(struct denali_nand_info *denali)
|
|
|
denali->max_banks <<= 1;
|
|
|
}
|
|
|
|
|
|
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint16_t status = PASS;
|
|
|
- uint32_t id_bytes[8], addr;
|
|
|
- uint8_t maf_id, device_id;
|
|
|
- int i;
|
|
|
-
|
|
|
- /*
|
|
|
- * Use read id method to get device ID and other params.
|
|
|
- * For some NAND chips, controller can't report the correct
|
|
|
- * device ID by reading from DEVICE_ID register
|
|
|
- */
|
|
|
- addr = MODE_11 | BANK(denali->flash_bank);
|
|
|
- index_addr(denali, addr | 0, 0x90);
|
|
|
- index_addr(denali, addr | 1, 0);
|
|
|
- for (i = 0; i < 8; i++)
|
|
|
- index_addr_read_data(denali, addr | 2, &id_bytes[i]);
|
|
|
- maf_id = id_bytes[0];
|
|
|
- device_id = id_bytes[1];
|
|
|
-
|
|
|
- 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 (maf_id == 0xEC) { /* Samsung NAND */
|
|
|
- get_samsung_nand_para(denali, device_id);
|
|
|
- }
|
|
|
-
|
|
|
- dev_info(denali->dev,
|
|
|
- "Dump timing register values:\n"
|
|
|
- "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
|
|
|
- "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
|
|
|
- "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 + RE_2_RE),
|
|
|
- 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));
|
|
|
-
|
|
|
- /*
|
|
|
- * 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_set(denali, onfi_timing_mode);
|
|
|
-
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
static void denali_set_intr_modes(struct denali_nand_info *denali,
|
|
|
uint16_t INT_ENABLE)
|
|
|
{
|
|
@@ -1209,7 +1006,121 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
-/* end NAND core entry points */
|
|
|
+
|
|
|
+#define DIV_ROUND_DOWN_ULL(ll, d) \
|
|
|
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
|
|
|
+
|
|
|
+static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|
|
+ const struct nand_data_interface *conf)
|
|
|
+{
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ const struct nand_sdr_timings *timings;
|
|
|
+ unsigned long t_clk;
|
|
|
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
|
|
|
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
|
|
|
+ int addr_2_data_mask;
|
|
|
+ uint32_t tmp;
|
|
|
+
|
|
|
+ timings = nand_get_sdr_timings(conf);
|
|
|
+ if (IS_ERR(timings))
|
|
|
+ return PTR_ERR(timings);
|
|
|
+
|
|
|
+ /* clk_x period in picoseconds */
|
|
|
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
|
|
|
+ if (!t_clk)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* tREA -> ACC_CLKS */
|
|
|
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
|
|
|
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + ACC_CLKS);
|
|
|
+ tmp &= ~ACC_CLKS__VALUE;
|
|
|
+ tmp |= acc_clks;
|
|
|
+ iowrite32(tmp, denali->flash_reg + ACC_CLKS);
|
|
|
+
|
|
|
+ /* tRWH -> RE_2_WE */
|
|
|
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
|
|
|
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + RE_2_WE);
|
|
|
+ tmp &= ~RE_2_WE__VALUE;
|
|
|
+ tmp |= re_2_we;
|
|
|
+ iowrite32(tmp, denali->flash_reg + RE_2_WE);
|
|
|
+
|
|
|
+ /* tRHZ -> RE_2_RE */
|
|
|
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
|
|
|
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + RE_2_RE);
|
|
|
+ tmp &= ~RE_2_RE__VALUE;
|
|
|
+ tmp |= re_2_re;
|
|
|
+ iowrite32(tmp, denali->flash_reg + RE_2_RE);
|
|
|
+
|
|
|
+ /* tWHR -> WE_2_RE */
|
|
|
+ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
|
|
|
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + TWHR2_AND_WE_2_RE);
|
|
|
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
|
|
|
+ tmp |= we_2_re;
|
|
|
+ iowrite32(tmp, denali->flash_reg + TWHR2_AND_WE_2_RE);
|
|
|
+
|
|
|
+ /* tADL -> ADDR_2_DATA */
|
|
|
+
|
|
|
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
|
|
|
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
|
|
|
+ if (denali->revision < 0x0501)
|
|
|
+ addr_2_data_mask >>= 1;
|
|
|
+
|
|
|
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
|
|
|
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
|
|
|
+ tmp &= ~addr_2_data_mask;
|
|
|
+ tmp |= addr_2_data;
|
|
|
+ iowrite32(tmp, denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
|
|
|
+
|
|
|
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
|
|
|
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
|
|
|
+ tmp |= rdwr_en_hi;
|
|
|
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
+
|
|
|
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
|
|
|
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
|
|
|
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
|
|
|
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
|
|
|
+ tmp |= rdwr_en_lo;
|
|
|
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
+
|
|
|
+ /* tCS, tCEA -> CS_SETUP_CNT */
|
|
|
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
|
|
|
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
|
|
|
+ 0);
|
|
|
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->flash_reg + CS_SETUP_CNT);
|
|
|
+ tmp &= ~CS_SETUP_CNT__VALUE;
|
|
|
+ tmp |= cs_setup;
|
|
|
+ iowrite32(tmp, denali->flash_reg + CS_SETUP_CNT);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
/* Initialization code to bring the device up to a known good state */
|
|
|
static void denali_hw_init(struct denali_nand_info *denali)
|
|
@@ -1241,7 +1152,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
|
|
|
/* Should set value for these registers when init */
|
|
|
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
|
|
|
iowrite32(1, denali->flash_reg + ECC_ENABLE);
|
|
|
- denali_nand_timing_set(denali);
|
|
|
denali_irq_init(denali);
|
|
|
}
|
|
|
|
|
@@ -1416,17 +1326,6 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
|
|
int ret;
|
|
|
|
|
|
- if (denali->platform == INTEL_CE4100) {
|
|
|
- /*
|
|
|
- * Due to a silicon limitation, we can only support
|
|
|
- * ONFI timing mode 1 and below.
|
|
|
- */
|
|
|
- if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
|
|
|
- pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/* allocate a temporary buffer for nand_scan_ident() */
|
|
|
denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
|
|
|
GFP_DMA | GFP_KERNEL);
|
|
@@ -1460,6 +1359,10 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
|
|
|
chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
|
|
|
|
|
|
+ /* clk rate info is needed for setup_data_interface */
|
|
|
+ if (denali->clk_x_rate)
|
|
|
+ chip->setup_data_interface = denali_setup_data_interface;
|
|
|
+
|
|
|
/*
|
|
|
* scan for NAND devices attached to the controller
|
|
|
* this is the first stage in a two step process to register
|