mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: Barebox List <barebox@lists.infradead.org>
Subject: [PATCH 04/13] ddr: imx8m: move PLL init to SoC specific code
Date: Fri, 10 Nov 2023 14:00:19 +0100	[thread overview]
Message-ID: <20231110130028.2123895-5-s.hauer@pengutronix.de> (raw)
In-Reply-To: <20231110130028.2123895-1-s.hauer@pengutronix.de>

The PLL initialization will be different on i.MX9, so move the
code to the i.MX8M specific file and a add a function hook to
struct dram_controller.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/ddr/imx/ddrphy_train.c   |   2 +-
 drivers/ddr/imx/ddrphy_utils.c   | 257 ------------------------------
 drivers/ddr/imx/imx8m_ddr_init.c | 261 ++++++++++++++++++++++++++++++-
 include/soc/imx8m/ddr.h          |   2 +-
 4 files changed, 262 insertions(+), 260 deletions(-)

diff --git a/drivers/ddr/imx/ddrphy_train.c b/drivers/ddr/imx/ddrphy_train.c
index 36fb6d3c11..b44f5ef9b4 100644
--- a/drivers/ddr/imx/ddrphy_train.c
+++ b/drivers/ddr/imx/ddrphy_train.c
@@ -116,7 +116,7 @@ int ddr_cfg_phy(struct dram_controller *dram, struct dram_timing_info *dram_timi
 	for (i = 0; i < dram_timing->fsp_msg_num; i++) {
 		pr_debug("DRAM PHY training for %dMTS\n", fsp_msg->drate);
 		/* set dram PHY input clocks to desired frequency */
-		ddrphy_init_set_dfi_clk(fsp_msg->drate, dram->ddrc_type);
+		dram->set_dfi_clk(dram, fsp_msg->drate);
 
 		/* load the dram training firmware image */
 		dwc_ddrphy_apb_wr(0xd0000, 0x0);
diff --git a/drivers/ddr/imx/ddrphy_utils.c b/drivers/ddr/imx/ddrphy_utils.c
index db94b85b74..4577547113 100644
--- a/drivers/ddr/imx/ddrphy_utils.c
+++ b/drivers/ddr/imx/ddrphy_utils.c
@@ -13,111 +13,6 @@
 #include <mach/imx/imx8m-regs.h>
 #include <mach/imx/imx8m-ccm-regs.h>
 
-/* DDR Transfer rate, bus clock is transfer rate / 2, and the DDRC runs at bus
- * clock / 2, which is therefor transfer rate / 4.  */
-enum ddr_rate {
-	DDR_4000,
-	DDR_3720,
-	DDR_3200,
-	DDR_3000,
-	DDR_2600, /* Unused */
-	DDR_2400,
-	DDR_2376, /* Unused */
-	DDR_1600,
-	DDR_1000, /* Unused */
-	DDR_1066,
-	DDR_667,
-	DDR_400,
-	DDR_250, /* Unused */
-	DDR_100,
-	DDR_NUM_RATES
-};
-
-/* PLL config for IMX8MM type DRAM PLL.  This PLL type isn't documented, but
- * it looks like it is a basically a fractional PLL:
- * Frequency = Ref (24 MHz) / P * M / 2^S
- * Note: Divider is equal to register value
- */
-#define MDIV(x) ((x) << 12)
-#define PDIV(x) ((x) << 4)
-#define SDIV(x) ((x) << 0)
-
-#define LOCK_STATUS     BIT(31)
-#define LOCK_SEL_MASK   BIT(29)
-#define CLKE_MASK       BIT(11)
-#define RST_MASK        BIT(9)
-#define BYPASS_MASK     BIT(4)
-
-static const struct imx8mm_fracpll_config {
-	uint32_t r1, r2;
-	bool valid;
-} imx8mm_fracpll_table[DDR_NUM_RATES] = {
-	[DDR_4000] = { .valid = true, .r1 = MDIV(250) | PDIV(3) | SDIV(1), .r2 = 0 },
-	[DDR_3720] = { .valid = true, .r1 = MDIV(310) | PDIV(2) | SDIV(2), .r2 = 0 },
-	[DDR_3200] = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(0), .r2 = 0 },
-	[DDR_3000] = { .valid = true, .r1 = MDIV(250) | PDIV(8) | SDIV(0), .r2 = 0 },
-	[DDR_2600] = { .valid = true, .r1 = MDIV(325) | PDIV(3) | SDIV(2), .r2 = 0 },
-	[DDR_2400] = { .valid = true, .r1 = MDIV(300) | PDIV(3) | SDIV(2), .r2 = 0 },
-	[DDR_2376] = { .valid = true, .r1 = MDIV( 99) | PDIV(1) | SDIV(2), .r2 = 0 },
-	[DDR_1600] = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(1), .r2 = 0 },
-	[DDR_1066] = { .valid = true, .r1 = MDIV(400) | PDIV(9) | SDIV(2), .r2 = 0 },
-	[DDR_667]  = { .valid = true, .r1 = MDIV(334) | PDIV(3) | SDIV(4), .r2 = 0 },
-	[DDR_400]  = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(3), .r2 = 0 },
-};
-
-/* PLL config for IMX8MQ type DRAM PLL.  This is SSCG_PLL:
- * Frequency = Ref (25 MHz) / divr1 * (2*divf1) / divr2 * divf2 / divq
- * Note: IMX8MQ RM, §5.1.5.4.4 Fig. 5-8 shows ÷2 on divf2, but this is not true.
- * Note: divider is register value + 1
- */
-#define SSCG_PLL_LOCK			BIT(31)
-#define SSCG_PLL_DRAM_PLL_CLKE		BIT(9)
-#define SSCG_PLL_PD			BIT(7)
-#define SSCG_PLL_BYPASS1		BIT(5)
-#define SSCG_PLL_BYPASS2		BIT(4)
-
-#define SSCG_PLL_REF_DIVR2_MASK		(0x3f << 19)
-#define SSCG_PLL_REF_DIVR2_VAL(n)	(((n) << 19) & SSCG_PLL_REF_DIVR2_MASK)
-#define SSCG_PLL_FEEDBACK_DIV_F1_MASK	(0x3f << 13)
-#define SSCG_PLL_FEEDBACK_DIV_F1_VAL(n)	(((n) << 13) & SSCG_PLL_FEEDBACK_DIV_F1_MASK)
-#define SSCG_PLL_FEEDBACK_DIV_F2_MASK	(0x3f << 7)
-#define SSCG_PLL_FEEDBACK_DIV_F2_VAL(n)	(((n) << 7) & SSCG_PLL_FEEDBACK_DIV_F2_MASK)
-#define SSCG_PLL_OUTPUT_DIV_VAL_MASK	(0x3f << 1)
-#define SSCG_PLL_OUTPUT_DIV_VAL(n)	(((n) << 1) & SSCG_PLL_OUTPUT_DIV_VAL_MASK)
-
-#define SSCG_PLL_CFG2(divf1, divr2, divf2, divq) \
-	(SSCG_PLL_FEEDBACK_DIV_F1_VAL(divf1) | SSCG_PLL_FEEDBACK_DIV_F2_VAL(divf2) | \
-	SSCG_PLL_REF_DIVR2_VAL(divr2) | SSCG_PLL_OUTPUT_DIV_VAL(divq))
-
-static const struct imx8mq_ssgcpll_config {
-	uint32_t val;
-	bool valid;
-} imx8mq_ssgcpll_table[DDR_NUM_RATES] = {
-	[DDR_3200] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 11, 0) },
-	[DDR_2400] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 17, 1) },
-	[DDR_1600] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 11, 1) },
-	[DDR_667]  = { .valid = true, .val = SSCG_PLL_CFG2(45, 30, 8, 3) }, /* ~166.935 MHz = 667.74 */
-};
-
-/* IMX8M Bypass clock config.  These configure dram_alt1_clk and the dram apb
- * clock.  For the bypass config, clock rate = DRAM tranfer rate, rather than
- * clock = dram / 4
- */
-
-/* prediv is actual divider, register will be set to divider - 1 */
-#define CCM_ROOT_CFG(mux, prediv) (IMX8M_CCM_TARGET_ROOTn_ENABLE | \
-	IMX8M_CCM_TARGET_ROOTn_MUX(mux) | IMX8M_CCM_TARGET_ROOTn_PRE_DIV(prediv-1))
-
-static const struct imx8m_bypass_config {
-	uint32_t alt_clk;
-	uint32_t apb_clk;
-	bool valid;
-} imx8m_bypass_table[DDR_NUM_RATES] = {
-	[DDR_400] = { .valid = true, .alt_clk = CCM_ROOT_CFG(1, 2), .apb_clk = CCM_ROOT_CFG(3, 2) },
-	[DDR_250] = { .valid = true, .alt_clk = CCM_ROOT_CFG(3, 2), .apb_clk = CCM_ROOT_CFG(2, 2) },
-	[DDR_100] = { .valid = true, .alt_clk = CCM_ROOT_CFG(2, 1), .apb_clk = CCM_ROOT_CFG(2, 2) },
-};
-
 void ddrc_phy_load_firmware(void __iomem *phy,
 			    enum ddrc_phy_firmware_offset offset,
 			    const u16 *blob, size_t size)
@@ -207,158 +102,6 @@ int wait_ddrphy_training_complete(void)
 	}
 }
 
-static void dram_enable_bypass(enum ddr_rate drate)
-{
-	const struct imx8m_bypass_config *config = &imx8m_bypass_table[drate];
-
-	if (!config->valid) {
-		pr_warn("No matched freq table entry %u\n", drate);
-		return;
-	}
-
-	imx8m_clock_set_target_val(IMX8M_DRAM_ALT_CLK_ROOT, config->alt_clk);
-	imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT, config->apb_clk);
-	imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG, IMX8M_CCM_TARGET_ROOTn_ENABLE |
-				   IMX8M_CCM_TARGET_ROOTn_MUX(1));
-}
-
-static void dram_disable_bypass(void)
-{
-	imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG,
-				   IMX8M_CCM_TARGET_ROOTn_ENABLE |
-				   IMX8M_CCM_TARGET_ROOTn_MUX(0));
-	imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT,
-				   IMX8M_CCM_TARGET_ROOTn_ENABLE |
-				   IMX8M_CCM_TARGET_ROOTn_MUX(4) |
-				   IMX8M_CCM_TARGET_ROOTn_PRE_DIV(5 - 1));
-}
-
-static int dram_frac_pll_init(enum ddr_rate drate)
-{
-	volatile int i;
-	u32 tmp;
-	void *pll_base;
-	const struct imx8mm_fracpll_config *config = &imx8mm_fracpll_table[drate];
-
-	if (!config->valid) {
-		pr_warn("No matched freq table entry %u\n", drate);
-		return -EINVAL;
-	}
-
-	setbits_le32(MX8M_GPC_BASE_ADDR + 0xec, 1 << 7);
-	setbits_le32(MX8M_GPC_BASE_ADDR + 0xf8, 1 << 5);
-	writel(0x8F000000UL, MX8M_SRC_BASE_ADDR + 0x1004);
-
-	pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x50;
-
-	/* Bypass clock and set lock to pll output lock */
-	tmp = readl(pll_base);
-	tmp |= BYPASS_MASK;
-	writel(tmp, pll_base);
-
-	/* Enable RST */
-	tmp &= ~RST_MASK;
-	writel(tmp, pll_base);
-
-	writel(config->r1, pll_base + 4);
-	writel(config->r2, pll_base + 8);
-
-	for (i = 0; i < 1000; i++);
-
-	/* Disable RST */
-	tmp |= RST_MASK;
-	writel(tmp, pll_base);
-
-	/* Wait Lock*/
-	while (!(readl(pll_base) & LOCK_STATUS));
-
-	/* Bypass */
-	tmp &= ~BYPASS_MASK;
-	writel(tmp, pll_base);
-
-	return 0;
-}
-
-static int dram_sscg_pll_init(enum ddr_rate drate)
-{
-	u32 val;
-	void __iomem *pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x60;
-	const struct imx8mq_ssgcpll_config *config = &imx8mq_ssgcpll_table[drate];
-
-	if (!config->valid) {
-		pr_warn("No matched freq table entry %u\n", drate);
-		return -EINVAL;
-	}
-
-	/* Bypass */
-	setbits_le32(pll_base, SSCG_PLL_BYPASS1 | SSCG_PLL_BYPASS2);
-
-	val = readl(pll_base + 0x8);
-	val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK |
-		 SSCG_PLL_FEEDBACK_DIV_F2_MASK |
-		 SSCG_PLL_FEEDBACK_DIV_F1_MASK |
-		 SSCG_PLL_REF_DIVR2_MASK);
-	val |= config->val;
-	writel(val, pll_base + 0x8);
-
-	/* Clear power down bit */
-	clrbits_le32(pll_base, SSCG_PLL_PD);
-	/* Enable PLL  */
-	setbits_le32(pll_base, SSCG_PLL_DRAM_PLL_CLKE);
-
-	/* Clear bypass */
-	clrbits_le32(pll_base, SSCG_PLL_BYPASS1);
-	udelay(100);
-	clrbits_le32(pll_base, SSCG_PLL_BYPASS2);
-	/* Wait lock */
-	while (!(readl(pll_base) & SSCG_PLL_LOCK))
-		;
-
-	return 0;
-}
-
-static int dram_pll_init(enum ddr_rate drate, enum ddrc_type type)
-{
-	switch (type) {
-	case DDRC_TYPE_MQ:
-		return dram_sscg_pll_init(drate);
-	case DDRC_TYPE_MM:
-	case DDRC_TYPE_MN:
-	case DDRC_TYPE_MP:
-		return dram_frac_pll_init(drate);
-	default:
-		return -ENODEV;
-	}
-}
-
-void ddrphy_init_set_dfi_clk(unsigned int drate_mhz, enum ddrc_type type)
-{
-	enum ddr_rate drate;
-
-	switch (drate_mhz) {
-	case 4000: drate = DDR_4000; break;
-	case 3720: drate = DDR_3720; break;
-	case 3200: drate = DDR_3200; break;
-	case 3000: drate = DDR_3000; break;
-	case 2400: drate = DDR_2400; break;
-	case 1600: drate = DDR_1600; break;
-	case 1066: drate = DDR_1066; break;
-	case 667: drate = DDR_667; break;
-	case 400: drate = DDR_400; break;
-	case 100: drate = DDR_100; break;
-	default:
-		pr_warn("Unsupported frequency %u\n", drate_mhz);
-		return;
-	}
-
-	if (drate_mhz > 400) {
-		dram_pll_init(drate, type);
-		dram_disable_bypass();
-	} else {
-		dram_enable_bypass(drate);
-	}
-}
-
 void ddrphy_init_read_msg_block(enum fw_type type)
 {
 }
diff --git a/drivers/ddr/imx/imx8m_ddr_init.c b/drivers/ddr/imx/imx8m_ddr_init.c
index 1e704ef8fc..856e7ee4fe 100644
--- a/drivers/ddr/imx/imx8m_ddr_init.c
+++ b/drivers/ddr/imx/imx8m_ddr_init.c
@@ -212,6 +212,264 @@ static void update_umctl2_rank_space_setting(unsigned int pstat_num,
 	}
 }
 
+
+/* DDR Transfer rate, bus clock is transfer rate / 2, and the DDRC runs at bus
+ * clock / 2, which is therefor transfer rate / 4.  */
+enum ddr_rate {
+	DDR_4000,
+	DDR_3720,
+	DDR_3200,
+	DDR_3000,
+	DDR_2600, /* Unused */
+	DDR_2400,
+	DDR_2376, /* Unused */
+	DDR_1600,
+	DDR_1000, /* Unused */
+	DDR_1066,
+	DDR_667,
+	DDR_400,
+	DDR_250, /* Unused */
+	DDR_100,
+	DDR_NUM_RATES
+};
+
+/* PLL config for IMX8MM type DRAM PLL.  This PLL type isn't documented, but
+ * it looks like it is a basically a fractional PLL:
+ * Frequency = Ref (24 MHz) / P * M / 2^S
+ * Note: Divider is equal to register value
+ */
+#define MDIV(x) ((x) << 12)
+#define PDIV(x) ((x) << 4)
+#define SDIV(x) ((x) << 0)
+
+#define LOCK_STATUS     BIT(31)
+#define LOCK_SEL_MASK   BIT(29)
+#define CLKE_MASK       BIT(11)
+#define RST_MASK        BIT(9)
+#define BYPASS_MASK     BIT(4)
+
+static const struct imx8mm_fracpll_config {
+	uint32_t r1, r2;
+	bool valid;
+} imx8mm_fracpll_table[DDR_NUM_RATES] = {
+	[DDR_4000] = { .valid = true, .r1 = MDIV(250) | PDIV(3) | SDIV(1), .r2 = 0 },
+	[DDR_3720] = { .valid = true, .r1 = MDIV(310) | PDIV(2) | SDIV(2), .r2 = 0 },
+	[DDR_3200] = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(0), .r2 = 0 },
+	[DDR_3000] = { .valid = true, .r1 = MDIV(250) | PDIV(8) | SDIV(0), .r2 = 0 },
+	[DDR_2600] = { .valid = true, .r1 = MDIV(325) | PDIV(3) | SDIV(2), .r2 = 0 },
+	[DDR_2400] = { .valid = true, .r1 = MDIV(300) | PDIV(3) | SDIV(2), .r2 = 0 },
+	[DDR_2376] = { .valid = true, .r1 = MDIV( 99) | PDIV(1) | SDIV(2), .r2 = 0 },
+	[DDR_1600] = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(1), .r2 = 0 },
+	[DDR_1066] = { .valid = true, .r1 = MDIV(400) | PDIV(9) | SDIV(2), .r2 = 0 },
+	[DDR_667]  = { .valid = true, .r1 = MDIV(334) | PDIV(3) | SDIV(4), .r2 = 0 },
+	[DDR_400]  = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(3), .r2 = 0 },
+};
+
+/* PLL config for IMX8MQ type DRAM PLL.  This is SSCG_PLL:
+ * Frequency = Ref (25 MHz) / divr1 * (2*divf1) / divr2 * divf2 / divq
+ * Note: IMX8MQ RM, §5.1.5.4.4 Fig. 5-8 shows ÷2 on divf2, but this is not true.
+ * Note: divider is register value + 1
+ */
+#define SSCG_PLL_LOCK			BIT(31)
+#define SSCG_PLL_DRAM_PLL_CLKE		BIT(9)
+#define SSCG_PLL_PD			BIT(7)
+#define SSCG_PLL_BYPASS1		BIT(5)
+#define SSCG_PLL_BYPASS2		BIT(4)
+
+#define SSCG_PLL_REF_DIVR2_MASK		(0x3f << 19)
+#define SSCG_PLL_REF_DIVR2_VAL(n)	(((n) << 19) & SSCG_PLL_REF_DIVR2_MASK)
+#define SSCG_PLL_FEEDBACK_DIV_F1_MASK	(0x3f << 13)
+#define SSCG_PLL_FEEDBACK_DIV_F1_VAL(n)	(((n) << 13) & SSCG_PLL_FEEDBACK_DIV_F1_MASK)
+#define SSCG_PLL_FEEDBACK_DIV_F2_MASK	(0x3f << 7)
+#define SSCG_PLL_FEEDBACK_DIV_F2_VAL(n)	(((n) << 7) & SSCG_PLL_FEEDBACK_DIV_F2_MASK)
+#define SSCG_PLL_OUTPUT_DIV_VAL_MASK	(0x3f << 1)
+#define SSCG_PLL_OUTPUT_DIV_VAL(n)	(((n) << 1) & SSCG_PLL_OUTPUT_DIV_VAL_MASK)
+
+#define SSCG_PLL_CFG2(divf1, divr2, divf2, divq) \
+	(SSCG_PLL_FEEDBACK_DIV_F1_VAL(divf1) | SSCG_PLL_FEEDBACK_DIV_F2_VAL(divf2) | \
+	SSCG_PLL_REF_DIVR2_VAL(divr2) | SSCG_PLL_OUTPUT_DIV_VAL(divq))
+
+static const struct imx8mq_ssgcpll_config {
+	uint32_t val;
+	bool valid;
+} imx8mq_ssgcpll_table[DDR_NUM_RATES] = {
+	[DDR_3200] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 11, 0) },
+	[DDR_2400] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 17, 1) },
+	[DDR_1600] = { .valid = true, .val = SSCG_PLL_CFG2(39, 29, 11, 1) },
+	[DDR_667]  = { .valid = true, .val = SSCG_PLL_CFG2(45, 30, 8, 3) }, /* ~166.935 MHz = 667.74 */
+};
+
+/* IMX8M Bypass clock config.  These configure dram_alt1_clk and the dram apb
+ * clock.  For the bypass config, clock rate = DRAM tranfer rate, rather than
+ * clock = dram / 4
+ */
+
+/* prediv is actual divider, register will be set to divider - 1 */
+#define CCM_ROOT_CFG(mux, prediv) (IMX8M_CCM_TARGET_ROOTn_ENABLE | \
+	IMX8M_CCM_TARGET_ROOTn_MUX(mux) | IMX8M_CCM_TARGET_ROOTn_PRE_DIV(prediv-1))
+
+static const struct imx8m_bypass_config {
+	uint32_t alt_clk;
+	uint32_t apb_clk;
+	bool valid;
+} imx8m_bypass_table[DDR_NUM_RATES] = {
+	[DDR_400] = { .valid = true, .alt_clk = CCM_ROOT_CFG(1, 2), .apb_clk = CCM_ROOT_CFG(3, 2) },
+	[DDR_250] = { .valid = true, .alt_clk = CCM_ROOT_CFG(3, 2), .apb_clk = CCM_ROOT_CFG(2, 2) },
+	[DDR_100] = { .valid = true, .alt_clk = CCM_ROOT_CFG(2, 1), .apb_clk = CCM_ROOT_CFG(2, 2) },
+};
+
+static void dram_enable_bypass(enum ddr_rate drate)
+{
+	const struct imx8m_bypass_config *config = &imx8m_bypass_table[drate];
+
+	if (!config->valid) {
+		pr_warn("No matched freq table entry %u\n", drate);
+		return;
+	}
+
+	imx8m_clock_set_target_val(IMX8M_DRAM_ALT_CLK_ROOT, config->alt_clk);
+	imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT, config->apb_clk);
+	imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG, IMX8M_CCM_TARGET_ROOTn_ENABLE |
+				   IMX8M_CCM_TARGET_ROOTn_MUX(1));
+}
+
+static void dram_disable_bypass(void)
+{
+	imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG,
+				   IMX8M_CCM_TARGET_ROOTn_ENABLE |
+				   IMX8M_CCM_TARGET_ROOTn_MUX(0));
+	imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT,
+				   IMX8M_CCM_TARGET_ROOTn_ENABLE |
+				   IMX8M_CCM_TARGET_ROOTn_MUX(4) |
+				   IMX8M_CCM_TARGET_ROOTn_PRE_DIV(5 - 1));
+}
+
+static int dram_frac_pll_init(enum ddr_rate drate)
+{
+	volatile int i;
+	u32 tmp;
+	void *pll_base;
+	const struct imx8mm_fracpll_config *config = &imx8mm_fracpll_table[drate];
+
+	if (!config->valid) {
+		pr_warn("No matched freq table entry %u\n", drate);
+		return -EINVAL;
+	}
+
+	setbits_le32(MX8M_GPC_BASE_ADDR + 0xec, 1 << 7);
+	setbits_le32(MX8M_GPC_BASE_ADDR + 0xf8, 1 << 5);
+	writel(0x8F000000UL, MX8M_SRC_BASE_ADDR + 0x1004);
+
+	pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x50;
+
+	/* Bypass clock and set lock to pll output lock */
+	tmp = readl(pll_base);
+	tmp |= BYPASS_MASK;
+	writel(tmp, pll_base);
+
+	/* Enable RST */
+	tmp &= ~RST_MASK;
+	writel(tmp, pll_base);
+
+	writel(config->r1, pll_base + 4);
+	writel(config->r2, pll_base + 8);
+
+	for (i = 0; i < 1000; i++);
+
+	/* Disable RST */
+	tmp |= RST_MASK;
+	writel(tmp, pll_base);
+
+	/* Wait Lock*/
+	while (!(readl(pll_base) & LOCK_STATUS));
+
+	/* Bypass */
+	tmp &= ~BYPASS_MASK;
+	writel(tmp, pll_base);
+
+	return 0;
+}
+
+static int dram_sscg_pll_init(enum ddr_rate drate)
+{
+	u32 val;
+	void __iomem *pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x60;
+	const struct imx8mq_ssgcpll_config *config = &imx8mq_ssgcpll_table[drate];
+
+	if (!config->valid) {
+		pr_warn("No matched freq table entry %u\n", drate);
+		return -EINVAL;
+	}
+
+	/* Bypass */
+	setbits_le32(pll_base, SSCG_PLL_BYPASS1 | SSCG_PLL_BYPASS2);
+
+	val = readl(pll_base + 0x8);
+	val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK |
+		 SSCG_PLL_FEEDBACK_DIV_F2_MASK |
+		 SSCG_PLL_FEEDBACK_DIV_F1_MASK |
+		 SSCG_PLL_REF_DIVR2_MASK);
+	val |= config->val;
+	writel(val, pll_base + 0x8);
+
+	/* Clear power down bit */
+	clrbits_le32(pll_base, SSCG_PLL_PD);
+	/* Enable PLL  */
+	setbits_le32(pll_base, SSCG_PLL_DRAM_PLL_CLKE);
+
+	/* Clear bypass */
+	clrbits_le32(pll_base, SSCG_PLL_BYPASS1);
+	udelay(100);
+	clrbits_le32(pll_base, SSCG_PLL_BYPASS2);
+	/* Wait lock */
+	while (!(readl(pll_base) & SSCG_PLL_LOCK))
+		;
+
+	return 0;
+}
+
+static int dram_pll_init(enum ddr_rate drate, enum ddrc_type type)
+{
+	switch (type) {
+	case DDRC_TYPE_MQ:
+		return dram_sscg_pll_init(drate);
+	case DDRC_TYPE_MM:
+	case DDRC_TYPE_MN:
+	case DDRC_TYPE_MP:
+		return dram_frac_pll_init(drate);
+	default:
+		return -ENODEV;
+	}
+}
+
+static void ddrphy_init_set_dfi_clk(struct dram_controller *dram, unsigned int drate_mhz)
+{
+	enum ddr_rate drate;
+
+	switch (drate_mhz) {
+	case 4000: drate = DDR_4000; break;
+	case 3720: drate = DDR_3720; break;
+	case 3200: drate = DDR_3200; break;
+	case 3000: drate = DDR_3000; break;
+	case 2400: drate = DDR_2400; break;
+	case 1600: drate = DDR_1600; break;
+	case 1066: drate = DDR_1066; break;
+	case 667: drate = DDR_667; break;
+	case 400: drate = DDR_400; break;
+	case 100: drate = DDR_100; break;
+	default:
+		pr_warn("Unsupported frequency %u\n", drate_mhz);
+		return;
+	}
+
+	if (drate_mhz > 400) {
+		dram_pll_init(drate, dram->ddrc_type);
+		dram_disable_bypass();
+	} else {
+		dram_enable_bypass(drate);
+	}
+}
+
 /*
  * We store the timing parameters here. the TF-A will pick these up.
  * Note that the timing used we leave the driver with is a PLL bypass 25MHz
@@ -229,6 +487,7 @@ int imx8m_ddr_init(struct dram_controller *dram, struct dram_timing_info *dram_t
 	pr_debug("start DRAM init\n");
 
 	dram->get_trained_CDD = get_trained_CDD;
+	dram->set_dfi_clk = ddrphy_init_set_dfi_clk;
 
 	/* Step1: Follow the power up procedure */
 	switch (dram->ddrc_type) {
@@ -253,7 +512,7 @@ int imx8m_ddr_init(struct dram_controller *dram, struct dram_timing_info *dram_t
 
 	initial_drate = dram_timing->fsp_msg[0].drate;
 	/* default to the frequency point 0 clock */
-	ddrphy_init_set_dfi_clk(initial_drate, dram->ddrc_type);
+	dram->set_dfi_clk(dram, initial_drate);
 
 	/* D-aasert the presetn */
 	reg32_write(src_ddrc_rcr, 0x8F000006);
diff --git a/include/soc/imx8m/ddr.h b/include/soc/imx8m/ddr.h
index c89dfe78cf..ec82f3233a 100644
--- a/include/soc/imx8m/ddr.h
+++ b/include/soc/imx8m/ddr.h
@@ -388,6 +388,7 @@ struct dram_controller {
 	enum ddrc_type ddrc_type;
 	enum dram_type dram_type;
 	void (*get_trained_CDD)(struct dram_controller *dram, u32 fsp);
+	void (*set_dfi_clk)(struct dram_controller *dram, unsigned int drate_mhz);
 };
 
 extern struct dram_timing_info dram_timing;
@@ -458,7 +459,6 @@ void dram_config_save(struct dram_timing_info *info, unsigned long base);
 
 /* utils function for ddr phy training */
 int wait_ddrphy_training_complete(void);
-void ddrphy_init_set_dfi_clk(unsigned int drate, enum ddrc_type ddrc_type);
 void ddrphy_init_read_msg_block(enum fw_type fw_type);
 
 #define reg32_write(a, v)	writel(v, a)
-- 
2.39.2




  parent reply	other threads:[~2023-11-10 13:02 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-10 13:00 [PATCH 00/13] Add i.MX9 DDR support Sascha Hauer
2023-11-10 13:00 ` [PATCH 01/13] ddr: imx8m: rename driver to imx Sascha Hauer
2023-11-10 13:00 ` [PATCH 02/13] ddr: imx8m: introduce dram_controller struct Sascha Hauer
2023-11-10 13:00 ` [PATCH 03/13] ddr: imx8m: move get_trained_CDD() to SoC code Sascha Hauer
2023-11-10 13:00 ` Sascha Hauer [this message]
2023-11-10 13:00 ` [PATCH 05/13] ddr: imx8m: clean up defines Sascha Hauer
2023-11-10 13:00 ` [PATCH 06/13] ddr: imx8m: move phy_base to controller struct Sascha Hauer
2023-11-10 13:00 ` [PATCH 07/13] ddr: imx8m: remove empty function Sascha Hauer
2023-11-10 13:00 ` [PATCH 08/13] ddr: imx8m: get rid of hardcoded phy address Sascha Hauer
2023-11-10 13:00 ` [PATCH 09/13] ddr: imx8m: split header file Sascha Hauer
2023-11-10 13:00 ` [PATCH 10/13] ddr: imx8m: return cfg from dram_config_save() Sascha Hauer
2023-11-10 13:00 ` [PATCH 11/13] ddr: imx8m: Drop '8m' suffix from pr_fmt Sascha Hauer
2023-11-10 13:00 ` [PATCH 12/13] ddr: move imx8m_ddr_old_spreadsheet to controller Sascha Hauer
2023-11-10 13:00 ` [PATCH 13/13] ddr: Initial i.MX9 support Sascha Hauer

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=20231110130028.2123895-5-s.hauer@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox