[U-Boot] [PATCH v3 43/57] ram: rk3399: Add LPPDR4 mr detection

Kever Yang
2019-07-16T13:18:27Z

On 2019/7/16 下午7:57, Jagan Teki wrote:
> Like data training in other sdram types, mr detection need
> to taken care for lpddr4 with looped rank and associated
> channel to make sure the proper configuration held.
>
> Once the mr detection successful for active and configured
> rank with channel number, the same can later reused during
> actual LPDDR4 initialization.
>
> So, add code to support for it.
>
> Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> Signed-off-by: YouMin Chen <cym@rock-chips.com>

Reviewed-by: Kever Yang <Kever.yang@rock-chips.com>

Thanks,
- Kever
> ---
> drivers/ram/rockchip/sdram_rk3399.c | 226 ++++++++++++++++++++++++++++
> 1 file changed, 226 insertions(+)
>
> diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c
> index da01f08732..623685e3c5 100644
> --- a/drivers/ram/rockchip/sdram_rk3399.c
> +++ b/drivers/ram/rockchip/sdram_rk3399.c
> @@ -1470,6 +1470,7 @@ static void dram_all_config(struct dram_info *dram,
> clrsetbits_le32(&dram->cru->glb_rst_con, 0x3, 0x3);
> }
>
> +#if !defined(CONFIG_RAM_RK3399_LPDDR4)
> static int default_data_training(struct dram_info *dram, u32 channel, u8 rank,
> struct rk3399_sdram_params *params)
> {
> @@ -1486,6 +1487,7 @@ static int default_data_training(struct dram_info *dram, u32 channel, u8 rank,
>
> return data_training(dram, channel, params, training_flag);
> }
> +#endif
>
> static int switch_to_phy_index1(struct dram_info *dram,
> const struct rk3399_sdram_params *params)
> @@ -1532,6 +1534,226 @@ static int switch_to_phy_index1(struct dram_info *dram,
> return 0;
> }
>
> +#if defined(CONFIG_RAM_RK3399_LPDDR4)
> +static u32 get_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf)
> +{
> + return ((readl(&pmusgrf->soc_con4) >> 10) & 0x1F);
> +}
> +
> +static void set_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf, u32 stride)
> +{
> + rk_clrsetreg(&pmusgrf->soc_con4, 0x1f << 10, stride << 10);
> +}
> +
> +static void set_cap_relate_config(const struct chan_info *chan,
> + struct rk3399_sdram_params *params,
> + unsigned int channel)
> +{
> + u32 *denali_ctl = chan->pctl->denali_ctl;
> + u32 tmp;
> + struct rk3399_msch_timings *noc_timing;
> +
> + if (params->base.dramtype == LPDDR3) {
> + tmp = (8 << params->ch[channel].cap_info.bw) /
> + (8 << params->ch[channel].cap_info.dbw);
> +
> + /**
> + * memdata_ratio
> + * 1 -> 0, 2 -> 1, 4 -> 2
> + */
> + clrsetbits_le32(&denali_ctl[197], 0x7,
> + (tmp >> 1));
> + clrsetbits_le32(&denali_ctl[198], 0x7 << 8,
> + (tmp >> 1) << 8);
> + }
> +
> + noc_timing = &params->ch[channel].noc_timings;
> +
> + /*
> + * noc timing bw relate timing is 32 bit, and real bw is 16bit
> + * actually noc reg is setting at function dram_all_config
> + */
> + if (params->ch[channel].cap_info.bw == 16 &&
> + noc_timing->ddrmode.b.mwrsize == 2) {
> + if (noc_timing->ddrmode.b.burstsize)
> + noc_timing->ddrmode.b.burstsize -= 1;
> + noc_timing->ddrmode.b.mwrsize -= 1;
> + noc_timing->ddrtimingc0.b.burstpenalty *= 2;
> + noc_timing->ddrtimingc0.b.wrtomwr *= 2;
> + }
> +}
> +
> +static u32 calculate_ddrconfig(struct rk3399_sdram_params *params, u32 channel)
> +{
> + unsigned int cs0_row = params->ch[channel].cap_info.cs0_row;
> + unsigned int col = params->ch[channel].cap_info.col;
> + unsigned int bw = params->ch[channel].cap_info.bw;
> + u16 ddr_cfg_2_rbc[] = {
> + /*
> + * [6] highest bit col
> + * [5:3] max row(14+n)
> + * [2] insertion row
> + * [1:0] col(9+n),col, data bus 32bit
> + *
> + * highbitcol, max_row, insertion_row, col
> + */
> + ((0 << 6) | (2 << 3) | (0 << 2) | 0), /* 0 */
> + ((0 << 6) | (2 << 3) | (0 << 2) | 1), /* 1 */
> + ((0 << 6) | (1 << 3) | (0 << 2) | 2), /* 2 */
> + ((0 << 6) | (0 << 3) | (0 << 2) | 3), /* 3 */
> + ((0 << 6) | (2 << 3) | (1 << 2) | 1), /* 4 */
> + ((0 << 6) | (1 << 3) | (1 << 2) | 2), /* 5 */
> + ((1 << 6) | (0 << 3) | (0 << 2) | 2), /* 6 */
> + ((1 << 6) | (1 << 3) | (0 << 2) | 2), /* 7 */
> + };
> + u32 i;
> +
> + col -= (bw == 2) ? 0 : 1;
> + col -= 9;
> +
> + for (i = 0; i < 4; i++) {
> + if ((col == (ddr_cfg_2_rbc[i] & 0x3)) &&
> + (cs0_row <= (((ddr_cfg_2_rbc[i] >> 3) & 0x7) + 14)))
> + break;
> + }
> +
> + if (i >= 4)
> + i = -EINVAL;
> +
> + return i;
> +}
> +
> +/**
> + * read mr_num mode register
> + * rank = 1: cs0
> + * rank = 2: cs1
> + */
> +static int read_mr(struct rk3399_ddr_pctl_regs *ddr_pctl_regs, u32 rank,
> + u32 mr_num, u32 *buf)
> +{
> + s32 timeout = 100;
> +
> + writel(((1 << 16) | (((rank == 2) ? 1 : 0) << 8) | mr_num) << 8,
> + &ddr_pctl_regs->denali_ctl[118]);
> +
> + while (0 == (readl(&ddr_pctl_regs->denali_ctl[203]) &
> + ((1 << 21) | (1 << 12)))) {
> + udelay(1);
> +
> + if (timeout <= 0) {
> + printf("%s: pctl timeout!\n", __func__);
> + return -ETIMEDOUT;
> + }
> +
> + timeout--;
> + }
> +
> + if (!(readl(&ddr_pctl_regs->denali_ctl[203]) & (1 << 12))) {
> + *buf = readl(&ddr_pctl_regs->denali_ctl[119]) & 0xFF;
> + } else {
> + printf("%s: read mr failed with 0x%x status\n", __func__,
> + readl(&ddr_pctl_regs->denali_ctl[17]) & 0x3);
> + *buf = 0;
> + }
> +
> + setbits_le32(&ddr_pctl_regs->denali_ctl[205], (1 << 21) | (1 << 12));
> +
> + return 0;
> +}
> +
> +static int lpddr4_mr_detect(struct dram_info *dram, u32 channel, u8 rank,
> + struct rk3399_sdram_params *params)
> +{
> + u64 cs0_cap;
> + u32 stride;
> + u32 cs = 0, col = 0, bk = 0, bw = 0, row_3_4 = 0;
> + u32 cs0_row = 0, cs1_row = 0, ddrconfig = 0;
> + u32 mr5, mr12, mr14;
> + struct chan_info *chan = &dram->chan[channel];
> + struct rk3399_ddr_pctl_regs *ddr_pctl_regs = chan->pctl;
> + void __iomem *addr = NULL;
> + int ret = 0;
> + u32 val;
> +
> + stride = get_ddr_stride(dram->pmusgrf);
> +
> + if (params->ch[channel].cap_info.col == 0) {
> + ret = -EPERM;
> + goto end;
> + }
> +
> + cs = params->ch[channel].cap_info.rank;
> + col = params->ch[channel].cap_info.col;
> + bk = params->ch[channel].cap_info.bk;
> + bw = params->ch[channel].cap_info.bw;
> + row_3_4 = params->ch[channel].cap_info.row_3_4;
> + cs0_row = params->ch[channel].cap_info.cs0_row;
> + cs1_row = params->ch[channel].cap_info.cs1_row;
> + ddrconfig = params->ch[channel].cap_info.ddrconfig;
> +
> + /* 2GB */
> + params->ch[channel].cap_info.rank = 2;
> + params->ch[channel].cap_info.col = 10;
> + params->ch[channel].cap_info.bk = 3;
> + params->ch[channel].cap_info.bw = 2;
> + params->ch[channel].cap_info.row_3_4 = 0;
> + params->ch[channel].cap_info.cs0_row = 15;
> + params->ch[channel].cap_info.cs1_row = 15;
> + params->ch[channel].cap_info.ddrconfig = 1;
> +
> + set_memory_map(chan, channel, params);
> + params->ch[channel].cap_info.ddrconfig =
> + calculate_ddrconfig(params, channel);
> + set_ddrconfig(chan, params, channel,
> + params->ch[channel].cap_info.ddrconfig);
> + set_cap_relate_config(chan, params, channel);
> +
> + cs0_cap = (1 << (params->ch[channel].cap_info.bw
> + + params->ch[channel].cap_info.col
> + + params->ch[channel].cap_info.bk
> + + params->ch[channel].cap_info.cs0_row));
> +
> + if (params->ch[channel].cap_info.row_3_4)
> + cs0_cap = cs0_cap * 3 / 4;
> +
> + if (channel == 0)
> + set_ddr_stride(dram->pmusgrf, 0x17);
> + else
> + set_ddr_stride(dram->pmusgrf, 0x18);
> +
> + /* read and write data to DRAM, avoid be optimized by compiler. */
> + if (rank == 1)
> + addr = (void __iomem *)0x100;
> + else if (rank == 2)
> + addr = (void __iomem *)(cs0_cap + 0x100);
> +
> + val = readl(addr);
> + writel(val + 1, addr);
> +
> + read_mr(ddr_pctl_regs, rank, 5, &mr5);
> + read_mr(ddr_pctl_regs, rank, 12, &mr12);
> + read_mr(ddr_pctl_regs, rank, 14, &mr14);
> +
> + if (mr5 == 0 || mr12 != 0x4d || mr14 != 0x4d) {
> + ret = -EINVAL;
> + goto end;
> + }
> +end:
> + params->ch[channel].cap_info.rank = cs;
> + params->ch[channel].cap_info.col = col;
> + params->ch[channel].cap_info.bk = bk;
> + params->ch[channel].cap_info.bw = bw;
> + params->ch[channel].cap_info.row_3_4 = row_3_4;
> + params->ch[channel].cap_info.cs0_row = cs0_row;
> + params->ch[channel].cap_info.cs1_row = cs1_row;
> + params->ch[channel].cap_info.ddrconfig = ddrconfig;
> +
> + set_ddr_stride(dram->pmusgrf, stride);
> +
> + return ret;
> +}
> +#endif /* CONFIG_RAM_RK3399_LPDDR4 */
> +
> static unsigned char calculate_stride(struct rk3399_sdram_params *params)
> {
> unsigned int stride = params->base.stride;
> @@ -1762,7 +1984,11 @@ static int conv_of_platdata(struct udevice *dev)
> #endif
>
> static const struct sdram_rk3399_ops rk3399_ops = {
> +#if !defined(CONFIG_RAM_RK3399_LPDDR4)
> .data_training = default_data_training,
> +#else
> + .data_training = lpddr4_mr_detect,
> +#endif
> };
>
> static int rk3399_dmc_init(struct udevice *dev)