Alexandru Gagniuc (mr.nuke.me@gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/4597
-gerrit
commit 974e79b64b05f9dffae8459455f751ae1d069bdd Author: Alexandru Gagniuc mr.nuke.me@gmail.com Date: Wed Dec 25 03:00:39 2013 -0500
cpu/allwinner/a10: Import raminit code from uboot
The memory initialization code is a work in progress for uboot, so we only import the bits needed to get RAM up and running. Any refactoring is cosmetic, and any functional refactoring should be done in separate patches, and preferably, in coordination with the sunxi team. Since it's not yet determined if we should initialize memory during the bootblock or romstage, we don't add raminit to the build just yet.
Change-Id: I2ec1821942c6970150a02fa3806a257da649e1c9 Signed-off-by: Alexandru Gagniuc mr.nuke.me@gmail.com --- src/cpu/allwinner/a10/dramc.h | 176 +++++++++++++++ src/cpu/allwinner/a10/raminit.c | 468 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 644 insertions(+)
diff --git a/src/cpu/allwinner/a10/dramc.h b/src/cpu/allwinner/a10/dramc.h new file mode 100644 index 0000000..7d44d83 --- /dev/null +++ b/src/cpu/allwinner/a10/dramc.h @@ -0,0 +1,176 @@ +/* + * Allwinner A10 platform dram register definition. + * + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c + * and earlier U-Boot Allwiner A10 SPL work + * + * Copyright (C) 2007-2012 Allwinner Technology Co., Ltd. + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * Copyright (C) 2013 Alexandru Gagniuc mr.nuke.me@gmail.com + * Subject to the GNU GPL v2, or (at your option) any later version. + */ + +#ifndef CPU_ALLWINNER_A10_DRAMC_H +#define CPU_ALLWINNER_A10_DRAMC_H + +#include <types.h> + +#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5) +#define DRAM_CCR_DQS_GATE (0x1 << 14) +#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17) +#define DRAM_CCR_ITM_OFF (0x1 << 28) +#define DRAM_CCR_DATA_TRAINING (0x1 << 30) +#define DRAM_CCR_INIT (0x1 << 31) + +#define DRAM_MEMORY_TYPE_DDR1 1 +#define DRAM_MEMORY_TYPE_DDR2 2 +#define DRAM_MEMORY_TYPE_DDR3 3 +#define DRAM_MEMORY_TYPE_LPDDR2 4 +#define DRAM_MEMORY_TYPE_LPDDR 5 +#define DRAM_DCR_TYPE (0x1 << 0) +#define DRAM_DCR_TYPE_DDR2 0x0 +#define DRAM_DCR_TYPE_DDR3 0x1 +#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1) +#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3) +#define DRAM_DCR_IO_WIDTH_8BIT 0x0 +#define DRAM_DCR_IO_WIDTH_16BIT 0x1 +#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3) +#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7) +#define DRAM_DCR_CHIP_DENSITY_256M 0x0 +#define DRAM_DCR_CHIP_DENSITY_512M 0x1 +#define DRAM_DCR_CHIP_DENSITY_1024M 0x2 +#define DRAM_DCR_CHIP_DENSITY_2048M 0x3 +#define DRAM_DCR_CHIP_DENSITY_4096M 0x4 +#define DRAM_DCR_CHIP_DENSITY_8192M 0x5 +#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6) +#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7) +#define DRAM_DCR_BUS_WIDTH_32BIT 0x3 +#define DRAM_DCR_BUS_WIDTH_16BIT 0x1 +#define DRAM_DCR_BUS_WIDTH_8BIT 0x0 +#define DRAM_DCR_NR_DLLCR_32BIT 5 +#define DRAM_DCR_NR_DLLCR_16BIT 3 +#define DRAM_DCR_NR_DLLCR_8BIT 2 +#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10) +#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3) +#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12) +#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13) +#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3) +#define DRAM_DCR_MODE_SEQ 0x0 +#define DRAM_DCR_MODE_INTERLEAVE 0x1 + +#define DRAM_CSR_FAILED (0x1 << 20) + +#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0) +#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3) +#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2) +#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3) +#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4) +#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3) +#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6) +#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3) +#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8) +#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7) +#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11) +#define DRAM_MCR_RESET (0x1 << 12) +#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13) +#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3) +#define DRAM_MCR_DCLK_OUT (0x1 << 16) + +#define DRAM_DLLCR_NRESET (0x1 << 30) +#define DRAM_DLLCR_DISABLE (0x1 << 31) + +#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20) +#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff) + +#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0) +#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3) + +#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0) +#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7) +#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4) +#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7) +#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9) +#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7) +#define DRAM_MR_POWER_DOWN (0x1 << 12) + +#define DRAM_CSEL_MAGIC 0x16237495 + +struct a1x_dramc { + u32 ccr; /* 0x00 controller configuration register */ + u32 dcr; /* 0x04 dram configuration register */ + u32 iocr; /* 0x08 i/o configuration register */ + u32 csr; /* 0x0c controller status register */ + u32 drr; /* 0x10 dram refresh register */ + u32 tpr0; /* 0x14 dram timing parameters register 0 */ + u32 tpr1; /* 0x18 dram timing parameters register 1 */ + u32 tpr2; /* 0x1c dram timing parameters register 2 */ + u32 gdllcr; /* 0x20 global dll control register */ + u8 res0[0x28]; + u32 rslr0; /* 0x4c rank system latency register */ + u32 rslr1; /* 0x50 rank system latency register */ + u8 res1[0x8]; + u32 rdgr0; /* 0x5c rank dqs gating register */ + u32 rdgr1; /* 0x60 rank dqs gating register */ + u8 res2[0x34]; + u32 odtcr; /* 0x98 odt configuration register */ + u32 dtr0; /* 0x9c data training register 0 */ + u32 dtr1; /* 0xa0 data training register 1 */ + u32 dtar; /* 0xa4 data training address register */ + u32 zqcr0; /* 0xa8 zq control register 0 */ + u32 zqcr1; /* 0xac zq control register 1 */ + u32 zqsr; /* 0xb0 zq status register */ + u32 idcr; /* 0xb4 initializaton delay configure reg */ + u8 res3[0x138]; + u32 mr; /* 0x1f0 mode register */ + u32 emr; /* 0x1f4 extended mode register */ + u32 emr2; /* 0x1f8 extended mode register */ + u32 emr3; /* 0x1fc extended mode register */ + u32 dllctr; /* 0x200 dll control register */ + u32 dllcr[5]; /* 0x204 dll control register 0(byte 0) */ + /* 0x208 dll control register 1(byte 1) */ + /* 0x20c dll control register 2(byte 2) */ + /* 0x210 dll control register 3(byte 3) */ + /* 0x214 dll control register 4(byte 4) */ + u32 dqtr0; /* 0x218 dq timing register */ + u32 dqtr1; /* 0x21c dq timing register */ + u32 dqtr2; /* 0x220 dq timing register */ + u32 dqtr3; /* 0x224 dq timing register */ + u32 dqstr; /* 0x228 dqs timing register */ + u32 dqsbtr; /* 0x22c dqsb timing register */ + u32 mcr; /* 0x230 mode configure register */ + u8 res[0x8]; + u32 ppwrsctl; /* 0x23c pad power save control */ + u32 apr; /* 0x240 arbiter period register */ + u32 pldtr; /* 0x244 priority level data threshold reg */ + u8 res5[0x8]; + u32 hpcr[32]; /* 0x250 host port configure register */ + u8 res6[0x10]; + u32 csel; /* 0x2e0 controller select register */ +}; + +struct dram_para { + u32 clock; + u32 type; + u32 rank_num; + u32 density; + u32 io_width; + u32 bus_width; + u32 cas; + u32 zq; + u32 odt_en; + u32 size; + u32 tpr0; + u32 tpr1; + u32 tpr2; + u32 tpr3; + u32 tpr4; + u32 tpr5; + u32 emr1; + u32 emr2; + u32 emr3; +}; + +unsigned long dramc_init(struct dram_para *para); + +#endif /* CPU_ALLWINNER_A10_DRAMC_H */ diff --git a/src/cpu/allwinner/a10/raminit.c b/src/cpu/allwinner/a10/raminit.c new file mode 100644 index 0000000..84fe4e8 --- /dev/null +++ b/src/cpu/allwinner/a10/raminit.c @@ -0,0 +1,468 @@ +/* + * Allwinner A10 DRAM controller initialization + * + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c + * and earlier U-Boot Allwiner A10 SPL work + * + * Copyright (C) 2012 Henrik Nordstrom henrik@henriknordstrom.net + * Copyright (C) 2013 Luke Kenneth Casson Leighton lkcl@lkcl.net + * Copyright (C) 2007-2012 Allwinner Technology Co., Ltd. + * Berg Xing bergxing@allwinnertech.com + * Tom Cubie tangliang@allwinnertech.com + * Copyright (C) 2013 Alexandru Gagniuc mr.nuke.me@gmail.com + * Subject to the GNU GPL v2, or (at your option) any later version. + */ + +#include "clock.h" +#include "dramc.h" +#include "memmap.h" +#include "timer.h" + +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> + +static struct a1x_dramc *const dram = (void *)A1X_DRAMC_BASE; + +static void mctl_ddr3_reset(void) +{ + if (a1x_get_cpu_chip_revision() != A1X_CHIP_REV_A) { + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(2); + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + } else { + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(2); + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + } +} + +static void mctl_set_drive(void) +{ + clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3), + DRAM_MCR_MODE_EN(0x3) | 0xffc); +} + +static void mctl_itm_disable(void) +{ + clrsetbits_le32(&dram->ccr, DRAM_CCR_INIT, DRAM_CCR_ITM_OFF); +} + +static void mctl_itm_enable(void) +{ + clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF); +} + +static void mctl_enable_dll0(u32 phase) +{ + clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, + ((phase >> 16) & 0x3f) << 6); + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET, DRAM_DLLCR_DISABLE); + udelay(2); + + clrbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE); + udelay(22); + + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET); + udelay(22); +} + +/* + * Note: This differs from pm/standby in that it checks the bus width + */ +static void mctl_enable_dllx(u32 phase) +{ + u32 i, n, bus_width; + + bus_width = read32(&dram->dcr); + + if ((bus_width & DRAM_DCR_BUS_WIDTH_MASK) == + DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) + n = DRAM_DCR_NR_DLLCR_32BIT; + else + n = DRAM_DCR_NR_DLLCR_16BIT; + + for (i = 1; i < n; i++) { + clrsetbits_le32(&dram->dllcr[i], 0x4 << 14, + (phase & 0xf) << 14); + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET, + DRAM_DLLCR_DISABLE); + phase >>= 4; + } + udelay(2); + + for (i = 1; i < n; i++) + clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | + DRAM_DLLCR_DISABLE); + udelay(22); + + for (i = 1; i < n; i++) + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, + DRAM_DLLCR_NRESET); + udelay(22); +} + +static u32 hpcr_value[32] = { + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x1031, 0x0301, 0x0301, 0x0731 +}; + +static void mctl_configure_hostport(void) +{ + u32 i; + + for (i = 0; i < 32; i++) + write32(hpcr_value[i], &dram->hpcr[i]); +} + +static void mctl_setup_dram_clock(u32 clk) +{ + /* setup DRAM PLL */ + a1x_pll5_configure(clk / 24, 2, 2, 1); + + /* FIXME: This bit is not documented for A10, and altering it doesn't + * seem to change anything. + * + * #define CCM_PLL5_CTRL_VCO_GAIN (0x1 << 19) + * reg_val = read32(&ccm->pll5_cfg); + * reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; // PLL VCO Gain off + * write32(reg_val, &ccm->pll5_cfg); + */ + udelay(5500); + + a1x_pll5_enable_dram_clock_output(); + + /* reset GPS */ + /* FIXME: These bits are also undocumented, and seem to have no effect + * on A10. + * + * #define CCM_GPS_CTRL_RESET (0x1 << 0) + * #define CCM_GPS_CTRL_GATE (0x1 << 1) + * clrbits_le32(&ccm->gps_clk_cfg, CCM_GPS_CTRL_RESET | CCM_GPS_CTRL_GATE); + */ + a1x_periph_clock_enable(A1X_CLKEN_GPS); + udelay(1); + a1x_periph_clock_disable(A1X_CLKEN_GPS); + + /* setup MBUS clock */ + /* FIXME: The MBUS does not seem to be present or do anything on A10. It + * is documented in the A13 user manual, but changing settings on A10 + * has no effect. + * + * #define CCM_MBUS_CTRL_M(n) (((n) & 0xf) << 0) + * #define CCM_MBUS_CTRL_M_MASK CCM_MBUS_CTRL_M(0xf) + * #define CCM_MBUS_CTRL_M_X(n) ((n) - 1) + * #define CCM_MBUS_CTRL_N(n) (((n) & 0xf) << 16) + * #define CCM_MBUS_CTRL_N_MASK CCM_MBUS_CTRL_N(0xf) + * #define CCM_MBUS_CTRL_N_X(n) (((n) >> 3) ? 3 : (((n) >> 2) ? 2 : (((n) >> 1) ? 1 : 0))) + * #define CCM_MBUS_CTRL_CLK_SRC(n) (((n) & 0x3) << 24) + * #define CCM_MBUS_CTRL_CLK_SRC_MASK CCM_MBUS_CTRL_CLK_SRC(0x3) + * #define CCM_MBUS_CTRL_CLK_SRC_HOSC 0x0 + * #define CCM_MBUS_CTRL_CLK_SRC_PLL6 0x1 + * #define CCM_MBUS_CTRL_CLK_SRC_PLL5 0x2 + * #define CCM_MBUS_CTRL_GATE (0x1 << 31) + * reg_val = CCM_MBUS_CTRL_GATE | + * CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | + * CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + * CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(2)); + * write32(reg_val, &ccm->mbus_clk_cfg); + */ + /* + * open DRAMC AHB & DLL register clock + * close it first + */ + a1x_periph_clock_disable(A1X_CLKEN_SDRAM); + + udelay(22); + + /* then open it */ + a1x_periph_clock_enable(A1X_CLKEN_SDRAM); + udelay(22); +} + +static int dramc_scan_readpipe(void) +{ + u32 reg32; + + /* data training trigger */ + setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); + + /* check whether data training process has completed */ + while (read32(&dram->ccr) & DRAM_CCR_DATA_TRAINING) ; + + /* check data training result */ + reg32 = read32(&dram->csr); + if (reg32 & DRAM_CSR_FAILED) + return -1; + + return 0; +} + +static int dramc_scan_dll_para(void) +{ + const u32 dqs_dly[7] = { 0x3, 0x2, 0x1, 0x0, 0xe, 0xd, 0xc }; + const u32 clk_dly[15] = { 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x00, 0x08, 0x10, + 0x18, 0x20, 0x28, 0x30, 0x38 + }; + u32 clk_dqs_count[15]; + u32 dqs_i, clk_i, cr_i; + u32 max_val, min_val; + u32 dqs_index, clk_index; + + /* Find DQS_DLY Pass Count for every CLK_DLY */ + for (clk_i = 0; clk_i < 15; clk_i++) { + clk_dqs_count[clk_i] = 0; + clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, + (clk_dly[clk_i] & 0x3f) << 6); + for (dqs_i = 0; dqs_i < 7; dqs_i++) { + for (cr_i = 1; cr_i < 5; cr_i++) { + clrsetbits_le32(&dram->dllcr[cr_i], + 0x4f << 14, + (dqs_dly[clk_i] & 0x4f) << 14); + } + udelay(2); + if (dramc_scan_readpipe() == 0) + clk_dqs_count[clk_i]++; + } + } + /* Test DQS_DLY Pass Count for every CLK_DLY from up to down */ + for (dqs_i = 15; dqs_i > 0; dqs_i--) { + max_val = 15; + min_val = 15; + for (clk_i = 0; clk_i < 15; clk_i++) { + if (clk_dqs_count[clk_i] == dqs_i) { + max_val = clk_i; + if (min_val == 15) + min_val = clk_i; + } + } + if (max_val < 15) + break; + } + + /* Check if Find a CLK_DLY failed */ + if (!dqs_i) + goto fail; + + /* Find the middle index of CLK_DLY */ + clk_index = (max_val + min_val) >> 1; + if ((max_val == (15 - 1)) && (min_val > 0)) + /* if CLK_DLY[MCTL_CLK_DLY_COUNT] is very good, then the middle + * value can be more close to the max_val + */ + clk_index = (15 + clk_index) >> 1; + else if ((max_val < (15 - 1)) && (min_val == 0)) + /* if CLK_DLY[0] is very good, then the middle value can be more + * close to the min_val + */ + clk_index >>= 1; + if (clk_dqs_count[clk_index] < dqs_i) + clk_index = min_val; + + /* Find the middle index of DQS_DLY for the CLK_DLY got above, and Scan + * read pipe again + */ + clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, + (clk_dly[clk_index] & 0x3f) << 6); + max_val = 7; + min_val = 7; + for (dqs_i = 0; dqs_i < 7; dqs_i++) { + clk_dqs_count[dqs_i] = 0; + for (cr_i = 1; cr_i < 5; cr_i++) { + clrsetbits_le32(&dram->dllcr[cr_i], + 0x4f << 14, + (dqs_dly[dqs_i] & 0x4f) << 14); + } + udelay(2); + if (dramc_scan_readpipe() == 0) { + clk_dqs_count[dqs_i] = 1; + max_val = dqs_i; + if (min_val == 7) + min_val = dqs_i; + } + } + + if (max_val < 7) { + dqs_index = (max_val + min_val) >> 1; + if ((max_val == (7 - 1)) && (min_val > 0)) + dqs_index = (7 + dqs_index) >> 1; + else if ((max_val < (7 - 1)) && (min_val == 0)) + dqs_index >>= 1; + if (!clk_dqs_count[dqs_index]) + dqs_index = min_val; + for (cr_i = 1; cr_i < 5; cr_i++) { + clrsetbits_le32(&dram->dllcr[cr_i], + 0x4f << 14, + (dqs_dly[dqs_index] & 0x4f) << 14); + } + udelay(2); + return dramc_scan_readpipe(); + } + + fail: + clrbits_le32(&dram->dllcr[0], 0x3f << 6); + for (cr_i = 1; cr_i < 5; cr_i++) + clrbits_le32(&dram->dllcr[cr_i], 0x4f << 14); + udelay(2); + + return dramc_scan_readpipe(); +} + +static void dramc_set_autorefresh_cycle(u32 clk) +{ + u32 reg32; + u32 tmp_val; + u32 reg_dcr; + + if (clk < 600) { + reg_dcr = read32(&dram->dcr); + if ((reg_dcr & DRAM_DCR_CHIP_DENSITY_MASK) <= + DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_1024M)) + reg32 = (131 * clk) >> 10; + else + reg32 = (336 * clk) >> 10; + + tmp_val = (7987 * clk) >> 10; + tmp_val = tmp_val * 9 - 200; + reg32 |= tmp_val << 8; + reg32 |= 0x8 << 24; + write32(reg32, &dram->drr); + } else { + write32(0x0, &dram->drr); + } +} + +unsigned long dramc_init(struct dram_para *para) +{ + u32 reg32; + int ret_val; + + /* check input dram parameter structure */ + if (!para) + return 0; + + /* setup DRAM relative clock */ + mctl_setup_dram_clock(para->clock); + + /* reset external DRAM */ + mctl_ddr3_reset(); + + mctl_set_drive(); + + /* dram clock off */ + a1x_gate_dram_clock_output(); + + /* select dram controller 1 */ + write32(DRAM_CSEL_MAGIC, &dram->csel); + + mctl_itm_disable(); + mctl_enable_dll0(para->tpr3); + + /* configure external DRAM */ + reg32 = 0x0; + if (para->type == DRAM_MEMORY_TYPE_DDR3) + reg32 |= DRAM_DCR_TYPE_DDR3; + reg32 |= DRAM_DCR_IO_WIDTH(para->io_width >> 3); + + if (para->density == 256) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_256M); + else if (para->density == 512) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_512M); + else if (para->density == 1024) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_1024M); + else if (para->density == 2048) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_2048M); + else if (para->density == 4096) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_4096M); + else if (para->density == 8192) + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_8192M); + else + reg32 |= DRAM_DCR_CHIP_DENSITY(DRAM_DCR_CHIP_DENSITY_256M); + + reg32 |= DRAM_DCR_BUS_WIDTH((para->bus_width >> 3) - 1); + reg32 |= DRAM_DCR_RANK_SEL(para->rank_num - 1); + reg32 |= DRAM_DCR_CMD_RANK_ALL; + reg32 |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE); + write32(reg32, &dram->dcr); + + /* dram clock on */ + a1x_ungate_dram_clock_output(); + + udelay(1); + + while (read32(&dram->ccr) & DRAM_CCR_INIT) ; + + mctl_enable_dllx(para->tpr3); + + /* set odt impendance divide ratio */ + reg32 = ((para->zq) >> 8) & 0xfffff; + reg32 |= ((para->zq) & 0xff) << 20; + reg32 |= (para->zq) & 0xf0000000; + write32(reg32, &dram->zqcr0); + + /* set I/O configure register */ + reg32 = 0x00cc0000; + reg32 |= (para->odt_en) & 0x3; + reg32 |= ((para->odt_en) & 0x3) << 30; + write32(reg32, &dram->iocr); + + /* set refresh period */ + dramc_set_autorefresh_cycle(para->clock); + + /* set timing parameters */ + write32(para->tpr0, &dram->tpr0); + write32(para->tpr1, &dram->tpr1); + write32(para->tpr2, &dram->tpr2); + + if (para->type == DRAM_MEMORY_TYPE_DDR3) { + reg32 = DRAM_MR_BURST_LENGTH(0x0); + reg32 |= DRAM_MR_CAS_LAT(para->cas - 4); + reg32 |= DRAM_MR_WRITE_RECOVERY(0x5); + } else if (para->type == DRAM_MEMORY_TYPE_DDR2) { + reg32 = DRAM_MR_BURST_LENGTH(0x2); + reg32 |= DRAM_MR_CAS_LAT(para->cas); + reg32 |= DRAM_MR_WRITE_RECOVERY(0x5); + } + write32(reg32, &dram->mr); + + write32(para->emr1, &dram->emr); + write32(para->emr2, &dram->emr2); + write32(para->emr3, &dram->emr3); + + /* set DQS window mode */ + clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); + + /* reset external DRAM */ + setbits_le32(&dram->ccr, DRAM_CCR_INIT); + while (read32(&dram->ccr) & DRAM_CCR_INIT) ; + + /* scan read pipe value */ + mctl_itm_enable(); + if (para->tpr3 & (0x1 << 31)) { + ret_val = dramc_scan_dll_para(); + if (ret_val == 0) + para->tpr3 = + (((read32(&dram->dllcr[0]) >> 6) & 0x3f) << 16) | + (((read32(&dram->dllcr[1]) >> 14) & 0xf) << 0) | + (((read32(&dram->dllcr[2]) >> 14) & 0xf) << 4) | + (((read32(&dram->dllcr[3]) >> 14) & 0xf) << 8) | + (((read32(&dram->dllcr[4]) >> 14) & 0xf) << 12); + } else { + ret_val = dramc_scan_readpipe(); + } + + if (ret_val < 0) + return 0; + + /* configure all host port */ + mctl_configure_hostport(); + + return para->size; +}