+
+static int spd_dimm_loading_socket(const struct mem_controller *ctrl, long dimm_mask, int *freq_1t)
+{
+
+#if CONFIG_CPU_AMD_SOCKET_939
+
+/* + 1 raise so we detect 0 as bad field */
+#define DDR200 (NBCAP_MEMCLK_100MHZ + 1)
+#define DDR333 (NBCAP_MEMCLK_166MHZ + 1)
+#define DDR400 (NBCAP_MEMCLK_200MHZ + 1)
+#define DDR_2T 0x80
+#define DDR_MASK 0x7
+
+#define DDR200_2T (DDR_2T | DDR200)
+#define DDR333_2T (DDR_2T | DDR333)
+#define DDR400_2T (DDR_2T | DDR400)
+
+/*
+ Following table comes directly from BKDG (unbuffered DIMM support)
+ [Y][X] Y = ch0_0, ch1_0, ch0_1, ch1_1 1=present 0=empty
+ X uses same layout but 1 means double rank 0 is single rank/empty
+
+ Following tables come from BKDG the ch{0_0,1_0,0_1,1_1} maps to
+ MEMCS_{1L,1H,2L,2H} in i the PDF. PreE is table 45, and revE table 46.
+*/
+
+ static const unsigned char dimm_loading_config_preE[16][16] = {
+ [0x8] = {[0x0] = DDR400,[0x8] = DDR400},
+ [0x2] = {[0x0] = DDR333,[0x2] = DDR400},
+ [0xa] = {[0x0] = DDR400_2T,[0x2] = DDR400_2T,
+ [0x8] = DDR400_2T,[0xa] = DDR333_2T},
+ [0xc] = {[0x0] = DDR400,[0xc] = DDR400},
+ [0x3] = {[0x0] = DDR333,[0x3] = DDR400},
+ [0xf] = {[0x0] = DDR400_2T,[0x3] = DDR400_2T,
+ [0xc] = DDR400_2T,[0xf] = DDR333_2T},
+ };
+
+ static const unsigned char dimm_loading_config_revE[16][16] = {
+ [0x8] = {[0x0] = DDR400, [0x8] = DDR400},
+ [0x2] = {[0x0] = DDR333, [0x2] = DDR400},
+ [0x4] = {[0x0] = DDR400, [0x4] = DDR400},
+ [0x1] = {[0x0] = DDR333, [0x1] = DDR400},
+ [0xa] = {[0x0] = DDR400_2T, [0x2] = DDR400_2T,
+ [0x8] = DDR400_2T, [0xa] = DDR333_2T},
+ [0x5] = {[0x0] = DDR400_2T, [0x1] = DDR400_2T,
+ [0x4] = DDR400_2T, [0x5] = DDR333_2T},
+ [0xc] = {[0x0] = DDR400, [0xc] = DDR400, [0x4] = DDR400, [0x8] = DDR400},
+ [0x3] = {[0x0] = DDR333, [0x1] = DDR333, [0x2] = DDR333, [0x3] = DDR400},
+ [0xe] = {[0x0] = DDR400_2T, [0x4] = DDR400_2T, [0x2] = DDR400_2T,
+ [0x6] = DDR400_2T, [0x8] = DDR400_2T, [0xc] = DDR400_2T,
+ [0xa] = DDR333_2T, [0xe] = DDR333_2T},
+ [0xb] = {[0x0] = DDR333, [0x1] = DDR400_2T, [0x2] = DDR333_2T,
+ [0x3] = DDR400_2T, [0x8] = DDR333_2T, [0x9] = DDR400_2T,
+ [0xa] = DDR333_2T, [0xb] = DDR333_2T},
+ [0xd] = {[0x0] = DDR400_2T, [0x8] = DDR400_2T, [0x1] = DDR400_2T,
+ [0x9] = DDR333_2T, [0x4] = DDR400_2T, [0xc] = DDR400_2T,
+ [0x5] = DDR333_2T, [0xd] = DDR333_2T},
+ [0x7] = {[0x0] = DDR333, [0x2] = DDR400_2T, [0x1] = DDR333_2T,
+ [0x3] = DDR400_2T, [0x4] = DDR333_2T, [0x6] = DDR400_2T,
+ [0x5] = DDR333_2T, [0x7] = DDR333_2T},
+ [0xf] = {[0x0] = DDR400_2T, [0x1] = DDR400_2T, [0x4] = DDR400_2T,
+ [0x5] = DDR333_2T, [0x2] = DDR400_2T, [0x3] = DDR400_2T,
+ [0x6] = DDR400_2T, [0x7] = DDR333_2T, [0x8] = DDR400_2T,
+ [0x9] = DDR400_2T, [0xc] = DDR400_2T, [0xd] = DDR333_2T,
+ [0xa] = DDR333_2T, [0xb] = DDR333_2T, [0xe] = DDR333_2T,
+ [0xf] = DDR333_2T},
+ };
+ /*The dpos matches channel positions defined in BKDG and above arrays
+ The rpos is bitmask of dual rank dimms in same order as dpos */
+ unsigned int dloading = 0, i, rpos = 0, dpos = 0;
+ const unsigned char (*dimm_loading_config)[16] = dimm_loading_config_revE;
+ int rank;
+ uint32_t dcl;
+
+ if (is_cpu_pre_e0()) {
+ dimm_loading_config = dimm_loading_config_preE;
+ }
+
+ /* only DIMMS two per channel */
+ for (i = 0; i < 2; i++) {
+ if ((dimm_mask & (1 << i))) {
+ /* read rank channel 0 */
+ rank = spd_read_byte(ctrl->channel0[i], 5);
+ if (rank < 0) goto hw_error;
+ rpos |= (rank == 2) ? (1 << (3 - (i * 2))) : 0;
+ dpos |= (1 << (3 - (i * 2)));
+ }
+
+ if ((dimm_mask & (1 << (i+DIMM_SOCKETS)))) {
+ /* read rank channel 1*/
+ rank = spd_read_byte(ctrl->channel1[i], 5);
+ if (rank < 0) goto hw_error;
+ rpos |= (rank == 2) ? (1 << (2 - (i * 2))) : 0;
+ dpos |= (1 << (2 - (i * 2)));
+ }
+ }
+ /* now the lookup, decode the max speed DDR400_2T etc */
+ dloading = dimm_loading_config[dpos][rpos] & DDR_MASK;
+#if 0
+ printk(BIOS_DEBUG, "XXX %x %x dload %x 2T %x\n", dpos,rpos, dloading, dimm_loading_config[dpos][rpos] & DDR_2T);
+#endif
+hw_error:
+ if (dloading != 0) {
+ /* we have valid combination check the restrictions */
+ dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+ dcl |= ((dimm_loading_config[dpos][rpos] & DDR_2T) || CONFIG_K8_FORCE_2T_DRAM_TIMING) ? (DCL_En2T) : 0;
+ /* Set DuallDimm is second channel is completely empty (revD+) */
+ if (((cpuid_eax(1) & 0xfff0f) >= 0x10f00) && ((dpos & 0x5) == 0)) {
+ printk(BIOS_DEBUG, "Setting DualDIMMen\n");
+ dcl |= DCL_DualDIMMen;
+ }
+ pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+ return dloading - 1;
+ } else {
+ /* if we don't find it we se it to DDR400 */
+ printk(BIOS_WARNING, "Detected strange DIMM configuration, may not work! (or bug)\n");
+ return NBCAP_MEMCLK_200MHZ;
+ }
+
+#elif CONFIG_CPU_AMD_SOCKET_754
+
+#define CFGIDX(DIMM1,DIMM2,DIMM3) ((DIMM3)*9+(DIMM2)*3+(DIMM1))
+
+#define EMPTY 0
+#define X8S_X16 1
+#define X8D 2
+
+#define DDR200 NBCAP_MEMCLK_100MHZ
+#define DDR333 NBCAP_MEMCLK_166MHZ
+#define DDR400 NBCAP_MEMCLK_200MHZ
+
+ /* this is table 42 from the BKDG, ignoring footnote 4,
+ * with the EMPTY, EMPTY, EMPTY row added */
+ static const unsigned char cfgtable[][2] = {
+ [CFGIDX(EMPTY, EMPTY, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(X8S_X16, EMPTY, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(EMPTY, X8S_X16, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(EMPTY, EMPTY, X8S_X16 )] = { DDR400, DDR400 },
+ [CFGIDX(X8D, EMPTY, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(EMPTY, X8D, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(EMPTY, EMPTY, X8D )] = { DDR400, DDR400 },
+ [CFGIDX(X8S_X16, X8S_X16, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(X8S_X16, X8D, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(X8S_X16, EMPTY, X8S_X16 )] = { DDR400, DDR400 },
+ [CFGIDX(X8S_X16, EMPTY, X8D )] = { DDR400, DDR400 },
+ [CFGIDX(X8D, X8S_X16, EMPTY )] = { DDR400, DDR400 },
+ [CFGIDX(X8D, X8D, EMPTY )] = { DDR333, DDR333 },
+ [CFGIDX(X8D, EMPTY, X8S_X16 )] = { DDR400, DDR400 },
+ [CFGIDX(X8D, EMPTY, X8D )] = { DDR333, DDR333 },
+ [CFGIDX(EMPTY, X8S_X16, X8S_X16 )] = { DDR333, DDR400 },
+ [CFGIDX(EMPTY, X8S_X16, X8D )] = { DDR200, DDR400 },
+ [CFGIDX(EMPTY, X8D, X8S_X16 )] = { DDR200, DDR400 },
+ [CFGIDX(EMPTY, X8D, X8D )] = { DDR200, DDR333 },
+ [CFGIDX(X8S_X16, X8S_X16, X8S_X16 )] = { DDR333, DDR400 },
+ [CFGIDX(X8S_X16, X8S_X16, X8D )] = { DDR200, DDR333 },
+ [CFGIDX(X8S_X16, X8D, X8S_X16 )] = { DDR200, DDR333 },
+ [CFGIDX(X8S_X16, X8D, X8D )] = { DDR200, DDR333 },
+ [CFGIDX(X8D, X8S_X16, X8S_X16 )] = { DDR333, DDR333 },
+ [CFGIDX(X8D, X8S_X16, X8D )] = { DDR200, DDR333 },
+ [CFGIDX(X8D, X8D, X8S_X16 )] = { DDR200, DDR333 },
+ [CFGIDX(X8D, X8D, X8D )] = { DDR200, DDR333 }
+ };
+
+ int i, rank, width, dimmtypes[3];
+ const unsigned char *cfg;
+
+ for (i = 0; i < 3; i++) {
+ if (dimm_mask & (1 << i)) {
+ rank = spd_read_byte(ctrl->channel0[i], 5);
+ width = spd_read_byte(ctrl->channel0[i], 13);
+ if (rank < 0 || width < 0) die("failed to read SPD");
+ width &= 0x7f;
+ /* this is my guess as to how the criteria in the table
+ * are to be understood:
+ */
+ dimmtypes[i] = width >= (rank == 1 ? 8 : 16) ? X8S_X16 : X8D;
+ } else {
+ dimmtypes[i] = EMPTY;
+ }
+ }
+ cfg = cfgtable[CFGIDX(dimmtypes[0], dimmtypes[1], dimmtypes[2])];
+ *freq_1t = cfg[0];
+ return is_cpu_c0() ? cfg[0] : cfg[1];
+
+#else /* CONFIG_CPU_AMD_SOCKET_* */
+
+/* well, there are socket 940 boards supported which obviously fail to
+ * compile with this */
+// #error load dependent memory clock limiting is not implemented for this socket
+
+ /* see BKDG 4.1.3--if you just want to test a setup that doesn't
+ * require limiting, you may use the following code */
+
+ *freq_1t = NBCAP_MEMCLK_200MHZ;
+ return NBCAP_MEMCLK_200MHZ;
+
+#endif /* CONFIG_CPU_AMD_SOCKET_* */
+
+}
+