summaryrefslogtreecommitdiffstats
path: root/drivers/nxp/ddr/nxp-ddr
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 17:43:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 17:43:51 +0000
commitbe58c81aff4cd4c0ccf43dbd7998da4a6a08c03b (patch)
tree779c248fb61c83f65d1f0dc867f2053d76b4e03a /drivers/nxp/ddr/nxp-ddr
parentInitial commit. (diff)
downloadarm-trusted-firmware-be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b.tar.xz
arm-trusted-firmware-be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b.zip
Adding upstream version 2.10.0+dfsg.upstream/2.10.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/nxp/ddr/nxp-ddr')
-rw-r--r--drivers/nxp/ddr/nxp-ddr/README.odt31
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddr.c931
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddr.mk80
-rw-r--r--drivers/nxp/ddr/nxp-ddr/ddrc.c594
-rw-r--r--drivers/nxp/ddr/nxp-ddr/dimm.c399
-rw-r--r--drivers/nxp/ddr/nxp-ddr/regs.c1394
-rw-r--r--drivers/nxp/ddr/nxp-ddr/utility.c288
7 files changed, 3717 insertions, 0 deletions
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..17c2bbb
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.c
@@ -0,0 +1,931 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <inttypes.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;
+
+ static const 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 determining 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 %" PRIu64 " 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 %" PRIu64 " 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 %" PRIu64 " ms\n", time);
+ debug("Program controller registers\n");
+ ret = write_ddrc_regs(priv);
+ if (ret != 0) {
+ ERROR("Programming 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 %" PRIu64 " 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..f827a1b
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/ddr.mk
@@ -0,0 +1,80 @@
+#
+# Copyright 2021-2022 NXP
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+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
+
+ifeq (${ERRATA_DDR_A050958}, 1)
+$(eval $(call add_define,ERRATA_DDR_A050958))
+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 := $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/ddr.c \
+ $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/ddrc.c \
+ $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/dimm.c \
+ $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/regs.c \
+ $(PLAT_DRIVERS_PATH)/ddr/nxp-ddr/utility.c \
+ $(PLAT_DRIVERS_PATH)/ddr/$(PLAT_DDR_PHY_DIR)/phy.c
+
+PLAT_INCLUDES += -I$(PLAT_DRIVERS_INCLUDE_PATH)/ddr
diff --git a/drivers/nxp/ddr/nxp-ddr/ddrc.c b/drivers/nxp/ddr/nxp-ddr/ddrc.c
new file mode 100644
index 0000000..4133fac
--- /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 version 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..a82db6c
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/dimm.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2021-2022 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 & 0x9f;
+ 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 & 0x9f;
+ 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..26155ab
--- /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) && 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..b6dffc8
--- /dev/null
+++ b/drivers/nxp/ddr/nxp-ddr/utility.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2021-2022 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_0 0x3
+#define CCN_HN_F_SAM_NODEID_DDR0_1 0x8
+#define CCN_HN_F_SAM_NODEID_DDR1_0 0x13
+#define CCN_HN_F_SAM_NODEID_DDR1_1 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);
+#ifdef NXP_HAS_CCN504
+ nodeid = disable_ddrc == 1 ? CCN_HN_F_SAM_NODEID_DDR1 :
+ (disable_ddrc == 2 ? CCN_HN_F_SAM_NODEID_DDR0 :
+ 0x0); /*Failure condition. never hit */
+#elif defined(NXP_HAS_CCN508)
+ if (disable_ddrc == 1) {
+ nodeid = (i < 2 || i >= 6) ? CCN_HN_F_SAM_NODEID_DDR1_1 :
+ CCN_HN_F_SAM_NODEID_DDR1_0;
+ } else if (disable_ddrc == 2) {
+ nodeid = (i < 2 || i >= 6) ? CCN_HN_F_SAM_NODEID_DDR0_0 :
+ CCN_HN_F_SAM_NODEID_DDR0_1;
+ } else {
+ nodeid = 0; /* Failure condition. never hit */
+ }
+#endif
+ 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");
+}