powerpc/8xxx: Enable DDR3 RDIMM support

Enabled registered DIMMs using data from SPD. RDIMMs have registers
which need to be configured before using. The register configuration
words are stored in SPD byte 60~116 (JEDEC standard No.21-C). Software
should read those RCWs and put into DDR controller before initialization.

Signed-off-by: York Sun <yorksun@freescale.com>
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h b/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h
index 5aea517..06706ed 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h
@@ -48,6 +48,9 @@
 
 	unsigned long long total_mem;
 	unsigned long long base_address;
+
+	/* DDR3 RDIMM */
+	unsigned char rcw[16];	/* Register Control Word 0-15 */
 } common_timing_params_t;
 
 #endif
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
index ff0ddd1..b2962d2 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
@@ -448,6 +448,35 @@
 	debug("FSLDDR: timing_cfg_2 = 0x%08x\n", ddr->timing_cfg_2);
 }
 
+/* DDR SDRAM Register Control Word */
+static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr,
+			       const common_timing_params_t *common_dimm)
+{
+	if (common_dimm->all_DIMMs_registered
+		&& !common_dimm->all_DIMMs_unbuffered) {
+		ddr->ddr_sdram_rcw_1 =
+			common_dimm->rcw[0] << 28 | \
+			common_dimm->rcw[1] << 24 | \
+			common_dimm->rcw[2] << 20 | \
+			common_dimm->rcw[3] << 16 | \
+			common_dimm->rcw[4] << 12 | \
+			common_dimm->rcw[5] << 8 | \
+			common_dimm->rcw[6] << 4 | \
+			common_dimm->rcw[7];
+		ddr->ddr_sdram_rcw_2 =
+			common_dimm->rcw[8] << 28 | \
+			common_dimm->rcw[9] << 24 | \
+			common_dimm->rcw[10] << 20 | \
+			common_dimm->rcw[11] << 16 | \
+			common_dimm->rcw[12] << 12 | \
+			common_dimm->rcw[13] << 8 | \
+			common_dimm->rcw[14] << 4 | \
+			common_dimm->rcw[15];
+		debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n", ddr->ddr_sdram_rcw_1);
+		debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n", ddr->ddr_sdram_rcw_2);
+	}
+}
+
 /* DDR SDRAM control configuration (DDR_SDRAM_CFG) */
 static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr,
 			       const memctl_options_t *popts,
@@ -938,6 +967,7 @@
 
 	clk_adjust = popts->clk_adjust;
 	ddr->ddr_sdram_clk_cntl = (clk_adjust & 0xF) << 23;
+	debug("FSLDDR: clk_cntl = 0x%08x\n", ddr->ddr_sdram_clk_cntl);
 }
 
 /* DDR Initialization Address (DDR_INIT_ADDR) */
@@ -1113,54 +1143,6 @@
 	ddr->ddr_sr_cntr = (sr_it & 0xF) << 16;
 }
 
-/* DDR SDRAM Register Control Word 1 (DDR_SDRAM_RCW_1) */
-static void set_ddr_sdram_rcw_1(fsl_ddr_cfg_regs_t *ddr)
-{
-	unsigned int rcw0 = 0;	/* RCW0: Register Control Word 0 */
-	unsigned int rcw1 = 0;	/* RCW1: Register Control Word 1 */
-	unsigned int rcw2 = 0;	/* RCW2: Register Control Word 2 */
-	unsigned int rcw3 = 0;	/* RCW3: Register Control Word 3 */
-	unsigned int rcw4 = 0;	/* RCW4: Register Control Word 4 */
-	unsigned int rcw5 = 0;	/* RCW5: Register Control Word 5 */
-	unsigned int rcw6 = 0;	/* RCW6: Register Control Word 6 */
-	unsigned int rcw7 = 0;	/* RCW7: Register Control Word 7 */
-
-	ddr->ddr_sdram_rcw_1 = (0
-				| ((rcw0 & 0xF) << 28)
-				| ((rcw1 & 0xF) << 24)
-				| ((rcw2 & 0xF) << 20)
-				| ((rcw3 & 0xF) << 16)
-				| ((rcw4 & 0xF) << 12)
-				| ((rcw5 & 0xF) << 8)
-				| ((rcw6 & 0xF) << 4)
-				| ((rcw7 & 0xF) << 0)
-				);
-}
-
-/* DDR SDRAM Register Control Word 2 (DDR_SDRAM_RCW_2) */
-static void set_ddr_sdram_rcw_2(fsl_ddr_cfg_regs_t *ddr)
-{
-	unsigned int rcw8 = 0;	/* RCW0: Register Control Word 8 */
-	unsigned int rcw9 = 0;	/* RCW1: Register Control Word 9 */
-	unsigned int rcw10 = 0;	/* RCW2: Register Control Word 10 */
-	unsigned int rcw11 = 0;	/* RCW3: Register Control Word 11 */
-	unsigned int rcw12 = 0;	/* RCW4: Register Control Word 12 */
-	unsigned int rcw13 = 0;	/* RCW5: Register Control Word 13 */
-	unsigned int rcw14 = 0;	/* RCW6: Register Control Word 14 */
-	unsigned int rcw15 = 0;	/* RCW7: Register Control Word 15 */
-
-	ddr->ddr_sdram_rcw_2 = (0
-				| ((rcw8 & 0xF) << 28)
-				| ((rcw9 & 0xF) << 24)
-				| ((rcw10 & 0xF) << 20)
-				| ((rcw11 & 0xF) << 16)
-				| ((rcw12 & 0xF) << 12)
-				| ((rcw13 & 0xF) << 8)
-				| ((rcw14 & 0xF) << 4)
-				| ((rcw15 & 0xF) << 0)
-				);
-}
-
 static void set_ddr_eor(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts)
 {
 	if (popts->addr_hash) {
@@ -1430,8 +1412,7 @@
 
 	set_ddr_sr_cntr(ddr, sr_it);
 
-	set_ddr_sdram_rcw_1(ddr);
-	set_ddr_sdram_rcw_2(ddr);
+	set_ddr_sdram_rcw(ddr, common_dimm);
 
 	return check_fsl_memctl_config_regs(ddr);
 }
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c
index d4199ba..29cea53 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c
@@ -90,6 +90,7 @@
 {
 	unsigned int retval;
 	unsigned int mtb_ps;
+	int i;
 
 	if (spd->mem_type) {
 		if (spd->mem_type != SPD_MEMTYPE_DDR3) {
@@ -131,8 +132,11 @@
 	case 0x01:	/* RDIMM */
 	case 0x05:	/* Mini-RDIMM */
 		pdimm->registered_dimm = 1; /* register buffered */
+		for (i = 0; i < 16; i += 2) {
+			pdimm->rcw[i] = spd->mod_section.registered.rcw[i/2] & 0x0F;
+			pdimm->rcw[i+1] = (spd->mod_section.registered.rcw[i/2] >> 4) & 0x0F;
+		}
 		break;
-
 	case 0x02:	/* UDIMM */
 	case 0x03:	/* SO-DIMM */
 	case 0x04:	/* Micro-DIMM */
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c
index ce6c148..029e566 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c
@@ -76,7 +76,7 @@
 				      common_timing_params_t *outpdimm,
 				      unsigned int number_of_dimms)
 {
-	unsigned int i;
+	unsigned int i, j;
 
 	unsigned int tCKmin_X_ps = 0;
 	unsigned int tCKmax_ps = 0xFFFFFFFF;
@@ -98,7 +98,7 @@
 	unsigned int tDQSQ_max_ps = 0;
 	unsigned int tQHS_ps = 0;
 
-	unsigned int temp1, temp2;
+	unsigned int temp1, temp2, temp3;
 	unsigned int additive_latency = 0;
 #if !defined(CONFIG_FSL_DDR3)
 	const unsigned int mclk_ps = get_memory_clk_period_ps();
@@ -231,6 +231,20 @@
 				"DIMMs detected!\n");
 	}
 
+	temp1 = 0;
+	if (outpdimm->all_DIMMs_registered)
+		for (j = 0; j < 16; j++) {
+			outpdimm->rcw[j] = dimm_params[0].rcw[j];
+			for (i = 1; i < number_of_dimms; i++)
+				if (dimm_params[i].rcw[j] != dimm_params[0].rcw[j]) {
+					temp3 = 1;
+					break;
+				}
+		}
+
+	if (temp1 != 0)
+		printf("ERROR: Mix different RDIMM detected!\n");
+
 #if defined(CONFIG_FSL_DDR3)
 	if (compute_cas_latency_ddr3(dimm_params, outpdimm, number_of_dimms))
 		return 1;