From da1e02a3a0cd8de244a03cc84d5ecc9663e5e694 Mon Sep 17 00:00:00 2001 From: Peter Kao Date: Fri, 31 Jul 2015 17:11:14 +0800 Subject: [PATCH] mediatek/mt8173: Add EMI driver, DRAM initialization BUG=none TEST=emerge-oak coreboot BRANCH=none Change-Id: I6b05898de2d0022e0de7b18f1db3c3e9c06d8135 Signed-off-by: Patrick Georgi Original-Commit-Id: b614eeb1bba5660438c214e82225832809caca8e Original-Change-Id: I0f7b0a426dae1548b34114a024c92befdf6002f6 Original-Signed-off-by: Peter Kao Original-Reviewed-on: https://chromium-review.googlesource.com/292692 Original-Commit-Ready: Yidi Lin Original-Tested-by: Yidi Lin Original-Reviewed-by: Julius Werner Reviewed-on: https://review.coreboot.org/13105 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/soc/mediatek/mt8173/Kconfig | 4 + src/soc/mediatek/mt8173/Makefile.inc | 3 + src/soc/mediatek/mt8173/dramc_pi_basic_api.c | 879 +++++++++++++ .../mt8173/dramc_pi_calibration_api.c | 1150 +++++++++++++++++ src/soc/mediatek/mt8173/emi.c | 142 ++ .../mt8173/include/soc/dramc_common.h | 42 + .../mt8173/include/soc/dramc_pi_api.h | 187 +++ .../mt8173/include/soc/dramc_register.h | 522 ++++++++ src/soc/mediatek/mt8173/include/soc/emi.h | 140 ++ src/soc/mediatek/mt8173/include/soc/pll.h | 5 + src/soc/mediatek/mt8173/memory.c | 357 +++++ src/soc/mediatek/mt8173/pll.c | 26 + 12 files changed, 3457 insertions(+) create mode 100644 src/soc/mediatek/mt8173/dramc_pi_basic_api.c create mode 100644 src/soc/mediatek/mt8173/dramc_pi_calibration_api.c create mode 100644 src/soc/mediatek/mt8173/emi.c create mode 100644 src/soc/mediatek/mt8173/include/soc/dramc_common.h create mode 100644 src/soc/mediatek/mt8173/include/soc/dramc_pi_api.h create mode 100644 src/soc/mediatek/mt8173/include/soc/dramc_register.h create mode 100644 src/soc/mediatek/mt8173/include/soc/emi.h create mode 100644 src/soc/mediatek/mt8173/memory.c diff --git a/src/soc/mediatek/mt8173/Kconfig b/src/soc/mediatek/mt8173/Kconfig index 84c4d15970..91a8d4fe75 100644 --- a/src/soc/mediatek/mt8173/Kconfig +++ b/src/soc/mediatek/mt8173/Kconfig @@ -17,6 +17,10 @@ config SOC_MEDIATEK_MT8173 if SOC_MEDIATEK_MT8173 +config MEMORY_TEST + bool + default n + config DEBUG_SOC_DRIVER bool "The top level switch for soc driver debug message" default n diff --git a/src/soc/mediatek/mt8173/Makefile.inc b/src/soc/mediatek/mt8173/Makefile.inc index fa344c0ee3..7b6dc75954 100644 --- a/src/soc/mediatek/mt8173/Makefile.inc +++ b/src/soc/mediatek/mt8173/Makefile.inc @@ -43,6 +43,7 @@ verstage-$(CONFIG_SPI_FLASH) += flash_controller.c ################################################################################ romstage-$(CONFIG_SPI_FLASH) += flash_controller.c +romstage-y += pll.c romstage-y += timer.c romstage-$(CONFIG_DRIVERS_UART) += uart.c @@ -50,6 +51,8 @@ romstage-y += cbmem.c romstage-y += spi.c romstage-y += gpio.c romstage-y += pmic_wrap.c mt6391.c +romstage-y += memory.c +romstage-y += emi.c dramc_pi_basic_api.c dramc_pi_calibration_api.c romstage-y += mmu_operations.c romstage-y += rtc.c diff --git a/src/soc/mediatek/mt8173/dramc_pi_basic_api.c b/src/soc/mediatek/mt8173/dramc_pi_basic_api.c new file mode 100644 index 0000000000..ac28373a39 --- /dev/null +++ b/src/soc/mediatek/mt8173/dramc_pi_basic_api.c @@ -0,0 +1,879 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mem_pll { + u8 delay; + u8 phase; + u8 done; +}; + +inline u8 is_dual_rank(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + /* judge ranks from EMI_CONA[17] (cha) and EMI_CONA[16] (chb) */ + return (sdram_params->emi_set.cona & (1 << (17 - channel))) ? 1 : 0; +} + +static void mem_pll_pre_init(u32 channel) +{ + write32(&ch[channel].ddrphy_regs->lpddr2_3, 0x1 << 29 | 0x1 << 25 | + 0xf << 16 | 0xffff); + + write32(&ch[channel].ddrphy_regs->lpddr2_4, 0x1 << 29 | 0x1 << 25 | + 0xf << 16 | 0xffff); + + /* adjust DQS/DQM phase to get best margin */ + write32(&ch[channel].ddrphy_regs->selph12, 0x1 << 28 | 0xf << 20 | + 0x1 << 12 | 0xf << 4); + /* adjust DQ phase to get best margin */ + write32(&ch[channel].ddrphy_regs->selph13, 0xffffffff << 0); + write32(&ch[channel].ddrphy_regs->selph14, 0xffffffff << 0); + + /* fix OCV effect */ + write32(&ch[channel].ddrphy_regs->selph15, 0x1 << 4 | 0xf << 0); + + /* pll register control by CPU and select internal pipe path */ + write32(&ch[channel].ddrphy_regs->peri[2], 0x11 << 24 | 0x11 << 16 | + 0xff << 8 | 0x11 << 0); + write32(&ch[channel].ddrphy_regs->peri[3], 0x11 << 24 | 0x51 << 16 | + 0x11 << 8 | 0x11 << 0); + + /* enable clock sync and spm control clock */ + write32(&ch[channel].ddrphy_regs->mempll_divider, 0x9 << 24 | + 0x1 << 15 | + 0x2 << 4 | + 0x1 << 1 | + 0x1 << 0); + /* pll2 enable from CPU control */ + write32(&ch[channel].ddrphy_regs->mempll05_divider, 0x1 << 27); + + /* enable chip top memory clock */ + setbits_le32(&ch[channel].ddrphy_regs->mempll_divider, 0x1 << 4); + + /* disable C/A and DQ M_CK clock gating */ + clrbits_le32(&ch[channel].ddrphy_regs->ddrphy_cg_ctrl, 0x1 << 2 | + 0x1 << 1); + + /* enable spm control clock */ + clrbits_le32(&ch[channel].ddrphy_regs->mempll_divider, 0x1 << 15 | + 0x1 << 0); + /* enable dramc 2X mode */ + setbits_le32(&ch[channel].ao_regs->ddr2ctl, 1 << 0); + + /* select internal clock path */ + write32(&ch[channel].ddrphy_regs->peri[0], 0x21 << 24 | 0x27 << 16 | + 0x1b << 8 | 0x3 << 0); + + write32(&ch[channel].ddrphy_regs->peri[1], 0x50 << 24 | 0x96 << 16 | + 0x6 << 8 | 0x1e << 0); + + /* trigger to make memory clock correct phase */ + setbits_le32(&ch[channel].ddrphy_regs->mempll_divider, 0x1 << 24 | + 0x1 << 7); + + if(channel == CHANNEL_A) { + /* select memory clock sync for channel A (internal source) */ + clrbits_le32(&ch[channel].ddrphy_regs->mempll_divider, 0x1 << 3); + } +} + +static void mem_pll_init_set_params(u32 channel) +{ + u32 pattern1, pattern2, pattern3; + u32 mempll_ic_3_0, mempll_bp_3_0; + u32 mempll_fbdiv_6_0, mempll_m4pdiv_1_0; + u32 mempll_br_1_0, mempll_bc_1_0, mempll_ir_3_0; + + mempll_fbdiv_6_0 = 0x7 << 16; + mempll_br_1_0 = 0x1 << 10; + mempll_bc_1_0 = 0x0 << 8; + mempll_ir_3_0 = 0xc << 28; + mempll_ic_3_0 = 0x6 << 8; + mempll_bp_3_0 = 0x1 << 12; + mempll_m4pdiv_1_0 = 0x0 << 28; + + write32(&ch[channel].ddrphy_regs->mempll[14], 0x0); + + write32(&ch[channel].ddrphy_regs->mempll[3], 0x3 << 30 | + 0x1 << 28); + /* mempll 2 config */ + pattern1 = mempll_ir_3_0 | mempll_fbdiv_6_0 | mempll_ic_3_0; + pattern2 = mempll_m4pdiv_1_0; + pattern3 = mempll_bp_3_0 | mempll_br_1_0 | mempll_bc_1_0; + + /* mempll2_autok_en = 1, mempll2_autok_load = 1 */ + write32(&ch[channel].ddrphy_regs->mempll[5], 0x1 << 26 | 0x3 << 24 | + 0x1 << 23 | pattern1); + write32(&ch[channel].ddrphy_regs->mempll[6], 0x1 << 30 | 0x3 << 26 | + 0x3 << 14 | pattern2); + write32(&ch[channel].ddrphy_regs->mempll[7], 0x1 << 17 | 0x1 << 0 | + pattern3); + /* mempll 4 */ + write32(&ch[channel].ddrphy_regs->mempll[11], 0x1 << 26 | 0x3 << 24 | + 0x1 << 23 | pattern1); + write32(&ch[channel].ddrphy_regs->mempll[12], 0x1 << 30 | 0x3 << 26 | + 0x3 << 14 | pattern2); + write32(&ch[channel].ddrphy_regs->mempll[13], 0x1 << 0 | pattern3); + + /* mempll 3 - enable signal tie together */ + write32(&ch[channel].ddrphy_regs->mempll[8], 0x1 << 26 | 0x3 << 24 | + 0x1 << 23 | pattern1); + write32(&ch[channel].ddrphy_regs->mempll[9], 0x1 << 30 | 0x3 << 26 | + 0x3 << 14 | pattern2); + write32(&ch[channel].ddrphy_regs->mempll[10], 0x1 << 17 | 0x1 << 0 | + pattern3); +} + +static void mem_pll_init_phase_sync(u32 channel) +{ + write32(&ch[channel].ddrphy_regs->mempll_divider, BIT(27) | BIT(24) | + BIT(7) | BIT(5) | + BIT(4) | BIT(0)); + /* spm control clock enable */ + clrsetbits_le32(&ch[channel].ddrphy_regs->mempll_divider, BIT(0), + BIT(1)); + + clrsetbits_le32(&ch[channel].ddrphy_regs->mempll_divider, BIT(1), + BIT(0)); +} + +static void pll_phase_adjust(u32 channel, struct mem_pll *mempll, int reg_offs) +{ + switch (mempll->phase) { + + case MEMPLL_INIT: + /* initial phase: zero out RG_MEPLL(2,3,4)_(REF_DL,FB)_DL */ + clrbits_le32(&ch[channel].ddrphy_regs->mempll[reg_offs], + 0x1f << MEMPLL_REF_DL_SHIFT | + 0x1f << MEMPLL_FB_DL_SHIFT); + break; + + case MEMPLL_REF_LAG: + /* REF lag FBK, delay FBK */ + clrsetbits_le32(&ch[channel].ddrphy_regs->mempll[reg_offs], + 0x1f << MEMPLL_REF_DL_SHIFT | + 0x1f << MEMPLL_FB_DL_SHIFT, + mempll->delay << MEMPLL_FB_DL_SHIFT); + break; + + case MEMPLL_REF_LEAD: + /* REF lead FBK, delay REF */ + clrsetbits_le32(&ch[channel].ddrphy_regs->mempll[reg_offs], + 0x1f << MEMPLL_REF_DL_SHIFT | + 0x1f << MEMPLL_FB_DL_SHIFT, + mempll->delay << MEMPLL_REF_DL_SHIFT); + }; +} + +static void pll_phase_check(u32 channel, struct mem_pll *mempll, int idx) +{ + u32 value = read32(&ch[channel].ddrphy_regs->jmeter_pll_st[idx]); + u16 one_count = (u16)((value >> 16) & 0xffff); + u16 zero_count = (u16)(value & 0xffff); + + dramc_dbg_msg("PLL %d, phase %d, one_count %d, zero_count %d\n", + (idx + 2), mempll->phase, one_count, zero_count); + + switch (mempll->phase) { + + case MEMPLL_INIT: + if ((one_count - zero_count) > JMETER_COUNT_N) { + /* REF lag FBK */ + mempll->phase = MEMPLL_REF_LAG; + mempll->delay++; + } else if ((zero_count - one_count) > JMETER_COUNT_N) { + /* REF lead FBK */ + mempll->phase = MEMPLL_REF_LEAD; + mempll->delay++; + } else { + /* in-phase at initial */ + mempll->done = 1; + } + break; + + case MEMPLL_REF_LAG: + if (JMETER_COUNT_N >= (one_count - zero_count)) { + mempll->done = 1; + } else { + mempll->delay++; + } + break; + + case MEMPLL_REF_LEAD: + if (JMETER_COUNT_N >= (zero_count - one_count)) { + mempll->done = 1; + } else { + mempll->delay++; + } + } +} + +static void mem_pll_phase_cali(u32 channel) +{ + u32 i; + + struct mem_pll mempll[3] = + { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + + dramc_dbg_msg("[PLL_Phase_Calib] ===== PLL Phase Calibration: "); + dramc_dbg_msg("CHANNEL %d (0: CHA, 1: CHB) =====\n", channel); + + /* 1. set jitter meter count number to 1024 for mempll 2 3 4 */ + for (i = 0; i < 3; i++) + clrsetbits_le32(&ch[channel].ddrphy_regs->jmeter[i], + JMETER_COUNTER_MASK, + JMETER_COUNT << JMETER_COUNTER_SHIFT); + + while (1) { + + for (i = 0; i < 3; i++) { + if (!mempll[i].done) { + pll_phase_adjust(channel, &mempll[i], (i + 2) * 3); + } + } + + udelay(20); /* delay 20us for external loop pll stable */ + + /* 2. enable mempll 2 3 4 jitter meter */ + for (i = 0; i < 3; i++) + setbits_le32(&ch[channel].ddrphy_regs->jmeter[i], + JMETER_EN_BIT); + + /* 3. wait for jitter meter complete */ + udelay(JMETER_WAIT_DONE_US); + + /* 4. check jitter meter counter value for mempll 2 3 4 */ + for (i = 0; i < 3; i++) { + if (!mempll[i].done) { + pll_phase_check(channel, &mempll[i], i); + } + } + + /* 5. disable mempll 2 3 4 jitter meter */ + for (i = 0; i < 3; i++) + clrbits_le32(&ch[channel].ddrphy_regs->jmeter[i], + JMETER_EN_BIT); + + /* 6. all done early break */ + if (mempll[0].done && mempll[1].done && mempll[2].done) + break; + + /* 7. delay line overflow break */ + for (i = 0; i < 3; i++) { + if(mempll[i].delay >= 32) { + die("MEMPLL calibration fail\n"); + } + } + } + + dramc_dbg_msg("pll done: "); + + dramc_dbg_msg("%d, %d, %d\n", + mempll[0].done, mempll[1].done, mempll[2].done); + dramc_dbg_msg("pll dl: %d, %d, %d\n", + mempll[0].delay, mempll[1].delay, mempll[2].delay); +} + +void mem_pll_init(const struct mt8173_sdram_params *sdram_params) +{ + u32 channel; + + /* udelay waits for PLL to stabilize in this function */ + printk(BIOS_DEBUG, "[PLL] mempll_init and cali\n"); + + /* mempll pre_init for two channels */ + for (channel = 0; channel < CHANNEL_NUM; channel++) + mem_pll_pre_init(channel); + + /* only set once in MPLL */ + mt_mem_pll_config_pre(sdram_params); + + for (channel = 0; channel < CHANNEL_NUM; channel++) + mem_pll_init_set_params(channel); + + udelay(1); /* wait after da_mpll_sdm_iso_en goes low */ + + /* only set once in MPLL */ + mt_mem_pll_config_post(); + + udelay(100); + + for (channel = 0; channel < CHANNEL_NUM; channel++) { + + /* mempll_bias_en */ + write32(&ch[channel].ddrphy_regs->mempll[3], 0xd << 28 | + 0x1 << 6); + udelay(2); + + /* mempll2_en -> mempll4_en -> mempll3_en */ + setbits_le32(&ch[channel].ddrphy_regs->mempll[5], 1 << 0); + setbits_le32(&ch[channel].ddrphy_regs->mempll[11], 1 << 0); + setbits_le32(&ch[channel].ddrphy_regs->mempll[8], 1 << 0); + + udelay(100); + + /* mempll_bias_lpf_en */ + setbits_le32(&ch[channel].ddrphy_regs->mempll[3], 1 << 7); + + udelay(30); + + /* select mempll4 band register */ + setbits_le32(&ch[channel].ddrphy_regs->mempll[4], 1 << 26); + clrbits_le32(&ch[channel].ddrphy_regs->mempll[4], 1 << 26); + + /* PLL ready */ + + /* disable mempll2_en -> mempll4_en -> mempll3_en */ + clrbits_le32(&ch[channel].ddrphy_regs->mempll[5], 1 << 0); + clrbits_le32(&ch[channel].ddrphy_regs->mempll[11], 1 << 0); + clrbits_le32(&ch[channel].ddrphy_regs->mempll[8], 1 << 0); + + /* disable autok mempll2_en -> mempll4_en -> mempll3_en */ + clrbits_le32(&ch[channel].ddrphy_regs->mempll[5], 1 << 23); + clrbits_le32(&ch[channel].ddrphy_regs->mempll[11], 1 << 23); + clrbits_le32(&ch[channel].ddrphy_regs->mempll[8], 1 << 23); + + udelay(1); + + /* mempll[2->4->3]_fb_mck_sel=1 (switch to outer loop) */ + setbits_le32(&ch[channel].ddrphy_regs->mempll[6], 1 << 25); + setbits_le32(&ch[channel].ddrphy_regs->mempll[12], 1 << 25); + setbits_le32(&ch[channel].ddrphy_regs->mempll[9], 1 << 25); + + udelay(1); + + /* enable mempll2_en -> mempll4_en -> mempll3_en */ + setbits_le32(&ch[channel].ddrphy_regs->mempll[5], 1 << 0); + setbits_le32(&ch[channel].ddrphy_regs->mempll[11], 1 << 0); + setbits_le32(&ch[channel].ddrphy_regs->mempll[8], 1 << 0); + } + + /* mempll new power-on */ + write32(&mt8173_spm->poweron_config_set, 0x1 << 0 | + SPM_PROJECT_CODE << 16); + /* request mempll reset/pdn mode */ + setbits_le32(&mt8173_spm->power_on_val0, 0x1 << 27); + + udelay(2); + + /* unrequest mempll reset/pdn mode and wait settle */ + clrbits_le32(&mt8173_spm->power_on_val0, 0x1 << 27); + + udelay(31); /* PLL ready */ + + for (channel = 0; channel < CHANNEL_NUM; channel++) + mem_pll_init_phase_sync(channel); + + udelay(1); + + /* mempll calibration for two channels */ + for (channel = 0; channel < CHANNEL_NUM; channel++) + mem_pll_phase_cali(channel); + + div2_phase_sync(); /* phase sync for channel B */ + + mt_mem_pll_mux(); +} + +void dramc_pre_init(u32 channel, const struct mt8173_sdram_params *sdram_params) +{ + /* txdly_cs, txdly_cs1 */ + write32(&ch[channel].ao_regs->selph1, 0x0); + /* txdly_dqsgate, txdly_dqsgate_p1 */ + write32(&ch[channel].ao_regs->selph2, 0x3 << 20 | 0x2 << 12); + /* txldy_ra* */ + write32(&ch[channel].ao_regs->selph3, 0x0); + /* txldy_ra* */ + write32(&ch[channel].ao_regs->selph4, 0x0); + + /* setting of write latency (WL=8) */ + write32(&ch[channel].ao_regs->selph7, 0x3333 << 16 | 0x3333); + write32(&ch[channel].ao_regs->selph8, 0x3333 << 16 | 0x3333); + write32(&ch[channel].ao_regs->selph9, 0x3333 << 16 | 0x3333); + write32(&ch[channel].ao_regs->selph10, 0x5555 << 16 | 0xffff); + write32(&ch[channel].ao_regs->selph11, 0x55 << 16 | 0xff); + + write32(&ch[channel].ao_regs->selph5, 0x1 << 26 | 0x2 << 22 | + 0x1 << 20 | 0x5 << 16 | + 0x5555); + + write32(&ch[channel].ao_regs->selph6_1, 0x4 << 8 | 0x3 << 4 | + 0x2 << 0); + + write32(&ch[channel].ao_regs->ac_time_05t, + sdram_params->ac_timing.actim05t); +} + +static void mrs_write(int channel, int rank, u32 mrs_value, unsigned int dly) +{ + write32(&ch[channel].ao_regs->mrs, rank << 28 | mrs_value); + + write32(&ch[channel].ao_regs->spcmd, 0x1); + udelay(dly); + write32(&ch[channel].ao_regs->spcmd, 0x0); +} + +static void dramc_set_mrs_value(int channel, int rank, + const struct mt8173_sdram_params *sdram_params) +{ + /* MR63 -> Reset, Wait >=10us if not check DAI */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_63, 10); + /* MR10 -> ZQ Init, tZQINIT>=1us */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_10, 1); + /* MR3 driving stregth set to max */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_3, 1); + /* MR1 */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_1, 1); + /* MR2 */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_2, 1); + /* MR11 ODT disable */ + mrs_write(channel, rank, sdram_params->mrs_set.mrs_11, 1); +} + +void dramc_init(u32 channel, const struct mt8173_sdram_params *sdram_params) +{ + u32 bit, dual_rank_set; + + const struct mt8173_calib_params *calib_params; + + dual_rank_set = is_dual_rank(channel, sdram_params); + calib_params = &sdram_params->calib_params; + + write32(&ch[channel].ddrphy_regs->peri[2], 0x1 << 12 | + 0x1 << 4); + + write32(&ch[channel].ddrphy_regs->peri[3], 0x0); + + write32(&ch[channel].ao_regs->test2_4, + sdram_params->ac_timing.test2_4); + + write32(&ch[channel].ao_regs->clk1delay, 0x1 << 23 | + 0x1 << 22 | + 0x1 << 21); + + /* rank config */ + assert((sdram_params->ac_timing.rkcfg & 0x1) == dual_rank_set); + write32(&ch[channel].ao_regs->rkcfg, + sdram_params->ac_timing.rkcfg); + + /* pimux */ + write32(&ch[channel].ao_regs->mckdly, 0x1 << 30 | + 0x1 << 20 | + 0x1 << 4); + + write32(&ch[channel].ddrphy_regs->mckdly, 0x1 << 8); + + write32(&ch[channel].ao_regs->padctl4, 0x1 << 0); + + /* tCKEH/tCKEL extend 1T */ + write32(&ch[channel].ao_regs->dummy, 0x1 << 31 | + 0x3 << 10 | + 0x1 << 4); + + /* driving control */ + write32(&ch[channel].ao_regs->iodrv6, DEFAULT_DRIVING | + DRIVING_DS2_0 << 20 | + DRIVING_DS2_0 << 4); + + write32(&ch[channel].ddrphy_regs->drvctl1, DEFAULT_DRIVING | + DRIVING_DS2_0 << 20); + + write32(&ch[channel].ao_regs->drvctl1, DEFAULT_DRIVING | + DRIVING_DS2_0 << 4); + + /* enable dqs signal output */ + write32(&ch[channel].ddrphy_regs->ioctl, 0x0); + + /* rank 0 dqs gating delay */ + write32(&ch[channel].ao_regs->dqsien[0], 0x40 << 24 | + 0x40 << 16 | + 0x40 << 8 | + 0x40 << 0); + + write32(&ch[channel].ao_regs->dqsctl1, 0x1 << 28 | + 0x5 << 24); + + write32(&ch[channel].ao_regs->dqsctl2, 0x5 << 0); + write32(&ch[channel].ao_regs->phyctl1, 0x1 << 25); + write32(&ch[channel].ao_regs->gddr3ctl1, 0x1 << 24); + write32(&ch[channel].ddrphy_regs->gddr3ctl1, 0x1 << 28); + write32(&ch[channel].ao_regs->arbctl0, 0x80 << 0); + + /* enable clock pad 0 */ + write32(&ch[channel].ao_regs->clkctl, 0x1 << 28); + + udelay(1); + + write32(&ch[channel].ao_regs->conf1, + sdram_params->ac_timing.conf1); + + write32(&ch[channel].ddrphy_regs->dqsgctl, 0x1 << 31 | + 0x1 << 30 | + 0x1 << 4 | + 0x1 << 0); + + write32(&ch[channel].ao_regs->dqscal0, 0x0); + write32(&ch[channel].ddrphy_regs->dqscal0, 0x0); + + write32(&ch[channel].ao_regs->actim0, + sdram_params->ac_timing.actim); + + write32(&ch[channel].ao_regs->misctl0, + sdram_params->ac_timing.misctl0); + write32(&ch[channel].ddrphy_regs->misctl0, + sdram_params->ac_timing.misctl0); + + write32(&ch[channel].ao_regs->perfctl0, 0x1 << 20); + + write32(&ch[channel].ao_regs->ddr2ctl, + sdram_params->ac_timing.ddr2ctl); + write32(&ch[channel].ddrphy_regs->ddr2ctl, + sdram_params->ac_timing.ddr2ctl); + + write32(&ch[channel].ao_regs->misc, 0xb << 8 | + 0x1 << 7 | + 0x1 << 6 | + 0x1 << 5); + + write32(&ch[channel].ao_regs->dllconf, 0xf << 28 | + 0x1 << 24); + + write32(&ch[channel].ao_regs->actim1, + sdram_params->ac_timing.actim1); + + write32(&ch[channel].ddrphy_regs->dqsisel, 0x0); + + /* disable ODT before ZQ calibration */ + write32(&ch[channel].ao_regs->wodt, 0x1 << 0); + + write32(&ch[channel].ao_regs->padctl4, 0x1 << 2 | + 0x1 << 0); + + udelay(200); /* tINIT3 > 200us */ + + write32(&ch[channel].ao_regs->gddr3ctl1, 0x1 << 24 | + 0x1 << 20); + + write32(&ch[channel].ddrphy_regs->gddr3ctl1, 0x1 << 28); + + /* set mode register value */ + dramc_set_mrs_value(channel, 0, sdram_params); + + if (dual_rank_set) + dramc_set_mrs_value(channel, 1, sdram_params); + + write32(&ch[channel].ao_regs->gddr3ctl1, + sdram_params->ac_timing.gddr3ctl1); + write32(&ch[channel].ddrphy_regs->gddr3ctl1, + sdram_params->ac_timing.gddr3ctl1); + + write32(&ch[channel].ao_regs->dramc_pd_ctrl, + sdram_params->ac_timing.pd_ctrl); + + write32(&ch[channel].ao_regs->padctl4, 0x1 << 0); + write32(&ch[channel].ao_regs->perfctl0, 0x1 << 20 | 0x1 << 0); + write32(&ch[channel].ao_regs->zqcs, 0xa << 8 | 0x56 << 0); + write32(&ch[channel].ddrphy_regs->padctl1, 0x0); + + write32(&ch[channel].ao_regs->test2_3, + sdram_params->ac_timing.test2_3); + + write32(&ch[channel].ao_regs->conf2, + sdram_params->ac_timing.conf2); + + write32(&ch[channel].ddrphy_regs->padctl2, 0x0); + + /* DISABLE_DRVREF */ + write32(&ch[channel].ao_regs->ocdk, 0x0); + write32(&ch[channel].ddrphy_regs->ocdk, 0x0); + + write32(&ch[channel].ao_regs->r1deldly, 0x12 << 24 | + 0x12 << 16 | + 0x12 << 8 | + 0x12 << 0); + + write32(&ch[channel].ao_regs->padctl7, 0x0); + + /* CLKTDN, DS0TDN, DS1TDN, DS2TDN, DS3TDN */ + setbits_le32(&ch[channel].ddrphy_regs->tdsel[2], 0x1 << 31 | + 0x1 << 29 | + 0x1 << 27 | + 0x1 << 25 | + 0x1 << 1); + /* DISABLE_PERBANK_REFRESH */ + clrbits_le32(&ch[channel].ao_regs->rkcfg, 0x1 << 7); + + /* clear R_DMREFTHD to reduce MR4 wait refresh queue time */ + clrbits_le32(&ch[channel].ao_regs->conf2, 0x7 << 24); + + /* duty default value */ + write32(&ch[channel].ddrphy_regs->phyclkduty, 0x1 << 28 | + 0x1 << 16); + + if (!dual_rank_set) { + /* single rank, CKE1 always off */ + setbits_le32(&ch[channel].ao_regs->gddr3ctl1, 0x1 << 21); + } + + /* default dqs rx perbit input delay */ + write32(&ch[channel].ao_regs->r0deldly, + calib_params->rx_dqs_dly[channel]); + + write32(&ch[channel].ao_regs->r1deldly, + calib_params->rx_dqs_dly[channel]); + + for (bit = 0; bit < DQS_BIT_NUMBER; bit++) + write32(&ch[channel].ao_regs->dqidly[bit], + calib_params->rx_dq_dly[channel][bit]); +} + +void div2_phase_sync(void) +{ + clrbits_le32(&ch[CHANNEL_B].ddrphy_regs->mempll_divider, + 1 << MEMCLKENB_SHIFT); + udelay(1); + + setbits_le32(&ch[CHANNEL_B].ddrphy_regs->mempll_divider, + 1 << MEMCLKENB_SHIFT); +} + +void dramc_phy_reset(u32 channel) +{ + /* reset phy */ + setbits_le32(&ch[channel].ddrphy_regs->phyctl1, + 1 << PHYCTL1_PHYRST_SHIFT); + + /* read data counter reset */ + setbits_le32(&ch[channel].ao_regs->gddr3ctl1, + 1 << GDDR3CTL1_RDATRST_SHIFT); + + udelay(1); /* delay 1ns */ + + clrbits_le32(&ch[channel].ao_regs->gddr3ctl1, + 1 << GDDR3CTL1_RDATRST_SHIFT); + + clrbits_le32(&ch[channel].ddrphy_regs->phyctl1, + 1 << PHYCTL1_PHYRST_SHIFT); +} + +void dramc_runtime_config(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + /* enable hw gating */ + setbits_le32(&ch[channel].ao_regs->dqscal0, + 1 << DQSCAL0_STBCALEN_SHIFT); + + /* if frequency >1600, tCKE should >7 clk */ + setbits_le32(&ch[channel].ao_regs->dummy, 0x1 << 4); + + if(sdram_params->dram_freq * 2 < 1600 * MHz) + die("set tCKE error in runtime config"); + + /* DDRPHY C/A and DQ M_CK clock gating enable */ + setbits_le32(&ch[channel].ddrphy_regs->ddrphy_cg_ctrl, 0x1 << 2 | + 0x1 << 1); + + setbits_le32(&ch[channel].ao_regs->perfctl0, BIT(19) | BIT(14) | + BIT(11) | BIT(10) | + BIT(9) | BIT(8) | + BIT(4) | BIT(0)); + /* ZQCS_ENABLE */ + if (sdram_params->emi_set.cona & 0x1) { + /* dual channel, clear ZQCSCNT */ + clrbits_le32(&ch[channel].ao_regs->spcmd, 0xff << 16); + /* set ZQCSMASK for different channels */ + if (channel == CHANNEL_A) { + clrbits_le32(&ch[channel].ao_regs->perfctl0, 0x1 << 24); + } else { + setbits_le32(&ch[channel].ao_regs->perfctl0, 0x1 << 24); + } + /* enable ZQCSDUAL */ + setbits_le32(&ch[channel].ao_regs->perfctl0, 0x1 << 25); + } else { + /* single channel, set ZQCSCNT */ + setbits_le32(&ch[channel].ao_regs->spcmd, 0x8 << 16); + } +} + +void transfer_to_spm_control(void) +{ + u32 msk; + + msk = BIT(7) | BIT(11) | BIT(15); + clrbits_le32(&mt8173_apmixed->ap_pll_con3, msk); + + msk = BIT(0) | BIT(4) | BIT(8); + clrbits_le32(&ch[CHANNEL_A].ddrphy_regs->peri[3], msk); + + msk = BIT(0) | BIT(8); + clrbits_le32(&ch[CHANNEL_B].ddrphy_regs->peri[3], msk); + + msk = BIT(0) | BIT(9) | BIT(10) | BIT(11) | BIT(16) | BIT(24); + clrbits_le32(&ch[CHANNEL_A].ddrphy_regs->peri[2], msk); + clrbits_le32(&ch[CHANNEL_B].ddrphy_regs->peri[2], msk); +} + +void transfer_to_reg_control(void) +{ + u32 val; + + val = BIT(7) | BIT(11) | BIT(15); + setbits_le32(&mt8173_apmixed->ap_pll_con3, val); + + val = BIT(0) | BIT(4) | BIT(8); + setbits_le32(&ch[CHANNEL_A].ddrphy_regs->peri[3], val); + + val = BIT(0) | BIT(8); + write32(&ch[CHANNEL_B].ddrphy_regs->peri[3], val); + + val = BIT(0) | BIT(9) | BIT(10) | BIT(11) | BIT(16) | BIT(24); + setbits_le32(&ch[CHANNEL_A].ddrphy_regs->peri[2], val); + setbits_le32(&ch[CHANNEL_B].ddrphy_regs->peri[2], val); +} + +u32 dramc_engine2(u32 channel, enum dram_tw_op wr, u32 test2_1, u32 test2_2, + u8 testaudpat, u8 log2loopcount) +{ + u32 value; + + if (log2loopcount > 15) + die("Invalid loopcount of engine2!"); + + /* Disable Test Agent1, Test Agent2 write/read */ + clrbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST1_EN | + CONF2_TEST2R_EN | + CONF2_TEST2W_EN); + + /* 1. set pattern, base address, offset address */ + write32(&ch[channel].nao_regs->test2_1, test2_1); + write32(&ch[channel].nao_regs->test2_2, test2_2); + + /* 2. select test pattern */ + /* TESTXTALKPAT | TESTAUDPAT + * ISI 0 | 0 + * AUD 0 | 1 + * XTALK 1 | 0 + * UNKNOW 1 | 1 + */ + switch (testaudpat) { + case XTALK: + /* TESTAUDPAT = 0 */ + clrbits_le32(&ch[channel].ao_regs->test2_3, + TEST2_3_TESTAUDPAT_EN); + /* TESTXTALKPAT = 1, select xtalk pattern + * TESTAUDMODE = 0, read only + * TESTAUDBITINV = 0, no bit inversion + */ + clrsetbits_le32(&ch[channel].ao_regs->test2_4, + TEST2_4_TESTAUDBITINV_EN | + TEST2_4_TESTAUDMODE_EN, + TEST2_4_TESTXTALKPAT_EN); + break; + case AUDIO: + /* TESTAUDPAT = 1 */ + setbits_le32(&ch[channel].ao_regs->test2_3, + TEST2_3_TESTAUDPAT_EN); + /* TESTXTALKPAT = 0 + * TESTAUDINIT = 0x11 + * TESTAUDINC = 0x0d + * TESTAUDBITINV = 1 + * TESTAUDMODE = 1 + */ + clrsetbits_le32(&ch[channel].ao_regs->test2_4, + TEST2_4_TESTXTALKPAT_EN | + TEST2_4_TESTAUDINIT_MASK | + TEST2_4_TESTAUDINC_MASK, + TEST2_4_TESTAUDMODE_EN | + TEST2_4_TESTAUDBITINV_EN | + 0x11 << TEST2_4_TESTAUDINIT_SHIFT | + 0xd << TEST2_4_TESTAUDINC_SHIFT); + + break; + case ISI: + /* TESTAUDPAT = 0 */ + clrbits_le32(&ch[channel].ao_regs->test2_3, + TEST2_3_TESTAUDPAT_EN); + /* TESTXTALKPAT = 0 */ + clrbits_le32(&ch[channel].ao_regs->test2_4, + TEST2_4_TESTXTALKPAT_EN); + } + + /* 3. set loop number */ + clrsetbits_le32(&ch[channel].ao_regs->test2_3, TEST2_3_TESTCNT_MASK, + log2loopcount << TEST2_3_TESTCNT_SHIFT); + + /* 4. enable read/write test */ + if (wr == TE_OP_READ_CHECK) { + if ((testaudpat == 1) || (testaudpat == 2)) { + /* if audio pattern, enable read only */ + /* (disable write after read), */ + /* AUDMODE=0x48[15]=0 */ + clrbits_le32(&ch[channel].ao_regs->test2_4, + TEST2_4_TESTAUDMODE_EN); + } + + /* enable read, 0x008[30:30] */ + setbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST2R_EN); + } else if (wr == TE_OP_WRITE_READ_CHECK) { + /* enable write, 0x008[31:31] */ + setbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST2W_EN); + + /* check "read data compare ready" bit */ + do { + value = read32(&ch[channel].nao_regs->testrpt); + } while ((value & (1 << TESTRPT_DM_CMP_CPT_SHIFT)) == 0); + + /* Disable Test Agent2 write and enable Test Agent2 read */ + clrbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST2W_EN); + setbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST2R_EN); + } + + /* 5 check "read data compare ready" bit */ + do { + value = read32(&ch[channel].nao_regs->testrpt); + } while ((value & (1 << TESTRPT_DM_CMP_CPT_SHIFT)) == 0); + + /* delay 10ns after ready check from DE suggestion (1us here) */ + udelay(1); + + /* read CMP_ERR result */ + value = read32(&ch[channel].nao_regs->cmp_err); + + /* 6 disable read */ + clrbits_le32(&ch[channel].ao_regs->conf2, CONF2_TEST2R_EN); + + /* return CMP_ERR result, pass: 0, failure: otherwise */ + return value; +} diff --git a/src/soc/mediatek/mt8173/dramc_pi_calibration_api.c b/src/soc/mediatek/mt8173/dramc_pi_calibration_api.c new file mode 100644 index 0000000000..1237b2e0ba --- /dev/null +++ b/src/soc/mediatek/mt8173/dramc_pi_calibration_api.c @@ -0,0 +1,1150 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u8 opt_gw_coarse_value[CHANNEL_NUM][DUAL_RANKS]; +static u8 opt_gw_fine_value[CHANNEL_NUM][DUAL_RANKS]; +static s8 wrlevel_dqs_dly[CHANNEL_NUM][DQS_NUMBER]; + +void sw_impedance_cal(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + u32 mask, value; + + const struct mt8173_calib_params *params = &sdram_params->calib_params; + + dramc_dbg_msg("[Imp Calibration] DRVP:%d\n", params->impedance_drvp); + dramc_dbg_msg("[Imp Calibration] DRVN:%d\n", params->impedance_drvn); + + mask = 0xf << 28 | 0xf << 24 | 0xf << 12 | 0xf << 8; /* driving */ + + value = params->impedance_drvp << 28 | params->impedance_drvn << 24 | + params->impedance_drvp << 12 | params->impedance_drvn << 8; + + /* DQS and DQ */ + clrsetbits_le32(&ch[channel].ao_regs->iodrv6, mask, value); + /* CLK and CMD */ + clrsetbits_le32(&ch[channel].ao_regs->drvctl1, mask, value); + clrsetbits_le32(&ch[channel].ddrphy_regs->drvctl1, mask, value); + /* DQ_2 and CMD_2 */ + clrsetbits_le32(&ch[channel].ao_regs->iodrv4, mask, value); + /* disable impcal calibration */ + clrbits_le32(&ch[channel].ao_regs->impcal, 1 << IMP_CALI_ENP_SHIFT | + 1 << IMP_CALI_ENN_SHIFT | + 1 << IMP_CALI_EN_SHIFT | + 0xf << IMP_CALI_DRVP_SHIFT | + 0xf << IMP_CALI_DRVN_SHIFT); +} + +void ca_training(u32 channel, const struct mt8173_sdram_params *sdram_params) +{ + const struct mt8173_calib_params *params = &sdram_params->calib_params; + + u32 i, ca_shift_avg32 = 0; + s8 ca_max_center, ca_shift_avg8 = 0, order, ca_shift[CATRAINING_NUM]; + + s8 shift[CATRAINING_NUM] = { + CMDDLY0_RA2_SHIFT, CMDDLY1_RA7_SHIFT, CMDDLY3_BA0_SHIFT, + CMDDLY3_BA1_SHIFT, CMDDLY3_BA2_SHIFT, CMDDLY4_RAS_SHIFT, + CMDDLY4_CAS_SHIFT, CMDDLY5_RA13_SHIFT, CMDDLY5_WE_SHIFT + }; + + s8 ca_order[CHANNEL_NUM][CATRAINING_NUM] = { + { 7, 5, 6, 1, 3, 0, 9, 8, 2, 4}, + { 2, 0, 3, 7, 5, 9, 4, 1, 6, 8} + }; + + s8 cmd_order[CATRAINING_NUM] = { + 0, 1, 3, 3, 3, 4, 4, 5, 5 + }; + + for(i = 0; i < CATRAINING_NUM; i++) { + ca_shift[i] = params->ca_train[channel][i]; + ca_shift_avg8 += ca_shift[i]; + } + + /* CA pins align the center */ + ca_max_center = params->ca_train_center[channel]; + + /* set CA pins output delay */ + for (i = 0; i < (CATRAINING_NUM - 1); i++) { + order = ca_order[channel][i]; + clrsetbits_le32(&ch[channel].ddrphy_regs->cmddly[cmd_order[i]], + 0xf << shift[i], ca_shift[order] << shift[i]); + } + + order = ca_order[channel][9]; + clrsetbits_le32(&ch[channel].ddrphy_regs->dqscal0, + 0xf << DQSCAL0_RA14_SHIFT, + ca_shift[order] << DQSCAL0_RA14_SHIFT); + + /* CKE and CS delay */ + ca_shift_avg32 = (u32)(ca_shift_avg8 + (CATRAINING_NUM >> 1)); + ca_shift_avg32 /= (u32) CATRAINING_NUM; + + /* CKEDLY */ + clrsetbits_le32(&ch[channel].ddrphy_regs->cmddly[4], + 0x1f << CMDDLY4_CS_SHIFT | + 0x1f << CMDDLY4_CKE_SHIFT, + ca_shift_avg32 << CMDDLY4_CS_SHIFT | + ca_shift_avg32 << CMDDLY4_CKE_SHIFT); + + /* CKE1DLY */ + clrsetbits_le32(&ch[channel].ao_regs->dqscal1, + 0x1f << DQSCAL1_CKE1_SHIFT, + ca_shift_avg32 << DQSCAL1_CKE1_SHIFT); + + /* CS1DLY */ + clrsetbits_le32(&ch[channel].ddrphy_regs->padctl1, + 0xf << PADCTL1_CS1_SHIFT, + ca_shift_avg32 << PADCTL1_CS1_SHIFT); + + /* set max center into clk output delay */ + clrsetbits_le32(&ch[channel].ddrphy_regs->padctl1, + 0xf << PADCTL1_CLK_SHIFT, + ca_max_center << PADCTL1_CLK_SHIFT); + + dramc_dbg_msg("=========================================\n"); + dramc_dbg_msg(" [Channel %d] CA training\n", channel); + dramc_dbg_msg("=========================================\n"); + + for (i = 0; i < CATRAINING_NUM; i++) + dramc_dbg_msg("[CA] CA %d\tShift %d\n", i, ca_shift[i]); + + dramc_dbg_msg("[CA] Reg CMDDLY4 = %xh\n", + read32(&ch[channel].ddrphy_regs->cmddly[4])); + dramc_dbg_msg("[CA] Reg DQSCAL1 = %xh\n", + read32(&ch[channel].ao_regs->dqscal1)); + dramc_dbg_msg("[CA] Reg PADCTL1 = %xh\n", + read32(&ch[channel].ddrphy_regs->padctl1)); +} + +void write_leveling(u32 channel, const struct mt8173_sdram_params *sdram_params) +{ + u8 i, byte_i; + u32 value; + + for (i = 0; i < DQS_NUMBER; i++) + wrlevel_dqs_dly[channel][i] = + sdram_params->calib_params.wr_level[channel][i]; + /* DQS */ + value = 0; + for (i = 0; i < DQS_NUMBER; i++) { + value += ((u32)wrlevel_dqs_dly[channel][i]) << (4 * i); + } + write32(&ch[channel].ddrphy_regs->padctl3, value); + + /* DQM */ + clrsetbits_le32(&ch[channel].ddrphy_regs->padctl2, MASK_PADCTL2_32BIT, + (value << PADCTL2_SHIFT) & MASK_PADCTL2_32BIT); + + /* DQ */ + for (byte_i = 0; byte_i < DQS_NUMBER; byte_i++) { + value = 0; + for (i = 0; i < DQS_BIT_NUMBER; i++) { + s8 val = wrlevel_dqs_dly[channel][byte_i]; + value += (((u32)val) << (4 * i)); + } + write32(&ch[channel].ddrphy_regs->dqodly[byte_i], value); + } + + dramc_dbg_msg("========================================\n"); + dramc_dbg_msg("[Channel %d] dramc_write_leveling_swcal\n", channel); + dramc_dbg_msg("========================================\n"); + + dramc_dbg_msg("[WL] DQS: %#x", + read32(&ch[channel].ddrphy_regs->padctl3)); + dramc_dbg_msg("[WL] DQM: %#x\n", + read32(&ch[channel].ddrphy_regs->padctl2)); + + for (byte_i = 0; byte_i < DQS_NUMBER; byte_i++) + dramc_dbg_msg("[WL] DQ byte%d: %#x\n", byte_i, + read32(&ch[channel].ddrphy_regs->dqodly[byte_i])); +} + +static void set_gw_coarse_factor(u32 channel, u8 curr_val) +{ + u8 curr_val_p1, selph2_dqsgate, selph2_dqsgate_p1; + + u32 coarse_tune_start = curr_val >> 2; + + if (coarse_tune_start > 3) { + coarse_tune_start -= 3; + } else { + if (coarse_tune_start) { + coarse_tune_start = 1; + } + } + + if (coarse_tune_start > 15) { + coarse_tune_start = 15; + } + + curr_val_p1 = curr_val + 2; /* diff is 0.5T */ + + /* Rank 0 P0/P1 coarse tune settings */ + clrsetbits_le32(&ch[channel].ao_regs->dqsctl1, + 0xf << DQSCTL1_DQSINCTL_SHIFT, + coarse_tune_start << DQSCTL1_DQSINCTL_SHIFT & + 0xf << DQSCTL1_DQSINCTL_SHIFT); + + /* DQSINCTL does not have P1. */ + /* Need to use TXDLY_DQSGATE/TXDLY_DQSGATE_P1 to set */ + /* different 1 M_CK coarse tune values for P0 & P1. */ + selph2_dqsgate = (curr_val >> 2) - coarse_tune_start; + selph2_dqsgate_p1 = (curr_val_p1 >> 2) - coarse_tune_start; + + clrsetbits_le32(&ch[channel].ao_regs->selph2, + 0x7 << SELPH2_TXDLY_DQSGATE_SHIFT | + 0x7 << SELPH2_TXDLY_DQSGATE_P1_SHIFT, + selph2_dqsgate << SELPH2_TXDLY_DQSGATE_SHIFT | + selph2_dqsgate_p1 << SELPH2_TXDLY_DQSGATE_P1_SHIFT); + + /* dly_DQSGATE and dly_DQSGATE_P1 */ + clrsetbits_le32(&ch[channel].ao_regs->selph5, + 0x3 << SELPH5_DLY_DQSGATE_SHIFT | + 0x3 << SELPH5_DLY_DQSGATE_P1_SHIFT, + (curr_val & 0x3) << SELPH5_DLY_DQSGATE_SHIFT | + (curr_val_p1 & 0x3) << SELPH5_DLY_DQSGATE_P1_SHIFT); +} + +static void set_gw_fine_factor(u32 channel, u8 curr_val, u8 rank) +{ + u32 set = curr_val & (0x7f << DQSIEN_DQS0IEN_SHIFT); + + clrsetbits_le32(&ch[channel].ao_regs->dqsien[rank], + 0x7f << DQSIEN_DQS0IEN_SHIFT | + 0x7f << DQSIEN_DQS1IEN_SHIFT | + 0x7f << DQSIEN_DQS2IEN_SHIFT | + 0x7f << DQSIEN_DQS3IEN_SHIFT, + set << DQSIEN_DQS0IEN_SHIFT | + set << DQSIEN_DQS1IEN_SHIFT | + set << DQSIEN_DQS2IEN_SHIFT | + set << DQSIEN_DQS3IEN_SHIFT); +} + +static void set_gw_coarse_factor_rank1(u32 channel, u8 curr_val, u8 dqsinctl) +{ + u8 curr_val_p1, r1dqsgate, r1dqsgate_p1; + + curr_val_p1 = curr_val + 2; /* diff is 0.5T */ + + clrsetbits_le32(&ch[channel].ao_regs->dqsctl2, + 0xf << DQSCTL2_DQSINCTL_SHIFT, + dqsinctl << DQSCTL2_DQSINCTL_SHIFT); + + /* TXDLY_R1DQSGATE and TXDLY_R1DQSGATE_P1 */ + r1dqsgate = (curr_val >> 2) - dqsinctl; + r1dqsgate_p1 = (curr_val_p1 >> 2) - dqsinctl; + + clrsetbits_le32(&ch[channel].ao_regs->selph6_1, + 0x7 << SELPH6_1_TXDLY_R1DQSGATE_SHIFT | + 0x7 << SELPH6_1_TXDLY_R1DQSGATE_P1_SHIFT, + r1dqsgate << SELPH6_1_TXDLY_R1DQSGATE_SHIFT | + r1dqsgate_p1 << SELPH6_1_TXDLY_R1DQSGATE_P1_SHIFT); + + /* dly_R1DQSGATE and dly_R1DQSGATE_P1 */ + clrsetbits_le32(&ch[channel].ao_regs->selph6_1, + 0x3 << SELPH6_1_DLY_R1DQSGATE_SHIFT | + 0x3 << SELPH6_1_DLY_R1DQSGATE_P1_SHIFT, + (curr_val & 0x3) << SELPH6_1_DLY_R1DQSGATE_SHIFT | + (curr_val_p1 & 0x3) << SELPH6_1_DLY_R1DQSGATE_P1_SHIFT); +} + +static void dqs_gw_counter_reset(u32 channel) +{ + /* reset dqs counter (1 to 0) */ + setbits_le32(&ch[channel].ao_regs->spcmd, 1 << SPCMD_DQSGCNTRST_SHIFT); + clrbits_le32(&ch[channel].ao_regs->spcmd, 1 << SPCMD_DQSGCNTRST_SHIFT); + dramc_phy_reset(channel); +} + +static int dqs_gw_test(u32 channel) +{ + u32 coarse_result01, coarse_result23; + + /* read data counter reset in PHY layer */ + dqs_gw_counter_reset(channel); + + /* use audio pattern to run the test */ + dramc_engine2(channel, TE_OP_READ_CHECK, DQS_GW_PATTERN2, + DQS_GW_PATTERN1 | DQS_GW_TE_OFFSET, 1, 0); + + /* get coarse result of DQS0, 1, 2, 3 */ + coarse_result01 = read32(&ch[channel].nao_regs->dqsgnwcnt[0]); + coarse_result23 = read32(&ch[channel].nao_regs->dqsgnwcnt[1]); + + if (coarse_result01 == DQS_GW_GOLD_COUNTER_32BIT && + coarse_result23 == DQS_GW_GOLD_COUNTER_32BIT) + return 1; + + return 0; +} + +static u8 dqs_gw_fine_tune_calib(u32 channel, u8 fine_val) +{ + u8 i, opt_fine_val; + s8 gw_ret[3], delta[3] = {0, -16, 16}; + + for (i = 0; i < 3; i++) { + /* adjust gw fine tune */ + opt_fine_val = fine_val + delta[i]; + set_gw_fine_factor(channel, opt_fine_val, 0); + /* get gw test result */ + gw_ret[i] = dqs_gw_test(channel); + } + + /* start fine tune adjustment from default fine value */ + opt_fine_val = fine_val; + + if (gw_ret[0] && gw_ret[1] && gw_ret[2]) { + opt_fine_val += ((delta[0] + delta[1] + delta[2]) / 3); + } + else if (gw_ret[0] && gw_ret[1]) { + opt_fine_val += ((delta[0] + delta[1]) / 2); + } + else if (gw_ret[0] && gw_ret[2]) { + opt_fine_val += ((delta[0] + delta[2]) / 2); + } + else { /* abnormal test result, set to default fine tune value */ + printk(BIOS_ERR, "[GW] ERROR, No found fine tune!!!\n"); + } + + return opt_fine_val; +} + +static u8 dqs_gw_coarse_tune_calib(u32 channel, u8 coarse_val) +{ + u8 i, opt_coarse_val[3]; + s8 gw_ret[3], delta[3] = {0, 1, -1}; + + for (i = 0; i < 3; i++) { + /* adjust gw coarse tune value */ + opt_coarse_val[i] = coarse_val + delta[i]; + set_gw_coarse_factor(channel, opt_coarse_val[i]); + /* get gw test result */ + gw_ret[i] = dqs_gw_test(channel); + /* judge test result */ + if (gw_ret[i] != 0) + return opt_coarse_val[i]; + } + + /* abnormal test result, set to default coarse tune value */ + printk(BIOS_ERR, "[GW] ERROR, No found coarse tune!!!\n"); + + return coarse_val; +} + +void rx_dqs_gating_cal(u32 channel, u8 rank, + const struct mt8173_sdram_params *sdram_params) +{ + u8 gw_coarse_val, gw_fine_val; + + /* disable HW gating */ + clrbits_le32(&ch[channel].ao_regs->dqscal0, + 1 << DQSCAL0_STBCALEN_SHIFT); + /* enable DQS gating window counter */ + setbits_le32(&ch[channel].ao_regs->dqsctl1, + 1 << DQSCTL1_DQSIENMODE_SHIFT); + setbits_le32(&ch[channel].ao_regs->spcmd, + 1 << SPCMD_DQSGCNTEN_SHIFT); + /* dual-phase DQS clock gating control enabling */ + setbits_le32(&ch[channel].ddrphy_regs->dqsgctl, + 1 << DQSGCTL_DQSGDUALP_SHIFT); + + /* gating calibration value */ + gw_coarse_val = sdram_params->calib_params.gating_win[channel][rank][0]; + gw_fine_val = sdram_params->calib_params.gating_win[channel][rank][1]; + + dramc_dbg_msg("****************************************************\n"); + dramc_dbg_msg("Channel %d Rank %d DQS GW Calibration\n", channel, rank); + dramc_dbg_msg("Default (coarse, fine) tune value %d, %d.\n", + gw_coarse_val, gw_fine_val); + dramc_dbg_msg("****************************************************\n"); + + /* set default coarse and fine value */ + set_gw_coarse_factor(channel, gw_coarse_val); + set_gw_fine_factor(channel, gw_fine_val, 0); + + /* adjust gw coarse tune */ + opt_gw_coarse_value[channel][rank] = + dqs_gw_coarse_tune_calib(channel, gw_coarse_val); + + /* set adjusted gw coarse tune */ + set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][rank]); + + /* adjust gw fine tune */ + opt_gw_fine_value[channel][rank] = + dqs_gw_fine_tune_calib(channel, gw_fine_val); + + /* set adjusted gw fine tune */ + set_gw_fine_factor(channel, opt_gw_fine_value[channel][rank], 0); + + /* read data counter reset in PHY layer */ + dqs_gw_counter_reset(channel); + + /* gating window training result */ + printk(BIOS_INFO, "[GW] [Channel %d] [Rank %d] adjusted (coarse, fine) tune value: %d, %d.\n", + channel, rank, opt_gw_coarse_value[channel][rank], + opt_gw_fine_value[channel][rank]); +} + +void dual_rank_rx_dqs_gating_cal(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + u32 dqsinctl; + + /* rank 0 gw calibration */ + rx_dqs_gating_cal(channel, 0, sdram_params); + + /* get dqsinctl after rank 0 calibration */ + dqsinctl = read32(&ch[channel].ao_regs->dqsctl1); + dqsinctl = (dqsinctl >> DQSCTL1_DQSINCTL_SHIFT) & (0xf << 0); + + /* swap cs0 and cs1 */ + setbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN); + + /* rank 1 gw calibration */ + rx_dqs_gating_cal(channel, 1, sdram_params); + + /* set rank 1 coarse tune and fine tune */ + set_gw_coarse_factor_rank1(channel, opt_gw_coarse_value[channel][1], + dqsinctl); + set_gw_fine_factor(channel, opt_gw_fine_value[channel][1], 1); + + /* swap cs back */ + clrbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN); + + /* set rank 0 coarse tune and fine tune back */ + set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][0]); + set_gw_fine_factor(channel, opt_gw_fine_value[channel][0], 0); +} + +void dramc_rankinctl_config(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + u32 value; + + if (is_dual_rank(channel, sdram_params)) { + /* RANKINCTL_ROOT1 = DQSINCTL + reg_TX_DLY_DQSGATE */ + value = min(opt_gw_coarse_value[channel][0], + opt_gw_coarse_value[channel][1]) >> 2; + + clrsetbits_le32(&ch[channel].ao_regs->dummy, 0xf, value); + + /* RANKINCTL = RANKINCTL_ROOT1 */ + clrsetbits_le32(&ch[channel].ao_regs->dqscal1, + 0xf << 16, value << 16); + } + /* disable per-bank refresh when refresh rate >= 5 */ + setbits_le32(&ch[channel].ao_regs->rkcfg, + 1 << RKCFG_PBREF_DISBYRATE_SHIFT); +} + +u32 dram_k_perbit(u32 channel) +{ + u32 err_value = 0x0; + + /* use XTALK pattern to run the test */ + err_value = dramc_engine2(channel, TE_OP_WRITE_READ_CHECK, + DEFAULT_TEST2_1_CAL, DEFAULT_TEST2_2_CAL, + 2, 0); + return err_value; +} + +void dramk_check_dqs_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step, + u32 fail_bit) +{ + s8 dqsdly_pass_win, best_pass_win; + + if (fail_bit == 0) { + if (p->first_dqsdly_pass == -1) { + /* first DQS pass delay tap */ + p->first_dqsdly_pass = dly_step; + } + if ((p->last_dqsdly_pass == -2) && (dly_step == last_step)) { + /* pass to the last tap */ + p->last_dqsdly_pass = dly_step; + dqsdly_pass_win = p->last_dqsdly_pass - + p->first_dqsdly_pass; + best_pass_win = p->best_last_dqsdly_pass - + p->best_first_dqsdly_pass; + if (dqsdly_pass_win > best_pass_win) { + p->best_last_dqsdly_pass = p->last_dqsdly_pass; + p->best_first_dqsdly_pass = p->first_dqsdly_pass; + } + /* clear to find the next pass range if it has */ + p->first_dqsdly_pass = -1; + p->last_dqsdly_pass = -2; + } + } else { + if ((p->first_dqsdly_pass != -1) && (p->last_dqsdly_pass == -2)) { + p->last_dqsdly_pass = dly_step - 1; + dqsdly_pass_win = p->last_dqsdly_pass - + p->first_dqsdly_pass; + best_pass_win = p->best_last_dqsdly_pass - + p->best_first_dqsdly_pass; + if (dqsdly_pass_win > best_pass_win) { + p->best_last_dqsdly_pass = p->last_dqsdly_pass; + p->best_first_dqsdly_pass = p->first_dqsdly_pass; + } + /* clear to find the next pass range if it has */ + p->first_dqsdly_pass = -1; + p->last_dqsdly_pass = -2; + } + } +} + +void dramk_check_dq_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step, + u32 fail_bit) +{ + s8 dqdly_pass_win, best_pass_win; + + if (fail_bit == 0) { + if (p->first_dqdly_pass == -1) { + /* first DQ pass delay tap */ + p->first_dqdly_pass = dly_step; + } + + if ((p->last_dqdly_pass == -2) && (dly_step == last_step)) { + /* pass to the last tap */ + p->last_dqdly_pass = dly_step; + dqdly_pass_win = p->last_dqdly_pass - + p->first_dqdly_pass; + best_pass_win = p->best_last_dqdly_pass - + p->best_first_dqdly_pass; + if (dqdly_pass_win > best_pass_win) { + p->best_last_dqdly_pass = p->last_dqdly_pass; + p->best_first_dqdly_pass = p->first_dqdly_pass; + } + /* clear to find the next pass range if it has */ + p->first_dqdly_pass = -1; + p->last_dqdly_pass = -2; + } + } else { + if ((p->first_dqdly_pass != -1) && (p->last_dqdly_pass == -2)) { + p->last_dqdly_pass = dly_step - 1; + dqdly_pass_win = p->last_dqdly_pass - + p->first_dqdly_pass; + best_pass_win = p->best_last_dqdly_pass - + p->best_first_dqdly_pass; + if (dqdly_pass_win > best_pass_win) { + p->best_last_dqdly_pass = p->last_dqdly_pass; + p->best_first_dqdly_pass = p->first_dqdly_pass; + } + /* clear to find the next pass range if it has */ + p->first_dqdly_pass = -1; + p->last_dqdly_pass = -2; + } + } +} + +u8 dramk_calcu_best_dly(u8 bit, struct dqs_perbit_dly *p, u8 *p_max_byte) +{ + u8 fail = 0; + u8 hold, setup; + + /* hold time = DQS pass taps */ + hold = p->best_last_dqsdly_pass - p->best_first_dqsdly_pass + 1; + /* setup time = DQ pass taps */ + setup = p->best_last_dqdly_pass - p->best_first_dqdly_pass + 1; + + /* The relationship of setup and hold time of dqs and dq signals + * is represented with delay tap in the following format: + * + * setup time(dq delay) hold time(dqs delay) + * xxxxxxxxxxxxxoooooooo|ooooooooooooooooooooxxxxx + * 15 0 1 15 tap + */ + + if (hold > setup) { + /* like this: (setup time != 0) */ + /* xxxxxxxxxxxxxoooooooo|ooooooooooooooooooooxxxxx */ + /* like this: (setup time == 0) */ + /* xxxxxxxxxxxxxxxxxxxxx|xxxooooooooooxxxxxxxxxxxx */ + + p->best_dqdly = 0; + p->best_dqsdly = (setup != 0)? (hold - setup) / 2: + (hold - setup) / 2 + p->best_first_dqsdly_pass; + + if (p->best_dqsdly > *p_max_byte) { + *p_max_byte = p->best_dqsdly; + } + + } else if (hold < setup) { + /* like this: (hold time != 0 )*/ + /* xxxoooooooooooooooooo|ooooooooxxxxxxxxxxxxxxxxx */ + /* like this: (hold time == 0 ) */ + /* xxxoooooooooooooooxxx|xxxxxxxxxxxxxxxxxxxxxxxxx */ + + p->best_dqsdly = 0; + p->best_dqdly = (hold != 0)? (setup - hold) / 2: + (setup - hold) / 2 + p->best_first_dqdly_pass; + + } else { /* hold time == setup time */ + p->best_dqsdly = 0; + p->best_dqdly = 0; + + if (hold == 0) { + /* like this: (mean this bit is error) */ + /* xxxxxxxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxxxxxxxxxxx */ + printk(BIOS_ERR, "ERROR, error bit %d, " + "setup_time = hold_time = 0!!\n", bit); + fail = 1; + } + } + + dramc_dbg_msg("bit#%d : dq =%d dqs=%d win=%d (%d, %d)\n", + bit, setup, hold, setup + hold, + p->best_dqdly, p->best_dqsdly); + + return fail; +} + +void clk_duty_cal(u32 channel) +{ + u8 max_duty_sel, max_duty; + u32 max_win_size = 0; + + max_duty_sel = max_duty = 1; + + clrsetbits_le32(&ch[channel].ddrphy_regs->phyclkduty, + 0x3 << PHYCLKDUTY_CMDCLKP0DUTYN_SHIFT | + 1 << PHYCLKDUTY_CMDCLKP0DUTYP_SHIFT, + 1 << PHYCLKDUTY_CMDCLKP0DUTYSEL_SHIFT | + max_duty << PHYCLKDUTY_CMDCLKP0DUTYN_SHIFT); + + max_win_size = read32(&ch[channel].ddrphy_regs->phyclkduty); + + dramc_dbg_msg("[Channel %d CLK DUTY CALIB] ", channel); + dramc_dbg_msg("Final DUTY_SEL=%d, DUTY=%d, rx window size=%d\n", + max_duty_sel, max_duty, max_win_size); +} + +static void set_dle_factor(u32 channel, u8 curr_val) +{ + clrsetbits_le32(&ch[channel].ao_regs->ddr2ctl, + 0x7 << DDR2CTL_DATLAT_SHIFT, + (curr_val & 0x7) << DDR2CTL_DATLAT_SHIFT); + + clrsetbits_le32(&ch[channel].ao_regs->padctl4, + 0x1 << PADCTL4_DATLAT3_SHIFT, + ((curr_val >> 3) & 0x1) << PADCTL4_DATLAT3_SHIFT); + + clrsetbits_le32(&ch[channel].ao_regs->phyctl1, + 0x1 << PHYCTL1_DATLAT4_SHIFT, + ((curr_val >> 4) & 0x1) << PHYCTL1_DATLAT4_SHIFT); + + clrsetbits_le32(&ch[channel].ao_regs->misc, + 0x1f << MISC_DATLAT_DSEL_SHIFT, + (curr_val - 8) << MISC_DATLAT_DSEL_SHIFT); + + /* optimize bandwidth for HW run time test engine use */ + clrsetbits_le32(&ch[channel].ao_regs->misc, + 0x1f << MISC_LATNORMP_SHIFT, + (curr_val - 3) << MISC_LATNORMP_SHIFT); +} + +void dual_rank_rx_datlat_cal(u32 channel, + const struct mt8173_sdram_params *sdram_params) +{ + u8 r0_dle_setting, r1_dle_setting; + + /* rank 0 dle calibration */ + r0_dle_setting = rx_datlat_cal(channel, 0, sdram_params); + + /* swap cs0 and cs1 */ + setbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN); + + /* set rank 1 coarse tune and fine tune back */ + set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][1]); + set_gw_fine_factor(channel, opt_gw_fine_value[channel][1], 0); + + /* rank 1 dle calibration */ + r1_dle_setting = rx_datlat_cal(channel, 1, sdram_params); + + /* set rank 0 coarse tune and fine tune back */ + set_gw_coarse_factor(channel, opt_gw_coarse_value[channel][0]); + set_gw_fine_factor(channel, opt_gw_fine_value[channel][0], 0); + + /* swap cs back */ + clrbits_le32(&ch[channel].ao_regs->rkcfg, MASK_RKCFG_RKSWAP_EN); + + /* output dle setting of rank 0 and 1 */ + dramc_dbg_msg("[DLE] Rank 0 DLE calibrated setting = %xh.\n" + "[DLE] Rank 1 DLE calibrated setting = %xh.\n", + r0_dle_setting, r1_dle_setting); + + if (r1_dle_setting < r0_dle_setting) { + /* compare dle setting of two ranks */ + dramc_dbg_msg("[DLE] rank 0 > rank 1. set to rank 0.\n"); + /* case 1: set rank 0 dle setting */ + set_dle_factor(channel, r0_dle_setting); + } else { + /* compare dle setting of two ranks */ + dramc_dbg_msg("[DLE] rank 0 < rank 1. use rank 1.\n"); + /* case 2: set rank 1 dle setting */ + set_dle_factor(channel, r1_dle_setting); + } +} + +u8 rx_datlat_cal(u32 channel, u8 rank, + const struct mt8173_sdram_params *sdram_params) +{ + u8 i, best_step; + u32 err[DLE_TEST_NUM]; + + dramc_dbg_msg("=========================================\n"); + dramc_dbg_msg("[Channel %d] [Rank %d] DATLAT calibration\n", + channel, rank); + dramc_dbg_msg("=========================================\n"); + + clrbits_le32(&ch[channel].ao_regs->mckdly, + 0x11 << MCKDLY_DQIENQKEND_SHIFT | + 0x1 << MCKDLY_DQIENLAT_SHIFT); + + /* set dle calibration initial value */ + best_step = sdram_params->calib_params.datlat_ucfirst + 1; + + /* do dle calibration test */ + for (i = 0; i < DLE_TEST_NUM; i++) { + set_dle_factor(channel, best_step - i); + err[i] = dramc_engine2(channel, TE_OP_WRITE_READ_CHECK, + DEFAULT_TEST2_1_CAL, + DEFAULT_TEST2_2_CAL, 2, 0); + } + + if (err[0]) { + /* dle test error */ + printk(BIOS_ERR, "[DLE] calibration ERROR!\n"); + } else { + /* judge dle test result */ + for (i = 0; i < DLE_TEST_NUM; i++) { + if (!err[i] && (i + 1 == DLE_TEST_NUM || err[i + 1])) { + /* dle test ok */ + best_step -= (i - 1); + break; + } + } + } + + /* Default dle value is set when test error (error recovery). + * Others, adjusted dle calibration value is set normally. + */ + set_dle_factor(channel, best_step); + + dramc_dbg_msg("[DLE] adjusted value = %#x\n", best_step); + + return best_step; +} + +void tx_delay_for_wrleveling(u32 channel, + struct dqs_perbit_dly *dqdqs_perbit_dly, + u8 *max_dqsdly_byte, u8 *ave_dqdly_byte) +{ + s8 i, delta, index, max_taps; + + max_taps = MAX_DQDLY_TAPS - 1; + + for (i = 0; i < DATA_WIDTH_32BIT; i++) { + + index = i / DQS_BIT_NUMBER; + + if (i % DQS_BIT_NUMBER == 0) + dramc_dbg_msg("DQS%d: %d \n", index, + wrlevel_dqs_dly[channel][index]); + + if (max_dqsdly_byte[index] <= wrlevel_dqs_dly[channel][index]) { + /* set diff value (delta) */ + delta = wrlevel_dqs_dly[channel][index] - + max_dqsdly_byte[index]; + + dqdqs_perbit_dly[i].best_dqdly += delta; + + /* max limit to 15 */ + if (dqdqs_perbit_dly[i].best_dqdly > max_taps) + dqdqs_perbit_dly[i].best_dqdly = max_taps; + + if ((i + 1) % DQS_BIT_NUMBER == 0) { + /* DQS */ + max_dqsdly_byte[index] = + wrlevel_dqs_dly[channel][index]; + /* DQM */ + ave_dqdly_byte[index] += delta; + /* max limit to 15 */ + if (ave_dqdly_byte[index] > max_taps) + ave_dqdly_byte[index] = max_taps; + } + + } else if (i % DQS_BIT_NUMBER == 0) { + /* max_dqsdly_byte[j] > wrlevel_dqs_dly[channel][j] + * Originally, we should move clk and CA delay. + * Then, do GW calibration again. However, DQ/DQS + * skew should not be large in MT8173, so we sacrifice + * the Clk/DQS margin by keeping the clk out delay. + */ + printk(BIOS_ERR, "[Warning] DQSO %d in TX " + "per-bit = %d > DQSO %d in WL = %d ", + index, max_dqsdly_byte[index], index, + wrlevel_dqs_dly[channel][index]); + } + } +} + +static void set_rx_dly_factor(u32 channel, u32 curr_val, u8 type) +{ + u32 i, value = 0; + + for (i = 0; i < DQS_NUMBER; i++) + value += (curr_val << (8 * i)); + + switch (type) { + case RX_DQS: + write32(&ch[channel].ao_regs->r0deldly, value); + break; + case RX_DQ: + for (i = 0; i < DATA_WIDTH_32BIT; i += 4) + write32(&ch[channel].ao_regs->dqidly[i/4], value); + break; + } +} + +static void set_tx_dly_factor(u32 channel, u32 curr_val, u8 type) +{ + u32 i, bit_num, value = 0; + + bit_num = (type == TX_DQ)? DQS_BIT_NUMBER: DQS_NUMBER; + + for (i = 0; i < bit_num; i++) + value += (curr_val << (4 * i)); + + switch (type) { + + case TX_DQS: + write32(&ch[channel].ddrphy_regs->padctl3, value); + break; + case TX_DQM: + write32(&ch[channel].ddrphy_regs->padctl2, value); + break; + case TX_DQ: + for (i = 0; i < DQS_NUMBER; i++) + write32(&ch[channel].ddrphy_regs->dqodly[i], value); + break; + } +} + +static void set_dly_factor(u32 channel, u8 stage, u8 type, u8 dly) +{ + switch (stage | type << 1) { + /* set delay for DQ/DQM/DQS by setup/hold stage and window type */ + case STAGE_SETUP_TX_WIN: + /* set DQ/DQM delay for tx window */ + set_tx_dly_factor(channel, dly, TX_DQ); + set_tx_dly_factor(channel, dly, TX_DQM); + break; + case STAGE_SETUP_RX_WIN: + /* set DQ delay for rx window */ + set_rx_dly_factor(channel, dly, RX_DQ); + break; + case STAGE_HOLD_TX_WIN: + /* set DQS delay for tx window */ + set_tx_dly_factor(channel, dly, TX_DQS); + break; + case STAGE_HOLD_RX_WIN: + /* set DQS delay for rx window */ + set_rx_dly_factor(channel, dly, RX_DQS); + break; + } +} + +static void set_rx_best_dly_factor(u32 channel, + struct dqs_perbit_dly *dqdqs_perbit_dly, + u8 *max_dqsdly_byte) +{ + u32 i, value = 0; + + for (i = 0; i < DQS_NUMBER; i++) + value += (((u32)max_dqsdly_byte[i]) << (8 * i)); + + write32(&ch[channel].ao_regs->r0deldly, value); + write32(&ch[channel].ao_regs->r1deldly, value); + + dramc_dbg_msg("[RX] DQS Reg R0DELDLY=%xh\n", + read32(&ch[channel].ao_regs->r0deldly)); + dramc_dbg_msg("[RX] DQS Reg R1DELDLY=%xh\n", + read32(&ch[channel].ao_regs->r1deldly)); + + for (i = 0; i < DATA_WIDTH_32BIT; i += 4) { + /* every 4bit dq have the same delay register address */ + value = ((u32)dqdqs_perbit_dly[i].best_dqdly) + + (((u32)dqdqs_perbit_dly[i + 1].best_dqdly) << 8) + + (((u32)dqdqs_perbit_dly[i + 2].best_dqdly) << 16) + + (((u32)dqdqs_perbit_dly[i + 3].best_dqdly) << 24); + + write32(&ch[channel].ao_regs->dqidly[i / 4], value); + dramc_dbg_msg("[RX] DQ DQIDLY%d = %xh\n", (i + 4) / 4, value); + } +} + +static void set_tx_best_dly_factor(u32 channel, + struct dqs_perbit_dly *dqdqs_perbit_dly, + u8 *max_dqsdly_byte, u8 *ave_dqdly_byte) +{ + u32 bit, value, shift, dqs_index = 0; + + value = 0; + for (bit = 0; bit < DQS_NUMBER; bit++) { + value += (((u32)max_dqsdly_byte[bit]) << (4 * bit)); + } + + write32(&ch[channel].ddrphy_regs->padctl3, value); + dramc_dbg_msg("[TX] DQS PADCTL3 Reg = %#x\n", value); + + /* DQ delay */ + for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) { + /* every 8 DQ reset */ + if (bit % DQS_BIT_NUMBER == 0) { + value = 0; + dqs_index = bit / DQS_BIT_NUMBER; + } + /* 4 bits field for each DQ */ + shift = 4 * (bit % DQS_BIT_NUMBER); + value += (((u32)(dqdqs_perbit_dly[bit].best_dqdly)) << shift); + /* each register is with 8 DQ */ + if ((bit + 1) % DQS_BIT_NUMBER == 0) { + write32(&ch[channel].ddrphy_regs->dqodly[dqs_index], value); + dramc_dbg_msg("[TX] DQ DQ0DLY%d = %xh\n", + dqs_index + 1, value); + } + } + + /* DQM delay */ + value = read32(&ch[channel].ddrphy_regs->padctl2); + value &= MASK_PADCTL2; + + for (bit = 0; bit < DQS_NUMBER; bit++) { + value += (((u32)ave_dqdly_byte[bit]) << (4 * bit)); + } + write32(&ch[channel].ddrphy_regs->padctl2, value); + dramc_dbg_msg("[TX] DQM PADCTL2 Reg = %#x\n", value); +} + +void perbit_window_cal(u32 channel, u8 type) +{ + u8 i, dly, bit, max_dqs_taps, fail = 0; + u8 max_dqsdly_byte[DQS_NUMBER], ave_dqdly_byte[DQS_NUMBER]; + u32 err_value, fail_bit, max_limit, index; + + struct dqs_perbit_dly dqdqs_perbit_dly[DQ_DATA_WIDTH]; + + dramc_dbg_msg("\n[Channel %d] %s DQ/DQS per bit :\n", + channel, (type == TX_WIN)? "TX": "RX"); + + if (type == TX_WIN) + dramc_phy_reset(channel); + + for (i = 0; i < DATA_WIDTH_32BIT; i++) { + dqdqs_perbit_dly[i].first_dqdly_pass = -1; + dqdqs_perbit_dly[i].last_dqdly_pass = -2; + dqdqs_perbit_dly[i].first_dqsdly_pass = -1; + dqdqs_perbit_dly[i].last_dqsdly_pass = -2; + dqdqs_perbit_dly[i].best_first_dqdly_pass = -1; + dqdqs_perbit_dly[i].best_last_dqdly_pass = -2; + dqdqs_perbit_dly[i].best_first_dqsdly_pass = -1; + dqdqs_perbit_dly[i].best_last_dqsdly_pass = -2; + } + + /* 1. delay DQ ,find the pass widnow (left boundary) + * 2. delay DQS find the pass window (right boundary) + * 3. find the best DQ / DQS to satify the middle value + * of the overall pass window per bit + * 4. set DQS delay to the max per byte, delay DQ to de-skew + */ + + /* 1. set DQS delay to 0 first */ + set_dly_factor(channel, STAGE_HOLD, type, FIRST_DQS_DELAY); + + dramc_dbg_msg("----------------------------------" + "--------------------\n"); + dramc_dbg_msg("Start DQ delay to find pass range," + "DQS delay fixed to %#x...\n", FIRST_DQS_DELAY); + dramc_dbg_msg("----------------------------------" + "-------------------\n"); + dramc_dbg_msg("x-axis is bit #; y-axis is DQ delay (%d~%d)\n", + FIRST_DQ_DELAY, MAX_DQDLY_TAPS - 1); + + /* delay DQ from 0 to 15 to get the setup time */ + for (dly = FIRST_DQ_DELAY; dly < MAX_DQDLY_TAPS; dly++) { + + set_dly_factor(channel, STAGE_SETUP, type, dly); + err_value = dram_k_perbit(channel); + + /* check fail bit, 0 ok, others fail */ + for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) { + fail_bit = err_value & ((u32)1 << bit); + dramk_check_dq_win(&(dqdqs_perbit_dly[bit]), dly, + MAX_DQDLY_TAPS - 1, fail_bit); + if (fail_bit == 0) { + dramc_dbg_msg("o"); + } else { + dramc_dbg_msg("x"); + } + } + dramc_dbg_msg("\n"); + } + + /* 2. set DQ delay to 0 */ + set_dly_factor(channel, STAGE_SETUP, type, FIRST_DQ_DELAY); + + /* DQS delay taps: tx and rx are 16 and 64 taps */ + max_dqs_taps = (type == TX_WIN)? MAX_TX_DQSDLY_TAPS: MAX_RX_DQSDLY_TAPS; + + dramc_dbg_msg("-----------------------------------" + "-------------------\n"); + dramc_dbg_msg("Start DQS delay to find pass range," + "DQ delay fixed to %#x...\n", FIRST_DQ_DELAY); + dramc_dbg_msg("------------------------------------" + "------------------\n"); + dramc_dbg_msg("x-axis is bit #; y-axis is DQS delay (%d~%d)\n", + FIRST_DQS_DELAY + 1, max_dqs_taps - 1); + + /* delay DQS to get the hold time, dq_dly = dqs_dly = 0 is counted */ + /* when we delay dq, so we set first dqs delay to 1 */ + for (dly = (FIRST_DQS_DELAY + 1); dly < max_dqs_taps; dly++) { + + set_dly_factor(channel, STAGE_HOLD, type, dly); + err_value = dram_k_perbit(channel); + + /* check fail bit, 0 ok, others fail */ + for (bit = 0; bit < DATA_WIDTH_32BIT; bit++) { + fail_bit = err_value & ((u32)1 << bit); + dramk_check_dqs_win(&(dqdqs_perbit_dly[bit]), dly, + max_dqs_taps - 1, fail_bit); + if (fail_bit == 0) { + dramc_dbg_msg("o"); + } else { + dramc_dbg_msg("x"); + } + } + dramc_dbg_msg("\n"); + } + + /* 3 calculate dq and dqs time */ + dramc_dbg_msg("-------------------------------" + "-----------------------\n"); + dramc_dbg_msg("Start calculate dq time and dqs " + "time:\n"); + dramc_dbg_msg("Find max DQS delay per byte / " + "Adjust DQ delay to align DQS...\n"); + dramc_dbg_msg("--------------------------------" + "----------------------\n"); + + /* As per byte, check max DQS delay in 8-bit. + * Except for the bit of max DQS delay, delay + * DQ to fulfill setup time = hold time + */ + for (i = 0; i < DQS_NUMBER; i++) { + max_dqsdly_byte[i] = 0; + ave_dqdly_byte[i] = 0; + } + + for (i = 0; i < DATA_WIDTH_32BIT; i++) { + /* we delay DQ or DQS to let DQS sample the middle */ + /* of tx/rx pass window for all the 8 bits */ + index = i / DQS_BIT_NUMBER; + fail |= dramk_calcu_best_dly(i, &dqdqs_perbit_dly[i], + &max_dqsdly_byte[index]); + + if ((i + 1) % DQS_BIT_NUMBER == 0) + dramc_dbg_msg("----separate line----\n"); + } + + for (i = 0; i < DATA_WIDTH_32BIT; i++) { + /* dqs index for every 8-bit */ + index = i / DQS_BIT_NUMBER; + /* set DQS to max for 8-bit */ + if (dqdqs_perbit_dly[i].best_dqsdly < max_dqsdly_byte[index]) { + /* delay DQ to compensate extra DQS delay */ + dly = max_dqsdly_byte[index] - + dqdqs_perbit_dly[i].best_dqsdly; + dqdqs_perbit_dly[i].best_dqdly += dly; + /* max limit to 15 */ + max_limit = MAX_DQDLY_TAPS - 1; + if (dqdqs_perbit_dly[i].best_dqdly > max_limit) { + dqdqs_perbit_dly[i].best_dqdly = max_limit; + } + } + /* accumulation variable for TX DQM */ + ave_dqdly_byte[index] += dqdqs_perbit_dly[i].best_dqdly; + /* take the average of DQ for TX DQM */ + if ((i + 1) % DQS_BIT_NUMBER == 0) { + ave_dqdly_byte[index] /= DQS_BIT_NUMBER; + } + } + + if (fail == 1) /* error handling */ + die("fail on perbit_window_cal()\n"); + + dramc_dbg_msg("==================================================\n"); + dramc_dbg_msg(" dramc_perbit_window_swcal:\n"); + dramc_dbg_msg(" channel=%d(0:cha, 1:chb)\n", channel); + dramc_dbg_msg(" bus width=%d\n", DATA_WIDTH_32BIT); + dramc_dbg_msg("==================================================\n"); + dramc_dbg_msg("DQS Delay :\n DQS0 = %d DQS1 = %d DQS2 = %d DQS3 = %d\n", + max_dqsdly_byte[0], max_dqsdly_byte[1], + max_dqsdly_byte[2], max_dqsdly_byte[3]); + + if (type == TX_WIN) + dramc_dbg_msg("DQM Delay :\n" + "DQM0 = %d DQM1 = %d DQM2 = %d DQM3 = %d\n", + ave_dqdly_byte[0], ave_dqdly_byte[1], + ave_dqdly_byte[2], ave_dqdly_byte[3]); + + dramc_dbg_msg("DQ Delay :\n"); + for (i = 0; i < DATA_WIDTH_32BIT; i++) { + dramc_dbg_msg("DQ%d = %d ", i, dqdqs_perbit_dly[i].best_dqdly); + if ( ((i + 1) % 4) == 0) + dramc_dbg_msg("\n"); + } + + dramc_dbg_msg("____________________________________" + "____________________________________\n"); + + if (type == TX_WIN) { + /* Add CLK to DQS/DQ skew after write leveling */ + dramc_dbg_msg("Add CLK to DQS/DQ skew based on write leveling.\n"); + /* this subroutine add clk delay to DQS/DQ after WL */ + tx_delay_for_wrleveling(channel, dqdqs_perbit_dly, + max_dqsdly_byte, ave_dqdly_byte); + } + + if (type == TX_WIN) + set_tx_best_dly_factor(channel, dqdqs_perbit_dly, + max_dqsdly_byte, ave_dqdly_byte); + else + set_rx_best_dly_factor(channel, dqdqs_perbit_dly, + max_dqsdly_byte); + + dramc_phy_reset(channel); +} diff --git a/src/soc/mediatek/mt8173/emi.c b/src/soc/mediatek/mt8173/emi.c new file mode 100644 index 0000000000..f6ef40f8f3 --- /dev/null +++ b/src/soc/mediatek/mt8173/emi.c @@ -0,0 +1,142 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct emi_regs *emi_regs = (void *)EMI_BASE; + +static void dram_vcore_adjust(void) +{ + /* options: Vcore_HV_LPPDR3/Vcore_NV_LPPDR3/Vcore_LV_LPPDR3 */ + mt6391_write(PMIC_RG_VCORE_CON9, Vcore_NV_LPPDR3, 0x7F, 0); + mt6391_write(PMIC_RG_VCORE_CON10, Vcore_NV_LPPDR3, 0x7F, 0); +} + +static void dram_vmem_adjust(void) +{ + /* options: Vmem_HV_LPPDR3/Vmem_NV_LPPDR3/Vmem_LV_LPPDR3 */ + mt6391_write(PMIC_RG_VDRM_CON9, Vmem_NV_LPDDR3, 0x7F, 0); + mt6391_write(PMIC_RG_VDRM_CON10, Vmem_NV_LPDDR3, 0x7F, 0); +} + +static void emi_init(const struct mt8173_sdram_params *sdram_params) +{ + /* EMI setting initialization */ + write32(&emi_regs->emi_conf, sdram_params->emi_set.conf); + write32(&emi_regs->emi_conm, sdram_params->emi_set.conm_1); + write32(&emi_regs->emi_arbi, sdram_params->emi_set.arbi); + write32(&emi_regs->emi_arba, sdram_params->emi_set.arba); + write32(&emi_regs->emi_arbc, sdram_params->emi_set.arbc); + write32(&emi_regs->emi_arbd, sdram_params->emi_set.arbd); + write32(&emi_regs->emi_arbe, sdram_params->emi_set.arbe); + write32(&emi_regs->emi_arbf, sdram_params->emi_set.arbf); + write32(&emi_regs->emi_arbg, sdram_params->emi_set.arbg); + write32(&emi_regs->emi_arbj, sdram_params->emi_set.arbj); + write32(&emi_regs->emi_cona, sdram_params->emi_set.cona); + write32(&emi_regs->emi_testd, sdram_params->emi_set.testd); + write32(&emi_regs->emi_bmen, sdram_params->emi_set.bmen); + write32(&emi_regs->emi_conb, sdram_params->emi_set.conb); + write32(&emi_regs->emi_conc, sdram_params->emi_set.conc); + write32(&emi_regs->emi_cond, sdram_params->emi_set.cond); + write32(&emi_regs->emi_cone, sdram_params->emi_set.cone); + write32(&emi_regs->emi_cong, sdram_params->emi_set.cong); + write32(&emi_regs->emi_conh, sdram_params->emi_set.conh); + write32(&emi_regs->emi_slct, sdram_params->emi_set.slct_1); + write32(&emi_regs->emi_mdct, sdram_params->emi_set.mdct_1); + write32(&emi_regs->emi_arbk, sdram_params->emi_set.arbk); + write32(&emi_regs->emi_testc, sdram_params->emi_set.testc); + write32(&emi_regs->emi_mdct, sdram_params->emi_set.mdct_2); + write32(&emi_regs->emi_testb, sdram_params->emi_set.testb); + write32(&emi_regs->emi_slct, sdram_params->emi_set.slct_2); + write32(&emi_regs->emi_conm, sdram_params->emi_set.conm_2); + write32(&emi_regs->emi_test0, sdram_params->emi_set.test0); + write32(&emi_regs->emi_test1, sdram_params->emi_set.test1); +} + +static void do_calib(const struct mt8173_sdram_params *sdram_params) +{ + u32 channel; + + sw_impedance_cal(CHANNEL_A, sdram_params); + sw_impedance_cal(CHANNEL_B, sdram_params); + + /* SPM_CONTROL_AFTERK */ + transfer_to_reg_control(); + + /* do dram calibration for channel A and B */ + for(channel = 0; channel < CHANNEL_NUM; channel++) { + ca_training(channel, sdram_params); + write_leveling(channel, sdram_params); + + /* rx gating and datlat for single or dual rank */ + if (is_dual_rank(channel, sdram_params)) { + dual_rank_rx_dqs_gating_cal(channel, sdram_params); + dual_rank_rx_datlat_cal(channel, sdram_params); + } else { + rx_dqs_gating_cal(channel, 0, sdram_params); + rx_datlat_cal(channel, 0, sdram_params); + } + + clk_duty_cal(channel); + /* rx window perbit calibration */ + perbit_window_cal(channel, RX_WIN); + /* tx window perbit calibration */ + perbit_window_cal(channel, TX_WIN); + + dramc_rankinctl_config(channel, sdram_params); + dramc_runtime_config(channel, sdram_params); + } + + /* SPM_CONTROL_AFTERK */ + transfer_to_spm_control(); +} + +static void init_dram(const struct mt8173_sdram_params *sdram_params) +{ + emi_init(sdram_params); + + dramc_pre_init(CHANNEL_A, sdram_params); + dramc_pre_init(CHANNEL_B, sdram_params); + + div2_phase_sync(); + + dramc_init(CHANNEL_A, sdram_params); + dramc_init(CHANNEL_B, sdram_params); +} + +void mt_set_emi(const struct mt8173_sdram_params *sdram_params) +{ + /* voltage info */ + dram_vcore_adjust(); + dram_vmem_adjust(); + + if (sdram_params->type != TYPE_LPDDR3) { + die("The DRAM type is not supported"); + } + + init_dram(sdram_params); + do_calib(sdram_params); +} diff --git a/src/soc/mediatek/mt8173/include/soc/dramc_common.h b/src/soc/mediatek/mt8173/include/soc/dramc_common.h new file mode 100644 index 0000000000..084e7de555 --- /dev/null +++ b/src/soc/mediatek/mt8173/include/soc/dramc_common.h @@ -0,0 +1,42 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#ifndef _DRAMC_COMMON_H_ +#define _DRAMC_COMMON_H_ + +enum { + CHANNEL_A = 0, + CHANNEL_B, + CHANNEL_NUM +}; + +enum { + GW_PARAM_COARSE = 0, + GW_PARAM_FINE, + GW_PARAM_NUM +}; + +enum { + DUAL_RANKS = 2, + CATRAINING_NUM = 10 +}; + +enum { + DQ_DATA_WIDTH = 32, + DQS_BIT_NUMBER = 8, + DQS_NUMBER = (DQ_DATA_WIDTH / DQS_BIT_NUMBER) +}; + +#endif /* _DRAMC_COMMON_H_ */ diff --git a/src/soc/mediatek/mt8173/include/soc/dramc_pi_api.h b/src/soc/mediatek/mt8173/include/soc/dramc_pi_api.h new file mode 100644 index 0000000000..1411d39f48 --- /dev/null +++ b/src/soc/mediatek/mt8173/include/soc/dramc_pi_api.h @@ -0,0 +1,187 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#ifndef _DRAMC_PI_API_H +#define _DRAMC_PI_API_H + +#include +#include + +enum { + MAX_CLKO_DELAY = 15 +}; + +enum { + /* jitter meter for PLL phase calibration */ + JMETER_COUNT = 1024, + JMETER_COUNT_N = JMETER_COUNT/10, + /* 10us for more margin, Fin = 52 */ + JMETER_WAIT_DONE_US = (JMETER_COUNT/52 + 10) +}; + +enum { + DLE_TEST_NUM = 4 +}; + +enum { + /* window type: tx/rx */ + RX_WIN = 0, + TX_WIN = 1, + /* stage type: setup/hold time */ + STAGE_SETUP = 0, + STAGE_HOLD = 1, + /* combinational flags of stage and window type */ + STAGE_SETUP_RX_WIN = STAGE_SETUP | RX_WIN << 1, + STAGE_SETUP_TX_WIN = STAGE_SETUP | TX_WIN << 1, + STAGE_HOLD_RX_WIN = STAGE_HOLD | RX_WIN << 1, + STAGE_HOLD_TX_WIN = STAGE_HOLD | TX_WIN << 1 +}; + +enum { + RX_DQ = 0, + RX_DQS, + TX_DQ, + TX_DQS, + TX_DQM +}; + +enum { + AUDIO = 1, + XTALK, + ISI +}; + +enum { + MEMPLL_INIT = 0, + MEMPLL_REF_LAG, + MEMPLL_REF_LEAD +}; + +enum { + FIRST_DQ_DELAY = 0, /* first DQ delay taps */ + FIRST_DQS_DELAY = 0, /* first DQS delay taps */ + MAX_DQDLY_TAPS = 16, /* max DQ delay taps */ + MAX_TX_DQSDLY_TAPS = 16, /* max TX DQS delay taps */ + MAX_RX_DQSDLY_TAPS = 64 /* max RX DQS delay taps */ +}; + +enum { + DRAMK_READ = 0, + DRAMK_WRITE = 1 +}; + +enum { + ENABLE = 1, + DISABLE = 0 +}; + +enum { + DATA_WIDTH_16BIT = 16, + DATA_WIDTH_32BIT = 32 +}; + +enum dram_tw_op { + TE_OP_WRITE_READ_CHECK = 0, + TE_OP_READ_CHECK +}; + +enum { + DQS_GW_TE_OFFSET = 0x10, + DQS_GW_GOLD_COUNTER_32BIT = 0x20202020, + DQS_GW_PATTERN1 = 0xaa000000, + DQS_GW_PATTERN2 = 0x55000000 +}; + +enum { + /* pattern0 and base address for test engine when we do calibration */ + DEFAULT_TEST2_1_CAL = 0x55000000, + /* for testing, to separate TA4-3 address for running simultaneously */ + /* pattern1 and offset address for test engine when we do calibraion */ + DEFAULT_TEST2_2_CAL = 0xaa000400, + /* pattern0 and base addr. for test engine when doing dqs GW */ + DEFAULT_TEST2_1_DQSIEN = 0x55000000, + /* pattern1 and offset addr. for test engine when doing dqs GW */ + DEFAULT_TEST2_2_DQSIEN = 0xaa000010, + /* gold pattern */ + DEFAULT_GOLD_DQSIEN = 0x20202020 +}; + +enum { + TEST_ISI_PATTERN = 0, + TEST_AUDIO_PATTERN, + TEST_TA1_SIMPLE, + TEST_TESTPAT4, + TEST_TESTPAT4_3, + TEST_XTALK_PATTERN, + TEST_MIX_PATTERN +}; + +struct dqs_perbit_dly { + s8 first_dqdly_pass; + s8 last_dqdly_pass; + s8 first_dqsdly_pass; + s8 last_dqsdly_pass; + s8 best_first_dqdly_pass; + s8 best_last_dqdly_pass; + s8 best_first_dqsdly_pass; + s8 best_last_dqsdly_pass; + u8 best_dqdly; + u8 best_dqsdly; +}; + +void transfer_to_spm_control(void); +void transfer_to_reg_control(void); +void dramc_phy_reset(u32 channel); +void clk_duty_cal(u32 channel); +void div2_phase_sync(void); +void dramc_runtime_config(u32 channel, const struct mt8173_sdram_params *sdram_params); +void dramc_rankinctl_config(u32 channel, const struct mt8173_sdram_params *sdram_params); + +/* dramc init prototypes */ +void mem_pll_init(const struct mt8173_sdram_params *sdram_params); +void dramc_init(u32 channel, const struct mt8173_sdram_params *sdram_params); +void dramc_pre_init(u32 channel, const struct mt8173_sdram_params *sdram_params); + +/* mandatory calibration function prototypes */ +void tx_window_perbit_cal(u32 channel); +void rx_window_perbit_cal(u32 channel); +void perbit_window_cal(u32 channel, u8 type); +void sw_impedance_cal(u32 channel, const struct mt8173_sdram_params *sdram_params); +void ca_training(u32 channel, const struct mt8173_sdram_params *sdram_params); +void rx_dqs_gating_cal(u32 channel, u8 rank, const struct mt8173_sdram_params *sdram_params); +void dual_rank_rx_datlat_cal(u32 channel, const struct mt8173_sdram_params *sdram_params); +void dual_rank_rx_dqs_gating_cal(u32 channel, const struct mt8173_sdram_params *sdram_params); +void write_leveling(u32 channel, const struct mt8173_sdram_params *sdram_params); + +u8 dramk_calcu_best_dly(u8 bit, struct dqs_perbit_dly *p, u8 *p_max_byte); +u8 is_dual_rank(u32 channel, const struct mt8173_sdram_params *sdram_params); +u8 rx_datlat_cal(u32 channel, u8 rank, const struct mt8173_sdram_params *sdram_params); +u32 dram_k_perbit(u32 channel); +u32 dramc_engine2(u32 channel, enum dram_tw_op wr, u32 test2_1, u32 test2_2, + u8 testaudpat, u8 log2loopcount); + +void dramk_check_dqs_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step, u32 fail_bit); +void dramk_check_dq_win(struct dqs_perbit_dly *p, u8 dly_step, u8 last_step, u32 fail_bit); + +void tx_delay_for_wrleveling(u32 channel, struct dqs_perbit_dly *dqdqs_perbit_dly, + u8 *ave_dqdly_byte, u8 *max_dqsdly_byte); + +#if CONFIG_DEBUG_DRAM +#define dramc_dbg_msg(_x_...) printk(BIOS_DEBUG, _x_) +#else +#define dramc_dbg_msg(_x_...) +#endif + +#endif /* _PI_API_H */ diff --git a/src/soc/mediatek/mt8173/include/soc/dramc_register.h b/src/soc/mediatek/mt8173/include/soc/dramc_register.h new file mode 100644 index 0000000000..ce6517376f --- /dev/null +++ b/src/soc/mediatek/mt8173/include/soc/dramc_register.h @@ -0,0 +1,522 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#ifndef _DRAMC_REGISTER_H_ +#define _DRAMC_REGISTER_H_ + +#include +#include + +#define DRIVING_DS2_0 7 /* DS[2:0] 7->6 */ +#define DEFAULT_DRIVING 0x99009900 + +enum { + /* CONF2 = 0x008 */ + CONF2_TEST1_EN = BIT(29), + CONF2_TEST2R_EN = BIT(30), + CONF2_TEST2W_EN = BIT(31), + /* PADCTL1 = 0x00c */ + PADCTL1_CLK_SHIFT = 24, + PADCTL1_CS1_SHIFT = 28, + /* PADCTL2 = 0x010 */ + MASK_PADCTL2_16BIT = 0x000000ff, + MASK_PADCTL2_32BIT = 0x0000ffff, + MASK_PADCTL2 = 0xffff0000, + PADCTL2_SHIFT = 0, + /* TEST2_3 = 0x044 */ + TEST2_3_TESTCNT_SHIFT = 0, + TEST2_3_TESTCNT_MASK = (0xful << TEST2_3_TESTCNT_SHIFT), + TEST2_3_TESTAUDPAT_EN = BIT(7), + TEST2_3_ADVREFEN_EN = BIT(30), + /* TEST2_4 = 0x048 */ + TEST2_4_TESTAUDINC_SHIFT = 0, + TEST2_4_TESTAUDINC_MASK = (0x1ful << TEST2_4_TESTAUDINC_SHIFT), + TEST2_4_TESTAUDINIT_SHIFT = 8, + TEST2_4_TESTAUDINIT_MASK = (0x1ful << TEST2_4_TESTAUDINIT_SHIFT), + TEST2_4_TESTAUDBITINV_EN = BIT(14), + TEST2_4_TESTAUDMODE_EN = BIT(15), + TEST2_4_TESTXTALKPAT_EN = BIT(16), + /* DDR2CTL = 0x07c */ + DDR2CTL_WOEN_SHIFT = 3, + DDR2CTL_DATLAT_SHIFT = 4, + /* MISC = 0x80 */ + MISC_LATNORMP_SHIFT = 0, + MISC_DATLAT_DSEL_SHIFT = 8, + /* MRS = 0x088 */ + MASK_MR2_OP = 0x00800000, + /* R0 R1 DQSIEN = 0x094 */ + DQSIEN_DQS0IEN_SHIFT = 0, + DQSIEN_DQS1IEN_SHIFT = 8, + DQSIEN_DQS2IEN_SHIFT = 16, + DQSIEN_DQS3IEN_SHIFT = 24, + /* MCKDLY = 0x0d8 */ + MCKDLY_DQIENLAT_SHIFT = 4, + MCKDLY_DQIENQKEND_SHIFT = 10, + MCKDLY_FIXDQIEN_SHIFT = 12, + MCKDLY_FIXODT_SHIFT = 23, + /* DQSCTL1 = 0x0e0 */ + DQSCTL1_DQSINCTL_SHIFT = 24, + DQSCTL1_DQSIENMODE_SHIFT = 28, + /* PADCTL4 = 0x0e4 */ + PADCTL4_CKEFIXON_SHIFT = 2, + PADCTL4_DATLAT3_SHIFT = 4, + /* PHYCTL1 = 0x0f0 */ + PHYCTL1_DATLAT4_SHIFT = 25, + PHYCTL1_PHYRST_SHIFT = 28, + /* GDDR3CTL1 = 0x0f4 */ + GDDR3CTL1_BKSWAP_SHIFT = 20, + GDDR3CTL1_RDATRST_SHIFT = 25, + GDDR3CTL1_DQMSWAP_SHIFT = 31, + /* RKCFG = 0x110 */ + MASK_RKCFG_RKSWAP_EN = 0x08, + RKCFG_PBREF_DISBYRATE_SHIFT = 6, + RKCFG_WDATKEY64_SHIFT = 29, + /* DQSCTL2 = 0x118 */ + DQSCTL2_DQSINCTL_SHIFT = 0, + /* DQSGCTL = 0x124 */ + DQSGCTL_DQSGDUALP_SHIFT = 30, + /* PHYCLKDUTY = 0x148 */ + PHYCLKDUTY_CMDCLKP0DUTYN_SHIFT = 16, + PHYCLKDUTY_CMDCLKP0DUTYP_SHIFT = 18, + PHYCLKDUTY_CMDCLKP0DUTYSEL_SHIFT = 28, + /* CMDDLY0 = 0x1a8 */ + CMDDLY0_RA0_SHIFT = 0, + CMDDLY0_RA1_SHIFT = 8, + CMDDLY0_RA2_SHIFT = 16, + CMDDLY0_RA3_SHIFT = 24, + /* CMDDLY1 = 0x1ac */ + CMDDLY1_RA7_SHIFT = 24, + /* CMDDLY3 = 0x1b4 */ + CMDDLY3_BA0_SHIFT = 8, + CMDDLY3_BA1_SHIFT = 16, + CMDDLY3_BA2_SHIFT = 24, + /* CMDDLY4 = 0x1b8 */ + CMDDLY4_CS_SHIFT = 0, + CMDDLY4_CKE_SHIFT = 8, + CMDDLY4_RAS_SHIFT = 16, + CMDDLY4_CAS_SHIFT = 24, + /* CMDDLY5 = 0x1bc */ + CMDDLY5_WE_SHIFT = 8, + CMDDLY5_RA13_SHIFT = 16, + /* DQSCAL0 = 0x1c0 */ + DQSCAL0_RA14_SHIFT = 24, + DQSCAL0_STBCALEN_SHIFT = 31, + /* DQSCAL1 = 0x1c4 */ + DQSCAL1_CKE1_SHIFT = 24, + /* IMPCAL = 0x1c8 */ + IMP_CALI_EN_SHIFT = 0, + IMP_CALI_HW_SHIFT = 1, + IMP_CALI_ENN_SHIFT = 4, + IMP_CALI_ENP_SHIFT = 5, + IMP_CALI_PDN_SHIFT = 6, + IMP_CALI_PDP_SHIFT = 7, + IMP_CALI_DRVP_SHIFT = 8, + IMP_CALI_DRVN_SHIFT = 12, + /* JMETER for PLL2, PLL3, PLL4 */ + JMETER_EN_BIT= BIT(0), + JMETER_COUNTER_SHIFT = 16, + JMETER_COUNTER_MASK = (0xffff << JMETER_COUNTER_SHIFT), + /* SPCMD = 0x1e4 */ + SPCMD_MRWEN_SHIFT = 0, + SPCMD_DQSGCNTEN_SHIFT = 8, + SPCMD_DQSGCNTRST_SHIFT = 9, + /* JMETER for PLL2/3/4 ST */ + JMETER_PLL_ZERO_SHIFT = 0, + JMETER_PLL_ONE_SHIFT = 16, + /* TESTRPT = 0x3fc */ + TESTRPT_DM_CMP_CPT_SHIFT = 10, + TESTRPT_DM_CMP_ERR_SHIFT = 14, + /* SELPH2 = 0x404 */ + SELPH2_TXDLY_DQSGATE_SHIFT = 12, + SELPH2_TXDLY_DQSGATE_P1_SHIFT = 20, + /* SELPH5 = 0x410 */ + SELPH5_DLY_DQSGATE_SHIFT = 22, + SELPH5_DLY_DQSGATE_P1_SHIFT = 24, + /* SELPH6_1 = 0x418 */ + SELPH6_1_DLY_R1DQSGATE_SHIFT = 0, + SELPH6_1_DLY_R1DQSGATE_P1_SHIFT = 2, + SELPH6_1_TXDLY_R1DQSGATE_SHIFT = 4, + SELPH6_1_TXDLY_R1DQSGATE_P1_SHIFT = 8, + /* MEMPLL_S14 = 0x638 */ + MASK_MEMPLL_DL = 0xc0ffffff, + MEMPLL_FB_DL_SHIFT = 0, + MEMPLL_REF_DL_SHIFT = 8, + MEMPLL_DL_SHIFT = 24, + MEMPLL_MODE_SHIFT = 29, + /* MEMPLL_DIVIDER = 0x640 */ + MEMCLKENB_SHIFT = 5 +}; + +struct dramc_ao_regs { + uint32_t actim0; /* 0x0 */ + uint32_t conf1; /* 0x4 */ + uint32_t conf2; /* 0x8 */ + uint32_t rsvd_ao1[3]; /* 0xc */ + uint32_t r0deldly; /* 0x18 */ + uint32_t r1deldly; /* 0x1c */ + uint32_t r0difdly; /* 0x20 */ + uint32_t r1difdly; /* 0x24 */ + uint32_t dllconf; /* 0x28 */ + uint32_t rsvd_ao2[6]; /* 0x2c */ + uint32_t test2_3; /* 0x44 */ + uint32_t test2_4; /* 0x48 */ + uint32_t catraining; /* 0x4c */ + uint32_t catraining2; /* 0x50 */ + uint32_t wodt; /* 0x54 */ + uint32_t rsvd_ao3[9]; /* 0x58 */ + uint32_t ddr2ctl; /* 0x7c */ + uint32_t misc; /* 0x80 */ + uint32_t zqcs; /* 0x84 */ + uint32_t mrs; /* 0x88 */ + uint32_t clk1delay; /* 0x8c */ + uint32_t rsvd_ao4[1]; /* 0x90 */ + uint32_t dqsien[2]; /* 0x94 */ + uint32_t rsvd_ao5[2]; /* 0x9c */ + uint32_t iodrv1; /* 0xa4 */ + uint32_t iodrv2; /* 0xa8 */ + uint32_t iodrv3; /* 0xac */ + uint32_t iodrv4; /* 0xb0 */ + uint32_t iodrv5; /* 0xb4 */ + uint32_t iodrv6; /* 0xb8 */ + uint32_t drvctl1; /* 0xbc */ + uint32_t dllsel; /* 0xc0 */ + uint32_t rsvd_ao7[5]; /* 0xc4 */ + uint32_t mckdly; /* 0xd8 */ + uint32_t rsvd_ao8[1]; /* 0xdc */ + uint32_t dqsctl1; /* 0xe0 */ + uint32_t padctl4; /* 0xe4 */ + uint32_t rsvd_ao9[2]; /* 0xe8 */ + uint32_t phyctl1; /* 0xf0 */ + uint32_t gddr3ctl1; /* 0xf4 */ + uint32_t padctl7; /* 0xf8 */ + uint32_t misctl0; /* 0xfc */ + uint32_t ocdk; /* 0x100 */ + uint32_t rsvd_ao10[3]; /* 0x104 */ + uint32_t rkcfg; /* 0x110 */ + uint32_t ckphdet; /* 0x114 */ + uint32_t dqsctl2; /* 0x118 */ + uint32_t rsvd_ao11[5]; /* 0x11c */ + uint32_t clkctl; /* 0x130 */ + uint32_t rsvd_ao12[1]; /* 0x134 */ + uint32_t dummy; /* 0x138 */ + uint32_t write_leveling; /* 0x13c */ + uint32_t rsvd_ao13[10]; /* 0x140 */ + uint32_t arbctl0; /* 0x168 */ + uint32_t rsvd_ao14[21]; /* 0x16c */ + uint32_t dqscal0; /* 0x1c0 */ + uint32_t dqscal1; /* 0x1c4 */ + uint32_t impcal; /* 0x1c8 */ + uint32_t rsvd_ao15[4]; /* 0x1cc */ + uint32_t dramc_pd_ctrl; /* 0x1dc */ + uint32_t lpddr2_3; /* 0x1e0 */ + uint32_t spcmd; /* 0x1e4 */ + uint32_t actim1; /* 0x1e8 */ + uint32_t perfctl0; /* 0x1ec */ + uint32_t ac_derating; /* 0x1f0 */ + uint32_t rrrate_ctl; /* 0x1f4 */ + uint32_t ac_time_05t; /* 0x1f8 */ + uint32_t mrr_ctl; /* 0x1fc */ + uint32_t rsvd_ao16[4]; /* 0x200 */ + uint32_t dqidly[9]; /* 0x210 */ + uint32_t rsvd_ao17[115]; /* 0x234 */ + uint32_t selph1; /* 0x400 */ + uint32_t selph2; /* 0x404 */ + uint32_t selph3; /* 0x408 */ + uint32_t selph4; /* 0x40c */ + uint32_t selph5; /* 0x410 */ + uint32_t selph6; /* 0x414 */ + uint32_t selph6_1; /* 0x418 */ + uint32_t selph7; /* 0x41c */ + uint32_t selph8; /* 0x420 */ + uint32_t selph9; /* 0x424 */ + uint32_t selph10; /* 0x428 */ + uint32_t selph11; /* 0x42c */ +}; + +check_member(dramc_ao_regs, selph11, 0x42c); + +struct dramc_nao_regs { + uint32_t rsvd_nao1[11]; /* 0x0 */ + uint32_t test_mode; /* 0x2c */ + uint32_t rsvd_nao2[3]; /* 0x30 */ + uint32_t test2_1; /* 0x3c */ + uint32_t test2_2; /* 0x40 */ + uint32_t rsvd_nao3[48]; /* 0x44 */ + uint32_t lbwdat0; /* 0x104 */ + uint32_t lbwdat1; /* 0x108 */ + uint32_t lbwdat2; /* 0x10c */ + uint32_t rsvd_nao4[1]; /* 0x110 */ + uint32_t ckphdet; /* 0x114 */ + uint32_t rsvd_nao5[48]; /* 0x118 */ + uint32_t dmmonitor; /* 0x1d8 */ + uint32_t rsvd_nao6[41]; /* 0x1dc */ + uint32_t r2r_page_hit_counter; /* 0x280 */ + uint32_t r2r_page_miss_counter; /* 0x284 */ + uint32_t r2r_interbank_counter; /* 0x288 */ + uint32_t r2w_page_hit_counter; /* 0x28c */ + uint32_t r2w_page_miss_counter; /* 0x290 */ + uint32_t r2w_interbank_counter; /* 0x294 */ + uint32_t w2r_page_hit_counter; /* 0x298 */ + uint32_t w2r_page_miss_counter; /* 0x29c */ + uint32_t w2r_page_interbank_counter; /* 0x2a0 */ + uint32_t w2w_page_hit_counter; /* 0x2a4 */ + uint32_t w2w_page_miss_counter; /* 0x2a8 */ + uint32_t w2w_page_interbank_counter; /* 0x2ac */ + uint32_t dramc_idle_counter; /* 0x2b0 */ + uint32_t freerun_26m_counter; /* 0x2b4 */ + uint32_t refresh_pop_counter; /* 0x2b8 */ + uint32_t jmeter_st; /* 0x2bc */ + uint32_t dq_cal_max[8]; /* 0x2c0 */ + uint32_t dqs_cal_min[8]; /* 0x2e0 */ + uint32_t dqs_cal_max[8]; /* 0x300 */ + uint32_t rsvd_nao7[4]; /* 0x320 */ + uint32_t read_bytes_counter; /* 0x330 */ + uint32_t write_bytes_counter; /* 0x334 */ + uint32_t rsvd_nao8[6]; /* 0x338 */ + uint32_t dqical[4]; /* 0x350 */ + uint32_t rsvd_nao9[4]; /* 0x360 */ + uint32_t cmp_err; /* 0x370 */ + uint32_t r0dqsiendly; /* 0x374 */ + uint32_t r1dqsiendly; /* 0x378 */ + uint32_t rsvd_nao10[9]; /* 0x37c */ + uint32_t dqsdly0; /* 0x3a0 */ + uint32_t rsvd_nao11[4]; /* 0x3a4 */ + uint32_t mrrdata; /* 0x3b4 */ + uint32_t spcmdresp; /* 0x3b8 */ + uint32_t iorgcnt; /* 0x3bc */ + uint32_t dqsgnwcnt[6]; /* 0x3c0 */ + uint32_t rsvd_nao12[4]; /* 0x3d8 */ + uint32_t ckphcnt; /* 0x3e8 */ + uint32_t rsvd_nao13[4]; /* 0x3ec */ + uint32_t testrpt; /* 0x3fc */ +}; + +check_member(dramc_nao_regs, testrpt, 0x3fc); + +struct dramc_ddrphy_regs { + uint32_t rsvd_phy1[3]; /* 0x0 */ + uint32_t padctl1; /* 0xc */ + uint32_t padctl2; /* 0x10 */ + uint32_t padctl3; /* 0x14 */ + uint32_t rsvd_phy2[25]; /* 0x18 */ + uint32_t ddr2ctl; /* 0x7c */ + uint32_t rsvd_phy3[3]; /* 0x80 */ + uint32_t clk1delay; /* 0x8c */ + uint32_t ioctl; /* 0x90 */ + uint32_t rsvd_phy4[7]; /* 0x94 */ + uint32_t iodrv4; /* 0xb0 */ + uint32_t iodrv5; /* 0xb4 */ + uint32_t iodrv6; /* 0xb8 */ + uint32_t drvctl1; /* 0xbc */ + uint32_t dllsel; /* 0xc0 */ + uint32_t rsvd_phy5[2]; /* 0xc4 */ + uint32_t tdsel[3]; /* 0xcc */ + uint32_t mckdly; /* 0xd8 */ + uint32_t dqsctl0; /* 0xdc */ + uint32_t dqsctl1; /* 0xe0 */ + uint32_t dqsctl4; /* 0xe4 */ + uint32_t dqsctl5; /* 0xe8 */ + uint32_t dqsctl6; /* 0xec */ + uint32_t phyctl1; /* 0xf0 */ + uint32_t gddr3ctl1; /* 0xf4 */ + uint32_t rsvd_phy6[1]; /* 0xf8 */ + uint32_t misctl0; /* 0xfc */ + uint32_t ocdk; /* 0x100 */ + uint32_t rsvd_phy7[8]; /* 0x104 */ + uint32_t dqsgctl; /* 0x124 */ + uint32_t rsvd_phy8[6]; /* 0x128 */ + uint32_t ddrphydqsgctl; /* 0x140 */ + uint32_t dqsgct2; /* 0x144 */ + uint32_t phyclkduty; /* 0x148 */ + uint32_t rsvd_phy9[3]; /* 0x14c */ + uint32_t dqsisel; /* 0x158 */ + uint32_t dqmdqs_sel; /* 0x15c */ + uint32_t rsvd_phy10[10]; /* 0x160 */ + uint32_t jmeterpop1; /* 0x188 */ + uint32_t jmeterpop2; /* 0x18c */ + uint32_t jmeterpop3; /* 0x190 */ + uint32_t jmeterpop4; /* 0x194 */ + uint32_t rsvd_phy11[4]; /* 0x198 */ + uint32_t cmddly[6]; /* 0x1a8 */ + uint32_t dqscal0; /* 0x1c0 */ + uint32_t rsvd_phy12[2]; /* 0x1c4 */ + uint32_t jmeter[3]; /* 0x1cc */ + uint32_t rsvd_phy13[2]; /* 0x1d8 */ + uint32_t lpddr2_3; /* 0x1e0 */ + uint32_t spcmd; /* 0x1e4 */ + uint32_t rsvd_phy14[6]; /* 0x1e8 */ + uint32_t dqodly[4]; /* 0x200 */ + uint32_t rsvd_phy15[11]; /* 0x210 */ + uint32_t lpddr2_4; /* 0x23c */ + uint32_t rsvd_phy16[56]; /* 0x240 */ + uint32_t jmeter_pll_st[3]; /* 0x320 */ + uint32_t jmeter_done_st; /* 0x32c */ + uint32_t rsvd_phy17[2]; /* 0x330 */ + uint32_t jmeter_pll1_st; /* 0x338 */ + uint32_t jmeter_pop_pll2_st; /* 0x33c */ + uint32_t jmeter_pop_pll3_st; /* 0x340 */ + uint32_t jmeter_pop_pll4_st; /* 0x344 */ + uint32_t jmeter_pop_pll1_st; /* 0x348 */ + uint32_t rsvd_phy18[13]; /* 0x34c */ + uint32_t dq_o1; /* 0x380 */ + uint32_t rsvd_phy19[2]; /* 0x384 */ + uint32_t stben[4]; /* 0x38c */ + uint32_t rsvd_phy20[16]; /* 0x39c */ + uint32_t dllcnt0; /* 0x3dc */ + uint32_t pllautok; /* 0x3e0 */ + uint32_t poppllautok; /* 0x3e4 */ + uint32_t rsvd_phy21[18]; /* 0x3e8 */ + uint32_t selph12; /* 0x430 */ + uint32_t selph13; /* 0x434 */ + uint32_t selph14; /* 0x438 */ + uint32_t selph15; /* 0x43c */ + uint32_t selph16; /* 0x440 */ + uint32_t selph17; /* 0x444 */ + uint32_t selph18; /* 0x448 */ + uint32_t selph19; /* 0x44c */ + uint32_t selph20; /* 0x450 */ + uint32_t rsvd_phy22[91]; /* 0x454 */ + uint32_t peri[4]; /* 0x5c0 */ + uint32_t rsvd_phy23[12]; /* 0x5d0 */ + uint32_t mempll[15]; /* 0x600 */ + uint32_t ddrphy_cg_ctrl; /* 0x63c */ + uint32_t mempll_divider; /* 0x640 */ + uint32_t vrefctl0; /* 0x644 */ + uint32_t rsvd_phy24[18]; /* 0x648 */ + uint32_t mempll05_divider; /* 0x690 */ +}; + +check_member(dramc_ddrphy_regs, mempll05_divider, 0x690); + +struct emi_regs { + uint32_t emi_cona; /* 0x0 */ + uint32_t rsvd_emi1; /* 0x4 */ + uint32_t emi_conb; /* 0x08 */ + uint32_t rsvd_emi2; /* 0x0c */ + uint32_t emi_conc; /* 0x10 */ + uint32_t rsvd_emi3; /* 0x14 */ + uint32_t emi_cond; /* 0x18 */ + uint32_t rsvd_emi4; /* 0x1c */ + uint32_t emi_cone; /* 0x20 */ + uint32_t rsvd_emi5; /* 0x24 */ + uint32_t emi_conf; /* 0x28 */ + uint32_t rsvd_emi6; /* 0x2c */ + uint32_t emi_cong; /* 0x30 */ + uint32_t rsvd_emi7; /* 0x34 */ + uint32_t emi_conh; /* 0x38 */ + uint32_t rsvd_emi8[9]; /* 0x3c */ + uint32_t emi_conm; /* 0x60 */ + uint32_t rsvd_emi9[5]; /* 0x64 */ + uint32_t emi_mdct; /* 0x78 */ + uint32_t rsvd_emi10[21]; /* 0x7c */ + uint32_t emi_test0; /* 0xd0 */ + uint32_t rsvd_emi11; /* 0xd4 */ + uint32_t emi_test1; /* 0xd8 */ + uint32_t rsvd_emi12; /* 0xdc */ + uint32_t emi_testa; /* 0xe0 */ + uint32_t rsvd_emi13; /* 0xe4 */ + uint32_t emi_testb; /* 0xe8 */ + uint32_t rsvd_emi14; /* 0xec */ + uint32_t emi_testc; /* 0xf0 */ + uint32_t rsvd_emi15; /* 0xf4 */ + uint32_t emi_testd; /* 0xf8 */ + uint32_t rsvd_emi16; /* 0xfc */ + uint32_t emi_arba; /* 0x100 */ + uint32_t rsvd_emi17[3]; /* 0x104 */ + uint32_t emi_arbc; /* 0x110 */ + uint32_t rsvd_emi18; /* 0x114 */ + uint32_t emi_arbd; /* 0x118 */ + uint32_t rsvd_emi19; /* 0x11c */ + uint32_t emi_arbe; /* 0x120 */ + uint32_t rsvd_emi20; /* 0x124 */ + uint32_t emi_arbf; /* 0x128 */ + uint32_t rsvd_emi21; /* 0x12c */ + uint32_t emi_arbg; /* 0x130 */ + uint32_t rsvd_emi22; /* 0x134 */ + uint32_t emi_arbh; /* 0x138 */ + uint32_t rsvd_emi23; /* 0x13c */ + uint32_t emi_arbi; /* 0x140 */ + uint32_t emi_arbi_2nd; /* 0x144 */ + uint32_t emi_arbj; /* 0x148 */ + uint32_t emi_arbj_2nd; /* 0x14c */ + uint32_t emi_arbk; /* 0x150 */ + uint32_t emi_arbk_2nd; /* 0x154 */ + uint32_t emi_slct; /* 0x158 */ + uint32_t rsvd_emi24; /* 0x15C */ + uint32_t emi_mpua; /* 0x160 */ + uint32_t rsvd_emi25; /* 0x164 */ + uint32_t emi_mpub; /* 0x168 */ + uint32_t rsvd_emi26; /* 0x16c */ + uint32_t emi_mpuc; /* 0x170 */ + uint32_t rsvd_emi27; /* 0x174 */ + uint32_t emi_mpud; /* 0x178 */ + uint32_t rsvd_emi28; /* 0x17C */ + uint32_t emi_mpue; /* 0x180 */ + uint32_t rsvd_emi29; /* 0x184 */ + uint32_t emi_mpuf; /* 0x188 */ + uint32_t rsvd_emi30; /* 0x18C */ + uint32_t emi_mpug; /* 0x190 */ + uint32_t rsvd_emi31; /* 0x194 */ + uint32_t emi_mpuh; /* 0x198 */ + uint32_t rsvd_emi32; /* 0x19C */ + uint32_t emi_mpui; /* 0x1A0 */ + uint32_t rsvd_emi33; /* 0x1A4 */ + uint32_t emi_mpuj; /* 0x1A8 */ + uint32_t rsvd_emi34; /* 0x1AC */ + uint32_t emi_mpuk; /* 0x1B0 */ + uint32_t rsvd_emi35; /* 0x1B4 */ + uint32_t emi_mpul; /* 0x1B8 */ + uint32_t rsvd_emi36; /* 0x1BC */ + uint32_t emi_mpum; /* 0x1C0 */ + uint32_t rsvd_emi37; /* 0x1C4 */ + uint32_t emi_mpun; /* 0x1C8 */ + uint32_t rsvd_emi38; /* 0x1CC */ + uint32_t emi_mpuo; /* 0x1D0 */ + uint32_t rsvd_emi39; /* 0x1D4 */ + uint32_t emi_mpup; /* 0x1D8 */ + uint32_t rsvd_emi40; /* 0x1DC */ + uint32_t emi_mpuq; /* 0x1E0 */ + uint32_t rsvd_emi41; /* 0x1E4 */ + uint32_t emi_mpur; /* 0x1E8 */ + uint32_t rsvd_emi42; /* 0x1EC */ + uint32_t emi_mpus; /* 0x1F0 */ + uint32_t rsvd_emi43; /* 0x1F4 */ + uint32_t emi_mput; /* 0x1F8 */ + uint32_t rsvd_emi44; /* 0x1FC */ + uint32_t emi_mpuu; /* 0x200 */ + uint32_t rsvd_emi45[7]; /* 0x204 */ + uint32_t emi_mpuy; /* 0x220 */ + uint32_t rsvd_emi46[119]; /* 0x224 */ + uint32_t emi_bmen; /* 0x400 */ +}; + +check_member(emi_regs, emi_bmen, 0x400); + +extern struct dramc_ao_regs *ao_regs; +extern struct dramc_nao_regs *nao_regs; +extern struct dramc_ddrphy_regs *ddrphy_regs; + +struct dramc_channel { + struct dramc_ao_regs *ao_regs; + struct dramc_nao_regs *nao_regs; + struct dramc_ddrphy_regs *ddrphy_regs; +}; + +static struct dramc_channel const ch[2] = { + {(void *)CHA_DRAMCAO_BASE, (void *)CHA_DRAMCNAO_BASE, (void *)CHA_DDRPHY_BASE}, + {(void *)CHB_DRAMCAO_BASE, (void *)CHB_DRAMCNAO_BASE, (void *)CHB_DDRPHY_BASE} +}; + +#endif /* _DRAMC_REGISTER_H_ */ diff --git a/src/soc/mediatek/mt8173/include/soc/emi.h b/src/soc/mediatek/mt8173/include/soc/emi.h new file mode 100644 index 0000000000..d3a2aeec9c --- /dev/null +++ b/src/soc/mediatek/mt8173/include/soc/emi.h @@ -0,0 +1,140 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#ifndef SOC_MEDIATEK_MT8173_EMI_H +#define SOC_MEDIATEK_MT8173_EMI_H + +#include +#include + +/* DDR type */ +enum ram_type { + TYPE_INVALID, + TYPE_DDR1, + TYPE_LPDDR2, + TYPE_LPDDR3, + TYPE_PCDDR3 +}; + +enum { + /* + * Vmem voltage adjustment: + * 1) HV: high voltage + * 2) NV: normal voltage + * 3) LV: low voltage + */ + Vmem_HV_LPDDR3 = 0x50, /* 1.300V */ + Vmem_NV_LPDDR3 = 0x44, /* 1.225V */ + Vmem_LV_LPDDR3 = 0x36 /* 1.138V */ +}; + +enum { + /* + * Vcore voltage adjustment: + * 1) HHV: extra high voltage + * 2) HV: high voltage + * 3) NV: normal voltage + * 4) LV: low voltage + * 5) LLV: extra low voltage + */ + Vcore_HHV_LPPDR3 = 0x60, /* 1.300V */ + Vcore_HV_LPPDR3 = 0x48, /* 1.150V */ + Vcore_NV_LPPDR3 = 0x44, /* 1.125V */ + Vcore_LV_LPPDR3 = 0x34, /* 1.025V */ + Vcore_LLV_LPPDR3 = 0x25 /* 0.931V */ +}; + +struct mt8173_calib_params { + u8 impedance_drvp; + u8 impedance_drvn; + u8 datlat_ucfirst; + s8 ca_train[CHANNEL_NUM][CATRAINING_NUM]; + s8 ca_train_center[CHANNEL_NUM]; + s8 wr_level[CHANNEL_NUM][DQS_NUMBER]; + u8 gating_win[CHANNEL_NUM][DUAL_RANKS][GW_PARAM_NUM]; + u32 rx_dqs_dly[CHANNEL_NUM]; + u32 rx_dq_dly[CHANNEL_NUM][DQS_BIT_NUMBER]; +}; + +struct mt8173_timing_params { + u32 actim; + u32 actim1; + u32 actim05t; + u32 conf1; + u32 conf2; + u32 ddr2ctl; + u32 gddr3ctl1; + u32 misctl0; + u32 pd_ctrl; + u32 rkcfg; + u32 test2_4; + u32 test2_3; +}; + +struct mt8173_emi_params { + u32 cona; + u32 conb; + u32 conc; + u32 cond; + u32 cone; + u32 conf; + u32 cong; + u32 conh; + u32 conm_1; + u32 conm_2; + u32 mdct_1; + u32 mdct_2; + u32 test0; + u32 test1; + u32 testb; + u32 testc; + u32 testd; + u32 arba; + u32 arbc; + u32 arbd; + u32 arbe; + u32 arbf; + u32 arbg; + u32 arbi; + u32 arbj; + u32 arbk; + u32 slct_1; + u32 slct_2; + u32 bmen; +}; + +struct mt8173_mrs_params { + u32 mrs_1; + u32 mrs_2; + u32 mrs_3; + u32 mrs_10; + u32 mrs_11; + u32 mrs_63; +}; + +struct mt8173_sdram_params { + struct mt8173_calib_params calib_params; + struct mt8173_timing_params ac_timing; + struct mt8173_emi_params emi_set; + struct mt8173_mrs_params mrs_set; + enum ram_type type; + unsigned int dram_freq; +}; + +void mt_set_emi(const struct mt8173_sdram_params *sdram_params); +void mt_mem_init(const struct mt8173_sdram_params *sdram_params); +const struct mt8173_sdram_params *get_sdram_config(void); + +#endif diff --git a/src/soc/mediatek/mt8173/include/soc/pll.h b/src/soc/mediatek/mt8173/include/soc/pll.h index 1eab709462..6d38bb3447 100644 --- a/src/soc/mediatek/mt8173/include/soc/pll.h +++ b/src/soc/mediatek/mt8173/include/soc/pll.h @@ -16,6 +16,7 @@ #ifndef SOC_MEDIATEK_MT8173_PLL_H #define SOC_MEDIATEK_MT8173_PLL_H +#include #include struct mt8173_topckgen_regs { @@ -285,5 +286,9 @@ void mt_pll_post_init(void); void mt_pll_init(void); void mt_pll_set_aud_div(u32 rate); void mt_pll_enable_ssusb_clk(void); +void mt_mem_pll_set_clk_cfg(void); +void mt_mem_pll_config_pre(const struct mt8173_sdram_params *sdram_params); +void mt_mem_pll_config_post(void); +void mt_mem_pll_mux(void); #endif /* SOC_MEDIATEK_MT8173_PLL_H */ diff --git a/src/soc/mediatek/mt8173/memory.c b/src/soc/mediatek/mt8173/memory.c new file mode 100644 index 0000000000..3db9df869e --- /dev/null +++ b/src/soc/mediatek/mt8173/memory.c @@ -0,0 +1,357 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 MediaTek 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + /* test patterns */ + PATTERN0 = 0x00000000, + PATTERN1 = 0x5A5A5A5A, + PATTERN2 = 0xA5A5A5A5, + PATTERN3 = 0xA5A5A500, + PATTERN4 = 0xA500A500, + PATTERN5 = 0xA5000000, + PATTERN6 = 0xFFFF0000, + PATTERN7 = 0x0000FFFF, + PATTERN8 = 0x00000012, + PATTERN9 = 0x00000034, + PATTERNA = 0x00000056, + PATTERNB = 0x00000078, + PATTERNC = 0x00001234, + PATTERND = 0x00005678, + PATTERNE = 0x12345678, + PATTERNF = 0xFFFFFFFF +}; + +static int complex_mem_test(unsigned int start, unsigned int len) +{ + unsigned char *mem8_base = (unsigned char *)(uintptr_t)start; + unsigned short *mem16_base = (unsigned short *)(uintptr_t)start; + unsigned int *mem32_base = (unsigned int *)(uintptr_t)start; + unsigned int *mem_base = (unsigned int *)(uintptr_t)start; + unsigned char pattern8; + unsigned short pattern16; + unsigned int i, j, size, pattern32; + unsigned int value; + + size = len >> 2; + + /* verify the tied bits (tied high) */ + for (i = 0; i < size; i++) { + mem32_base[i] = PATTERN0; + } + + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN0) { + return -1; + } else { + mem32_base[i] = PATTERNF; + } + } + + /* verify the tied bits (tied low) */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERNF) { + return -2; + } else + mem32_base[i] = PATTERN0; + } + + /* verify pattern 1 (0x00~0xff) */ + pattern8 = PATTERN0; + for (i = 0; i < len; i++) + mem8_base[i] = pattern8++; + pattern8 = PATTERN0; + for (i = 0; i < len; i++) { + if (mem8_base[i] != pattern8++) { + return -3; + } + } + + /* verify pattern 2 (0x00~0xff) */ + pattern8 = PATTERN0; + for (i = j = 0; i < len; i += 2, j++) { + if (mem8_base[i] == pattern8) + mem16_base[j] = pattern8; + if (mem16_base[j] != pattern8) { + return -4; + } + pattern8 += 2; + } + + /* verify pattern 3 (0x00~0xffff) */ + pattern16 = PATTERN0; + for (i = 0; i < (len >> 1); i++) + mem16_base[i] = pattern16++; + pattern16 = PATTERN0; + for (i = 0; i < (len >> 1); i++) { + if (mem16_base[i] != pattern16++) { + return -5; + } + } + + /* verify pattern 4 (0x00~0xffffffff) */ + pattern32 = PATTERN0; + for (i = 0; i < (len >> 2); i++) + mem32_base[i] = pattern32++; + pattern32 = PATTERN0; + for (i = 0; i < (len >> 2); i++) { + if (mem32_base[i] != pattern32++) { + return -6; + } + } + + /* pattern 5: filling memory range with 0x12345678 */ + for (i = 0; i < size; i++) + mem32_base[i] = PATTERNE; + + /* read check then fill memory with a5a5a5a5 pattern */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERNE) { + return -7; + } else { + mem32_base[i] = PATTERN2; + } + } + + /* read check then fill memory with 00 byte pattern at offset 0h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN2) { + return -8; + } else { + mem8_base[i * 4] = PATTERN0; + } + } + + /* read check then fill memory with 00 byte pattern at offset 2h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN3) { + return -9; + } else { + mem8_base[i * 4 + 2] = PATTERN0; + } + } + + /* read check then fill memory with 00 byte pattern at offset 1h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN4) { + return -10; + } else { + mem8_base[i * 4 + 1] = PATTERN0; + } + } + + /* read check then fill memory with 00 byte pattern at offset 3h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN5) { + return -11; + } else { + mem8_base[i * 4 + 3] = PATTERN0; + } + } + + /* read check then fill memory with ffff word pattern at offset 1h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN0) { + return -12; + } else { + mem16_base[i * 2 + 1] = PATTERN7; + } + } + + /* read check then fill memory with ffff word pattern at offset 0h */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERN6) { + return -13; + } else { + mem16_base[i * 2] = PATTERN7; + } + } + + /* read check */ + for (i = 0; i < size; i++) { + if (mem32_base[i] != PATTERNF) { + return -14; + } + } + + /* stage 1 => write 0 */ + for (i = 0; i < size; i++) { + mem_base[i] = PATTERN1; + } + + /* stage 2 => read 0, write 0xf */ + for (i = 0; i < size; i++) { + value = mem_base[i]; + + if (value != PATTERN1) { + return -15; + } + mem_base[i] = PATTERN2; + } + + /* stage 3 => read 0xf, write 0 */ + for (i = 0; i < size; i++) { + value = mem_base[i]; + if (value != PATTERN2) { + return -16; + } + mem_base[i] = PATTERN1; + } + + /* stage 4 => read 0, write 0xf */ + for (i = 0; i < size; i++) { + value = mem_base[i]; + if (value != PATTERN1) { + return -17; + } + mem_base[i] = PATTERN2; + } + + /* stage 5 => read 0xf, write 0 */ + for (i = 0; i < size; i++) { + value = mem_base[i]; + if (value != PATTERN2) { + return -18; + } + mem_base[i] = PATTERN1; + } + + /* stage 6 => read 0 */ + for (i = 0; i < size; i++) { + value = mem_base[i]; + if (value != PATTERN1) { + return -19; + } + } + + /* 1/2/4-byte combination test */ + i = (unsigned int)(uintptr_t)mem_base; + + while (i < (unsigned int)(uintptr_t)mem_base + (size << 2)) { + *((unsigned char *)(uintptr_t)i) = PATTERNB; + i += 1; + *((unsigned char *)(uintptr_t)i) = PATTERNA; + i += 1; + *((unsigned short *)(uintptr_t)i) = PATTERNC; + i += 2; + *((unsigned int *)(uintptr_t)i) = PATTERNE; + i += 4; + *((unsigned short *)(uintptr_t)i) = PATTERND; + i += 2; + *((unsigned char *)(uintptr_t)i) = PATTERN9; + i += 1; + *((unsigned char *)(uintptr_t)i) = PATTERN8; + i += 1; + *((unsigned int *)(uintptr_t)i) = PATTERNE; + i += 4; + *((unsigned char *)(uintptr_t)i) = PATTERNB; + i += 1; + *((unsigned char *)(uintptr_t)i) = PATTERNA; + i += 1; + *((unsigned short *)(uintptr_t)i) = PATTERNC; + i += 2; + *((unsigned int *)(uintptr_t)i) = PATTERNE; + i += 4; + *((unsigned short *)(uintptr_t)i) = PATTERND; + i += 2; + *((unsigned char *)(uintptr_t)i) = PATTERN9; + i += 1; + *((unsigned char *)(uintptr_t)i) = PATTERN8; + i += 1; + *((unsigned int *)(uintptr_t)i) = PATTERNE; + i += 4; + } + + for (i = 0; i < size; i++) { + value = mem_base[i]; + if (value != PATTERNE) { + return -20; + } + } + + /* verify pattern 1 (0x00~0xff) */ + pattern8 = PATTERN0; + mem8_base[0] = pattern8; + for (i = 0; i < size * 4; i++) { + unsigned char waddr8, raddr8; + + waddr8 = i + 1; + raddr8 = i; + if (i < size * 4 - 1) + mem8_base[waddr8] = pattern8 + 1; + if (mem8_base[raddr8] != pattern8) { + return -21; + } + pattern8++; + } + + /* verify pattern 2 (0x00~0xffff) */ + pattern16 = PATTERN0; + mem16_base[0] = pattern16; + for (i = 0; i < size * 2; i++) { + if (i < size * 2 - 1) + mem16_base[i + 1] = pattern16 + 1; + if (mem16_base[i] != pattern16) { + return -22; + } + pattern16++; + } + + /* verify pattern 3 (0x00~0xffffffff) */ + pattern32 = PATTERN0; + mem32_base[0] = pattern32; + for (i = 0; i < size; i++) { + if (i < size - 1) + mem32_base[i + 1] = pattern32 + 1; + if (mem32_base[i] != pattern32) { + return -23; + } + pattern32++; + } + + return 0; +} + +void mt_mem_init(const struct mt8173_sdram_params *sdram_params) +{ + int i = 0; + + /* init mempll */ + mem_pll_init(sdram_params); + + /* memory calibration */ + mt_set_emi(sdram_params); + + if (IS_ENABLED(CONFIG_MEMORY_TEST)) { + /* do memory test: + * set memory scan range 0x2000 + * larger test length, longer system boot up time + */ + i = complex_mem_test(DDR_BASE, 0x2000); + + printk(BIOS_DEBUG, "[MEM] complex R/W mem test %s : %d\n", + (i == 0) ? "pass" : "fail", i); + + ASSERT(i == 0); + } +} diff --git a/src/soc/mediatek/mt8173/pll.c b/src/soc/mediatek/mt8173/pll.c index 3faf7859a7..d54538d91d 100644 --- a/src/soc/mediatek/mt8173/pll.c +++ b/src/soc/mediatek/mt8173/pll.c @@ -505,3 +505,29 @@ void mt_pll_set_aud_div(u32 rate) 7 << 28); } } + +void mt_mem_pll_config_pre(const struct mt8173_sdram_params *sdram_params) +{ + u32 mpll_sdm_pcw_20_0 = 0xF13B1; + + /* disable MPLL for adjusting memory clk frequency */ + clrbits_le32(&mt8173_apmixed->mpll_con0, BIT(0)); + /* MPLL configuration: mode selection */ + setbits_le32(&mt8173_apmixed->mpll_con0, BIT(16)); + clrbits_le32(&mt8173_apmixed->mpll_con0, 0x7 << 4); + clrbits_le32(&mt8173_apmixed->pll_test_con0, 1 << 31); + /* set RG_MPLL_SDM_PCW for feedback divide ratio */ + clrsetbits_le32(&mt8173_apmixed->mpll_con1, 0x1fffff, mpll_sdm_pcw_20_0); +} + +void mt_mem_pll_config_post(void) +{ + /* power up sequence starts: enable MPLL */ + setbits_le32(&mt8173_apmixed->mpll_con0, BIT(0)); +} + +void mt_mem_pll_mux(void) +{ + /* CLK_CFG_0 */ + mux_set_sel(&muxes[TOP_MEM_SEL], 1); /* 1: dmpll_ck */ +}