blob: ab3386bebb17785dd789c8fe31d0a311dcb8c3b0 [file] [log] [blame]
Angel Pons6e5aabd2020-03-23 23:44:42 +01001/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01004#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01005#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +05306#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02007#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02008#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01009#include <northbridge/intel/sandybridge/chip.h>
10#include <device/pci_def.h>
11#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020012
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010013#include "raminit_native.h"
14#include "raminit_common.h"
Angel Pons7f6586f2020-03-21 12:45:12 +010015#include "raminit_tables.h"
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010016#include "sandybridge.h"
17
Angel Pons7c49cb82020-03-16 23:17:32 +010018/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010019
Angel Pons88521882020-01-05 20:21:20 +010020/* length: [1..4] */
21#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1)
Felix Held9cf1dd22018-07-31 14:52:40 +020022
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010023static void sfence(void)
24{
25 asm volatile ("sfence");
26}
27
Angel Pons7c49cb82020-03-16 23:17:32 +010028/* Toggle IO reset bit */
29static void toggle_io_reset(void)
30{
Angel Pons88521882020-01-05 20:21:20 +010031 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010032 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010033 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010034 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010035 udelay(1);
36}
37
38static u32 get_XOVER_CLK(u8 rankmap)
39{
40 return rankmap << 24;
41}
42
43static u32 get_XOVER_CMD(u8 rankmap)
44{
45 u32 reg;
46
Angel Pons7c49cb82020-03-16 23:17:32 +010047 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010048 reg = 0x4000;
49
Angel Pons7c49cb82020-03-16 23:17:32 +010050 /* Enable xover ctl */
51 if (rankmap & 0x03)
52 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010053
Angel Pons7c49cb82020-03-16 23:17:32 +010054 if (rankmap & 0x0c)
55 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010056
57 return reg;
58}
59
Angel Pons7c49cb82020-03-16 23:17:32 +010060/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010061u8 get_CWL(u32 tCK)
62{
Angel Pons7c49cb82020-03-16 23:17:32 +010063 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010064 switch (tCK) {
65 case TCK_1333MHZ:
66 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010067
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010068 case TCK_1200MHZ:
69 case TCK_1100MHZ:
70 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010071
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010072 case TCK_1066MHZ:
73 case TCK_1000MHZ:
74 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010075
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010076 case TCK_933MHZ:
77 case TCK_900MHZ:
78 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +010079
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010080 case TCK_800MHZ:
81 case TCK_700MHZ:
82 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +010083
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010084 case TCK_666MHZ:
85 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +010086
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010087 case TCK_533MHZ:
88 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +010089
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010090 default:
91 return 5;
92 }
93}
94
95void dram_find_common_params(ramctr_timing *ctrl)
96{
97 size_t valid_dimms;
98 int channel, slot;
99 dimm_info *dimms = &ctrl->info;
100
101 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
102 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100103
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100104 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100105
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100106 const dimm_attr *dimm = &dimms->dimm[channel][slot];
107 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
108 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100109
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100110 valid_dimms++;
111
112 /* Find all possible CAS combinations */
113 ctrl->cas_supported &= dimm->cas_supported;
114
115 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100116 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
117 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
118 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100119 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
120 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100121 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100122 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
123 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
124 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
125 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
126 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300127 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
128 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100129 }
130
131 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100132 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
133
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100134 if (!valid_dimms)
135 die("No valid DIMMs found");
136}
137
Angel Pons88521882020-01-05 20:21:20 +0100138void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100139{
140 u32 reg;
141 int channel;
142
143 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100144 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100145 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100146 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
147 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100148
Angel Pons7c49cb82020-03-16 23:17:32 +0100149 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100150 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100151 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
152 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100153 }
154}
155
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100156static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100157{
Angel Pons89ae6b82020-03-21 13:23:32 +0100158 u32 addr, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100159
160 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100161 /*
162 * ODT stretch:
163 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
164 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100165 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100166 if (stretch == 2)
167 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100168
Angel Pons88521882020-01-05 20:21:20 +0100169 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100170 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
171 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100172 } else {
Angel Pons88521882020-01-05 20:21:20 +0100173 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100174 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800175 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100176 }
177}
178
179void dram_timing_regs(ramctr_timing *ctrl)
180{
181 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100182 int channel;
183
184 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100185 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100186 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100187 reg |= (ctrl->tRCD << 0);
188 reg |= (ctrl->tRP << 4);
189 reg |= (ctrl->CAS << 8);
190 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100191 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100192 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
193 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100194
Angel Pons7c49cb82020-03-16 23:17:32 +0100195 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100196 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100197 reg |= (ctrl->tRRD << 0);
198 reg |= (ctrl->tRTP << 4);
199 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100200 reg |= (ctrl->tWTR << 12);
201 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100202 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100203 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100204 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
205 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100206
Angel Pons7c49cb82020-03-16 23:17:32 +0100207 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100208 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100209 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100210 reg |= (ctrl->tXPDLL << 0);
211 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100212 reg |= (ctrl->tAONPD << 8);
213 reg |= 0xa0000;
214 printram("OTHP [%x] = %x\n", addr, reg);
215 MCHBAR32(addr) = reg;
216
Angel Ponsca2f68a2020-03-22 13:15:12 +0100217 /* Debug parameters - only applies to Ivy Bridge */
218 if (IS_IVY_CPU(ctrl->cpu)) {
219 reg = 0;
220
221 /*
222 * If tXP and tXPDLL are very high, we need to increase them by one.
223 * This can only happen on Ivy Bridge, and when overclocking the RAM.
224 */
225 if (ctrl->tXP >= 8)
226 reg |= (1 << 12);
227
228 if (ctrl->tXPDLL >= 32)
229 reg |= (1 << 13);
230
231 MCHBAR32(TC_DTP_ch(channel)) = reg;
232 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100233
Felix Held9fe248f2018-07-31 20:59:45 +0200234 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100235
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100236 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100237
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100238 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100239 * TC-Refresh timing parameters:
240 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
241 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100242 */
243 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
244
Angel Pons7c49cb82020-03-16 23:17:32 +0100245 reg = ((ctrl->tREFI & 0xffff) << 0) |
246 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
247
Angel Pons88521882020-01-05 20:21:20 +0100248 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
249 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100250
Angel Pons88521882020-01-05 20:21:20 +0100251 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100252
Angel Pons7c49cb82020-03-16 23:17:32 +0100253 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100254 reg = 0;
255 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100256 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100257 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100258 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100259 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100260 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100261 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100262 reg = (reg & ~0xf0000000) | (val32 << 28);
263 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100264 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100265 }
266}
267
268void dram_dimm_mapping(ramctr_timing *ctrl)
269{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100270 int channel;
271 dimm_info *info = &ctrl->info;
272
273 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200274 dimm_attr *dimmA, *dimmB;
275 u32 reg = 0;
276
Angel Pons7c49cb82020-03-16 23:17:32 +0100277 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100278 dimmA = &info->dimm[channel][0];
279 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100280 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100281 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100282 dimmA = &info->dimm[channel][1];
283 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100284 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100285 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100286
Nico Huberac4f2162017-10-01 18:14:43 +0200287 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100288 reg |= (dimmA->size_mb / 256) << 0;
289 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200290 reg |= (dimmA->width / 8 - 1) << 19;
291 }
292
293 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100294 reg |= (dimmB->size_mb / 256) << 8;
295 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200296 reg |= (dimmB->width / 8 - 1) << 20;
297 }
298
Angel Pons7c49cb82020-03-16 23:17:32 +0100299 reg |= 1 << 21; /* Rank interleave */
300 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200301
Angel Pons7c49cb82020-03-16 23:17:32 +0100302 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100303 ctrl->mad_dimm[channel] = reg;
304 } else {
305 ctrl->mad_dimm[channel] = 0;
306 }
307 }
308}
309
Patrick Rudolphdd662872017-10-28 18:20:11 +0200310void dram_dimm_set_mapping(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100311{
312 int channel;
Patrick Rudolphdd662872017-10-28 18:20:11 +0200313 u32 ecc;
314
315 if (ctrl->ecc_enabled)
316 ecc = training ? (1 << 24) : (3 << 24);
317 else
318 ecc = 0;
319
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100320 FOR_ALL_CHANNELS {
Patrick Rudolphdd662872017-10-28 18:20:11 +0200321 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100322 }
Patrick Rudolphdd662872017-10-28 18:20:11 +0200323
324 //udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100325}
326
Angel Pons88521882020-01-05 20:21:20 +0100327void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100328{
329 u32 reg, ch0size, ch1size;
330 u8 val;
331 reg = 0;
332 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100333
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100334 if (training) {
335 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
336 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
337 } else {
338 ch0size = ctrl->channel_size_mb[0];
339 ch1size = ctrl->channel_size_mb[1];
340 }
341
342 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100343 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100344 val = ch1size / 256;
345 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100346 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100347 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100348 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100349
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100350 } else {
Angel Pons88521882020-01-05 20:21:20 +0100351 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100352 val = ch0size / 256;
353 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100354 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100355 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100356 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100357 }
358}
359
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100360#define DEFAULT_PCI_MMIO_SIZE 2048
361
362static unsigned int get_mmio_size(void)
363{
364 const struct device *dev;
365 const struct northbridge_intel_sandybridge_config *cfg = NULL;
366
Angel Ponsb31d1d72020-01-10 01:35:09 +0100367 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100368 if (dev)
369 cfg = dev->chip_info;
370
371 /* If this is zero, it just means devicetree.cb didn't set it */
372 if (!cfg || cfg->pci_mmio_size == 0)
373 return DEFAULT_PCI_MMIO_SIZE;
374 else
375 return cfg->pci_mmio_size;
376}
377
Patrick Rudolph05d4bf7e2017-10-28 16:36:09 +0200378/*
379 * Returns the ECC mode the NB is running at. It takes precedence over ECC capability.
380 * The ME/PCU/.. has the ability to change this.
381 * Return 0: ECC is optional
382 * Return 1: ECC is forced
383 */
384bool get_host_ecc_forced(void)
385{
386 /* read Capabilities A Register */
387 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
388 return !!(reg32 & (1 << 24));
389}
390
391/*
392 * Returns the ECC capability.
393 * The ME/PCU/.. has the ability to change this.
394 * Return 0: ECC is disabled
395 * Return 1: ECC is possible
396 */
397bool get_host_ecc_cap(void)
398{
399 /* read Capabilities A Register */
400 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
401 return !(reg32 & (1 << 25));
402}
403
Angel Pons88521882020-01-05 20:21:20 +0100404void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100405{
Angel Pons7c49cb82020-03-16 23:17:32 +0100406 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
407 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
408 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100409 uint16_t ggc;
410
411 mmiosize = get_mmio_size();
412
Felix Held87ddea22020-01-26 04:55:27 +0100413 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100414 if (!(ggc & 2)) {
415 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100416 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100417 } else {
418 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100419 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100420 }
421
422 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
423
424 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
425
426 mestolenbase = tom - me_uma_size;
427
Angel Pons7c49cb82020-03-16 23:17:32 +0100428 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
429
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100430 gfxstolenbase = toludbase - gfxstolen;
431 gttbase = gfxstolenbase - gttsize;
432
433 tsegbase = gttbase - tsegsize;
434
Angel Pons7c49cb82020-03-16 23:17:32 +0100435 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100436 tsegbasedelta = tsegbase & (tsegsize - 1);
437 tsegbase &= ~(tsegsize - 1);
438
439 gttbase -= tsegbasedelta;
440 gfxstolenbase -= tsegbasedelta;
441 toludbase -= tsegbasedelta;
442
Angel Pons7c49cb82020-03-16 23:17:32 +0100443 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100444 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100445 /* Reclaim is possible */
446 reclaim = 1;
447 remapbase = MAX(4096, tom - me_uma_size);
448 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
449 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100450 } else {
451 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100452 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100453 touudbase = tom - me_uma_size;
454 }
455
Angel Pons7c49cb82020-03-16 23:17:32 +0100456 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100457 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
458
Angel Pons7c49cb82020-03-16 23:17:32 +0100459 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100460 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100461 val = tom & 0xfff;
462 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100463 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100464 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100465
Angel Ponsb31d1d72020-01-10 01:35:09 +0100466 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100467 val = tom & 0xfffff000;
468 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100469 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100470 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100471
Angel Pons7c49cb82020-03-16 23:17:32 +0100472 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100473 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100474 val = toludbase & 0xfff;
475 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100476 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100477 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100478
Angel Pons7c49cb82020-03-16 23:17:32 +0100479 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100480 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100481 val = touudbase & 0xfff;
482 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100483 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100484 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100485
Angel Pons7c49cb82020-03-16 23:17:32 +0100486 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100487 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100488 val = touudbase & 0xfffff000;
489 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100490 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100491 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100492
493 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100494 /* REMAP BASE */
495 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100496 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100497
Angel Pons7c49cb82020-03-16 23:17:32 +0100498 /* REMAP LIMIT */
499 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100500 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100501 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100502 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100503 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100504 val = tsegbase & 0xfff;
505 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100506 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100507 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100508
Angel Pons7c49cb82020-03-16 23:17:32 +0100509 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100510 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100511 val = gfxstolenbase & 0xfff;
512 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100513 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100514 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100515
Angel Pons7c49cb82020-03-16 23:17:32 +0100516 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100517 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100518 val = gttbase & 0xfff;
519 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100520 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100521 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100522
523 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100524 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100525 val = (0x80000 - me_uma_size) & 0xfffff000;
526 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100527 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100528 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100529
Angel Pons7c49cb82020-03-16 23:17:32 +0100530 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100531 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100532 val = mestolenbase & 0xfff;
533 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100534 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100535 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100536
Angel Ponsb31d1d72020-01-10 01:35:09 +0100537 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100538 val = mestolenbase & 0xfffff000;
539 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100540 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100541 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100542
Angel Pons7c49cb82020-03-16 23:17:32 +0100543 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100544 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100545 val = (0x80000 - me_uma_size) & 0xfff;
546 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100547 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
548 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100549 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100550 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100551 }
552}
553
Angel Pons88521882020-01-05 20:21:20 +0100554static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100555{
556 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100557 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100558 return;
559 }
560}
561
Angel Pons88521882020-01-05 20:21:20 +0100562static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100563{
564 int channel, slotrank;
565
Angel Pons7c49cb82020-03-16 23:17:32 +0100566 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100567 channel = (ctrl->rankmap[0]) ? 0 : 1;
568
Angel Pons88521882020-01-05 20:21:20 +0100569 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100570
Angel Pons7c49cb82020-03-16 23:17:32 +0100571 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100572 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
573
574 /* DRAM command ZQCS */
Angel Pons69e17142020-03-23 12:26:29 +0100575 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +0100576 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x80c01;
Angel Pons88521882020-01-05 20:21:20 +0100577 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100578 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100579
Angel Pons7c49cb82020-03-16 23:17:32 +0100580 /*
581 * Execute command queue - why is bit 22 set here?!
582 *
583 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
584 */
Angel Pons88521882020-01-05 20:21:20 +0100585 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200586
Angel Pons88521882020-01-05 20:21:20 +0100587 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100588}
589
Angel Pons88521882020-01-05 20:21:20 +0100590void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100591{
Felix Held9fe248f2018-07-31 20:59:45 +0200592 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100593 int channel;
594
Angel Pons7c49cb82020-03-16 23:17:32 +0100595 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
596 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100597 do {
Angel Pons88521882020-01-05 20:21:20 +0100598 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100599 } while ((reg & 0x14) == 0);
600
Angel Pons7c49cb82020-03-16 23:17:32 +0100601 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100602 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100603 MCHBAR32(MC_INIT_STATE_G) = reg;
604 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100605 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100606 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100607
Angel Pons7c49cb82020-03-16 23:17:32 +0100608 /* Assert DIMM reset signal */
609 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100610
Angel Pons7c49cb82020-03-16 23:17:32 +0100611 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100612 udelay(200);
613
Angel Pons7c49cb82020-03-16 23:17:32 +0100614 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100615 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100616
Angel Pons7c49cb82020-03-16 23:17:32 +0100617 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100618 udelay(500);
619
Angel Pons7c49cb82020-03-16 23:17:32 +0100620 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100621 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100622
Angel Pons7c49cb82020-03-16 23:17:32 +0100623 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100624 udelay(1);
625
626 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100627 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200628 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100629 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100630
Angel Pons7c49cb82020-03-16 23:17:32 +0100631 /* Wait 10ns for ranks to settle */
632 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100633
634 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100635 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100636
Angel Pons7c49cb82020-03-16 23:17:32 +0100637 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100638 write_reset(ctrl);
639 }
640}
641
642static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
643{
Angel Pons7c49cb82020-03-16 23:17:32 +0100644 /* Get ODT based on rankmap */
645 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100646
647 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100648 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100649 } else {
650 return (const odtmap){120, 30};
651 }
652}
653
Angel Pons7c49cb82020-03-16 23:17:32 +0100654static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100655{
Angel Pons88521882020-01-05 20:21:20 +0100656 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657
658 if (ctrl->rank_mirror[channel][slotrank]) {
659 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100660 swap the following pins:
661 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100662 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100663 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100664 }
665
666 /* DRAM command MRS */
Angel Pons69e17142020-03-23 12:26:29 +0100667 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +0100668 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100669 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Angel Pons2b6bb792020-05-02 16:46:44 +0200670 val | 0x60000 | (reg << 20) | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +0100671 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100672
673 /* DRAM command MRS */
Angel Pons69e17142020-03-23 12:26:29 +0100674 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +0100675 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100676 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons2b6bb792020-05-02 16:46:44 +0200677 val | 0x60000 | (reg << 20) | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +0100678 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100679
680 /* DRAM command MRS */
Angel Pons69e17142020-03-23 12:26:29 +0100681 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_MRS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +0100682 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +0100683 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Angel Pons2b6bb792020-05-02 16:46:44 +0200684 val | 0x60000 | (reg << 20) | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +0100685 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200686
Angel Pons7c49cb82020-03-16 23:17:32 +0100687 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100688 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100689}
690
Angel Pons88521882020-01-05 20:21:20 +0100691static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100692{
693 u16 mr0reg, mch_cas, mch_wr;
694 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 +0100695 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100696
697 /* DLL Reset - self clearing - set after CLK frequency has been changed */
698 mr0reg = 0x100;
699
Angel Pons7c49cb82020-03-16 23:17:32 +0100700 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100701 if (ctrl->CAS < 12) {
702 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
703 } else {
704 mch_cas = (u16) (ctrl->CAS - 12);
705 mch_cas = ((mch_cas << 1) | 0x1);
706 }
707
Angel Pons7c49cb82020-03-16 23:17:32 +0100708 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100709 mch_wr = mch_wr_t[ctrl->tWR - 5];
710
Angel Pons7c49cb82020-03-16 23:17:32 +0100711 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
712 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
713 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100714
Angel Pons7c49cb82020-03-16 23:17:32 +0100715 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
716 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100717 return mr0reg;
718}
719
720static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
721{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200722 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100723}
724
725static u32 encode_odt(u32 odt)
726{
727 switch (odt) {
728 case 30:
729 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
730 case 60:
731 return (1 << 2); // RZQ/4
732 case 120:
733 return (1 << 6); // RZQ/2
734 default:
735 case 0:
736 return 0;
737 }
738}
739
740static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
741{
742 odtmap odt;
743 u32 mr1reg;
744
745 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100746 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100747
748 mr1reg |= encode_odt(odt.rttnom);
749
750 return mr1reg;
751}
752
753static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
754{
755 u16 mr1reg;
756
757 mr1reg = make_mr1(ctrl, rank, channel);
758
759 write_mrreg(ctrl, channel, rank, 1, mr1reg);
760}
761
762static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
763{
764 u16 pasr, cwl, mr2reg;
765 odtmap odt;
766 int srt;
767
768 pasr = 0;
769 cwl = ctrl->CWL - 5;
770 odt = get_ODT(ctrl, rank, channel);
771
772 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
773
774 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100775 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100776 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
777 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
778 mr2reg = (mr2reg & ~0x80) | (srt << 7);
779 mr2reg |= (odt.rttwr / 60) << 9;
780
781 write_mrreg(ctrl, channel, rank, 2, mr2reg);
782}
783
784static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
785{
786 write_mrreg(ctrl, channel, rank, 3, 0);
787}
788
Angel Pons88521882020-01-05 20:21:20 +0100789void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100790{
791 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100792 int channel;
793
794 FOR_ALL_POPULATED_CHANNELS {
795 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100796 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100797 dram_mr2(ctrl, slotrank, channel);
798
Angel Pons7c49cb82020-03-16 23:17:32 +0100799 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100800 dram_mr3(ctrl, slotrank, channel);
801
Angel Pons7c49cb82020-03-16 23:17:32 +0100802 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100803 dram_mr1(ctrl, slotrank, channel);
804
Angel Pons7c49cb82020-03-16 23:17:32 +0100805 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100806 dram_mr0(ctrl, slotrank, channel);
807 }
808 }
809
Angel Pons69e17142020-03-23 12:26:29 +0100810 /* DRAM command NOP (without ODT nor chip selects) */
Angel Pons8e661242020-05-02 19:02:53 +0200811 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(BROADCAST_CH, 0)) = IOSAV_NOP & NO_RANKSEL & ~(0xff << 8);
812 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(BROADCAST_CH, 0)) = 0xf1001;
813 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(BROADCAST_CH, 0)) = 0x60002;
814 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(BROADCAST_CH, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100815
816 /* DRAM command ZQCL */
Angel Pons8e661242020-05-02 19:02:53 +0200817 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(BROADCAST_CH, 1)) = IOSAV_ZQCS;
818 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(BROADCAST_CH, 1)) = 0x1901001;
819 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(BROADCAST_CH, 1)) = 0x60400;
820 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(BROADCAST_CH, 1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100821
Angel Pons7c49cb82020-03-16 23:17:32 +0100822 /* Execute command queue on all channels. Do it four times. */
823 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100824
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100825 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100826 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100827 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100828 }
829
Angel Pons7c49cb82020-03-16 23:17:32 +0100830 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100831 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100832
833 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100834 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100835
Angel Pons88521882020-01-05 20:21:20 +0100836 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100837
838 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
839
Angel Pons7c49cb82020-03-16 23:17:32 +0100840 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100841 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100842
843 /* DRAM command ZQCS */
Angel Pons69e17142020-03-23 12:26:29 +0100844 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +0100845 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
846 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
847 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200848
Angel Pons7c49cb82020-03-16 23:17:32 +0100849 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100850 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100851
Angel Pons7c49cb82020-03-16 23:17:32 +0100852 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100853 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100854 }
855}
856
Felix Held3b906032020-01-14 17:05:43 +0100857static const u32 lane_base[] = {
858 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
859 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
860 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100861};
862
Angel Pons88521882020-01-05 20:21:20 +0100863void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100864{
Angel Pons88521882020-01-05 20:21:20 +0100865 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100866 int lane;
867 int slotrank, slot;
868 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100869 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100870
871 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100872 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
873 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100874 }
875
876 for (slot = 0; slot < NUM_SLOTS; slot++)
877 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
878 case 0:
879 default:
Angel Pons88521882020-01-05 20:21:20 +0100880 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100881 break;
882 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100883 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100884 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100885 break;
886 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100887 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100888 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100889 break;
890 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100891 pi_coding_ctrl[slot] =
892 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100893 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100894 break;
895 }
896
Angel Pons7c49cb82020-03-16 23:17:32 +0100897 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100898 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100899 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
900 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100901 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100902 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
903
Angel Pons88521882020-01-05 20:21:20 +0100904 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100905
Angel Pons7c49cb82020-03-16 23:17:32 +0100906 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100907 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
908 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100909
910 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100911 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100912 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100913 if (shift < 0)
914 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100915
Angel Pons88521882020-01-05 20:21:20 +0100916 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100917
918 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100919 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
920 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100921 }
922
Angel Pons88521882020-01-05 20:21:20 +0100923 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
924 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100925
Angel Pons88521882020-01-05 20:21:20 +0100926 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100927 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100928
Angel Pons88521882020-01-05 20:21:20 +0100929 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100930
931 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100932 int post_timA_min_high = 7, pre_timA_min_high = 7;
933 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100934 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100935 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100936
937 if (shift < 0)
938 shift = 0;
939
940 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200941 post_timA_min_high = MIN(post_timA_min_high,
942 (ctrl->timings[channel][slotrank].lanes[lane].
943 timA + shift) >> 6);
944 pre_timA_min_high = MIN(pre_timA_min_high,
945 ctrl->timings[channel][slotrank].lanes[lane].
946 timA >> 6);
947 post_timA_max_high = MAX(post_timA_max_high,
948 (ctrl->timings[channel][slotrank].lanes[lane].
949 timA + shift) >> 6);
950 pre_timA_max_high = MAX(pre_timA_max_high,
951 ctrl->timings[channel][slotrank].lanes[lane].
952 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100953 }
954
955 if (pre_timA_max_high - pre_timA_min_high <
956 post_timA_max_high - post_timA_min_high)
957 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100958
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100959 else if (pre_timA_max_high - pre_timA_min_high >
960 post_timA_max_high - post_timA_min_high)
961 shift_402x = -1;
962
Felix Helddee167e2019-12-30 17:30:16 +0100963 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100964 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100965 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100966
Angel Pons88521882020-01-05 20:21:20 +0100967 reg_roundtrip_latency |=
968 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100969 shift_402x) << (8 * slotrank);
970
971 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100972 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100973 (((ctrl->timings[channel][slotrank].lanes[lane].
974 timA + shift) & 0x3f)
975 |
976 ((ctrl->timings[channel][slotrank].lanes[lane].
977 rising + shift) << 8)
978 |
979 (((ctrl->timings[channel][slotrank].lanes[lane].
980 timA + shift -
981 (post_timA_min_high << 6)) & 0x1c0) << 10)
982 | ((ctrl->timings[channel][slotrank].lanes[lane].
983 falling + shift) << 20));
984
Felix Heldfb19c8a2020-01-14 21:27:59 +0100985 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100986 (((ctrl->timings[channel][slotrank].lanes[lane].
987 timC + shift) & 0x3f)
988 |
989 (((ctrl->timings[channel][slotrank].lanes[lane].
990 timB + shift) & 0x3f) << 8)
991 |
992 (((ctrl->timings[channel][slotrank].lanes[lane].
993 timB + shift) & 0x1c0) << 9)
994 |
995 (((ctrl->timings[channel][slotrank].lanes[lane].
996 timC + shift) & 0x40) << 13));
997 }
998 }
Angel Pons88521882020-01-05 20:21:20 +0100999 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1000 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001001}
1002
Angel Pons88521882020-01-05 20:21:20 +01001003static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001004{
Angel Pons88521882020-01-05 20:21:20 +01001005 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001006
1007 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001008 write MR3 MPR enable
1009 in this mode only RD and RDA are allowed
1010 all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01001011 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001012 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
Angel Pons88521882020-01-05 20:21:20 +01001013 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001014 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001015
1016 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001017 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001018 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4040c01;
Angel Pons88521882020-01-05 20:21:20 +01001019 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001020 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001021
1022 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001023 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001024 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001025 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001026 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001027
1028 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001029 write MR3 MPR disable */
Angel Pons69e17142020-03-23 12:26:29 +01001030 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001031 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001032 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001033 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001034
Angel Pons7c49cb82020-03-16 23:17:32 +01001035 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001036 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001037
Angel Pons88521882020-01-05 20:21:20 +01001038 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001039}
1040
Angel Pons7c49cb82020-03-16 23:17:32 +01001041static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001042{
1043 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001044
1045 return (MCHBAR32(lane_base[lane] +
1046 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001047}
1048
1049struct run {
1050 int middle;
1051 int end;
1052 int start;
1053 int all;
1054 int length;
1055};
1056
1057static struct run get_longest_zero_run(int *seq, int sz)
1058{
1059 int i, ls;
1060 int bl = 0, bs = 0;
1061 struct run ret;
1062
1063 ls = 0;
1064 for (i = 0; i < 2 * sz; i++)
1065 if (seq[i % sz]) {
1066 if (i - ls > bl) {
1067 bl = i - ls;
1068 bs = ls;
1069 }
1070 ls = i + 1;
1071 }
1072 if (bl == 0) {
1073 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001074 ret.start = 0;
1075 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001076 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001077 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001078 return ret;
1079 }
1080
Angel Pons7c49cb82020-03-16 23:17:32 +01001081 ret.start = bs % sz;
1082 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001083 ret.middle = (bs + (bl - 1) / 2) % sz;
1084 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001085 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001086
1087 return ret;
1088}
1089
Angel Pons7c49cb82020-03-16 23:17:32 +01001090static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001091{
1092 int timA;
1093 int statistics[NUM_LANES][128];
1094 int lane;
1095
1096 for (timA = 0; timA < 128; timA++) {
1097 FOR_ALL_LANES {
1098 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1099 }
1100 program_timings(ctrl, channel);
1101
1102 test_timA(ctrl, channel, slotrank);
1103
1104 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001105 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001106 }
1107 }
1108 FOR_ALL_LANES {
1109 struct run rn = get_longest_zero_run(statistics[lane], 128);
1110 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1111 upperA[lane] = rn.end;
1112 if (upperA[lane] < rn.middle)
1113 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001114
Patrick Rudolph368b6152016-11-25 16:36:52 +01001115 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001116 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001117 }
1118}
1119
Angel Pons7c49cb82020-03-16 23:17:32 +01001120static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001121{
1122 int timA_delta;
1123 int statistics[NUM_LANES][51];
1124 int lane, i;
1125
1126 memset(statistics, 0, sizeof(statistics));
1127
1128 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001129
1130 FOR_ALL_LANES {
1131 ctrl->timings[channel][slotrank].lanes[lane].timA
1132 = upperA[lane] + timA_delta + 0x40;
1133 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001134 program_timings(ctrl, channel);
1135
1136 for (i = 0; i < 100; i++) {
1137 test_timA(ctrl, channel, slotrank);
1138 FOR_ALL_LANES {
1139 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001140 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001141 }
1142 }
1143 }
1144 FOR_ALL_LANES {
1145 int last_zero, first_all;
1146
1147 for (last_zero = -25; last_zero <= 25; last_zero++)
1148 if (statistics[lane][last_zero + 25])
1149 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001150
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001151 last_zero--;
1152 for (first_all = -25; first_all <= 25; first_all++)
1153 if (statistics[lane][first_all + 25] == 100)
1154 break;
1155
Angel Pons7c49cb82020-03-16 23:17:32 +01001156 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001157
1158 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001159 (last_zero + first_all) / 2 + upperA[lane];
1160
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001161 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001162 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001163 }
1164}
1165
Angel Pons891f2bc2020-01-10 01:27:28 +01001166static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001167{
1168 int works[NUM_LANES];
1169 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001170
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001171 while (1) {
1172 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001173
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001174 program_timings(ctrl, channel);
1175 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001176
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001177 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001178 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1179
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001180 if (works[lane])
1181 some_works = 1;
1182 else
1183 all_works = 0;
1184 }
1185 if (all_works)
1186 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001187
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001188 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001189 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001190 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1191 channel, slotrank);
1192 return MAKE_ERR;
1193 }
Angel Pons88521882020-01-05 20:21:20 +01001194 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001195 printram("4024 -= 2;\n");
1196 continue;
1197 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001198 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001199 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001200
Felix Heldef4fe3e2019-12-31 14:15:05 +01001201 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001202 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1203 channel, slotrank);
1204 return MAKE_ERR;
1205 }
1206 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001207 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001208 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001209 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 }
1211 }
1212 return 0;
1213}
1214
1215struct timA_minmax {
1216 int timA_min_high, timA_max_high;
1217};
1218
Angel Pons88521882020-01-05 20:21:20 +01001219static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001220 struct timA_minmax *mnmx)
1221{
1222 int lane;
1223 mnmx->timA_min_high = 7;
1224 mnmx->timA_max_high = 0;
1225
1226 FOR_ALL_LANES {
1227 if (mnmx->timA_min_high >
1228 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1229 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001230 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001231 if (mnmx->timA_max_high <
1232 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1233 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001234 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001235 }
1236}
1237
Angel Pons88521882020-01-05 20:21:20 +01001238static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001239 struct timA_minmax *mnmx)
1240{
1241 struct timA_minmax post;
1242 int shift_402x = 0;
1243
Angel Pons7c49cb82020-03-16 23:17:32 +01001244 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001245 pre_timA_change(ctrl, channel, slotrank, &post);
1246
1247 if (mnmx->timA_max_high - mnmx->timA_min_high <
1248 post.timA_max_high - post.timA_min_high)
1249 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001250
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001251 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1252 post.timA_max_high - post.timA_min_high)
1253 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001254
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001255 else
1256 shift_402x = 0;
1257
Felix Heldef4fe3e2019-12-31 14:15:05 +01001258 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001259 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001260 printram("4024 += %d;\n", shift_402x);
1261 printram("4028 += %d;\n", shift_402x);
1262}
1263
Angel Pons7c49cb82020-03-16 23:17:32 +01001264/*
1265 * Compensate the skew between DQS and DQs.
1266 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001267 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1268 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001269 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001270 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001271 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001272 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1273 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1274 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001275 * DQ in phase the data being read is expected to alternate on every byte:
1276 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001277 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001278 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001279 * Once the controller has detected this pattern a bit in the result register is set for the
1280 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001281 */
Angel Pons88521882020-01-05 20:21:20 +01001282int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001283{
1284 int channel, slotrank, lane;
1285 int err;
1286
1287 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1288 int all_high, some_high;
1289 int upperA[NUM_LANES];
1290 struct timA_minmax mnmx;
1291
Angel Pons88521882020-01-05 20:21:20 +01001292 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001293
Felix Held2bb3cdf2018-07-28 00:23:59 +02001294 /* DRAM command PREA */
Angel Pons69e17142020-03-23 12:26:29 +01001295 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01001296 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001297 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001298 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001299
Angel Pons7c49cb82020-03-16 23:17:32 +01001300 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001301 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001302
Angel Pons88521882020-01-05 20:21:20 +01001303 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001304
Felix Heldef4fe3e2019-12-31 14:15:05 +01001305 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001306 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001307 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001308
Felix Held2bb3cdf2018-07-28 00:23:59 +02001309 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001310
Felix Held2bb3cdf2018-07-28 00:23:59 +02001311 all_high = 1;
1312 some_high = 0;
1313 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001314 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001315 some_high = 1;
1316 else
1317 all_high = 0;
1318 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001319
1320 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001321 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001322 printram("4028--;\n");
1323 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001324 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001325 upperA[lane] -= 0x40;
1326
1327 }
1328 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001329 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001330 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001331 printram("4024++;\n");
1332 printram("4028++;\n");
1333 }
1334
1335 program_timings(ctrl, channel);
1336
1337 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1338
1339 err = discover_402x(ctrl, channel, slotrank, upperA);
1340 if (err)
1341 return err;
1342
1343 post_timA_change(ctrl, channel, slotrank, &mnmx);
1344 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1345
1346 discover_timA_fine(ctrl, channel, slotrank, upperA);
1347
1348 post_timA_change(ctrl, channel, slotrank, &mnmx);
1349 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1350
1351 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001352 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1353 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001354 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001355 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001356 printram("4028 -= %d;\n", mnmx.timA_min_high);
1357
1358 post_timA_change(ctrl, channel, slotrank, &mnmx);
1359
1360 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001361 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001362 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001363
1364 printram("final results:\n");
1365 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001366 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001367 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001368
Angel Pons88521882020-01-05 20:21:20 +01001369 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001370
1371 toggle_io_reset();
1372 }
1373
1374 FOR_ALL_POPULATED_CHANNELS {
1375 program_timings(ctrl, channel);
1376 }
1377 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001378 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001379 }
1380 return 0;
1381}
1382
Angel Pons88521882020-01-05 20:21:20 +01001383static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001384{
1385 int lane;
1386
1387 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001388 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1389 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001390 }
1391
Angel Pons88521882020-01-05 20:21:20 +01001392 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001393
1394 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01001395 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01001396 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
1397 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001398 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001399 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001400
1401 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001402 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_NOP;
Angel Pons7c49cb82020-03-16 23:17:32 +01001403 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8041001;
Angel Pons88521882020-01-05 20:21:20 +01001404 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001405 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001406
1407 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01001408 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01001409 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x80411f4;
Angel Pons88521882020-01-05 20:21:20 +01001410 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001411 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001412
1413 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001414 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_NOP;
Angel Pons7c49cb82020-03-16 23:17:32 +01001415 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
1416 0x08000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001417 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001418 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001419
Angel Pons7c49cb82020-03-16 23:17:32 +01001420 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001421 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001422
Angel Pons88521882020-01-05 20:21:20 +01001423 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001424
1425 /* DRAM command PREA */
Angel Pons69e17142020-03-23 12:26:29 +01001426 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01001427 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001428 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001429 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001430
1431 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01001432 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01001433 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
1434 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001435 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001436 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001437
1438 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001439 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001440 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001441 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001442 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001443 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001444
1445 /* DRAM command PREA */
Angel Pons69e17142020-03-23 12:26:29 +01001446 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01001447 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001448 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001449 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001450
Angel Pons7c49cb82020-03-16 23:17:32 +01001451 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001452 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001453
Angel Pons88521882020-01-05 20:21:20 +01001454 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001455}
1456
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001457static void timC_threshold_process(int *data, const int count)
1458{
1459 int min = data[0];
1460 int max = min;
1461 int i;
1462 for (i = 1; i < count; i++) {
1463 if (min > data[i])
1464 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001465
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001466 if (max < data[i])
1467 max = data[i];
1468 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001469 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001470 for (i = 0; i < count; i++)
1471 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001472
Angel Pons891f2bc2020-01-10 01:27:28 +01001473 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001474}
1475
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001476static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1477{
1478 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001479 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001480 int lane;
1481
Angel Pons88521882020-01-05 20:21:20 +01001482 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001483
1484 /* DRAM command PREA */
Angel Pons69e17142020-03-23 12:26:29 +01001485 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01001486 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001487 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001488 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001489
Angel Pons7c49cb82020-03-16 23:17:32 +01001490 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001491 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001492
1493 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001494 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001495 program_timings(ctrl, channel);
1496
1497 test_timC(ctrl, channel, slotrank);
1498
1499 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001500 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001501 }
1502 }
1503 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001504 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1505
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001506 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001507 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1508 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001509 /*
1510 * With command training not being done yet, the lane can be erroneous.
1511 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001512 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001513 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1514 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1515
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001516 if (rn.all || rn.length < 8) {
1517 printk(BIOS_EMERG, "timC recovery failed\n");
1518 return MAKE_ERR;
1519 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001520 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001521 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001522 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001523 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001524 }
1525 return 0;
1526}
1527
Angel Pons88521882020-01-05 20:21:20 +01001528static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001529{
1530 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001531
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001532 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1533 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001534
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001535 return ret;
1536}
1537
Angel Pons88521882020-01-05 20:21:20 +01001538static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001539{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301540 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001541 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001542
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001543 for (j = 0; j < 16; j++)
1544 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001545
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001546 sfence();
1547}
1548
Angel Pons88521882020-01-05 20:21:20 +01001549static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001550{
1551 int ret = 0;
1552 int channel;
1553 FOR_ALL_POPULATED_CHANNELS ret++;
1554 return ret;
1555}
1556
Angel Pons88521882020-01-05 20:21:20 +01001557static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001558{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301559 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001560 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301561 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001562
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001563 for (j = 0; j < 16; j++)
1564 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001565
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001566 for (j = 0; j < 16; j++)
1567 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001568
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001569 sfence();
1570}
1571
Angel Pons88521882020-01-05 20:21:20 +01001572static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001573{
1574 int channel, slotrank, lane;
1575
1576 FOR_ALL_POPULATED_CHANNELS {
1577 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001578 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1579 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001580 }
1581
1582 program_timings(ctrl, channel);
1583
1584 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001585 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001586
1587 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001588 write MR3 MPR enable
1589 in this mode only RD and RDA are allowed
1590 all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01001591 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001592 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001593 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001594 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001595 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001596 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001597
1598 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001599 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001600 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001601 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001602 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001603
1604 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001605 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001606 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001607 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001608 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001609 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001610 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001611
1612 /* DRAM command MRS
1613 * write MR3 MPR disable */
Angel Pons69e17142020-03-23 12:26:29 +01001614 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001615 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001616 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001617 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001618 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001619 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001620
Angel Pons7c49cb82020-03-16 23:17:32 +01001621 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001622 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001623
Angel Pons88521882020-01-05 20:21:20 +01001624 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001625 }
1626
1627 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001628 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1629 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001630 }
1631
1632 program_timings(ctrl, channel);
1633
1634 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001635 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001636 /* DRAM command MRS
1637 * write MR3 MPR enable
1638 * in this mode only RD and RDA are allowed
1639 * all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01001640 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001641 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001642 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001643 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001644 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001645 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001646
1647 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001648 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001649 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001650 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001651 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001652
1653 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001654 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01001655 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001656 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001657 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001658 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001659 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001660
1661 /* DRAM command MRS
1662 * write MR3 MPR disable */
Angel Pons69e17142020-03-23 12:26:29 +01001663 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01001664 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001665 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001666 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001667 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001668 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001669
Angel Pons7c49cb82020-03-16 23:17:32 +01001670 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001671 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001672
Angel Pons88521882020-01-05 20:21:20 +01001673 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001674 }
1675 }
1676}
1677
Angel Pons88521882020-01-05 20:21:20 +01001678static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001679{
1680 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001681 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001682
Angel Pons88521882020-01-05 20:21:20 +01001683 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001684 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001685 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_NOP;
Angel Pons7c49cb82020-03-16 23:17:32 +01001686 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001687 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001688 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001689 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001690
1691 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001692 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_NOP_ALT;
Angel Pons7c49cb82020-03-16 23:17:32 +01001693 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001694 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01001695 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001696
Angel Pons7c49cb82020-03-16 23:17:32 +01001697 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001698 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001699
Angel Pons88521882020-01-05 20:21:20 +01001700 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001701
1702 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001703 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001704}
1705
1706static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1707{
1708 int timB;
1709 int statistics[NUM_LANES][128];
1710 int lane;
1711
Angel Pons88521882020-01-05 20:21:20 +01001712 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001713
1714 for (timB = 0; timB < 128; timB++) {
1715 FOR_ALL_LANES {
1716 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1717 }
1718 program_timings(ctrl, channel);
1719
1720 test_timB(ctrl, channel, slotrank);
1721
1722 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001723 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1724 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1725 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001726 }
1727 }
1728 FOR_ALL_LANES {
1729 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001730 /*
1731 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1732 * of timB by a small value, which might cause the 6-bit value to overflow if
1733 * it's close to 0x3f. Increment the value by a small offset if it's likely
1734 * to overflow, to make sure it won't overflow while running tests and bricks
1735 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001736 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001737 * TODO: find out why some tests (edge write discovery) increment timB.
1738 */
1739 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001740 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001741 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001742 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001743
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001744 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1745 if (rn.all) {
1746 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1747 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001748
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001749 return MAKE_ERR;
1750 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001751 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1752 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001753 }
1754 return 0;
1755}
1756
1757static int get_timB_high_adjust(u64 val)
1758{
1759 int i;
1760
1761 /* good */
1762 if (val == 0xffffffffffffffffLL)
1763 return 0;
1764
1765 if (val >= 0xf000000000000000LL) {
1766 /* needs negative adjustment */
1767 for (i = 0; i < 8; i++)
1768 if (val << (8 * (7 - i) + 4))
1769 return -i;
1770 } else {
1771 /* needs positive adjustment */
1772 for (i = 0; i < 8; i++)
1773 if (val >> (8 * (7 - i) + 4))
1774 return i;
1775 }
1776 return 8;
1777}
1778
Angel Pons88521882020-01-05 20:21:20 +01001779static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001780{
1781 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001782 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001783 FOR_ALL_POPULATED_CHANNELS {
1784 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001785 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001786 }
1787 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1788
Angel Pons88521882020-01-05 20:21:20 +01001789 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001790
Angel Pons88521882020-01-05 20:21:20 +01001791 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001792
1793 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01001794 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01001795 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001796 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001797 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001798
1799 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001800 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_NOP;
Angel Pons7c49cb82020-03-16 23:17:32 +01001801 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8040c01;
Angel Pons88521882020-01-05 20:21:20 +01001802 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001803 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001804
1805 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01001806 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01001807 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x8041003;
Angel Pons88521882020-01-05 20:21:20 +01001808 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001809 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001810
1811 /* DRAM command NOP */
Angel Pons69e17142020-03-23 12:26:29 +01001812 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_NOP;
Angel Pons7c49cb82020-03-16 23:17:32 +01001813 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001814 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001815 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001816 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001817
Angel Pons7c49cb82020-03-16 23:17:32 +01001818 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001819 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001820
Angel Pons88521882020-01-05 20:21:20 +01001821 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001822
1823 /* DRAM command PREA */
Angel Pons69e17142020-03-23 12:26:29 +01001824 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01001825 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01001826 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001827 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001828
1829 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01001830 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01001831 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001832 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001833 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001834
1835 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01001836 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD | (3 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001837 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
Angel Pons88521882020-01-05 20:21:20 +01001838 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01001839 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001840 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
Angel Pons7c49cb82020-03-16 23:17:32 +01001841 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001842
Angel Pons7c49cb82020-03-16 23:17:32 +01001843 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001844 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001845
Angel Pons88521882020-01-05 20:21:20 +01001846 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001847 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001848 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001849 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001850 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001851 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1852 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1853 get_timB_high_adjust(res) * 64;
1854
1855 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001856 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1857 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001858 }
1859 }
Angel Pons88521882020-01-05 20:21:20 +01001860 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001861}
1862
Angel Pons88521882020-01-05 20:21:20 +01001863static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001864{
1865 int slotrank;
1866
Angel Pons88521882020-01-05 20:21:20 +01001867 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001868
1869 /* choose an existing rank. */
1870 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1871
Angel Pons69e17142020-03-23 12:26:29 +01001872 /* DRAM command ZQCS */
1873 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +01001874 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01001875 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001876 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001877
Angel Pons7c49cb82020-03-16 23:17:32 +01001878 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001879 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001880
Angel Pons88521882020-01-05 20:21:20 +01001881 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001882}
1883
Angel Pons7c49cb82020-03-16 23:17:32 +01001884/*
1885 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001886 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001887 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1888 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1889 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1890 * CLK/ADDR/CMD signals have the same routing delay.
1891 *
1892 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1893 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1894 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001895 */
Angel Pons88521882020-01-05 20:21:20 +01001896int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897{
1898 int channel, slotrank, lane;
1899 int err;
1900
1901 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001902 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001903
1904 FOR_ALL_POPULATED_CHANNELS {
1905 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001906 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001907 }
1908
Angel Pons7c49cb82020-03-16 23:17:32 +01001909 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001910 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001911 FOR_ALL_POPULATED_CHANNELS {
1912 write_op(ctrl, channel);
1913 }
1914
Angel Pons7c49cb82020-03-16 23:17:32 +01001915 /* Enable write leveling on all ranks
1916 Disable all DQ outputs
1917 Only NOP is allowed in this mode */
1918 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1919 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001920 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001921
Angel Pons88521882020-01-05 20:21:20 +01001922 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001923
1924 toggle_io_reset();
1925
Angel Pons7c49cb82020-03-16 23:17:32 +01001926 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001927 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1928 err = discover_timB(ctrl, channel, slotrank);
1929 if (err)
1930 return err;
1931 }
1932
Angel Pons7c49cb82020-03-16 23:17:32 +01001933 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001934 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001935 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001936
Angel Pons88521882020-01-05 20:21:20 +01001937 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001938
1939 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001940 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001941
Angel Pons7c49cb82020-03-16 23:17:32 +01001942 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001943 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001944
1945 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001946 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1947 MCHBAR32(IOSAV_STATUS_ch(channel));
1948 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001949
1950 /* DRAM command ZQCS */
Angel Pons69e17142020-03-23 12:26:29 +01001951 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +01001952 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
Angel Pons88521882020-01-05 20:21:20 +01001953 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001954 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001955
Angel Pons7c49cb82020-03-16 23:17:32 +01001956 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001957 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001958
Angel Pons88521882020-01-05 20:21:20 +01001959 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960 }
1961
1962 toggle_io_reset();
1963
1964 printram("CPE\n");
1965 precharge(ctrl);
1966 printram("CPF\n");
1967
1968 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001969 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001970 }
1971
1972 FOR_ALL_POPULATED_CHANNELS {
1973 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01001974 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001975 }
1976
1977 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1978 err = discover_timC(ctrl, channel, slotrank);
1979 if (err)
1980 return err;
1981 }
1982
1983 FOR_ALL_POPULATED_CHANNELS
1984 program_timings(ctrl, channel);
1985
1986 /* measure and adjust timB timings */
1987 adjust_high_timB(ctrl);
1988
1989 FOR_ALL_POPULATED_CHANNELS
1990 program_timings(ctrl, channel);
1991
1992 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001993 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001994 }
1995 return 0;
1996}
1997
Angel Pons88521882020-01-05 20:21:20 +01001998static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001999{
2000 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2001 int timC_delta;
2002 int lanes_ok = 0;
2003 int ctr = 0;
2004 int lane;
2005
2006 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2007 FOR_ALL_LANES {
2008 ctrl->timings[channel][slotrank].lanes[lane].timC =
2009 saved_rt.lanes[lane].timC + timC_delta;
2010 }
2011 program_timings(ctrl, channel);
2012 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002013 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002014 }
2015
Angel Pons88521882020-01-05 20:21:20 +01002016 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002017
Angel Pons88521882020-01-05 20:21:20 +01002018 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002019 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01002020 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01002021 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002022 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002023 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002024 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002025 (slotrank << 24) | ctr | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002026 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002027
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002028 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01002029 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01002030 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002031 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002032 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002033 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x20e42;
Angel Ponsc36cd072020-05-02 16:51:39 +02002034 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002035
2036 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002037 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002038 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002039 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002040 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002041 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x20e42;
Angel Ponsc36cd072020-05-02 16:51:39 +02002042 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002043
2044 /* DRAM command PRE */
Angel Pons69e17142020-03-23 12:26:29 +01002045 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01002046 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +01002047 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002048 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002049
Angel Pons7c49cb82020-03-16 23:17:32 +01002050 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002051 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002052
Angel Pons88521882020-01-05 20:21:20 +01002053 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002054 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002055 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002056
2057 if (r32 == 0)
2058 lanes_ok |= 1 << lane;
2059 }
2060 ctr++;
Patrick Rudolphdd662872017-10-28 18:20:11 +02002061 if (lanes_ok == ((1 << ctrl->lanes) - 1))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002062 break;
2063 }
2064
2065 ctrl->timings[channel][slotrank] = saved_rt;
2066
Patrick Rudolphdd662872017-10-28 18:20:11 +02002067 return lanes_ok != ((1 << ctrl->lanes) - 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002068}
2069
Angel Pons88521882020-01-05 20:21:20 +01002070static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002071{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302072 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002073 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2074 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002075
2076 if (patno) {
2077 u8 base8 = 0x80 >> ((patno - 1) % 8);
2078 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2079 for (i = 0; i < 32; i++) {
2080 for (j = 0; j < 16; j++) {
2081 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002082
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002083 if (invert[patno - 1][i] & (1 << (j / 2)))
2084 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002085
2086 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002087 }
2088 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002089 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002090 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2091 for (j = 0; j < 16; j++) {
2092 const u32 val = pattern[i][j];
2093 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2094 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002095 }
2096 sfence();
2097 }
2098}
2099
Angel Pons88521882020-01-05 20:21:20 +01002100static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002101{
2102 int channel, slotrank;
2103
2104 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002105 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002106
Angel Pons7c49cb82020-03-16 23:17:32 +01002107 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002108 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2109
2110 /* DRAM command ZQCS */
Angel Pons69e17142020-03-23 12:26:29 +01002111 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +01002112 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002113 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002114 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002115
Angel Pons7c49cb82020-03-16 23:17:32 +01002116 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002117 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002118
Angel Pons88521882020-01-05 20:21:20 +01002119 wait_for_iosav(channel);
2120 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002121 }
2122
2123 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002124 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002125 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002126 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002127
2128 /* choose an existing rank. */
2129 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2130
2131 /* DRAM command ZQCS */
Angel Pons69e17142020-03-23 12:26:29 +01002132 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ZQCS & NO_RANKSEL;
Angel Pons7c49cb82020-03-16 23:17:32 +01002133 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002134 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002135 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002136
Angel Pons7c49cb82020-03-16 23:17:32 +01002137 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002138 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002139
Angel Pons88521882020-01-05 20:21:20 +01002140 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002141 }
2142
Angel Pons7c49cb82020-03-16 23:17:32 +01002143 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002144 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002145
2146 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002147 dram_mrscommands(ctrl);
2148
2149 toggle_io_reset();
2150}
2151
2152#define MIN_C320C_LEN 13
2153
2154static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2155{
2156 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2157 int slotrank;
2158 int c320c;
2159 int stat[NUM_SLOTRANKS][256];
2160 int delta = 0;
2161
2162 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2163
2164 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002165 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002166 }
2167
2168 ctrl->cmd_stretch[channel] = cmd_stretch;
2169
Angel Pons88521882020-01-05 20:21:20 +01002170 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002171 (ctrl->tRRD << 0)
2172 | (ctrl->tRTP << 4)
2173 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002174 | (ctrl->tWTR << 12)
2175 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002176 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002177 | (ctrl->cmd_stretch[channel] << 30);
2178
2179 if (ctrl->cmd_stretch[channel] == 2)
2180 delta = 2;
2181 else if (ctrl->cmd_stretch[channel] == 0)
2182 delta = 4;
2183
2184 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002185 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002186 }
2187
2188 for (c320c = -127; c320c <= 127; c320c++) {
2189 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002190 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002191 }
2192 program_timings(ctrl, channel);
2193 reprogram_320c(ctrl);
2194 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002195 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002196 }
2197 }
2198 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002199 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2200
Angel Pons88521882020-01-05 20:21:20 +01002201 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002202 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2203 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002204
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002205 if (rn.all || rn.length < MIN_C320C_LEN) {
2206 FOR_ALL_POPULATED_RANKS {
2207 ctrl->timings[channel][slotrank] =
2208 saved_timings[channel][slotrank];
2209 }
2210 return MAKE_ERR;
2211 }
2212 }
2213
2214 return 0;
2215}
2216
Angel Pons7c49cb82020-03-16 23:17:32 +01002217/*
2218 * Adjust CMD phase shift and try multiple command rates.
2219 * A command rate of 2T doubles the time needed for address and command decode.
2220 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002221int command_training(ramctr_timing *ctrl)
2222{
2223 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002224
2225 FOR_ALL_POPULATED_CHANNELS {
2226 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002227 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002228 }
2229
2230 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002231 int cmdrate, err;
2232
2233 /*
2234 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002235 * Issue:
2236 * While c320c discovery seems to succeed raminit will fail in write training.
2237 *
2238 * Workaround:
2239 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2240 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002241 *
2242 * Single DIMM per channel:
2243 * Try command rate 1T and 2T
2244 */
2245 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002246 if (ctrl->tCMD)
2247 /* XMP gives the CMD rate in clock ticks, not ns */
2248 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002249
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002250 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002251 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2252
2253 if (!err)
2254 break;
2255 }
2256
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002257 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002258 printk(BIOS_EMERG, "c320c discovery failed\n");
2259 return err;
2260 }
2261
Angel Pons891f2bc2020-01-10 01:27:28 +01002262 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002263 }
2264
2265 FOR_ALL_POPULATED_CHANNELS
2266 program_timings(ctrl, channel);
2267
2268 reprogram_320c(ctrl);
2269 return 0;
2270}
2271
Angel Pons891f2bc2020-01-10 01:27:28 +01002272static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002273{
2274 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002275 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002276 int lane;
2277
2278 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2279 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002280 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002281 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002282 }
2283 program_timings(ctrl, channel);
2284
2285 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002286 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2287 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002288 }
2289
Angel Pons88521882020-01-05 20:21:20 +01002290 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002291
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002292 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002293 write MR3 MPR enable
2294 in this mode only RD and RDA are allowed
2295 all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01002296 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002297 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002298 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002299 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002300
2301 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002302 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002303 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x40411f4;
Angel Pons88521882020-01-05 20:21:20 +01002304 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002305 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002306
2307 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002308 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002309 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002310 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002311 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002312
2313 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002314 MR3 disable MPR */
Angel Pons69e17142020-03-23 12:26:29 +01002315 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002316 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002317 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002318 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002319
Angel Pons7c49cb82020-03-16 23:17:32 +01002320 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002321 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002322
Angel Pons88521882020-01-05 20:21:20 +01002323 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002324
2325 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002326 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002327 }
2328 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002329
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002330 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002331 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002332 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002333
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002334 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002335 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2336 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002337 return MAKE_ERR;
2338 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002339 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002340 }
2341 return 0;
2342}
2343
2344int discover_edges(ramctr_timing *ctrl)
2345{
2346 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2347 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2348 int channel, slotrank, lane;
2349 int err;
2350
Angel Pons88521882020-01-05 20:21:20 +01002351 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002352
2353 toggle_io_reset();
2354
2355 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002356 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002357 }
2358
2359 FOR_ALL_POPULATED_CHANNELS {
2360 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002361 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002362 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002363 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002364 }
2365
2366 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002367 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2368 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002369 }
2370
2371 program_timings(ctrl, channel);
2372
2373 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002374 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002375
2376 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002377 MR3 enable MPR
2378 write MR3 MPR enable
2379 in this mode only RD and RDA are allowed
2380 all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01002381 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002382 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002383 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002384 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002385 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002386 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002387
2388 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002389 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002390 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002391 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002392 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002393
2394 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002395 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002396 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002397 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002398 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002399 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002400 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002401
2402 /* DRAM command MRS
2403 * MR3 disable MPR */
Angel Pons69e17142020-03-23 12:26:29 +01002404 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002405 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002406 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002407 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002408 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002409 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002410
Angel Pons7c49cb82020-03-16 23:17:32 +01002411 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002412 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002413
Angel Pons88521882020-01-05 20:21:20 +01002414 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002415 }
2416
2417 /* XXX: check any measured value ? */
2418
2419 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002420 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002421 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002422 }
2423
2424 program_timings(ctrl, channel);
2425
2426 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002427 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002428
2429 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002430 MR3 enable MPR
2431 write MR3 MPR enable
2432 in this mode only RD and RDA are allowed
2433 all reads return a predefined pattern */
Angel Pons69e17142020-03-23 12:26:29 +01002434 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002435 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002436 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002437 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002438 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002439 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002440
2441 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002442 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002443 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons88521882020-01-05 20:21:20 +01002444 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002445 (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002446 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002447
2448 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002449 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002450 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002451 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002452 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002453 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002454 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002455
2456 /* DRAM command MRS
2457 * MR3 disable MPR */
Angel Pons69e17142020-03-23 12:26:29 +01002458 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_MRS;
Angel Pons7c49cb82020-03-16 23:17:32 +01002459 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002460 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002461 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002462 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002463 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002464
Angel Pons7c49cb82020-03-16 23:17:32 +01002465 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002466 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002467
Angel Pons88521882020-01-05 20:21:20 +01002468 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002469 }
2470
2471 /* XXX: check any measured value ? */
2472
2473 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002474 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002475 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002476 }
2477
2478 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002479 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002480 }
2481
Angel Pons0c3936e2020-03-22 12:49:27 +01002482 /*
2483 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2484 * also use a single loop. It would seem that it is a debugging configuration.
2485 */
Angel Pons88521882020-01-05 20:21:20 +01002486 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2487 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002488
2489 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2490 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002491 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002492 if (err)
2493 return err;
2494 }
2495
Angel Pons88521882020-01-05 20:21:20 +01002496 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2497 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002498
2499 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2500 err = discover_edges_real(ctrl, channel, slotrank,
2501 rising_edges[channel][slotrank]);
2502 if (err)
2503 return err;
2504 }
2505
Angel Pons88521882020-01-05 20:21:20 +01002506 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002507
2508 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2509 ctrl->timings[channel][slotrank].lanes[lane].falling =
2510 falling_edges[channel][slotrank][lane];
2511 ctrl->timings[channel][slotrank].lanes[lane].rising =
2512 rising_edges[channel][slotrank][lane];
2513 }
2514
2515 FOR_ALL_POPULATED_CHANNELS {
2516 program_timings(ctrl, channel);
2517 }
2518
2519 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002520 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002521 }
2522 return 0;
2523}
2524
Angel Pons7c49cb82020-03-16 23:17:32 +01002525static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002526{
2527 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002528 u32 raw_stats[MAX_EDGE_TIMING + 1];
2529 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002530 const int reg3000b24[] = { 0, 0xc, 0x2c };
2531 int lane, i;
2532 int lower[NUM_LANES];
2533 int upper[NUM_LANES];
2534 int pat;
2535
2536 FOR_ALL_LANES {
2537 lower[lane] = 0;
2538 upper[lane] = MAX_EDGE_TIMING;
2539 }
2540
2541 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002542 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002543 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2544
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002545 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2546 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002547 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002548 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002549
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002550 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2551 FOR_ALL_LANES {
2552 ctrl->timings[channel][slotrank].lanes[lane].
2553 rising = edge;
2554 ctrl->timings[channel][slotrank].lanes[lane].
2555 falling = edge;
2556 }
2557 program_timings(ctrl, channel);
2558
2559 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002560 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2561 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002562 }
Angel Pons88521882020-01-05 20:21:20 +01002563 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002564
2565 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01002566 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01002567 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002568 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002569 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002570 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002571 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002572 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002573
2574 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01002575 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01002576 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002577 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002578 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002579 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002580 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002581
2582 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002583 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002584 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002585 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002586 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002587 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002588 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002589
2590 /* DRAM command PRE */
Angel Pons69e17142020-03-23 12:26:29 +01002591 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01002592 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002593 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002594 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002595 (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002596 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002597
Angel Pons7c49cb82020-03-16 23:17:32 +01002598 /* Execute command queue */
2599 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002600
Angel Pons88521882020-01-05 20:21:20 +01002601 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002602 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002603 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002604 }
2605
Angel Pons7c49cb82020-03-16 23:17:32 +01002606 /* FIXME: This register only exists on Ivy Bridge */
Angel Pons098240eb2020-03-22 12:55:32 +01002607 raw_stats[edge] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002608 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002609
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002610 FOR_ALL_LANES {
2611 struct run rn;
2612 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002613 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2614
2615 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2616
2617 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2618 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2619 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002620 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002621
2622 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2623 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2624
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002625 edges[lane] = (lower[lane] + upper[lane]) / 2;
2626 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002627 printk(BIOS_EMERG, "edge write discovery failed: "
2628 "%d, %d, %d\n", channel, slotrank, lane);
2629
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002630 return MAKE_ERR;
2631 }
2632 }
2633 }
2634 }
2635
Angel Pons88521882020-01-05 20:21:20 +01002636 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002637 printram("CPA\n");
2638 return 0;
2639}
2640
2641int discover_edges_write(ramctr_timing *ctrl)
2642{
2643 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002644 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2645 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002646
Angel Pons7c49cb82020-03-16 23:17:32 +01002647 /*
2648 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2649 * also use a single loop. It would seem that it is a debugging configuration.
2650 */
Angel Pons88521882020-01-05 20:21:20 +01002651 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2652 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002653
2654 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2655 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002656 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002657 if (err)
2658 return err;
2659 }
2660
Angel Pons88521882020-01-05 20:21:20 +01002661 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2662 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002663
2664 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2665 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002666 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002667 if (err)
2668 return err;
2669 }
2670
Angel Pons88521882020-01-05 20:21:20 +01002671 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002672
2673 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2674 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002675 falling_edges[channel][slotrank][lane];
2676
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002677 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002678 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002679 }
2680
2681 FOR_ALL_POPULATED_CHANNELS
2682 program_timings(ctrl, channel);
2683
2684 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002685 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002686 }
2687 return 0;
2688}
2689
2690static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2691{
Angel Pons88521882020-01-05 20:21:20 +01002692 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002693
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002694 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01002695 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01002696 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002697 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01002698 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2699 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x0244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002700
2701 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01002702 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01002703 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002704 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002705 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002706 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002707
2708 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002709 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002710 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002711 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002712 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002713
2714 /* DRAM command PRE */
Angel Pons69e17142020-03-23 12:26:29 +01002715 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01002716 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002717 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002718 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002719
Angel Pons7c49cb82020-03-16 23:17:32 +01002720 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002721 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002722
Angel Pons88521882020-01-05 20:21:20 +01002723 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002724}
2725
2726int discover_timC_write(ramctr_timing *ctrl)
2727{
Angel Pons7c49cb82020-03-16 23:17:32 +01002728 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002729 int i, pat;
2730
2731 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2732 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2733 int channel, slotrank, lane;
2734
2735 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2736 lower[channel][slotrank][lane] = 0;
2737 upper[channel][slotrank][lane] = MAX_TIMC;
2738 }
2739
Angel Pons88521882020-01-05 20:21:20 +01002740 /*
2741 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2742 * FIXME: This must only be done on Ivy Bridge.
2743 */
2744 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002745 printram("discover timC write:\n");
2746
2747 for (i = 0; i < 3; i++)
2748 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002749
2750 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2751 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2752 ~0x3f000000, rege3c_b24[i] << 24);
2753
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002754 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002755
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002756 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2757 FOR_ALL_POPULATED_RANKS {
2758 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002759 u32 raw_stats[MAX_TIMC + 1];
2760 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002761
2762 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002763 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002764
2765 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002766 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2767
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002768 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002769 FOR_ALL_LANES {
2770 ctrl->timings[channel][slotrank]
2771 .lanes[lane].timC = timC;
2772 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002773 program_timings(ctrl, channel);
2774
2775 test_timC_write (ctrl, channel, slotrank);
2776
Angel Pons7c49cb82020-03-16 23:17:32 +01002777 /* FIXME: Another IVB-only register! */
Angel Pons098240eb2020-03-22 12:55:32 +01002778 raw_stats[timC] = MCHBAR32(
2779 IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002780 }
2781 FOR_ALL_LANES {
2782 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002783 for (timC = 0; timC < MAX_TIMC; timC++) {
2784 stats[timC] = !!(raw_stats[timC]
2785 & (1 << lane));
2786 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002787
Angel Pons7c49cb82020-03-16 23:17:32 +01002788 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002789 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002790 printk(BIOS_EMERG,
2791 "timC write discovery failed: "
2792 "%d, %d, %d\n", channel,
2793 slotrank, lane);
2794
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002795 return MAKE_ERR;
2796 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002797 printram("timC: %d, %d, %d: "
2798 "0x%02x-0x%02x-0x%02x, "
2799 "0x%02x-0x%02x\n", channel, slotrank,
2800 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002801 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002802 rn.end - ctrl->timC_offset[i]);
2803
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002804 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002805 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002806 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002807
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002808 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002809 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002810 upper[channel][slotrank][lane]);
2811
2812 }
2813 }
2814 }
2815 }
2816
2817 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002818 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002819 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002820 udelay(2);
2821 }
2822
Angel Pons88521882020-01-05 20:21:20 +01002823 /*
2824 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2825 * FIXME: This must only be done on Ivy Bridge.
2826 */
2827 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002828
2829 printram("CPB\n");
2830
2831 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002832 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002833 (lower[channel][slotrank][lane] +
2834 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002835
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002836 ctrl->timings[channel][slotrank].lanes[lane].timC =
2837 (lower[channel][slotrank][lane] +
2838 upper[channel][slotrank][lane]) / 2;
2839 }
2840 FOR_ALL_POPULATED_CHANNELS {
2841 program_timings(ctrl, channel);
2842 }
2843 return 0;
2844}
2845
Angel Pons88521882020-01-05 20:21:20 +01002846void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002847{
2848 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002849 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002850
2851 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2852 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002853 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002854 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002855 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002856 printram("normalize %d, %d, %d: mat %d\n",
2857 channel, slotrank, lane, mat);
2858
Felix Heldef4fe3e2019-12-31 14:15:05 +01002859 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002860 printram("normalize %d, %d, %d: delta %d\n",
2861 channel, slotrank, lane, delta);
2862
Angel Pons88521882020-01-05 20:21:20 +01002863 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002864 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002865 }
2866
2867 FOR_ALL_POPULATED_CHANNELS {
2868 program_timings(ctrl, channel);
2869 }
2870}
2871
Angel Pons88521882020-01-05 20:21:20 +01002872void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002873{
2874 int channel, slotrank;
2875
2876 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002877 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002878 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002879 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002880 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002881 }
2882}
2883
2884int channel_test(ramctr_timing *ctrl)
2885{
2886 int channel, slotrank, lane;
2887
2888 slotrank = 0;
2889 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002890 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002891 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002892 return MAKE_ERR;
2893 }
2894 FOR_ALL_POPULATED_CHANNELS {
2895 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2896
Angel Pons88521882020-01-05 20:21:20 +01002897 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002898 }
2899
2900 for (slotrank = 0; slotrank < 4; slotrank++)
2901 FOR_ALL_CHANNELS
2902 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2903 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002904 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2905 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002906 }
Angel Pons88521882020-01-05 20:21:20 +01002907 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002908
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002909 /* DRAM command ACT */
Angel Pons69e17142020-03-23 12:26:29 +01002910 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
Angel Pons7c49cb82020-03-16 23:17:32 +01002911 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01002912 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002913 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02002914
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002915 /* DRAM command WR */
Angel Pons69e17142020-03-23 12:26:29 +01002916 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR;
Angel Pons7c49cb82020-03-16 23:17:32 +01002917 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002918 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002919 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002920
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002921 /* DRAM command RD */
Angel Pons69e17142020-03-23 12:26:29 +01002922 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_RD;
Angel Pons7c49cb82020-03-16 23:17:32 +01002923 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002924 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002925 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002926
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002927 /* DRAM command PRE */
Angel Pons69e17142020-03-23 12:26:29 +01002928 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = IOSAV_PRE;
Angel Pons7c49cb82020-03-16 23:17:32 +01002929 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01002930 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002931 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02002932
Angel Pons7c49cb82020-03-16 23:17:32 +01002933 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002934 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002935
Angel Pons88521882020-01-05 20:21:20 +01002936 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002937 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002938 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002939 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2940 channel, slotrank, lane);
2941 return MAKE_ERR;
2942 }
2943 }
2944 return 0;
2945}
2946
Patrick Rudolphdd662872017-10-28 18:20:11 +02002947void channel_scrub(ramctr_timing *ctrl)
2948{
2949 int channel, slotrank, row, rowsize;
2950
2951 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
2952 rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
2953 for (row = 0; row < rowsize; row += 16) {
2954
2955 wait_for_iosav(channel);
2956
2957 /* DRAM command ACT */
2958 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT;
2959 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
2960 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10)
2961 | 1 | (ctrl->tRCD << 16);
2962 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
2963 row | 0x00060000 | (slotrank << 24);
2964 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000241;
2965
2966 /* DRAM command WR */
2967 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR;
2968 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281081;
2969 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = row | (slotrank << 24);
2970 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
2971
2972 /* DRAM command PRE */
2973 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_PRE;
2974 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x00280c01;
2975 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
2976 0x00060400 | (slotrank << 24);
2977 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000240;
2978
2979 /* execute command queue */
2980 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
2981
2982 wait_for_iosav(channel);
2983 }
2984 }
2985}
2986
Angel Pons88521882020-01-05 20:21:20 +01002987void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002988{
2989 int channel;
2990
Angel Pons7c49cb82020-03-16 23:17:32 +01002991 /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? I don't think so. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002992 static u32 seeds[NUM_CHANNELS][3] = {
2993 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
2994 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
2995 };
2996 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002997 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002998 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
2999 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
3000 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003001 }
3002}
3003
Angel Pons89ae6b82020-03-21 13:23:32 +01003004void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003005{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003006 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003007 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003008 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003009 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003010 }
3011}
3012
Angel Pons88521882020-01-05 20:21:20 +01003013void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003014{
3015 int channel;
3016
3017 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003018 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003019 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003020 }
3021
3022 udelay(1);
3023
3024 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003025 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003026 }
3027}
3028
Angel Pons7c49cb82020-03-16 23:17:32 +01003029void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003030{
3031 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003032
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003033 FOR_ALL_POPULATED_CHANNELS {
3034 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003035 int min_pi = 10000;
3036 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003037
3038 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003039 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3040 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003041 }
3042
Angel Pons7c49cb82020-03-16 23:17:32 +01003043 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003044
Angel Pons7c49cb82020-03-16 23:17:32 +01003045 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003046
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003047 dram_odt_stretch(ctrl, channel);
3048
Angel Pons7c49cb82020-03-16 23:17:32 +01003049 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003050 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003051 }
3052}
3053
Angel Pons88521882020-01-05 20:21:20 +01003054void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003055{
3056 int channel;
3057 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003058 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3059 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003060 }
3061}
3062
Angel Pons7c49cb82020-03-16 23:17:32 +01003063/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3064static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003065{
Angel Pons88521882020-01-05 20:21:20 +01003066 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003067}
3068
Angel Pons7c49cb82020-03-16 23:17:32 +01003069/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003070void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003071{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003072 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3073
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003074 int channel;
3075 int t1_cycles = 0, t1_ns = 0, t2_ns;
3076 int t3_ns;
3077 u32 r32;
3078
Angel Pons7c49cb82020-03-16 23:17:32 +01003079 /* FIXME: This register only exists on Ivy Bridge */
3080 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003081
Felix Heldf9b826a2018-07-30 17:56:52 +02003082 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003083 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003084
Patrick Rudolph74203de2017-11-20 11:57:01 +01003085 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003086 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003087 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003088 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003089 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003090 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003091
Felix Heldf9b826a2018-07-30 17:56:52 +02003092 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003093 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003094
Angel Pons88521882020-01-05 20:21:20 +01003095 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3096 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003097
3098 FOR_ALL_CHANNELS {
3099 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003100 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003101 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003102 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003103 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003104 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003105 case 1:
3106 case 4:
3107 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003108 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003109 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003110 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003111 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003112 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003113 break;
3114 }
3115 }
3116
Felix Held50b7ed22019-12-30 20:41:54 +01003117 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003118 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003119 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003120
3121 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003122 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003123
Angel Pons88521882020-01-05 20:21:20 +01003124 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3125 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3126 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003127
Angel Pons7c49cb82020-03-16 23:17:32 +01003128 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003129 FOR_ALL_POPULATED_CHANNELS
3130 break;
3131
Angel Pons88521882020-01-05 20:21:20 +01003132 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3133 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003134 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003135 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003136 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003137 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003138 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003139 t1_ns += 500;
3140
Angel Pons88521882020-01-05 20:21:20 +01003141 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003142 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003143 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003144 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003145 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003146 t3_ns = 500;
3147 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003148
3149 /* The graphics driver will use these watermark values */
3150 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3151 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3152 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3153 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003154}
3155
Angel Pons88521882020-01-05 20:21:20 +01003156void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003157{
3158 int channel, slotrank, lane;
3159
Angel Pons7c49cb82020-03-16 23:17:32 +01003160 FOR_ALL_POPULATED_CHANNELS {
3161 MCHBAR32(TC_RAP_ch(channel)) =
3162 (ctrl->tRRD << 0)
3163 | (ctrl->tRTP << 4)
3164 | (ctrl->tCKE << 8)
3165 | (ctrl->tWTR << 12)
3166 | (ctrl->tFAW << 16)
3167 | (ctrl->tWR << 24)
3168 | (ctrl->cmd_stretch[channel] << 30);
3169 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003170
3171 udelay(1);
3172
3173 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003174 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003175 }
3176
3177 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003178 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003179 }
3180
3181 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003182 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003183
3184 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003185 udelay(1);
3186 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003187 }
3188
3189 printram("CPE\n");
3190
Angel Pons88521882020-01-05 20:21:20 +01003191 MCHBAR32(GDCRTRAININGMOD) = 0;
3192 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003193
3194 printram("CP5b\n");
3195
3196 FOR_ALL_POPULATED_CHANNELS {
3197 program_timings(ctrl, channel);
3198 }
3199
3200 u32 reg, addr;
3201
Angel Pons7c49cb82020-03-16 23:17:32 +01003202 /* Poll for RCOMP */
3203 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3204 ;
3205
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003206 do {
Angel Pons88521882020-01-05 20:21:20 +01003207 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208 } while ((reg & 0x14) == 0);
3209
Angel Pons7c49cb82020-03-16 23:17:32 +01003210 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003211 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003212 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003213
Angel Pons7c49cb82020-03-16 23:17:32 +01003214 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003215 udelay(500);
3216
3217 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003218 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003219 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003220 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003221 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003222 MCHBAR32(addr) = reg;
3223
Angel Pons7c49cb82020-03-16 23:17:32 +01003224 /* Wait 10ns for ranks to settle */
3225 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003226
3227 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3228 MCHBAR32(addr) = reg;
3229
Angel Pons7c49cb82020-03-16 23:17:32 +01003230 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003231 write_reset(ctrl);
3232 }
3233
Angel Pons7c49cb82020-03-16 23:17:32 +01003234 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003235 dram_mrscommands(ctrl);
3236
3237 printram("CP5c\n");
3238
Angel Pons88521882020-01-05 20:21:20 +01003239 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003240
3241 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003242 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003243 udelay(2);
3244 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003245}