blob: c69c827136b534451282b9aedc834457cd971ee1 [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
Angel Ponsb31d1d72020-01-10 01:35:09 +0100500#define HOST_BRIDGE PCI_DEV(0, 0, 0)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100501#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
Angel Ponsb31d1d72020-01-10 01:35:09 +0100510 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
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
Angel Ponsb31d1d72020-01-10 01:35:09 +0100519 rev = pci_read_config8(HOST_BRIDGE, PCI_DEVICE_ID);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100520
521 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
522 /* read Capabilities A Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100523 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100524 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 */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100536 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_B);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100537 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
Angel Ponsb31d1d72020-01-10 01:35:09 +0100576 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
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)
Angel Ponsb31d1d72020-01-10 01:35:09 +0100646 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100650 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100651
Angel Ponsb31d1d72020-01-10 01:35:09 +0100652 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100656 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657
658 // TOLUD (top of low used dram)
Angel Ponsb31d1d72020-01-10 01:35:09 +0100659 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100663 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100664
665 // TOUUD LSB (top of upper usable dram)
Angel Ponsb31d1d72020-01-10 01:35:09 +0100666 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100670 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100671
672 // TOUUD MSB
Angel Ponsb31d1d72020-01-10 01:35:09 +0100673 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100677 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100678
679 if (reclaim) {
680 // REMAP BASE
Angel Ponsb31d1d72020-01-10 01:35:09 +0100681 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
682 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100683
684 // REMAP LIMIT
Angel Ponsb31d1d72020-01-10 01:35:09 +0100685 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
686 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100687 }
688 // TSEG
Angel Ponsb31d1d72020-01-10 01:35:09 +0100689 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100693 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100694
695 // GFX stolen memory
Angel Ponsb31d1d72020-01-10 01:35:09 +0100696 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100700 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100701
702 // GTT stolen memory
Angel Ponsb31d1d72020-01-10 01:35:09 +0100703 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100707 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100708
709 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100710 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100714 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100715
716 // ME base
Angel Ponsb31d1d72020-01-10 01:35:09 +0100717 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100721 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100722
Angel Ponsb31d1d72020-01-10 01:35:09 +0100723 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100727 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100728
729 // ME mask
Angel Ponsb31d1d72020-01-10 01:35:09 +0100730 reg = pci_read_config32(HOST_BRIDGE, 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);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100736 pci_write_config32(HOST_BRIDGE, 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
Felix Held3b906032020-01-14 17:05:43 +01001043static const u32 lane_base[] = {
1044 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
1045 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
1046 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001047};
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 {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001158 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001159 (((ctrl->timings[channel][slotrank].lanes[lane].
1160 timA + shift) & 0x3f)
1161 |
1162 ((ctrl->timings[channel][slotrank].lanes[lane].
1163 rising + shift) << 8)
1164 |
1165 (((ctrl->timings[channel][slotrank].lanes[lane].
1166 timA + shift -
1167 (post_timA_min_high << 6)) & 0x1c0) << 10)
1168 | ((ctrl->timings[channel][slotrank].lanes[lane].
1169 falling + shift) << 20));
1170
Felix Heldfb19c8a2020-01-14 21:27:59 +01001171 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001172 (((ctrl->timings[channel][slotrank].lanes[lane].
1173 timC + shift) & 0x3f)
1174 |
1175 (((ctrl->timings[channel][slotrank].lanes[lane].
1176 timB + shift) & 0x3f) << 8)
1177 |
1178 (((ctrl->timings[channel][slotrank].lanes[lane].
1179 timB + shift) & 0x1c0) << 9)
1180 |
1181 (((ctrl->timings[channel][slotrank].lanes[lane].
1182 timC + shift) & 0x40) << 13));
1183 }
1184 }
Angel Pons88521882020-01-05 20:21:20 +01001185 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1186 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001187}
1188
Angel Pons88521882020-01-05 20:21:20 +01001189static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001190{
Angel Pons88521882020-01-05 20:21:20 +01001191 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001192
1193 /* DRAM command MRS
1194 * write MR3 MPR enable
1195 * in this mode only RD and RDA are allowed
1196 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001197 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1198 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
1199 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
1200 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001201
1202 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001203 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1204 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4040c01;
1205 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
1206 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207
1208 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001209 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1210 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
1211 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
1212 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001213
1214 /* DRAM command MRS
1215 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001216 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1217 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
1218 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
1219 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001220
Felix Held9cf1dd22018-07-31 14:52:40 +02001221 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001222 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001223
Angel Pons88521882020-01-05 20:21:20 +01001224 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001225}
1226
Angel Pons88521882020-01-05 20:21:20 +01001227static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001228 int lane)
1229{
1230 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Felix Heldfb19c8a2020-01-14 21:27:59 +01001231 return ((MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >>
1232 (timA % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001233}
1234
1235struct run {
1236 int middle;
1237 int end;
1238 int start;
1239 int all;
1240 int length;
1241};
1242
1243static struct run get_longest_zero_run(int *seq, int sz)
1244{
1245 int i, ls;
1246 int bl = 0, bs = 0;
1247 struct run ret;
1248
1249 ls = 0;
1250 for (i = 0; i < 2 * sz; i++)
1251 if (seq[i % sz]) {
1252 if (i - ls > bl) {
1253 bl = i - ls;
1254 bs = ls;
1255 }
1256 ls = i + 1;
1257 }
1258 if (bl == 0) {
1259 ret.middle = sz / 2;
1260 ret.start = 0;
1261 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001262 ret.length = sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001263 ret.all = 1;
1264 return ret;
1265 }
1266
1267 ret.start = bs % sz;
1268 ret.end = (bs + bl - 1) % sz;
1269 ret.middle = (bs + (bl - 1) / 2) % sz;
1270 ret.length = bl;
1271 ret.all = 0;
1272
1273 return ret;
1274}
1275
Angel Pons88521882020-01-05 20:21:20 +01001276static void discover_timA_coarse(ramctr_timing *ctrl, int channel,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001277 int slotrank, int *upperA)
1278{
1279 int timA;
1280 int statistics[NUM_LANES][128];
1281 int lane;
1282
1283 for (timA = 0; timA < 128; timA++) {
1284 FOR_ALL_LANES {
1285 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1286 }
1287 program_timings(ctrl, channel);
1288
1289 test_timA(ctrl, channel, slotrank);
1290
1291 FOR_ALL_LANES {
1292 statistics[lane][timA] =
1293 !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001294 }
1295 }
1296 FOR_ALL_LANES {
1297 struct run rn = get_longest_zero_run(statistics[lane], 128);
1298 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1299 upperA[lane] = rn.end;
1300 if (upperA[lane] < rn.middle)
1301 upperA[lane] += 128;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001302 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001303 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001304 }
1305}
1306
Angel Pons88521882020-01-05 20:21:20 +01001307static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001308 int *upperA)
1309{
1310 int timA_delta;
1311 int statistics[NUM_LANES][51];
1312 int lane, i;
1313
1314 memset(statistics, 0, sizeof(statistics));
1315
1316 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
1317 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].
1318 timA = upperA[lane] + timA_delta + 0x40;
1319 program_timings(ctrl, channel);
1320
1321 for (i = 0; i < 100; i++) {
1322 test_timA(ctrl, channel, slotrank);
1323 FOR_ALL_LANES {
1324 statistics[lane][timA_delta + 25] +=
Felix Held2bb3cdf2018-07-28 00:23:59 +02001325 does_lane_work(ctrl, channel, slotrank,
1326 lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001327 }
1328 }
1329 }
1330 FOR_ALL_LANES {
1331 int last_zero, first_all;
1332
1333 for (last_zero = -25; last_zero <= 25; last_zero++)
1334 if (statistics[lane][last_zero + 25])
1335 break;
1336 last_zero--;
1337 for (first_all = -25; first_all <= 25; first_all++)
1338 if (statistics[lane][first_all + 25] == 100)
1339 break;
1340
1341 printram("lane %d: %d, %d\n", lane, last_zero,
1342 first_all);
1343
1344 ctrl->timings[channel][slotrank].lanes[lane].timA =
1345 (last_zero + first_all) / 2 + upperA[lane];
1346 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1347 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
1348 }
1349}
1350
Angel Pons891f2bc2020-01-10 01:27:28 +01001351static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001352{
1353 int works[NUM_LANES];
1354 int lane;
1355 while (1) {
1356 int all_works = 1, some_works = 0;
1357 program_timings(ctrl, channel);
1358 test_timA(ctrl, channel, slotrank);
1359 FOR_ALL_LANES {
1360 works[lane] =
1361 !does_lane_work(ctrl, channel, slotrank, lane);
1362 if (works[lane])
1363 some_works = 1;
1364 else
1365 all_works = 0;
1366 }
1367 if (all_works)
1368 return 0;
1369 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001370 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001371 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1372 channel, slotrank);
1373 return MAKE_ERR;
1374 }
Angel Pons88521882020-01-05 20:21:20 +01001375 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001376 printram("4024 -= 2;\n");
1377 continue;
1378 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001379 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001380 printram("4028 += 2;\n");
Felix Heldef4fe3e2019-12-31 14:15:05 +01001381 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001382 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1383 channel, slotrank);
1384 return MAKE_ERR;
1385 }
1386 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001387 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001388 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001389 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001390 }
1391 }
1392 return 0;
1393}
1394
1395struct timA_minmax {
1396 int timA_min_high, timA_max_high;
1397};
1398
Angel Pons88521882020-01-05 20:21:20 +01001399static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001400 struct timA_minmax *mnmx)
1401{
1402 int lane;
1403 mnmx->timA_min_high = 7;
1404 mnmx->timA_max_high = 0;
1405
1406 FOR_ALL_LANES {
1407 if (mnmx->timA_min_high >
1408 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1409 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001410 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001411 if (mnmx->timA_max_high <
1412 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1413 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001414 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001415 }
1416}
1417
Angel Pons88521882020-01-05 20:21:20 +01001418static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001419 struct timA_minmax *mnmx)
1420{
1421 struct timA_minmax post;
1422 int shift_402x = 0;
1423
Angel Pons891f2bc2020-01-10 01:27:28 +01001424 /* Get changed maxima. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001425 pre_timA_change(ctrl, channel, slotrank, &post);
1426
1427 if (mnmx->timA_max_high - mnmx->timA_min_high <
1428 post.timA_max_high - post.timA_min_high)
1429 shift_402x = +1;
1430 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1431 post.timA_max_high - post.timA_min_high)
1432 shift_402x = -1;
1433 else
1434 shift_402x = 0;
1435
Felix Heldef4fe3e2019-12-31 14:15:05 +01001436 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001437 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001438 printram("4024 += %d;\n", shift_402x);
1439 printram("4028 += %d;\n", shift_402x);
1440}
1441
1442/* Compensate the skew between DQS and DQs.
Angel Pons891f2bc2020-01-10 01:27:28 +01001443 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1444 * The controller has to measure and compensate this skew for every byte-lane. By delaying
1445 * either all DQs signals or DQS signal, a full phase shift can be introduced. It is assumed
1446 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001447 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001448 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1449 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1450 * over all possible values to do a full phase shift and issues read commands. With DQS and
1451 * DQs in phase the data read is expected to alternate on every byte:
1452 * 0xFF 0x00 0xFF ...
1453 * Once the controller has detected this pattern a bit in the result register is set for the
1454 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001455 */
Angel Pons88521882020-01-05 20:21:20 +01001456int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001457{
1458 int channel, slotrank, lane;
1459 int err;
1460
1461 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1462 int all_high, some_high;
1463 int upperA[NUM_LANES];
1464 struct timA_minmax mnmx;
1465
Angel Pons88521882020-01-05 20:21:20 +01001466 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001467
Felix Held2bb3cdf2018-07-28 00:23:59 +02001468 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001469 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1470 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1471 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1472 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001473
1474 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001475 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001476
Angel Pons88521882020-01-05 20:21:20 +01001477 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001478
Felix Heldef4fe3e2019-12-31 14:15:05 +01001479 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001480 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001481 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001482
Felix Held2bb3cdf2018-07-28 00:23:59 +02001483 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001484
Felix Held2bb3cdf2018-07-28 00:23:59 +02001485 all_high = 1;
1486 some_high = 0;
1487 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001488 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001489 some_high = 1;
1490 else
1491 all_high = 0;
1492 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001493
1494 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001495 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001496 printram("4028--;\n");
1497 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001498 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001499 upperA[lane] -= 0x40;
1500
1501 }
1502 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001503 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001504 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001505 printram("4024++;\n");
1506 printram("4028++;\n");
1507 }
1508
1509 program_timings(ctrl, channel);
1510
1511 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1512
1513 err = discover_402x(ctrl, channel, slotrank, upperA);
1514 if (err)
1515 return err;
1516
1517 post_timA_change(ctrl, channel, slotrank, &mnmx);
1518 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1519
1520 discover_timA_fine(ctrl, channel, slotrank, upperA);
1521
1522 post_timA_change(ctrl, channel, slotrank, &mnmx);
1523 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1524
1525 FOR_ALL_LANES {
1526 ctrl->timings[channel][slotrank].lanes[lane].timA -= mnmx.timA_min_high * 0x40;
1527 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001528 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001529 printram("4028 -= %d;\n", mnmx.timA_min_high);
1530
1531 post_timA_change(ctrl, channel, slotrank, &mnmx);
1532
1533 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001534 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001535 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001536
1537 printram("final results:\n");
1538 FOR_ALL_LANES
Felix Held2bb3cdf2018-07-28 00:23:59 +02001539 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1540 lane,
1541 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001542
Angel Pons88521882020-01-05 20:21:20 +01001543 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001544
1545 toggle_io_reset();
1546 }
1547
1548 FOR_ALL_POPULATED_CHANNELS {
1549 program_timings(ctrl, channel);
1550 }
1551 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001552 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001553 }
1554 return 0;
1555}
1556
Angel Pons88521882020-01-05 20:21:20 +01001557static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001558{
1559 int lane;
1560
1561 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001562 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1563 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001564 }
1565
Angel Pons88521882020-01-05 20:21:20 +01001566 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001567
1568 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001569 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1570 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001571 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001572 | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001573 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
1574 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001575
1576 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001577 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1578 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8041001;
1579 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
1580 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001581
1582 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01001583 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
1584 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x80411f4;
1585 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
1586 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001587
1588 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001589 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
1590 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001591 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001592 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
1593 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001594
Felix Held9cf1dd22018-07-31 14:52:40 +02001595 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001596 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001597
Angel Pons88521882020-01-05 20:21:20 +01001598 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001599
1600 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001601 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1602 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1603 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1604 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001605
1606 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001607 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
1608 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001609 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001610 | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001611 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
1612 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001613
1614 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001615 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1616 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001617 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001618 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
1619 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001620
1621 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001622 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
1623 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
1624 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
1625 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001626
1627 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001628 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001629
Angel Pons88521882020-01-05 20:21:20 +01001630 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001631}
1632
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001633static void timC_threshold_process(int *data, const int count)
1634{
1635 int min = data[0];
1636 int max = min;
1637 int i;
1638 for (i = 1; i < count; i++) {
1639 if (min > data[i])
1640 min = data[i];
1641 if (max < data[i])
1642 max = data[i];
1643 }
1644 int threshold = min/2 + max/2;
1645 for (i = 0; i < count; i++)
1646 data[i] = data[i] > threshold;
Angel Pons891f2bc2020-01-10 01:27:28 +01001647 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001648}
1649
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001650static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1651{
1652 int timC;
1653 int statistics[NUM_LANES][MAX_TIMC + 1];
1654 int lane;
1655
Angel Pons88521882020-01-05 20:21:20 +01001656 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001657
1658 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001659 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1660 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1661 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1662 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001663
1664 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001665 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001666
1667 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001668 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001669 program_timings(ctrl, channel);
1670
1671 test_timC(ctrl, channel, slotrank);
1672
1673 FOR_ALL_LANES {
1674 statistics[lane][timC] =
Angel Pons88521882020-01-05 20:21:20 +01001675 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001676 }
1677 }
1678 FOR_ALL_LANES {
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001679 struct run rn = get_longest_zero_run(
1680 statistics[lane], ARRAY_SIZE(statistics[lane]));
1681 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001682 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1683 channel, slotrank, lane);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001684 /* With command training not happend yet, the lane can
1685 * be erroneous. Take the avarage as reference and try
1686 * again to find a run.
1687 */
1688 timC_threshold_process(statistics[lane],
1689 ARRAY_SIZE(statistics[lane]));
1690 rn = get_longest_zero_run(statistics[lane],
1691 ARRAY_SIZE(statistics[lane]));
1692 if (rn.all || rn.length < 8) {
1693 printk(BIOS_EMERG, "timC recovery failed\n");
1694 return MAKE_ERR;
1695 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001696 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001697 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001698 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001699 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001700 }
1701 return 0;
1702}
1703
Angel Pons88521882020-01-05 20:21:20 +01001704static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001705{
1706 int channel, ret = 0;
1707 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1708 ret++;
1709 return ret;
1710}
1711
Angel Pons88521882020-01-05 20:21:20 +01001712static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001713{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301714 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001715 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001716 for (j = 0; j < 16; j++)
1717 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
1718 sfence();
1719}
1720
Angel Pons88521882020-01-05 20:21:20 +01001721static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001722{
1723 int ret = 0;
1724 int channel;
1725 FOR_ALL_POPULATED_CHANNELS ret++;
1726 return ret;
1727}
1728
Angel Pons88521882020-01-05 20:21:20 +01001729static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001730{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301731 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001732 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301733 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001734 for (j = 0; j < 16; j++)
1735 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
1736 for (j = 0; j < 16; j++)
1737 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
1738 sfence();
1739}
1740
Angel Pons88521882020-01-05 20:21:20 +01001741static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001742{
1743 int channel, slotrank, lane;
1744
1745 FOR_ALL_POPULATED_CHANNELS {
1746 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001747 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1748 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001749 }
1750
1751 program_timings(ctrl, channel);
1752
1753 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001754 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001755
1756 /* DRAM command MRS
1757 * write MR3 MPR enable
1758 * in this mode only RD and RDA are allowed
1759 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001760 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1761 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001762 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001763 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001764 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001765 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001766
1767 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001768 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1769 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001770 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01001771 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001772
1773 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001774 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1775 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001776 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001777 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001778 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001779 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001780
1781 /* DRAM command MRS
1782 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001783 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1784 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001785 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001786 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001787 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001788 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001789
1790 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001791 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001792
Angel Pons88521882020-01-05 20:21:20 +01001793 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001794 }
1795
1796 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001797 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1798 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001799 }
1800
1801 program_timings(ctrl, channel);
1802
1803 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001804 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001805 /* DRAM command MRS
1806 * write MR3 MPR enable
1807 * in this mode only RD and RDA are allowed
1808 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001809 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1810 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001811 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001812 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001813 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001814 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001815
1816 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001817 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1818 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001819 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01001820 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001821
1822 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001823 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1824 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001825 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001826 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001827 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001828 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001829
1830 /* DRAM command MRS
1831 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001832 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1833 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001834 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001835 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001836 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001837 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001838
Felix Held9cf1dd22018-07-31 14:52:40 +02001839 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001840 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001841
Angel Pons88521882020-01-05 20:21:20 +01001842 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001843 }
1844 }
1845}
1846
Angel Pons88521882020-01-05 20:21:20 +01001847static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001848{
1849 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001850 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001851
Angel Pons88521882020-01-05 20:21:20 +01001852 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001853 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001854 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f207;
1855 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001856 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001857 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
1858 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001859
1860 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001861 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f107;
Angel Pons891f2bc2020-01-10 01:27:28 +01001862 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001863 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
1864 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001865
Felix Held9cf1dd22018-07-31 14:52:40 +02001866 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001867 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001868
Angel Pons88521882020-01-05 20:21:20 +01001869 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001870
1871 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001872 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001873}
1874
1875static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1876{
1877 int timB;
1878 int statistics[NUM_LANES][128];
1879 int lane;
1880
Angel Pons88521882020-01-05 20:21:20 +01001881 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001882
1883 for (timB = 0; timB < 128; timB++) {
1884 FOR_ALL_LANES {
1885 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1886 }
1887 program_timings(ctrl, channel);
1888
1889 test_timB(ctrl, channel, slotrank);
1890
1891 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001892 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1893 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1894 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001895 }
1896 }
1897 FOR_ALL_LANES {
1898 struct run rn = get_longest_zero_run(statistics[lane], 128);
1899 /* timC is a direct function of timB's 6 LSBs.
1900 * Some tests increments the value of timB by a small value,
1901 * which might cause the 6bit value to overflow, if it's close
1902 * to 0x3F. Increment the value by a small offset if it's likely
1903 * to overflow, to make sure it won't overflow while running
1904 * tests and bricks the system due to a non matching timC.
1905 *
1906 * TODO: find out why some tests (edge write discovery)
1907 * increment timB. */
1908 if ((rn.start & 0x3F) == 0x3E)
1909 rn.start += 2;
1910 else if ((rn.start & 0x3F) == 0x3F)
1911 rn.start += 1;
1912 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1913 if (rn.all) {
1914 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1915 channel, slotrank, lane);
1916 return MAKE_ERR;
1917 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001918 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1919 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001920 }
1921 return 0;
1922}
1923
1924static int get_timB_high_adjust(u64 val)
1925{
1926 int i;
1927
1928 /* good */
1929 if (val == 0xffffffffffffffffLL)
1930 return 0;
1931
1932 if (val >= 0xf000000000000000LL) {
1933 /* needs negative adjustment */
1934 for (i = 0; i < 8; i++)
1935 if (val << (8 * (7 - i) + 4))
1936 return -i;
1937 } else {
1938 /* needs positive adjustment */
1939 for (i = 0; i < 8; i++)
1940 if (val >> (8 * (7 - i) + 4))
1941 return i;
1942 }
1943 return 8;
1944}
1945
Angel Pons88521882020-01-05 20:21:20 +01001946static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001947{
1948 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001949 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001950 FOR_ALL_POPULATED_CHANNELS {
1951 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001952 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001953 }
1954 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1955
Angel Pons88521882020-01-05 20:21:20 +01001956 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001957
Angel Pons88521882020-01-05 20:21:20 +01001958 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001959
1960 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001961 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1962 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
1963 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
1964 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001965
1966 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001967 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1968 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8040c01;
1969 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
1970 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001971
1972 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01001973 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
1974 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x8041003;
1975 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
1976 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001977
1978 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001979 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
1980 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001981 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001982 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
1983 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001984
Felix Held9cf1dd22018-07-31 14:52:40 +02001985 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001986 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001987
Angel Pons88521882020-01-05 20:21:20 +01001988 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001989
1990 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001991 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
Angel Pons891f2bc2020-01-10 01:27:28 +01001992 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
1993 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01001994 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001995
1996 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001997 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
Angel Pons891f2bc2020-01-10 01:27:28 +01001998 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001999 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
2000 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002001
2002 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002003 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x3f105;
2004 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
2005 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01002006 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002007 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
2008 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002009
Felix Held9cf1dd22018-07-31 14:52:40 +02002010 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002011 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02002012
Angel Pons88521882020-01-05 20:21:20 +01002013 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002014 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002015 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01002016 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01002017 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002018 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
2019 ctrl->timings[channel][slotrank].lanes[lane].timB +=
2020 get_timB_high_adjust(res) * 64;
2021
2022 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01002023 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
2024 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002025 }
2026 }
Angel Pons88521882020-01-05 20:21:20 +01002027 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002028}
2029
Angel Pons88521882020-01-05 20:21:20 +01002030static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002031{
2032 int slotrank;
2033
Angel Pons88521882020-01-05 20:21:20 +01002034 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002035
2036 /* choose an existing rank. */
2037 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2038
2039 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002040 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2041 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2042 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2043 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002044
Felix Held9cf1dd22018-07-31 14:52:40 +02002045 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002046 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002047
Angel Pons88521882020-01-05 20:21:20 +01002048 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002049}
2050
2051/* Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
2052 * DDR3 adopted the fly-by topology. The data and strobes signals reach
2053 * the chips at different times with respect to command, address and
2054 * clock signals.
2055 * By delaying either all DQ/DQs or all CMD/ADDR/CLK signals, a full phase
2056 * shift can be introduced.
2057 * It is assumed that the CLK/ADDR/CMD signals have the same routing delay.
2058 *
2059 * To find the required phase shift the DRAM is placed in "write leveling" mode.
2060 * In this mode the DRAM-chip samples the CLK on every DQS edge and feeds back the
2061 * sampled value on the data lanes (DQs).
2062 */
Angel Pons88521882020-01-05 20:21:20 +01002063int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002064{
2065 int channel, slotrank, lane;
2066 int err;
2067
2068 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002069 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002070
2071 FOR_ALL_POPULATED_CHANNELS {
2072 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01002073 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002074 }
2075
2076 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002077 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002078 FOR_ALL_POPULATED_CHANNELS {
2079 write_op(ctrl, channel);
2080 }
2081
2082 /* enable write leveling on all ranks
2083 * disable all DQ outputs
2084 * only NOP is allowed in this mode */
2085 FOR_ALL_CHANNELS
Felix Held2bb3cdf2018-07-28 00:23:59 +02002086 FOR_ALL_POPULATED_RANKS
2087 write_mrreg(ctrl, channel, slotrank, 1,
2088 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002089
Angel Pons88521882020-01-05 20:21:20 +01002090 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002091
2092 toggle_io_reset();
2093
2094 /* set any valid value for timB, it gets corrected later */
2095 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2096 err = discover_timB(ctrl, channel, slotrank);
2097 if (err)
2098 return err;
2099 }
2100
2101 /* disable write leveling on all ranks */
2102 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
2103 write_mrreg(ctrl, channel,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002104 slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002105
Angel Pons88521882020-01-05 20:21:20 +01002106 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002107
2108 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002109 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002110
2111 /* refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01002112 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002113
2114 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002115 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
2116 MCHBAR32(IOSAV_STATUS_ch(channel));
2117 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002118
2119 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002120 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2121 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x659001;
2122 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
2123 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002124
Felix Held9cf1dd22018-07-31 14:52:40 +02002125 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002126 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002127
Angel Pons88521882020-01-05 20:21:20 +01002128 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002129 }
2130
2131 toggle_io_reset();
2132
2133 printram("CPE\n");
2134 precharge(ctrl);
2135 printram("CPF\n");
2136
2137 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002138 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002139 }
2140
2141 FOR_ALL_POPULATED_CHANNELS {
2142 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002143 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002144 }
2145
2146 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2147 err = discover_timC(ctrl, channel, slotrank);
2148 if (err)
2149 return err;
2150 }
2151
2152 FOR_ALL_POPULATED_CHANNELS
2153 program_timings(ctrl, channel);
2154
2155 /* measure and adjust timB timings */
2156 adjust_high_timB(ctrl);
2157
2158 FOR_ALL_POPULATED_CHANNELS
2159 program_timings(ctrl, channel);
2160
2161 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002162 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002163 }
2164 return 0;
2165}
2166
Angel Pons88521882020-01-05 20:21:20 +01002167static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002168{
2169 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2170 int timC_delta;
2171 int lanes_ok = 0;
2172 int ctr = 0;
2173 int lane;
2174
2175 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2176 FOR_ALL_LANES {
2177 ctrl->timings[channel][slotrank].lanes[lane].timC =
2178 saved_rt.lanes[lane].timC + timC_delta;
2179 }
2180 program_timings(ctrl, channel);
2181 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002182 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002183 }
2184
Angel Pons88521882020-01-05 20:21:20 +01002185 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002186
Angel Pons88521882020-01-05 20:21:20 +01002187 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002188 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002189 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2190 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002191 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002192 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002193 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002194 (slotrank << 24) | ctr | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002195 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002196
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002197 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002198 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2199 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002200 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002201 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2202 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
2203 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002204
2205 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002206 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2207 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002208 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002209 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2210 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
2211 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002212
2213 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002214 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2215 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xf1001;
2216 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2217 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002218
Felix Held9cf1dd22018-07-31 14:52:40 +02002219 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002220 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002221
Angel Pons88521882020-01-05 20:21:20 +01002222 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002223 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002224 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002225
2226 if (r32 == 0)
2227 lanes_ok |= 1 << lane;
2228 }
2229 ctr++;
2230 if (lanes_ok == ((1 << NUM_LANES) - 1))
2231 break;
2232 }
2233
2234 ctrl->timings[channel][slotrank] = saved_rt;
2235
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002236 return lanes_ok != ((1 << NUM_LANES) - 1);
2237}
2238
2239#include "raminit_patterns.h"
2240
Angel Pons88521882020-01-05 20:21:20 +01002241static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002242{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302243 unsigned int i, j;
Angel Pons891f2bc2020-01-10 01:27:28 +01002244 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05302245 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002246
2247 if (patno) {
2248 u8 base8 = 0x80 >> ((patno - 1) % 8);
2249 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2250 for (i = 0; i < 32; i++) {
2251 for (j = 0; j < 16; j++) {
2252 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
2253 if (invert[patno - 1][i] & (1 << (j / 2)))
2254 val = ~val;
2255 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2256 j * 4), val);
2257 }
2258 }
2259
2260 } else {
2261 for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) {
2262 for (j = 0; j < 16; j++)
2263 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2264 j * 4), pattern[i][j]);
2265 }
2266 sfence();
2267 }
2268}
2269
Angel Pons88521882020-01-05 20:21:20 +01002270static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002271{
2272 int channel, slotrank;
2273
2274 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002275 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002276
2277 /* choose an existing rank. */
2278 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2279
2280 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002281 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2282 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2283 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2284 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002285
Felix Held9cf1dd22018-07-31 14:52:40 +02002286 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002287 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002288
Angel Pons88521882020-01-05 20:21:20 +01002289 wait_for_iosav(channel);
2290 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002291 }
2292
2293 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002294 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002295 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002296 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002297
2298 /* choose an existing rank. */
2299 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2300
2301 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002302 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2303 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2304 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2305 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002306
Felix Held9cf1dd22018-07-31 14:52:40 +02002307 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002308 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002309
Angel Pons88521882020-01-05 20:21:20 +01002310 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002311 }
2312
2313 /* jedec reset */
2314 dram_jedecreset(ctrl);
2315 /* mrs commands. */
2316 dram_mrscommands(ctrl);
2317
2318 toggle_io_reset();
2319}
2320
2321#define MIN_C320C_LEN 13
2322
2323static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2324{
2325 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2326 int slotrank;
2327 int c320c;
2328 int stat[NUM_SLOTRANKS][256];
2329 int delta = 0;
2330
2331 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2332
2333 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002334 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002335 }
2336
2337 ctrl->cmd_stretch[channel] = cmd_stretch;
2338
Angel Pons88521882020-01-05 20:21:20 +01002339 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002340 ctrl->tRRD
2341 | (ctrl->tRTP << 4)
2342 | (ctrl->tCKE << 8)
2343 | (ctrl->tWTR << 12)
2344 | (ctrl->tFAW << 16)
2345 | (ctrl->tWR << 24)
2346 | (ctrl->cmd_stretch[channel] << 30);
2347
2348 if (ctrl->cmd_stretch[channel] == 2)
2349 delta = 2;
2350 else if (ctrl->cmd_stretch[channel] == 0)
2351 delta = 4;
2352
2353 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002354 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002355 }
2356
2357 for (c320c = -127; c320c <= 127; c320c++) {
2358 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002359 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002360 }
2361 program_timings(ctrl, channel);
2362 reprogram_320c(ctrl);
2363 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002364 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002365 }
2366 }
2367 FOR_ALL_POPULATED_RANKS {
2368 struct run rn =
2369 get_longest_zero_run(stat[slotrank], 255);
Angel Pons88521882020-01-05 20:21:20 +01002370 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002371 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2372 channel, slotrank, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002373 if (rn.all || rn.length < MIN_C320C_LEN) {
2374 FOR_ALL_POPULATED_RANKS {
2375 ctrl->timings[channel][slotrank] =
2376 saved_timings[channel][slotrank];
2377 }
2378 return MAKE_ERR;
2379 }
2380 }
2381
2382 return 0;
2383}
2384
2385/* Adjust CMD phase shift and try multiple command rates.
2386 * A command rate of 2T doubles the time needed for address and
2387 * command decode. */
2388int command_training(ramctr_timing *ctrl)
2389{
2390 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002391
2392 FOR_ALL_POPULATED_CHANNELS {
2393 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002394 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002395 }
2396
2397 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002398 int cmdrate, err;
2399
2400 /*
2401 * Dual DIMM per channel:
2402 * Issue: While c320c discovery seems to succeed raminit
2403 * will fail in write training.
2404 * Workaround: Skip 1T in dual DIMM mode, that's only
2405 * supported by a few DIMMs.
Dan Elkoubydabebc32018-04-13 18:47:10 +03002406 * Only try 1T mode for XMP DIMMs that request it in dual DIMM
2407 * mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002408 *
2409 * Single DIMM per channel:
2410 * Try command rate 1T and 2T
2411 */
2412 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002413 if (ctrl->tCMD)
2414 /* XMP gives the CMD rate in clock ticks, not ns */
2415 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002416
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002417 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002418 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2419
2420 if (!err)
2421 break;
2422 }
2423
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002424 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002425 printk(BIOS_EMERG, "c320c discovery failed\n");
2426 return err;
2427 }
2428
Angel Pons891f2bc2020-01-10 01:27:28 +01002429 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002430 }
2431
2432 FOR_ALL_POPULATED_CHANNELS
2433 program_timings(ctrl, channel);
2434
2435 reprogram_320c(ctrl);
2436 return 0;
2437}
2438
2439
Angel Pons891f2bc2020-01-10 01:27:28 +01002440static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002441{
2442 int edge;
2443 int statistics[NUM_LANES][MAX_EDGE_TIMING + 1];
2444 int lane;
2445
2446 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2447 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002448 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
2449 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002450 }
2451 program_timings(ctrl, channel);
2452
2453 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002454 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2455 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002456 }
2457
Angel Pons88521882020-01-05 20:21:20 +01002458 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002459 /* DRAM command MRS
2460 * write MR3 MPR enable
2461 * in this mode only RD and RDA are allowed
2462 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002463 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2464 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002465 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002466 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002467
2468 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002469 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2470 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x40411f4;
2471 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2472 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002473
2474 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002475 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
Angel Pons891f2bc2020-01-10 01:27:28 +01002476 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002477 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
2478 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002479
2480 /* DRAM command MRS
2481 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002482 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2483 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002484 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002485 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002486
Felix Held9cf1dd22018-07-31 14:52:40 +02002487 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002488 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002489
Angel Pons88521882020-01-05 20:21:20 +01002490 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002491
2492 FOR_ALL_LANES {
2493 statistics[lane][edge] =
Angel Pons88521882020-01-05 20:21:20 +01002494 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002495 }
2496 }
2497 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002498 struct run rn = get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002499 edges[lane] = rn.middle;
2500 if (rn.all) {
2501 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n",
2502 channel, slotrank, lane);
2503 return MAKE_ERR;
2504 }
2505 printram("eval %d, %d, %d: %02x\n", channel, slotrank,
2506 lane, edges[lane]);
2507 }
2508 return 0;
2509}
2510
2511int discover_edges(ramctr_timing *ctrl)
2512{
2513 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2514 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2515 int channel, slotrank, lane;
2516 int err;
2517
Angel Pons88521882020-01-05 20:21:20 +01002518 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002519
2520 toggle_io_reset();
2521
2522 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002523 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002524 }
2525
2526 FOR_ALL_POPULATED_CHANNELS {
2527 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002528 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002529 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002530 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002531 }
2532
2533 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002534 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2535 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002536 }
2537
2538 program_timings(ctrl, channel);
2539
2540 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002541 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002542
2543 /* DRAM command MRS
2544 * MR3 enable MPR
2545 * write MR3 MPR enable
2546 * in this mode only RD and RDA are allowed
2547 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002548 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2549 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002550 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002551 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002552 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002553 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002554
2555 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002556 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2557 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002558 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002559 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002560
2561 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002562 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2563 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002564 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002565 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002566 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002567 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002568
2569 /* DRAM command MRS
2570 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002571 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2572 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002573 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002574 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002575 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002576 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002577
2578 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002579 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002580
Angel Pons88521882020-01-05 20:21:20 +01002581 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002582 }
2583
2584 /* XXX: check any measured value ? */
2585
2586 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002587 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
2588 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002589 }
2590
2591 program_timings(ctrl, channel);
2592
2593 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002594 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002595
2596 /* DRAM command MRS
2597 * MR3 enable MPR
2598 * write MR3 MPR enable
2599 * in this mode only RD and RDA are allowed
2600 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002601 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2602 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002603 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002604 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002605 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002606 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002607
2608 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002609 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2610 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
2611 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002612 (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01002613 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002614
2615 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002616 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2617 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002618 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002619 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002620 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002621 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002622
2623 /* DRAM command MRS
2624 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002625 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2626 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002627 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002628 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002629 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002630 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002631
Felix Held9cf1dd22018-07-31 14:52:40 +02002632 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002633 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002634
Angel Pons88521882020-01-05 20:21:20 +01002635 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002636 }
2637
2638 /* XXX: check any measured value ? */
2639
2640 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002641 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002642 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002643 }
2644
2645 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002646 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002647 }
2648
2649 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002650 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2651 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002652
2653 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2654 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002655 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002656 if (err)
2657 return err;
2658 }
2659
Angel Pons88521882020-01-05 20:21:20 +01002660 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2661 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002662
2663 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2664 err = discover_edges_real(ctrl, channel, slotrank,
2665 rising_edges[channel][slotrank]);
2666 if (err)
2667 return err;
2668 }
2669
Angel Pons88521882020-01-05 20:21:20 +01002670 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002671
2672 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2673 ctrl->timings[channel][slotrank].lanes[lane].falling =
2674 falling_edges[channel][slotrank][lane];
2675 ctrl->timings[channel][slotrank].lanes[lane].rising =
2676 rising_edges[channel][slotrank][lane];
2677 }
2678
2679 FOR_ALL_POPULATED_CHANNELS {
2680 program_timings(ctrl, channel);
2681 }
2682
2683 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002684 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002685 }
2686 return 0;
2687}
2688
2689static int discover_edges_write_real(ramctr_timing *ctrl, int channel,
2690 int slotrank, int *edges)
2691{
2692 int edge;
2693 u32 raw_statistics[MAX_EDGE_TIMING + 1];
2694 int statistics[MAX_EDGE_TIMING + 1];
2695 const int reg3000b24[] = { 0, 0xc, 0x2c };
2696 int lane, i;
2697 int lower[NUM_LANES];
2698 int upper[NUM_LANES];
2699 int pat;
2700
2701 FOR_ALL_LANES {
2702 lower[lane] = 0;
2703 upper[lane] = MAX_EDGE_TIMING;
2704 }
2705
2706 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002707 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002708 printram("[%x] = 0x%08x\n",
Angel Pons88521882020-01-05 20:21:20 +01002709 GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002710 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2711 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002712 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002713 printram("using pattern %d\n", pat);
2714 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2715 FOR_ALL_LANES {
2716 ctrl->timings[channel][slotrank].lanes[lane].
2717 rising = edge;
2718 ctrl->timings[channel][slotrank].lanes[lane].
2719 falling = edge;
2720 }
2721 program_timings(ctrl, channel);
2722
2723 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002724 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2725 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002726 }
Angel Pons88521882020-01-05 20:21:20 +01002727 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002728
2729 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002730 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2731 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002732 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002733 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002734 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002735 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002736 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002737
2738 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002739 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2740 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002741 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002742 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002743 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002744 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002745
2746 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002747 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2748 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002749 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002750 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002751 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002752 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002753
2754 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002755 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2756 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002757 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002758 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002759 (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01002760 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002761
Felix Held9cf1dd22018-07-31 14:52:40 +02002762 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002763 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) =
2764 IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002765
Angel Pons88521882020-01-05 20:21:20 +01002766 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002767 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002768 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002769 }
2770
Angel Pons891f2bc2020-01-10 01:27:28 +01002771 raw_statistics[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002772 }
2773 FOR_ALL_LANES {
2774 struct run rn;
2775 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
2776 statistics[edge] =
2777 ! !(raw_statistics[edge] & (1 << lane));
2778 rn = get_longest_zero_run(statistics,
2779 MAX_EDGE_TIMING + 1);
2780 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
2781 channel, slotrank, i, rn.start, rn.middle,
2782 rn.end, rn.start + ctrl->edge_offset[i],
2783 rn.end - ctrl->edge_offset[i]);
2784 lower[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002785 MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786 upper[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002787 MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002788 edges[lane] = (lower[lane] + upper[lane]) / 2;
2789 if (rn.all || (lower[lane] > upper[lane])) {
2790 printk(BIOS_EMERG, "edge write discovery failed: %d, %d, %d\n",
2791 channel, slotrank, lane);
2792 return MAKE_ERR;
2793 }
2794 }
2795 }
2796 }
2797
Angel Pons88521882020-01-05 20:21:20 +01002798 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002799 printram("CPA\n");
2800 return 0;
2801}
2802
2803int discover_edges_write(ramctr_timing *ctrl)
2804{
2805 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2806 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2807 int channel, slotrank, lane;
2808 int err;
2809
2810 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002811 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2812 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002813
2814 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2815 err = discover_edges_write_real(ctrl, channel, slotrank,
2816 falling_edges[channel][slotrank]);
2817 if (err)
2818 return err;
2819 }
2820
Angel Pons88521882020-01-05 20:21:20 +01002821 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2822 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002823
2824 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2825 err = discover_edges_write_real(ctrl, channel, slotrank,
2826 rising_edges[channel][slotrank]);
2827 if (err)
2828 return err;
2829 }
2830
Angel Pons88521882020-01-05 20:21:20 +01002831 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002832
2833 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2834 ctrl->timings[channel][slotrank].lanes[lane].falling =
2835 falling_edges[channel][slotrank][lane];
2836 ctrl->timings[channel][slotrank].lanes[lane].rising =
2837 rising_edges[channel][slotrank][lane];
2838 }
2839
2840 FOR_ALL_POPULATED_CHANNELS
2841 program_timings(ctrl, channel);
2842
2843 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002844 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002845 }
2846 return 0;
2847}
2848
2849static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2850{
Angel Pons88521882020-01-05 20:21:20 +01002851 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002852 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002853 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2854 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002855 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons88521882020-01-05 20:21:20 +01002856 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002857 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002858 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002859
2860 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002861 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2862 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002863 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002864 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2865 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002866
2867 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002868 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
Angel Pons891f2bc2020-01-10 01:27:28 +01002869 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002870 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
2871 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002872
2873 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002874 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2875 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
2876 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2877 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002878
Felix Held9cf1dd22018-07-31 14:52:40 +02002879 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002880 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002881
Angel Pons88521882020-01-05 20:21:20 +01002882 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002883}
2884
2885int discover_timC_write(ramctr_timing *ctrl)
2886{
2887 const u8 rege3c_b24[3] = { 0, 0xf, 0x2f };
2888 int i, pat;
2889
2890 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2891 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2892 int channel, slotrank, lane;
2893
2894 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2895 lower[channel][slotrank][lane] = 0;
2896 upper[channel][slotrank][lane] = MAX_TIMC;
2897 }
2898
Angel Pons88521882020-01-05 20:21:20 +01002899 /*
2900 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2901 * FIXME: This must only be done on Ivy Bridge.
2902 */
2903 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002904 printram("discover timC write:\n");
2905
2906 for (i = 0; i < 3; i++)
2907 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002908 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000,
Felix Held2463aa92018-07-29 21:37:55 +02002909 rege3c_b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002910 udelay(2);
2911 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2912 FOR_ALL_POPULATED_RANKS {
2913 int timC;
2914 u32 raw_statistics[MAX_TIMC + 1];
2915 int statistics[MAX_TIMC + 1];
2916
2917 /* Make sure rn.start < rn.end */
2918 statistics[MAX_TIMC] = 1;
2919
2920 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002921 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002922 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002923 for (timC = 0; timC < MAX_TIMC; timC++) {
2924 FOR_ALL_LANES
2925 ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
2926 program_timings(ctrl, channel);
2927
2928 test_timC_write (ctrl, channel, slotrank);
2929
2930 raw_statistics[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002931 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002932 }
2933 FOR_ALL_LANES {
2934 struct run rn;
2935 for (timC = 0; timC < MAX_TIMC; timC++)
2936 statistics[timC] =
2937 !!(raw_statistics[timC] &
2938 (1 << lane));
2939
2940 rn = get_longest_zero_run(statistics,
2941 MAX_TIMC + 1);
2942 if (rn.all) {
2943 printk(BIOS_EMERG, "timC write discovery failed: %d, %d, %d\n",
2944 channel, slotrank, lane);
2945 return MAKE_ERR;
2946 }
2947 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
2948 channel, slotrank, i, rn.start,
2949 rn.middle, rn.end,
2950 rn.start + ctrl->timC_offset[i],
2951 rn.end - ctrl->timC_offset[i]);
2952 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002953 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002954 lower[channel][slotrank][lane]);
2955 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002956 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002957 upper[channel][slotrank][lane]);
2958
2959 }
2960 }
2961 }
2962 }
2963
2964 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002965 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002966 udelay(2);
2967 }
2968
Angel Pons88521882020-01-05 20:21:20 +01002969 /*
2970 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2971 * FIXME: This must only be done on Ivy Bridge.
2972 */
2973 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002974
2975 printram("CPB\n");
2976
2977 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2978 printram("timC %d, %d, %d: %x\n", channel,
2979 slotrank, lane,
2980 (lower[channel][slotrank][lane] +
2981 upper[channel][slotrank][lane]) / 2);
2982 ctrl->timings[channel][slotrank].lanes[lane].timC =
2983 (lower[channel][slotrank][lane] +
2984 upper[channel][slotrank][lane]) / 2;
2985 }
2986 FOR_ALL_POPULATED_CHANNELS {
2987 program_timings(ctrl, channel);
2988 }
2989 return 0;
2990}
2991
Angel Pons88521882020-01-05 20:21:20 +01002992void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002993{
2994 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002995 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002996
2997 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2998 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002999 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003000 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003001 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01003002 printram("normalize %d, %d, %d: mat %d\n",
3003 channel, slotrank, lane, mat);
3004
Felix Heldef4fe3e2019-12-31 14:15:05 +01003005 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01003006 printram("normalize %d, %d, %d: delta %d\n",
3007 channel, slotrank, lane, delta);
3008
Angel Pons88521882020-01-05 20:21:20 +01003009 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01003010 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003011 }
3012
3013 FOR_ALL_POPULATED_CHANNELS {
3014 program_timings(ctrl, channel);
3015 }
3016}
3017
Angel Pons88521882020-01-05 20:21:20 +01003018void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003019{
3020 int channel, slotrank;
3021
3022 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01003023 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003024 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01003025 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003026 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003027 }
3028}
3029
3030int channel_test(ramctr_timing *ctrl)
3031{
3032 int channel, slotrank, lane;
3033
3034 slotrank = 0;
3035 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003036 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01003037 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003038 return MAKE_ERR;
3039 }
3040 FOR_ALL_POPULATED_CHANNELS {
3041 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
3042
Angel Pons88521882020-01-05 20:21:20 +01003043 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003044 }
3045
3046 for (slotrank = 0; slotrank < 4; slotrank++)
3047 FOR_ALL_CHANNELS
3048 if (ctrl->rankmap[channel] & (1 << slotrank)) {
3049 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003050 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
3051 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003052 }
Angel Pons88521882020-01-05 20:21:20 +01003053 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02003054
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003055 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01003056 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0001f006;
3057 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01003058 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003059 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02003060
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003061 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01003062 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x0001f201;
3063 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01003064 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01003065 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003066
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003067 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01003068 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x0001f105;
3069 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01003070 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01003071 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003072
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003073 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01003074 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x0001f002;
3075 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01003076 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003077 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02003078
3079 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01003080 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02003081
Angel Pons88521882020-01-05 20:21:20 +01003082 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003083 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01003084 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003085 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
3086 channel, slotrank, lane);
3087 return MAKE_ERR;
3088 }
3089 }
3090 return 0;
3091}
3092
Angel Pons88521882020-01-05 20:21:20 +01003093void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003094{
3095 int channel;
3096
3097 /* FIXME: we hardcode seeds. Do we need to use some PRNG for them?
3098 I don't think so. */
3099 static u32 seeds[NUM_CHANNELS][3] = {
3100 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3101 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3102 };
3103 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003104 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
3105 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3106 MCHBAR32(SCRAMBLING_SEED_2_HIGH_ch(channel)) = seeds[channel][1];
3107 MCHBAR32(SCRAMBLING_SEED_2_LOW_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003108 }
3109}
3110
3111void set_4f8c(void)
3112{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003113 u32 cpu;
3114
Subrata Banik53b08c32018-12-10 14:11:35 +05303115 cpu = cpu_get_cpuid();
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003116 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons88521882020-01-05 20:21:20 +01003117 MCHBAR32(SC_WDBWM) = 0x141D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003118 } else {
Angel Pons88521882020-01-05 20:21:20 +01003119 MCHBAR32(SC_WDBWM) = 0x551D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003120 }
3121}
3122
Angel Pons88521882020-01-05 20:21:20 +01003123void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003124{
3125 int channel;
3126
3127 FOR_ALL_POPULATED_CHANNELS {
3128 // Always drive command bus
Angel Pons88521882020-01-05 20:21:20 +01003129 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003130 }
3131
3132 udelay(1);
3133
3134 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003135 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003136 }
3137}
3138
Angel Pons88521882020-01-05 20:21:20 +01003139void set_4008c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003140{
3141 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003142
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003143 FOR_ALL_POPULATED_CHANNELS {
3144 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003145 int min_pi = 10000;
3146 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003147
3148 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003149 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3150 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003151 }
3152
Angel Pons88521882020-01-05 20:21:20 +01003153 if (max_pi - min_pi > 51)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003154 b20 = 0;
3155 else
3156 b20 = ctrl->ref_card_offset[channel];
3157
Angel Pons88521882020-01-05 20:21:20 +01003158 if (ctrl->pi_coding_threshold < max_pi - min_pi)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003159 b4_8_12 = 0x3330;
3160 else
3161 b4_8_12 = 0x2220;
3162
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003163 dram_odt_stretch(ctrl, channel);
3164
Angel Pons88521882020-01-05 20:21:20 +01003165 MCHBAR32(TC_RWP_ch(channel)) =
Felix Held2463aa92018-07-29 21:37:55 +02003166 0x0a000000 | (b20 << 20) |
3167 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003168 }
3169}
3170
Angel Pons88521882020-01-05 20:21:20 +01003171void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003172{
3173 int channel;
3174 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003175 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3176 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003177 }
3178}
3179
3180static int encode_5d10(int ns)
3181{
Angel Pons88521882020-01-05 20:21:20 +01003182 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003183}
3184
3185/* FIXME: values in this function should be hardware revision-dependent. */
Angel Pons88521882020-01-05 20:21:20 +01003186void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003187{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003188 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3189
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003190 int channel;
3191 int t1_cycles = 0, t1_ns = 0, t2_ns;
3192 int t3_ns;
3193 u32 r32;
3194
Angel Pons88521882020-01-05 20:21:20 +01003195 /* FIXME: This register only exists on Ivy Bridge. */
3196 MCHBAR32(WMM_READ_CONFIG) = 0x00000046;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003197
Felix Heldf9b826a2018-07-30 17:56:52 +02003198 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003199 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xFFFFCFFF, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003200
Patrick Rudolph74203de2017-11-20 11:57:01 +01003201 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003202 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003203 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003204 else
3205 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003206 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003207
Felix Heldf9b826a2018-07-30 17:56:52 +02003208 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003209 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003210
Angel Pons88521882020-01-05 20:21:20 +01003211 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3212 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003213
3214 FOR_ALL_CHANNELS {
3215 switch (ctrl->rankmap[channel]) {
3216 /* Unpopulated channel. */
3217 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003218 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003219 break;
3220 /* Only single-ranked dimms. */
3221 case 1:
3222 case 4:
3223 case 5:
Angel Pons88521882020-01-05 20:21:20 +01003224 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003225 break;
3226 /* Dual-ranked dimms present. */
3227 default:
Angel Pons88521882020-01-05 20:21:20 +01003228 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x9b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003229 break;
3230 }
3231 }
3232
Felix Held50b7ed22019-12-30 20:41:54 +01003233 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
3234 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0xffffff, 0xe4d5d0);
3235 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003236
3237 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003238 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~0x30000, 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003239
Angel Pons88521882020-01-05 20:21:20 +01003240 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3241 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3242 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003243
3244 /* Find a populated channel. */
3245 FOR_ALL_POPULATED_CHANNELS
3246 break;
3247
Angel Pons88521882020-01-05 20:21:20 +01003248 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3249 r32 = MCHBAR32(PM_DLL_CONFIG);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003250 if (r32 & 0x20000)
3251 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003252 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003253 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
3254 if (!(r32 & 0x20000))
3255 t1_ns += 500;
3256
Angel Pons88521882020-01-05 20:21:20 +01003257 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003258 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons88521882020-01-05 20:21:20 +01003259 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
3260 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003261 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003262 t3_ns = 500;
3263 }
3264 printk(BIOS_DEBUG, "t123: %d, %d, %d\n",
3265 t1_ns, t2_ns, t3_ns);
Felix Heldb802c072018-07-29 21:46:19 +02003266 MCHBAR32_AND_OR(0x5d10, 0xC0C0C0C0,
3267 ((encode_5d10(t1_ns) + encode_5d10(t2_ns)) << 16) |
Felix Held2bb3cdf2018-07-28 00:23:59 +02003268 (encode_5d10(t1_ns) << 8) | ((encode_5d10(t3_ns) +
Felix Heldb802c072018-07-29 21:46:19 +02003269 encode_5d10(t2_ns) + encode_5d10(t1_ns)) << 24) | 0xc);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003270}
3271
Angel Pons88521882020-01-05 20:21:20 +01003272void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003273{
3274 int channel, slotrank, lane;
3275
3276 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003277 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003278 ctrl->tRRD
3279 | (ctrl->tRTP << 4)
3280 | (ctrl->tCKE << 8)
3281 | (ctrl->tWTR << 12)
3282 | (ctrl->tFAW << 16)
3283 | (ctrl->tWR << 24)
3284 | (ctrl->cmd_stretch[channel] << 30);
3285
3286 udelay(1);
3287
3288 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003289 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003290 }
3291
3292 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003293 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003294 }
3295
3296 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003297 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003298
3299 FOR_ALL_POPULATED_CHANNELS {
3300 udelay (1);
Angel Pons88521882020-01-05 20:21:20 +01003301 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003302 }
3303
3304 printram("CPE\n");
3305
Angel Pons88521882020-01-05 20:21:20 +01003306 MCHBAR32(GDCRTRAININGMOD) = 0;
3307 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003308
3309 printram("CP5b\n");
3310
3311 FOR_ALL_POPULATED_CHANNELS {
3312 program_timings(ctrl, channel);
3313 }
3314
3315 u32 reg, addr;
3316
Angel Pons88521882020-01-05 20:21:20 +01003317 while (!(MCHBAR32(RCOMP_TIMER) & 0x10000));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003318 do {
Angel Pons88521882020-01-05 20:21:20 +01003319 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003320 } while ((reg & 0x14) == 0);
3321
3322 // Set state of memory controller
Angel Pons88521882020-01-05 20:21:20 +01003323 MCHBAR32(MC_INIT_STATE_G) = 0x116;
3324 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003325
3326 // Wait 500us
3327 udelay(500);
3328
3329 FOR_ALL_CHANNELS {
3330 // Set valid rank CKE
3331 reg = 0;
3332 reg = (reg & ~0xf) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003333 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003334 MCHBAR32(addr) = reg;
3335
3336 // Wait 10ns for ranks to settle
3337 //udelay(0.01);
3338
3339 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3340 MCHBAR32(addr) = reg;
3341
3342 // Write reset using a NOP
3343 write_reset(ctrl);
3344 }
3345
3346 /* mrs commands. */
3347 dram_mrscommands(ctrl);
3348
3349 printram("CP5c\n");
3350
Angel Pons88521882020-01-05 20:21:20 +01003351 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003352
3353 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003354 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003355 udelay(2);
3356 }
3357
Angel Pons88521882020-01-05 20:21:20 +01003358 /*
3359 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
3360 * FIXME: This must only be done on Ivy Bridge. Moreover, this instance seems to be
3361 * spurious, because nothing else enabled this optimization before.
3362 */
3363 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003364}