From 53c45f0ca27cbf6acd840e87beaa1ba1be74399b Mon Sep 17 00:00:00 2001 From: huang lin Date: Tue, 17 Nov 2015 14:20:26 +0800 Subject: rockchip: add rk3036 sdram driver add rk3036 sdram driver so we can set up sdram in SPL Signed-off-by: Lin Huang Acked-by: Simon Glass --- arch/arm/mach-rockchip/rk3036/sdram_rk3036.c | 764 +++++++++++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100644 arch/arm/mach-rockchip/rk3036/sdram_rk3036.c (limited to 'arch/arm/mach-rockchip/rk3036/sdram_rk3036.c') diff --git a/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c b/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c new file mode 100644 index 0000000..7a05e31 --- /dev/null +++ b/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c @@ -0,0 +1,764 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * we can not fit the code to access the device tree in SPL + * (due to 4K SRAM size limits), so these are hard-coded + */ +#define CRU_BASE 0x20000000 +#define GRF_BASE 0x20008000 +#define DDR_PHY_BASE 0x2000a000 +#define DDR_PCTL_BASE 0x20004000 +#define CPU_AXI_BUS_BASE 0x10128000 + +struct rk3036_sdram_priv { + struct rk3036_cru *cru; + struct rk3036_grf *grf; + struct rk3036_ddr_phy *phy; + struct rk3036_ddr_pctl *pctl; + struct rk3036_service_sys *axi_bus; + + /* ddr die config */ + struct rk3036_ddr_config ddr_config; +}; + +/* use integer mode, 396MHz dpll setting + * refdiv, fbdiv, postdiv1, postdiv2 + */ +const struct pll_div dpll_init_cfg = {1, 66, 4, 1}; + +/* 396Mhz ddr timing */ +const struct rk3036_ddr_timing ddr_timing = {0x18c, + {0x18c, 0xc8, 0x1f4, 0x27, 0x4e, + 0x4, 0x8b, 0x06, 0x03, 0x0, 0x06, 0x05, 0x0f, 0x15, 0x06, 0x04, 0x04, + 0x06, 0x04, 0x200, 0x03, 0x0a, 0x40, 0x2710, 0x01, 0x05, 0x05, 0x03, + 0x0c, 0x28, 0x100, 0x0, 0x04, 0x0}, + {{0x420, 0x42, 0x0, 0x0}, 0x01, 0x60}, + {0x24717315} }; + +/* + * [7:6] bank(n:n bit bank) + * [5:4] row(13+n) + * [3] cs(0:1 cs, 1:2 cs) + * [2:1] bank(n:n bit bank) + * [0] col(10+n) + */ +const char ddr_cfg_2_rbc[] = { + ((3 << 6) | (3 << 4) | (0 << 3) | (0 << 1) | 1), + ((0 << 6) | (1 << 4) | (0 << 3) | (3 << 1) | 0), + ((0 << 6) | (2 << 4) | (0 << 3) | (3 << 1) | 0), + ((0 << 6) | (3 << 4) | (0 << 3) | (3 << 1) | 0), + ((0 << 6) | (1 << 4) | (0 << 3) | (3 << 1) | 1), + ((0 << 6) | (2 << 4) | (0 << 3) | (3 << 1) | 1), + ((0 << 6) | (3 << 4) | (0 << 3) | (3 << 1) | 1), + ((0 << 6) | (0 << 4) | (0 << 3) | (3 << 1) | 0), + ((0 << 6) | (0 << 4) | (0 << 3) | (3 << 1) | 1), + ((0 << 6) | (3 << 4) | (1 << 3) | (3 << 1) | 0), + ((0 << 6) | (3 << 4) | (1 << 3) | (3 << 1) | 1), + ((1 << 6) | (2 << 4) | (0 << 3) | (2 << 1) | 0), + ((3 << 6) | (2 << 4) | (0 << 3) | (0 << 1) | 1), + ((3 << 6) | (3 << 4) | (0 << 3) | (0 << 1) | 0), +}; + +/* DDRPHY REG */ +enum { + /* DDRPHY_REG1 */ + SOFT_RESET_MASK = 3, + SOFT_RESET_SHIFT = 2, + + /* DDRPHY_REG2 */ + MEMORY_SELECT_DDR3 = 0 << 6, + DQS_SQU_CAL_NORMAL_MODE = 0 << 1, + DQS_SQU_CAL_START = 1 << 0, + DQS_SQU_NO_CAL = 0 << 0, + + /* DDRPHY_REG2A */ + CMD_DLL_BYPASS = 1 << 4, + CMD_DLL_BYPASS_DISABLE = 0 << 4, + HIGH_8BIT_DLL_BYPASS = 1 << 3, + HIGH_8BIT_DLL_BYPASS_DISABLE = 0 << 3, + LOW_8BIT_DLL_BYPASS = 1 << 2, + LOW_8BIT_DLL_BYPASS_DISABLE = 0 << 2, + + /* DDRPHY_REG19 */ + CMD_FEEDBACK_ENABLE = 1 << 5, + CMD_SLAVE_DLL_INVERSE_MODE = 1 << 4, + CMD_SLAVE_DLL_NO_INVERSE_MODE = 0 << 4, + CMD_SLAVE_DLL_ENALBE = 1 << 3, + CMD_TX_SLAVE_DLL_DELAY_MASK = 7, + CMD_TX_SLAVE_DLL_DELAY_SHIFT = 0, + + /* DDRPHY_REG6 */ + LEFT_CHN_TX_DQ_PHASE_BYPASS_90 = 1 << 4, + LEFT_CHN_TX_DQ_PHASE_BYPASS_0 = 0 << 4, + LEFT_CHN_TX_DQ_DLL_ENABLE = 1 << 3, + LEFT_CHN_TX_DQ_DLL_DELAY_MASK = 7, + LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT = 0, + + /* DDRPHY_REG8 */ + LEFT_CHN_RX_DQS_DELAY_TAP_MASK = 3, + LEFT_CHN_RX_DQS_DELAY_TAP_SHIFT = 0, + + /* DDRPHY_REG9 */ + RIGHT_CHN_TX_DQ_PHASE_BYPASS_90 = 1 << 4, + RIGHT_CHN_TX_DQ_PHASE_BYPASS_0 = 0 << 4, + RIGHT_CHN_TX_DQ_DLL_ENABLE = 1 << 3, + RIGHT_CHN_TX_DQ_DLL_DELAY_MASK = 7, + RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT = 0, + + /* DDRPHY_REG11 */ + RIGHT_CHN_RX_DQS_DELAY_TAP_MASK = 3, + RIGHT_CHN_RX_DQS_DELAY_TAP_SHIFT = 0, + + /* DDRPHY_REG62 */ + CAL_DONE_MASK = 3, + HIGH_8BIT_CAL_DONE = 1 << 1, + LOW_8BIT_CAL_DONE = 1 << 0, +}; + +/* PTCL */ +enum { + /* PCTL_DFISTCFG0 */ + DFI_INIT_START = 1 << 0, + DFI_DATA_BYTE_DISABLE_EN = 1 << 2, + + /* PCTL_DFISTCFG1 */ + DFI_DRAM_CLK_SR_EN = 1 << 0, + DFI_DRAM_CLK_DPD_EN = 1 << 1, + + /* PCTL_DFISTCFG2 */ + DFI_PARITY_INTR_EN = 1 << 0, + DFI_PARITY_EN = 1 << 1, + + /* PCTL_DFILPCFG0 */ + TLP_RESP_TIME_SHIFT = 16, + LP_SR_EN = 1 << 8, + LP_PD_EN = 1 << 0, + + /* PCTL_DFIODTCFG */ + RANK0_ODT_WRITE_SEL = 1 << 3, + RANK1_ODT_WRITE_SEL = 1 << 11, + + /* PCTL_DFIODTCFG1 */ + ODT_LEN_BL8_W_SHIFT = 16, + + /* PCTL_MCFG */ + TFAW_CFG_MASK = 3, + TFAW_CFG_SHIFT = 18, + PD_EXIT_SLOW_MODE = 0 << 17, + PD_ACTIVE_POWER_DOWN = 1 << 16, + PD_IDLE_MASK = 0xff, + PD_IDLE_SHIFT = 8, + MEM_BL4 = 0 << 0, + MEM_BL8 = 1 << 0, + + /* PCTL_MCFG1 */ + HW_EXIT_IDLE_EN_MASK = 1, + HW_EXIT_IDLE_EN_SHIFT = 31, + SR_IDLE_MASK = 0x1ff, + SR_IDLE_SHIFT = 0, + + /* PCTL_SCFG */ + HW_LOW_POWER_EN = 1 << 0, + + /* PCTL_POWCTL */ + POWER_UP_START = 1 << 0, + + /* PCTL_POWSTAT */ + POWER_UP_DONE = 1 << 0, + + /* PCTL_MCMD */ + START_CMD = 1 << 31, + BANK_ADDR_MASK = 7, + BANK_ADDR_SHIFT = 17, + CMD_ADDR_MASK = 0x1fff, + CMD_ADDR_SHIFT = 4, + DESELECT_CMD = 0, + PREA_CMD, + REF_CMD, + MRS_CMD, + ZQCS_CMD, + ZQCL_CMD, + RSTL_CMD, + MRR_CMD = 8, + + /* PCTL_STAT */ + INIT_MEM = 0, + CONFIG, + CONFIG_REQ, + ACCESS, + ACCESS_REQ, + LOW_POWER, + LOW_POWER_ENTRY_REQ, + LOW_POWER_EXIT_REQ, + PCTL_STAT_MASK = 7, + + /* PCTL_SCTL */ + INIT_STATE = 0, + CFG_STATE = 1, + GO_STATE = 2, + SLEEP_STATE = 3, + WAKEUP_STATE = 4, +}; + +/* GRF_SOC_CON2 */ +#define MSCH4_MAINDDR3 (1 << 7) +#define PHY_DRV_ODT_SET(n) ((n << 4) | n) +#define DDR3_DLL_RESET (1 << 8) + +/* CK pull up/down driver strength control */ +enum { + PHY_RON_DISABLE = 0, + PHY_RON_309OHM = 1, + PHY_RON_155OHM, + PHY_RON_103OHM = 3, + PHY_RON_63OHM = 5, + PHY_RON_45OHM = 7, + PHY_RON_77OHM, + PHY_RON_62OHM, + PHY_RON_52OHM, + PHY_RON_44OHM, + PHY_RON_39OHM, + PHY_RON_34OHM, + PHY_RON_31OHM, + PHY_RON_28OHM, +}; + +/* DQ pull up/down control */ +enum { + PHY_RTT_DISABLE = 0, + PHY_RTT_861OHM = 1, + PHY_RTT_431OHM, + PHY_RTT_287OHM, + PHY_RTT_216OHM, + PHY_RTT_172OHM, + PHY_RTT_145OHM, + PHY_RTT_124OHM, + PHY_RTT_215OHM, + PHY_RTT_144OHM = 0xa, + PHY_RTT_123OHM, + PHY_RTT_108OHM, + PHY_RTT_96OHM, + PHY_RTT_86OHM, + PHY_RTT_78OHM, +}; + +/* DQS squelch DLL delay */ +enum { + DQS_DLL_NO_DELAY = 0, + DQS_DLL_22P5_DELAY, + DQS_DLL_45_DELAY, + DQS_DLL_67P5_DELAY, + DQS_DLL_90_DELAY, + DQS_DLL_112P5_DELAY, + DQS_DLL_135_DELAY, + DQS_DLL_157P5_DELAY, +}; + +/* GRF_OS_REG1 */ +enum { + /* + * 000: lpddr + * 001: ddr + * 010: ddr2 + * 011: ddr3 + * 100: lpddr2-s2 + * 101: lpddr2-s4 + * 110: lpddr3 + */ + DDR_TYPE_MASK = 7, + DDR_TYPE_SHIFT = 13, + + /* 0: 1 chn, 1: 2 chn */ + DDR_CHN_CNT_SHIFT = 12, + + /* 0: 1 rank, 1: 2 rank */ + DDR_RANK_CNT_MASK = 1, + DDR_RANK_CNT_SHIFT = 11, + + /* + * 00: 9col + * 01: 10col + * 10: 11col + * 11: 12col + */ + DDR_COL_MASK = 3, + DDR_COL_SHIFT = 9, + + /* 0: 8 bank, 1: 4 bank*/ + DDR_BANK_MASK = 1, + DDR_BANK_SHIFT = 8, + + /* + * 00: 13 row + * 01: 14 row + * 10: 15 row + * 11: 16 row + */ + DDR_CS0_ROW_MASK = 3, + DDR_CS0_ROW_SHIFT = 6, + DDR_CS1_ROW_MASK = 3, + DDR_CS1_ROW_SHIFT = 4, + + /* + * 00: 32 bit + * 01: 16 bit + * 10: 8 bit + * rk3036 only support 16bit + */ + DDR_BW_MASK = 3, + DDR_BW_SHIFT = 2, + DDR_DIE_BW_MASK = 3, + DDR_DIE_BW_SHIFT = 0, +}; + +static void rkdclk_init(struct rk3036_sdram_priv *priv) +{ + struct rk3036_pll *pll = &priv->cru->pll[1]; + + /* pll enter slow-mode */ + rk_clrsetreg(&priv->cru->cru_mode_con, + DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_SLOW << DPLL_MODE_SHIFT); + + /* use integer mode */ + rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); + + rk_clrsetreg(&pll->con0, + PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK, + (dpll_init_cfg.postdiv1 << PLL_POSTDIV1_SHIFT) | + dpll_init_cfg.fbdiv); + rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT | + PLL_REFDIV_MASK << PLL_REFDIV_SHIFT, + (dpll_init_cfg.postdiv2 << PLL_POSTDIV2_SHIFT | + dpll_init_cfg.refdiv << PLL_REFDIV_SHIFT)); + + /* waiting for pll lock */ + while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)) + rockchip_udelay(1); + + /* PLL enter normal-mode */ + rk_clrsetreg(&priv->cru->cru_mode_con, + DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_NORM << DPLL_MODE_SHIFT); +} + +static void copy_to_reg(u32 *dest, const u32 *src, u32 n) +{ + int i; + + for (i = 0; i < n / sizeof(u32); i++) { + writel(*src, dest); + src++; + dest++; + } +} + +void phy_pctrl_reset(struct rk3036_sdram_priv *priv) +{ + struct rk3036_ddr_phy *ddr_phy = priv->phy; + + rk_clrsetreg(&priv->cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT | + 1 << DDRCTRL_SRST_SHIFT | 1 << DDRPHY_PSRST_SHIFT | + 1 << DDRPHY_SRST_SHIFT, + 1 << DDRCTRL_PSRST_SHIFT | 1 << DDRCTRL_SRST_SHIFT | + 1 << DDRPHY_PSRST_SHIFT | 1 << DDRPHY_SRST_SHIFT); + + rockchip_udelay(10); + + rk_clrreg(&priv->cru->cru_softrst_con[5], 1 << DDRPHY_PSRST_SHIFT | + 1 << DDRPHY_SRST_SHIFT); + rockchip_udelay(10); + + rk_clrreg(&priv->cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT | + 1 << DDRCTRL_SRST_SHIFT); + rockchip_udelay(10); + + clrsetbits_le32(&ddr_phy->ddrphy_reg1, + SOFT_RESET_MASK << SOFT_RESET_SHIFT, + 0 << SOFT_RESET_SHIFT); + rockchip_udelay(10); + clrsetbits_le32(&ddr_phy->ddrphy_reg1, + SOFT_RESET_MASK << SOFT_RESET_SHIFT, + 3 << SOFT_RESET_SHIFT); + + rockchip_udelay(1); +} + +void phy_dll_bypass_set(struct rk3036_sdram_priv *priv, unsigned int freq) +{ + struct rk3036_ddr_phy *ddr_phy = priv->phy; + + if (freq < ddr_timing.freq) { + writel(CMD_DLL_BYPASS | HIGH_8BIT_DLL_BYPASS | + LOW_8BIT_DLL_BYPASS, &ddr_phy->ddrphy_reg2a); + + writel(LEFT_CHN_TX_DQ_PHASE_BYPASS_90 | + LEFT_CHN_TX_DQ_DLL_ENABLE | + (0 & LEFT_CHN_TX_DQ_DLL_DELAY_MASK) << + LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT, &ddr_phy->ddrphy_reg6); + + writel(RIGHT_CHN_TX_DQ_PHASE_BYPASS_90 | + RIGHT_CHN_TX_DQ_DLL_ENABLE | + (0 & RIGHT_CHN_TX_DQ_DLL_DELAY_MASK) << + RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT, + &ddr_phy->ddrphy_reg9); + } else { + writel(CMD_DLL_BYPASS_DISABLE | HIGH_8BIT_DLL_BYPASS_DISABLE | + LOW_8BIT_DLL_BYPASS_DISABLE, &ddr_phy->ddrphy_reg2a); + + writel(LEFT_CHN_TX_DQ_PHASE_BYPASS_0 | + LEFT_CHN_TX_DQ_DLL_ENABLE | + (4 & LEFT_CHN_TX_DQ_DLL_DELAY_MASK) << + LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT, + &ddr_phy->ddrphy_reg6); + + writel(RIGHT_CHN_TX_DQ_PHASE_BYPASS_0 | + RIGHT_CHN_TX_DQ_DLL_ENABLE | + (4 & RIGHT_CHN_TX_DQ_DLL_DELAY_MASK) << + RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT, + &ddr_phy->ddrphy_reg9); + } + + writel(CMD_SLAVE_DLL_NO_INVERSE_MODE | CMD_SLAVE_DLL_ENALBE | + (0 & CMD_TX_SLAVE_DLL_DELAY_MASK) << + CMD_TX_SLAVE_DLL_DELAY_SHIFT, &ddr_phy->ddrphy_reg19); + + /* 45 degree delay */ + writel((DQS_DLL_45_DELAY & LEFT_CHN_RX_DQS_DELAY_TAP_MASK) << + LEFT_CHN_RX_DQS_DELAY_TAP_SHIFT, &ddr_phy->ddrphy_reg8); + writel((DQS_DLL_45_DELAY & RIGHT_CHN_RX_DQS_DELAY_TAP_MASK) << + RIGHT_CHN_RX_DQS_DELAY_TAP_SHIFT, &ddr_phy->ddrphy_reg11); +} + +static void send_command(struct rk3036_ddr_pctl *pctl, + u32 rank, u32 cmd, u32 arg) +{ + writel((START_CMD | (rank << 20) | arg | cmd), &pctl->mcmd); + rockchip_udelay(1); + while (readl(&pctl->mcmd) & START_CMD) + ; +} + +static void memory_init(struct rk3036_sdram_priv *priv) +{ + struct rk3036_ddr_pctl *pctl = priv->pctl; + + send_command(pctl, 3, DESELECT_CMD, 0); + rockchip_udelay(1); + send_command(pctl, 3, PREA_CMD, 0); + send_command(pctl, 3, MRS_CMD, + (0x02 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT | + (ddr_timing.phy_timing.mr[2] & CMD_ADDR_MASK) << + CMD_ADDR_SHIFT); + + send_command(pctl, 3, MRS_CMD, + (0x03 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT | + (ddr_timing.phy_timing.mr[3] & CMD_ADDR_MASK) << + CMD_ADDR_SHIFT); + + send_command(pctl, 3, MRS_CMD, + (0x01 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT | + (ddr_timing.phy_timing.mr[1] & CMD_ADDR_MASK) << + CMD_ADDR_SHIFT); + + send_command(pctl, 3, MRS_CMD, + (0x00 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT | + (ddr_timing.phy_timing.mr[0] & CMD_ADDR_MASK) << + CMD_ADDR_SHIFT | DDR3_DLL_RESET); + + send_command(pctl, 3, ZQCL_CMD, 0); +} + +static void data_training(struct rk3036_sdram_priv *priv) +{ + struct rk3036_ddr_phy *ddr_phy = priv->phy; + struct rk3036_ddr_pctl *pctl = priv->pctl; + u32 value; + + /* disable auto refresh */ + value = readl(&pctl->trefi), + writel(0, &pctl->trefi); + + clrsetbits_le32(&ddr_phy->ddrphy_reg2, 0x03, + DQS_SQU_CAL_NORMAL_MODE | DQS_SQU_CAL_START); + + rockchip_udelay(1); + while ((readl(&ddr_phy->ddrphy_reg62) & CAL_DONE_MASK) != + (HIGH_8BIT_CAL_DONE | LOW_8BIT_CAL_DONE)) { + ; + } + + clrsetbits_le32(&ddr_phy->ddrphy_reg2, 0x03, + DQS_SQU_CAL_NORMAL_MODE | DQS_SQU_NO_CAL); + + /* + * since data training will take about 20us, so send some auto + * refresh(about 7.8us) to complement the lost time + */ + send_command(pctl, 3, REF_CMD, 0); + send_command(pctl, 3, REF_CMD, 0); + send_command(pctl, 3, REF_CMD, 0); + + writel(value, &pctl->trefi); +} + +static void move_to_config_state(struct rk3036_sdram_priv *priv) +{ + unsigned int state; + struct rk3036_ddr_pctl *pctl = priv->pctl; + + while (1) { + state = readl(&pctl->stat) & PCTL_STAT_MASK; + switch (state) { + case LOW_POWER: + writel(WAKEUP_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MASK) + != ACCESS) + ; + /* + * If at low power state, need wakeup first, and then + * enter the config, so fallthrough + */ + case ACCESS: + /* fallthrough */ + case INIT_MEM: + writel(CFG_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MASK) != CONFIG) + ; + break; + case CONFIG: + return; + default: + break; + } + } +} + +static void move_to_access_state(struct rk3036_sdram_priv *priv) +{ + unsigned int state; + struct rk3036_ddr_pctl *pctl = priv->pctl; + + while (1) { + state = readl(&pctl->stat) & PCTL_STAT_MASK; + switch (state) { + case LOW_POWER: + writel(WAKEUP_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MASK) != ACCESS) + ; + break; + case INIT_MEM: + writel(CFG_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MASK) != CONFIG) + ; + /* fallthrough */ + case CONFIG: + writel(GO_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MASK) != ACCESS) + ; + break; + case ACCESS: + return; + default: + break; + } + } +} + +static void pctl_cfg(struct rk3036_sdram_priv *priv) +{ + struct rk3036_ddr_pctl *pctl = priv->pctl; + u32 burst_len; + u32 reg; + + writel(DFI_INIT_START | DFI_DATA_BYTE_DISABLE_EN, &pctl->dfistcfg0); + writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, &pctl->dfistcfg1); + writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2); + writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN, + &pctl->dfilpcfg0); + + writel(1, &pctl->dfitphyupdtype0); + writel(0x0d, &pctl->dfitphyrdlat); + + /* cs0 and cs1 write odt enable */ + writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL), + &pctl->dfiodtcfg); + + /* odt write length */ + writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1); + + /* phyupd and ctrlupd disabled */ + writel(0, &pctl->dfiupdcfg); + + if ((ddr_timing.noc_timing.burstlen << 1) == 4) + burst_len = MEM_BL4; + else + burst_len = MEM_BL8; + + copy_to_reg(&pctl->togcnt1u, &ddr_timing.pctl_timing.togcnt1u, + sizeof(struct rk3036_pctl_timing)); + reg = readl(&pctl->tcl); + writel(reg - 3, &pctl->dfitrddataen); + reg = readl(&pctl->tcwl); + writel(reg - 1, &pctl->dfitphywrlat); + + writel(burst_len | (1 & TFAW_CFG_MASK) << TFAW_CFG_SHIFT | + PD_EXIT_SLOW_MODE | PD_ACTIVE_POWER_DOWN | + (0 & PD_IDLE_MASK) << PD_IDLE_SHIFT, + &pctl->mcfg); + + writel(RK_SETBITS(MSCH4_MAINDDR3), &priv->grf->soc_con2); + setbits_le32(&pctl->scfg, HW_LOW_POWER_EN); +} + +static void phy_cfg(struct rk3036_sdram_priv *priv) +{ + struct rk3036_ddr_phy *ddr_phy = priv->phy; + struct rk3036_service_sys *axi_bus = priv->axi_bus; + + writel(ddr_timing.noc_timing.noc_timing, &axi_bus->ddrtiming); + writel(0x3f, &axi_bus->readlatency); + + writel(MEMORY_SELECT_DDR3 | DQS_SQU_CAL_NORMAL_MODE, + &ddr_phy->ddrphy_reg2); + + clrsetbits_le32(&ddr_phy->ddrphy_reg3, 1, ddr_timing.phy_timing.bl); + writel(ddr_timing.phy_timing.cl_al, &ddr_phy->ddrphy_reg4a); + writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg16); + writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg22); + writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg25); + writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg26); + writel(PHY_DRV_ODT_SET(PHY_RTT_216OHM), &ddr_phy->ddrphy_reg27); + writel(PHY_DRV_ODT_SET(PHY_RTT_216OHM), &ddr_phy->ddrphy_reg28); +} + +void dram_cfg_rbc(struct rk3036_sdram_priv *priv) +{ + char noc_config; + int i = 0; + struct rk3036_ddr_config config = priv->ddr_config; + struct rk3036_service_sys *axi_bus = priv->axi_bus; + + move_to_config_state(priv); + + /* 2bit in BIT1, 2 */ + if (config.rank == 2) { + noc_config = (config.cs0_row - 13) << 4 | config.bank << 1 | + 1 << 3 | (config.col - 10); + if (noc_config == ddr_cfg_2_rbc[9]) { + i = 9; + goto finish; + } else if (noc_config == ddr_cfg_2_rbc[10]) { + i = 10; + goto finish; + } + } + + noc_config = (config.cs0_row - 13) << 4 | config.bank << 1 | + (config.col - 10); + + for (i = 0; i < sizeof(ddr_cfg_2_rbc); i++) { + if (noc_config == ddr_cfg_2_rbc[i]) + goto finish; + } + + /* bank: 1 bit in BIT6,7, 1bit in BIT1, 2 */ + noc_config = 1 << 6 | (config.cs0_row - 13) << 4 | + 2 << 1 | (config.col - 10); + if (noc_config == ddr_cfg_2_rbc[11]) { + i = 11; + goto finish; + } + + /* bank: 2bit in BIT6,7 */ + noc_config = (config.bank << 6) | (config.cs0_row - 13) << 4 | + (config.col - 10); + + if (noc_config == ddr_cfg_2_rbc[0]) + i = 0; + else if (noc_config == ddr_cfg_2_rbc[12]) + i = 12; + else if (noc_config == ddr_cfg_2_rbc[13]) + i = 13; +finish: + writel(i, &axi_bus->ddrconf); + move_to_access_state(priv); +} + +static void sdram_all_config(struct rk3036_sdram_priv *priv) +{ + u32 os_reg = 0; + struct rk3036_ddr_config config = priv->ddr_config; + + os_reg = config.ddr_type << DDR_TYPE_SHIFT | + 0 << DDR_CHN_CNT_SHIFT | + (config.rank - 1) << DDR_RANK_CNT_SHIFT | + (config.col - 1) << DDR_COL_SHIFT | + (config.bank == 3 ? 0 : 1) << DDR_BANK_SHIFT | + (config.cs0_row - 13) << DDR_CS0_ROW_SHIFT | + (config.cs1_row - 13) << DDR_CS1_ROW_SHIFT | + 1 << DDR_BW_SHIFT | config.bw << DDR_DIE_BW_SHIFT; + writel(os_reg, &priv->grf->os_reg[1]); +} + +size_t sdram_size(void) +{ + u32 size, os_reg, cs0_row, cs1_row, col, bank, rank; + struct rk3036_grf *grf = (void *)GRF_BASE; + + os_reg = readl(&grf->os_reg[1]); + + cs0_row = 13 + ((os_reg >> DDR_CS0_ROW_SHIFT) & DDR_CS0_ROW_MASK); + cs1_row = 13 + ((os_reg >> DDR_CS1_ROW_SHIFT) & DDR_CS1_ROW_MASK); + col = 9 + ((os_reg >> DDR_COL_SHIFT) & DDR_COL_MASK); + bank = 3 - ((os_reg >> DDR_BANK_SHIFT) & DDR_BANK_MASK); + rank = 1 + ((os_reg >> DDR_RANK_CNT_SHIFT) & DDR_RANK_CNT_MASK); + + /* row + col + bank + bw(rk3036 only support 16bit, so fix in 1) */ + size = 1 << (cs0_row + col + bank + 1); + + if (rank > 1) + size += size >> (cs0_row - cs1_row); + + return size; +} + +void sdram_init(void) +{ + struct rk3036_sdram_priv sdram_priv; + + sdram_priv.cru = (void *)CRU_BASE; + sdram_priv.grf = (void *)GRF_BASE; + sdram_priv.phy = (void *)DDR_PHY_BASE; + sdram_priv.pctl = (void *)DDR_PCTL_BASE; + sdram_priv.axi_bus = (void *)CPU_AXI_BUS_BASE; + + get_ddr_config(&sdram_priv.ddr_config); + sdram_all_config(&sdram_priv); + rkdclk_init(&sdram_priv); + phy_pctrl_reset(&sdram_priv); + phy_dll_bypass_set(&sdram_priv, ddr_timing.freq); + pctl_cfg(&sdram_priv); + phy_cfg(&sdram_priv); + writel(POWER_UP_START, &sdram_priv.pctl->powctl); + while (!(readl(&sdram_priv.pctl->powstat) & POWER_UP_DONE)) + ; + memory_init(&sdram_priv); + move_to_config_state(&sdram_priv); + data_training(&sdram_priv); + move_to_access_state(&sdram_priv); + dram_cfg_rbc(&sdram_priv); +} -- cgit v1.1