[PATCH 1/1] ddr: imx8m: Rework ddrphy code and fix DDR-1067 on iMX8MM
Ahmad Fatoum
a.fatoum at pengutronix.de
Wed Sep 29 02:48:18 PDT 2021
On 24.09.21 05:14, Trent Piepho wrote:
> This code could not decide if it was using speeds in Hz, or MHz, or just
> constants that identify specific speeds.
>
> Rework it to use symbolic constants for all speeds. Use arrays
> for configurations values for all cases. The DDR DRAM speed constants
> will be the array index, so the linear search for the configuration is
> eliminated.
>
> For iMX8MM type SoCs using the integr PLL the speed DDR-1067 with a 266⅔
> MHz clock will now work.
>
> The iM8MQ type SSCG PLL SoCs would previously silently program the PLL
> will zero values if a non-supported DDR rate was used. Now they will
> generate an error.
>
> Note that some PLL tables have entries for speeds that the main entry
> point for the code does not support, so they can't actually ever be
> used. This is not fixed.
>
> Signed-off-by: Trent Piepho <tpiepho at gmail.com>
> ---
Tested-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
> drivers/ddr/imx8m/ddrphy_utils.c | 332 ++++++++++++++-----------------
> 1 file changed, 147 insertions(+), 185 deletions(-)
>
> diff --git a/drivers/ddr/imx8m/ddrphy_utils.c b/drivers/ddr/imx8m/ddrphy_utils.c
> index 9a4e1a22e..a56033f78 100644
> --- a/drivers/ddr/imx8m/ddrphy_utils.c
> +++ b/drivers/ddr/imx8m/ddrphy_utils.c
> @@ -13,6 +13,109 @@
> #include <mach/imx8m-regs.h>
> #include <mach/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_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_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)
> @@ -102,64 +205,18 @@ int wait_ddrphy_training_complete(void)
> }
> }
>
> -struct dram_bypass_clk_setting {
> - ulong clk;
> - int alt_root_sel;
> - int alt_pre_div;
> - int apb_root_sel;
> - int apb_pre_div;
> -};
> -
> -#define MHZ(x) (1000000UL * (x))
> -
> -static struct dram_bypass_clk_setting imx8mq_dram_bypass_tbl[] = {
> - {
> - .clk = MHZ(100),
> - .alt_root_sel = 2,
> - .alt_pre_div = 1 - 1,
> - .apb_root_sel = 2,
> - .apb_pre_div = 2 - 1,
> - } , {
> - .clk = MHZ(250),
> - .alt_root_sel = 3,
> - .alt_pre_div = 2 - 1,
> - .apb_root_sel = 2,
> - .apb_pre_div = 2 - 1,
> - }, {
> - .clk = MHZ(400),
> - .alt_root_sel = 1,
> - .alt_pre_div = 2 - 1,
> - .apb_root_sel = 3,
> - .apb_pre_div = 2 - 1,
> - },
> -};
> -
> -static void dram_enable_bypass(ulong clk_val)
> +static void dram_enable_bypass(enum ddr_rate drate)
> {
> - int i;
> - struct dram_bypass_clk_setting *config;
> + const struct imx8m_bypass_config *config = &imx8m_bypass_table[drate];
>
> - for (i = 0; i < ARRAY_SIZE(imx8mq_dram_bypass_tbl); i++) {
> - if (clk_val == imx8mq_dram_bypass_tbl[i].clk)
> - break;
> - }
> -
> - if (i == ARRAY_SIZE(imx8mq_dram_bypass_tbl)) {
> - printf("No matched freq table %lu\n", clk_val);
> + if (!config->valid) {
> + printf("No matched freq table entry %u\n", drate);
> return;
> }
>
> - config = &imx8mq_dram_bypass_tbl[i];
> -
> - imx8m_clock_set_target_val(IMX8M_DRAM_ALT_CLK_ROOT,
> - IMX8M_CCM_TARGET_ROOTn_ENABLE |
> - IMX8M_CCM_TARGET_ROOTn_MUX(config->alt_root_sel) |
> - IMX8M_CCM_TARGET_ROOTn_PRE_DIV(config->alt_pre_div));
> - imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT,
> - IMX8M_CCM_TARGET_ROOTn_ENABLE |
> - IMX8M_CCM_TARGET_ROOTn_MUX(config->apb_root_sel) |
> - IMX8M_CCM_TARGET_ROOTn_PRE_DIV(config->apb_pre_div));
> - imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG, IMX8M_CCM_TARGET_ROOTn_ENABLE |
> + 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));
> }
>
> @@ -174,56 +231,15 @@ static void dram_disable_bypass(void)
> IMX8M_CCM_TARGET_ROOTn_PRE_DIV(5 - 1));
> }
>
> -struct imx_int_pll_rate_table {
> - u32 rate;
> - u32 r1;
> - u32 r2;
> -};
> -
> -#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 struct imx_int_pll_rate_table imx8mm_fracpll_tbl[] = {
> - { .rate = 1000000000U, .r1 = MDIV(250) | PDIV(3) | SDIV(1), .r2 = 0 },
> - { .rate = 800000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(0), .r2 = 0 },
> - { .rate = 750000000U, .r1 = MDIV(250) | PDIV(8) | SDIV(0), .r2 = 0 },
> - { .rate = 650000000U, .r1 = MDIV(325) | PDIV(3) | SDIV(2), .r2 = 0 },
> - { .rate = 600000000U, .r1 = MDIV(300) | PDIV(3) | SDIV(2), .r2 = 0 },
> - { .rate = 594000000U, .r1 = MDIV( 99) | PDIV(1) | SDIV(2), .r2 = 0 },
> - { .rate = 400000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(1), .r2 = 0 },
> - { .rate = 266666667U, .r1 = MDIV(400) | PDIV(9) | SDIV(2), .r2 = 0 },
> - { .rate = 167000000U, .r1 = MDIV(334) | PDIV(3) | SDIV(4), .r2 = 0 },
> - { .rate = 100000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(3), .r2 = 0 },
> -};
> -
> -static struct imx_int_pll_rate_table *fracpll(u32 freq)
> -{
> - int i;
> -
> - for (i = 0; i < ARRAY_SIZE(imx8mm_fracpll_tbl); i++)
> - if (freq == imx8mm_fracpll_tbl[i].rate)
> - return &imx8mm_fracpll_tbl[i];
> -
> - return NULL;
> -}
> -
> -static int dram_frac_pll_init(u32 freq)
> +static int dram_frac_pll_init(enum ddr_rate drate)
> {
> volatile int i;
> u32 tmp;
> void *pll_base;
> - struct imx_int_pll_rate_table *rate;
> + const struct imx8mm_fracpll_config *config = &imx8mm_fracpll_table[drate];
>
> - rate = fracpll(freq);
> - if (!rate) {
> - printf("No matched freq table %u\n", freq);
> + if (!config->valid) {
> + printf("No matched freq table entry %u\n", drate);
> return -EINVAL;
> }
>
> @@ -242,8 +258,8 @@ static int dram_frac_pll_init(u32 freq)
> tmp &= ~RST_MASK;
> writel(tmp, pll_base);
>
> - writel(rate->r1, pll_base + 4);
> - writel(rate->r2, pll_base + 8);
> + writel(config->r1, pll_base + 4);
> + writel(config->r2, pll_base + 8);
>
> for (i = 0; i < 1000; i++);
>
> @@ -261,25 +277,16 @@ static int dram_frac_pll_init(u32 freq)
> return 0;
> }
>
> -#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)
> +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) {
> + printf("No matched freq table entry %u\n", drate);
> + return -EINVAL;
> + }
>
> /* Bypass */
> setbits_le32(pll_base, SSCG_PLL_BYPASS1 | SSCG_PLL_BYPASS2);
> @@ -289,36 +296,7 @@ static int dram_sscg_pll_init(u32 freq)
> 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;
> - }
> -
> + val |= config->val;
> writel(val, pll_base + 0x8);
>
> /* Clear power down bit */
> @@ -337,59 +315,43 @@ static int dram_sscg_pll_init(u32 freq)
> return 0;
> }
>
> -static int dram_pll_init(u32 freq, enum ddrc_type type)
> +static int dram_pll_init(enum ddr_rate drate, enum ddrc_type type)
> {
> switch (type) {
> case DDRC_TYPE_MQ:
> - return dram_sscg_pll_init(freq);
> + return dram_sscg_pll_init(drate);
> case DDRC_TYPE_MM:
> case DDRC_TYPE_MP:
> - return dram_frac_pll_init(freq);
> + return dram_frac_pll_init(drate);
> default:
> return -ENODEV;
> }
> }
>
> -void ddrphy_init_set_dfi_clk(unsigned int drate, enum ddrc_type type)
> +void ddrphy_init_set_dfi_clk(unsigned int drate_mhz, enum ddrc_type type)
> {
> - switch (drate) {
> - case 4000:
> - dram_pll_init(MHZ(1000), type);
> - dram_disable_bypass();
> - break;
> - case 3200:
> - dram_pll_init(MHZ(800), type);
> - dram_disable_bypass();
> - break;
> - case 3000:
> - dram_pll_init(MHZ(750), type);
> - dram_disable_bypass();
> - break;
> - case 2400:
> - dram_pll_init(MHZ(600), type);
> - dram_disable_bypass();
> - break;
> - case 1600:
> - dram_pll_init(MHZ(400), type);
> - dram_disable_bypass();
> - break;
> - case 1066:
> - dram_pll_init(MHZ(266),type);
> - dram_disable_bypass();
> - break;
> - case 667:
> - dram_pll_init(MHZ(167), type);
> - dram_disable_bypass();
> - break;
> - case 400:
> - dram_enable_bypass(MHZ(400));
> - break;
> - case 100:
> - dram_enable_bypass(MHZ(100));
> - break;
> + enum ddr_rate drate;
> +
> + switch (drate_mhz) {
> + case 4000: drate = DDR_4000; 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:
> 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)
>
--
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