2012-07-03 20:02:56 +00:00
|
|
|
/*
|
|
|
|
* Mem setup common file for different types of DDR present on SMDK5250 boards.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Samsung Electronics
|
|
|
|
*
|
2013-07-08 07:37:19 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
2012-07-03 20:02:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <asm/arch/spl.h>
|
|
|
|
|
|
|
|
#include "clock_init.h"
|
2013-07-04 06:59:17 +00:00
|
|
|
#include "common_setup.h"
|
|
|
|
#include "exynos5_setup.h"
|
2012-07-03 20:02:56 +00:00
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
2013-07-04 06:59:17 +00:00
|
|
|
void mem_ctrl_init(int reset)
|
2012-07-03 20:02:56 +00:00
|
|
|
{
|
|
|
|
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) {
|
2013-07-04 06:59:17 +00:00
|
|
|
ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size, reset);
|
2012-07-03 20:02:56 +00:00
|
|
|
if (ret) {
|
|
|
|
/* will hang if failed to init memory control */
|
|
|
|
while (1)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* will hang if unknow memory type */
|
|
|
|
while (1)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|