e57c303153
This patch adds some documentation to the additional PLL divisor constraints on the intermediary VCO and CF values that we just found out about. PLLC divisors for some oscillators had to be adjusted accordingly. It also adds a new clock_get_pll_input_khz() function to replace clock_get_osc_khz() in cases where you want to factor in the built-in predivider for 38.4 and 48 MHz oscillators. BUG=None TEST=Still boots. Original-Change-Id: Ib6e026dbab9fcc50d6d81a884774ad07c7b0dbc3 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/194474 Original-Reviewed-by: Hung-Te Lin <hungte@chromium.org> (cherry picked from commit 3f1f565baf100edcd486055e4317c675c882396f) Signed-off-by: Marc Jones <marc.jones@se-eng.com> Change-Id: I091f42bf952a4b58ef2c30586baa5bf7496fa599 Reviewed-on: http://review.coreboot.org/7768 Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net> Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Tested-by: build bot (Jenkins)
647 lines
24 KiB
C
647 lines
24 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright 2013 Google Inc.
|
|
*
|
|
* 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; version 2 of the License.
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <arch/io.h>
|
|
#include <console/console.h>
|
|
#include <delay.h>
|
|
#include <soc/addressmap.h>
|
|
#include <soc/clock.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "emc.h"
|
|
#include "mc.h"
|
|
#include "pmc.h"
|
|
#include "sdram.h"
|
|
|
|
static void sdram_patch(uintptr_t addr, uint32_t value)
|
|
{
|
|
if (addr)
|
|
writel(value, (uint32_t*)addr);
|
|
}
|
|
|
|
static void writebits(uint32_t value, uint32_t *addr, uint32_t mask)
|
|
{
|
|
clrsetbits_le32(addr, mask, (value & mask));
|
|
}
|
|
|
|
/* PMC must be configured before clock-enable and de-reset of MC/EMC. */
|
|
static void sdram_configure_pmc(const struct sdram_params *param,
|
|
struct tegra_pmc_regs *regs)
|
|
{
|
|
/* VDDP Select */
|
|
writel(param->PmcVddpSel, ®s->vddp_sel);
|
|
udelay(param->PmcVddpSelWait);
|
|
|
|
/* Set DDR pad voltage */
|
|
writebits(param->PmcDdrPwr, ®s->ddr_pwr, PMC_DDR_PWR_VAL_MASK);
|
|
|
|
/* Set package and DPD pad control */
|
|
writebits(param->PmcDdrCfg, ®s->ddr_cfg,
|
|
(PMC_DDR_CFG_PKG_MASK | PMC_DDR_CFG_IF_MASK |
|
|
PMC_DDR_CFG_XM0_RESET_TRI_MASK |
|
|
PMC_DDR_CFG_XM0_RESET_DPDIO_MASK));
|
|
|
|
/* Turn on MEM IO Power */
|
|
writebits(param->PmcNoIoPower, ®s->no_iopower,
|
|
(PMC_NO_IOPOWER_MEM_MASK | PMC_NO_IOPOWER_MEM_COMP_MASK));
|
|
|
|
writel(param->PmcRegShort, ®s->reg_short);
|
|
}
|
|
|
|
static void sdram_start_clocks(const struct sdram_params *param)
|
|
{
|
|
u32 is_same_freq = (param->McEmemArbMisc0 &
|
|
MC_EMEM_ARB_MISC0_MC_EMC_SAME_FREQ_MASK) ? 1 : 0;
|
|
|
|
clock_sdram(param->PllMInputDivider, param->PllMFeedbackDivider,
|
|
param->PllMSelectDiv2, param->PllMSetupControl,
|
|
param->PllMPDLshiftPh45, param->PllMPDLshiftPh90,
|
|
param->PllMPDLshiftPh135, param->PllMKVCO,
|
|
param->PllMKCP, param->PllMStableTime,
|
|
param->EmcClockSource, is_same_freq);
|
|
}
|
|
|
|
static void sdram_deassert_clock_enable_signal(const struct sdram_params *param,
|
|
struct tegra_pmc_regs *regs)
|
|
{
|
|
clrbits_le32(®s->por_dpd_ctrl,
|
|
PMC_POR_DPD_CTRL_MEM0_HOLD_CKE_LOW_OVR_MASK);
|
|
udelay(param->PmcPorDpdCtrlWait);
|
|
}
|
|
|
|
static void sdram_deassert_sel_dpd(const struct sdram_params *param,
|
|
struct tegra_pmc_regs *regs)
|
|
{
|
|
clrbits_le32(®s->por_dpd_ctrl,
|
|
(PMC_POR_DPD_CTRL_MEM0_ADDR0_CLK_SEL_DPD_MASK |
|
|
PMC_POR_DPD_CTRL_MEM0_ADDR1_CLK_SEL_DPD_MASK));
|
|
/*
|
|
* Note NVIDIA recommended to always do 10us delay here and ignore
|
|
* BCT.PmcPorDpdCtrlWait.
|
|
* */
|
|
udelay(10);
|
|
}
|
|
|
|
static void sdram_set_swizzle(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
writel(param->EmcSwizzleRank0ByteCfg, ®s->swizzle_rank0_byte_cfg);
|
|
writel(param->EmcSwizzleRank0Byte0, ®s->swizzle_rank0_byte0);
|
|
writel(param->EmcSwizzleRank0Byte1, ®s->swizzle_rank0_byte1);
|
|
writel(param->EmcSwizzleRank0Byte2, ®s->swizzle_rank0_byte2);
|
|
writel(param->EmcSwizzleRank0Byte3, ®s->swizzle_rank0_byte3);
|
|
writel(param->EmcSwizzleRank1ByteCfg, ®s->swizzle_rank1_byte_cfg);
|
|
writel(param->EmcSwizzleRank1Byte0, ®s->swizzle_rank1_byte0);
|
|
writel(param->EmcSwizzleRank1Byte1, ®s->swizzle_rank1_byte1);
|
|
writel(param->EmcSwizzleRank1Byte2, ®s->swizzle_rank1_byte2);
|
|
writel(param->EmcSwizzleRank1Byte3, ®s->swizzle_rank1_byte3);
|
|
}
|
|
|
|
static void sdram_set_pad_controls(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Program the pad controls */
|
|
writel(param->EmcXm2CmdPadCtrl, ®s->xm2cmdpadctrl);
|
|
writel(param->EmcXm2CmdPadCtrl2, ®s->xm2cmdpadctrl2);
|
|
writel(param->EmcXm2CmdPadCtrl3, ®s->xm2cmdpadctrl3);
|
|
writel(param->EmcXm2CmdPadCtrl4, ®s->xm2cmdpadctrl4);
|
|
writel(param->EmcXm2CmdPadCtrl5, ®s->xm2cmdpadctrl5);
|
|
|
|
writel(param->EmcXm2DqsPadCtrl, ®s->xm2dqspadctrl);
|
|
writel(param->EmcXm2DqsPadCtrl2, ®s->xm2dqspadctrl2);
|
|
writel(param->EmcXm2DqsPadCtrl3, ®s->xm2dqspadctrl3);
|
|
writel(param->EmcXm2DqsPadCtrl4, ®s->xm2dqspadctrl4);
|
|
writel(param->EmcXm2DqsPadCtrl5, ®s->xm2dqspadctrl5);
|
|
writel(param->EmcXm2DqsPadCtrl6, ®s->xm2dqspadctrl6);
|
|
|
|
writel(param->EmcXm2DqPadCtrl, ®s->xm2dqpadctrl);
|
|
writel(param->EmcXm2DqPadCtrl2, ®s->xm2dqpadctrl2);
|
|
writel(param->EmcXm2DqPadCtrl3, ®s->xm2dqpadctrl3);
|
|
|
|
writel(param->EmcXm2ClkPadCtrl, ®s->xm2clkpadctrl);
|
|
writel(param->EmcXm2ClkPadCtrl2, ®s->xm2clkpadctrl2);
|
|
|
|
writel(param->EmcXm2CompPadCtrl, ®s->xm2comppadctrl);
|
|
|
|
writel(param->EmcXm2VttGenPadCtrl, ®s->xm2vttgenpadctrl);
|
|
writel(param->EmcXm2VttGenPadCtrl2, ®s->xm2vttgenpadctrl2);
|
|
writel(param->EmcXm2VttGenPadCtrl3, ®s->xm2vttgenpadctrl3);
|
|
|
|
writel(param->EmcCttTermCtrl, ®s->ctt_term_ctrl);
|
|
}
|
|
|
|
static void sdram_trigger_emc_timing_update(struct tegra_emc_regs *regs)
|
|
{
|
|
writel(EMC_TIMING_CONTROL_TIMING_UPDATE, ®s->timing_control);
|
|
}
|
|
|
|
static void sdram_init_mc(const struct sdram_params *param,
|
|
struct tegra_mc_regs *regs)
|
|
{
|
|
/* Initialize MC VPR settings */
|
|
writel(param->McDisplaySnapRing, ®s->display_snap_ring);
|
|
writel(param->McVideoProtectBom, ®s->video_protect_bom);
|
|
writel(param->McVideoProtectBomAdrHi, ®s->video_protect_bom_adr_hi);
|
|
writel(param->McVideoProtectSizeMb, ®s->video_protect_size_mb);
|
|
writel(param->McVideoProtectVprOverride,
|
|
®s->video_protect_vpr_override);
|
|
writel(param->McVideoProtectVprOverride1,
|
|
®s->video_protect_vpr_override1);
|
|
writel(param->McVideoProtectGpuOverride0,
|
|
®s->video_protect_gpu_override_0);
|
|
writel(param->McVideoProtectGpuOverride1,
|
|
®s->video_protect_gpu_override_1);
|
|
|
|
/* Program SDRAM geometry paarameters */
|
|
writel(param->McEmemAdrCfg, ®s->emem_adr_cfg);
|
|
writel(param->McEmemAdrCfgDev0, ®s->emem_adr_cfg_dev0);
|
|
writel(param->McEmemAdrCfgDev1, ®s->emem_adr_cfg_dev1);
|
|
|
|
/* Program bank swizzling */
|
|
writel(param->McEmemAdrCfgBankMask0, ®s->emem_bank_swizzle_cfg0);
|
|
writel(param->McEmemAdrCfgBankMask1, ®s->emem_bank_swizzle_cfg1);
|
|
writel(param->McEmemAdrCfgBankMask2, ®s->emem_bank_swizzle_cfg2);
|
|
writel(param->McEmemAdrCfgBankSwizzle3, ®s->emem_bank_swizzle_cfg3);
|
|
|
|
/* Program external memory aperature (base and size) */
|
|
writel(param->McEmemCfg, ®s->emem_cfg);
|
|
|
|
/* Program SEC carveout (base and size) */
|
|
writel(param->McSecCarveoutBom, ®s->sec_carveout_bom);
|
|
writel(param->McSecCarveoutAdrHi, ®s->sec_carveout_adr_hi);
|
|
writel(param->McSecCarveoutSizeMb, ®s->sec_carveout_size_mb);
|
|
|
|
/* Program MTS carveout (base and size) */
|
|
writel(param->McMtsCarveoutBom, ®s->mts_carveout_bom);
|
|
writel(param->McMtsCarveoutAdrHi, ®s->mts_carveout_adr_hi);
|
|
writel(param->McMtsCarveoutSizeMb, ®s->mts_carveout_size_mb);
|
|
|
|
/* Program the memory arbiter */
|
|
writel(param->McEmemArbCfg, ®s->emem_arb_cfg);
|
|
writel(param->McEmemArbOutstandingReq, ®s->emem_arb_outstanding_req);
|
|
writel(param->McEmemArbTimingRcd, ®s->emem_arb_timing_rcd);
|
|
writel(param->McEmemArbTimingRp, ®s->emem_arb_timing_rp);
|
|
writel(param->McEmemArbTimingRc, ®s->emem_arb_timing_rc);
|
|
writel(param->McEmemArbTimingRas, ®s->emem_arb_timing_ras);
|
|
writel(param->McEmemArbTimingFaw, ®s->emem_arb_timing_faw);
|
|
writel(param->McEmemArbTimingRrd, ®s->emem_arb_timing_rrd);
|
|
writel(param->McEmemArbTimingRap2Pre, ®s->emem_arb_timing_rap2pre);
|
|
writel(param->McEmemArbTimingWap2Pre, ®s->emem_arb_timing_wap2pre);
|
|
writel(param->McEmemArbTimingR2R, ®s->emem_arb_timing_r2r);
|
|
writel(param->McEmemArbTimingW2W, ®s->emem_arb_timing_w2w);
|
|
writel(param->McEmemArbTimingR2W, ®s->emem_arb_timing_r2w);
|
|
writel(param->McEmemArbTimingW2R, ®s->emem_arb_timing_w2r);
|
|
writel(param->McEmemArbDaTurns, ®s->emem_arb_da_turns);
|
|
writel(param->McEmemArbDaCovers, ®s->emem_arb_da_covers);
|
|
writel(param->McEmemArbMisc0, ®s->emem_arb_misc0);
|
|
writel(param->McEmemArbMisc1, ®s->emem_arb_misc1);
|
|
writel(param->McEmemArbRing1Throttle, ®s->emem_arb_ring1_throttle);
|
|
writel(param->McEmemArbOverride, ®s->emem_arb_override);
|
|
writel(param->McEmemArbOverride1, ®s->emem_arb_override_1);
|
|
writel(param->McEmemArbRsv, ®s->emem_arb_rsv);
|
|
|
|
/* Program extra snap levels for display client */
|
|
writel(param->McDisExtraSnapLevels, ®s->dis_extra_snap_levels);
|
|
|
|
/* Trigger MC timing update */
|
|
writel(MC_TIMING_CONTROL_TIMING_UPDATE, ®s->timing_control);
|
|
|
|
/* Program second-level clock enable overrides */
|
|
writel(param->McClkenOverride, ®s->clken_override);
|
|
|
|
/* Program statistics gathering */
|
|
writel(param->McStatControl, ®s->stat_control);
|
|
}
|
|
|
|
static void sdram_init_emc(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Program SDRAM geometry parameters */
|
|
writel(param->EmcAdrCfg, ®s->adr_cfg);
|
|
|
|
/* Program second-level clock enable overrides */
|
|
writel(param->EmcClkenOverride, ®s->clken_override);
|
|
|
|
/* Program EMC pad auto calibration */
|
|
writel(param->EmcAutoCalInterval, ®s->auto_cal_interval);
|
|
writel(param->EmcAutoCalConfig2, ®s->auto_cal_config2);
|
|
writel(param->EmcAutoCalConfig3, ®s->auto_cal_config3);
|
|
writel(param->EmcAutoCalConfig, ®s->auto_cal_config);
|
|
udelay(param->EmcAutoCalWait);
|
|
}
|
|
|
|
static void sdram_set_emc_timing(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Program EMC timing configuration */
|
|
writel(param->EmcCfg2, ®s->cfg_2);
|
|
writel(param->EmcCfgPipe, ®s->cfg_pipe);
|
|
writel(param->EmcDbg, ®s->dbg);
|
|
writel(param->EmcCmdQ, ®s->cmdq);
|
|
writel(param->EmcMc2EmcQ, ®s->mc2emcq);
|
|
writel(param->EmcMrsWaitCnt, ®s->mrs_wait_cnt);
|
|
writel(param->EmcMrsWaitCnt2, ®s->mrs_wait_cnt2);
|
|
writel(param->EmcFbioCfg5, ®s->fbio_cfg5);
|
|
writel(param->EmcRc, ®s->rc);
|
|
writel(param->EmcRfc, ®s->rfc);
|
|
writel(param->EmcRfcSlr, ®s->rfc_slr);
|
|
writel(param->EmcRas, ®s->ras);
|
|
writel(param->EmcRp, ®s->rp);
|
|
writel(param->EmcR2r, ®s->r2r);
|
|
writel(param->EmcW2w, ®s->w2w);
|
|
writel(param->EmcR2w, ®s->r2w);
|
|
writel(param->EmcW2r, ®s->w2r);
|
|
writel(param->EmcR2p, ®s->r2p);
|
|
writel(param->EmcW2p, ®s->w2p);
|
|
writel(param->EmcRdRcd, ®s->rd_rcd);
|
|
writel(param->EmcWrRcd, ®s->wr_rcd);
|
|
writel(param->EmcRrd, ®s->rrd);
|
|
writel(param->EmcRext, ®s->rext);
|
|
writel(param->EmcWext, ®s->wext);
|
|
writel(param->EmcWdv, ®s->wdv);
|
|
writel(param->EmcWdvMask, ®s->wdv_mask);
|
|
writel(param->EmcQUse, ®s->quse);
|
|
writel(param->EmcQuseWidth, ®s->quse_width);
|
|
writel(param->EmcIbdly, ®s->ibdly);
|
|
writel(param->EmcEInput, ®s->einput);
|
|
writel(param->EmcEInputDuration, ®s->einput_duration);
|
|
writel(param->EmcPutermExtra, ®s->puterm_extra);
|
|
writel(param->EmcPutermWidth, ®s->puterm_width);
|
|
writel(param->EmcPutermAdj, ®s->puterm_adj);
|
|
writel(param->EmcCdbCntl1, ®s->cdb_cntl_1);
|
|
writel(param->EmcCdbCntl2, ®s->cdb_cntl_2);
|
|
writel(param->EmcCdbCntl3, ®s->cdb_cntl_3);
|
|
writel(param->EmcQRst, ®s->qrst);
|
|
writel(param->EmcQSafe, ®s->qsafe);
|
|
writel(param->EmcRdv, ®s->rdv);
|
|
writel(param->EmcRdvMask, ®s->rdv_mask);
|
|
writel(param->EmcQpop, ®s->qpop);
|
|
writel(param->EmcCtt, ®s->ctt);
|
|
writel(param->EmcCttDuration, ®s->ctt_duration);
|
|
writel(param->EmcRefresh, ®s->refresh);
|
|
writel(param->EmcBurstRefreshNum, ®s->burst_refresh_num);
|
|
writel(param->EmcPreRefreshReqCnt, ®s->pre_refresh_req_cnt);
|
|
writel(param->EmcPdEx2Wr, ®s->pdex2wr);
|
|
writel(param->EmcPdEx2Rd, ®s->pdex2rd);
|
|
writel(param->EmcPChg2Pden, ®s->pchg2pden);
|
|
writel(param->EmcAct2Pden, ®s->act2pden);
|
|
writel(param->EmcAr2Pden, ®s->ar2pden);
|
|
writel(param->EmcRw2Pden, ®s->rw2pden);
|
|
writel(param->EmcTxsr, ®s->txsr);
|
|
writel(param->EmcTxsrDll, ®s->txsrdll);
|
|
writel(param->EmcTcke, ®s->tcke);
|
|
writel(param->EmcTckesr, ®s->tckesr);
|
|
writel(param->EmcTpd, ®s->tpd);
|
|
writel(param->EmcTfaw, ®s->tfaw);
|
|
writel(param->EmcTrpab, ®s->trpab);
|
|
writel(param->EmcTClkStable, ®s->tclkstable);
|
|
writel(param->EmcTClkStop, ®s->tclkstop);
|
|
writel(param->EmcTRefBw, ®s->trefbw);
|
|
writel(param->EmcOdtWrite, ®s->odt_write);
|
|
writel(param->EmcOdtRead, ®s->odt_read);
|
|
writel(param->EmcFbioCfg6, ®s->fbio_cfg6);
|
|
writel(param->EmcCfgDigDll, ®s->cfg_dig_dll);
|
|
writel(param->EmcCfgDigDllPeriod, ®s->cfg_dig_dll_period);
|
|
|
|
/* Don't write bit 1: addr swizzle lock bit. Written at end of sequence. */
|
|
writel(param->EmcFbioSpare & 0xfffffffd, ®s->fbio_spare);
|
|
|
|
writel(param->EmcCfgRsv, ®s->cfg_rsv);
|
|
writel(param->EmcDllXformDqs0, ®s->dll_xform_dqs0);
|
|
writel(param->EmcDllXformDqs1, ®s->dll_xform_dqs1);
|
|
writel(param->EmcDllXformDqs2, ®s->dll_xform_dqs2);
|
|
writel(param->EmcDllXformDqs3, ®s->dll_xform_dqs3);
|
|
writel(param->EmcDllXformDqs4, ®s->dll_xform_dqs4);
|
|
writel(param->EmcDllXformDqs5, ®s->dll_xform_dqs5);
|
|
writel(param->EmcDllXformDqs6, ®s->dll_xform_dqs6);
|
|
writel(param->EmcDllXformDqs7, ®s->dll_xform_dqs7);
|
|
writel(param->EmcDllXformDqs8, ®s->dll_xform_dqs8);
|
|
writel(param->EmcDllXformDqs9, ®s->dll_xform_dqs9);
|
|
writel(param->EmcDllXformDqs10, ®s->dll_xform_dqs10);
|
|
writel(param->EmcDllXformDqs11, ®s->dll_xform_dqs11);
|
|
writel(param->EmcDllXformDqs12, ®s->dll_xform_dqs12);
|
|
writel(param->EmcDllXformDqs13, ®s->dll_xform_dqs13);
|
|
writel(param->EmcDllXformDqs14, ®s->dll_xform_dqs14);
|
|
writel(param->EmcDllXformDqs15, ®s->dll_xform_dqs15);
|
|
writel(param->EmcDllXformQUse0, ®s->dll_xform_quse0);
|
|
writel(param->EmcDllXformQUse1, ®s->dll_xform_quse1);
|
|
writel(param->EmcDllXformQUse2, ®s->dll_xform_quse2);
|
|
writel(param->EmcDllXformQUse3, ®s->dll_xform_quse3);
|
|
writel(param->EmcDllXformQUse4, ®s->dll_xform_quse4);
|
|
writel(param->EmcDllXformQUse5, ®s->dll_xform_quse5);
|
|
writel(param->EmcDllXformQUse6, ®s->dll_xform_quse6);
|
|
writel(param->EmcDllXformQUse7, ®s->dll_xform_quse7);
|
|
writel(param->EmcDllXformQUse8, ®s->dll_xform_quse8);
|
|
writel(param->EmcDllXformQUse9, ®s->dll_xform_quse9);
|
|
writel(param->EmcDllXformQUse10, ®s->dll_xform_quse10);
|
|
writel(param->EmcDllXformQUse11, ®s->dll_xform_quse11);
|
|
writel(param->EmcDllXformQUse12, ®s->dll_xform_quse12);
|
|
writel(param->EmcDllXformQUse13, ®s->dll_xform_quse13);
|
|
writel(param->EmcDllXformQUse14, ®s->dll_xform_quse14);
|
|
writel(param->EmcDllXformQUse15, ®s->dll_xform_quse15);
|
|
writel(param->EmcDllXformDq0, ®s->dll_xform_dq0);
|
|
writel(param->EmcDllXformDq1, ®s->dll_xform_dq1);
|
|
writel(param->EmcDllXformDq2, ®s->dll_xform_dq2);
|
|
writel(param->EmcDllXformDq3, ®s->dll_xform_dq3);
|
|
writel(param->EmcDllXformDq4, ®s->dll_xform_dq4);
|
|
writel(param->EmcDllXformDq5, ®s->dll_xform_dq5);
|
|
writel(param->EmcDllXformDq6, ®s->dll_xform_dq6);
|
|
writel(param->EmcDllXformDq7, ®s->dll_xform_dq7);
|
|
writel(param->EmcDllXformAddr0, ®s->dll_xform_addr0);
|
|
writel(param->EmcDllXformAddr1, ®s->dll_xform_addr1);
|
|
writel(param->EmcDllXformAddr2, ®s->dll_xform_addr2);
|
|
writel(param->EmcDllXformAddr3, ®s->dll_xform_addr3);
|
|
writel(param->EmcDllXformAddr4, ®s->dll_xform_addr4);
|
|
writel(param->EmcDllXformAddr5, ®s->dll_xform_addr5);
|
|
writel(param->EmcAcpdControl, ®s->acpd_control);
|
|
writel(param->EmcDsrVttgenDrv, ®s->dsr_vttgen_drv);
|
|
writel(param->EmcTxdsrvttgen, ®s->txdsrvttgen);
|
|
writel(param->EmcBgbiasCtl0, ®s->bgbias_ctl0);
|
|
|
|
/*
|
|
* Set pipe bypass enable bits before sending any DRAM commands.
|
|
* Note other bits in EMC_CFG must be set AFTER REFCTRL is configured.
|
|
*/
|
|
writebits(param->EmcCfg, ®s->cfg,
|
|
(EMC_CFG_EMC2PMACRO_CFG_BYPASS_ADDRPIPE_MASK |
|
|
EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE1_MASK |
|
|
EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE2_MASK));
|
|
}
|
|
|
|
static void sdram_patch_bootrom(const struct sdram_params *param,
|
|
struct tegra_mc_regs *regs)
|
|
{
|
|
if (param->BootRomPatchControl & BOOT_ROM_PATCH_CONTROL_ENABLE_MASK) {
|
|
uintptr_t addr = ((param->BootRomPatchControl &
|
|
BOOT_ROM_PATCH_CONTROL_OFFSET_MASK) >>
|
|
BOOT_ROM_PATCH_CONTROL_OFFSET_SHIFT);
|
|
addr = BOOT_ROM_PATCH_CONTROL_BASE_ADDRESS + (addr << 2);
|
|
writel(param->BootRomPatchData, (uint32_t *)addr);
|
|
writel(1, ®s->timing_control);
|
|
}
|
|
}
|
|
|
|
static void sdram_set_dpd3(const struct sdram_params *param,
|
|
struct tegra_pmc_regs *regs)
|
|
{
|
|
/* Program DPD request */
|
|
writel(param->PmcIoDpd3Req, ®s->io_dpd3_req);
|
|
udelay(param->PmcIoDpd3ReqWait);
|
|
}
|
|
|
|
static void sdram_set_dli_trims(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Program DLI trims */
|
|
writel(param->EmcDliTrimTxDqs0, ®s->dli_trim_txdqs0);
|
|
writel(param->EmcDliTrimTxDqs1, ®s->dli_trim_txdqs1);
|
|
writel(param->EmcDliTrimTxDqs2, ®s->dli_trim_txdqs2);
|
|
writel(param->EmcDliTrimTxDqs3, ®s->dli_trim_txdqs3);
|
|
writel(param->EmcDliTrimTxDqs4, ®s->dli_trim_txdqs4);
|
|
writel(param->EmcDliTrimTxDqs5, ®s->dli_trim_txdqs5);
|
|
writel(param->EmcDliTrimTxDqs6, ®s->dli_trim_txdqs6);
|
|
writel(param->EmcDliTrimTxDqs7, ®s->dli_trim_txdqs7);
|
|
writel(param->EmcDliTrimTxDqs8, ®s->dli_trim_txdqs8);
|
|
writel(param->EmcDliTrimTxDqs9, ®s->dli_trim_txdqs9);
|
|
writel(param->EmcDliTrimTxDqs10, ®s->dli_trim_txdqs10);
|
|
writel(param->EmcDliTrimTxDqs11, ®s->dli_trim_txdqs11);
|
|
writel(param->EmcDliTrimTxDqs12, ®s->dli_trim_txdqs12);
|
|
writel(param->EmcDliTrimTxDqs13, ®s->dli_trim_txdqs13);
|
|
writel(param->EmcDliTrimTxDqs14, ®s->dli_trim_txdqs14);
|
|
writel(param->EmcDliTrimTxDqs15, ®s->dli_trim_txdqs15);
|
|
|
|
writel(param->EmcCaTrainingTimingCntl1,
|
|
®s->ca_training_timing_cntl1);
|
|
writel(param->EmcCaTrainingTimingCntl2,
|
|
®s->ca_training_timing_cntl2);
|
|
|
|
sdram_trigger_emc_timing_update(regs);
|
|
udelay(param->EmcTimingControlWait);
|
|
}
|
|
|
|
static void sdram_set_clock_enable_signal(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
volatile uint32_t dummy = 0;
|
|
clrbits_le32(®s->pin, (EMC_PIN_RESET_MASK | EMC_PIN_DQM_MASK |
|
|
EMC_PIN_CKE_MASK));
|
|
/*
|
|
* Assert dummy read of PIN register to ensure above write to PIN
|
|
* register went through. 200 is the recommended value by NVIDIA.
|
|
*/
|
|
dummy |= readl(®s->pin);
|
|
udelay(200 + param->EmcPinExtraWait);
|
|
|
|
/* Deassert reset */
|
|
setbits_le32(®s->pin, EMC_PIN_RESET_INACTIVE);
|
|
/*
|
|
* Assert dummy read of PIN register to ensure above write to PIN
|
|
* register went through. 200 is the recommended value by NVIDIA.
|
|
*/
|
|
dummy |= readl(®s->pin);
|
|
udelay(500 + param->EmcPinExtraWait);
|
|
|
|
/* Enable clock enable signal */
|
|
setbits_le32(®s->pin, EMC_PIN_CKE_NORMAL);
|
|
/*
|
|
* Assert dummy read of PIN register to ensure above write to PIN
|
|
* register went through. 200 is the recommended value by NVIDIA.
|
|
*/
|
|
dummy |= readl(®s->pin);
|
|
udelay(param->EmcPinProgramWait);
|
|
|
|
if (!dummy) {
|
|
die("Failed to program EMC pin.");
|
|
}
|
|
|
|
/* Send NOP (trigger) */
|
|
writebits(((1 << EMC_NOP_NOP_CMD_SHIFT) |
|
|
(param->EmcDevSelect << EMC_NOP_NOP_DEV_SELECTN_SHIFT)),
|
|
®s->nop,
|
|
EMC_NOP_NOP_CMD_MASK | EMC_NOP_NOP_DEV_SELECTN_MASK);
|
|
|
|
/* Write mode registers */
|
|
writel(param->EmcEmrs2, ®s->emrs2);
|
|
writel(param->EmcEmrs3, ®s->emrs3);
|
|
writel(param->EmcEmrs, ®s->emrs);
|
|
writel(param->EmcMrs, ®s->mrs);
|
|
|
|
if (param->EmcExtraModeRegWriteEnable) {
|
|
writel(param->EmcMrwExtra, ®s->mrs);
|
|
}
|
|
}
|
|
|
|
static void sdram_init_zq_calibration(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
if ((param->EmcZcalWarmColdBootEnables &
|
|
EMC_ZCAL_WARM_COLD_BOOT_ENABLES_COLDBOOT_MASK) == 1) {
|
|
/* Need to initialize ZCAL on coldboot. */
|
|
writel(param->EmcZcalInitDev0, ®s->zq_cal);
|
|
udelay(param->EmcZcalInitWait);
|
|
|
|
if ((param->EmcDevSelect & 2) == 0) {
|
|
writel(param->EmcZcalInitDev1, ®s->zq_cal);
|
|
udelay(param->EmcZcalInitWait);
|
|
}
|
|
} else {
|
|
udelay(param->EmcZcalInitWait);
|
|
}
|
|
}
|
|
|
|
static void sdram_set_zq_calibration(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Start periodic ZQ calibration */
|
|
writel(param->EmcZcalInterval, ®s->zcal_interval);
|
|
writel(param->EmcZcalWaitCnt, ®s->zcal_wait_cnt);
|
|
writel(param->EmcZcalMrwCmd, ®s->zcal_mrw_cmd);
|
|
}
|
|
|
|
static void sdram_set_refresh(const struct sdram_params *param,
|
|
struct tegra_emc_regs *regs)
|
|
{
|
|
/* Insert burst refresh */
|
|
if (param->EmcExtraRefreshNum > 0) {
|
|
uint32_t refresh_num = (1 << param->EmcExtraRefreshNum) - 1;
|
|
writebits((EMC_REF_CMD_REFRESH | EMC_REF_NORMAL_ENABLED |
|
|
(refresh_num << EMC_REF_NUM_SHIFT) |
|
|
(param->EmcDevSelect << EMC_REF_DEV_SELECTN_SHIFT)),
|
|
®s->ref, (EMC_REF_CMD_MASK | EMC_REF_NORMAL_MASK |
|
|
EMC_REF_NUM_MASK |
|
|
EMC_REF_DEV_SELECTN_MASK));
|
|
}
|
|
|
|
/* Enable refresh */
|
|
writel((param->EmcDevSelect | EMC_REFCTRL_REF_VALID_ENABLED),
|
|
®s->refctrl);
|
|
|
|
writel(param->EmcDynSelfRefControl, ®s->dyn_self_ref_control);
|
|
writel(param->EmcCfg, ®s->cfg);
|
|
writel(param->EmcSelDpdCtrl, ®s->sel_dpd_ctrl);
|
|
|
|
/* Write addr swizzle lock bit */
|
|
writel(param->EmcFbioSpare, ®s->fbio_spare);
|
|
|
|
/* Re-trigger timing to latch power saving functions */
|
|
sdram_trigger_emc_timing_update(regs);
|
|
}
|
|
|
|
static void sdram_enable_arbiter(const struct sdram_params *param)
|
|
{
|
|
/* TODO(hungte) Move values here to standalone header file. */
|
|
uint32_t *AHB_ARBITRATION_XBAR_CTRL = (uint32_t*)(0x6000c000 + 0xe0);
|
|
setbits_le32(AHB_ARBITRATION_XBAR_CTRL,
|
|
param->AhbArbitrationXbarCtrlMemInitDone << 16);
|
|
}
|
|
|
|
static void sdram_lock_carveouts(const struct sdram_params *param,
|
|
struct tegra_mc_regs *regs)
|
|
{
|
|
/* Lock carveouts, and emem_cfg registers */
|
|
writel(param->McVideoProtectWriteAccess, ®s->video_protect_reg_ctrl);
|
|
writel(MC_EMEM_CFG_ACCESS_CTRL_WRITE_ACCESS_DISABLED,
|
|
®s->emem_cfg_access_ctrl);
|
|
writel(param->McSecCarveoutProtectWriteAccess,
|
|
®s->sec_carveout_reg_ctrl);
|
|
writel(param->McMtsCarveoutRegCtrl, ®s->mts_carveout_reg_ctrl);
|
|
}
|
|
|
|
void sdram_init(const struct sdram_params *param)
|
|
{
|
|
struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs*)TEGRA_PMC_BASE;
|
|
struct tegra_mc_regs *mc = (struct tegra_mc_regs*)TEGRA_MC_BASE;
|
|
struct tegra_emc_regs *emc = (struct tegra_emc_regs*)TEGRA_EMC_BASE;
|
|
|
|
printk(BIOS_DEBUG, "Initializing SDRAM of type %d with %dKHz\n",
|
|
param->MemoryType, clock_get_pll_input_khz() *
|
|
param->PllMFeedbackDivider / param->PllMInputDivider /
|
|
(1 + param->PllMSelectDiv2));
|
|
if (param->MemoryType != NvBootMemoryType_Ddr3)
|
|
die("Unsupported memory type!\n");
|
|
|
|
sdram_configure_pmc(param, pmc);
|
|
sdram_patch(param->EmcBctSpare0, param->EmcBctSpare1);
|
|
|
|
sdram_start_clocks(param);
|
|
sdram_patch(param->EmcBctSpare2, param->EmcBctSpare3);
|
|
|
|
sdram_deassert_sel_dpd(param, pmc);
|
|
sdram_set_swizzle(param, emc);
|
|
sdram_set_pad_controls(param, emc);
|
|
sdram_patch(param->EmcBctSpare4, param->EmcBctSpare5);
|
|
|
|
sdram_trigger_emc_timing_update(emc);
|
|
sdram_init_mc(param, mc);
|
|
sdram_init_emc(param, emc);
|
|
sdram_patch(param->EmcBctSpare6, param->EmcBctSpare7);
|
|
|
|
sdram_set_emc_timing(param, emc);
|
|
sdram_patch_bootrom(param, mc);
|
|
sdram_set_dpd3(param, pmc);
|
|
sdram_set_dli_trims(param, emc);
|
|
sdram_deassert_clock_enable_signal(param, pmc);
|
|
sdram_set_clock_enable_signal(param, emc);
|
|
sdram_init_zq_calibration(param, emc);
|
|
sdram_patch(param->EmcBctSpare8, param->EmcBctSpare9);
|
|
|
|
sdram_set_zq_calibration(param, emc);
|
|
sdram_patch(param->EmcBctSpare10, param->EmcBctSpare11);
|
|
|
|
sdram_trigger_emc_timing_update(emc);
|
|
sdram_set_refresh(param, emc);
|
|
sdram_enable_arbiter(param);
|
|
sdram_lock_carveouts(param, mc);
|
|
|
|
sdram_lp0_save_params(param);
|
|
}
|
|
|
|
uint32_t sdram_get_ram_code(void)
|
|
{
|
|
struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs*)TEGRA_PMC_BASE;
|
|
return ((readl(&pmc->strapping_opt_a) &
|
|
PMC_STRAPPING_OPT_A_RAM_CODE_MASK) >>
|
|
PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT);
|
|
}
|
|
|
|
/* returns total amount of DRAM (in MB) from memory controller registers */
|
|
int sdram_size_mb(void)
|
|
{
|
|
struct tegra_mc_regs *mc = (struct tegra_mc_regs *)TEGRA_MC_BASE;
|
|
static int total_size = 0;
|
|
|
|
if (total_size)
|
|
return total_size;
|
|
|
|
/*
|
|
* This obtains memory size from the External Memory Aperture
|
|
* Configuration register. Nvidia confirmed that it is safe to assume
|
|
* this value represents the total physical DRAM size.
|
|
*/
|
|
total_size = (read32(&mc->emem_cfg) >>
|
|
MC_EMEM_CFG_SIZE_MB_SHIFT) & MC_EMEM_CFG_SIZE_MB_MASK;
|
|
|
|
printk(BIOS_DEBUG, "%s: Total SDRAM (MB): %u\n", __func__, total_size);
|
|
return total_size;
|
|
}
|
|
|
|
uintptr_t sdram_max_addressable_mb(void)
|
|
{
|
|
return MIN((CONFIG_SYS_SDRAM_BASE/MiB) + sdram_size_mb(), 4096);
|
|
}
|