// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2008, 2010-2016 Freescale Semiconductor, Inc.
 * Copyright 2017-2018 NXP Semiconductor
 */

#include <common.h>
#include <soc/fsl/fsl_ddr_sdram.h>
#include "fsl_ddr.h"

struct dynamic_odt {
	unsigned int odt_rd_cfg;
	unsigned int odt_wr_cfg;
	unsigned int odt_rtt_norm;
	unsigned int odt_rtt_wr;
};

/* Quad rank is not verified yet due availability.
 * Replacing 20 OHM with 34 OHM since DDR4 doesn't have 20 OHM option
 */
static const struct dynamic_odt single_Q_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS_AND_OTHER_DIMM,
		DDR4_RTT_34_OHM,	/* unverified */
		DDR4_RTT_120_OHM
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR4_RTT_OFF,
		DDR4_RTT_120_OHM
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS_AND_OTHER_DIMM,
		DDR4_RTT_34_OHM,
		DDR4_RTT_120_OHM
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,	/* tied high */
		DDR4_RTT_OFF,
		DDR4_RTT_120_OHM
	}
};

static const struct dynamic_odt single_D_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR4_RTT_OFF,
		DDR4_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt single_S_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
};

static const struct dynamic_odt dual_DD_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR4_RTT_34_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR4_RTT_34_OHM,
		DDR4_RTT_OFF
	}
};

static const struct dynamic_odt dual_DS_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR4_RTT_34_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_34_OHM,
		DDR4_RTT_120_OHM
	},
	{0, 0, 0, 0}
};
static const struct dynamic_odt dual_SD_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_34_OHM,
		DDR4_RTT_120_OHM
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR4_RTT_34_OHM,
		DDR4_RTT_OFF
	}
};

static const struct dynamic_odt dual_SS_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_34_OHM,
		DDR4_RTT_120_OHM
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR4_RTT_34_OHM,
		DDR4_RTT_120_OHM
	},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_D0_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR4_RTT_OFF,
		DDR4_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_0D_ddr4[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR4_RTT_OFF,
		DDR4_RTT_OFF
	}
};

static const struct dynamic_odt dual_S0_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0}

};

static const struct dynamic_odt dual_0S_ddr4[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_40_OHM,
		DDR4_RTT_OFF
	},
	{0, 0, 0, 0}

};

static const struct dynamic_odt odt_unknown_ddr4[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR4_RTT_120_OHM,
		DDR4_RTT_OFF
	}
};

static const struct dynamic_odt single_Q_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS_AND_OTHER_DIMM,
		DDR3_RTT_20_OHM,
		DDR3_RTT_120_OHM
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,	/* tied high */
		DDR3_RTT_OFF,
		DDR3_RTT_120_OHM
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS_AND_OTHER_DIMM,
		DDR3_RTT_20_OHM,
		DDR3_RTT_120_OHM
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,	/* tied high */
		DDR3_RTT_OFF,
		DDR3_RTT_120_OHM
	}
};

static const struct dynamic_odt single_D_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR3_RTT_OFF,
		DDR3_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt single_S_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
};

static const struct dynamic_odt dual_DD_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR3_RTT_30_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR3_RTT_30_OHM,
		DDR3_RTT_OFF
	}
};

static const struct dynamic_odt dual_DS_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR3_RTT_30_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_20_OHM,
		DDR3_RTT_120_OHM
	},
	{0, 0, 0, 0}
};
static const struct dynamic_odt dual_SD_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_20_OHM,
		DDR3_RTT_120_OHM
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR3_RTT_20_OHM,
		DDR3_RTT_OFF
	}
};

static const struct dynamic_odt dual_SS_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_30_OHM,
		DDR3_RTT_120_OHM
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_ALL,
		DDR3_RTT_30_OHM,
		DDR3_RTT_120_OHM
	},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_D0_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR3_RTT_OFF,
		DDR3_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_0D_ddr3[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_SAME_DIMM,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR3_RTT_OFF,
		DDR3_RTT_OFF
	}
};

static const struct dynamic_odt dual_S0_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0}

};

static const struct dynamic_odt dual_0S_ddr3[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_40_OHM,
		DDR3_RTT_OFF
	},
	{0, 0, 0, 0}

};

static const struct dynamic_odt odt_unknown_ddr3[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR3_RTT_120_OHM,
		DDR3_RTT_OFF
	}
};

static const struct dynamic_odt single_Q_ddr12[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt single_D_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt single_S_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
};

static const struct dynamic_odt dual_DD_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	}
};

static const struct dynamic_odt dual_DS_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_SD_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	}
};

static const struct dynamic_odt dual_SS_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_OTHER_DIMM,
		FSL_DDR_ODT_OTHER_DIMM,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_D0_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0}
};

static const struct dynamic_odt dual_0D_ddr12[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_ALL,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	}
};

static const struct dynamic_odt dual_S0_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{0, 0, 0, 0}

};

static const struct dynamic_odt dual_0S_ddr12[4] = {
	{0, 0, 0, 0},
	{0, 0, 0, 0},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR2_RTT_150_OHM,
		DDR2_RTT_OFF
	},
	{0, 0, 0, 0}

};

static const struct dynamic_odt odt_unknown_ddr12[4] = {
	{	/* cs0 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs1 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	},
	{	/* cs2 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_CS,
		DDR2_RTT_75_OHM,
		DDR2_RTT_OFF
	},
	{	/* cs3 */
		FSL_DDR_ODT_NEVER,
		FSL_DDR_ODT_NEVER,
		DDR2_RTT_OFF,
		DDR2_RTT_OFF
	}
};

/*
 * Automatically seleect 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(struct fsl_ddr_controller *c,
					   struct dimm_params *pdimm)
{
	if (c->dimm_slots_per_ctrl == 1) {
		if (pdimm[0].n_ranks == 4)
			return FSL_DDR_CS0_CS1_CS2_CS3;
		else if (pdimm[0].n_ranks == 2)
			return FSL_DDR_CS0_CS1;
	}

	if (c->dimm_slots_per_ctrl == 2) {
		if (pdimm[0].n_ranks == 2) {
			if (pdimm[1].n_ranks == 2)
				return FSL_DDR_CS0_CS1_CS2_CS3;
			else
				return FSL_DDR_CS0_CS1;
		}
	}

	return 0;
}

unsigned int populate_memctl_options(struct fsl_ddr_controller *c)
{
	const struct common_timing_params *common_dimm = &c->common_timing_params;
	memctl_options_t *popts = &c->memctl_opts;
	struct dimm_params *pdimm = c->dimm_params;
	unsigned int i;
	const struct dynamic_odt *pdodt;
	const struct dynamic_odt *single_Q, *single_S, *single_D;
	const struct dynamic_odt *dual_DD, *dual_DS, *dual_0S;
	const struct dynamic_odt *dual_D0, *dual_SD, *dual_SS, *dual_S0, *dual_0D;

	if (is_ddr1(popts) || is_ddr2(popts)) {
		pdodt = odt_unknown_ddr12;
		single_Q = single_Q_ddr12;
		single_D = single_D_ddr12;
		single_S = single_S_ddr12;
		dual_DD = dual_DD_ddr12;
		dual_DS = dual_DS_ddr12;
		dual_0S = dual_0S_ddr12;
		dual_D0 = dual_D0_ddr12;
		dual_SD = dual_SD_ddr12;
		dual_SS = dual_SS_ddr12;
		dual_S0 = dual_S0_ddr12;
		dual_0D = dual_0D_ddr12;
	} else if (is_ddr3(popts)) {
		pdodt = odt_unknown_ddr3;
		single_Q = single_Q_ddr3;
		single_D = single_D_ddr3;
		single_S = single_S_ddr3;
		dual_DD = dual_DD_ddr3;
		dual_DS = dual_DS_ddr3;
		dual_0S = dual_0S_ddr3;
		dual_D0 = dual_D0_ddr3;
		dual_SD = dual_SD_ddr3;
		dual_SS = dual_SS_ddr3;
		dual_S0 = dual_S0_ddr3;
		dual_0D = dual_0D_ddr3;
	} else if (is_ddr4(popts)) {
		pdodt = odt_unknown_ddr4;
		single_Q = single_Q_ddr4;
		single_D = single_D_ddr4;
		single_S = single_S_ddr4;
		dual_DD = dual_DD_ddr4;
		dual_DS = dual_DS_ddr4;
		dual_0S = dual_0S_ddr4;
		dual_D0 = dual_D0_ddr4;
		dual_SD = dual_SD_ddr4;
		dual_SS = dual_SS_ddr4;
		dual_S0 = dual_S0_ddr4;
		dual_0D = dual_0D_ddr4;
	} else {
		return -EINVAL;
	}

	if (!is_ddr1(popts)) {
		/* Chip select options. */
		if (c->dimm_slots_per_ctrl == 1) {
			switch (pdimm[0].n_ranks) {
			case 1:
				pdodt = single_S;
				break;
			case 2:
				pdodt = single_D;
				break;
			case 4:
				pdodt = single_Q;
				break;
			}
		} else if (c->dimm_slots_per_ctrl == 2) {
			switch (pdimm[0].n_ranks) {
			case 4:
				pdodt = single_Q;
				if (pdimm[1].n_ranks)
					printf("Error: Quad- and Dual-rank DIMMs cannot be used together\n");
				break;
			case 2:
				switch (pdimm[1].n_ranks) {
				case 2:
					pdodt = dual_DD;
					break;
				case 1:
					pdodt = dual_DS;
					break;
				case 0:
					pdodt = dual_D0;
					break;
				}
				break;
			case 1:
				switch (pdimm[1].n_ranks) {
				case 2:
					pdodt = dual_SD;
					break;
				case 1:
					pdodt = dual_SS;
					break;
				case 0:
					pdodt = dual_S0;
					break;
				}
				break;
			case 0:
				switch (pdimm[1].n_ranks) {
				case 2:
					pdodt = dual_0D;
					break;
				case 1:
					pdodt = dual_0S;
					break;
				}
				break;
			}
		}
	}

	/* Pick chip-select local options. */
	for (i = 0; i < c->chip_selects_per_ctrl; i++) {
		if (is_ddr1(popts)) {
			popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
			popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS;
		} else {
			popts->cs_local_opts[i].odt_rd_cfg = pdodt[i].odt_rd_cfg;
			popts->cs_local_opts[i].odt_wr_cfg = pdodt[i].odt_wr_cfg;
			popts->cs_local_opts[i].odt_rtt_norm = pdodt[i].odt_rtt_norm;
			popts->cs_local_opts[i].odt_rtt_wr = pdodt[i].odt_rtt_wr;
		}
		popts->cs_local_opts[i].auto_precharge = 0;
	}

	/* Pick interleaving mode. */

	/*
	 * 0 = no interleaving
	 * 1 = interleaving between 2 controllers
	 */
	popts->memctl_interleaving = 0;

	/*
	 * 0 = cacheline
	 * 1 = page
	 * 2 = (logical) bank
	 * 3 = superbank (only if CS interleaving is enabled)
	 */
	popts->memctl_interleaving_mode = 0;

	/*
	 * 0: cacheline: bit 30 of the 36-bit physical addr selects the memctl
	 * 1: page:      bit to the left of the column bits selects the memctl
	 * 2: bank:      bit to the left of the bank bits selects the memctl
	 * 3: superbank: bit to the left of the chip select selects the memctl
	 *
	 * NOTE: ba_intlv (rank interleaving) is independent of memory
	 * controller interleaving; it is only within a memory controller.
	 * Must use superbank interleaving if rank interleaving is used and
	 * memory controller interleaving is enabled.
	 */

	/*
	 * 0 = no
	 * 0x40 = CS0,CS1
	 * 0x20 = CS2,CS3
	 * 0x60 = CS0,CS1 + CS2,CS3
	 * 0x04 = CS0,CS1,CS2,CS3
	 */
	popts->ba_intlv_ctl = 0;

	/* Memory Organization Parameters */
	popts->registered_dimm_en = common_dimm->all_dimms_registered;

	/*
	 * Choose DQS config
	 * 0 for DDR1
	 * 1 for DDR2
	 */
	if (is_ddr2(popts) || is_ddr3(popts))
		popts->dqs_config = 1;

	/* Choose self-refresh during sleep. */
	popts->self_refresh_in_sleep = 1;

	/* Choose dynamic power management mode. */
	popts->dynamic_power = 0;

	popts->x4_en = (pdimm[0].device_width == 4) ? 1 : 0;

	/* Choose ddr controller address mirror mode */
	if (is_ddr3_4(popts)) {
		if (pdimm[0].n_ranks != 0) {
			if (pdimm[0].primary_sdram_width == 64)
				popts->data_bus_width = 0;
			else if (pdimm[0].primary_sdram_width == 32)
				popts->data_bus_width = 1;
			else if (pdimm[0].primary_sdram_width == 16)
				popts->data_bus_width = 2;
			else {
				panic("Error: primary sdram width %u is invalid!\n",
					pdimm[0].primary_sdram_width);
			}
		}

		if ((popts->data_bus_width == 1) || (popts->data_bus_width == 2)) {
			/* 32-bit or 16-bit bus */
			popts->otf_burst_chop_en = 0;
			popts->burst_length = DDR_BL8;
		} else {
			popts->otf_burst_chop_en = 1;	/* on-the-fly burst chop */
			popts->burst_length = DDR_OTF;	/* on-the-fly BC4 and BL8 */
		}

		for (i = 0; i < c->dimm_slots_per_ctrl; i++) {
			if (pdimm[i].n_ranks) {
				popts->mirrored_dimm = pdimm[i].mirrored_dimm;
				break;
			}
		}
	} else {
		if (pdimm[0].n_ranks != 0) {
			if ((pdimm[0].data_width >= 64) && \
				(pdimm[0].data_width <= 72))
				popts->data_bus_width = 0;
			else if ((pdimm[0].data_width >= 32) && \
				(pdimm[0].data_width <= 40))
				popts->data_bus_width = 1;
			else {
				panic("Error: data width %u is invalid!\n",
					pdimm[0].data_width);
			}
		}

		popts->burst_length = DDR_BL4;	/* has to be 4 for DDR2 */
	}


	/* Global Timing Parameters. */
	debug("mclk_ps = %u ps\n", get_memory_clk_period_ps(c));

	/* Pick a caslat override. */
	popts->cas_latency_override = 0;
	popts->cas_latency_override_value = 3;
	if (popts->cas_latency_override) {
		debug("using caslat override value = %u\n",
		       popts->cas_latency_override_value);
	}

	/* Decide whether to use the computed derated latency */
	popts->use_derated_caslat = 0;

	/* Choose an additive latency. */
	popts->additive_latency_override = 0;
	popts->additive_latency_override_value = 3;
	if (popts->additive_latency_override) {
		debug("using additive latency override value = %u\n",
		       popts->additive_latency_override_value);
	}

	/*
	 * 2T_EN setting
	 *
	 * Factors to consider for 2T_EN:
	 *	- number of DIMMs installed
	 *	- number of components, number of active ranks
	 *	- how much time you want to spend playing around
	 */
	popts->twot_en = 0;
	popts->threet_en = 0;

	/* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */
	if (popts->registered_dimm_en)
		popts->ap_en = 1; /* 0 = disable,  1 = enable */
	else
		popts->ap_en = 0; /* disabled for DDR4 UDIMM/discrete default */

	/*
	 * BSTTOPRE precharge interval
	 *
	 * Set this to 0 for global auto precharge
	 * The value of 0x100 has been used for DDR1, DDR2, DDR3.
	 * It is not wrong. Any value should be OK. The performance depends on
	 * applications. There is no one good value for all. One way to set
	 * is to use 1/4 of refint value.
	 */
	popts->bstopre = picos_to_mclk(c, common_dimm->refresh_rate_ps)
			 >> 2;

	/*
	 * Window for four activates -- tFAW
	 *
	 * FIXME: UM: applies only to DDR2/DDR3 with eight logical banks only
	 * FIXME: varies depending upon number of column addresses or data
	 * FIXME: width, was considering looking at pdimm->primary_sdram_width
	 */
	if (is_ddr1(popts))
		popts->tfaw_window_four_activates_ps = mclk_to_picos(c, 1);
	else if (is_ddr2(popts))
		/*
		 * x4/x8;  some datasheets have 35000
		 * x16 wide columns only?  Use 50000?
		 */
		popts->tfaw_window_four_activates_ps = 37500;
	else
		popts->tfaw_window_four_activates_ps = pdimm[0].tfaw_ps;

	popts->zq_en = 0;
	popts->wrlvl_en = 0;

	if (is_ddr3_4(popts)) {
		/*
		 * due to ddr3 dimm is fly-by topology
		 * we suggest to enable write leveling to
		 * meet the tQDSS under different loading.
		 */
		popts->wrlvl_en = 1;
		popts->zq_en = 1;
		popts->wrlvl_override = 0;
	}

	if (pdimm[0].n_ranks == 4)
		popts->quad_rank_present = 1;

	popts->package_3ds = pdimm->package_3ds;

	if (!is_ddr4(popts)) {
		ulong ddr_freq = c->ddr_freq / 1000000;
		if (popts->registered_dimm_en) {
			popts->rcw_override = 1;
			popts->rcw_1 = 0x000a5a00;
			if (ddr_freq <= 800)
				popts->rcw_2 = 0x00000000;
			else if (ddr_freq <= 1066)
				popts->rcw_2 = 0x00100000;
			else if (ddr_freq <= 1333)
				popts->rcw_2 = 0x00200000;
			else
				popts->rcw_2 = 0x00300000;
		}
	}

	if (c->board_options)
		c->board_options(popts, pdimm, c);

	return 0;
}

void check_interleaving_options(struct fsl_ddr_info *pinfo)
{
	int i, j, k, check_n_ranks, intlv_invalid = 0;
	unsigned int check_intlv, check_n_row_addr, check_n_col_addr;
	unsigned long long check_rank_density;
	struct dimm_params *dimm;

	/*
	 * Check if all controllers are configured for memory
	 * controller interleaving. Identical dimms are recommended. At least
	 * the size, row and col address should be checked.
	 */
	j = 0;
	check_n_ranks = pinfo->c[0].dimm_params[0].n_ranks;
	check_rank_density = pinfo->c[0].dimm_params[0].rank_density;
	check_n_row_addr =  pinfo->c[0].dimm_params[0].n_row_addr;
	check_n_col_addr = pinfo->c[0].dimm_params[0].n_col_addr;
	check_intlv = pinfo->c[0].memctl_opts.memctl_interleaving_mode;
	for (i = 0; i < pinfo->num_ctrls; i++) {
		dimm = &pinfo->c[i].dimm_params[0];
		if (!pinfo->c[i].memctl_opts.memctl_interleaving) {
			continue;
		} else if (((check_rank_density != dimm->rank_density) ||
		     (check_n_ranks != dimm->n_ranks) ||
		     (check_n_row_addr != dimm->n_row_addr) ||
		     (check_n_col_addr != dimm->n_col_addr) ||
		     (check_intlv !=
			pinfo->c[i].memctl_opts.memctl_interleaving_mode))){
			intlv_invalid = 1;
			break;
		} else {
			j++;
		}

	}
	if (intlv_invalid) {
		for (i = 0; i < pinfo->num_ctrls; i++)
			pinfo->c[i].memctl_opts.memctl_interleaving = 0;
		printf("Not all DIMMs are identical. "
			"Memory controller interleaving disabled.\n");
	} else {
		switch (check_intlv) {
		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:
			if (pinfo->num_ctrls == 3)
				k = 2;
			else
				k = pinfo->num_ctrls;
			break;
		case FSL_DDR_3WAY_1KB_INTERLEAVING:
		case FSL_DDR_3WAY_4KB_INTERLEAVING:
		case FSL_DDR_3WAY_8KB_INTERLEAVING:
		case FSL_DDR_4WAY_1KB_INTERLEAVING:
		case FSL_DDR_4WAY_4KB_INTERLEAVING:
		case FSL_DDR_4WAY_8KB_INTERLEAVING:
		default:
			k = pinfo->num_ctrls;
			break;
		}
		debug("%d of %d controllers are interleaving.\n", j, k);
		if (j && (j != k)) {
			for (i = 0; i < pinfo->num_ctrls; i++)
				pinfo->c[i].memctl_opts.memctl_interleaving = 0;
			if (pinfo->num_ctrls > 1)
				printf("Not all controllers have compatible interleaving mode. All disabled.\n");
		}
	}
	debug("Checking interleaving options completed\n");
}
