diff --git a/src/northbridge/intel/haswell/native_raminit/change_margin.c b/src/northbridge/intel/haswell/native_raminit/change_margin.c index 299c44a6b0..517ddc23a6 100644 --- a/src/northbridge/intel/haswell/native_raminit/change_margin.c +++ b/src/northbridge/intel/haswell/native_raminit/change_margin.c @@ -288,3 +288,313 @@ void change_1d_margin_multicast( { change_margin(ctrl, param, value0, true, 0, rank, 0, update_ctrl, regfile); } + +uint32_t update_comp_global_offset( + struct sysinfo *ctrl, + const enum global_comp_offset param, + const int32_t offset, + const uint8_t update_ctrl) +{ + union ddr_comp_ctl_0_reg ddr_comp_ctl_0 = ctrl->comp_ctl_0; + union ddr_comp_ctl_1_reg ddr_comp_ctl_1 = ctrl->comp_ctl_1; + switch (param) { + case RdOdt: + /* Disable fixed ODT offset before changing this param */ + ddr_comp_ctl_0.fixed_odt_offset = 0; + ddr_comp_ctl_0.dq_odt_vref = offset; + break; + case WrDS: + ddr_comp_ctl_0.dq_drv_vref = offset; + break; + case WrDSCmd: + ddr_comp_ctl_0.cmd_drv_vref = offset; + break; + case WrDSCtl: + ddr_comp_ctl_0.ctl_drv_vref = offset; + break; + case WrDSClk: + ddr_comp_ctl_0.clk_drv_vref = offset; + break; + case SCompDq: + ddr_comp_ctl_1.dq_scomp = offset; + break; + case SCompCmd: + ddr_comp_ctl_1.cmd_scomp = offset; + break; + case SCompCtl: + ddr_comp_ctl_1.ctl_scomp = offset; + break; + case SCompClk: + ddr_comp_ctl_1.clk_scomp = offset; + break; + case DisOdtStatic: + ctrl->comp_ctl_0.disable_odt_static = !!offset; + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { + union ddr_data_control_0_reg data_control_0 = { + .raw = ctrl->dq_control_0[channel], + }; + data_control_0.disable_odt_static = !!offset; + mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); + if (update_ctrl) { + ctrl->dq_control_0[channel] = data_control_0.raw; + } + } + break; + default: + die("%s: Invalid global comp offset %d\n", __func__, param); + } + mchbar_write32(DDR_COMP_CTL_0, ddr_comp_ctl_0.raw); + mchbar_write32(DDR_COMP_CTL_1, ddr_comp_ctl_1.raw); + if (update_ctrl) { + ctrl->comp_ctl_0 = ddr_comp_ctl_0; + ctrl->comp_ctl_1 = ddr_comp_ctl_1; + } + force_rcomp_and_wait_us(8); + const union ddr_data_comp_1_reg ddr_data_comp_1 = { + .raw = mchbar_read32(DDR_DATA_COMP_1), + }; + const uint8_t rcomp_odt_up = ddr_data_comp_1.rcomp_odt_up; + /* Check if we are close to saturation and try changing the static legs */ + if (param == RdOdt && (rcomp_odt_up < 16 || rcomp_odt_up > 48)) { + const bool dis_odt_static = rcomp_odt_up < 16; + + /* Host always needs to be up-to-date with the static leg state */ + ddr_comp_ctl_0.disable_odt_static = dis_odt_static; + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { + if (!does_ch_exist(ctrl, channel)) + continue; + + union ddr_data_control_0_reg data_control_0 = { + .raw = ctrl->dq_control_0[channel], + }; + data_control_0.disable_odt_static = dis_odt_static; + mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), + data_control_0.raw); + ctrl->dq_control_0[channel] = data_control_0.raw; + } + mchbar_write32(DDR_COMP_CTL_0, ddr_comp_ctl_0.raw); + ctrl->comp_ctl_0.disable_odt_static = dis_odt_static; + force_rcomp_and_wait_us(8); + } + switch (param) { + case RdOdt: + case DisOdtStatic: { + /* Re-enable fixed ODT offset after changing this param */ + const uint32_t delta = ddr_data_comp_1.rcomp_odt_down - ddr_data_comp_1.rcomp_odt_up; + ddr_comp_ctl_0.odt_up_down_off = delta; + ddr_comp_ctl_0.fixed_odt_offset = 1; + mchbar_write32(DDR_COMP_CTL_0, ddr_comp_ctl_0.raw); + if (update_ctrl) + ctrl->comp_ctl_0 = ddr_comp_ctl_0; + + return ddr_data_comp_1.rcomp_odt_up; + } + case WrDS: + case SCompDq: { + const union ddr_data_comp_0_reg data_comp_0 = { + .raw = mchbar_read32(DDR_DATA_COMP_0), + }; + return param == WrDS ? data_comp_0.rcomp_drv_up : data_comp_0.slew_rate_comp; + } + case WrDSCmd: + case SCompCmd: { + const union ddr_comp_cmd_comp_reg cmd_comp = { + .raw = mchbar_read32(DDR_COMP_CMD_COMP), + }; + return param == WrDSCmd ? cmd_comp.rcomp_drv_up : cmd_comp.slew_rate_comp; + } + case WrDSCtl: + case SCompCtl: { + const union ddr_comp_ctl_comp_reg ctl_comp = { + .raw = mchbar_read32(DDR_COMP_CTL_COMP), + }; + return param == WrDSCtl ? ctl_comp.rcomp_drv_up : ctl_comp.slew_rate_comp; + } + case WrDSClk: + case SCompClk: { + const union ddr_comp_clk_comp_reg clk_comp = { + .raw = mchbar_read32(DDR_COMP_CLK_COMP), + }; + return param == WrDSClk ? clk_comp.rcomp_drv_up : clk_comp.slew_rate_comp; + } + default: + die("%s: Invalid global comp offset %d\n", __func__, param); + return 0; + } +} + +static bool is_cpu_comp(const enum opt_param optparam) +{ + switch (optparam) { + case OptWrDS: + case OptRdOdt: + case OptSComp: + case OptTComp: + return true; + default: + return false; + } +} + +static const uint8_t odt_map[6] = {0, 120, 60, 40, 30, 20}; + +void update_opt_param_offset( + struct sysinfo *ctrl, + const uint8_t channel, + const uint8_t ranks_in, + const uint8_t byte, + const enum opt_param optparam, + const int16_t off_in, + const bool update_ctrl) +{ + const uint8_t rankmask = ranks_in & ctrl->rankmap[channel]; + union ddr_data_offset_comp_reg data_offset_comp = ctrl->data_offset_comp[channel][byte]; + if (optparam == OptDefault) { + mchbar_write32(DDR_DATA_OFFSET_COMP_ch_b(channel, byte), data_offset_comp.raw); + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { + if (!rank_in_mask(rank, rankmask)) { + continue; + } + update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); + update_txt(ctrl, channel, rank, byte, TXT_RESTORE, 0); + } + mchbar_write32(DQ_CONTROL_1(channel, byte), ctrl->dq_control_1[channel][byte]); + return; + } + if (is_cpu_comp(optparam)) { + int16_t off; + if (optparam == OptWrDS) { + off = clamp_s16(-32, off_in, 31); + } else { + off = clamp_s16(-16, off_in, 15); + } + if (optparam == OptWrDS) { + data_offset_comp.drv_up = off; + data_offset_comp.drv_down = off; + } else if (optparam == OptRdOdt) { + data_offset_comp.odt_up = off; + data_offset_comp.odt_down = off; + } else if (optparam == OptTComp) { + data_offset_comp.t_clk_out = off; + } else if (optparam == OptSComp) { + data_offset_comp.slew_rate = off; + } + mchbar_write32(DDR_DATA_OFFSET_COMP_ch_b(channel, byte), data_offset_comp.raw); + if (update_ctrl) { + ctrl->data_offset_comp[channel][byte] = data_offset_comp; + } + union ddr_scram_misc_control_reg misc_control = ctrl->misc_control_0; + misc_control.force_comp_update = 1; + mchbar_write32(DDR_SCRAM_MISC_CONTROL, misc_control.raw); + } + if (optparam == OptTxEq) { + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { + if (!rank_in_mask(rank, rankmask)) { + continue; + } + /* + * TxEq[5:4] = Emphasize = [3, 6, 9, 12] legs + * TxEq[3:0] = Deemphasize = [0 .. 11, 4 * Rsvd] legs + */ + /* Use 12 emphasis legs (not trained) */ + const uint8_t off_code = clamp_s16(0, off_in, 11) | TXEQFULLDRV; + update_txt(ctrl, channel, rank, byte, TXT_TX_EQ, off_code); + if (update_ctrl) { + ctrl->tx_eq[channel][rank][byte] = off_code; + } + } + } + if (optparam == OptRxEq) { + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { + if (!rank_in_mask(rank, rankmask)) { + continue; + } + /* + * RxEQ[4:0] CR Decoding (pF/kOhm) + * [2:0] + * [4:3] 0 1 2 3 4 5-7 + * 0 0.5/.02 0.5/1.0 0.5/.50 0.5/.25 0.5/.12 rsvd + * 1 1.0/.02 1.0/1.0 1.0/.50 1.0/.25 1.0/.12 rsvd + * 2 1.5/.02 1.5/1.0 1.5/.50 1.5/.25 1.5/.12 rsvd + * 3 2.0/.02 2.0/1.0 2.0/.50 2.0/.25 2.0/.12 rsvd + * Sweep = 0-19 [4:3] = (Sweep / 5) [2:0] = (Sweep % 5) + */ + const int16_t off = clamp_s16(0, off_in, 19); + const uint8_t value = ((off / 5) << 3) + (off % 5); + update_rxt(ctrl, channel, rank, byte, RXT_RX_EQ, value); + if (update_ctrl) { + ctrl->rx_eq[channel][rank][byte] = value; + } + } + } + if (optparam == OptRxBias) { + /* + * Mapping: 0: 0.44 1: 0.66 2: 0.88 3: 1.00 + * 4: 1.33 5: 1.66 6: 2.00 7: 2.33 + */ + union ddr_data_control_1_reg data_control_1 = { + .raw = ctrl->dq_control_1[channel][byte], + }; + data_control_1.rx_bias_ctl = clamp_s16(0, off_in, 7); + mchbar_write32(DQ_CONTROL_1(channel, byte), data_control_1.raw); + if (update_ctrl) { + ctrl->dq_control_1[channel][byte] = data_control_1.raw; + } + } + if (optparam == OptDimmRon) { + if (ctrl->lpddr) { + die("%s: LPDDR3 support missing\n", __func__); + } + /* DIMM Ron: 240/6, 240/7 Ohms */ + const uint16_t ron = !!off_in << 1; + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { + if (!rank_in_mask(rank, rankmask)) { + continue; + } + const uint16_t mr1reg = (ctrl->mr1[channel][rank] & ~BIT(1)) | ron; + reut_issue_mrs(ctrl, channel, BIT(rank), 1, mr1reg); + if (update_ctrl) { + ctrl->mr1[channel][rank] = mr1reg; + } + } + } + if (optparam == OptDimmOdt || optparam == OptDimmOdtWr) { + if (ctrl->lpddr) { + die("%s: LPDDR3 support missing\n", __func__); + } + const uint16_t rtt_wr_mrs_encoding[] = { 0, 2, 1 }; + const uint16_t rtt_wr_mask = BIT(10) | BIT(9); + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { + if (!rank_in_mask(rank, rankmask)) { + continue; + } + uint8_t index = off_in; + if (optparam == OptDimmOdt) { + index >>= 4; + } + index = MIN(index, 2); + const uint16_t rtt_wr = rtt_wr_mrs_encoding[index] << 9; + uint16_t mr2reg = (ctrl->mr2[channel][rank] & ~rtt_wr_mask) | rtt_wr; + reut_issue_mrs(ctrl, channel, BIT(rank), 2, mr2reg); + if (update_ctrl) { + ctrl->mr2[channel][rank] = mr2reg; + } + if (optparam == OptDimmOdtWr) { + continue; + } + index = off_in; + index &= 0xf; + if (index >= ARRAY_SIZE(odt_map)) { + printk(BIOS_ERR, "C%uR%u RTT NOM idx %u is too large\n", + channel, rank, index); + index = ARRAY_SIZE(odt_map) - 1; + } + const uint16_t rtt_nom = encode_ddr3_rttnom(odt_map[index]); + uint16_t mr1reg = (ctrl->mr1[channel][rank] & ~RTTNOM_MASK) | rtt_nom; + reut_issue_mrs(ctrl, channel, BIT(rank), 1, mr1reg); + if (update_ctrl) { + ctrl->mr1[channel][rank] = mr1reg; + } + } + } +} diff --git a/src/northbridge/intel/haswell/native_raminit/configure_mc.c b/src/northbridge/intel/haswell/native_raminit/configure_mc.c index 8ff81727ec..a86a11ca4e 100644 --- a/src/northbridge/intel/haswell/native_raminit/configure_mc.c +++ b/src/northbridge/intel/haswell/native_raminit/configure_mc.c @@ -205,7 +205,7 @@ static void program_ddr_data(struct sysinfo *ctrl, const bool dis_odt_static, co const uint8_t stg = latency * byte_stagger[byte] / ctrl->lanes; data_control_2.rx_stagger_ctl = stg & 0x1f; mchbar_write32(DQ_CONTROL_2(channel, byte), data_control_2.raw); - ctrl->data_offset_comp[channel][byte] = 0; + ctrl->data_offset_comp[channel][byte].raw = 0; ctrl->dq_control_1[channel][byte] = data_control_1.raw; ctrl->dq_control_2[channel][byte] = data_control_2.raw; } @@ -548,14 +548,7 @@ static void override_comp(uint32_t value, uint32_t width, uint32_t shift, uint32 static void program_ls_comp(struct sysinfo *ctrl) { - /* Disable periodic COMP */ - const union pcu_comp_reg m_comp = { - .comp_disable = 1, - .comp_interval = COMP_INT, - .comp_force = 1, - }; - mchbar_write32(M_COMP, m_comp.raw); - udelay(10); + force_rcomp_and_wait_us(10); /* Override level shifter compensation */ const uint32_t ls_comp = 2; @@ -563,7 +556,7 @@ static void program_ls_comp(struct sysinfo *ctrl) override_comp(ls_comp, 3, 24, DDR_CMD_COMP); override_comp(ls_comp, 3, 24, DDR_CKE_CTL_COMP); override_comp(ls_comp, 3, 23, DDR_CLK_COMP); - override_comp(ls_comp, 3, 28, DDR_COMP_DATA_COMP_1); + override_comp(ls_comp, 3, 28, DDR_DATA_COMP_1); override_comp(ls_comp, 3, 24, DDR_COMP_CMD_COMP); override_comp(ls_comp, 4, 24, DDR_COMP_CTL_COMP); override_comp(ls_comp, 4, 23, DDR_COMP_CLK_COMP); @@ -575,8 +568,8 @@ static void program_ls_comp(struct sysinfo *ctrl) mchbar_write32(DDR_SCRAM_MISC_CONTROL, ddr_scram_misc_ctrl.raw); /* Use a fixed offset between ODT Up/Dn */ - const union ddr_comp_data_comp_1_reg data_comp_1 = { - .raw = mchbar_read32(DDR_COMP_DATA_COMP_1), + const union ddr_data_comp_1_reg data_comp_1 = { + .raw = mchbar_read32(DDR_DATA_COMP_1), }; const uint32_t odt_offset = data_comp_1.rcomp_odt_down - data_comp_1.rcomp_odt_up; ctrl->comp_ctl_0.odt_up_down_off = odt_offset; diff --git a/src/northbridge/intel/haswell/native_raminit/io_comp_control.c b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c index 8a55fd81b2..77d2c73f66 100644 --- a/src/northbridge/intel/haswell/native_raminit/io_comp_control.c +++ b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -39,3 +40,168 @@ enum raminit_status wait_for_first_rcomp(void) printk(BIOS_ERR, "Timed out waiting for RCOMP to complete\n"); return RAMINIT_STATUS_POLL_TIMEOUT; } + +void force_rcomp_and_wait_us(unsigned int usecs) +{ + /* Disable periodic COMP */ + const union pcu_comp_reg m_comp = { + .comp_disable = 1, + .comp_interval = COMP_INT, + .comp_force = 1, + }; + mchbar_write32(M_COMP, m_comp.raw); + if (usecs) { + udelay(usecs); + } +} + +static int8_t get_comp(union ddr_data_offset_comp_reg data_offset_comp, enum opt_param optparam) +{ + switch (optparam) { + case OptWrDS: return data_offset_comp.drv_up; + case OptRdOdt: return data_offset_comp.odt_up; + case OptSComp: return data_offset_comp.slew_rate; + default: return 0; + } +} + +static void optimise_comp_offset(struct sysinfo *ctrl, const enum opt_param optparam) +{ + const uint8_t reserved_codes = 3; + const bool update_ctrl = true; + + uint8_t param; + uint8_t current_comp; + int32_t min_comp_vref; + int32_t max_comp_vref; + int32_t curr_comp_vref; + switch (optparam) { + case OptWrDS: { + const union ddr_data_comp_0_reg data_comp_0 = { + .raw = mchbar_read32(DDR_DATA_COMP_0), + }; + current_comp = data_comp_0.rcomp_drv_up; + curr_comp_vref = ctrl->comp_ctl_0.dq_drv_vref; + min_comp_vref = -8; + max_comp_vref = 7; + param = WrDS; + break; + } + case OptRdOdt: { + const union ddr_data_comp_1_reg data_comp_1 = { + .raw = mchbar_read32(DDR_DATA_COMP_1), + }; + current_comp = data_comp_1.rcomp_odt_up; + curr_comp_vref = ctrl->comp_ctl_0.dq_odt_vref; + min_comp_vref = -16; + max_comp_vref = 15; + param = RdOdt; + break; + } + case OptSComp: { + const union ddr_data_comp_0_reg data_comp_0 = { + .raw = mchbar_read32(DDR_DATA_COMP_0), + }; + current_comp = data_comp_0.slew_rate_comp; + /* Mask out the phase/cycle bit */ + curr_comp_vref = ctrl->comp_ctl_1.dq_scomp & ~BIT(4); + min_comp_vref = 4; + max_comp_vref = 15; + param = SCompDq; + break; + } + default: + die("%s: Invalid optparam: %d\n", __func__, optparam); + } + + uint8_t comp_codes[NUM_CHANNELS][NUM_LANES] = {0}; + int16_t avg_offset = 0; + uint8_t num_ch = 0; + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { + if (!does_ch_exist(ctrl, channel)) { + continue; + } + + num_ch++; + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { + int8_t off = get_comp(ctrl->data_offset_comp[channel][byte], optparam); + avg_offset += off; + comp_codes[channel][byte] = current_comp + off; + } + } + if (!num_ch) { + return; + } + + avg_offset = DIV_ROUND_CLOSEST(avg_offset, num_ch * ctrl->lanes); + if (!avg_offset) { + return; + } + + const uint8_t start_delta = ABS(avg_offset); + uint8_t best_vref_off = curr_comp_vref; + uint8_t min_delta = start_delta; + uint8_t new_comp = current_comp; + uint8_t dq_scomp_pc = ctrl->comp_ctl_1.dq_scomp & BIT(4); /* phase/cycle bit */ + int8_t sign = sign_of(avg_offset); + if (optparam == OptSComp) { + sign = -sign; + } + bool done = false; + for (uint8_t offset = 1; !done; offset++) { + int8_t new_comp_vref = curr_comp_vref + sign * offset; + if (new_comp_vref < min_comp_vref || new_comp_vref > max_comp_vref) { + done = true; + } + if (reserved_codes > new_comp || new_comp > 63 - reserved_codes) { + done = true; + } + if (optparam == OptSComp) { + if (new_comp_vref >= 16) { + dq_scomp_pc = 0; + } + new_comp_vref = dq_scomp_pc | new_comp_vref; + } + if (!done) { + new_comp = update_comp_global_offset(ctrl, param, new_comp_vref, 0); + const uint8_t curr_delta = ABS(current_comp + avg_offset - new_comp); + if (curr_delta >= start_delta) { + done = true; + } else if (curr_delta < min_delta) { + best_vref_off = new_comp_vref; + min_delta = curr_delta; + if (min_delta == 0) { + done = true; + } + } + } + } + new_comp = update_comp_global_offset(ctrl, param, best_vref_off, update_ctrl); + if (best_vref_off == curr_comp_vref) { + return; + } + + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { + if (!does_ch_exist(ctrl, channel)) { + continue; + } + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { + update_opt_param_offset( + ctrl, + channel, + 0, + byte, + optparam, + comp_codes[channel][byte] - new_comp, + update_ctrl); + } + } +} + +enum raminit_status optimise_comp(struct sysinfo *ctrl) +{ + optimise_comp_offset(ctrl, OptWrDS); + optimise_comp_offset(ctrl, OptRdOdt); + optimise_comp_offset(ctrl, OptSComp); + return RAMINIT_STATUS_SUCCESS; +} diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c index 0e72ae66aa..84db33ebdf 100644 --- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c +++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c @@ -129,6 +129,7 @@ static const struct task_entry cold_boot[] = { { train_receive_enable, true, "RCVET", }, { train_read_mpr, true, "RDMPRT", }, { train_jedec_write_leveling, true, "JWRL", }, + { optimise_comp, true, "OPTCOMP", }, { post_training, true, "POSTTRAIN", }, { activate_mc, true, "ACTIVATE", }, { save_training_values, true, "SAVE_TRAIN", }, diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h index 097b6411b3..b9e84a11df 100644 --- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h @@ -70,6 +70,33 @@ enum margin_parameter { RdV, }; +enum opt_param { + OptWrDS = 0, + OptRdOdt, + OptSComp, + OptTComp, + OptTxEq, + OptRxEq, + OptRxBias, + OptDimmOdt, + OptDimmOdtWr, + OptDimmRon, + OptDefault, +}; + +enum global_comp_offset { + RdOdt, + WrDS, + WrDSCmd, + WrDSCtl, + WrDSClk, + SCompDq, + SCompCmd, + SCompCtl, + SCompClk, + DisOdtStatic, +}; + /* ZQ calibration types */ enum { ZQ_INIT, /* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init with tZQinit */ @@ -312,7 +339,7 @@ struct sysinfo { uint32_t rt_io_comp[NUM_CHANNELS]; uint32_t data_offset_train[NUM_CHANNELS][NUM_LANES]; - uint32_t data_offset_comp[NUM_CHANNELS][NUM_LANES]; + union ddr_data_offset_comp_reg data_offset_comp[NUM_CHANNELS][NUM_LANES]; uint32_t dq_control_0[NUM_CHANNELS]; uint32_t dq_control_1[NUM_CHANNELS][NUM_LANES]; @@ -354,6 +381,11 @@ struct sysinfo { uint8_t dq_pat_lc; }; +static inline int8_t sign_of(const int32_t val) +{ + return val < 0 ? -1 : 1; +} + static inline bool is_hsw_ult(void) { return CONFIG(INTEL_LYNXPOINT_LP); @@ -456,6 +488,7 @@ enum raminit_status train_sense_amp_offset(struct sysinfo *ctrl); enum raminit_status train_receive_enable(struct sysinfo *ctrl); enum raminit_status train_read_mpr(struct sysinfo *ctrl); enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl); +enum raminit_status optimise_comp(struct sysinfo *ctrl); enum raminit_status save_training_values(struct sysinfo *ctrl); enum raminit_status restore_training_values(struct sysinfo *ctrl); enum raminit_status save_non_training(struct sysinfo *ctrl); @@ -482,6 +515,7 @@ uint32_t get_tZQCS(uint32_t mem_clock_mhz, bool lpddr); enum raminit_status io_reset(void); enum raminit_status wait_for_first_rcomp(void); +void force_rcomp_and_wait_us(unsigned int usecs); uint16_t encode_ddr3_rttnom(uint32_t rttnom); void ddr3_program_mr1(struct sysinfo *ctrl, uint8_t wl_mode, uint8_t q_off); @@ -588,6 +622,21 @@ void change_1d_margin_multicast( const bool update_ctrl, const enum regfile_mode regfile); +uint32_t update_comp_global_offset( + struct sysinfo *ctrl, + const enum global_comp_offset param, + const int32_t offset, + const uint8_t update_ctrl); + +void update_opt_param_offset( + struct sysinfo *ctrl, + const uint8_t channel, + const uint8_t ranks, + const uint8_t byte, + const enum opt_param optparam, + const int16_t off_in, + const bool update_ctrl); + uint8_t get_rx_bias(const struct sysinfo *ctrl); uint8_t get_tCWL(uint32_t mem_clock_mhz); diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h index b9043e8883..b0a121fa1a 100644 --- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h +++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h @@ -25,6 +25,18 @@ union ddr_data_tx_train_rank_reg { uint32_t raw; }; +union ddr_data_offset_comp_reg { + struct __packed { + int32_t drv_up : 6; // Bits 5:0 + int32_t drv_down : 6; // Bits 11:6 + int32_t odt_up : 5; // Bits 16:12 + int32_t odt_down : 5; // Bits 21:17 + int32_t t_clk_out : 5; // Bits 26:22 + int32_t slew_rate : 5; // Bits 31:27 + }; + uint32_t raw; +}; + union ddr_data_offset_train_reg { struct __packed { int32_t rcven : 6; // Bits 5:0 @@ -114,7 +126,20 @@ union ddr_data_control_2_reg { uint32_t raw; }; -union ddr_comp_data_comp_1_reg { +union ddr_data_comp_0_reg { + struct __packed { + uint32_t rcomp_drv_up : 6; // Bits 5:0 + uint32_t : 3; // Bits 8:6 + uint32_t rcomp_drv_down : 6; // Bits 14:9 + uint32_t vt_comp : 5; // Bits 19:15 + uint32_t tco_comp : 6; // Bits 25:20 + uint32_t slew_rate_comp : 5; // Bits 30:26 + uint32_t : 1; // Bits 31:31 + }; + uint32_t raw; +}; + +union ddr_data_comp_1_reg { struct __packed { uint32_t rcomp_odt_up : 6; // Bits 5:0 uint32_t : 3; // Bits 8:6 @@ -128,6 +153,42 @@ union ddr_comp_data_comp_1_reg { uint32_t raw; }; +union ddr_comp_cmd_comp_reg { + struct __packed { + uint32_t slew_rate_comp : 6; // Bits 5:0 + uint32_t tco_comp : 6; // Bits 11:6 + uint32_t rcomp_drv_up : 6; // Bits 17:12 + uint32_t rcomp_drv_down : 6; // Bits 23:18 + uint32_t ls_comp : 3; // Bits 26:24 + uint32_t : 5; // Bits 31:27 + }; + uint32_t raw; +}; + +union ddr_comp_ctl_comp_reg { + struct __packed { + uint32_t slew_rate_comp : 6; // Bits 5:0 + uint32_t tco_comp : 6; // Bits 11:6 + uint32_t rcomp_drv_up : 6; // Bits 17:12 + uint32_t rcomp_drv_down : 6; // Bits 23:18 + uint32_t ls_comp : 4; // Bits 27:24 + uint32_t : 4; // Bits 31:28 + }; + uint32_t raw; +}; + +union ddr_comp_clk_comp_reg { + struct __packed { + uint32_t slew_rate_comp : 5; // Bits 4:0 + uint32_t tco_comp : 6; // Bits 10:5 + uint32_t rcomp_drv_up : 6; // Bits 16:11 + uint32_t rcomp_drv_down : 6; // Bits 22:17 + uint32_t ls_comp : 4; // Bits 26:23 + uint32_t : 5; // Bits 31:27 + }; + uint32_t raw; +}; + union ddr_comp_ctl_0_reg { struct __packed { uint32_t : 3; // Bits 2:0 diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h index 1a168a3fc8..a944e12625 100644 --- a/src/northbridge/intel/haswell/registers/mchbar.h +++ b/src/northbridge/intel/haswell/registers/mchbar.h @@ -22,6 +22,7 @@ #define DDR_DATA_TRAIN_FEEDBACK(ch, byte) _DDRIO_C_R_B(0x0054, ch, 0, byte) +#define DDR_DATA_OFFSET_COMP_ch_b(ch, byte) _DDRIO_C_R_B(0x005c, ch, 0, byte) #define DQ_CONTROL_1(ch, byte) _DDRIO_C_R_B(0x0060, ch, 0, byte) #define DQ_CONTROL_2(ch, byte) _DDRIO_C_R_B(0x0064, ch, 0, byte) #define DDR_DATA_OFFSET_TRAIN_ch_b(ch, byte) _DDRIO_C_R_B(0x0070, ch, 0, byte) @@ -88,7 +89,8 @@ #define DDR_CLK_CB_STATUS 0x3918 /* DDR COMP (global) */ -#define DDR_COMP_DATA_COMP_1 0x3a04 +#define DDR_DATA_COMP_0 0x3a00 +#define DDR_DATA_COMP_1 0x3a04 #define DDR_COMP_CMD_COMP 0x3a08 #define DDR_COMP_CTL_COMP 0x3a0c #define DDR_COMP_CLK_COMP 0x3a10