nxp: ddr driver enablement for nxp layerscape soc

DDR driver for NXP layerscape SoC(s):
 - lx2160aqds
 - lx2162aqds
 - lx2160ardb
 - Other Board with SoC(s) like ls1046a, ls1043a etc;
	-- These other boards are not verified yet.

Signed-off-by: Rajesh Bhagat <rajesh.bhagat@nxp.com>
Signed-off-by: York Sun <york.sun@nxp.com>
Signed-off-by: Udit Agarwal <udit.agarwal@nxp.com>
Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Change-Id: Ic84a63cb30eba054f432d479862cd4d1097cbbaf
diff --git a/drivers/nxp/ddr/nxp-ddr/README.odt b/drivers/nxp/ddr/nxp-ddr/README.odt
new file mode 100644
index 0000000..8796302
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/README.odt
@@ -0,0 +1,31 @@
+Table for dynamic ODT for DDR4 with PHY generation 2
+====================================================
+Two-slot system
+Only symmetric configurations are supported for interleaving. Non-symmetric
+configurations are possible but not covered here. First slot empty is possbile
+but prohibited for simplicity.
++-----------------------+-------------+---------------+-----------------------------+-----------------------------+
+|     Configuration     |             |DRAM controller|           Slot 1            |           Slot 2            |
++-----------+-----------+-------------+-------+-------+--------------+--------------+--------------+--------------+
+|           |           |             |       |       |    Rank 1    |   Rank 2     |   Rank 1     |    Rank 2    |
+|  Slot 1   |  Slot 2   | Write/Read  | Write | Read  |-------+------+-------+------+-------+------+-------+------+
+|           |           |             |       |       | Write | Read | Write | Read | Write | Read | Write | Read |
++-----------+-----------+------+------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |      |Rank 1|  off  |  60   |  240  | off  |   60  | 240  |   60  |  60  |   60  |  60  |
+|           |           |Slot 1|------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |      |Rank 2|  off  |  60   |   60  | 240  |  240  | off  |   60  |  60  |   60  |  60  |
+| Dual Rank | Dual Rank |------+------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |      |Rank 1|  off  |  60   |   60  |  60  |   60  |  60  |  240  | off  |   60  | 240  |
+|           |           |Slot 2|------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |      |Rank 2|  off  |  60   |   60  |  60  |   60  |  60  |   60  | 240  |  240  | off  |
++-----------+-----------+------+------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |  Slot 1     |  off  |  60   |   80  |  off |       |      |       |      |       |      |
+|Single Rank|Single Rank|-------------+-------+-------+-------+------+-------+------+-------+------+-------+------+
+|           |           |  Slot 2     |  off  |  60   |       |      |       |      |   80  | off  |
++-----------+-----------+------+------+-------+-------+-------+------+-------+------+-------+------+
+|           |           |      |Rank 1|  off  |  80   |   80  | off  |  off  | off  |
+| Dual Rank |           |Slot 1|------+-------+-------+-------+------+-------+------+
+|           |           |      |Rank 2|  off  |  80   |   80  | off  |  off  | off  |
++-----------+-----------+-------------+-------+-------+-------+------+-------+------+
+|Single Rank|           |  Slot 1     |  off  |  80   |   80  | off  |
++-----------+-----------+-------------+-------+-------+-------+------+
diff --git a/drivers/nxp/ddr/nxp-ddr/ddr.c b/drivers/nxp/ddr/nxp-ddr/ddr.c
new file mode 100644
index 0000000..216e05c
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <ddr.h>
+#ifndef CONFIG_DDR_NODIMM
+#include <i2c.h>
+#endif
+#include <nxp_timer.h>
+
+struct dynamic_odt {
+	unsigned int odt_rd_cfg;
+	unsigned int odt_wr_cfg;
+	unsigned int odt_rtt_norm;
+	unsigned int odt_rtt_wr;
+};
+
+#ifndef CONFIG_STATIC_DDR
+#if defined(PHY_GEN2_FW_IMAGE_BUFFER) && !defined(NXP_DDR_PHY_GEN2)
+#error Missing NXP_DDR_PHY_GEN2
+#endif
+#ifdef NXP_DDR_PHY_GEN2
+static const struct dynamic_odt single_D[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs1 */
+		DDR_ODT_NEVER,
+		DDR_ODT_NEVER,
+		DDR4_RTT_OFF,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{}
+};
+
+static const struct dynamic_odt single_S[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{},
+	{},
+};
+
+static const struct dynamic_odt dual_DD[4] = {
+	{	/* cs0 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_60_OHM,
+		DDR4_RTT_WR_240_OHM
+	},
+	{	/* cs1 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_60_OHM,
+		DDR4_RTT_WR_240_OHM
+	},
+	{	/* cs2 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_60_OHM,
+		DDR4_RTT_WR_240_OHM
+	},
+	{	/* cs3 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_60_OHM,
+		DDR4_RTT_WR_240_OHM
+	}
+};
+
+static const struct dynamic_odt dual_SS[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{	/* cs2 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{}
+};
+
+static const struct dynamic_odt dual_D0[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_SAME_DIMM,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs1 */
+		DDR_ODT_NEVER,
+		DDR_ODT_NEVER,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{}
+};
+
+static const struct dynamic_odt dual_S0[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_CS,
+		DDR4_RTT_80_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{},
+	{}
+};
+#else
+static const struct dynamic_odt single_D[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_40_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs1 */
+		DDR_ODT_NEVER,
+		DDR_ODT_NEVER,
+		DDR4_RTT_OFF,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{}
+};
+
+static const struct dynamic_odt single_S[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_ALL,
+		DDR4_RTT_40_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{},
+	{},
+};
+
+static const struct dynamic_odt dual_DD[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_SAME_DIMM,
+		DDR4_RTT_120_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs1 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_OTHER_DIMM,
+		DDR4_RTT_34_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs2 */
+		DDR_ODT_NEVER,
+		DDR_ODT_SAME_DIMM,
+		DDR4_RTT_120_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs3 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_OTHER_DIMM,
+		DDR4_RTT_34_OHM,
+		DDR4_RTT_WR_OFF
+	}
+};
+
+static const struct dynamic_odt dual_SS[4] = {
+	{	/* cs0 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_34_OHM,
+		DDR4_RTT_WR_120_OHM
+	},
+	{},
+	{	/* cs2 */
+		DDR_ODT_OTHER_DIMM,
+		DDR_ODT_ALL,
+		DDR4_RTT_34_OHM,
+		DDR4_RTT_WR_120_OHM
+	},
+	{}
+};
+
+static const struct dynamic_odt dual_D0[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_SAME_DIMM,
+		DDR4_RTT_40_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{	/* cs1 */
+		DDR_ODT_NEVER,
+		DDR_ODT_NEVER,
+		DDR4_RTT_OFF,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{}
+};
+
+static const struct dynamic_odt dual_S0[4] = {
+	{	/* cs0 */
+		DDR_ODT_NEVER,
+		DDR_ODT_CS,
+		DDR4_RTT_40_OHM,
+		DDR4_RTT_WR_OFF
+	},
+	{},
+	{},
+	{}
+};
+#endif /* NXP_DDR_PHY_GEN2 */
+
+/*
+ * Automatically select bank interleaving mode based on DIMMs
+ * in this order: cs0_cs1_cs2_cs3, cs0_cs1, null.
+ * This function only deal with one or two slots per controller.
+ */
+static inline unsigned int auto_bank_intlv(const int cs_in_use,
+					   const struct dimm_params *pdimm)
+{
+	switch (cs_in_use) {
+	case 0xf:
+		return DDR_BA_INTLV_CS0123;
+	case 0x3:
+		return DDR_BA_INTLV_CS01;
+	case 0x1:
+		return DDR_BA_NONE;
+	case 0x5:
+		return DDR_BA_NONE;
+	default:
+		break;
+	}
+
+	return 0U;
+}
+
+static int cal_odt(const unsigned int clk,
+		   struct memctl_opt *popts,
+		   struct ddr_conf *conf,
+		   struct dimm_params *pdimm,
+		   const int dimm_slot_per_ctrl)
+
+{
+	unsigned int i;
+	const struct dynamic_odt *pdodt = NULL;
+
+	const static struct dynamic_odt *table[2][5] = {
+		{single_S, single_D, NULL, NULL},
+		{dual_SS, dual_DD, NULL, NULL},
+	};
+
+	if (dimm_slot_per_ctrl != 1 && dimm_slot_per_ctrl != 2) {
+		ERROR("Unsupported number of DIMMs\n");
+		return -EINVAL;
+	}
+
+	pdodt = table[dimm_slot_per_ctrl - 1][pdimm->n_ranks - 1];
+	if (pdodt == dual_SS) {
+		pdodt = (conf->cs_in_use == 0x5) ? dual_SS :
+			((conf->cs_in_use == 0x1) ? dual_S0 : NULL);
+	} else if (pdodt == dual_DD) {
+		pdodt = (conf->cs_in_use == 0xf) ? dual_DD :
+			((conf->cs_in_use == 0x3) ? dual_D0 : NULL);
+	}
+	if (pdodt == dual_DD && pdimm->package_3ds) {
+		ERROR("Too many 3DS DIMMs.\n");
+		return -EINVAL;
+	}
+
+	if (pdodt == NULL) {
+		ERROR("Error determing ODT.\n");
+		return -EINVAL;
+	}
+
+	/* Pick chip-select local options. */
+	for (i = 0U; i < DDRC_NUM_CS; i++) {
+		debug("cs %d\n", i);
+		popts->cs_odt[i].odt_rd_cfg = pdodt[i].odt_rd_cfg;
+		debug("     odt_rd_cfg 0x%x\n",
+			  popts->cs_odt[i].odt_rd_cfg);
+		popts->cs_odt[i].odt_wr_cfg = pdodt[i].odt_wr_cfg;
+		debug("     odt_wr_cfg 0x%x\n",
+			  popts->cs_odt[i].odt_wr_cfg);
+		popts->cs_odt[i].odt_rtt_norm = pdodt[i].odt_rtt_norm;
+		debug("     odt_rtt_norm 0x%x\n",
+			  popts->cs_odt[i].odt_rtt_norm);
+		popts->cs_odt[i].odt_rtt_wr = pdodt[i].odt_rtt_wr;
+		debug("     odt_rtt_wr 0x%x\n",
+			  popts->cs_odt[i].odt_rtt_wr);
+		popts->cs_odt[i].auto_precharge = 0;
+		debug("     auto_precharge %d\n",
+			  popts->cs_odt[i].auto_precharge);
+	}
+
+	return 0;
+}
+
+static int cal_opts(const unsigned int clk,
+		    struct memctl_opt *popts,
+		    struct ddr_conf *conf,
+		    struct dimm_params *pdimm,
+		    const int dimm_slot_per_ctrl,
+		    const unsigned int ip_rev)
+{
+	popts->rdimm = pdimm->rdimm;
+	popts->mirrored_dimm = pdimm->mirrored_dimm;
+#ifdef CONFIG_DDR_ECC_EN
+	popts->ecc_mode = pdimm->edc_config == 0x02 ? 1 : 0;
+#endif
+	popts->ctlr_init_ecc = popts->ecc_mode;
+	debug("ctlr_init_ecc %d\n", popts->ctlr_init_ecc);
+	popts->self_refresh_in_sleep = 1;
+	popts->dynamic_power = 0;
+
+	/*
+	 * check sdram width, allow platform override
+	 * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit
+	 */
+	if (pdimm->primary_sdram_width == 64) {
+		popts->data_bus_dimm = DDR_DBUS_64;
+		popts->otf_burst_chop_en = 1;
+	} else if (pdimm->primary_sdram_width == 32) {
+		popts->data_bus_dimm = DDR_DBUS_32;
+		popts->otf_burst_chop_en = 0;
+	} else if (pdimm->primary_sdram_width == 16) {
+		popts->data_bus_dimm = DDR_DBUS_16;
+		popts->otf_burst_chop_en = 0;
+	} else {
+		ERROR("primary sdram width invalid!\n");
+		return -EINVAL;
+	}
+	popts->data_bus_used = popts->data_bus_dimm;
+	popts->x4_en = (pdimm->device_width == 4) ? 1 : 0;
+	debug("x4_en %d\n", popts->x4_en);
+
+	/* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */
+	if (popts->rdimm != 0) {
+		popts->ap_en = 1; /* 0 = disable,  1 = enable */
+	} else {
+		popts->ap_en = 0; /* disabled for DDR4 UDIMM/discrete default */
+	}
+
+	if (ip_rev == 0x50500) {
+		popts->ap_en = 0;
+	}
+
+	debug("ap_en %d\n", popts->ap_en);
+
+	/* BSTTOPRE precharge interval uses 1/4 of refint value. */
+	popts->bstopre = picos_to_mclk(clk, pdimm->refresh_rate_ps) >> 2;
+	popts->tfaw_ps = pdimm->tfaw_ps;
+
+	return 0;
+}
+
+static void cal_intlv(const int num_ctlrs,
+		      struct memctl_opt *popts,
+		      struct ddr_conf *conf,
+		      struct dimm_params *pdimm)
+{
+#ifdef NXP_DDR_INTLV_256B
+	if (num_ctlrs == 2) {
+		popts->ctlr_intlv = 1;
+		popts->ctlr_intlv_mode = DDR_256B_INTLV;
+	}
+#endif
+	debug("ctlr_intlv %d\n", popts->ctlr_intlv);
+	debug("ctlr_intlv_mode %d\n", popts->ctlr_intlv_mode);
+
+	popts->ba_intlv = auto_bank_intlv(conf->cs_in_use, pdimm);
+	debug("ba_intlv 0x%x\n", popts->ba_intlv);
+}
+
+static int update_burst_length(struct memctl_opt *popts)
+{
+	/* Choose burst length. */
+	if ((popts->data_bus_used == DDR_DBUS_32) ||
+	    (popts->data_bus_used == DDR_DBUS_16)) {
+		/* 32-bit or 16-bit bus */
+		popts->otf_burst_chop_en = 0;
+		popts->burst_length = DDR_BL8;
+	} else if (popts->otf_burst_chop_en != 0) { /* on-the-fly burst chop */
+		popts->burst_length = DDR_OTF;	/* on-the-fly BC4 and BL8 */
+	} else {
+		popts->burst_length = DDR_BL8;
+	}
+	debug("data_bus_used %d\n", popts->data_bus_used);
+	debug("otf_burst_chop_en %d\n", popts->otf_burst_chop_en);
+	debug("burst_length 0x%x\n", popts->burst_length);
+	/*
+	 * If a reduced data width is requested, but the SPD
+	 * specifies a physically wider device, adjust the
+	 * computed dimm capacities accordingly before
+	 * assigning addresses.
+	 * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit
+	 */
+	if (popts->data_bus_dimm > popts->data_bus_used) {
+		ERROR("Data bus configuration error\n");
+		return -EINVAL;
+	}
+	popts->dbw_cap_shift = popts->data_bus_used - popts->data_bus_dimm;
+	debug("dbw_cap_shift %d\n", popts->dbw_cap_shift);
+
+	return 0;
+}
+
+int cal_board_params(struct ddr_info *priv,
+		     const struct board_timing *dimm,
+		     int len)
+{
+	const unsigned long speed = priv->clk / 1000000;
+	const struct dimm_params *pdimm = &priv->dimm;
+	struct memctl_opt *popts = &priv->opt;
+	struct rc_timing const *prt = NULL;
+	struct rc_timing const *chosen = NULL;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (pdimm->rc == dimm[i].rc) {
+			prt = dimm[i].p;
+			break;
+		}
+	}
+	if (prt == NULL) {
+		ERROR("Board parameters no match.\n");
+		return -EINVAL;
+	}
+	while (prt->speed_bin != 0) {
+		if (speed <= prt->speed_bin) {
+			chosen = prt;
+			break;
+		}
+		prt++;
+	}
+	if (chosen == NULL) {
+		ERROR("timing no match for speed %lu\n", speed);
+		return -EINVAL;
+	}
+	popts->clk_adj = prt->clk_adj;
+	popts->wrlvl_start = prt->wrlvl;
+	popts->wrlvl_ctl_2 = (prt->wrlvl * 0x01010101 + dimm[i].add1) &
+			     0xFFFFFFFF;
+	popts->wrlvl_ctl_3 = (prt->wrlvl * 0x01010101 + dimm[i].add2) &
+			     0xFFFFFFFF;
+
+	return 0;
+}
+
+static int synthesize_ctlr(struct ddr_info *priv)
+{
+	int ret;
+
+	ret = cal_odt(priv->clk,
+		      &priv->opt,
+		      &priv->conf,
+		      &priv->dimm,
+		      priv->dimm_on_ctlr);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = cal_opts(priv->clk,
+		       &priv->opt,
+		       &priv->conf,
+		       &priv->dimm,
+		       priv->dimm_on_ctlr,
+		       priv->ip_rev);
+
+	if (ret != 0) {
+		return ret;
+	}
+
+	cal_intlv(priv->num_ctlrs, &priv->opt, &priv->conf, &priv->dimm);
+	ret = ddr_board_options(priv);
+	if (ret != 0) {
+		ERROR("Failed matching board timing.\n");
+	}
+
+	ret = update_burst_length(&priv->opt);
+
+	return ret;
+}
+
+/* Return the bit mask of valid DIMMs found */
+static int parse_spd(struct ddr_info *priv)
+{
+	struct ddr_conf *conf = &priv->conf;
+	struct dimm_params *dimm = &priv->dimm;
+	int j, valid_mask = 0;
+
+#ifdef CONFIG_DDR_NODIMM
+	valid_mask = ddr_get_ddr_params(dimm, conf);
+	if (valid_mask < 0) {
+		ERROR("DDR params error\n");
+		return valid_mask;
+	}
+#else
+	const int *spd_addr = priv->spd_addr;
+	const int num_ctlrs = priv->num_ctlrs;
+	const int num_dimm = priv->dimm_on_ctlr;
+	struct ddr4_spd spd[2];
+	unsigned int spd_checksum[2];
+	int addr_idx = 0;
+	int spd_idx = 0;
+	int ret, addr, i;
+
+	/* Scan all DIMMs */
+	for (i = 0; i < num_ctlrs; i++) {
+		debug("Controller %d\n", i);
+		for (j = 0; j < num_dimm; j++, addr_idx++) {
+			debug("DIMM %d\n", j);
+			addr = spd_addr[addr_idx];
+			if (addr == 0) {
+				if (j == 0) {
+					ERROR("First SPD addr wrong.\n");
+					return -EINVAL;
+				}
+				continue;
+			}
+			debug("addr 0x%x\n", addr);
+			ret = read_spd(addr, &spd[spd_idx],
+				       sizeof(struct ddr4_spd));
+			if (ret != 0) {	/* invalid */
+				debug("Invalid SPD at address 0x%x\n", addr);
+				continue;
+			}
+
+			spd_checksum[spd_idx] =
+				(spd[spd_idx].crc[1] << 24) |
+				(spd[spd_idx].crc[0] << 16) |
+				(spd[spd_idx].mod_section.uc[127] << 8) |
+				(spd[spd_idx].mod_section.uc[126] << 0);
+			debug("checksum 0x%x\n", spd_checksum[spd_idx]);
+			if (spd_checksum[spd_idx] == 0) {
+				debug("Bad checksum, ignored.\n");
+				continue;
+			}
+			if (spd_idx == 0) {
+				/* first valid SPD */
+				ret = cal_dimm_params(&spd[0], dimm);
+				if (ret != 0) {
+					ERROR("SPD calculation error\n");
+					return -EINVAL;
+				}
+			}
+
+			if (spd_idx != 0 && spd_checksum[0] !=
+			    spd_checksum[spd_idx]) {
+				ERROR("Not identical DIMMs.\n");
+				return -EINVAL;
+			}
+			conf->dimm_in_use[j] = 1;
+			valid_mask |= 1 << addr_idx;
+			spd_idx = 1;
+		}
+		debug("done with controller %d\n", i);
+	}
+	switch (num_ctlrs) {
+	case 1:
+		if ((valid_mask & 0x1) == 0) {
+			ERROR("First slot cannot be empty.\n");
+			return -EINVAL;
+		}
+		break;
+	case 2:
+		switch (num_dimm) {
+		case 1:
+			if (valid_mask == 0) {
+				ERROR("Both slot empty\n");
+				return -EINVAL;
+			}
+			break;
+		case 2:
+			if (valid_mask != 0x5 &&
+			    valid_mask != 0xf &&
+			    (valid_mask & 0x7) != 0x4 &&
+			    (valid_mask & 0xd) != 0x1) {
+				ERROR("Invalid DIMM combination.\n");
+				return -EINVAL;
+			}
+			break;
+		default:
+			ERROR("Invalid number of DIMMs.\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		ERROR("Invalid number of controllers.\n");
+		return -EINVAL;
+	}
+	/* now we have valid and identical DIMMs on controllers */
+#endif	/* CONFIG_DDR_NODIMM */
+
+	debug("cal cs\n");
+	conf->cs_in_use = 0;
+	for (j = 0; j < DDRC_NUM_DIMM; j++) {
+		if (conf->dimm_in_use[j] == 0) {
+			continue;
+		}
+		switch (dimm->n_ranks) {
+		case 4:
+			ERROR("Quad-rank DIMM not supported\n");
+			return -EINVAL;
+		case 2:
+			conf->cs_on_dimm[j] = 0x3 << (j * CONFIG_CS_PER_SLOT);
+			conf->cs_in_use |= conf->cs_on_dimm[j];
+			break;
+		case 1:
+			conf->cs_on_dimm[j] = 0x1 << (j * CONFIG_CS_PER_SLOT);
+			conf->cs_in_use |= conf->cs_on_dimm[j];
+			break;
+		default:
+			ERROR("SPD error with n_ranks\n");
+			return -EINVAL;
+		}
+		debug("cs_in_use = %x\n", conf->cs_in_use);
+		debug("cs_on_dimm[%d] = %x\n", j, conf->cs_on_dimm[j]);
+	}
+#ifndef CONFIG_DDR_NODIMM
+	if (priv->dimm.rdimm != 0) {
+		NOTICE("RDIMM %s\n", priv->dimm.mpart);
+	} else {
+		NOTICE("UDIMM %s\n", priv->dimm.mpart);
+	}
+#else
+	NOTICE("%s\n", priv->dimm.mpart);
+#endif
+
+	return valid_mask;
+}
+
+static unsigned long long assign_intlv_addr(
+	const struct dimm_params *pdimm,
+	const struct memctl_opt *opt,
+	struct ddr_conf *conf,
+	const unsigned long long current_mem_base)
+{
+	int i;
+	int ctlr_density_mul = 0;
+	const unsigned long long rank_density = pdimm->rank_density >>
+						opt->dbw_cap_shift;
+	unsigned long long total_ctlr_mem;
+
+	debug("rank density 0x%llx\n", rank_density);
+	switch (opt->ba_intlv & DDR_BA_INTLV_CS0123) {
+	case DDR_BA_INTLV_CS0123:
+		ctlr_density_mul = 4;
+		break;
+	case DDR_BA_INTLV_CS01:
+		ctlr_density_mul = 2;
+		break;
+	default:
+		ctlr_density_mul = 1;
+		break;
+	}
+	debug("ctlr density mul %d\n", ctlr_density_mul);
+	switch (opt->ctlr_intlv_mode) {
+	case DDR_256B_INTLV:
+		total_ctlr_mem = 2 * ctlr_density_mul * rank_density;
+		break;
+	default:
+		ERROR("Unknown interleaving mode");
+		return 0;
+	}
+	conf->base_addr = current_mem_base;
+	conf->total_mem = total_ctlr_mem;
+
+	/* overwrite cs_in_use bitmask with controller interleaving */
+	conf->cs_in_use = (1 << ctlr_density_mul) - 1;
+	debug("Overwrite cs_in_use as %x\n", conf->cs_in_use);
+
+	/* Fill addr with each cs in use */
+	for (i = 0; i < ctlr_density_mul; i++) {
+		conf->cs_base_addr[i] = current_mem_base;
+		conf->cs_size[i] = total_ctlr_mem;
+		debug("CS %d\n", i);
+		debug("    base_addr 0x%llx\n", conf->cs_base_addr[i]);
+		debug("    size 0x%llx\n", conf->cs_size[i]);
+	}
+
+	return total_ctlr_mem;
+}
+
+static unsigned long long assign_non_intlv_addr(
+	const struct dimm_params *pdimm,
+	const struct memctl_opt *opt,
+	struct ddr_conf *conf,
+	unsigned long long current_mem_base)
+{
+	int i;
+	const unsigned long long rank_density = pdimm->rank_density >>
+						opt->dbw_cap_shift;
+	unsigned long long total_ctlr_mem = 0ULL;
+
+	debug("rank density 0x%llx\n", rank_density);
+	conf->base_addr = current_mem_base;
+
+	/* assign each cs */
+	switch (opt->ba_intlv & DDR_BA_INTLV_CS0123) {
+	case DDR_BA_INTLV_CS0123:
+		for (i = 0; i < DDRC_NUM_CS; i++) {
+			conf->cs_base_addr[i] = current_mem_base;
+			conf->cs_size[i] = rank_density << 2;
+			total_ctlr_mem += rank_density;
+		}
+		break;
+	case DDR_BA_INTLV_CS01:
+		for (i = 0; ((conf->cs_in_use & (1 << i)) != 0) && i < 2; i++) {
+			conf->cs_base_addr[i] = current_mem_base;
+			conf->cs_size[i] = rank_density << 1;
+			total_ctlr_mem += rank_density;
+		}
+		current_mem_base += total_ctlr_mem;
+		for (; ((conf->cs_in_use & (1 << i)) != 0) && i < DDRC_NUM_CS;
+		     i++) {
+			conf->cs_base_addr[i] = current_mem_base;
+			conf->cs_size[i] = rank_density;
+			total_ctlr_mem += rank_density;
+			current_mem_base += rank_density;
+		}
+		break;
+	case DDR_BA_NONE:
+		for (i = 0; ((conf->cs_in_use & (1 << i)) != 0) &&
+			     (i < DDRC_NUM_CS); i++) {
+			conf->cs_base_addr[i] = current_mem_base;
+			conf->cs_size[i] = rank_density;
+			current_mem_base += rank_density;
+			total_ctlr_mem += rank_density;
+		}
+		break;
+	default:
+		ERROR("Unsupported bank interleaving\n");
+		return 0;
+	}
+	for (i = 0; ((conf->cs_in_use & (1 << i)) != 0) &&
+		     (i < DDRC_NUM_CS); i++) {
+		debug("CS %d\n", i);
+		debug("    base_addr 0x%llx\n", conf->cs_base_addr[i]);
+		debug("    size 0x%llx\n", conf->cs_size[i]);
+	}
+
+	return total_ctlr_mem;
+}
+
+unsigned long long assign_addresses(struct ddr_info *priv)
+		   __attribute__ ((weak));
+
+unsigned long long assign_addresses(struct ddr_info *priv)
+{
+	struct memctl_opt *opt = &priv->opt;
+	const struct dimm_params *dimm = &priv->dimm;
+	struct ddr_conf *conf = &priv->conf;
+	unsigned long long current_mem_base = priv->mem_base;
+	unsigned long long total_mem;
+
+	total_mem = 0ULL;
+	debug("ctlr_intlv %d\n", opt->ctlr_intlv);
+	if (opt->ctlr_intlv != 0) {
+		total_mem = assign_intlv_addr(dimm, opt, conf,
+					      current_mem_base);
+	} else {
+		/*
+		 * Simple linear assignment if memory controllers are not
+		 * interleaved. This is only valid for SoCs with single DDRC.
+		 */
+		total_mem = assign_non_intlv_addr(dimm, opt, conf,
+						  current_mem_base);
+	}
+	conf->total_mem = total_mem;
+	debug("base 0x%llx\n", current_mem_base);
+	debug("Total mem by assignment is 0x%llx\n", total_mem);
+
+	return total_mem;
+}
+
+static int cal_ddrc_regs(struct ddr_info *priv)
+{
+	int ret;
+
+	ret = compute_ddrc(priv->clk,
+			   &priv->opt,
+			   &priv->conf,
+			   &priv->ddr_reg,
+			   &priv->dimm,
+			   priv->ip_rev);
+	if (ret != 0) {
+		ERROR("Calculating DDR registers failed\n");
+	}
+
+	return ret;
+}
+
+#endif /* CONFIG_STATIC_DDR */
+
+static int write_ddrc_regs(struct ddr_info *priv)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < priv->num_ctlrs; i++) {
+		ret = ddrc_set_regs(priv->clk, &priv->ddr_reg, priv->ddr[i], 0);
+		if (ret != 0) {
+			ERROR("Writing DDR register(s) failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+long long dram_init(struct ddr_info *priv
+#if defined(NXP_HAS_CCN504) || defined(NXP_HAS_CCN508)
+		    , uintptr_t nxp_ccn_hn_f0_addr
+#endif
+		)
+{
+	uint64_t time __unused;
+	long long dram_size;
+	int ret;
+	const uint64_t time_base = get_timer_val(0);
+	unsigned int ip_rev = get_ddrc_version(priv->ddr[0]);
+
+	int valid_spd_mask __unused;
+	int scratch = 0x0;
+
+	priv->ip_rev = ip_rev;
+
+#ifndef CONFIG_STATIC_DDR
+	INFO("time base %llu ms\n", time_base);
+	debug("Parse DIMM SPD(s)\n");
+	valid_spd_mask = parse_spd(priv);
+
+	if (valid_spd_mask < 0) {
+		ERROR("Parsing DIMM Error\n");
+		return valid_spd_mask;
+	}
+
+#if defined(NXP_HAS_CCN504) || defined(NXP_HAS_CCN508)
+	if (priv->num_ctlrs == 2 || priv->num_ctlrs == 1) {
+		ret = disable_unused_ddrc(priv, valid_spd_mask,
+					  nxp_ccn_hn_f0_addr);
+		if (ret != 0) {
+			return ret;
+		}
+	}
+#endif
+
+	time = get_timer_val(time_base);
+	INFO("Time after parsing SPD %llu ms\n", time);
+	debug("Synthesize configurations\n");
+	ret = synthesize_ctlr(priv);
+	if (ret != 0) {
+		ERROR("Synthesize config error\n");
+		return ret;
+	}
+
+	debug("Assign binding addresses\n");
+	dram_size = assign_addresses(priv);
+	if (dram_size == 0) {
+		ERROR("Assigning address error\n");
+		return -EINVAL;
+	}
+
+	debug("Calculate controller registers\n");
+	ret = cal_ddrc_regs(priv);
+	if (ret != 0) {
+		ERROR("Calculate register error\n");
+		return ret;
+	}
+
+	ret = compute_ddr_phy(priv);
+	if (ret != 0)
+		ERROR("Calculating DDR PHY registers failed.\n");
+
+#else
+	dram_size = board_static_ddr(priv);
+	if (dram_size == 0) {
+		ERROR("Error getting static DDR settings.\n");
+		return -EINVAL;
+	}
+#endif
+
+	if (priv->warm_boot_flag == DDR_WARM_BOOT) {
+		scratch = (priv->ddr_reg).sdram_cfg[1];
+		scratch = scratch & ~(SDRAM_CFG2_D_INIT);
+		priv->ddr_reg.sdram_cfg[1] = scratch;
+	}
+
+	time = get_timer_val(time_base);
+	INFO("Time before programming controller %llu ms\n", time);
+	debug("Program controller registers\n");
+	ret = write_ddrc_regs(priv);
+	if (ret != 0) {
+		ERROR("Programing DDRC error\n");
+		return ret;
+	}
+
+	puts("");
+	NOTICE("%lld GB ", dram_size >> 30);
+	print_ddr_info(priv->ddr[0]);
+
+	time = get_timer_val(time_base);
+	INFO("Time used by DDR driver %llu ms\n", time);
+
+	return dram_size;
+}
diff --git a/drivers/nxp/ddr/nxp-ddr/ddr.mk b/drivers/nxp/ddr/nxp-ddr/ddr.mk
new file mode 100644
index 0000000..866c092
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.mk
@@ -0,0 +1,79 @@
+#
+# Copyright 2021 NXP
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+DDR_DRIVERS_PATH	:= ${PLAT_DRIVERS_PATH}/ddr
+
+ifeq ($(PLAT_DDR_PHY), PHY_GEN2)
+$(eval $(call add_define, PHY_GEN2))
+PLAT_DDR_PHY_DIR		:= phy-gen2
+ifeq (${APPLY_MAX_CDD},yes)
+$(eval $(call add_define,NXP_APPLY_MAX_CDD))
+endif
+
+ifeq (${ERRATA_DDR_A011396}, 1)
+$(eval $(call add_define,ERRATA_DDR_A011396))
+endif
+
+ifeq (${ERRATA_DDR_A050450}, 1)
+$(eval $(call add_define,ERRATA_DDR_A050450))
+endif
+
+endif
+
+ifeq ($(PLAT_DDR_PHY), PHY_GEN1)
+PLAT_DDR_PHY_DIR		:= phy-gen1
+
+ifeq (${ERRATA_DDR_A008511},1)
+$(eval $(call add_define,ERRATA_DDR_A008511))
+endif
+
+ifeq (${ERRATA_DDR_A009803},1)
+$(eval $(call add_define,ERRATA_DDR_A009803))
+endif
+
+ifeq (${ERRATA_DDR_A009942},1)
+$(eval $(call add_define,ERRATA_DDR_A009942))
+endif
+
+ifeq (${ERRATA_DDR_A010165},1)
+$(eval $(call add_define,ERRATA_DDR_A010165))
+endif
+
+endif
+
+ifeq ($(DDR_BIST), yes)
+$(eval $(call add_define, BIST_EN))
+endif
+
+ifeq ($(DDR_DEBUG), yes)
+$(eval $(call add_define, DDR_DEBUG))
+endif
+
+ifeq ($(DDR_PHY_DEBUG), yes)
+$(eval $(call add_define, DDR_PHY_DEBUG))
+endif
+
+ifeq ($(DEBUG_PHY_IO), yes)
+$(eval $(call add_define, DEBUG_PHY_IO))
+endif
+
+ifeq ($(DEBUG_WARM_RESET), yes)
+$(eval $(call add_define, DEBUG_WARM_RESET))
+endif
+
+ifeq ($(DEBUG_DDR_INPUT_CONFIG), yes)
+$(eval $(call add_define, DEBUG_DDR_INPUT_CONFIG))
+endif
+
+DDR_CNTLR_SOURCES	:= $(DDR_DRIVERS_PATH)/nxp-ddr/ddr.c \
+			   $(DDR_DRIVERS_PATH)/nxp-ddr/ddrc.c \
+			   $(DDR_DRIVERS_PATH)/nxp-ddr/dimm.c \
+			   $(DDR_DRIVERS_PATH)/nxp-ddr/regs.c \
+			   $(DDR_DRIVERS_PATH)/nxp-ddr/utility.c \
+			   $(DDR_DRIVERS_PATH)/$(PLAT_DDR_PHY_DIR)/phy.c
+
+PLAT_INCLUDES		+= -I$(DDR_DRIVERS_PATH)/nxp-ddr \
+			   -I$(DDR_DRIVERS_PATH)/include
diff --git a/drivers/nxp/ddr/nxp-ddr/ddrc.c b/drivers/nxp/ddr/nxp-ddr/ddrc.c
new file mode 100644
index 0000000..17a2b6a
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/ddrc.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <common/debug.h>
+#include <ddr.h>
+#include <drivers/delay_timer.h>
+#include <immap.h>
+
+#define BIST_CR		0x80060000
+#define BIST_CR_EN	0x80000000
+#define BIST_CR_STAT	0x00000001
+#define CTLR_INTLV_MASK	0x20000000
+
+#pragma weak run_bist
+
+bool run_bist(void)
+{
+#ifdef BIST_EN
+	return true;
+#else
+	return false;
+#endif
+}
+
+/*
+ * Perform build-in test on memory
+ * timeout value in 10ms
+ */
+int bist(const struct ccsr_ddr *ddr, int timeout)
+{
+	const unsigned int test_pattern[10] = {
+		0xffffffff,
+		0x00000000,
+		0xaaaaaaaa,
+		0x55555555,
+		0xcccccccc,
+		0x33333333,
+		0x12345678,
+		0xabcdef01,
+		0xaa55aa55,
+		0x55aa55aa
+	};
+	unsigned int mtcr, err_detect, err_sbe;
+	unsigned int cs0_config;
+	unsigned int csn_bnds[4];
+	int ret = 0;
+	uint32_t i;
+#ifdef CONFIG_DDR_ADDR_DEC
+	uint32_t dec_9 = ddr_in32(&ddr->dec[9]);
+	uint32_t pos = 0U;
+	uint32_t map_save = 0U;
+	uint32_t temp32 = 0U;
+	uint32_t map, shift, highest;
+#endif
+
+	cs0_config = ddr_in32(&ddr->csn_cfg[0]);
+	if ((cs0_config & CTLR_INTLV_MASK) != 0U) {
+		/* set bnds to non-interleaving */
+		for (i = 0U; i < 4U; i++) {
+			csn_bnds[i] = ddr_in32(&ddr->bnds[i].a);
+			ddr_out32(&ddr->bnds[i].a,
+				  (csn_bnds[i] & U(0xfffefffe)) >> 1U);
+		}
+		ddr_out32(&ddr->csn_cfg[0], cs0_config & ~CTLR_INTLV_MASK);
+#ifdef CONFIG_DDR_ADDR_DEC
+		if ((dec_9 & 0x1U) != 0U) {
+			highest = (dec_9 >> 26U) == U(0x3F) ? 0U : dec_9 >> 26U;
+			pos = 37U;
+			for (i = 0U; i < 36U; i++) {      /* Go through all 37 */
+				if ((i % 4U) == 0U) {
+					temp32 = ddr_in32(&ddr->dec[i >> 2U]);
+				}
+				shift = (3U - i % 4U) * 8U + 2U;
+				map = (temp32 >> shift) & U(0x3F);
+				if (map > highest && map != U(0x3F)) {
+					highest = map;
+					pos = i;
+				}
+			}
+			debug("\nFound highest position %d, mapping to %d, ",
+			      pos, highest);
+			map_save = ddr_in32(&ddr->dec[pos >> 2]);
+			shift = (3U - pos % 4U) * 8U + 2U;
+			debug("in dec[%d], bit %d (0x%x)\n",
+			      pos >> 2U, shift, map_save);
+			temp32 = map_save & ~(U(0x3F) << shift);
+			temp32 |= 8U << shift;
+			ddr_out32(&ddr->dec[pos >> 2U], temp32);
+			timeout <<= 2U;
+			debug("Increase wait time to %d ms\n", timeout * 10);
+		}
+#endif
+	}
+	for (i = 0U; i < 10U; i++) {
+		ddr_out32(&ddr->mtp[i], test_pattern[i]);
+	}
+	mtcr = BIST_CR;
+	ddr_out32(&ddr->mtcr, mtcr);
+	do {
+		mdelay(10);
+		mtcr = ddr_in32(&ddr->mtcr);
+	} while (timeout-- > 0 && ((mtcr & BIST_CR_EN) != 0));
+	if (timeout <= 0) {
+		ERROR("Timeout\n");
+	} else {
+		debug("Timer remains %d\n", timeout);
+	}
+
+	err_detect = ddr_in32(&ddr->err_detect);
+	err_sbe = ddr_in32(&ddr->err_sbe);
+	if (err_detect != 0U || ((err_sbe & U(0xffff)) != 0U)) {
+		ERROR("ECC error detected\n");
+		ret = -EIO;
+	}
+
+	if ((cs0_config & CTLR_INTLV_MASK) != 0) {
+		for (i = 0U; i < 4U; i++) {
+			ddr_out32(&ddr->bnds[i].a, csn_bnds[i]);
+		}
+		ddr_out32(&ddr->csn_cfg[0], cs0_config);
+#ifdef CONFIG_DDR_ADDR_DEC
+		if ((dec_9 & U(0x1)) != 0U) {
+			ddr_out32(&ddr->dec[pos >> 2], map_save);
+		}
+#endif
+	}
+	if ((mtcr & BIST_CR_STAT) != 0) {
+		ERROR("Built-in self test failed\n");
+		ret = -EIO;
+	} else {
+		NOTICE("Build-in self test passed\n");
+	}
+
+	return ret;
+}
+
+void dump_ddrc(unsigned int *ddr)
+{
+#ifdef DDR_DEBUG
+	uint32_t i;
+	unsigned long val;
+
+	for (i = 0U; i < U(0x400); i++, ddr++) {
+		val = ddr_in32(ddr);
+		if (val != 0U) {	/* skip zeros */
+			debug("*0x%lx = 0x%lx\n", (unsigned long)ddr, val);
+		}
+	}
+#endif
+}
+
+#ifdef ERRATA_DDR_A009803
+static void set_wait_for_bits_clear(const void *ptr,
+				    unsigned int value,
+				    unsigned int bits)
+{
+	int timeout = 1000;
+
+	ddr_out32(ptr, value);
+	do {
+		udelay(100);
+	} while (timeout-- > 0 && ((ddr_in32(ptr) & bits) != 0));
+
+	if (timeout <= 0) {
+		ERROR("wait for clear timeout.\n");
+	}
+}
+#endif
+
+#if (DDRC_NUM_CS > 4)
+#error Invalid setting for DDRC_NUM_CS
+#endif
+
+/*
+ * If supported by the platform, writing to DDR controller takes two
+ * passes to deassert DDR reset to comply with JEDEC specs for RDIMMs.
+ */
+int ddrc_set_regs(const unsigned long clk,
+		  const struct ddr_cfg_regs *regs,
+		  const struct ccsr_ddr *ddr,
+		  int twopass)
+{
+	unsigned int i, bus_width;
+	unsigned int temp_sdram_cfg;
+	unsigned int total_mem_per_ctrl, total_mem_per_ctrl_adj;
+	const int mod_bnds = regs->cs[0].config & CTLR_INTLV_MASK;
+	int timeout;
+	int ret = 0;
+#if defined(ERRATA_DDR_A009942) || defined(ERRATA_DDR_A010165)
+	unsigned long ddr_freq;
+	unsigned int tmp;
+#ifdef ERRATA_DDR_A009942
+	unsigned int check;
+	unsigned int cpo_min = U(0xff);
+	unsigned int cpo_max = 0U;
+#endif
+#endif
+
+	if (twopass == 2U) {
+		goto after_reset;
+	}
+
+	/* Set cdr1 first in case 0.9v VDD is enabled for some SoCs*/
+	ddr_out32(&ddr->ddr_cdr1, regs->cdr[0]);
+
+	ddr_out32(&ddr->sdram_clk_cntl, regs->clk_cntl);
+
+	for (i = 0U; i < DDRC_NUM_CS; i++) {
+		if (mod_bnds != 0U) {
+			ddr_out32(&ddr->bnds[i].a,
+				  (regs->cs[i].bnds & U(0xfffefffe)) >> 1U);
+		} else {
+			ddr_out32(&ddr->bnds[i].a, regs->cs[i].bnds);
+		}
+		ddr_out32(&ddr->csn_cfg_2[i], regs->cs[i].config_2);
+	}
+
+	ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg[0]);
+	ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg[1]);
+	ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg[2]);
+	ddr_out32(&ddr->timing_cfg_3, regs->timing_cfg[3]);
+	ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg[4]);
+	ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg[5]);
+	ddr_out32(&ddr->timing_cfg_6, regs->timing_cfg[6]);
+	ddr_out32(&ddr->timing_cfg_7, regs->timing_cfg[7]);
+	ddr_out32(&ddr->timing_cfg_8, regs->timing_cfg[8]);
+	ddr_out32(&ddr->timing_cfg_9, regs->timing_cfg[9]);
+	ddr_out32(&ddr->zq_cntl, regs->zq_cntl);
+	for (i = 0U; i < 4U; i++) {
+		ddr_out32(&ddr->dq_map[i], regs->dq_map[i]);
+	}
+	ddr_out32(&ddr->sdram_cfg_3, regs->sdram_cfg[2]);
+	ddr_out32(&ddr->sdram_mode, regs->sdram_mode[0]);
+	ddr_out32(&ddr->sdram_mode_2, regs->sdram_mode[1]);
+	ddr_out32(&ddr->sdram_mode_3, regs->sdram_mode[2]);
+	ddr_out32(&ddr->sdram_mode_4, regs->sdram_mode[3]);
+	ddr_out32(&ddr->sdram_mode_5, regs->sdram_mode[4]);
+	ddr_out32(&ddr->sdram_mode_6, regs->sdram_mode[5]);
+	ddr_out32(&ddr->sdram_mode_7, regs->sdram_mode[6]);
+	ddr_out32(&ddr->sdram_mode_8, regs->sdram_mode[7]);
+	ddr_out32(&ddr->sdram_mode_9, regs->sdram_mode[8]);
+	ddr_out32(&ddr->sdram_mode_10, regs->sdram_mode[9]);
+	ddr_out32(&ddr->sdram_mode_11, regs->sdram_mode[10]);
+	ddr_out32(&ddr->sdram_mode_12, regs->sdram_mode[11]);
+	ddr_out32(&ddr->sdram_mode_13, regs->sdram_mode[12]);
+	ddr_out32(&ddr->sdram_mode_14, regs->sdram_mode[13]);
+	ddr_out32(&ddr->sdram_mode_15, regs->sdram_mode[14]);
+	ddr_out32(&ddr->sdram_mode_16, regs->sdram_mode[15]);
+	ddr_out32(&ddr->sdram_md_cntl, regs->md_cntl);
+#ifdef ERRATA_DDR_A009663
+	ddr_out32(&ddr->sdram_interval,
+		  regs->interval & ~SDRAM_INTERVAL_BSTOPRE);
+#else
+	ddr_out32(&ddr->sdram_interval, regs->interval);
+#endif
+	ddr_out32(&ddr->sdram_data_init, regs->data_init);
+	if (regs->eor != 0) {
+		ddr_out32(&ddr->eor, regs->eor);
+	}
+
+	ddr_out32(&ddr->wrlvl_cntl, regs->wrlvl_cntl[0]);
+#ifndef NXP_DDR_EMU
+	/*
+	 * Skip these two registers if running on emulator
+	 * because emulator doesn't have skew between bytes.
+	 */
+
+	if (regs->wrlvl_cntl[1] != 0) {
+		ddr_out32(&ddr->ddr_wrlvl_cntl_2, regs->wrlvl_cntl[1]);
+	}
+	if (regs->wrlvl_cntl[2] != 0) {
+		ddr_out32(&ddr->ddr_wrlvl_cntl_3, regs->wrlvl_cntl[2]);
+	}
+#endif
+
+	ddr_out32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr);
+	ddr_out32(&ddr->ddr_sdram_rcw_1, regs->sdram_rcw[0]);
+	ddr_out32(&ddr->ddr_sdram_rcw_2, regs->sdram_rcw[1]);
+	ddr_out32(&ddr->ddr_sdram_rcw_3, regs->sdram_rcw[2]);
+	ddr_out32(&ddr->ddr_sdram_rcw_4, regs->sdram_rcw[3]);
+	ddr_out32(&ddr->ddr_sdram_rcw_5, regs->sdram_rcw[4]);
+	ddr_out32(&ddr->ddr_sdram_rcw_6, regs->sdram_rcw[5]);
+	ddr_out32(&ddr->ddr_cdr2, regs->cdr[1]);
+	ddr_out32(&ddr->sdram_cfg_2, regs->sdram_cfg[1]);
+	ddr_out32(&ddr->init_addr, regs->init_addr);
+	ddr_out32(&ddr->init_ext_addr, regs->init_ext_addr);
+
+#ifdef ERRATA_DDR_A009803
+	/* part 1 of 2 */
+	if ((regs->sdram_cfg[1] & SDRAM_CFG2_AP_EN) != 0) {
+		if ((regs->sdram_cfg[0] & SDRAM_CFG_RD_EN) != 0) {
+			ddr_out32(&ddr->ddr_sdram_rcw_2,
+				  regs->sdram_rcw[1] & ~0xf0);
+		}
+
+		ddr_out32(&ddr->err_disable,
+				regs->err_disable | DDR_ERR_DISABLE_APED);
+	}
+#else
+	ddr_out32(&ddr->err_disable, regs->err_disable);
+#endif
+	ddr_out32(&ddr->err_int_en, regs->err_int_en);
+
+	/* For DDRC 5.05 only */
+	if (get_ddrc_version(ddr) == 0x50500) {
+		ddr_out32(&ddr->tx_cfg[1], 0x1f1f1f1f);
+		ddr_out32(&ddr->debug[3], 0x124a02c0);
+	}
+
+	for (i = 0U; i < 4U; i++) {
+		if (regs->tx_cfg[i] != 0) {
+			ddr_out32(&ddr->tx_cfg[i], regs->tx_cfg[i]);
+		}
+	}
+	for (i = 0U; i < 64U; i++) {
+		if (regs->debug[i] != 0) {
+#ifdef ERRATA_DDR_A009942
+			if (i == 28U) {
+				continue;
+			}
+#endif
+			ddr_out32(&ddr->debug[i], regs->debug[i]);
+		}
+	}
+#ifdef CONFIG_DDR_ADDR_DEC
+	if ((regs->dec[9] & 1) != 0U) {
+		for (i = 0U; i < 10U; i++) {
+			ddr_out32(&ddr->dec[i], regs->dec[i]);
+		}
+		if (mod_bnds != 0) {
+			debug("Disable address decoding\n");
+			ddr_out32(&ddr->dec[9], 0);
+		}
+	}
+#endif
+
+#ifdef ERRATA_DDR_A008511
+	/* Part 1 of 2 */
+	/* This erraum only applies to verion 5.2.1 */
+	if (get_ddrc_version(ddr) == 0x50200) {
+		ERROR("Unsupported SoC.\n");
+	} else if (get_ddrc_version(ddr) == 0x50201) {
+		ddr_out32(&ddr->debug[37], (U(1) << 31));
+		ddr_out32(&ddr->ddr_cdr2,
+			  regs->cdr[1] | DDR_CDR2_VREF_TRAIN_EN);
+	} else {
+		debug("Erratum A008511 doesn't apply.\n");
+	}
+#endif
+
+#ifdef ERRATA_DDR_A009942
+	ddr_freq = clk / 1000000U;
+	tmp = ddr_in32(&ddr->debug[28]);
+	tmp &= U(0xff0fff00);
+	tmp |= ddr_freq <= 1333U ? U(0x0080006a) :
+		(ddr_freq <= 1600U ? U(0x0070006f) :
+		 (ddr_freq <= 1867U ? U(0x00700076) : U(0x0060007b)));
+	if (regs->debug[28] != 0) {
+		tmp &= ~0xff;
+		tmp |= regs->debug[28] & 0xff;
+	} else {
+		WARN("Warning: Optimal CPO value not set.\n");
+	}
+	ddr_out32(&ddr->debug[28], tmp);
+#endif
+
+#ifdef ERRATA_DDR_A010165
+	ddr_freq = clk / 1000000U;
+	if ((ddr_freq > 1900) && (ddr_freq < 2300)) {
+		tmp = ddr_in32(&ddr->debug[28]);
+		ddr_out32(&ddr->debug[28], tmp | 0x000a0000);
+	}
+#endif
+	/*
+	 * For RDIMMs, JEDEC spec requires clocks to be stable before reset is
+	 * deasserted. Clocks start when any chip select is enabled and clock
+	 * control register is set. Because all DDR components are connected to
+	 * one reset signal, this needs to be done in two steps. Step 1 is to
+	 * get the clocks started. Step 2 resumes after reset signal is
+	 * deasserted.
+	 */
+	if (twopass == 1) {
+		udelay(200);
+		return 0;
+	}
+
+	/* As per new sequence flow shall be write CSn_CONFIG registers needs to
+	 * be set after all the other DDR controller registers are set, then poll
+	 * for PHY_INIT_CMPLT = 1 , then wait at least 100us (micro seconds),
+	 * then set the MEM_EN = 1
+	 */
+	for (i = 0U; i < DDRC_NUM_CS; i++) {
+		if (mod_bnds != 0U && i == 0U) {
+			ddr_out32(&ddr->csn_cfg[i],
+					(regs->cs[i].config & ~CTLR_INTLV_MASK));
+		} else {
+			ddr_out32(&ddr->csn_cfg[i], regs->cs[i].config);
+		}
+	}
+
+after_reset:
+	/* Set, but do not enable the memory */
+	temp_sdram_cfg = regs->sdram_cfg[0];
+	temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN);
+	ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg);
+
+	if (get_ddrc_version(ddr) < U(0x50500)) {
+		/*
+		 * 500 painful micro-seconds must elapse between
+		 * the DDR clock setup and the DDR config enable.
+		 * DDR2 need 200 us, and DDR3 need 500 us from spec,
+		 * we choose the max, that is 500 us for all of case.
+		 */
+		udelay(500);
+		/* applied memory barrier */
+		mb();
+		isb();
+	} else {
+		/* wait for PHY complete */
+		timeout = 40;
+		while (((ddr_in32(&ddr->ddr_dsr2) & 0x4) != 0) &&
+		       (timeout > 0)) {
+			udelay(500);
+			timeout--;
+		}
+		if (timeout <= 0) {
+			printf("PHY handshake timeout, ddr_dsr2 = %x\n",
+			       ddr_in32(&ddr->ddr_dsr2));
+		} else {
+			debug("PHY handshake completed, timer remains %d\n",
+			      timeout);
+		}
+	}
+
+	temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg);
+	/* Let the controller go */
+	udelay(100);
+	ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN);
+
+	/* applied memory barrier */
+	mb();
+	isb();
+
+	total_mem_per_ctrl = 0;
+	for (i = 0; i < DDRC_NUM_CS; i++) {
+		if ((regs->cs[i].config & 0x80000000) == 0) {
+			continue;
+		}
+		total_mem_per_ctrl += 1 << (
+			((regs->cs[i].config >> 14) & 0x3) + 2 +
+			((regs->cs[i].config >> 8) & 0x7) + 12 +
+			((regs->cs[i].config >> 4) & 0x3) + 0 +
+			((regs->cs[i].config >> 0) & 0x7) + 8 +
+			((regs->sdram_cfg[2] >> 4) & 0x3) +
+			3 - ((regs->sdram_cfg[0] >> 19) & 0x3) -
+			26);		/* minus 26 (count of 64M) */
+	}
+	total_mem_per_ctrl_adj = total_mem_per_ctrl;
+	/*
+	 * total memory / bus width = transactions needed
+	 * transactions needed / data rate = seconds
+	 * to add plenty of buffer, double the time
+	 * For example, 2GB on 666MT/s 64-bit bus takes about 402ms
+	 * Let's wait for 800ms
+	 */
+	bus_width = 3 - ((ddr_in32(&ddr->sdram_cfg) & SDRAM_CFG_DBW_MASK)
+			>> SDRAM_CFG_DBW_SHIFT);
+	timeout = ((total_mem_per_ctrl_adj << (6 - bus_width)) * 100 /
+		   (clk >> 20)) << 2;
+	total_mem_per_ctrl_adj >>= 4;	/* shift down to gb size */
+	if ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) != 0) {
+		debug("total size %d GB\n", total_mem_per_ctrl_adj);
+		debug("Need to wait up to %d ms\n", timeout * 10);
+
+		do {
+			mdelay(10);
+		} while (timeout-- > 0 &&
+			 ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT)) != 0);
+
+		if (timeout <= 0) {
+			if (ddr_in32(&ddr->debug[1]) & 0x3d00) {
+				ERROR("Found training error(s): 0x%x\n",
+				      ddr_in32(&ddr->debug[1]));
+			}
+			ERROR("Error: Waiting for D_INIT timeout.\n");
+			return -EIO;
+		}
+	}
+
+	if (mod_bnds != 0U) {
+		debug("Restore original bnds\n");
+		for (i = 0U; i < DDRC_NUM_CS; i++) {
+			ddr_out32(&ddr->bnds[i].a, regs->cs[i].bnds);
+		}
+		ddr_out32(&ddr->csn_cfg[0], regs->cs[0].config);
+#ifdef CONFIG_DDR_ADDR_DEC
+		if ((regs->dec[9] & U(0x1)) != 0U) {
+			debug("Restore address decoding\n");
+			ddr_out32(&ddr->dec[9], regs->dec[9]);
+		}
+#endif
+	}
+
+#ifdef ERRATA_DDR_A009803
+	/* Part 2 of 2 */
+	if ((regs->sdram_cfg[1] & SDRAM_CFG2_AP_EN) != 0) {
+		timeout = 400;
+		do {
+			mdelay(1);
+		} while (timeout-- > 0 && ((ddr_in32(&ddr->debug[1]) & 0x2) == 0));
+
+		if ((regs->sdram_cfg[0] & SDRAM_CFG_RD_EN) != 0) {
+			for (i = 0U; i < DDRC_NUM_CS; i++) {
+				if ((regs->cs[i].config & SDRAM_CS_CONFIG_EN) == 0) {
+					continue;
+				}
+				set_wait_for_bits_clear(&ddr->sdram_md_cntl,
+						MD_CNTL_MD_EN |
+						MD_CNTL_CS_SEL(i) |
+						0x070000ed,
+						MD_CNTL_MD_EN);
+				udelay(1);
+			}
+		}
+
+		ddr_out32(&ddr->err_disable,
+			  regs->err_disable & ~DDR_ERR_DISABLE_APED);
+	}
+#endif
+
+#ifdef ERRATA_DDR_A009663
+	ddr_out32(&ddr->sdram_interval, regs->interval);
+#endif
+
+#ifdef ERRATA_DDR_A009942
+	timeout = 400;
+	do {
+		mdelay(1);
+	} while (timeout-- > 0 && ((ddr_in32(&ddr->debug[1]) & 0x2) == 0));
+	tmp = (regs->sdram_cfg[0] >> 19) & 0x3;
+	check = (tmp == DDR_DBUS_64) ? 4 : ((tmp == DDR_DBUS_32) ? 2 : 1);
+	for (i = 0; i < check; i++) {
+		tmp = ddr_in32(&ddr->debug[9 + i]);
+		debug("Reading debug[%d] as 0x%x\n", i + 9, tmp);
+		cpo_min = min(cpo_min,
+			      min((tmp >> 24) & 0xff, (tmp >> 8) & 0xff));
+		cpo_max = max(cpo_max,
+			      max((tmp >> 24) & 0xff, (tmp >> 8) & 0xff));
+	}
+	if ((regs->sdram_cfg[0] & SDRAM_CFG_ECC_EN) != 0) {
+		tmp = ddr_in32(&ddr->debug[13]);
+		cpo_min = min(cpo_min, (tmp >> 24) & 0xff);
+		cpo_max = max(cpo_max, (tmp >> 24) & 0xff);
+	}
+	debug("cpo_min 0x%x\n", cpo_min);
+	debug("cpo_max 0x%x\n", cpo_max);
+	tmp = ddr_in32(&ddr->debug[28]);
+	debug("debug[28] 0x%x\n", tmp);
+	if ((cpo_min + 0x3B) < (tmp & 0xff)) {
+		WARN("Warning: A009942 requires setting cpo_sample to 0x%x\n",
+		     (cpo_min + cpo_max) / 2 + 0x27);
+	} else {
+		debug("Optimal cpo_sample 0x%x\n",
+			(cpo_min + cpo_max) / 2 + 0x27);
+	}
+#endif
+	if (run_bist() != 0) {
+		if ((ddr_in32(&ddr->debug[1]) &
+		    ((get_ddrc_version(ddr) == 0x50500) ? 0x3c00 : 0x3d00)) != 0) {
+			ERROR("Found training error(s): 0x%x\n",
+			     ddr_in32(&ddr->debug[1]));
+			return -EIO;
+		}
+		INFO("Running built-in self test ...\n");
+		/* give it 10x time to cover whole memory */
+		timeout = ((total_mem_per_ctrl << (6 - bus_width)) *
+			   100 / (clk >> 20)) * 10;
+		INFO("\tWait up to %d ms\n", timeout * 10);
+		ret = bist(ddr, timeout);
+	}
+	dump_ddrc((void *)ddr);
+
+	return ret;
+}
diff --git a/drivers/nxp/ddr/nxp-ddr/dimm.c b/drivers/nxp/ddr/nxp-ddr/dimm.c
new file mode 100644
index 0000000..16efcba
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/dimm.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <common/debug.h>
+#include <ddr.h>
+#include <dimm.h>
+#include <i2c.h>
+#include <lib/utils.h>
+
+int read_spd(unsigned char chip, void *buf, int len)
+{
+	unsigned char dummy = 0U;
+	int ret;
+
+	if (len < 256) {
+		ERROR("Invalid SPD length\n");
+		return -EINVAL;
+	}
+
+	i2c_write(SPD_SPA0_ADDRESS, 0, 1, &dummy, 1);
+	ret = i2c_read(chip, 0, 1, buf, 256);
+	if (ret == 0) {
+		i2c_write(SPD_SPA1_ADDRESS, 0, 1, &dummy, 1);
+		ret = i2c_read(chip, 0, 1, buf + 256, min(256, len - 256));
+	}
+	if (ret != 0) {
+		zeromem(buf, len);
+	}
+
+	return ret;
+}
+
+int crc16(unsigned char *ptr, int count)
+{
+	int i;
+	int crc = 0;
+
+	while (--count >= 0) {
+		crc = crc ^ (int)*ptr++ << 8;
+		for (i = 0; i < 8; ++i) {
+			if ((crc & 0x8000) != 0) {
+				crc = crc << 1 ^ 0x1021;
+			} else {
+				crc = crc << 1;
+			}
+		}
+	}
+	return crc & 0xffff;
+}
+
+static int ddr4_spd_check(const struct ddr4_spd *spd)
+{
+	void *p = (void *)spd;
+	int csum16;
+	int len;
+	char crc_lsb;	/* byte 126 */
+	char crc_msb;	/* byte 127 */
+
+	len = 126;
+	csum16 = crc16(p, len);
+
+	crc_lsb = (char) (csum16 & 0xff);
+	crc_msb = (char) (csum16 >> 8);
+
+	if (spd->crc[0] != crc_lsb || spd->crc[1] != crc_msb) {
+		ERROR("SPD CRC = 0x%x%x, computed CRC = 0x%x%x\n",
+		      spd->crc[1], spd->crc[0], crc_msb, crc_lsb);
+		return -EINVAL;
+	}
+
+	p = (void *)spd + 128;
+	len = 126;
+	csum16 = crc16(p, len);
+
+	crc_lsb = (char) (csum16 & 0xff);
+	crc_msb = (char) (csum16 >> 8);
+
+	if (spd->mod_section.uc[126] != crc_lsb ||
+	    spd->mod_section.uc[127] != crc_msb) {
+		ERROR("SPD CRC = 0x%x%x, computed CRC = 0x%x%x\n",
+		      spd->mod_section.uc[127], spd->mod_section.uc[126],
+		      crc_msb, crc_lsb);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static unsigned long long
+compute_ranksize(const struct ddr4_spd *spd)
+{
+	unsigned long long bsize;
+
+	int nbit_sdram_cap_bsize = 0;
+	int nbit_primary_bus_width = 0;
+	int nbit_sdram_width = 0;
+	int die_count = 0;
+	bool package_3ds;
+
+	if ((spd->density_banks & 0xf) <= 7) {
+		nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
+	}
+	if ((spd->bus_width & 0x7) < 4) {
+		nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
+	}
+	if ((spd->organization & 0x7) < 4) {
+		nbit_sdram_width = (spd->organization & 0x7) + 2;
+	}
+	package_3ds = (spd->package_type & 0x3) == 0x2;
+	if (package_3ds) {
+		die_count = (spd->package_type >> 4) & 0x7;
+	}
+
+	bsize = 1ULL << (nbit_sdram_cap_bsize - 3 +
+			 nbit_primary_bus_width - nbit_sdram_width +
+			 die_count);
+
+	return bsize;
+}
+
+int cal_dimm_params(const struct ddr4_spd *spd, struct dimm_params *pdimm)
+{
+	int ret;
+	int i;
+	static const unsigned char udimm_rc_e_dq[18] = {
+		0x0c, 0x2c, 0x15, 0x35, 0x15, 0x35, 0x0b, 0x2c, 0x15,
+		0x35, 0x0b, 0x35, 0x0b, 0x2c, 0x0b, 0x35, 0x15, 0x36
+	};
+	int spd_error = 0;
+	unsigned char *ptr;
+	unsigned char val;
+
+	if (spd->mem_type != SPD_MEMTYPE_DDR4) {
+		ERROR("Not a DDR4 DIMM.\n");
+		return -EINVAL;
+	}
+
+	ret = ddr4_spd_check(spd);
+	if (ret != 0) {
+		ERROR("DIMM SPD checksum mismatch\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * The part name in ASCII in the SPD EEPROM is not null terminated.
+	 * Guarantee null termination here by presetting all bytes to 0
+	 * and copying the part name in ASCII from the SPD onto it
+	 */
+	if ((spd->info_size_crc & 0xF) > 2) {
+		memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+	}
+
+	/* DIMM organization parameters */
+	pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
+	debug("n_ranks %d\n", pdimm->n_ranks);
+	pdimm->rank_density = compute_ranksize(spd);
+	if (pdimm->rank_density == 0) {
+		return -EINVAL;
+	}
+
+	debug("rank_density 0x%llx\n", pdimm->rank_density);
+	pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+	debug("capacity 0x%llx\n", pdimm->capacity);
+	pdimm->die_density = spd->density_banks & 0xf;
+	debug("die density 0x%x\n", pdimm->die_density);
+	pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
+	debug("primary_sdram_width %d\n", pdimm->primary_sdram_width);
+	if (((spd->bus_width >> 3) & 0x3) != 0) {
+		pdimm->ec_sdram_width = 8;
+	} else {
+		pdimm->ec_sdram_width = 0;
+	}
+	debug("ec_sdram_width %d\n", pdimm->ec_sdram_width);
+	pdimm->device_width = 1 << ((spd->organization & 0x7) + 2);
+	debug("device_width %d\n", pdimm->device_width);
+	pdimm->package_3ds = (spd->package_type & 0x3) == 0x2 ?
+			     (spd->package_type >> 4) & 0x7 : 0;
+	debug("package_3ds %d\n", pdimm->package_3ds);
+
+	switch (spd->module_type & DDR4_SPD_MODULETYPE_MASK) {
+	case DDR4_SPD_RDIMM:
+	case DDR4_SPD_MINI_RDIMM:
+	case DDR4_SPD_72B_SO_RDIMM:
+		pdimm->rdimm = 1;
+		pdimm->rc = spd->mod_section.registered.ref_raw_card & 0x8f;
+		if ((spd->mod_section.registered.reg_map & 0x1) != 0) {
+			pdimm->mirrored_dimm = 1;
+		}
+		val = spd->mod_section.registered.ca_stren;
+		pdimm->rcw[3] = val >> 4;
+		pdimm->rcw[4] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
+		val = spd->mod_section.registered.clk_stren;
+		pdimm->rcw[5] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
+		pdimm->rcw[6] = 0xf;
+		/* A17 used for 16Gb+, C[2:0] used for 3DS */
+		pdimm->rcw[8] = pdimm->die_density >= 0x6 ? 0x0 : 0x8 |
+				(pdimm->package_3ds > 0x3 ? 0x0 :
+				 (pdimm->package_3ds > 0x1 ? 0x1 :
+				  (pdimm->package_3ds > 0 ? 0x2 : 0x3)));
+		if (pdimm->package_3ds != 0 || pdimm->n_ranks != 4) {
+			pdimm->rcw[13] = 0x4;
+		} else {
+			pdimm->rcw[13] = 0x5;
+		}
+		pdimm->rcw[13] |= pdimm->mirrored_dimm ? 0x8 : 0;
+		break;
+
+	case DDR4_SPD_UDIMM:
+	case DDR4_SPD_SO_DIMM:
+	case DDR4_SPD_MINI_UDIMM:
+	case DDR4_SPD_72B_SO_UDIMM:
+	case DDR4_SPD_16B_SO_DIMM:
+	case DDR4_SPD_32B_SO_DIMM:
+		pdimm->rc = spd->mod_section.unbuffered.ref_raw_card & 0x8f;
+		if ((spd->mod_section.unbuffered.addr_mapping & 0x1) != 0) {
+			pdimm->mirrored_dimm = 1;
+		}
+		if ((spd->mod_section.unbuffered.mod_height & 0xe0) == 0 &&
+		    (spd->mod_section.unbuffered.ref_raw_card == 0x04)) {
+			/* Fix SPD error found on DIMMs with raw card E0 */
+			for (i = 0; i < 18; i++) {
+				if (spd->mapping[i] == udimm_rc_e_dq[i]) {
+					continue;
+				}
+				spd_error = 1;
+				ptr = (unsigned char *)&spd->mapping[i];
+				*ptr = udimm_rc_e_dq[i];
+			}
+			if (spd_error != 0) {
+				INFO("SPD DQ mapping error fixed\n");
+			}
+		}
+		break;
+
+	default:
+		ERROR("Unknown module_type 0x%x\n", spd->module_type);
+		return -EINVAL;
+	}
+	debug("rdimm %d\n", pdimm->rdimm);
+	debug("mirrored_dimm %d\n", pdimm->mirrored_dimm);
+	debug("rc 0x%x\n", pdimm->rc);
+
+	/* SDRAM device parameters */
+	pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
+	debug("n_row_addr %d\n", pdimm->n_row_addr);
+	pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
+	debug("n_col_addr %d\n", pdimm->n_col_addr);
+	pdimm->bank_addr_bits = (spd->density_banks >> 4) & 0x3;
+	debug("bank_addr_bits %d\n", pdimm->bank_addr_bits);
+	pdimm->bank_group_bits = (spd->density_banks >> 6) & 0x3;
+	debug("bank_group_bits %d\n", pdimm->bank_group_bits);
+
+	if (pdimm->ec_sdram_width != 0) {
+		pdimm->edc_config = 0x02;
+	} else {
+		pdimm->edc_config = 0x00;
+	}
+	debug("edc_config %d\n", pdimm->edc_config);
+
+	/* DDR4 spec has BL8 -bit3, BC4 -bit2 */
+	pdimm->burst_lengths_bitmask = 0x0c;
+	debug("burst_lengths_bitmask 0x%x\n", pdimm->burst_lengths_bitmask);
+
+	/* MTB - medium timebase
+	 * The MTB in the SPD spec is 125ps,
+	 *
+	 * FTB - fine timebase
+	 * use 1/10th of ps as our unit to avoid floating point
+	 * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
+	 */
+	if ((spd->timebases & 0xf) == 0x0) {
+		pdimm->mtb_ps = 125;
+		pdimm->ftb_10th_ps = 10;
+
+	} else {
+		ERROR("Unknown Timebases\n");
+		return -EINVAL;
+	}
+
+	/* sdram minimum cycle time */
+	pdimm->tckmin_x_ps = spd_to_ps(spd->tck_min, spd->fine_tck_min);
+	debug("tckmin_x_ps %d\n", pdimm->tckmin_x_ps);
+
+	/* sdram max cycle time */
+	pdimm->tckmax_ps = spd_to_ps(spd->tck_max, spd->fine_tck_max);
+	debug("tckmax_ps %d\n", pdimm->tckmax_ps);
+
+	/*
+	 * CAS latency supported
+	 * bit0 - CL7
+	 * bit4 - CL11
+	 * bit8 - CL15
+	 * bit12- CL19
+	 * bit16- CL23
+	 */
+	pdimm->caslat_x  = (spd->caslat_b1 << 7)	|
+			   (spd->caslat_b2 << 15)	|
+			   (spd->caslat_b3 << 23);
+	debug("caslat_x 0x%x\n", pdimm->caslat_x);
+
+	if (spd->caslat_b4 != 0) {
+		WARN("Unhandled caslat_b4 value\n");
+	}
+
+	/*
+	 * min CAS latency time
+	 */
+	pdimm->taa_ps = spd_to_ps(spd->taa_min, spd->fine_taa_min);
+	debug("taa_ps %d\n", pdimm->taa_ps);
+
+	/*
+	 * min RAS to CAS delay time
+	 */
+	pdimm->trcd_ps = spd_to_ps(spd->trcd_min, spd->fine_trcd_min);
+	debug("trcd_ps %d\n", pdimm->trcd_ps);
+
+	/*
+	 * Min Row Precharge Delay Time
+	 */
+	pdimm->trp_ps = spd_to_ps(spd->trp_min, spd->fine_trp_min);
+	debug("trp_ps %d\n", pdimm->trp_ps);
+
+	/* min active to precharge delay time */
+	pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) +
+			  spd->tras_min_lsb) * pdimm->mtb_ps;
+	debug("tras_ps %d\n", pdimm->tras_ps);
+
+	/* min active to actice/refresh delay time */
+	pdimm->trc_ps = spd_to_ps((((spd->tras_trc_ext & 0xf0) << 4) +
+				   spd->trc_min_lsb), spd->fine_trc_min);
+	debug("trc_ps %d\n", pdimm->trc_ps);
+	/* Min Refresh Recovery Delay Time */
+	pdimm->trfc1_ps = ((spd->trfc1_min_msb << 8) | (spd->trfc1_min_lsb)) *
+		       pdimm->mtb_ps;
+	debug("trfc1_ps %d\n", pdimm->trfc1_ps);
+	pdimm->trfc2_ps = ((spd->trfc2_min_msb << 8) | (spd->trfc2_min_lsb)) *
+		       pdimm->mtb_ps;
+	debug("trfc2_ps %d\n", pdimm->trfc2_ps);
+	pdimm->trfc4_ps = ((spd->trfc4_min_msb << 8) | (spd->trfc4_min_lsb)) *
+			pdimm->mtb_ps;
+	debug("trfc4_ps %d\n", pdimm->trfc4_ps);
+	/* min four active window delay time */
+	pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) *
+			pdimm->mtb_ps;
+	debug("tfaw_ps %d\n", pdimm->tfaw_ps);
+
+	/* min row active to row active delay time, different bank group */
+	pdimm->trrds_ps = spd_to_ps(spd->trrds_min, spd->fine_trrds_min);
+	debug("trrds_ps %d\n", pdimm->trrds_ps);
+	/* min row active to row active delay time, same bank group */
+	pdimm->trrdl_ps = spd_to_ps(spd->trrdl_min, spd->fine_trrdl_min);
+	debug("trrdl_ps %d\n", pdimm->trrdl_ps);
+	/* min CAS to CAS Delay Time (tCCD_Lmin), same bank group */
+	pdimm->tccdl_ps = spd_to_ps(spd->tccdl_min, spd->fine_tccdl_min);
+	debug("tccdl_ps %d\n", pdimm->tccdl_ps);
+	if (pdimm->package_3ds != 0) {
+		if (pdimm->die_density > 5) {
+			debug("Unsupported logical rank density 0x%x\n",
+				  pdimm->die_density);
+			return -EINVAL;
+		}
+		pdimm->trfc_slr_ps = (pdimm->die_density <= 4) ?
+				     260000 : 350000;
+	}
+	debug("trfc_slr_ps %d\n", pdimm->trfc_slr_ps);
+
+	/* 15ns for all speed bins */
+	pdimm->twr_ps = 15000;
+	debug("twr_ps %d\n", pdimm->twr_ps);
+
+	/*
+	 * Average periodic refresh interval
+	 * tREFI = 7.8 us at normal temperature range
+	 */
+	pdimm->refresh_rate_ps = 7800000;
+	debug("refresh_rate_ps %d\n", pdimm->refresh_rate_ps);
+
+	for (i = 0; i < 18; i++) {
+		pdimm->dq_mapping[i] = spd->mapping[i];
+		debug("dq_mapping 0x%x\n", pdimm->dq_mapping[i]);
+	}
+
+	pdimm->dq_mapping_ors = ((spd->mapping[0] >> 6) & 0x3) == 0 ? 1 : 0;
+	debug("dq_mapping_ors %d\n", pdimm->dq_mapping_ors);
+
+	return 0;
+}
diff --git a/drivers/nxp/ddr/nxp-ddr/regs.c b/drivers/nxp/ddr/nxp-ddr/regs.c
new file mode 100644
index 0000000..cedd7ca
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/regs.c
@@ -0,0 +1,1394 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <common/debug.h>
+#include <ddr.h>
+#include <lib/utils.h>
+
+static inline unsigned int cal_cwl(const unsigned long clk)
+{
+	const unsigned int mclk_ps = get_memory_clk_ps(clk);
+
+	return mclk_ps >= 1250U ? 9U :
+		(mclk_ps >= 1070U ? 10U :
+		 (mclk_ps >= 935U ? 11U :
+		  (mclk_ps >= 833U ? 12U :
+		   (mclk_ps >= 750U ? 14U :
+		    (mclk_ps >= 625U ? 16U : 18U)))));
+}
+
+static void cal_csn_config(int i,
+			   struct ddr_cfg_regs *regs,
+			   const struct memctl_opt *popts,
+			   const struct dimm_params *pdimm)
+{
+	unsigned int intlv_en = 0U;
+	unsigned int intlv_ctl = 0U;
+	const unsigned int cs_n_en = 1U;
+	const unsigned int ap_n_en = popts->cs_odt[i].auto_precharge;
+	const unsigned int odt_rd_cfg = popts->cs_odt[i].odt_rd_cfg;
+	const unsigned int odt_wr_cfg = popts->cs_odt[i].odt_wr_cfg;
+	const unsigned int ba_bits_cs_n = pdimm->bank_addr_bits;
+	const unsigned int row_bits_cs_n = pdimm->n_row_addr - 12U;
+	const unsigned int col_bits_cs_n = pdimm->n_col_addr - 8U;
+	const unsigned int bg_bits_cs_n = pdimm->bank_group_bits;
+
+	if (i == 0) {
+		/* These fields only available in CS0_CONFIG */
+		if (popts->ctlr_intlv != 0) {
+			switch (popts->ctlr_intlv_mode) {
+			case DDR_256B_INTLV:
+				intlv_en = popts->ctlr_intlv;
+				intlv_ctl = popts->ctlr_intlv_mode;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	regs->cs[i].config = ((cs_n_en & 0x1) << 31)		|
+			    ((intlv_en & 0x3) << 29)		|
+			    ((intlv_ctl & 0xf) << 24)		|
+			    ((ap_n_en & 0x1) << 23)		|
+			    ((odt_rd_cfg & 0x7) << 20)		|
+			    ((odt_wr_cfg & 0x7) << 16)		|
+			    ((ba_bits_cs_n & 0x3) << 14)	|
+			    ((row_bits_cs_n & 0x7) << 8)	|
+			    ((bg_bits_cs_n & 0x3) << 4)		|
+			    ((col_bits_cs_n & 0x7) << 0);
+	debug("cs%d\n", i);
+	debug("   _config = 0x%x\n", regs->cs[i].config);
+}
+
+static inline int avoid_odt_overlap(const struct ddr_conf *conf,
+				    const struct dimm_params *pdimm)
+{
+	if ((conf->cs_in_use == 0xf) != 0) {
+		return 2;
+	}
+
+#if DDRC_NUM_DIMM >= 2
+	if (conf->dimm_in_use[0] != 0 && conf->dimm_in_use[1] != 0) {
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/* Requires rcw2 set first */
+static void cal_timing_cfg(const unsigned long clk,
+			   struct ddr_cfg_regs *regs,
+			   const struct memctl_opt *popts,
+			   const struct dimm_params *pdimm,
+			   const struct ddr_conf *conf,
+			   unsigned int cas_latency,
+			   unsigned int additive_latency)
+{
+	const unsigned int mclk_ps = get_memory_clk_ps(clk);
+	/* tXP=max(4nCK, 6ns) */
+	const int txp = max((int)mclk_ps * 4, 6000);
+	/* DDR4 supports 10, 12, 14, 16, 18, 20, 24 */
+	static const int wrrec_table[] = {
+		10, 10, 10, 10, 10,
+		10, 10, 10, 10, 10,
+		12, 12, 14, 14, 16,
+		16, 18, 18, 20, 20,
+		24, 24, 24, 24,
+	};
+	int trwt_mclk = (clk / 1000000 > 1900) ? 3 : 2;
+	int twrt_mclk;
+	int trrt_mclk;
+	int twwt_mclk;
+	const int act_pd_exit_mclk = picos_to_mclk(clk, txp);
+	const int pre_pd_exit_mclk = act_pd_exit_mclk;
+	const int taxpd_mclk = 0;
+	/*
+	 * MRS_CYC = max(tMRD, tMOD)
+	 * tMRD = 8nCK, tMOD = max(24nCK, 15ns)
+	 */
+	const int tmrd_mclk = max(24U, picos_to_mclk(clk, 15000));
+	const int pretoact_mclk = picos_to_mclk(clk, pdimm->trp_ps);
+	const int acttopre_mclk = picos_to_mclk(clk, pdimm->tras_ps);
+	const int acttorw_mclk = picos_to_mclk(clk, pdimm->trcd_ps);
+	const int caslat_ctrl = (cas_latency - 1) << 1;
+	const int trfc1_min = pdimm->die_density >= 0x3 ? 16000 :
+			      (pdimm->die_density == 0x4 ? 26000 :
+			       (pdimm->die_density == 0x5 ? 35000 :
+				55000));
+	const int refrec_ctrl = picos_to_mclk(clk,
+							pdimm->trfc1_ps) - 8;
+	int wrrec_mclk = picos_to_mclk(clk, pdimm->twr_ps);
+	const int acttoact_mclk = max(picos_to_mclk(clk,
+							      pdimm->trrds_ps),
+						4U);
+	int wrtord_mclk = max(2U, picos_to_mclk(clk, 2500));
+	const unsigned int cpo = 0U;
+	const int wr_lat = cal_cwl(clk);
+	int rd_to_pre = picos_to_mclk(clk, 7500);
+	const int wr_data_delay = popts->wr_data_delay;
+	const int cke_pls = max(3U, picos_to_mclk(clk, 5000));
+#ifdef ERRATA_DDR_A050450
+	const unsigned short four_act = ((popts->twot_en == 0) &&
+					 (popts->threet_en == 0) &&
+					 (popts->tfaw_ps % 2 == 0)) ?
+						(picos_to_mclk(clk, popts->tfaw_ps) + 1) :
+						picos_to_mclk(clk, popts->tfaw_ps);
+#else
+	const unsigned short four_act = picos_to_mclk(clk,
+					 popts->tfaw_ps);
+#endif
+	const unsigned int cntl_adj = 0U;
+	const unsigned int ext_pretoact = picos_to_mclk(clk,
+							pdimm->trp_ps) >> 4U;
+	const unsigned int ext_acttopre = picos_to_mclk(clk,
+							pdimm->tras_ps) >> 4U;
+	const unsigned int ext_acttorw = picos_to_mclk(clk,
+						       pdimm->trcd_ps) >> 4U;
+	const unsigned int ext_caslat = (2U * cas_latency - 1U) >> 4U;
+	const unsigned int ext_add_lat = additive_latency >> 4U;
+	const unsigned int ext_refrec = (picos_to_mclk(clk,
+					       pdimm->trfc1_ps) - 8U) >> 4U;
+	const unsigned int ext_wrrec = (picos_to_mclk(clk, pdimm->twr_ps) +
+				  (popts->otf_burst_chop_en ? 2U : 0U)) >> 4U;
+	const unsigned int rwt_same_cs = 0U;
+	const unsigned int wrt_same_cs = 0U;
+	const unsigned int rrt_same_cs = popts->burst_length == DDR_BL8 ? 0U : 2U;
+	const unsigned int wwt_same_cs = popts->burst_length == DDR_BL8 ? 0U : 2U;
+	const unsigned int dll_lock = 2U;
+	unsigned int rodt_on = 0U;
+	const unsigned int rodt_off = 4U;
+	const unsigned int wodt_on = 1U;
+	const unsigned int wodt_off = 4U;
+	const unsigned int hs_caslat = 0U;
+	const unsigned int hs_wrlat = 0U;
+	const unsigned int hs_wrrec = 0U;
+	const unsigned int hs_clkadj = 0U;
+	const unsigned int hs_wrlvl_start = 0U;
+	const unsigned int txpr = max(5U,
+				      picos_to_mclk(clk,
+						    pdimm->trfc1_ps + 10000U));
+	const unsigned int tcksre = max(5U, picos_to_mclk(clk, 10000U));
+	const unsigned int tcksrx = max(5U, picos_to_mclk(clk, 10000U));
+	const unsigned int cs_to_cmd = 0U;
+	const unsigned int cke_rst = txpr <= 200U ? 0U :
+				     (txpr <= 256U ? 1U :
+				      (txpr <= 512U ? 2U : 3U));
+	const unsigned int cksre = tcksre <= 19U ? tcksre - 5U : 15U;
+	const unsigned int cksrx = tcksrx <= 19U ? tcksrx - 5U : 15U;
+	unsigned int par_lat = 0U;
+	const int tccdl = max(5U, picos_to_mclk(clk, pdimm->tccdl_ps));
+	int rwt_bg = cas_latency + 2 + 4 - wr_lat;
+	int wrt_bg = wr_lat + 4 + 1 - cas_latency;
+	const int rrt_bg = popts->burst_length == DDR_BL8 ?
+				tccdl - 4 : tccdl - 2;
+	const int wwt_bg = popts->burst_length == DDR_BL8 ?
+					tccdl - 4 : tccdl - 2;
+	const unsigned int acttoact_bg = picos_to_mclk(clk, pdimm->trrdl_ps);
+	const unsigned int wrtord_bg = max(4U, picos_to_mclk(clk, 7500)) +
+				       (popts->otf_burst_chop_en ? 2 : 0);
+	const unsigned int pre_all_rec = 0;
+	const unsigned int refrec_cid_mclk = pdimm->package_3ds ?
+				picos_to_mclk(clk, pdimm->trfc_slr_ps) : 0;
+	const unsigned int acttoact_cid_mclk = pdimm->package_3ds ? 4U : 0;
+
+
+	/* for two dual-rank DIMMs to avoid ODT overlap */
+	if (avoid_odt_overlap(conf, pdimm) == 2) {
+		twrt_mclk = 2;
+		twwt_mclk = 2;
+		trrt_mclk = 2;
+	} else {
+		twrt_mclk = 1;
+		twwt_mclk = 1;
+		trrt_mclk = 0;
+	}
+
+	if (popts->trwt_override != 0) {
+		trwt_mclk = popts->trwt;
+		if (popts->twrt != 0) {
+			twrt_mclk = popts->twrt;
+		}
+		if (popts->trrt != 0) {
+			trrt_mclk = popts->trrt;
+		}
+		if (popts->twwt != 0) {
+			twwt_mclk = popts->twwt;
+		}
+	}
+	regs->timing_cfg[0] = (((trwt_mclk & 0x3) << 30)		|
+			     ((twrt_mclk & 0x3) << 28)			|
+			     ((trrt_mclk & 0x3) << 26)			|
+			     ((twwt_mclk & 0x3) << 24)			|
+			     ((act_pd_exit_mclk & 0xf) << 20)		|
+			     ((pre_pd_exit_mclk & 0xF) << 16)		|
+			     ((taxpd_mclk & 0xf) << 8)			|
+			     ((tmrd_mclk & 0x1f) << 0));
+	debug("timing_cfg[0] = 0x%x\n", regs->timing_cfg[0]);
+
+	if ((wrrec_mclk < 1) || (wrrec_mclk > 24)) {
+		ERROR("WRREC doesn't support clock %d\n", wrrec_mclk);
+	} else {
+		wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+	}
+
+	if (popts->otf_burst_chop_en != 0) {
+		wrrec_mclk += 2;
+		wrtord_mclk += 2;
+	}
+
+	if (pdimm->trfc1_ps < trfc1_min) {
+		ERROR("trfc1_ps (%d) < %d\n", pdimm->trfc1_ps, trfc1_min);
+	}
+
+	regs->timing_cfg[1] = (((pretoact_mclk & 0x0F) << 28)		|
+			     ((acttopre_mclk & 0x0F) << 24)		|
+			     ((acttorw_mclk & 0xF) << 20)		|
+			     ((caslat_ctrl & 0xF) << 16)		|
+			     ((refrec_ctrl & 0xF) << 12)		|
+			     ((wrrec_mclk & 0x0F) << 8)			|
+			     ((acttoact_mclk & 0x0F) << 4)		|
+			     ((wrtord_mclk & 0x0F) << 0));
+	debug("timing_cfg[1] = 0x%x\n", regs->timing_cfg[1]);
+
+	if (rd_to_pre < 4) {
+		rd_to_pre = 4;
+	}
+	if (popts->otf_burst_chop_en) {
+		rd_to_pre += 2;
+	}
+
+	regs->timing_cfg[2] = (((additive_latency & 0xf) << 28)		|
+			     ((cpo & 0x1f) << 23)			|
+			     ((wr_lat & 0xf) << 19)			|
+			     (((wr_lat & 0x10) >> 4) << 18)		|
+			     ((rd_to_pre & 0xf) << 13)			|
+			     ((wr_data_delay & 0xf) << 9)		|
+			     ((cke_pls & 0x7) << 6)			|
+			     ((four_act & 0x3f) << 0));
+	debug("timing_cfg[2] = 0x%x\n", regs->timing_cfg[2]);
+
+	regs->timing_cfg[3] = (((ext_pretoact & 0x1) << 28)		|
+			     ((ext_acttopre & 0x3) << 24)		|
+			     ((ext_acttorw & 0x1) << 22)		|
+			     ((ext_refrec & 0x3F) << 16)		|
+			     ((ext_caslat & 0x3) << 12)			|
+			     ((ext_add_lat & 0x1) << 10)		|
+			     ((ext_wrrec & 0x1) << 8)			|
+			     ((cntl_adj & 0x7) << 0));
+	debug("timing_cfg[3] = 0x%x\n", regs->timing_cfg[3]);
+
+	regs->timing_cfg[4] = (((rwt_same_cs & 0xf) << 28)		|
+			     ((wrt_same_cs & 0xf) << 24)		|
+			     ((rrt_same_cs & 0xf) << 20)		|
+			     ((wwt_same_cs & 0xf) << 16)		|
+			     ((trwt_mclk & 0xc) << 12)			|
+			     ((twrt_mclk & 0x4) << 10)			|
+			     ((trrt_mclk & 0x4) << 8)			|
+			     ((twwt_mclk & 0x4) << 6)			|
+			     (dll_lock & 0x3));
+	debug("timing_cfg[4] = 0x%x\n", regs->timing_cfg[4]);
+
+	/* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */
+	if (cas_latency >= wr_lat) {
+		rodt_on = cas_latency - wr_lat + 1;
+	}
+
+	regs->timing_cfg[5] = (((rodt_on & 0x1f) << 24)			|
+			     ((rodt_off & 0x7) << 20)			|
+			     ((wodt_on & 0x1f) << 12)			|
+			     (wodt_off & 0x7) << 8);
+	debug("timing_cfg[5] = 0x%x\n", regs->timing_cfg[5]);
+
+	regs->timing_cfg[6] = (((hs_caslat & 0x1f) << 24)		|
+			     ((hs_wrlat & 0x1f) << 19)			|
+			     ((hs_wrrec & 0x1f) << 12)			|
+			     ((hs_clkadj & 0x1f) << 6)			|
+			     ((hs_wrlvl_start & 0x1f) << 0));
+	debug("timing_cfg[6] = 0x%x\n", regs->timing_cfg[6]);
+
+	if (popts->ap_en != 0) {
+		par_lat = (regs->sdram_rcw[1] & 0xf) + 1;
+		debug("PAR_LAT = 0x%x\n", par_lat);
+	}
+
+	regs->timing_cfg[7] = (((cke_rst & 0x3) << 28)			|
+			     ((cksre & 0xf) << 24)			|
+			     ((cksrx & 0xf) << 20)			|
+			     ((par_lat & 0xf) << 16)			|
+			     ((cs_to_cmd & 0xf) << 4));
+	debug("timing_cfg[7] = 0x%x\n", regs->timing_cfg[7]);
+
+	if (rwt_bg < tccdl) {
+		rwt_bg = tccdl - rwt_bg;
+	} else {
+		rwt_bg = 0;
+	}
+	if (wrt_bg < tccdl) {
+		wrt_bg = tccdl - wrt_bg;
+	} else {
+		wrt_bg = 0;
+	}
+	regs->timing_cfg[8] = (((rwt_bg & 0xf) << 28)			|
+			     ((wrt_bg & 0xf) << 24)			|
+			     ((rrt_bg & 0xf) << 20)			|
+			     ((wwt_bg & 0xf) << 16)			|
+			     ((acttoact_bg & 0xf) << 12)		|
+			     ((wrtord_bg & 0xf) << 8)			|
+			     ((pre_all_rec & 0x1f) << 0));
+	debug("timing_cfg[8] = 0x%x\n", regs->timing_cfg[8]);
+
+	regs->timing_cfg[9] = (refrec_cid_mclk & 0x3ff) << 16		|
+			      (acttoact_cid_mclk & 0xf) << 8;
+	debug("timing_cfg[9] = 0x%x\n", regs->timing_cfg[9]);
+}
+
+static void cal_ddr_sdram_rcw(const unsigned long clk,
+			      struct ddr_cfg_regs *regs,
+			      const struct memctl_opt *popts,
+			      const struct dimm_params *pdimm)
+{
+	const unsigned int freq = clk / 1000000U;
+	unsigned int rc0a, rc0f;
+
+	if (pdimm->rdimm == 0) {
+		return;
+	}
+
+	rc0a = freq > 3200U ? 7U :
+	       (freq > 2933U ? 6U :
+		(freq > 2666U ? 5U :
+		 (freq > 2400U ? 4U :
+		  (freq > 2133U ? 3U :
+		   (freq > 1866U ? 2U :
+		    (freq > 1600U ? 1U : 0U))))));
+	rc0f = freq > 3200U ? 3U :
+		(freq > 2400U ? 2U :
+		 (freq > 2133U ? 1U : 0U));
+	rc0f = (regs->sdram_cfg[1] & SDRAM_CFG2_AP_EN) ? rc0f : 4;
+	regs->sdram_rcw[0] =
+		pdimm->rcw[0] << 28	|
+		pdimm->rcw[1] << 24	|
+		pdimm->rcw[2] << 20	|
+		pdimm->rcw[3] << 16	|
+		pdimm->rcw[4] << 12	|
+		pdimm->rcw[5] << 8	|
+		pdimm->rcw[6] << 4	|
+		pdimm->rcw[7];
+	regs->sdram_rcw[1] =
+		pdimm->rcw[8] << 28	|
+		pdimm->rcw[9] << 24	|
+		rc0a << 20		|
+		pdimm->rcw[11] << 16	|
+		pdimm->rcw[12] << 12	|
+		pdimm->rcw[13] << 8	|
+		pdimm->rcw[14] << 4	|
+		rc0f;
+	regs->sdram_rcw[2] =
+		((freq - 1260 + 19) / 20) << 8;
+
+	debug("sdram_rcw[0] = 0x%x\n", regs->sdram_rcw[0]);
+	debug("sdram_rcw[1] = 0x%x\n", regs->sdram_rcw[1]);
+	debug("sdram_rcw[2] = 0x%x\n", regs->sdram_rcw[2]);
+}
+
+static void cal_ddr_sdram_cfg(const unsigned long clk,
+			      struct ddr_cfg_regs *regs,
+			      const struct memctl_opt *popts,
+			      const struct dimm_params *pdimm,
+			      const unsigned int ip_rev)
+{
+	const unsigned int mem_en = 1U;
+	const unsigned int sren = popts->self_refresh_in_sleep;
+	const unsigned int ecc_en = popts->ecc_mode;
+	const unsigned int rd_en = (pdimm->rdimm != 0U) ? 1U : 0U;
+	const unsigned int dyn_pwr = popts->dynamic_power;
+	const unsigned int dbw = popts->data_bus_used;
+	const unsigned int eight_be = (dbw == 1U ||
+				       popts->burst_length == DDR_BL8) ? 1U : 0U;
+	const unsigned int ncap = 0U;
+	const unsigned int threet_en = popts->threet_en;
+	const unsigned int twot_en = pdimm->rdimm ?
+					0U : popts->twot_en;
+	const unsigned int ba_intlv = popts->ba_intlv;
+	const unsigned int x32_en = 0U;
+	const unsigned int pchb8 = 0U;
+	const unsigned int hse = popts->half_strength_drive_en;
+	const unsigned int acc_ecc_en = (dbw != 0U && ecc_en == 1U) ? 1U : 0U;
+	const unsigned int mem_halt = 0U;
+#ifdef PHY_GEN2
+	const unsigned int bi = 1U;
+#else
+	const unsigned int bi = 0U;
+#endif
+	const unsigned int sdram_type = SDRAM_TYPE_DDR4;
+	unsigned int odt_cfg = 0U;
+	const unsigned int frc_sr = 0U;
+	const unsigned int sr_ie = popts->self_refresh_irq_en;
+	const unsigned int num_pr = pdimm->package_3ds + 1U;
+	const unsigned int slow = (clk < 1249000000U) ? 1U : 0U;
+	const unsigned int x4_en = popts->x4_en;
+	const unsigned int obc_cfg = popts->otf_burst_chop_en;
+	const unsigned int ap_en = ip_rev == 0x50500U ? 0U : popts->ap_en;
+	const unsigned int d_init = popts->ctlr_init_ecc;
+	const unsigned int rcw_en = popts->rdimm;
+	const unsigned int md_en = popts->mirrored_dimm;
+	const unsigned int qd_en = popts->quad_rank_present;
+	const unsigned int unq_mrs_en = ip_rev < 0x50500U ? 1U : 0U;
+	const unsigned int rd_pre = popts->quad_rank_present;
+	int i;
+
+	regs->sdram_cfg[0] = ((mem_en & 0x1) << 31)		|
+				((sren & 0x1) << 30)		|
+				((ecc_en & 0x1) << 29)		|
+				((rd_en & 0x1) << 28)		|
+				((sdram_type & 0x7) << 24)	|
+				((dyn_pwr & 0x1) << 21)		|
+				((dbw & 0x3) << 19)		|
+				((eight_be & 0x1) << 18)	|
+				((ncap & 0x1) << 17)		|
+				((threet_en & 0x1) << 16)	|
+				((twot_en & 0x1) << 15)		|
+				((ba_intlv & 0x7F) << 8)	|
+				((x32_en & 0x1) << 5)		|
+				((pchb8 & 0x1) << 4)		|
+				((hse & 0x1) << 3)		|
+				((acc_ecc_en & 0x1) << 2)	|
+				((mem_halt & 0x1) << 1)		|
+				((bi & 0x1) << 0);
+	debug("sdram_cfg[0] = 0x%x\n", regs->sdram_cfg[0]);
+
+	for (i = 0; i < DDRC_NUM_CS; i++) {
+		if (popts->cs_odt[i].odt_rd_cfg != 0 ||
+		    popts->cs_odt[i].odt_wr_cfg != 0) {
+			odt_cfg = SDRAM_CFG2_ODT_ONLY_READ;
+			break;
+		}
+	}
+
+	regs->sdram_cfg[1] = (0
+		| ((frc_sr & 0x1) << 31)
+		| ((sr_ie & 0x1) << 30)
+		| ((odt_cfg & 0x3) << 21)
+		| ((num_pr & 0xf) << 12)
+		| ((slow & 1) << 11)
+		| (x4_en << 10)
+		| (qd_en << 9)
+		| (unq_mrs_en << 8)
+		| ((obc_cfg & 0x1) << 6)
+		| ((ap_en & 0x1) << 5)
+		| ((d_init & 0x1) << 4)
+		| ((rcw_en & 0x1) << 2)
+		| ((md_en & 0x1) << 0)
+		);
+	debug("sdram_cfg[1] = 0x%x\n", regs->sdram_cfg[1]);
+
+	regs->sdram_cfg[2] = (rd_pre & 0x1) << 16	|
+				 (popts->rdimm ? 1 : 0);
+	if (pdimm->package_3ds != 0) {
+		if (((pdimm->package_3ds + 1) & 0x1) != 0) {
+			WARN("Unsupported 3DS DIMM\n");
+		} else {
+			regs->sdram_cfg[2] |= ((pdimm->package_3ds + 1) >> 1)
+						  << 4;
+		}
+	}
+	debug("sdram_cfg[2] = 0x%x\n", regs->sdram_cfg[2]);
+}
+
+
+static void cal_ddr_sdram_interval(const unsigned long clk,
+				   struct ddr_cfg_regs *regs,
+				   const struct memctl_opt *popts,
+				   const struct dimm_params *pdimm)
+{
+	const unsigned int refint = picos_to_mclk(clk, pdimm->refresh_rate_ps);
+	const unsigned int bstopre = popts->bstopre;
+
+	regs->interval = ((refint & 0xFFFF) << 16)	|
+				  ((bstopre & 0x3FFF) << 0);
+	debug("interval = 0x%x\n", regs->interval);
+}
+
+/* Require cs and cfg first */
+static void cal_ddr_sdram_mode(const unsigned long clk,
+			       struct ddr_cfg_regs *regs,
+			       const struct memctl_opt *popts,
+			       const struct ddr_conf *conf,
+			       const struct dimm_params *pdimm,
+			       unsigned int cas_latency,
+			       unsigned int additive_latency,
+			       const unsigned int ip_rev)
+{
+	int i;
+	unsigned short esdmode;		/* Extended SDRAM mode */
+	unsigned short sdmode;		/* SDRAM mode */
+
+	/* Mode Register - MR1 */
+	const unsigned int qoff = 0;
+	const unsigned int tdqs_en = 0;
+	unsigned int rtt;
+	const unsigned int wrlvl_en = 0;
+	unsigned int al = 0;
+	unsigned int dic = 0;
+	const unsigned int dll_en = 1;
+
+	/* Mode Register - MR0 */
+	unsigned int wr = 0;
+	const unsigned int dll_rst = 0;
+	const unsigned int mode = 0;
+	unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */
+	/* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */
+	const unsigned int bt = 0;
+	const unsigned int bl = popts->burst_length == DDR_BL8 ? 0 :
+				 (popts->burst_length == DDR_BC4 ? 2 : 1);
+
+	const unsigned int wr_mclk = picos_to_mclk(clk, pdimm->twr_ps);
+	/* DDR4 support WR 10, 12, 14, 16, 18, 20, 24 */
+	static const int wr_table[] = {
+		0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6
+	};
+	/* DDR4 support CAS 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24 */
+	static const int cas_latency_table[] = {
+		0, 1, 2, 3, 4, 5, 6, 7, 13, 8,
+		14, 9, 15, 10, 12, 11, 16, 17,
+		18, 19, 20, 21, 22, 23
+	};
+	const unsigned int unq_mrs_en = ip_rev < U(0x50500) ? 1U : 0U;
+	unsigned short esdmode2 = 0U;
+	unsigned short esdmode3 = 0U;
+	const unsigned int wr_crc = 0U;
+	unsigned int rtt_wr = 0U;
+	const unsigned int srt = 0U;
+	unsigned int cwl = cal_cwl(clk);
+	const unsigned int mpr = 0U;
+	const unsigned int mclk_ps = get_memory_clk_ps(clk);
+	const unsigned int wc_lat = 0U;
+	unsigned short esdmode4 = 0U;
+	unsigned short esdmode5;
+	int rtt_park_all = 0;
+	unsigned int rtt_park;
+	const bool four_cs = conf->cs_in_use == 0xf ? true : false;
+	unsigned short esdmode6 = 0U;	/* Extended SDRAM mode 6 */
+	unsigned short esdmode7 = 0U;	/* Extended SDRAM mode 7 */
+	const unsigned int tccdl_min = max(5U,
+					   picos_to_mclk(clk, pdimm->tccdl_ps));
+
+	if (popts->rtt_override != 0U) {
+		rtt = popts->rtt_override_value;
+	} else {
+		rtt = popts->cs_odt[0].odt_rtt_norm;
+	}
+
+	if (additive_latency == (cas_latency - 1)) {
+		al = 1;
+	}
+	if (additive_latency == (cas_latency - 2)) {
+		al = 2;
+	}
+
+	if (popts->quad_rank_present != 0 || popts->output_driver_impedance != 0) {
+		dic = 1;	/* output driver impedance 240/7 ohm */
+	}
+
+	esdmode = (((qoff & 0x1) << 12)				|
+		   ((tdqs_en & 0x1) << 11)			|
+		   ((rtt & 0x7) << 8)				|
+		   ((wrlvl_en & 0x1) << 7)			|
+		   ((al & 0x3) << 3)				|
+		   ((dic & 0x3) << 1)				|
+		   ((dll_en & 0x1) << 0));
+
+	if (wr_mclk >= 10 && wr_mclk <= 24) {
+		wr = wr_table[wr_mclk - 10];
+	} else {
+		ERROR("unsupported wc_mclk = %d for mode register\n", wr_mclk);
+	}
+
+	/* look up table to get the cas latency bits */
+	if (cas_latency >= 9 && cas_latency <= 32) {
+		caslat = cas_latency_table[cas_latency - 9];
+	} else {
+		WARN("Error: unsupported cas latency for mode register\n");
+	}
+
+	sdmode = (((caslat & 0x10) << 8)			|
+		  ((wr & 0x7) << 9)				|
+		  ((dll_rst & 0x1) << 8)			|
+		  ((mode & 0x1) << 7)				|
+		  (((caslat >> 1) & 0x7) << 4)			|
+		  ((bt & 0x1) << 3)				|
+		  ((caslat & 1) << 2)				|
+		  ((bl & 0x3) << 0));
+
+	regs->sdram_mode[0] = (((esdmode & 0xFFFF) << 16)	|
+				 ((sdmode & 0xFFFF) << 0));
+	debug("sdram_mode[0] = 0x%x\n", regs->sdram_mode[0]);
+
+	switch (cwl) {
+	case 9:
+	case 10:
+	case 11:
+	case 12:
+		cwl -= 9;
+		break;
+	case 14:
+		cwl -= 10;
+		break;
+	case 16:
+		cwl -= 11;
+		break;
+	case 18:
+		cwl -= 12;
+		break;
+	case 20:
+		cwl -= 13;
+		break;
+	default:
+		printf("Error CWL\n");
+		break;
+	}
+
+	if (popts->rtt_override != 0) {
+		rtt_wr = popts->rtt_wr_override_value;
+	} else {
+		rtt_wr = popts->cs_odt[0].odt_rtt_wr;
+	}
+
+	esdmode2 = ((wr_crc & 0x1) << 12)			|
+		   ((rtt_wr & 0x7) << 9)			|
+		   ((srt & 0x3) << 6)				|
+		   ((cwl & 0x7) << 3);
+	esdmode3 = ((mpr & 0x3) << 11) | ((wc_lat & 0x3) << 9);
+
+	regs->sdram_mode[1] = ((esdmode2 & 0xFFFF) << 16)	|
+				((esdmode3 & 0xFFFF) << 0);
+	debug("sdram_mode[1] = 0x%x\n", regs->sdram_mode[1]);
+
+	esdmode6 = ((tccdl_min - 4) & 0x7) << 10;
+	if (popts->vref_dimm != 0) {
+		esdmode6 |= popts->vref_dimm & 0x7f;
+	} else if ((popts->ddr_cdr2 & DDR_CDR2_VREF_RANGE_2) != 0) {
+		esdmode6 |= 1 << 6;	/* Range 2 */
+	}
+
+	regs->sdram_mode[9] = ((esdmode6 & 0xffff) << 16)	|
+				 ((esdmode7 & 0xffff) << 0);
+	debug("sdram_mode[9] = 0x%x\n", regs->sdram_mode[9]);
+
+	rtt_park = (popts->rtt_park != 0) ? popts->rtt_park : 240;
+	switch (rtt_park) {
+	case 240:
+		rtt_park = 0x4;
+		break;
+	case 120:
+		rtt_park = 0x2;
+		break;
+	case 80:
+		rtt_park = 0x6;
+		break;
+	case 60:
+		rtt_park = 0x1;
+		break;
+	case 48:
+		rtt_park = 0x5;
+		break;
+	case 40:
+		rtt_park = 0x3;
+		break;
+	case 34:
+		rtt_park = 0x7;
+		break;
+	default:
+		rtt_park = 0;
+		break;
+	}
+
+	for (i = 0; i < DDRC_NUM_CS; i++) {
+		if (i != 0 && unq_mrs_en == 0) {
+			break;
+		}
+
+		if (popts->rtt_override != 0) {
+			rtt = popts->rtt_override_value;
+			rtt_wr = popts->rtt_wr_override_value;
+		} else {
+			rtt = popts->cs_odt[i].odt_rtt_norm;
+			rtt_wr = popts->cs_odt[i].odt_rtt_wr;
+		}
+
+		esdmode &= 0xF8FF;	/* clear bit 10,9,8 for rtt */
+		esdmode |= (rtt & 0x7) << 8;
+		esdmode2 &= 0xF9FF;	/* clear bit 10, 9 */
+		esdmode2 |= (rtt_wr & 0x3) << 9;
+		esdmode5 = (popts->x4_en) ? 0 : 0x400; /* data mask */
+
+		if (rtt_park_all == 0 &&
+		    ((regs->cs[i].config & SDRAM_CS_CONFIG_EN) != 0)) {
+			esdmode5 |= rtt_park << 6;
+			rtt_park_all = four_cs ? 0 : 1;
+		}
+
+		if (((regs->sdram_cfg[1] & SDRAM_CFG2_AP_EN) != 0) &&
+		    (popts->rdimm == 0)) {
+			if (mclk_ps >= 935) {
+				esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK;
+			} else if (mclk_ps >= 833) {
+				esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
+			} else {
+				esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
+				WARN("mclk_ps not supported %d", mclk_ps);
+
+			}
+		}
+
+		switch (i) {
+		case 0:
+			regs->sdram_mode[8] = ((esdmode4 & 0xffff) << 16) |
+						((esdmode5 & 0xffff) << 0);
+			debug("sdram_mode[8] = 0x%x\n", regs->sdram_mode[8]);
+			break;
+		case 1:
+			regs->sdram_mode[2] = (((esdmode & 0xFFFF) << 16) |
+					      ((sdmode & 0xFFFF) << 0));
+			regs->sdram_mode[3] = ((esdmode2 & 0xFFFF) << 16) |
+					      ((esdmode3 & 0xFFFF) << 0);
+			regs->sdram_mode[10] = ((esdmode4 & 0xFFFF) << 16) |
+					       ((esdmode5 & 0xFFFF) << 0);
+			regs->sdram_mode[11] = ((esdmode6 & 0xFFFF) << 16) |
+					       ((esdmode7 & 0xFFFF) << 0);
+			debug("sdram_mode[2] = 0x%x\n", regs->sdram_mode[2]);
+			debug("sdram_mode[3] = 0x%x\n", regs->sdram_mode[3]);
+			debug("sdram_mode[10] = 0x%x\n", regs->sdram_mode[10]);
+			debug("sdram_mode[11] = 0x%x\n", regs->sdram_mode[11]);
+			break;
+		case 2:
+			regs->sdram_mode[4] = (((esdmode & 0xFFFF) << 16) |
+					      ((sdmode & 0xFFFF) << 0));
+			regs->sdram_mode[5] = ((esdmode2 & 0xFFFF) << 16) |
+					      ((esdmode3 & 0xFFFF) << 0);
+			regs->sdram_mode[12] = ((esdmode4 & 0xFFFF) << 16) |
+					       ((esdmode5 & 0xFFFF) << 0);
+			regs->sdram_mode[13] = ((esdmode6 & 0xFFFF) << 16) |
+					       ((esdmode7 & 0xFFFF) << 0);
+			debug("sdram_mode[4] = 0x%x\n", regs->sdram_mode[4]);
+			debug("sdram_mode[5] = 0x%x\n", regs->sdram_mode[5]);
+			debug("sdram_mode[12] = 0x%x\n", regs->sdram_mode[12]);
+			debug("sdram_mode[13] = 0x%x\n", regs->sdram_mode[13]);
+			break;
+		case 3:
+			regs->sdram_mode[6] = (((esdmode & 0xFFFF) << 16) |
+					      ((sdmode & 0xFFFF) << 0));
+			regs->sdram_mode[7] = ((esdmode2 & 0xFFFF) << 16) |
+					      ((esdmode3 & 0xFFFF) << 0);
+			regs->sdram_mode[14] = ((esdmode4 & 0xFFFF) << 16) |
+					       ((esdmode5 & 0xFFFF) << 0);
+			regs->sdram_mode[15] = ((esdmode6 & 0xFFFF) << 16) |
+					       ((esdmode7 & 0xFFFF) << 0);
+			debug("sdram_mode[6] = 0x%x\n", regs->sdram_mode[6]);
+			debug("sdram_mode[7] = 0x%x\n", regs->sdram_mode[7]);
+			debug("sdram_mode[14] = 0x%x\n", regs->sdram_mode[14]);
+			debug("sdram_mode[15] = 0x%x\n", regs->sdram_mode[15]);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+#ifndef CONFIG_MEM_INIT_VALUE
+#define CONFIG_MEM_INIT_VALUE 0xDEADBEEF
+#endif
+static void cal_ddr_data_init(struct ddr_cfg_regs *regs)
+{
+	regs->data_init = CONFIG_MEM_INIT_VALUE;
+}
+
+static void cal_ddr_dq_mapping(struct ddr_cfg_regs *regs,
+			       const struct dimm_params *pdimm)
+{
+	const unsigned int acc_ecc_en = (regs->sdram_cfg[0] >> 2) & 0x1;
+/* FIXME: revert the dq mapping from DIMM */
+	regs->dq_map[0] = ((pdimm->dq_mapping[0] & 0x3F) << 26)	|
+			 ((pdimm->dq_mapping[1] & 0x3F) << 20)	|
+			 ((pdimm->dq_mapping[2] & 0x3F) << 14)	|
+			 ((pdimm->dq_mapping[3] & 0x3F) << 8)	|
+			 ((pdimm->dq_mapping[4] & 0x3F) << 2);
+
+	regs->dq_map[1] = ((pdimm->dq_mapping[5] & 0x3F) << 26)	|
+			 ((pdimm->dq_mapping[6] & 0x3F) << 20)	|
+			 ((pdimm->dq_mapping[7] & 0x3F) << 14)	|
+			 ((pdimm->dq_mapping[10] & 0x3F) << 8)	|
+			 ((pdimm->dq_mapping[11] & 0x3F) << 2);
+
+	regs->dq_map[2] = ((pdimm->dq_mapping[12] & 0x3F) << 26)	|
+			 ((pdimm->dq_mapping[13] & 0x3F) << 20)		|
+			 ((pdimm->dq_mapping[14] & 0x3F) << 14)		|
+			 ((pdimm->dq_mapping[15] & 0x3F) << 8)		|
+			 ((pdimm->dq_mapping[16] & 0x3F) << 2);
+
+	/* dq_map for ECC[4:7] is set to 0 if accumulated ECC is enabled */
+	regs->dq_map[3] = ((pdimm->dq_mapping[17] & 0x3F) << 26)	|
+			 ((pdimm->dq_mapping[8] & 0x3F) << 20)		|
+			 ((acc_ecc_en != 0) ? 0 :
+			  (pdimm->dq_mapping[9] & 0x3F) << 14)		|
+			 pdimm->dq_mapping_ors;
+	debug("dq_map[0] = 0x%x\n", regs->dq_map[0]);
+	debug("dq_map[1] = 0x%x\n", regs->dq_map[1]);
+	debug("dq_map[2] = 0x%x\n", regs->dq_map[2]);
+	debug("dq_map[3] = 0x%x\n", regs->dq_map[3]);
+}
+static void cal_ddr_zq_cntl(struct ddr_cfg_regs *regs)
+{
+	const unsigned int zqinit = 10U;	/* 1024 clocks */
+	const unsigned int zqoper = 9U;		/* 512 clocks */
+	const unsigned int zqcs = 7U;		/* 128 clocks */
+	const unsigned int zqcs_init = 5U;	/* 1024 refresh seqences */
+	const unsigned int zq_en = 1U;		/* enabled */
+
+	regs->zq_cntl = ((zq_en & 0x1) << 31)			|
+			   ((zqinit & 0xF) << 24)		|
+			   ((zqoper & 0xF) << 16)		|
+			   ((zqcs & 0xF) << 8)			|
+			   ((zqcs_init & 0xF) << 0);
+	debug("zq_cntl = 0x%x\n", regs->zq_cntl);
+}
+
+static void cal_ddr_sr_cntr(struct ddr_cfg_regs *regs,
+			    const struct memctl_opt *popts)
+{
+	const unsigned int sr_it = (popts->auto_self_refresh_en) ?
+					popts->sr_it : 0;
+
+	regs->ddr_sr_cntr = (sr_it & 0xF) << 16;
+	debug("ddr_sr_cntr = 0x%x\n", regs->ddr_sr_cntr);
+}
+
+static void cal_ddr_eor(struct ddr_cfg_regs *regs,
+			const struct memctl_opt *popts)
+{
+	if (popts->addr_hash != 0) {
+		regs->eor = 0x40000000;	/* address hash enable */
+		debug("eor = 0x%x\n", regs->eor);
+	}
+}
+
+static void cal_ddr_csn_bnds(struct ddr_cfg_regs *regs,
+			     const struct memctl_opt *popts,
+			     const struct ddr_conf *conf,
+			     const struct dimm_params *pdimm)
+{
+	int i;
+	unsigned long long ea, sa;
+
+	/* Chip Select Memory Bounds (CSn_BNDS) */
+	for (i = 0;
+		i < DDRC_NUM_CS && conf->cs_size[i];
+		i++) {
+		debug("cs_in_use = 0x%x\n", conf->cs_in_use);
+		if (conf->cs_in_use != 0) {
+			sa = conf->cs_base_addr[i];
+			ea = sa + conf->cs_size[i] - 1;
+			sa >>= 24;
+			ea >>= 24;
+			regs->cs[i].bnds = ((sa & 0xffff) << 16) |
+					   ((ea & 0xffff) << 0);
+			cal_csn_config(i, regs, popts, pdimm);
+		} else {
+			/* setting bnds to 0xffffffff for inactive CS */
+			regs->cs[i].bnds = 0xffffffff;
+		}
+
+		debug("cs[%d].bnds = 0x%x\n", i, regs->cs[i].bnds);
+	}
+}
+
+static void cal_ddr_addr_dec(struct ddr_cfg_regs *regs)
+{
+#ifdef CONFIG_DDR_ADDR_DEC
+	unsigned int ba_bits __unused;
+	char p __unused;
+	const unsigned int cs0_config = regs->cs[0].config;
+	const int cacheline = PLATFORM_CACHE_LINE_SHIFT;
+	unsigned int bg_bits;
+	unsigned int row_bits;
+	unsigned int col_bits;
+	unsigned int cs;
+	unsigned int map_row[18];
+	unsigned int map_col[11];
+	unsigned int map_ba[2];
+	unsigned int map_cid[2] = {0x3F, 0x3F};
+	unsigned int map_bg[2] = {0x3F, 0x3F};
+	unsigned int map_cs[2] = {0x3F, 0x3F};
+	unsigned int dbw;
+	unsigned int ba_intlv;
+	int placement;
+	int intlv;
+	int abort = 0;
+	int i;
+	int j;
+
+	col_bits = (cs0_config >> 0) & 0x7;
+	if (col_bits < 4) {
+		col_bits += 8;
+	} else if (col_bits < 7 || col_bits > 10) {
+		ERROR("Error %s col_bits = %d\n", __func__, col_bits);
+	}
+	row_bits = ((cs0_config >> 8) & 0x7) + 12;
+	ba_bits = ((cs0_config >> 14) & 0x3) + 2;
+	bg_bits = ((cs0_config >> 4) & 0x3) + 0;
+	intlv = (cs0_config >> 24) & 0xf;
+	ba_intlv = (regs->sdram_cfg[0] >> 8) & 0x7f;
+	switch (ba_intlv) {
+	case DDR_BA_INTLV_CS01:
+		cs = 1;
+		break;
+	case DDR_BA_INTLV_CS0123:
+		cs = 2;
+		break;
+	case DDR_BA_NONE:
+		cs = 0;
+		break;
+	default:
+		ERROR("%s ba_intlv 0x%x\n", __func__, ba_intlv);
+		return;
+	}
+	debug("col %d, row %d, ba %d, bg %d, intlv %d\n",
+			col_bits, row_bits, ba_bits, bg_bits, intlv);
+	/*
+	 * Example mapping of 15x2x2x10
+	 * ---- --rr rrrr rrrr rrrr rCBB Gccc cccI cGcc cbbb
+	 */
+	dbw = (regs->sdram_cfg[0] >> 19) & 0x3;
+	switch (dbw) {
+	case 0:	/* 64-bit */
+		placement = 3;
+		break;
+	case 1:	/* 32-bit */
+		placement = 2;
+		break;
+	default:
+		ERROR("%s dbw = %d\n", __func__, dbw);
+		return;
+	}
+	debug("cacheline size %d\n", cacheline);
+	for (i = 0; placement < cacheline; i++) {
+		map_col[i] = placement++;
+	}
+	map_bg[0] = placement++;
+	for ( ; i < col_bits; i++) {
+		map_col[i] = placement++;
+		if (placement == intlv) {
+			placement++;
+		}
+	}
+	for ( ; i < 11; i++) {
+		map_col[i] = 0x3F;	/* unused col bits */
+	}
+
+	if (bg_bits >= 2) {
+		map_bg[1] = placement++;
+	}
+	map_ba[0] = placement++;
+	map_ba[1] = placement++;
+	if (cs != 0U) {
+		map_cs[0] = placement++;
+		if (cs == 2U) {
+			map_cs[1] = placement++;
+		}
+	} else {
+		map_cs[0] = U(0x3F);
+	}
+
+	for (i = 0; i < row_bits; i++) {
+		map_row[i] = placement++;
+	}
+
+	for ( ; i < 18; i++) {
+		map_row[i] = 0x3F;	/* unused row bits */
+	}
+
+	for (i = 39; i >= 0 ; i--) {
+		if (i == intlv) {
+			placement = 8;
+			p = 'I';
+		} else if (i < 3) {
+			p = 'b';
+			placement = 0;
+		} else {
+			placement = 0;
+			p = '-';
+		}
+		for (j = 0; j < 18; j++) {
+			if (map_row[j] != i) {
+				continue;
+			}
+			if (placement != 0) {
+				abort = 1;
+				ERROR("%s wrong address bit %d\n", __func__, i);
+			}
+			placement = i;
+			p = 'r';
+		}
+		for (j = 0; j < 11; j++) {
+			if (map_col[j] != i) {
+				continue;
+			}
+			if (placement != 0) {
+				abort = 1;
+				ERROR("%s wrong address bit %d\n", __func__, i);
+			}
+			placement = i;
+			p = 'c';
+		}
+		for (j = 0; j < 2; j++) {
+			if (map_ba[j] != i) {
+				continue;
+			}
+			if (placement != 0) {
+				abort = 1;
+				ERROR("%s wrong address bit %d\n", __func__, i);
+			}
+			placement = i;
+			p = 'B';
+		}
+		for (j = 0; j < 2; j++) {
+			if (map_bg[j] != i) {
+				continue;
+			}
+			if (placement != 0) {
+				abort = 1;
+				ERROR("%s wrong address bit %d\n", __func__, i);
+			}
+			placement = i;
+			p = 'G';
+		}
+		for (j = 0; j < 2; j++) {
+			if (map_cs[j] != i) {
+				continue;
+			}
+			if (placement != 0) {
+				abort = 1;
+				ERROR("%s wrong address bit %d\n", __func__, i);
+			}
+			placement = i;
+			p = 'C';
+		}
+#ifdef DDR_DEBUG
+		printf("%c", p);
+		if ((i % 4) == 0) {
+			printf(" ");
+		}
+#endif
+	}
+#ifdef DDR_DEBUG
+	puts("\n");
+#endif
+
+	if (abort != 0) {
+		return;
+	}
+
+	regs->dec[0] = map_row[17] << 26		|
+		      map_row[16] << 18			|
+		      map_row[15] << 10			|
+		      map_row[14] << 2;
+	regs->dec[1] = map_row[13] << 26		|
+		      map_row[12] << 18			|
+		      map_row[11] << 10			|
+		      map_row[10] << 2;
+	regs->dec[2] = map_row[9] << 26			|
+		      map_row[8] << 18			|
+		      map_row[7] << 10			|
+		      map_row[6] << 2;
+	regs->dec[3] = map_row[5] << 26			|
+		      map_row[4] << 18			|
+		      map_row[3] << 10			|
+		      map_row[2] << 2;
+	regs->dec[4] = map_row[1] << 26			|
+		      map_row[0] << 18			|
+		      map_col[10] << 10			|
+		      map_col[9] << 2;
+	regs->dec[5] = map_col[8] << 26			|
+		      map_col[7] << 18			|
+		      map_col[6] << 10			|
+		      map_col[5] << 2;
+	regs->dec[6] = map_col[4] << 26			|
+		      map_col[3] << 18			|
+		      map_col[2] << 10			|
+		      map_col[1] << 2;
+	regs->dec[7] = map_col[0] << 26			|
+		      map_ba[1] << 18			|
+		      map_ba[0] << 10			|
+		      map_cid[1] << 2;
+	regs->dec[8] = map_cid[1] << 26			|
+		      map_cs[1] << 18			|
+		      map_cs[0] << 10			|
+		      map_bg[1] << 2;
+	regs->dec[9] = map_bg[0] << 26			|
+		      1;
+	for (i = 0; i < 10; i++) {
+		debug("dec[%d] = 0x%x\n", i, regs->dec[i]);
+	}
+#endif
+}
+static unsigned int skip_caslat(unsigned int tckmin_ps,
+				unsigned int taamin_ps,
+				unsigned int mclk_ps,
+				unsigned int package_3ds)
+{
+	int i, j, k;
+	struct cas {
+		const unsigned int tckmin_ps;
+		const unsigned int caslat[4];
+	};
+	struct speed {
+		const struct cas *cl;
+		const unsigned int taamin_ps[4];
+	};
+	const struct cas cl_3200[] = {
+		{625,	{0xa00000, 0xb00000, 0xf000000,} },
+		{750,	{ 0x20000,  0x60000,  0xe00000,} },
+		{833,	{  0x8000,  0x18000,   0x38000,} },
+		{937,	{  0x4000,   0x4000,    0xc000,} },
+		{1071,	{  0x1000,   0x1000,    0x3000,} },
+		{1250,	{   0x400,    0x400,     0xc00,} },
+		{1500,	{       0,    0x600,     0x200,} },
+	};
+	const struct cas cl_2933[] = {
+		{682,	{       0,  0x80000, 0x180000, 0x380000} },
+		{750,	{ 0x20000,  0x60000,  0x60000,  0xe0000} },
+		{833,	{  0x8000,  0x18000,  0x18000,  0x38000} },
+		{937,	{  0x4000,   0x4000,   0x4000,   0xc000} },
+		{1071,	{  0x1000,   0x1000,   0x1000,   0x3000} },
+		{1250,	{   0x400,    0x400,    0x400,    0xc00} },
+		{1500,	{       0,    0x200,    0x200,    0x200} },
+	};
+	const struct cas cl_2666[] = {
+		{750,	{       0,  0x20000,  0x60000,  0xe0000} },
+		{833,	{  0x8000,  0x18000,  0x18000,  0x38000} },
+		{937,	{  0x4000,   0x4000,   0x4000,   0xc000} },
+		{1071,	{  0x1000,   0x1000,   0x1000,   0x3000} },
+		{1250,	{   0x400,    0x400,    0x400,    0xc00} },
+		{1500,	{       0,        0,    0x200,    0x200} },
+	};
+	const struct cas cl_2400[] = {
+		{833,	{       0,   0x8000,  0x18000,  0x38000} },
+		{937,	{  0xc000,   0x4000,   0x4000,   0xc000} },
+		{1071,	{  0x3000,   0x1000,   0x1000,   0x3000} },
+		{1250,	{   0xc00,    0x400,    0x400,    0xc00} },
+		{1500,	{       0,    0x400,    0x200,    0x200} },
+	};
+	const struct cas cl_2133[] = {
+		{937,	{       0,   0x4000,   0xc000,} },
+		{1071,	{  0x2000,        0,   0x2000,} },
+		{1250,	{   0x800,        0,    0x800,} },
+		{1500,	{       0,    0x400,    0x200,} },
+	};
+	const struct cas cl_1866[] = {
+		{1071,	{       0,   0x1000,   0x3000,} },
+		{1250,	{   0xc00,    0x400,    0xc00,} },
+		{1500,	{       0,    0x400,    0x200,} },
+	};
+	const struct cas cl_1600[] = {
+		{1250,	{       0,    0x400,    0xc00,} },
+		{1500,	{       0,    0x400,    0x200,} },
+	};
+	const struct speed bin_0[] = {
+		{cl_3200, {12500, 13750, 15000,} },
+		{cl_2933, {12960, 13640, 13750, 15000,} },
+		{cl_2666, {12750, 13500, 13750, 15000,} },
+		{cl_2400, {12500, 13320, 13750, 15000,} },
+		{cl_2133, {13130, 13500, 15000,} },
+		{cl_1866, {12850, 13500, 15000,} },
+		{cl_1600, {12500, 13500, 15000,} }
+	};
+	const struct cas cl_3200_3ds[] = {
+		{625,	{ 0xa000000, 0xb000000, 0xf000000,} },
+		{750,	{ 0xaa00000, 0xab00000, 0xef00000,} },
+		{833,	{ 0xaac0000, 0xaac0000, 0xebc0000,} },
+		{937,	{ 0xaab0000, 0xaab0000, 0xeaf0000,} },
+		{1071,	{ 0xaaa4000, 0xaaac000, 0xeaec000,} },
+		{1250,	{ 0xaaa0000, 0xaaa2000, 0xeaeb000,} },
+	};
+	const struct cas cl_2666_3ds[] = {
+		{750,	{ 0xa00000, 0xb00000, 0xf00000,} },
+		{833,	{ 0xac0000, 0xac0000, 0xbc0000,} },
+		{937,	{ 0xab0000, 0xab0000, 0xaf0000,} },
+		{1071,	{ 0xaa4000, 0xaac000, 0xaac000,} },
+		{1250,	{ 0xaa0000, 0xaaa000, 0xaaa000,} },
+	};
+	const struct cas cl_2400_3ds[] = {
+		{833,	{ 0xe00000, 0xe40000, 0xec0000, 0xb00000} },
+		{937,	{ 0xe00000, 0xe00000, 0xea0000, 0xae0000} },
+		{1071,	{ 0xe00000, 0xe04000, 0xeac000, 0xaec000} },
+		{1250,	{ 0xe00000, 0xe00000, 0xeaa000, 0xae2000} },
+	};
+	const struct cas cl_2133_3ds[] = {
+		{937,	{  0x90000,  0xb0000,  0xf0000,} },
+		{1071,	{  0x84000,  0xac000,  0xec000,} },
+		{1250,	{  0x80000,  0xa2000,  0xe2000,} },
+	};
+	const struct cas cl_1866_3ds[] = {
+		{1071,	{        0,   0x4000,   0xc000,} },
+		{1250,	{        0,   0x1000,   0x3000,} },
+	};
+	const struct cas cl_1600_3ds[] = {
+		{1250,	{        0,   0x1000,   0x3000,} },
+	};
+	const struct speed bin_3ds[] = {
+		{cl_3200_3ds, {15000, 16250, 17140,} },
+		{cl_2666_3ds, {15000, 16500, 17140,} },
+		{cl_2400_3ds, {15000, 15830, 16670, 17140} },
+		{cl_2133_3ds, {15950, 16880, 17140,} },
+		{cl_1866_3ds, {15000, 16070, 17140,} },
+		{cl_1600_3ds, {15000, 16250, 17500,} },
+	};
+	const struct speed *bin;
+	int size;
+	unsigned int taamin_max, tck_max;
+
+	if (taamin_ps > ((package_3ds != 0) ? 21500 : 18000)) {
+		ERROR("taamin_ps %u invalid\n", taamin_ps);
+		return 0;
+	}
+	if (package_3ds != 0) {
+		bin = bin_3ds;
+		size = ARRAY_SIZE(bin_3ds);
+		taamin_max = 1250;
+		tck_max = 1500;
+	} else {
+		bin = bin_0;
+		size = ARRAY_SIZE(bin_0);
+		taamin_max = 1500;
+		tck_max = 1600;
+	}
+	if (mclk_ps < 625 || mclk_ps > tck_max) {
+		ERROR("mclk %u invalid\n", mclk_ps);
+		return 0;
+	}
+
+	for (i = 0; i < size; i++) {
+		if (bin[i].cl[0].tckmin_ps >= tckmin_ps) {
+			break;
+		}
+	}
+	if (i >= size) {
+		ERROR("speed bin not found\n");
+		return 0;
+	}
+	if (bin[i].cl[0].tckmin_ps > tckmin_ps && i > 0) {
+		i--;
+	}
+
+	for (j = 0; j < 4; j++) {
+		if ((bin[i].taamin_ps[j] == 0) ||
+		    bin[i].taamin_ps[j] >= taamin_ps) {
+			break;
+		}
+	}
+
+	if (j >= 4) {
+		ERROR("taamin_ps out of range.\n");
+		return 0;
+	}
+
+	if ((bin[i].taamin_ps[j] == 0) ||
+	    (bin[i].taamin_ps[j] > taamin_ps && j > 0)) {
+		j--;
+	}
+
+	for (k = 0; bin[i].cl[k].tckmin_ps < mclk_ps &&
+		    bin[i].cl[k].tckmin_ps < taamin_max; k++)
+		;
+	if (bin[i].cl[k].tckmin_ps > mclk_ps && k > 0) {
+		k--;
+	}
+
+	debug("Skip CL mask for this speed 0x%x\n", bin[i].cl[k].caslat[j]);
+
+	return bin[i].cl[k].caslat[j];
+}
+
+int compute_ddrc(const unsigned long clk,
+		 const struct memctl_opt *popts,
+		 const struct ddr_conf *conf,
+		 struct ddr_cfg_regs *regs,
+		 const struct dimm_params *pdimm,
+		 unsigned int ip_rev)
+{
+	unsigned int cas_latency;
+	unsigned int caslat_skip;
+	unsigned int additive_latency;
+	const unsigned int mclk_ps = get_memory_clk_ps(clk);
+	int i;
+
+	zeromem(regs, sizeof(struct ddr_cfg_regs));
+
+	if (mclk_ps < pdimm->tckmin_x_ps) {
+		ERROR("DDR Clk: MCLK cycle is %u ps.\n", mclk_ps);
+		ERROR("DDR Clk is faster than DIMM can support.\n");
+	}
+
+	/* calculate cas latency, override first */
+	cas_latency = (popts->caslat_override != 0) ?
+			popts->caslat_override_value :
+			(pdimm->taa_ps + mclk_ps - 1) / mclk_ps;
+
+	/* skip unsupported caslat based on speed bin */
+	caslat_skip = skip_caslat(pdimm->tckmin_x_ps,
+				  pdimm->taa_ps,
+				  mclk_ps,
+				  pdimm->package_3ds);
+	debug("Skip caslat 0x%x\n", caslat_skip);
+
+	/* Check if DIMM supports the cas latency */
+	i = 24;
+	while (((pdimm->caslat_x & ~caslat_skip & (1 << cas_latency)) == 0) &&
+	       (i-- > 0)) {
+		cas_latency++;
+	}
+
+	if (i <= 0) {
+		ERROR("Failed to find a proper cas latency\n");
+		return -EINVAL;
+	}
+	/* Verify cas latency does not exceed 18ns for DDR4 */
+	if (cas_latency * mclk_ps > 18000) {
+		ERROR("cas latency is too large %d\n", cas_latency);
+		return -EINVAL;
+	}
+
+	additive_latency = (popts->addt_lat_override != 0) ?
+				popts->addt_lat_override_value : 0;
+
+	cal_ddr_csn_bnds(regs, popts, conf, pdimm);
+	cal_ddr_sdram_cfg(clk, regs, popts, pdimm, ip_rev);
+	cal_ddr_sdram_rcw(clk, regs, popts, pdimm);
+	cal_timing_cfg(clk, regs, popts, pdimm, conf, cas_latency,
+		       additive_latency);
+	cal_ddr_dq_mapping(regs, pdimm);
+
+	if (ip_rev >= 0x50500) {
+		cal_ddr_addr_dec(regs);
+	}
+
+	cal_ddr_sdram_mode(clk, regs, popts, conf, pdimm, cas_latency,
+			   additive_latency, ip_rev);
+	cal_ddr_eor(regs, popts);
+	cal_ddr_data_init(regs);
+	cal_ddr_sdram_interval(clk, regs, popts, pdimm);
+	cal_ddr_zq_cntl(regs);
+	cal_ddr_sr_cntr(regs, popts);
+
+	return 0;
+}
diff --git a/drivers/nxp/ddr/nxp-ddr/utility.c b/drivers/nxp/ddr/nxp-ddr/utility.c
new file mode 100644
index 0000000..d33ad77
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/utility.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <common/debug.h>
+#include <ddr.h>
+#include <immap.h>
+#include <lib/mmio.h>
+
+#define UL_5POW12	244140625UL
+#define ULL_2E12	2000000000000ULL
+#define UL_2POW13	(1UL << 13)
+#define ULL_8FS		0xFFFFFFFFULL
+
+#define do_div(n, base) ({				\
+	unsigned int __base = (base);			\
+	unsigned int __rem;				\
+	__rem = ((unsigned long long)(n)) % __base;	\
+	(n) = ((unsigned long long)(n)) / __base;	\
+	__rem;						\
+})
+
+#define CCN_HN_F_SAM_NODEID_MASK	0x7f
+#ifdef NXP_HAS_CCN504
+#define CCN_HN_F_SAM_NODEID_DDR0	0x4
+#define CCN_HN_F_SAM_NODEID_DDR1	0xe
+#elif defined(NXP_HAS_CCN508)
+#define CCN_HN_F_SAM_NODEID_DDR0	0x8
+#define CCN_HN_F_SAM_NODEID_DDR1	0x18
+#endif
+
+unsigned long get_ddr_freq(struct sysinfo *sys, int ctrl_num)
+{
+	if (sys->freq_ddr_pll0 == 0) {
+		get_clocks(sys);
+	}
+
+	switch (ctrl_num) {
+	case 0:
+		return sys->freq_ddr_pll0;
+	case 1:
+		return sys->freq_ddr_pll0;
+	case 2:
+		return sys->freq_ddr_pll1;
+	}
+
+	return 0;
+}
+
+unsigned int get_memory_clk_ps(const unsigned long data_rate)
+{
+	unsigned int result;
+	/* Round to nearest 10ps, being careful about 64-bit multiply/divide */
+	unsigned long long rem, mclk_ps = ULL_2E12;
+
+	/* Now perform the big divide, the result fits in 32-bits */
+	rem = do_div(mclk_ps, data_rate);
+	result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps;
+
+	return result;
+}
+
+unsigned int picos_to_mclk(unsigned long data_rate, unsigned int picos)
+{
+	unsigned long long clks, clks_rem;
+
+	/* Short circuit for zero picos */
+	if ((picos == 0U) || (data_rate == 0UL)) {
+		return 0U;
+	}
+
+	/* First multiply the time by the data rate (32x32 => 64) */
+	clks = picos * (unsigned long long)data_rate;
+	/*
+	 * Now divide by 5^12 and track the 32-bit remainder, then divide
+	 * by 2*(2^12) using shifts (and updating the remainder).
+	 */
+	clks_rem = do_div(clks, UL_5POW12);
+	clks_rem += (clks & (UL_2POW13-1)) * UL_5POW12;
+	clks >>= 13U;
+
+	/* If we had a remainder greater than the 1ps error, then round up */
+	if (clks_rem > data_rate) {
+		clks++;
+	}
+
+	/* Clamp to the maximum representable value */
+	if (clks > ULL_8FS) {
+		clks = ULL_8FS;
+	}
+	return (unsigned int) clks;
+}
+
+/* valid_spd_mask has been checked by parse_spd */
+int disable_unused_ddrc(struct ddr_info *priv,
+			int valid_spd_mask, uintptr_t nxp_ccn_hn_f0_addr)
+{
+#if defined(NXP_HAS_CCN504) || defined(NXP_HAS_CCN508)
+	void *hnf_sam_ctrl = (void *)(nxp_ccn_hn_f0_addr + CCN_HN_F_SAM_CTL);
+	uint32_t val, nodeid;
+#ifdef NXP_HAS_CCN504
+	uint32_t num_hnf_nodes = 4U;
+#else
+	uint32_t num_hnf_nodes = 8U;
+#endif
+	int disable_ddrc = 0;
+	int i;
+
+	if (priv->num_ctlrs < 2) {
+		debug("%s: nothing to do.\n", __func__);
+	}
+
+	switch (priv->dimm_on_ctlr) {
+	case 1:
+		disable_ddrc = ((valid_spd_mask &0x2) == 0) ? 2 : 0;
+		disable_ddrc = ((valid_spd_mask &0x1) == 0) ? 1 : disable_ddrc;
+		break;
+	case 2:
+		disable_ddrc = ((valid_spd_mask &0x4) == 0) ? 2 : 0;
+		disable_ddrc = ((valid_spd_mask &0x1) == 0) ? 1 : disable_ddrc;
+		break;
+	default:
+		ERROR("Invalid number of DIMMs %d\n", priv->dimm_on_ctlr);
+		return -EINVAL;
+	}
+
+	if (disable_ddrc != 0) {
+		debug("valid_spd_mask = 0x%x\n", valid_spd_mask);
+	}
+
+	switch (disable_ddrc) {
+	case 1:
+		priv->num_ctlrs = 1;
+		priv->spd_addr = &priv->spd_addr[priv->dimm_on_ctlr];
+		priv->ddr[0] = priv->ddr[1];
+		priv->ddr[1] = NULL;
+		priv->phy[0] = priv->phy[0];
+		priv->phy[1] = NULL;
+		debug("Disable first DDR controller\n");
+		break;
+	case 2:
+		priv->num_ctlrs = 1;
+		priv->ddr[1] = NULL;
+		priv->phy[1] = NULL;
+		debug("Disable second DDR controller\n");
+		/* fallthrough */
+	case 0:
+		break;
+	default:
+		ERROR("Program error.\n");
+		return -EINVAL;
+	}
+
+	if (disable_ddrc == 0) {
+		debug("Both controllers in use.\n");
+		return 0;
+	}
+
+	for (i = 0; i < num_hnf_nodes; i++) {
+		val = mmio_read_64((uintptr_t)hnf_sam_ctrl);
+		nodeid = disable_ddrc == 1 ? CCN_HN_F_SAM_NODEID_DDR1 :
+			 (disable_ddrc == 2 ? CCN_HN_F_SAM_NODEID_DDR0 :
+			  (i < 4 ? CCN_HN_F_SAM_NODEID_DDR0
+				 : CCN_HN_F_SAM_NODEID_DDR1));
+		if (nodeid != (val & CCN_HN_F_SAM_NODEID_MASK)) {
+			debug("Setting HN-F node %d\n", i);
+			debug("nodeid = 0x%x\n", nodeid);
+			val &= ~CCN_HN_F_SAM_NODEID_MASK;
+			val |= nodeid;
+			mmio_write_64((uintptr_t)hnf_sam_ctrl, val);
+		}
+		hnf_sam_ctrl += CCN_HN_F_REGION_SIZE;
+	}
+#endif
+	return 0;
+}
+
+unsigned int get_ddrc_version(const struct ccsr_ddr *ddr)
+{
+	unsigned int ver;
+
+	ver = (ddr_in32(&ddr->ip_rev1) & 0xFFFF) << 8U;
+	ver |= (ddr_in32(&ddr->ip_rev2) & 0xFF00) >> 8U;
+
+	return ver;
+}
+
+void print_ddr_info(struct ccsr_ddr *ddr)
+{
+	unsigned int cs0_config = ddr_in32(&ddr->csn_cfg[0]);
+	unsigned int sdram_cfg = ddr_in32(&ddr->sdram_cfg);
+	int cas_lat;
+
+	if ((sdram_cfg & SDRAM_CFG_MEM_EN) == 0U) {
+		printf(" (DDR not enabled)\n");
+		return;
+	}
+
+	printf("DDR");
+	switch ((sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >>
+		SDRAM_CFG_SDRAM_TYPE_SHIFT) {
+	case SDRAM_TYPE_DDR4:
+		printf("4");
+		break;
+	default:
+		printf("?");
+		break;
+	}
+
+	switch (sdram_cfg & SDRAM_CFG_DBW_MASK) {
+	case SDRAM_CFG_32_BW:
+		printf(", 32-bit");
+		break;
+	case SDRAM_CFG_16_BW:
+		printf(", 16-bit");
+		break;
+	case SDRAM_CFG_8_BW:
+		printf(", 8-bit");
+		break;
+	default:
+		printf(", 64-bit");
+		break;
+	}
+
+	/* Calculate CAS latency based on timing cfg values */
+	cas_lat = ((ddr_in32(&ddr->timing_cfg_1) >> 16) & 0xf);
+	cas_lat += 2;	/* for DDRC newer than 4.4 */
+	cas_lat += ((ddr_in32(&ddr->timing_cfg_3) >> 12) & 3) << 4;
+	printf(", CL=%d", cas_lat >> 1);
+	if ((cas_lat & 0x1) != 0) {
+		printf(".5");
+	}
+
+	if ((sdram_cfg & SDRAM_CFG_ECC_EN) != 0) {
+		printf(", ECC on");
+	} else {
+		printf(", ECC off");
+	}
+
+	if ((cs0_config & 0x20000000) != 0) {
+		printf(", ");
+		switch ((cs0_config >> 24) & 0xf) {
+		case DDR_256B_INTLV:
+			printf("256B");
+			break;
+		default:
+			printf("invalid");
+			break;
+		}
+	}
+
+	if (((sdram_cfg >> 8) & 0x7f) != 0) {
+		printf(", ");
+		switch (sdram_cfg >> 8 & 0x7f) {
+		case DDR_BA_INTLV_CS0123:
+			printf("CS0+CS1+CS2+CS3");
+			break;
+		case DDR_BA_INTLV_CS01:
+			printf("CS0+CS1");
+			break;
+		default:
+			printf("invalid");
+			break;
+		}
+	}
+	printf("\n");
+}