/* * 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 #include #include "clock_init.h" #include "common_setup.h" #include "exynos5_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(int reset) { 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, reset); if (ret) { /* will hang if failed to init memory control */ while (1) ; } } else { /* will hang if unknow memory type */ while (1) ; } }