Add new, common setup code for QC/A WiSoC DDR controllers
[oweals/u-boot_mod.git] / u-boot / cpu / mips / ar7240 / qca_dram.c
index c7ee83d806779ebae31e88f16c4eddcdfbf09eef..3bca1d3e09dcf8ca7bf240604c997c993d77b5ae 100644 (file)
@@ -1,8 +1,15 @@
 /*
- * Qualcomm/Atheros WiSoCs DRAM related common functions
+ * Qualcomm/Atheros WiSoCs DRAM related
+ * functions for WiSoC families:
+ * - Atheros AR933x
+ * - Atheros AR934x
+ * - Qualcomm/Atheros QCA953x
+ * - Qualcomm/Atheros QCA955x
+ * - Qualcomm/Atheros QCA956x
  *
  * Copyright (C) 2016 Piotr Dymacz <piotr@dymacz.pl>
- * Copyright (C) 2013 Qualcomm Atheros, Inc.
+ * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
+ * Copyright (C) 2014 Qualcomm Atheros, Inc.
  * Copyright (C) 2008-2010 Atheros Communications Inc.
  *
  * SPDX-License-Identifier: GPL-2.0
@@ -62,6 +69,7 @@ u32 qca_dram_size(void)
 u32 qca_dram_type(void)
 {
 #if defined(CONFIG_BOARD_DRAM_TYPE_SDR)
+       #error "SDRAM is not supported!"
        return RAM_MEMORY_TYPE_SDR;
 #elif defined(CONFIG_BOARD_DRAM_TYPE_DDR1)
        return RAM_MEMORY_TYPE_DDR1;
@@ -132,3 +140,744 @@ u32 qca_dram_cas_lat(void)
        return CONFIG_BOARD_DRAM_CAS_LATENCY
 #endif
 }
+
+/*
+ * ===============================================
+ * DQS delay tap controller tune related functions
+ * ===============================================
+ */
+#define DQS_DELAY_TAP_DEFAULT_VAL              8
+
+#if (SOC_TYPE & QCA_AR933X_SOC) |\
+       (SOC_TYPE & QCA_AR934X_SOC)
+       #define DQS_DELAY_TAP_MAX_VAL           62
+#else
+       #define DQS_DELAY_TAP_MAX_VAL           63
+#endif
+
+/*
+ * Setup DQS_{0,1,2,3} delay tap control register/s
+ */
+static void qca_ddr_tap_save(u32 tap, u32 ddr_width)
+{
+#if (SOC_TYPE & QCA_AR933X_SOC) |\
+       (SOC_TYPE & QCA_AR934X_SOC)
+       u32 tap_h;
+
+       /* It seems that AR93xx SoCs have two delay chains */
+       if (tap > (DQS_DELAY_TAP_MAX_VAL / 2)) {
+               tap_h = tap - (DQS_DELAY_TAP_MAX_VAL / 2);
+               tap = tap & QCA_DDR_TAP_CTRL_TAP_L_MASK;
+               tap = tap | (tap_h << QCA_DDR_TAP_CTRL_TAP_H_SHIFT);
+       }
+#endif
+
+       qca_soc_reg_write(QCA_DDR_TAP_CTRL_0_REG, tap);
+       qca_soc_reg_write(QCA_DDR_TAP_CTRL_1_REG, tap);
+
+       /* Setup DQS2 and DQS3 only for 32-bit DDR interface width */
+       if (ddr_width == 32) {
+               qca_soc_reg_write(QCA_DDR_TAP_CTRL_2_REG, tap);
+               qca_soc_reg_write(QCA_DDR_TAP_CTRL_3_REG, tap);
+       }
+}
+
+/*
+ * Only for AR933x we will use different code
+ * for delay tap controller tune as it seems
+ * that this SoC doesn't have DDR BIST.
+ *
+ * Below function is universal, so it should
+ * work also for other QC/A WiSoCs and give
+ * same (or very similar) results. The only
+ * difference is that the DDR BIST based
+ * version seems to be much faster.
+ */
+#if (SOC_TYPE & QCA_AR933X_SOC)
+
+       #define DQS_DELAY_TAP_PATTERN_OFFSET    0x2000
+       #define DQS_DELAY_TAP_PATTERN_SIZE              0x1000
+       #define DQS_DELAY_TAP_TEST_LOOPS                2
+
+/*
+ * Prepare pattern for further tests
+ */
+static inline void qca_ddr_tap_patt(void)
+{
+       u32 i, j, pat;
+       u32 *addr;
+
+       /* Prepare 4M (256 x 4 x 4 bytes) pattern */
+       addr = (void *)KSEG1ADDR(DQS_DELAY_TAP_PATTERN_OFFSET);
+
+       for (i = 0; i < 256; i++) {
+               pat = 0;
+
+               for (j = 0; j < 8; j++) {
+                       if (i & (1 << j)) {
+                               if (j % 2)
+                                       pat |= 0xFFFF0000;
+                               else
+                                       pat |= 0x0000FFFF;
+                       }
+
+                       if (j % 2) {
+                               *addr++ = pat;
+                               pat = 0;
+                       }
+               }
+       }
+}
+
+/*
+ * This function is a modified C version of the original
+ * ar933x_ddr_tap_init() function, written in asm,
+ * included in Atheros (Q)SDK code.
+ *
+ * Below is a modified version, partially based on:
+ * https://patchwork.ozlabs.org/patch/569046/
+ */
+static void qca_ddr_tap_tune(u32 ddr_width)
+{
+       u32 *addr, *addr_k0, *addr_k1;
+       u32 tap, tap_hi, tap_lo;
+       u32 err, got_lo, i;
+
+       /* Pattern */
+       qca_ddr_tap_patt();
+
+       got_lo = 0;
+       tap_hi = 0;
+       tap_lo = 0;
+
+       addr = (void *)KSEG1ADDR(DQS_DELAY_TAP_PATTERN_OFFSET
+                                                        + DQS_DELAY_TAP_PATTERN_SIZE);
+
+       /*
+        * Idea here is to test all possible tap values, one by one,
+        * starting from the lowest. We are looking for a range within
+        * the written and read back data is the same. We assume here
+        * that the valid tap range is continuous.
+        *
+        * From hardware POV, delay tap controller is used to adjust
+        * the data window.
+        */
+       for (tap = 0; tap <= DQS_DELAY_TAP_MAX_VAL; tap++) {
+               qca_ddr_tap_save(tap, ddr_width);
+
+               err = 0;
+
+               for (i = 0; i < DQS_DELAY_TAP_TEST_LOOPS; i++) {
+                       addr_k1 = (void *)KSEG1ADDR(DQS_DELAY_TAP_PATTERN_OFFSET);
+                       addr_k0 = (void *)KSEG0ADDR(DQS_DELAY_TAP_PATTERN_OFFSET);
+
+                       while (addr_k1 < addr) {
+                               if (*addr_k1++ != *addr_k0++) {
+                                       err = 1;
+                                       break;
+                               }
+                       }
+
+                       if (err)
+                               break;
+               }
+
+               if (err) {
+                       if (got_lo) {
+                               if (tap > 0)
+                                       tap_hi = tap - 1;
+
+                               break;
+                       }
+               } else {
+                       if (!got_lo) {
+                               tap_lo = tap;
+                               got_lo = 1;
+                       } else {
+                               tap_hi = tap;
+                       }
+               }
+       }
+
+       /* Calculate final tap value (rounded up average) */
+       if (got_lo) {
+               tap = (tap_hi + tap_lo + 1) / 2;
+       } else {
+               tap = DQS_DELAY_TAP_DEFAULT_VAL;
+       }
+
+       qca_ddr_tap_save(tap, ddr_width);
+}
+
+#else /* SOC_TYPE & QCA_AR933X_SOC */
+
+       #define DQS_DELAY_TAP_TEST_LOOPS        8
+
+/*
+ * Unknown magic values and registers from Atheros (Q)SDK.
+ *
+ * It looks like some test patterns and masks setup,
+ * but it's not confirmed. Used here values are
+ * different, but were tested on real hardware.
+ */
+static inline void qca_ddr_tap_bist_init(void)
+{
+       qca_soc_reg_write(QCA_DDR_PERF_COMP_AHB_GE0_0_REG, 0xAAAAAAAA);
+       qca_soc_reg_write(QCA_DDR_PERF_MASK_AHB_GE0_0_REG, 0xAAAAAAAA);
+
+       qca_soc_reg_write(QCA_DDR_PERF_COMP_AHB_GE0_1_REG, 0x55555555);
+       qca_soc_reg_write(QCA_DDR_PERF_MASK_AHB_GE0_1_REG, 0x55555555);
+
+       qca_soc_reg_write(QCA_DDR_PERF_COMP_AHB_GE1_0_REG, 0xAAAAAAAA);
+       qca_soc_reg_write(QCA_DDR_PERF_MASK_AHB_GE1_0_REG, 0xAAAAAAAA);
+
+       qca_soc_reg_write(QCA_DDR_PERF_COMP_AHB_GE1_1_REG, 0x55555555);
+       qca_soc_reg_write(QCA_DDR_PERF_MASK_AHB_GE1_1_REG, 0x55555555);
+}
+
+/*
+ * This function is a modified C version of the original
+ * ath_ddr_tap_cal() function, written in asm,
+ * included in Atheros (Q)SDK code.
+ *
+ * It seems that newer QC/A WiSoCs have some kind of
+ * built-in self-test (BIST) for DDR controller, but
+ * none of the used registers or their values are
+ * described in datasheets, so for now, we will just
+ * use them as in original code.
+ *
+ * Below is a modified version, partially based on:
+ * https://patchwork.ozlabs.org/patch/569047/
+ */
+static void qca_ddr_tap_tune(u32 ddr_width)
+{
+       u32 tap, tap_hi, tap_lo;
+       u32 fail, got_lo, reg;
+
+       got_lo = 0;
+       tap_hi = 0;
+       tap_lo = 0;
+
+       /* How many test loops per tested tap value */
+       qca_soc_reg_write(QCA_DDR_PERF_COMP_ADDR_1_REG,
+                                         (DQS_DELAY_TAP_TEST_LOOPS
+                                          << QCA_DDR_PERF_COMP_ADDR_1_TEST_CNT_SHIFT));
+
+       /*
+        * Unknown magic value, original comment:
+        * "4 Row Address Bits, 4 Column Address Bits, 2 BA bits"
+        */
+       qca_soc_reg_write(QCA_DDR_PERF_MASK_ADDR_0_REG, 0xFA5DE83F);
+
+       /*
+        * Test all possible tap values, try to find working range
+        * (minimum and maximum delays) and use average value
+        */
+       for (tap = 0; tap <= DQS_DELAY_TAP_MAX_VAL; tap++) {
+               qca_ddr_tap_save(tap, ddr_width);
+
+               qca_ddr_tap_bist_init();
+
+               /* Enable BIST test and wait for finish */
+               qca_soc_reg_write(QCA_DDR_BIST_REG, QCA_DDR_BIST_TEST_EN_MASK);
+
+               do {
+                       reg = qca_soc_reg_read(QCA_DDR_BIST_STATUS_REG);
+               } while (!(reg & QCA_DDR_BIST_STATUS_DONE_MASK));
+
+               /* Disable BIST test */
+               qca_soc_reg_write(QCA_DDR_BIST_REG, 0);
+
+               /* Check how many tests failed */
+               fail = (reg & QCA_DDR_BIST_STATUS_FAIL_CNT_MASK)
+                          >> QCA_DDR_BIST_STATUS_FAIL_CNT_SHIFT;
+
+               if (fail == 0) {
+                       if (!got_lo) {
+                               tap_lo = tap;
+                               got_lo = 1;
+                       } else {
+                               tap_hi = tap;
+                       }
+               } else {
+                       if (got_lo) {
+                               if (tap > 0)
+                                       tap_hi = tap - 1;
+
+                               break;
+                       }
+               }
+       }
+
+       /* Calculate final tap value (rounded up average) */
+       if (got_lo) {
+               tap = (tap_hi + tap_lo + 1) / 2;
+       } else {
+               tap = DQS_DELAY_TAP_DEFAULT_VAL;
+       }
+
+       qca_ddr_tap_save(tap, ddr_width);
+}
+
+#endif /* SOC_TYPE & QCA_AR933X_SOC */
+
+/*
+ * ===============================================
+ * DDR controller initialization related functions
+ * ===============================================
+ */
+
+/*
+ * Below defines are "safe" DDR1/DDR2 timing parameters.
+ * They should work for most chips, but not for all.
+ *
+ * For different values, user can define target value
+ * of all memory controller related registers.
+ *
+ */
+#define DDRx_tMRD_ns   10
+#define DDRx_tRAS_ns   40
+#define DDRx_tRCD_ns   15
+#define DDRx_tRP_ns            15
+#define DDRx_tRRD_ns   10
+#define DDRx_tWR_ns            15
+#define DDRx_tWTR_ns   10
+
+#define DDR1_tRFC_ns   75
+#define DDR2_tRFC_ns   120
+
+#define DDR2_tFAW_ns   50
+#define DDR2_tWL_ns            5
+
+#define DDR_addit_lat  0
+#define DDR_burst_len  8
+
+/* All above values are safe for clocks not lower than below values */
+#define DDR1_timing_clk_max            400
+#define DDR2_timing_clk_max            533
+
+/* Maximum timing values, based on register fields sizes */
+#define MAX_tFAW               BITS(0, 6)
+#define MAX_tMRD               BITS(0, 4)
+#define MAX_tRAS               BITS(0, 5)
+#define MAX_tRCD               BITS(0, 4)
+#define MAX_tRFC               BITS(0, 6)
+#define MAX_tRP                        BITS(0, 4)
+#define MAX_tRRD               BITS(0, 4)
+#define MAX_tRTP               BITS(0, 4)
+#define MAX_tRTW               BITS(0, 5)
+#define MAX_tWL                        BITS(0, 4)
+#define MAX_tWR                        BITS(0, 4)
+#define MAX_tWTR               BITS(0, 5)
+
+/*
+ * Setup DDR_CONFIG register
+ */
+static inline void qca_dram_set_ddr_cfg(u32 mem_cas,
+                                                                               u32 ddr_clk,
+                                                                               u32 mem_type)
+{
+#ifndef CONFIG_QCA_DDR_CFG_REG_VAL
+       u32 reg = 0;
+       u32 tmp = 0;
+
+       reg = qca_soc_reg_read(QCA_DDR_CFG_REG);
+
+       /* Always use page close policy */
+       reg = reg | QCA_DDR_CFG_PAGE_CLOSE_MASK;
+
+       /* CAS should be (2 * MEM_CAS) or (2 * MEM_CAS) + 1/2/3 */
+       tmp = 2 * mem_cas;
+       tmp = (tmp << QCA_DDR_CFG_CAS_3LSB_SHIFT) & QCA_DDR_CFG_CAS_3LSB_MASK;
+       if (mem_cas > 3) {
+               tmp = tmp | QCA_DDR_CFG_CAS_MSB_MASK;
+       }
+
+       reg = reg & ~QCA_DDR_CFG_CAS_3LSB_MASK;
+       reg = reg | tmp;
+
+       /*
+        * Calculate rest of timing related values,
+        * always round up to closest integer
+        */
+
+       /* tMRD */
+       tmp = ((DDRx_tMRD_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tMRD)
+               tmp = MAX_tMRD;
+
+       tmp = (tmp << QCA_DDR_CFG_TMRD_SHIFT) & QCA_DDR_CFG_TMRD_MASK;
+       reg = reg & ~QCA_DDR_CFG_TMRD_MASK;
+       reg = reg | tmp;
+
+       /* tRFC */
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+               tmp = ((DDR2_tRFC_ns * ddr_clk) + 500) / 1000;
+       } else {
+               tmp = ((DDR1_tRFC_ns * ddr_clk) + 500) / 1000;
+       }
+
+       if (tmp > MAX_tRFC)
+               tmp = MAX_tRFC;
+
+       tmp = (tmp << QCA_DDR_CFG_TRFC_SHIFT) & QCA_DDR_CFG_TRFC_MASK;
+       reg = reg & ~QCA_DDR_CFG_TRFC_MASK;
+       reg = reg | tmp;
+
+       /* tRRD */
+       tmp = ((DDRx_tRRD_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tRRD)
+               tmp = MAX_tRRD;
+
+       tmp = (tmp << QCA_DDR_CFG_TRRD_SHIFT) & QCA_DDR_CFG_TRRD_MASK;
+       reg = reg & ~QCA_DDR_CFG_TRRD_MASK;
+       reg = reg | tmp;
+
+       /* tRP */
+       tmp = ((DDRx_tRP_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tRP)
+               tmp = MAX_tRP;
+
+       tmp = (tmp << QCA_DDR_CFG_TRP_SHIFT) & QCA_DDR_CFG_TRP_MASK;
+       reg = reg & ~QCA_DDR_CFG_TRP_MASK;
+       reg = reg | tmp;
+
+       /* tRCD */
+       tmp = ((DDRx_tRCD_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tRCD)
+               tmp = MAX_tRCD;
+
+       tmp = (tmp << QCA_DDR_CFG_TRCD_SHIFT) & QCA_DDR_CFG_TRCD_MASK;
+       reg = reg & ~QCA_DDR_CFG_TRCD_MASK;
+       reg = reg | tmp;
+
+       /* tRAS */
+       tmp = ((DDRx_tRAS_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tRAS)
+               tmp = MAX_tRAS;
+
+       tmp = (tmp << QCA_DDR_CFG_TRAS_SHIFT) & QCA_DDR_CFG_TRAS_MASK;
+       reg = reg & ~QCA_DDR_CFG_TRAS_MASK;
+       reg = reg | tmp;
+
+       qca_soc_reg_write(QCA_DDR_CFG_REG, reg);
+#else
+       qca_soc_reg_write(QCA_DDR_CFG_REG, CONFIG_QCA_DDR_CFG_REG_VAL);
+#endif
+}
+
+/*
+ * Setup DDR_CONFIG2 register
+ */
+static inline void qca_dram_set_ddr_cfg2(u32 mem_cas,
+                                                                                u32 ddr_clk,
+                                                                                u32 mem_type,
+                                                                                u32 ddr_width)
+{
+#ifndef CONFIG_QCA_DDR_CFG2_REG_VAL
+       u32 reg = 0;
+       u32 tmp = 0;
+
+       reg = qca_soc_reg_read(QCA_DDR_CFG2_REG);
+
+       /* Enable CKE */
+       reg = reg | QCA_DDR_CFG2_CKE_MASK;
+
+       /* Gate open latency = 2 * MEM_CAS */
+       tmp = 2 * mem_cas;
+       tmp = (tmp << QCA_DDR_CFG2_GATE_OPEN_LATENCY_SHIFT)
+                 & QCA_DDR_CFG2_GATE_OPEN_LATENCY_MASK;
+       reg = reg & ~QCA_DDR_CFG2_GATE_OPEN_LATENCY_MASK;
+       reg = reg | tmp;
+
+       /* tWTR */
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+               /* tWTR = 2 * WL + BL + 2 * max(tWTR/tCK, 2) */
+               tmp = 2 * (mem_cas + DDR_addit_lat - 1) + DDR_burst_len + 4;
+
+               if (ddr_clk >= 600)
+                       tmp = tmp + 2;
+       } else {
+               /* tWTR = 2 + BL + (2 * tWTR/tCK) */
+               tmp = 2 + DDR_burst_len + (((DDRx_tWTR_ns * ddr_clk) + 500) / 1000);
+       }
+
+       if (tmp > MAX_tWTR)
+               tmp = MAX_tWTR;
+
+       tmp = (tmp << QCA_DDR_CFG2_TWTR_SHIFT) & QCA_DDR_CFG2_TWTR_MASK;
+       reg = reg & ~QCA_DDR_CFG2_TWTR_MASK;
+       reg = reg | tmp;
+
+       /* tRTP */
+       if (ddr_width == 32) {
+               tmp = DDR_burst_len;
+       } else {
+               tmp = MAX_tRTP;
+       }
+
+       tmp = (tmp << QCA_DDR_CFG2_TRTP_SHIFT) & QCA_DDR_CFG2_TRTP_MASK;
+       reg = reg & ~QCA_DDR_CFG2_TRTP_MASK;
+       reg = reg | tmp;
+
+       /* tRTW */
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+               /* tRTW = 2 * (RL + BL/2 + 1 -WL), RL = CL + AL, WL = RL - 1 */
+               tmp = DDR_burst_len + 4;
+       } else {
+               /* tRTW = 2 * (CL + BL/2) */
+               tmp = DDR_burst_len + (2 * mem_cas);
+       }
+
+       if (tmp > MAX_tRTW)
+               tmp = MAX_tRTW;
+
+       tmp = (tmp << QCA_DDR_CFG2_TRTW_SHIFT) & QCA_DDR_CFG2_TRTW_MASK;
+       reg = reg & ~QCA_DDR_CFG2_TRTW_MASK;
+       reg = reg | tmp;
+
+       /* tWR */
+       tmp = ((DDRx_tWR_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tWR)
+               tmp = MAX_tWR;
+
+       tmp = (tmp << QCA_DDR_CFG2_TWR_SHIFT) & QCA_DDR_CFG2_TWR_MASK;
+       reg = reg & ~QCA_DDR_CFG2_TWR_MASK;
+       reg = reg | tmp;
+
+       /* Always use burst length = 8 and type: sequential */
+       tmp = (DDR_burst_len << QCA_DDR_CFG2_BURST_LEN_SHIFT)
+                 & QCA_DDR_CFG2_BURST_LEN_MASK;
+       reg = reg & ~(QCA_DDR_CFG2_BURST_LEN_MASK
+                                 | QCA_DDR_CFG2_BURST_TYPE_MASK);
+       reg = reg | tmp;
+
+       qca_soc_reg_write(QCA_DDR_CFG2_REG, reg);
+#else
+       qca_soc_reg_write(QCA_DDR_CFG2_REG, CONFIG_QCA_DDR_CFG2_REG_VAL);
+#endif
+}
+
+/*
+ * Setup DDR2_CONFIG register (only for DDR2)
+ */
+static inline void qca_dram_set_ddr2_cfg(u32 mem_cas,
+                                                                                u32 ddr_clk)
+{
+#ifndef CONFIG_QCA_DDR_DDR2_CFG_REG_VAL
+       u32 reg = 0;
+       u32 tmp = 0;
+
+       reg = qca_soc_reg_read(QCA_DDR_DDR2_CFG_REG);
+
+       /* Enable DDR2 */
+       reg = reg | QCA_DDR_DDR2_CFG_DDR2_EN_MASK;
+
+       /* tFAW */
+       tmp = ((DDR2_tFAW_ns * ddr_clk) + 500) / 1000;
+       if (tmp > MAX_tFAW)
+               tmp = MAX_tFAW;
+
+       tmp = (tmp << QCA_DDR_DDR2_CFG_DDR2_TFAW_SHIFT)
+                 & QCA_DDR_DDR2_CFG_DDR2_TFAW_MASK;
+       reg = reg & ~QCA_DDR_DDR2_CFG_DDR2_TFAW_MASK;
+       reg = reg | tmp;
+
+       /* tWL */
+       tmp = (2 * mem_cas) - 3;
+
+       /* For some reason, odd value doesn't work on AR933x (FIXME) */
+       #if (SOC_TYPE & QCA_AR933X_SOC)
+       if (tmp % 2)
+               tmp = tmp - 1;
+       #endif
+
+       tmp = (tmp << QCA_DDR_DDR2_CFG_DDR2_TWL_SHIFT)
+                 & QCA_DDR_DDR2_CFG_DDR2_TWL_MASK;
+       reg = reg & ~QCA_DDR_DDR2_CFG_DDR2_TWL_MASK;
+       reg = reg | tmp;
+
+       qca_soc_reg_write(QCA_DDR_DDR2_CFG_REG, reg);
+#else
+       qca_soc_reg_write(QCA_DDR_DDR2_CFG_REG, CONFIG_QCA_DDR_DDR2_CFG_REG_VAL);
+#endif
+}
+
+/*
+ * Enables DDR refresh and sets
+ * refresh period based on XTAL
+ */
+static inline void qca_dram_set_en_refresh(void)
+{
+       /*
+        * Enable DDR refresh and setup refresh period:
+        * 1. We assume 7.8 us maximum average period refresh interval
+        * 2. 7.8 us ~= 0.1282 MHz
+        * 3. For 25 MHz XTAL: (25 / 0.1282) ~= 195
+        * 4. For 40 MHz XTAL: (40 / 0.1282) ~= 312
+        */
+       if (qca_xtal_is_40mhz()) {
+               qca_soc_reg_write(QCA_DDR_REFRESH_REG,
+                                                 QCA_DDR_REFRESH_EN_MASK
+                                                 | (312 << QCA_DDR_REFRESH_PERIOD_SHIFT));
+       } else {
+               qca_soc_reg_write(QCA_DDR_REFRESH_REG,
+                                                 QCA_DDR_REFRESH_EN_MASK
+                                                 | (195 << QCA_DDR_REFRESH_PERIOD_SHIFT));
+       }
+}
+
+/*
+ * Initial DRAM configuration
+ */
+void qca_dram_init(void)
+{
+       u32 ahb_clk, cpu_clk, ddr_clk, mem_type, tmp_clk;
+       u32 cas_lat, ddr_width, wr_recovery;
+
+       mem_type = qca_dram_type();
+
+       qca_sys_clocks(&cpu_clk, &ddr_clk, &ahb_clk, NULL, NULL);
+       cpu_clk = cpu_clk / 1000000;
+       ddr_clk = ddr_clk / 1000000;
+       ahb_clk = ahb_clk / 1000000;
+
+       /* Set CAS based on clock, but allow to set static value */
+#ifndef CONFIG_BOARD_DRAM_CAS_LATENCY
+       if (mem_type == RAM_MEMORY_TYPE_DDR1) {
+               if (ddr_clk <= 266) {
+                       cas_lat = 2;
+               } else {
+                       cas_lat = 3;
+               }
+       } else {
+               if (ddr_clk <= 400) {
+                       cas_lat = 3;
+               } else if (ddr_clk <= 533) {
+                       cas_lat = 4;
+               } else if (ddr_clk <= 666) {
+                       cas_lat = 5;
+               } else if (ddr_clk <= 800) {
+                       cas_lat = 6;
+               } else {
+                       cas_lat = 7;
+               }
+       }
+#else
+       cas_lat = CONFIG_BOARD_DRAM_CAS_LATENCY;
+#endif
+
+#if (SOC_TYPE & QCA_AR933X_SOC)
+       /* AR933x supports only 16-bit memory */
+       ddr_width = 16;
+       qca_soc_reg_write(QCA_DDR_RD_DATA_THIS_CYCLE_REG, 0xFF);
+#else
+       /* For other WiSoCs we can determine DDR width, based on bootstrap */
+       ddr_width = qca_dram_ddr_width();
+
+       if (ddr_width == 32) {
+               /* For 32-bit clear HALF_WIDTH and set VEC = 0xFF */
+               qca_soc_reg_read_clear(QCA_DDR_CTRL_CFG_REG,
+                                                          QCA_DDR_CTRL_CFG_HALF_WIDTH_MASK);
+
+               qca_soc_reg_write(QCA_DDR_RD_DATA_THIS_CYCLE_REG, 0xFF);
+       } else {
+               qca_soc_reg_read_set(QCA_DDR_CTRL_CFG_REG,
+                                                        QCA_DDR_CTRL_CFG_HALF_WIDTH_MASK);
+
+               qca_soc_reg_write(QCA_DDR_RD_DATA_THIS_CYCLE_REG, 0xFFFF);
+       }
+
+       /* If DDR_CLK < 2 * AHB_CLK, set DDR FSM wait control to 0xA24 */
+       if (ddr_clk < (2 * ahb_clk))
+               qca_soc_reg_write(QCA_DDR_FSM_WAIT_CTRL_REG, 0xA24);
+#endif
+
+       /* CPU/DDR sync mode */
+       if (cpu_clk == ddr_clk) {
+#if (SOC_TYPE & QCA_AR933X_SOC)
+               qca_soc_reg_read_set(QCA_DDR_TAP_CTRL_3_REG, (1 << 8));
+#else
+               qca_soc_reg_read_set(QCA_DDR_CTRL_CFG_REG,
+                                                        QCA_DDR_CTRL_CFG_CPU_DDR_SYNC_MASK);
+#endif
+       } else {
+#if (SOC_TYPE & QCA_AR933X_SOC)
+               qca_soc_reg_read_clear(QCA_DDR_TAP_CTRL_3_REG, (1 << 8));
+#else
+               qca_soc_reg_read_clear(QCA_DDR_CTRL_CFG_REG,
+                                                          QCA_DDR_CTRL_CFG_CPU_DDR_SYNC_MASK);
+#endif
+       }
+
+       /* Check if clock is not too low for our "safe" timing values */
+       tmp_clk = ddr_clk;
+       if (mem_type == RAM_MEMORY_TYPE_DDR1) {
+               if (tmp_clk < DDR1_timing_clk_max)
+                       tmp_clk = DDR1_timing_clk_max;
+       } else {
+               if (tmp_clk < DDR2_timing_clk_max)
+                       tmp_clk = DDR2_timing_clk_max;
+       }
+
+       /* Enable DDR2 */
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+#if (SOC_TYPE & QCA_AR933X_SOC)
+               qca_dram_set_ddr2_cfg(cas_lat, tmp_clk);
+#else
+               qca_soc_reg_write(QCA_DDR_CTRL_CFG_REG,
+                                                 QCA_DDR_CTRL_CFG_PAD_DDR2_SEL_MASK);
+
+               qca_dram_set_ddr2_cfg(cas_lat, tmp_clk);
+#endif
+
+       }
+
+       /* Setup DDR timing related registers */
+       qca_dram_set_ddr_cfg(cas_lat, tmp_clk, mem_type);
+       qca_dram_set_ddr_cfg2(cas_lat, tmp_clk, mem_type, ddr_width);
+
+       /* Precharge all */
+       qca_dram_force_preall();
+
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+               /* Setup target EMR2 and EMR3 */
+               qca_dram_set_emr2(_ddr_sdram_emr2_val(0, 0, 0));
+               qca_dram_set_emr3(0);
+       }
+
+       /* Enable and reset DLL */
+       qca_dram_set_emr(_ddr_sdram_emr_val(0, 1, 0, 0, 0, 0));
+       qca_dram_set_mr(_ddr_sdram_mr_val(0, 0, 1, 0));
+
+       /* Precharge all, 2x auto refresh */
+       qca_dram_force_preall();
+
+       qca_dram_force_aref();
+       qca_dram_force_aref();
+
+       if (mem_type == RAM_MEMORY_TYPE_DDR2) {
+               /* Setup target MR */
+               wr_recovery = ((DDRx_tWR_ns * tmp_clk) + 1000) / 2000;
+               qca_dram_set_mr(_ddr_sdram_mr_val(0, cas_lat, 0, wr_recovery));
+
+               /* OCD calibration, target EMR (nDQS disable, weak strength) */
+               qca_dram_set_emr(
+                       _ddr_sdram_emr_val(0, 1, DDR_SDRAM_EMR_OCD_DEFAULT_VAL, 1, 0, 0));
+
+               qca_dram_set_emr(
+                       _ddr_sdram_emr_val(0, 1, DDR_SDRAM_EMR_OCD_EXIT_VAL, 1, 0, 0));
+       } else {
+               /* Setup target MR */
+               qca_dram_set_mr(_ddr_sdram_mr_val(0, cas_lat, 0, 0));
+       }
+
+       /* Enable DDR refresh and setup refresh period */
+       qca_dram_set_en_refresh();
+
+       /*
+        * At this point memory should be fully configured,
+        * so we can perform delay tap controller tune.
+        */
+       qca_ddr_tap_tune(ddr_width);
+}