powerpc/8xxx: Fix bug in memctrl interleaving & bank interleaving on cs0~cs4
Verified on MPC8641HPCN with four DDR2 dimms. Each dimm has dual
rank with 512MB each rank.
Also check dimm size and rank size for memory controller interleaving
Signed-off-by: York Sun <yorksun@freescale.com>
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
index 4a282bc..69c1c7c 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c
@@ -1201,20 +1201,28 @@
/* Chip Select Memory Bounds (CSn_BNDS) */
for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
unsigned long long ea = 0, sa = 0;
+ 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;
- if (popts->ba_intlv_ctl && (i > 0) &&
- ((popts->ba_intlv_ctl & 0x60) != FSL_DDR_CS2_CS3 )) {
- /* Don't set up boundaries for other CS
- * other than CS0, if bank interleaving
- * is enabled and not CS2+CS3 interleaved.
+ 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(i, ddr, popts, dimm_params);
- break;
+ continue;
}
-
- if (dimm_params[i/2].n_ranks == 0) {
+ if (dimm_params[dimm_number].n_ranks == 0) {
debug("Skipping setup of CS%u "
"because n_ranks on DIMM %u is 0\n", i, i/2);
continue;
@@ -1222,16 +1230,34 @@
if (popts->memctl_interleaving && popts->ba_intlv_ctl) {
/*
* This works superbank 2CS
- * There are 2 memory controllers configured
+ * 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.
*/
- unsigned long long rank_density
- = dimm_params[0].capacity;
- ea = (2 * (rank_density >> dbw_cap_adj)) - 1;
+ unsigned long long ctlr_density = 0;
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1:
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ ctlr_density = dimm_params[0].rank_density * 2;
+ break;
+ case FSL_DDR_CS2_CS3:
+ ctlr_density = dimm_params[0].rank_density;
+ 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) {
/*
@@ -1243,8 +1269,6 @@
* controller needs to be programmed into its
* respective CS0_BNDS.
*/
- unsigned long long rank_density
- = dimm_params[i/2].rank_density;
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
@@ -1257,9 +1281,13 @@
/* CS0+CS1 and CS2+CS3 interleaving, CS0_CNDS
* and CS2_CNDS need to be set.
*/
- if (!(i&1)) {
- sa = dimm_params[i/2].base_address;
- ea = sa + (i * (rank_density >>
+ 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;
+ } else {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + (2 * (rank_density >>
dbw_cap_adj)) - 1;
}
break;
@@ -1267,16 +1295,31 @@
/* CS0+CS1 interleaving, CS0_CNDS needs
* to be set
*/
- sa = common_dimm->base_address;
- ea = sa + (2 * (rank_density >> dbw_cap_adj))-1;
+ 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);
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ if (i == 0)
+ ea += (rank_density >> dbw_cap_adj);
break;
case FSL_DDR_CS2_CS3:
/* CS2+CS3 interleaving*/
- if (i == 2) {
- sa = dimm_params[i/2].base_address;
- ea = sa + (2 * (rank_density >>
- dbw_cap_adj)) - 1;
+ 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);
+ } else {
+ sa = 0;
+ ea = 0;
}
+ if (i == 2)
+ ea += (rank_density >> dbw_cap_adj);
break;
default: /* No bank(chip-select) interleaving */
break;
@@ -1292,8 +1335,6 @@
* memory in the two CS0 ranks.
*/
if (i == 0) {
- unsigned long long rank_density
- = dimm_params[0].rank_density;
ea = (2 * (rank_density >> dbw_cap_adj)) - 1;
}
@@ -1303,20 +1344,14 @@
* No rank interleaving and no memory controller
* interleaving.
*/
- unsigned long long rank_density
- = dimm_params[i/2].rank_density;
- sa = dimm_params[i/2].base_address;
+ sa = dimm_params[dimm_number].base_address;
ea = sa + (rank_density >> dbw_cap_adj) - 1;
- if (i&1) {
- if ((dimm_params[i/2].n_ranks == 1)) {
- /* Odd chip select, single-rank dimm */
- sa = 0;
- ea = 0;
- } else {
- /* Odd chip select, dual-rank DIMM */
- sa += rank_density >> dbw_cap_adj;
- ea += rank_density >> dbw_cap_adj;
- }
+ 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;
}
}