// SPDX-License-Identifier: GPL-2.0-only
/*
 * 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;
}
