blob: 43e9e91f1275bb360d64f9bfa006e7f09e9f3111 [file] [log] [blame]
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014 Damien Zammit <damien@zamaudio.com>
5 * Copyright (C) 2014 Vladimir Serbinenko <phcoder@gmail.com>
6 * Copyright (C) 2016 Patrick Rudolph <siro@das-labor.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
Elyes HAOUASf97c1c92019-12-03 18:22:06 +010018#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010019#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010020#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +053021#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +020022#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020023#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010024#include <northbridge/intel/sandybridge/chip.h>
25#include <device/pci_def.h>
26#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020027
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010028#include "raminit_native.h"
29#include "raminit_common.h"
30#include "sandybridge.h"
31
32/* FIXME: no ECC support. */
33/* FIXME: no support for 3-channel chipsets. */
34
35/*
Angel Pons88521882020-01-05 20:21:20 +010036 * ### IOSAV command queue notes ###
37 *
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010038 * Intel provides a command queue of depth four.
Angel Pons88521882020-01-05 20:21:20 +010039 * Every command is configured by using multiple MCHBAR registers.
40 * On executing the command queue, you have to specify its depth (number of commands).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010041 *
Angel Pons88521882020-01-05 20:21:20 +010042 * The macros for these registers can take some integer parameters, within these bounds:
43 * channel: [0..1]
44 * index: [0..3]
45 * lane: [0..8]
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010046 *
Angel Pons88521882020-01-05 20:21:20 +010047 * Note that these ranges are 'closed': both endpoints are included.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010048 *
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010049 *
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010050 *
Angel Pons88521882020-01-05 20:21:20 +010051 * ### Register description ###
52 *
53 * IOSAV_n_SP_CMD_ADDR_ch(channel, index)
54 * Sub-sequence command addresses. Controls the address, bank address and slotrank signals.
55 *
56 * Bitfields:
57 * [0..15] Row / Column Address.
58 * [16..18] The result of (10 + [16..18]) is the number of valid row bits.
59 * Note: Value 1 is not implemented. Not that it really matters, though.
60 * Value 7 is reserved, as the hardware does not support it.
61 * [20..22] Bank Address.
62 * [24..25] Rank select. Let's call it "ranksel", as it is mentioned later.
63 *
64 * IOSAV_n_ADDR_UPD_ch(channel, index)
65 * How the address shall be updated after executing the sub-sequence command.
66 *
67 * Bitfields:
68 * [0] Increment CAS/RAS by 1.
69 * [1] Increment CAS/RAS by 8.
70 * [2] Increment bank select by 1.
71 * [3..4] Increment rank select by 1, 2 or 3.
72 * [5..9] Known as "addr_wrap". Address bits will wrap around the [addr_wrap..0] range.
73 * [10..11] LFSR update:
74 * 00: Do not use the LFSR function.
75 * 01: Undefined, treat as Reserved.
76 * 10: Apply LFSR on the [addr_wrap..0] bit range.
77 * 11: Apply LFSR on the [addr_wrap..3] bit range.
78 *
79 * [12..15] Update rate. The number of command runs between address updates. For example:
80 * 0: Update every command run.
81 * 1: Update every second command run. That is, half of the command rate.
82 * N: Update after N command runs without updates.
83 *
84 * [16..17] LFSR behavior on the deselect cycles (when no sub-seq command is issued):
85 * 0: No change w.r.t. the last issued command.
86 * 1: LFSR XORs with address & command (excluding CS), but does not update.
87 * 2: LFSR XORs with address & command (excluding CS), and updates.
88 *
89 * IOSAV_n_SP_CMD_CTL_ch(channel, index)
90 * Special command control register. Controls the DRAM command signals.
91 *
92 * Bitfields:
93 * [0] !RAS signal.
94 * [1] !CAS signal.
95 * [2] !WE signal.
96 * [4..7] CKE, per rank and channel.
97 * [8..11] ODT, per rank and channel.
98 * [12] Chip Select mode control.
99 * [13..16] Chip select, per rank and channel. It works as follows:
100 *
101 * entity CS_BLOCK is
102 * port (
103 * MODE : in std_logic; -- Mode select at [12]
104 * RANKSEL : in std_logic_vector(0 to 3); -- Decoded "ranksel" value
105 * CS_CTL : in std_logic_vector(0 to 3); -- Chip select control at [13..16]
106 * CS_Q : out std_logic_vector(0 to 3) -- CS signals
107 * );
108 * end entity CS_BLOCK;
109 *
110 * architecture RTL of CS_BLOCK is
111 * begin
112 * if MODE = '1' then
113 * CS_Q <= not RANKSEL and CS_CTL;
114 * else
115 * CS_Q <= CS_CTL;
116 * end if;
117 * end architecture RTL;
118 *
119 * [17] Auto Precharge. Only valid when using 10 row bits!
120 *
121 * IOSAV_n_SUBSEQ_CTL_ch(channel, index)
122 * Sub-sequence parameters. Controls repetititons, delays and data orientation.
123 *
124 * Bitfields:
125 * [0..8] Number of repetitions of the sub-sequence command.
126 * [10..14] Gap, number of clock-cycles to wait before sending the next command.
127 * [16..24] Number of clock-cycles to idle between sub-sequence commands.
128 * [26..27] The direction of the data.
129 * 00: None, does not handle data
130 * 01: Read
131 * 10: Write
132 * 11: Read & Write
133 *
134 * IOSAV_n_ADDRESS_LFSR_ch(channel, index)
135 * 23-bit LFSR state register. It is written into the LFSR when the sub-sequence is loaded,
136 * and then read back from the LFSR when the sub-sequence is done.
137 *
138 * Bitfields:
139 * [0..22] LFSR state.
140 *
141 * IOSAV_SEQ_CTL_ch(channel)
142 * Control the sequence level in IOSAV: number of sub-sequences, iterations, maintenance...
143 *
144 * Bitfields:
145 * [0..7] Number of full sequence executions. When this field becomes non-zero, then the
146 * sequence starts running immediately. This value is decremented after completing
147 * a full sequence iteration. When it is zero, the sequence is done. No decrement
148 * is done if this field is set to 0xff. This is the "infinite repeat" mode, and
149 * it is manually aborted by clearing this field.
150 *
151 * [8..16] Number of wait cycles after each sequence iteration. This wait's purpose is to
152 * allow performing maintenance in infinite loops. When non-zero, RCOMP, refresh
153 * and ZQXS operations can take place.
154 *
155 * [17] Stop-on-error mode: Whether to stop sequence execution when an error occurs.
156 * [18..19] Number of sub-sequences. The programmed value is the index of the last sub-seq.
157 * [20] If set, keep refresh disabled until the next sequence execution.
158 * DANGER: Refresh must be re-enabled within the (9 * tREFI) period!
159 *
160 * [22] If set, sequence execution will not prevent refresh. This cannot be set when
161 * bit [20] is also set, or was set on the previous sequence. This bit exists so
162 * that the sequence machine can be used as a timer without affecting the memory.
163 *
164 * [23] If set, a output pin is asserted on the first detected error. This output can
165 * be used as a trigger for an oscilloscope or a logic analyzer, which is handy.
166 *
167 * IOSAV_DATA_CTL_ch(channel)
168 * Data-related controls in IOSAV mode.
169 *
170 * Bitfields:
171 * [0..7] WDB (Write Data Buffer) pattern length: [0..7] = (length / 8) - 1;
172 * [8..15] WDB read pointer. Points at the data used for IOSAV write transactions.
173 * [16..23] Comparison pointer. Used to compare data from IOSAV read transactions.
174 * [24] If set, increment pointers only when micro-breakpoint is active.
175 *
176 * IOSAV_STATUS_ch(channel)
177 * State of the IOSAV sequence machine. Should be polled after sending an IOSAV sequence.
178 *
179 * Bitfields:
180 * [0] IDLE: IOSAV is sleeping.
181 * [1] BUSY: IOSAV is running a sequence.
182 * [2] DONE: IOSAV has completed a sequence.
183 * [3] ERROR: IOSAV detected an error and stopped on it, when using Stop-on-error.
184 * [4] PANIC: The refresh machine issued a Panic Refresh, and IOSAV was aborted.
185 * [5] RCOMP: RComp failure. Unused, consider Reserved.
186 * [6] Cleared with a new sequence, and set when done and refresh counter is drained.
187 *
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100188 */
189
Angel Pons88521882020-01-05 20:21:20 +0100190/* length: [1..4] */
191#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1)
Felix Held9cf1dd22018-07-31 14:52:40 +0200192
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100193static void sfence(void)
194{
195 asm volatile ("sfence");
196}
197
198static void toggle_io_reset(void) {
199 /* toggle IO reset bit */
Angel Pons88521882020-01-05 20:21:20 +0100200 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
201 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100202 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +0100203 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100204 udelay(1);
205}
206
207static u32 get_XOVER_CLK(u8 rankmap)
208{
209 return rankmap << 24;
210}
211
212static u32 get_XOVER_CMD(u8 rankmap)
213{
214 u32 reg;
215
216 // enable xover cmd
217 reg = 0x4000;
218
219 // enable xover ctl
220 if (rankmap & 0x3)
221 reg |= 0x20000;
222
223 if (rankmap & 0xc)
224 reg |= 0x4000000;
225
226 return reg;
227}
228
229/* CAS write latency. To be programmed in MR2.
230 * See DDR3 SPEC for MR2 documentation. */
231u8 get_CWL(u32 tCK)
232{
233 /* Get CWL based on tCK using the following rule: */
234 switch (tCK) {
235 case TCK_1333MHZ:
236 return 12;
237 case TCK_1200MHZ:
238 case TCK_1100MHZ:
239 return 11;
240 case TCK_1066MHZ:
241 case TCK_1000MHZ:
242 return 10;
243 case TCK_933MHZ:
244 case TCK_900MHZ:
245 return 9;
246 case TCK_800MHZ:
247 case TCK_700MHZ:
248 return 8;
249 case TCK_666MHZ:
250 return 7;
251 case TCK_533MHZ:
252 return 6;
253 default:
254 return 5;
255 }
256}
257
258void dram_find_common_params(ramctr_timing *ctrl)
259{
260 size_t valid_dimms;
261 int channel, slot;
262 dimm_info *dimms = &ctrl->info;
263
264 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
265 valid_dimms = 0;
266 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
267 const dimm_attr *dimm = &dimms->dimm[channel][slot];
268 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
269 continue;
270 valid_dimms++;
271
272 /* Find all possible CAS combinations */
273 ctrl->cas_supported &= dimm->cas_supported;
274
275 /* Find the smallest common latencies supported by all DIMMs */
276 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
277 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
278 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
279 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
280 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
281 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
282 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
283 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
284 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
285 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
286 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300287 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
288 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100289 }
290
291 if (!ctrl->cas_supported)
292 die("Unsupported DIMM combination. "
293 "DIMMS do not support common CAS latency");
294 if (!valid_dimms)
295 die("No valid DIMMs found");
296}
297
Angel Pons88521882020-01-05 20:21:20 +0100298void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100299{
300 u32 reg;
301 int channel;
302
303 FOR_ALL_CHANNELS {
304 // enable xover clk
305 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100306 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
307 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100308
309 // enable xover ctl & xover cmd
310 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100311 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
312 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100313 }
314}
315
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100316static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100317{
Iru Cai89af71c2018-08-16 16:46:27 +0800318 u32 addr, cpu, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100319
320 stretch = ctrl->ref_card_offset[channel];
321 /* ODT stretch: Delay ODT signal by stretch value.
322 * Useful for multi DIMM setups on the same channel. */
Subrata Banik53b08c32018-12-10 14:11:35 +0530323 cpu = cpu_get_cpuid();
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100324 if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) {
325 if (stretch == 2)
326 stretch = 3;
Angel Pons88521882020-01-05 20:21:20 +0100327 addr = SCHED_SECOND_CBIT_ch(channel);
Iru Cai89af71c2018-08-16 16:46:27 +0800328 MCHBAR32_AND_OR(addr, 0xffffc3ff,
Felix Held9fe248f2018-07-31 20:59:45 +0200329 (stretch << 12) | (stretch << 10));
Iru Cai89af71c2018-08-16 16:46:27 +0800330 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr,
331 MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100332 } else {
333 // OTHP
Angel Pons88521882020-01-05 20:21:20 +0100334 addr = TC_OTHP_ch(channel);
Iru Cai89af71c2018-08-16 16:46:27 +0800335 MCHBAR32_AND_OR(addr, 0xfff0ffff,
Felix Held9fe248f2018-07-31 20:59:45 +0200336 (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800337 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100338 }
339}
340
341void dram_timing_regs(ramctr_timing *ctrl)
342{
343 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100344 int channel;
345
346 FOR_ALL_CHANNELS {
347 // DBP
348 reg = 0;
349 reg |= ctrl->tRCD;
350 reg |= (ctrl->tRP << 4);
351 reg |= (ctrl->CAS << 8);
352 reg |= (ctrl->CWL << 12);
353 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100354 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
355 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100356
357 // RAP
358 reg = 0;
359 reg |= ctrl->tRRD;
360 reg |= (ctrl->tRTP << 4);
361 reg |= (ctrl->tCKE << 8);
362 reg |= (ctrl->tWTR << 12);
363 reg |= (ctrl->tFAW << 16);
364 reg |= (ctrl->tWR << 24);
365 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100366 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
367 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100368
369 // OTHP
Angel Pons88521882020-01-05 20:21:20 +0100370 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100371 reg = 0;
372 reg |= ctrl->tXPDLL;
373 reg |= (ctrl->tXP << 5);
374 reg |= (ctrl->tAONPD << 8);
375 reg |= 0xa0000;
376 printram("OTHP [%x] = %x\n", addr, reg);
377 MCHBAR32(addr) = reg;
378
Angel Pons1aba2a32020-01-05 22:31:41 +0100379 MCHBAR32(0x4014 + channel * 0x400) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100380
Felix Held9fe248f2018-07-31 20:59:45 +0200381 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100382
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100383 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100384
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100385 /*
Patrick Rudolphb009ac42018-07-25 15:27:50 +0200386 * TC-Refresh timing parameters
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100387 * The tREFIx9 field should be programmed to minimum of
388 * 8.9*tREFI (to allow for possible delays from ZQ or
389 * isoc) and tRASmax (70us) divided by 1024.
390 */
391 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
392
393 reg = ((ctrl->tREFI & 0xffff) << 0) |
394 ((ctrl->tRFC & 0x1ff) << 16) |
395 (((val32 / 1024) & 0x7f) << 25);
Angel Pons88521882020-01-05 20:21:20 +0100396 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
397 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100398
Angel Pons88521882020-01-05 20:21:20 +0100399 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100400
401 // SRFTP
402 reg = 0;
403 val32 = tDLLK;
404 reg = (reg & ~0xfff) | val32;
405 val32 = ctrl->tXSOffset;
406 reg = (reg & ~0xf000) | (val32 << 12);
407 val32 = tDLLK - ctrl->tXSOffset;
408 reg = (reg & ~0x3ff0000) | (val32 << 16);
409 val32 = ctrl->tMOD - 8;
410 reg = (reg & ~0xf0000000) | (val32 << 28);
Angel Pons88521882020-01-05 20:21:20 +0100411 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel),
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100412 reg);
Angel Pons88521882020-01-05 20:21:20 +0100413 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100414 }
415}
416
417void dram_dimm_mapping(ramctr_timing *ctrl)
418{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100419 int channel;
420 dimm_info *info = &ctrl->info;
421
422 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200423 dimm_attr *dimmA, *dimmB;
424 u32 reg = 0;
425
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100426 if (info->dimm[channel][0].size_mb >=
427 info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100428 dimmA = &info->dimm[channel][0];
429 dimmB = &info->dimm[channel][1];
Nico Huberac4f2162017-10-01 18:14:43 +0200430 reg |= 0 << 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100431 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100432 dimmA = &info->dimm[channel][1];
433 dimmB = &info->dimm[channel][0];
Nico Huberac4f2162017-10-01 18:14:43 +0200434 reg |= 1 << 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100435 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100436
Nico Huberac4f2162017-10-01 18:14:43 +0200437 if (dimmA && (dimmA->ranks > 0)) {
438 reg |= dimmA->size_mb / 256;
439 reg |= (dimmA->ranks - 1) << 17;
440 reg |= (dimmA->width / 8 - 1) << 19;
441 }
442
443 if (dimmB && (dimmB->ranks > 0)) {
444 reg |= (dimmB->size_mb / 256) << 8;
445 reg |= (dimmB->ranks - 1) << 18;
446 reg |= (dimmB->width / 8 - 1) << 20;
447 }
448
449 reg |= 1 << 21; /* rank interleave */
450 reg |= 1 << 22; /* enhanced interleave */
451
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100452 if ((dimmA && (dimmA->ranks > 0))
453 || (dimmB && (dimmB->ranks > 0))) {
454 ctrl->mad_dimm[channel] = reg;
455 } else {
456 ctrl->mad_dimm[channel] = 0;
457 }
458 }
459}
460
Angel Pons88521882020-01-05 20:21:20 +0100461void dram_dimm_set_mapping(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100462{
463 int channel;
464 FOR_ALL_CHANNELS {
Felix Helddee167e2019-12-30 17:30:16 +0100465 MCHBAR32(MAD_DIMM_CH0 + channel * 4) = ctrl->mad_dimm[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100466 }
467}
468
Angel Pons88521882020-01-05 20:21:20 +0100469void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100470{
471 u32 reg, ch0size, ch1size;
472 u8 val;
473 reg = 0;
474 val = 0;
475 if (training) {
476 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
477 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
478 } else {
479 ch0size = ctrl->channel_size_mb[0];
480 ch1size = ctrl->channel_size_mb[1];
481 }
482
483 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100484 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100485 val = ch1size / 256;
486 reg = (reg & ~0xff000000) | val << 24;
487 reg = (reg & ~0xff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100488 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100489 MCHBAR32(MAD_CHNL) = 0x24;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100490 } else {
Angel Pons88521882020-01-05 20:21:20 +0100491 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100492 val = ch0size / 256;
493 reg = (reg & ~0xff000000) | val << 24;
494 reg = (reg & ~0xff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100495 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100496 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100497 }
498}
499
500#define HOST_BRIDGE PCI_DEVFN(0, 0)
501#define DEFAULT_TCK TCK_800MHZ
502
503unsigned int get_mem_min_tck(void)
504{
505 u32 reg32;
506 u8 rev;
507 const struct device *dev;
508 const struct northbridge_intel_sandybridge_config *cfg = NULL;
509
Kyösti Mälkkie7377552018-06-21 16:20:55 +0300510 dev = pcidev_path_on_root(HOST_BRIDGE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100511 if (dev)
512 cfg = dev->chip_info;
513
514 /* If this is zero, it just means devicetree.cb didn't set it */
515 if (!cfg || cfg->max_mem_clock_mhz == 0) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800516 if (CONFIG(NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES))
Patrick Rudolphb794a692017-08-08 13:13:51 +0200517 return TCK_1333MHZ;
518
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100519 rev = pci_read_config8(PCI_DEV(0, 0, 0), PCI_DEVICE_ID);
520
521 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
522 /* read Capabilities A Register DMFC bits */
523 reg32 = pci_read_config32(PCI_DEV(0, 0, 0), CAPID0_A);
524 reg32 &= 0x7;
525
526 switch (reg32) {
527 case 7: return TCK_533MHZ;
528 case 6: return TCK_666MHZ;
529 case 5: return TCK_800MHZ;
530 /* reserved: */
531 default:
532 break;
533 }
534 } else {
535 /* read Capabilities B Register DMFC bits */
536 reg32 = pci_read_config32(PCI_DEV(0, 0, 0), CAPID0_B);
537 reg32 = (reg32 >> 4) & 0x7;
538
539 switch (reg32) {
540 case 7: return TCK_533MHZ;
541 case 6: return TCK_666MHZ;
542 case 5: return TCK_800MHZ;
543 case 4: return TCK_933MHZ;
544 case 3: return TCK_1066MHZ;
545 case 2: return TCK_1200MHZ;
546 case 1: return TCK_1333MHZ;
547 /* reserved: */
548 default:
549 break;
550 }
551 }
552 return DEFAULT_TCK;
553 } else {
554 if (cfg->max_mem_clock_mhz >= 1066)
555 return TCK_1066MHZ;
556 else if (cfg->max_mem_clock_mhz >= 933)
557 return TCK_933MHZ;
558 else if (cfg->max_mem_clock_mhz >= 800)
559 return TCK_800MHZ;
560 else if (cfg->max_mem_clock_mhz >= 666)
561 return TCK_666MHZ;
562 else if (cfg->max_mem_clock_mhz >= 533)
563 return TCK_533MHZ;
564 else
565 return TCK_400MHZ;
566 }
567}
568
569#define DEFAULT_PCI_MMIO_SIZE 2048
570
571static unsigned int get_mmio_size(void)
572{
573 const struct device *dev;
574 const struct northbridge_intel_sandybridge_config *cfg = NULL;
575
Kyösti Mälkkie7377552018-06-21 16:20:55 +0300576 dev = pcidev_path_on_root(HOST_BRIDGE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100577 if (dev)
578 cfg = dev->chip_info;
579
580 /* If this is zero, it just means devicetree.cb didn't set it */
581 if (!cfg || cfg->pci_mmio_size == 0)
582 return DEFAULT_PCI_MMIO_SIZE;
583 else
584 return cfg->pci_mmio_size;
585}
586
Angel Pons88521882020-01-05 20:21:20 +0100587void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100588{
589 u32 reg, val, reclaim;
590 u32 tom, gfxstolen, gttsize;
591 size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase,
592 tsegbase, mestolenbase;
593 size_t tsegbasedelta, remapbase, remaplimit;
594 uint16_t ggc;
595
596 mmiosize = get_mmio_size();
597
598 ggc = pci_read_config16(NORTHBRIDGE, GGC);
599 if (!(ggc & 2)) {
600 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
601 gttsize = ((ggc >> 8) & 0x3);
602 } else {
603 gfxstolen = 0;
604 gttsize = 0;
605 }
606
607 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
608
609 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
610
611 mestolenbase = tom - me_uma_size;
612
613 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize,
614 tom - me_uma_size);
615 gfxstolenbase = toludbase - gfxstolen;
616 gttbase = gfxstolenbase - gttsize;
617
618 tsegbase = gttbase - tsegsize;
619
620 // Round tsegbase down to nearest address aligned to tsegsize
621 tsegbasedelta = tsegbase & (tsegsize - 1);
622 tsegbase &= ~(tsegsize - 1);
623
624 gttbase -= tsegbasedelta;
625 gfxstolenbase -= tsegbasedelta;
626 toludbase -= tsegbasedelta;
627
628 // Test if it is possible to reclaim a hole in the RAM addressing
629 if (tom - me_uma_size > toludbase) {
630 // Reclaim is possible
631 reclaim = 1;
632 remapbase = MAX(4096, tom - me_uma_size);
633 remaplimit =
634 remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
635 touudbase = remaplimit + 1;
636 } else {
637 // Reclaim not possible
638 reclaim = 0;
639 touudbase = tom - me_uma_size;
640 }
641
642 // Update memory map in pci-e configuration space
643 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
644
645 // TOM (top of memory)
Felix Held4902fee2019-12-28 18:09:47 +0100646 reg = pci_read_config32(PCI_DEV(0, 0, 0), TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100647 val = tom & 0xfff;
648 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100649 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
650 pci_write_config32(PCI_DEV(0, 0, 0), TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100651
Felix Held4902fee2019-12-28 18:09:47 +0100652 reg = pci_read_config32(PCI_DEV(0, 0, 0), TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100653 val = tom & 0xfffff000;
654 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100655 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
656 pci_write_config32(PCI_DEV(0, 0, 0), TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657
658 // TOLUD (top of low used dram)
Felix Held4902fee2019-12-28 18:09:47 +0100659 reg = pci_read_config32(PCI_DEV(0, 0, 0), TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100660 val = toludbase & 0xfff;
661 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100662 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
663 pci_write_config32(PCI_DEV(0, 0, 0), TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100664
665 // TOUUD LSB (top of upper usable dram)
Felix Held4902fee2019-12-28 18:09:47 +0100666 reg = pci_read_config32(PCI_DEV(0, 0, 0), TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100667 val = touudbase & 0xfff;
668 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100669 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
670 pci_write_config32(PCI_DEV(0, 0, 0), TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100671
672 // TOUUD MSB
Felix Held4902fee2019-12-28 18:09:47 +0100673 reg = pci_read_config32(PCI_DEV(0, 0, 0), TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100674 val = touudbase & 0xfffff000;
675 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100676 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
677 pci_write_config32(PCI_DEV(0, 0, 0), TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100678
679 if (reclaim) {
680 // REMAP BASE
Felix Held4902fee2019-12-28 18:09:47 +0100681 pci_write_config32(PCI_DEV(0, 0, 0), REMAPBASE, remapbase << 20);
682 pci_write_config32(PCI_DEV(0, 0, 0), REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100683
684 // REMAP LIMIT
Felix Held4902fee2019-12-28 18:09:47 +0100685 pci_write_config32(PCI_DEV(0, 0, 0), REMAPLIMIT, remaplimit << 20);
686 pci_write_config32(PCI_DEV(0, 0, 0), REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100687 }
688 // TSEG
Felix Held4902fee2019-12-28 18:09:47 +0100689 reg = pci_read_config32(PCI_DEV(0, 0, 0), TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100690 val = tsegbase & 0xfff;
691 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100692 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
693 pci_write_config32(PCI_DEV(0, 0, 0), TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100694
695 // GFX stolen memory
Felix Held4902fee2019-12-28 18:09:47 +0100696 reg = pci_read_config32(PCI_DEV(0, 0, 0), BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100697 val = gfxstolenbase & 0xfff;
698 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100699 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
700 pci_write_config32(PCI_DEV(0, 0, 0), BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100701
702 // GTT stolen memory
Felix Held4902fee2019-12-28 18:09:47 +0100703 reg = pci_read_config32(PCI_DEV(0, 0, 0), BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100704 val = gttbase & 0xfff;
705 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100706 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
707 pci_write_config32(PCI_DEV(0, 0, 0), BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100708
709 if (me_uma_size) {
Felix Held651f99f2019-12-30 16:28:48 +0100710 reg = pci_read_config32(PCI_DEV(0, 0, 0), MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100711 val = (0x80000 - me_uma_size) & 0xfffff000;
712 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100713 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
714 pci_write_config32(PCI_DEV(0, 0, 0), MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100715
716 // ME base
Felix Held651f99f2019-12-30 16:28:48 +0100717 reg = pci_read_config32(PCI_DEV(0, 0, 0), MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100718 val = mestolenbase & 0xfff;
719 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100720 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
721 pci_write_config32(PCI_DEV(0, 0, 0), MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100722
Felix Held651f99f2019-12-30 16:28:48 +0100723 reg = pci_read_config32(PCI_DEV(0, 0, 0), MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100724 val = mestolenbase & 0xfffff000;
725 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100726 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
727 pci_write_config32(PCI_DEV(0, 0, 0), MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100728
729 // ME mask
Felix Held651f99f2019-12-30 16:28:48 +0100730 reg = pci_read_config32(PCI_DEV(0, 0, 0), MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100731 val = (0x80000 - me_uma_size) & 0xfff;
732 reg = (reg & ~0xfff00000) | (val << 20);
Felix Heldf54ae382019-12-30 18:18:02 +0100733 reg = reg | ME_STLEN_EN; // set ME memory enable
734 reg = reg | MELCK; // set lockbit on ME mem
Felix Held651f99f2019-12-30 16:28:48 +0100735 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
736 pci_write_config32(PCI_DEV(0, 0, 0), MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100737 }
738}
739
Angel Pons88521882020-01-05 20:21:20 +0100740static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100741{
742 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100743 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100744 return;
745 }
746}
747
Angel Pons88521882020-01-05 20:21:20 +0100748static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100749{
750 int channel, slotrank;
751
752 /* choose a populated channel. */
753 channel = (ctrl->rankmap[0]) ? 0 : 1;
754
Angel Pons88521882020-01-05 20:21:20 +0100755 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100756
757 /* choose a populated rank. */
758 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
759
760 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +0100761 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
762 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x80c01;
763 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
764 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100765
Felix Held9cf1dd22018-07-31 14:52:40 +0200766 // execute command queue - why is bit 22 set here?!
Angel Pons88521882020-01-05 20:21:20 +0100767 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200768
Angel Pons88521882020-01-05 20:21:20 +0100769 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100770}
771
Angel Pons88521882020-01-05 20:21:20 +0100772void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100773{
Felix Held9fe248f2018-07-31 20:59:45 +0200774 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100775 int channel;
776
Angel Pons88521882020-01-05 20:21:20 +0100777 while (!(MCHBAR32(RCOMP_TIMER) & 0x10000));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100778 do {
Angel Pons88521882020-01-05 20:21:20 +0100779 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100780 } while ((reg & 0x14) == 0);
781
782 // Set state of memory controller
783 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100784 MCHBAR32(MC_INIT_STATE_G) = reg;
785 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100786 reg |= 2; //ddr reset
Angel Pons88521882020-01-05 20:21:20 +0100787 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100788
789 // Assert dimm reset signal
Angel Pons88521882020-01-05 20:21:20 +0100790 MCHBAR32_AND(MC_INIT_STATE_G, ~0x2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100791
792 // Wait 200us
793 udelay(200);
794
795 // Deassert dimm reset signal
Angel Pons88521882020-01-05 20:21:20 +0100796 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100797
798 // Wait 500us
799 udelay(500);
800
801 // Enable DCLK
Angel Pons88521882020-01-05 20:21:20 +0100802 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100803
804 // XXX Wait 20ns
805 udelay(1);
806
807 FOR_ALL_CHANNELS {
808 // Set valid rank CKE
Felix Held9fe248f2018-07-31 20:59:45 +0200809 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100810 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100811
812 // Wait 10ns for ranks to settle
813 //udelay(0.01);
814
815 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100816 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100817
818 // Write reset using a NOP
819 write_reset(ctrl);
820 }
821}
822
823static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
824{
825 /* Get ODT based on rankmap: */
826 int dimms_per_ch = (ctrl->rankmap[channel] & 1)
827 + ((ctrl->rankmap[channel] >> 2) & 1);
828
829 if (dimms_per_ch == 1) {
830 return (const odtmap){60, 60};
831 } else {
832 return (const odtmap){120, 30};
833 }
834}
835
836static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank,
837 int reg, u32 val)
838{
Angel Pons88521882020-01-05 20:21:20 +0100839 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100840
841 if (ctrl->rank_mirror[channel][slotrank]) {
842 /* DDR3 Rank1 Address mirror
843 * swap the following pins:
844 * A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
845 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
846 val = (val & ~0x1f8) | ((val >> 1) & 0xa8)
847 | ((val & 0xa8) << 1);
848 }
849
850 /* DRAM command MRS */
Angel Pons88521882020-01-05 20:21:20 +0100851 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f000;
852 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
853 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200854 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +0100855 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100856
857 /* DRAM command MRS */
Angel Pons88521882020-01-05 20:21:20 +0100858 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f000;
859 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x41001;
860 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200861 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +0100862 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100863
864 /* DRAM command MRS */
Angel Pons88521882020-01-05 20:21:20 +0100865 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x0f000;
866 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
867 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200868 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +0100869 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200870
871 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +0100872 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100873}
874
Angel Pons88521882020-01-05 20:21:20 +0100875static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100876{
877 u16 mr0reg, mch_cas, mch_wr;
878 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 +0100879 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100880
881 /* DLL Reset - self clearing - set after CLK frequency has been changed */
882 mr0reg = 0x100;
883
884 // Convert CAS to MCH register friendly
885 if (ctrl->CAS < 12) {
886 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
887 } else {
888 mch_cas = (u16) (ctrl->CAS - 12);
889 mch_cas = ((mch_cas << 1) | 0x1);
890 }
891
892 // Convert tWR to MCH register friendly
893 mch_wr = mch_wr_t[ctrl->tWR - 5];
894
895 mr0reg = (mr0reg & ~0x4) | ((mch_cas & 0x1) << 2);
896 mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3);
897 mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9);
898
899 // Precharge PD - Fast (desktop) 0x1 or slow (mobile) 0x0 - mostly power-saving feature
Patrick Rudolph74203de2017-11-20 11:57:01 +0100900 mr0reg = (mr0reg & ~0x1000) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100901 return mr0reg;
902}
903
904static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
905{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200906 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100907}
908
909static u32 encode_odt(u32 odt)
910{
911 switch (odt) {
912 case 30:
913 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
914 case 60:
915 return (1 << 2); // RZQ/4
916 case 120:
917 return (1 << 6); // RZQ/2
918 default:
919 case 0:
920 return 0;
921 }
922}
923
924static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
925{
926 odtmap odt;
927 u32 mr1reg;
928
929 odt = get_ODT(ctrl, rank, channel);
930 mr1reg = 0x2;
931
932 mr1reg |= encode_odt(odt.rttnom);
933
934 return mr1reg;
935}
936
937static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
938{
939 u16 mr1reg;
940
941 mr1reg = make_mr1(ctrl, rank, channel);
942
943 write_mrreg(ctrl, channel, rank, 1, mr1reg);
944}
945
946static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
947{
948 u16 pasr, cwl, mr2reg;
949 odtmap odt;
950 int srt;
951
952 pasr = 0;
953 cwl = ctrl->CWL - 5;
954 odt = get_ODT(ctrl, rank, channel);
955
956 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
957
958 mr2reg = 0;
959 mr2reg = (mr2reg & ~0x7) | pasr;
960 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
961 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
962 mr2reg = (mr2reg & ~0x80) | (srt << 7);
963 mr2reg |= (odt.rttwr / 60) << 9;
964
965 write_mrreg(ctrl, channel, rank, 2, mr2reg);
966}
967
968static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
969{
970 write_mrreg(ctrl, channel, rank, 3, 0);
971}
972
Angel Pons88521882020-01-05 20:21:20 +0100973void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100974{
975 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100976 int channel;
977
978 FOR_ALL_POPULATED_CHANNELS {
979 FOR_ALL_POPULATED_RANKS {
980 // MR2
981 dram_mr2(ctrl, slotrank, channel);
982
983 // MR3
984 dram_mr3(ctrl, slotrank, channel);
985
986 // MR1
987 dram_mr1(ctrl, slotrank, channel);
988
989 // MR0
990 dram_mr0(ctrl, slotrank, channel);
991 }
992 }
993
994 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +0100995 MCHBAR32(IOSAV_n_SP_CMD_CTL(0)) = 0x7;
996 MCHBAR32(IOSAV_n_SUBSEQ_CTL(0)) = 0xf1001;
997 MCHBAR32(IOSAV_n_SP_CMD_ADDR(0)) = 0x60002;
998 MCHBAR32(IOSAV_n_ADDR_UPD(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100999
1000 /* DRAM command ZQCL */
Angel Pons88521882020-01-05 20:21:20 +01001001 MCHBAR32(IOSAV_n_SP_CMD_CTL(1)) = 0x1f003;
1002 MCHBAR32(IOSAV_n_SUBSEQ_CTL(1)) = 0x1901001;
1003 MCHBAR32(IOSAV_n_SP_CMD_ADDR(1)) = 0x60400;
1004 MCHBAR32(IOSAV_n_ADDR_UPD(1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001005
Felix Held9cf1dd22018-07-31 14:52:40 +02001006 // execute command queue on all channels? Why isn't bit 0 set here?
Angel Pons88521882020-01-05 20:21:20 +01001007 MCHBAR32(IOSAV_SEQ_CTL) = 0x40004;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001008
1009 // Drain
1010 FOR_ALL_CHANNELS {
1011 // Wait for ref drained
Angel Pons88521882020-01-05 20:21:20 +01001012 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001013 }
1014
1015 // Refresh enable
Angel Pons88521882020-01-05 20:21:20 +01001016 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001017
1018 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001019 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001020
Angel Pons88521882020-01-05 20:21:20 +01001021 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001022
1023 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
1024
1025 // Drain
Angel Pons88521882020-01-05 20:21:20 +01001026 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001027
1028 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01001029 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
1030 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x659001;
1031 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001032 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001033 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001034
1035 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001036 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001037
1038 // Drain
Angel Pons88521882020-01-05 20:21:20 +01001039 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001040 }
1041}
1042
1043static const u32 lane_registers[] = {
1044 0x0000, 0x0200, 0x0400, 0x0600,
1045 0x1000, 0x1200, 0x1400, 0x1600,
1046 0x0800
1047};
1048
Angel Pons88521882020-01-05 20:21:20 +01001049void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001050{
Angel Pons88521882020-01-05 20:21:20 +01001051 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001052 int lane;
1053 int slotrank, slot;
1054 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +01001055 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001056
1057 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001058 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
1059 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001060 }
1061
1062 for (slot = 0; slot < NUM_SLOTS; slot++)
1063 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
1064 case 0:
1065 default:
Angel Pons88521882020-01-05 20:21:20 +01001066 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001067 break;
1068 case 1:
Angel Pons88521882020-01-05 20:21:20 +01001069 pi_coding_ctrl[slot] =
1070 ctrl->timings[channel][2 * slot + 0].pi_coding +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001071 full_shift;
1072 break;
1073 case 2:
Angel Pons88521882020-01-05 20:21:20 +01001074 pi_coding_ctrl[slot] =
1075 ctrl->timings[channel][2 * slot + 1].pi_coding +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001076 full_shift;
1077 break;
1078 case 3:
Angel Pons88521882020-01-05 20:21:20 +01001079 pi_coding_ctrl[slot] =
1080 (ctrl->timings[channel][2 * slot].pi_coding +
1081 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001082 full_shift;
1083 break;
1084 }
1085
1086 /* enable CMD XOVER */
1087 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +01001088 reg32 |= ((pi_coding_ctrl[0] & 0x3f) << 6) | ((pi_coding_ctrl[0] & 0x40) << 9);
1089 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001090 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
1091
Angel Pons88521882020-01-05 20:21:20 +01001092 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001093
1094 /* enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +01001095 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
1096 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001097
1098 FOR_ALL_POPULATED_RANKS {
1099 int shift =
Angel Pons88521882020-01-05 20:21:20 +01001100 ctrl->timings[channel][slotrank].pi_coding + full_shift;
1101 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001102 if (shift < 0)
1103 shift = 0;
Angel Pons88521882020-01-05 20:21:20 +01001104 offset_pi_code = ctrl->pi_code_offset + shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001105 /* set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +01001106 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
1107 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001108 }
1109
Angel Pons88521882020-01-05 20:21:20 +01001110 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
1111 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001112
Angel Pons88521882020-01-05 20:21:20 +01001113 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +01001114 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001115
Angel Pons88521882020-01-05 20:21:20 +01001116 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001117
1118 FOR_ALL_POPULATED_RANKS {
1119 int post_timA_min_high = 7, post_timA_max_high = 0;
1120 int pre_timA_min_high = 7, pre_timA_max_high = 0;
1121 int shift_402x = 0;
1122 int shift =
Angel Pons88521882020-01-05 20:21:20 +01001123 ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001124
1125 if (shift < 0)
1126 shift = 0;
1127
1128 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +02001129 post_timA_min_high = MIN(post_timA_min_high,
1130 (ctrl->timings[channel][slotrank].lanes[lane].
1131 timA + shift) >> 6);
1132 pre_timA_min_high = MIN(pre_timA_min_high,
1133 ctrl->timings[channel][slotrank].lanes[lane].
1134 timA >> 6);
1135 post_timA_max_high = MAX(post_timA_max_high,
1136 (ctrl->timings[channel][slotrank].lanes[lane].
1137 timA + shift) >> 6);
1138 pre_timA_max_high = MAX(pre_timA_max_high,
1139 ctrl->timings[channel][slotrank].lanes[lane].
1140 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001141 }
1142
1143 if (pre_timA_max_high - pre_timA_min_high <
1144 post_timA_max_high - post_timA_min_high)
1145 shift_402x = +1;
1146 else if (pre_timA_max_high - pre_timA_min_high >
1147 post_timA_max_high - post_timA_min_high)
1148 shift_402x = -1;
1149
Felix Helddee167e2019-12-30 17:30:16 +01001150 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +01001151 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001152 post_timA_min_high) << (4 * slotrank);
Angel Pons88521882020-01-05 20:21:20 +01001153 reg_roundtrip_latency |=
1154 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001155 shift_402x) << (8 * slotrank);
1156
1157 FOR_ALL_LANES {
Angel Pons1aba2a32020-01-05 22:31:41 +01001158 MCHBAR32(lane_registers[lane] + 0x10 + channel * 0x100 +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001159 4 * slotrank)
1160 =
1161 (((ctrl->timings[channel][slotrank].lanes[lane].
1162 timA + shift) & 0x3f)
1163 |
1164 ((ctrl->timings[channel][slotrank].lanes[lane].
1165 rising + shift) << 8)
1166 |
1167 (((ctrl->timings[channel][slotrank].lanes[lane].
1168 timA + shift -
1169 (post_timA_min_high << 6)) & 0x1c0) << 10)
1170 | ((ctrl->timings[channel][slotrank].lanes[lane].
1171 falling + shift) << 20));
1172
Angel Pons1aba2a32020-01-05 22:31:41 +01001173 MCHBAR32(lane_registers[lane] + 0x20 + channel * 0x100 +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001174 4 * slotrank)
1175 =
1176 (((ctrl->timings[channel][slotrank].lanes[lane].
1177 timC + shift) & 0x3f)
1178 |
1179 (((ctrl->timings[channel][slotrank].lanes[lane].
1180 timB + shift) & 0x3f) << 8)
1181 |
1182 (((ctrl->timings[channel][slotrank].lanes[lane].
1183 timB + shift) & 0x1c0) << 9)
1184 |
1185 (((ctrl->timings[channel][slotrank].lanes[lane].
1186 timC + shift) & 0x40) << 13));
1187 }
1188 }
Angel Pons88521882020-01-05 20:21:20 +01001189 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1190 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001191}
1192
Angel Pons88521882020-01-05 20:21:20 +01001193static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001194{
Angel Pons88521882020-01-05 20:21:20 +01001195 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001196
1197 /* DRAM command MRS
1198 * write MR3 MPR enable
1199 * in this mode only RD and RDA are allowed
1200 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001201 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1202 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
1203 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
1204 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001205
1206 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001207 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1208 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4040c01;
1209 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
1210 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001211
1212 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001213 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1214 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
1215 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
1216 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001217
1218 /* DRAM command MRS
1219 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001220 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1221 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
1222 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
1223 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001224
Felix Held9cf1dd22018-07-31 14:52:40 +02001225 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001226 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001227
Angel Pons88521882020-01-05 20:21:20 +01001228 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001229}
1230
Angel Pons88521882020-01-05 20:21:20 +01001231static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 int lane)
1233{
1234 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001235 return ((MCHBAR32(lane_registers[lane] + channel * 0x100 + 4 +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001236 ((timA / 32) & 1) * 4)
1237 >> (timA % 32)) & 1);
1238}
1239
1240struct run {
1241 int middle;
1242 int end;
1243 int start;
1244 int all;
1245 int length;
1246};
1247
1248static struct run get_longest_zero_run(int *seq, int sz)
1249{
1250 int i, ls;
1251 int bl = 0, bs = 0;
1252 struct run ret;
1253
1254 ls = 0;
1255 for (i = 0; i < 2 * sz; i++)
1256 if (seq[i % sz]) {
1257 if (i - ls > bl) {
1258 bl = i - ls;
1259 bs = ls;
1260 }
1261 ls = i + 1;
1262 }
1263 if (bl == 0) {
1264 ret.middle = sz / 2;
1265 ret.start = 0;
1266 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001267 ret.length = sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001268 ret.all = 1;
1269 return ret;
1270 }
1271
1272 ret.start = bs % sz;
1273 ret.end = (bs + bl - 1) % sz;
1274 ret.middle = (bs + (bl - 1) / 2) % sz;
1275 ret.length = bl;
1276 ret.all = 0;
1277
1278 return ret;
1279}
1280
Angel Pons88521882020-01-05 20:21:20 +01001281static void discover_timA_coarse(ramctr_timing *ctrl, int channel,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001282 int slotrank, int *upperA)
1283{
1284 int timA;
1285 int statistics[NUM_LANES][128];
1286 int lane;
1287
1288 for (timA = 0; timA < 128; timA++) {
1289 FOR_ALL_LANES {
1290 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1291 }
1292 program_timings(ctrl, channel);
1293
1294 test_timA(ctrl, channel, slotrank);
1295
1296 FOR_ALL_LANES {
1297 statistics[lane][timA] =
1298 !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001299 }
1300 }
1301 FOR_ALL_LANES {
1302 struct run rn = get_longest_zero_run(statistics[lane], 128);
1303 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1304 upperA[lane] = rn.end;
1305 if (upperA[lane] < rn.middle)
1306 upperA[lane] += 128;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001307 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001308 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001309 }
1310}
1311
Angel Pons88521882020-01-05 20:21:20 +01001312static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001313 int *upperA)
1314{
1315 int timA_delta;
1316 int statistics[NUM_LANES][51];
1317 int lane, i;
1318
1319 memset(statistics, 0, sizeof(statistics));
1320
1321 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
1322 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].
1323 timA = upperA[lane] + timA_delta + 0x40;
1324 program_timings(ctrl, channel);
1325
1326 for (i = 0; i < 100; i++) {
1327 test_timA(ctrl, channel, slotrank);
1328 FOR_ALL_LANES {
1329 statistics[lane][timA_delta + 25] +=
Felix Held2bb3cdf2018-07-28 00:23:59 +02001330 does_lane_work(ctrl, channel, slotrank,
1331 lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001332 }
1333 }
1334 }
1335 FOR_ALL_LANES {
1336 int last_zero, first_all;
1337
1338 for (last_zero = -25; last_zero <= 25; last_zero++)
1339 if (statistics[lane][last_zero + 25])
1340 break;
1341 last_zero--;
1342 for (first_all = -25; first_all <= 25; first_all++)
1343 if (statistics[lane][first_all + 25] == 100)
1344 break;
1345
1346 printram("lane %d: %d, %d\n", lane, last_zero,
1347 first_all);
1348
1349 ctrl->timings[channel][slotrank].lanes[lane].timA =
1350 (last_zero + first_all) / 2 + upperA[lane];
1351 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1352 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
1353 }
1354}
1355
1356static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank,
1357 int *upperA)
1358{
1359 int works[NUM_LANES];
1360 int lane;
1361 while (1) {
1362 int all_works = 1, some_works = 0;
1363 program_timings(ctrl, channel);
1364 test_timA(ctrl, channel, slotrank);
1365 FOR_ALL_LANES {
1366 works[lane] =
1367 !does_lane_work(ctrl, channel, slotrank, lane);
1368 if (works[lane])
1369 some_works = 1;
1370 else
1371 all_works = 0;
1372 }
1373 if (all_works)
1374 return 0;
1375 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001376 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001377 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1378 channel, slotrank);
1379 return MAKE_ERR;
1380 }
Angel Pons88521882020-01-05 20:21:20 +01001381 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001382 printram("4024 -= 2;\n");
1383 continue;
1384 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001385 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001386 printram("4028 += 2;\n");
Felix Heldef4fe3e2019-12-31 14:15:05 +01001387 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001388 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1389 channel, slotrank);
1390 return MAKE_ERR;
1391 }
1392 FOR_ALL_LANES if (works[lane]) {
1393 ctrl->timings[channel][slotrank].lanes[lane].timA +=
1394 128;
1395 upperA[lane] += 128;
1396 printram("increment %d, %d, %d\n", channel,
1397 slotrank, lane);
1398 }
1399 }
1400 return 0;
1401}
1402
1403struct timA_minmax {
1404 int timA_min_high, timA_max_high;
1405};
1406
Angel Pons88521882020-01-05 20:21:20 +01001407static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001408 struct timA_minmax *mnmx)
1409{
1410 int lane;
1411 mnmx->timA_min_high = 7;
1412 mnmx->timA_max_high = 0;
1413
1414 FOR_ALL_LANES {
1415 if (mnmx->timA_min_high >
1416 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1417 mnmx->timA_min_high =
1418 (ctrl->timings[channel][slotrank].lanes[lane].
1419 timA >> 6);
1420 if (mnmx->timA_max_high <
1421 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1422 mnmx->timA_max_high =
1423 (ctrl->timings[channel][slotrank].lanes[lane].
1424 timA >> 6);
1425 }
1426}
1427
Angel Pons88521882020-01-05 20:21:20 +01001428static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001429 struct timA_minmax *mnmx)
1430{
1431 struct timA_minmax post;
1432 int shift_402x = 0;
1433
1434 /* Get changed maxima. */
1435 pre_timA_change(ctrl, channel, slotrank, &post);
1436
1437 if (mnmx->timA_max_high - mnmx->timA_min_high <
1438 post.timA_max_high - post.timA_min_high)
1439 shift_402x = +1;
1440 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1441 post.timA_max_high - post.timA_min_high)
1442 shift_402x = -1;
1443 else
1444 shift_402x = 0;
1445
Felix Heldef4fe3e2019-12-31 14:15:05 +01001446 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001447 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001448 printram("4024 += %d;\n", shift_402x);
1449 printram("4028 += %d;\n", shift_402x);
1450}
1451
1452/* Compensate the skew between DQS and DQs.
1453 * To ease PCB design a small skew between Data Strobe signals and
1454 * Data Signals is allowed.
1455 * The controller has to measure and compensate this skew for every byte-lane.
1456 * By delaying either all DQs signals or DQS signal, a full phase
1457 * shift can be introduced.
1458 * It is assumed that one byte-lane's DQs signals have the same routing delay.
1459 *
1460 * To measure the actual skew, the DRAM is placed in "read leveling" mode.
1461 * In read leveling mode the DRAM-chip outputs an alternating periodic pattern.
1462 * The memory controller iterates over all possible values to do a full phase shift
1463 * and issues read commands.
1464 * With DQS and DQs in phase the data read is expected to alternate on every byte:
1465 * 0xFF 0x00 0xFF ...
1466 * Once the controller has detected this pattern a bit in the result register is
1467 * set for the current phase shift.
1468 */
Angel Pons88521882020-01-05 20:21:20 +01001469int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001470{
1471 int channel, slotrank, lane;
1472 int err;
1473
1474 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1475 int all_high, some_high;
1476 int upperA[NUM_LANES];
1477 struct timA_minmax mnmx;
1478
Angel Pons88521882020-01-05 20:21:20 +01001479 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001480
Felix Held2bb3cdf2018-07-28 00:23:59 +02001481 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001482 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1483 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1484 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1485 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001486
1487 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001488 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001489
Angel Pons88521882020-01-05 20:21:20 +01001490 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001491
Felix Heldef4fe3e2019-12-31 14:15:05 +01001492 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001493 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001494 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001495
Felix Held2bb3cdf2018-07-28 00:23:59 +02001496 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001497
Felix Held2bb3cdf2018-07-28 00:23:59 +02001498 all_high = 1;
1499 some_high = 0;
1500 FOR_ALL_LANES {
1501 if (ctrl->timings[channel][slotrank].lanes[lane].timA >=
1502 0x40)
1503 some_high = 1;
1504 else
1505 all_high = 0;
1506 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001507
1508 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001509 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001510 printram("4028--;\n");
1511 FOR_ALL_LANES {
1512 ctrl->timings[channel][slotrank].lanes[lane].
1513 timA -= 0x40;
1514 upperA[lane] -= 0x40;
1515
1516 }
1517 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001518 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001519 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001520 printram("4024++;\n");
1521 printram("4028++;\n");
1522 }
1523
1524 program_timings(ctrl, channel);
1525
1526 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1527
1528 err = discover_402x(ctrl, channel, slotrank, upperA);
1529 if (err)
1530 return err;
1531
1532 post_timA_change(ctrl, channel, slotrank, &mnmx);
1533 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1534
1535 discover_timA_fine(ctrl, channel, slotrank, upperA);
1536
1537 post_timA_change(ctrl, channel, slotrank, &mnmx);
1538 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1539
1540 FOR_ALL_LANES {
1541 ctrl->timings[channel][slotrank].lanes[lane].timA -= mnmx.timA_min_high * 0x40;
1542 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001543 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001544 printram("4028 -= %d;\n", mnmx.timA_min_high);
1545
1546 post_timA_change(ctrl, channel, slotrank, &mnmx);
1547
1548 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001549 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001550 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001551
1552 printram("final results:\n");
1553 FOR_ALL_LANES
Felix Held2bb3cdf2018-07-28 00:23:59 +02001554 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1555 lane,
1556 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557
Angel Pons88521882020-01-05 20:21:20 +01001558 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001559
1560 toggle_io_reset();
1561 }
1562
1563 FOR_ALL_POPULATED_CHANNELS {
1564 program_timings(ctrl, channel);
1565 }
1566 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001567 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568 }
1569 return 0;
1570}
1571
Angel Pons88521882020-01-05 20:21:20 +01001572static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001573{
1574 int lane;
1575
1576 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001577 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1578 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001579 }
1580
Angel Pons88521882020-01-05 20:21:20 +01001581 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001582
1583 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001584 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1585 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001586 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001587 | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001588 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
1589 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001590
1591 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001592 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1593 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8041001;
1594 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
1595 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001596
1597 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01001598 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
1599 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x80411f4;
1600 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
1601 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001602
1603 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001604 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
1605 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001606 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001607 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
1608 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001609
Felix Held9cf1dd22018-07-31 14:52:40 +02001610 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001611 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001612
Angel Pons88521882020-01-05 20:21:20 +01001613 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001614
1615 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001616 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1617 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1618 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1619 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001620
1621 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001622 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
1623 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001624 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001625 | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001626 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
1627 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001628
1629 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001630 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1631 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001632 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001633 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
1634 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001635
1636 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001637 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
1638 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
1639 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
1640 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001641
1642 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001643 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001644
Angel Pons88521882020-01-05 20:21:20 +01001645 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001646}
1647
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001648static void timC_threshold_process(int *data, const int count)
1649{
1650 int min = data[0];
1651 int max = min;
1652 int i;
1653 for (i = 1; i < count; i++) {
1654 if (min > data[i])
1655 min = data[i];
1656 if (max < data[i])
1657 max = data[i];
1658 }
1659 int threshold = min/2 + max/2;
1660 for (i = 0; i < count; i++)
1661 data[i] = data[i] > threshold;
1662 printram("threshold=%d min=%d max=%d\n",
1663 threshold, min, max);
1664}
1665
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001666static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1667{
1668 int timC;
1669 int statistics[NUM_LANES][MAX_TIMC + 1];
1670 int lane;
1671
Angel Pons88521882020-01-05 20:21:20 +01001672 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001673
1674 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001675 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1676 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1677 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1678 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001679
1680 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001681 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001682
1683 for (timC = 0; timC <= MAX_TIMC; timC++) {
1684 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].
1685 timC = timC;
1686 program_timings(ctrl, channel);
1687
1688 test_timC(ctrl, channel, slotrank);
1689
1690 FOR_ALL_LANES {
1691 statistics[lane][timC] =
Angel Pons88521882020-01-05 20:21:20 +01001692 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001693 }
1694 }
1695 FOR_ALL_LANES {
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001696 struct run rn = get_longest_zero_run(
1697 statistics[lane], ARRAY_SIZE(statistics[lane]));
1698 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001699 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1700 channel, slotrank, lane);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001701 /* With command training not happend yet, the lane can
1702 * be erroneous. Take the avarage as reference and try
1703 * again to find a run.
1704 */
1705 timC_threshold_process(statistics[lane],
1706 ARRAY_SIZE(statistics[lane]));
1707 rn = get_longest_zero_run(statistics[lane],
1708 ARRAY_SIZE(statistics[lane]));
1709 if (rn.all || rn.length < 8) {
1710 printk(BIOS_EMERG, "timC recovery failed\n");
1711 return MAKE_ERR;
1712 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001713 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001714 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001715 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001716 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001717 }
1718 return 0;
1719}
1720
Angel Pons88521882020-01-05 20:21:20 +01001721static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001722{
1723 int channel, ret = 0;
1724 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1725 ret++;
1726 return ret;
1727}
1728
Angel Pons88521882020-01-05 20:21:20 +01001729static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001730{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301731 unsigned int j;
1732 unsigned int channel_offset =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001733 get_precedening_channels(ctrl, channel) * 0x40;
1734 for (j = 0; j < 16; j++)
1735 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
1736 sfence();
1737}
1738
Angel Pons88521882020-01-05 20:21:20 +01001739static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001740{
1741 int ret = 0;
1742 int channel;
1743 FOR_ALL_POPULATED_CHANNELS ret++;
1744 return ret;
1745}
1746
Angel Pons88521882020-01-05 20:21:20 +01001747static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001748{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301749 unsigned int j;
1750 unsigned int channel_offset =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001751 get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301752 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001753 for (j = 0; j < 16; j++)
1754 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
1755 for (j = 0; j < 16; j++)
1756 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
1757 sfence();
1758}
1759
Angel Pons88521882020-01-05 20:21:20 +01001760static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001761{
1762 int channel, slotrank, lane;
1763
1764 FOR_ALL_POPULATED_CHANNELS {
1765 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
1766 ctrl->timings[channel][slotrank].lanes[lane].falling =
1767 16;
1768 ctrl->timings[channel][slotrank].lanes[lane].rising =
1769 16;
1770 }
1771
1772 program_timings(ctrl, channel);
1773
1774 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001775 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001776
1777 /* DRAM command MRS
1778 * write MR3 MPR enable
1779 * in this mode only RD and RDA are allowed
1780 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001781 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1782 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001783 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001784 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001785 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001786 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001787
1788 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001789 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1790 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
1791 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001792 (slotrank << 24) | 0;
Angel Pons88521882020-01-05 20:21:20 +01001793 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001794
1795 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001796 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1797 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001798 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001799 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001800 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001801 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001802
1803 /* DRAM command MRS
1804 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001805 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1806 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001807 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001808 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001809 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001810 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001811
1812 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001813 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001814
Angel Pons88521882020-01-05 20:21:20 +01001815 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001816 }
1817
1818 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
1819 ctrl->timings[channel][slotrank].lanes[lane].falling =
1820 48;
1821 ctrl->timings[channel][slotrank].lanes[lane].rising =
1822 48;
1823 }
1824
1825 program_timings(ctrl, channel);
1826
1827 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001828 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001829 /* DRAM command MRS
1830 * write MR3 MPR enable
1831 * in this mode only RD and RDA are allowed
1832 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001833 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1834 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001835 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001836 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001837 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001838 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001839
1840 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001841 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1842 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
1843 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001844 (slotrank << 24) | 0;
Angel Pons88521882020-01-05 20:21:20 +01001845 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001846
1847 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001848 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1849 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001850 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001851 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001852 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001853 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001854
1855 /* DRAM command MRS
1856 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001857 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1858 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001859 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001860 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001861 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001862 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001863
Felix Held9cf1dd22018-07-31 14:52:40 +02001864 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001865 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
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 }
1869 }
1870}
1871
Angel Pons88521882020-01-05 20:21:20 +01001872static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001873{
1874 /* enable DQs on this slotrank */
1875 write_mrreg(ctrl, channel, slotrank, 1,
1876 0x80 | make_mr1(ctrl, slotrank, channel));
1877
Angel Pons88521882020-01-05 20:21:20 +01001878 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001879 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001880 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f207;
1881 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001882 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001883 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
1884 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001885
1886 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001887 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f107;
1888 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001889 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001890 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
1891 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001892
Felix Held9cf1dd22018-07-31 14:52:40 +02001893 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001894 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001895
Angel Pons88521882020-01-05 20:21:20 +01001896 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897
1898 /* disable DQs on this slotrank */
1899 write_mrreg(ctrl, channel, slotrank, 1,
1900 0x1080 | make_mr1(ctrl, slotrank, channel));
1901}
1902
1903static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1904{
1905 int timB;
1906 int statistics[NUM_LANES][128];
1907 int lane;
1908
Angel Pons88521882020-01-05 20:21:20 +01001909 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001910
1911 for (timB = 0; timB < 128; timB++) {
1912 FOR_ALL_LANES {
1913 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1914 }
1915 program_timings(ctrl, channel);
1916
1917 test_timB(ctrl, channel, slotrank);
1918
1919 FOR_ALL_LANES {
1920 statistics[lane][timB] =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001921 !((MCHBAR32(lane_registers[lane] +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001922 channel * 0x100 + 4 + ((timB / 32) & 1) * 4)
1923 >> (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001924 }
1925 }
1926 FOR_ALL_LANES {
1927 struct run rn = get_longest_zero_run(statistics[lane], 128);
1928 /* timC is a direct function of timB's 6 LSBs.
1929 * Some tests increments the value of timB by a small value,
1930 * which might cause the 6bit value to overflow, if it's close
1931 * to 0x3F. Increment the value by a small offset if it's likely
1932 * to overflow, to make sure it won't overflow while running
1933 * tests and bricks the system due to a non matching timC.
1934 *
1935 * TODO: find out why some tests (edge write discovery)
1936 * increment timB. */
1937 if ((rn.start & 0x3F) == 0x3E)
1938 rn.start += 2;
1939 else if ((rn.start & 0x3F) == 0x3F)
1940 rn.start += 1;
1941 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1942 if (rn.all) {
1943 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1944 channel, slotrank, lane);
1945 return MAKE_ERR;
1946 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001947 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1948 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001949 }
1950 return 0;
1951}
1952
1953static int get_timB_high_adjust(u64 val)
1954{
1955 int i;
1956
1957 /* good */
1958 if (val == 0xffffffffffffffffLL)
1959 return 0;
1960
1961 if (val >= 0xf000000000000000LL) {
1962 /* needs negative adjustment */
1963 for (i = 0; i < 8; i++)
1964 if (val << (8 * (7 - i) + 4))
1965 return -i;
1966 } else {
1967 /* needs positive adjustment */
1968 for (i = 0; i < 8; i++)
1969 if (val >> (8 * (7 - i) + 4))
1970 return i;
1971 }
1972 return 8;
1973}
1974
Angel Pons88521882020-01-05 20:21:20 +01001975static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001976{
1977 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001978 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001979 FOR_ALL_POPULATED_CHANNELS {
1980 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001981 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001982 }
1983 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1984
Angel Pons88521882020-01-05 20:21:20 +01001985 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001986
Angel Pons88521882020-01-05 20:21:20 +01001987 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001988
1989 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001990 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1991 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
1992 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
1993 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001994
1995 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001996 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1997 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8040c01;
1998 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
1999 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002000
2001 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002002 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
2003 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x8041003;
2004 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2005 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002006
2007 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01002008 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
2009 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002010 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002011 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
2012 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002013
Felix Held9cf1dd22018-07-31 14:52:40 +02002014 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002015 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002016
Angel Pons88521882020-01-05 20:21:20 +01002017 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002018
2019 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01002020 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
2021 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002022 0xc01 | ((ctrl->tRP) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002023 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002024 (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01002025 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002026
2027 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002028 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
2029 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002030 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002031 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
2032 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002033
2034 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002035 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x3f105;
2036 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
2037 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01002038 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002039 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
2040 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002041
Felix Held9cf1dd22018-07-31 14:52:40 +02002042 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002043 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02002044
Angel Pons88521882020-01-05 20:21:20 +01002045 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002046 FOR_ALL_LANES {
Felix Held2bb3cdf2018-07-28 00:23:59 +02002047 u64 res = MCHBAR32(lane_registers[lane] +
Angel Pons1aba2a32020-01-05 22:31:41 +01002048 channel * 0x100 + 4);
Felix Held2bb3cdf2018-07-28 00:23:59 +02002049 res |= ((u64) MCHBAR32(lane_registers[lane] +
Angel Pons1aba2a32020-01-05 22:31:41 +01002050 channel * 0x100 + 8)) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002051 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
2052 ctrl->timings[channel][slotrank].lanes[lane].timB +=
2053 get_timB_high_adjust(res) * 64;
2054
2055 printram("High adjust %d:%016llx\n", lane, res);
2056 printram("Bval+: %d, %d, %d, %x -> %x\n", channel,
2057 slotrank, lane, old,
2058 ctrl->timings[channel][slotrank].lanes[lane].
2059 timB);
2060 }
2061 }
Angel Pons88521882020-01-05 20:21:20 +01002062 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002063}
2064
Angel Pons88521882020-01-05 20:21:20 +01002065static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002066{
2067 int slotrank;
2068
Angel Pons88521882020-01-05 20:21:20 +01002069 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002070
2071 /* choose an existing rank. */
2072 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2073
2074 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002075 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2076 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2077 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2078 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002079
Felix Held9cf1dd22018-07-31 14:52:40 +02002080 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002081 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002082
Angel Pons88521882020-01-05 20:21:20 +01002083 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002084}
2085
2086/* Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
2087 * DDR3 adopted the fly-by topology. The data and strobes signals reach
2088 * the chips at different times with respect to command, address and
2089 * clock signals.
2090 * By delaying either all DQ/DQs or all CMD/ADDR/CLK signals, a full phase
2091 * shift can be introduced.
2092 * It is assumed that the CLK/ADDR/CMD signals have the same routing delay.
2093 *
2094 * To find the required phase shift the DRAM is placed in "write leveling" mode.
2095 * In this mode the DRAM-chip samples the CLK on every DQS edge and feeds back the
2096 * sampled value on the data lanes (DQs).
2097 */
Angel Pons88521882020-01-05 20:21:20 +01002098int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002099{
2100 int channel, slotrank, lane;
2101 int err;
2102
2103 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002104 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002105
2106 FOR_ALL_POPULATED_CHANNELS {
2107 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01002108 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002109 }
2110
2111 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002112 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002113 FOR_ALL_POPULATED_CHANNELS {
2114 write_op(ctrl, channel);
2115 }
2116
2117 /* enable write leveling on all ranks
2118 * disable all DQ outputs
2119 * only NOP is allowed in this mode */
2120 FOR_ALL_CHANNELS
Felix Held2bb3cdf2018-07-28 00:23:59 +02002121 FOR_ALL_POPULATED_RANKS
2122 write_mrreg(ctrl, channel, slotrank, 1,
2123 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002124
Angel Pons88521882020-01-05 20:21:20 +01002125 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002126
2127 toggle_io_reset();
2128
2129 /* set any valid value for timB, it gets corrected later */
2130 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2131 err = discover_timB(ctrl, channel, slotrank);
2132 if (err)
2133 return err;
2134 }
2135
2136 /* disable write leveling on all ranks */
2137 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
2138 write_mrreg(ctrl, channel,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002139 slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002140
Angel Pons88521882020-01-05 20:21:20 +01002141 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002142
2143 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002144 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002145
2146 /* refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01002147 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002148
2149 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002150 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
2151 MCHBAR32(IOSAV_STATUS_ch(channel));
2152 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002153
2154 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002155 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2156 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x659001;
2157 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
2158 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002159
Felix Held9cf1dd22018-07-31 14:52:40 +02002160 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002161 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002162
Angel Pons88521882020-01-05 20:21:20 +01002163 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002164 }
2165
2166 toggle_io_reset();
2167
2168 printram("CPE\n");
2169 precharge(ctrl);
2170 printram("CPF\n");
2171
2172 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002173 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002174 }
2175
2176 FOR_ALL_POPULATED_CHANNELS {
2177 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002178 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002179 }
2180
2181 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2182 err = discover_timC(ctrl, channel, slotrank);
2183 if (err)
2184 return err;
2185 }
2186
2187 FOR_ALL_POPULATED_CHANNELS
2188 program_timings(ctrl, channel);
2189
2190 /* measure and adjust timB timings */
2191 adjust_high_timB(ctrl);
2192
2193 FOR_ALL_POPULATED_CHANNELS
2194 program_timings(ctrl, channel);
2195
2196 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002197 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002198 }
2199 return 0;
2200}
2201
Angel Pons88521882020-01-05 20:21:20 +01002202static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002203{
2204 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2205 int timC_delta;
2206 int lanes_ok = 0;
2207 int ctr = 0;
2208 int lane;
2209
2210 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2211 FOR_ALL_LANES {
2212 ctrl->timings[channel][slotrank].lanes[lane].timC =
2213 saved_rt.lanes[lane].timC + timC_delta;
2214 }
2215 program_timings(ctrl, channel);
2216 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002217 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002218 }
2219
Angel Pons88521882020-01-05 20:21:20 +01002220 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002221
Angel Pons88521882020-01-05 20:21:20 +01002222 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002223 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002224 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2225 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002226 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002227 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002228 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002229 (slotrank << 24) | ctr | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002230 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002231
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002232 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002233 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2234 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002235 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002236 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2237 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
2238 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002239
2240 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002241 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2242 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002243 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002244 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2245 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
2246 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002247
2248 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002249 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2250 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xf1001;
2251 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2252 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002253
Felix Held9cf1dd22018-07-31 14:52:40 +02002254 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002255 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002256
Angel Pons88521882020-01-05 20:21:20 +01002257 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002258 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002259 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002260
2261 if (r32 == 0)
2262 lanes_ok |= 1 << lane;
2263 }
2264 ctr++;
2265 if (lanes_ok == ((1 << NUM_LANES) - 1))
2266 break;
2267 }
2268
2269 ctrl->timings[channel][slotrank] = saved_rt;
2270
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002271 return lanes_ok != ((1 << NUM_LANES) - 1);
2272}
2273
2274#include "raminit_patterns.h"
2275
Angel Pons88521882020-01-05 20:21:20 +01002276static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002277{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302278 unsigned int i, j;
2279 unsigned int channel_offset =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002280 get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05302281 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002282
2283 if (patno) {
2284 u8 base8 = 0x80 >> ((patno - 1) % 8);
2285 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2286 for (i = 0; i < 32; i++) {
2287 for (j = 0; j < 16; j++) {
2288 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
2289 if (invert[patno - 1][i] & (1 << (j / 2)))
2290 val = ~val;
2291 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2292 j * 4), val);
2293 }
2294 }
2295
2296 } else {
2297 for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) {
2298 for (j = 0; j < 16; j++)
2299 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2300 j * 4), pattern[i][j]);
2301 }
2302 sfence();
2303 }
2304}
2305
Angel Pons88521882020-01-05 20:21:20 +01002306static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002307{
2308 int channel, slotrank;
2309
2310 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002311 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002312
2313 /* choose an existing rank. */
2314 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2315
2316 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002317 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2318 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2319 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2320 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002321
Felix Held9cf1dd22018-07-31 14:52:40 +02002322 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002323 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002324
Angel Pons88521882020-01-05 20:21:20 +01002325 wait_for_iosav(channel);
2326 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002327 }
2328
2329 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002330 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002331 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002332 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002333
2334 /* choose an existing rank. */
2335 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2336
2337 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002338 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2339 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2340 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2341 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002342
Felix Held9cf1dd22018-07-31 14:52:40 +02002343 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002344 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002345
Angel Pons88521882020-01-05 20:21:20 +01002346 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002347 }
2348
2349 /* jedec reset */
2350 dram_jedecreset(ctrl);
2351 /* mrs commands. */
2352 dram_mrscommands(ctrl);
2353
2354 toggle_io_reset();
2355}
2356
2357#define MIN_C320C_LEN 13
2358
2359static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2360{
2361 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2362 int slotrank;
2363 int c320c;
2364 int stat[NUM_SLOTRANKS][256];
2365 int delta = 0;
2366
2367 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2368
2369 FOR_ALL_POPULATED_RANKS {
2370 saved_timings[channel][slotrank] =
2371 ctrl->timings[channel][slotrank];
2372 }
2373
2374 ctrl->cmd_stretch[channel] = cmd_stretch;
2375
Angel Pons88521882020-01-05 20:21:20 +01002376 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002377 ctrl->tRRD
2378 | (ctrl->tRTP << 4)
2379 | (ctrl->tCKE << 8)
2380 | (ctrl->tWTR << 12)
2381 | (ctrl->tFAW << 16)
2382 | (ctrl->tWR << 24)
2383 | (ctrl->cmd_stretch[channel] << 30);
2384
2385 if (ctrl->cmd_stretch[channel] == 2)
2386 delta = 2;
2387 else if (ctrl->cmd_stretch[channel] == 0)
2388 delta = 4;
2389
2390 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002391 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002392 }
2393
2394 for (c320c = -127; c320c <= 127; c320c++) {
2395 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002396 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002397 }
2398 program_timings(ctrl, channel);
2399 reprogram_320c(ctrl);
2400 FOR_ALL_POPULATED_RANKS {
2401 stat[slotrank][c320c + 127] =
2402 test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002403 }
2404 }
2405 FOR_ALL_POPULATED_RANKS {
2406 struct run rn =
2407 get_longest_zero_run(stat[slotrank], 255);
Angel Pons88521882020-01-05 20:21:20 +01002408 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002409 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2410 channel, slotrank, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002411 if (rn.all || rn.length < MIN_C320C_LEN) {
2412 FOR_ALL_POPULATED_RANKS {
2413 ctrl->timings[channel][slotrank] =
2414 saved_timings[channel][slotrank];
2415 }
2416 return MAKE_ERR;
2417 }
2418 }
2419
2420 return 0;
2421}
2422
2423/* Adjust CMD phase shift and try multiple command rates.
2424 * A command rate of 2T doubles the time needed for address and
2425 * command decode. */
2426int command_training(ramctr_timing *ctrl)
2427{
2428 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002429
2430 FOR_ALL_POPULATED_CHANNELS {
2431 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002432 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002433 }
2434
2435 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002436 int cmdrate, err;
2437
2438 /*
2439 * Dual DIMM per channel:
2440 * Issue: While c320c discovery seems to succeed raminit
2441 * will fail in write training.
2442 * Workaround: Skip 1T in dual DIMM mode, that's only
2443 * supported by a few DIMMs.
Dan Elkoubydabebc32018-04-13 18:47:10 +03002444 * Only try 1T mode for XMP DIMMs that request it in dual DIMM
2445 * mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002446 *
2447 * Single DIMM per channel:
2448 * Try command rate 1T and 2T
2449 */
2450 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002451 if (ctrl->tCMD)
2452 /* XMP gives the CMD rate in clock ticks, not ns */
2453 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002454
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002455 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002456 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2457
2458 if (!err)
2459 break;
2460 }
2461
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002462 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002463 printk(BIOS_EMERG, "c320c discovery failed\n");
2464 return err;
2465 }
2466
2467 printram("Using CMD rate %uT on channel %u\n",
2468 cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002469 }
2470
2471 FOR_ALL_POPULATED_CHANNELS
2472 program_timings(ctrl, channel);
2473
2474 reprogram_320c(ctrl);
2475 return 0;
2476}
2477
2478
2479static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank,
2480 int *edges)
2481{
2482 int edge;
2483 int statistics[NUM_LANES][MAX_EDGE_TIMING + 1];
2484 int lane;
2485
2486 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2487 FOR_ALL_LANES {
2488 ctrl->timings[channel][slotrank].lanes[lane].rising =
2489 edge;
2490 ctrl->timings[channel][slotrank].lanes[lane].falling =
2491 edge;
2492 }
2493 program_timings(ctrl, channel);
2494
2495 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002496 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2497 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002498 }
2499
Angel Pons88521882020-01-05 20:21:20 +01002500 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002501 /* DRAM command MRS
2502 * write MR3 MPR enable
2503 * in this mode only RD and RDA are allowed
2504 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002505 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2506 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
2507 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002508 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002509 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002510
2511 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002512 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2513 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x40411f4;
2514 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2515 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002516
2517 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002518 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2519 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002520 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002521 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
2522 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002523
2524 /* DRAM command MRS
2525 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002526 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2527 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
2528 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002529 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002530 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002531
Felix Held9cf1dd22018-07-31 14:52:40 +02002532 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002533 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002534
Angel Pons88521882020-01-05 20:21:20 +01002535 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002536
2537 FOR_ALL_LANES {
2538 statistics[lane][edge] =
Angel Pons88521882020-01-05 20:21:20 +01002539 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002540 }
2541 }
2542 FOR_ALL_LANES {
2543 struct run rn =
2544 get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1);
2545 edges[lane] = rn.middle;
2546 if (rn.all) {
2547 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n",
2548 channel, slotrank, lane);
2549 return MAKE_ERR;
2550 }
2551 printram("eval %d, %d, %d: %02x\n", channel, slotrank,
2552 lane, edges[lane]);
2553 }
2554 return 0;
2555}
2556
2557int discover_edges(ramctr_timing *ctrl)
2558{
2559 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2560 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2561 int channel, slotrank, lane;
2562 int err;
2563
Angel Pons88521882020-01-05 20:21:20 +01002564 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002565
2566 toggle_io_reset();
2567
2568 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002569 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002570 }
2571
2572 FOR_ALL_POPULATED_CHANNELS {
2573 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002574 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002575 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002576 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002577 }
2578
2579 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2580 ctrl->timings[channel][slotrank].lanes[lane].falling =
2581 16;
2582 ctrl->timings[channel][slotrank].lanes[lane].rising =
2583 16;
2584 }
2585
2586 program_timings(ctrl, channel);
2587
2588 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002589 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002590
2591 /* DRAM command MRS
2592 * MR3 enable MPR
2593 * write MR3 MPR enable
2594 * in this mode only RD and RDA are allowed
2595 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002596 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2597 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002598 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002599 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002600 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002601 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002602
2603 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002604 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2605 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
2606 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002607 (slotrank << 24) | 0;
Angel Pons88521882020-01-05 20:21:20 +01002608 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002609
2610 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002611 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2612 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002613 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002614 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002615 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002616 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002617
2618 /* DRAM command MRS
2619 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002620 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2621 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002622 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002623 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002624 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002625 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002626
2627 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002628 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002629
Angel Pons88521882020-01-05 20:21:20 +01002630 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002631 }
2632
2633 /* XXX: check any measured value ? */
2634
2635 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2636 ctrl->timings[channel][slotrank].lanes[lane].falling =
2637 48;
2638 ctrl->timings[channel][slotrank].lanes[lane].rising =
2639 48;
2640 }
2641
2642 program_timings(ctrl, channel);
2643
2644 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002645 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002646
2647 /* DRAM command MRS
2648 * MR3 enable MPR
2649 * write MR3 MPR enable
2650 * in this mode only RD and RDA are allowed
2651 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002652 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2653 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002654 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002655 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002656 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002657 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002658
2659 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002660 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2661 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
2662 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002663 (slotrank << 24) | 0;
Angel Pons88521882020-01-05 20:21:20 +01002664 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002665
2666 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002667 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2668 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002669 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002670 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002671 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002672 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002673
2674 /* DRAM command MRS
2675 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002676 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2677 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002678 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002679 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002680 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002681 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002682
Felix Held9cf1dd22018-07-31 14:52:40 +02002683 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002684 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002685
Angel Pons88521882020-01-05 20:21:20 +01002686 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002687 }
2688
2689 /* XXX: check any measured value ? */
2690
2691 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002692 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
2693 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane))
Felix Held2bb3cdf2018-07-28 00:23:59 +02002694 & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002695 }
2696
2697 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002698 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002699 }
2700
2701 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002702 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2703 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002704
2705 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2706 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002707 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002708 if (err)
2709 return err;
2710 }
2711
Angel Pons88521882020-01-05 20:21:20 +01002712 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2713 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002714
2715 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2716 err = discover_edges_real(ctrl, channel, slotrank,
2717 rising_edges[channel][slotrank]);
2718 if (err)
2719 return err;
2720 }
2721
Angel Pons88521882020-01-05 20:21:20 +01002722 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002723
2724 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2725 ctrl->timings[channel][slotrank].lanes[lane].falling =
2726 falling_edges[channel][slotrank][lane];
2727 ctrl->timings[channel][slotrank].lanes[lane].rising =
2728 rising_edges[channel][slotrank][lane];
2729 }
2730
2731 FOR_ALL_POPULATED_CHANNELS {
2732 program_timings(ctrl, channel);
2733 }
2734
2735 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002736 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002737 }
2738 return 0;
2739}
2740
2741static int discover_edges_write_real(ramctr_timing *ctrl, int channel,
2742 int slotrank, int *edges)
2743{
2744 int edge;
2745 u32 raw_statistics[MAX_EDGE_TIMING + 1];
2746 int statistics[MAX_EDGE_TIMING + 1];
2747 const int reg3000b24[] = { 0, 0xc, 0x2c };
2748 int lane, i;
2749 int lower[NUM_LANES];
2750 int upper[NUM_LANES];
2751 int pat;
2752
2753 FOR_ALL_LANES {
2754 lower[lane] = 0;
2755 upper[lane] = MAX_EDGE_TIMING;
2756 }
2757
2758 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002759 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002760 printram("[%x] = 0x%08x\n",
Angel Pons88521882020-01-05 20:21:20 +01002761 GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002762 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2763 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002764 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002765 printram("using pattern %d\n", pat);
2766 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2767 FOR_ALL_LANES {
2768 ctrl->timings[channel][slotrank].lanes[lane].
2769 rising = edge;
2770 ctrl->timings[channel][slotrank].lanes[lane].
2771 falling = edge;
2772 }
2773 program_timings(ctrl, channel);
2774
2775 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002776 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2777 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002778 }
Angel Pons88521882020-01-05 20:21:20 +01002779 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002780
2781 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002782 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2783 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002784 0x4 | (ctrl->tRCD << 16) |
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002785 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002786 << 10);
Angel Pons88521882020-01-05 20:21:20 +01002787 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002788 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002789 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002790
2791 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002792 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2793 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002794 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002795 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002796 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002797 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002798
2799 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002800 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2801 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002802 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002803 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002804 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002805 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002806
2807 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002808 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2809 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002810 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002811 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002812 (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01002813 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002814
Felix Held9cf1dd22018-07-31 14:52:40 +02002815 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002816 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) =
2817 IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002818
Angel Pons88521882020-01-05 20:21:20 +01002819 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002820 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002821 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002822 }
2823
2824 raw_statistics[edge] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002825 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826 }
2827 FOR_ALL_LANES {
2828 struct run rn;
2829 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
2830 statistics[edge] =
2831 ! !(raw_statistics[edge] & (1 << lane));
2832 rn = get_longest_zero_run(statistics,
2833 MAX_EDGE_TIMING + 1);
2834 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
2835 channel, slotrank, i, rn.start, rn.middle,
2836 rn.end, rn.start + ctrl->edge_offset[i],
2837 rn.end - ctrl->edge_offset[i]);
2838 lower[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002839 MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002840 upper[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002841 MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002842 edges[lane] = (lower[lane] + upper[lane]) / 2;
2843 if (rn.all || (lower[lane] > upper[lane])) {
2844 printk(BIOS_EMERG, "edge write discovery failed: %d, %d, %d\n",
2845 channel, slotrank, lane);
2846 return MAKE_ERR;
2847 }
2848 }
2849 }
2850 }
2851
Angel Pons88521882020-01-05 20:21:20 +01002852 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002853 printram("CPA\n");
2854 return 0;
2855}
2856
2857int discover_edges_write(ramctr_timing *ctrl)
2858{
2859 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2860 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2861 int channel, slotrank, lane;
2862 int err;
2863
2864 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002865 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2866 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002867
2868 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2869 err = discover_edges_write_real(ctrl, channel, slotrank,
2870 falling_edges[channel][slotrank]);
2871 if (err)
2872 return err;
2873 }
2874
Angel Pons88521882020-01-05 20:21:20 +01002875 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2876 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002877
2878 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2879 err = discover_edges_write_real(ctrl, channel, slotrank,
2880 rising_edges[channel][slotrank]);
2881 if (err)
2882 return err;
2883 }
2884
Angel Pons88521882020-01-05 20:21:20 +01002885 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002886
2887 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2888 ctrl->timings[channel][slotrank].lanes[lane].falling =
2889 falling_edges[channel][slotrank][lane];
2890 ctrl->timings[channel][slotrank].lanes[lane].rising =
2891 rising_edges[channel][slotrank][lane];
2892 }
2893
2894 FOR_ALL_POPULATED_CHANNELS
2895 program_timings(ctrl, channel);
2896
2897 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002898 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002899 }
2900 return 0;
2901}
2902
2903static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2904{
Angel Pons88521882020-01-05 20:21:20 +01002905 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002906 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002907 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2908 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002909 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002910 << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons88521882020-01-05 20:21:20 +01002911 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002912 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002913 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002914
2915 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002916 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2917 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002918 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002919 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2920 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002921
2922 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002923 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2924 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002925 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002926 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
2927 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002928
2929 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002930 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2931 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
2932 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2933 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002934
Felix Held9cf1dd22018-07-31 14:52:40 +02002935 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002936 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002937
Angel Pons88521882020-01-05 20:21:20 +01002938 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002939}
2940
2941int discover_timC_write(ramctr_timing *ctrl)
2942{
2943 const u8 rege3c_b24[3] = { 0, 0xf, 0x2f };
2944 int i, pat;
2945
2946 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2947 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2948 int channel, slotrank, lane;
2949
2950 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2951 lower[channel][slotrank][lane] = 0;
2952 upper[channel][slotrank][lane] = MAX_TIMC;
2953 }
2954
Angel Pons88521882020-01-05 20:21:20 +01002955 /*
2956 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2957 * FIXME: This must only be done on Ivy Bridge.
2958 */
2959 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002960 printram("discover timC write:\n");
2961
2962 for (i = 0; i < 3; i++)
2963 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002964 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000,
Felix Held2463aa92018-07-29 21:37:55 +02002965 rege3c_b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002966 udelay(2);
2967 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2968 FOR_ALL_POPULATED_RANKS {
2969 int timC;
2970 u32 raw_statistics[MAX_TIMC + 1];
2971 int statistics[MAX_TIMC + 1];
2972
2973 /* Make sure rn.start < rn.end */
2974 statistics[MAX_TIMC] = 1;
2975
2976 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002977 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002978 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002979 for (timC = 0; timC < MAX_TIMC; timC++) {
2980 FOR_ALL_LANES
2981 ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
2982 program_timings(ctrl, channel);
2983
2984 test_timC_write (ctrl, channel, slotrank);
2985
2986 raw_statistics[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002987 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002988 }
2989 FOR_ALL_LANES {
2990 struct run rn;
2991 for (timC = 0; timC < MAX_TIMC; timC++)
2992 statistics[timC] =
2993 !!(raw_statistics[timC] &
2994 (1 << lane));
2995
2996 rn = get_longest_zero_run(statistics,
2997 MAX_TIMC + 1);
2998 if (rn.all) {
2999 printk(BIOS_EMERG, "timC write discovery failed: %d, %d, %d\n",
3000 channel, slotrank, lane);
3001 return MAKE_ERR;
3002 }
3003 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
3004 channel, slotrank, i, rn.start,
3005 rn.middle, rn.end,
3006 rn.start + ctrl->timC_offset[i],
3007 rn.end - ctrl->timC_offset[i]);
3008 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003009 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003010 lower[channel][slotrank][lane]);
3011 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003012 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003013 upper[channel][slotrank][lane]);
3014
3015 }
3016 }
3017 }
3018 }
3019
3020 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003021 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003022 udelay(2);
3023 }
3024
Angel Pons88521882020-01-05 20:21:20 +01003025 /*
3026 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
3027 * FIXME: This must only be done on Ivy Bridge.
3028 */
3029 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003030
3031 printram("CPB\n");
3032
3033 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
3034 printram("timC %d, %d, %d: %x\n", channel,
3035 slotrank, lane,
3036 (lower[channel][slotrank][lane] +
3037 upper[channel][slotrank][lane]) / 2);
3038 ctrl->timings[channel][slotrank].lanes[lane].timC =
3039 (lower[channel][slotrank][lane] +
3040 upper[channel][slotrank][lane]) / 2;
3041 }
3042 FOR_ALL_POPULATED_CHANNELS {
3043 program_timings(ctrl, channel);
3044 }
3045 return 0;
3046}
3047
Angel Pons88521882020-01-05 20:21:20 +01003048void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003049{
3050 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01003051 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003052
3053 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
3054 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01003055 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003056 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003057 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01003058 printram("normalize %d, %d, %d: mat %d\n",
3059 channel, slotrank, lane, mat);
3060
Felix Heldef4fe3e2019-12-31 14:15:05 +01003061 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01003062 printram("normalize %d, %d, %d: delta %d\n",
3063 channel, slotrank, lane, delta);
3064
Angel Pons88521882020-01-05 20:21:20 +01003065 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01003066 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003067 }
3068
3069 FOR_ALL_POPULATED_CHANNELS {
3070 program_timings(ctrl, channel);
3071 }
3072}
3073
Angel Pons88521882020-01-05 20:21:20 +01003074void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003075{
3076 int channel, slotrank;
3077
3078 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Angel Pons1aba2a32020-01-05 22:31:41 +01003079 MCHBAR32(0x0004 + channel * 0x100 + lane_registers[slotrank]) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003080 make_mr0(ctrl, slotrank);
Angel Pons1aba2a32020-01-05 22:31:41 +01003081 MCHBAR32(0x0008 + channel * 0x100 + lane_registers[slotrank]) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003082 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003083 }
3084}
3085
3086int channel_test(ramctr_timing *ctrl)
3087{
3088 int channel, slotrank, lane;
3089
3090 slotrank = 0;
3091 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003092 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003093 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n",
3094 channel);
3095 return MAKE_ERR;
3096 }
3097 FOR_ALL_POPULATED_CHANNELS {
3098 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
3099
Angel Pons88521882020-01-05 20:21:20 +01003100 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003101 }
3102
3103 for (slotrank = 0; slotrank < 4; slotrank++)
3104 FOR_ALL_CHANNELS
3105 if (ctrl->rankmap[channel] & (1 << slotrank)) {
3106 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003107 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
3108 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003109 }
Angel Pons88521882020-01-05 20:21:20 +01003110 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02003111
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003112 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01003113 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0001f006;
3114 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x0028a004;
3115 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003116 0x00060000 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003117 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02003118
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003119 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01003120 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x0001f201;
3121 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x08281064;
3122 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003123 0x00000000 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003124 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003125
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003126 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01003127 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x0001f105;
3128 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x04281064;
3129 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003130 0x00000000 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003131 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003132
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003133 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01003134 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x0001f002;
3135 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x00280c01;
3136 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003137 0x00060400 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003138 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02003139
3140 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01003141 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02003142
Angel Pons88521882020-01-05 20:21:20 +01003143 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003144 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01003145 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003146 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
3147 channel, slotrank, lane);
3148 return MAKE_ERR;
3149 }
3150 }
3151 return 0;
3152}
3153
Angel Pons88521882020-01-05 20:21:20 +01003154void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003155{
3156 int channel;
3157
3158 /* FIXME: we hardcode seeds. Do we need to use some PRNG for them?
3159 I don't think so. */
3160 static u32 seeds[NUM_CHANNELS][3] = {
3161 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3162 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3163 };
3164 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003165 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
3166 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3167 MCHBAR32(SCRAMBLING_SEED_2_HIGH_ch(channel)) = seeds[channel][1];
3168 MCHBAR32(SCRAMBLING_SEED_2_LOW_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003169 }
3170}
3171
3172void set_4f8c(void)
3173{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003174 u32 cpu;
3175
Subrata Banik53b08c32018-12-10 14:11:35 +05303176 cpu = cpu_get_cpuid();
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003177 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons88521882020-01-05 20:21:20 +01003178 MCHBAR32(SC_WDBWM) = 0x141D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003179 } else {
Angel Pons88521882020-01-05 20:21:20 +01003180 MCHBAR32(SC_WDBWM) = 0x551D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003181 }
3182}
3183
Angel Pons88521882020-01-05 20:21:20 +01003184void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003185{
3186 int channel;
3187
3188 FOR_ALL_POPULATED_CHANNELS {
3189 // Always drive command bus
Angel Pons88521882020-01-05 20:21:20 +01003190 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003191 }
3192
3193 udelay(1);
3194
3195 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003196 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003197 }
3198}
3199
Angel Pons88521882020-01-05 20:21:20 +01003200void set_4008c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003201{
3202 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003203
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003204 FOR_ALL_POPULATED_CHANNELS {
3205 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003206 int min_pi = 10000;
3207 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208
3209 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003210 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3211 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003212 }
3213
Angel Pons88521882020-01-05 20:21:20 +01003214 if (max_pi - min_pi > 51)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003215 b20 = 0;
3216 else
3217 b20 = ctrl->ref_card_offset[channel];
3218
Angel Pons88521882020-01-05 20:21:20 +01003219 if (ctrl->pi_coding_threshold < max_pi - min_pi)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003220 b4_8_12 = 0x3330;
3221 else
3222 b4_8_12 = 0x2220;
3223
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003224 dram_odt_stretch(ctrl, channel);
3225
Angel Pons88521882020-01-05 20:21:20 +01003226 MCHBAR32(TC_RWP_ch(channel)) =
Felix Held2463aa92018-07-29 21:37:55 +02003227 0x0a000000 | (b20 << 20) |
3228 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003229 }
3230}
3231
Angel Pons88521882020-01-05 20:21:20 +01003232void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003233{
3234 int channel;
3235 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003236 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3237 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003238 }
3239}
3240
3241static int encode_5d10(int ns)
3242{
Angel Pons88521882020-01-05 20:21:20 +01003243 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003244}
3245
3246/* FIXME: values in this function should be hardware revision-dependent. */
Angel Pons88521882020-01-05 20:21:20 +01003247void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003248{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003249 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3250
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003251 int channel;
3252 int t1_cycles = 0, t1_ns = 0, t2_ns;
3253 int t3_ns;
3254 u32 r32;
3255
Angel Pons88521882020-01-05 20:21:20 +01003256 /* FIXME: This register only exists on Ivy Bridge. */
3257 MCHBAR32(WMM_READ_CONFIG) = 0x00000046;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003258
Felix Heldf9b826a2018-07-30 17:56:52 +02003259 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003260 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xFFFFCFFF, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003261
Patrick Rudolph74203de2017-11-20 11:57:01 +01003262 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003263 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003264 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003265 else
3266 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003267 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003268
Felix Heldf9b826a2018-07-30 17:56:52 +02003269 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003270 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003271
Angel Pons88521882020-01-05 20:21:20 +01003272 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3273 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003274
3275 FOR_ALL_CHANNELS {
3276 switch (ctrl->rankmap[channel]) {
3277 /* Unpopulated channel. */
3278 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003279 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003280 break;
3281 /* Only single-ranked dimms. */
3282 case 1:
3283 case 4:
3284 case 5:
Angel Pons88521882020-01-05 20:21:20 +01003285 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003286 break;
3287 /* Dual-ranked dimms present. */
3288 default:
Angel Pons88521882020-01-05 20:21:20 +01003289 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x9b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003290 break;
3291 }
3292 }
3293
Felix Held50b7ed22019-12-30 20:41:54 +01003294 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
3295 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0xffffff, 0xe4d5d0);
3296 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003297
3298 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003299 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~0x30000, 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003300
Angel Pons88521882020-01-05 20:21:20 +01003301 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3302 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3303 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003304
3305 /* Find a populated channel. */
3306 FOR_ALL_POPULATED_CHANNELS
3307 break;
3308
Angel Pons88521882020-01-05 20:21:20 +01003309 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3310 r32 = MCHBAR32(PM_DLL_CONFIG);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003311 if (r32 & 0x20000)
3312 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003313 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003314 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
3315 if (!(r32 & 0x20000))
3316 t1_ns += 500;
3317
Angel Pons88521882020-01-05 20:21:20 +01003318 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
3319 if (MCHBAR32(SAPMCTL) & 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003320 {
Angel Pons88521882020-01-05 20:21:20 +01003321 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
3322 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003323 }
3324 else
3325 {
3326 t3_ns = 500;
3327 }
3328 printk(BIOS_DEBUG, "t123: %d, %d, %d\n",
3329 t1_ns, t2_ns, t3_ns);
Felix Heldb802c072018-07-29 21:46:19 +02003330 MCHBAR32_AND_OR(0x5d10, 0xC0C0C0C0,
3331 ((encode_5d10(t1_ns) + encode_5d10(t2_ns)) << 16) |
Felix Held2bb3cdf2018-07-28 00:23:59 +02003332 (encode_5d10(t1_ns) << 8) | ((encode_5d10(t3_ns) +
Felix Heldb802c072018-07-29 21:46:19 +02003333 encode_5d10(t2_ns) + encode_5d10(t1_ns)) << 24) | 0xc);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003334}
3335
Angel Pons88521882020-01-05 20:21:20 +01003336void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003337{
3338 int channel, slotrank, lane;
3339
3340 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003341 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003342 ctrl->tRRD
3343 | (ctrl->tRTP << 4)
3344 | (ctrl->tCKE << 8)
3345 | (ctrl->tWTR << 12)
3346 | (ctrl->tFAW << 16)
3347 | (ctrl->tWR << 24)
3348 | (ctrl->cmd_stretch[channel] << 30);
3349
3350 udelay(1);
3351
3352 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003353 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003354 }
3355
3356 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003357 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003358 }
3359
3360 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003361 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003362
3363 FOR_ALL_POPULATED_CHANNELS {
3364 udelay (1);
Angel Pons88521882020-01-05 20:21:20 +01003365 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003366 }
3367
3368 printram("CPE\n");
3369
Angel Pons88521882020-01-05 20:21:20 +01003370 MCHBAR32(GDCRTRAININGMOD) = 0;
3371 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003372
3373 printram("CP5b\n");
3374
3375 FOR_ALL_POPULATED_CHANNELS {
3376 program_timings(ctrl, channel);
3377 }
3378
3379 u32 reg, addr;
3380
Angel Pons88521882020-01-05 20:21:20 +01003381 while (!(MCHBAR32(RCOMP_TIMER) & 0x10000));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003382 do {
Angel Pons88521882020-01-05 20:21:20 +01003383 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003384 } while ((reg & 0x14) == 0);
3385
3386 // Set state of memory controller
Angel Pons88521882020-01-05 20:21:20 +01003387 MCHBAR32(MC_INIT_STATE_G) = 0x116;
3388 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003389
3390 // Wait 500us
3391 udelay(500);
3392
3393 FOR_ALL_CHANNELS {
3394 // Set valid rank CKE
3395 reg = 0;
3396 reg = (reg & ~0xf) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003397 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003398 MCHBAR32(addr) = reg;
3399
3400 // Wait 10ns for ranks to settle
3401 //udelay(0.01);
3402
3403 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3404 MCHBAR32(addr) = reg;
3405
3406 // Write reset using a NOP
3407 write_reset(ctrl);
3408 }
3409
3410 /* mrs commands. */
3411 dram_mrscommands(ctrl);
3412
3413 printram("CP5c\n");
3414
Angel Pons88521882020-01-05 20:21:20 +01003415 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003416
3417 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003418 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003419 udelay(2);
3420 }
3421
Angel Pons88521882020-01-05 20:21:20 +01003422 /*
3423 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
3424 * FIXME: This must only be done on Ivy Bridge. Moreover, this instance seems to be
3425 * spurious, because nothing else enabled this optimization before.
3426 */
3427 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003428}