diff options
Diffstat (limited to 'drivers/ddr/fsl/main.c')
-rw-r--r-- | drivers/ddr/fsl/main.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c new file mode 100644 index 0000000000..b0df34c933 --- /dev/null +++ b/drivers/ddr/fsl/main.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ +#include <common.h> +#include <soc/fsl/fsl_ddr_sdram.h> +#include <linux/log2.h> +#include "fsl_ddr.h" + +/* + * ASSUMPTIONS: + * - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller + * - Same memory data bus width on all controllers + * + * NOTES: + * + * The memory controller and associated documentation use confusing + * terminology when referring to the orgranization of DRAM. + * + * Here is a terminology translation table: + * + * memory controller/documention |industry |this code |signals + * -------------------------------|-----------|-----------|----------------- + * physical bank/bank |rank |rank |chip select (CS) + * logical bank/sub-bank |bank |bank |bank address (BA) + * page/row |row |page |row address + * ??? |column |column |column address + * + * The naming confusion is further exacerbated by the descriptions of the + * memory controller interleaving feature, where accesses are interleaved + * _BETWEEN_ two seperate memory controllers. This is configured only in + * CS0_CONFIG[INTLV_CTL] of each memory controller. + * + * memory controller documentation | number of chip selects + * | per memory controller supported + * --------------------------------|----------------------------------------- + * cache line interleaving | 1 (CS0 only) + * page interleaving | 1 (CS0 only) + * bank interleaving | 1 (CS0 only) + * superbank interleraving | depends on bank (chip select) + * | interleraving [rank interleaving] + * | mode used on every memory controller + * + * Even further confusing is the existence of the interleaving feature + * _WITHIN_ each memory controller. The feature is referred to in + * documentation as chip select interleaving or bank interleaving, + * although it is configured in the DDR_SDRAM_CFG field. + * + * Name of field | documentation name | this code + * -----------------------------|-----------------------|------------------ + * DDR_SDRAM_CFG[BA_INTLV_CTL] | Bank (chip select) | rank interleaving + * | interleaving + */ + +static unsigned long long step_assign_addresses_linear(struct fsl_ddr_info *pinfo, + unsigned long long current_mem_base) +{ + int i, j; + unsigned long long total_mem = 0; + + /* + * Simple linear assignment if memory + * controllers are not interleaved. + */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + unsigned long long total_ctlr_mem = 0; + + c->common_timing_params.base_address = current_mem_base; + + for (j = 0; j < c->dimm_slots_per_ctrl; j++) { + /* Compute DIMM base addresses. */ + unsigned long long cap = c->dimm_params[j].capacity >> + pinfo->c[i].dbw_capacity_adjust; + + c->dimm_params[j].base_address = current_mem_base; + debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base); + current_mem_base += cap; + total_ctlr_mem += cap; + } + debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem); + c->common_timing_params.total_mem = total_ctlr_mem; + total_mem += total_ctlr_mem; + } + + return total_mem; +} + +static unsigned long long step_assign_addresses_interleaved(struct fsl_ddr_info *pinfo, + unsigned long long current_mem_base) +{ + unsigned long long total_mem, total_ctlr_mem; + unsigned long long rank_density, ctlr_density = 0; + int i; + + rank_density = pinfo->c[0].dimm_params[0].rank_density >> + pinfo->c[0].dbw_capacity_adjust; + + switch (pinfo->c[0].memctl_opts.ba_intlv_ctl & + FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + ctlr_density = 4 * rank_density; + break; + case FSL_DDR_CS0_CS1: + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + ctlr_density = 2 * rank_density; + break; + case FSL_DDR_CS2_CS3: + default: + ctlr_density = rank_density; + break; + } + + debug("rank density is 0x%llx, ctlr density is 0x%llx\n", + rank_density, ctlr_density); + + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + if (c->memctl_opts.memctl_interleaving) { + switch (c->memctl_opts.memctl_interleaving_mode) { + case FSL_DDR_256B_INTERLEAVING: + case FSL_DDR_CACHE_LINE_INTERLEAVING: + case FSL_DDR_PAGE_INTERLEAVING: + case FSL_DDR_BANK_INTERLEAVING: + case FSL_DDR_SUPERBANK_INTERLEAVING: + total_ctlr_mem = 2 * ctlr_density; + break; + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + total_ctlr_mem = 3 * ctlr_density; + break; + case FSL_DDR_4WAY_1KB_INTERLEAVING: + case FSL_DDR_4WAY_4KB_INTERLEAVING: + case FSL_DDR_4WAY_8KB_INTERLEAVING: + total_ctlr_mem = 4 * ctlr_density; + break; + default: + panic("Unknown interleaving mode"); + } + c->common_timing_params.base_address = current_mem_base; + c->common_timing_params.total_mem = total_ctlr_mem; + total_mem = current_mem_base + total_ctlr_mem; + debug("ctrl %d base 0x%llx\n", i, current_mem_base); + debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem); + } else { + total_mem += step_assign_addresses_linear(pinfo, current_mem_base); + } + } + + return total_mem; +} + +static unsigned long long step_assign_addresses(struct fsl_ddr_info *pinfo) +{ + unsigned int i, j; + unsigned long long total_mem; + + /* + * If a reduced data width is requested, but the SPD + * specifies a physically wider device, adjust the + * computed dimm capacities accordingly before + * assigning addresses. + */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + unsigned int found = 0; + + switch (c->memctl_opts.data_bus_width) { + case 2: + /* 16-bit */ + for (j = 0; j < c->dimm_slots_per_ctrl; j++) { + unsigned int dw; + if (!c->dimm_params[j].n_ranks) + continue; + dw = c->dimm_params[j].primary_sdram_width; + if ((dw == 72 || dw == 64)) { + pinfo->c[i].dbw_capacity_adjust = 2; + break; + } else if ((dw == 40 || dw == 32)) { + pinfo->c[i].dbw_capacity_adjust = 1; + break; + } + } + break; + + case 1: + /* 32-bit */ + for (j = 0; j < c->dimm_slots_per_ctrl; j++) { + unsigned int dw; + dw = c->dimm_params[j].data_width; + if (c->dimm_params[j].n_ranks + && (dw == 72 || dw == 64)) { + /* + * FIXME: can't really do it + * like this because this just + * further reduces the memory + */ + found = 1; + break; + } + } + if (found) + pinfo->c[i].dbw_capacity_adjust = 1; + + break; + + case 0: + /* 64-bit */ + break; + + default: + printf("unexpected data bus width " + "specified controller %u\n", i); + return 1; + } + debug("dbw_cap_adj[%d]=%d\n", i, pinfo->c[i].dbw_capacity_adjust); + } + + if (pinfo->c[0].memctl_opts.memctl_interleaving) + total_mem = step_assign_addresses_interleaved(pinfo, pinfo->mem_base); + else + total_mem = step_assign_addresses_linear(pinfo, pinfo->mem_base); + + debug("Total mem by %s is 0x%llx\n", __func__, total_mem); + + return total_mem; +} + +static int compute_dimm_parameters(struct fsl_ddr_controller *c, + struct spd_eeprom *spd, + struct dimm_params *pdimm) +{ + const memctl_options_t *popts = &c->memctl_opts; + int ret = -EINVAL; + + memset(pdimm, 0, sizeof(*pdimm)); + + if (is_ddr1(popts)) + ret = ddr1_compute_dimm_parameters(c, (void *)spd, pdimm); + else if (is_ddr2(popts)) + ret = ddr2_compute_dimm_parameters(c, (void *)spd, pdimm); + else if (is_ddr3(popts)) + ret = ddr3_compute_dimm_parameters(c, (void *)spd, pdimm); + else if (is_ddr4(popts)) + ret = ddr4_compute_dimm_parameters(c, (void *)spd, pdimm); + + return ret; +} + +static unsigned long long fsl_ddr_compute(struct fsl_ddr_info *pinfo) +{ + unsigned int i, j; + unsigned long long total_mem = 0; + int assert_reset = 0; + int retval; + unsigned int max_end = 0; + + /* STEP 2: Compute DIMM parameters from SPD data */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + if (!c->spd_installed_dimms) + continue; + + for (j = 0; j < c->dimm_slots_per_ctrl; j++) { + struct spd_eeprom *spd = &c->spd_installed_dimms[j]; + struct dimm_params *pdimm = &c->dimm_params[j]; + + retval = compute_dimm_parameters(c, spd, pdimm); + if (retval == 2) { + printf("Error: compute_dimm_parameters" + " non-zero returned FATAL value " + "for memctl=%u dimm=%u\n", i, j); + return 0; + } + if (retval) { + debug("Warning: compute_dimm_parameters" + " non-zero return value for memctl=%u " + "dimm=%u\n", i, j); + } + } + } + + /* + * STEP 3: Compute a common set of timing parameters + * suitable for all of the DIMMs on each memory controller + */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + debug("Computing lowest common DIMM parameters for memctl=%u\n", + i); + compute_lowest_common_dimm_parameters(c); + } + + /* STEP 4: Gather configuration requirements from user */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + debug("Reloading memory controller " + "configuration options for memctl=%u\n", i); + /* + * This "reloads" the memory controller options + * to defaults. If the user "edits" an option, + * next_step points to the step after this, + * which is currently STEP_ASSIGN_ADDRESSES. + */ + populate_memctl_options(c); + /* + * For RDIMMs, JEDEC spec requires clocks to be stable + * before reset signal is deasserted. For the boards + * using fixed parameters, this function should be + * be called from board init file. + */ + if (c->common_timing_params.all_dimms_registered) + assert_reset = 1; + } + + /* STEP 5: Assign addresses to chip selects */ + check_interleaving_options(pinfo); + total_mem = step_assign_addresses(pinfo); + debug("Total mem %llu assigned\n", total_mem); + + /* STEP 6: compute controller register values */ + debug("FSL Memory ctrl register computation\n"); + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + if (c->common_timing_params.ndimms_present == 0) { + memset(&c->fsl_ddr_config_reg, 0, + sizeof(fsl_ddr_cfg_regs_t)); + continue; + } + + compute_fsl_memctl_config_regs(c); + } + + /* + * Compute the amount of memory available just by + * looking for the highest valid CSn_BNDS value. + * This allows us to also experiment with using + * only CS0 when using dual-rank DIMMs. + */ + + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + for (j = 0; j < c->chip_selects_per_ctrl; j++) { + fsl_ddr_cfg_regs_t *reg = &c->fsl_ddr_config_reg; + if (reg->cs[j].config & 0x80000000) { + unsigned int end; + /* + * 0xfffffff is a special value we put + * for unused bnds + */ + if (reg->cs[j].bnds == 0xffffffff) + continue; + end = reg->cs[j].bnds & 0xffff; + if (end > max_end) { + max_end = end; + } + } + } + } + + total_mem = 1 + (((unsigned long long)max_end << 24ULL) | + 0xFFFFFFULL) - pinfo->mem_base; + + return total_mem; +} + +phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo) +{ + unsigned int i; + unsigned long long total_memory; + int deassert_reset = 0; + + total_memory = fsl_ddr_compute(pinfo); + + /* setup 3-way interleaving before enabling DDRC */ + switch (pinfo->c[0].memctl_opts.memctl_interleaving_mode) { + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + fsl_ddr_set_intl3r( + pinfo->c[0].memctl_opts. + memctl_interleaving_mode); + break; + default: + break; + } + + /* + * Program configuration registers. + * JEDEC specs requires clocks to be stable before deasserting reset + * for RDIMMs. Clocks start after chip select is enabled and clock + * control register is set. During step 1, all controllers have their + * registers set but not enabled. Step 2 proceeds after deasserting + * reset through board FPGA or GPIO. + * For non-registered DIMMs, initialization can go through but it is + * also OK to follow the same flow. + */ + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + if (c->common_timing_params.all_dimms_registered) + deassert_reset = 1; + } + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + debug("Programming controller %u\n", i); + if (c->common_timing_params.ndimms_present == 0) { + debug("No dimms present on controller %u; " + "skipping programming\n", i); + continue; + } + /* + * The following call with step = 1 returns before enabling + * the controller. It has to finish with step = 2 later. + */ + fsl_ddr_set_memctl_regs(c, deassert_reset ? 1 : 0); + } + if (deassert_reset) { + for (i = 0; i < pinfo->num_ctrls; i++) { + struct fsl_ddr_controller *c = &pinfo->c[i]; + + /* Call with step = 2 to continue initialization */ + fsl_ddr_set_memctl_regs(c, 2); + } + } + + debug("total_memory by %s = %llu\n", __func__, total_memory); + + return total_memory; +} |