Haswell NRI: Implement COMP offset optimisation
This algorithm minimises the per-channel, per-lane digital COMP offsets by adjusting the global COMP offset accordingly. The purpose of this is not fully known, but it is likely to prevent saturation of per-channel, per-lane registers during subsequent training steps, which NRI does not implement yet. Some of the COMP offset functions are generic since they are also used in said training steps. Tested on Asrock B85M Pro4, still boots to Arch Linux. Change-Id: Idb03c6c5ed85a522ff1b55905f522211d1472bd9 Signed-off-by: Angel Pons <th3fanbus@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/87833 Reviewed-by: Benjamin Doron <benjamin.doron00@gmail.com> Reviewed-by: Matt DeVillier <matt.devillier@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
2739c4b773
commit
d5854e4139
7 changed files with 597 additions and 15 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <commonlib/bsd/clamp.h>
|
||||
#include <console/console.h>
|
||||
#include <delay.h>
|
||||
#include <northbridge/intel/haswell/haswell.h>
|
||||
#include <timer.h>
|
||||
#include <types.h>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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", },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue