blob: 702addaf92141c6581b5e6cf17214c1f7368a795 [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 Held283b44662020-01-14 21:14:42 +01001158 MCHBAR32(lane_base[lane] +
1159 (0x10 + (channel * 0x100) + (slotrank * 4))) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001160 (((ctrl->timings[channel][slotrank].lanes[lane].
1161 timA + shift) & 0x3f)
1162 |
1163 ((ctrl->timings[channel][slotrank].lanes[lane].
1164 rising + shift) << 8)
1165 |
1166 (((ctrl->timings[channel][slotrank].lanes[lane].
1167 timA + shift -
1168 (post_timA_min_high << 6)) & 0x1c0) << 10)
1169 | ((ctrl->timings[channel][slotrank].lanes[lane].
1170 falling + shift) << 20));
1171
Felix Held283b44662020-01-14 21:14:42 +01001172 MCHBAR32(lane_base[lane] +
1173 (0x20 + (channel * 0x100) + (slotrank * 4))) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001174 (((ctrl->timings[channel][slotrank].lanes[lane].
1175 timC + shift) & 0x3f)
1176 |
1177 (((ctrl->timings[channel][slotrank].lanes[lane].
1178 timB + shift) & 0x3f) << 8)
1179 |
1180 (((ctrl->timings[channel][slotrank].lanes[lane].
1181 timB + shift) & 0x1c0) << 9)
1182 |
1183 (((ctrl->timings[channel][slotrank].lanes[lane].
1184 timC + shift) & 0x40) << 13));
1185 }
1186 }
Angel Pons88521882020-01-05 20:21:20 +01001187 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1188 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001189}
1190
Angel Pons88521882020-01-05 20:21:20 +01001191static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001192{
Angel Pons88521882020-01-05 20:21:20 +01001193 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001194
1195 /* DRAM command MRS
1196 * write MR3 MPR enable
1197 * in this mode only RD and RDA are allowed
1198 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001199 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1200 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
1201 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
1202 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001203
1204 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001205 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1206 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4040c01;
1207 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
1208 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001209
1210 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001211 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1212 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
1213 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
1214 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001215
1216 /* DRAM command MRS
1217 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001218 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1219 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
1220 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
1221 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001222
Felix Held9cf1dd22018-07-31 14:52:40 +02001223 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001224 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001225
Angel Pons88521882020-01-05 20:21:20 +01001226 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001227}
1228
Angel Pons88521882020-01-05 20:21:20 +01001229static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001230 int lane)
1231{
1232 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Felix Held283b44662020-01-14 21:14:42 +01001233 return ((MCHBAR32(lane_base[lane] + (4 + (channel * 0x100) +
1234 (((timA / 32) & 1) * 4))) >> (timA % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001235}
1236
1237struct run {
1238 int middle;
1239 int end;
1240 int start;
1241 int all;
1242 int length;
1243};
1244
1245static struct run get_longest_zero_run(int *seq, int sz)
1246{
1247 int i, ls;
1248 int bl = 0, bs = 0;
1249 struct run ret;
1250
1251 ls = 0;
1252 for (i = 0; i < 2 * sz; i++)
1253 if (seq[i % sz]) {
1254 if (i - ls > bl) {
1255 bl = i - ls;
1256 bs = ls;
1257 }
1258 ls = i + 1;
1259 }
1260 if (bl == 0) {
1261 ret.middle = sz / 2;
1262 ret.start = 0;
1263 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001264 ret.length = sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001265 ret.all = 1;
1266 return ret;
1267 }
1268
1269 ret.start = bs % sz;
1270 ret.end = (bs + bl - 1) % sz;
1271 ret.middle = (bs + (bl - 1) / 2) % sz;
1272 ret.length = bl;
1273 ret.all = 0;
1274
1275 return ret;
1276}
1277
Angel Pons88521882020-01-05 20:21:20 +01001278static void discover_timA_coarse(ramctr_timing *ctrl, int channel,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001279 int slotrank, int *upperA)
1280{
1281 int timA;
1282 int statistics[NUM_LANES][128];
1283 int lane;
1284
1285 for (timA = 0; timA < 128; timA++) {
1286 FOR_ALL_LANES {
1287 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1288 }
1289 program_timings(ctrl, channel);
1290
1291 test_timA(ctrl, channel, slotrank);
1292
1293 FOR_ALL_LANES {
1294 statistics[lane][timA] =
1295 !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001296 }
1297 }
1298 FOR_ALL_LANES {
1299 struct run rn = get_longest_zero_run(statistics[lane], 128);
1300 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1301 upperA[lane] = rn.end;
1302 if (upperA[lane] < rn.middle)
1303 upperA[lane] += 128;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001304 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001305 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001306 }
1307}
1308
Angel Pons88521882020-01-05 20:21:20 +01001309static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001310 int *upperA)
1311{
1312 int timA_delta;
1313 int statistics[NUM_LANES][51];
1314 int lane, i;
1315
1316 memset(statistics, 0, sizeof(statistics));
1317
1318 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
1319 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].
1320 timA = upperA[lane] + timA_delta + 0x40;
1321 program_timings(ctrl, channel);
1322
1323 for (i = 0; i < 100; i++) {
1324 test_timA(ctrl, channel, slotrank);
1325 FOR_ALL_LANES {
1326 statistics[lane][timA_delta + 25] +=
Felix Held2bb3cdf2018-07-28 00:23:59 +02001327 does_lane_work(ctrl, channel, slotrank,
1328 lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001329 }
1330 }
1331 }
1332 FOR_ALL_LANES {
1333 int last_zero, first_all;
1334
1335 for (last_zero = -25; last_zero <= 25; last_zero++)
1336 if (statistics[lane][last_zero + 25])
1337 break;
1338 last_zero--;
1339 for (first_all = -25; first_all <= 25; first_all++)
1340 if (statistics[lane][first_all + 25] == 100)
1341 break;
1342
1343 printram("lane %d: %d, %d\n", lane, last_zero,
1344 first_all);
1345
1346 ctrl->timings[channel][slotrank].lanes[lane].timA =
1347 (last_zero + first_all) / 2 + upperA[lane];
1348 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1349 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
1350 }
1351}
1352
Angel Pons891f2bc2020-01-10 01:27:28 +01001353static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001354{
1355 int works[NUM_LANES];
1356 int lane;
1357 while (1) {
1358 int all_works = 1, some_works = 0;
1359 program_timings(ctrl, channel);
1360 test_timA(ctrl, channel, slotrank);
1361 FOR_ALL_LANES {
1362 works[lane] =
1363 !does_lane_work(ctrl, channel, slotrank, lane);
1364 if (works[lane])
1365 some_works = 1;
1366 else
1367 all_works = 0;
1368 }
1369 if (all_works)
1370 return 0;
1371 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001372 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001373 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1374 channel, slotrank);
1375 return MAKE_ERR;
1376 }
Angel Pons88521882020-01-05 20:21:20 +01001377 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001378 printram("4024 -= 2;\n");
1379 continue;
1380 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001381 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001382 printram("4028 += 2;\n");
Felix Heldef4fe3e2019-12-31 14:15:05 +01001383 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001384 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1385 channel, slotrank);
1386 return MAKE_ERR;
1387 }
1388 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001389 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001390 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001391 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001392 }
1393 }
1394 return 0;
1395}
1396
1397struct timA_minmax {
1398 int timA_min_high, timA_max_high;
1399};
1400
Angel Pons88521882020-01-05 20:21:20 +01001401static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001402 struct timA_minmax *mnmx)
1403{
1404 int lane;
1405 mnmx->timA_min_high = 7;
1406 mnmx->timA_max_high = 0;
1407
1408 FOR_ALL_LANES {
1409 if (mnmx->timA_min_high >
1410 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1411 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001412 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001413 if (mnmx->timA_max_high <
1414 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1415 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001416 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001417 }
1418}
1419
Angel Pons88521882020-01-05 20:21:20 +01001420static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001421 struct timA_minmax *mnmx)
1422{
1423 struct timA_minmax post;
1424 int shift_402x = 0;
1425
Angel Pons891f2bc2020-01-10 01:27:28 +01001426 /* Get changed maxima. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001427 pre_timA_change(ctrl, channel, slotrank, &post);
1428
1429 if (mnmx->timA_max_high - mnmx->timA_min_high <
1430 post.timA_max_high - post.timA_min_high)
1431 shift_402x = +1;
1432 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1433 post.timA_max_high - post.timA_min_high)
1434 shift_402x = -1;
1435 else
1436 shift_402x = 0;
1437
Felix Heldef4fe3e2019-12-31 14:15:05 +01001438 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001439 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001440 printram("4024 += %d;\n", shift_402x);
1441 printram("4028 += %d;\n", shift_402x);
1442}
1443
1444/* Compensate the skew between DQS and DQs.
Angel Pons891f2bc2020-01-10 01:27:28 +01001445 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1446 * The controller has to measure and compensate this skew for every byte-lane. By delaying
1447 * either all DQs signals or DQS signal, a full phase shift can be introduced. It is assumed
1448 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001449 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001450 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1451 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1452 * over all possible values to do a full phase shift and issues read commands. With DQS and
1453 * DQs in phase the data read is expected to alternate on every byte:
1454 * 0xFF 0x00 0xFF ...
1455 * Once the controller has detected this pattern a bit in the result register is set for the
1456 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001457 */
Angel Pons88521882020-01-05 20:21:20 +01001458int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001459{
1460 int channel, slotrank, lane;
1461 int err;
1462
1463 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1464 int all_high, some_high;
1465 int upperA[NUM_LANES];
1466 struct timA_minmax mnmx;
1467
Angel Pons88521882020-01-05 20:21:20 +01001468 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001469
Felix Held2bb3cdf2018-07-28 00:23:59 +02001470 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001471 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1472 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1473 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1474 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001475
1476 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001477 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001478
Angel Pons88521882020-01-05 20:21:20 +01001479 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001480
Felix Heldef4fe3e2019-12-31 14:15:05 +01001481 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001482 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001483 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001484
Felix Held2bb3cdf2018-07-28 00:23:59 +02001485 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001486
Felix Held2bb3cdf2018-07-28 00:23:59 +02001487 all_high = 1;
1488 some_high = 0;
1489 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001490 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001491 some_high = 1;
1492 else
1493 all_high = 0;
1494 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001495
1496 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001497 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001498 printram("4028--;\n");
1499 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001500 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001501 upperA[lane] -= 0x40;
1502
1503 }
1504 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001505 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001506 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001507 printram("4024++;\n");
1508 printram("4028++;\n");
1509 }
1510
1511 program_timings(ctrl, channel);
1512
1513 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1514
1515 err = discover_402x(ctrl, channel, slotrank, upperA);
1516 if (err)
1517 return err;
1518
1519 post_timA_change(ctrl, channel, slotrank, &mnmx);
1520 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1521
1522 discover_timA_fine(ctrl, channel, slotrank, upperA);
1523
1524 post_timA_change(ctrl, channel, slotrank, &mnmx);
1525 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1526
1527 FOR_ALL_LANES {
1528 ctrl->timings[channel][slotrank].lanes[lane].timA -= mnmx.timA_min_high * 0x40;
1529 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001530 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001531 printram("4028 -= %d;\n", mnmx.timA_min_high);
1532
1533 post_timA_change(ctrl, channel, slotrank, &mnmx);
1534
1535 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001536 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001537 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001538
1539 printram("final results:\n");
1540 FOR_ALL_LANES
Felix Held2bb3cdf2018-07-28 00:23:59 +02001541 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
1542 lane,
1543 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001544
Angel Pons88521882020-01-05 20:21:20 +01001545 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001546
1547 toggle_io_reset();
1548 }
1549
1550 FOR_ALL_POPULATED_CHANNELS {
1551 program_timings(ctrl, channel);
1552 }
1553 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001554 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001555 }
1556 return 0;
1557}
1558
Angel Pons88521882020-01-05 20:21:20 +01001559static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001560{
1561 int lane;
1562
1563 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001564 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1565 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001566 }
1567
Angel Pons88521882020-01-05 20:21:20 +01001568 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001569
1570 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001571 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1572 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001573 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001574 | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001575 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
1576 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001577
1578 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001579 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1580 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8041001;
1581 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
1582 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001583
1584 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01001585 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
1586 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x80411f4;
1587 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
1588 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001589
1590 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001591 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
1592 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001593 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001594 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
1595 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001596
Felix Held9cf1dd22018-07-31 14:52:40 +02001597 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001598 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001599
Angel Pons88521882020-01-05 20:21:20 +01001600 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001601
1602 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001603 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1604 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1605 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1606 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001607
1608 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001609 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
1610 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001611 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001612 | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001613 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
1614 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001615
1616 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001617 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1618 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001619 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001620 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
1621 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001622
1623 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001624 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
1625 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
1626 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
1627 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001628
1629 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001630 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001631
Angel Pons88521882020-01-05 20:21:20 +01001632 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001633}
1634
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001635static void timC_threshold_process(int *data, const int count)
1636{
1637 int min = data[0];
1638 int max = min;
1639 int i;
1640 for (i = 1; i < count; i++) {
1641 if (min > data[i])
1642 min = data[i];
1643 if (max < data[i])
1644 max = data[i];
1645 }
1646 int threshold = min/2 + max/2;
1647 for (i = 0; i < count; i++)
1648 data[i] = data[i] > threshold;
Angel Pons891f2bc2020-01-10 01:27:28 +01001649 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001650}
1651
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001652static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1653{
1654 int timC;
1655 int statistics[NUM_LANES][MAX_TIMC + 1];
1656 int lane;
1657
Angel Pons88521882020-01-05 20:21:20 +01001658 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001659
1660 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001661 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
1662 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
1663 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
1664 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001665
1666 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001667 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001668
1669 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001670 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001671 program_timings(ctrl, channel);
1672
1673 test_timC(ctrl, channel, slotrank);
1674
1675 FOR_ALL_LANES {
1676 statistics[lane][timC] =
Angel Pons88521882020-01-05 20:21:20 +01001677 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001678 }
1679 }
1680 FOR_ALL_LANES {
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001681 struct run rn = get_longest_zero_run(
1682 statistics[lane], ARRAY_SIZE(statistics[lane]));
1683 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001684 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1685 channel, slotrank, lane);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001686 /* With command training not happend yet, the lane can
1687 * be erroneous. Take the avarage as reference and try
1688 * again to find a run.
1689 */
1690 timC_threshold_process(statistics[lane],
1691 ARRAY_SIZE(statistics[lane]));
1692 rn = get_longest_zero_run(statistics[lane],
1693 ARRAY_SIZE(statistics[lane]));
1694 if (rn.all || rn.length < 8) {
1695 printk(BIOS_EMERG, "timC recovery failed\n");
1696 return MAKE_ERR;
1697 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001698 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001699 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001700 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001701 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001702 }
1703 return 0;
1704}
1705
Angel Pons88521882020-01-05 20:21:20 +01001706static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001707{
1708 int channel, ret = 0;
1709 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1710 ret++;
1711 return ret;
1712}
1713
Angel Pons88521882020-01-05 20:21:20 +01001714static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001715{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301716 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001717 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001718 for (j = 0; j < 16; j++)
1719 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
1720 sfence();
1721}
1722
Angel Pons88521882020-01-05 20:21:20 +01001723static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001724{
1725 int ret = 0;
1726 int channel;
1727 FOR_ALL_POPULATED_CHANNELS ret++;
1728 return ret;
1729}
1730
Angel Pons88521882020-01-05 20:21:20 +01001731static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001732{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301733 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001734 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301735 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001736 for (j = 0; j < 16; j++)
1737 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
1738 for (j = 0; j < 16; j++)
1739 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
1740 sfence();
1741}
1742
Angel Pons88521882020-01-05 20:21:20 +01001743static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001744{
1745 int channel, slotrank, lane;
1746
1747 FOR_ALL_POPULATED_CHANNELS {
1748 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001749 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1750 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001751 }
1752
1753 program_timings(ctrl, channel);
1754
1755 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001756 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001757
1758 /* DRAM command MRS
1759 * write MR3 MPR enable
1760 * in this mode only RD and RDA are allowed
1761 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001762 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1763 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001764 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001765 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001766 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001767 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001768
1769 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001770 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1771 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001772 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01001773 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001774
1775 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001776 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1777 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001778 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001779 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001780 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001781 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001782
1783 /* DRAM command MRS
1784 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001785 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1786 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001787 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001788 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001789 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001790 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001791
1792 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001793 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001794
Angel Pons88521882020-01-05 20:21:20 +01001795 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001796 }
1797
1798 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001799 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1800 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001801 }
1802
1803 program_timings(ctrl, channel);
1804
1805 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001806 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001807 /* DRAM command MRS
1808 * write MR3 MPR enable
1809 * in this mode only RD and RDA are allowed
1810 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01001811 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
1812 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001813 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001814 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001815 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01001816 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001817
1818 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001819 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
1820 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001821 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01001822 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001823
1824 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01001825 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
1826 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001827 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001828 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001829 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01001830 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001831
1832 /* DRAM command MRS
1833 * write MR3 MPR disable */
Angel Pons88521882020-01-05 20:21:20 +01001834 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
1835 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001836 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001837 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001838 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01001839 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001840
Felix Held9cf1dd22018-07-31 14:52:40 +02001841 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001842 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001843
Angel Pons88521882020-01-05 20:21:20 +01001844 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001845 }
1846 }
1847}
1848
Angel Pons88521882020-01-05 20:21:20 +01001849static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001850{
1851 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001852 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001853
Angel Pons88521882020-01-05 20:21:20 +01001854 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001855 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001856 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f207;
1857 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001858 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001859 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
1860 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001861
1862 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001863 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f107;
Angel Pons891f2bc2020-01-10 01:27:28 +01001864 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001865 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
1866 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001867
Felix Held9cf1dd22018-07-31 14:52:40 +02001868 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001869 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001870
Angel Pons88521882020-01-05 20:21:20 +01001871 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001872
1873 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001874 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001875}
1876
1877static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1878{
1879 int timB;
1880 int statistics[NUM_LANES][128];
1881 int lane;
1882
Angel Pons88521882020-01-05 20:21:20 +01001883 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001884
1885 for (timB = 0; timB < 128; timB++) {
1886 FOR_ALL_LANES {
1887 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1888 }
1889 program_timings(ctrl, channel);
1890
1891 test_timB(ctrl, channel, slotrank);
1892
1893 FOR_ALL_LANES {
1894 statistics[lane][timB] =
Felix Held283b44662020-01-14 21:14:42 +01001895 !((MCHBAR32(lane_base[lane] + (4 +
1896 (channel * 0x100) + (((timB / 32) & 1) * 4)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897 >> (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001898 }
1899 }
1900 FOR_ALL_LANES {
1901 struct run rn = get_longest_zero_run(statistics[lane], 128);
1902 /* timC is a direct function of timB's 6 LSBs.
1903 * Some tests increments the value of timB by a small value,
1904 * which might cause the 6bit value to overflow, if it's close
1905 * to 0x3F. Increment the value by a small offset if it's likely
1906 * to overflow, to make sure it won't overflow while running
1907 * tests and bricks the system due to a non matching timC.
1908 *
1909 * TODO: find out why some tests (edge write discovery)
1910 * increment timB. */
1911 if ((rn.start & 0x3F) == 0x3E)
1912 rn.start += 2;
1913 else if ((rn.start & 0x3F) == 0x3F)
1914 rn.start += 1;
1915 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1916 if (rn.all) {
1917 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1918 channel, slotrank, lane);
1919 return MAKE_ERR;
1920 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001921 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1922 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001923 }
1924 return 0;
1925}
1926
1927static int get_timB_high_adjust(u64 val)
1928{
1929 int i;
1930
1931 /* good */
1932 if (val == 0xffffffffffffffffLL)
1933 return 0;
1934
1935 if (val >= 0xf000000000000000LL) {
1936 /* needs negative adjustment */
1937 for (i = 0; i < 8; i++)
1938 if (val << (8 * (7 - i) + 4))
1939 return -i;
1940 } else {
1941 /* needs positive adjustment */
1942 for (i = 0; i < 8; i++)
1943 if (val >> (8 * (7 - i) + 4))
1944 return i;
1945 }
1946 return 8;
1947}
1948
Angel Pons88521882020-01-05 20:21:20 +01001949static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001950{
1951 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001952 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001953 FOR_ALL_POPULATED_CHANNELS {
1954 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001955 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956 }
1957 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1958
Angel Pons88521882020-01-05 20:21:20 +01001959 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960
Angel Pons88521882020-01-05 20:21:20 +01001961 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001962
1963 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01001964 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
1965 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
1966 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
1967 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001968
1969 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001970 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f207;
1971 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8040c01;
1972 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
1973 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001974
1975 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01001976 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f201;
1977 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x8041003;
1978 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
1979 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001980
1981 /* DRAM command NOP */
Angel Pons88521882020-01-05 20:21:20 +01001982 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f207;
1983 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001984 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001985 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
1986 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001987
Felix Held9cf1dd22018-07-31 14:52:40 +02001988 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01001989 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001990
Angel Pons88521882020-01-05 20:21:20 +01001991 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001992
1993 /* DRAM command PREA */
Angel Pons88521882020-01-05 20:21:20 +01001994 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f002;
Angel Pons891f2bc2020-01-10 01:27:28 +01001995 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
1996 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01001997 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001998
1999 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002000 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f006;
Angel Pons891f2bc2020-01-10 01:27:28 +01002001 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002002 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
2003 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002004
2005 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002006 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x3f105;
2007 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
2008 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01002009 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002010 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
2011 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002012
Felix Held9cf1dd22018-07-31 14:52:40 +02002013 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002014 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02002015
Angel Pons88521882020-01-05 20:21:20 +01002016 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002017 FOR_ALL_LANES {
Felix Held283b44662020-01-14 21:14:42 +01002018 u64 res = MCHBAR32(lane_base[lane] + 4 + (channel * 0x100) + (0 * 4));
2019 res |= ((u64) MCHBAR32(lane_base[lane] +
2020 (4 + (channel * 0x100) + (1 * 4)))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002021 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
2022 ctrl->timings[channel][slotrank].lanes[lane].timB +=
2023 get_timB_high_adjust(res) * 64;
2024
2025 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01002026 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
2027 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002028 }
2029 }
Angel Pons88521882020-01-05 20:21:20 +01002030 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002031}
2032
Angel Pons88521882020-01-05 20:21:20 +01002033static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002034{
2035 int slotrank;
2036
Angel Pons88521882020-01-05 20:21:20 +01002037 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002038
2039 /* choose an existing rank. */
2040 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2041
2042 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002043 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2044 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2045 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2046 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002047
Felix Held9cf1dd22018-07-31 14:52:40 +02002048 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002049 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002050
Angel Pons88521882020-01-05 20:21:20 +01002051 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002052}
2053
2054/* Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
2055 * DDR3 adopted the fly-by topology. The data and strobes signals reach
2056 * the chips at different times with respect to command, address and
2057 * clock signals.
2058 * By delaying either all DQ/DQs or all CMD/ADDR/CLK signals, a full phase
2059 * shift can be introduced.
2060 * It is assumed that the CLK/ADDR/CMD signals have the same routing delay.
2061 *
2062 * To find the required phase shift the DRAM is placed in "write leveling" mode.
2063 * In this mode the DRAM-chip samples the CLK on every DQS edge and feeds back the
2064 * sampled value on the data lanes (DQs).
2065 */
Angel Pons88521882020-01-05 20:21:20 +01002066int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002067{
2068 int channel, slotrank, lane;
2069 int err;
2070
2071 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002072 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002073
2074 FOR_ALL_POPULATED_CHANNELS {
2075 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01002076 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002077 }
2078
2079 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002080 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002081 FOR_ALL_POPULATED_CHANNELS {
2082 write_op(ctrl, channel);
2083 }
2084
2085 /* enable write leveling on all ranks
2086 * disable all DQ outputs
2087 * only NOP is allowed in this mode */
2088 FOR_ALL_CHANNELS
Felix Held2bb3cdf2018-07-28 00:23:59 +02002089 FOR_ALL_POPULATED_RANKS
2090 write_mrreg(ctrl, channel, slotrank, 1,
2091 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002092
Angel Pons88521882020-01-05 20:21:20 +01002093 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002094
2095 toggle_io_reset();
2096
2097 /* set any valid value for timB, it gets corrected later */
2098 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2099 err = discover_timB(ctrl, channel, slotrank);
2100 if (err)
2101 return err;
2102 }
2103
2104 /* disable write leveling on all ranks */
2105 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
2106 write_mrreg(ctrl, channel,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002107 slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002108
Angel Pons88521882020-01-05 20:21:20 +01002109 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002110
2111 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002112 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002113
2114 /* refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01002115 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002116
2117 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002118 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
2119 MCHBAR32(IOSAV_STATUS_ch(channel));
2120 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002121
2122 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002123 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2124 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x659001;
2125 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
2126 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002127
Felix Held9cf1dd22018-07-31 14:52:40 +02002128 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002129 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002130
Angel Pons88521882020-01-05 20:21:20 +01002131 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002132 }
2133
2134 toggle_io_reset();
2135
2136 printram("CPE\n");
2137 precharge(ctrl);
2138 printram("CPF\n");
2139
2140 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002141 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002142 }
2143
2144 FOR_ALL_POPULATED_CHANNELS {
2145 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002146 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002147 }
2148
2149 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2150 err = discover_timC(ctrl, channel, slotrank);
2151 if (err)
2152 return err;
2153 }
2154
2155 FOR_ALL_POPULATED_CHANNELS
2156 program_timings(ctrl, channel);
2157
2158 /* measure and adjust timB timings */
2159 adjust_high_timB(ctrl);
2160
2161 FOR_ALL_POPULATED_CHANNELS
2162 program_timings(ctrl, channel);
2163
2164 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002165 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002166 }
2167 return 0;
2168}
2169
Angel Pons88521882020-01-05 20:21:20 +01002170static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002171{
2172 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2173 int timC_delta;
2174 int lanes_ok = 0;
2175 int ctr = 0;
2176 int lane;
2177
2178 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2179 FOR_ALL_LANES {
2180 ctrl->timings[channel][slotrank].lanes[lane].timC =
2181 saved_rt.lanes[lane].timC + timC_delta;
2182 }
2183 program_timings(ctrl, channel);
2184 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002185 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002186 }
2187
Angel Pons88521882020-01-05 20:21:20 +01002188 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002189
Angel Pons88521882020-01-05 20:21:20 +01002190 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002191 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002192 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2193 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002194 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002195 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002196 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002197 (slotrank << 24) | ctr | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002198 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002199
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002200 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002201 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2202 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002203 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002204 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2205 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
2206 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002207
2208 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002209 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2210 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002211 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002212 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2213 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
2214 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002215
2216 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002217 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2218 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xf1001;
2219 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2220 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002221
Felix Held9cf1dd22018-07-31 14:52:40 +02002222 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002223 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002224
Angel Pons88521882020-01-05 20:21:20 +01002225 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002226 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002227 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002228
2229 if (r32 == 0)
2230 lanes_ok |= 1 << lane;
2231 }
2232 ctr++;
2233 if (lanes_ok == ((1 << NUM_LANES) - 1))
2234 break;
2235 }
2236
2237 ctrl->timings[channel][slotrank] = saved_rt;
2238
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002239 return lanes_ok != ((1 << NUM_LANES) - 1);
2240}
2241
2242#include "raminit_patterns.h"
2243
Angel Pons88521882020-01-05 20:21:20 +01002244static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002245{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302246 unsigned int i, j;
Angel Pons891f2bc2020-01-10 01:27:28 +01002247 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05302248 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002249
2250 if (patno) {
2251 u8 base8 = 0x80 >> ((patno - 1) % 8);
2252 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2253 for (i = 0; i < 32; i++) {
2254 for (j = 0; j < 16; j++) {
2255 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
2256 if (invert[patno - 1][i] & (1 << (j / 2)))
2257 val = ~val;
2258 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2259 j * 4), val);
2260 }
2261 }
2262
2263 } else {
2264 for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) {
2265 for (j = 0; j < 16; j++)
2266 write32((void *)(0x04000000 + channel_offset + i * channel_step +
2267 j * 4), pattern[i][j]);
2268 }
2269 sfence();
2270 }
2271}
2272
Angel Pons88521882020-01-05 20:21:20 +01002273static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002274{
2275 int channel, slotrank;
2276
2277 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002278 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002279
2280 /* choose an existing rank. */
2281 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2282
2283 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002284 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2285 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2286 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2287 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002288
Felix Held9cf1dd22018-07-31 14:52:40 +02002289 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002290 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002291
Angel Pons88521882020-01-05 20:21:20 +01002292 wait_for_iosav(channel);
2293 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002294 }
2295
2296 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002297 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002298 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002299 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002300
2301 /* choose an existing rank. */
2302 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2303
2304 /* DRAM command ZQCS */
Angel Pons88521882020-01-05 20:21:20 +01002305 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0f003;
2306 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x41001;
2307 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2308 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002309
Felix Held9cf1dd22018-07-31 14:52:40 +02002310 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002311 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002312
Angel Pons88521882020-01-05 20:21:20 +01002313 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002314 }
2315
2316 /* jedec reset */
2317 dram_jedecreset(ctrl);
2318 /* mrs commands. */
2319 dram_mrscommands(ctrl);
2320
2321 toggle_io_reset();
2322}
2323
2324#define MIN_C320C_LEN 13
2325
2326static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2327{
2328 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2329 int slotrank;
2330 int c320c;
2331 int stat[NUM_SLOTRANKS][256];
2332 int delta = 0;
2333
2334 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2335
2336 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002337 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002338 }
2339
2340 ctrl->cmd_stretch[channel] = cmd_stretch;
2341
Angel Pons88521882020-01-05 20:21:20 +01002342 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002343 ctrl->tRRD
2344 | (ctrl->tRTP << 4)
2345 | (ctrl->tCKE << 8)
2346 | (ctrl->tWTR << 12)
2347 | (ctrl->tFAW << 16)
2348 | (ctrl->tWR << 24)
2349 | (ctrl->cmd_stretch[channel] << 30);
2350
2351 if (ctrl->cmd_stretch[channel] == 2)
2352 delta = 2;
2353 else if (ctrl->cmd_stretch[channel] == 0)
2354 delta = 4;
2355
2356 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002357 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002358 }
2359
2360 for (c320c = -127; c320c <= 127; c320c++) {
2361 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002362 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002363 }
2364 program_timings(ctrl, channel);
2365 reprogram_320c(ctrl);
2366 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002367 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002368 }
2369 }
2370 FOR_ALL_POPULATED_RANKS {
2371 struct run rn =
2372 get_longest_zero_run(stat[slotrank], 255);
Angel Pons88521882020-01-05 20:21:20 +01002373 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002374 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2375 channel, slotrank, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002376 if (rn.all || rn.length < MIN_C320C_LEN) {
2377 FOR_ALL_POPULATED_RANKS {
2378 ctrl->timings[channel][slotrank] =
2379 saved_timings[channel][slotrank];
2380 }
2381 return MAKE_ERR;
2382 }
2383 }
2384
2385 return 0;
2386}
2387
2388/* Adjust CMD phase shift and try multiple command rates.
2389 * A command rate of 2T doubles the time needed for address and
2390 * command decode. */
2391int command_training(ramctr_timing *ctrl)
2392{
2393 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002394
2395 FOR_ALL_POPULATED_CHANNELS {
2396 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002397 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002398 }
2399
2400 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002401 int cmdrate, err;
2402
2403 /*
2404 * Dual DIMM per channel:
2405 * Issue: While c320c discovery seems to succeed raminit
2406 * will fail in write training.
2407 * Workaround: Skip 1T in dual DIMM mode, that's only
2408 * supported by a few DIMMs.
Dan Elkoubydabebc32018-04-13 18:47:10 +03002409 * Only try 1T mode for XMP DIMMs that request it in dual DIMM
2410 * mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002411 *
2412 * Single DIMM per channel:
2413 * Try command rate 1T and 2T
2414 */
2415 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002416 if (ctrl->tCMD)
2417 /* XMP gives the CMD rate in clock ticks, not ns */
2418 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002419
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002420 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002421 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2422
2423 if (!err)
2424 break;
2425 }
2426
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002427 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002428 printk(BIOS_EMERG, "c320c discovery failed\n");
2429 return err;
2430 }
2431
Angel Pons891f2bc2020-01-10 01:27:28 +01002432 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002433 }
2434
2435 FOR_ALL_POPULATED_CHANNELS
2436 program_timings(ctrl, channel);
2437
2438 reprogram_320c(ctrl);
2439 return 0;
2440}
2441
2442
Angel Pons891f2bc2020-01-10 01:27:28 +01002443static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002444{
2445 int edge;
2446 int statistics[NUM_LANES][MAX_EDGE_TIMING + 1];
2447 int lane;
2448
2449 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2450 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002451 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
2452 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002453 }
2454 program_timings(ctrl, channel);
2455
2456 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002457 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2458 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002459 }
2460
Angel Pons88521882020-01-05 20:21:20 +01002461 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002462 /* DRAM command MRS
2463 * write MR3 MPR enable
2464 * in this mode only RD and RDA are allowed
2465 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002466 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2467 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002468 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002469 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002470
2471 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002472 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2473 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x40411f4;
2474 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2475 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002476
2477 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002478 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
Angel Pons891f2bc2020-01-10 01:27:28 +01002479 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002480 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
2481 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002482
2483 /* DRAM command MRS
2484 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002485 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2486 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002487 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002488 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002489
Felix Held9cf1dd22018-07-31 14:52:40 +02002490 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002491 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002492
Angel Pons88521882020-01-05 20:21:20 +01002493 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002494
2495 FOR_ALL_LANES {
2496 statistics[lane][edge] =
Angel Pons88521882020-01-05 20:21:20 +01002497 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002498 }
2499 }
2500 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002501 struct run rn = get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002502 edges[lane] = rn.middle;
2503 if (rn.all) {
2504 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n",
2505 channel, slotrank, lane);
2506 return MAKE_ERR;
2507 }
2508 printram("eval %d, %d, %d: %02x\n", channel, slotrank,
2509 lane, edges[lane]);
2510 }
2511 return 0;
2512}
2513
2514int discover_edges(ramctr_timing *ctrl)
2515{
2516 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2517 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2518 int channel, slotrank, lane;
2519 int err;
2520
Angel Pons88521882020-01-05 20:21:20 +01002521 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002522
2523 toggle_io_reset();
2524
2525 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002526 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002527 }
2528
2529 FOR_ALL_POPULATED_CHANNELS {
2530 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002531 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002532 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002533 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002534 }
2535
2536 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002537 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2538 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002539 }
2540
2541 program_timings(ctrl, channel);
2542
2543 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002544 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002545
2546 /* DRAM command MRS
2547 * MR3 enable MPR
2548 * write MR3 MPR enable
2549 * in this mode only RD and RDA are allowed
2550 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002551 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2552 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002553 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002554 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002555 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002556 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002557
2558 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002559 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2560 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002561 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002562 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002563
2564 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002565 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2566 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002567 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002568 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002569 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002570 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002571
2572 /* DRAM command MRS
2573 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002574 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2575 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002576 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002577 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002578 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002579 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002580
2581 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002582 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002583
Angel Pons88521882020-01-05 20:21:20 +01002584 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002585 }
2586
2587 /* XXX: check any measured value ? */
2588
2589 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002590 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
2591 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002592 }
2593
2594 program_timings(ctrl, channel);
2595
2596 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002597 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002598
2599 /* DRAM command MRS
2600 * MR3 enable MPR
2601 * write MR3 MPR enable
2602 * in this mode only RD and RDA are allowed
2603 * all reads return a predefined pattern */
Angel Pons88521882020-01-05 20:21:20 +01002604 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f000;
2605 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002606 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002607 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002608 (slotrank << 24) | 0x360004;
Angel Pons88521882020-01-05 20:21:20 +01002609 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002610
2611 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002612 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f105;
2613 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x4041003;
2614 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002615 (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01002616 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002617
2618 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002619 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2620 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002621 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002622 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002623 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002624 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002625
2626 /* DRAM command MRS
2627 * MR3 disable MPR */
Angel Pons88521882020-01-05 20:21:20 +01002628 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f000;
2629 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002630 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002631 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002632 (slotrank << 24) | 0x360000;
Angel Pons88521882020-01-05 20:21:20 +01002633 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002634
Felix Held9cf1dd22018-07-31 14:52:40 +02002635 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002636 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002637
Angel Pons88521882020-01-05 20:21:20 +01002638 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002639 }
2640
2641 /* XXX: check any measured value ? */
2642
2643 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002644 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002645 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002646 }
2647
2648 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002649 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002650 }
2651
2652 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002653 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2654 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002655
2656 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2657 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002658 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002659 if (err)
2660 return err;
2661 }
2662
Angel Pons88521882020-01-05 20:21:20 +01002663 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2664 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002665
2666 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2667 err = discover_edges_real(ctrl, channel, slotrank,
2668 rising_edges[channel][slotrank]);
2669 if (err)
2670 return err;
2671 }
2672
Angel Pons88521882020-01-05 20:21:20 +01002673 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002674
2675 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2676 ctrl->timings[channel][slotrank].lanes[lane].falling =
2677 falling_edges[channel][slotrank][lane];
2678 ctrl->timings[channel][slotrank].lanes[lane].rising =
2679 rising_edges[channel][slotrank][lane];
2680 }
2681
2682 FOR_ALL_POPULATED_CHANNELS {
2683 program_timings(ctrl, channel);
2684 }
2685
2686 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002687 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002688 }
2689 return 0;
2690}
2691
2692static int discover_edges_write_real(ramctr_timing *ctrl, int channel,
2693 int slotrank, int *edges)
2694{
2695 int edge;
2696 u32 raw_statistics[MAX_EDGE_TIMING + 1];
2697 int statistics[MAX_EDGE_TIMING + 1];
2698 const int reg3000b24[] = { 0, 0xc, 0x2c };
2699 int lane, i;
2700 int lower[NUM_LANES];
2701 int upper[NUM_LANES];
2702 int pat;
2703
2704 FOR_ALL_LANES {
2705 lower[lane] = 0;
2706 upper[lane] = MAX_EDGE_TIMING;
2707 }
2708
2709 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002710 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002711 printram("[%x] = 0x%08x\n",
Angel Pons88521882020-01-05 20:21:20 +01002712 GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002713 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2714 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002715 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002716 printram("using pattern %d\n", pat);
2717 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2718 FOR_ALL_LANES {
2719 ctrl->timings[channel][slotrank].lanes[lane].
2720 rising = edge;
2721 ctrl->timings[channel][slotrank].lanes[lane].
2722 falling = edge;
2723 }
2724 program_timings(ctrl, channel);
2725
2726 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002727 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2728 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002729 }
Angel Pons88521882020-01-05 20:21:20 +01002730 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002731
2732 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002733 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2734 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002735 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002736 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002737 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002738 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002739 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002740
2741 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002742 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2743 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002744 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002745 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002746 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002747 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002748
2749 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002750 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
2751 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002752 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002753 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002754 slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01002755 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002756
2757 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002758 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2759 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002760 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002761 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002762 (slotrank << 24) | 0x60400;
Angel Pons88521882020-01-05 20:21:20 +01002763 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002764
Felix Held9cf1dd22018-07-31 14:52:40 +02002765 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002766 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) =
2767 IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002768
Angel Pons88521882020-01-05 20:21:20 +01002769 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002770 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002771 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002772 }
2773
Angel Pons891f2bc2020-01-10 01:27:28 +01002774 raw_statistics[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002775 }
2776 FOR_ALL_LANES {
2777 struct run rn;
2778 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
2779 statistics[edge] =
2780 ! !(raw_statistics[edge] & (1 << lane));
2781 rn = get_longest_zero_run(statistics,
2782 MAX_EDGE_TIMING + 1);
2783 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
2784 channel, slotrank, i, rn.start, rn.middle,
2785 rn.end, rn.start + ctrl->edge_offset[i],
2786 rn.end - ctrl->edge_offset[i]);
2787 lower[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002788 MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002789 upper[lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002790 MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002791 edges[lane] = (lower[lane] + upper[lane]) / 2;
2792 if (rn.all || (lower[lane] > upper[lane])) {
2793 printk(BIOS_EMERG, "edge write discovery failed: %d, %d, %d\n",
2794 channel, slotrank, lane);
2795 return MAKE_ERR;
2796 }
2797 }
2798 }
2799 }
2800
Angel Pons88521882020-01-05 20:21:20 +01002801 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002802 printram("CPA\n");
2803 return 0;
2804}
2805
2806int discover_edges_write(ramctr_timing *ctrl)
2807{
2808 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2809 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2810 int channel, slotrank, lane;
2811 int err;
2812
2813 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002814 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2815 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002816
2817 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2818 err = discover_edges_write_real(ctrl, channel, slotrank,
2819 falling_edges[channel][slotrank]);
2820 if (err)
2821 return err;
2822 }
2823
Angel Pons88521882020-01-05 20:21:20 +01002824 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2825 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826
2827 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2828 err = discover_edges_write_real(ctrl, channel, slotrank,
2829 rising_edges[channel][slotrank]);
2830 if (err)
2831 return err;
2832 }
2833
Angel Pons88521882020-01-05 20:21:20 +01002834 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002835
2836 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2837 ctrl->timings[channel][slotrank].lanes[lane].falling =
2838 falling_edges[channel][slotrank][lane];
2839 ctrl->timings[channel][slotrank].lanes[lane].rising =
2840 rising_edges[channel][slotrank][lane];
2841 }
2842
2843 FOR_ALL_POPULATED_CHANNELS
2844 program_timings(ctrl, channel);
2845
2846 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002847 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002848 }
2849 return 0;
2850}
2851
2852static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2853{
Angel Pons88521882020-01-05 20:21:20 +01002854 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002855 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01002856 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x1f006;
2857 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002858 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons88521882020-01-05 20:21:20 +01002859 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002860 (slotrank << 24) | 0x60000;
Angel Pons88521882020-01-05 20:21:20 +01002861 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002862
2863 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01002864 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x1f201;
2865 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002866 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002867 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
2868 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002869
2870 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01002871 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x1f105;
Angel Pons891f2bc2020-01-10 01:27:28 +01002872 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002873 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
2874 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002875
2876 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01002877 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x1f002;
2878 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
2879 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
2880 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002881
Felix Held9cf1dd22018-07-31 14:52:40 +02002882 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01002883 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002884
Angel Pons88521882020-01-05 20:21:20 +01002885 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002886}
2887
2888int discover_timC_write(ramctr_timing *ctrl)
2889{
2890 const u8 rege3c_b24[3] = { 0, 0xf, 0x2f };
2891 int i, pat;
2892
2893 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2894 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2895 int channel, slotrank, lane;
2896
2897 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2898 lower[channel][slotrank][lane] = 0;
2899 upper[channel][slotrank][lane] = MAX_TIMC;
2900 }
2901
Angel Pons88521882020-01-05 20:21:20 +01002902 /*
2903 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2904 * FIXME: This must only be done on Ivy Bridge.
2905 */
2906 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002907 printram("discover timC write:\n");
2908
2909 for (i = 0; i < 3; i++)
2910 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002911 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000,
Felix Held2463aa92018-07-29 21:37:55 +02002912 rege3c_b24[i] << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002913 udelay(2);
2914 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2915 FOR_ALL_POPULATED_RANKS {
2916 int timC;
2917 u32 raw_statistics[MAX_TIMC + 1];
2918 int statistics[MAX_TIMC + 1];
2919
2920 /* Make sure rn.start < rn.end */
2921 statistics[MAX_TIMC] = 1;
2922
2923 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002924 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002925 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002926 for (timC = 0; timC < MAX_TIMC; timC++) {
2927 FOR_ALL_LANES
2928 ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
2929 program_timings(ctrl, channel);
2930
2931 test_timC_write (ctrl, channel, slotrank);
2932
2933 raw_statistics[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002934 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002935 }
2936 FOR_ALL_LANES {
2937 struct run rn;
2938 for (timC = 0; timC < MAX_TIMC; timC++)
2939 statistics[timC] =
2940 !!(raw_statistics[timC] &
2941 (1 << lane));
2942
2943 rn = get_longest_zero_run(statistics,
2944 MAX_TIMC + 1);
2945 if (rn.all) {
2946 printk(BIOS_EMERG, "timC write discovery failed: %d, %d, %d\n",
2947 channel, slotrank, lane);
2948 return MAKE_ERR;
2949 }
2950 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x, 0x%02x-0x%02x\n",
2951 channel, slotrank, i, rn.start,
2952 rn.middle, rn.end,
2953 rn.start + ctrl->timC_offset[i],
2954 rn.end - ctrl->timC_offset[i]);
2955 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002956 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002957 lower[channel][slotrank][lane]);
2958 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002959 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002960 upper[channel][slotrank][lane]);
2961
2962 }
2963 }
2964 }
2965 }
2966
2967 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002968 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002969 udelay(2);
2970 }
2971
Angel Pons88521882020-01-05 20:21:20 +01002972 /*
2973 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2974 * FIXME: This must only be done on Ivy Bridge.
2975 */
2976 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002977
2978 printram("CPB\n");
2979
2980 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2981 printram("timC %d, %d, %d: %x\n", channel,
2982 slotrank, lane,
2983 (lower[channel][slotrank][lane] +
2984 upper[channel][slotrank][lane]) / 2);
2985 ctrl->timings[channel][slotrank].lanes[lane].timC =
2986 (lower[channel][slotrank][lane] +
2987 upper[channel][slotrank][lane]) / 2;
2988 }
2989 FOR_ALL_POPULATED_CHANNELS {
2990 program_timings(ctrl, channel);
2991 }
2992 return 0;
2993}
2994
Angel Pons88521882020-01-05 20:21:20 +01002995void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002996{
2997 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002998 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002999
3000 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
3001 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01003002 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003003 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003004 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01003005 printram("normalize %d, %d, %d: mat %d\n",
3006 channel, slotrank, lane, mat);
3007
Felix Heldef4fe3e2019-12-31 14:15:05 +01003008 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01003009 printram("normalize %d, %d, %d: delta %d\n",
3010 channel, slotrank, lane, delta);
3011
Angel Pons88521882020-01-05 20:21:20 +01003012 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01003013 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003014 }
3015
3016 FOR_ALL_POPULATED_CHANNELS {
3017 program_timings(ctrl, channel);
3018 }
3019}
3020
Angel Pons88521882020-01-05 20:21:20 +01003021void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003022{
3023 int channel, slotrank;
3024
3025 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Held283b44662020-01-14 21:14:42 +01003026 MCHBAR32(lane_base[slotrank] + (0x0004 + (channel * 0x100) + (0 * 4))) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003027 make_mr0(ctrl, slotrank);
Felix Held283b44662020-01-14 21:14:42 +01003028 MCHBAR32(lane_base[slotrank] + (0x0004 + (channel * 0x100) + (1 * 4))) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02003029 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003030 }
3031}
3032
3033int channel_test(ramctr_timing *ctrl)
3034{
3035 int channel, slotrank, lane;
3036
3037 slotrank = 0;
3038 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003039 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01003040 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003041 return MAKE_ERR;
3042 }
3043 FOR_ALL_POPULATED_CHANNELS {
3044 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
3045
Angel Pons88521882020-01-05 20:21:20 +01003046 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003047 }
3048
3049 for (slotrank = 0; slotrank < 4; slotrank++)
3050 FOR_ALL_CHANNELS
3051 if (ctrl->rankmap[channel] & (1 << slotrank)) {
3052 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003053 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
3054 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003055 }
Angel Pons88521882020-01-05 20:21:20 +01003056 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02003057
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003058 /* DRAM command ACT */
Angel Pons88521882020-01-05 20:21:20 +01003059 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 0)) = 0x0001f006;
3060 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01003061 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003062 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02003063
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003064 /* DRAM command WR */
Angel Pons88521882020-01-05 20:21:20 +01003065 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 1)) = 0x0001f201;
3066 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01003067 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01003068 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003069
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003070 /* DRAM command RD */
Angel Pons88521882020-01-05 20:21:20 +01003071 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 2)) = 0x0001f105;
3072 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01003073 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons88521882020-01-05 20:21:20 +01003074 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02003075
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003076 /* DRAM command PRE */
Angel Pons88521882020-01-05 20:21:20 +01003077 MCHBAR32(IOSAV_n_SP_CMD_CTL_ch(channel, 3)) = 0x0001f002;
3078 MCHBAR32(IOSAV_n_SUBSEQ_CTL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01003079 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons88521882020-01-05 20:21:20 +01003080 MCHBAR32(IOSAV_n_ADDR_UPD_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02003081
3082 // execute command queue
Angel Pons88521882020-01-05 20:21:20 +01003083 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02003084
Angel Pons88521882020-01-05 20:21:20 +01003085 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003086 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01003087 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003088 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
3089 channel, slotrank, lane);
3090 return MAKE_ERR;
3091 }
3092 }
3093 return 0;
3094}
3095
Angel Pons88521882020-01-05 20:21:20 +01003096void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003097{
3098 int channel;
3099
3100 /* FIXME: we hardcode seeds. Do we need to use some PRNG for them?
3101 I don't think so. */
3102 static u32 seeds[NUM_CHANNELS][3] = {
3103 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3104 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3105 };
3106 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003107 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
3108 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3109 MCHBAR32(SCRAMBLING_SEED_2_HIGH_ch(channel)) = seeds[channel][1];
3110 MCHBAR32(SCRAMBLING_SEED_2_LOW_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003111 }
3112}
3113
3114void set_4f8c(void)
3115{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003116 u32 cpu;
3117
Subrata Banik53b08c32018-12-10 14:11:35 +05303118 cpu = cpu_get_cpuid();
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003119 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons88521882020-01-05 20:21:20 +01003120 MCHBAR32(SC_WDBWM) = 0x141D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003121 } else {
Angel Pons88521882020-01-05 20:21:20 +01003122 MCHBAR32(SC_WDBWM) = 0x551D1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003123 }
3124}
3125
Angel Pons88521882020-01-05 20:21:20 +01003126void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003127{
3128 int channel;
3129
3130 FOR_ALL_POPULATED_CHANNELS {
3131 // Always drive command bus
Angel Pons88521882020-01-05 20:21:20 +01003132 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003133 }
3134
3135 udelay(1);
3136
3137 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003138 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003139 }
3140}
3141
Angel Pons88521882020-01-05 20:21:20 +01003142void set_4008c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003143{
3144 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003145
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003146 FOR_ALL_POPULATED_CHANNELS {
3147 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003148 int min_pi = 10000;
3149 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003150
3151 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003152 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3153 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003154 }
3155
Angel Pons88521882020-01-05 20:21:20 +01003156 if (max_pi - min_pi > 51)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003157 b20 = 0;
3158 else
3159 b20 = ctrl->ref_card_offset[channel];
3160
Angel Pons88521882020-01-05 20:21:20 +01003161 if (ctrl->pi_coding_threshold < max_pi - min_pi)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003162 b4_8_12 = 0x3330;
3163 else
3164 b4_8_12 = 0x2220;
3165
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003166 dram_odt_stretch(ctrl, channel);
3167
Angel Pons88521882020-01-05 20:21:20 +01003168 MCHBAR32(TC_RWP_ch(channel)) =
Felix Held2463aa92018-07-29 21:37:55 +02003169 0x0a000000 | (b20 << 20) |
3170 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003171 }
3172}
3173
Angel Pons88521882020-01-05 20:21:20 +01003174void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003175{
3176 int channel;
3177 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003178 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3179 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003180 }
3181}
3182
3183static int encode_5d10(int ns)
3184{
Angel Pons88521882020-01-05 20:21:20 +01003185 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003186}
3187
3188/* FIXME: values in this function should be hardware revision-dependent. */
Angel Pons88521882020-01-05 20:21:20 +01003189void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003190{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003191 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3192
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003193 int channel;
3194 int t1_cycles = 0, t1_ns = 0, t2_ns;
3195 int t3_ns;
3196 u32 r32;
3197
Angel Pons88521882020-01-05 20:21:20 +01003198 /* FIXME: This register only exists on Ivy Bridge. */
3199 MCHBAR32(WMM_READ_CONFIG) = 0x00000046;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003200
Felix Heldf9b826a2018-07-30 17:56:52 +02003201 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003202 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xFFFFCFFF, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003203
Patrick Rudolph74203de2017-11-20 11:57:01 +01003204 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003205 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003206 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003207 else
3208 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003209 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003210
Felix Heldf9b826a2018-07-30 17:56:52 +02003211 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003212 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003213
Angel Pons88521882020-01-05 20:21:20 +01003214 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3215 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003216
3217 FOR_ALL_CHANNELS {
3218 switch (ctrl->rankmap[channel]) {
3219 /* Unpopulated channel. */
3220 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003221 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003222 break;
3223 /* Only single-ranked dimms. */
3224 case 1:
3225 case 4:
3226 case 5:
Angel Pons88521882020-01-05 20:21:20 +01003227 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003228 break;
3229 /* Dual-ranked dimms present. */
3230 default:
Angel Pons88521882020-01-05 20:21:20 +01003231 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x9b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003232 break;
3233 }
3234 }
3235
Felix Held50b7ed22019-12-30 20:41:54 +01003236 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
3237 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0xffffff, 0xe4d5d0);
3238 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003239
3240 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003241 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~0x30000, 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003242
Angel Pons88521882020-01-05 20:21:20 +01003243 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3244 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3245 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003246
3247 /* Find a populated channel. */
3248 FOR_ALL_POPULATED_CHANNELS
3249 break;
3250
Angel Pons88521882020-01-05 20:21:20 +01003251 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3252 r32 = MCHBAR32(PM_DLL_CONFIG);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003253 if (r32 & 0x20000)
3254 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003255 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003256 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
3257 if (!(r32 & 0x20000))
3258 t1_ns += 500;
3259
Angel Pons88521882020-01-05 20:21:20 +01003260 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003261 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons88521882020-01-05 20:21:20 +01003262 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
3263 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003264 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003265 t3_ns = 500;
3266 }
3267 printk(BIOS_DEBUG, "t123: %d, %d, %d\n",
3268 t1_ns, t2_ns, t3_ns);
Felix Heldb802c072018-07-29 21:46:19 +02003269 MCHBAR32_AND_OR(0x5d10, 0xC0C0C0C0,
3270 ((encode_5d10(t1_ns) + encode_5d10(t2_ns)) << 16) |
Felix Held2bb3cdf2018-07-28 00:23:59 +02003271 (encode_5d10(t1_ns) << 8) | ((encode_5d10(t3_ns) +
Felix Heldb802c072018-07-29 21:46:19 +02003272 encode_5d10(t2_ns) + encode_5d10(t1_ns)) << 24) | 0xc);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003273}
3274
Angel Pons88521882020-01-05 20:21:20 +01003275void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003276{
3277 int channel, slotrank, lane;
3278
3279 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003280 MCHBAR32(TC_RAP_ch(channel)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003281 ctrl->tRRD
3282 | (ctrl->tRTP << 4)
3283 | (ctrl->tCKE << 8)
3284 | (ctrl->tWTR << 12)
3285 | (ctrl->tFAW << 16)
3286 | (ctrl->tWR << 24)
3287 | (ctrl->cmd_stretch[channel] << 30);
3288
3289 udelay(1);
3290
3291 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003292 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003293 }
3294
3295 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003296 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003297 }
3298
3299 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003300 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003301
3302 FOR_ALL_POPULATED_CHANNELS {
3303 udelay (1);
Angel Pons88521882020-01-05 20:21:20 +01003304 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003305 }
3306
3307 printram("CPE\n");
3308
Angel Pons88521882020-01-05 20:21:20 +01003309 MCHBAR32(GDCRTRAININGMOD) = 0;
3310 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003311
3312 printram("CP5b\n");
3313
3314 FOR_ALL_POPULATED_CHANNELS {
3315 program_timings(ctrl, channel);
3316 }
3317
3318 u32 reg, addr;
3319
Angel Pons88521882020-01-05 20:21:20 +01003320 while (!(MCHBAR32(RCOMP_TIMER) & 0x10000));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003321 do {
Angel Pons88521882020-01-05 20:21:20 +01003322 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003323 } while ((reg & 0x14) == 0);
3324
3325 // Set state of memory controller
Angel Pons88521882020-01-05 20:21:20 +01003326 MCHBAR32(MC_INIT_STATE_G) = 0x116;
3327 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003328
3329 // Wait 500us
3330 udelay(500);
3331
3332 FOR_ALL_CHANNELS {
3333 // Set valid rank CKE
3334 reg = 0;
3335 reg = (reg & ~0xf) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003336 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003337 MCHBAR32(addr) = reg;
3338
3339 // Wait 10ns for ranks to settle
3340 //udelay(0.01);
3341
3342 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3343 MCHBAR32(addr) = reg;
3344
3345 // Write reset using a NOP
3346 write_reset(ctrl);
3347 }
3348
3349 /* mrs commands. */
3350 dram_mrscommands(ctrl);
3351
3352 printram("CP5c\n");
3353
Angel Pons88521882020-01-05 20:21:20 +01003354 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003355
3356 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003357 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003358 udelay(2);
3359 }
3360
Angel Pons88521882020-01-05 20:21:20 +01003361 /*
3362 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
3363 * FIXME: This must only be done on Ivy Bridge. Moreover, this instance seems to be
3364 * spurious, because nothing else enabled this optimization before.
3365 */
3366 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003367}