void get_trained_CDD(unsigned int fsp);
unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr);
+ulong ddrphy_addr_remap(uint32_t paddr_apb_from_ctlr);
+
static inline void reg32_write(unsigned long addr, u32 val)
{
writel(val, addr);
}
#define dwc_ddrphy_apb_wr(addr, data) \
- reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * (addr), data)
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(addr), data)
#define dwc_ddrphy_apb_rd(addr) \
- reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * (addr))
+ reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(addr))
extern struct dram_cfg_param ddrphy_trained_csr[];
extern uint32_t ddrphy_trained_csr_num;
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2022 NXP
+ */
+
+#ifndef __ASM_ARCH_IMX8M_DDR_H
+#define __ASM_ARCH_IMX8M_DDR_H
+
+#include <asm/io.h>
+#include <asm/types.h>
+
+#define DDR_CTL_BASE 0x4E300000
+#define DDR_PHY_BASE 0x4E100000
+#define DDRMIX_BLK_CTRL_BASE 0x4E010000
+
+#define REG_DDRDSR_2 (DDR_CTL_BASE + 0xB24)
+#define REG_DDR_SDRAM_CFG (DDR_CTL_BASE + 0x110)
+#define REG_DDR_DEBUG_19 (DDR_CTL_BASE + 0xF48)
+
+#define SRC_BASE_ADDR (0x44460000)
+#define SRC_DPHY_BASE_ADDR (SRC_BASE_ADDR + 0x1400)
+#define REG_SRC_DPHY_SW_CTRL (SRC_DPHY_BASE_ADDR + 0x20)
+#define REG_SRC_DPHY_SINGLE_RESET_SW_CTRL (SRC_DPHY_BASE_ADDR + 0x24)
+
+#define IP2APB_DDRPHY_IPS_BASE_ADDR(X) (DDR_PHY_BASE + ((X) * 0x2000000))
+#define DDRPHY_MEM(X) (DDR_PHY_BASE + ((X) * 0x2000000) + 0x50000)
+
+/* PHY State */
+enum pstate {
+ PS0,
+ PS1,
+ PS2,
+ PS3,
+};
+
+enum msg_response {
+ TRAIN_SUCCESS = 0x7,
+ TRAIN_STREAM_START = 0x8,
+ TRAIN_FAIL = 0xff,
+};
+
+/* user data type */
+enum fw_type {
+ FW_1D_IMAGE,
+ FW_2D_IMAGE,
+};
+
+struct dram_cfg_param {
+ unsigned int reg;
+ unsigned int val;
+};
+
+struct dram_fsp_msg {
+ unsigned int drate;
+ enum fw_type fw_type;
+ struct dram_cfg_param *fsp_cfg;
+ unsigned int fsp_cfg_num;
+};
+
+struct dram_timing_info {
+ /* umctl2 config */
+ struct dram_cfg_param *ddrc_cfg;
+ unsigned int ddrc_cfg_num;
+ /* ddrphy config */
+ struct dram_cfg_param *ddrphy_cfg;
+ unsigned int ddrphy_cfg_num;
+ /* ddr fsp train info */
+ struct dram_fsp_msg *fsp_msg;
+ unsigned int fsp_msg_num;
+ /* ddr phy trained CSR */
+ struct dram_cfg_param *ddrphy_trained_csr;
+ unsigned int ddrphy_trained_csr_num;
+ /* ddr phy PIE */
+ struct dram_cfg_param *ddrphy_pie;
+ unsigned int ddrphy_pie_num;
+ /* initialized drate table */
+ unsigned int fsp_table[4];
+};
+
+extern struct dram_timing_info dram_timing;
+
+void ddr_load_train_firmware(enum fw_type type);
+int ddr_init(struct dram_timing_info *timing_info);
+int ddr_cfg_phy(struct dram_timing_info *timing_info);
+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);
+void board_dram_ecc_scrub(void);
+void ddrc_inline_ecc_scrub(unsigned int start_address,
+ unsigned int range_address);
+void ddrc_inline_ecc_scrub_end(unsigned int start_address,
+ unsigned int range_address);
+
+/* utils function for ddr phy training */
+int wait_ddrphy_training_complete(void);
+void ddrphy_init_set_dfi_clk(unsigned int drate);
+void ddrphy_init_read_msg_block(enum fw_type type);
+
+void get_trained_CDD(unsigned int fsp);
+
+ulong ddrphy_addr_remap(u32 paddr_apb_from_ctlr);
+
+static inline void reg32_write(unsigned long addr, u32 val)
+{
+ writel(val, addr);
+}
+
+static inline u32 reg32_read(unsigned long addr)
+{
+ return readl(addr);
+}
+
+static inline void reg32setbit(unsigned long addr, u32 bit)
+{
+ setbits_le32(addr, (1 << bit));
+}
+
+#define dwc_ddrphy_apb_wr(addr, data) \
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(addr), data)
+#define dwc_ddrphy_apb_rd(addr) \
+ reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(addr))
+
+extern struct dram_cfg_param ddrphy_trained_csr[];
+extern u32 ddrphy_trained_csr_num;
+
+#endif
obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/
obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/
obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/
+obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/
obj-$(CONFIG_SPL_DM_RESET) += reset/
obj-$(CONFIG_SPL_MUSB_NEW) += usb/musb-new/
obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/
source "drivers/ddr/imx/imx8m/Kconfig"
source "drivers/ddr/imx/imx8ulp/Kconfig"
+source "drivers/ddr/imx/imx9/Kconfig"
+source "drivers/ddr/imx/phy/Kconfig"
config IMX8M_DRAM
bool "imx8m dram"
+ select IMX_SNPS_DDR_PHY
config IMX8M_LPDDR4
bool "imx8m lpddr4"
#
ifdef CONFIG_SPL_BUILD
-obj-$(CONFIG_IMX8M_DRAM) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o ddr_init.o
+obj-$(CONFIG_IMX8M_DRAM) += ddr_init.o
+obj-y += ../phy/
endif
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
+static unsigned int g_cdd_rr_max[4];
+static unsigned int g_cdd_rw_max[4];
+static unsigned int g_cdd_wr_max[4];
+static unsigned int g_cdd_ww_max[4];
+
void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num)
{
int i = 0;
{
}
+void lpddr4_mr_write(unsigned int mr_rank, unsigned int mr_addr,
+ unsigned int mr_data)
+{
+ unsigned int tmp;
+ /*
+ * 1. Poll MRSTAT.mr_wr_busy until it is 0.
+ * This checks that there is no outstanding MR transaction.
+ * No writes should be performed to MRCTRL0 and MRCTRL1 if
+ * MRSTAT.mr_wr_busy = 1.
+ */
+ do {
+ tmp = reg32_read(DDRC_MRSTAT(0));
+ } while (tmp & 0x1);
+ /*
+ * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank and
+ * (for MRWs) MRCTRL1.mr_data to define the MR transaction.
+ */
+ reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4));
+ reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8) | mr_data);
+ reg32setbit(DDRC_MRCTRL0(0), 31);
+}
+
+unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr)
+{
+ unsigned int tmp;
+
+ reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1);
+ do {
+ tmp = reg32_read(DDRC_MRSTAT(0));
+ } while (tmp & 0x1);
+
+ reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1);
+ reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8));
+ reg32setbit(DDRC_MRCTRL0(0), 31);
+ do {
+ tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0));
+ } while ((tmp & 0x8) == 0);
+ tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0));
+ tmp = tmp & 0xff;
+ reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
+
+ return tmp;
+}
+
+static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
+ unsigned int addr_end)
+{
+ unsigned int i, imax = 0;
+
+ for (i = addr_start; i <= addr_end; i++) {
+ if (((data[i] >> 7) == 0) && data[i] > imax)
+ imax = data[i];
+ }
+
+ return imax;
+}
+
+void get_trained_CDD(u32 fsp)
+{
+ unsigned int i, ddr_type, tmp;
+ unsigned int cdd_cha[12], cdd_chb[12];
+ unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
+ unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ if (ddr_type == 0x20) {
+ for (i = 0; i < 6; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54013 + i) * 4);
+ cdd_cha[i * 2] = tmp & 0xff;
+ cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ for (i = 0; i < 7; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x5402c + i) * 4);
+ if (i == 0) {
+ cdd_cha[0] = (tmp >> 8) & 0xff;
+ } else if (i == 6) {
+ cdd_cha[11] = tmp & 0xff;
+ } else {
+ cdd_chb[i * 2 - 1] = tmp & 0xff;
+ cdd_chb[i * 2] = (tmp >> 8) & 0xff;
+ }
+ }
+
+ cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
+ cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
+ cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
+ cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
+ cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
+ cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
+ cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
+ cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
+ g_cdd_rr_max[fsp] =
+ cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
+ g_cdd_rw_max[fsp] =
+ cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
+ g_cdd_wr_max[fsp] =
+ cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
+ g_cdd_ww_max[fsp] =
+ cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
+ } else {
+ unsigned int ddr4_cdd[64];
+
+ for (i = 0; i < 29; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54012 + i) * 4);
+ ddr4_cdd[i * 2] = tmp & 0xff;
+ ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
+ g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
+ g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
+ g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
+ }
+}
+
+void update_umctl2_rank_space_setting(unsigned int pstat_num)
+{
+ unsigned int i, ddr_type;
+ unsigned int addr_slot, rdata, tmp, tmp_t;
+ unsigned int ddrc_w2r, ddrc_r2w, ddrc_wr_gap, ddrc_rd_gap;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ for (i = 0; i < pstat_num; i++) {
+ addr_slot = i ? (i + 1) * 0x1000 : 0;
+ if (ddr_type == 0x20) {
+ /* update r2w:[13:8], w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ } else {
+ /* update w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+ tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
+
+ /* update r2w:[13:8] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ }
+
+ if (!is_imx8mq()) {
+ /*
+ * update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static)
+ */
+ rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ if (is_imx8mp())
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
+ else
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ if (is_imx8mp())
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
+ else
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
+ }
+ }
+
+ if (is_imx8mq()) {
+ /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
+ rdata = reg32_read(DDRC_RANKCTL(0));
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write(DDRC_RANKCTL(0), tmp_t);
+ }
+}
+
int ddr_init(struct dram_timing_info *dram_timing)
{
unsigned int tmp, initial_drate, target_freq;
return 0;
}
+
+ulong ddrphy_addr_remap(uint32_t paddr_apb_from_ctlr)
+{
+ return 4 * paddr_apb_from_ctlr;
+}
--- /dev/null
+menu "i.MX9 DDR controllers"
+ depends on ARCH_IMX9
+
+config IMX9_DRAM
+ bool "imx9 dram"
+ select IMX_SNPS_DDR_PHY
+
+config IMX9_LPDDR4X
+ bool "imx9 lpddr4 and lpddr4x"
+ select IMX9_DRAM
+ help
+ Select the i.MX9 LPDDR4/4X driver support on i.MX9 SOC.
+
+config SAVED_DRAM_TIMING_BASE
+ hex "Define the base address for saved dram timing"
+ help
+ after DRAM is trained, need to save the dram related timming
+ info into memory for low power use.
+ default 0x204DC000
+
+endmenu
--- /dev/null
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX9_DRAM) += ddr_init.o
+obj-y += ../phy/
+endif
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/delay.h>
+
+void ddrphy_coldreset(void)
+{
+ /* dramphy_apb_n default 1 , assert -> 0, de_assert -> 1 */
+ /* dramphy_reset_n default 0 , assert -> 0, de_assert -> 1 */
+ /* dramphy_PwrOKIn default 0 , assert -> 1, de_assert -> 0 */
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ /* src_gen_dphy_PwrOKIn_sw_rst_de_assert() */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ mdelay(10);
+ /* src_gen_dphy_PwrOKIn_sw_rst_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert() */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+}
+
+void check_ddrc_idle(void)
+{
+ u32 regval;
+
+ do {
+ regval = readl(REG_DDRDSR_2);
+ if (regval & BIT(31))
+ break;
+ } while (1);
+}
+
+void check_dfi_init_complete(void)
+{
+ u32 regval;
+
+ do {
+ regval = readl(REG_DDRDSR_2);
+ if (regval & BIT(2))
+ break;
+ } while (1);
+ setbits_le32(REG_DDRDSR_2, BIT(2));
+}
+
+void ddrc_config(struct dram_cfg_param *ddrc_config, int num)
+{
+ int i = 0;
+
+ for (i = 0; i < num; i++) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+}
+
+void get_trained_CDD(u32 fsp)
+{
+}
+
+int ddr_init(struct dram_timing_info *dram_timing)
+{
+ unsigned int initial_drate;
+ int ret;
+ u32 regval;
+
+ debug("DDRINFO: start DRAM init\n");
+
+ /* reset ddrphy */
+ ddrphy_coldreset();
+
+ debug("DDRINFO: cfg clk\n");
+
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ ddrphy_init_set_dfi_clk(initial_drate);
+
+ /*
+ * Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ debug("DDRINFO:ddrphy config start\n");
+
+ ret = ddr_cfg_phy(dram_timing);
+ if (ret)
+ return ret;
+
+ debug("DDRINFO: ddrphy config done\n");
+
+ /* rogram the ddrc registers */
+ debug("DDRINFO: ddrc config start\n");
+ ddrc_config(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num);
+ debug("DDRINFO: ddrc config done\n");
+
+ check_dfi_init_complete();
+
+ regval = readl(REG_DDR_SDRAM_CFG);
+ writel((regval | 0x80000000), REG_DDR_SDRAM_CFG);
+
+ check_ddrc_idle();
+
+ /* save the dram timing config into memory */
+ dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+ return 0;
+}
+
+ulong ddrphy_addr_remap(u32 paddr_apb_from_ctlr)
+{
+ u32 paddr_apb_qual;
+ u32 paddr_apb_unqual_dec_22_13;
+ u32 paddr_apb_unqual_dec_19_13;
+ u32 paddr_apb_unqual_dec_12_1;
+ u32 paddr_apb_unqual;
+ u32 paddr_apb_phy;
+
+ paddr_apb_qual = (paddr_apb_from_ctlr << 1);
+ paddr_apb_unqual_dec_22_13 = ((paddr_apb_qual & 0x7fe000) >> 13);
+ paddr_apb_unqual_dec_12_1 = ((paddr_apb_qual & 0x1ffe) >> 1);
+
+ switch (paddr_apb_unqual_dec_22_13) {
+ case 0x000:
+ paddr_apb_unqual_dec_19_13 = 0x00;
+ break;
+ case 0x001:
+ paddr_apb_unqual_dec_19_13 = 0x01;
+ break;
+ case 0x002:
+ paddr_apb_unqual_dec_19_13 = 0x02;
+ break;
+ case 0x003:
+ paddr_apb_unqual_dec_19_13 = 0x03;
+ break;
+ case 0x004:
+ paddr_apb_unqual_dec_19_13 = 0x04;
+ break;
+ case 0x005:
+ paddr_apb_unqual_dec_19_13 = 0x05;
+ break;
+ case 0x006:
+ paddr_apb_unqual_dec_19_13 = 0x06;
+ break;
+ case 0x007:
+ paddr_apb_unqual_dec_19_13 = 0x07;
+ break;
+ case 0x008:
+ paddr_apb_unqual_dec_19_13 = 0x08;
+ break;
+ case 0x009:
+ paddr_apb_unqual_dec_19_13 = 0x09;
+ break;
+ case 0x00a:
+ paddr_apb_unqual_dec_19_13 = 0x0a;
+ break;
+ case 0x00b:
+ paddr_apb_unqual_dec_19_13 = 0x0b;
+ break;
+ case 0x100:
+ paddr_apb_unqual_dec_19_13 = 0x0c;
+ break;
+ case 0x101:
+ paddr_apb_unqual_dec_19_13 = 0x0d;
+ break;
+ case 0x102:
+ paddr_apb_unqual_dec_19_13 = 0x0e;
+ break;
+ case 0x103:
+ paddr_apb_unqual_dec_19_13 = 0x0f;
+ break;
+ case 0x104:
+ paddr_apb_unqual_dec_19_13 = 0x10;
+ break;
+ case 0x105:
+ paddr_apb_unqual_dec_19_13 = 0x11;
+ break;
+ case 0x106:
+ paddr_apb_unqual_dec_19_13 = 0x12;
+ break;
+ case 0x107:
+ paddr_apb_unqual_dec_19_13 = 0x13;
+ break;
+ case 0x108:
+ paddr_apb_unqual_dec_19_13 = 0x14;
+ break;
+ case 0x109:
+ paddr_apb_unqual_dec_19_13 = 0x15;
+ break;
+ case 0x10a:
+ paddr_apb_unqual_dec_19_13 = 0x16;
+ break;
+ case 0x10b:
+ paddr_apb_unqual_dec_19_13 = 0x17;
+ break;
+ case 0x200:
+ paddr_apb_unqual_dec_19_13 = 0x18;
+ break;
+ case 0x201:
+ paddr_apb_unqual_dec_19_13 = 0x19;
+ break;
+ case 0x202:
+ paddr_apb_unqual_dec_19_13 = 0x1a;
+ break;
+ case 0x203:
+ paddr_apb_unqual_dec_19_13 = 0x1b;
+ break;
+ case 0x204:
+ paddr_apb_unqual_dec_19_13 = 0x1c;
+ break;
+ case 0x205:
+ paddr_apb_unqual_dec_19_13 = 0x1d;
+ break;
+ case 0x206:
+ paddr_apb_unqual_dec_19_13 = 0x1e;
+ break;
+ case 0x207:
+ paddr_apb_unqual_dec_19_13 = 0x1f;
+ break;
+ case 0x208:
+ paddr_apb_unqual_dec_19_13 = 0x20;
+ break;
+ case 0x209:
+ paddr_apb_unqual_dec_19_13 = 0x21;
+ break;
+ case 0x20a:
+ paddr_apb_unqual_dec_19_13 = 0x22;
+ break;
+ case 0x20b:
+ paddr_apb_unqual_dec_19_13 = 0x23;
+ break;
+ case 0x300:
+ paddr_apb_unqual_dec_19_13 = 0x24;
+ break;
+ case 0x301:
+ paddr_apb_unqual_dec_19_13 = 0x25;
+ break;
+ case 0x302:
+ paddr_apb_unqual_dec_19_13 = 0x26;
+ break;
+ case 0x303:
+ paddr_apb_unqual_dec_19_13 = 0x27;
+ break;
+ case 0x304:
+ paddr_apb_unqual_dec_19_13 = 0x28;
+ break;
+ case 0x305:
+ paddr_apb_unqual_dec_19_13 = 0x29;
+ break;
+ case 0x306:
+ paddr_apb_unqual_dec_19_13 = 0x2a;
+ break;
+ case 0x307:
+ paddr_apb_unqual_dec_19_13 = 0x2b;
+ break;
+ case 0x308:
+ paddr_apb_unqual_dec_19_13 = 0x2c;
+ break;
+ case 0x309:
+ paddr_apb_unqual_dec_19_13 = 0x2d;
+ break;
+ case 0x30a:
+ paddr_apb_unqual_dec_19_13 = 0x2e;
+ break;
+ case 0x30b:
+ paddr_apb_unqual_dec_19_13 = 0x2f;
+ break;
+ case 0x010:
+ paddr_apb_unqual_dec_19_13 = 0x30;
+ break;
+ case 0x011:
+ paddr_apb_unqual_dec_19_13 = 0x31;
+ break;
+ case 0x012:
+ paddr_apb_unqual_dec_19_13 = 0x32;
+ break;
+ case 0x013:
+ paddr_apb_unqual_dec_19_13 = 0x33;
+ break;
+ case 0x014:
+ paddr_apb_unqual_dec_19_13 = 0x34;
+ break;
+ case 0x015:
+ paddr_apb_unqual_dec_19_13 = 0x35;
+ break;
+ case 0x016:
+ paddr_apb_unqual_dec_19_13 = 0x36;
+ break;
+ case 0x017:
+ paddr_apb_unqual_dec_19_13 = 0x37;
+ break;
+ case 0x018:
+ paddr_apb_unqual_dec_19_13 = 0x38;
+ break;
+ case 0x019:
+ paddr_apb_unqual_dec_19_13 = 0x39;
+ break;
+ case 0x110:
+ paddr_apb_unqual_dec_19_13 = 0x3a;
+ break;
+ case 0x111:
+ paddr_apb_unqual_dec_19_13 = 0x3b;
+ break;
+ case 0x112:
+ paddr_apb_unqual_dec_19_13 = 0x3c;
+ break;
+ case 0x113:
+ paddr_apb_unqual_dec_19_13 = 0x3d;
+ break;
+ case 0x114:
+ paddr_apb_unqual_dec_19_13 = 0x3e;
+ break;
+ case 0x115:
+ paddr_apb_unqual_dec_19_13 = 0x3f;
+ break;
+ case 0x116:
+ paddr_apb_unqual_dec_19_13 = 0x40;
+ break;
+ case 0x117:
+ paddr_apb_unqual_dec_19_13 = 0x41;
+ break;
+ case 0x118:
+ paddr_apb_unqual_dec_19_13 = 0x42;
+ break;
+ case 0x119:
+ paddr_apb_unqual_dec_19_13 = 0x43;
+ break;
+ case 0x210:
+ paddr_apb_unqual_dec_19_13 = 0x44;
+ break;
+ case 0x211:
+ paddr_apb_unqual_dec_19_13 = 0x45;
+ break;
+ case 0x212:
+ paddr_apb_unqual_dec_19_13 = 0x46;
+ break;
+ case 0x213:
+ paddr_apb_unqual_dec_19_13 = 0x47;
+ break;
+ case 0x214:
+ paddr_apb_unqual_dec_19_13 = 0x48;
+ break;
+ case 0x215:
+ paddr_apb_unqual_dec_19_13 = 0x49;
+ break;
+ case 0x216:
+ paddr_apb_unqual_dec_19_13 = 0x4a;
+ break;
+ case 0x217:
+ paddr_apb_unqual_dec_19_13 = 0x4b;
+ break;
+ case 0x218:
+ paddr_apb_unqual_dec_19_13 = 0x4c;
+ break;
+ case 0x219:
+ paddr_apb_unqual_dec_19_13 = 0x4d;
+ break;
+ case 0x310:
+ paddr_apb_unqual_dec_19_13 = 0x4e;
+ break;
+ case 0x311:
+ paddr_apb_unqual_dec_19_13 = 0x4f;
+ break;
+ case 0x312:
+ paddr_apb_unqual_dec_19_13 = 0x50;
+ break;
+ case 0x313:
+ paddr_apb_unqual_dec_19_13 = 0x51;
+ break;
+ case 0x314:
+ paddr_apb_unqual_dec_19_13 = 0x52;
+ break;
+ case 0x315:
+ paddr_apb_unqual_dec_19_13 = 0x53;
+ break;
+ case 0x316:
+ paddr_apb_unqual_dec_19_13 = 0x54;
+ break;
+ case 0x317:
+ paddr_apb_unqual_dec_19_13 = 0x55;
+ break;
+ case 0x318:
+ paddr_apb_unqual_dec_19_13 = 0x56;
+ break;
+ case 0x319:
+ paddr_apb_unqual_dec_19_13 = 0x57;
+ break;
+ case 0x020:
+ paddr_apb_unqual_dec_19_13 = 0x58;
+ break;
+ case 0x120:
+ paddr_apb_unqual_dec_19_13 = 0x59;
+ break;
+ case 0x220:
+ paddr_apb_unqual_dec_19_13 = 0x5a;
+ break;
+ case 0x320:
+ paddr_apb_unqual_dec_19_13 = 0x5b;
+ break;
+ case 0x040:
+ paddr_apb_unqual_dec_19_13 = 0x5c;
+ break;
+ case 0x140:
+ paddr_apb_unqual_dec_19_13 = 0x5d;
+ break;
+ case 0x240:
+ paddr_apb_unqual_dec_19_13 = 0x5e;
+ break;
+ case 0x340:
+ paddr_apb_unqual_dec_19_13 = 0x5f;
+ break;
+ case 0x050:
+ paddr_apb_unqual_dec_19_13 = 0x60;
+ break;
+ case 0x051:
+ paddr_apb_unqual_dec_19_13 = 0x61;
+ break;
+ case 0x052:
+ paddr_apb_unqual_dec_19_13 = 0x62;
+ break;
+ case 0x053:
+ paddr_apb_unqual_dec_19_13 = 0x63;
+ break;
+ case 0x054:
+ paddr_apb_unqual_dec_19_13 = 0x64;
+ break;
+ case 0x055:
+ paddr_apb_unqual_dec_19_13 = 0x65;
+ break;
+ case 0x056:
+ paddr_apb_unqual_dec_19_13 = 0x66;
+ break;
+ case 0x057:
+ paddr_apb_unqual_dec_19_13 = 0x67;
+ break;
+ case 0x070:
+ paddr_apb_unqual_dec_19_13 = 0x68;
+ break;
+ case 0x090:
+ paddr_apb_unqual_dec_19_13 = 0x69;
+ break;
+ case 0x190:
+ paddr_apb_unqual_dec_19_13 = 0x6a;
+ break;
+ case 0x290:
+ paddr_apb_unqual_dec_19_13 = 0x6b;
+ break;
+ case 0x390:
+ paddr_apb_unqual_dec_19_13 = 0x6c;
+ break;
+ case 0x0c0:
+ paddr_apb_unqual_dec_19_13 = 0x6d;
+ break;
+ case 0x0d0:
+ paddr_apb_unqual_dec_19_13 = 0x6e;
+ break;
+ default:
+ paddr_apb_unqual_dec_19_13 = 0x00;
+ break;
+ }
+
+ paddr_apb_unqual = ((paddr_apb_unqual_dec_19_13 << 13) | (paddr_apb_unqual_dec_12_1 << 1));
+
+ paddr_apb_phy = (paddr_apb_unqual << 1);
+
+ return paddr_apb_phy;
+}
--- /dev/null
+config IMX_SNPS_DDR_PHY
+ bool "i.MX Snopsys DDR PHY"
+ help
+ Select the DDR PHY driver support on i.MX8M and i.MX9 SOC.
--- /dev/null
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX_SNPS_DDR_PHY) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o
+endif
#include <log.h>
#include <linux/kernel.h>
#include <asm/arch/ddr.h>
-#include <asm/arch/lpddr4_define.h>
#include <asm/arch/sys_proto.h>
int ddr_cfg_phy(struct dram_timing_info *dram_timing)
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/sys_proto.h>
+
+static inline void poll_pmu_message_ready(void)
+{
+ unsigned int reg;
+
+ do {
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0004));
+ } while (reg & 0x1);
+}
+
+static inline void ack_pmu_message_receive(void)
+{
+ unsigned int reg;
+
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0031), 0x0);
+
+ do {
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0004));
+ } while (!(reg & 0x1));
+
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0031), 0x1);
+}
+
+static inline unsigned int get_mail(void)
+{
+ unsigned int reg;
+
+ poll_pmu_message_ready();
+
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0032));
+
+ ack_pmu_message_receive();
+
+ return reg;
+}
+
+static inline unsigned int get_stream_message(void)
+{
+ unsigned int reg, reg2;
+
+ poll_pmu_message_ready();
+
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0032));
+
+ reg2 = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0034));
+
+ reg2 = (reg2 << 16) | reg;
+
+ ack_pmu_message_receive();
+
+ return reg2;
+}
+
+static inline void decode_major_message(unsigned int mail)
+{
+ debug("[PMU Major message = 0x%08x]\n", mail);
+}
+
+static inline void decode_streaming_message(void)
+{
+ unsigned int string_index, arg __maybe_unused;
+ int i = 0;
+
+ string_index = get_stream_message();
+ debug("PMU String index = 0x%08x\n", string_index);
+ while (i < (string_index & 0xffff)) {
+ arg = get_stream_message();
+ debug("arg[%d] = 0x%08x\n", i, arg);
+ i++;
+ }
+
+ debug("\n");
+}
+
+int wait_ddrphy_training_complete(void)
+{
+ unsigned int mail;
+
+ while (1) {
+ mail = get_mail();
+ decode_major_message(mail);
+ if (mail == 0x08) {
+ decode_streaming_message();
+ } else if (mail == 0x07) {
+ debug("Training PASS\n");
+ return 0;
+ } else if (mail == 0xff) {
+ printf("Training FAILED\n");
+ return -1;
+ }
+ }
+}
+
+void ddrphy_init_set_dfi_clk(unsigned int drate)
+{
+ switch (drate) {
+ case 4000:
+ dram_pll_init(MHZ(1000));
+ dram_disable_bypass();
+ break;
+ case 3733:
+ dram_pll_init(MHZ(933));
+ dram_disable_bypass();
+ break;
+ case 3200:
+ dram_pll_init(MHZ(800));
+ dram_disable_bypass();
+ break;
+ case 3000:
+ dram_pll_init(MHZ(750));
+ dram_disable_bypass();
+ break;
+ case 2800:
+ dram_pll_init(MHZ(700));
+ dram_disable_bypass();
+ break;
+ case 2400:
+ dram_pll_init(MHZ(600));
+ dram_disable_bypass();
+ break;
+ case 1866:
+ dram_pll_init(MHZ(466));
+ dram_disable_bypass();
+ break;
+ case 1600:
+ dram_pll_init(MHZ(400));
+ dram_disable_bypass();
+ break;
+ case 1066:
+ dram_pll_init(MHZ(266));
+ dram_disable_bypass();
+ break;
+ case 667:
+ dram_pll_init(MHZ(167));
+ dram_disable_bypass();
+ break;
+ case 400:
+ dram_enable_bypass(MHZ(400));
+ break;
+ case 333:
+ dram_enable_bypass(MHZ(333));
+ break;
+ case 200:
+ dram_enable_bypass(MHZ(200));
+ break;
+ case 100:
+ dram_enable_bypass(MHZ(100));
+ break;
+ default:
+ return;
+ }
+}
+
+void ddrphy_init_read_msg_block(enum fw_type type)
+{
+}
#include <asm/io.h>
#include <asm/arch/ddr.h>
#include <asm/arch/ddr.h>
-#include <asm/arch/lpddr4_define.h>
#include <asm/sections.h>
DECLARE_GLOBAL_DATA_PTR;
dmem_start = imem_start + IMEM_LEN;
pr_from32 = imem_start;
- pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * IMEM_OFFSET_ADDR;
+ pr_to32 = IMEM_OFFSET_ADDR;
for (i = 0x0; i < IMEM_LEN; ) {
tmp32 = readl(pr_from32);
- writew(tmp32 & 0x0000ffff, pr_to32);
- pr_to32 += 4;
- writew((tmp32 >> 16) & 0x0000ffff, pr_to32);
- pr_to32 += 4;
+ writew(tmp32 & 0x0000ffff, DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ writew((tmp32 >> 16) & 0x0000ffff,
+ DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
pr_from32 += 4;
i += 4;
}
pr_from32 = dmem_start;
- pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * DMEM_OFFSET_ADDR;
+ pr_to32 = DMEM_OFFSET_ADDR;
for (i = 0x0; i < DMEM_LEN; ) {
tmp32 = readl(pr_from32);
- writew(tmp32 & 0x0000ffff, pr_to32);
- pr_to32 += 4;
- writew((tmp32 >> 16) & 0x0000ffff, pr_to32);
- pr_to32 += 4;
+ writew(tmp32 & 0x0000ffff, DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ writew((tmp32 >> 16) & 0x0000ffff,
+ DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
pr_from32 += 4;
i += 4;
}
debug("check ddr_pmu_train_imem code\n");
pr_from32 = imem_start;
- pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * IMEM_OFFSET_ADDR;
+ pr_to32 = IMEM_OFFSET_ADDR;
for (i = 0x0; i < IMEM_LEN; ) {
- tmp32 = (readw(pr_to32) & 0x0000ffff);
- pr_to32 += 4;
- tmp32 += ((readw(pr_to32) & 0x0000ffff) << 16);
+ tmp32 = (readw(DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32)) & 0x0000ffff);
+ pr_to32 += 1;
+ tmp32 += ((readw(DDR_TRAIN_CODE_BASE_ADDR +
+ ddrphy_addr_remap(pr_to32)) & 0x0000ffff) << 16);
if (tmp32 != readl(pr_from32)) {
debug("%lx %lx\n", pr_from32, pr_to32);
error++;
}
pr_from32 += 4;
- pr_to32 += 4;
+ pr_to32 += 1;
i += 4;
}
if (error)
debug("check ddr4_pmu_train_dmem code\n");
pr_from32 = dmem_start;
- pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * DMEM_OFFSET_ADDR;
+ pr_to32 = DMEM_OFFSET_ADDR;
for (i = 0x0; i < DMEM_LEN;) {
- tmp32 = (readw(pr_to32) & 0x0000ffff);
- pr_to32 += 4;
- tmp32 += ((readw(pr_to32) & 0x0000ffff) << 16);
+ tmp32 = (readw(DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32)) & 0x0000ffff);
+ pr_to32 += 1;
+ tmp32 += ((readw(DDR_TRAIN_CODE_BASE_ADDR +
+ ddrphy_addr_remap(pr_to32)) & 0x0000ffff) << 16);
if (tmp32 != readl(pr_from32)) {
debug("%lx %lx\n", pr_from32, pr_to32);
error++;
}
pr_from32 += 4;
- pr_to32 += 4;
+ pr_to32 += 1;
i += 4;
}