diff options
Diffstat (limited to 'board/samsung')
-rw-r--r-- | board/samsung/smdk5250/Makefile | 2 | ||||
-rw-r--r-- | board/samsung/smdk5250/dmc_common.c | 199 | ||||
-rw-r--r-- | board/samsung/smdk5250/dmc_init.c | 462 | ||||
-rw-r--r-- | board/samsung/smdk5250/dmc_init_ddr3.c | 228 | ||||
-rw-r--r-- | board/samsung/smdk5250/setup.h | 59 |
5 files changed, 486 insertions, 464 deletions
diff --git a/board/samsung/smdk5250/Makefile b/board/samsung/smdk5250/Makefile index 3675fad..1474fa8 100644 --- a/board/samsung/smdk5250/Makefile +++ b/board/samsung/smdk5250/Makefile @@ -27,7 +27,7 @@ LIB = $(obj)lib$(BOARD).o SOBJS := lowlevel_init.o COBJS := clock_init.o -COBJS += dmc_init.o +COBJS += dmc_common.o dmc_init_ddr3.o COBJS += tzpc_init.o COBJS += smdk5250_spl.o diff --git a/board/samsung/smdk5250/dmc_common.c b/board/samsung/smdk5250/dmc_common.c new file mode 100644 index 0000000..109602a --- /dev/null +++ b/board/samsung/smdk5250/dmc_common.c @@ -0,0 +1,199 @@ +/* + * Mem setup common file for different types of DDR present on SMDK5250 boards. + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/arch/spl.h> + +#include "clock_init.h" +#include "setup.h" + +#define ZQ_INIT_TIMEOUT 10000 + +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl) +{ + unsigned long val = 0; + int i; + + /* + * ZQ Calibration: + * Select Driver Strength, + * long calibration for manual calibration + */ + val = PHY_CON16_RESET_VAL; + val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT; + val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT; + val |= ZQ_CLK_DIV_EN; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* Disable termination */ + if (mem->zq_mode_noterm) + val |= PHY_CON16_ZQ_MODE_NOTERM_MASK; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Enable */ + val |= ZQ_MANUAL_STR; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Disable */ + val &= ~ZQ_MANUAL_STR; + + /* + * Since we are manaully calibrating the ZQ values, + * we are looping for the ZQ_init to complete. + */ + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + sdelay(100); + i--; + } + if (!i) + return -1; + writel(val, &phy0_ctrl->phy_con16); + + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + sdelay(100); + i--; + } + if (!i) + return -1; + writel(val, &phy1_ctrl->phy_con16); + + return 0; +} + +void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode) +{ + unsigned long val; + + if (mode == DDR_MODE_DDR3) { + val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE; + writel(val, &dmc->phycontrol0); + } + + /* Update DLL Information: Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val |= FP_RSYNC; + writel(val, &dmc->phycontrol0); + + /* Reset Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val &= ~FP_RSYNC; + writel(val, &dmc->phycontrol0); +} + +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_to_configure; chip++) { + int i; + + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* Sending NOP command */ + writel(DIRECT_CMD_NOP | mask, &dmc->directcmd); + + /* + * TODO(alim.akhtar@samsung.com): Do we need these + * delays? This one and the next were not there for + * DDR3. + */ + sdelay(0x10000); + + /* Sending EMRS/MRS commands */ + for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) { + writel(mem->direct_cmd_msr[i] | mask, + &dmc->directcmd); + sdelay(0x10000); + } + + if (mem->send_zq_init) { + /* Sending ZQINIT command */ + writel(DIRECT_CMD_ZQINIT | mask, + &dmc->directcmd); + + sdelay(10000); + } + } + } +} + +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_per_channel; chip++) { + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* PALL (all banks precharge) CMD */ + writel(DIRECT_CMD_PALL | mask, &dmc->directcmd); + sdelay(0x10000); + } + } +} + +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + writel(mem->memconfig, &dmc->memconfig0); + writel(mem->memconfig, &dmc->memconfig1); + writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0); + writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1); +} + +void mem_ctrl_init() +{ + struct spl_machine_param *param = spl_get_machine_params(); + struct mem_timings *mem; + int ret; + + mem = clock_get_mem_timings(); + + /* If there are any other memory variant, add their init call below */ + if (param->mem_type == DDR_MODE_DDR3) { + ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size); + if (ret) { + /* will hang if failed to init memory control */ + while (1) + ; + } + } else { + /* will hang if unknow memory type */ + while (1) + ; + } +} diff --git a/board/samsung/smdk5250/dmc_init.c b/board/samsung/smdk5250/dmc_init.c deleted file mode 100644 index 7881074..0000000 --- a/board/samsung/smdk5250/dmc_init.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Memory setup for SMDK5250 board based on EXYNOS5 - * - * Copyright (C) 2012 Samsung Electronics - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include <config.h> -#include <asm/io.h> -#include <asm/arch/dmc.h> -#include <asm/arch/clock.h> -#include <asm/arch/cpu.h> -#include "setup.h" - -/* APLL : 1GHz */ -/* MCLK_CDREX: MCLK_CDREX_533*/ -/* LPDDR support: LPDDR2 */ -static void reset_phy_ctrl(void); -static void config_zq(struct exynos5_phy_control *, - struct exynos5_phy_control *); -static void update_reset_dll(struct exynos5_dmc *); -static void config_cdrex(void); -static void config_mrs(struct exynos5_dmc *); -static void sec_sdram_phy_init(struct exynos5_dmc *); -static void config_prech(struct exynos5_dmc *); -static void config_rdlvl(struct exynos5_dmc *, - struct exynos5_phy_control *, - struct exynos5_phy_control *); -static void config_memory(struct exynos5_dmc *); - -static void config_offsets(unsigned int, - struct exynos5_phy_control *, - struct exynos5_phy_control *); - -static void reset_phy_ctrl(void) -{ - struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; - - writel(PHY_RESET_VAL, &clk->lpddr3phy_ctrl); - sdelay(0x10000); -} - -static void config_zq(struct exynos5_phy_control *phy0_ctrl, - struct exynos5_phy_control *phy1_ctrl) -{ - unsigned long val = 0; - /* - * ZQ Calibration: - * Select Driver Strength, - * long calibration for manual calibration - */ - val = PHY_CON16_RESET_VAL; - SET_ZQ_MODE_DDS_VAL(val); - SET_ZQ_MODE_TERM_VAL(val); - val |= ZQ_CLK_DIV_EN; - writel(val, &phy0_ctrl->phy_con16); - writel(val, &phy1_ctrl->phy_con16); - - /* Disable termination */ - val |= ZQ_MODE_NOTERM; - writel(val, &phy0_ctrl->phy_con16); - writel(val, &phy1_ctrl->phy_con16); - - /* ZQ_MANUAL_START: Enable */ - val |= ZQ_MANUAL_STR; - writel(val, &phy0_ctrl->phy_con16); - writel(val, &phy1_ctrl->phy_con16); - sdelay(0x10000); - - /* ZQ_MANUAL_START: Disable */ - val &= ~ZQ_MANUAL_STR; - writel(val, &phy0_ctrl->phy_con16); - writel(val, &phy1_ctrl->phy_con16); -} - -static void update_reset_dll(struct exynos5_dmc *dmc) -{ - unsigned long val; - /* - * Update DLL Information: - * Force DLL Resyncronization - */ - val = readl(&dmc->phycontrol0); - val |= FP_RSYNC; - writel(val, &dmc->phycontrol0); - - /* Reset Force DLL Resyncronization */ - val = readl(&dmc->phycontrol0); - val &= ~FP_RSYNC; - writel(val, &dmc->phycontrol0); -} - -static void config_mrs(struct exynos5_dmc *dmc) -{ - unsigned long channel, chip, mask = 0, val; - - for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) { - SET_CMD_CHANNEL(mask, channel); - for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) { - /* - * NOP CMD: - * Assert and hold CKE to logic high level - */ - SET_CMD_CHIP(mask, chip); - val = DIRECT_CMD_NOP | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - - /* EMRS, MRS Cmds(Mode Reg Settings) Using Direct Cmd */ - val = DIRECT_CMD_MRS1 | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - - val = DIRECT_CMD_MRS2 | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - - /* MCLK_CDREX_533 */ - val = DIRECT_CMD_MRS3 | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - - val = DIRECT_CMD_MRS4 | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - } - } -} - -static void config_prech(struct exynos5_dmc *dmc) -{ - unsigned long channel, chip, mask = 0, val; - - for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) { - SET_CMD_CHANNEL(mask, channel); - for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) { - SET_CMD_CHIP(mask, chip); - /* PALL (all banks precharge) CMD */ - val = DIRECT_CMD_PALL | mask; - writel(val, &dmc->directcmd); - sdelay(0x10000); - } - } -} - -static void sec_sdram_phy_init(struct exynos5_dmc *dmc) -{ - unsigned long val; - val = readl(&dmc->concontrol); - val |= DFI_INIT_START; - writel(val, &dmc->concontrol); - sdelay(0x10000); - - val = readl(&dmc->concontrol); - val &= ~DFI_INIT_START; - writel(val, &dmc->concontrol); -} - -static void config_offsets(unsigned int state, - struct exynos5_phy_control *phy0_ctrl, - struct exynos5_phy_control *phy1_ctrl) -{ - unsigned long val; - /* Set Offsets to read DQS */ - val = (state == SET) ? SET_DQS_OFFSET_VAL : RESET_DQS_OFFSET_VAL; - writel(val, &phy0_ctrl->phy_con4); - writel(val, &phy1_ctrl->phy_con4); - - /* Set Offsets to read DQ */ - val = (state == SET) ? SET_DQ_OFFSET_VAL : RESET_DQ_OFFSET_VAL; - writel(val, &phy0_ctrl->phy_con6); - writel(val, &phy1_ctrl->phy_con6); - - /* Debug Offset */ - val = (state == SET) ? SET_DEBUG_OFFSET_VAL : RESET_DEBUG_OFFSET_VAL; - writel(val, &phy0_ctrl->phy_con10); - writel(val, &phy1_ctrl->phy_con10); -} - -static void config_cdrex(void) -{ - struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; - writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex); - writel(CLK_SRC_CDREX_VAL, &clk->src_cdrex); - sdelay(0x30000); -} - -static void config_ctrl_dll_on(unsigned int state, - unsigned int ctrl_force_val, - struct exynos5_phy_control *phy0_ctrl, - struct exynos5_phy_control *phy1_ctrl) -{ - unsigned long val; - val = readl(&phy0_ctrl->phy_con12); - CONFIG_CTRL_DLL_ON(val, state); - SET_CTRL_FORCE_VAL(val, ctrl_force_val); - writel(val, &phy0_ctrl->phy_con12); - - val = readl(&phy1_ctrl->phy_con12); - CONFIG_CTRL_DLL_ON(val, state); - SET_CTRL_FORCE_VAL(val, ctrl_force_val); - writel(val, &phy1_ctrl->phy_con12); -} - -static void config_ctrl_start(unsigned int state, - struct exynos5_phy_control *phy0_ctrl, - struct exynos5_phy_control *phy1_ctrl) -{ - unsigned long val; - val = readl(&phy0_ctrl->phy_con12); - CONFIG_CTRL_START(val, state); - writel(val, &phy0_ctrl->phy_con12); - - val = readl(&phy1_ctrl->phy_con12); - CONFIG_CTRL_START(val, state); - writel(val, &phy1_ctrl->phy_con12); -} - -#if defined(CONFIG_RD_LVL) -static void config_rdlvl(struct exynos5_dmc *dmc, - struct exynos5_phy_control *phy0_ctrl, - struct exynos5_phy_control *phy1_ctrl) -{ - unsigned long val; - - /* Disable CTRL_DLL_ON and set ctrl_force */ - config_ctrl_dll_on(RESET, 0x2D, phy0_ctrl, phy1_ctrl); - - /* - * Set ctrl_gateadj, ctrl_readadj - * ctrl_gateduradj, rdlvl_pass_adj - * rdlvl_rddataPadj - */ - val = SET_RDLVL_RDDATAPADJ; - writel(val, &phy0_ctrl->phy_con1); - writel(val, &phy1_ctrl->phy_con1); - - /* LPDDR2 Address */ - writel(LPDDR2_ADDR, &phy0_ctrl->phy_con22); - writel(LPDDR2_ADDR, &phy1_ctrl->phy_con22); - - /* Enable Byte Read Leveling set ctrl_ddr_mode */ - val = readl(&phy0_ctrl->phy_con0); - val |= BYTE_RDLVL_EN; - writel(val, &phy0_ctrl->phy_con0); - val = readl(&phy1_ctrl->phy_con0); - val |= BYTE_RDLVL_EN; - writel(val, &phy1_ctrl->phy_con0); - - /* rdlvl_en: Use levelling offset instead ctrl_shiftc */ - val = PHY_CON2_RESET_VAL | RDLVL_EN; - writel(val, &phy0_ctrl->phy_con2); - writel(val, &phy1_ctrl->phy_con2); - sdelay(0x10000); - - /* Enable Data Eye Training */ - val = readl(&dmc->rdlvl_config); - val |= CTRL_RDLVL_DATA_EN; - writel(val, &dmc->rdlvl_config); - sdelay(0x10000); - - /* Disable Data Eye Training */ - val = readl(&dmc->rdlvl_config); - val &= ~CTRL_RDLVL_DATA_EN; - writel(val, &dmc->rdlvl_config); - - /* RdDeSkew_clear: Clear */ - val = readl(&phy0_ctrl->phy_con2); - val |= RDDSKEW_CLEAR; - writel(val, &phy0_ctrl->phy_con2); - val = readl(&phy1_ctrl->phy_con2); - val |= RDDSKEW_CLEAR; - writel(val, &phy1_ctrl->phy_con2); - - /* Enable CTRL_DLL_ON */ - config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl); - - update_reset_dll(dmc); - sdelay(0x10000); - - /* ctrl_atgte: ctrl_gate_p*, ctrl_read_p* generated by PHY */ - val = readl(&phy0_ctrl->phy_con0); - val &= ~CTRL_ATGATE; - writel(val, &phy0_ctrl->phy_con0); - val = readl(&phy1_ctrl->phy_con0); - val &= ~CTRL_ATGATE; - writel(val, &phy1_ctrl->phy_con0); -} -#endif - -static void config_memory(struct exynos5_dmc *dmc) -{ - /* - * Memory Configuration Chip 0 - * Address Mapping: Interleaved - * Number of Column address Bits: 10 bits - * Number of Rows Address Bits: 14 - * Number of Banks: 8 - */ - writel(DMC_MEMCONFIG0_VAL, &dmc->memconfig0); - - /* - * Memory Configuration Chip 1 - * Address Mapping: Interleaved - * Number of Column address Bits: 10 bits - * Number of Rows Address Bits: 14 - * Number of Banks: 8 - */ - writel(DMC_MEMCONFIG1_VAL, &dmc->memconfig1); - - /* - * Chip0: AXI - * AXI Base Address: 0x40000000 - * AXI Base Address Mask: 0x780 - */ - writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0); - - /* - * Chip1: AXI - * AXI Base Address: 0x80000000 - * AXI Base Address Mask: 0x780 - */ - writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1); -} - -void mem_ctrl_init() -{ - struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl; - struct exynos5_dmc *dmc; - unsigned long val; - - phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE; - phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE; - dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE; - - /* Reset PHY Controllor: PHY_RESET[0] */ - reset_phy_ctrl(); - - /*set Read Latancy and Burst Length for PHY0 and PHY1 */ - writel(PHY_CON42_VAL, &phy0_ctrl->phy_con42); - writel(PHY_CON42_VAL, &phy1_ctrl->phy_con42); - - /* ZQ Cofiguration */ - config_zq(phy0_ctrl, phy1_ctrl); - - /* Operation Mode : LPDDR2 */ - val = PHY_CON0_RESET_VAL; - SET_CTRL_DDR_MODE(val, DDR_MODE_LPDDR2); - writel(val, &phy0_ctrl->phy_con0); - writel(val, &phy1_ctrl->phy_con0); - - /* DQS, DQ: Signal, for LPDDR2: Always Set */ - val = CTRL_PULLD_DQ | CTRL_PULLD_DQS; - writel(val, &phy0_ctrl->phy_con14); - writel(val, &phy1_ctrl->phy_con14); - - /* Init SEC SDRAM PHY */ - sec_sdram_phy_init(dmc); - sdelay(0x10000); - - update_reset_dll(dmc); - - /* - * Dynamic Clock: Always Running - * Memory Burst length: 4 - * Number of chips: 2 - * Memory Bus width: 32 bit - * Memory Type: LPDDR2-S4 - * Additional Latancy for PLL: 1 Cycle - */ - writel(DMC_MEMCONTROL_VAL, &dmc->memcontrol); - - config_memory(dmc); - - /* Precharge Configuration */ - writel(DMC_PRECHCONFIG_VAL, &dmc->prechconfig); - - /* Power Down mode Configuration */ - writel(DMC_PWRDNCONFIG_VAL, &dmc->pwrdnconfig); - - /* Periodic Refrese Interval */ - writel(DMC_TIMINGREF_VAL, &dmc->timingref); - - /* - * TimingRow, TimingData, TimingPower Setting: - * Values as per Memory AC Parameters - */ - writel(DMC_TIMINGROW_VAL, &dmc->timingrow); - - writel(DMC_TIMINGDATA_VAL, &dmc->timingdata); - - writel(DMC_TIMINGPOWER_VAL, &dmc->timingpower); - - /* Memory Channel Inteleaving Size: 128 Bytes */ - writel(CONFIG_IV_SIZE, &dmc->ivcontrol); - - /* Set DQS, DQ and DEBUG offsets */ - config_offsets(SET, phy0_ctrl, phy1_ctrl); - - /* Disable CTRL_DLL_ON and set ctrl_force */ - config_ctrl_dll_on(RESET, 0x7F, phy0_ctrl, phy1_ctrl); - sdelay(0x10000); - - update_reset_dll(dmc); - - /* Config MRS(Mode Register Settingg) */ - config_mrs(dmc); - - config_cdrex(); - - /* Reset DQS DQ and DEBUG offsets */ - config_offsets(RESET, phy0_ctrl, phy1_ctrl); - - /* Enable CTRL_DLL_ON */ - config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl); - - /* Stop DLL Locking */ - config_ctrl_start(RESET, phy0_ctrl, phy1_ctrl); - sdelay(0x10000); - - /* Start DLL Locking */ - config_ctrl_start(SET, phy0_ctrl, phy1_ctrl); - sdelay(0x10000); - - update_reset_dll(dmc); - -#if defined(CONFIG_RD_LVL) - config_rdlvl(dmc, phy0_ctrl, phy1_ctrl); -#endif - config_prech(dmc); - - /* - * Dynamic Clock: Stops During Idle Period - * Dynamic Power Down: Enable - * Dynamic Self refresh: Enable - */ - val = readl(&dmc->memcontrol); - val |= CLK_STOP_EN | DPWRDN_EN | DSREF_EN; - writel(val, &dmc->memcontrol); - - /* Start Auto refresh */ - val = readl(&dmc->concontrol); - val |= AREF_EN; - writel(val, &dmc->concontrol); -} diff --git a/board/samsung/smdk5250/dmc_init_ddr3.c b/board/samsung/smdk5250/dmc_init_ddr3.c new file mode 100644 index 0000000..e050790 --- /dev/null +++ b/board/samsung/smdk5250/dmc_init_ddr3.c @@ -0,0 +1,228 @@ +/* + * DDR3 mem setup file for SMDK5250 board based on EXYNOS5 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include <asm/arch/dmc.h> +#include "setup.h" +#include "clock_init.h" + +#define RDLVL_COMPLETE_TIMEOUT 10000 + +static void reset_phy_ctrl(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + writel(DDR3PHY_CTRL_PHY_RESET_OFF, &clk->lpddr3phy_ctrl); + writel(DDR3PHY_CTRL_PHY_RESET, &clk->lpddr3phy_ctrl); +} + +int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size) +{ + unsigned int val; + struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl; + struct exynos5_dmc *dmc; + int i; + + phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE; + phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE; + dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE; + + reset_phy_ctrl(); + + /* Set Impedance Output Driver */ + val = (mem->impedance << CA_CK_DRVR_DS_OFFSET) | + (mem->impedance << CA_CKE_DRVR_DS_OFFSET) | + (mem->impedance << CA_CS_DRVR_DS_OFFSET) | + (mem->impedance << CA_ADR_DRVR_DS_OFFSET); + writel(val, &phy0_ctrl->phy_con39); + writel(val, &phy1_ctrl->phy_con39); + + /* Set Read Latency and Burst Length for PHY0 and PHY1 */ + val = (mem->ctrl_bstlen << PHY_CON42_CTRL_BSTLEN_SHIFT) | + (mem->ctrl_rdlat << PHY_CON42_CTRL_RDLAT_SHIFT); + writel(val, &phy0_ctrl->phy_con42); + writel(val, &phy1_ctrl->phy_con42); + + /* ZQ Calibration */ + if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl)) + return SETUP_ERR_ZQ_CALIBRATION_FAILURE; + + /* DQ Signal */ + writel(mem->phy0_pulld_dqs, &phy0_ctrl->phy_con14); + writel(mem->phy1_pulld_dqs, &phy1_ctrl->phy_con14); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT) + | (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT), + &dmc->concontrol); + + update_reset_dll(dmc, DDR_MODE_DDR3); + + /* DQS Signal */ + writel(mem->phy0_dqs, &phy0_ctrl->phy_con4); + writel(mem->phy1_dqs, &phy1_ctrl->phy_con4); + + writel(mem->phy0_dq, &phy0_ctrl->phy_con6); + writel(mem->phy1_dq, &phy1_ctrl->phy_con6); + + writel(mem->phy0_tFS, &phy0_ctrl->phy_con10); + writel(mem->phy1_tFS, &phy1_ctrl->phy_con10); + + val = (mem->ctrl_start_point << PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + /* Start DLL locking */ + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &phy0_ctrl->phy_con12); + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &phy1_ctrl->phy_con12); + + update_reset_dll(dmc, DDR_MODE_DDR3); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT), + &dmc->concontrol); + + /* Memory Channel Inteleaving Size */ + writel(mem->iv_size, &dmc->ivcontrol); + + writel(mem->memconfig, &dmc->memconfig0); + writel(mem->memconfig, &dmc->memconfig1); + writel(mem->membaseconfig0, &dmc->membaseconfig0); + writel(mem->membaseconfig1, &dmc->membaseconfig1); + + /* Precharge Configuration */ + writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT, + &dmc->prechconfig); + + /* Power Down mode Configuration */ + writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT | + mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT, + &dmc->pwrdnconfig); + + /* TimingRow, TimingData, TimingPower and Timingaref + * values as per Memory AC parameters + */ + writel(mem->timing_ref, &dmc->timingref); + writel(mem->timing_row, &dmc->timingrow); + writel(mem->timing_data, &dmc->timingdata); + writel(mem->timing_power, &dmc->timingpower); + + /* Send PALL command */ + dmc_config_prech(mem, dmc); + + /* Send NOP, MRS and ZQINIT commands */ + dmc_config_mrs(mem, dmc); + + if (mem->gate_leveling_enable) { + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + writel(val, &phy0_ctrl->phy_con2); + writel(val, &phy1_ctrl->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = (mem->ctrl_start_point << + PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) | + (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + val |= RDLVL_GATE_EN; + writel(val, &phy0_ctrl->phy_con2); + writel(val, &phy1_ctrl->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + val |= CTRL_SHGATE; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = PHY_CON1_RESET_VAL; + val &= ~(CTRL_GATEDURADJ_MASK); + writel(val, &phy0_ctrl->phy_con1); + writel(val, &phy1_ctrl->phy_con1); + + writel(CTRL_RDLVL_GATE_ENABLE, &dmc->rdlvl_config); + i = RDLVL_COMPLETE_TIMEOUT; + while ((readl(&dmc->phystatus) & + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) != + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) { + /* + * TODO(waihong): Comment on how long this take to + * timeout + */ + sdelay(100); + i--; + } + if (!i) + return SETUP_ERR_RDLV_COMPLETE_TIMEOUT; + writel(CTRL_RDLVL_GATE_DISABLE, &dmc->rdlvl_config); + + writel(0, &phy0_ctrl->phy_con14); + writel(0, &phy1_ctrl->phy_con14); + + val = (mem->ctrl_start_point << + PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) | + (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) | + (mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + update_reset_dll(dmc, DDR_MODE_DDR3); + } + + /* Send PALL command */ + dmc_config_prech(mem, dmc); + + writel(mem->memcontrol, &dmc->memcontrol); + + /* Set DMC Concontrol and enable auto-refresh counter */ + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT) + | (mem->aref_en << CONCONTROL_AREF_EN_SHIFT), &dmc->concontrol); + return 0; +} diff --git a/board/samsung/smdk5250/setup.h b/board/samsung/smdk5250/setup.h index 4bdd095..a159601 100644 --- a/board/samsung/smdk5250/setup.h +++ b/board/samsung/smdk5250/setup.h @@ -529,9 +529,66 @@ enum { SETUP_ERR_ZQ_CALIBRATION_FAILURE = -2, }; +/* + * Memory variant specific initialization code + * + * @param mem Memory timings for this memory type. + * @param mem_iv_size Memory interleaving size is a configurable parameter + * which the DMC uses to decide how to split a memory + * chunk into smaller chunks to support concurrent + * accesses; may vary across boards. + * @return 0 if ok, SETUP_ERR_... if there is a problem + */ +int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size); + +/* + * Configure ZQ I/O interface + * + * @param mem Memory timings for this memory type. + * @param phy0_ctrl Pointer to struct containing PHY0 control reg + * @param phy1_ctrl Pointer to struct containing PHY1 control reg + * @return 0 if ok, -1 on error + */ +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl); + +/* + * Send NOP and MRS/EMRS Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Send PALL Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Configure the memconfig and membaseconfig registers + * + * @param mem Memory timings for this memory type. + * @param exynos5_dmc Pointer to struct of DMC registers + */ +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Reset the DLL. This function is common between DDR3 and LPDDR2. + * However, the reset value is different. So we are passing a flag + * ddr_mode to distinguish between LPDDR2 and DDR3. + * + * @param exynos5_dmc Pointer to struct of DMC registers + * @param ddr_mode Type of DDR memory + */ +void update_reset_dll(struct exynos5_dmc *, enum ddr_mode); + void sdelay(unsigned long); void mem_ctrl_init(void); void system_clock_init(void); void tzpc_init(void); - #endif |