diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 17:43:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 17:43:51 +0000 |
commit | be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b (patch) | |
tree | 779c248fb61c83f65d1f0dc867f2053d76b4e03a /drivers/nxp/ddr/nxp-ddr | |
parent | Initial commit. (diff) | |
download | arm-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.odt | 31 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/ddr.c | 931 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/ddr.mk | 80 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/ddrc.c | 594 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/dimm.c | 399 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/regs.c | 1394 | ||||
-rw-r--r-- | drivers/nxp/ddr/nxp-ddr/utility.c | 288 |
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"); +} |