[PATCH 1/5] ddr: imx8m: implement i.MX8MQ support
Sascha Hauer
sha at pengutronix.de
Wed Jan 6 03:47:40 EST 2021
On Sun, Dec 27, 2020 at 10:50:38PM +0100, Lucas Stach wrote:
> 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(-)
Applied, thanks
Sascha
>
> 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
>
>
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list