mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-20 17:08:52 +09:00
463 lines
12 KiB
C
463 lines
12 KiB
C
![]() |
/*
|
||
|
* 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);
|
||
|
}
|