blob: 96a618911842bc90632a64cc112e20aa3a7fb99b [file] [log] [blame]
Angel Pons6e5aabd2020-03-23 23:44:42 +01001/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01004#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01005#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +05306#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02007#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02008#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01009#include <northbridge/intel/sandybridge/chip.h>
10#include <device/pci_def.h>
11#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020012
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010013#include "raminit_native.h"
14#include "raminit_common.h"
Angel Pons7f6586f2020-03-21 12:45:12 +010015#include "raminit_tables.h"
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010016#include "sandybridge.h"
17
Angel Pons7c49cb82020-03-16 23:17:32 +010018/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010019
Angel Pons88521882020-01-05 20:21:20 +010020/* length: [1..4] */
Angel Ponsad704002020-05-02 22:51:58 +020021/* FIXME: replace with proper functions later */
22#define iosav_run_queue(ch, loops, length, as_timer) \
23 MCHBAR32(IOSAV_SEQ_CTL_ch(ch)) = ((loops) | (((length) - 1) << 18) | ((as_timer) << 22))
24
25#define iosav_run_once(ch, length) iosav_run_queue(ch, 1, length, 0)
Felix Held9cf1dd22018-07-31 14:52:40 +020026
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010027static void sfence(void)
28{
29 asm volatile ("sfence");
30}
31
Angel Pons7c49cb82020-03-16 23:17:32 +010032/* Toggle IO reset bit */
33static void toggle_io_reset(void)
34{
Angel Pons88521882020-01-05 20:21:20 +010035 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010036 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010037 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010038 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010039 udelay(1);
40}
41
42static u32 get_XOVER_CLK(u8 rankmap)
43{
44 return rankmap << 24;
45}
46
47static u32 get_XOVER_CMD(u8 rankmap)
48{
49 u32 reg;
50
Angel Pons7c49cb82020-03-16 23:17:32 +010051 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010052 reg = 0x4000;
53
Angel Pons7c49cb82020-03-16 23:17:32 +010054 /* Enable xover ctl */
55 if (rankmap & 0x03)
56 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010057
Angel Pons7c49cb82020-03-16 23:17:32 +010058 if (rankmap & 0x0c)
59 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010060
61 return reg;
62}
63
Angel Pons7c49cb82020-03-16 23:17:32 +010064/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010065u8 get_CWL(u32 tCK)
66{
Angel Pons7c49cb82020-03-16 23:17:32 +010067 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010068 switch (tCK) {
69 case TCK_1333MHZ:
70 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010071
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010072 case TCK_1200MHZ:
73 case TCK_1100MHZ:
74 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010075
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010076 case TCK_1066MHZ:
77 case TCK_1000MHZ:
78 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010079
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010080 case TCK_933MHZ:
81 case TCK_900MHZ:
82 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +010083
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010084 case TCK_800MHZ:
85 case TCK_700MHZ:
86 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +010087
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010088 case TCK_666MHZ:
89 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +010090
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010091 case TCK_533MHZ:
92 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +010093
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010094 default:
95 return 5;
96 }
97}
98
99void dram_find_common_params(ramctr_timing *ctrl)
100{
101 size_t valid_dimms;
102 int channel, slot;
103 dimm_info *dimms = &ctrl->info;
104
105 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
106 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100107
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100108 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100109
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100110 const dimm_attr *dimm = &dimms->dimm[channel][slot];
111 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
112 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100113
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100114 valid_dimms++;
115
116 /* Find all possible CAS combinations */
117 ctrl->cas_supported &= dimm->cas_supported;
118
119 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100120 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
121 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
122 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100123 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
124 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100125 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100126 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
127 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
128 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
129 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
130 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300131 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
132 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100133 }
134
135 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100136 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
137
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100138 if (!valid_dimms)
139 die("No valid DIMMs found");
140}
141
Angel Pons88521882020-01-05 20:21:20 +0100142void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100143{
144 u32 reg;
145 int channel;
146
147 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100148 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100149 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100150 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
151 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100152
Angel Pons7c49cb82020-03-16 23:17:32 +0100153 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100154 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100155 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
156 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100157 }
158}
159
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100160static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100161{
Angel Pons89ae6b82020-03-21 13:23:32 +0100162 u32 addr, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100163
164 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100165 /*
166 * ODT stretch:
167 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
168 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100169 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100170 if (stretch == 2)
171 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100172
Angel Pons88521882020-01-05 20:21:20 +0100173 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100174 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
175 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100176 } else {
Angel Pons88521882020-01-05 20:21:20 +0100177 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100178 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800179 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100180 }
181}
182
183void dram_timing_regs(ramctr_timing *ctrl)
184{
185 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100186 int channel;
187
188 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100189 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100190 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100191 reg |= (ctrl->tRCD << 0);
192 reg |= (ctrl->tRP << 4);
193 reg |= (ctrl->CAS << 8);
194 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100195 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100196 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
197 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100198
Angel Pons7c49cb82020-03-16 23:17:32 +0100199 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100200 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100201 reg |= (ctrl->tRRD << 0);
202 reg |= (ctrl->tRTP << 4);
203 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100204 reg |= (ctrl->tWTR << 12);
205 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100206 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100207 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100208 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
209 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100210
Angel Pons7c49cb82020-03-16 23:17:32 +0100211 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100212 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100213 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100214 reg |= (ctrl->tXPDLL << 0);
215 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100216 reg |= (ctrl->tAONPD << 8);
217 reg |= 0xa0000;
218 printram("OTHP [%x] = %x\n", addr, reg);
219 MCHBAR32(addr) = reg;
220
Angel Ponsca2f68a2020-03-22 13:15:12 +0100221 /* Debug parameters - only applies to Ivy Bridge */
222 if (IS_IVY_CPU(ctrl->cpu)) {
223 reg = 0;
224
225 /*
226 * If tXP and tXPDLL are very high, we need to increase them by one.
227 * This can only happen on Ivy Bridge, and when overclocking the RAM.
228 */
229 if (ctrl->tXP >= 8)
230 reg |= (1 << 12);
231
232 if (ctrl->tXPDLL >= 32)
233 reg |= (1 << 13);
234
235 MCHBAR32(TC_DTP_ch(channel)) = reg;
236 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100237
Felix Held9fe248f2018-07-31 20:59:45 +0200238 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100239
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100240 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100241
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100242 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100243 * TC-Refresh timing parameters:
244 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
245 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100246 */
247 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
248
Angel Pons7c49cb82020-03-16 23:17:32 +0100249 reg = ((ctrl->tREFI & 0xffff) << 0) |
250 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
251
Angel Pons88521882020-01-05 20:21:20 +0100252 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
253 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100254
Angel Pons88521882020-01-05 20:21:20 +0100255 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100256
Angel Pons7c49cb82020-03-16 23:17:32 +0100257 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100258 reg = 0;
259 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100260 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100261 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100262 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100263 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100264 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100265 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100266 reg = (reg & ~0xf0000000) | (val32 << 28);
267 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100268 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100269 }
270}
271
272void dram_dimm_mapping(ramctr_timing *ctrl)
273{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100274 int channel;
275 dimm_info *info = &ctrl->info;
276
277 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200278 dimm_attr *dimmA, *dimmB;
279 u32 reg = 0;
280
Angel Pons7c49cb82020-03-16 23:17:32 +0100281 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100282 dimmA = &info->dimm[channel][0];
283 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100284 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100285 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100286 dimmA = &info->dimm[channel][1];
287 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100288 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100289 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100290
Nico Huberac4f2162017-10-01 18:14:43 +0200291 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100292 reg |= (dimmA->size_mb / 256) << 0;
293 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200294 reg |= (dimmA->width / 8 - 1) << 19;
295 }
296
297 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100298 reg |= (dimmB->size_mb / 256) << 8;
299 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200300 reg |= (dimmB->width / 8 - 1) << 20;
301 }
302
Angel Pons7c49cb82020-03-16 23:17:32 +0100303 reg |= 1 << 21; /* Rank interleave */
304 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200305
Angel Pons7c49cb82020-03-16 23:17:32 +0100306 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100307 ctrl->mad_dimm[channel] = reg;
308 } else {
309 ctrl->mad_dimm[channel] = 0;
310 }
311 }
312}
313
Patrick Rudolphdd662872017-10-28 18:20:11 +0200314void dram_dimm_set_mapping(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100315{
316 int channel;
Patrick Rudolphdd662872017-10-28 18:20:11 +0200317 u32 ecc;
318
319 if (ctrl->ecc_enabled)
320 ecc = training ? (1 << 24) : (3 << 24);
321 else
322 ecc = 0;
323
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100324 FOR_ALL_CHANNELS {
Patrick Rudolphdd662872017-10-28 18:20:11 +0200325 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100326 }
Patrick Rudolphdd662872017-10-28 18:20:11 +0200327
328 //udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100329}
330
Angel Pons88521882020-01-05 20:21:20 +0100331void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100332{
333 u32 reg, ch0size, ch1size;
334 u8 val;
335 reg = 0;
336 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100337
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100338 if (training) {
339 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
340 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
341 } else {
342 ch0size = ctrl->channel_size_mb[0];
343 ch1size = ctrl->channel_size_mb[1];
344 }
345
346 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100347 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100348 val = ch1size / 256;
349 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100350 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100351 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100352 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100353
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100354 } else {
Angel Pons88521882020-01-05 20:21:20 +0100355 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100356 val = ch0size / 256;
357 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100358 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100359 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100360 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100361 }
362}
363
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100364#define DEFAULT_PCI_MMIO_SIZE 2048
365
366static unsigned int get_mmio_size(void)
367{
368 const struct device *dev;
369 const struct northbridge_intel_sandybridge_config *cfg = NULL;
370
Angel Ponsb31d1d72020-01-10 01:35:09 +0100371 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100372 if (dev)
373 cfg = dev->chip_info;
374
375 /* If this is zero, it just means devicetree.cb didn't set it */
376 if (!cfg || cfg->pci_mmio_size == 0)
377 return DEFAULT_PCI_MMIO_SIZE;
378 else
379 return cfg->pci_mmio_size;
380}
381
Patrick Rudolph05d4bf7e2017-10-28 16:36:09 +0200382/*
383 * Returns the ECC mode the NB is running at. It takes precedence over ECC capability.
384 * The ME/PCU/.. has the ability to change this.
385 * Return 0: ECC is optional
386 * Return 1: ECC is forced
387 */
388bool get_host_ecc_forced(void)
389{
390 /* read Capabilities A Register */
391 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
392 return !!(reg32 & (1 << 24));
393}
394
395/*
396 * Returns the ECC capability.
397 * The ME/PCU/.. has the ability to change this.
398 * Return 0: ECC is disabled
399 * Return 1: ECC is possible
400 */
401bool get_host_ecc_cap(void)
402{
403 /* read Capabilities A Register */
404 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
405 return !(reg32 & (1 << 25));
406}
407
Angel Pons88521882020-01-05 20:21:20 +0100408void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100409{
Angel Pons7c49cb82020-03-16 23:17:32 +0100410 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
411 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
412 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100413 uint16_t ggc;
414
415 mmiosize = get_mmio_size();
416
Felix Held87ddea22020-01-26 04:55:27 +0100417 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100418 if (!(ggc & 2)) {
419 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100420 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100421 } else {
422 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100423 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100424 }
425
426 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
427
428 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
429
430 mestolenbase = tom - me_uma_size;
431
Angel Pons7c49cb82020-03-16 23:17:32 +0100432 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
433
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100434 gfxstolenbase = toludbase - gfxstolen;
435 gttbase = gfxstolenbase - gttsize;
436
437 tsegbase = gttbase - tsegsize;
438
Angel Pons7c49cb82020-03-16 23:17:32 +0100439 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100440 tsegbasedelta = tsegbase & (tsegsize - 1);
441 tsegbase &= ~(tsegsize - 1);
442
443 gttbase -= tsegbasedelta;
444 gfxstolenbase -= tsegbasedelta;
445 toludbase -= tsegbasedelta;
446
Angel Pons7c49cb82020-03-16 23:17:32 +0100447 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100448 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100449 /* Reclaim is possible */
450 reclaim = 1;
451 remapbase = MAX(4096, tom - me_uma_size);
452 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
453 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100454 } else {
455 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100456 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100457 touudbase = tom - me_uma_size;
458 }
459
Angel Pons7c49cb82020-03-16 23:17:32 +0100460 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100461 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
462
Angel Pons7c49cb82020-03-16 23:17:32 +0100463 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100464 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100465 val = tom & 0xfff;
466 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100467 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100468 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100469
Angel Ponsb31d1d72020-01-10 01:35:09 +0100470 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100471 val = tom & 0xfffff000;
472 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100473 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100474 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100475
Angel Pons7c49cb82020-03-16 23:17:32 +0100476 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100477 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100478 val = toludbase & 0xfff;
479 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100480 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100481 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100482
Angel Pons7c49cb82020-03-16 23:17:32 +0100483 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100484 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100485 val = touudbase & 0xfff;
486 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100487 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100488 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100489
Angel Pons7c49cb82020-03-16 23:17:32 +0100490 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100491 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100492 val = touudbase & 0xfffff000;
493 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100494 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100495 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100496
497 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100498 /* REMAP BASE */
499 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100500 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100501
Angel Pons7c49cb82020-03-16 23:17:32 +0100502 /* REMAP LIMIT */
503 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100504 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100505 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100506 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100507 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100508 val = tsegbase & 0xfff;
509 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100510 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100511 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100512
Angel Pons7c49cb82020-03-16 23:17:32 +0100513 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100514 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100515 val = gfxstolenbase & 0xfff;
516 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100517 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100518 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100519
Angel Pons7c49cb82020-03-16 23:17:32 +0100520 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100521 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100522 val = gttbase & 0xfff;
523 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100524 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100525 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100526
527 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100528 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100529 val = (0x80000 - me_uma_size) & 0xfffff000;
530 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100531 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100532 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100533
Angel Pons7c49cb82020-03-16 23:17:32 +0100534 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100535 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100536 val = mestolenbase & 0xfff;
537 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100538 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100539 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100540
Angel Ponsb31d1d72020-01-10 01:35:09 +0100541 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100542 val = mestolenbase & 0xfffff000;
543 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100544 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100545 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100546
Angel Pons7c49cb82020-03-16 23:17:32 +0100547 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100548 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100549 val = (0x80000 - me_uma_size) & 0xfff;
550 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100551 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
552 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100553 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100554 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100555 }
556}
557
Angel Pons88521882020-01-05 20:21:20 +0100558static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100559{
560 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100561 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100562 return;
563 }
564}
565
Angel Pons88521882020-01-05 20:21:20 +0100566static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100567{
568 int channel, slotrank;
569
Angel Pons7c49cb82020-03-16 23:17:32 +0100570 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100571 channel = (ctrl->rankmap[0]) ? 0 : 1;
572
Angel Pons88521882020-01-05 20:21:20 +0100573 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100574
Angel Pons7c49cb82020-03-16 23:17:32 +0100575 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100576 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
577
578 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200579 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200580 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200581 1, 3, 8, SSQ_NA,
582 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200583 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100584
Angel Pons7c49cb82020-03-16 23:17:32 +0100585 /*
586 * Execute command queue - why is bit 22 set here?!
587 *
588 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
589 */
Angel Ponsad704002020-05-02 22:51:58 +0200590 iosav_run_queue(channel, 1, 1, true);
Felix Held9cf1dd22018-07-31 14:52:40 +0200591
Angel Pons88521882020-01-05 20:21:20 +0100592 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100593}
594
Angel Pons88521882020-01-05 20:21:20 +0100595void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100596{
Felix Held9fe248f2018-07-31 20:59:45 +0200597 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100598 int channel;
599
Angel Pons7c49cb82020-03-16 23:17:32 +0100600 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
601 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100602 do {
Angel Pons88521882020-01-05 20:21:20 +0100603 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100604 } while ((reg & 0x14) == 0);
605
Angel Pons7c49cb82020-03-16 23:17:32 +0100606 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100607 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100608 MCHBAR32(MC_INIT_STATE_G) = reg;
609 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100610 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100611 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100612
Angel Pons7c49cb82020-03-16 23:17:32 +0100613 /* Assert DIMM reset signal */
614 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100615
Angel Pons7c49cb82020-03-16 23:17:32 +0100616 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100617 udelay(200);
618
Angel Pons7c49cb82020-03-16 23:17:32 +0100619 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100620 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100621
Angel Pons7c49cb82020-03-16 23:17:32 +0100622 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100623 udelay(500);
624
Angel Pons7c49cb82020-03-16 23:17:32 +0100625 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100626 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100627
Angel Pons7c49cb82020-03-16 23:17:32 +0100628 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100629 udelay(1);
630
631 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100632 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200633 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100634 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100635
Angel Pons7c49cb82020-03-16 23:17:32 +0100636 /* Wait 10ns for ranks to settle */
637 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100638
639 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100640 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100641
Angel Pons7c49cb82020-03-16 23:17:32 +0100642 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100643 write_reset(ctrl);
644 }
645}
646
647static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
648{
Angel Pons7c49cb82020-03-16 23:17:32 +0100649 /* Get ODT based on rankmap */
650 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100651
652 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100653 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100654 } else {
655 return (const odtmap){120, 30};
656 }
657}
658
Angel Pons7c49cb82020-03-16 23:17:32 +0100659static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100660{
Angel Pons88521882020-01-05 20:21:20 +0100661 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100662
663 if (ctrl->rank_mirror[channel][slotrank]) {
664 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100665 swap the following pins:
666 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100667 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100668 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100669 }
670
671 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200672 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200673 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200674 1, 4, 4, SSQ_NA,
675 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200676 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100677
678 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200679 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200680 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200681 1, 4, 4, SSQ_NA,
682 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200683 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100684
685 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200686 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +0200687 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200688 1, 4, ctrl->tMOD, SSQ_NA,
689 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200690 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200691
Angel Pons7c49cb82020-03-16 23:17:32 +0100692 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +0200693 iosav_run_once(channel, 3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100694}
695
Angel Pons88521882020-01-05 20:21:20 +0100696static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100697{
698 u16 mr0reg, mch_cas, mch_wr;
699 static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 };
Patrick Rudolph74203de2017-11-20 11:57:01 +0100700 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100701
702 /* DLL Reset - self clearing - set after CLK frequency has been changed */
703 mr0reg = 0x100;
704
Angel Pons7c49cb82020-03-16 23:17:32 +0100705 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100706 if (ctrl->CAS < 12) {
707 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
708 } else {
709 mch_cas = (u16) (ctrl->CAS - 12);
710 mch_cas = ((mch_cas << 1) | 0x1);
711 }
712
Angel Pons7c49cb82020-03-16 23:17:32 +0100713 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100714 mch_wr = mch_wr_t[ctrl->tWR - 5];
715
Angel Pons7c49cb82020-03-16 23:17:32 +0100716 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
717 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
718 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100719
Angel Pons7c49cb82020-03-16 23:17:32 +0100720 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
721 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100722 return mr0reg;
723}
724
725static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
726{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200727 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100728}
729
730static u32 encode_odt(u32 odt)
731{
732 switch (odt) {
733 case 30:
734 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
735 case 60:
736 return (1 << 2); // RZQ/4
737 case 120:
738 return (1 << 6); // RZQ/2
739 default:
740 case 0:
741 return 0;
742 }
743}
744
745static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
746{
747 odtmap odt;
748 u32 mr1reg;
749
750 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100751 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100752
753 mr1reg |= encode_odt(odt.rttnom);
754
755 return mr1reg;
756}
757
758static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
759{
760 u16 mr1reg;
761
762 mr1reg = make_mr1(ctrl, rank, channel);
763
764 write_mrreg(ctrl, channel, rank, 1, mr1reg);
765}
766
767static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
768{
769 u16 pasr, cwl, mr2reg;
770 odtmap odt;
771 int srt;
772
773 pasr = 0;
774 cwl = ctrl->CWL - 5;
775 odt = get_ODT(ctrl, rank, channel);
776
777 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
778
779 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100780 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100781 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
782 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
783 mr2reg = (mr2reg & ~0x80) | (srt << 7);
784 mr2reg |= (odt.rttwr / 60) << 9;
785
786 write_mrreg(ctrl, channel, rank, 2, mr2reg);
787}
788
789static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
790{
791 write_mrreg(ctrl, channel, rank, 3, 0);
792}
793
Angel Pons88521882020-01-05 20:21:20 +0100794void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100795{
796 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100797 int channel;
798
799 FOR_ALL_POPULATED_CHANNELS {
800 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100801 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100802 dram_mr2(ctrl, slotrank, channel);
803
Angel Pons7c49cb82020-03-16 23:17:32 +0100804 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100805 dram_mr3(ctrl, slotrank, channel);
806
Angel Pons7c49cb82020-03-16 23:17:32 +0100807 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100808 dram_mr1(ctrl, slotrank, channel);
809
Angel Pons7c49cb82020-03-16 23:17:32 +0100810 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100811 dram_mr0(ctrl, slotrank, channel);
812 }
813 }
814
Angel Pons69e17142020-03-23 12:26:29 +0100815 /* DRAM command NOP (without ODT nor chip selects) */
Angel Ponsca00dec2020-05-02 15:04:00 +0200816 IOSAV_SUBSEQUENCE(BROADCAST_CH, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200817 IOSAV_NOP & ~(0xff << 8), 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200818 1, 4, 15, SSQ_NA,
819 2, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200820 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100821
822 /* DRAM command ZQCL */
Angel Ponsca00dec2020-05-02 15:04:00 +0200823 IOSAV_SUBSEQUENCE(BROADCAST_CH, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200824 IOSAV_ZQCS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200825 1, 4, 400, SSQ_NA,
826 1024, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200827 0, 0, 0, 1, 20, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100828
Angel Pons7c49cb82020-03-16 23:17:32 +0100829 /* Execute command queue on all channels. Do it four times. */
Angel Ponsad704002020-05-02 22:51:58 +0200830 iosav_run_queue(BROADCAST_CH, 4, 2, false);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100831
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100832 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100833 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100834 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100835 }
836
Angel Pons7c49cb82020-03-16 23:17:32 +0100837 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100838 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100839
840 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100841 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100842
Angel Pons88521882020-01-05 20:21:20 +0100843 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100844
845 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
846
Angel Pons7c49cb82020-03-16 23:17:32 +0100847 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100848 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100849
850 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200851 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200852 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200853 1, 36, 101, SSQ_NA,
854 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200855 0, 0, 0, 0, 31, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200856
Angel Pons7c49cb82020-03-16 23:17:32 +0100857 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +0200858 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100859
Angel Pons7c49cb82020-03-16 23:17:32 +0100860 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100861 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100862 }
863}
864
Felix Held3b906032020-01-14 17:05:43 +0100865static const u32 lane_base[] = {
866 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
867 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
868 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100869};
870
Angel Pons88521882020-01-05 20:21:20 +0100871void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100872{
Angel Pons88521882020-01-05 20:21:20 +0100873 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100874 int lane;
875 int slotrank, slot;
876 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100877 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100878
879 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100880 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
881 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100882 }
883
884 for (slot = 0; slot < NUM_SLOTS; slot++)
885 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
886 case 0:
887 default:
Angel Pons88521882020-01-05 20:21:20 +0100888 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100889 break;
890 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100891 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100892 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100893 break;
894 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100895 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100896 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100897 break;
898 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100899 pi_coding_ctrl[slot] =
900 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100901 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100902 break;
903 }
904
Angel Pons7c49cb82020-03-16 23:17:32 +0100905 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100906 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100907 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
908 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100909 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100910 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
911
Angel Pons88521882020-01-05 20:21:20 +0100912 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100913
Angel Pons7c49cb82020-03-16 23:17:32 +0100914 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100915 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
916 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100917
918 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100919 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100920 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100921 if (shift < 0)
922 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100923
Angel Pons88521882020-01-05 20:21:20 +0100924 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100925
926 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100927 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
928 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100929 }
930
Angel Pons88521882020-01-05 20:21:20 +0100931 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
932 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100933
Angel Pons88521882020-01-05 20:21:20 +0100934 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100935 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100936
Angel Pons88521882020-01-05 20:21:20 +0100937 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100938
939 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100940 int post_timA_min_high = 7, pre_timA_min_high = 7;
941 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100942 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100943 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100944
945 if (shift < 0)
946 shift = 0;
947
948 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200949 post_timA_min_high = MIN(post_timA_min_high,
950 (ctrl->timings[channel][slotrank].lanes[lane].
951 timA + shift) >> 6);
952 pre_timA_min_high = MIN(pre_timA_min_high,
953 ctrl->timings[channel][slotrank].lanes[lane].
954 timA >> 6);
955 post_timA_max_high = MAX(post_timA_max_high,
956 (ctrl->timings[channel][slotrank].lanes[lane].
957 timA + shift) >> 6);
958 pre_timA_max_high = MAX(pre_timA_max_high,
959 ctrl->timings[channel][slotrank].lanes[lane].
960 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100961 }
962
963 if (pre_timA_max_high - pre_timA_min_high <
964 post_timA_max_high - post_timA_min_high)
965 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100966
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100967 else if (pre_timA_max_high - pre_timA_min_high >
968 post_timA_max_high - post_timA_min_high)
969 shift_402x = -1;
970
Felix Helddee167e2019-12-30 17:30:16 +0100971 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100972 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100973 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100974
Angel Pons88521882020-01-05 20:21:20 +0100975 reg_roundtrip_latency |=
976 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100977 shift_402x) << (8 * slotrank);
978
979 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100980 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100981 (((ctrl->timings[channel][slotrank].lanes[lane].
982 timA + shift) & 0x3f)
983 |
984 ((ctrl->timings[channel][slotrank].lanes[lane].
985 rising + shift) << 8)
986 |
987 (((ctrl->timings[channel][slotrank].lanes[lane].
988 timA + shift -
989 (post_timA_min_high << 6)) & 0x1c0) << 10)
990 | ((ctrl->timings[channel][slotrank].lanes[lane].
991 falling + shift) << 20));
992
Felix Heldfb19c8a2020-01-14 21:27:59 +0100993 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100994 (((ctrl->timings[channel][slotrank].lanes[lane].
995 timC + shift) & 0x3f)
996 |
997 (((ctrl->timings[channel][slotrank].lanes[lane].
998 timB + shift) & 0x3f) << 8)
999 |
1000 (((ctrl->timings[channel][slotrank].lanes[lane].
1001 timB + shift) & 0x1c0) << 9)
1002 |
1003 (((ctrl->timings[channel][slotrank].lanes[lane].
1004 timC + shift) & 0x40) << 13));
1005 }
1006 }
Angel Pons88521882020-01-05 20:21:20 +01001007 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1008 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001009}
1010
Angel Pons88521882020-01-05 20:21:20 +01001011static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001012{
Angel Pons88521882020-01-05 20:21:20 +01001013 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001014
1015 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001016 write MR3 MPR enable
1017 in this mode only RD and RDA are allowed
1018 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001019 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001020 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001021 1, 3, ctrl->tMOD, SSQ_NA,
1022 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001023 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001024
1025 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001026 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001027 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001028 1, 3, 4, SSQ_RD,
1029 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001030 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001031
1032 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001033 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001034 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001035 15, 4, ctrl->CAS + 36, SSQ_NA,
1036 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001037 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001038
1039 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001040 write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001041 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001042 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001043 1, 3, ctrl->tMOD, SSQ_NA,
1044 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001045 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001046
Angel Pons7c49cb82020-03-16 23:17:32 +01001047 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001048 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001049
Angel Pons88521882020-01-05 20:21:20 +01001050 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001051}
1052
Angel Pons7c49cb82020-03-16 23:17:32 +01001053static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001054{
1055 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001056
1057 return (MCHBAR32(lane_base[lane] +
1058 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001059}
1060
1061struct run {
1062 int middle;
1063 int end;
1064 int start;
1065 int all;
1066 int length;
1067};
1068
1069static struct run get_longest_zero_run(int *seq, int sz)
1070{
1071 int i, ls;
1072 int bl = 0, bs = 0;
1073 struct run ret;
1074
1075 ls = 0;
1076 for (i = 0; i < 2 * sz; i++)
1077 if (seq[i % sz]) {
1078 if (i - ls > bl) {
1079 bl = i - ls;
1080 bs = ls;
1081 }
1082 ls = i + 1;
1083 }
1084 if (bl == 0) {
1085 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001086 ret.start = 0;
1087 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001088 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001089 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001090 return ret;
1091 }
1092
Angel Pons7c49cb82020-03-16 23:17:32 +01001093 ret.start = bs % sz;
1094 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001095 ret.middle = (bs + (bl - 1) / 2) % sz;
1096 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001097 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001098
1099 return ret;
1100}
1101
Angel Pons7c49cb82020-03-16 23:17:32 +01001102static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001103{
1104 int timA;
1105 int statistics[NUM_LANES][128];
1106 int lane;
1107
1108 for (timA = 0; timA < 128; timA++) {
1109 FOR_ALL_LANES {
1110 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1111 }
1112 program_timings(ctrl, channel);
1113
1114 test_timA(ctrl, channel, slotrank);
1115
1116 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001117 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001118 }
1119 }
1120 FOR_ALL_LANES {
1121 struct run rn = get_longest_zero_run(statistics[lane], 128);
1122 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1123 upperA[lane] = rn.end;
1124 if (upperA[lane] < rn.middle)
1125 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001126
Patrick Rudolph368b6152016-11-25 16:36:52 +01001127 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001128 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001129 }
1130}
1131
Angel Pons7c49cb82020-03-16 23:17:32 +01001132static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001133{
1134 int timA_delta;
1135 int statistics[NUM_LANES][51];
1136 int lane, i;
1137
1138 memset(statistics, 0, sizeof(statistics));
1139
1140 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001141
1142 FOR_ALL_LANES {
1143 ctrl->timings[channel][slotrank].lanes[lane].timA
1144 = upperA[lane] + timA_delta + 0x40;
1145 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001146 program_timings(ctrl, channel);
1147
1148 for (i = 0; i < 100; i++) {
1149 test_timA(ctrl, channel, slotrank);
1150 FOR_ALL_LANES {
1151 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001152 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001153 }
1154 }
1155 }
1156 FOR_ALL_LANES {
1157 int last_zero, first_all;
1158
1159 for (last_zero = -25; last_zero <= 25; last_zero++)
1160 if (statistics[lane][last_zero + 25])
1161 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001162
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001163 last_zero--;
1164 for (first_all = -25; first_all <= 25; first_all++)
1165 if (statistics[lane][first_all + 25] == 100)
1166 break;
1167
Angel Pons7c49cb82020-03-16 23:17:32 +01001168 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001169
1170 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001171 (last_zero + first_all) / 2 + upperA[lane];
1172
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001173 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001174 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001175 }
1176}
1177
Angel Pons891f2bc2020-01-10 01:27:28 +01001178static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001179{
1180 int works[NUM_LANES];
1181 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001182
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001183 while (1) {
1184 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001185
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001186 program_timings(ctrl, channel);
1187 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001188
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001189 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001190 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1191
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001192 if (works[lane])
1193 some_works = 1;
1194 else
1195 all_works = 0;
1196 }
1197 if (all_works)
1198 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001199
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001200 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001201 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001202 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1203 channel, slotrank);
1204 return MAKE_ERR;
1205 }
Angel Pons88521882020-01-05 20:21:20 +01001206 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207 printram("4024 -= 2;\n");
1208 continue;
1209 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001210 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001211 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001212
Felix Heldef4fe3e2019-12-31 14:15:05 +01001213 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001214 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1215 channel, slotrank);
1216 return MAKE_ERR;
1217 }
1218 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001219 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001220 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001221 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001222 }
1223 }
1224 return 0;
1225}
1226
1227struct timA_minmax {
1228 int timA_min_high, timA_max_high;
1229};
1230
Angel Pons88521882020-01-05 20:21:20 +01001231static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 struct timA_minmax *mnmx)
1233{
1234 int lane;
1235 mnmx->timA_min_high = 7;
1236 mnmx->timA_max_high = 0;
1237
1238 FOR_ALL_LANES {
1239 if (mnmx->timA_min_high >
1240 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1241 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001242 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001243 if (mnmx->timA_max_high <
1244 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1245 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001246 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001247 }
1248}
1249
Angel Pons88521882020-01-05 20:21:20 +01001250static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001251 struct timA_minmax *mnmx)
1252{
1253 struct timA_minmax post;
1254 int shift_402x = 0;
1255
Angel Pons7c49cb82020-03-16 23:17:32 +01001256 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001257 pre_timA_change(ctrl, channel, slotrank, &post);
1258
1259 if (mnmx->timA_max_high - mnmx->timA_min_high <
1260 post.timA_max_high - post.timA_min_high)
1261 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001262
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001263 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1264 post.timA_max_high - post.timA_min_high)
1265 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001266
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001267 else
1268 shift_402x = 0;
1269
Felix Heldef4fe3e2019-12-31 14:15:05 +01001270 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001271 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001272 printram("4024 += %d;\n", shift_402x);
1273 printram("4028 += %d;\n", shift_402x);
1274}
1275
Angel Pons7c49cb82020-03-16 23:17:32 +01001276/*
1277 * Compensate the skew between DQS and DQs.
1278 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001279 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1280 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001281 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001282 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001283 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001284 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1285 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1286 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001287 * DQ in phase the data being read is expected to alternate on every byte:
1288 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001289 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001290 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001291 * Once the controller has detected this pattern a bit in the result register is set for the
1292 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001293 */
Angel Pons88521882020-01-05 20:21:20 +01001294int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001295{
1296 int channel, slotrank, lane;
1297 int err;
1298
1299 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1300 int all_high, some_high;
1301 int upperA[NUM_LANES];
1302 struct timA_minmax mnmx;
1303
Angel Pons88521882020-01-05 20:21:20 +01001304 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001305
Felix Held2bb3cdf2018-07-28 00:23:59 +02001306 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001307 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001308 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001309 1, 3, ctrl->tRP, SSQ_NA,
1310 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001311 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001312
Angel Pons7c49cb82020-03-16 23:17:32 +01001313 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001314 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001315
Angel Pons88521882020-01-05 20:21:20 +01001316 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001317
Felix Heldef4fe3e2019-12-31 14:15:05 +01001318 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001319 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001320 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001321
Felix Held2bb3cdf2018-07-28 00:23:59 +02001322 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001323
Felix Held2bb3cdf2018-07-28 00:23:59 +02001324 all_high = 1;
1325 some_high = 0;
1326 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001327 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001328 some_high = 1;
1329 else
1330 all_high = 0;
1331 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001332
1333 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001334 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001335 printram("4028--;\n");
1336 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001337 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001338 upperA[lane] -= 0x40;
1339
1340 }
1341 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001342 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001343 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001344 printram("4024++;\n");
1345 printram("4028++;\n");
1346 }
1347
1348 program_timings(ctrl, channel);
1349
1350 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1351
1352 err = discover_402x(ctrl, channel, slotrank, upperA);
1353 if (err)
1354 return err;
1355
1356 post_timA_change(ctrl, channel, slotrank, &mnmx);
1357 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1358
1359 discover_timA_fine(ctrl, channel, slotrank, upperA);
1360
1361 post_timA_change(ctrl, channel, slotrank, &mnmx);
1362 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1363
1364 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001365 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1366 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001367 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001368 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001369 printram("4028 -= %d;\n", mnmx.timA_min_high);
1370
1371 post_timA_change(ctrl, channel, slotrank, &mnmx);
1372
1373 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001374 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001375 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001376
1377 printram("final results:\n");
1378 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001379 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001380 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001381
Angel Pons88521882020-01-05 20:21:20 +01001382 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001383
1384 toggle_io_reset();
1385 }
1386
1387 FOR_ALL_POPULATED_CHANNELS {
1388 program_timings(ctrl, channel);
1389 }
1390 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001391 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001392 }
1393 return 0;
1394}
1395
Angel Pons88521882020-01-05 20:21:20 +01001396static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001397{
1398 int lane;
1399
1400 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001401 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1402 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001403 }
1404
Angel Pons88521882020-01-05 20:21:20 +01001405 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001406
1407 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001408 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001409 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001410 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
1411 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001412 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001413
1414 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001415 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001416 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001417 1, 4, 4, SSQ_WR,
1418 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001419 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001420
1421 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001422 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001423 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001424 500, 4, 4, SSQ_WR,
1425 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001426 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001427
1428 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001429 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001430 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001431 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1432 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001433 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001434
Angel Pons7c49cb82020-03-16 23:17:32 +01001435 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001436 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001437
Angel Pons88521882020-01-05 20:21:20 +01001438 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001439
1440 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001441 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001442 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001443 1, 3, ctrl->tRP, SSQ_NA,
1444 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001445 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001446
1447 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001448 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001449 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001450 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->CAS, SSQ_NA,
1451 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001452 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001453
1454 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001455 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001456 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001457 500, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
1458 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001459 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001460
1461 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001462 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001463 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001464 1, 3, ctrl->tRP, SSQ_NA,
1465 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001466 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001467
Angel Pons7c49cb82020-03-16 23:17:32 +01001468 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001469 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001470
Angel Pons88521882020-01-05 20:21:20 +01001471 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001472}
1473
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001474static void timC_threshold_process(int *data, const int count)
1475{
1476 int min = data[0];
1477 int max = min;
1478 int i;
1479 for (i = 1; i < count; i++) {
1480 if (min > data[i])
1481 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001482
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001483 if (max < data[i])
1484 max = data[i];
1485 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001486 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001487 for (i = 0; i < count; i++)
1488 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001489
Angel Pons891f2bc2020-01-10 01:27:28 +01001490 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001491}
1492
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001493static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1494{
1495 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001496 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001497 int lane;
1498
Angel Pons88521882020-01-05 20:21:20 +01001499 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001500
1501 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001502 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001503 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001504 1, 3, ctrl->tRP, SSQ_NA,
1505 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001506 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001507
Angel Pons7c49cb82020-03-16 23:17:32 +01001508 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001509 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001510
1511 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001512 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001513 program_timings(ctrl, channel);
1514
1515 test_timC(ctrl, channel, slotrank);
1516
1517 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001518 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001519 }
1520 }
1521 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001522 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1523
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001524 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001525 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1526 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001527 /*
1528 * With command training not being done yet, the lane can be erroneous.
1529 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001530 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001531 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1532 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1533
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001534 if (rn.all || rn.length < 8) {
1535 printk(BIOS_EMERG, "timC recovery failed\n");
1536 return MAKE_ERR;
1537 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001538 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001539 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001540 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001541 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001542 }
1543 return 0;
1544}
1545
Angel Pons88521882020-01-05 20:21:20 +01001546static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001547{
1548 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001549
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001550 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1551 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001552
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001553 return ret;
1554}
1555
Angel Pons88521882020-01-05 20:21:20 +01001556static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301558 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001559 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001560
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001561 for (j = 0; j < 16; j++)
1562 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001563
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001564 sfence();
1565}
1566
Angel Pons88521882020-01-05 20:21:20 +01001567static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568{
1569 int ret = 0;
1570 int channel;
1571 FOR_ALL_POPULATED_CHANNELS ret++;
1572 return ret;
1573}
1574
Angel Pons88521882020-01-05 20:21:20 +01001575static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001576{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301577 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001578 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301579 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001580
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001581 for (j = 0; j < 16; j++)
1582 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001583
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001584 for (j = 0; j < 16; j++)
1585 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001586
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001587 sfence();
1588}
1589
Angel Pons88521882020-01-05 20:21:20 +01001590static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001591{
1592 int channel, slotrank, lane;
1593
1594 FOR_ALL_POPULATED_CHANNELS {
1595 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001596 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1597 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001598 }
1599
1600 program_timings(ctrl, channel);
1601
1602 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001603 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001604
1605 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001606 write MR3 MPR enable
1607 in this mode only RD and RDA are allowed
1608 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001609 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001610 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001611 1, 3, ctrl->tMOD, SSQ_NA,
1612 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001613 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001614
1615 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001616 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001617 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001618 3, 4, 4, SSQ_RD,
1619 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001620 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001621
1622 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001623 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001624 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001625 1, 4, ctrl->CAS + 8, SSQ_NA,
1626 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001627 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001628
1629 /* DRAM command MRS
1630 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001631 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001632 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001633 1, 3, ctrl->tMOD, SSQ_NA,
1634 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001635 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001636
Angel Pons7c49cb82020-03-16 23:17:32 +01001637 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001638 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001639
Angel Pons88521882020-01-05 20:21:20 +01001640 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001641 }
1642
1643 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001644 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1645 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001646 }
1647
1648 program_timings(ctrl, channel);
1649
1650 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001651 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001652 /* DRAM command MRS
1653 * write MR3 MPR enable
1654 * in this mode only RD and RDA are allowed
1655 * all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001656 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001657 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001658 1, 3, ctrl->tMOD, SSQ_NA,
1659 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001660 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001661
1662 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001663 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001664 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001665 3, 4, 4, SSQ_RD,
1666 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001667 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001668
1669 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001670 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001671 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001672 1, 4, ctrl->CAS + 8, SSQ_NA,
1673 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001674 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001675
1676 /* DRAM command MRS
1677 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001678 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001679 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001680 1, 3, ctrl->tMOD, SSQ_NA,
1681 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001682 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001683
Angel Pons7c49cb82020-03-16 23:17:32 +01001684 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001685 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001686
Angel Pons88521882020-01-05 20:21:20 +01001687 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001688 }
1689 }
1690}
1691
Angel Pons88521882020-01-05 20:21:20 +01001692static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001693{
1694 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001695 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001696
Angel Pons88521882020-01-05 20:21:20 +01001697 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001698 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001699 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001700 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001701 1, 3, ctrl->CWL + ctrl->tWLO, SSQ_WR,
1702 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001703 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001704
1705 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001706 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001707 IOSAV_NOP_ALT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001708 1, 3, ctrl->CAS + 38, SSQ_RD,
1709 4, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001710 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001711
Angel Pons7c49cb82020-03-16 23:17:32 +01001712 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001713 iosav_run_once(channel, 2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001714
Angel Pons88521882020-01-05 20:21:20 +01001715 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001716
1717 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001718 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001719}
1720
1721static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1722{
1723 int timB;
1724 int statistics[NUM_LANES][128];
1725 int lane;
1726
Angel Pons88521882020-01-05 20:21:20 +01001727 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001728
1729 for (timB = 0; timB < 128; timB++) {
1730 FOR_ALL_LANES {
1731 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1732 }
1733 program_timings(ctrl, channel);
1734
1735 test_timB(ctrl, channel, slotrank);
1736
1737 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001738 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1739 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1740 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001741 }
1742 }
1743 FOR_ALL_LANES {
1744 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001745 /*
1746 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1747 * of timB by a small value, which might cause the 6-bit value to overflow if
1748 * it's close to 0x3f. Increment the value by a small offset if it's likely
1749 * to overflow, to make sure it won't overflow while running tests and bricks
1750 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001751 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001752 * TODO: find out why some tests (edge write discovery) increment timB.
1753 */
1754 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001755 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001756 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001757 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001758
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001759 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1760 if (rn.all) {
1761 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1762 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001763
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001764 return MAKE_ERR;
1765 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001766 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1767 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001768 }
1769 return 0;
1770}
1771
1772static int get_timB_high_adjust(u64 val)
1773{
1774 int i;
1775
1776 /* good */
1777 if (val == 0xffffffffffffffffLL)
1778 return 0;
1779
1780 if (val >= 0xf000000000000000LL) {
1781 /* needs negative adjustment */
1782 for (i = 0; i < 8; i++)
1783 if (val << (8 * (7 - i) + 4))
1784 return -i;
1785 } else {
1786 /* needs positive adjustment */
1787 for (i = 0; i < 8; i++)
1788 if (val >> (8 * (7 - i) + 4))
1789 return i;
1790 }
1791 return 8;
1792}
1793
Angel Pons88521882020-01-05 20:21:20 +01001794static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001795{
1796 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001797 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001798 FOR_ALL_POPULATED_CHANNELS {
1799 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001800 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001801 }
1802 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1803
Angel Pons88521882020-01-05 20:21:20 +01001804 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001805
Angel Pons88521882020-01-05 20:21:20 +01001806 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001807
1808 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001809 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001810 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001811 1, 3, ctrl->tRCD, SSQ_NA,
1812 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001813 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001814
1815 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001816 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001817 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001818 1, 3, 4, SSQ_WR,
1819 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001820 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001821
1822 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001823 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001824 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001825 3, 4, 4, SSQ_WR,
1826 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001827 0, 1, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001828
1829 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001830 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001831 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001832 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1833 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001834 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001835
Angel Pons7c49cb82020-03-16 23:17:32 +01001836 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001837 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001838
Angel Pons88521882020-01-05 20:21:20 +01001839 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001840
1841 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001842 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001843 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001844 1, 3, ctrl->tRP, SSQ_NA,
1845 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001846 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001847
1848 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001849 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001850 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001851 1, 3, ctrl->tRCD, SSQ_NA,
1852 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001853 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001854
1855 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001856 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001857 IOSAV_RD, 3,
Angel Ponsca00dec2020-05-02 15:04:00 +02001858 1, 3, ctrl->tRP +
1859 ctrl->timings[channel][slotrank].roundtrip_latency +
1860 ctrl->timings[channel][slotrank].io_latency, SSQ_RD,
1861 8, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001862 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001863
Angel Pons7c49cb82020-03-16 23:17:32 +01001864 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001865 iosav_run_once(channel, 3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001866
Angel Pons88521882020-01-05 20:21:20 +01001867 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001868 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001869 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001870 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001871 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001872 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1873 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1874 get_timB_high_adjust(res) * 64;
1875
1876 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001877 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1878 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001879 }
1880 }
Angel Pons88521882020-01-05 20:21:20 +01001881 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001882}
1883
Angel Pons88521882020-01-05 20:21:20 +01001884static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001885{
1886 int slotrank;
1887
Angel Pons88521882020-01-05 20:21:20 +01001888 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001889
1890 /* choose an existing rank. */
1891 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1892
Angel Pons69e17142020-03-23 12:26:29 +01001893 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001894 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001895 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02001896 1, 4, 4, SSQ_NA,
1897 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001898 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001899
Angel Pons7c49cb82020-03-16 23:17:32 +01001900 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001901 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001902
Angel Pons88521882020-01-05 20:21:20 +01001903 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001904}
1905
Angel Pons7c49cb82020-03-16 23:17:32 +01001906/*
1907 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001908 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001909 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1910 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1911 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1912 * CLK/ADDR/CMD signals have the same routing delay.
1913 *
1914 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1915 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1916 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001917 */
Angel Pons88521882020-01-05 20:21:20 +01001918int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001919{
1920 int channel, slotrank, lane;
1921 int err;
1922
1923 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001924 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001925
1926 FOR_ALL_POPULATED_CHANNELS {
1927 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001928 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001929 }
1930
Angel Pons7c49cb82020-03-16 23:17:32 +01001931 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001932 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001933 FOR_ALL_POPULATED_CHANNELS {
1934 write_op(ctrl, channel);
1935 }
1936
Angel Pons7c49cb82020-03-16 23:17:32 +01001937 /* Enable write leveling on all ranks
1938 Disable all DQ outputs
1939 Only NOP is allowed in this mode */
1940 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1941 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001942 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001943
Angel Pons88521882020-01-05 20:21:20 +01001944 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001945
1946 toggle_io_reset();
1947
Angel Pons7c49cb82020-03-16 23:17:32 +01001948 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001949 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1950 err = discover_timB(ctrl, channel, slotrank);
1951 if (err)
1952 return err;
1953 }
1954
Angel Pons7c49cb82020-03-16 23:17:32 +01001955 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001957 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001958
Angel Pons88521882020-01-05 20:21:20 +01001959 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960
1961 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001962 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001963
Angel Pons7c49cb82020-03-16 23:17:32 +01001964 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001965 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001966
1967 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001968 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1969 MCHBAR32(IOSAV_STATUS_ch(channel));
1970 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001971
1972 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001973 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001974 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02001975 1, 36, 101, SSQ_NA,
1976 0, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001977 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001978
Angel Pons7c49cb82020-03-16 23:17:32 +01001979 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001980 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001981
Angel Pons88521882020-01-05 20:21:20 +01001982 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001983 }
1984
1985 toggle_io_reset();
1986
1987 printram("CPE\n");
1988 precharge(ctrl);
1989 printram("CPF\n");
1990
1991 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001992 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001993 }
1994
1995 FOR_ALL_POPULATED_CHANNELS {
1996 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01001997 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001998 }
1999
2000 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2001 err = discover_timC(ctrl, channel, slotrank);
2002 if (err)
2003 return err;
2004 }
2005
2006 FOR_ALL_POPULATED_CHANNELS
2007 program_timings(ctrl, channel);
2008
2009 /* measure and adjust timB timings */
2010 adjust_high_timB(ctrl);
2011
2012 FOR_ALL_POPULATED_CHANNELS
2013 program_timings(ctrl, channel);
2014
2015 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002016 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002017 }
2018 return 0;
2019}
2020
Angel Pons88521882020-01-05 20:21:20 +01002021static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002022{
2023 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2024 int timC_delta;
2025 int lanes_ok = 0;
2026 int ctr = 0;
2027 int lane;
2028
2029 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2030 FOR_ALL_LANES {
2031 ctrl->timings[channel][slotrank].lanes[lane].timC =
2032 saved_rt.lanes[lane].timC + timC_delta;
2033 }
2034 program_timings(ctrl, channel);
2035 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002036 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002037 }
2038
Angel Pons88521882020-01-05 20:21:20 +01002039 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002040
Angel Pons88521882020-01-05 20:21:20 +01002041 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002042 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002043 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002044 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002045 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2046 ctr, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002047 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9fe248f2018-07-31 20:59:45 +02002048
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002049 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002050 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002051 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002052 32, 4, ctrl->CWL + ctrl->tWTR + 8, SSQ_WR,
2053 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002054 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002055
Angel Ponsc36cd072020-05-02 16:51:39 +02002056 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002057
2058 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002059 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002060 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002061 32, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2062 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002063 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002064
Angel Ponsc36cd072020-05-02 16:51:39 +02002065 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002066
2067 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002068 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002069 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002070 1, 4, 15, SSQ_NA,
2071 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002072 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002073
Angel Pons7c49cb82020-03-16 23:17:32 +01002074 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002075 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002076
Angel Pons88521882020-01-05 20:21:20 +01002077 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002078 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002079 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002080
2081 if (r32 == 0)
2082 lanes_ok |= 1 << lane;
2083 }
2084 ctr++;
Patrick Rudolphdd662872017-10-28 18:20:11 +02002085 if (lanes_ok == ((1 << ctrl->lanes) - 1))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002086 break;
2087 }
2088
2089 ctrl->timings[channel][slotrank] = saved_rt;
2090
Patrick Rudolphdd662872017-10-28 18:20:11 +02002091 return lanes_ok != ((1 << ctrl->lanes) - 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002092}
2093
Angel Pons88521882020-01-05 20:21:20 +01002094static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002095{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302096 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002097 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2098 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002099
2100 if (patno) {
2101 u8 base8 = 0x80 >> ((patno - 1) % 8);
2102 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2103 for (i = 0; i < 32; i++) {
2104 for (j = 0; j < 16; j++) {
2105 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002106
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002107 if (invert[patno - 1][i] & (1 << (j / 2)))
2108 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002109
2110 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002111 }
2112 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002113 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002114 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2115 for (j = 0; j < 16; j++) {
2116 const u32 val = pattern[i][j];
2117 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2118 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002119 }
2120 sfence();
2121 }
2122}
2123
Angel Pons88521882020-01-05 20:21:20 +01002124static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002125{
2126 int channel, slotrank;
2127
2128 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002129 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002130
Angel Pons7c49cb82020-03-16 23:17:32 +01002131 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002132 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2133
2134 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002135 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002136 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002137 1, 4, 4, SSQ_NA,
2138 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002139 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002140
Angel Pons7c49cb82020-03-16 23:17:32 +01002141 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002142 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002143
Angel Pons88521882020-01-05 20:21:20 +01002144 wait_for_iosav(channel);
2145 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002146 }
2147
2148 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002149 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002150 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002151 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002152
2153 /* choose an existing rank. */
2154 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2155
2156 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002157 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002158 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002159 1, 4, 4, SSQ_NA,
2160 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002161 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002162
Angel Pons7c49cb82020-03-16 23:17:32 +01002163 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002164 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002165
Angel Pons88521882020-01-05 20:21:20 +01002166 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002167 }
2168
Angel Pons7c49cb82020-03-16 23:17:32 +01002169 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002170 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002171
2172 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002173 dram_mrscommands(ctrl);
2174
2175 toggle_io_reset();
2176}
2177
2178#define MIN_C320C_LEN 13
2179
2180static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2181{
2182 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2183 int slotrank;
2184 int c320c;
2185 int stat[NUM_SLOTRANKS][256];
2186 int delta = 0;
2187
2188 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2189
2190 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002191 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002192 }
2193
2194 ctrl->cmd_stretch[channel] = cmd_stretch;
2195
Angel Pons88521882020-01-05 20:21:20 +01002196 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002197 (ctrl->tRRD << 0)
2198 | (ctrl->tRTP << 4)
2199 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002200 | (ctrl->tWTR << 12)
2201 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002202 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002203 | (ctrl->cmd_stretch[channel] << 30);
2204
2205 if (ctrl->cmd_stretch[channel] == 2)
2206 delta = 2;
2207 else if (ctrl->cmd_stretch[channel] == 0)
2208 delta = 4;
2209
2210 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002211 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002212 }
2213
2214 for (c320c = -127; c320c <= 127; c320c++) {
2215 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002216 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002217 }
2218 program_timings(ctrl, channel);
2219 reprogram_320c(ctrl);
2220 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002221 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002222 }
2223 }
2224 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002225 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2226
Angel Pons88521882020-01-05 20:21:20 +01002227 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002228 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2229 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002230
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002231 if (rn.all || rn.length < MIN_C320C_LEN) {
2232 FOR_ALL_POPULATED_RANKS {
2233 ctrl->timings[channel][slotrank] =
2234 saved_timings[channel][slotrank];
2235 }
2236 return MAKE_ERR;
2237 }
2238 }
2239
2240 return 0;
2241}
2242
Angel Pons7c49cb82020-03-16 23:17:32 +01002243/*
2244 * Adjust CMD phase shift and try multiple command rates.
2245 * A command rate of 2T doubles the time needed for address and command decode.
2246 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002247int command_training(ramctr_timing *ctrl)
2248{
2249 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002250
2251 FOR_ALL_POPULATED_CHANNELS {
2252 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002253 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002254 }
2255
2256 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002257 int cmdrate, err;
2258
2259 /*
2260 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002261 * Issue:
2262 * While c320c discovery seems to succeed raminit will fail in write training.
2263 *
2264 * Workaround:
2265 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2266 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002267 *
2268 * Single DIMM per channel:
2269 * Try command rate 1T and 2T
2270 */
2271 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002272 if (ctrl->tCMD)
2273 /* XMP gives the CMD rate in clock ticks, not ns */
2274 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002275
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002276 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002277 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2278
2279 if (!err)
2280 break;
2281 }
2282
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002283 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002284 printk(BIOS_EMERG, "c320c discovery failed\n");
2285 return err;
2286 }
2287
Angel Pons891f2bc2020-01-10 01:27:28 +01002288 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002289 }
2290
2291 FOR_ALL_POPULATED_CHANNELS
2292 program_timings(ctrl, channel);
2293
2294 reprogram_320c(ctrl);
2295 return 0;
2296}
2297
Angel Pons891f2bc2020-01-10 01:27:28 +01002298static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002299{
2300 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002301 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002302 int lane;
2303
2304 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2305 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002306 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002307 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002308 }
2309 program_timings(ctrl, channel);
2310
2311 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002312 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2313 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002314 }
2315
Angel Pons88521882020-01-05 20:21:20 +01002316 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002317
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002318 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002319 write MR3 MPR enable
2320 in this mode only RD and RDA are allowed
2321 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002322 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002323 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002324 1, 3, ctrl->tMOD, SSQ_NA,
2325 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002326 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002327
2328 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002329 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002330 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002331 500, 4, 4, SSQ_RD,
2332 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002333 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002334
2335 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002336 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002337 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002338 1, 4, ctrl->CAS + 8, SSQ_NA,
2339 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002340 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002341
2342 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002343 MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002344 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002345 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002346 1, 3, ctrl->tMOD, SSQ_NA,
2347 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002348 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002349
Angel Pons7c49cb82020-03-16 23:17:32 +01002350 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002351 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002352
Angel Pons88521882020-01-05 20:21:20 +01002353 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002354
2355 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002356 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002357 }
2358 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002359
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002360 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002361 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002362 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002363
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002364 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002365 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2366 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002367 return MAKE_ERR;
2368 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002369 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002370 }
2371 return 0;
2372}
2373
2374int discover_edges(ramctr_timing *ctrl)
2375{
2376 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2377 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2378 int channel, slotrank, lane;
2379 int err;
2380
Angel Pons88521882020-01-05 20:21:20 +01002381 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002382
2383 toggle_io_reset();
2384
2385 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002386 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002387 }
2388
2389 FOR_ALL_POPULATED_CHANNELS {
2390 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002391 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002392 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002393 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002394 }
2395
2396 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002397 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2398 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002399 }
2400
2401 program_timings(ctrl, channel);
2402
2403 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002404 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002405
2406 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002407 MR3 enable MPR
2408 write MR3 MPR enable
2409 in this mode only RD and RDA are allowed
2410 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002411 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002412 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002413 1, 3, ctrl->tMOD, SSQ_NA,
2414 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002415 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002416
2417 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002418 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002419 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002420 3, 4, 4, SSQ_RD,
2421 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002422 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002423
2424 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002425 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002426 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002427 1, 4, ctrl->CAS + 8, SSQ_NA,
2428 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002429 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002430
2431 /* DRAM command MRS
2432 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002433 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002434 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002435 1, 3, ctrl->tMOD, SSQ_NA,
2436 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002437 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002438
Angel Pons7c49cb82020-03-16 23:17:32 +01002439 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002440 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002441
Angel Pons88521882020-01-05 20:21:20 +01002442 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002443 }
2444
2445 /* XXX: check any measured value ? */
2446
2447 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002448 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002449 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002450 }
2451
2452 program_timings(ctrl, channel);
2453
2454 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002455 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002456
2457 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002458 MR3 enable MPR
2459 write MR3 MPR enable
2460 in this mode only RD and RDA are allowed
2461 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002462 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002463 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002464 1, 3, ctrl->tMOD, SSQ_NA,
2465 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002466 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002467
2468 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002469 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002470 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002471 3, 4, 4, SSQ_RD,
2472 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002473 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002474
2475 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002476 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002477 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002478 1, 4, ctrl->CAS + 8, SSQ_NA,
2479 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002480 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002481
2482 /* DRAM command MRS
2483 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002484 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002485 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002486 1, 3, ctrl->tMOD, SSQ_NA,
2487 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002488 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002489
Angel Pons7c49cb82020-03-16 23:17:32 +01002490 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002491 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002492
Angel Pons88521882020-01-05 20:21:20 +01002493 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002494 }
2495
2496 /* XXX: check any measured value ? */
2497
2498 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002499 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002500 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002501 }
2502
2503 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002504 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002505 }
2506
Angel Pons0c3936e2020-03-22 12:49:27 +01002507 /*
2508 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2509 * also use a single loop. It would seem that it is a debugging configuration.
2510 */
Angel Pons88521882020-01-05 20:21:20 +01002511 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2512 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002513
2514 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2515 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002516 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002517 if (err)
2518 return err;
2519 }
2520
Angel Pons88521882020-01-05 20:21:20 +01002521 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2522 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002523
2524 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2525 err = discover_edges_real(ctrl, channel, slotrank,
2526 rising_edges[channel][slotrank]);
2527 if (err)
2528 return err;
2529 }
2530
Angel Pons88521882020-01-05 20:21:20 +01002531 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002532
2533 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2534 ctrl->timings[channel][slotrank].lanes[lane].falling =
2535 falling_edges[channel][slotrank][lane];
2536 ctrl->timings[channel][slotrank].lanes[lane].rising =
2537 rising_edges[channel][slotrank][lane];
2538 }
2539
2540 FOR_ALL_POPULATED_CHANNELS {
2541 program_timings(ctrl, channel);
2542 }
2543
2544 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002545 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002546 }
2547 return 0;
2548}
2549
Angel Pons7c49cb82020-03-16 23:17:32 +01002550static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002551{
2552 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002553 u32 raw_stats[MAX_EDGE_TIMING + 1];
2554 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002555 const int reg3000b24[] = { 0, 0xc, 0x2c };
2556 int lane, i;
2557 int lower[NUM_LANES];
2558 int upper[NUM_LANES];
2559 int pat;
2560
2561 FOR_ALL_LANES {
2562 lower[lane] = 0;
2563 upper[lane] = MAX_EDGE_TIMING;
2564 }
2565
2566 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002567 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002568 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2569
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002570 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2571 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002572 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002573 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002574
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002575 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2576 FOR_ALL_LANES {
2577 ctrl->timings[channel][slotrank].lanes[lane].
2578 rising = edge;
2579 ctrl->timings[channel][slotrank].lanes[lane].
2580 falling = edge;
2581 }
2582 program_timings(ctrl, channel);
2583
2584 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002585 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2586 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002587 }
Angel Pons88521882020-01-05 20:21:20 +01002588 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002589
2590 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002591 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002592 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002593 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2594 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002595 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002596
2597 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002598 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002599 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002600 32, 20, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2601 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002602 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002603
2604 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002605 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002606 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002607 32, 20, MAX(ctrl->tRTP, 8), SSQ_RD,
2608 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002609 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002610
2611 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002612 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002613 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002614 1, 3, ctrl->tRP, SSQ_NA,
2615 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002616 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002617
Angel Pons7c49cb82020-03-16 23:17:32 +01002618 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002619 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002620
Angel Pons88521882020-01-05 20:21:20 +01002621 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002622 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002623 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002624 }
2625
Angel Pons7c49cb82020-03-16 23:17:32 +01002626 /* FIXME: This register only exists on Ivy Bridge */
Angel Pons098240eb2020-03-22 12:55:32 +01002627 raw_stats[edge] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002628 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002629
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002630 FOR_ALL_LANES {
2631 struct run rn;
2632 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002633 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2634
2635 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2636
2637 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2638 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2639 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002640 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002641
2642 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2643 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2644
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002645 edges[lane] = (lower[lane] + upper[lane]) / 2;
2646 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002647 printk(BIOS_EMERG, "edge write discovery failed: "
2648 "%d, %d, %d\n", channel, slotrank, lane);
2649
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002650 return MAKE_ERR;
2651 }
2652 }
2653 }
2654 }
2655
Angel Pons88521882020-01-05 20:21:20 +01002656 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002657 printram("CPA\n");
2658 return 0;
2659}
2660
2661int discover_edges_write(ramctr_timing *ctrl)
2662{
2663 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002664 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2665 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002666
Angel Pons7c49cb82020-03-16 23:17:32 +01002667 /*
2668 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2669 * also use a single loop. It would seem that it is a debugging configuration.
2670 */
Angel Pons88521882020-01-05 20:21:20 +01002671 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2672 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002673
2674 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2675 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002676 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002677 if (err)
2678 return err;
2679 }
2680
Angel Pons88521882020-01-05 20:21:20 +01002681 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2682 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002683
2684 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2685 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002686 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002687 if (err)
2688 return err;
2689 }
2690
Angel Pons88521882020-01-05 20:21:20 +01002691 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002692
2693 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2694 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002695 falling_edges[channel][slotrank][lane];
2696
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002697 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002698 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002699 }
2700
2701 FOR_ALL_POPULATED_CHANNELS
2702 program_timings(ctrl, channel);
2703
2704 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002705 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002706 }
2707 return 0;
2708}
2709
2710static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2711{
Angel Pons88521882020-01-05 20:21:20 +01002712 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002713
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002714 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002715 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002716 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002717 4, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2718 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002719 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002720
2721 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002722 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002723 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002724 480, 4, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2725 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002726 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002727
2728 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002729 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002730 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002731 480, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2732 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002733 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002734
2735 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002736 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002737 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002738 1, 4, ctrl->tRP, SSQ_NA,
2739 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002740 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002741
Angel Pons7c49cb82020-03-16 23:17:32 +01002742 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002743 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002744
Angel Pons88521882020-01-05 20:21:20 +01002745 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002746}
2747
2748int discover_timC_write(ramctr_timing *ctrl)
2749{
Angel Pons7c49cb82020-03-16 23:17:32 +01002750 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002751 int i, pat;
2752
2753 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2754 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2755 int channel, slotrank, lane;
2756
2757 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2758 lower[channel][slotrank][lane] = 0;
2759 upper[channel][slotrank][lane] = MAX_TIMC;
2760 }
2761
Angel Pons88521882020-01-05 20:21:20 +01002762 /*
2763 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2764 * FIXME: This must only be done on Ivy Bridge.
2765 */
2766 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002767 printram("discover timC write:\n");
2768
2769 for (i = 0; i < 3; i++)
2770 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002771
2772 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2773 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2774 ~0x3f000000, rege3c_b24[i] << 24);
2775
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002776 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002777
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002778 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2779 FOR_ALL_POPULATED_RANKS {
2780 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002781 u32 raw_stats[MAX_TIMC + 1];
2782 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002783
2784 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002785 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786
2787 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002788 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2789
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002790 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002791 FOR_ALL_LANES {
2792 ctrl->timings[channel][slotrank]
2793 .lanes[lane].timC = timC;
2794 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002795 program_timings(ctrl, channel);
2796
2797 test_timC_write (ctrl, channel, slotrank);
2798
Angel Pons7c49cb82020-03-16 23:17:32 +01002799 /* FIXME: Another IVB-only register! */
Angel Pons098240eb2020-03-22 12:55:32 +01002800 raw_stats[timC] = MCHBAR32(
2801 IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002802 }
2803 FOR_ALL_LANES {
2804 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002805 for (timC = 0; timC < MAX_TIMC; timC++) {
2806 stats[timC] = !!(raw_stats[timC]
2807 & (1 << lane));
2808 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002809
Angel Pons7c49cb82020-03-16 23:17:32 +01002810 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002811 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002812 printk(BIOS_EMERG,
2813 "timC write discovery failed: "
2814 "%d, %d, %d\n", channel,
2815 slotrank, lane);
2816
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002817 return MAKE_ERR;
2818 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002819 printram("timC: %d, %d, %d: "
2820 "0x%02x-0x%02x-0x%02x, "
2821 "0x%02x-0x%02x\n", channel, slotrank,
2822 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002823 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002824 rn.end - ctrl->timC_offset[i]);
2825
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002827 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002828 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002829
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002830 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002831 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002832 upper[channel][slotrank][lane]);
2833
2834 }
2835 }
2836 }
2837 }
2838
2839 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002840 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002841 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002842 udelay(2);
2843 }
2844
Angel Pons88521882020-01-05 20:21:20 +01002845 /*
2846 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2847 * FIXME: This must only be done on Ivy Bridge.
2848 */
2849 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002850
2851 printram("CPB\n");
2852
2853 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002854 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002855 (lower[channel][slotrank][lane] +
2856 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002857
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002858 ctrl->timings[channel][slotrank].lanes[lane].timC =
2859 (lower[channel][slotrank][lane] +
2860 upper[channel][slotrank][lane]) / 2;
2861 }
2862 FOR_ALL_POPULATED_CHANNELS {
2863 program_timings(ctrl, channel);
2864 }
2865 return 0;
2866}
2867
Angel Pons88521882020-01-05 20:21:20 +01002868void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002869{
2870 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002871 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002872
2873 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2874 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002875 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002876 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002877 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002878 printram("normalize %d, %d, %d: mat %d\n",
2879 channel, slotrank, lane, mat);
2880
Felix Heldef4fe3e2019-12-31 14:15:05 +01002881 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002882 printram("normalize %d, %d, %d: delta %d\n",
2883 channel, slotrank, lane, delta);
2884
Angel Pons88521882020-01-05 20:21:20 +01002885 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002886 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002887 }
2888
2889 FOR_ALL_POPULATED_CHANNELS {
2890 program_timings(ctrl, channel);
2891 }
2892}
2893
Angel Pons88521882020-01-05 20:21:20 +01002894void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002895{
2896 int channel, slotrank;
2897
2898 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002899 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002900 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002901 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002902 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002903 }
2904}
2905
2906int channel_test(ramctr_timing *ctrl)
2907{
2908 int channel, slotrank, lane;
2909
2910 slotrank = 0;
2911 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002912 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002913 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002914 return MAKE_ERR;
2915 }
2916 FOR_ALL_POPULATED_CHANNELS {
2917 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2918
Angel Pons88521882020-01-05 20:21:20 +01002919 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002920 }
2921
2922 for (slotrank = 0; slotrank < 4; slotrank++)
2923 FOR_ALL_CHANNELS
2924 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2925 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002926 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2927 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002928 }
Angel Pons88521882020-01-05 20:21:20 +01002929 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002930
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002931 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002932 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002933 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002934 4, 40, 40, SSQ_NA,
2935 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002936 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002937
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002938 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002939 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002940 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002941 100, 4, 40, SSQ_WR,
2942 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002943 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002944
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002945 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002946 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002947 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002948 100, 4, 40, SSQ_RD,
2949 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002950 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002951
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002952 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002953 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002954 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002955 1, 3, 40, SSQ_NA,
2956 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002957 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002958
Angel Pons7c49cb82020-03-16 23:17:32 +01002959 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002960 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002961
Angel Pons88521882020-01-05 20:21:20 +01002962 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002963 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002964 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002965 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2966 channel, slotrank, lane);
2967 return MAKE_ERR;
2968 }
2969 }
2970 return 0;
2971}
2972
Patrick Rudolphdd662872017-10-28 18:20:11 +02002973void channel_scrub(ramctr_timing *ctrl)
2974{
2975 int channel, slotrank, row, rowsize;
2976
2977 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
2978 rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
2979 for (row = 0; row < rowsize; row += 16) {
2980
2981 wait_for_iosav(channel);
2982
2983 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002984 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002985 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002986 1, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2987 row, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002988 1, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02002989
2990 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002991 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002992 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002993 129, 4, 40, SSQ_WR,
2994 row, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002995 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02002996
2997 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002998 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002999 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003000 1, 3, 40, SSQ_NA,
3001 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003002 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003003
3004 /* execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02003005 iosav_run_once(channel, 3);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003006
3007 wait_for_iosav(channel);
3008 }
3009 }
3010}
3011
Angel Pons88521882020-01-05 20:21:20 +01003012void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003013{
3014 int channel;
3015
Angel Pons7c49cb82020-03-16 23:17:32 +01003016 /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? I don't think so. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003017 static u32 seeds[NUM_CHANNELS][3] = {
3018 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3019 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3020 };
3021 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003022 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01003023 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3024 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
3025 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003026 }
3027}
3028
Angel Pons89ae6b82020-03-21 13:23:32 +01003029void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003030{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003031 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003032 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003033 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003034 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003035 }
3036}
3037
Angel Pons88521882020-01-05 20:21:20 +01003038void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003039{
3040 int channel;
3041
3042 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003043 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003044 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003045 }
3046
3047 udelay(1);
3048
3049 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003050 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003051 }
3052}
3053
Angel Pons7c49cb82020-03-16 23:17:32 +01003054void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003055{
3056 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003057
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003058 FOR_ALL_POPULATED_CHANNELS {
3059 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003060 int min_pi = 10000;
3061 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003062
3063 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003064 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3065 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003066 }
3067
Angel Pons7c49cb82020-03-16 23:17:32 +01003068 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003069
Angel Pons7c49cb82020-03-16 23:17:32 +01003070 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003071
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003072 dram_odt_stretch(ctrl, channel);
3073
Angel Pons7c49cb82020-03-16 23:17:32 +01003074 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003075 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003076 }
3077}
3078
Angel Pons88521882020-01-05 20:21:20 +01003079void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003080{
3081 int channel;
3082 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003083 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3084 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003085 }
3086}
3087
Angel Pons7c49cb82020-03-16 23:17:32 +01003088/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3089static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003090{
Angel Pons88521882020-01-05 20:21:20 +01003091 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003092}
3093
Angel Pons7c49cb82020-03-16 23:17:32 +01003094/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003095void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003096{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003097 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3098
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003099 int channel;
3100 int t1_cycles = 0, t1_ns = 0, t2_ns;
3101 int t3_ns;
3102 u32 r32;
3103
Angel Pons7c49cb82020-03-16 23:17:32 +01003104 /* FIXME: This register only exists on Ivy Bridge */
3105 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003106
Felix Heldf9b826a2018-07-30 17:56:52 +02003107 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003108 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003109
Patrick Rudolph74203de2017-11-20 11:57:01 +01003110 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003111 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003112 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003113 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003114 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003115 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003116
Felix Heldf9b826a2018-07-30 17:56:52 +02003117 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003118 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003119
Angel Pons88521882020-01-05 20:21:20 +01003120 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3121 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003122
3123 FOR_ALL_CHANNELS {
3124 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003125 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003126 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003127 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003128 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003129 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003130 case 1:
3131 case 4:
3132 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003133 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003134 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003135 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003136 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003137 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003138 break;
3139 }
3140 }
3141
Felix Held50b7ed22019-12-30 20:41:54 +01003142 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003143 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003144 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003145
3146 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003147 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003148
Angel Pons88521882020-01-05 20:21:20 +01003149 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3150 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3151 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003152
Angel Pons7c49cb82020-03-16 23:17:32 +01003153 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003154 FOR_ALL_POPULATED_CHANNELS
3155 break;
3156
Angel Pons88521882020-01-05 20:21:20 +01003157 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3158 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003159 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003160 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003161 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003162 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003163 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003164 t1_ns += 500;
3165
Angel Pons88521882020-01-05 20:21:20 +01003166 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003167 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003168 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003169 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003170 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003171 t3_ns = 500;
3172 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003173
3174 /* The graphics driver will use these watermark values */
3175 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3176 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3177 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3178 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003179}
3180
Angel Pons88521882020-01-05 20:21:20 +01003181void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003182{
3183 int channel, slotrank, lane;
3184
Angel Pons7c49cb82020-03-16 23:17:32 +01003185 FOR_ALL_POPULATED_CHANNELS {
3186 MCHBAR32(TC_RAP_ch(channel)) =
3187 (ctrl->tRRD << 0)
3188 | (ctrl->tRTP << 4)
3189 | (ctrl->tCKE << 8)
3190 | (ctrl->tWTR << 12)
3191 | (ctrl->tFAW << 16)
3192 | (ctrl->tWR << 24)
3193 | (ctrl->cmd_stretch[channel] << 30);
3194 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003195
3196 udelay(1);
3197
3198 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003199 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003200 }
3201
3202 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003203 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003204 }
3205
3206 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003207 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208
3209 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003210 udelay(1);
3211 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003212 }
3213
3214 printram("CPE\n");
3215
Angel Pons88521882020-01-05 20:21:20 +01003216 MCHBAR32(GDCRTRAININGMOD) = 0;
3217 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003218
3219 printram("CP5b\n");
3220
3221 FOR_ALL_POPULATED_CHANNELS {
3222 program_timings(ctrl, channel);
3223 }
3224
3225 u32 reg, addr;
3226
Angel Pons7c49cb82020-03-16 23:17:32 +01003227 /* Poll for RCOMP */
3228 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3229 ;
3230
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003231 do {
Angel Pons88521882020-01-05 20:21:20 +01003232 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003233 } while ((reg & 0x14) == 0);
3234
Angel Pons7c49cb82020-03-16 23:17:32 +01003235 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003236 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003237 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003238
Angel Pons7c49cb82020-03-16 23:17:32 +01003239 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003240 udelay(500);
3241
3242 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003243 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003244 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003245 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003246 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003247 MCHBAR32(addr) = reg;
3248
Angel Pons7c49cb82020-03-16 23:17:32 +01003249 /* Wait 10ns for ranks to settle */
3250 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003251
3252 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3253 MCHBAR32(addr) = reg;
3254
Angel Pons7c49cb82020-03-16 23:17:32 +01003255 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003256 write_reset(ctrl);
3257 }
3258
Angel Pons7c49cb82020-03-16 23:17:32 +01003259 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003260 dram_mrscommands(ctrl);
3261
3262 printram("CP5c\n");
3263
Angel Pons88521882020-01-05 20:21:20 +01003264 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003265
3266 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003267 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003268 udelay(2);
3269 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003270}