From f1287266ab7587672080aed2ddbc272a95fba9a3 Mon Sep 17 00:00:00 2001 From: Arthur Heymans Date: Mon, 25 Dec 2017 18:30:01 +0100 Subject: nb/intel/x4x: Add DDR3 JEDEC init Add DDR3 JEDEC init (Power up and Initialization by setting emrs regs) This also modifies the send_jedec_cmd function as DDR3 dimms can have ranks mirrored which needs to be accounted for. The ddr3_emrs1_config array is placed externally since it is also needed for write leveling. Change-Id: I510b8669aaa48ba99fb4dcf1ece716aef26741bb Signed-off-by: Arthur Heymans Reviewed-on: https://review.coreboot.org/22994 Tested-by: build bot (Jenkins) Reviewed-by: Felix Held --- src/northbridge/intel/x4x/raminit.c | 3 + src/northbridge/intel/x4x/raminit_ddr23.c | 98 +++++++++++++++++++++++++++--- src/northbridge/intel/x4x/raminit_tables.c | 19 ++++++ src/northbridge/intel/x4x/x4x.h | 2 + 4 files changed, 115 insertions(+), 7 deletions(-) (limited to 'src/northbridge/intel') diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c index efd197f714..9e649b0ec4 100644 --- a/src/northbridge/intel/x4x/raminit.c +++ b/src/northbridge/intel/x4x/raminit.c @@ -408,6 +408,9 @@ static int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd, s->dimms[dimm_idx].spd_crc = spd_ddr3_calc_unique_crc(raw_spd, raw_spd[0]); + + s->dimms[dimm_idx].mirrored = decoded_dimm.flags.pins_mirrored; + return CB_SUCCESS; } diff --git a/src/northbridge/intel/x4x/raminit_ddr23.c b/src/northbridge/intel/x4x/raminit_ddr23.c index e4cdcd2d56..6d6111c5c4 100644 --- a/src/northbridge/intel/x4x/raminit_ddr23.c +++ b/src/northbridge/intel/x4x/raminit_ddr23.c @@ -1347,14 +1347,45 @@ u32 test_address(int channel, int rank) return channel * 512 * MiB + rank * 128 * MiB; } -static void dojedec_ddr2(u8 r, u8 ch, u8 cmd, u16 val) + +/* DDR3 Rank1 Address mirror + * swap the following pins: + * A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */ +static u32 mirror_shift_bit(const u32 data, u8 bit) +{ + u32 temp0 = data, temp1 = data; + temp0 &= 1 << bit; + temp0 <<= 1; + temp1 &= 1 << (bit + 1); + temp1 >>= 1; + return (data & ~(3 << bit)) | temp0 | temp1; +} + +static void send_jedec_cmd(const struct sysinfo *s, u8 r, + u8 ch, u8 cmd, u32 val) { u32 addr = test_address(ch, r); volatile u32 rubbish; + u8 data8 = cmd; + u32 data32; + + if (s->spd_type == DDR3 && (r & 1) + && s->dimms[ch * 2 + (r >> 1)].mirrored) { + data8 = (u8)mirror_shift_bit(data8, 4); + } - MCHBAR8(0x271) = (MCHBAR8(0x271) & ~0x3e) | cmd; - MCHBAR8(0x671) = (MCHBAR8(0x671) & ~0x3e) | cmd; - rubbish = read32((void *)((val<<3) | addr)); + MCHBAR8(0x271) = (MCHBAR8(0x271) & ~0x3e) | data8; + MCHBAR8(0x671) = (MCHBAR8(0x671) & ~0x3e) | data8; + data32 = val; + if (s->spd_type == DDR3 && (r & 1) + && s->dimms[ch * 2 + (r >> 1)].mirrored) { + data32 = mirror_shift_bit(data32, 3); + data32 = mirror_shift_bit(data32, 5); + data32 = mirror_shift_bit(data32, 7); + } + data32 <<= 3; + + rubbish = read32((void *)((data32 | addr))); udelay(10); MCHBAR8(0x271) = (MCHBAR8(0x271) & ~0x3e) | NORMALOP_CMD; MCHBAR8(0x671) = (MCHBAR8(0x671) & ~0x3e) | NORMALOP_CMD; @@ -1419,7 +1450,7 @@ static void jedec_ddr2(struct sysinfo *s) default: break; } - dojedec_ddr2(r, ch, jedec[i][0], v); + send_jedec_cmd(s, r, ch, jedec[i][0], v); udelay(1); printk(RAM_SPEW, "Jedec step %d\n", i); } @@ -1427,6 +1458,55 @@ static void jedec_ddr2(struct sysinfo *s) printk(BIOS_DEBUG, "MRS done\n"); } +static void jedec_ddr3(struct sysinfo *s) +{ + int ch, r, dimmconfig, cmd, ddr3_freq; + + u8 ddr3_emrs2_rtt_wr_config[16][4] = { /* [config][Rank] */ + {0, 0, 0, 0}, /* NC_NC */ + {0, 0, 0, 0}, /* x8ss_NC */ + {0, 0, 0, 0}, /* x8ds_NC */ + {0, 0, 0, 0}, /* x16ss_NC */ + {0, 0, 0, 0}, /* NC_x8ss */ + {2, 0, 2, 0}, /* x8ss_x8ss */ + {2, 2, 2, 0}, /* x8ds_x8ss */ + {2, 0, 2, 0}, /* x16ss_x8ss */ + {0, 0, 0, 0}, /* NC_x8ss */ + {2, 0, 2, 2}, /* x8ss_x8ds */ + {2, 2, 2, 2}, /* x8ds_x8ds */ + {2, 0, 2, 2}, /* x16ss_x8ds */ + {0, 0, 0, 0}, /* NC_x16ss */ + {2, 0, 2, 0}, /* x8ss_x16ss */ + {2, 2, 2, 0}, /* x8ds_x16ss */ + {2, 0, 2, 0}, /* x16ss_x16ss */ + }; + + printk(BIOS_DEBUG, "MRS...\n"); + + ddr3_freq = s->selected_timings.mem_clk - MEM_CLOCK_800MHz; + FOR_EACH_POPULATED_RANK(s->dimms, ch, r) { + printk(BIOS_DEBUG, "CH%d: Found Rank %d\n", ch, r); + send_jedec_cmd(s, r, ch, NOP_CMD, 0); + udelay(200); + dimmconfig = s->dimm_config[ch]; + cmd = ddr3_freq << 3; /* actually twl - 5 which is same */ + cmd |= ddr3_emrs2_rtt_wr_config[dimmconfig][r] << 9; + send_jedec_cmd(s, r, ch, EMRS2_CMD, cmd); + send_jedec_cmd(s, r, ch, EMRS3_CMD, 0); + cmd = ddr3_emrs1_rtt_nom_config[dimmconfig][r] << 2; + /* Hardcode output drive strength to 34 Ohm / RZQ/7 (why??) */ + cmd |= (1 << 1); + send_jedec_cmd(s, r, ch, EMRS1_CMD, cmd); + /* Burst type interleaved, burst length 8, Reset DLL, + * Precharge PD: DLL on */ + send_jedec_cmd(s, r, ch, MRS_CMD, (1 << 3) | (1 << 8) + | (1 << 12) | ((s->selected_timings.CAS - 4) << 4) + | ((s->selected_timings.tWR - 4) << 9)); + send_jedec_cmd(s, r, ch, ZQCAL_CMD, (1 << 10)); + } + printk(BIOS_DEBUG, "MRS done\n"); +} + static void sdram_recover_receive_enable(const struct sysinfo *s) { u32 reg32; @@ -1949,8 +2029,12 @@ void do_raminit(struct sysinfo *s, int fast_boot) printk(BIOS_DEBUG, "Done pre-jedec\n"); // JEDEC reset - if (s->boot_path != BOOT_PATH_RESUME) - jedec_ddr2(s); + if (s->boot_path != BOOT_PATH_RESUME) { + if (s->spd_type == DDR2) + jedec_ddr2(s); + else /* DDR3 */ + jedec_ddr3(s); + } printk(BIOS_DEBUG, "Done jedec steps\n"); diff --git a/src/northbridge/intel/x4x/raminit_tables.c b/src/northbridge/intel/x4x/raminit_tables.c index 9a39b28046..47d8a98bee 100644 --- a/src/northbridge/intel/x4x/raminit_tables.c +++ b/src/northbridge/intel/x4x/raminit_tables.c @@ -269,3 +269,22 @@ const struct dll_setting default_ddr3_1333_dq[2][TOTAL_BYTELANES] = { {13, 6, 1, 0, 1, 0}, {0, 3, 1, 1, 0, 1}, } }; + +const u8 ddr3_emrs1_rtt_nom_config[16][4] = { /* [Config][Rank] */ + {0x00, 0x00, 0x00, 0x00}, /* NC_NC */ + {0x11, 0x00, 0x00, 0x00}, /* 8S_NC */ + {0x11, 0x11, 0x00, 0x00}, /* 8D_NC */ + {0x11, 0x00, 0x00, 0x00}, /* 16S_NC */ + {0x00, 0x00, 0x11, 0x00}, /* NC_8S */ + {0x81, 0x00, 0x81, 0x00}, /* 8S_8S */ + {0x81, 0x81, 0x81, 0x00}, /* 8D_8S */ + {0x81, 0x00, 0x81, 0x00}, /* 16S_8S */ + {0x00, 0x00, 0x11, 0x11}, /* NC_8D */ + {0x81, 0x00, 0x81, 0x81}, /* 8S_8D */ + {0x81, 0x81, 0x81, 0x81}, /* 8D_8D */ + {0x81, 0x00, 0x81, 0x81}, /* 16S_8D */ + {0x00, 0x00, 0x11, 0x00}, /* NC_16S */ + {0x81, 0x00, 0x81, 0x00}, /* 8S_16S */ + {0x81, 0x81, 0x81, 0x00}, /* 8D_16S */ + {0x81, 0x00, 0x81, 0x00}, /* 16S_16S */ +}; diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h index 8532d60ed2..53b73ae364 100644 --- a/src/northbridge/intel/x4x/x4x.h +++ b/src/northbridge/intel/x4x/x4x.h @@ -309,6 +309,7 @@ struct dimminfo { unsigned int rows; unsigned int cols; u16 spd_crc; + u8 mirrored; }; struct rcven_timings { @@ -388,6 +389,7 @@ extern const struct dll_setting default_ddr2_800_dq[TOTAL_BYTELANES]; extern const struct dll_setting default_ddr3_800_dq[2][TOTAL_BYTELANES]; extern const struct dll_setting default_ddr3_1067_dq[2][TOTAL_BYTELANES]; extern const struct dll_setting default_ddr3_1333_dq[2][TOTAL_BYTELANES]; +extern const u8 ddr3_emrs1_rtt_nom_config[16][4]; struct acpi_rsdp; #ifndef __SIMPLE_DEVICE__ -- cgit v1.2.3