/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include "raminit_native.h" #include "raminit_common.h" #include "raminit_tables.h" #include "sandybridge.h" /* FIXME: no support for 3-channel chipsets */ /* Number of programmed IOSAV subsequences. */ static unsigned int ssq_count = 0; void iosav_write_sequence(const int ch, const struct iosav_ssq *seq, const unsigned int length) { for (unsigned int i = 0; i < length; i++) { MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(ch, i)) = seq[i].sp_cmd_ctrl.raw; MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(ch, i)) = seq[i].subseq_ctrl.raw; MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(ch, i)) = seq[i].sp_cmd_addr.raw; MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(ch, i)) = seq[i].addr_update.raw; } ssq_count = length; } void iosav_run_queue(const int ch, const u8 loops, const u8 as_timer) { /* Should never happen */ if (ssq_count == 0) return; MCHBAR32(IOSAV_SEQ_CTL_ch(ch)) = loops | ((ssq_count - 1) << 18) | (as_timer << 22); } void wait_for_iosav(int channel) { while (1) { if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50) return; } } void iosav_run_once_and_wait(const int ch) { iosav_run_queue(ch, 1, 0); wait_for_iosav(ch); } void iosav_write_zqcs_sequence(int channel, int slotrank, u32 gap, u32 post, u32 wrap) { const struct iosav_ssq sequence[] = { /* DRAM command ZQCS */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ZQCS, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = gap, .post_ssq_wait = post, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = wrap, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_prea_sequence(int channel, int slotrank, u32 post, u32 wrap) { const struct iosav_ssq sequence[] = { /* DRAM command PREA */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = post, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = wrap, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_read_mpr_sequence( int channel, int slotrank, u32 tMOD, u32 loops, u32 gap, u32 loops2, u32 post2) { const struct iosav_ssq sequence[] = { /* * DRAM command MRS * * Write MR3 MPR enable. In this mode only RD and RDA * are allowed, and all reads return a predefined pattern. */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_MRS, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = tMOD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 4, .rowbits = 6, .bank = 3, .rank = slotrank, }, }, /* DRAM command RD */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = loops, .cmd_delay_gap = gap, .post_ssq_wait = 4, .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = loops2, .cmd_delay_gap = 4, .post_ssq_wait = post2, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, }, /* * DRAM command MRS * * Write MR3 MPR disable. */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_MRS, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = tMOD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 3, .rank = slotrank, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_prea_act_read_sequence(ramctr_timing *ctrl, int channel, int slotrank) { const struct iosav_ssq sequence[] = { /* DRAM command PREA */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->tRP, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 18, }, }, /* DRAM command ACT */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 8, .cmd_delay_gap = MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), .post_ssq_wait = ctrl->CAS, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = 1, .addr_wrap = 18, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 500, .cmd_delay_gap = 4, .post_ssq_wait = MAX(ctrl->tRTP, 8), .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command PREA */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->tRP, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 18, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_jedec_write_leveling_sequence( ramctr_timing *ctrl, int channel, int slotrank, int bank, u32 mr1reg) { /* First DQS/DQS# rising edge after write leveling mode is programmed */ const u32 tWLMRD = 40; const struct iosav_ssq sequence[] = { /* DRAM command MRS: enable DQs on this slotrank */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_MRS, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = tWLMRD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = mr1reg, .rowbits = 6, .bank = bank, .rank = slotrank, }, }, /* DRAM command NOP */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_NOP, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->CWL + ctrl->tWLO, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 8, .rowbits = 0, .bank = 0, .rank = slotrank, }, }, /* DRAM command NOP */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_NOP_ALT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->CAS + 38, .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 4, .rowbits = 0, .bank = 0, .rank = slotrank, }, }, /* DRAM command MRS: disable DQs on this slotrank */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_MRS, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->tMOD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = mr1reg | 1 << 12, .rowbits = 6, .bank = bank, .rank = slotrank, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_misc_write_sequence(ramctr_timing *ctrl, int channel, int slotrank, u32 gap0, u32 loops0, u32 gap1, u32 loops2, u32 wrap2) { const struct iosav_ssq sequence[] = { /* DRAM command ACT */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = loops0, .cmd_delay_gap = gap0, .post_ssq_wait = ctrl->tRCD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = loops0 == 1 ? 0 : 1, .addr_wrap = loops0 == 1 ? 0 : 18, }, }, /* DRAM command NOP */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_NOP, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = gap1, .post_ssq_wait = 4, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 8, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 31, }, }, /* DRAM command WR */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_WR, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = loops2, .cmd_delay_gap = 4, .post_ssq_wait = 4, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = wrap2, }, }, /* DRAM command NOP */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_NOP, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->CWL + ctrl->tWTR + 5, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 8, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 31, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_command_training_sequence( ramctr_timing *ctrl, int channel, int slotrank, unsigned int address) { const struct iosav_ssq sequence[] = { /* DRAM command ACT */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 8, .cmd_delay_gap = MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), .post_ssq_wait = ctrl->tRCD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = address, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = 1, .addr_wrap = 18, }, }, /* DRAM command WR */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_WR, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 32, .cmd_delay_gap = 4, .post_ssq_wait = ctrl->CWL + ctrl->tWTR + 8, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, .lfsr_upd = 3, .lfsr_xors = 2, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 32, .cmd_delay_gap = 4, .post_ssq_wait = MAX(ctrl->tRTP, 8), .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, .lfsr_upd = 3, .lfsr_xors = 2, }, }, /* DRAM command PRE */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 4, .post_ssq_wait = 15, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 18, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_data_write_sequence(ramctr_timing *ctrl, int channel, int slotrank) { const struct iosav_ssq sequence[] = { /* DRAM command ACT */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 4, .cmd_delay_gap = MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), .post_ssq_wait = ctrl->tRCD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = 0, .addr_wrap = 18, }, }, /* DRAM command WR */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_WR, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 32, .cmd_delay_gap = 20, .post_ssq_wait = ctrl->CWL + ctrl->tWTR + 8, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 32, .cmd_delay_gap = 20, .post_ssq_wait = MAX(ctrl->tRTP, 8), .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command PRE */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = ctrl->tRP, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_aggressive_write_read_sequence(ramctr_timing *ctrl, int channel, int slotrank) { const struct iosav_ssq sequence[] = { /* DRAM command ACT */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 4, .cmd_delay_gap = MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), .post_ssq_wait = ctrl->tRCD, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = 1, .addr_wrap = 18, }, }, /* DRAM command WR */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_WR, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 480, .cmd_delay_gap = 4, .post_ssq_wait = ctrl->tWTR + ctrl->CWL + 8, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 480, .cmd_delay_gap = 4, .post_ssq_wait = MAX(ctrl->tRTP, 8), .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command PRE */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 4, .post_ssq_wait = ctrl->tRP, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); } void iosav_write_memory_test_sequence(ramctr_timing *ctrl, int channel, int slotrank) { const struct iosav_ssq sequence[] = { /* DRAM command ACT */ [0] = { .sp_cmd_ctrl = { .command = IOSAV_ACT, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 4, .cmd_delay_gap = 8, .post_ssq_wait = 40, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 0, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_bank = 1, .addr_wrap = 18, }, }, /* DRAM command WR */ [1] = { .sp_cmd_ctrl = { .command = IOSAV_WR, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 100, .cmd_delay_gap = 4, .post_ssq_wait = 40, .data_direction = SSQ_WR, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command RD */ [2] = { .sp_cmd_ctrl = { .command = IOSAV_RD, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 100, .cmd_delay_gap = 4, .post_ssq_wait = 40, .data_direction = SSQ_RD, }, .sp_cmd_addr = { .address = 0, .rowbits = 0, .bank = 0, .rank = slotrank, }, .addr_update = { .inc_addr_8 = 1, .addr_wrap = 18, }, }, /* DRAM command PRE */ [3] = { .sp_cmd_ctrl = { .command = IOSAV_PRE, .ranksel_ap = 1, }, .subseq_ctrl = { .cmd_executions = 1, .cmd_delay_gap = 3, .post_ssq_wait = 40, .data_direction = SSQ_NA, }, .sp_cmd_addr = { .address = 1 << 10, .rowbits = 6, .bank = 0, .rank = slotrank, }, .addr_update = { .addr_wrap = 18, }, }, }; iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence)); }