summaryrefslogtreecommitdiff
path: root/board/samsung/smdk5250/dmc_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/samsung/smdk5250/dmc_init.c')
-rw-r--r--board/samsung/smdk5250/dmc_init.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/board/samsung/smdk5250/dmc_init.c b/board/samsung/smdk5250/dmc_init.c
new file mode 100644
index 00000000000..7881074652a
--- /dev/null
+++ b/board/samsung/smdk5250/dmc_init.c
@@ -0,0 +1,462 @@
+/*
+ * 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);
+}