blob: 109602a2e6ac2b9d6926e113c9171f339e2d104c [file] [log] [blame]
/*
* 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)
;
}
}