diff options
author | Marc Jones <marc.jones@amd.com> | 2008-09-29 18:09:51 +0000 |
---|---|---|
committer | Marc Jones <marc.jones@amd.com> | 2008-09-29 18:09:51 +0000 |
commit | b5623d18ccc642e14f7df73f5d439bac47e337ae (patch) | |
tree | 9fc39704b1f37256d1593eb9fea3c0e956990faa /src/northbridge/amd | |
parent | a67aab70834fe28c34d4a1c9203f6f1b8462cc38 (diff) |
This patch for the AMD K8 allows a single DIMM to be populated in the
ChannelB slot. Previously a DIMM could only be populated in ChannelB
if there was a DIMM already in ChannelA. This patch doesn't allow unmatched
DIMMs to be populate in ChannelA and ChannelB. In an A & B configuration
the DIMM must still be matched.
Signed-off-by: Marc Jones <marc.jones@amd.com>
Acked-by: Stefan Reinauer <stepan@coresystems.de>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3614 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/amd')
-rw-r--r-- | src/northbridge/amd/amdk8/amdk8_f.h | 3 | ||||
-rw-r--r-- | src/northbridge/amd/amdk8/raminit_f.c | 440 | ||||
-rw-r--r-- | src/northbridge/amd/amdk8/raminit_f_dqs.c | 54 |
3 files changed, 329 insertions, 168 deletions
diff --git a/src/northbridge/amd/amdk8/amdk8_f.h b/src/northbridge/amd/amdk8/amdk8_f.h index d1fcb8bb37..20db98ff53 100644 --- a/src/northbridge/amd/amdk8/amdk8_f.h +++ b/src/northbridge/amd/amdk8/amdk8_f.h @@ -481,8 +481,9 @@ struct mem_info { // pernode uint8_t is_registered; uint8_t is_ecc; uint8_t is_Width128; + uint8_t is_64MuxMode; uint8_t memclk_set; // we need to use this to retrieve the mem param - uint8_t rsv[3]; + uint8_t rsv[2]; } __attribute__((packed)); struct link_pair_st { diff --git a/src/northbridge/amd/amdk8/raminit_f.c b/src/northbridge/amd/amdk8/raminit_f.c index a1a3522500..94b1d461ef 100644 --- a/src/northbridge/amd/amdk8/raminit_f.c +++ b/src/northbridge/amd/amdk8/raminit_f.c @@ -849,7 +849,7 @@ static void spd_get_dimm_size(unsigned device, struct dimm_size *sz) static void set_dimm_size(const struct mem_controller *ctrl, - struct dimm_size *sz, unsigned index, int is_Width128) + struct dimm_size *sz, unsigned index, struct mem_info *meminfo) { uint32_t base0, base1; @@ -872,7 +872,7 @@ static void set_dimm_size(const struct mem_controller *ctrl, } /* Double the size if we are using dual channel memory */ - if (is_Width128) { + if (meminfo->is_Width128) { base0 = (base0 << 1) | (base0 & 1); base1 = (base1 << 1) | (base1 & 1); } @@ -881,15 +881,20 @@ static void set_dimm_size(const struct mem_controller *ctrl, base0 &= ~0xe007fffe; base1 &= ~0xe007fffe; - /* Set the appropriate DIMM base address register */ - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), base0); - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), base1); + if (!(meminfo->dimm_mask & 0x0F) && (meminfo->dimm_mask & 0xF0)) { /* channelB only? */ + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 4) << 2), base0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 5) << 2), base1); + } else { + /* Set the appropriate DIMM base address register */ + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 0) << 2), base0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 1) << 2), base1); #if QRANK_DIMM_SUPPORT == 1 - if (sz->rank == 4) { - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+4)<<2), base0); - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+5)<<2), base1); - } + if (sz->rank == 4) { + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 4) << 2), base0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 5) << 2), base1); + } #endif + } /* Enable the memory clocks for this DIMM by Clear the MemClkDis bit*/ if (base0) { @@ -903,24 +908,31 @@ static void set_dimm_size(const struct mem_controller *ctrl, ClkDis0 = DTL_MemClkDis0_S1g1; #endif - dword = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW); //Channel A - dword &= ~(ClkDis0 >> index); -#if QRANK_DIMM_SUPPORT == 1 - if (sz->rank == 4) { - dword &= ~(ClkDis0 >> (index+2)); - } -#endif - pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dword); - - if (is_Width128) { //Channel B + if (!(meminfo->dimm_mask & 0x0F) && (meminfo->dimm_mask & 0xF0)) { /* channelB only? */ dword = pci_read_config32(ctrl->f2, DRAM_CTRL_MISC); dword &= ~(ClkDis0 >> index); + pci_write_config32(ctrl->f2, DRAM_CTRL_MISC, dword); + + } else { + dword = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW); //Channel A + dword &= ~(ClkDis0 >> index); #if QRANK_DIMM_SUPPORT == 1 if (sz->rank == 4) { dword &= ~(ClkDis0 >> (index+2)); } #endif - pci_write_config32(ctrl->f2, DRAM_CTRL_MISC, dword); + pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dword); + + if (meminfo->is_Width128) { // ChannelA+B + dword = pci_read_config32(ctrl->f2, DRAM_CTRL_MISC); + dword &= ~(ClkDis0 >> index); +#if QRANK_DIMM_SUPPORT == 1 + if (sz->rank == 4) { + dword &= ~(ClkDis0 >> (index+2)); + } +#endif + pci_write_config32(ctrl->f2, DRAM_CTRL_MISC, dword); + } } } @@ -943,7 +955,8 @@ static void set_dimm_size(const struct mem_controller *ctrl, static void set_dimm_cs_map(const struct mem_controller *ctrl, - struct dimm_size *sz, unsigned index) + struct dimm_size *sz, unsigned index, + struct mem_info *meminfo) { static const uint8_t cs_map_aaa[24] = { /* (bank=2, row=13, col=9)(3, 16, 11) ---> (0, 0, 0) (1, 3, 2) */ @@ -961,6 +974,9 @@ static void set_dimm_cs_map(const struct mem_controller *ctrl, uint32_t map; + if (!(meminfo->dimm_mask & 0x0F) && (meminfo->dimm_mask & 0xF0)) { /* channelB only? */ + index += 2; + } map = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP); map &= ~(0xf << (index * 4)); #if QRANK_DIMM_SUPPORT == 1 @@ -986,24 +1002,31 @@ static void set_dimm_cs_map(const struct mem_controller *ctrl, } -static long spd_set_ram_size(const struct mem_controller *ctrl, long dimm_mask, +static long spd_set_ram_size(const struct mem_controller *ctrl, struct mem_info *meminfo) { int i; for (i = 0; i < DIMM_SOCKETS; i++) { struct dimm_size *sz = &(meminfo->sz[i]); - if (!(dimm_mask & (1 << i))) { - continue; + u32 spd_device = ctrl->channel0[i]; + + if (!(meminfo->dimm_mask & (1 << i))) { + if (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } else { + continue; + } } - spd_get_dimm_size(ctrl->channel0[i], sz); + + spd_get_dimm_size(spd_device, sz); if (sz->per_rank == 0) { return -1; /* Report SPD error */ } - set_dimm_size(ctrl, sz, i, meminfo->is_Width128); - set_dimm_cs_map (ctrl, sz, i); + set_dimm_size(ctrl, sz, i, meminfo); + set_dimm_cs_map (ctrl, sz, i, meminfo); } - return dimm_mask; + return meminfo->dimm_mask; } @@ -1243,9 +1266,9 @@ static unsigned long order_chip_selects(const struct mem_controller *ctrl) /* Write the new base register */ pci_write_config32(ctrl->f2, DRAM_CSBASE + (canidate << 2), csbase); /* Write the new mask register */ - if ((canidate & 1) == 0) { //only have 4 CSMASK - pci_write_config32(ctrl->f2, DRAM_CSMASK + ((canidate>>1) << 2), csmask); - } + if ((canidate & 1) == 0) { //only have 4 CSMASK + pci_write_config32(ctrl->f2, DRAM_CSMASK + ((canidate >> 1) << 2), csmask); + } } /* Return the memory size in K */ @@ -1299,27 +1322,32 @@ static void order_dimms(const struct mem_controller *ctrl, static long disable_dimm(const struct mem_controller *ctrl, unsigned index, - long dimm_mask, struct mem_info *meminfo) + struct mem_info *meminfo) { print_debug("disabling dimm"); print_debug_hex8(index); print_debug("\r\n"); - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), 0); - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), 0); + if (!(meminfo->dimm_mask & 0x0F) && (meminfo->dimm_mask & 0xF0)) { /* channelB only? */ + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 4) << 2), 0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 5) << 2), 0); + } else { + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 0) << 2), 0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 1) << 2), 0); #if QRANK_DIMM_SUPPORT == 1 - if (meminfo->sz[index].rank == 4) { - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+4)<<2), 0); - pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+5)<<2), 0); - } + if (meminfo->sz[index].rank == 4) { + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 4) << 2), 0); + pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1) + 5) << 2), 0); + } #endif + } - dimm_mask &= ~(1 << index); - return dimm_mask; + meminfo->dimm_mask &= ~(1 << index); + return meminfo->dimm_mask; } static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, - long dimm_mask, struct mem_info *meminfo) + struct mem_info *meminfo) { int i; uint32_t registered; @@ -1327,29 +1355,31 @@ static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, registered = 0; for (i = 0; (i < DIMM_SOCKETS); i++) { int value; - if (!(dimm_mask & (1 << i))) { - continue; + u32 spd_device = ctrl->channel0[i]; + if (!(meminfo->dimm_mask & (1 << i))) { + if (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } else { + continue; + } } - - value = spd_read_byte(ctrl->channel0[i], SPD_DIMM_TYPE); + value = spd_read_byte(spd_device, SPD_DIMM_TYPE); if (value < 0) { return -1; } /* Registered dimm ? */ value &= 0x3f; - if ((value == SPD_DIMM_TYPE_RDIMM) || - (value == SPD_DIMM_TYPE_mRDIMM)) { - /* check SPD_MOD_ATTRIB to verify it is - SPD_MOD_ATTRIB_REGADC (0x11)? */ + if ((value == SPD_DIMM_TYPE_RDIMM) || (value == SPD_DIMM_TYPE_mRDIMM)) { + //check SPD_MOD_ATTRIB to verify it is SPD_MOD_ATTRIB_REGADC (0x11)? registered |= (1<<i); } } if (is_opteron(ctrl)) { #if 0 - if ( registered != (dimm_mask & ((1<<DIMM_SOCKETS)-1)) ) { - dimm_mask &= (registered | (registered << DIMM_SOCKETS) ); //disable unbuffed dimm + if ( registered != (meminfo->dimm_mask & ((1<<DIMM_SOCKETS)-1)) ) { + meminfo->dimm_mask &= (registered | (registered << DIMM_SOCKETS) ); //disable unbuffed dimm // die("Mixed buffered and registered dimms not supported"); } //By yhlu for debug M2, s1g1 can do dual channel, but it use unbuffer DIMM @@ -1376,7 +1406,7 @@ static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, print_debug("Unbuffered\r\n"); } #endif - return dimm_mask; + return meminfo->dimm_mask; } @@ -1406,8 +1436,7 @@ static unsigned int spd_detect_dimms(const struct mem_controller *ctrl) return dimm_mask; } - -static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_mask, struct mem_info *meminfo) +static long spd_enable_2channels(const struct mem_controller *ctrl, struct mem_info *meminfo) { int i; uint32_t nbcap; @@ -1438,13 +1467,21 @@ static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_ma 41, /* *Minimum Active to Active/Auto Refresh Time(Trc) */ 42, /* *Minimum Auto Refresh Command Time(Trfc) */ }; + u32 dcl, dcm; + +/* S1G1 and AM2 sockets are Mod64BitMux capable. */ +#if CPU_SOCKET_TYPE == 0x11 || CPU_SOCKET_TYPE == 0x12 + u8 mux_cap = 1; +#else + u8 mux_cap = 0; +#endif + /* If the dimms are not in pairs do not do dual channels */ - if ((dimm_mask & ((1 << DIMM_SOCKETS) - 1)) != - ((dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { + if ((meminfo->dimm_mask & ((1 << DIMM_SOCKETS) - 1)) != + ((meminfo->dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { goto single_channel; } - /* If the cpu is not capable of doing dual channels - don't do dual channels */ + /* If the cpu is not capable of doing dual channels don't do dual channels */ nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP); if (!(nbcap & NBCAP_128Bit)) { goto single_channel; @@ -1454,7 +1491,7 @@ static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_ma int value0, value1; int j; /* If I don't have a dimm skip this one */ - if (!(dimm_mask & (1 << i))) { + if (!(meminfo->dimm_mask & (1 << i))) { continue; } device0 = ctrl->channel0[i]; @@ -1476,18 +1513,42 @@ static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_ma } } print_spew("Enabling dual channel memory\r\n"); - uint32_t dcl; dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW); dcl &= ~DCL_BurstLength32; /* 32byte mode may be preferred in platforms that include graphics controllers that generate a lot of 32-bytes system memory accesses 32byte mode is not supported when the DRAM interface is 128 bits wides, even 32byte mode is set, system still use 64 byte mode */ dcl |= DCL_Width128; pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl); meminfo->is_Width128 = 1; - return dimm_mask; + return meminfo->dimm_mask; + single_channel: - dimm_mask &= ~((1 << (DIMM_SOCKETS *2)) - (1 << DIMM_SOCKETS)); meminfo->is_Width128 = 0; - return dimm_mask; + meminfo->is_64MuxMode = 0; + + /* single dimm */ + if ((meminfo->dimm_mask & ((1 << DIMM_SOCKETS) - 1)) != + ((meminfo->dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { + if (((meminfo->dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { + /* mux capable and single dimm in channelB */ + if (mux_cap) { + printk_spew("Enable 64MuxMode & BurstLength32\n"); + dcm = pci_read_config32(ctrl->f2, DRAM_CTRL_MISC); + dcm |= DCM_Mode64BitMux; + pci_write_config32(ctrl->f2, DRAM_CTRL_MISC, dcm); + dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW); + //dcl |= DCL_BurstLength32; /* 32byte mode for channelB only */ + pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl); + meminfo->is_64MuxMode = 1; + } else { + meminfo->dimm_mask &= ~((1 << (DIMM_SOCKETS * 2)) - (1 << DIMM_SOCKETS)); + } + } + } else { /* unmatched dual dimms ? */ + /* unmatched dual dimms not supported by meminit code. Use single channelA dimm. */ + meminfo->dimm_mask &= ~((1 << (DIMM_SOCKETS * 2)) - (1 << DIMM_SOCKETS)); + printk_spew("Unmatched dual dimms. Use single channelA dimm.\n"); + } + return meminfo->dimm_mask; } struct mem_param { @@ -1633,7 +1694,7 @@ static unsigned convert_to_linear(unsigned value) return value; } -static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller *ctrl, long dimm_mask, struct mem_info *meminfo) +static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller *ctrl, struct mem_info *meminfo) { /* Compute the minimum cycle time for these dimms */ struct spd_set_memclk_result result; @@ -1670,9 +1731,15 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * int index; int latencies; int latency; + u32 spd_device = ctrl->channel0[i]; - if (!(dimm_mask & (1 << i))) { - continue; + print_tx("1.1 dimm_mask:", meminfo->dimm_mask); + if (!(meminfo->dimm_mask & (1 << i))) { + if (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } else { + continue; + } } /* First find the supported CAS latencies @@ -1685,7 +1752,7 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * new_cycle_time = 0x500; new_latency = 6; - latencies = spd_read_byte(ctrl->channel0[i], SPD_CAS_LAT); + latencies = spd_read_byte(spd_device, SPD_CAS_LAT); if (latencies <= 0) continue; print_tx("i:",i); @@ -1700,7 +1767,7 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * (!(latencies & (1 << latency)))) { continue; } - value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]); + value = spd_read_byte(spd_device, latency_indicies[index]); if (value < 0) { goto hw_error; } @@ -1753,16 +1820,22 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * print_tx("3 min_cycle_time:", min_cycle_time); print_tx("3 min_latency:", min_latency); - for (i = 0; (i < DIMM_SOCKETS) && (ctrl->channel0[i]); i++) { + for (i = 0; (i < DIMM_SOCKETS); i++) { int latencies; int latency; int index; int value; - if (!(dimm_mask & (1 << i))) { - continue; + u32 spd_device = ctrl->channel0[i]; + + if (!(meminfo->dimm_mask & (1 << i))) { + if (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } else { + continue; + } } - latencies = spd_read_byte(ctrl->channel0[i], SPD_CAS_LAT); + latencies = spd_read_byte(spd_device, SPD_CAS_LAT); if (latencies < 0) goto hw_error; if (latencies == 0) { continue; @@ -1785,7 +1858,7 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * } /* Read the min_cycle_time for this latency */ - value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]); + value = spd_read_byte(spd_device, latency_indicies[index]); if (value < 0) goto hw_error; value = convert_to_linear(value); @@ -1797,7 +1870,7 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * } /* Otherwise I have an error, disable the dimm */ dimm_err: - dimm_mask = disable_dimm(ctrl, i, dimm_mask, meminfo); + meminfo->dimm_mask = disable_dimm(ctrl, i, meminfo); } print_tx("4 min_cycle_time:", min_cycle_time); @@ -1820,7 +1893,7 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * value |= (min_latency - DTL_TCL_BASE) << DTL_TCL_SHIFT; pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, value); - result.dimm_mask = dimm_mask; + result.dimm_mask = meminfo->dimm_mask; return result; hw_error: result.param = (const struct mem_param *)0; @@ -1837,18 +1910,26 @@ static unsigned convert_to_1_4(unsigned value) valuex = fraction [value & 0x7]; return valuex; } -static int update_dimm_Trc(const struct mem_controller *ctrl, const struct mem_param *param, int i) +static int update_dimm_Trc(const struct mem_controller *ctrl, + const struct mem_param *param, + int i, long dimm_mask) { unsigned clocks, old_clocks; uint32_t dtl; int value; int value2; - value = spd_read_byte(ctrl->channel0[i], SPD_TRC); + u32 spd_device = ctrl->channel0[i]; + + if (!(dimm_mask & (1 << i)) && (dimm_mask & (1 << (DIMM_SOCKETS + i)))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } + + value = spd_read_byte(spd_device, SPD_TRC); if (value < 0) return -1; - value2 = spd_read_byte(ctrl->channel0[i], SPD_TRC -1); - value <<= 2; - value += convert_to_1_4(value2>>4); + value2 = spd_read_byte(spd_device, SPD_TRC -1); + value <<= 2; + value += convert_to_1_4(value2>>4); value *=10; @@ -1878,9 +1959,16 @@ static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_ unsigned clocks, old_clocks; uint32_t dth; int value; + u8 ch_b = 0; + u32 spd_device = ctrl->channel0[i]; + + if (!(meminfo->dimm_mask & (1 << i)) && (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i)))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + ch_b = 2; /* offset to channelB trfc setting */ + } //get the cs_size --> logic dimm size - value = spd_read_byte(ctrl->channel0[i], SPD_PRI_WIDTH); + value = spd_read_byte(spd_device, SPD_PRI_WIDTH); if (value < 0) { return -1; } @@ -1891,24 +1979,31 @@ static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_ dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH); - old_clocks = ((dth >> (DTH_TRFC0_SHIFT+i*3)) & DTH_TRFC_MASK); + old_clocks = ((dth >> (DTH_TRFC0_SHIFT + ((i + ch_b) * 3))) & DTH_TRFC_MASK); + if (old_clocks >= clocks) { // some one did it? return 1; } - dth &= ~(DTH_TRFC_MASK << (DTH_TRFC0_SHIFT+i*3)); - dth |= clocks << (DTH_TRFC0_SHIFT+i*3); + dth &= ~(DTH_TRFC_MASK << (DTH_TRFC0_SHIFT + ((i + ch_b) * 3))); + dth |= clocks << (DTH_TRFC0_SHIFT + ((i + ch_b) * 3)); pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth); return 1; } -static int update_dimm_TT_1_4(const struct mem_controller *ctrl, const struct mem_param *param, int i, +static int update_dimm_TT_1_4(const struct mem_controller *ctrl, const struct mem_param *param, int i, long dimm_mask, unsigned TT_REG, unsigned SPD_TT, unsigned TT_SHIFT, unsigned TT_MASK, unsigned TT_BASE, unsigned TT_MIN, unsigned TT_MAX ) { unsigned clocks, old_clocks; uint32_t dtl; int value; - value = spd_read_byte(ctrl->channel0[i], SPD_TT); //already in 1/4 ns + u32 spd_device = ctrl->channel0[i]; + + if (!(dimm_mask & (1 << i)) && (dimm_mask & (1 << (DIMM_SOCKETS + i)))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } + + value = spd_read_byte(spd_device, SPD_TT); //already in 1/4 ns if (value < 0) return -1; value *=10; clocks = (value + param->divisor -1)/param->divisor; @@ -1917,14 +2012,14 @@ static int update_dimm_TT_1_4(const struct mem_controller *ctrl, const struct me } if (clocks > TT_MAX) { - return 0; + return 0; } dtl = pci_read_config32(ctrl->f2, TT_REG); old_clocks = ((dtl >> TT_SHIFT) & TT_MASK) + TT_BASE; if (old_clocks >= clocks) { //some one did it? -// clocks = old_clocks; +// clocks = old_clocks; return 1; } dtl &= ~(TT_MASK << TT_SHIFT); @@ -1935,24 +2030,28 @@ static int update_dimm_TT_1_4(const struct mem_controller *ctrl, const struct me static int update_dimm_Trcd(const struct mem_controller *ctrl, - const struct mem_param *param, int i) + const struct mem_param *param, int i, long dimm_mask) { - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_LOW, SPD_TRCD, DTL_TRCD_SHIFT, DTL_TRCD_MASK, DTL_TRCD_BASE, DTL_TRCD_MIN, DTL_TRCD_MAX); + return update_dimm_TT_1_4(ctrl, param, i, dimm_mask, DRAM_TIMING_LOW, SPD_TRCD, DTL_TRCD_SHIFT, DTL_TRCD_MASK, DTL_TRCD_BASE, DTL_TRCD_MIN, DTL_TRCD_MAX); } - -static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i) +static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i, long dimm_mask) { - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_LOW, SPD_TRRD, DTL_TRRD_SHIFT, DTL_TRRD_MASK, DTL_TRRD_BASE, DTL_TRRD_MIN, DTL_TRRD_MAX); + return update_dimm_TT_1_4(ctrl, param, i, dimm_mask, DRAM_TIMING_LOW, SPD_TRRD, DTL_TRRD_SHIFT, DTL_TRRD_MASK, DTL_TRRD_BASE, DTL_TRRD_MIN, DTL_TRRD_MAX); } - -static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i) +static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i, long dimm_mask) { unsigned clocks, old_clocks; uint32_t dtl; int value; - value = spd_read_byte(ctrl->channel0[i], SPD_TRAS); //in 1 ns + u32 spd_device = ctrl->channel0[i]; + + if (!(dimm_mask & (1 << i)) && (dimm_mask & (1 << (DIMM_SOCKETS + i)))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } + + value = spd_read_byte(spd_device, SPD_TRAS); //in 1 ns if (value < 0) return -1; print_tx("update_dimm_Tras: 0 value=", value); @@ -1985,9 +2084,9 @@ static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_ } static int update_dimm_Trp(const struct mem_controller *ctrl, - const struct mem_param *param, int i) + const struct mem_param *param, int i, long dimm_mask) { - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_LOW, SPD_TRP, DTL_TRP_SHIFT, DTL_TRP_MASK, DTL_TRP_BASE, DTL_TRP_MIN, DTL_TRP_MAX); + return update_dimm_TT_1_4(ctrl, param, i, dimm_mask, DRAM_TIMING_LOW, SPD_TRP, DTL_TRP_SHIFT, DTL_TRP_MASK, DTL_TRP_BASE, DTL_TRP_MIN, DTL_TRP_MAX); } @@ -2001,22 +2100,28 @@ static int update_dimm_Trtp(const struct mem_controller *ctrl, dword = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW); if ((dword & DCL_BurstLength32)) offset = 0; } - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_LOW, SPD_TRTP, DTL_TRTP_SHIFT, DTL_TRTP_MASK, DTL_TRTP_BASE+offset, DTL_TRTP_MIN+offset, DTL_TRTP_MAX+offset); + return update_dimm_TT_1_4(ctrl, param, i, meminfo->dimm_mask, DRAM_TIMING_LOW, SPD_TRTP, DTL_TRTP_SHIFT, DTL_TRTP_MASK, DTL_TRTP_BASE+offset, DTL_TRTP_MIN+offset, DTL_TRTP_MAX+offset); } -static int update_dimm_Twr(const struct mem_controller *ctrl, const struct mem_param *param, int i) +static int update_dimm_Twr(const struct mem_controller *ctrl, const struct mem_param *param, int i, long dimm_mask) { - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_LOW, SPD_TWR, DTL_TWR_SHIFT, DTL_TWR_MASK, DTL_TWR_BASE, DTL_TWR_MIN, DTL_TWR_MAX); + return update_dimm_TT_1_4(ctrl, param, i, dimm_mask, DRAM_TIMING_LOW, SPD_TWR, DTL_TWR_SHIFT, DTL_TWR_MASK, DTL_TWR_BASE, DTL_TWR_MIN, DTL_TWR_MAX); } static int update_dimm_Tref(const struct mem_controller *ctrl, - const struct mem_param *param, int i) + const struct mem_param *param, int i, long dimm_mask) { uint32_t dth, dth_old; int value; - value = spd_read_byte(ctrl->channel0[i], SPD_TREF); // 0: 15.625us, 1: 3.9us 2: 7.8 us.... + u32 spd_device = ctrl->channel0[i]; + + if (!(dimm_mask & (1 << i)) && (dimm_mask & (1 << (DIMM_SOCKETS + i)))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } + + value = spd_read_byte(spd_device, SPD_TREF); // 0: 15.625us, 1: 3.9us 2: 7.8 us.... if (value < 0) return -1; if (value == 1 ) { @@ -2093,8 +2198,13 @@ static uint32_t get_extra_dimm_mask(const struct mem_controller *ctrl, mask_page_1k = 0; for (i = 0; i < DIMM_SOCKETS; i++) { + u32 spd_device = ctrl->channel0[i]; if (!(dimm_mask & (1 << i))) { - continue; + if (dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + } else { + continue; + } } if (meminfo->sz[i].rank == 1) { @@ -2106,7 +2216,7 @@ static uint32_t get_extra_dimm_mask(const struct mem_controller *ctrl, } - value = spd_read_byte(ctrl->channel0[i], SPD_PRI_WIDTH); + value = spd_read_byte(spd_device, SPD_PRI_WIDTH); #if QRANK_DIMM_SUPPORT == 1 rank = meminfo->sz[i].rank; @@ -2156,7 +2266,7 @@ static int count_ones(uint32_t dimm_mask) int dimms; unsigned index; dimms = 0; - for (index = 0; index < DIMM_SOCKETS; index++, dimm_mask>>=1) { + for (index = 0; index < (2 * DIMM_SOCKETS); index++, dimm_mask >>= 1) { if (dimm_mask & 1) { dimms++; } @@ -2194,7 +2304,7 @@ static void set_DramTerm(const struct mem_controller *ctrl, static void set_ecc(const struct mem_controller *ctrl, - const struct mem_param *param, long dimm_mask, struct mem_info *meminfo) + const struct mem_param *param, struct mem_info *meminfo) { int i; int value; @@ -2218,9 +2328,14 @@ static void set_ecc(const struct mem_controller *ctrl, } for (i = 0; i < DIMM_SOCKETS; i++) { - - if (!(dimm_mask & (1 << i))) { - continue; + u32 spd_device = ctrl->channel0[i]; + if (!(meminfo->dimm_mask & (1 << i))) { + if (meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) { /* channelB only? */ + spd_device = ctrl->channel1[i]; + printk_debug("set_ecc spd_device: 0x%x\n", spd_device); + } else { + continue; + } } value = spd_read_byte(ctrl->channel0[i], SPD_DIMM_CONF_TYPE); @@ -2237,13 +2352,11 @@ static void set_ecc(const struct mem_controller *ctrl, static int update_dimm_Twtr(const struct mem_controller *ctrl, - const struct mem_param *param, int i) + const struct mem_param *param, int i, long dimm_mask) { - - return update_dimm_TT_1_4(ctrl, param, i, DRAM_TIMING_HIGH, SPD_TWTR, DTH_TWTR_SHIFT, DTH_TWTR_MASK, DTH_TWTR_BASE, DTH_TWTR_MIN, DTH_TWTR_MAX); + return update_dimm_TT_1_4(ctrl, param, i, dimm_mask, DRAM_TIMING_HIGH, SPD_TWTR, DTH_TWTR_SHIFT, DTH_TWTR_MASK, DTH_TWTR_BASE, DTH_TWTR_MIN, DTH_TWTR_MAX); } - static void set_TT(const struct mem_controller *ctrl, const struct mem_param *param, unsigned TT_REG, unsigned TT_SHIFT, unsigned TT_MASK, unsigned TT_BASE, unsigned TT_MIN, unsigned TT_MAX, @@ -2510,18 +2623,25 @@ static void set_misc_timing(const struct mem_controller *ctrl, struct mem_info * } #endif - /* Program the Output Driver Compensation Control Registers (Function 2:Offset 0x9c, index 0, 0x20) */ - pci_write_config32_index_wait(ctrl->f2, 0x98, 0, dword); - if (meminfo->is_Width128) { + if (!(meminfo->dimm_mask & 0x0F) && (meminfo->dimm_mask & 0xF0)) { /* channelB only? */ + /* Program the Output Driver Compensation Control Registers (Function 2:Offset 0x9c, index 0, 0x20) */ pci_write_config32_index_wait(ctrl->f2, 0x98, 0x20, dword); - } - /* Program the Address Timing Control Registers (Function 2:Offset 0x9c, index 4, 0x24) */ - pci_write_config32_index_wait(ctrl->f2, 0x98, 4, dwordx); - if (meminfo->is_Width128) { + /* Program the Address Timing Control Registers (Function 2:Offset 0x9c, index 4, 0x24) */ pci_write_config32_index_wait(ctrl->f2, 0x98, 0x24, dwordx); - } + } else { + /* Program the Output Driver Compensation Control Registers (Function 2:Offset 0x9c, index 0, 0x20) */ + pci_write_config32_index_wait(ctrl->f2, 0x98, 0, dword); + if (meminfo->is_Width128) { + pci_write_config32_index_wait(ctrl->f2, 0x98, 0x20, dword); + } + /* Program the Address Timing Control Registers (Function 2:Offset 0x9c, index 4, 0x24) */ + pci_write_config32_index_wait(ctrl->f2, 0x98, 4, dwordx); + if (meminfo->is_Width128) { + pci_write_config32_index_wait(ctrl->f2, 0x98, 0x24, dwordx); + } + } } @@ -2564,44 +2684,47 @@ static void set_RdWrQByp(const struct mem_controller *ctrl, } -static long spd_set_dram_timing(const struct mem_controller *ctrl, const struct mem_param *param, long dimm_mask, struct mem_info *meminfo) +static long spd_set_dram_timing(const struct mem_controller *ctrl, + const struct mem_param *param, + struct mem_info *meminfo) { int i; for (i = 0; i < DIMM_SOCKETS; i++) { int rc; - if (!(dimm_mask & (1 << i))) { + if (!(meminfo->dimm_mask & (1 << i)) && + !(meminfo->dimm_mask & (1 << (DIMM_SOCKETS + i))) ) { continue; } - print_tx("dimm socket: ", i); + print_tx("spd_set_dram_timing dimm socket: ", i); /* DRAM Timing Low Register */ print_t("\ttrc\r\n"); - if ((rc = update_dimm_Trc (ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Trc (ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttrcd\r\n"); - if ((rc = update_dimm_Trcd(ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Trcd(ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttrrd\r\n"); - if ((rc = update_dimm_Trrd(ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Trrd(ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttras\r\n"); - if ((rc = update_dimm_Tras(ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Tras(ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttrp\r\n"); - if ((rc = update_dimm_Trp (ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Trp (ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttrtp\r\n"); if ((rc = update_dimm_Trtp(ctrl, param, i, meminfo)) <= 0) goto dimm_err; print_t("\ttwr\r\n"); - if ((rc = update_dimm_Twr (ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Twr (ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; /* DRAM Timing High Register */ print_t("\ttref\r\n"); - if ((rc = update_dimm_Tref(ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Tref(ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttwtr\r\n"); - if ((rc = update_dimm_Twtr(ctrl, param, i)) <= 0) goto dimm_err; + if ((rc = update_dimm_Twtr(ctrl, param, i, meminfo->dimm_mask)) <= 0) goto dimm_err; print_t("\ttrfc\r\n"); if ((rc = update_dimm_Trfc(ctrl, param, i, meminfo)) <= 0) goto dimm_err; @@ -2610,14 +2733,13 @@ static long spd_set_dram_timing(const struct mem_controller *ctrl, const struct continue; dimm_err: + printk_debug("spd_set_dram_timing dimm_err!\n"); if (rc < 0) { return -1; } - dimm_mask = disable_dimm(ctrl, i, dimm_mask, meminfo); + meminfo->dimm_mask = disable_dimm(ctrl, i, meminfo); } - meminfo->dimm_mask = dimm_mask; // store final dimm_mask - get_extra_dimm_mask(ctrl, meminfo); // will be used by RDqsEn and dimm_x4 /* DRAM Timing Low Register */ @@ -2636,7 +2758,7 @@ static long spd_set_dram_timing(const struct mem_controller *ctrl, const struct set_RDqsEn(ctrl, param, meminfo); /* DRAM Config Low */ - set_ecc(ctrl, param, dimm_mask, meminfo); + set_ecc(ctrl, param, meminfo); set_dimm_x4(ctrl, param, meminfo); set_DramTerm(ctrl, param, meminfo); @@ -2644,7 +2766,7 @@ static long spd_set_dram_timing(const struct mem_controller *ctrl, const struct set_idle_cycle_limit(ctrl, param); set_RdWrQByp(ctrl, param); - return dimm_mask; + return meminfo->dimm_mask; } static void sdram_set_spd_registers(const struct mem_controller *ctrl, @@ -2654,7 +2776,6 @@ static void sdram_set_spd_registers(const struct mem_controller *ctrl, const struct mem_param *param; struct mem_param paramx; struct mem_info *meminfo; - long dimm_mask; #if 1 if (!sysinfo->ctrl_present[ctrl->node_id]) { return; @@ -2665,24 +2786,35 @@ static void sdram_set_spd_registers(const struct mem_controller *ctrl, print_debug_addr("sdram_set_spd_registers: paramx :", ¶mx); activate_spd_rom(ctrl); - dimm_mask = spd_detect_dimms(ctrl); - if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) { + meminfo->dimm_mask = spd_detect_dimms(ctrl); + + print_tx("sdram_set_spd_registers: dimm_mask=0x%x\n", meminfo->dimm_mask); + + if (!(meminfo->dimm_mask & ((1 << 2*DIMM_SOCKETS) - 1))) + { print_debug("No memory for this cpu\r\n"); return; } - dimm_mask = spd_enable_2channels(ctrl, dimm_mask, meminfo); - if (dimm_mask < 0) + meminfo->dimm_mask = spd_enable_2channels(ctrl, meminfo); + print_tx("spd_enable_2channels: dimm_mask=0x%x\n", meminfo->dimm_mask); + if (meminfo->dimm_mask == -1) goto hw_spd_err; - dimm_mask = spd_set_ram_size(ctrl , dimm_mask, meminfo); - if (dimm_mask < 0) + + meminfo->dimm_mask = spd_set_ram_size(ctrl, meminfo); + print_tx("spd_set_ram_size: dimm_mask=0x%x\n", meminfo->dimm_mask); + if (meminfo->dimm_mask == -1) goto hw_spd_err; - dimm_mask = spd_handle_unbuffered_dimms(ctrl, dimm_mask, meminfo); - if (dimm_mask < 0) + + meminfo->dimm_mask = spd_handle_unbuffered_dimms(ctrl, meminfo); + print_tx("spd_handle_unbuffered_dimms: dimm_mask=0x%x\n", meminfo->dimm_mask); + if (meminfo->dimm_mask == -1) goto hw_spd_err; - result = spd_set_memclk(ctrl, dimm_mask, meminfo); + + result = spd_set_memclk(ctrl, meminfo); param = result.param; - dimm_mask = result.dimm_mask; - if (dimm_mask < 0) + meminfo->dimm_mask = result.dimm_mask; + print_tx("spd_set_memclk: dimm_mask=0x%x\n", meminfo->dimm_mask); + if (meminfo->dimm_mask == -1) goto hw_spd_err; //store memclk set to sysinfo, incase we need rebuilt param again @@ -2692,8 +2824,9 @@ static void sdram_set_spd_registers(const struct mem_controller *ctrl, paramx.divisor = get_exact_divisor(param->dch_memclk, paramx.divisor); - dimm_mask = spd_set_dram_timing(ctrl, ¶mx , dimm_mask, meminfo); // dimm_mask will be stored to meminfo->dimm_mask - if (dimm_mask < 0) + meminfo->dimm_mask = spd_set_dram_timing(ctrl, ¶mx, meminfo); + print_tx("spd_set_dram_timing: dimm_mask=0x%x\n", meminfo->dimm_mask); + if (meminfo->dimm_mask == -1) goto hw_spd_err; order_dimms(ctrl, meminfo); @@ -2701,8 +2834,7 @@ static void sdram_set_spd_registers(const struct mem_controller *ctrl, return; hw_spd_err: /* Unrecoverable error reading SPD data */ - print_err("SPD error - reset\r\n"); - hard_reset(); + die("Unrecoverable error reading SPD data. No qualified DIMMs?"); return; } diff --git a/src/northbridge/amd/amdk8/raminit_f_dqs.c b/src/northbridge/amd/amdk8/raminit_f_dqs.c index 1b5af6864f..4c97bd40df 100644 --- a/src/northbridge/amd/amdk8/raminit_f_dqs.c +++ b/src/northbridge/amd/amdk8/raminit_f_dqs.c @@ -1,6 +1,23 @@ /* - yhlu 2005.10 dqs training + * This file is part of the coreboot project. + * + * Copyright (C) 2005 YingHai Lu + * Copyright (C) 2008 Advanced Micro Devices, 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 Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + //0: mean no debug info #define DQS_TRAIN_DEBUG 0 @@ -187,11 +204,6 @@ static unsigned ChipSelPresent(const struct mem_controller *ctrl, unsigned cs_id static unsigned RcvrRankEnabled(const struct mem_controller *ctrl, int channel, int cs_idx, unsigned is_Width128, struct sys_info *sysinfo) { - /* FIXME: process 64Muxed */ - if(!is_Width128) { - if(channel) return 0; // no channel b - } - return ChipSelPresent(ctrl, cs_idx, sysinfo); } @@ -386,7 +398,11 @@ static void ResetDCTWrPtr(const struct mem_controller *ctrl) { uint32_t dword; unsigned index = 0x10; - + + dword = pci_read_config32_index_wait(ctrl->f2, 0x98, index); + pci_write_config32_index_wait(ctrl->f2, 0x98, index, dword); + + index += 0x20; dword = pci_read_config32_index_wait(ctrl->f2, 0x98, index); pci_write_config32_index_wait(ctrl->f2, 0x98, index, dword); @@ -504,9 +520,9 @@ static unsigned TrainRcvrEn(const struct mem_controller *ctrl, unsigned Pass, st unsigned PatternA; unsigned PatternB; - unsigned TestAddr0, TestAddr0B, TestAddr1, TestAddr1B; + unsigned TestAddr0, TestAddr0B, TestAddr1, TestAddr1B = 0; - unsigned CurrRcvrCHADelay; + unsigned CurrRcvrCHADelay = 0; unsigned tmp; @@ -575,7 +591,14 @@ static unsigned TrainRcvrEn(const struct mem_controller *ctrl, unsigned Pass, st Errors = 0; /* for each channel */ CTLRMaxDelay = 0; - for(channel = 0; (channel < 2) && (!Errors); channel++) + channel = 0; + + if (!(sysinfo->meminfo[ctrl->node_id].dimm_mask & 0x0F) && + (sysinfo->meminfo[ctrl->node_id].dimm_mask & 0xF0)) { /* channelB only? */ + channel = 1; + } + + for ( ; (channel < 2) && (!Errors); channel++) { print_debug_dqs("\tTrainRcvEn51: channel ",channel, 1); @@ -1104,8 +1127,8 @@ static unsigned TrainDQSPos(const struct mem_controller *ctrl, unsigned channel, unsigned TestAddr; unsigned LastTest; - unsigned RnkDlyFilterMax, RnkDlyFilterMin; - unsigned RnkDlySeqPassMax, RnkDlySeqPassMin; + unsigned RnkDlyFilterMax, RnkDlyFilterMin = 0; + unsigned RnkDlySeqPassMax, RnkDlySeqPassMin = 0; Errors = 0; BanksPresent = 0; @@ -1388,8 +1411,13 @@ static unsigned TrainDQSRdWrPos(const struct mem_controller *ctrl, struct sys_in print_debug_addr("TrainDQSRdWrPos: buf_a:", buf_a); Errors = 0; - channel = 0; + + if (!(sysinfo->meminfo[ctrl->node_id].dimm_mask & 0x0F) && + (sysinfo->meminfo[ctrl->node_id].dimm_mask & 0xF0)) { /* channelB only? */ + channel = 1; + } + while( (channel<2) && (!Errors)) { print_debug_dqs("\tTrainDQSRdWrPos: 1 channel ",channel, 1); for(DQSWrDelay = 0; DQSWrDelay < 48; DQSWrDelay++) { |