[PATCH 1/5] ddr: imx8m: implement i.MX8MQ support
Lucas Stach
dev at lynxeye.de
Sun Dec 27 16:50:38 EST 2020
The i.MX8MQ uses a different PLL type than the later i.MX8M family
members, so the PLL setup did not actually work on this SoC. In U-Boot
the used PLL setup routine is a compile time decision. As we want
our DRAM init code to work for multi-image builds, this passes the
SoC type through to the PLL init, so we can use the correct setup
routine depending on the SoC we are running on.
Signed-off-by: Lucas Stach <dev at lynxeye.de>
---
drivers/ddr/imx8m/ddr_init.c | 4 +-
drivers/ddr/imx8m/ddrphy_train.c | 4 +-
drivers/ddr/imx8m/ddrphy_utils.c | 107 ++++++++++++++++++++++++++++---
include/soc/imx8m/ddr.h | 4 +-
4 files changed, 104 insertions(+), 15 deletions(-)
diff --git a/drivers/ddr/imx8m/ddr_init.c b/drivers/ddr/imx8m/ddr_init.c
index 1cd7b7406dc7..34da44af6446 100644
--- a/drivers/ddr/imx8m/ddr_init.c
+++ b/drivers/ddr/imx8m/ddr_init.c
@@ -62,7 +62,7 @@ static int imx8m_ddr_init(struct dram_timing_info *dram_timing,
initial_drate = dram_timing->fsp_msg[0].drate;
/* default to the frequency point 0 clock */
- ddrphy_init_set_dfi_clk(initial_drate);
+ ddrphy_init_set_dfi_clk(initial_drate, type);
/* D-aasert the presetn */
reg32_write(src_ddrc_rcr, 0x8F000006);
@@ -115,7 +115,7 @@ static int imx8m_ddr_init(struct dram_timing_info *dram_timing,
*/
pr_debug("ddrphy config start\n");
- ret = ddr_cfg_phy(dram_timing);
+ ret = ddr_cfg_phy(dram_timing, type);
if (ret)
return ret;
diff --git a/drivers/ddr/imx8m/ddrphy_train.c b/drivers/ddr/imx8m/ddrphy_train.c
index 9280c853aa1c..a4677f903c6b 100644
--- a/drivers/ddr/imx8m/ddrphy_train.c
+++ b/drivers/ddr/imx8m/ddrphy_train.c
@@ -31,7 +31,7 @@ void ddr_load_train_code(enum fw_type type)
DDRC_PHY_DMEM, dmem, dsize);
}
-int ddr_cfg_phy(struct dram_timing_info *dram_timing)
+int ddr_cfg_phy(struct dram_timing_info *dram_timing, enum ddrc_type type)
{
struct dram_cfg_param *dram_cfg;
struct dram_fsp_msg *fsp_msg;
@@ -54,7 +54,7 @@ int ddr_cfg_phy(struct dram_timing_info *dram_timing)
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);
+ ddrphy_init_set_dfi_clk(fsp_msg->drate, type);
/* load the dram training firmware image */
dwc_ddrphy_apb_wr(0xd0000, 0x0);
diff --git a/drivers/ddr/imx8m/ddrphy_utils.c b/drivers/ddr/imx8m/ddrphy_utils.c
index c48372491015..9a4e1a22ee5e 100644
--- a/drivers/ddr/imx8m/ddrphy_utils.c
+++ b/drivers/ddr/imx8m/ddrphy_utils.c
@@ -214,7 +214,7 @@ static struct imx_int_pll_rate_table *fracpll(u32 freq)
return NULL;
}
-static int dram_pll_init(u32 freq)
+static int dram_frac_pll_init(u32 freq)
{
volatile int i;
u32 tmp;
@@ -261,35 +261,124 @@ static int dram_pll_init(u32 freq)
return 0;
}
-void ddrphy_init_set_dfi_clk(unsigned int drate)
+#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)
+
+static int dram_sscg_pll_init(u32 freq)
+{
+ u32 val;
+ void __iomem *pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x60;
+
+ /* 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);
+
+ switch (freq) {
+ case MHZ(800):
+ val |= SSCG_PLL_OUTPUT_DIV_VAL(0);
+ val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(11);
+ val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39);
+ val |= SSCG_PLL_REF_DIVR2_VAL(29);
+ break;
+ case MHZ(600):
+ val |= SSCG_PLL_OUTPUT_DIV_VAL(1);
+ val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(17);
+ val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39);
+ val |= SSCG_PLL_REF_DIVR2_VAL(29);
+ break;
+ case MHZ(400):
+ val |= SSCG_PLL_OUTPUT_DIV_VAL(1);
+ val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(11);
+ val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39);
+ val |= SSCG_PLL_REF_DIVR2_VAL(29);
+ break;
+ case MHZ(167):
+ val |= SSCG_PLL_OUTPUT_DIV_VAL(3);
+ val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(8);
+ val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(45);
+ val |= SSCG_PLL_REF_DIVR2_VAL(30);
+ break;
+ default:
+ break;
+ }
+
+ 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(u32 freq, enum ddrc_type type)
+{
+ switch (type) {
+ case DDRC_TYPE_MQ:
+ return dram_sscg_pll_init(freq);
+ case DDRC_TYPE_MM:
+ case DDRC_TYPE_MP:
+ return dram_frac_pll_init(freq);
+ default:
+ return -ENODEV;
+ }
+}
+
+void ddrphy_init_set_dfi_clk(unsigned int drate, enum ddrc_type type)
{
switch (drate) {
case 4000:
- dram_pll_init(MHZ(1000));
+ dram_pll_init(MHZ(1000), type);
dram_disable_bypass();
break;
case 3200:
- dram_pll_init(MHZ(800));
+ dram_pll_init(MHZ(800), type);
dram_disable_bypass();
break;
case 3000:
- dram_pll_init(MHZ(750));
+ dram_pll_init(MHZ(750), type);
dram_disable_bypass();
break;
case 2400:
- dram_pll_init(MHZ(600));
+ dram_pll_init(MHZ(600), type);
dram_disable_bypass();
break;
case 1600:
- dram_pll_init(MHZ(400));
+ dram_pll_init(MHZ(400), type);
dram_disable_bypass();
break;
case 1066:
- dram_pll_init(MHZ(266));
+ dram_pll_init(MHZ(266),type);
dram_disable_bypass();
break;
case 667:
- dram_pll_init(MHZ(167));
+ dram_pll_init(MHZ(167), type);
dram_disable_bypass();
break;
case 400:
diff --git a/include/soc/imx8m/ddr.h b/include/soc/imx8m/ddr.h
index a5a610909212..78b15f1d461a 100644
--- a/include/soc/imx8m/ddr.h
+++ b/include/soc/imx8m/ddr.h
@@ -372,14 +372,14 @@ enum ddrc_type {
int imx8mm_ddr_init(struct dram_timing_info *timing_info);
int imx8mq_ddr_init(struct dram_timing_info *timing_info);
int imx8mp_ddr_init(struct dram_timing_info *timing_info);
-int ddr_cfg_phy(struct dram_timing_info *timing_info);
+int ddr_cfg_phy(struct dram_timing_info *timing_info, enum ddrc_type type);
void load_lpddr4_phy_pie(void);
void ddrphy_trained_csr_save(struct dram_cfg_param *param, unsigned int num);
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);
+void ddrphy_init_set_dfi_clk(unsigned int drate, enum ddrc_type type);
void ddrphy_init_read_msg_block(enum fw_type type);
void update_umctl2_rank_space_setting(unsigned int pstat_num,
--
2.29.2
More information about the barebox
mailing list