diff --git a/src/northbridge/intel/haswell/native_raminit/change_margin.c b/src/northbridge/intel/haswell/native_raminit/change_margin.c index 055c666eee..299c44a6b0 100644 --- a/src/northbridge/intel/haswell/native_raminit/change_margin.c +++ b/src/northbridge/intel/haswell/native_raminit/change_margin.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include #include #include #include @@ -152,3 +153,138 @@ void download_regfile( ddr_data_control_0.read_rf_rank = phys_rank; mchbar_write32(reg, ddr_data_control_0.raw); } + +static void update_data_offset_train( + struct sysinfo *ctrl, + const uint8_t param, + const uint8_t en_multicast, + const uint8_t channel_in, + const uint8_t rank, + const uint8_t byte_in, + const bool update_ctrl, + const enum regfile_mode regfile, + const uint32_t value) +{ + bool is_rd = false; + bool is_wr = false; + switch (param) { + case RdT: + case RdV: + case RcvEna: + is_rd = true; + break; + case WrT: + case WrDqsT: + is_wr = true; + break; + default: + die("%s: Invalid margin parameter %u\n", __func__, param); + } + if (en_multicast) { + mchbar_write32(DDR_DATA_OFFSET_TRAIN, value); + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { + if (!does_ch_exist(ctrl, channel)) + continue; + + download_regfile(ctrl, channel, true, rank, regfile, 0, is_rd, is_wr); + if (update_ctrl) { + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) + ctrl->data_offset_train[channel][byte] = value; + } + } + } else { + mchbar_write32(DDR_DATA_OFFSET_TRAIN_ch_b(channel_in, byte_in), value); + download_regfile(ctrl, channel_in, false, rank, regfile, byte_in, is_rd, is_wr); + if (update_ctrl) + ctrl->data_offset_train[channel_in][byte_in] = value; + } +} + +static uint32_t get_max_margin(const enum margin_parameter param) +{ + switch (param) { + case RcvEna: + case RdT: + case WrT: + case WrDqsT: + return MAX_POSSIBLE_TIME; + case RdV: + return MAX_POSSIBLE_VREF; + default: + die("%s: Invalid margin parameter %u\n", __func__, param); + } +} + +void change_margin( + struct sysinfo *ctrl, + const enum margin_parameter param, + const int32_t value0, + const bool en_multicast, + const uint8_t channel, + const uint8_t rank, + const uint8_t byte, + const bool update_ctrl, + const enum regfile_mode regfile) +{ + /** FIXME: Remove this **/ + if (rank == 0xff) + die("%s: rank is 0xff\n", __func__); + + if (!en_multicast && !does_ch_exist(ctrl, channel)) + die("%s: Tried to change margin of empty channel %u\n", __func__, channel); + + const uint32_t max_value = get_max_margin(param); + const int32_t v0 = clamp_s32(-max_value, value0, max_value); + + union ddr_data_offset_train_reg ddr_data_offset_train = { + .raw = en_multicast ? 0 : ctrl->data_offset_train[channel][byte], + }; + bool update_offset_train = false; + switch (param) { + case RcvEna: + ddr_data_offset_train.rcven = v0; + update_offset_train = true; + break; + case RdT: + ddr_data_offset_train.rx_dqs = v0; + update_offset_train = true; + break; + case WrT: + ddr_data_offset_train.tx_dq = v0; + update_offset_train = true; + break; + case WrDqsT: + ddr_data_offset_train.tx_dqs = v0; + update_offset_train = true; + break; + case RdV: + ddr_data_offset_train.vref = v0; + update_offset_train = true; + break; + default: + die("%s: Invalid margin parameter %u\n", __func__, param); + } + if (update_offset_train) { + update_data_offset_train( + ctrl, + param, + en_multicast, + channel, + rank, + byte, + update_ctrl, + regfile, + ddr_data_offset_train.raw); + } +} + +void change_1d_margin_multicast( + struct sysinfo *ctrl, + const enum margin_parameter param, + const int32_t value0, + const uint8_t rank, + const bool update_ctrl, + const enum regfile_mode regfile) +{ + change_margin(ctrl, param, value0, true, 0, rank, 0, update_ctrl, regfile); +} diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h index eaaaedad1e..1c8473056b 100644 --- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h @@ -36,6 +36,18 @@ #define RTTNOM_MASK (BIT(9) | BIT(6) | BIT(2)) +/* Margin parameter limits */ +#define MAX_POSSIBLE_TIME 31 +#define MAX_POSSIBLE_VREF 54 + +#define MAX_POSSIBLE_BOTH MAX_POSSIBLE_VREF + +#define MIN_TIME (-MAX_POSSIBLE_TIME) +#define MAX_TIME (MAX_POSSIBLE_TIME) + +#define MIN_VREF (-MAX_POSSIBLE_VREF) +#define MAX_VREF (MAX_POSSIBLE_VREF) + #define BASIC_VA_PAT_SPREAD_8 0x01010101 #define WDB_CACHE_LINE_SIZE 8 @@ -46,6 +58,14 @@ /* Specified in PI ticks. 64 PI ticks == 1 qclk */ #define tDQSCK_DRIFT 64 +enum margin_parameter { + RcvEna, + RdT, + WrT, + WrDqsT, + RdV, +}; + /* ZQ calibration types */ enum { ZQ_INIT, /* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init with tZQinit */ @@ -515,6 +535,25 @@ void download_regfile( bool read_rf_rd, bool read_rf_wr); +void change_margin( + struct sysinfo *ctrl, + const enum margin_parameter param, + const int32_t value0, + const bool en_multicast, + const uint8_t channel, + const uint8_t rank, + const uint8_t byte, + const bool update_ctrl, + const enum regfile_mode regfile); + +void change_1d_margin_multicast( + struct sysinfo *ctrl, + const enum margin_parameter param, + const int32_t value0, + const uint8_t rank, + const bool update_ctrl, + const enum regfile_mode regfile); + 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 b099f4bb82..a0e36ed082 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_train_reg { + struct __packed { + int32_t rcven : 6; // Bits 5:0 + int32_t rx_dqs : 6; // Bits 11:6 + int32_t tx_dq : 6; // Bits 17:12 + int32_t tx_dqs : 6; // Bits 23:18 + int32_t vref : 7; // Bits 30:24 + int32_t : 1; // Bits 31:31 + }; + uint32_t raw; +}; + union ddr_data_control_0_reg { struct __packed { uint32_t rx_training_mode : 1; // Bits 0:0 diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h index 9172d4f2b0..0acafbc826 100644 --- a/src/northbridge/intel/haswell/registers/mchbar.h +++ b/src/northbridge/intel/haswell/registers/mchbar.h @@ -21,6 +21,7 @@ #define DDR_DATA_TRAIN_FEEDBACK(ch, byte) _DDRIO_C_R_B(0x0054, 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) #define DQ_CONTROL_0(ch, byte) _DDRIO_C_R_B(0x0074, ch, 0, byte) /* DDR CKE per-channel */