powerpc/mpc8xxx: Enable 3-way and 4-way DDR interleaving

Restructure DDR interleaving option to support 3 and 4 DDR controllers
for 2-, 3- and 4-way interleaving.

Signed-off-by: York Sun <yorksun@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
diff --git a/arch/powerpc/cpu/mpc85xx/cpu.c b/arch/powerpc/cpu/mpc85xx/cpu.c
index 8d2ef9f..5ddb294 100644
--- a/arch/powerpc/cpu/mpc85xx/cpu.c
+++ b/arch/powerpc/cpu/mpc85xx/cpu.c
@@ -430,11 +430,21 @@
 		case 0:
 			ddr[i] = (void *)CONFIG_SYS_MPC85xx_DDR_ADDR;
 			break;
-#ifdef CONFIG_SYS_MPC85xx_DDR2_ADDR
+#if defined(CONFIG_SYS_MPC85xx_DDR2_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 1)
 		case 1:
 			ddr[i] = (void *)CONFIG_SYS_MPC85xx_DDR2_ADDR;
 			break;
 #endif
+#if defined(CONFIG_SYS_MPC85xx_DDR3_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 2)
+		case 2:
+			ddr[i] = (void *)CONFIG_SYS_MPC85xx_DDR3_ADDR;
+			break;
+#endif
+#if defined(CONFIG_SYS_MPC85xx_DDR4_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 3)
+		case 3:
+			ddr[i] = (void *)CONFIG_SYS_MPC85xx_DDR4_ADDR;
+			break;
+#endif
 		default:
 			printf("%s unexpected controller number = %u\n",
 				__func__, i);
diff --git a/arch/powerpc/cpu/mpc85xx/ddr-gen3.c b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c
index 0761d70..ca4ed62 100644
--- a/arch/powerpc/cpu/mpc85xx/ddr-gen3.c
+++ b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c
@@ -32,9 +32,21 @@
 	case 0:
 		ddr = (void *)CONFIG_SYS_MPC85xx_DDR_ADDR;
 		break;
+#if defined(CONFIG_SYS_MPC85xx_DDR2_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 1)
 	case 1:
 		ddr = (void *)CONFIG_SYS_MPC85xx_DDR2_ADDR;
 		break;
+#endif
+#if defined(CONFIG_SYS_MPC85xx_DDR3_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 2)
+	case 2:
+		ddr = (void *)CONFIG_SYS_MPC85xx_DDR3_ADDR;
+		break;
+#endif
+#if defined(CONFIG_SYS_MPC85xx_DDR4_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 3)
+	case 3:
+		ddr = (void *)CONFIG_SYS_MPC85xx_DDR4_ADDR;
+		break;
+#endif
 	default:
 		printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num);
 		return;
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
index a8b2132..5938c44 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
@@ -151,8 +151,19 @@
 		if (dimm_params[dimm_number].n_ranks > 0) {
 			go_config = 1;
 			/* These fields only available in CS0_CONFIG */
-			intlv_en = popts->memctl_interleaving;
-			intlv_ctl = popts->memctl_interleaving_mode;
+			if (!popts->memctl_interleaving)
+				break;
+			switch (popts->memctl_interleaving_mode) {
+			case FSL_DDR_CACHE_LINE_INTERLEAVING:
+			case FSL_DDR_PAGE_INTERLEAVING:
+			case FSL_DDR_BANK_INTERLEAVING:
+			case FSL_DDR_SUPERBANK_INTERLEAVING:
+				intlv_en = popts->memctl_interleaving;
+				intlv_ctl = popts->memctl_interleaving_mode;
+				break;
+			default:
+				break;
+			}
 		}
 		break;
 	case 1:
@@ -1413,73 +1424,37 @@
 
 	/* Chip Select Memory Bounds (CSn_BNDS) */
 	for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
-		unsigned long long ea = 0, sa = 0;
+		unsigned long long ea, sa;
 		unsigned int cs_per_dimm
 			= CONFIG_CHIP_SELECTS_PER_CTRL / CONFIG_DIMM_SLOTS_PER_CTLR;
 		unsigned int dimm_number
 			= i / cs_per_dimm;
 		unsigned long long rank_density
-			= dimm_params[dimm_number].rank_density;
+			= dimm_params[dimm_number].rank_density >> dbw_cap_adj;
 
-		if (((i == 1) && (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1)) ||
-			((i == 2) && (popts->ba_intlv_ctl & 0x04)) ||
-			((i == 3) && (popts->ba_intlv_ctl & FSL_DDR_CS2_CS3))) {
-			/*
-			 * Don't set up boundaries for unused CS
-			 * cs1 for cs0_cs1, cs0_cs1_and_cs2_cs3, cs0_cs1_cs2_cs3
-			 * cs2 for cs0_cs1_cs2_cs3
-			 * cs3 for cs2_cs3, cs0_cs1_and_cs2_cs3, cs0_cs1_cs2_cs3
-			 * But we need to set the ODT_RD_CFG and
-			 * ODT_WR_CFG for CS1_CONFIG here.
-			 */
-			set_csn_config(dimm_number, i, ddr, popts, dimm_params);
-			continue;
-		}
 		if (dimm_params[dimm_number].n_ranks == 0) {
 			debug("Skipping setup of CS%u "
 				"because n_ranks on DIMM %u is 0\n", i, dimm_number);
 			continue;
 		}
-		if (popts->memctl_interleaving && popts->ba_intlv_ctl) {
-			/*
-			 * This works superbank 2CS
-			 * There are 2 or more memory controllers configured
-			 * identically, memory is interleaved between them,
-			 * and each controller uses rank interleaving within
-			 * itself. Therefore the starting and ending address
-			 * on each controller is twice the amount present on
-			 * each controller. If any CS is not included in the
-			 * interleaving, the memory on that CS is not accssible
-			 * and the total memory size is reduced. The CS is also
-			 * disabled.
-			 */
-			unsigned long long ctlr_density = 0;
+		if (popts->memctl_interleaving) {
 			switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+			case FSL_DDR_CS0_CS1_CS2_CS3:
+				break;
 			case FSL_DDR_CS0_CS1:
 			case FSL_DDR_CS0_CS1_AND_CS2_CS3:
-				ctlr_density = dimm_params[0].rank_density * 2;
 				if (i > 1)
 					cs_en = 0;
 				break;
 			case FSL_DDR_CS2_CS3:
-				ctlr_density = dimm_params[0].rank_density;
+			default:
 				if (i > 0)
 					cs_en = 0;
 				break;
-			case FSL_DDR_CS0_CS1_CS2_CS3:
-				/*
-				 * The four CS interleaving should have been verified by
-				 * populate_memctl_options()
-				 */
-				ctlr_density = dimm_params[0].rank_density * 4;
-				break;
-			default:
-				break;
 			}
-			ea = (CONFIG_NUM_DDR_CONTROLLERS *
-				(ctlr_density >> dbw_cap_adj)) - 1;
-		}
-		else if (!popts->memctl_interleaving && popts->ba_intlv_ctl) {
+			sa = common_dimm->base_address;
+			ea = common_dimm->total_mem - 1;
+		} else if (!popts->memctl_interleaving) {
 			/*
 			 * If memory interleaving between controllers is NOT
 			 * enabled, the starting address for each memory
@@ -1491,49 +1466,40 @@
 			 */
 			switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
 			case FSL_DDR_CS0_CS1_CS2_CS3:
-				/* CS0+CS1+CS2+CS3 interleaving, only CS0_CNDS
-				 * needs to be set.
-				 */
 				sa = common_dimm->base_address;
-				ea = sa + (4 * (rank_density >> dbw_cap_adj))-1;
+				ea = common_dimm->total_mem - 1;
 				break;
 			case FSL_DDR_CS0_CS1_AND_CS2_CS3:
-				/* CS0+CS1 and CS2+CS3 interleaving, CS0_CNDS
-				 * and CS2_CNDS need to be set.
-				 */
-				if ((i == 2) && (dimm_number == 0)) {
+				if ((i >= 2) && (dimm_number == 0)) {
 					sa = dimm_params[dimm_number].base_address +
-					      2 * (rank_density >> dbw_cap_adj);
-					ea = sa + 2 * (rank_density >> dbw_cap_adj) - 1;
+					      2 * rank_density;
+					ea = sa + 2 * rank_density - 1;
 				} else {
 					sa = dimm_params[dimm_number].base_address;
-					ea = sa + (2 * (rank_density >>
-						dbw_cap_adj)) - 1;
+					ea = sa + 2 * rank_density - 1;
 				}
 				break;
 			case FSL_DDR_CS0_CS1:
-				/* CS0+CS1 interleaving, CS0_CNDS needs
-				 * to be set
-				 */
 				if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
 					sa = dimm_params[dimm_number].base_address;
-					ea = sa + (rank_density >> dbw_cap_adj) - 1;
-					sa += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
-					ea += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
+					ea = sa + rank_density - 1;
+					if (i != 1)
+						sa += (i % cs_per_dimm) * rank_density;
+					ea += (i % cs_per_dimm) * rank_density;
 				} else {
 					sa = 0;
 					ea = 0;
 				}
 				if (i == 0)
-					ea += (rank_density >> dbw_cap_adj);
+					ea += rank_density;
 				break;
 			case FSL_DDR_CS2_CS3:
-				/* CS2+CS3 interleaving*/
 				if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
 					sa = dimm_params[dimm_number].base_address;
-					ea = sa + (rank_density >> dbw_cap_adj) - 1;
-					sa += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
-					ea += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
+					ea = sa + rank_density - 1;
+					if (i != 3)
+						sa += (i % cs_per_dimm) * rank_density;
+					ea += (i % cs_per_dimm) * rank_density;
 				} else {
 					sa = 0;
 					ea = 0;
@@ -1542,38 +1508,18 @@
 					ea += (rank_density >> dbw_cap_adj);
 				break;
 			default:  /* No bank(chip-select) interleaving */
+				sa = dimm_params[dimm_number].base_address;
+				ea = sa + rank_density - 1;
+				if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+					sa += (i % cs_per_dimm) * rank_density;
+					ea += (i % cs_per_dimm) * rank_density;
+				} else {
+					sa = 0;
+					ea = 0;
+				}
 				break;
 			}
 		}
-		else if (popts->memctl_interleaving && !popts->ba_intlv_ctl) {
-			/*
-			 * Only the rank on CS0 of each memory controller may
-			 * be used if memory controller interleaving is used
-			 * without rank interleaving within each memory
-			 * controller.  However, the ending address programmed
-			 * into each CS0 must be the sum of the amount of
-			 * memory in the two CS0 ranks.
-			 */
-			if (i == 0) {
-				ea = (2 * (rank_density >> dbw_cap_adj)) - 1;
-			}
-
-		}
-		else if (!popts->memctl_interleaving && !popts->ba_intlv_ctl) {
-			/*
-			 * No rank interleaving and no memory controller
-			 * interleaving.
-			 */
-			sa = dimm_params[dimm_number].base_address;
-			ea = sa + (rank_density >> dbw_cap_adj) - 1;
-			if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
-				sa += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
-				ea += (i % cs_per_dimm) * (rank_density >> dbw_cap_adj);
-			} else {
-				sa = 0;
-				ea = 0;
-			}
-		}
 
 		sa >>= 24;
 		ea >>= 24;
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/main.c b/arch/powerpc/cpu/mpc8xxx/ddr/main.c
index c2a03e3..4fd4f8f 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/main.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -15,13 +15,15 @@
 #include <common.h>
 #include <i2c.h>
 #include <asm/fsl_ddr_sdram.h>
+#include <asm/fsl_law.h>
 
 #include "ddr.h"
 
-extern void fsl_ddr_set_lawbar(
+void fsl_ddr_set_lawbar(
 		const common_timing_params_t *memctl_common_params,
 		unsigned int memctl_interleaved,
 		unsigned int ctrl_num);
+void fsl_ddr_set_intl3r(const unsigned int granule_size);
 
 /* processor specific function */
 extern void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs,
@@ -51,6 +53,22 @@
 	[1][0] = SPD_EEPROM_ADDRESS3,	/* controller 2 */
 	[1][1] = SPD_EEPROM_ADDRESS4,	/* controller 2 */
 };
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+	[0][0] = SPD_EEPROM_ADDRESS1,	/* controller 1 */
+	[1][0] = SPD_EEPROM_ADDRESS2,	/* controller 2 */
+	[2][0] = SPD_EEPROM_ADDRESS3,	/* controller 3 */
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+	[0][0] = SPD_EEPROM_ADDRESS1,	/* controller 1 */
+	[0][1] = SPD_EEPROM_ADDRESS2,	/* controller 1 */
+	[1][0] = SPD_EEPROM_ADDRESS3,	/* controller 2 */
+	[1][1] = SPD_EEPROM_ADDRESS4,	/* controller 2 */
+	[2][0] = SPD_EEPROM_ADDRESS5,	/* controller 3 */
+	[2][1] = SPD_EEPROM_ADDRESS6,	/* controller 3 */
+};
+
 #endif
 
 static void __get_spd(generic_spd_eeprom_t *spd, u8 i2c_address)
@@ -156,12 +174,12 @@
 	return step_string_tbl[s];
 }
 
-int step_assign_addresses(fsl_ddr_info_t *pinfo,
-			  unsigned int dbw_cap_adj[],
-			  unsigned int *all_memctl_interleaving,
-			  unsigned int *all_ctlr_rank_interleaving)
+unsigned long long step_assign_addresses(fsl_ddr_info_t *pinfo,
+			  unsigned int dbw_cap_adj[])
 {
 	int i, j;
+	unsigned long long total_mem, current_mem_base, total_ctlr_mem;
+	unsigned long long rank_density, ctlr_density = 0;
 
 	/*
 	 * If a reduced data width is requested, but the SPD
@@ -220,86 +238,108 @@
 				"specified controller %u\n", i);
 			return 1;
 		}
+		debug("dbw_cap_adj[%d]=%d\n", i, dbw_cap_adj[i]);
 	}
 
-	j = 0;
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
-		if (pinfo->memctl_opts[i].memctl_interleaving)
-			j++;
-	/*
-	 * Not support less than all memory controllers interleaving
-	 * if more than two controllers
-	 */
-	if (j == CONFIG_NUM_DDR_CONTROLLERS)
-		*all_memctl_interleaving = 1;
-
-	/* Check that all controllers are rank interleaving. */
-	j = 0;
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
-		if (pinfo->memctl_opts[i].ba_intlv_ctl)
-			j++;
-	/*
-	 * All memory controllers must be populated to qualify for
-	 * all controller rank interleaving
-	 */
-	 if (j == CONFIG_NUM_DDR_CONTROLLERS)
-		*all_ctlr_rank_interleaving = 1;
-
-	if (*all_memctl_interleaving) {
-		unsigned long long addr, total_mem_per_ctlr = 0;
-		/*
-		 * If interleaving between memory controllers,
-		 * make each controller start at a base address
-		 * of 0.
-		 *
-		 * Also, if bank interleaving (chip select
-		 * interleaving) is enabled on each memory
-		 * controller, CS0 needs to be programmed to
-		 * cover the entire memory range on that memory
-		 * controller
-		 *
-		 * Bank interleaving also implies that each
-		 * addressed chip select is identical in size.
-		 */
-
+	current_mem_base = 0ull;
+	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 &
+					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 < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-			addr = 0;
-			pinfo->common_timing_params[i].base_address = 0ull;
-			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
-				unsigned long long cap
-					= pinfo->dimm_params[i][j].capacity;
-
-				pinfo->dimm_params[i][j].base_address = addr;
-				addr += cap >> dbw_cap_adj[i];
-				total_mem_per_ctlr += cap >> dbw_cap_adj[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:
+					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");
+				}
+				pinfo->common_timing_params[i].base_address =
+							current_mem_base;
+				pinfo->common_timing_params[i].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 {
+				/* when 3rd controller not interleaved */
+				current_mem_base = total_mem;
+				total_ctlr_mem = 0;
+				pinfo->common_timing_params[i].base_address =
+							current_mem_base;
+				for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+					unsigned long long cap =
+						pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i];
+					pinfo->dimm_params[i][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);
+				pinfo->common_timing_params[i].total_mem =
+							total_ctlr_mem;
+				total_mem += total_ctlr_mem;
 			}
 		}
-		pinfo->common_timing_params[0].total_mem = total_mem_per_ctlr;
 	} else {
 		/*
 		 * Simple linear assignment if memory
 		 * controllers are not interleaved.
 		 */
-		unsigned long long cur_memsize = 0;
 		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-			u64 total_mem_per_ctlr = 0;
+			total_ctlr_mem = 0;
 			pinfo->common_timing_params[i].base_address =
-						cur_memsize;
+						current_mem_base;
 			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
 				/* Compute DIMM base addresses. */
 				unsigned long long cap =
-					pinfo->dimm_params[i][j].capacity;
+					pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i];
 				pinfo->dimm_params[i][j].base_address =
-					cur_memsize;
-				cur_memsize += cap >> dbw_cap_adj[i];
-				total_mem_per_ctlr += cap >> dbw_cap_adj[i];
+					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);
 			pinfo->common_timing_params[i].total_mem =
-							total_mem_per_ctlr;
+							total_ctlr_mem;
+			total_mem += total_ctlr_mem;
 		}
 	}
+	debug("Total mem by %s is 0x%llx\n", __func__, total_mem);
 
-	return 0;
+	return total_mem;
 }
 
 unsigned long long
@@ -307,8 +347,6 @@
 				       unsigned int size_only)
 {
 	unsigned int i, j;
-	unsigned int all_controllers_memctl_interleaving = 0;
-	unsigned int all_controllers_rank_interleaving = 0;
 	unsigned long long total_mem = 0;
 
 	fsl_ddr_cfg_regs_t *ddr_reg = pinfo->fsl_ddr_config_reg;
@@ -346,8 +384,9 @@
 				retval = compute_dimm_parameters(spd, pdimm, i);
 #ifdef CONFIG_SYS_DDR_RAW_TIMING
 				if (retval != 0) {
-					printf("SPD error! Trying fallback to "
-					"raw timing calculation\n");
+					printf("SPD error on controller %d! "
+					"Trying fallback to raw timing "
+					"calculation\n", i);
 					fsl_ddr_get_dimm_params(pdimm, i, j);
 				}
 #else
@@ -407,17 +446,14 @@
 					&pinfo->memctl_opts[i],
 					pinfo->dimm_params[i], i);
 		}
-		check_interleaving_options(pinfo);
 	case STEP_ASSIGN_ADDRESSES:
 		/* STEP 5:  Assign addresses to chip selects */
-		step_assign_addresses(pinfo,
-				dbw_capacity_adjust,
-				&all_controllers_memctl_interleaving,
-				&all_controllers_rank_interleaving);
+		check_interleaving_options(pinfo);
+		total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust);
 
 	case STEP_COMPUTE_REGS:
 		/* STEP 6:  compute controller register values */
-		debug("FSL Memory ctrl cg register computation\n");
+		debug("FSL Memory ctrl register computation\n");
 		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
 			if (timing_params[i].ndimms_present == 0) {
 				memset(&ddr_reg[i], 0,
@@ -437,21 +473,7 @@
 		break;
 	}
 
-	/* Compute the total amount of memory. */
-
-	/*
-	 * If bank interleaving but NOT memory controller interleaving
-	 * CS_BNDS describe the quantity of memory on each memory
-	 * controller, so the total is the sum across.
-	 */
-	if (!all_controllers_memctl_interleaving
-	    && all_controllers_rank_interleaving) {
-		total_mem = 0;
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-			total_mem += timing_params[i].total_mem;
-		}
-
-	} else {
+	{
 		/*
 		 * Compute the amount of memory available just by
 		 * looking for the highest valid CSn_BNDS value.
@@ -489,7 +511,7 @@
 phys_size_t fsl_ddr_sdram(void)
 {
 	unsigned int i;
-	unsigned int memctl_interleaved;
+	unsigned int law_memctl = LAW_TRGT_IF_DDR_1;
 	unsigned long long total_memory;
 	fsl_ddr_info_t info;
 
@@ -504,34 +526,6 @@
 #endif
 		total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0);
 
-	/* Check for memory controller interleaving. */
-	memctl_interleaved = 0;
-	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-		memctl_interleaved +=
-			info.memctl_opts[i].memctl_interleaving;
-	}
-
-	if (memctl_interleaved) {
-		if (memctl_interleaved == CONFIG_NUM_DDR_CONTROLLERS) {
-			debug("memctl interleaving\n");
-			/*
-			 * Change the meaning of memctl_interleaved
-			 * to be "boolean".
-			 */
-			memctl_interleaved = 1;
-		} else {
-			printf("Warning: memctl interleaving not "
-				"properly configured on all controllers\n");
-			memctl_interleaved = 0;
-			for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
-				info.memctl_opts[i].memctl_interleaving = 0;
-			debug("Recomputing with memctl_interleaving off.\n");
-			total_memory = fsl_ddr_compute(&info,
-						       STEP_ASSIGN_ADDRESSES,
-						       0);
-		}
-	}
-
 	/* Program configuration registers. */
 	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
 		debug("Programming controller %u\n", i);
@@ -544,24 +538,69 @@
 		fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), i);
 	}
 
-	if (memctl_interleaved) {
-		const unsigned int ctrl_num = 0;
-
-		/* Only set LAWBAR1 if memory controller interleaving is on. */
-		fsl_ddr_set_lawbar(&info.common_timing_params[0],
-					 memctl_interleaved, ctrl_num);
-	} else {
-		/*
-		 * Memory controller interleaving is NOT on;
-		 * set each lawbar individually.
-		 */
-		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+	/* 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) {
+			case FSL_DDR_CACHE_LINE_INTERLEAVING:
+			case FSL_DDR_PAGE_INTERLEAVING:
+			case FSL_DDR_BANK_INTERLEAVING:
+			case FSL_DDR_SUPERBANK_INTERLEAVING:
+				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],
+						law_memctl, i);
+				}
+				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_intl3r(info.memctl_opts[i].memctl_interleaving_mode);
+					fsl_ddr_set_lawbar(&info.common_timing_params[i],
+						law_memctl, i);
+				}
+				break;
+			case FSL_DDR_4WAY_1KB_INTERLEAVING:
+			case FSL_DDR_4WAY_4KB_INTERLEAVING:
+			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],
+						law_memctl, i);
+				/* place holder for future 4-way interleaving */
+				break;
+			default:
+				break;
+			}
+		} else {
+			switch (i) {
+			case 0:
+				law_memctl = LAW_TRGT_IF_DDR_1;
+				break;
+			case 1:
+				law_memctl = LAW_TRGT_IF_DDR_2;
+				break;
+			case 2:
+				law_memctl = LAW_TRGT_IF_DDR_3;
+				break;
+			case 3:
+				law_memctl = LAW_TRGT_IF_DDR_4;
+				break;
+			default:
+				break;
+			}
 			fsl_ddr_set_lawbar(&info.common_timing_params[i],
-						 0, i);
+					law_memctl, i);
 		}
 	}
 
-	debug("total_memory = %llu\n", total_memory);
+	debug("total_memory by %s = %llu\n", __func__, total_memory);
 
 #if !defined(CONFIG_PHYS_64BIT)
 	/* Check for 4G or more.  Bad. */
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/options.c b/arch/powerpc/cpu/mpc8xxx/ddr/options.c
index 00ec57b..2923456 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/options.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/options.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008, 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008, 2010-2012 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
@@ -790,46 +790,97 @@
 	 * should be a subset of the requested configuration.
 	 */
 #if (CONFIG_NUM_DDR_CONTROLLERS > 1)
-	if (hwconfig_sub_f("fsl_ddr", "ctlr_intlv", buf)) {
-		if (pdimm[0].n_ranks == 0) {
-			printf("There is no rank on CS0 for controller %d. Because only"
-				" rank on CS0 and ranks chip-select interleaved with CS0"
-				" are controller interleaved, force non memory "
-				"controller interleaving\n", ctrl_num);
-			popts->memctl_interleaving = 0;
-		} else {
-			popts->memctl_interleaving = 1;
-			/*
-			 * test null first. if CONFIG_HWCONFIG is not defined
-			 * hwconfig_arg_cmp returns non-zero
-			 */
-			if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
-						    "null", buf)) {
-				popts->memctl_interleaving = 0;
-				debug("memory controller interleaving disabled.\n");
-			} else if (hwconfig_subarg_cmp_f("fsl_ddr",
-							 "ctlr_intlv",
-							 "cacheline", buf))
-				popts->memctl_interleaving_mode =
-					FSL_DDR_CACHE_LINE_INTERLEAVING;
-			else if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
-						       "page", buf))
-				popts->memctl_interleaving_mode =
-					FSL_DDR_PAGE_INTERLEAVING;
-			else if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
-						       "bank", buf))
-				popts->memctl_interleaving_mode =
-					FSL_DDR_BANK_INTERLEAVING;
-			else if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
-						       "superbank", buf))
-				popts->memctl_interleaving_mode =
-					FSL_DDR_SUPERBANK_INTERLEAVING;
-			else {
-				popts->memctl_interleaving = 0;
-				printf("hwconfig has unrecognized parameter for ctlr_intlv.\n");
-			}
-		}
+	if (!hwconfig_sub_f("fsl_ddr", "ctlr_intlv", buf))
+		goto done;
+
+	if (pdimm[0].n_ranks == 0) {
+		printf("There is no rank on CS0 for controller %d.\n", ctrl_num);
+		popts->memctl_interleaving = 0;
+		goto done;
+	}
+	popts->memctl_interleaving = 1;
+	/*
+	 * test null first. if CONFIG_HWCONFIG is not defined
+	 * hwconfig_arg_cmp returns non-zero
+	 */
+	if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
+				    "null", buf)) {
+		popts->memctl_interleaving = 0;
+		debug("memory controller interleaving disabled.\n");
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"cacheline", buf)) {
+		popts->memctl_interleaving_mode =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : FSL_DDR_CACHE_LINE_INTERLEAVING;
+		popts->memctl_interleaving =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : 1;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"page", buf)) {
+		popts->memctl_interleaving_mode =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : FSL_DDR_PAGE_INTERLEAVING;
+		popts->memctl_interleaving =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : 1;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"bank", buf)) {
+		popts->memctl_interleaving_mode =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : FSL_DDR_BANK_INTERLEAVING;
+		popts->memctl_interleaving =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : 1;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"superbank", buf)) {
+		popts->memctl_interleaving_mode =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : FSL_DDR_SUPERBANK_INTERLEAVING;
+		popts->memctl_interleaving =
+			((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+			0 : 1;
+#if (CONFIG_NUM_DDR_CONTROLLERS == 3)
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"3way_1KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_3WAY_1KB_INTERLEAVING;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"3way_4KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_3WAY_4KB_INTERLEAVING;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"3way_8KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_3WAY_8KB_INTERLEAVING;
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 4)
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"4way_1KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_4WAY_1KB_INTERLEAVING;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"4way_4KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_4WAY_4KB_INTERLEAVING;
+	} else if (hwconfig_subarg_cmp_f("fsl_ddr",
+					"ctlr_intlv",
+					"4way_8KB", buf)) {
+		popts->memctl_interleaving_mode =
+			FSL_DDR_4WAY_8KB_INTERLEAVING;
+#endif
+	} else {
+		popts->memctl_interleaving = 0;
+		printf("hwconfig has unrecognized parameter for ctlr_intlv.\n");
 	}
+done:
 #endif
 	if ((hwconfig_sub_f("fsl_ddr", "bank_intlv", buf)) &&
 		(CONFIG_CHIP_SELECTS_PER_CTRL > 1)) {
@@ -859,20 +910,20 @@
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(chip-select) for "
 					"CS0+CS1+CS2+CS3 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 #elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
 			if ((pdimm[0].n_ranks < 2) && (pdimm[1].n_ranks < 2)) {
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(chip-select) for "
 					"CS0+CS1+CS2+CS3 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 			if (pdimm[0].capacity != pdimm[1].capacity) {
 				popts->ba_intlv_ctl = 0;
 				printf("Not identical DIMM size for "
 					"CS0+CS1+CS2+CS3 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 #endif
 			break;
@@ -881,7 +932,7 @@
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(chip-select) for "
 					"CS0+CS1 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 			break;
 		case FSL_DDR_CS2_CS3:
@@ -889,13 +940,13 @@
 			if (pdimm[0].n_ranks < 4) {
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(chip-select) for CS2+CS3 "
-					"on controller %d, force non-interleaving!\n", ctrl_num);
+					"on controller %d, interleaving disabled!\n", ctrl_num);
 			}
 #elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
 			if (pdimm[1].n_ranks < 2) {
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(chip-select) for CS2+CS3 "
-					"on controller %d, force non-interleaving!\n", ctrl_num);
+					"on controller %d, interleaving disabled!\n", ctrl_num);
 			}
 #endif
 			break;
@@ -905,14 +956,14 @@
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(CS) for CS0+CS1 and "
 					"CS2+CS3 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 #elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
 			if ((pdimm[0].n_ranks < 2) || (pdimm[1].n_ranks < 2)) {
 				popts->ba_intlv_ctl = 0;
 				printf("Not enough bank(CS) for CS0+CS1 and "
 					"CS2+CS3 on controller %d, "
-					"force non-interleaving!\n", ctrl_num);
+					"interleaving disabled!\n", ctrl_num);
 			}
 #endif
 			break;
@@ -954,33 +1005,73 @@
 
 void check_interleaving_options(fsl_ddr_info_t *pinfo)
 {
-	int i, j, check_n_ranks, intlv_fixed = 0;
+	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_s *dimm;
 	/*
 	 * Check if all controllers are configured for memory
 	 * controller interleaving. Identical dimms are recommended. At least
-	 * the size should be checked.
+	 * the size, row and col address should be checked.
 	 */
 	j = 0;
 	check_n_ranks = pinfo->dimm_params[0][0].n_ranks;
 	check_rank_density = pinfo->dimm_params[0][0].rank_density;
+	check_n_row_addr =  pinfo->dimm_params[0][0].n_row_addr;
+	check_n_col_addr = pinfo->dimm_params[0][0].n_col_addr;
+	check_intlv = pinfo->memctl_opts[0].memctl_interleaving_mode;
 	for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
-		if ((pinfo->memctl_opts[i].memctl_interleaving) && \
-		    (check_rank_density == pinfo->dimm_params[i][0].rank_density) && \
-		    (check_n_ranks == pinfo->dimm_params[i][0].n_ranks)) {
+		dimm = &pinfo->dimm_params[i][0];
+		if (!pinfo->memctl_opts[i].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->memctl_opts[i].memctl_interleaving_mode))){
+			intlv_invalid = 1;
+			break;
+		} else {
 			j++;
 		}
+
 	}
-	if (j != CONFIG_NUM_DDR_CONTROLLERS) {
+	if (intlv_invalid) {
 		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
-			if (pinfo->memctl_opts[i].memctl_interleaving) {
+			pinfo->memctl_opts[i].memctl_interleaving = 0;
+		printf("Not all DIMMs are identical. "
+			"Memory controller interleaving disabled.\n");
+	} else {
+		switch (check_intlv) {
+		case FSL_DDR_CACHE_LINE_INTERLEAVING:
+		case FSL_DDR_PAGE_INTERLEAVING:
+		case FSL_DDR_BANK_INTERLEAVING:
+		case FSL_DDR_SUPERBANK_INTERLEAVING:
+			if (3 == CONFIG_NUM_DDR_CONTROLLERS)
+				k = 2;
+			else
+				k = CONFIG_NUM_DDR_CONTROLLERS;
+			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 = CONFIG_NUM_DDR_CONTROLLERS;
+			break;
+		}
+		debug("%d of %d controllers are interleaving.\n", j, k);
+		if (j != k) {
+			for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
 				pinfo->memctl_opts[i].memctl_interleaving = 0;
-				intlv_fixed = 1;
-			}
-		if (intlv_fixed)
-			printf("Not all DIMMs are identical in size. "
-				"Memory controller interleaving disabled.\n");
+			printf("Not all controllers have compatible "
+				"interleaving mode. All disabled.\n");
+		}
 	}
+	debug("Checking interleaving options completed\n");
 }
 
 int fsl_use_spd(void)
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/util.c b/arch/powerpc/cpu/mpc8xxx/ddr/util.c
index eb6a17a..664ad09 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/util.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/util.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -79,7 +79,7 @@
 
 void
 __fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params,
-			   unsigned int memctl_interleaved,
+			   unsigned int law_memctl,
 			   unsigned int ctrl_num)
 {
 	unsigned long long base = memctl_common_params->base_address;
@@ -98,28 +98,13 @@
 	if ((base + size) >= CONFIG_MAX_MEM_MAPPED)
 		size = CONFIG_MAX_MEM_MAPPED - base;
 #endif
-
-	if (ctrl_num == 0) {
-		/*
-		 * Set up LAW for DDR controller 1 space.
-		 */
-		unsigned int lawbar1_target_id = memctl_interleaved
-			? LAW_TRGT_IF_DDR_INTRLV : LAW_TRGT_IF_DDR_1;
-
-		if (set_ddr_laws(base, size, lawbar1_target_id) < 0) {
-			printf("%s: ERROR (ctrl #0, intrlv=%d)\n", __func__,
-				memctl_interleaved);
-			return ;
-		}
-	} else if (ctrl_num == 1) {
-		if (set_ddr_laws(base, size, LAW_TRGT_IF_DDR_2) < 0) {
-			printf("%s: ERROR (ctrl #1)\n", __func__);
-			return ;
-		}
-	} else {
-		printf("%s: unexpected DDR controller number (%u)\n", __func__,
-			ctrl_num);
+	if (set_ddr_laws(base, size, law_memctl) < 0) {
+		printf("%s: ERROR (ctrl #%d, TRGT ID=%x)\n", __func__, ctrl_num,
+			law_memctl);
+		return ;
 	}
+	debug("setup ddr law base = 0x%llx, size 0x%llx, TRGT_ID 0x%x\n",
+		base, size, law_memctl);
 }
 
 __attribute__((weak, alias("__fsl_ddr_set_lawbar"))) void
@@ -127,6 +112,15 @@
 			 unsigned int memctl_interleaved,
 			 unsigned int ctrl_num);
 
+void fsl_ddr_set_intl3r(const unsigned int granule_size)
+{
+#ifdef CONFIG_E6500
+	u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004);
+	*mcintl3r = 0x80000000 | (granule_size & 0x1f);
+	debug("Enable MCINTL3R with granule size 0x%x\n", granule_size);
+#endif
+}
+
 void board_add_ram_info(int use_default)
 {
 #if defined(CONFIG_MPC83xx)
@@ -137,6 +131,9 @@
 #elif defined(CONFIG_MPC86xx)
 	ccsr_ddr_t *ddr = (void *)(CONFIG_SYS_MPC86xx_DDR_ADDR);
 #endif
+#if	defined(CONFIG_E6500) && (CONFIG_NUM_DDR_CONTROLLERS == 3)
+	u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004);
+#endif
 #if (CONFIG_NUM_DDR_CONTROLLERS > 1)
 	uint32_t cs0_config = in_be32(&ddr->cs0_config);
 #endif
@@ -180,7 +177,29 @@
 	else
 		puts(", ECC off)");
 
-#if (CONFIG_NUM_DDR_CONTROLLERS > 1)
+#if (CONFIG_NUM_DDR_CONTROLLERS == 3)
+#ifdef CONFIG_E6500
+	if (*mcintl3r & 0x80000000) {
+		puts("\n");
+		puts("       DDR Controller Interleaving Mode: ");
+		switch (*mcintl3r & 0x1f) {
+		case FSL_DDR_3WAY_1KB_INTERLEAVING:
+			puts("3-way 1KB");
+			break;
+		case FSL_DDR_3WAY_4KB_INTERLEAVING:
+			puts("3-way 4KB");
+			break;
+		case FSL_DDR_3WAY_8KB_INTERLEAVING:
+			puts("3-way 8KB");
+			break;
+		default:
+			puts("3-way UNKNOWN");
+			break;
+		}
+	}
+#endif
+#endif
+#if (CONFIG_NUM_DDR_CONTROLLERS >= 2)
 	if (cs0_config & 0x20000000) {
 		puts("\n");
 		puts("       DDR Controller Interleaving Mode: ");
diff --git a/arch/powerpc/include/asm/fsl_ddr_sdram.h b/arch/powerpc/include/asm/fsl_ddr_sdram.h
index 157ae24..e271342 100644
--- a/arch/powerpc/include/asm/fsl_ddr_sdram.h
+++ b/arch/powerpc/include/asm/fsl_ddr_sdram.h
@@ -76,6 +76,13 @@
 #define FSL_DDR_PAGE_INTERLEAVING	0x1
 #define FSL_DDR_BANK_INTERLEAVING	0x2
 #define FSL_DDR_SUPERBANK_INTERLEAVING	0x3
+#define FSL_DDR_3WAY_1KB_INTERLEAVING	0xA
+#define FSL_DDR_3WAY_4KB_INTERLEAVING	0xC
+#define FSL_DDR_3WAY_8KB_INTERLEAVING	0xD
+/* placeholder for 4-way interleaving */
+#define FSL_DDR_4WAY_1KB_INTERLEAVING	0x1A
+#define FSL_DDR_4WAY_4KB_INTERLEAVING	0x1C
+#define FSL_DDR_4WAY_8KB_INTERLEAVING	0x1D
 
 /* DDR_SDRAM_CFG - DDR SDRAM Control Configuration
  */
diff --git a/arch/powerpc/include/asm/fsl_law.h b/arch/powerpc/include/asm/fsl_law.h
index dc3985e..f9cec8e 100644
--- a/arch/powerpc/include/asm/fsl_law.h
+++ b/arch/powerpc/include/asm/fsl_law.h
@@ -60,8 +60,12 @@
 
 	LAW_TRGT_IF_DDR_1 = 0x10,
 	LAW_TRGT_IF_DDR_2 = 0x11,	/* 2nd controller */
+	LAW_TRGT_IF_DDR_3 = 0x12,
+	LAW_TRGT_IF_DDR_4 = 0x13,
 	LAW_TRGT_IF_DDR_INTRLV = 0x14,
-
+	LAW_TRGT_IF_DDR_INTLV_34 = 0x15,
+	LAW_TRGT_IF_DDR_INTLV_123 = 0x17,
+	LAW_TRGT_IF_DDR_INTLV_1234 = 0x16,
 	LAW_TRGT_IF_BMAN = 0x18,
 	LAW_TRGT_IF_DCSR = 0x1d,
 	LAW_TRGT_IF_LBC = 0x1f,
@@ -87,6 +91,12 @@
 	LAW_TRGT_IF_DPAA_SWP_SRAM = 0x0e,
 	LAW_TRGT_IF_DDR = 0x0f,
 	LAW_TRGT_IF_DDR_2 = 0x16,	/* 2nd controller */
+	/* place holder for 3-way and 4-way interleaving */
+	LAW_TRGT_IF_DDR_3,
+	LAW_TRGT_IF_DDR_4,
+	LAW_TRGT_IF_DDR_INTLV_34,
+	LAW_TRGT_IF_DDR_INTLV_123,
+	LAW_TRGT_IF_DDR_INTLV_1234,
 };
 #define LAW_TRGT_IF_DDR_1	LAW_TRGT_IF_DDR
 #define LAW_TRGT_IF_PCI_1	LAW_TRGT_IF_PCI
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index 412244e..7de33a7 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -2621,6 +2621,7 @@
 #define CONFIG_SYS_FSL_CORENET_CCM_OFFSET	0x0000
 #define CONFIG_SYS_MPC85xx_DDR_OFFSET		0x8000
 #define CONFIG_SYS_MPC85xx_DDR2_OFFSET		0x9000
+#define CONFIG_SYS_MPC85xx_DDR3_OFFSET		0xA000
 #define CONFIG_SYS_FSL_CORENET_CLK_OFFSET	0xE1000
 #define CONFIG_SYS_FSL_CORENET_RCPM_OFFSET	0xE2000
 #define CONFIG_SYS_FSL_CORENET_SERDES_OFFSET	0xEA000
@@ -2740,6 +2741,8 @@
 	(CONFIG_SYS_IMMR + CONFIG_SYS_MPC85xx_DDR_OFFSET)
 #define CONFIG_SYS_MPC85xx_DDR2_ADDR \
 	(CONFIG_SYS_IMMR + CONFIG_SYS_MPC85xx_DDR2_OFFSET)
+#define CONFIG_SYS_MPC85xx_DDR3_ADDR \
+	(CONFIG_SYS_IMMR + CONFIG_SYS_MPC85xx_DDR3_OFFSET)
 #define CONFIG_SYS_LBC_ADDR \
 	(CONFIG_SYS_IMMR + CONFIG_SYS_MPC85xx_LBC_OFFSET)
 #define CONFIG_SYS_IFC_ADDR \
diff --git a/doc/README.fsl-ddr b/doc/README.fsl-ddr
index 5e21658..f94b56f 100644
--- a/doc/README.fsl-ddr
+++ b/doc/README.fsl-ddr
@@ -1,5 +1,28 @@
+Table of interleaving 2-4 controllers
+=====================================
+  +--------------+-----------------------------------------------------------+
+  |Configuration |                    Memory Controller                      |
+  |              |       1              2              3             4       |
+  |--------------+--------------+--------------+-----------------------------+
+  | Two memory   | Not Intlv'ed | Not Intlv'ed |                             |
+  | complexes    +--------------+--------------+                             |
+  |              |      2-way Intlv'ed         |                             |
+  |--------------+--------------+--------------+--------------+              |
+  |              | Not Intlv'ed | Not Intlv'ed | Not Intlv'ed |              |
+  | Three memory +--------------+--------------+--------------+              |
+  | complexes    |         2-way Intlv'ed      | Not Intlv'ed |              |
+  |              +-----------------------------+--------------+              |
+  |              |                  3-way Intlv'ed            |              |
+  +--------------+--------------+--------------+--------------+--------------+
+  |              | Not Intlv'ed | Not Intlv'ed | Not Intlv'ed | Not Intlv'ed |
+  | Four memory  +--------------+--------------+--------------+--------------+
+  | complexes    |       2-way Intlv'ed        |       2-way Intlv'ed        |
+  |              +-----------------------------+-----------------------------+
+  |              |                      4-way Intlv'ed                       |
+  +--------------+-----------------------------------------------------------+
 
-Table of interleaving modes supported in cpu/8xxx/ddr/
+
+Table of 2-way interleaving modes supported in cpu/8xxx/ddr/
 ======================================================
   +-------------+---------------------------------------------------------+
   |		|		    Rank Interleaving			  |
@@ -56,6 +79,15 @@
   # superbank
   setenv hwconfig "fsl_ddr:ctlr_intlv=superbank"
 
+  # 1KB 3-way interleaving
+  setenv hwconfig "fsl_ddr:ctlr_intlv=3way_1KB"
+
+  # 4KB 3-way interleaving
+  setenv hwconfig "fsl_ddr:ctlr_intlv=3way_4KB"
+
+  # 8KB 3-way interleaving
+  setenv hwconfig "fsl_ddr:ctlr_intlv=3way_8KB"
+
   # disable bank (chip-select) interleaving
   setenv hwconfig "fsl_ddr:bank_intlv=null"