driver/ddr: Restruct driver to allow standalone memory space

U-boot has been initializing DDR for the main memory. The presumption
is the memory stays as a big continuous block, either linear or
interleaved. This change is to support putting some DDR controllers
to separated space without counting into main memory. The standalone
memory controller could use different number of DIMM slots.

Signed-off-by: York Sun <yorksun@freescale.com>
diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c
index 5e001fc..b43b669 100644
--- a/drivers/ddr/fsl/main.c
+++ b/drivers/ddr/fsl/main.c
@@ -135,7 +135,7 @@
 void get_spd(generic_spd_eeprom_t *spd, u8 i2c_address);
 
 void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
-		      unsigned int ctrl_num)
+		      unsigned int ctrl_num, unsigned int dimm_slots_per_ctrl)
 {
 	unsigned int i;
 	unsigned int i2c_address = 0;
@@ -145,14 +145,14 @@
 		return;
 	}
 
-	for (i = 0; i < CONFIG_DIMM_SLOTS_PER_CTLR; i++) {
+	for (i = 0; i < dimm_slots_per_ctrl; i++) {
 		i2c_address = spd_i2c_addr[ctrl_num][i];
 		get_spd(&(ctrl_dimms_spd[i]), i2c_address);
 	}
 }
 #else
 void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
-		      unsigned int ctrl_num)
+		      unsigned int ctrl_num, unsigned int dimm_slots_per_ctrl)
 {
 }
 #endif /* SPD_EEPROM_ADDRESSx */
@@ -231,9 +231,11 @@
 static unsigned long long __step_assign_addresses(fsl_ddr_info_t *pinfo,
 			  unsigned int dbw_cap_adj[])
 {
-	int i, j;
+	unsigned int i, j;
 	unsigned long long total_mem, current_mem_base, total_ctlr_mem;
 	unsigned long long rank_density, ctlr_density = 0;
+	unsigned int first_ctrl = pinfo->first_ctrl;
+	unsigned int last_ctrl = first_ctrl + pinfo->num_ctrls - 1;
 
 	/*
 	 * If a reduced data width is requested, but the SPD
@@ -241,7 +243,7 @@
 	 * computed dimm capacities accordingly before
 	 * assigning addresses.
 	 */
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+	for (i = first_ctrl; i <= last_ctrl; i++) {
 		unsigned int found = 0;
 
 		switch (pinfo->memctl_opts[i].data_bus_width) {
@@ -295,12 +297,12 @@
 		debug("dbw_cap_adj[%d]=%d\n", i, dbw_cap_adj[i]);
 	}
 
-	current_mem_base = CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY;
+	current_mem_base = pinfo->mem_base;
 	total_mem = 0;
-	if (pinfo->memctl_opts[0].memctl_interleaving) {
-		rank_density = pinfo->dimm_params[0][0].rank_density >>
-					dbw_cap_adj[0];
-		switch (pinfo->memctl_opts[0].ba_intlv_ctl &
+	if (pinfo->memctl_opts[first_ctrl].memctl_interleaving) {
+		rank_density = pinfo->dimm_params[first_ctrl][0].rank_density >>
+					dbw_cap_adj[first_ctrl];
+		switch (pinfo->memctl_opts[first_ctrl].ba_intlv_ctl &
 					FSL_DDR_CS0_CS1_CS2_CS3) {
 		case FSL_DDR_CS0_CS1_CS2_CS3:
 			ctlr_density = 4 * rank_density;
@@ -316,7 +318,7 @@
 		}
 		debug("rank density is 0x%llx, ctlr density is 0x%llx\n",
 			rank_density, ctlr_density);
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			if (pinfo->memctl_opts[i].memctl_interleaving) {
 				switch (pinfo->memctl_opts[i].memctl_interleaving_mode) {
 				case FSL_DDR_256B_INTERLEAVING:
@@ -372,7 +374,7 @@
 		 * Simple linear assignment if memory
 		 * controllers are not interleaved.
 		 */
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			total_ctlr_mem = 0;
 			pinfo->common_timing_params[i].base_address =
 						current_mem_base;
@@ -408,18 +410,23 @@
 {
 	unsigned int i, j;
 	unsigned long long total_mem = 0;
-	int assert_reset;
+	int assert_reset = 0;
+	unsigned int first_ctrl =  pinfo->first_ctrl;
+	unsigned int last_ctrl = first_ctrl + pinfo->num_ctrls - 1;
+	__maybe_unused int retval;
+	__maybe_unused bool goodspd = false;
+	__maybe_unused int dimm_slots_per_ctrl = pinfo->dimm_slots_per_ctrl;
 
 	fsl_ddr_cfg_regs_t *ddr_reg = pinfo->fsl_ddr_config_reg;
 	common_timing_params_t *timing_params = pinfo->common_timing_params;
-	assert_reset = board_need_mem_reset();
+	if (pinfo->board_need_mem_reset)
+		assert_reset = pinfo->board_need_mem_reset();
 
 	/* data bus width capacity adjust shift amount */
 	unsigned int dbw_capacity_adjust[CONFIG_NUM_DDR_CONTROLLERS];
 
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+	for (i = first_ctrl; i <= last_ctrl; i++)
 		dbw_capacity_adjust[i] = 0;
-	}
 
 	debug("starting at step %u (%s)\n",
 	      start_step, step_to_string(start_step));
@@ -428,28 +435,28 @@
 	case STEP_GET_SPD:
 #if defined(CONFIG_DDR_SPD) || defined(CONFIG_SPD_EEPROM)
 		/* STEP 1:  Gather all DIMM SPD data */
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-			fsl_ddr_get_spd(pinfo->spd_installed_dimms[i], i);
+		for (i = first_ctrl; i <= last_ctrl; i++) {
+			fsl_ddr_get_spd(pinfo->spd_installed_dimms[i], i,
+					dimm_slots_per_ctrl);
 		}
 
 	case STEP_COMPUTE_DIMM_PARMS:
 		/* STEP 2:  Compute DIMM parameters from SPD data */
 
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
-				unsigned int retval;
 				generic_spd_eeprom_t *spd =
 					&(pinfo->spd_installed_dimms[i][j]);
 				dimm_params_t *pdimm =
 					&(pinfo->dimm_params[i][j]);
-
 				retval = compute_dimm_parameters(spd, pdimm, i);
 #ifdef CONFIG_SYS_DDR_RAW_TIMING
 				if (!i && !j && retval) {
 					printf("SPD error on controller %d! "
 					"Trying fallback to raw timing "
 					"calculation\n", i);
-					fsl_ddr_get_dimm_params(pdimm, i, j);
+					retval = fsl_ddr_get_dimm_params(pdimm,
+									 i, j);
 				}
 #else
 				if (retval == 2) {
@@ -463,13 +470,26 @@
 					debug("Warning: compute_dimm_parameters"
 					" non-zero return value for memctl=%u "
 					"dimm=%u\n", i, j);
+				} else {
+					goodspd = true;
 				}
 			}
 		}
+		if (!goodspd) {
+			/*
+			 * No valid SPD found
+			 * Throw an error if this is for main memory, i.e.
+			 * first_ctrl == 0. Otherwise, siliently return 0
+			 * as the memory size.
+			 */
+			if (first_ctrl == 0)
+				printf("Error: No valid SPD detected.\n");
 
+			return 0;
+		}
 #elif defined(CONFIG_SYS_DDR_RAW_TIMING)
 	case STEP_COMPUTE_DIMM_PARMS:
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
 				dimm_params_t *pdimm =
 					&(pinfo->dimm_params[i][j]);
@@ -483,7 +503,7 @@
 		 * STEP 3: Compute a common set of timing parameters
 		 * suitable for all of the DIMMs on each memory controller
 		 */
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			debug("Computing lowest common DIMM"
 				" parameters for memctl=%u\n", i);
 			compute_lowest_common_dimm_parameters(
@@ -494,7 +514,7 @@
 
 	case STEP_GATHER_OPTS:
 		/* STEP 4:  Gather configuration requirements from user */
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			debug("Reloading memory controller "
 				"configuration options for memctl=%u\n", i);
 			/*
@@ -516,9 +536,13 @@
 			if (timing_params[i].all_dimms_registered)
 				assert_reset = 1;
 		}
-		if (assert_reset) {
-			debug("Asserting mem reset\n");
-			board_assert_mem_reset();
+		if (assert_reset && !size_only) {
+			if (pinfo->board_mem_reset) {
+				debug("Asserting mem reset\n");
+				pinfo->board_mem_reset();
+			} else {
+				debug("Asserting mem reset missing\n");
+			}
 		}
 
 	case STEP_ASSIGN_ADDRESSES:
@@ -530,7 +554,7 @@
 	case STEP_COMPUTE_REGS:
 		/* STEP 6:  compute controller register values */
 		debug("FSL Memory ctrl register computation\n");
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			if (timing_params[i].ndimms_present == 0) {
 				memset(&ddr_reg[i], 0,
 					sizeof(fsl_ddr_cfg_regs_t));
@@ -558,7 +582,7 @@
 		 */
 		unsigned int max_end = 0;
 
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			for (j = 0; j < CONFIG_CHIP_SELECTS_PER_CTRL; j++) {
 				fsl_ddr_cfg_regs_t *reg = &ddr_reg[i];
 				if (reg->cs[j].config & 0x80000000) {
@@ -578,53 +602,45 @@
 		}
 
 		total_mem = 1 + (((unsigned long long)max_end << 24ULL) |
-			    0xFFFFFFULL) - CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY;
+			    0xFFFFFFULL) - pinfo->mem_base;
 	}
 
 	return total_mem;
 }
 
-/*
- * fsl_ddr_sdram() -- this is the main function to be called by
- *	initdram() in the board file.
- *
- * It returns amount of memory configured in bytes.
- */
-phys_size_t fsl_ddr_sdram(void)
+phys_size_t __fsl_ddr_sdram(fsl_ddr_info_t *pinfo)
 {
-	unsigned int i;
+	unsigned int i, first_ctrl, last_ctrl;
 #ifdef CONFIG_PPC
 	unsigned int law_memctl = LAW_TRGT_IF_DDR_1;
 #endif
 	unsigned long long total_memory;
-	fsl_ddr_info_t info;
-	int deassert_reset;
+	int deassert_reset = 0;
 
-	/* Reset info structure. */
-	memset(&info, 0, sizeof(fsl_ddr_info_t));
+	first_ctrl = pinfo->first_ctrl;
+	last_ctrl = first_ctrl + pinfo->num_ctrls - 1;
 
 	/* Compute it once normally. */
 #ifdef CONFIG_FSL_DDR_INTERACTIVE
 	if (tstc() && (getc() == 'd')) {	/* we got a key press of 'd' */
-		total_memory = fsl_ddr_interactive(&info, 0);
+		total_memory = fsl_ddr_interactive(pinfo, 0);
 	} else if (fsl_ddr_interactive_env_var_exists()) {
-		total_memory = fsl_ddr_interactive(&info, 1);
+		total_memory = fsl_ddr_interactive(pinfo, 1);
 	} else
 #endif
-		total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0);
+		total_memory = fsl_ddr_compute(pinfo, STEP_GET_SPD, 0);
 
 	/* setup 3-way interleaving before enabling DDRC */
-	if (info.memctl_opts[0].memctl_interleaving) {
-		switch (info.memctl_opts[0].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(
-				info.memctl_opts[0].memctl_interleaving_mode);
-			break;
-		default:
-			break;
-		}
+	switch (pinfo->memctl_opts[first_ctrl].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->memctl_opts[first_ctrl].
+			memctl_interleaving_mode);
+		break;
+	default:
+		break;
 	}
 
 	/*
@@ -637,14 +653,15 @@
 	 * For non-registered DIMMs, initialization can go through but it is
 	 * also OK to follow the same flow.
 	 */
-	deassert_reset = board_need_mem_reset();
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-		if (info.common_timing_params[i].all_dimms_registered)
+	if (pinfo->board_need_mem_reset)
+		deassert_reset = pinfo->board_need_mem_reset();
+	for (i = first_ctrl; i <= last_ctrl; i++) {
+		if (pinfo->common_timing_params[i].all_dimms_registered)
 			deassert_reset = 1;
 	}
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+	for (i = first_ctrl; i <= last_ctrl; i++) {
 		debug("Programming controller %u\n", i);
-		if (info.common_timing_params[i].ndimms_present == 0) {
+		if (pinfo->common_timing_params[i].ndimms_present == 0) {
 			debug("No dimms present on controller %u; "
 					"skipping programming\n", i);
 			continue;
@@ -653,45 +670,58 @@
 		 * The following call with step = 1 returns before enabling
 		 * the controller. It has to finish with step = 2 later.
 		 */
-		fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), i,
+		fsl_ddr_set_memctl_regs(&(pinfo->fsl_ddr_config_reg[i]), i,
 					deassert_reset ? 1 : 0);
 	}
 	if (deassert_reset) {
 		/* Use board FPGA or GPIO to deassert reset signal */
-		debug("Deasserting mem reset\n");
-		board_deassert_mem_reset();
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+		if (pinfo->board_mem_de_reset) {
+			debug("Deasserting mem reset\n");
+			pinfo->board_mem_de_reset();
+		} else {
+			debug("Deasserting mem reset missing\n");
+		}
+		for (i = first_ctrl; i <= last_ctrl; i++) {
 			/* Call with step = 2 to continue initialization */
-			fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]),
+			fsl_ddr_set_memctl_regs(&(pinfo->fsl_ddr_config_reg[i]),
 						i, 2);
 		}
 	}
 
 #ifdef CONFIG_PPC
 	/* program LAWs */
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-		if (info.memctl_opts[i].memctl_interleaving) {
-			switch (info.memctl_opts[i].memctl_interleaving_mode) {
+	for (i = first_ctrl; i <= last_ctrl; i++) {
+		if (pinfo->memctl_opts[i].memctl_interleaving) {
+			switch (pinfo->memctl_opts[i].
+				memctl_interleaving_mode) {
 			case FSL_DDR_CACHE_LINE_INTERLEAVING:
 			case FSL_DDR_PAGE_INTERLEAVING:
 			case FSL_DDR_BANK_INTERLEAVING:
 			case FSL_DDR_SUPERBANK_INTERLEAVING:
+				if (i % 2)
+					break;
 				if (i == 0) {
 					law_memctl = LAW_TRGT_IF_DDR_INTRLV;
-					fsl_ddr_set_lawbar(&info.common_timing_params[i],
-						law_memctl, i);
-				} else if (i == 2) {
-					law_memctl = LAW_TRGT_IF_DDR_INTLV_34;
-					fsl_ddr_set_lawbar(&info.common_timing_params[i],
+					fsl_ddr_set_lawbar(
+						&pinfo->common_timing_params[i],
 						law_memctl, i);
 				}
+#if CONFIG_NUM_DDR_CONTROLLERS > 3
+				else if (i == 2) {
+					law_memctl = LAW_TRGT_IF_DDR_INTLV_34;
+					fsl_ddr_set_lawbar(
+						&pinfo->common_timing_params[i],
+						law_memctl, i);
+				}
+#endif
 				break;
 			case FSL_DDR_3WAY_1KB_INTERLEAVING:
 			case FSL_DDR_3WAY_4KB_INTERLEAVING:
 			case FSL_DDR_3WAY_8KB_INTERLEAVING:
 				law_memctl = LAW_TRGT_IF_DDR_INTLV_123;
 				if (i == 0) {
-					fsl_ddr_set_lawbar(&info.common_timing_params[i],
+					fsl_ddr_set_lawbar(
+						&pinfo->common_timing_params[i],
 						law_memctl, i);
 				}
 				break;
@@ -700,7 +730,8 @@
 			case FSL_DDR_4WAY_8KB_INTERLEAVING:
 				law_memctl = LAW_TRGT_IF_DDR_INTLV_1234;
 				if (i == 0)
-					fsl_ddr_set_lawbar(&info.common_timing_params[i],
+					fsl_ddr_set_lawbar(
+						&pinfo->common_timing_params[i],
 						law_memctl, i);
 				/* place holder for future 4-way interleaving */
 				break;
@@ -724,8 +755,8 @@
 			default:
 				break;
 			}
-			fsl_ddr_set_lawbar(&info.common_timing_params[i],
-					law_memctl, i);
+			fsl_ddr_set_lawbar(&pinfo->common_timing_params[i],
+					   law_memctl, i);
 		}
 	}
 #endif
@@ -734,7 +765,7 @@
 
 #if !defined(CONFIG_PHYS_64BIT)
 	/* Check for 4G or more.  Bad. */
-	if (total_memory >= (1ull << 32)) {
+	if ((first_ctrl == 0) && (total_memory >= (1ull << 32))) {
 		puts("Detected ");
 		print_size(total_memory, " of memory\n");
 		printf("       This U-Boot only supports < 4G of DDR\n");
@@ -748,8 +779,56 @@
 }
 
 /*
- * fsl_ddr_sdram_size() - This function only returns the size of the total
- * memory without setting ddr control registers.
+ * fsl_ddr_sdram(void) -- this is the main function to be
+ * called by initdram() in the board file.
+ *
+ * It returns amount of memory configured in bytes.
+ */
+phys_size_t fsl_ddr_sdram(void)
+{
+	fsl_ddr_info_t info;
+
+	/* Reset info structure. */
+	memset(&info, 0, sizeof(fsl_ddr_info_t));
+	info.mem_base = CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY;
+	info.first_ctrl = 0;
+	info.num_ctrls = CONFIG_SYS_FSL_DDR_MAIN_NUM_CTRLS;
+	info.dimm_slots_per_ctrl = CONFIG_DIMM_SLOTS_PER_CTLR;
+	info.board_need_mem_reset = board_need_mem_reset;
+	info.board_mem_reset = board_assert_mem_reset;
+	info.board_mem_de_reset = board_deassert_mem_reset;
+
+	return __fsl_ddr_sdram(&info);
+}
+
+#ifdef CONFIG_SYS_FSL_OTHER_DDR_NUM_CTRLS
+phys_size_t fsl_other_ddr_sdram(unsigned long long base,
+				unsigned int first_ctrl,
+				unsigned int num_ctrls,
+				unsigned int dimm_slots_per_ctrl,
+				int (*board_need_reset)(void),
+				void (*board_reset)(void),
+				void (*board_de_reset)(void))
+{
+	fsl_ddr_info_t info;
+
+	/* Reset info structure. */
+	memset(&info, 0, sizeof(fsl_ddr_info_t));
+	info.mem_base = base;
+	info.first_ctrl = first_ctrl;
+	info.num_ctrls = num_ctrls;
+	info.dimm_slots_per_ctrl = dimm_slots_per_ctrl;
+	info.board_need_mem_reset = board_need_reset;
+	info.board_mem_reset = board_reset;
+	info.board_mem_de_reset = board_de_reset;
+
+	return __fsl_ddr_sdram(&info);
+}
+#endif
+
+/*
+ * fsl_ddr_sdram_size(first_ctrl, last_intlv) - This function only returns the
+ * size of the total memory without setting ddr control registers.
  */
 phys_size_t
 fsl_ddr_sdram_size(void)
@@ -758,6 +837,11 @@
 	unsigned long long total_memory = 0;
 
 	memset(&info, 0 , sizeof(fsl_ddr_info_t));
+	info.mem_base = CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY;
+	info.first_ctrl = 0;
+	info.num_ctrls = CONFIG_SYS_FSL_DDR_MAIN_NUM_CTRLS;
+	info.dimm_slots_per_ctrl = CONFIG_DIMM_SLOTS_PER_CTLR;
+	info.board_need_mem_reset = NULL;
 
 	/* Compute it once normally. */
 	total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 1);