All of lore.kernel.org
 help / color / mirror / Atom feed
From: Johan Jonker <jbx6244@gmail.com>
To: kever.yang@rock-chips.com
Cc: sjg@chromium.org, philipp.tomsich@vrull.eu, lukma@denx.de,
	seanga2@gmail.com, u-boot@lists.denx.de
Subject: [PATCH v9 05/16] rockchip: rk3066: add sdram driver
Date: Mon,  4 Apr 2022 16:19:15 +0200	[thread overview]
Message-ID: <20220404141926.6085-6-jbx6244@gmail.com> (raw)
In-Reply-To: <20220404141926.6085-1-jbx6244@gmail.com>

From: Paweł Jarosz <paweljarosz3691@gmail.com>

Add rockchip rk3066 sdram driver

Signed-off-by: Paweł Jarosz <paweljarosz3691@gmail.com>
Signed-off-by: Johan Jonker <jbx6244@gmail.com>
---

Changed V7:
  restyle
  rename TEST_PATTERN
  changed function prefix
  changed #if where possible
  restyle U_BOOT_DRIVER structure
  remove rk3066_dmc_of_to_plat because dmc DT data
  only used in TPL in combination with OF_PLATDATA
---
 drivers/ram/rockchip/Makefile       |   1 +
 drivers/ram/rockchip/sdram_rk3066.c | 892 ++++++++++++++++++++++++++++
 2 files changed, 893 insertions(+)
 create mode 100644 drivers/ram/rockchip/sdram_rk3066.c

diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile
index ca1c289b88..6d530c29af 100644
--- a/drivers/ram/rockchip/Makefile
+++ b/drivers/ram/rockchip/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_ROCKCHIP_PX30) += sdram_px30.o sdram_pctl_px30.o sdram_phy_px30.o
 obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o
+obj-$(CONFIG_ROCKCHIP_RK3066) = sdram_rk3066.o
 obj-$(CONFIG_ROCKCHIP_RK3128) = sdram_rk3128.o
 obj-$(CONFIG_ROCKCHIP_RK3188) = sdram_rk3188.o
 obj-$(CONFIG_ROCKCHIP_RK322X) = sdram_rk322x.o
diff --git a/drivers/ram/rockchip/sdram_rk3066.c b/drivers/ram/rockchip/sdram_rk3066.c
new file mode 100644
index 0000000000..832154ee3a
--- /dev/null
+++ b/drivers/ram/rockchip/sdram_rk3066.c
@@ -0,0 +1,892 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Copyright 2014 Rockchip Inc.
+ *
+ * Adapted from the very similar rk3188 ddr init.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <hang.h>
+#include <init.h>
+#include <log.h>
+#include <ram.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/cru_rk3066.h>
+#include <asm/arch-rockchip/ddr_rk3188.h>
+#include <asm/arch-rockchip/grf_rk3066.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <asm/arch-rockchip/pmu_rk3188.h>
+#include <asm/arch-rockchip/sdram_rk3288.h>
+#include <asm/arch-rockchip/sdram.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+struct rk3066_dmc_chan_info {
+	struct rk3288_ddr_pctl *pctl;
+	struct rk3288_ddr_publ *publ;
+	struct rk3188_msch *msch;
+};
+
+struct rk3066_dmc_dram_info {
+	struct rk3066_dmc_chan_info chan[1];
+	struct ram_info info;
+	struct clk ddr_clk;
+	struct rk3066_cru *cru;
+	struct rk3066_grf *grf;
+	struct rk3066_sgrf *sgrf;
+	struct rk3188_pmu *pmu;
+};
+
+struct rk3066_dmc_sdram_params {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3066_dmc of_plat;
+#endif
+	struct rk3288_sdram_channel ch[2];
+	struct rk3288_sdram_pctl_timing pctl_timing;
+	struct rk3288_sdram_phy_timing phy_timing;
+	struct rk3288_base_params base;
+	int num_channels;
+	struct regmap *map;
+};
+
+const int rk3066_dmc_ddrconf_table[] = {
+	/*
+	 * [5:4] row(13+n)
+	 * [1:0] col(9+n), assume bw=2
+	 * row	    col,bw
+	 */
+	0,
+	(2 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT,
+	(1 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT,
+	(0 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT,
+	(2 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT,
+	(1 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT,
+	(0 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT,
+	(1 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT,
+	(0 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+};
+
+#define TEST_PATTERN			0x5aa5f00f
+#define DQS_GATE_TRAINING_ERROR_RANK0	BIT(4)
+#define DQS_GATE_TRAINING_ERROR_RANK1	BIT(5)
+
+static void rk3066_dmc_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++;
+	}
+}
+
+static void rk3066_dmc_ddr_reset(struct rk3066_cru *cru, u32 ch, u32 ctl, u32 phy)
+{
+	u32 phy_ctl_srstn_shift = 13;
+	u32 ctl_psrstn_shift = 11;
+	u32 ctl_srstn_shift = 10;
+	u32 phy_psrstn_shift = 9;
+	u32 phy_srstn_shift = 8;
+
+	rk_clrsetreg(&cru->cru_softrst_con[5],
+		     1 << phy_ctl_srstn_shift | 1 << ctl_psrstn_shift |
+		     1 << ctl_srstn_shift | 1 << phy_psrstn_shift |
+		     1 << phy_srstn_shift,
+		     phy << phy_ctl_srstn_shift | ctl << ctl_psrstn_shift |
+		     ctl << ctl_srstn_shift | phy << phy_psrstn_shift |
+		     phy << phy_srstn_shift);
+}
+
+static void rk3066_dmc_ddr_phy_ctl_reset(struct rk3066_cru *cru, u32 ch, u32 n)
+{
+	u32 phy_ctl_srstn_shift = 13;
+
+	rk_clrsetreg(&cru->cru_softrst_con[5],
+		     1 << phy_ctl_srstn_shift, n << phy_ctl_srstn_shift);
+}
+
+static void rk3066_dmc_phy_pctrl_reset(struct rk3066_cru *cru,
+				       struct rk3288_ddr_publ *publ,
+				       int channel)
+{
+	int i;
+
+	rk3066_dmc_ddr_reset(cru, channel, 1, 1);
+	udelay(1);
+	clrbits_le32(&publ->acdllcr, ACDLLCR_DLLSRST);
+	for (i = 0; i < 4; i++)
+		clrbits_le32(&publ->datx8[i].dxdllcr, DXDLLCR_DLLSRST);
+
+	udelay(10);
+	setbits_le32(&publ->acdllcr, ACDLLCR_DLLSRST);
+	for (i = 0; i < 4; i++)
+		setbits_le32(&publ->datx8[i].dxdllcr, DXDLLCR_DLLSRST);
+
+	udelay(10);
+	rk3066_dmc_ddr_reset(cru, channel, 1, 0);
+	udelay(10);
+	rk3066_dmc_ddr_reset(cru, channel, 0, 0);
+	udelay(10);
+}
+
+static void rk3066_dmc_phy_dll_bypass_set(struct rk3288_ddr_publ *publ, u32 freq)
+{
+	int i;
+
+	if (freq <= 250000000) {
+		if (freq <= 150000000)
+			clrbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		else
+			setbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		setbits_le32(&publ->acdllcr, ACDLLCR_DLLDIS);
+		for (i = 0; i < 4; i++)
+			setbits_le32(&publ->datx8[i].dxdllcr,
+				     DXDLLCR_DLLDIS);
+
+		setbits_le32(&publ->pir, PIR_DLLBYP);
+	} else {
+		clrbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		clrbits_le32(&publ->acdllcr, ACDLLCR_DLLDIS);
+		for (i = 0; i < 4; i++) {
+			clrbits_le32(&publ->datx8[i].dxdllcr,
+				     DXDLLCR_DLLDIS);
+		}
+
+		clrbits_le32(&publ->pir, PIR_DLLBYP);
+	}
+}
+
+static void rk3066_dmc_dfi_cfg(struct rk3288_ddr_pctl *pctl, u32 dramtype)
+{
+	writel(DFI_INIT_START, &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(2 << TCTRL_DELAY_TIME_SHIFT, &pctl->dfitctrldelay);
+	writel(1 << TPHY_WRDATA_TIME_SHIFT, &pctl->dfitphywrdata);
+	writel(0xf << TPHY_RDLAT_TIME_SHIFT, &pctl->dfitphyrdlat);
+	writel(2 << TDRAM_CLK_DIS_TIME_SHIFT, &pctl->dfitdramclkdis);
+	writel(2 << TDRAM_CLK_EN_TIME_SHIFT, &pctl->dfitdramclken);
+	writel(1, &pctl->dfitphyupdtype0);
+
+	/* CS0 and CS1 write ODT enable. */
+	writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL),
+	       &pctl->dfiodtcfg);
+	/* Write ODT length. */
+	writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1);
+	/* Disable phyupd and ctrlupd. */
+	writel(0, &pctl->dfiupdcfg);
+}
+
+static void rk3066_dmc_ddr_set_ddr3_mode(struct rk3066_grf *grf, uint channel,
+					 bool ddr3_mode)
+{
+	uint mask, val;
+
+	mask = MSCH4_MAINDDR3_MASK << MSCH4_MAINDDR3_SHIFT;
+	val = ddr3_mode << MSCH4_MAINDDR3_SHIFT;
+	rk_clrsetreg(&grf->soc_con2, mask, val);
+}
+
+static void rk3066_dmc_ddr_rank_2_row15en(struct rk3066_grf *grf, bool enable)
+{
+	uint mask, val;
+
+	mask = RANK_TO_ROW15_EN_MASK << RANK_TO_ROW15_EN_SHIFT;
+	val = enable << RANK_TO_ROW15_EN_SHIFT;
+	rk_clrsetreg(&grf->soc_con2, mask, val);
+}
+
+static void rk3066_dmc_pctl_cfg(int channel, struct rk3288_ddr_pctl *pctl,
+				struct rk3066_dmc_sdram_params *sdram_params,
+				struct rk3066_grf *grf)
+{
+	rk3066_dmc_copy_to_reg(&pctl->togcnt1u, &sdram_params->pctl_timing.togcnt1u,
+			       sizeof(sdram_params->pctl_timing));
+	switch (sdram_params->base.dramtype) {
+	case DDR3:
+		if (sdram_params->phy_timing.mr[1] & DDR3_DLL_DISABLE) {
+			writel(sdram_params->pctl_timing.tcl - 3,
+			       &pctl->dfitrddataen);
+		} else {
+			writel(sdram_params->pctl_timing.tcl - 2,
+			       &pctl->dfitrddataen);
+		}
+		writel(sdram_params->pctl_timing.tcwl - 1,
+		       &pctl->dfitphywrlat);
+		writel(0 << MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT | DDR3_EN |
+		       DDR2_DDR3_BL_8 | (6 - 4) << TFAW_SHIFT | PD_EXIT_SLOW |
+		       1 << PD_TYPE_SHIFT | 0 << PD_IDLE_SHIFT,
+		       &pctl->mcfg);
+		rk3066_dmc_ddr_set_ddr3_mode(grf, channel, true);
+		break;
+	}
+
+	setbits_le32(&pctl->scfg, 1);
+}
+
+static void rk3066_dmc_phy_cfg(const struct rk3066_dmc_chan_info *chan, int channel,
+			       struct rk3066_dmc_sdram_params *sdram_params)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3188_msch *msch = chan->msch;
+	uint ddr_freq_mhz = sdram_params->base.ddr_freq / 1000000;
+	u32 dinit2;
+	int i;
+
+	dinit2 = DIV_ROUND_UP(ddr_freq_mhz * 200000, 1000);
+	/* Set DDR PHY timing. */
+	rk3066_dmc_copy_to_reg(&publ->dtpr[0], &sdram_params->phy_timing.dtpr0,
+			       sizeof(sdram_params->phy_timing));
+	writel(sdram_params->base.noc_timing, &msch->ddrtiming);
+	writel(0x3f, &msch->readlatency);
+	writel(DIV_ROUND_UP(ddr_freq_mhz * 5120, 1000) << PRT_DLLLOCK_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 50, 1000) << PRT_DLLSRST_SHIFT |
+	       8 << PRT_ITMSRST_SHIFT, &publ->ptr[0]);
+	writel(DIV_ROUND_UP(ddr_freq_mhz * 500000, 1000) << PRT_DINIT0_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 400, 1000) << PRT_DINIT1_SHIFT,
+	       &publ->ptr[1]);
+	writel(min(dinit2, 0x1ffffU) << PRT_DINIT2_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 1000, 1000) << PRT_DINIT3_SHIFT,
+	       &publ->ptr[2]);
+
+	switch (sdram_params->base.dramtype) {
+	case DDR3:
+		clrbits_le32(&publ->pgcr, 0x1f);
+		clrsetbits_le32(&publ->dcr, DDRMD_MASK << DDRMD_SHIFT,
+				DDRMD_DDR3 << DDRMD_SHIFT);
+		break;
+	}
+	if (sdram_params->base.odt) {
+		/* Enable dynamic RTT. */
+		for (i = 0; i < 4; i++)
+			setbits_le32(&publ->datx8[i].dxgcr, DQSRTT | DQRTT);
+	} else {
+		/* Disable dynamic RTT. */
+		for (i = 0; i < 4; i++)
+			clrbits_le32(&publ->datx8[i].dxgcr, DQSRTT | DQRTT);
+	}
+}
+
+static void rk3066_dmc_phy_init(struct rk3288_ddr_publ *publ)
+{
+	setbits_le32(&publ->pir, PIR_INIT | PIR_DLLSRST
+		     | PIR_DLLLOCK | PIR_ZCAL | PIR_ITMSRST | PIR_CLRSR);
+	udelay(1);
+	while ((readl(&publ->pgsr) &
+		(PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE)) !=
+	       (PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE))
+		;
+}
+
+static void rk3066_dmc_send_command(struct rk3288_ddr_pctl *pctl, u32 rank,
+				    u32 cmd, u32 arg)
+{
+	writel((START_CMD | (rank << 20) | arg | cmd), &pctl->mcmd);
+	udelay(1);
+	while (readl(&pctl->mcmd) & START_CMD)
+		;
+}
+
+static inline void rk3066_dmc_send_command_op(struct rk3288_ddr_pctl *pctl,
+					      u32 rank, u32 cmd, u32 ma, u32 op)
+{
+	rk3066_dmc_send_command(pctl, rank, cmd, (ma & LPDDR2_MA_MASK) << LPDDR2_MA_SHIFT |
+				(op & LPDDR2_OP_MASK) << LPDDR2_OP_SHIFT);
+}
+
+static void rk3066_dmc_memory_init(struct rk3288_ddr_publ *publ,
+				   u32 dramtype)
+{
+	setbits_le32(&publ->pir,
+		     (PIR_INIT | PIR_DRAMINIT | PIR_LOCKBYP
+		      | PIR_ZCALBYP | PIR_CLRSR | PIR_ICPC
+		      | (dramtype == DDR3 ? PIR_DRAMRST : 0)));
+	udelay(1);
+	while ((readl(&publ->pgsr) & (PGSR_IDONE | PGSR_DLDONE))
+	       != (PGSR_IDONE | PGSR_DLDONE))
+		;
+}
+
+static void rk3066_dmc_move_to_config_state(struct rk3288_ddr_publ *publ,
+					    struct rk3288_ddr_pctl *pctl)
+{
+	unsigned int state;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MSK;
+
+		switch (state) {
+		case LOW_POWER:
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK)
+			       != ACCESS)
+				;
+			/* Wait DLL lock. */
+			while ((readl(&publ->pgsr) & PGSR_DLDONE)
+			       != PGSR_DLDONE)
+				;
+			/*
+			 * If at low power state we need to wakeup first
+			 * and then enter the config.
+			 */
+			fallthrough;
+		case ACCESS:
+			fallthrough;
+		case INIT_MEM:
+			writel(CFG_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG)
+				;
+			break;
+		case CONFIG:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void rk3066_dmc_set_bandwidth_ratio(const struct rk3066_dmc_chan_info *chan, int channel,
+					   u32 n, struct rk3066_grf *grf)
+{
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3188_msch *msch = chan->msch;
+
+	if (n == 1) {
+		setbits_le32(&pctl->ppcfg, 1);
+		setbits_le32(&msch->ddrtiming, 1 << 31);
+		/* Data byte disable. */
+		clrbits_le32(&publ->datx8[2].dxgcr, 1);
+		clrbits_le32(&publ->datx8[3].dxgcr, 1);
+		/* Disable DLL. */
+		setbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS);
+		setbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS);
+	} else {
+		clrbits_le32(&pctl->ppcfg, 1);
+		clrbits_le32(&msch->ddrtiming, 1 << 31);
+		/* Data byte enable.*/
+		setbits_le32(&publ->datx8[2].dxgcr, 1);
+		setbits_le32(&publ->datx8[3].dxgcr, 1);
+
+		/* Enable DLL. */
+		clrbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS);
+		clrbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS);
+		/* Reset DLL. */
+		clrbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLSRST);
+		clrbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLSRST);
+		udelay(10);
+		setbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLSRST);
+		setbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLSRST);
+	}
+	setbits_le32(&pctl->dfistcfg0, 1 << 2);
+}
+
+static int rk3066_dmc_data_training(const struct rk3066_dmc_chan_info *chan, int channel,
+				    struct rk3066_dmc_sdram_params *sdram_params)
+{
+	unsigned int j;
+	int ret = 0;
+	u32 rank;
+	int i;
+	u32 step[2] = { PIR_QSTRN, PIR_RVTRN };
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+
+	/* Disable auto refresh. */
+	writel(0, &pctl->trefi);
+
+	if (sdram_params->base.dramtype != LPDDR3)
+		setbits_le32(&publ->pgcr, 1 << PGCR_DQSCFG_SHIFT);
+	rank = sdram_params->ch[channel].rank | 1;
+	for (j = 0; j < ARRAY_SIZE(step); j++) {
+		/*
+		 * Trigger QSTRN and RVTRN.
+		 * Clear DTDONE status.
+		 */
+		setbits_le32(&publ->pir, PIR_CLRSR);
+
+		/* Trigger DTT. */
+		setbits_le32(&publ->pir,
+			     PIR_INIT | step[j] | PIR_LOCKBYP | PIR_ZCALBYP |
+			     PIR_CLRSR);
+		udelay(1);
+		/* Wait echo byte DTDONE. */
+		while ((readl(&publ->datx8[0].dxgsr[0]) & rank)
+		       != rank)
+			;
+		while ((readl(&publ->datx8[1].dxgsr[0]) & rank)
+		       != rank)
+			;
+		if (!(readl(&pctl->ppcfg) & 1)) {
+			while ((readl(&publ->datx8[2].dxgsr[0])
+				& rank) != rank)
+				;
+			while ((readl(&publ->datx8[3].dxgsr[0])
+				& rank) != rank)
+				;
+		}
+		if (readl(&publ->pgsr) &
+		    (PGSR_DTERR | PGSR_RVERR | PGSR_RVEIRR)) {
+			ret = -1;
+			break;
+		}
+	}
+	/* Send some auto refresh to complement the lost while DTT. */
+	for (i = 0; i < (rank > 1 ? 8 : 4); i++)
+		rk3066_dmc_send_command(pctl, rank, REF_CMD, 0);
+
+	if (sdram_params->base.dramtype != LPDDR3)
+		clrbits_le32(&publ->pgcr, 1 << PGCR_DQSCFG_SHIFT);
+
+	/* Resume auto refresh. */
+	writel(sdram_params->pctl_timing.trefi, &pctl->trefi);
+
+	return ret;
+}
+
+static void rk3066_dmc_move_to_access_state(const struct rk3066_dmc_chan_info *chan)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	unsigned int state;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MSK;
+
+		switch (state) {
+		case LOW_POWER:
+			if (((readl(&pctl->stat) >> LP_TRIG_SHIFT) &
+			     LP_TRIG_MASK) == 1)
+				return;
+
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS)
+				;
+			/* Wait DLL lock. */
+			while ((readl(&publ->pgsr) & PGSR_DLDONE)
+			       != PGSR_DLDONE)
+				;
+			break;
+		case INIT_MEM:
+			writel(CFG_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG)
+				;
+			fallthrough;
+		case CONFIG:
+			writel(GO_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) == CONFIG)
+				;
+			break;
+		case ACCESS:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void rk3066_dmc_dram_cfg_rbc(const struct rk3066_dmc_chan_info *chan, u32 chnum,
+				    struct rk3066_dmc_sdram_params *sdram_params)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+
+	if (sdram_params->ch[chnum].bk == 3)
+		clrsetbits_le32(&publ->dcr, PDQ_MASK << PDQ_SHIFT,
+				1 << PDQ_SHIFT);
+	else
+		clrbits_le32(&publ->dcr, PDQ_MASK << PDQ_SHIFT);
+
+	writel(sdram_params->base.ddrconfig, &chan->msch->ddrconf);
+}
+
+static void rk3066_dmc_dram_all_config(const struct rk3066_dmc_dram_info *dram,
+				       struct rk3066_dmc_sdram_params *sdram_params)
+{
+	unsigned int chan;
+	u32 sys_reg = 0;
+
+	sys_reg |= sdram_params->base.dramtype << SYS_REG_DDRTYPE_SHIFT;
+	sys_reg |= (sdram_params->num_channels - 1) << SYS_REG_NUM_CH_SHIFT;
+	for (chan = 0; chan < sdram_params->num_channels; chan++) {
+		const struct rk3288_sdram_channel *info =
+				&sdram_params->ch[chan];
+
+		sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan);
+		sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(chan);
+		sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(chan);
+		sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(chan);
+		sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(chan);
+		sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(chan);
+		sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(chan);
+		sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(chan);
+		sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(chan);
+
+		rk3066_dmc_dram_cfg_rbc(&dram->chan[chan], chan, sdram_params);
+	}
+	if (sdram_params->ch[0].rank == 2)
+		rk3066_dmc_ddr_rank_2_row15en(dram->grf, 0);
+	else
+		rk3066_dmc_ddr_rank_2_row15en(dram->grf, 1);
+
+	writel(sys_reg, &dram->pmu->sys_reg[2]);
+}
+
+static int rk3066_dmc_sdram_rank_bw_detect(struct rk3066_dmc_dram_info *dram, int channel,
+					   struct rk3066_dmc_sdram_params *sdram_params)
+{
+	int reg;
+	int need_trainig = 0;
+	const struct rk3066_dmc_chan_info *chan = &dram->chan[channel];
+	struct rk3288_ddr_publ *publ = chan->publ;
+
+	rk3066_dmc_ddr_rank_2_row15en(dram->grf, 0);
+
+	if (rk3066_dmc_data_training(chan, channel, sdram_params) < 0) {
+		debug("first data training fail!\n");
+		reg = readl(&publ->datx8[0].dxgsr[0]);
+		/* Check the result for rank 0. */
+		if (channel == 0 && (reg & DQS_GATE_TRAINING_ERROR_RANK0)) {
+			debug("data training fail!\n");
+			return -EIO;
+		}
+
+		/* Check the result for rank 1. */
+		if (reg & DQS_GATE_TRAINING_ERROR_RANK1) {
+			sdram_params->ch[channel].rank = 1;
+			clrsetbits_le32(&publ->pgcr, 0xF << 18,
+					sdram_params->ch[channel].rank << 18);
+			need_trainig = 1;
+		}
+		reg = readl(&publ->datx8[2].dxgsr[0]);
+		if (reg & (1 << 4)) {
+			sdram_params->ch[channel].bw = 1;
+			rk3066_dmc_set_bandwidth_ratio(chan, channel,
+						       sdram_params->ch[channel].bw,
+						       dram->grf);
+			need_trainig = 1;
+		}
+	}
+	/* Assume that the die bit width is equel to the chip bit width. */
+	sdram_params->ch[channel].dbw = sdram_params->ch[channel].bw;
+
+	if (need_trainig &&
+	    (rk3066_dmc_data_training(chan, channel, sdram_params) < 0)) {
+		if (sdram_params->base.dramtype == LPDDR3) {
+			rk3066_dmc_ddr_phy_ctl_reset(dram->cru, channel, 1);
+			udelay(10);
+			rk3066_dmc_ddr_phy_ctl_reset(dram->cru, channel, 0);
+			udelay(10);
+		}
+		debug("2nd data training failed!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int rk3066_dmc_sdram_col_row_detect(struct rk3066_dmc_dram_info *dram, int channel,
+					   struct rk3066_dmc_sdram_params *sdram_params)
+{
+	int row, col;
+	unsigned int addr;
+	const struct rk3066_dmc_chan_info *chan = &dram->chan[channel];
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	struct rk3288_ddr_publ *publ = chan->publ;
+	int ret = 0;
+
+	/* Detect col. */
+	for (col = 11; col >= 9; col--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE +
+		       (1 << (col + sdram_params->ch[channel].bw - 1));
+		writel(TEST_PATTERN, addr);
+		if ((readl(addr) == TEST_PATTERN) &&
+		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+			break;
+	}
+	if (col == 8) {
+		debug("Col detect error\n");
+		ret = -EINVAL;
+		goto out;
+	} else {
+		sdram_params->ch[channel].col = col;
+	}
+
+	rk3066_dmc_ddr_rank_2_row15en(dram->grf, 1);
+	rk3066_dmc_move_to_config_state(publ, pctl);
+	writel(1, &chan->msch->ddrconf);
+	rk3066_dmc_move_to_access_state(chan);
+	/* Detect row, max 15, min13 for rk3066 */
+	for (row = 16; row >= 13; row--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE + (1 << (row + 15 - 1));
+		writel(TEST_PATTERN, addr);
+		if ((readl(addr) == TEST_PATTERN) &&
+		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+			break;
+	}
+	if (row == 12) {
+		debug("Row detect error\n");
+		ret = -EINVAL;
+	} else {
+		sdram_params->ch[channel].cs1_row = row;
+		sdram_params->ch[channel].row_3_4 = 0;
+		debug("chn %d col %d, row %d\n", channel, col, row);
+		sdram_params->ch[channel].cs0_row = row;
+	}
+
+out:
+	return ret;
+}
+
+static int rk3066_dmc_sdram_get_niu_config(struct rk3066_dmc_sdram_params *sdram_params)
+{
+	int i, tmp, size, ret = 0;
+
+	tmp = sdram_params->ch[0].col - 9;
+	tmp -= (sdram_params->ch[0].bw == 2) ? 0 : 1;
+	tmp |= ((sdram_params->ch[0].cs0_row - 13) << 4);
+	size = ARRAY_SIZE(rk3066_dmc_ddrconf_table) / sizeof(rk3066_dmc_ddrconf_table[0]);
+	for (i = 0; i < size; i++)
+		if (tmp == rk3066_dmc_ddrconf_table[i])
+			break;
+	if (i >= size) {
+		debug("niu config not found\n");
+		ret = -EINVAL;
+	} else {
+		debug("niu config %d\n", i);
+		sdram_params->base.ddrconfig = i;
+	}
+
+	return ret;
+}
+
+static int rk3066_dmc_sdram_init(struct rk3066_dmc_dram_info *dram,
+				 struct rk3066_dmc_sdram_params *sdram_params)
+{
+	int channel;
+	int zqcr;
+	int ret;
+
+	if ((sdram_params->base.dramtype == DDR3 &&
+	     sdram_params->base.ddr_freq > 800000000)) {
+		debug("SDRAM frequency is too high!");
+		return -E2BIG;
+	}
+
+	ret = clk_set_rate(&dram->ddr_clk, sdram_params->base.ddr_freq);
+	if (ret) {
+		debug("Could not set DDR clock\n");
+		return ret;
+	}
+
+	for (channel = 0; channel < 1; channel++) {
+		const struct rk3066_dmc_chan_info *chan = &dram->chan[channel];
+		struct rk3288_ddr_pctl *pctl = chan->pctl;
+		struct rk3288_ddr_publ *publ = chan->publ;
+
+		rk3066_dmc_phy_pctrl_reset(dram->cru, publ, channel);
+		rk3066_dmc_phy_dll_bypass_set(publ, sdram_params->base.ddr_freq);
+
+		rk3066_dmc_dfi_cfg(pctl, sdram_params->base.dramtype);
+
+		rk3066_dmc_pctl_cfg(channel, pctl, sdram_params, dram->grf);
+
+		rk3066_dmc_phy_cfg(chan, channel, sdram_params);
+
+		rk3066_dmc_phy_init(publ);
+
+		writel(POWER_UP_START, &pctl->powctl);
+		while (!(readl(&pctl->powstat) & POWER_UP_DONE))
+			;
+
+		rk3066_dmc_memory_init(publ, sdram_params->base.dramtype);
+		rk3066_dmc_move_to_config_state(publ, pctl);
+
+		/* Use 32bit bus width for detection. */
+		sdram_params->ch[channel].bw = 2;
+		rk3066_dmc_set_bandwidth_ratio(chan, channel,
+					       sdram_params->ch[channel].bw, dram->grf);
+		/*
+		 * set cs, using n=3 for detect
+		 * CS0, n=1
+		 * CS1, n=2
+		 * CS0 & CS1, n = 3
+		 */
+		sdram_params->ch[channel].rank = 2;
+		clrsetbits_le32(&publ->pgcr, 0xF << 18,
+				(sdram_params->ch[channel].rank | 1) << 18);
+
+		/* DS=40ohm,ODT=155ohm */
+		zqcr = 1 << ZDEN_SHIFT | 2 << PU_ONDIE_SHIFT |
+		       2 << PD_ONDIE_SHIFT | 0x19 << PU_OUTPUT_SHIFT |
+		       0x19 << PD_OUTPUT_SHIFT;
+		writel(zqcr, &publ->zq1cr[0]);
+		writel(zqcr, &publ->zq0cr[0]);
+
+		/* Detect the rank and bit-width with data-training. */
+		writel(1, &chan->msch->ddrconf);
+		rk3066_dmc_sdram_rank_bw_detect(dram, channel, sdram_params);
+
+		if (sdram_params->base.dramtype == LPDDR3) {
+			u32 i;
+
+			writel(0, &pctl->mrrcfg0);
+
+			for (i = 0; i < 17; i++)
+				rk3066_dmc_send_command_op(pctl, 1, MRR_CMD, i, 0);
+		}
+		writel(4, &chan->msch->ddrconf);
+		rk3066_dmc_move_to_access_state(chan);
+		/* DDR3 and LPDDR3 are always 8 bank, no need to detect. */
+		sdram_params->ch[channel].bk = 3;
+		/* Detect Col and Row number. */
+		ret = rk3066_dmc_sdram_col_row_detect(dram, channel, sdram_params);
+		if (ret)
+			goto error;
+	}
+	/* Find NIU DDR configuration. */
+	ret = rk3066_dmc_sdram_get_niu_config(sdram_params);
+	if (ret)
+		goto error;
+
+	rk3066_dmc_dram_all_config(dram, sdram_params);
+	debug("SDRAM init OK!\n");
+
+	return 0;
+error:
+	debug("SDRAM init failed!\n");
+	hang();
+}
+
+static int rk3066_dmc_setup_sdram(struct udevice *dev)
+{
+	struct rk3066_dmc_dram_info *priv = dev_get_priv(dev);
+	struct rk3066_dmc_sdram_params *params = dev_get_plat(dev);
+
+	return rk3066_dmc_sdram_init(priv, params);
+}
+
+static int rk3066_dmc_conv_of_plat(struct udevice *dev)
+{
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3066_dmc_sdram_params *plat = dev_get_plat(dev);
+	struct dtd_rockchip_rk3066_dmc *of_plat = &plat->of_plat;
+	int ret;
+
+	memcpy(&plat->pctl_timing, of_plat->rockchip_pctl_timing,
+	       sizeof(plat->pctl_timing));
+	memcpy(&plat->phy_timing, of_plat->rockchip_phy_timing,
+	       sizeof(plat->phy_timing));
+	memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base));
+	/* RK3066 supports dual-channel, set default channel num to 2. */
+	plat->num_channels = 1;
+	ret = regmap_init_mem_plat(dev, of_plat->reg,
+				   ARRAY_SIZE(of_plat->reg) / 2, &plat->map);
+	if (ret)
+		return ret;
+
+	return 0;
+#else
+	return -EINVAL;
+#endif
+}
+
+static int rk3066_dmc_probe(struct udevice *dev)
+{
+	struct rk3066_dmc_dram_info *priv = dev_get_priv(dev);
+
+	priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
+
+	if (IS_ENABLED(CONFIG_TPL_BUILD)) {
+		struct rk3066_dmc_sdram_params *plat = dev_get_plat(dev);
+		struct regmap *map;
+		struct udevice *dev_clk;
+		int ret;
+
+		ret = rk3066_dmc_conv_of_plat(dev);
+		if (ret)
+			return ret;
+
+		map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_NOC);
+		if (IS_ERR(map))
+			return PTR_ERR(map);
+		priv->chan[0].msch = regmap_get_range(map, 0);
+		priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+		priv->chan[0].pctl = regmap_get_range(plat->map, 0);
+		priv->chan[0].publ = regmap_get_range(plat->map, 1);
+
+		ret = rockchip_get_clk(&dev_clk);
+		if (ret)
+			return ret;
+
+		priv->ddr_clk.id = CLK_DDR;
+		ret = clk_request(dev_clk, &priv->ddr_clk);
+		if (ret)
+			return ret;
+
+		priv->cru = rockchip_get_cru();
+		if (IS_ERR(priv->cru))
+			return PTR_ERR(priv->cru);
+
+		ret = rk3066_dmc_setup_sdram(dev);
+		if (ret)
+			return ret;
+	} else {
+		priv->info.base = CONFIG_SYS_SDRAM_BASE;
+		priv->info.size = rockchip_sdram_size((phys_addr_t)&priv->pmu->sys_reg[2]);
+	}
+
+	return 0;
+}
+
+static int rk3066_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+	struct rk3066_dmc_dram_info *priv = dev_get_priv(dev);
+
+	*info = priv->info;
+
+	return 0;
+}
+
+static struct ram_ops rk3066_dmc_ops = {
+	.get_info = rk3066_dmc_get_info,
+};
+
+static const struct udevice_id rk3066_dmc_ids[] = {
+	{ .compatible = "rockchip,rk3066-dmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3066_dmc) = {
+	.name		= "rockchip_rk3066_dmc",
+	.id		= UCLASS_RAM,
+	.ops		= &rk3066_dmc_ops,
+	.probe		= rk3066_dmc_probe,
+	.of_match	= rk3066_dmc_ids,
+	.priv_auto	= sizeof(struct rk3066_dmc_dram_info),
+#if IS_ENABLED(CONFIG_TPL_BUILD)
+	.plat_auto	= sizeof(struct rk3066_dmc_sdram_params),
+#endif
+};
-- 
2.20.1


  parent reply	other threads:[~2022-04-04 14:22 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-04 14:19 [PATCH v9 00/16] Add Rikomagic MK808 board Johan Jonker
2022-04-04 14:19 ` [PATCH v9 01/16] rockchip: rk3066-power: sync power domain dt-binding header from Linux Johan Jonker
2022-04-06 15:01   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 02/16] rockchip: rk3066: add grf header file Johan Jonker
2022-04-09  3:49   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 03/16] rockchip: rk3066: add clock driver for rk3066 soc Johan Jonker
2022-04-09  3:49   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 04/16] rockchip: rk3066: add rk3066 pinctrl driver Johan Jonker
2022-04-09  3:49   ` Kever Yang
2022-04-04 14:19 ` Johan Jonker [this message]
2022-04-09  3:50   ` [PATCH v9 05/16] rockchip: rk3066: add sdram driver Kever Yang
2022-04-04 14:19 ` [PATCH v9 06/16] arm: dts: rockchip: fix rk3xxx-u-boot.dtsi Johan Jonker
2022-04-09  3:50   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 07/16] arm: dts: rockchip: fix include rk3xxx-u-boot.dtsi Johan Jonker
2022-04-09  3:50   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 08/16] arm: dts: rockchip: add rk3066a.dtsi Johan Jonker
2022-04-09  3:52   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 09/16] arm: dts: rockchip: add rk3066a-mk808.dts Johan Jonker
2022-04-09  3:52   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 10/16] rockchip: rk3066: add include Johan Jonker
2022-04-04 14:19 ` [PATCH v9 11/16] rockchip: rk3066: add rk3066_common.h include Johan Jonker
2022-04-04 14:19 ` [PATCH v9 12/16] rockchip: rk3066: add core support Johan Jonker
2022-04-09  3:55   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 13/16] rockchip: rk3066: add Rikomagic MK808 board Johan Jonker
2022-04-09  3:57   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 14/16] rockchip: rk3066: add mk808_defconfig Johan Jonker
2022-04-09  3:57   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 15/16] rockchip: tools: add rk3066 support to rkcommon.c Johan Jonker
2022-04-09  3:56   ` Kever Yang
2022-04-04 14:19 ` [PATCH v9 16/16] doc: rockchip: add rk3066 Rikomagic MK808 Johan Jonker
2022-04-09  4:00   ` Kever Yang
2022-04-10  9:08     ` Johan Jonker
2022-04-11 10:06       ` Kever Yang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220404141926.6085-6-jbx6244@gmail.com \
    --to=jbx6244@gmail.com \
    --cc=kever.yang@rock-chips.com \
    --cc=lukma@denx.de \
    --cc=philipp.tomsich@vrull.eu \
    --cc=seanga2@gmail.com \
    --cc=sjg@chromium.org \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.